- Home
- Documentation
- Strumenti di runtime
- Strumenti personalizzati
Strumenti personalizzati
Gli strumenti personalizzati sono funzioni richiamabili dal modello che si integrano nella stessa pipeline di esecuzione degli strumenti integrati.
Uno strumento personalizzato è un modulo TypeScript/JavaScript che esporta una factory. La factory riceve un’API host (CustomToolAPI) e restituisce uno strumento o un array di strumenti.
Cosa è (e cosa non è)
Sezione intitolata “Cosa è (e cosa non è)”- Strumento personalizzato: richiamabile dal modello durante un turno (
execute+ schema TypeBox). - Estensione: framework di ciclo di vita/eventi che può registrare strumenti e intercettare/modificare eventi.
- Hook: script esterni pre/post comando.
- Skill: pacchetto statico di guida/contesto, non codice di strumento eseguibile.
Se è necessario che il modello richiami codice direttamente, utilizzare uno strumento personalizzato.
Percorsi di integrazione nel codice corrente
Sezione intitolata “Percorsi di integrazione nel codice corrente”Esistono due stili di integrazione attivi:
-
Strumenti personalizzati forniti dall’SDK (
options.customTools)- Incapsulati in strumenti agente tramite
CustomToolAdaptero wrapper di estensione. - Sempre inclusi nel set di strumenti attivi iniziale nel bootstrap dell’SDK.
- Incapsulati in strumenti agente tramite
-
Moduli rilevati dal filesystem tramite API loader (
discoverAndLoadCustomTools/loadCustomTools)- Esposti come API di libreria in
src/extensibility/custom-tools/loader.ts. - Il codice host può chiamarli per rilevare e caricare moduli di strumenti dai percorsi di configurazione/provider/plugin.
- Esposti come API di libreria in
Flusso chiamata strumento modello
Chiamata strumento LLM │ ▼Registro strumenti (integrati + adattatori strumenti personalizzati) │ ▼CustomTool.execute(toolCallId, params, onUpdate, ctx, signal) │ ├─ onUpdate(...) -> risultato parziale in streaming └─ return result -> contenuto/dettagli strumento finalePosizioni di rilevamento (API loader)
Sezione intitolata “Posizioni di rilevamento (API loader)”discoverAndLoadCustomTools(configuredPaths, cwd, builtInToolNames) combina:
- Provider di capacità (
toolCapability), inclusi:- Configurazione OMP nativa (
~/.xcsh/agent/tools,.xcsh/tools) - Configurazione Claude (
~/.claude/tools,.claude/tools) - Configurazione Codex (
~/.codex/tools,.codex/tools) - Provider cache plugin marketplace Claude
- Configurazione OMP nativa (
- Manifesti plugin installati (
~/.xcsh/plugins/node_modules/*tramite plugin loader) - Percorsi configurati esplicitamente passati al loader
Comportamento importante
Sezione intitolata “Comportamento importante”- I percorsi risolti duplicati vengono deduplicati.
- I conflitti di nomi degli strumenti vengono rifiutati rispetto agli strumenti integrati e agli strumenti personalizzati già caricati.
- I file
.mde.jsonvengono rilevati come metadati degli strumenti da alcuni provider, ma il loader di moduli eseguibili li rifiuta come strumenti eseguibili. - I percorsi configurati relativi vengono risolti a partire da
cwd;~viene espanso.
Contratto del modulo
Sezione intitolata “Contratto del modulo”Un modulo di strumento personalizzato deve esportare una funzione (preferibilmente export default):
import type { CustomToolFactory } from "@f5-sales-demo/xcsh";
const factory: CustomToolFactory = (pi) => ({ name: "repo_stats", label: "Repo Stats", description: "Counts tracked TypeScript files", parameters: pi.typebox.Type.Object({ glob: pi.typebox.Type.Optional(pi.typebox.Type.String({ default: "**/*.ts" })), }),
async execute(toolCallId, params, onUpdate, ctx, signal) { onUpdate?.({ content: [{ type: "text", text: "Scanning files..." }], details: { phase: "scan" }, });
const result = await pi.exec("git", ["ls-files", params.glob ?? "**/*.ts"], { signal, cwd: pi.cwd }); if (result.killed) { throw new Error("Scan was cancelled"); } if (result.code !== 0) { throw new Error(result.stderr || "git ls-files failed"); }
const files = result.stdout.split("\n").filter(Boolean); return { content: [{ type: "text", text: `Found ${files.length} files` }], details: { count: files.length, sample: files.slice(0, 10) }, }; },
onSession(event) { if (event.reason === "shutdown") { // cleanup resources if needed } },});
export default factory;Tipo restituito dalla factory:
CustomToolCustomTool[]Promise<CustomTool | CustomTool[]>
Superficie API passata alle factory (CustomToolAPI)
Sezione intitolata “Superficie API passata alle factory (CustomToolAPI)”Da types.ts e loader.ts:
cwd: directory di lavoro dell’hostexec(command, args, options?): helper per l’esecuzione di processiui: contesto UI (può essere no-op nelle modalità headless)hasUI:falsenei flussi non interattivilogger: logger su file condivisotypebox:@sinclair/typeboxiniettatopi: export di@f5-sales-demo/xcshiniettatipushPendingAction(action): registra un’azione di anteprima per lo strumento nascostoresolve(docs/resolve-tool-runtime.md)
Il loader avvia con un contesto UI no-op e richiede che il codice host chiami setUIContext(...) quando la UI reale è pronta.
Contratto di esecuzione e tipizzazione
Sezione intitolata “Contratto di esecuzione e tipizzazione”Firma di CustomTool.execute:
execute(toolCallId, params, onUpdate, ctx, signal)paramsè tipizzato staticamente dallo schema TypeBox tramiteStatic<TParams>.- La validazione degli argomenti a runtime avviene prima dell’esecuzione nel ciclo agente.
onUpdateemette risultati parziali per lo streaming UI.ctxinclude lo stato sessione/modello e un helperabort().signalgestisce la cancellazione.
CustomToolAdapter collega questo all’interfaccia dello strumento agente e inoltra le chiamate nell’ordine corretto degli argomenti.
Come gli strumenti vengono esposti al modello
Sezione intitolata “Come gli strumenti vengono esposti al modello”- Gli strumenti vengono incapsulati in istanze
AgentTool(CustomToolAdaptero wrapper di estensione). - Vengono inseriti nel registro degli strumenti di sessione per nome.
- Nel bootstrap dell’SDK, gli strumenti personalizzati e quelli registrati tramite estensione vengono forzatamente inclusi nel set attivo iniziale.
- L’opzione CLI
--toolsal momento valida solo i nomi degli strumenti integrati; l’inclusione degli strumenti personalizzati è gestita tramite i percorsi di rilevamento/registrazione e le opzioni dell’SDK.
Hook di rendering
Sezione intitolata “Hook di rendering”Hook di rendering facoltativi:
renderCall(args, theme)renderResult(result, options, theme, args?)
Comportamento a runtime nella TUI:
- Se gli hook esistono, l’output dello strumento viene renderizzato all’interno di un contenitore
Box. renderResultriceve{ expanded, isPartial, spinnerFrame? }.- Gli errori del renderer vengono intercettati e registrati; la UI torna al rendering testuale predefinito.
Gestione sessione/stato
Sezione intitolata “Gestione sessione/stato”L’hook facoltativo onSession(event, ctx) riceve gli eventi del ciclo di vita della sessione, inclusi:
start,switch,branch,tree,shutdownauto_compaction_start,auto_compaction_endauto_retry_start,auto_retry_endttsr_triggered,todo_reminder
Utilizzare ctx.sessionManager per ricostruire lo stato dalla cronologia quando il contesto branch/sessione cambia.
Semantica di fallimenti e cancellazione
Sezione intitolata “Semantica di fallimenti e cancellazione”Fallimenti sincroni/asincroni
Sezione intitolata “Fallimenti sincroni/asincroni”- Sollevare un’eccezione (o promise rifiutate) in
executeviene trattato come fallimento dello strumento. - Il runtime agente converte i fallimenti in messaggi di risultato dello strumento con
isError: truee contenuto testuale dell’errore. - Con i wrapper di estensione, i gestori
tool_resultpossono ulteriormente riscrivere contenuto/dettagli e persino sovrascrivere lo stato di errore.
Cancellazione
Sezione intitolata “Cancellazione”- L’abort dell’agente si propaga tramite
AbortSignalaexecute. - Inoltrare
signalai processi secondari (pi.exec(..., { signal })) per la cancellazione cooperativa. ctx.abort()consente a uno strumento di richiedere l’abort dell’operazione agente corrente.
Errori onSession
Sezione intitolata “Errori onSession”- Gli errori di
onSessionvengono intercettati e registrati come avvisi; non provocano il crash della sessione.
Vincoli reali da considerare in fase di progettazione
Sezione intitolata “Vincoli reali da considerare in fase di progettazione”- I nomi degli strumenti devono essere globalmente univoci nel registro attivo.
- Preferire output deterministici a forma di schema in
detailsper la ricostruzione renderer/stato. - Proteggere l’utilizzo dell’UI con
pi.hasUI. - Trattare i file
.md/.jsonnelle directory degli strumenti come metadati, non come moduli eseguibili.