@llmnative/agentico 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["registry","zodToJsonSchema","useI18n","createContext","useState","useRef","useCallback","useMemo","useContext","useState","Code","_Fragment","React","Icon","useRef","ActionButton","useAIProviderRegistry","useState","PromptUtils","useState","useRef","useAIProviderRegistry","parseAIModelRef","interpolate","useProxy","useState","interpolate","registry","useState","Code","useProxy","useRef","useState","useMemo","_Fragment","useMemo","useState","useRef","useRef","useState","useMemo","ActionButton","useState","Chatbot","interpolate","Badge","useI18n","useMemo","Dropdown","Icon","ActionButton","DropdownItem","useRef","_Fragment","useRef","useRef","__create","__defProp","__getOwnPropDesc","__getOwnPropNames","__getProtoOf","__hasOwnProp","__copyProps","from","keys","i","n","key","to","except","k","desc","__toESM","mod","isNodeMode","target","zod_to_json_schema","_llmnative_react","react","react_jsx_runtime","defineSubagent","config","registry$2","registerSubagents","manifests","getSubagentRegistry","findSubagentManifest","m","id","factories","registerCrossAgentTools","toolFactories","createCrossAgentTools","factory","context","toJsonSchema","rest","schema","toMcpTool","inputSchema","outputSchema","describeCommandFields","command","properties","required","def","description","agenticoPromptsEn","agenticoPromptsIt","activePrompts","configureAgenticoPrompts","prompts","getAgenticoPrompts","resetAgenticoPrompts","renderInputSchemaUsage","commandName","fields","f","renderCommand","usage","paramLines","subagentId","indent","line","pad","spaces","text","renderSubagentCli","manifest","c","renderRegistryCli","registry","SUMMARY_MICRO_PROMPT","MICRO_PROMPT_REGISTRY","applyMicroPrompts","ids","prompt","fragments","t","extractErrorMessage","err","e","inner","extractJsonBlock","fenced","raw","stripped","extractFirstBalancedJsonValue","start","open","close","depth","inString","escaped","ch","JSON_EXPORT_DESTINATION_ID","jsonExportDestination","blob","result","url","a","hostDestinations","registerDestinations","destinations","getDestinationRegistry","storageKey","configureEntityChatStore","options","normalize","entry","record","scopeId","_legacy","readAll","parsed","writeAll","entries","entityId","getEntityChat","saveEntityChat","snapshot","next","removeEntityChat","listEntityChats","b","entityChatTaskId","agenticoDe","agenticoEn","agenticoIt","agenticoTranslations","useAgenticoI18n","fromHost","dict","ORCHESTRATOR_TASK_ID","AgenticoContext","AgenticoProvider","task","setTaskState","taskRef","isOpen","setIsOpen","openTask","current","merged","closeTask","prev","toggleOpen","v","openPanel","pendingGoal","setPendingGoal","pendingExtraDestinations","setPendingExtraDestinations","pendingAttachments","setPendingAttachments","pendingModel","setPendingModel","orchestratorRunId","setOrchestratorRunId","pendingCarryOver","setPendingCarryOver","pendingScopedSubagentId","setPendingScopedSubagentId","pageContextEntries","setPageContextEntries","setPageContext","lastPageContextEntry","componentAnchorEntries","setComponentAnchorEntries","registerComponentAnchor","anchor","componentAnchors","uniqueAnchors","bySubagent","anchorTexts","ctx","agenticoHint","x","anchoredUserMessages","pageContext","list","pageContextCard","uniqueAnchoredSubagentIds","scopedSubagentId","startOrchestrator","existingTask","goal","commitEntityChat","taskId","snap","pendingResume","setPendingResume","resumeRunId","setResumeRunId","resumeEntityChat","pendingManualEntry","setPendingManualEntry","manualEntryRunId","setManualEntryRunId","startManualEntry","pendingResumeWithAI","setPendingResumeWithAI","resumeWithAIRunId","setResumeWithAIRunId","resumeWithAI","pendingStart","setPendingStart","startRunId","setStartRunId","startSubagent","dismissForeignTask","value","scope","useAgentico","splitUnfencedJsonSegments","end","candidate","segments","parseDetailSegments","fenceRe","lastIndex","match","DetailCodeBlock","expanded","setExpanded","content","parseInlineMarkdown","tokens","re","InlineMarkdown","tok","DetailContent","seg","ActivityStatusIcon","status","thinkingDisplayLabel","USER_BUBBLE_COLLAPSED_MAX_HEIGHT","UserMessageBubble","contentRef","overflowing","setOverflowing","el","collapsed","name","ActivityRow","codeSegments","s","textSegments","label","AgentActivityLog","activity","AgenticoToggle","running","className","SELECTED_MODEL_STORAGE_KEY","readStoredModel","writeStoredModel","useAIModelCatalog","hasProviders","modelsByProvider","setModelsByProvider","modelsLoading","setModelsLoading","selectedModel","setSelectedModelState","cancelled","catalog","flat","error","MAX_TOOL_ROUNDTRIPS","MAX_INLINE_TEXT_ATTACHMENT_BASE64_LENGTH","filterInlineAttachments","attachments","useAgent","enabled","setEnabled","defaultEnabled","setRunning","setError","canContinue","setCanContinue","setActivity","labels","labelsRef","sessionRef","runningRef","abortControllerRef","stop","ref","provider","executor","definition","remote","remoteModels","setRemoteModels","remoteModel","setRemoteModelState","remoteModelRef","setRemoteModel","model","offerRemoteModels","params","runRemoteTurn","session","controller","thinkingId","setThinking","detail","onStep","step","at","reply","partial","runTurn","effectiveRef","overrideModel","effectiveProvider","aiAttachments","attachmentsHistoryText","inlineAttachments","toolDefs","toolsByName","setEntryStatus","toolNames","history","promptText","userText","roundTrip","toolIntentLabels","tool","call","toolResults","output","closingId","closingResult","setSelectedModel","opts","attachmentsRef","tools","hasProvider","fallbackResult","file","seed","describeOrchestratorResponseContract","rule","buildOrchestratorPrompt","pageContextSection","cliCatalog","userGoal","parseOrchestratorResponse","handoff","offeredSubagents","createOrchestratorAgentDefinition","allCrossAgentTools","scopedManifest","crossAgentTools","handoffTools","handoffParams","input","deriveHistoryTitle","ResultDestinationPicker","sidebar","proxyFetch","pending","setPending","done","setDone","initiallyApplied","destinationMap","d","extraDestinations","run","destinationId","destination","outcome","meta","onApplied","registry$1","registerSubagentReports","reports","getSubagentReport","GenericResultPreview","buildLiveAnchorDestination","matching","registerSubagentControls","controls","getSubagentControls","noControls","SubagentRunner","taskIdProp","agent","setResult","appliedIds","setAppliedIds","autoApplyError","setAutoApplyError","reportModule","selectionModule","selection","setSelection","startedRef","panelExtraAnchorIndexRef","anchoredResultRef","precedingActivity","dismissedRef","readyToStart","resumeSeed","r","autoIds","pinnedEntityId","handleSelectionChange","handleResultChange","filteredResult","matchingAnchors","liveAnchorDestination","pickerExtraDestinations","chatEntityId","wasRunningRef","ended","dismiss","useControls","handleDestinationApplied","OrchestratorRunner","setHandoff","extraDestinationsRef","modelRef","scopedSubagentIdRef","carryOverRef","handleResult","combinedPrecedingActivity","ManualEntryRunner","lastParams","setLastParams","handleSubmit","rawText","AGENTICO_WIDTH","flattenModelOptions","AgenticoComposer","setText","manualEntry","payload","noModel","modelRequired","models","AgenticoStarterComposer","relativeTime","seconds","now","format","locale","unit","size","SubagentUsageCardView","card","AnchoredUserMessageView","messages","AgenticoHistoryDropdown","search","setSearch","version","setVersion","archive","question","currentTaskId","onArchivedCurrent","filtered","q","onNewChat","onSelect","Agentico","bodyRef","historyOpen","setHistoryOpen","handleNewChat","displayedActivity","activityCount","panelExtraAnchorIndex","activityBeforePanelExtra","activityAfterPanelExtra","hasError","scopedManifestForError","manualEntryManifestForError","isNearBottomRef","handleScroll","lastDetailLength","observer","orchestrator","resumeManifest","resumeRunner","manualEntryManifest","manualEntryRunner","resumeWithAIManifest","resumeWithAIRunner","startManifest","startRunner","useAgenticoPageContext","useSubagentComponentContext","onResultRef","onResult","componentKey","contextInfo"],"sources":["../src/domain/defineSubagent.ts","../src/domain/subagentRegistry.ts","../src/domain/crossAgentTools.ts","../src/domain/mcpTool.ts","../src/domain/subagentUsageCard.ts","../src/domain/prompts.ts","../src/domain/cli/renderManifestCli.ts","../src/domain/microPrompts.ts","../src/domain/extractErrorMessage.ts","../src/domain/extractJsonBlock.ts","../src/domain/jsonExportDestination.ts","../src/domain/destinationRegistry.ts","../src/domain/entityChatStore.ts","../src/i18n/de.ts","../src/i18n/en.ts","../src/i18n/it.ts","../src/i18n/index.ts","../src/presentation/AgenticoContext.tsx","../src/presentation/AgentActivityLog.tsx","../src/presentation/AgenticoToggle.tsx","../src/presentation/useAIModelCatalog.ts","../src/presentation/useAgent.ts","../src/domain/orchestrator/prompt.ts","../src/domain/orchestrator/agenticoOrchestratorAgent.ts","../src/presentation/deriveHistoryTitle.ts","../src/presentation/ResultDestinationPicker.tsx","../src/presentation/reports/subagentReportRegistry.tsx","../src/presentation/reports/GenericResultPreview.tsx","../src/presentation/liveAnchorDestination.ts","../src/presentation/subagentControls.ts","../src/presentation/SubagentRunner.tsx","../src/presentation/OrchestratorRunner.tsx","../src/presentation/ManualEntryRunner.tsx","../src/presentation/Agentico.tsx","../src/presentation/useAgenticoPageContext.ts","../src/presentation/useSubagentComponentContext.ts"],"sourcesContent":["// Punto unico che uno sviluppatore deve passare per creare un subagent — sostituisce\n// l'oggetto letterale libero usato finora (vedi themeSubagentManifest.ts prima della\n// migrazione): qui i generici TParams/TContext/TResult sono dedotti da `definition`, e\n// TypeScript segnala subito se `toParams` non ritorna il TParams che l'AgentDefinition si\n// aspetta, o se manca un campo del manifest — lo stesso livello di guida che darebbe\n// \"estendere una classe\", senza introdurre eredità in un codebase che non la usa altrove.\nimport type { AgentDefinition } from './agentDefinition';\nimport type { SubagentManifest } from './subagentManifest';\n\nexport function defineSubagent<TParams, TContext, TResult>(\n config: Omit<SubagentManifest<TParams, TContext, TResult>, 'definition'> & {\n definition: AgentDefinition<TParams, TContext, TResult>;\n },\n): SubagentManifest<TParams, TContext, TResult> {\n return config;\n}\n","// Il registro dei subagent — store mutabile popolato UNA VOLTA dall'host all'avvio (vedi\n// registerSubagents sotto), MAI un array statico che importa manifest concreti da qui: Agentico\n// (questo modulo) non deve conoscere quali subagent esistono in un progetto che lo consuma —\n// altrimenti un'estrazione futura in un pacchetto a sé porterebbe con sé una dipendenza diretta\n// su theme/redirects/html-to-liquid, specifici di QUESTO CMS. L'host (vedi\n// src/application/context/CmsProviders.tsx) importa i propri manifest e li registra qui a\n// livello di modulo, prima che qualunque componente sotto AgenticoProvider possa leggerli.\nimport type { SubagentManifest } from './subagentManifest';\n\nlet registry: ReadonlyArray<SubagentManifest<any, any, any>> = [];\n\n/** Chiamata dall'host UNA VOLTA, a livello di modulo (non dentro un componente React) — un\n * secondo giro SOSTITUISCE il registro precedente, non lo somma: niente doppie registrazioni\n * accidentali (es. da un hot-reload) da dover deduplicare a valle. */\nexport function registerSubagents(manifests: ReadonlyArray<SubagentManifest<any, any, any>>): void {\n registry = manifests;\n}\n\n/** Letto da codice che ha bisogno dell'INTERO elenco (es. la Grid di ToolsPage, il catalogo CLI\n * dell'orchestratore) — mai per costruzione statica a import-time: se questo ritorna `[]` è\n * quasi sempre perché `registerSubagents` non è ancora stato chiamato (o il modulo che lo chiama\n * non è ancora stato importato) prima di questo punto, non un progetto senza subagent. */\nexport function getSubagentRegistry(): ReadonlyArray<SubagentManifest<any, any, any>> {\n if (registry.length === 0 && typeof console !== 'undefined') {\n console.warn('[agentico] getSubagentRegistry() letto a registro vuoto — registerSubagents() non è ancora stato chiamato?');\n }\n return registry;\n}\n\nexport function findSubagentManifest(id: string): SubagentManifest<any, any, any> | undefined {\n return registry.find((m) => m.id === id);\n}\n","// Cross-agent tools: data-collection tools the orchestrator can call while it gathers the\n// parameters of a subagent command (read a sitemap, list the columns of an attached CSV, scrape\n// a page…). Agentico ships none of them: the host registers a factory once at startup, exactly\n// like registerSubagents().\n//\n// Tools often produce large values (hundreds of paths) that the model would have to copy again\n// into the final command, and models truncate long arrays when they do. So a tool can also write\n// its complete output into `collected`, a per-conversation scratchpad; the subagent the\n// conversation hands off to reads it back through SubagentManifest.withCollected.\nimport type { AgentTool, LatestAttachmentsRef } from './agentDefinition';\n\nexport interface CrossAgentToolContext {\n /** Raw attachments of the current turn — for tools that read files too big for the prompt. */\n attachmentsRef: LatestAttachmentsRef | undefined;\n /** Per-conversation scratchpad shared with the subagent manifests (see withCollected). */\n collected: Record<string, unknown>;\n /** CORS-safe fetch of the host app (the framework's proxyFetch). */\n proxyFetch: typeof fetch;\n}\n\nexport type CrossAgentToolFactory = (context: CrossAgentToolContext) => AgentTool[];\n\nlet factories: CrossAgentToolFactory[] = [];\n\n/** Registers the host's cross-agent tools. Called once at startup; a later call replaces the set. */\nexport function registerCrossAgentTools(...toolFactories: CrossAgentToolFactory[]): void {\n factories = toolFactories;\n}\n\nexport function createCrossAgentTools(context: CrossAgentToolContext): AgentTool[] {\n return factories.flatMap((factory) => factory(context));\n}\n","// Punto unico di conversione Zod → JSON Schema — usato SOLO ai due bordi che hanno\n// davvero bisogno della forma serializzata (il provider AI in useAgent.ts, che parla\n// JSON Schema sul wire come ogni function-calling API; il render CLI in\n// cli/renderManifestCli.ts). Ovunque altro nel dominio (AgentTool, SubagentCommand) lo\n// schema resta un vero z.ZodType — un solo formato \"di verità\", mai due dialetti.\n//\n// La forma `McpTool` è quella del formato MCP Tool (Model Context Protocol) —\n// name/description/inputSchema JSON-Schema (+ outputSchema opzionale): non un'invenzione\n// interna, così ogni subagent resta esponibile 1:1 come vero MCP tool in futuro, senza\n// ulteriore lavoro di adattamento.\nimport { z } from 'zod';\nimport { zodToJsonSchema } from 'zod-to-json-schema';\n\nexport interface McpTool {\n name: string;\n description: string;\n inputSchema: Record<string, unknown>;\n outputSchema?: Record<string, unknown>;\n}\n\nexport function toJsonSchema(schema: z.ZodType): Record<string, unknown> {\n // zodToJsonSchema inserisce sempre `$schema` (un meta-riferimento al draft, non parte\n // dello schema stesso) alla radice dell'output. Nel wire format function-calling\n // (OpenAI-compatible), quel campo finisce dentro `tools[].function.parameters` — dove\n // non è previsto — e alcuni backend (verificato con OpenCode Zen: falliva su OGNI\n // modello, in modo consistente, non transitorio) rispondono 400/500 non appena\n // `parameters` lo contiene.\n const { $schema, ...rest } = zodToJsonSchema(schema, { target: 'jsonSchema7', $refStrategy: 'none' }) as Record<string, unknown>;\n return rest;\n}\n\nexport function toMcpTool(name: string, description: string, inputSchema: z.ZodType, outputSchema?: z.ZodType): McpTool {\n return {\n name,\n description,\n inputSchema: toJsonSchema(inputSchema),\n ...(outputSchema ? { outputSchema: toJsonSchema(outputSchema) } : {}),\n };\n}\n","// Card deterministica \"cosa serve per avviare una nuova istanza di questo subagent\" — stessa\n// fonte (Zod → JSON Schema, vedi mcpTool.ts) già usata dal render CLI (cli/renderManifestCli.ts)\n// e dal wire del provider AI (function calling in useAgent.ts), qui proiettata per l'UI di\n// Agentico (ToolHistoryPanel/Agentico.tsx) invece che per testo --help o per il tool schema. Un\n// solo posto che estrae \"quali campi, obbligatori o no, con che descrizione\" da un\n// SubagentCommand — mai duplicato a mano in prosa per ogni subagent.\nimport type { SubagentCommand, SubagentManifest } from './subagentManifest';\nimport { toJsonSchema } from './mcpTool';\n\nexport interface SubagentUsageField {\n key: string;\n type: string;\n required: boolean;\n /** Tecnica, machine-facing — la `.describe()` Zod tale e quale, stessa fonte del JSON\n * Schema/CLI help. Verbosa ed enumerativa per progettazione: un LLM sbaglia meno con casi\n * limite espliciti, un umano di passaggio no. */\n description: string;\n /** Human-facing, per la card di Agentico — `command.humanFieldHints[key]` se presente,\n * altrimenti ripiega su `description` (mai vuota, mai un secondo giro di scrittura\n * obbligato per ogni subagent). */\n humanDescription: string;\n}\n\nexport interface SubagentUsageCard {\n subagentId: string;\n label: string;\n commandName: string;\n /** Tecnica, machine-facing — vedi SubagentCommand.summary. */\n summary: string;\n /** Human-facing — `command.humanSummary` se presente, altrimenti ripiega su `summary`. */\n humanSummary: string;\n fields: SubagentUsageField[];\n example?: string;\n /** Quando true, il consumer UI (SubagentUsageCardView) mostra SOLO `humanSummary` — niente\n * `fields`/`example` (pensati per l'occhio tecnico) — vedi SubagentCommand.humanSummaryOnly\n * per il perché. */\n humanSummaryOnly: boolean;\n}\n\n/** Estrae la lista di campi (nome/tipo/obbligatorietà/descrizione) dallo z.ZodType di un\n * comando — riusata sia qui sia da renderManifestCli.ts, che altrimenti duplicherebbe la stessa\n * lettura di `properties`/`required` dal JSON Schema. */\nexport function describeCommandFields(command: SubagentCommand): SubagentUsageField[] {\n const schema = toJsonSchema(command.inputSchema);\n const properties = (schema.properties as Record<string, Record<string, unknown>> | undefined) ?? {};\n const required = new Set((schema.required as string[] | undefined) ?? []);\n return Object.entries(properties).map(([key, def]) => {\n const description = typeof def.description === 'string' ? def.description : '';\n return {\n key,\n type: typeof def.type === 'string' ? def.type : 'string',\n required: required.has(key),\n description,\n humanDescription: command.humanFieldHints?.[key] ?? description,\n };\n });\n}\n\n/** Il comando primario di un manifest (oggi quasi sempre l'unico, vedi SubagentManifest.commands)\n * proiettato in una card — `null` solo se un manifest non ne dichiara nessuno (non dovrebbe\n * succedere per un subagent registrato, ma il chiamante UI non deve crashare se succede). */\nexport function buildSubagentUsageCard(manifest: SubagentManifest<any, any, any>): SubagentUsageCard | null {\n const command = manifest.commands[0];\n if (!command) return null;\n return {\n subagentId: manifest.id,\n label: manifest.label,\n commandName: command.name,\n summary: command.summary,\n humanSummary: command.humanSummary ?? command.summary,\n fields: describeCommandFields(command),\n example: command.example,\n humanSummaryOnly: command.humanSummaryOnly ?? false,\n };\n}\n","// Model-facing text: what Agentico itself sends to the AI provider (orchestrator prompt, response\n// contract, closing-summary micro-prompt, the \"stop calling tools\" nudge, the queued handoff note).\n// User-facing text lives in the `agentico` i18n namespace instead (i18n/), never here.\n//\n// One pack per language. The host picks one and extends it once at startup with\n// configureAgenticoPrompts() — e.g. its own orchestrator intro and the rules that only make sense\n// for its own subagents and cross-agent tools. Agentico never mentions a concrete subagent here.\n\nexport interface AgenticoPrompts {\n /** First paragraph of the orchestrator prompt: who the orchestrator is and what it does. */\n orchestratorIntro: string;\n /** Bullet rules of the orchestrator response contract (generic ones first, host ones after). */\n responseRules: string[];\n responseRulesHeading: string;\n toolsHeading: string;\n pageContextHeading: string;\n pageContextIntro: string;\n userRequestHeading: string;\n /** CLI catalog shown when no subagent is registered. */\n noToolsAvailable: string;\n /** Sent to the model when it keeps calling tools past the round-trip limit. */\n stopCallingTools: string;\n /** Returned to the model by a handoff tool: the work is queued, not done. `{label}` = subagent. */\n handoffQueued: string;\n /** Closing-summary micro-prompt (see microPrompts.ts). */\n closingSummary: string;\n /** Page context preamble when several subagents are anchored on the same page. */\n multipleCandidates: string;\n}\n\nexport const agenticoPromptsEn: AgenticoPrompts = {\n orchestratorIntro:\n 'You are Agentico, the orchestrator of this application\\'s tools. Your job is to understand what the user wants to achieve and, once every required parameter is collected, start the right tool among those listed below — do not carry it out yourself \"in words\", invoke it as a tool.',\n responseRules: [\n 'BEFORE asking for any parameter, check the \"Page context\" below (if present): if a parameter required by the command is already there or can be deduced from it with reasonable certainty, USE it directly — do not ask the user again. Only ask for parameters that cannot be derived from the context.',\n 'The \"<subagent>_<command>\" command you close with is the one that does the real work (analysis, matching, generation, extraction — whatever its description says) — NEVER try to produce that result yourself in chat once the raw data is collected.',\n 'If parameters are really missing (not deducible from the context nor from a data-collection tool) for one of the listed commands, ask in natural language, one question at a time, until you have everything.',\n 'When you have ALL required parameters of ONE command, actually invoke the tool with that name (\"<subagent>_<command>\") passing the parameters — do not just describe it, and do not replace the work that command is designed to do.',\n 'Invoke at most one command per turn.',\n 'If the user\\'s request is not covered by any of the listed tools, say so clearly instead of inventing one.',\n 'IMPORTANT: when you invoke a \"<subagent>_<command>\" command, your comment in THIS turn must only announce the START of the work — NEVER describe its outcome as already obtained, and NEVER say that something \"opened\" or \"is ready to review\". The real work starts AFTER this turn, in a session you will never see: any description of the outcome at this point would be invented, even if the tool answered without errors.',\n 'After invoking a command, do not spontaneously suggest other subagents unrelated to the current request — the user will open a new conversation if they need something else.',\n ],\n responseRulesHeading: '## How to answer',\n toolsHeading: '## Available tools',\n pageContextHeading: '## Page context',\n pageContextIntro: 'The user is interacting from this point of the app — use it to understand what they refer to when the request is not explicit:',\n userRequestHeading: '## User request',\n noToolsAvailable: 'No tools available.',\n stopCallingTools: 'Stop calling tools. Answer NOW with the final result, in the required format, using everything collected so far.',\n handoffQueued: '{label} has been queued for the real execution — the result is not available in this conversation yet.',\n closingSummary: [\n 'When you are done answering (with or without tools in this turn),',\n 'ALWAYS close your text answer with a concise but complete summary of what',\n 'you did — a few lines, a bullet list when there is more than one action, no',\n 'needless repetition of the original task. The summary goes AFTER the JSON',\n 'block requested above, never before it and never instead of it.',\n ].join(' '),\n multipleCandidates: 'Several subagents are candidates on this page — ask the user which one they mean if their message does not make it clear, before going on.',\n};\n\nexport const agenticoPromptsIt: AgenticoPrompts = {\n orchestratorIntro:\n 'Sei Agentico, l\\'orchestratore dei tool di questa applicazione. Il tuo compito è capire cosa vuole ottenere l\\'utente e, una volta raccolti tutti i parametri necessari, avviare il tool giusto tra quelli elencati sotto — non eseguirlo tu stesso \"a parole\", invocalo come strumento.',\n responseRules: [\n 'PRIMA di chiedere qualunque parametro, controlla il \"Contesto pagina\" (se presente) qui sotto: se un parametro richiesto dal comando è già lì o si può dedurre da lì con ragionevole certezza (es. l\\'utente sta guardando lo storico di un tool specifico, o un\\'esecuzione passata con parametri simili), USALO direttamente — non chiederlo di nuovo all\\'utente. Chiedi solo i parametri che NON emergono in nessun modo dal contesto.',\n 'Il comando \"<subagent>_<comando>\" con cui chiudi è quello che fa il lavoro di analisi vero (matching, generazione, estrazione — qualunque cosa il suo comando descriva) — MAI provare a produrlo tu stesso in chat una volta raccolti i dati grezzi.',\n 'Se mancano davvero parametri (non deducibili dal contesto né da un tool di raccolta dati) per uno dei comandi elencati, fai domande in linguaggio naturale, una alla volta, finché non hai raccolto tutto.',\n 'Quando hai TUTTI i parametri richiesti di UN comando (raccolti dall\\'utente, dedotti dal contesto, o ottenuti da un tool di raccolta dati), invoca davvero lo strumento con quel nome (\"<subagent>_<comando>\") passandogli i parametri — non limitarti a descriverlo a parole, e non sostituirti al lavoro che quel comando è progettato per fare.',\n 'Invoca al massimo un comando per turno.',\n 'Se la richiesta dell\\'utente non è coperta da nessuno dei tool elencati, dillo chiaramente invece di inventarne uno.',\n 'IMPORTANTE: quando invochi un comando \"<subagent>_<comando>\", il tuo commento in QUESTO turno deve annunciare solo l\\'AVVIO del lavoro (es. \"Avvio l\\'estrazione…\", \"Sto preparando il mapping…\") — MAI descriverne l\\'esito come già ottenuto (colori, font, loghi, mapping, o qualunque altro dato del risultato) e MAI dire che qualcosa \"si è aperto\" o \"è pronto da rivedere\". Il vero lavoro parte DOPO questo turno, in una sessione che tu non vedrai mai: qualunque tua descrizione dell\\'esito a questo punto sarebbe un\\'invenzione, anche se il tool ha risposto senza errori.',\n 'Dopo aver invocato un comando, non proporre spontaneamente altri subagent scollegati dalla richiesta corrente — l\\'utente aprirà lui una nuova conversazione se gli servirà altro.',\n ],\n responseRulesHeading: '## Come rispondere',\n toolsHeading: '## Tool disponibili',\n pageContextHeading: '## Contesto pagina',\n pageContextIntro: 'L\\'utente sta interagendo da questo punto dell\\'app — usalo per capire a cosa si riferisce se non è esplicito nella richiesta:',\n userRequestHeading: '## Richiesta dell\\'utente',\n noToolsAvailable: 'Nessun tool disponibile.',\n stopCallingTools: 'Basta chiamare tool. Rispondi ORA con il risultato finale, nel formato richiesto, usando tutte le informazioni raccolte finora.',\n handoffQueued: '{label} è stato messo in coda per l\\'esecuzione reale — il risultato non è ancora disponibile in questa conversazione.',\n closingSummary: [\n 'Quando hai finito di rispondere (con o senza aver usato dei tool in questo turno),',\n 'chiudi SEMPRE la tua risposta testuale con un riepilogo conciso ma esaustivo di cosa',\n 'hai fatto — poche righe, elenco puntato se le azioni sono più di una, niente',\n 'ripetizioni superflue del compito originale. Il riepilogo va DOPO il blocco JSON',\n 'richiesto sopra, mai prima e mai al posto suo.',\n ].join(' '),\n multipleCandidates: 'Più subagent sono candidati su questa pagina — chiedi all\\'utente quale intende se non è chiaro dal suo messaggio, prima di procedere.',\n};\n\nlet activePrompts: AgenticoPrompts = agenticoPromptsEn;\n\n/** Sets the model-facing pack once at startup — e.g. `configureAgenticoPrompts({ ...agenticoPromptsIt,\n * responseRules: [...agenticoPromptsIt.responseRules, ...hostRules] })`. Fields left out keep their value. */\nexport function configureAgenticoPrompts(prompts: Partial<AgenticoPrompts>): void {\n activePrompts = { ...activePrompts, ...prompts };\n}\n\nexport function getAgenticoPrompts(): AgenticoPrompts {\n return activePrompts;\n}\n\n/** Restores the English defaults (tests). */\nexport function resetAgenticoPrompts(): void {\n activePrompts = agenticoPromptsEn;\n}\n","// Rendering deterministico (nessuna chiamata LLM) di un SubagentManifest in testo \"CLI --help\".\n// Usato dall'orchestratore Agentico (buildContext, che gira SEMPRE — vedi agentDefinition.ts)\n// per il discovery: il modello legge questo testo, non JSON Schema grezzo — più denso, più\n// leggibile, e allineato al vocabolario \"usage/example\" con cui i modelli sono più addestrati a\n// ragionare su strumenti a riga di comando.\nimport type { SubagentCommand, SubagentManifest } from '../subagentManifest';\nimport { describeCommandFields } from '../subagentUsageCard';\nimport { getAgenticoPrompts } from '../prompts';\n\nfunction renderInputSchemaUsage(commandName: string, fields: ReturnType<typeof describeCommandFields>): string {\n const flags = fields.map((f) => (f.required ? `--${f.key} <${f.type}>` : `[--${f.key} <${f.type}>]`));\n return `${commandName} ${flags.join(' ')}`.trim();\n}\n\nfunction renderCommand(subagentId: string, command: SubagentCommand): string {\n // command.inputSchema è Zod (sorgente unica) — la lista di campi (nome/tipo/obbligatorietà/\n // descrizione) è la stessa estratta da subagentUsageCard.ts per la card UI di Agentico,\n // mai duplicata a mano qui.\n const fields = describeCommandFields(command);\n const usage = renderInputSchemaUsage(command.name, fields);\n const paramLines = fields.map((f) =>\n ` --${f.key} <${f.type}> (${f.required ? 'required' : 'optional'})${f.description ? ` — ${f.description}` : ''}`);\n\n return [\n `COMMAND: ${subagentId} ${command.name}`,\n ` ${command.summary}`,\n '',\n ` USAGE`,\n ` ${subagentId} ${usage}`,\n paramLines.length ? '' : '',\n paramLines.length ? ` PARAMS` : '',\n ...paramLines,\n '',\n ` OUTPUT`,\n indent(command.describeOutput(), 4),\n command.example ? '' : '',\n command.example ? ` EXAMPLE` : '',\n command.example ? ` ${command.example}` : '',\n ].filter((line) => line !== '').join('\\n');\n}\n\nfunction indent(text: string, spaces: number): string {\n const pad = ' '.repeat(spaces);\n return text.split('\\n').map((line) => (line ? pad + line : line)).join('\\n');\n}\n\n/** Help completo di UN subagent — tutte le sue command, la sua descrizione. */\nexport function renderSubagentCli<TParams, TContext, TResult>(\n manifest: SubagentManifest<TParams, TContext, TResult>,\n): string {\n return [\n `NAME`,\n ` ${manifest.id} — ${manifest.label}`,\n '',\n `DESCRIPTION`,\n indent(manifest.description, 2),\n '',\n ...manifest.commands.map((c) => renderCommand(manifest.id, c)),\n ].join('\\n');\n}\n\n/** Indice sintetico di TUTTI i subagent registrati — una riga per subagent, i suoi comandi\n * elencati per nome. Questa è la vista che l'orchestratore Agentico usa come punto di partenza\n * (\"cosa esiste\") prima di approfondire un singolo subagent con renderSubagentCli(). */\nexport function renderRegistryCli(registry: ReadonlyArray<SubagentManifest<any, any, any>>): string {\n if (registry.length === 0) return getAgenticoPrompts().noToolsAvailable;\n return registry\n .map((m) => `${m.id} [${m.commands.map((c) => c.name).join('|')}] — ${m.label}: ${m.commands.map((c) => c.summary).join(' / ')}`)\n .join('\\n');\n}\n","// Layer di micro-prompt trasversali — frammenti di istruzione che si aggiungono in coda al\n// prompt VERTICALE di un agente specifico (theme-extract, html-to-liquid, e i prossimi)\n// SENZA che quell'agente debba conoscerne il testo o duplicarlo nel proprio buildPrompt/\n// buildFollowUpPrompt. Applicati centralmente da useAgent (l'unico esecutore, vedi\n// agentDefinition.ts) — un agente si limita a dichiarare QUALI vuole via\n// `AgentDefinition.microPromptIds`, un elenco di id che referenzia questo registro.\n//\n// Perché un registro per id invece di far comporre il testo direttamente a ogni agente: un\n// domani, un nuovo comportamento trasversale (es. \"rispondi sempre in italiano\", \"cita sempre\n// le fonti quando disponibili\") si aggiunge in un solo posto qui sotto e diventa disponibile a\n// ogni agente esistente e futuro con una sola riga (`microPromptIds: [...]`), invece di dover\n// toccare il buildPrompt di ciascuno.\nimport { getAgenticoPrompts } from './prompts';\n\nexport interface MicroPrompt {\n id: string;\n text: string;\n}\n\n/** Chiede al modello di chiudere SEMPRE la propria risposta testuale con un riepilogo\n * conciso di cosa ha fatto — utile perché l'utente altrimenti vede solo il log delle tool\n * call/turni \"sto pensando\" (già deterministico, mai narrato dal modello) ma nessuna sintesi\n * finale in linguaggio naturale di cosa è stato effettivamente ottenuto.\n *\n * Il riepilogo va DOPO il blocco ```json — mai prima, mai a comporlo — cosi\n * extractJsonBlock() lo trova comunque cercando il fence ovunque nel testo, e il riepilogo\n * stesso resta visibile all'utente come testo libero accanto al JSON (vedi DetailContent in\n * AgentActivityLog.tsx, che già sa render testo + blocco codice misti nello stesso turno). */\nexport const SUMMARY_MICRO_PROMPT: MicroPrompt = {\n id: 'closing-summary',\n // Read from the active prompt pack (prompts.ts) at use time, so the host's language applies.\n get text() {\n return getAgenticoPrompts().closingSummary;\n },\n};\n\n/** Registro dei micro-prompt disponibili, per id. */\nexport const MICRO_PROMPT_REGISTRY: Record<string, MicroPrompt> = {\n [SUMMARY_MICRO_PROMPT.id]: SUMMARY_MICRO_PROMPT,\n};\n\n/** Applica in coda al prompt verticale i micro-prompt richiesti per id — no-op se l'agente\n * non ne richiede nessuno (o richiede un id sconosciuto, ignorato silenziosamente: un id\n * scritto male non deve far fallire l'intero prompt). */\nexport function applyMicroPrompts(prompt: string, ids: string[] | undefined): string {\n if (!ids || ids.length === 0) return prompt;\n const fragments = ids.map((id) => MICRO_PROMPT_REGISTRY[id]?.text).filter((t): t is string => !!t);\n if (fragments.length === 0) return prompt;\n return `${prompt}\\n\\n${fragments.join('\\n')}`;\n}\n","// Normalizza un errore di chiamata AI in un messaggio leggibile — condiviso da ogni chiamante\n// di useAgent (prima duplicato identico in SiteEditorPage.tsx). I provider AI (OpenAI,\n// Anthropic, Gemini) non lanciano sempre un vero Error: spesso è un oggetto con la forma\n// { error: { message } } propagato as-is dalla loro risposta HTTP.\nexport function extractErrorMessage(err: unknown): string {\n if (err instanceof Error) return err.message;\n if (err !== null && typeof err === 'object') {\n const e = err as Record<string, unknown>;\n // Forma comune quanto { error: { message } } — alcuni provider (visto con OpenCode\n // locale) rispondono con { error: \"testo\" } invece che { error: { message: \"testo\" } }.\n if (typeof e.error === 'string') return e.error;\n if (e.error && typeof e.error === 'object') {\n const inner = e.error as Record<string, unknown>;\n if (typeof inner.message === 'string') return inner.message;\n }\n if (typeof e.message === 'string') return e.message;\n if (typeof e.status === 'number') return `HTTP ${e.status}`;\n // Forma sconosciuta — meglio un dump JSON leggibile (e diagnosticabile) che\n // \"[object Object]\" da String(err) sul default toString() di un plain object.\n try { return JSON.stringify(err); } catch { /* fall through */ }\n }\n return String(err);\n}\n","// Estrae un blocco JSON dalla risposta grezza di un agente — condiviso da ogni parseResponse\n// che si aspetta un ```json ... ``` (theme-extract, html-to-liquid, e i prossimi).\n//\n// Cerca il fence OVUNQUE nel testo, non solo se occupa l'intera risposta: un micro-prompt\n// trasversale (vedi microPrompts.ts — es. \"chiudi sempre con un riepilogo\") può far si che il\n// modello aggiunga prosa prima o dopo il blocco JSON. Il vecchio comportamento (strip dei soli\n// fence iniziale/finale, poi JSON.parse sull'intera stringa) romperebbe silenziosamente il\n// parsing non appena comparisse anche una sola riga di prosa attorno al JSON.\nexport function extractJsonBlock(raw: string): string {\n const fenced = raw.match(/```(?:json)?\\s*\\n?([\\s\\S]*?)```/i);\n if (fenced) return fenced[1].trim();\n // Nessun fence trovato — il modello a volte lo dimentica proprio (l'istruzione \"output\n // ONLY the raw JSON\" non è sempre rispettata su turni di follow-up lunghi) e scrive il JSON\n // valido seguito, SENZA delimitatori, da un paragrafo di riepilogo in prosa (bug osservato:\n // JSON.parse falliva con \"Unexpected non-whitespace character AFTER json\" — cioè il JSON\n // stesso era completo e corretto, il problema era solo il testo attaccato subito dopo).\n // Isoliamo il primo valore JSON bilanciato (oggetto o array) contando parentesi/graffe e\n // ignorando quelle dentro le stringhe, e tagliamo lì — qualunque prosa dopo viene scartata.\n const stripped = raw.replace(/^```json\\s*/i, '').replace(/^```\\s*/i, '').replace(/```\\s*$/, '').trim();\n const balanced = extractFirstBalancedJsonValue(stripped);\n return balanced ?? stripped;\n}\n\nfunction extractFirstBalancedJsonValue(text: string): string | null {\n const start = text.search(/[{[]/);\n if (start === -1) return null;\n\n const open = text[start];\n const close = open === '{' ? '}' : ']';\n let depth = 0;\n let inString = false;\n let escaped = false;\n\n for (let i = start; i < text.length; i++) {\n const ch = text[i];\n if (inString) {\n if (escaped) escaped = false;\n else if (ch === '\\\\') escaped = true;\n else if (ch === '\"') inString = false;\n continue;\n }\n if (ch === '\"') { inString = true; continue; }\n if (ch === open) depth++;\n else if (ch === close) {\n depth--;\n if (depth === 0) return text.slice(start, i + 1);\n }\n }\n return null;\n}\n","// Destinazione generica — funziona per QUALUNQUE TResult, sempre disponibile (vedi\n// ResultDestinationPicker, che la elenca oltre a manifest.destinationIds, non al posto loro).\nimport type { ResultDestination } from './resultDestination';\n\nexport const JSON_EXPORT_DESTINATION_ID = 'json-export';\n\nexport const jsonExportDestination: ResultDestination<unknown> = {\n id: JSON_EXPORT_DESTINATION_ID,\n label: 'Export as JSON', // shown through the agentico.exportJson translation by the picker\n async apply(result) {\n const blob = new Blob([JSON.stringify(result, null, 2)], { type: 'application/json' });\n const url = URL.createObjectURL(blob);\n try {\n const a = document.createElement('a');\n a.href = url;\n a.download = `tool-result-${Date.now()}.json`;\n a.click();\n } finally {\n URL.revokeObjectURL(url);\n }\n },\n};\n","// Registro delle destinazioni per id, letto da ResultDestinationPicker per risolvere\n// `manifest.destinationIds` in `ResultDestination` reali — stesso principio di\n// subagentRegistry.ts: Agentico possiede SOLO `jsonExportDestination` (generica, funziona per\n// qualunque TResult), mai una destinazione specifica di un subagent concreto (es.\n// componentLibraryDestination, specifica di html-to-liquid). L'host la registra una volta\n// all'avvio (vedi CmsProviders.tsx), Agentico si limita a fondere quel registro con la propria\n// voce generica sempre presente.\nimport type { ResultDestination } from './resultDestination';\nimport { jsonExportDestination } from './jsonExportDestination';\n\nlet hostDestinations: Record<string, ResultDestination<any>> = {};\n\n/** Chiamata dall'host UNA VOLTA, a livello di modulo — un secondo giro SOSTITUISCE il registro\n * precedente, stesso principio di registerSubagents. */\nexport function registerDestinations(destinations: Record<string, ResultDestination<any>>): void {\n hostDestinations = destinations;\n}\n\nexport function getDestinationRegistry(): Record<string, ResultDestination<any>> {\n return { [jsonExportDestination.id]: jsonExportDestination, ...hostDestinations };\n}\n","// Chat Agentico legata a UN'entità salvata (es. un tema) — scrive SOLO al salvataggio riuscito\r\n// dell'entità che quella conversazione ha popolato (vedi AgenticoContext.commitEntityChat), e la\r\n// persiste per intero: `history` (AIConversationTurn[], dato puro) permette un resume vero — il\r\n// modello può continuare da lì — non solo un replay in sola lettura.\r\n//\r\n// Nessuna scrittura = nessuna chat mai salvata per quell'entità (creazione manuale, o\r\n// un'estrazione abbandonata senza salvare — sparisce da sola, mai persistita). Una sola chat per\r\n// entità: un nuovo salvataggio con una nuova estrazione associata SOVRASCRIVE quella precedente\r\n// (decisione esplicita — non una cronologia di tentativi). Un salvataggio SENZA nessuna\r\n// estrazione coinvolta (edit manuale) non tocca lo store — vedi commitEntityChat, che è un no-op\r\n// quando non c'è un taskId da cui pescare uno snapshot.\r\nimport type { AIConversationTurn } from '@llmnative/react';\r\n\r\n/** Stessa forma di AgentActivityEntry (agentico/presentation/useAgent.ts) — ridefinita qui invece\r\n * di importata: il domain layer non dipende dal presentation layer, e questi campi sono\r\n * semplici dati (mai funzioni), quindi TypeScript accetta un AgentActivityEntry[] ovunque sia\r\n * richiesto questo tipo per compatibilità strutturale, senza bisogno di un cast esplicito. */\r\nexport interface EntityChatActivityEntry {\r\n id: string;\r\n kind: 'user' | 'thinking' | 'tool';\r\n label: string;\r\n status: 'running' | 'done' | 'error' | 'stopped';\r\n detail?: string;\r\n toolNames?: string[];\r\n}\r\n\r\nexport interface EntityChatSnapshot {\r\n /** Host scope (tenant, company…) the chat belongs to — see AgenticoScope. */\r\n scopeId: string;\r\n subagentId: string;\r\n entityId: string;\r\n /** TParams grezzo (pre-manifest.toParams, es. `{ url }` per theme) con cui la sessione\r\n * originale era stata avviata — necessario per ricostruire context/tools al resume (vedi\r\n * useAgent.resume/SubagentRunner.resumeSeed): senza, `manifest.toParams` non avrebbe nulla\r\n * da validare/convertire per riavviare la sessione. */\r\n params: Record<string, unknown>;\r\n history: AIConversationTurn[];\r\n activity: EntityChatActivityEntry[];\r\n savedAt: number;\r\n /** Etichetta human-friendly dell'entità (es. il nome del tema) — usata dallo storico chat\r\n * di Agentico (vedi Agentico.tsx) per mostrare \"cosa\" senza dover rileggere l'entità stessa\r\n * dal suo DataProviderAdapter (che questo store, domain-side, non possiede). */\r\n title: string;\r\n}\r\n\r\nlet storageKey = 'llmnative.agentico.entityChat';\r\n\r\n/** Sets the localStorage key of the chat store — call once at startup, before any read. */\r\nexport function configureEntityChatStore(options: { storageKey: string }): void {\r\n storageKey = options.storageKey;\r\n}\r\n\r\n// Snapshots written before the scope rename carry `tenantId`: read them as `scopeId`.\r\nfunction normalize(entry: unknown): EntityChatSnapshot | null {\r\n if (!entry || typeof entry !== 'object') return null;\r\n const record = entry as Partial<EntityChatSnapshot> & { tenantId?: string };\r\n if (typeof record.entityId !== 'string') return null;\r\n const scopeId = record.scopeId ?? record.tenantId;\r\n if (typeof scopeId !== 'string') return null;\r\n const { tenantId: _legacy, ...rest } = record;\r\n return { ...rest, scopeId } as EntityChatSnapshot;\r\n}\r\n\r\nfunction readAll(): EntityChatSnapshot[] {\r\n try {\r\n const raw = localStorage.getItem(storageKey);\r\n if (!raw) return [];\r\n const parsed: unknown = JSON.parse(raw);\r\n return Array.isArray(parsed) ? parsed.map(normalize).filter((e): e is EntityChatSnapshot => e !== null) : [];\r\n } catch {\r\n return [];\r\n }\r\n}\r\n\r\nfunction writeAll(entries: EntityChatSnapshot[]): void {\r\n try {\r\n localStorage.setItem(storageKey, JSON.stringify(entries));\r\n } catch { /* quota superata o localStorage non disponibile — non fatale, è un extra */ }\r\n}\r\n\r\nconst key = (scopeId: string, subagentId: string, entityId: string) => `${scopeId}:${subagentId}:${entityId}`;\r\n\r\n/** La chat attualmente associata a un'entità — `null` se non ne è mai stata salvata una (o è\r\n * stata rimossa, vedi removeEntityChat). */\r\nexport function getEntityChat(scopeId: string, subagentId: string, entityId: string): EntityChatSnapshot | null {\r\n const k = key(scopeId, subagentId, entityId);\r\n return readAll().find((e) => key(e.scopeId, e.subagentId, e.entityId) === k) ?? null;\r\n}\r\n\r\n/** Sovrascrive (o crea) la chat associata a questa entità — vedi il commento in testa al file\r\n * per perché è sempre un overwrite, mai un accumulo. */\r\nexport function saveEntityChat(snapshot: Omit<EntityChatSnapshot, 'savedAt'>): void {\r\n const k = key(snapshot.scopeId, snapshot.subagentId, snapshot.entityId);\r\n const next = readAll().filter((e) => key(e.scopeId, e.subagentId, e.entityId) !== k);\r\n next.push({ ...snapshot, savedAt: Date.now() });\r\n writeAll(next);\r\n}\r\n\r\n/** Rimuove la chat di un'entità — chiamata quando l'entità stessa viene eliminata, altrimenti\r\n * resterebbe un'entry orfana senza nulla a cui riattaccarsi. */\r\nexport function removeEntityChat(scopeId: string, subagentId: string, entityId: string): void {\r\n const k = key(scopeId, subagentId, entityId);\r\n writeAll(readAll().filter((e) => key(e.scopeId, e.subagentId, e.entityId) !== k));\r\n}\r\n\r\n/** Tutte le chat salvate di uno scope, più recenti prima — per lo storico di Agentico\r\n * (Agentico.tsx), che le mescola across subagent (theme, redirects, html-to-liquid) in un'unica\r\n * lista ordinata per data. */\r\nexport function listEntityChats(scopeId: string): EntityChatSnapshot[] {\r\n return readAll().filter((e) => e.scopeId === scopeId).sort((a, b) => b.savedAt - a.savedAt);\r\n}\r\n\r\n/** The id of the Agentico task of the chat of an entity: the same when the chat is resumed from\r\n * the history and when a page starts it again (AgenticoContext.startSubagent), so it stays one\r\n * task (CR-003). */\r\nexport const entityChatTaskId = (subagentId: string, entityId: string) => `entitychat-${subagentId}-${entityId}`;\r\n","import type { AgenticoDict } from './types';\n\nexport const agenticoDe: AgenticoDict = {\n modelLabel: 'Modell',\n modelsLoading: 'Wird geladen…',\n noModelsAvailable: 'Keine Modelle verfügbar',\n noProviderConnected: 'Kein AI-Provider verbunden',\n selectModelPlaceholder: 'Modell wählen…',\n orchestratorStarterHint: 'Sag Agentico, was du erreichen willst — es wählt das passende Werkzeug und fragt nach den nötigen Angaben.',\n orchestratorStarterPlaceholder: 'Beschreibe, was du tun möchtest…',\n destinationPickerTitle: 'Ergebnis speichern in…',\n destinationApplying: 'Wird gespeichert…',\n destinationNoScope: 'Kein Ziel ausgewählt — vor dem Speichern eines auswählen.',\n unknownDestination: 'Unbekanntes Ziel: {id}',\n exportJson: 'Als JSON exportieren',\n orchestratorUnavailable: 'AI nicht verfügbar — öffne das benötigte Werkzeug direkt auf seiner Seite.',\n orchestratorContinueWithoutAi: 'Weiter ohne AI.',\n orchestratorPreparing: '{label} wird vorbereitet…',\n orchestratorCollecting: 'Parameter für {label} werden gesammelt…',\n thinking: 'Denke nach…',\n stopped: 'Vom Benutzer abgebrochen.',\n toolRequest: 'Werkzeugausführung angefordert',\n modelRequestError: 'Fehler bei der Anfrage an das Modell',\n noProviderResponse: 'Keine Antwort vom AI-Provider.',\n noActiveSession: 'Keine aktive Sitzung — zuerst run() aufrufen.',\n requestInProgress: 'Eine andere Anfrage läuft bereits.',\n invalidResponse: 'Ungültige AI-Antwort.',\n responseReceived: 'Antwort erhalten',\n noProviderAvailable: 'Kein AI-Provider verfügbar.',\n tooManyToolTurns: 'Zu viele aufeinanderfolgende Werkzeugaufrufe — das bisher gesammelte deterministische Ergebnis wird verwendet.',\n aiDisabledFallback: 'AI deaktiviert — das deterministische Ergebnis wird verwendet.',\n continueWithoutAiFallback: 'Weiter ohne AI — das deterministische Ergebnis wird verwendet.',\n newInstance: 'Neue Instanz von \"{label}\"',\n chatHistory: 'Chatverlauf',\n newChat: 'Neuer Chat',\n searchChats: 'Chats durchsuchen…',\n messagePlaceholder: 'Nachricht schreiben…',\n unsavedConversationConfirm: 'Diese Unterhaltung wurde nicht gespeichert und geht verloren. Fortfahren?',\n noSavedChats: 'Noch keine gespeicherten Chats.',\n archiveChat: 'Archivieren',\n noResults: 'Keine Ergebnisse.',\n retry: 'Erneut versuchen',\n retryMessage: 'Erneut versuchen.',\n continueWithoutAi: 'Ohne AI fortfahren',\n resumeWithAi: 'Mit AI fortsetzen',\n openPanel: 'Agentenbereich öffnen',\n closePanel: 'Agentenbereich schließen',\n};\n","import type { AgenticoDict } from './types';\n\nexport const agenticoEn: AgenticoDict = {\n modelLabel: 'Model',\n modelsLoading: 'Loading…',\n noModelsAvailable: 'No models available',\n noProviderConnected: 'No AI provider connected',\n selectModelPlaceholder: 'Choose a model…',\n orchestratorStarterHint: 'Tell Agentico what you want to do — it will pick the right tool and ask for what it needs.',\n orchestratorStarterPlaceholder: 'Describe what you want to do…',\n destinationPickerTitle: 'Save result to…',\n destinationApplying: 'Saving…',\n destinationNoScope: 'Nothing selected to save into — select one before saving.',\n unknownDestination: 'Unknown destination: {id}',\n exportJson: 'Export as JSON',\n orchestratorUnavailable: 'AI not available — open the tool you need directly from its page.',\n orchestratorContinueWithoutAi: 'Continuing without AI.',\n orchestratorPreparing: 'Preparing {label}…',\n orchestratorCollecting: 'Collecting the parameters for {label}…',\n thinking: 'Thinking…',\n stopped: 'Stopped by the user.',\n toolRequest: 'Requested to run some tools',\n modelRequestError: 'Error in the request to the model',\n noProviderResponse: 'No response from the AI provider.',\n noActiveSession: 'No active session — call run() first.',\n requestInProgress: 'Another request is already running.',\n invalidResponse: 'Invalid AI response.',\n responseReceived: 'Response received',\n noProviderAvailable: 'No AI provider available.',\n tooManyToolTurns: 'Too many consecutive tool-calling turns — using the deterministic result collected so far.',\n aiDisabledFallback: 'AI disabled — using the deterministic result.',\n continueWithoutAiFallback: 'Continuing without AI — using the deterministic result.',\n newInstance: 'New \"{label}\" instance',\n chatHistory: 'Chat history',\n newChat: 'New chat',\n searchChats: 'Search chats…',\n messagePlaceholder: 'Write a message…',\n unsavedConversationConfirm: 'This conversation has not been saved and will be lost. Continue?',\n noSavedChats: 'No saved chats yet.',\n archiveChat: 'Archive',\n noResults: 'No results.',\n retry: 'Retry',\n retryMessage: 'Retry.',\n continueWithoutAi: 'Continue without AI',\n resumeWithAi: 'Resume with AI',\n openPanel: 'Open agent panel',\n closePanel: 'Close agent panel',\n};\n","import type { AgenticoDict } from './types';\n\nexport const agenticoIt: AgenticoDict = {\n modelLabel: 'Modello',\n modelsLoading: 'Caricamento…',\n noModelsAvailable: 'Nessun modello disponibile',\n noProviderConnected: 'Nessun provider AI connesso',\n selectModelPlaceholder: 'Scegli un modello…',\n orchestratorStarterHint: 'Dì ad Agentico cosa vuoi fare — sceglierà lo strumento giusto e ti chiederà cosa serve.',\n orchestratorStarterPlaceholder: 'Descrivi cosa vuoi fare…',\n destinationPickerTitle: 'Salva risultato in…',\n destinationApplying: 'Salvataggio…',\n destinationNoScope: 'Nessuna destinazione selezionata — selezionane una prima di salvare.',\n unknownDestination: 'Destinazione sconosciuta: {id}',\n exportJson: 'Esporta come JSON',\n orchestratorUnavailable: 'AI non disponibile — apri il tool che ti serve direttamente dalla sua pagina.',\n orchestratorContinueWithoutAi: 'Continuo senza AI.',\n orchestratorPreparing: 'Preparo {label}…',\n orchestratorCollecting: 'Sto raccogliendo i parametri per {label}…',\n thinking: 'Sto pensando…',\n stopped: \"Interrotto dall'utente.\",\n toolRequest: 'Richiesta di eseguire dei tool',\n modelRequestError: 'Errore nella richiesta al modello',\n noProviderResponse: 'Nessuna risposta dal provider AI.',\n noActiveSession: 'Nessuna sessione attiva — chiama prima run().',\n requestInProgress: \"Un'altra richiesta è già in corso.\",\n invalidResponse: 'Risposta AI non valida.',\n responseReceived: 'Risposta ricevuta',\n noProviderAvailable: 'Nessun provider AI disponibile.',\n tooManyToolTurns: 'Troppi turni di tool-calling consecutivi — uso il risultato deterministico raccolto finora.',\n aiDisabledFallback: 'AI disabilitata — uso il risultato deterministico.',\n continueWithoutAiFallback: 'Continuo senza AI — uso il risultato deterministico.',\n newInstance: 'Nuova istanza di \"{label}\"',\n chatHistory: 'Storico chat',\n newChat: 'Nuova chat',\n searchChats: 'Cerca nelle chat…',\n messagePlaceholder: 'Scrivi un messaggio…',\n unsavedConversationConfirm: 'Questa conversazione non è stata salvata e andrà persa. Continuare?',\n noSavedChats: 'Nessuna chat salvata ancora.',\n archiveChat: 'Archivia',\n noResults: 'Nessun risultato.',\n retry: 'Riprova',\n retryMessage: 'Riprova.',\n continueWithoutAi: 'Continua senza AI',\n resumeWithAi: 'Riprendi con AI',\n openPanel: 'Apri pannello agente',\n closePanel: 'Chiudi pannello agente',\n};\n","import { useI18n } from '@llmnative/react';\nimport { agenticoDe } from './de';\nimport { agenticoEn } from './en';\nimport { agenticoIt } from './it';\nimport type { AgenticoDict } from './types';\n\nexport type { AgenticoDict } from './types';\nexport { agenticoDe, agenticoEn, agenticoIt };\n\n/** Merge into <App i18n={{ translations }}> — e.g. `it: { ...locales.it, ...agenticoTranslations.it, ...yourIt }`. */\nexport const agenticoTranslations = {\n en: { agentico: agenticoEn },\n it: { agentico: agenticoIt },\n de: { agentico: agenticoDe },\n};\n\n/** Agentico strings for the active locale: what the host registered, on top of English. */\nexport function useAgenticoI18n(): AgenticoDict {\n const { dict } = useI18n();\n const fromHost = (dict as { agentico?: Partial<AgenticoDict> }).agentico;\n return fromHost ? { ...agenticoEn, ...fromHost } : agenticoEn;\n}\n","import React, { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react';\r\nimport type { AIModelDescriptor, ChatbotAction, DataProviderAdapter, StorageProviderAdapter } from '@llmnative/react';\r\nimport type { AgentActivityEntry, AgentConversation } from './useAgent';\r\nimport type { ResultDestination } from '../domain/resultDestination';\r\nimport type { SubagentUsageCard } from '../domain/subagentUsageCard';\r\nimport { findSubagentManifest } from '../domain/subagentRegistry';\r\nimport { saveEntityChat, type EntityChatSnapshot } from '../domain/entityChatStore';\r\nimport { getAgenticoPrompts } from '../domain/prompts';\r\n\r\n/** Where results are saved and chats are kept: the host's current tenant, company, workspace…\r\n * `null` while the host has none selected — destinations then refuse to save. */\r\nexport interface AgenticoScope {\r\n id: string;\r\n dataProvider: DataProviderAdapter;\r\n storageProvider: StorageProviderAdapter | null;\r\n}\r\n\r\n/** Cattura la conversazione di un task subagent già esistente da \"continuare\" invece di forkare\r\n * — vedi startOrchestrator sotto. `taskId` viene riusato dal nuovo SubagentRunner (invece di un\r\n * uuid fresco) cosi il task esistente si aggiorna in place; `activity` (precedingActivity +\r\n * agent.activity del vecchio task, già concatenati) viene prepesa al transcript del nuovo giro —\r\n * l'utente legge tutto come UNA chat continua, mai due sessioni scollegate per lo stesso\r\n * subagent. */\r\nexport interface PendingCarryOver {\r\n taskId: string;\r\n activity: AgentActivityEntry[];\r\n}\r\n\r\n/** Id fisso del task dell'orchestratore Agentico — al massimo UNA conversazione orchestratore\r\n * per volta, come per qualunque altro task (vedi AgenticoTask sotto: un solo task alla volta in\r\n * tutta l'app, mai una tab bar da switchare). Un id fisso permette a startOrchestrator() di\r\n * riconoscere \"ce n'è già uno aperto\" senza dover tenere un riferimento separato. */\r\nexport const ORCHESTRATOR_TASK_ID = 'agentico-orchestrator';\r\n\r\n/** Una sessione agente aperta nel pannello globale — riferimento vivo all'oggetto restituito\r\n * da useAgent() nel componente che l'ha avviata (NON una copia: quel componente deve restare\r\n * montato perché questo riferimento resti valido, vedi Agentico.tsx). Solo le sessioni aperte in\r\n * questa sessione browser — nessuna persistenza su Firestore (Fase 3, deferita).\r\n *\r\n * Al massimo UN task alla volta in tutta l'app (decisione esplicita: \"una chat unica\", niente\r\n * tab multiple per run paralleli) — avviarne uno nuovo mentre un altro è in corso lo sostituisce\r\n * (vedi openTask sotto), mai lo affianca. */\r\nexport interface AgenticoTask<TResult = unknown> {\r\n id: string;\r\n /** Titolo mostrato nell'header del pannello — es. l'URL importato, il nome del componente. */\r\n title: string;\r\n /** Nome icona (@llmnative/react Icon) mostrata nell'header — 'bot' per l'orchestratore\r\n * (OrchestratorRunner, stessa identità di AgenticoToggle), manifest.icon per un subagent\r\n * (SubagentRunner). */\r\n icon: string;\r\n /** SOLO sul task dell'orchestratore — id del subagent verso cui la pagina/componente che ha\r\n * avviato questa conversazione era scoped (catturato da OrchestratorRunner al mount, da\r\n * sidebar.scopedSubagentId). Permette a un indice come redirects/IndexList.tsx di mostrare\r\n * \"in corso\" per QUESTO subagent anche prima dell'handoff (mentre l'orchestratore sta ancora\r\n * raccogliendo parametri/chiamando tool di raccolta dati). Assente sui task dei subagent\r\n * stessi (quelli sono già inequivocabilmente scoped dal proprio id\r\n * `subagent-${subagentId}-...`). */\r\n scopedSubagentId?: string;\r\n /** Presente SOLO dopo un commitEntityChat riuscito su questo task — questa conversazione è\r\n * genuinamente su disco (vedi entityChatStore), non solo in memoria. Usato dal bottone\r\n * \"nuova chat\" (vedi Agentico.tsx) per decidere se avvertire l'utente prima di scartarla:\r\n * senza salvataggio andrebbe persa per sempre (nessun \"torna indietro\", scelta esplicita). */\r\n savedToDisk?: boolean;\r\n agent: AgentConversation<TResult> & {\r\n hasProviders: boolean;\r\n selectedModel: string;\r\n setSelectedModel: (id: string) => void;\r\n modelsByProvider: Record<string, AIModelDescriptor[]>;\r\n };\r\n onApplied: (result: TResult) => void;\r\n /** Transcript di UNA conversazione precedente da mostrare \"prima\" di `agent.activity` nello\r\n * stesso log, senza soluzione di continuità — usato dal task del subagent per prependere la\r\n * conversazione dell'orchestratore che lo ha invocato (vedi OrchestratorRunner/SubagentRunner):\r\n * l'utente deve vivere Agentico come UNA chat discorsiva unica, mai accorgersi che ci sono\r\n * due \"attori\" distinti dietro le quinte. */\r\n precedingActivity?: AgentActivityEntry[];\r\n /** Contenuto extra renderizzato nel body del pannello, sotto il log di attività (es.\r\n * ResultDestinationPicker una volta che un subagent ha prodotto un risultato senza un\r\n * componente vivo ad ascoltarlo — vedi SubagentRunner). Deve restare dentro il pannello\r\n * Agentico, non un elemento reso altrove nell'albero: chi possiede il task\r\n * (SubagentRunner/OrchestratorRunner) è montato come sibling PRIMA del pannello visibile in\r\n * Agentico.tsx (per sopravvivere al toggle di isOpen), quindi il proprio JSX di ritorno\r\n * finirebbe fuori dal pannello se non passato tramite questo slot esplicito. */\r\n panelExtra?: React.ReactNode;\r\n /** Presente SOLO sul task di un ManualEntryRunner — quando impostato, AgenticoComposer\r\n * (Agentico.tsx) instrada il testo digitato QUI invece che ad `agent.sendMessage`: niente\r\n * chiamata AI, `onSubmit` costruisce i TParams dal testo grezzo e avvia il subagent con\r\n * l'AI disabilitata (vedi SubagentManifest.manualEntry). `prompt` è il messaggio statico\r\n * mostrato quando non c'è ancora nessuna attività (invece del solito pageContext/hint). */\r\n manualEntry?: { prompt: string; onSubmit: (rawText: string) => void };\r\n /** Quante voci di `precedingActivity + agent.activity` (in ordine, vedi Agentico.tsx)\r\n * precedono `panelExtra` nella timeline — cioè QUANTE erano già maturate quando il\r\n * risultato mostrato in panelExtra è stato prodotto. Senza questo indice, panelExtra veniva\r\n * sempre reso dopo l'INTERO log corrente, quindi ogni messaggio di follow-up successivo\r\n * (es. un retry finito in errore) appariva SOPRA un risultato che in realtà era arrivato\r\n * PRIMA — invertendo la cronologia di una chat che l'utente si aspetta scorra\r\n * top=vecchio/bottom=nuovo (bug segnalato). Assente = comportamento invariato, sempre in\r\n * fondo (equivalente a passare la lunghezza corrente del log). */\r\n panelExtraAnchorIndex?: number;\r\n /** What the host shows at the bottom of the panel, above the composer, while the task is open\r\n * (see registerSubagentControls, CR-003). */\r\n panelActions?: React.ReactNode;\r\n /** Buttons of the host in the composer (see registerSubagentControls, CR-003). */\r\n composerActions?: ChatbotAction[];\r\n /** Chiamato quando questo task viene sostituito da uno nuovo (vedi openTask) o quando\r\n * l'utente lo interrompe — SEMPRE definito da chi possiede il task\r\n * (OrchestratorRunner/SubagentRunner), mai un fallback generico su closeTask: quel\r\n * componente resta montato anche a task chiuso, quindi il proprio effect che ri-registra il\r\n * task a ogni cambiamento di agent.activity/running lo RIAPRIREBBE da solo (es.\r\n * l'aggiornamento \"Interrotto dall'utente\" dopo uno stop()) se non gli si dice\r\n * esplicitamente di smettere — vedi il ref `dismissedRef` in entrambi i chiamanti. */\r\n onDismiss: () => void;\r\n}\r\n\r\n/** Un punto di ancoraggio semantico subagent↔componente attivo in questo momento — vedi\r\n * useSubagentComponentContext (presentation/hooks) per come si registra, e\r\n * SubagentManifest.componentContexts per dove vivono i testi (agenticoHint/userMessage). */\r\nexport interface ComponentAnchor {\r\n subagentId: string;\r\n componentKey: string;\r\n /** Chiamato da SubagentRunner quando arriva un risultato per `subagentId` mentre questo\r\n * anchor è montato — il componente/pagina popola se stesso, mai un salvataggio automatico.\r\n * `meta` è il TParams con cui il subagent è stato lanciato (es. `{ url }` per theme).\r\n * `taskId` è l'id del task Agentico che ha prodotto questo risultato — un componente che\r\n * vuole poter persistere la chat al proprio salvataggio (vedi commitEntityChat sotto) lo\r\n * tiene da parte, altrimenti può ignorarlo. */\r\n onResult?: (result: unknown, meta: Record<string, unknown>, taskId: string) => void;\r\n /** Fatti live su QUESTA istanza dell'entità aperta (es. \"Il tema aperto si chiama\r\n * ducatimilano.it\"), aggiunti in coda all'`agenticoHint` statico del manifest (vedi\r\n * anchorTexts) — l'`agenticoHint` è prosa fissa scritta una volta nel manifest, non può\r\n * sapere quale entità specifica è aperta ORA. Assente = comportamento invariato (solo il\r\n * testo statico). Vedi useSubagentComponentContext per come un componente lo valorizza. */\r\n contextInfo?: string;\r\n /** Id dell'entità a cui QUESTA istanza è vincolata (es. l'hostId di RedirectsEditor) — a\r\n * differenza di `contextInfo` (prosa, solo per il prompt), è un valore STRUTTURATO che\r\n * SubagentRunner inoltra a `ResultDestination.apply()` (vedi `meta.pinnedEntityId`) perché una\r\n * destinazione con logica di identità propria (es. redirectsSaveDestination, che calcola\r\n * l'hostId di destinazione dal risultato) possa rifiutare un salvataggio il cui hostId\r\n * risolto NON combacia — un backstop deterministico, non affidato solo all'istruzione nel\r\n * prompt (che può comunque essere ignorata dal modello). Assente = nessun controllo\r\n * aggiuntivo, comportamento invariato (es. RedirectsIndexList, che non è vincolato a un solo\r\n * host). */\r\n pinnedEntityId?: string;\r\n}\r\n\r\ninterface AgenticoContextValue {\r\n /** The host scope passed to <AgenticoProvider scope>. */\r\n scope: AgenticoScope | null;\r\n task: AgenticoTask | null;\r\n /** Visibilità del pannello — completamente indipendente dal task (vedi AgenticoToggle:\r\n * icona persistente stile \"estensione del browser\"). Nessuna chiamata a `openTask` la forza\r\n * mai a true: un fetch/run() NON apre da solo il pannello (solo l'icona anima per segnalare\r\n * che l'agente sta lavorando) — solo l'utente, cliccando il toggle, decide di vederlo. */\r\n isOpen: boolean;\r\n /** Crea o sostituisce il task attivo (upsert idempotente se stesso id, altrimenti sostituisce:\r\n * un task con un id DIVERSO da quello corrente ne causa la chiusura esplicita via\r\n * `onDismiss()` prima di installare il nuovo — mai due conversazioni vive insieme, vedi\r\n * AgenticoTask). Richiamalo a ogni render, l'oggetto `agent` è nuovo a ogni render dell'hook\r\n * useAgent. */\r\n openTask: <TResult>(task: AgenticoTask<TResult>) => void;\r\n closeTask: (id: string) => void;\r\n /** Mostra/nasconde il pannello — l'unica azione che tocca `isOpen` insieme a `openPanel`\r\n * (vedi commento su `isOpen`). Usato da AgenticoToggle. */\r\n toggleOpen: () => void;\r\n /** Goal in attesa che un OrchestratorRunner catturi al mount e lanci — settato SOLO se non\r\n * esiste già un task orchestratore (vedi startOrchestrator). `null` quando non c'è nessun\r\n * avvio in sospeso. */\r\n pendingGoal: string | null;\r\n /** Cambia a ogni conversazione orchestratore avviata per davvero — usato solo come React\r\n * `key` sull'elemento <OrchestratorRunner>, mai letto per altro. */\r\n orchestratorRunId: string | null;\r\n /** Chiamato da ToolsPage (\"Usa agent\" su una card), dall'input vuoto del pannello Agentico, o\r\n * da una pagina che vuole avviare una conversazione già scoped (es. SiteEditorPage) — apre il\r\n * pannello e, se non c'è già una conversazione orchestratore in corso, la avvia con questo\r\n * goal. Se ce n'è già una, la porta semplicemente a fuoco: NON inietta il nuovo goal in\r\n * quella conversazione (limite noto, accettato per l'MVP).\r\n * `options.scopedSubagentId` — presente quando il chiamante sa GIÀ, senza bisogno di AI,\r\n * quale subagent servirà (es. il bottone \"Importa HTML/JSX\", sempre `html-to-liquid`) — a\r\n * differenza dello scoping derivato da un componentAnchor (vedi `scopedSubagentId` sotto,\r\n * che richiede un ancoraggio live), questo vale per un click one-shot che non monta alcun\r\n * anchor. Usato SOLO per abilitare il percorso \"Continua senza AI\" → manuale (vedi\r\n * startManualEntry/ManualEntryRunner) quando l'orchestratore stesso non riesce a partire:\r\n * senza sapere il target, quel bottone non potrebbe offrire altro che il messaggio generico\r\n * di fallback dell'orchestratore (non un vero handoff, non può indovinare l'intento). */\r\n startOrchestrator: (goal: string, options?: { extraDestinations?: ResultDestination[]; attachments?: File[]; model?: string; scopedSubagentId?: string }) => void;\r\n /** Destinazioni scoped alla pagina che ha avviato l'ultimo startOrchestrator() con\r\n * `options.extraDestinations` — catturate da OrchestratorRunner al momento dell'handoff e\r\n * inoltrate al SubagentRunner che monta, insieme a `pendingGoal`. `undefined` per i\r\n * chiamanti che non ne passano. */\r\n pendingExtraDestinations: ResultDestination[] | undefined;\r\n /** File allegati al messaggio che ha avviato l'ultima conversazione orchestratore — catturati\r\n * da OrchestratorRunner al mount, insieme a `pendingGoal`. */\r\n pendingAttachments: File[] | undefined;\r\n /** Modello scelto dall'utente nel composer \"starter\", prima ancora che esista una sessione\r\n * useAgent — catturato da OrchestratorRunner al mount, insieme a `pendingGoal`. */\r\n pendingModel: string | undefined;\r\n /** Vedi PendingCarryOver — catturato da OrchestratorRunner al mount insieme a pendingGoal,\r\n * `null` quando startOrchestrator() non ha trovato nessun task da continuare. */\r\n pendingCarryOver: PendingCarryOver | null;\r\n /** Vedi `options.scopedSubagentId` di startOrchestrator sopra — catturato da OrchestratorRunner\r\n * al mount, insieme agli altri pending*. `undefined` = nessun target esplicito (comportamento\r\n * invariato, l'orchestratore deriva lo scope SOLO da un componentAnchor live, se presente). */\r\n pendingScopedSubagentId: string | undefined;\r\n /** Testo \"dove si trova l'utente adesso\" per contestualizzare l'orchestratore — se ci sono\r\n * ancoraggi componente↔subagent attivi (vedi componentAnchors sotto) deriva da quelli\r\n * (agenticoHint, combinato se ne sono attivi più di uno di subagent diversi), altrimenti\r\n * ripiega sul vecchio meccanismo a stack per-pagina (useAgenticoPageContext, ancora usato da\r\n * pagine non migrate a componentContexts). Letto una sola volta da OrchestratorRunner al\r\n * momento di run(). */\r\n pageContext: string | null;\r\n /** Card strutturata (campi richiesti/opzionali) — SOLO dal vecchio meccanismo a stack\r\n * per-pagina (mai per un anchor component-based, che usa invece `anchoredUserMessages` sotto,\r\n * un semplice messaggio invece di un modulo a campi). `null` quando non applicabile. */\r\n pageContextCard: SubagentUsageCard | null;\r\n /** Registra/rimuove (text === null) il contesto di UNA pagina/pannello identificata da `id`\r\n * — usare tramite l'hook useAgenticoPageContext, non direttamente. Vedi pageContext sopra per\r\n * come si combina con componentAnchors. */\r\n setPageContext: (id: string, text: string | null, card?: SubagentUsageCard | null) => void;\r\n /** Id del subagent a cui l'orchestratore deve restare scoped — deriva da componentAnchors\r\n * quando esattamente UN subagent è ancorato, altrimenti (0 o >1 subagent distinti) ripiega\r\n * sul vecchio pageContextCard/`null`. Sostituisce il vecchio `pageContextCard?.subagentId`\r\n * come UNICA fonte per OrchestratorRunner/startOrchestrator. */\r\n scopedSubagentId: string | null;\r\n /** Ancoraggi componente↔subagent attualmente montati, deduplicati per subagentId (l'ultimo\r\n * registrato per uno stesso subagent vince) — usato da SubagentRunner per instradare un\r\n * risultato al componente vivo invece di mostrare sempre ResultDestinationPicker. */\r\n componentAnchors: ComponentAnchor[];\r\n /** Messaggi utente (SubagentComponentContext.userMessage) per gli anchor attivi — mostrati\r\n * da Agentico.tsx al posto della vecchia card a campi quando ci sono anchor component-based.\r\n * Vuoto se nessun anchor attivo, o se un anchor non ha un componentContexts corrispondente\r\n * (es. SiteEditorPage, che ascolta un risultato senza fornire un proprio messaggio). */\r\n anchoredUserMessages: string[];\r\n /** Registra/rimuove (anchor === null) un ancoraggio componente↔subagent identificato da `id`\r\n * — usare tramite l'hook useSubagentComponentContext, non direttamente. */\r\n registerComponentAnchor: (id: string, anchor: ComponentAnchor | null) => void;\r\n /** Apre il pannello incondizionatamente — a differenza di toggleOpen(), non dipende dal\r\n * valore corrente di isOpen. */\r\n openPanel: () => void;\r\n /** Persiste (sovrascrivendo un'eventuale chat già salvata per la STESSA entità, vedi\r\n * entityChatStore) la conversazione del task `taskId`, keyed su `${scopeId}:${subagentId}:\r\n * ${entityId}` — chiamato da un componente (es. ThemeEditor) subito dopo un salvataggio\r\n * riuscito. No-op se `taskId` è assente (nessuna estrazione coinvolta in questo salvataggio,\r\n * es. edit manuale — un'eventuale chat già salvata in precedenza per questa entità resta\r\n * intatta) o se il task `taskId` non è più quello corrente (l'utente ha aperto un'altra\r\n * conversazione nel frattempo — niente si salva, per non attaccare a un'entità la chat\r\n * sbagliata). */\r\n commitEntityChat: (subagentId: string, entityId: string, taskId: string | null | undefined, scopeId: string | undefined, params: Record<string, unknown>, title: string) => void;\r\n /** Riprende una chat salvata (vedi entityChatStore) — apre il pannello e monta un\r\n * SubagentRunner con `resumeSeed` valorizzato invece di un run/handoff dall'orchestratore\r\n * (vedi Agentico.tsx). Sostituisce (mai affianca) qualunque task corrente, stesso principio\r\n * di startOrchestrator/openTask — una chat unica alla volta in tutta l'app. */\r\n resumeEntityChat: (subagentId: string, entityId: string, snapshot: EntityChatSnapshot) => void;\r\n /** Presente = un resumeEntityChat() è in sospeso, letto da Agentico.tsx per montare il\r\n * SubagentRunner col resumeSeed giusto — analogo a pendingGoal per l'orchestratore. */\r\n pendingResume: { subagentId: string; entityId: string; snapshot: EntityChatSnapshot } | null;\r\n /** Cambia a ogni resumeEntityChat() — usato solo come React `key` sul SubagentRunner di\r\n * resume (vedi Agentico.tsx), stesso principio di orchestratorRunId. */\r\n resumeRunId: string | null;\r\n /** Presente = un startManualEntry() è in sospeso, letto da Agentico.tsx per montare un\r\n * ManualEntryRunner per QUESTO subagent — percorso \"senza AI\" per quando l'orchestratore\r\n * stesso non riesce a partire ma il target è già noto (vedi\r\n * SubagentManifest.manualEntry/pendingScopedSubagentId). Analogo a pendingGoal/pendingResume. */\r\n pendingManualEntry: { subagentId: string; extraDestinations?: ResultDestination[] } | null;\r\n /** Cambia a ogni startManualEntry() — React `key` sul ManualEntryRunner, stesso principio di\r\n * orchestratorRunId/resumeRunId. */\r\n manualEntryRunId: string | null;\r\n /** Avvia il percorso \"senza AI\" per un subagent già noto — chiamato dal bottone \"Continua\r\n * senza AI\" quando la conversazione orchestratore è scoped e non può proseguire (vedi\r\n * Agentico.tsx). Congeda subito il task orchestratore corrente, stesso principio di un vero\r\n * handoff AI-driven (OrchestratorRunner.handleResult). */\r\n startManualEntry: (subagentId: string, extraDestinations?: ResultDestination[]) => void;\r\n /** Presente = un resumeWithAI() è in sospeso, letto da Agentico.tsx per montare un vero\r\n * SubagentRunner (AI abilitata) per QUESTO subagent con QUESTI parametri — il \"contrario\" di\r\n * startManualEntry: dopo aver lavorato nel percorso senza AI, l'utente può far raffinare\r\n * dall'AI lo stesso input, invece di restare bloccato lì per sempre. */\r\n pendingResumeWithAI: { subagentId: string; params: Record<string, unknown>; extraDestinations?: ResultDestination[] } | null;\r\n /** Cambia a ogni resumeWithAI() — React `key` sul SubagentRunner risultante, stesso principio\r\n * di manualEntryRunId. */\r\n resumeWithAIRunId: string | null;\r\n /** Passa da un task ManualEntryRunner (senza AI) a un vero SubagentRunner (AI abilitata) per\r\n * lo STESSO subagent, riusando `params` (gli ultimi usati con successo nel percorso senza\r\n * AI) come punto di partenza — mai una conversazione persa, solo il canale che cambia.\r\n * Generico per costruzione: nessuna conoscenza di QUALE subagent, funziona per ogni\r\n * manifest con `manualEntry` dichiarato, non solo html-to-liquid. */\r\n resumeWithAI: (subagentId: string, params: Record<string, unknown>, extraDestinations?: ResultDestination[]) => void;\r\n /** Congeda il task corrente — usato da un componente che ha già stabilito (in base al\r\n * proprio subagentId/entità) che il task visibile non è il suo (vedi ThemeEditor/\r\n * ThemeIndexList). Regola unica, nessuna eccezione: quale chat si vede dipende SOLO dal\r\n * componente/entità a cui si è agganciati in questo momento — niente puntatori \"torna\r\n * indietro\", niente eccezione per un task ancora in corso (deliberatamente semplice,\r\n * raffinabile in seguito). No-op se non c'è alcun task. */\r\n dismissForeignTask: () => void;\r\n /** Starts a subagent from a page, without the orchestrator (CR-003): it replaces the current\r\n * task. `taskId` keeps one task per entity (start it again = the same task); `openingMessage`\r\n * is shown as the first message of the person and given to the first turn. */\r\n startSubagent: (subagentId: string, params: Record<string, unknown>, options?: { taskId?: string; openingMessage?: string }) => void;\r\n /** Present = a startSubagent() is pending, read by Agentico.tsx to mount its SubagentRunner. */\r\n pendingStart: { subagentId: string; params: Record<string, unknown>; taskId?: string; openingMessage?: string } | null;\r\n /** Changes at every startSubagent() — React `key` of that SubagentRunner. */\r\n startRunId: string | null;\r\n}\r\n\r\nconst AgenticoContext = createContext<AgenticoContextValue | null>(null);\r\n\r\nexport function AgenticoProvider({ children, scope = null }: { children: React.ReactNode; scope?: AgenticoScope | null }) {\r\n const [task, setTaskState] = useState<AgenticoTask | null>(null);\r\n // Stabile per tutta la vita del provider (vedi openTask sotto) — leggere `task` da una ref\r\n // invece che dalle dipendenze di useCallback evita che openTask cambi identità ogni volta che\r\n // il task cambia, che altrimenti farebbe ripartire in loop gli effect di registrazione di\r\n // OrchestratorRunner/SubagentRunner (le cui dipendenze includono sidebar.openTask) — stesso\r\n // bug class già osservato altrove in questo file (vedi filteredResult in SubagentRunner.tsx).\r\n const taskRef = useRef<AgenticoTask | null>(null);\r\n useEffect(() => { taskRef.current = task; }, [task]);\r\n const [isOpen, setIsOpen] = useState(false);\r\n\r\n const openTask = useCallback(<TResult,>(next: AgenticoTask<TResult>) => {\r\n const current = taskRef.current;\r\n if (current && current.id !== next.id) current.onDismiss();\r\n // Chi possiede il task (SubagentRunner) ricostruisce l'intero oggetto a ogni\r\n // ri-registrazione (un cambio qualunque di agent.activity/running/...) SENZA sapere\r\n // nulla di `savedToDisk` (settato altrove, da commitEntityChat) — senza questo merge,\r\n // un follow-up in chat DOPO il salvataggio cancellerebbe silenziosamente il flag appena\r\n // impostato (bug osservato: il dialogo \"nuova chat\" tornava a chiedere conferma per una\r\n // chat già salvata). Preservato SOLO quando è genuinamente lo STESSO task (stesso id) —\r\n // un vero task nuovo riparte da zero, correttamente non salvato.\r\n const merged = (current && current.id === next.id && current.savedToDisk && next.savedToDisk === undefined)\r\n ? { ...next, savedToDisk: true }\r\n : next;\r\n setTaskState(merged as AgenticoTask);\r\n }, []);\r\n\r\n // Chiudere il task NON nasconde il pannello — il pannello è un \"posto persistente\", come\r\n // l'icona di un'estensione del browser: resta dov'è (magari vuoto) finché non è l'utente\r\n // stesso a nasconderlo col toggle globale.\r\n const closeTask = useCallback((id: string) => {\r\n setTaskState((prev) => (prev && prev.id === id ? null : prev));\r\n }, []);\r\n\r\n const toggleOpen = useCallback(() => setIsOpen((v) => !v), []);\r\n const openPanel = useCallback(() => setIsOpen(true), []);\r\n\r\n const [pendingGoal, setPendingGoal] = useState<string | null>(null);\r\n const [pendingExtraDestinations, setPendingExtraDestinations] = useState<ResultDestination[] | undefined>(undefined);\r\n const [pendingAttachments, setPendingAttachments] = useState<File[] | undefined>(undefined);\r\n const [pendingModel, setPendingModel] = useState<string | undefined>(undefined);\r\n // Cambia a ogni conversazione orchestratore avviata per davvero (ramo \"else\" sotto) — usato\r\n // come React `key` sull'elemento <OrchestratorRunner> (vedi Agentico.tsx), cosi un nuovo\r\n // startOrchestrator() smonta e rimonta un'istanza FRESCA invece di riusare quella precedente.\r\n const [orchestratorRunId, setOrchestratorRunId] = useState<string | null>(null);\r\n const [pendingCarryOver, setPendingCarryOver] = useState<PendingCarryOver | null>(null);\r\n const [pendingScopedSubagentId, setPendingScopedSubagentId] = useState<string | undefined>(undefined);\r\n\r\n // ── Contesto pagina, vecchio meccanismo a stack (useAgenticoPageContext) ──────────────────\r\n const [pageContextEntries, setPageContextEntries] = useState<Array<{ id: string; text: string; card: SubagentUsageCard | null }>>([]);\r\n const setPageContext = useCallback((id: string, text: string | null, card: SubagentUsageCard | null = null) => {\r\n setPageContextEntries((prev) => {\r\n const next = prev.filter((e) => e.id !== id);\r\n return text ? [...next, { id, text, card }] : next;\r\n });\r\n }, []);\r\n const lastPageContextEntry = pageContextEntries.length > 0 ? pageContextEntries[pageContextEntries.length - 1] : null;\r\n\r\n // ── Ancoraggi componente↔subagent, nuovo meccanismo (useSubagentComponentContext) ────────\r\n const [componentAnchorEntries, setComponentAnchorEntries] = useState<Array<{ id: string } & ComponentAnchor>>([]);\r\n const registerComponentAnchor = useCallback((id: string, anchor: ComponentAnchor | null) => {\r\n setComponentAnchorEntries((prev) => {\r\n const next = prev.filter((e) => e.id !== id);\r\n return anchor ? [...next, { id, ...anchor }] : next;\r\n });\r\n }, []);\r\n // NON deduplicato per subagentId — a differenza del vecchio stack pageContextEntries, QUI\r\n // più anchor per lo STESSO subagentId sono un caso reale e voluto (es. ThemeEditor, per i\r\n // campi tema, e SiteEditorPage, per i soli campi di site-branding, entrambi ancorati a\r\n // 'theme' mentre la modale è aperta — vedi SiteEditorPage.tsx): ognuno deve ricevere il\r\n // risultato, mai solo \"l'ultimo registrato\" (bug corretto: un dedup qui faceva sparire in\r\n // silenzio uno dei due listener). Il dedup per hint/scoping (sotto) resta separato.\r\n const componentAnchors = useMemo(\r\n () => componentAnchorEntries.map((e) => ({ subagentId: e.subagentId, componentKey: e.componentKey, onResult: e.onResult, pinnedEntityId: e.pinnedEntityId })),\r\n [componentAnchorEntries],\r\n );\r\n\r\n // QUI sì, deduplicato per subagentId — un hint/messaggio per subagent ancorato, non uno per\r\n // OGNI componente che lo ascolta (altrimenti un anchor \"solo ascolto\" senza propri\r\n // agenticoHint/userMessage, come SiteEditorPage, o due componenti sullo stesso subagent\r\n // produrrebbero hint duplicati/nessuno). L'ultimo registrato vince, stesso principio del\r\n // vecchio stack pageContextEntries.\r\n const uniqueAnchors = useMemo(() => {\r\n const bySubagent = new Map<string, ComponentAnchor>();\r\n for (const e of componentAnchorEntries) bySubagent.set(e.subagentId, { subagentId: e.subagentId, componentKey: e.componentKey, onResult: e.onResult, contextInfo: e.contextInfo });\r\n return [...bySubagent.values()];\r\n }, [componentAnchorEntries]);\r\n\r\n // Testi (agenticoHint/userMessage) per ogni anchor attivo il cui manifest dichiara davvero un\r\n // componentContexts per quella chiave — un anchor \"solo ascolto\" (es. SiteEditorPage, che\r\n // ascolta 'theme' senza essere uno dei suoi componenti dichiarati) non produce testo qui, ma\r\n // resta comunque nella lista `componentAnchors` sopra per il routing di onResult.\r\n const anchorTexts = useMemo(() => {\r\n return uniqueAnchors\r\n .map((a) => {\r\n const manifest = findSubagentManifest(a.subagentId);\r\n const ctx = manifest?.componentContexts?.[a.componentKey];\r\n if (!ctx) return null;\r\n // `contextInfo` (fatti live sull'entità aperta ORA, es. \"il tema aperto si\r\n // chiama ducatimilano.it\") in coda al testo statico del manifest — mai al posto\r\n // suo: l'hint spiega COSA fare, contextInfo COSA sa già il componente.\r\n const agenticoHint = a.contextInfo ? `${ctx.agenticoHint}\\n${a.contextInfo}` : ctx.agenticoHint;\r\n return { subagentId: a.subagentId, agenticoHint, userMessage: ctx.userMessage };\r\n })\r\n .filter((x): x is { subagentId: string; agenticoHint: string; userMessage: string } => x !== null);\r\n }, [uniqueAnchors]);\r\n\r\n const anchoredUserMessages = useMemo(() => anchorTexts.map((t) => t.userMessage), [anchorTexts]);\r\n\r\n // Testo combinato per il prompt dell'orchestratore — un solo hint se un solo subagent è\r\n // ancorato, altrimenti una premessa esplicita che elenca tutti i candidati e istruisce il\r\n // modello a chiedere all'utente quale intende prima di procedere (mai un tentativo silenzioso\r\n // di indovinare quale dei due l'utente vuole davvero).\r\n const anchoredPageContextText = useMemo(() => {\r\n if (anchorTexts.length === 0) return null;\r\n if (anchorTexts.length === 1) return anchorTexts[0].agenticoHint;\r\n const list = anchorTexts.map((t, i) => `${i + 1}. ${t.agenticoHint}`).join('\\n');\r\n return `${getAgenticoPrompts().multipleCandidates}\\n\\n${list}`;\r\n }, [anchorTexts]);\r\n\r\n const pageContext = anchoredPageContextText ?? lastPageContextEntry?.text ?? null;\r\n // Una card a campi (SubagentUsageCardView) ha senso solo per il vecchio meccanismo — un\r\n // anchor component-based mostra invece `anchoredUserMessages` (una frase, non un modulo).\r\n const pageContextCard = anchorTexts.length > 0 ? null : (lastPageContextEntry?.card ?? null);\r\n // Un solo subagent ancorato → scope diretto; zero o più di uno (ambiguo, l'LLM deve chiedere)\r\n // → ripiega sul vecchio pageContextCard (pagine non ancora migrate) o null.\r\n const uniqueAnchoredSubagentIds = useMemo(() => [...new Set(componentAnchors.map((a) => a.subagentId))], [componentAnchors]);\r\n const scopedSubagentId = uniqueAnchoredSubagentIds.length === 1\r\n ? uniqueAnchoredSubagentIds[0]\r\n : (uniqueAnchoredSubagentIds.length === 0 ? (lastPageContextEntry?.card?.subagentId ?? null) : null);\r\n\r\n const startOrchestrator = useCallback((goal: string, options?: { extraDestinations?: ResultDestination[]; attachments?: File[]; model?: string; scopedSubagentId?: string }) => {\r\n openPanel();\r\n const current = taskRef.current;\r\n if (current?.id === ORCHESTRATOR_TASK_ID) return; // già in corso, la porta a fuoco basta aprire il pannello\r\n\r\n // \"Stesso contesto\" = la pagina che ha invocato questa richiesta è scoped allo stesso\r\n // subagent del task già esistente (es. richiedi di nuovo \"estrai tema\" mentre il task\r\n // precedente per lo stesso subagent è ancora aperto) — in quel caso continua QUELLA\r\n // conversazione (stesso id, transcript prepeso) invece di aprirne una scollegata.\r\n const existingTask = (scopedSubagentId && current?.id.startsWith(`subagent-${scopedSubagentId}-`)) ? current : undefined;\r\n setPendingCarryOver(existingTask ? {\r\n taskId: existingTask.id,\r\n activity: [...(existingTask.precedingActivity ?? []), ...existingTask.agent.activity],\r\n } : null);\r\n setPendingGoal(goal);\r\n setPendingExtraDestinations(options?.extraDestinations);\r\n setPendingAttachments(options?.attachments);\r\n setPendingModel(options?.model);\r\n setPendingScopedSubagentId(options?.scopedSubagentId);\r\n setOrchestratorRunId(crypto.randomUUID());\r\n }, [openPanel, scopedSubagentId]);\r\n\r\n const commitEntityChat = useCallback((subagentId: string, entityId: string, taskId: string | null | undefined, scopeId: string | undefined, params: Record<string, unknown>, title: string) => {\r\n if (!taskId || !scopeId) return; // nessuna estrazione coinvolta in questo salvataggio\r\n const current = taskRef.current;\r\n if (!current || current.id !== taskId) return; // il task che ha popolato il draft non è più quello vivo\r\n const snap = current.agent.snapshot();\r\n if (!snap) return;\r\n // `current.precedingActivity` (vedi PendingCarryOver/orchestrator→subagent handoff) è il\r\n // trascritto prepeso SOLO per la visualizzazione (displayedActivity in Agentico.tsx) —\r\n // l'`activity` salvata qui deve includerlo esplicitamente, altrimenti un salvataggio dopo\r\n // aver continuato una conversazione già in corso perderebbe comunque la parte precedente.\r\n saveEntityChat({\r\n scopeId, subagentId, entityId, params, title,\r\n history: snap.history,\r\n activity: [...(current.precedingActivity ?? []), ...snap.activity],\r\n });\r\n setTaskState((prev) => (prev && prev.id === taskId ? { ...prev, savedToDisk: true } : prev));\r\n }, []);\r\n\r\n const [pendingResume, setPendingResume] = useState<{ subagentId: string; entityId: string; snapshot: EntityChatSnapshot } | null>(null);\r\n const [resumeRunId, setResumeRunId] = useState<string | null>(null);\r\n\r\n const resumeEntityChat = useCallback((subagentId: string, entityId: string, snapshot: EntityChatSnapshot) => {\r\n openPanel();\r\n setPendingResume({ subagentId, entityId, snapshot });\r\n setResumeRunId(crypto.randomUUID());\r\n }, [openPanel]);\r\n\r\n const [pendingManualEntry, setPendingManualEntry] = useState<{ subagentId: string; extraDestinations?: ResultDestination[] } | null>(null);\r\n const [manualEntryRunId, setManualEntryRunId] = useState<string | null>(null);\r\n\r\n const startManualEntry = useCallback((subagentId: string, extraDestinations?: ResultDestination[]) => {\r\n openPanel();\r\n // Stesso identico trattamento di un vero handoff AI-driven (OrchestratorRunner.\r\n // handleResult, ramo 'handoff'): closeTask diretto, MAI onDismiss — quando il\r\n // ManualEntryRunner monta ed esegue il proprio openTask, `taskRef.current` è già `null`\r\n // (svuotato qui), quindi il ramo \"sostituisci il task precedente\" di openTask non scatta\r\n // nemmeno: nessun problema, l'agent dell'orchestratore semplicemente non produce più\r\n // attività dopo questo punto, quindi il suo effect di registrazione non riparte da solo.\r\n closeTask(ORCHESTRATOR_TASK_ID);\r\n setPendingManualEntry({ subagentId, extraDestinations });\r\n setManualEntryRunId(crypto.randomUUID());\r\n }, [openPanel, closeTask]);\r\n\r\n const [pendingResumeWithAI, setPendingResumeWithAI] = useState<{ subagentId: string; params: Record<string, unknown>; extraDestinations?: ResultDestination[] } | null>(null);\r\n const [resumeWithAIRunId, setResumeWithAIRunId] = useState<string | null>(null);\r\n\r\n const resumeWithAI = useCallback((subagentId: string, params: Record<string, unknown>, extraDestinations?: ResultDestination[]) => {\r\n // Smonta SUBITO il ManualEntryRunner corrente (invece di un closeTask diretto, come in\r\n // startManualEntry sopra) — a differenza dell'orchestratore dopo un handoff,\r\n // ManualEntryRunner resta un componente vivo che si ri-registra a ogni cambio del proprio\r\n // stato (result/applyLabel/...): un semplice closeTask() qui verrebbe subito vanificato\r\n // dal suo prossimo re-render. Smontarlo (pendingManualEntry → null) fa scattare il suo\r\n // effect di cleanup (closeTask sul PROPRIO id) prima che il nuovo SubagentRunner monti e\r\n // si registri — nessuna corsa tra i due task.\r\n setPendingManualEntry(null);\r\n setPendingResumeWithAI({ subagentId, params, extraDestinations });\r\n setResumeWithAIRunId(crypto.randomUUID());\r\n }, []);\r\n\r\n const [pendingStart, setPendingStart] = useState<{ subagentId: string; params: Record<string, unknown>; taskId?: string; openingMessage?: string } | null>(null);\r\n const [startRunId, setStartRunId] = useState<string | null>(null);\r\n\r\n const startSubagent = useCallback((subagentId: string, params: Record<string, unknown>, options?: { taskId?: string; openingMessage?: string }) => {\r\n openPanel();\r\n setPendingStart({ subagentId, params, ...options });\r\n setStartRunId(crypto.randomUUID());\r\n }, [openPanel]);\r\n\r\n const dismissForeignTask = useCallback(() => {\r\n const current = taskRef.current;\r\n if (!current) return;\r\n current.onDismiss();\r\n }, []);\r\n\r\n const value = useMemo(\r\n () => ({\r\n scope, task, isOpen, openTask, closeTask, toggleOpen, pendingGoal, startOrchestrator,\r\n pageContext, pageContextCard, setPageContext, scopedSubagentId, pendingScopedSubagentId,\r\n componentAnchors, anchoredUserMessages, registerComponentAnchor,\r\n openPanel, pendingExtraDestinations,\r\n pendingAttachments, pendingModel, orchestratorRunId, pendingCarryOver, commitEntityChat,\r\n resumeEntityChat, pendingResume, resumeRunId, dismissForeignTask,\r\n pendingManualEntry, manualEntryRunId, startManualEntry,\r\n pendingResumeWithAI, resumeWithAIRunId, resumeWithAI,\r\n startSubagent, pendingStart, startRunId,\r\n }),\r\n [scope, task, isOpen, openTask, closeTask, toggleOpen, pendingGoal, startOrchestrator, pageContext, pageContextCard, setPageContext, scopedSubagentId, pendingScopedSubagentId, componentAnchors, anchoredUserMessages, registerComponentAnchor, openPanel, pendingExtraDestinations, pendingAttachments, pendingModel, orchestratorRunId, pendingCarryOver, commitEntityChat, resumeEntityChat, pendingResume, resumeRunId, dismissForeignTask, pendingManualEntry, manualEntryRunId, startManualEntry, pendingResumeWithAI, resumeWithAIRunId, resumeWithAI, startSubagent, pendingStart, startRunId],\r\n );\r\n\r\n return (\r\n <AgenticoContext.Provider value={value}>\r\n {children}\r\n </AgenticoContext.Provider>\r\n );\r\n}\r\n\r\nexport function useAgentico() {\r\n const context = useContext(AgenticoContext);\r\n\r\n if (!context) {\r\n throw new Error('useAgentico must be used inside AgenticoProvider');\r\n }\r\n\r\n return context;\r\n}\r\n","import React, { useLayoutEffect, useRef, useState } from 'react';\nimport { Code, Icon } from '@llmnative/react';\nimport type { AgentActivityEntry } from './useAgent';\nimport { useAgenticoI18n, type AgenticoDict } from '../i18n';\n\n/** Un blocco ```lang ... ``` dentro al testo, o testo semplice attorno — il modello spesso\n * mescola un commento libero con un blocco di codice/JSON (vedi lo screenshot che ha portato\n * a questo componente: la risposta di un tool con un blocco ```json grezzo). */\ntype DetailSegment = { type: 'text'; content: string } | { type: 'code'; content: string; language?: string };\n\n/** Individua un blob JSON bilanciato dentro un pezzo di testo SENZA fence attorno (il modello a\n * volte lo dimentica del tutto sui turni di follow-up, non solo sul primo — bug segnalato: un\n * `{\"matches\": [...]}` grezzo comparso come muro di testo in chat, niente collapse, niente\n * evidenziazione). Stesso algoritmo a conteggio graffe/parentesi di extractJsonBlock (ignora\n * quelle dentro le stringhe) — soglia di lunghezza minima per non trattare come \"codice\" un\n * oggetto minuscolo citato di sfuggita in una frase. */\nfunction splitUnfencedJsonSegments(text: string): DetailSegment[] {\n const start = text.search(/[{[]/);\n if (start === -1) return [{ type: 'text', content: text }];\n\n const open = text[start];\n const close = open === '{' ? '}' : ']';\n let depth = 0;\n let inString = false;\n let escaped = false;\n let end = -1;\n for (let i = start; i < text.length; i++) {\n const ch = text[i];\n if (inString) {\n if (escaped) escaped = false;\n else if (ch === '\\\\') escaped = true;\n else if (ch === '\"') inString = false;\n continue;\n }\n if (ch === '\"') { inString = true; continue; }\n if (ch === open) depth++;\n else if (ch === close) { depth--; if (depth === 0) { end = i; break; } }\n }\n\n const candidate = end === -1 ? null : text.slice(start, end + 1);\n if (!candidate || candidate.length < 60) return [{ type: 'text', content: text }];\n try { JSON.parse(candidate); } catch { return [{ type: 'text', content: text }]; }\n\n const segments: DetailSegment[] = [];\n if (start > 0) segments.push({ type: 'text', content: text.slice(0, start) });\n segments.push({ type: 'code', content: candidate, language: 'json' });\n const rest = text.slice(end + 1);\n if (rest.trim()) segments.push(...splitUnfencedJsonSegments(rest));\n return segments;\n}\n\n/** Divide `detail` in segmenti testo/codice sui fence ```lang\\n...``` — non un parser\n * markdown completo, solo quel tanto che serve a non mostrare i backtick letterali all'utente\n * e a poter passare il contenuto del blocco al componente Code (evidenziazione + copia) del\n * framework invece che a un <p> piatto. Ogni segmento di testo risultante passa POI per\n * `splitUnfencedJsonSegments`, cosi un blob JSON senza fence attorno riceve comunque lo stesso\n * trattamento (collassato/espandibile) di uno fenced, invece di restare testo piatto. */\nfunction parseDetailSegments(text: string): DetailSegment[] {\n const segments: DetailSegment[] = [];\n const fenceRe = /```(\\w+)?\\n?([\\s\\S]*?)```/g;\n let lastIndex = 0;\n let match: RegExpExecArray | null;\n while ((match = fenceRe.exec(text))) {\n if (match.index > lastIndex) segments.push(...splitUnfencedJsonSegments(text.slice(lastIndex, match.index)));\n segments.push({ type: 'code', content: match[2].replace(/\\n$/, ''), language: match[1] });\n lastIndex = fenceRe.lastIndex;\n }\n if (lastIndex < text.length) segments.push(...splitUnfencedJsonSegments(text.slice(lastIndex)));\n return segments;\n}\n\n/** Blocco di codice nel log — SEMPRE visibile non appena la riga che lo contiene lo è (niente\n * doppio collapse: non serve un primo click solo per farlo apparire, quello era il problema\n * del vecchio testo ```json grezzo in riga). Il collapse che resta è solo quello proprio del\n * blocco: parte alto ~3-4 righe (evidenziazione Prism del framework), un clic lo espande a\n * un'altezza maggiore con scroll interno se il contenuto non ci sta comunque. Copia solo da\n * espanso — da collassato il bottone verrebbe tagliato dal max-height comunque. */\nfunction DetailCodeBlock({ content, language }: { content: string; language?: string }) {\n const [expanded, setExpanded] = useState(false);\n return (\n <div\n className={`relative cursor-pointer rounded-md ${expanded ? 'max-h-80 overflow-y-auto' : 'max-h-28 overflow-hidden'}`}\n onClick={() => setExpanded((v) => !v)}\n title={expanded ? 'Clic per comprimere' : 'Clic per espandere'}\n >\n <Code language={language as never} showCopy={expanded} background=\"transparent\" className=\"!text-[11px]\">\n {content}\n </Code>\n {!expanded && (\n <div className=\"pointer-events-none absolute inset-x-0 bottom-0 h-6 bg-gradient-to-t from-muted/30 to-transparent\" />\n )}\n </div>\n );\n}\n\n/** Un pezzo di testo inline riconosciuto dal mini-markdown qui sotto. */\ntype InlineToken =\n | { type: 'text'; content: string }\n | { type: 'bold'; content: string }\n | { type: 'code'; content: string }\n | { type: 'link'; content: string; href: string };\n\n/** Markdown inline minimo — **bold**, `code`, [link](url) — non un parser markdown completo\n * (stesso spirito di parseDetailSegments sopra): il modello a volte scrive prosa con questa\n * sintassi (riepiloghi, nomi di campo in bold, link alle fonti) e mostrarla letterale coi\n * simboli è peggio che non gestirla affatto. Niente liste/heading/tabelle: non servono in una\n * riga di log/riepilogo breve. */\nfunction parseInlineMarkdown(text: string): InlineToken[] {\n const tokens: InlineToken[] = [];\n const re = /\\*\\*(.+?)\\*\\*|`([^`]+)`|\\[([^\\]]+)\\]\\(([^)]+)\\)/g;\n let lastIndex = 0;\n let match: RegExpExecArray | null;\n while ((match = re.exec(text))) {\n if (match.index > lastIndex) tokens.push({ type: 'text', content: text.slice(lastIndex, match.index) });\n if (match[1] !== undefined) tokens.push({ type: 'bold', content: match[1] });\n else if (match[2] !== undefined) tokens.push({ type: 'code', content: match[2] });\n else if (match[3] !== undefined) tokens.push({ type: 'link', content: match[3], href: match[4] });\n lastIndex = re.lastIndex;\n }\n if (lastIndex < text.length) tokens.push({ type: 'text', content: text.slice(lastIndex) });\n return tokens;\n}\n\nfunction InlineMarkdown({ text }: { text: string }) {\n return (\n <>\n {parseInlineMarkdown(text).map((tok, i) => {\n if (tok.type === 'bold') return <strong key={i} className=\"font-semibold text-foreground\">{tok.content}</strong>;\n if (tok.type === 'code') return <code key={i} className=\"rounded bg-muted px-1 py-0.5 font-mono text-[11px] break-words\">{tok.content}</code>;\n if (tok.type === 'link') {\n return (\n <a key={i} href={tok.href} target=\"_blank\" rel=\"noreferrer\" className=\"text-primary underline underline-offset-2\">\n {tok.content}\n </a>\n );\n }\n return <React.Fragment key={i}>{tok.content}</React.Fragment>;\n })}\n </>\n );\n}\n\n/** Il contenuto di `detail` — testo semplice più eventuali blocchi di codice, ciascuno reso\n * col syntax-highlighter del framework invece che coi backtick grezzi in un <p>. */\nfunction DetailContent({ text }: { text: string }) {\n const segments = parseDetailSegments(text);\n return (\n <>\n {segments.map((seg, i) => seg.type === 'code'\n ? <DetailCodeBlock key={i} content={seg.content} language={seg.language} />\n : (seg.content.trim() && <p key={i} className=\"whitespace-pre-wrap break-words\"><InlineMarkdown text={seg.content} /></p>)\n )}\n </>\n );\n}\n\n/** Icona di stato di una voce di attività — icon-system del framework (Lucide), mai emoji.\n * \"running\" resta lo spinner a bordo rotante già usato da LoadingButton in tutto il CMS,\n * non un'icona Lucide: stessa identica animazione di ogni altro stato di caricamento. */\nfunction ActivityStatusIcon({ status }: { status: AgentActivityEntry['status'] }) {\n if (status === 'running') {\n return <span className=\"inline-block h-3 w-3 shrink-0 animate-spin rounded-full border-2 border-current border-t-transparent\" />;\n }\n if (status === 'done') return <Icon name=\"check\" size={12} className=\"shrink-0 text-green-600\" />;\n if (status === 'stopped') return <Icon name=\"circle-slash\" size={12} className=\"shrink-0 text-muted-foreground\" />;\n return <Icon name=\"x\" size={12} className=\"shrink-0 text-destructive\" />;\n}\n\n/** \"Sto pensando…\" è transitorio — SOLO mentre quel turno è `running`. Appena arriva la\n * risposta, la riga smette di dire \"sto pensando\" (che a quel punto sarebbe falso: ha già\n * finito). Nessuna anteprima-prima-riga qui: la prosa (se c'è) è sempre mostrata per intero\n * subito sotto (vedi ActivityRow), quindi un'etichetta che ne anticipi un pezzo sarebbe solo\n * la stessa frase ripetuta due volte a distanza di un rigo — esattamente il riepilogo del\n * micro-prompt \"closing-summary\" che, prima di questa versione, veniva sia troncato in\n * etichetta SIA ripetuto per intero subito sotto una volta espanso. */\nfunction thinkingDisplayLabel(entry: AgentActivityEntry, t: AgenticoDict): string {\n if (entry.status === 'running') return t.thinking;\n if (entry.status === 'error') return t.modelRequestError;\n if (entry.status === 'stopped') return t.stopped;\n if (!entry.detail) {\n // Il modello ha richiesto tool_calls senza scrivere testo libero — dice direttamente\n // lo scopo generale dell'azione (frasi già complete, vedi AgentTool.describeIntent)\n // invece del generico \"Richiesta di eseguire dei tool\". Nessun prefisso \"Eseguo:\"\n // aggiunto qui: ogni frase è già completa di suo, e i parametri della chiamata\n // specifica sono già sulla riga della tool call subito sotto — ripeterli qui sarebbe\n // di nuovo la stessa duplicazione che questo campo esiste per evitare.\n if (entry.toolNames && entry.toolNames.length > 0) return entry.toolNames.join(' · ');\n return t.toolRequest;\n }\n return t.responseReceived;\n}\n\n/** Una voce del log — le tool call sono una riga sola (etichetta già breve, niente da\n * espandere). I turni \"sto pensando\": codice E prosa (se presenti) sono SEMPRE visibili,\n * subito, mai dietro un click — nessun collapse a livello di riga. Due motivi, non uno solo:\n * (1) un dump JSON grezzo non ha \"ragionamento\" da nascondere; (2) il riepilogo del\n * micro-prompt \"closing-summary\" esiste PROPRIO perché l'utente lo legga — nasconderlo dietro\n * un chevron ne vanificherebbe lo scopo (il difetto segnalato: appariva un'etichetta\n * \"Riepilogo:\" da cliccare, che poi mostrava di nuovo \"Riepilogo: …\" per intero — la stessa\n * frase due volte, un click di troppo per qualcosa che dovrebbe essere già lì). La prosa vive\n * in un riquadro delimitato (bordo + sfondo, come un box di testo/uno \"snippet\"), con un\n * tetto di altezza e scroll interno solo se davvero molto lunga — mai nascosta, mai flottante\n * senza contenitore. I messaggi utente sono una bolla di chat allineata a destra — sempre per\n * intero — e \"sticky\" in cima allo scroll (stile IDE agentici: il prompt corrente resta\n * visibile mentre l'attività che genera scorre sotto). `sticky` su OGNI bolla utente, non solo\n * sull'ultima: è il normale comportamento a cascata degli elementi sticky (ogni bolla\n * successiva spinge via quella precedente non appena la raggiunge scorrendo) a garantire che\n * sia sempre e solo l'ultima a restare agganciata in un dato momento — nessuno stato/ref da\n * tracciare a mano. */\n/** Altezza da collassato, in px — abbastanza per ~4 righe a text-xs. Sopra questa soglia la\n * bolla utente collassa di default (fade + maniglia \"espandi\" sotto, stesso pattern di\n * DetailCodeBlock) invece di occupare tutto lo spazio verticale disponibile: la bolla è\n * `sticky top-0`, quindi un messaggio lungo resterebbe agganciato in cima schiacciando via\n * la vista sull'attività/risposta dell'AI che scorre sotto — esattamente il problema segnalato. */\nconst USER_BUBBLE_COLLAPSED_MAX_HEIGHT = 96;\n\n/** Bolla utente — collassabile SOLO se il testo eccede l'altezza di soglia (misurato via ref,\n * niente stima a priori sul numero di caratteri: dipende da wrap/allegati/lingua). L'unica zona\n * cliccabile per espandere/comprimere è la maniglia dedicata in fondo alla bolla (chevron),\n * MAI l'intera bolla — stesso motivo per cui ChatGPT/Claude fanno lo stesso: il testo dentro\n * deve restare selezionabile col mouse senza che un click/trascinamento apra o chiuda la bolla.\n * Non inventato da zero: è il pattern \"show more/less con maniglia separata\" già standard nei\n * chatbot conversazionali per i turni utente lunghi. */\nfunction UserMessageBubble({ entry }: { entry: AgentActivityEntry }) {\n const contentRef = useRef<HTMLDivElement>(null);\n const [expanded, setExpanded] = useState(false);\n const [overflowing, setOverflowing] = useState(false);\n\n useLayoutEffect(() => {\n const el = contentRef.current;\n if (!el) return;\n setOverflowing(el.scrollHeight > USER_BUBBLE_COLLAPSED_MAX_HEIGHT + 1);\n }, [entry.detail, entry.attachmentNames]);\n\n const collapsed = overflowing && !expanded;\n\n return (\n <li className=\"sticky top-0 z-10 flex justify-end border-b border-border/40 bg-background py-1.5\">\n <div className=\"max-w-[85%] rounded-lg bg-primary/10 text-xs text-foreground\">\n <div\n ref={contentRef}\n className={`space-y-1 px-2.5 pt-1.5 ${collapsed ? 'overflow-hidden' : ''}`}\n style={collapsed ? { maxHeight: USER_BUBBLE_COLLAPSED_MAX_HEIGHT } : undefined}\n >\n {entry.attachmentNames && entry.attachmentNames.length > 0 && (\n // Pillole SOPRA il testo, non dentro DetailContent — sono metadati del\n // messaggio (cosa era allegato), non parte del testo scritto dall'utente,\n // niente markdown/segmenti codice da applicargli.\n <div className=\"flex flex-wrap gap-1\">\n {entry.attachmentNames.map((name, i) => (\n <span key={i} className=\"inline-flex items-center gap-1 rounded-md bg-primary/15 px-1.5 py-0.5 text-[11px] text-foreground\">\n <Icon name=\"paperclip\" size={10} className=\"shrink-0\" />\n <span className=\"max-w-[160px] truncate\">{name}</span>\n </span>\n ))}\n </div>\n )}\n <DetailContent text={entry.detail ?? ''} />\n </div>\n {overflowing && (\n // Riquadro cliccabile a sé, non un overlay sopra il testo — un `padding`\n // generoso (non un filo di 1px) rende la maniglia facile da colpire senza\n // dover mirare con precisione, mantenendo comunque il testo sopra intoccato.\n <button\n type=\"button\"\n onClick={() => setExpanded((v) => !v)}\n className=\"relative flex w-full items-center justify-center rounded-b-lg py-1 text-muted-foreground hover:bg-primary/15\"\n title={expanded ? 'Comprimi' : 'Espandi'}\n >\n {collapsed && (\n <div className=\"pointer-events-none absolute inset-x-0 -top-4 h-4 bg-gradient-to-t from-primary/10 to-transparent\" />\n )}\n <Icon name={expanded ? 'chevron-up' : 'chevron-down'} size={12} />\n </button>\n )}\n </div>\n </li>\n );\n}\n\nfunction ActivityRow({ entry }: { entry: AgentActivityEntry }) {\n const t = useAgenticoI18n();\n if (entry.kind === 'user') return <UserMessageBubble entry={entry} />;\n\n const segments = entry.kind === 'thinking' && entry.detail ? parseDetailSegments(entry.detail) : [];\n const codeSegments = segments.filter((s): s is Extract<DetailSegment, { type: 'code' }> => s.type === 'code');\n const textSegments = segments.filter((s) => s.type === 'text' && s.content.trim());\n const label = entry.kind === 'thinking' ? thinkingDisplayLabel(entry, t) : entry.label;\n\n return (\n <li className=\"text-xs text-muted-foreground\">\n {/* `items-start`, non `items-center`: l'etichetta va a capo invece di troncarsi\n (niente `truncate`/ellissi), e le righe successive devono restare allineate\n sotto la prima, non sotto l'icona di stato — l'icona (larghezza fissa via\n shrink-0) e il testo (flex-1) restano comunque due colonne separate qualunque\n sia l'altezza del testo. */}\n <div className=\"flex items-start gap-1.5\">\n <span className=\"pt-px\"><ActivityStatusIcon status={entry.status} /></span>\n <span className=\"min-w-0 flex-1 whitespace-pre-wrap break-words\">{label}</span>\n </div>\n {codeSegments.length > 0 && (\n <div className=\"mt-1 ml-[18px] space-y-1\">\n {codeSegments.map((seg, i) => <DetailCodeBlock key={i} content={seg.content} language={seg.language} />)}\n </div>\n )}\n {textSegments.length > 0 && (\n // Testo libero, non incastonato in un box — stessa resa delle altre righe\n // (\"Sto ricontrollando…\" ecc.), niente bordo/sfondo/scroll proprio: scorre con\n // lo scroll principale del pannello, non con un secondo scroll interno.\n <div className=\"mt-1 ml-[18px] space-y-2\">\n {textSegments.map((seg, i) => (\n <p key={i} className=\"whitespace-pre-wrap leading-relaxed text-muted-foreground\">\n <InlineMarkdown text={seg.content.trim()} />\n </p>\n ))}\n </div>\n )}\n </li>\n );\n}\n\n/** Il log di attività di un agente, usato dal pannello globale (Agentico) — ogni agente ora\n * ci passa attraverso, il pannello embedded legacy (AgentFollowUp) è stato ritirato una volta\n * migrato anche l'ultimo consumer (import HTML/JSX, vedi ComponentEditPage.tsx). Un solo posto\n * per l'icon-system e la logica \"sto pensando\" transitorio/collassabile. */\nexport function AgentActivityLog({ activity }: { activity: AgentActivityEntry[] }) {\n if (activity.length === 0) return null;\n return (\n <ul className=\"space-y-0.5\">\n {activity.map((entry) => <ActivityRow key={entry.id} entry={entry} />)}\n </ul>\n );\n}\n","import React from 'react';\nimport { ActionButton } from '@llmnative/react';\nimport { useAgentico } from './AgenticoContext';\nimport { useAgenticoI18n } from '../i18n';\n\n/** L'unica icona che apre/chiude il pannello agente globale, Agentico (Agentico.tsx) — stile\n * icona-estensione-del-browser: sempre nello stesso angolo, sempre lo stesso significato, sia\n * che tu stia guardando il CMS a schermo pieno (nell'Header) sia che ci sia una modale right\n * aperta sopra (come `closeSlot` di quella Modal, al posto della sua ×). Un'unica istanza di\n * questo componente — mai una ricreata ad hoc — cosi il comportamento resta identico ovunque\n * compaia. `ActionButton` del framework (non un <button> a mano) per ereditare `cursor-pointer`\n * e lo stato hover/focus-visible già standard su ogni bottone del CMS dalla classe condivisa\n * `.btn` — solo sfondo/dimensione sono nostri via className, nessuna variante colore predefinita\n * si adatta a un'icona-toggle isolata come questa.\n *\n * Animata (icona pulsante) quando l'unico task attivo ha l'agente al lavoro — anche se il\n * pannello è chiuso, cosi si vede che sta succedendo qualcosa senza doverlo aprire apposta\n * (l'agente NON apre più da solo il pannello durante un run(), vedi AgenticoContext). */\nexport function AgenticoToggle({ className = '' }: { className?: string }) {\n const { isOpen, toggleOpen, task } = useAgentico();\n const t = useAgenticoI18n();\n const running = task?.agent.running ?? false;\n\n return (\n <ActionButton\n icon=\"bot\"\n ariaLabel={isOpen ? t.closePanel : t.openPanel}\n title={isOpen ? t.closePanel : t.openPanel}\n onClick={toggleOpen}\n iconClassName={running ? 'animate-pulse' : undefined}\n className={`!h-9 !w-9 !p-0 rounded-md ${\n isOpen ? 'bg-primary/10 text-primary hover:bg-primary/15' : 'bg-transparent text-muted-foreground hover:bg-accent hover:text-foreground'\n } ${className}`}\n />\n );\n}\n","// Estratto da useAgent.ts — la sola parte \"catalogo modelli disponibili per il tenant\",\n// riusabile anche da chi non ha (ancora) una sessione useAgent attiva, es.\n// AgenticoStarterComposer (Agentico.tsx): l'utente deve poter scegliere il modello ANCHE\n// prima che l'orchestratore parta, non solo nei turni di follow-up.\nimport { useEffect, useState } from 'react';\nimport { getAIModelCatalog, useAIProviderRegistry } from '@llmnative/react';\nimport type { AIModelDescriptor, AIProviderAdapter } from '@llmnative/react';\n\nexport interface UseAIModelCatalogResult {\n hasProviders: boolean;\n modelsByProvider: Record<string, AIModelDescriptor[]>;\n modelsLoading: boolean;\n selectedModel: string;\n setSelectedModel: (id: string) => void;\n}\n\n// Un solo modello selezionato per l'intera app (non per subagent/tab) — tutti gli usi\n// dell'hook (starter composer, sessione useAgent attiva, ...) devono vedere la stessa scelta,\n// quindi una chiave globale condivisa, non una per istanza.\nconst SELECTED_MODEL_STORAGE_KEY = 'llmnative-cms.selectedAIModel';\n\nfunction readStoredModel(): string {\n try {\n return localStorage.getItem(SELECTED_MODEL_STORAGE_KEY) ?? '';\n } catch {\n return ''; // storage non disponibile (privacy mode, ecc.) — nessuna persistenza, non un errore fatale\n }\n}\n\nfunction writeStoredModel(id: string): void {\n try {\n if (id) localStorage.setItem(SELECTED_MODEL_STORAGE_KEY, id);\n else localStorage.removeItem(SELECTED_MODEL_STORAGE_KEY);\n } catch {\n // idem — persistenza best-effort\n }\n}\n\nexport function useAIModelCatalog(): UseAIModelCatalogResult {\n const registry: Record<string, AIProviderAdapter> = useAIProviderRegistry()?.registry ?? {};\n const hasProviders = Object.keys(registry).length > 0;\n const [modelsByProvider, setModelsByProvider] = useState<Record<string, AIModelDescriptor[]>>({});\n const [modelsLoading, setModelsLoading] = useState(false);\n const [selectedModel, setSelectedModelState] = useState(readStoredModel);\n\n const setSelectedModel = (id: string) => {\n writeStoredModel(id);\n setSelectedModelState(id);\n };\n\n useEffect(() => {\n if (!hasProviders) { setModelsByProvider({}); return; }\n let cancelled = false;\n setModelsLoading(true);\n getAIModelCatalog(registry).then((catalog) => {\n if (cancelled) return;\n setModelsByProvider(catalog.modelsByProvider);\n const flat = Object.values(catalog.modelsByProvider).flat();\n // BUG FISSATO: prima, se la selezione corrente non era più nel catalogo, si\n // ripiegava sul `defaultModel` hardcoded del primo provider (o sul primo modello\n // qualunque) — un default scritto a mano nel file del provider framework, che\n // diventa obsoleto ogni volta che il catalogo dei modelli disponibili cambia\n // upstream (i modelli AI cambiano molto più spesso di quanto venga aggiornato il\n // codice). Nessun auto-fallback: se la selezione non è più valida, resta vuota —\n // l'utente è costretto a sceglierne uno esplicitamente dal menu (stesso principio\n // già in uso per il caso \"nessuna selezione ancora fatta\", vedi readyToStart in\n // OrchestratorRunner.tsx/SubagentRunner.tsx, che già gestiva selectedModel === '').\n // Stesso trattamento per il valore riletto da localStorage all'avvio: se il modello\n // salvato non esiste più nel catalogo (rimosso/rinominato upstream), si scarta e si\n // ripulisce anche lo storage — mai un ID stantio che farebbe fallire ogni richiesta\n // in silenzio finché l'utente non lo cambia a mano.\n setSelectedModelState((prev) => {\n if (flat.some((m) => m.id === prev)) return prev;\n writeStoredModel('');\n return '';\n });\n }).catch((error) => {\n // BUG FISSATO: questo catch azzerava modelsByProvider SENZA loggare nulla — un\n // fallimento qui (getAIModelCatalog rigettato) sembrava dal lato utente \"nessun\n // errore in console, tendina modello sparita\" senza alcuna pista per diagnosticarlo.\n // getAIModelCatalog ora isola i fallimenti per singolo provider (framework,\n // shared.ts), quindi questo ramo dovrebbe restare eccezionale — ma se scatta\n // comunque, deve almeno essere visibile.\n console.error('useAIModelCatalog: getAIModelCatalog failed', error);\n if (!cancelled) setModelsByProvider({});\n })\n .finally(() => { if (!cancelled) setModelsLoading(false); });\n return () => { cancelled = true; };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [hasProviders, JSON.stringify(Object.keys(registry).sort())]);\n\n return { hasProviders, modelsByProvider, modelsLoading, selectedModel, setSelectedModel };\n}\n","// Hook condiviso per ogni feature \"opt-in AI\" del CMS (import tema, import HTML/JSX, e i\r\n// prossimi) — un solo posto da correggere se il meccanismo cambia (vedi il bug del registry\r\n// sempre popolato risolto in react/src/providers/manifest.ts — prima avrebbe dovuto essere\r\n// corretto in due punti diversi di questo repo).\r\n//\r\n// `useAgent` è l'UNICO esecutore: consuma un `AgentDefinition` (../domain/agentDefinition) — il\r\n// chiamante non costruisce mai il prompt né chiama il provider direttamente. Il controller\r\n// dell'agente (`buildContext`) gira sempre, con o senza AI abilitata; `run()` ritorna il\r\n// fallback deterministico quando l'AI è spenta, non disponibile, o la sua risposta non è\r\n// valida — mai un errore non gestito per il chiamante.\r\n//\r\n// Conversazione multi-turno: `run()` avvia una sessione (controller + primo prompt),\r\n// `sendMessage()` la continua — l'utente affina la richiesta (\"controlla anche /about\",\r\n// \"riprova con questo markup\") e il modello può invocare i tool dell'agente (getTools) con i\r\n// parametri che l'utente ha suggerito, invece di dover essere ririchiamato da zero dal\r\n// chiamante. Il loop tool_use → execute → continua è interamente interno a questo hook.\r\nimport { useEffect, useRef, useState } from 'react';\r\nimport { parseAIModelRef, PromptUtils, useAIProviderRegistry } from '@llmnative/react';\r\nimport type { AIAttachment, AIConversationTurn, AIModelDescriptor, AIProviderAdapter, AIToolResult } from '@llmnative/react';\r\nimport type { AgentDefinition, AgentExecutorStep, AgentTool, LatestAttachmentsRef } from '../domain/agentDefinition';\r\nimport { extractErrorMessage } from '../domain/extractErrorMessage';\r\nimport { applyMicroPrompts } from '../domain/microPrompts';\r\nimport { toJsonSchema } from '../domain/mcpTool';\r\nimport { useAIModelCatalog } from './useAIModelCatalog';\r\nimport { getAgenticoPrompts } from '../domain/prompts';\r\nimport { useAgenticoI18n } from '../i18n';\r\n\r\n/** Numero massimo di round-trip tool_calls consecutivi in un singolo turno — argine contro\r\n * un modello che continua a invocare tool senza mai arrivare a una risposta testuale. Un\r\n * task legittimo (es. verificare 5-6 URL candidati per loghi/favicon) può richiederne\r\n * parecchi: il limite è un argine contro un loop infinito, non un tetto stretto al lavoro\r\n * reale — vedi il turno di chiusura forzata sotto per cosa succede quando lo si raggiunge\r\n * comunque con lavoro legittimo ancora in corso. */\r\nconst MAX_TOOL_ROUNDTRIPS = 20;\r\n\r\n/** Soglia oltre la quale un allegato testuale NON viene più incollato nel messaggio al\r\n * provider (né persistito in history, vedi attachmentsHistoryText sotto) — solo esclusa dalla\r\n * chiamata AI, resta comunque raggiungibile per intero da un tool via LatestAttachmentsRef\r\n * (vedi agentDefinition.ts). ~260KB di base64 ≈ ~195KB di testo decodificato: un CSV di\r\n * migliaia di righe (bug osservato: un file da 7MB mandava in 400 l'upstream, ben oltre\r\n * qualunque finestra di contesto) resta gestibile SOLO se un tool lo legge direttamente,\r\n * mai incollandolo per intero nel prompt. */\r\nconst MAX_INLINE_TEXT_ATTACHMENT_BASE64_LENGTH = 260_000;\r\n\r\n/** Allegati non testuali (immagini, ecc.) passano sempre — solo i testuali oltre soglia\r\n * vengono esclusi dal prompt/dalla history (vedi sopra), mai dal ref grezzo per i tool. */\r\nfunction filterInlineAttachments(attachments: AIAttachment[]): AIAttachment[] {\r\n return attachments.filter((a) => !PromptUtils.isTextAttachment(a.mimeType) || a.base64.length <= MAX_INLINE_TEXT_ATTACHMENT_BASE64_LENGTH);\r\n}\r\n\r\n/** Una voce del log di attività — o un messaggio dell'utente (il testo di sendMessage(),\r\n * mostrato subito com'è, mai \"sto pensando\" perché non è il modello a scriverlo), o una tool\r\n * call (etichetta SEMPRE deterministica/locale, vedi AgentTool.describeInvocation: mai testo\r\n * generato chiedendo al modello di narrare se stesso, name+input sono già strutturati nella\r\n * risposta del provider — stesso approccio degli IDE agentici, che renderizzano le tool call\r\n * dal client, non dal testo del modello), o un turno \"sto pensando\" (una chiamata al modello,\r\n * testo libero in `detail` — QUESTO è l'unico testo scritto davvero dal modello nel log,\r\n * collassato a una riga dal chiamante). */\r\nexport interface AgentActivityEntry {\r\n id: string;\r\n /** 'user' = il testo passato a sendMessage(), mostrato come bolla di chat (vedi\r\n * AgentActivityLog). 'thinking' = una chiamata al modello (etichetta transitoria, vedi\r\n * AgentActivityLog: \"Sto pensando…\" mentre gira, sostituita da un'anteprima di `detail` a\r\n * risposta arrivata — mai testo che persiste invariato). 'tool' = una tool call,\r\n * etichetta sempre statica (AgentTool.describeInvocation). */\r\n kind: 'user' | 'thinking' | 'tool';\r\n label: string;\r\n status: 'running' | 'done' | 'error' | 'stopped';\r\n /** Sui turni \"sto pensando\" — il testo che il modello ha scritto in quel turno (commento\r\n * prima di un tool_calls, o la risposta finale). Sui messaggi utente — il testo inviato\r\n * a sendMessage(), integrale (non troncato, non collassabile: è già quello che l'utente\r\n * ha scritto, non c'è \"ragionamento\" da nascondere dietro un collapse). */\r\n detail?: string;\r\n /** Solo sui turni \"sto pensando\" che hanno richiesto tool_calls SENZA testo libero\r\n * (`detail` assente) — le frasi discorsive (deterministiche, vedi AgentTool.describeIntent)\r\n * sullo SCOPO GENERALE dei tool che quel turno sta per invocare (mai i parametri della\r\n * chiamata specifica, quelli sono già sulla riga della tool call sotto — vedi\r\n * describeIntent per il perché) cosi la UI dice qualcosa di concreto invece del generico\r\n * \"Richiesta di eseguire dei tool\" (vedi thinkingDisplayLabel in AgentActivityLog.tsx). */\r\n toolNames?: string[];\r\n /** Solo su 'user' — nomi dei file allegati a QUESTO messaggio (run()/sendMessage()\r\n * opts.attachments), mostrati come pillole sopra il testo (vedi AgentActivityLog) — senza,\r\n * l'utente non ha modo di verificare \"ho davvero attaccato quel file?\" scorrendo indietro,\r\n * doveva fidarsi a memoria (bug osservato: un allegato sembrava \"sparito\" perché non\r\n * c'era traccia visiva che fosse mai arrivato). */\r\n attachmentNames?: string[];\r\n}\r\n\r\n/** Stato di gating/selezione — non generico, condiviso dal componente <AgentControls />\r\n * (che non ha bisogno di conoscere TParams/TResult, solo di disegnare switch + select). */\r\nexport interface AgentControlState {\r\n enabled: boolean;\r\n setEnabled: (v: boolean) => void;\r\n hasProviders: boolean;\r\n selectedModel: string;\r\n setSelectedModel: (id: string) => void;\r\n modelsByProvider: Record<string, AIModelDescriptor[]>;\r\n modelsLoading: boolean;\r\n}\r\n\r\nexport interface UseAgentResult<TParams, TResult> extends AgentControlState {\r\n running: boolean;\r\n error: string | null;\r\n /** true dopo che run() ha eseguito almeno un turno con AI abilitata — solo allora la\r\n * conversazione ha una history su cui sendMessage() può continuare. */\r\n canContinue: boolean;\r\n /** true when the turns go to `AgentDefinition.executor` (CR-003): no attachments, no\r\n * deterministic fallback; the models are the executor's (`remoteModels`), not the catalog. */\r\n remote: boolean;\r\n /** The models a remote executor offers (`AgentExecutor.models`); `selectedModel` is one of them. */\r\n remoteModels: Array<{ label: string; value: string; group?: string }>;\r\n /** Log delle tool call della sessione corrente, più recenti in fondo — azzerato a ogni\r\n * run(), accumulato attraverso i sendMessage() successivi. */\r\n activity: AgentActivityEntry[];\r\n /** Esegue il controller (sempre) e, se abilitato e disponibile un provider, il prompt\r\n * dell'agente. Ritorna `null` solo se il controller stesso fallisce (es. fetch di rete) —\r\n * in ogni altro caso ritorna almeno il fallback deterministico dell'agente. Avvia una\r\n * nuova sessione conversazionale (azzera la history di eventuali run precedenti).\r\n * `opts.attachments` — file allegati dall'utente al messaggio di apertura, convertiti in\r\n * `AIAttachment` (`PromptUtils.fileToAttachment`) e passati al provider — è il provider\r\n * stesso (react/src/providers/ai/*) a decidere se un mimetype testuale va decodificato e\r\n * inserito come blocco di testo o trattato come documento nativo (CR-071bis): questo hook\r\n * non fa più quella distinzione, per non duplicare una logica che deve restare unica e\r\n * per-provider dentro il framework. `opts.model` — sovrascrive\r\n * `selectedModel` SOLO per questa chiamata (mai una `setSelectedModel` seguita subito da\r\n * `run()`: lo stato React non sarebbe ancora committato nello stesso tick — vedi Chatbot\r\n * in Agentico.tsx, che riporta il modello scelto nel payload di ogni submit invece di\r\n * affidarsi a un `setSelectedModel` sincrono). `opts.openingMessage` — testo da mostrare\r\n * SUBITO come bolla \"Tu\" in cima all'activity log (stesso trattamento del testo di\r\n * sendMessage(), vedi sotto): senza, il messaggio che ha avviato la conversazione (es. il\r\n * goal scritto nel composer \"starter\" di Agentico, magari con un allegato) non appariva mai\r\n * nel log — solo i turni di sendMessage() successivi lo facevano, rendendo il primo\r\n * messaggio \"invisibile\" e facile da dimenticare fosse mai stato inviato (bug osservato).\r\n * Opzionale perché non ogni chiamante di run() è una chat (es. i subagent lanciati da\r\n * modale/form come theme/html-to-liquid non hanno un \"messaggio utente\" testuale da\r\n * mostrare). */\r\n run: (params: TParams, opts?: { attachments?: File[]; model?: string; openingMessage?: string }) => Promise<TResult | null>;\r\n /** Continua la conversazione avviata da run() con un messaggio dell'utente — il modello\r\n * può invocare i tool dell'agente con i parametri suggeriti nel messaggio. Richiede che\r\n * run() sia già stato eseguito con AI abilitata (altrimenti `error` viene impostato).\r\n * `opts` — stesso trattamento di run(), vedi sopra. */\r\n sendMessage: (text: string, opts?: { attachments?: File[]; model?: string }) => Promise<TResult | null>;\r\n /** Interrompe il turno in corso (run() o sendMessage()) — annulla la chiamata AI\r\n * attualmente in volo. Nessun effetto se non c'è nulla in corso. Le tool call già in\r\n * esecuzione in quel momento completano comunque (sono fetch brevi); il turno si ferma\r\n * al prossimo checkpoint (subito se si stava aspettando il modello, altrimenti dopo che\r\n * il round di tool in corso ha finito). */\r\n stop: () => void;\r\n /** Ricarica una sessione già esistente (vedi entityChatStore/AgenticoContext.resumeEntityChat)\r\n * invece di iniziarne una nuova — NON invia alcun prompt al modello, si limita a ricostruire\r\n * context/tools (via buildContext/getTools, come run()) e a installare `seed.history`/\r\n * `seed.activity` cosi un sendMessage() successivo continui esattamente come se la\r\n * conversazione non fosse mai stata interrotta. `params` deve avere la stessa forma con cui\r\n * la sessione originale era stata avviata (lo stesso TParams grezzo salvato nello snapshot). */\r\n resume: (params: TParams, seed: { history: AIConversationTurn[]; activity: AgentActivityEntry[] }) => Promise<void>;\r\n /** Esegue direttamente il fallback deterministico dell'agente, bypassando l'AI — per il\r\n * bottone \"Continua senza AI\" mostrato accanto a \"Riprova\" quando la chiamata al modello\r\n * fallisce (rete/provider giù, non una risposta non valida: quel caso ricade già da solo\r\n * sul fallback dentro runTurn). Richiede che run() sia già stato eseguito almeno una volta\r\n * in questa sessione (altrimenti non c'è `params`/`context` da cui derivarlo) — `null` se\r\n * chiamato prima. Sincrono: `AgentDefinition.fallback` non è mai una Promise (è sempre\r\n * derivato da `buildContext`, già risolto a questo punto). */\r\n runFallback: () => TResult | null;\r\n /** Foto corrente, serializzabile, della conversazione — `history` (turni grezzi\r\n * `AIConversationTurn[]`, dato puro senza funzioni/riferimenti al provider) più `activity`\r\n * (lo stesso log mostrato in UI). Usata da AgenticoContext.commitEntityChat per persistere\r\n * la chat che ha prodotto un'entità appena salvata (vedi entityChatStore) — a differenza di\r\n * `activity` da solo, `history` è ciò che serve per un vero resume (un nuovo giro può\r\n * continuare la conversazione col modello, non solo mostrarne il transcript in sola\r\n * lettura). `null` se run() non è mai stato eseguito in questa sessione. Una funzione, non\r\n * un valore memoizzato: deve leggere `sessionRef`/`activity` correnti al momento della\r\n * chiamata (tipicamente un click su Salva, molto dopo l'ultimo turno), mai una chiusura\r\n * stantia catturata al mount. */\r\n snapshot: () => { history: AIConversationTurn[]; activity: AgentActivityEntry[] } | null;\r\n}\r\n\r\n/** Solo la fetta di UseAgentResult che serve a un consumer del pannello globale (Agentico.tsx)\r\n * per continuare una conversazione già avviata da run() — TParams non compare in nessuno dei\r\n * membri qui sotto, quindi `never` è innocuo. */\r\nexport type AgentConversation<TResult> = Pick<UseAgentResult<never, TResult>, 'canContinue' | 'remote' | 'remoteModels' | 'running' | 'error' | 'activity' | 'sendMessage' | 'stop' | 'runFallback' | 'snapshot'>;\r\n\r\nexport function useAgent<TParams, TContext, TResult>(\r\n definition: AgentDefinition<TParams, TContext, TResult>,\r\n options: { defaultEnabled?: boolean } = {},\r\n): UseAgentResult<TParams, TResult> {\r\n const { defaultEnabled = false } = options;\r\n const [enabled, setEnabled] = useState(defaultEnabled);\r\n const { hasProviders, modelsByProvider, modelsLoading, selectedModel, setSelectedModel } = useAIModelCatalog();\r\n const [running, setRunning] = useState(false);\r\n const [error, setError] = useState<string | null>(null);\r\n const [canContinue, setCanContinue] = useState(false);\r\n const [activity, setActivity] = useState<AgentActivityEntry[]>([]);\r\n const labels = useAgenticoI18n();\r\n const labelsRef = useRef(labels);\r\n labelsRef.current = labels;\r\n\r\n // Stato della sessione conversazionale corrente — non ricalcolabile dal render (params/\r\n // context/tools di run() devono sopravvivere invariati fino al prossimo run()), quindi\r\n // ref e non state: sendMessage() li legge senza dover essere lui stesso il motivo del\r\n // render che li ha popolati.\r\n // `logId` — generato una sola volta per sessione (run()), non per turno: è quello che\r\n // permette al proxy dev di accodare request/response di TUTTI i turni della stessa\r\n // conversazione (run() + ogni sendMessage() successivo) allo stesso file di log (vedi\r\n // AICompleteRequest.logId e react/src/providers/proxy/vite.ts).\r\n // `attachmentsRef` — LatestAttachmentsRef (vedi agentDefinition.ts), creato una volta per\r\n // sessione e passato a getTools(): un tool come extract_csv_column lo legge a ogni\r\n // esecuzione, sempre aggiornato con gli allegati grezzi del turno CORRENTE (vedi runTurn).\r\n const sessionRef = useRef<{ params: TParams; context: TContext; tools: AgentTool[]; history: AIConversationTurn[]; logId: string; attachmentsRef: LatestAttachmentsRef } | null>(null);\r\n // Guardia di rientranza — un ref, non lo state `running`: lo stato React è aggiornato in\r\n // batch e non è garantito visibile sincronamente a una seconda chiamata di run()/\r\n // sendMessage() scattata nello stesso tick (es. doppio click prima del re-render).\r\n const runningRef = useRef(false);\r\n // Il turno in corso (se c'è) — stop() lo annulla. Un ref, non state: creato e distrutto\r\n // dentro runTurn, mai il motivo di un render.\r\n const abortControllerRef = useRef<AbortController | null>(null);\r\n const stop = () => { abortControllerRef.current?.abort(); };\r\n\r\n // Il catalogo modelli (fetch, selezione di default) vive in useAIModelCatalog — condiviso\r\n // con AgenticoStarterComposer (Agentico.tsx), che ne ha bisogno PRIMA che esista una\r\n // sessione useAgent. Qui serve solo il registry grezzo per risolvere provider/ref.\r\n const registry: Record<string, AIProviderAdapter> = useAIProviderRegistry()?.registry ?? {};\r\n\r\n const ref = enabled && selectedModel ? parseAIModelRef(selectedModel) : null;\r\n const provider = ref ? registry[ref.provider] : null;\r\n const executor = definition.executor;\r\n const remote = Boolean(executor);\r\n // The model of a remote executor, picked among its own options: a ref too, so a turn reads the\r\n // last pick even within the same render.\r\n const [remoteModels, setRemoteModels] = useState<UseAgentResult<TParams, TResult>['remoteModels']>([]);\r\n const [remoteModel, setRemoteModelState] = useState('');\r\n const remoteModelRef = useRef('');\r\n const setRemoteModel = (model: string) => {\r\n remoteModelRef.current = model;\r\n setRemoteModelState(model);\r\n };\r\n const offerRemoteModels = (params: TParams) => {\r\n setRemoteModels(executor?.models?.(params) ?? []);\r\n setRemoteModel(executor?.defaultModel?.(params) ?? '');\r\n };\r\n\r\n /** A turn of a remote agent (CR-003): the text of the person goes to the executor as typed,\r\n * its reply grows in one \"thinking\" entry and its steps appear above it, so the reply stays\r\n * the last thing in the log. No provider, no tool loop, no fallback. */\r\n const runRemoteTurn = async (text: string, first: boolean, model?: string): Promise<TResult | null> => {\r\n const session = sessionRef.current;\r\n if (!session || !executor) { setError(labelsRef.current.noActiveSession); return null; }\r\n const controller = new AbortController();\r\n abortControllerRef.current = controller;\r\n const thinkingId = crypto.randomUUID();\r\n setActivity((prev) => [...prev, { id: thinkingId, kind: 'thinking', label: labelsRef.current.thinking, status: 'running' }]);\r\n const setThinking = (status: AgentActivityEntry['status'], detail?: string) =>\r\n setActivity((prev) => prev.map((a) => (a.id === thinkingId ? { ...a, status, ...(detail !== undefined ? { detail } : {}) } : a)));\r\n const onStep = (step: AgentExecutorStep) => {\r\n const id = `step-${thinkingId}-${step.id}`;\r\n setActivity((prev) => {\r\n if (prev.some((a) => a.id === id)) return prev.map((a) => (a.id === id ? { ...a, label: step.label, status: step.status } : a));\r\n const at = prev.findIndex((a) => a.id === thinkingId);\r\n const entry: AgentActivityEntry = { id, kind: 'tool', label: step.label, status: step.status };\r\n return at < 0 ? [...prev, entry] : [...prev.slice(0, at), entry, ...prev.slice(at)];\r\n });\r\n };\r\n try {\r\n const reply = await executor.turn({\r\n params: session.params, context: session.context, text, first, signal: controller.signal,\r\n ...((model || remoteModelRef.current) ? { model: model || remoteModelRef.current } : {}),\r\n onText: (partial) => setThinking('running', partial),\r\n onStep,\r\n });\r\n setThinking('done', reply.text);\r\n session.history = [\r\n ...session.history,\r\n ...(text ? [{ role: 'user' as const, content: text }] : []),\r\n { role: 'assistant' as const, content: reply.text },\r\n ];\r\n return reply.result ?? null;\r\n } catch (err) {\r\n session.history = [...session.history, ...(text ? [{ role: 'user' as const, content: text }] : [])];\r\n if (controller.signal.aborted) {\r\n setThinking('stopped');\r\n return null;\r\n }\r\n setThinking('error');\r\n setError(extractErrorMessage(err));\r\n return null;\r\n } finally {\r\n abortControllerRef.current = null;\r\n }\r\n };\r\n\r\n /** Un turno: manda `userText` (eventualmente vuoto — vedi sotto) più la history/i tool\r\n * della sessione, esegue in loop i tool_calls richiesti dal modello finché non arriva un\r\n * turno testuale, poi ritorna il risultato parsato. `userText` vuoto significa \"nessun\r\n * testo nuovo, si continua solo perché il turno precedente era un tool_result\" — i\r\n * provider adapter (react/src/providers/ai/*) sanno omettere il turno finale in quel caso.\r\n * `aiAttachments` (allegati binari, già convertiti — vedi run()/sendMessage()) accompagna\r\n * SOLO la primissima chiamata al provider di questo turno: la history non ha un concetto\r\n * di allegato persistente per round-trip successivi, ed è comunque lì che vive il vero\r\n * messaggio dell'utente. `overrideModel` — vedi run()/sendMessage(): risolto localmente\r\n * qui (mai da `selectedModel` di stato, che potrebbe non aver ancora fatto commit). */\r\n const runTurn = async (userText: string, aiAttachments?: AIAttachment[], overrideModel?: string): Promise<TResult | null> => {\r\n const session = sessionRef.current;\r\n if (!session) { setError(labelsRef.current.noActiveSession); return null; }\r\n const effectiveRef = overrideModel ? parseAIModelRef(overrideModel) : ref;\r\n const effectiveProvider = effectiveRef ? registry[effectiveRef.provider] : null;\r\n if (!effectiveProvider || !effectiveRef) { setError(labelsRef.current.noProviderAvailable); return null; }\r\n\r\n // Il ref grezzo per i tool (vedi LatestAttachmentsRef) ACCUMULA gli allegati di TUTTA\r\n // la conversazione, non solo del turno corrente — sovrascriverlo a ogni turno faceva\r\n // sparire un allegato grande (che non è mai incollato in history, vedi sotto) non\r\n // appena l'utente scriveva un messaggio successivo SENZA riallegare nulla: il modello si\r\n // ritrovava a dover chiedere \"puoi riallegare il CSV?\" anche se l'utente l'aveva già\r\n // fornito due turni prima (bug osservato). Aggiornato PRIMA di filtrare per il provider:\r\n // un tool deve poter leggere anche ciò che al modello non arriva mai.\r\n if (aiAttachments?.length) {\r\n session.attachmentsRef.current = [...(session.attachmentsRef.current ?? []), ...aiAttachments];\r\n }\r\n\r\n // Gli allegati binari (aiAttachments) accompagnano SOLO la chiamata live del primo\r\n // round-trip (vedi sotto) — il provider li traduce nel blocco nativo giusto (image/\r\n // document/text) per QUESTA chiamata, ma AIConversationTurn non ha un campo \"allegato\"\r\n // persistente: senza questo, il contenuto del file spariva dalla history non appena il\r\n // turno finiva, rendendolo irraggiungibile a qualunque sendMessage() successivo (bug\r\n // osservato: \"gli old path sono quelli che ti ho allegato\" in un turno 3 non trovava più\r\n // nulla). Per i mimetype testuali (CSV/TXT/JSON/...) sotto soglia, il fix è incorporare\r\n // lo stesso testo decodificato DENTRO il contenuto persistito in history — da lì in\r\n // avanti è testo normale, sopravvive a ogni turno futuro esattamente come se l'utente\r\n // l'avesse scritto a mano. Sopra soglia (vedi MAX_INLINE_TEXT_ATTACHMENT_BASE64_LENGTH),\r\n // MAI incollato: un CSV da alcuni MB manderebbe in errore l'upstream per dimensione della\r\n // richiesta (bug osservato) — resta raggiungibile SOLO da un tool via attachmentsRef. MA\r\n // un marker SENZA contenuto va comunque persistito in history anche per questi — senza,\r\n // il modello non ha alcuna traccia testuale che un file sia stato allegato (bug osservato:\r\n // un CSV di traffico reale, quasi sempre sopra soglia, veniva richiesto di nuovo\r\n // all'utente in un turno successivo perché \"per il modello\" non era mai arrivato nulla —\r\n // l'unico segnale della sua esistenza era la chip UI dell'allegato, che non raggiunge mai\r\n // il prompt). I binari (immagini, PDF) restano turn-scoped: non c'è un modo sensato di\r\n // \"incorporarli come testo\" nella history.\r\n const textAttachments = (aiAttachments ?? []).filter((a) => PromptUtils.isTextAttachment(a.mimeType));\r\n const attachmentsHistoryText = textAttachments\r\n .map((a) => (a.base64.length <= MAX_INLINE_TEXT_ATTACHMENT_BASE64_LENGTH\r\n ? `\\n\\n[File allegato: ${a.name}]\\n${PromptUtils.decodeBase64Text(a.base64)}`\r\n : `\\n\\n[File allegato: ${a.name} — troppo grande per essere incollato qui. Usa uno dei tool di lettura file disponibili (es. list_csv_columns/extract_csv_column) per leggerne il contenuto prima di chiedere altro all'utente.]`))\r\n .join('');\r\n const inlineAttachments = filterInlineAttachments(aiAttachments ?? []);\r\n\r\n // t.inputSchema è Zod (sorgente unica) — il provider AI parla JSON Schema sul wire\r\n // come ogni function-calling API: questo è l'unico punto in cui la forma serializzata\r\n // serve davvero (vedi mcpTool.ts).\r\n const toolDefs = session.tools.map((t) => ({ name: t.name, description: t.description, inputSchema: toJsonSchema(t.inputSchema) }));\r\n const toolsByName = new Map(session.tools.map((t) => [t.name, t]));\r\n\r\n const controller = new AbortController();\r\n abortControllerRef.current = controller;\r\n\r\n /** Aggiorna una voce di activity per id — usata sia per il turno \"sto pensando\" di\r\n * ogni round sia per le singole tool call. */\r\n const setEntryStatus = (id: string, status: AgentActivityEntry['status'], detail?: string, toolNames?: string[]) =>\r\n setActivity((prev) => prev.map((a) => (a.id === id ? {\r\n ...a, status,\r\n ...(detail !== undefined ? { detail } : {}),\r\n ...(toolNames !== undefined ? { toolNames } : {}),\r\n } : a)));\r\n\r\n try {\r\n let history = session.history;\r\n let promptText = userText;\r\n\r\n for (let roundTrip = 0; roundTrip < MAX_TOOL_ROUNDTRIPS; roundTrip++) {\r\n // Un turno \"sto pensando\" per OGNI chiamata al modello, non solo per le tool\r\n // call — prima l'attesa della risposta AI (spesso la più lunga, specie tra un\r\n // round di tool e il successivo) non aveva nessun indicatore visibile.\r\n const thinkingId = crypto.randomUUID();\r\n setActivity((prev) => [...prev, { id: thinkingId, kind: 'thinking', label: labelsRef.current.thinking, status: 'running' }]);\r\n\r\n // provider.complete() può sia risolvere a `null` (fetch.ts inghiotte molti\r\n // errori di rete e li normalizza così) SIA rigettare (es. il dev-proxy\r\n // risponde con uno status non-ok quando la connessione a monte viene\r\n // interrotta da un abort — capita spesso con un errore \"CORS\"-shaped invece\r\n // di un pulito AbortError). Un try/catch qui è obbligatorio: senza, un throw\r\n // scavalca l'aggiornamento della voce \"sto pensando\", che resta bloccata su\r\n // \"running\" per sempre — esattamente il sintomo di un abort non gestito.\r\n let result;\r\n try {\r\n result = await effectiveProvider.complete({\r\n prompt: promptText,\r\n model: effectiveRef.model,\r\n history,\r\n tools: toolDefs.length ? toolDefs : undefined,\r\n signal: controller.signal,\r\n attachments: roundTrip === 0 ? inlineAttachments : undefined,\r\n logId: session.logId,\r\n });\r\n } catch (err) {\r\n // Must include the pending user turn, not just `history` as of the START of\r\n // this round — otherwise a failure on round 0 (the common case: provider\r\n // rate-limited/unavailable) leaves `session.history` at `[]`, and the next\r\n // sendMessage('Riprova.') from Agentico.tsx's retry button finds no trace of\r\n // the original goal or its orchestrator prompt. The model then sees only\r\n // \"Riprova.\" + the tool catalog, with nothing to retry — hence answering as\r\n // a fresh, contextless assistant instead of resuming the actual request.\r\n session.history = [\r\n ...history,\r\n ...(promptText ? [{ role: 'user' as const, content: promptText + attachmentsHistoryText }] : []),\r\n ];\r\n if (controller.signal.aborted) {\r\n // Niente setError qui: la voce di activity già dice \"Interrotto\r\n // dall'utente.\" con l'icona giusta — un secondo messaggio identico\r\n // sotto sarebbe una pura duplicazione.\r\n setEntryStatus(thinkingId, 'stopped');\r\n return definition.fallback(session.params, session.context);\r\n }\r\n setEntryStatus(thinkingId, 'error');\r\n setError(extractErrorMessage(err));\r\n return null;\r\n }\r\n // Persisti la history maturata FINORA (inclusi eventuali round-trip di tool già\r\n // eseguiti con effetti reali) prima di ritornare — altrimenti un sendMessage()\r\n // successivo riparte dalla history pre-turno, \"dimenticando\" tool call già\r\n // avvenute e rischiando di farle rieseguire dal modello. Include anche il turno\r\n // utente pendente di QUESTO round (stesso motivo del ramo catch sopra) — se\r\n // `result` risulta falsy sotto, questa è l'ultima history persistita prima del\r\n // return, e deve comunque contenere cosa l'utente ha chiesto in questo round.\r\n session.history = [\r\n ...history,\r\n ...(promptText ? [{ role: 'user' as const, content: promptText + attachmentsHistoryText }] : []),\r\n ];\r\n\r\n if (!result) {\r\n if (controller.signal.aborted) {\r\n setEntryStatus(thinkingId, 'stopped');\r\n return definition.fallback(session.params, session.context);\r\n }\r\n setEntryStatus(thinkingId, 'error');\r\n setError(labelsRef.current.noProviderResponse);\r\n return null;\r\n }\r\n\r\n if (result.type === 'text') {\r\n setEntryStatus(thinkingId, 'done', result.text);\r\n session.history = [\r\n ...history,\r\n ...(promptText ? [{ role: 'user' as const, content: promptText + attachmentsHistoryText }] : []),\r\n { role: 'assistant' as const, content: result.text },\r\n ];\r\n const parsed = definition.parseResponse(result.text, session.params, session.context);\r\n if (!parsed) {\r\n // `parseResponse` non riporta MAI il motivo esatto (ogni subagent ha il\r\n // proprio parser/schema — vedi es. parseRedirectsResponse, che logga già\r\n // l'errore di JSON.parse specifico prima di arrivare qui) — questo è il\r\n // log generico, comune a QUALUNQUE subagent, che risponde alla domanda\r\n // \"cosa ha scritto davvero il modello?\" senza dover riprodurre la\r\n // conversazione. Preview head/tail (non il testo intero, potenzialmente\r\n // enorme — es. centinaia di redirect) perché un troncamento upstream\r\n // (limite di token in output) si vede quasi sempre alla FINE della\r\n // risposta, non all'inizio.\r\n console.error(\r\n '[useAgent] parseResponse ha restituito null — la risposta del modello non è nel formato atteso da questo subagent. Uso il fallback deterministico.',\r\n {\r\n responseLength: result.text.length,\r\n responsePreview: result.text.length > 2000\r\n ? `${result.text.slice(0, 1000)}\\n…[troncato qui nel log, ${result.text.length} caratteri totali]…\\n${result.text.slice(-1000)}`\r\n : result.text,\r\n params: session.params,\r\n },\r\n );\r\n setError(labelsRef.current.invalidResponse);\r\n return definition.fallback(session.params, session.context);\r\n }\r\n return parsed;\r\n }\r\n\r\n // Etichette deterministiche (mai narrate dal modello) dei tool che sta per\r\n // invocare. Due testi distinti per due righe distinte, non lo stesso\r\n // riciclato — altrimenti il turno \"sto pensando\" e la tool call subito sotto\r\n // dicono LETTERALMENTE la stessa frase, una sopra l'altra (il difetto\r\n // segnalato: \"Eseguo: Ricontrollo https://…\" seguito da \"Ricontrollo\r\n // https://…\"): describeIntent (scopo generale, no parametri) per la riga \"sto\r\n // pensando\" quando non ha scritto testo libero; describeInvocation (con i\r\n // parametri della chiamata specifica) per la riga della singola tool call.\r\n const toolIntentLabels = result.toolCalls.map((call) => {\r\n const tool = toolsByName.get(call.name);\r\n return tool?.describeIntent ?? tool?.describeInvocation?.(call.input) ?? `Chiamata: ${call.name}`;\r\n });\r\n setEntryStatus(thinkingId, 'done', result.text || undefined, result.text ? undefined : toolIntentLabels);\r\n\r\n const toolResults: AIToolResult[] = await Promise.all(result.toolCalls.map(async (call) => {\r\n const tool = toolsByName.get(call.name);\r\n const label = tool?.describeInvocation?.(call.input) ?? `Chiamata: ${call.name}`;\r\n setActivity((prev) => [...prev, { id: call.id, kind: 'tool', label, status: 'running' }]);\r\n\r\n if (!tool) {\r\n setEntryStatus(call.id, 'error');\r\n return { toolCallId: call.id, name: call.name, output: `Tool sconosciuto: ${call.name}`, isError: true };\r\n }\r\n try {\r\n const output = await tool.execute(call.input);\r\n setEntryStatus(call.id, 'done');\r\n return { toolCallId: call.id, name: call.name, output };\r\n } catch (err) {\r\n setEntryStatus(call.id, 'error');\r\n return { toolCallId: call.id, name: call.name, output: extractErrorMessage(err), isError: true };\r\n }\r\n }));\r\n\r\n history = [\r\n ...history,\r\n ...(promptText ? [{ role: 'user' as const, content: promptText + attachmentsHistoryText }] : []),\r\n { role: 'assistant' as const, content: result.text, toolCalls: result.toolCalls },\r\n { role: 'tool_result' as const, results: toolResults },\r\n ];\r\n promptText = '';\r\n }\r\n\r\n // Budget esaurito ma il modello voleva ancora invocare tool: il lavoro fatto finora\r\n // (i risultati dei tool già eseguiti sono già in `history`) non va buttato via con\r\n // un errore secco — un ultimo turno SENZA tool disponibili forza una risposta\r\n // finale usando tutto quello raccolto fino a qui.\r\n const closingId = crypto.randomUUID();\r\n setActivity((prev) => [...prev, { id: closingId, kind: 'thinking', label: labelsRef.current.thinking, status: 'running' }]);\r\n let closingResult;\r\n try {\r\n closingResult = await effectiveProvider.complete({\r\n // Anche questo turno passa dai micro-prompt (vedi run()/sendMessage() sopra)\r\n // — altrimenti un run che esaurisce il budget di round-trip (tanti tool_calls\r\n // di fila, es. molti URL di font/asset da ricontrollare) salta il riepilogo\r\n // finale senza che nessuno se ne accorga: era esattamente questo il path che\r\n // lo perdeva, non un rifiuto del modello.\r\n prompt: applyMicroPrompts(\r\n getAgenticoPrompts().stopCallingTools,\r\n definition.microPromptIds,\r\n ),\r\n model: effectiveRef.model,\r\n history,\r\n signal: controller.signal,\r\n logId: session.logId,\r\n });\r\n } catch {\r\n closingResult = null;\r\n }\r\n if (closingResult?.type === 'text') {\r\n setEntryStatus(closingId, 'done', closingResult.text);\r\n session.history = [...history, { role: 'assistant', content: closingResult.text }];\r\n const parsed = definition.parseResponse(closingResult.text, session.params, session.context);\r\n if (parsed) return parsed;\r\n } else {\r\n setEntryStatus(closingId, controller.signal.aborted ? 'stopped' : 'error');\r\n session.history = history;\r\n }\r\n\r\n // Niente setError se interrotto dall'utente: la voce di activity già dice\r\n // \"Interrotto dall'utente.\" con l'icona giusta, un secondo messaggio sotto\r\n // sarebbe una pura duplicazione.\r\n if (!controller.signal.aborted) {\r\n setError(labelsRef.current.tooManyToolTurns);\r\n }\r\n return definition.fallback(session.params, session.context);\r\n } finally {\r\n abortControllerRef.current = null;\r\n }\r\n };\r\n\r\n const run = async (params: TParams, opts?: { attachments?: File[]; model?: string; openingMessage?: string }): Promise<TResult | null> => {\r\n if (runningRef.current) { setError(labelsRef.current.requestInProgress); return null; }\r\n runningRef.current = true;\r\n setError(null);\r\n // Stesso trattamento del testo di sendMessage() sotto — mostrato subito, prima di\r\n // aspettare la risposta, cosi il messaggio che ha avviato la conversazione non \"scompare\"\r\n // dal log (vedi il commento su opts.openingMessage in UseAgentResult.run).\r\n setActivity(opts?.openingMessage\r\n ? [{\r\n id: crypto.randomUUID(), kind: 'user', label: 'Tu', status: 'done', detail: opts.openingMessage,\r\n ...(opts.attachments?.length ? { attachmentNames: opts.attachments.map((f) => f.name) } : {}),\r\n }]\r\n : []);\r\n setRunning(true);\r\n try {\r\n const context = await definition.buildContext(params);\r\n const attachmentsRef: LatestAttachmentsRef = { current: undefined };\r\n const tools = definition.getTools?.(params, context, attachmentsRef) ?? [];\r\n sessionRef.current = { params, context, tools, history: [], logId: crypto.randomUUID(), attachmentsRef };\r\n\r\n if (remote) {\r\n setCanContinue(true);\r\n offerRemoteModels(params);\r\n return await runRemoteTurn(opts?.openingMessage ?? '', true);\r\n }\r\n\r\n const overrideModel = opts?.model || undefined;\r\n const hasProvider = overrideModel ? Boolean(registry[parseAIModelRef(overrideModel)?.provider ?? '']) : Boolean(provider);\r\n if (!enabled || !hasProvider) {\r\n setCanContinue(false);\r\n const fallbackResult = definition.fallback(params, context);\r\n // Stessa conferma di runFallback() (vedi describeFallback) — senza, un run con\r\n // AI disabilitata a priori (es. ManualEntryRunner) produrrebbe il risultato ma\r\n // zero riga di log a dirlo: indistinguibile da \"non è successo niente\" finché\r\n // non arriva panelExtra (bug della stessa famiglia di quello risolto in\r\n // runFallback, qui sul percorso \"mai nemmeno tentato\" invece che \"fallito\").\r\n setActivity((prev) => [...prev, {\r\n id: crypto.randomUUID(), kind: 'tool', status: 'done',\r\n label: definition.describeFallback?.(fallbackResult) ?? labelsRef.current.aiDisabledFallback,\r\n }]);\r\n return fallbackResult;\r\n }\r\n\r\n setCanContinue(true);\r\n const aiAttachments = opts?.attachments?.length\r\n ? await Promise.all(opts.attachments.map((file) => PromptUtils.fileToAttachment(file)))\r\n : undefined;\r\n const prompt = applyMicroPrompts(definition.buildPrompt(params, context), definition.microPromptIds);\r\n return await runTurn(prompt, aiAttachments, overrideModel);\r\n } catch (err) {\r\n setError(extractErrorMessage(err));\r\n return null;\r\n } finally {\r\n runningRef.current = false;\r\n setRunning(false);\r\n }\r\n };\r\n\r\n const resume = async (params: TParams, seed: { history: AIConversationTurn[]; activity: AgentActivityEntry[] }): Promise<void> => {\r\n if (runningRef.current) { setError(labelsRef.current.requestInProgress); return; }\r\n runningRef.current = true;\r\n setError(null);\r\n setActivity(seed.activity);\r\n // `running`, come in run() — buildContext() per theme rifà una vera fetch di rete\r\n // (extractSiteSnapshot): senza, `task.agent.running` resterebbe falsamente `false` per\r\n // tutta quella finestra, e qualunque logica che vi si affida (spinner del composer,\r\n // un dismiss/replace concorrente che si fida di \"running\" per non interrompere un\r\n // lavoro in corso) lo leggerebbe come già inattivo mentre sta ancora lavorando.\r\n setRunning(true);\r\n try {\r\n const context = await definition.buildContext(params);\r\n const attachmentsRef: LatestAttachmentsRef = { current: undefined };\r\n const tools = definition.getTools?.(params, context, attachmentsRef) ?? [];\r\n sessionRef.current = { params, context, tools, history: seed.history, logId: crypto.randomUUID(), attachmentsRef };\r\n if (remote) offerRemoteModels(params);\r\n setCanContinue(remote || (enabled && Boolean(provider)));\r\n } catch (err) {\r\n setError(extractErrorMessage(err));\r\n } finally {\r\n runningRef.current = false;\r\n setRunning(false);\r\n }\r\n };\r\n\r\n const sendMessage = async (text: string, opts?: { attachments?: File[]; model?: string }): Promise<TResult | null> => {\r\n if (runningRef.current) { setError(labelsRef.current.requestInProgress); return null; }\r\n setError(null);\r\n if (!text.trim()) { setError('Messaggio vuoto.'); return null; }\r\n const session = sessionRef.current;\r\n if (!session) { setError(labelsRef.current.noActiveSession); return null; }\r\n runningRef.current = true;\r\n setRunning(true);\r\n // Mostrato subito, prima di aspettare la risposta — è quello che l'utente ha appena\r\n // scritto, non c'è nulla da attendere per saperlo (a differenza del turno \"sto\r\n // pensando\" del modello, che invece parte vuoto e si riempie solo a risposta arrivata).\r\n setActivity((prev) => [...prev, {\r\n id: crypto.randomUUID(), kind: 'user', label: 'Tu', status: 'done', detail: text,\r\n ...(opts?.attachments?.length ? { attachmentNames: opts.attachments.map((f) => f.name) } : {}),\r\n }]);\r\n try {\r\n if (remote) return await runRemoteTurn(text, false, opts?.model || undefined);\r\n const aiAttachments = opts?.attachments?.length\r\n ? await Promise.all(opts.attachments.map((file) => PromptUtils.fileToAttachment(file)))\r\n : undefined;\r\n const prompt = applyMicroPrompts(definition.buildFollowUpPrompt(text, session.params, session.context), definition.microPromptIds);\r\n return await runTurn(prompt, aiAttachments, opts?.model || undefined);\r\n } catch (err) {\r\n setError(extractErrorMessage(err));\r\n return null;\r\n } finally {\r\n runningRef.current = false;\r\n setRunning(false);\r\n }\r\n };\r\n\r\n const runFallback = (): TResult | null => {\r\n const session = sessionRef.current;\r\n if (!session) return null;\r\n setError(null);\r\n const result = definition.fallback(session.params, session.context);\r\n // kind 'tool', non 'thinking' — thinkingDisplayLabel (AgentActivityLog.tsx) ricalcola\r\n // sempre la sua etichetta da status/detail per i turni \"sto pensando\" (mai quella\r\n // passata qui), mentre le righe 'tool' mostrano `label` verbatim: esattamente la resa\r\n // semplice \"icona + una frase\" che serve per una singola riga di log deterministica.\r\n // `describeFallback` (se l'agente lo implementa) sostituisce l'etichetta generica con\r\n // una specifica al risultato appena prodotto — vedi il commento su\r\n // AgentDefinition.describeFallback per il perché serve DAVVERO per l'orchestratore\r\n // (senza, il suo messaggio di fallback sparirebbe senza che l'utente lo veda mai).\r\n setActivity((prev) => [...prev, {\r\n id: crypto.randomUUID(), kind: 'tool', status: 'done',\r\n label: definition.describeFallback?.(result) ?? labelsRef.current.continueWithoutAiFallback,\r\n }]);\r\n return result;\r\n };\r\n\r\n // Non un useCallback — deve chiudere sempre su `activity`/`sessionRef` del render CORRENTE,\r\n // letta però solo quando qualcuno la invoca davvero (un salvataggio, evento raro innescato\r\n // dall'utente, non un percorso caldo): niente da memoizzare, il costo di ricrearla ad ogni\r\n // render è trascurabile.\r\n const snapshot = (): { history: AIConversationTurn[]; activity: AgentActivityEntry[] } | null => (\r\n sessionRef.current ? { history: sessionRef.current.history, activity } : null\r\n );\r\n\r\n return {\r\n enabled, setEnabled, hasProviders,\r\n // A remote executor picks among its own models, not the AI catalog of the app.\r\n selectedModel: remote ? remoteModel : selectedModel,\r\n setSelectedModel: remote ? setRemoteModel : setSelectedModel,\r\n modelsByProvider, modelsLoading, running, error, canContinue, remote, remoteModels, activity, run, resume, sendMessage, stop, runFallback, snapshot,\r\n };\r\n}\r\n","// Prompt + parsing per l'orchestratore Agentico — vedi\n// ./agenticoOrchestratorAgent.ts per l'AgentDefinition che li usa.\n//\n// A differenza di theme/html-to-liquid, qui non c'è un contratto JSON da far rispettare: il\n// modello o continua a conversare in prosa libera (per raccogliere parametri) o invoca uno dei\n// tool generati dal registry (../subagentRegistry.ts) — la vera \"risposta\n// strutturata\" arriva dalla tool call, non dal testo. Vedi agenticoOrchestratorAgent.ts per come\n// il tool eseguito scrive nel context (`handoff`) che parseOrchestratorResponse legge qui.\nimport { getAgenticoPrompts } from '../prompts';\n\nexport interface OrchestratorHandoff {\n subagentId: string;\n command: string;\n params: Record<string, unknown>;\n}\n\nexport type OrchestratorResult =\n | { kind: 'chat'; message: string }\n | ({ kind: 'handoff' } & OrchestratorHandoff);\n\nexport function describeOrchestratorResponseContract(): string {\n const prompts = getAgenticoPrompts();\n return [prompts.responseRulesHeading, ...prompts.responseRules.map((rule) => `- ${rule}`)].join('\\n');\n}\n\nexport function buildOrchestratorPrompt(userGoal: string, cliCatalog: string, pageContext?: string | null): string {\n const prompts = getAgenticoPrompts();\n const pageContextSection = pageContext\n ? `\\n${prompts.pageContextHeading}\\n${prompts.pageContextIntro}\\n${pageContext}\\n`\n : '';\n\n return `${prompts.orchestratorIntro}\n\n${prompts.toolsHeading}\n${cliCatalog}\n${pageContextSection}\n${describeOrchestratorResponseContract()}\n\n${prompts.userRequestHeading}\n${userGoal}`;\n}\n\n/** `handoff` arriva dal context della sessione (settato dall'execute() del tool invocato in\n * questo turno, vedi agenticoOrchestratorAgent.ts) — se presente ha sempre priorità sul testo\n * libero del modello, che a quel punto è solo un commento discorsivo sul tool appena invocato. */\nexport function parseOrchestratorResponse(raw: string, handoff: OrchestratorHandoff | null): OrchestratorResult {\n if (handoff) return { kind: 'handoff', ...handoff };\n return { kind: 'chat', message: raw };\n}\n","// AgentDefinition of the Agentico orchestrator — see ../agentDefinition.ts for the contract. Its\r\n// \"result\" is never used by the user directly: either conversation text (kind: 'chat', still\r\n// collecting parameters) or a handoff (kind: 'handoff') that the caller (Agentico.tsx) turns into\r\n// a SubagentRunner on the chosen subagent.\r\n//\r\n// Agentico knows no concrete subagent or tool: the handoff tools are generated from the registry\r\n// (registerSubagents) and the data-collection tools come from the host (registerCrossAgentTools).\r\nimport { interpolate, proxyFetch } from '@llmnative/react';\r\nimport type { AgentDefinition, AgentTool } from '../agentDefinition';\r\nimport { getSubagentRegistry, findSubagentManifest } from '../subagentRegistry';\r\nimport { renderRegistryCli } from '../cli/renderManifestCli';\r\nimport { createCrossAgentTools } from '../crossAgentTools';\r\nimport { getAgenticoPrompts } from '../prompts';\r\nimport {\r\n buildOrchestratorPrompt,\r\n describeOrchestratorResponseContract,\r\n parseOrchestratorResponse,\r\n type OrchestratorHandoff,\r\n type OrchestratorResult,\r\n} from './prompt';\r\n\r\nexport interface OrchestratorAgentParams {\r\n /** The opening message — the user's free request, or a goal pre-seeded from a subagent card. */\r\n userGoal: string;\r\n /** \"Where the user is now\" (see useAgenticoPageContext), captured once at run(). */\r\n pageContext: string | null;\r\n /** Subagent the starting page is pinned to, `null` for a generic chat. Restricts the\r\n * cross-agent tools to the ones that subagent declares (SubagentManifest.crossAgentToolIds). */\r\n scopedSubagentId: string | null;\r\n}\r\n\r\nexport interface OrchestratorAgentContext {\r\n cliCatalog: string;\r\n /** Written by the execute() of the handoff tool invoked in this turn and read by parseResponse:\r\n * the only way a tool call outcome reaches parseResponse, which only sees the final text.\r\n * Reset every turn — a handoff is valid only for the turn that invoked it. */\r\n handoff: { current: OrchestratorHandoff | null };\r\n /** Complete outputs of the cross-agent tools of this conversation (see crossAgentTools.ts). */\r\n collected: Record<string, unknown>;\r\n}\r\n\r\n/** User-facing labels of the orchestrator (from the `agentico` i18n namespace). */\r\nexport interface OrchestratorLabels {\r\n /** Chat message used when the AI is not available. */\r\n unavailable: string;\r\n /** Log line of \"continue without AI\". */\r\n continueWithoutAi: string;\r\n /** Tool call label while a handoff is prepared — `{label}` = subagent label. */\r\n preparing: string;\r\n /** \"Thinking\" label of the turn that requested the handoff — `{label}` = subagent label. */\r\n collecting: string;\r\n}\r\n\r\n/** The subagents the orchestrator offers: all of them, but one for a page only (onlyWhenAnchored,\r\n * CR-003) only when the conversation is scoped to it — in its catalog and in its tools alike. */\r\nfunction offeredSubagents(scopedSubagentId: string | null) {\r\n return getSubagentRegistry().filter((manifest) => !manifest.onlyWhenAnchored || scopedSubagentId === manifest.id);\r\n}\r\n\r\nexport function createOrchestratorAgentDefinition(\r\n labels: OrchestratorLabels,\r\n): AgentDefinition<OrchestratorAgentParams, OrchestratorAgentContext, OrchestratorResult> {\r\n return {\r\n buildContext(params) {\r\n return {\r\n cliCatalog: renderRegistryCli(offeredSubagents(params.scopedSubagentId)),\r\n handoff: { current: null },\r\n collected: {},\r\n };\r\n },\r\n\r\n buildPrompt({ userGoal, pageContext }, { cliCatalog }) {\r\n return buildOrchestratorPrompt(userGoal, cliCatalog, pageContext);\r\n },\r\n\r\n buildFollowUpPrompt(userText, _params, { cliCatalog }) {\r\n return `${userText}\\n\\n${describeOrchestratorResponseContract()}\\n\\n${getAgenticoPrompts().toolsHeading}\\n${cliCatalog}`;\r\n },\r\n\r\n parseResponse(raw, _params, context) {\r\n const handoff = context.handoff.current;\r\n context.handoff.current = null; // consumed — must not survive into the next turn\r\n return parseOrchestratorResponse(raw, handoff);\r\n },\r\n\r\n fallback() {\r\n return { kind: 'chat', message: labels.unavailable };\r\n },\r\n\r\n // The fallback is never a real handoff (it cannot guess the subagent without AI), only a\r\n // message to read: without this hook \"continue without AI\" would show nothing.\r\n describeFallback(result) {\r\n return result.kind === 'chat' ? result.message : labels.continueWithoutAi;\r\n },\r\n\r\n // One handoff tool per (subagent, command) of the registry, generated — registering a\r\n // subagent makes it invocable here. execute() does NOT run the subagent (that would nest a\r\n // multi-turn AI conversation in a tool call): it only stores the parameters for parseResponse.\r\n getTools(params, context, attachmentsRef): AgentTool[] {\r\n const allCrossAgentTools = createCrossAgentTools({ attachmentsRef, collected: context.collected, proxyFetch });\r\n\r\n // A conversation pinned to a subagent only sees the cross-agent tools it declares; a\r\n // generic chat (not yet scoped) sees them all, since the target is still unknown.\r\n const scopedManifest = params.scopedSubagentId ? findSubagentManifest(params.scopedSubagentId) : null;\r\n const crossAgentTools = scopedManifest\r\n ? allCrossAgentTools.filter((tool) => (scopedManifest.crossAgentToolIds ?? []).includes(tool.name))\r\n : allCrossAgentTools;\r\n\r\n const handoffTools: AgentTool[] = offeredSubagents(params.scopedSubagentId).flatMap((manifest) =>\r\n manifest.commands.map((command) => ({\r\n name: `${manifest.id}_${command.name}`,\r\n description: `${manifest.label}: ${command.summary}`,\r\n inputSchema: command.inputSchema,\r\n describeInvocation: () => interpolate(labels.preparing, { label: manifest.label }),\r\n describeIntent: interpolate(labels.collecting, { label: manifest.label }),\r\n async execute(input) {\r\n // What the cross-agent tools collected always wins over what the model copied.\r\n const handoffParams = manifest.withCollected ? manifest.withCollected({ ...input }, context.collected) : input;\r\n context.handoff.current = { subagentId: manifest.id, command: command.name, params: handoffParams };\r\n // NEVER `{ ready: true }`-like: a model seeing \"ready\" narrates a finished\r\n // result in this same turn, before the real work (mounted after this turn)\r\n // has even started. The payload must read unmistakably as \"queued\".\r\n return { status: 'queued', note: interpolate(getAgenticoPrompts().handoffQueued, { label: manifest.label }) };\r\n },\r\n })),\r\n );\r\n\r\n return [...crossAgentTools, ...handoffTools];\r\n },\r\n };\r\n}\r\n","import type { SubagentManifest } from '../domain/subagentManifest';\n\n/** Etichetta breve mostrata nell'header di Agentico/nello storico chat per un task — Agentico\n * stesso non conosce la forma di TParams di nessun subagent specifico (nessun `params.url`/\n * `params.siteHostname` hard-codato qui): ogni manifest dichiara il proprio\n * `deriveHistoryTitle` se ha un campo sensato da mostrare (vedi\n * SubagentManifest.deriveHistoryTitle), altrimenti si ripiega sul nome generico del subagent\n * (`manifest.label`) — mai un tentativo di indovinare quale campo di `params` sia \"il titolo\".\n * File a sé (non dentro SubagentRunner.tsx/ResultDestinationPicker.tsx) perché entrambi lo\n * consumano — tenerlo in uno dei due avrebbe creato un import circolare tra loro. */\nexport function deriveHistoryTitle(manifest: SubagentManifest<any, any, any>, params: Record<string, unknown>): string {\n return manifest.deriveHistoryTitle?.(params) ?? manifest.label;\n}\n","import React, { useState } from 'react';\nimport { useProxy } from '@llmnative/react';\nimport type { SubagentManifest } from '../domain/subagentManifest';\nimport { getDestinationRegistry } from '../domain/destinationRegistry';\nimport { JSON_EXPORT_DESTINATION_ID, jsonExportDestination } from '../domain/jsonExportDestination';\nimport type { ResultDestination } from '../domain/resultDestination';\nimport { interpolate } from '@llmnative/react';\nimport { useAgenticoI18n } from '../i18n';\nimport { useAgentico } from './AgenticoContext';\nimport { deriveHistoryTitle } from './deriveHistoryTitle';\n\n/** Mostrata da SubagentRunner una volta che il subagent ha prodotto un risultato — un bottone\n * per destinazione dichiarata dal manifest (`destinationIds`) più \"Esporta come JSON\", sempre\n * disponibile per qualunque risultato (vedi jsonExportDestination). Le destinazioni tenant-scoped\n * (theme, component-library) scrivono SOLO nel tenant attualmente attivo — vedi\n * DestinationContext, nessuna scelta di un tenant diverso qui (decisione esplicita, vedi piano). */\nexport function ResultDestinationPicker({\n manifest, result, meta, taskId, initiallyApplied, onApplied, extraDestinations,\n}: {\n manifest: SubagentManifest<any, any, any>;\n result: unknown;\n meta: Record<string, unknown>;\n /** Id del task Agentico che ha prodotto questo risultato — passato a `commitEntityChat`\n * quando un apply esplicito (click qui) restituisce un `id` (vedi sotto). Assente = nessuna\n * persistenza dello storico per questo click (nessun task noto da cui pescare uno snapshot),\n * comportamento invariato per un eventuale chiamante futuro senza un task Agentico dietro. */\n taskId?: string;\n /** Destinazioni già applicate in precedenza (es. riaprendo un'esecuzione dallo storico) —\n * mostrate subito come spuntate invece di far credere che vadano riapplicate da zero. */\n initiallyApplied?: string[];\n /** Chiamato dopo un apply riuscito — usato da SubagentRunner per aggiornare la voce di\n * storico (appliedDestinationIds), assente quando il chiamante non ne ha bisogno (es. una\n * destinazione già storicizzata che si vuole solo ri-esportare). */\n onApplied?: (destinationId: string) => void;\n /** Destinazioni scoped alla pagina/istanza corrente (es. \"Applica a questo Site aperto\"),\n * mai registrate nel destinationRegistry condiviso perché chiudono su stato React di UNA\n * pagina specifica — mostrate accanto a quelle del registry, non al loro posto. */\n extraDestinations?: ResultDestination[];\n}) {\n const t = useAgenticoI18n();\n const sidebar = useAgentico();\n const scope = sidebar.scope;\n const proxyFetch = useProxy();\n const [pending, setPending] = useState<string | null>(null);\n const [done, setDone] = useState<Set<string>>(new Set(initiallyApplied ?? []));\n const [error, setError] = useState<string | null>(null);\n\n // Mappa per id, non una semplice concatenazione: un extraDestination con lo STESSO id di\n // una destinazione del registry la SOVRASCRIVE nella stessa posizione invece di duplicarla —\n // utile a una pagina che vuole avvolgere una destinazione generica con un'informazione\n // scoped a sé (es. \"collega anche al Site aperto\") che il registry tenant-wide non può\n // conoscere da solo.\n const registry = getDestinationRegistry();\n const destinationMap = new Map<string, ResultDestination>();\n manifest.destinationIds.forEach((id: string) => { const d = registry[id]; if (d) destinationMap.set(id, d); });\n (extraDestinations ?? []).forEach((d) => destinationMap.set(d.id, d));\n destinationMap.set(jsonExportDestination.id, jsonExportDestination);\n const destinations = Array.from(destinationMap.values());\n\n const run = async (destinationId: string) => {\n setPending(destinationId);\n setError(null);\n try {\n const destination = destinationMap.get(destinationId);\n if (!destination) throw new Error(interpolate(t.unknownDestination, { id: destinationId }));\n if (destinationId !== JSON_EXPORT_DESTINATION_ID && !scope) {\n throw new Error(t.destinationNoScope);\n }\n const outcome = await destination.apply(result, {\n scopeId: scope?.id ?? '',\n // json-export (the only destination allowed without a scope) never reads it.\n dataProvider: scope?.dataProvider as NonNullable<typeof scope>['dataProvider'],\n storageProvider: scope?.storageProvider ?? null,\n proxyFetch,\n }, meta);\n setDone((prev) => new Set(prev).add(destinationId));\n onApplied?.(destinationId);\n // Un apply riuscito CON un id è, per definizione, l'entità che questo click ha\n // scritto — stessa persistenza dello storico già fatta per l'auto-apply\n // (SubagentRunner.tsx), qui per il caso \"l'utente ha cliccato lui stesso il\n // bottone\" (es. \"Applica a questo Component\" di html-to-liquid, che non ha\n // autoApplyDestinationIds): senza questo, quella chat non finiva MAI nello storico\n // (bug segnalato — solo theme/redirects, che la persistono per conto proprio,\n // comparivano).\n if (outcome?.id) {\n sidebar.commitEntityChat(manifest.id, outcome.id, taskId, scope?.id, meta, deriveHistoryTitle(manifest, meta));\n }\n } catch (err) {\n setError(err instanceof Error ? err.message : String(err));\n } finally {\n setPending(null);\n }\n };\n\n return (\n <div className=\"space-y-2\">\n <p className=\"text-xs font-medium text-muted-foreground\">{t.destinationPickerTitle}</p>\n <div className=\"flex flex-wrap gap-2\">\n {destinations.map((destination) => (\n <button\n key={destination.id}\n type=\"button\"\n disabled={pending !== null}\n onClick={() => void run(destination.id)}\n className=\"cursor-pointer rounded-md border border-input bg-background px-2.5 py-1.5 text-xs font-medium transition-colors hover:bg-accent hover:text-accent-foreground disabled:cursor-not-allowed disabled:opacity-60\"\n >\n {pending === destination.id ? t.destinationApplying : `${done.has(destination.id) ? '✓ ' : ''}${destination.id === JSON_EXPORT_DESTINATION_ID ? t.exportJson : destination.label}`}\n </button>\n ))}\n </div>\n {error && <p className=\"text-xs text-destructive\">{error}</p>}\n </div>\n );\n}\n","import type { ComponentType } from 'react';\n\n/** Booleano = includi/escludi (colori, font — un solo valore risolto per chiave, niente tra cui\n * scegliere). Stringa = candidato scelto per uno slot con più opzioni recuperate (logo/favicon/\n * OG image — '' significa \"Nessuno\"). Vedi LiquidResultReport per l'uso concreto (theme non ha\n * più un report qui: il risultato popola direttamente il ThemeEditor aperto, vedi\n * SubagentRunner.componentAnchors — nessuna selezione intermedia). */\nexport type SubagentSelection = Record<string, boolean | string>;\n\n/** Un subagent \"a selezione\" (branding, html-to-liquid): il risultato è generato UNA volta,\n * l'utente sceglie solo quali campi/candidati includere prima di spedirlo a una destinazione —\n * mai una modifica diretta del contenuto del risultato. */\nexport interface SelectionReportModule<TResult = any> {\n kind: 'selection';\n Report: ComponentType<{\n result: TResult;\n selection: SubagentSelection;\n onChange: (key: string, value: boolean | string) => void;\n }>;\n /** Solo le chiavi per campi effettivamente presenti nel risultato — l'assenza di una chiave\n * significa \"nessuna checkbox per questo campo\", non \"deselezionato\". Parte sempre a true\n * (comportamento opt-out, come il vecchio flusso di import pre-AI). */\n defaultSelection(result: TResult): SubagentSelection;\n /** Copia di result con i campi deselezionati azzerati/filtrati — usata SOLO per alimentare\n * ResultDestinationPicker, mai per il Report stesso (che mostra sempre il result grezzo +\n * stato checkbox, altrimenti un campo deselezionato sparirebbe invece di restare spuntabile). */\n applySelection(result: TResult, selection: SubagentSelection): TResult;\n}\n\n/** Un subagent \"editabile\": il risultato STESSO è l'oggetto che l'utente continua a modificare\n * nel tempo (righe di una griglia, non un opt-in/opt-out su campi noti a priori) — non c'è nulla\n * da \"filtrare\" prima di una destinazione. Il `Report` di un modulo così presuppone SEMPRE un\n * `<Form>` vivo intorno a sé (i suoi campi editabili sono `Input`/`Select` legati a\n * `useFormContext`) — per questo NON va registrato qui se quel Form lo possiede già un componente\n * component-anchor sempre montato (es. redirects: `RedirectsEditor` monta il proprio Report\n * dentro il proprio `<Form>` con un import diretto, mai tramite questo registro). Registrarlo qui\n * lo esporrebbe ANCHE al fallback \"nessun anchor vivo\" di SubagentRunner (vedi sotto), che lo\n * renderizza SENZA alcun Form intorno — crash reale osservato (`useFormContext must be used\n * within a FormContext.Provider`, root-caused a redirects registrato qui mentre l'utente naviga\n * via dall'editor senza salvare: un task ancora vivo con un risultato in sospeso, nessun anchor\n * per riceverlo, fallback che monta il Report nudo). Un modulo \"editabile\" appartiene qui SOLO se\n * il subagent NON garantisce un componente-anchor sempre vivo per ricevere il risultato. */\nexport interface EditableReportModule<TResult = any> {\n kind: 'editable';\n Report: ComponentType<{\n result: TResult;\n onResultChange: (next: TResult) => void;\n }>;\n}\n\n/** Registro presentation-side per id di subagent (stesso principio filesystem-first/id-keyed di\n * destinationRegistry.ts) — MAI un campo su SubagentManifest: quel\n * contratto vive nel domain layer, presentation-agnostico per design (nessun file in domain/\n * importa React in questo repo), mentre un report è JSX puro. Un subagent senza voce qui\n * semplicemente non mostra nessun report nel fallback \"nessun anchor vivo\" di SubagentRunner —\n * comportamento identico a oggi per redirects (il suo report vive SOLO dentro RedirectsEditor,\n * mai qui, vedi il commento su EditableReportModule sopra), mai un errore.\n * Estendere per un futuro subagent: un modulo + una riga qui SOLO se non ha un componente-anchor\n * sempre vivo che possa possedere il proprio Form. */\nexport type SubagentReportModule<TResult = any> = SelectionReportModule<TResult> | EditableReportModule<TResult>;\n\n// Store mutabile, non un letterale statico — Agentico (questo file) non deve MAI importare un\n// report di un subagent concreto (es. LiquidResultReport, specifico di html-to-liquid): stessa\n// inversione di dipendenza di subagentRegistry.ts/registerSubagents, qui applicata al registro\n// dei report React invece che ai manifest. L'host (il CMS) registra i propri report una volta\n// sola all'avvio (vedi CmsProviders.tsx) — un id senza voce registrata resta \"nessun report\" per\n// il fallback \"nessun anchor vivo\" di SubagentRunner, mai un errore (comportamento invariato).\nconst registry: Record<string, SubagentReportModule> = {};\n\nexport function registerSubagentReports(reports: Record<string, SubagentReportModule>): void {\n Object.assign(registry, reports);\n}\n\nexport function getSubagentReport(subagentId: string): SubagentReportModule | undefined {\n return registry[subagentId];\n}\n","import React, { useState } from 'react';\nimport { Code } from '@llmnative/react';\n\n/** Anteprima di riserva per un subagent SENZA un report registrato (oggi: theme, redirects) —\n * mostrata nella stessa identica posizione/momento di un vero `SubagentReportModule.Report`\n * (prima del bottone \"Applica\"), cosi ogni subagent, con o senza report dedicato, con o senza\n * AI, passa dallo STESSO schema \"vedi il risultato → poi applica\", mai un apply automatico\n * silenzioso. Un JSON grezzo non è bello quanto uno swatch di colori o una griglia editabile,\n * ma è sempre meglio di applicare alla cieca — un report dedicato resta la via preferita quando\n * ne vale la pena scriverne uno (vedi subagentReportRegistry.tsx). */\nexport function GenericResultPreview({ result }: { result: unknown }) {\n const [expanded, setExpanded] = useState(false);\n return (\n <div\n className={`relative cursor-pointer rounded-md border border-border/60 ${expanded ? 'max-h-80 overflow-y-auto' : 'max-h-28 overflow-hidden'}`}\n onClick={() => setExpanded((v) => !v)}\n title={expanded ? 'Clic per comprimere' : 'Clic per espandere'}\n >\n <Code language=\"json\" showCopy={expanded} background=\"transparent\" className=\"!text-[11px]\">\n {JSON.stringify(result, null, 2)}\n </Code>\n {!expanded && (\n <div className=\"pointer-events-none absolute inset-x-0 bottom-0 h-6 bg-gradient-to-t from-muted/30 to-transparent\" />\n )}\n </div>\n );\n}\n","import type { ComponentAnchor } from './AgenticoContext';\nimport type { ResultDestination } from '../domain/resultDestination';\n\n/** Consegna a un componentAnchor live (es. ThemeEditor per theme) trattata come UNA destinazione\n * qualunque tra quelle proposte da ResultDestinationPicker — non più un canale a parte con\n * consegna automatica silenziosa (bug segnalato: il risultato finiva nel componente PRIMA che\n * l'utente vedesse alcuna anteprima o cliccasse alcunché). Stesso schema per ogni subagent, con\n * o senza AI: risultato → anteprima in chat → click esplicito → applica. Nessun `id` restituito\n * (l'entità non è ancora persistita finché l'utente non salva lui stesso nel componente aperto —\n * `commitEntityChat` si occupa di quel momento separatamente, dal Save del componente stesso). */\nexport function buildLiveAnchorDestination(\n matching: ComponentAnchor[],\n meta: Record<string, unknown>,\n taskId: string,\n): ResultDestination | null {\n if (matching.length === 0) return null;\n return {\n id: 'live-component',\n label: 'Applica al componente aperto',\n async apply(result) {\n matching.forEach((a) => a.onResult?.(result, meta, taskId));\n },\n };\n}\n","import type { ReactNode } from 'react';\nimport type { ChatbotAction } from '@llmnative/react';\n\n/** What the controls of a subagent receive (CR-003). */\nexport interface SubagentControlsProps {\n /** The raw params the subagent was started with. */\n params: Record<string, unknown>;\n /** The id of the task in the Agentico panel. */\n taskId: string;\n /** A turn is running: actions that would interrupt it should wait. */\n running: boolean;\n /** Ends the task (stops a running turn and closes it). */\n close: () => void;\n}\n\n/** The controls of a task: actions of the prompt, and what the host shows above it (a modal, a message). */\nexport interface SubagentControls {\n /** Buttons of the composer, next to the attachments (`ChatbotAction` with `onClick`). */\n actions?: ChatbotAction[];\n /** Shown above the composer while the task is open. */\n content?: ReactNode;\n}\n\n/**\n * A React hook of the host: called by the runner of each task of its subagent, on every render.\n * Its `actions` and `content` must keep their identity while nothing changes (`useMemo`): a new\n * one registers the task again, and a new one at every render would never stop.\n */\nexport type UseSubagentControls = (props: SubagentControlsProps) => SubagentControls;\n\n// Keyed by subagent id, presentation side as the report registry: Agentico never imports a\n// concrete one. The hook of a subagent never changes, so a runner always calls the same one.\nconst registry: Record<string, UseSubagentControls> = {};\n\nexport function registerSubagentControls(controls: Record<string, UseSubagentControls>): void {\n Object.assign(registry, controls);\n}\n\nexport function getSubagentControls(subagentId: string): UseSubagentControls | undefined {\n return registry[subagentId];\n}\n","import React, { useEffect, useMemo, useRef, useState } from 'react';\nimport { useProxy } from '@llmnative/react';\nimport type { AIConversationTurn } from '@llmnative/react';\nimport { useAgent, type AgentActivityEntry } from './useAgent';\nimport { useAgentico } from './AgenticoContext';\nimport type { SubagentManifest } from '../domain/subagentManifest';\nimport { getDestinationRegistry } from '../domain/destinationRegistry';\nimport type { ResultDestination } from '../domain/resultDestination';\nimport { ResultDestinationPicker } from './ResultDestinationPicker';\nimport { getSubagentReport, type SubagentSelection } from './reports/subagentReportRegistry';\nimport { GenericResultPreview } from './reports/GenericResultPreview';\nimport { deriveHistoryTitle } from './deriveHistoryTitle';\nimport { buildLiveAnchorDestination } from './liveAnchorDestination';\nimport { getSubagentControls, type UseSubagentControls } from './subagentControls';\nimport { saveEntityChat } from '../domain/entityChatStore';\n\n/** Monta il vero subagent (useAgent(manifest.definition)) e lo esegue una volta al mount con i\n * parametri già raccolti dall'orchestratore Agentico — nessun passo utente aggiuntivo, la\n * conversazione di raccolta parametri è già avvenuta a monte (vedi Agentico.tsx). Si registra\n * nel pannello Agentico con id stabile via useRef + cleanup closeTask allo smontaggio, con il\n * transcript dell'orchestratore (`precedingActivity`) prepeso al proprio log — l'utente vede\n * UNA chat continua, mai due task distinti da switchare (vedi OrchestratorRunner).\n *\n * Con `resumeSeed` valorizzato, il primo giro chiama `agent.resume(...)` invece di\n * `agent.run(...)` — riprende una chat già salvata (vedi entityChatStore/\n * AgenticoContext.resumeEntityChat) invece di avviarne una nuova. Tutto il resto (consegna del\n * risultato ai componentAnchors, ResultDestinationPicker di riserva, registrazione del task) è\n * IDENTICO a un run normale — un follow-up dopo un resume produce un risultato esattamente come\n * un follow-up dopo un run, nessuna duplicazione di logica per il caso \"ripreso\". */\nconst noControls: UseSubagentControls = () => ({});\n\nexport function SubagentRunner({\n manifest, params, extraDestinations, precedingActivity, taskId: taskIdProp, resumeSeed, model, openingMessage,\n}: {\n manifest: SubagentManifest<any, any, any>;\n params: Record<string, unknown>;\n /** Destinazioni scoped alla pagina che ha avviato questo run (es. \"Applica a questo Site\n * aperto\") — inoltrate tale e quali a ResultDestinationPicker, vedi quel file. */\n extraDestinations?: ResultDestination[];\n /** Transcript dell'orchestratore all'handoff — vedi AgenticoTask.precedingActivity. Non ha\n * senso insieme a `resumeSeed` (una chat ripresa non ha un orchestratore a monte in questo\n * giro) — semplicemente non passato dal chiamante in quel caso. */\n precedingActivity?: AgentActivityEntry[];\n /** Id di tab da RIUSARE invece di generarne uno fresco — vedi PendingCarryOver\n * (AgenticoContext.tsx)/OrchestratorRunner.tsx: quando presente, questo run \"continua\" una\n * tab già esistente (stessa posizione, stesso id) invece di aprirne una seconda per lo\n * stesso subagent. Assente = comportamento invariato, id fresco per ogni run. */\n taskId?: string;\n /** Presente = riprendi una chat già salvata invece di avviarne una nuova (vedi\n * AgenticoContext.resumeEntityChat) — `params` deve comunque essere la stessa forma raccolta\n * originariamente (manifest.toParams(params) viene chiamato in entrambi i casi). */\n resumeSeed?: { history: AIConversationTurn[]; activity: AgentActivityEntry[] };\n /** Modello scelto nel composer \"starter\" a monte (OrchestratorRunner.tsx's modelRef) — questo\n * componente monta la sua PROPRIA istanza di useAgent/useAIModelCatalog, indipendente da\n * quella dell'orchestratore che ha appena fatto l'handoff, quindi senza propagarlo qui\n * esplicitamente `agent.selectedModel` si auto-inizializzerebbe al default del provider\n * invece di continuare con la scelta dell'utente (stesso bug, stessa causa, di\n * OrchestratorRunner — vedi il commento lì). Non passato per un `resumeSeed` — lì non c'è una\n * scelta \"di questo giro\" da propagare, si continua semplicemente la chat salvata così com'era. */\n model?: string;\n /** The first message of the person, when a page starts the subagent directly\n * (AgenticoContext.startSubagent, CR-003): shown in the log and given to the first turn. */\n openingMessage?: string;\n}) {\n const agent = useAgent(manifest.definition, { defaultEnabled: true });\n const sidebar = useAgentico();\n const scope = sidebar.scope;\n const proxyFetch = useProxy();\n const taskId = useRef(taskIdProp ?? `subagent-${manifest.id}-${crypto.randomUUID()}`).current;\n const [result, setResult] = useState<unknown>(null);\n const [appliedIds, setAppliedIds] = useState<string[]>([]);\n const [autoApplyError, setAutoApplyError] = useState<string | null>(null);\n const reportModule = getSubagentReport(manifest.id);\n const selectionModule = reportModule?.kind === 'selection' ? reportModule : null;\n const [selection, setSelection] = useState<SubagentSelection | null>(null);\n const startedRef = useRef(false);\n // Punto della timeline (precedingActivity + agent.activity, stesso ordine di Agentico.tsx) in\n // cui è arrivato l'ULTIMO risultato NUOVO — passato come panelExtraAnchorIndex a openTask\n // sotto, cosi un follow-up successivo (retry finito in errore, nuovo messaggio) appare\n // SOTTO il pannello risultati invece di scavalcarlo (bug segnalato: il pannello sembrava\n // \"risalire\" sopra conversazione più recente perché veniva sempre reso in fondo al log,\n // indipendentemente da quando il risultato che mostra fosse realmente arrivato).\n const panelExtraAnchorIndexRef = useRef(0);\n const anchoredResultRef = useRef<unknown>(null);\n if (result && result !== anchoredResultRef.current) {\n anchoredResultRef.current = result;\n panelExtraAnchorIndexRef.current = (precedingActivity?.length ?? 0) + agent.activity.length;\n }\n // Stesso guard di OrchestratorRunner — vedi il commento lì per il perché è necessario\n // (senza, la tab chiusa dall'utente riapparirebbe da sola al prossimo aggiornamento di\n // agent.activity/running, es. l'abort innescato dalla chiusura stessa).\n const dismissedRef = useRef(false);\n\n // Aspetta che il catalogo modelli sia risolto (async, vedi useAgent.ts) prima di lanciare\n // run() — altrimenti run() partirebbe con selectedModel ancora vuoto e cadrebbe sempre sul\n // fallback deterministico, anche quando un provider è configurato. Se non c'è nessun\n // provider (`!hasProviders`), non arriverà mai un selectedModel: si parte comunque subito,\n // run() userà il fallback come previsto.\n // A remote subagent (CR-003) has no model to wait for.\n const readyToStart = agent.remote || !agent.hasProviders || agent.selectedModel !== '';\n\n useEffect(() => {\n if (startedRef.current || !readyToStart) return;\n startedRef.current = true;\n if (resumeSeed) {\n void agent.resume(manifest.toParams(params), resumeSeed);\n } else {\n // Same reasoning as OrchestratorRunner.tsx: `model` overrides `selectedModel` for\n // this run() call only, so setSelectedModel() also needs calling here for every\n // later interaction (a follow-up message, a retry) to keep reading the user's actual\n // choice instead of this instance's own just-resolved default.\n if (model) agent.setSelectedModel(model);\n void agent.run(manifest.toParams(params), { model, openingMessage }).then((r) => { if (r) setResult(r); });\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [readyToStart]);\n\n // Auto-apply — per un subagent come redirects il risultato STESSO è il dato definitivo\n // (docs/30-redirects-system.md), non un export opzionale: ogni volta che `result` cambia\n // (run iniziale o un edit successivo in RedirectMappingReport), le destinazioni elencate in\n // `autoApplyDestinationIds` vengono applicate da sole, senza che l'utente clicchi nulla in\n // ResultDestinationPicker (che resta comunque disponibile per un secondo apply esplicito,\n // es. ri-esportare un CSV). Richiede uno `scope` (AgenticoProvider) — nessun apply silenzioso\n // verso un provider ancora placeholder.\n useEffect(() => {\n if (!result || !scope) return;\n const autoIds = manifest.autoApplyDestinationIds ?? [];\n // Se un componente vivo per QUESTO subagent dichiara un `pinnedEntityId` (es.\n // RedirectsEditor, aperto su un host specifico — vedi ComponentAnchor.pinnedEntityId),\n // lo si inoltra alla destinazione: una destinazione con logica di identità propria (es.\n // redirectsSaveDestination) può rifiutare un salvataggio il cui id risolto non combacia,\n // invece di scrivere silenziosamente sull'entità sbagliata perché il modello ha ignorato\n // il vincolo nel prompt. Nessun anchor pinnato (es. run partito da un contesto senza\n // componente vivo) → `undefined`, nessun controllo aggiuntivo.\n const pinnedEntityId = sidebar.componentAnchors.find((a) => a.subagentId === manifest.id && a.pinnedEntityId)?.pinnedEntityId;\n const registry = getDestinationRegistry();\n autoIds.forEach((id) => {\n const destination = registry[id];\n if (!destination) return;\n setAutoApplyError(null);\n destination.apply(result, {\n scopeId: scope.id,\n dataProvider: scope.dataProvider,\n storageProvider: scope.storageProvider,\n proxyFetch,\n }, { ...params, pinnedEntityId })\n // Un auto-apply riuscito CON un id (es. redirects: {id: hostId}) è, per\n // definizione, l'entità che questo giro ha scritto — commitEntityChat generico\n // qui invece che duplicato nel componente di ogni subagent (a differenza di\n // theme, che non ha auto-apply e deve chiamarlo lui stesso dal proprio Save):\n // qualunque futuro subagent con autoApplyDestinationIds ottiene la persistenza\n // chat gratis, senza scriverne una riga in più.\n .then((outcome) => {\n if (outcome?.id) {\n sidebar.commitEntityChat(manifest.id, outcome.id, taskId, scope.id, params, deriveHistoryTitle(manifest, params));\n }\n })\n // Un auto-apply fallito (es. hostname non risolvibile) non deve restare\n // invisibile solo perché non l'ha innescato un click esplicito — bug osservato:\n // \"Tools > Redirects\" restava vuoto dopo un'elaborazione riuscita, senza alcun\n // segnale che il salvataggio non fosse mai avvenuto.\n .catch((err) => setAutoApplyError(err instanceof Error ? err.message : String(err)));\n });\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [result, agent.activity, scope]);\n\n // Inizializza la selezione (tutti i campi opzionali spuntati) non appena arriva un risultato\n // per un subagent con report registrato — si resetta anche se `result` cambia riferimento\n // (es. un refinement successivo produce un nuovo oggetto), così l'utente rivede sempre uno\n // stato \"tutto incluso\" di fronte a un risultato nuovo invece di ereditare vecchie esclusioni.\n useEffect(() => {\n if (result && selectionModule) setSelection(selectionModule.defaultSelection(result));\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [result, selectionModule]);\n\n const handleSelectionChange = (key: string, value: boolean | string) => {\n setSelection((prev) => (prev ? { ...prev, [key]: value } : prev));\n };\n\n // Un report \"editabile\" (redirects) rimpiazza direttamente `result` — l'effect di auto-apply\n // sopra reagisce a `result` e ripropaga da solo, nessun secondo canale da aggiungere qui.\n const handleResultChange = (next: unknown) => setResult(next);\n\n // Memoizzato: applySelection ritorna sempre un oggetto NUOVO (spread), quindi senza\n // useMemo ogni render di questo componente produrrebbe un filteredResult diverso — ed\n // essendo nelle dipendenze dell'effect di sidebar.openTask sotto, l'effect non si sarebbe\n // mai stabilizzato: openTask cambia il task nel context → il context re-renderizza i\n // consumer (anche questo componente) → filteredResult ricalcolato con un nuovo riferimento\n // → l'effect riparte da capo. Loop infinito osservato in console (\"Maximum update depth\n // exceeded\"), corretto memoizzando sulle dipendenze effettive.\n const filteredResult = useMemo(\n () => (result && selectionModule && selection ? selectionModule.applySelection(result, selection) : result),\n [result, selectionModule, selection],\n );\n\n // Un componentAnchor live (vedi useSubagentComponentContext) per QUESTO subagent è trattato\n // come UNA destinazione qualunque — MAI più una consegna automatica silenziosa appena\n // arriva il risultato (bug segnalato: l'utente non vedeva nessuna anteprima prima che il\n // componente si popolasse da solo). Più anchor possono ascoltare lo STESSO subagentId (es.\n // ThemeEditor per i campi tema + SiteEditorPage per i soli campi di site-branding) — tutti\n // ricevuti insieme da un solo click su \"Applica al componente aperto\" (vedi\n // liveAnchorDestination.ts). Nessun anchor combacia (es. run partito da un contesto senza\n // componente vivo) → quella destinazione semplicemente non compare tra le altre.\n // Memoizzati per lo stesso motivo di filteredResult sopra — non sono nelle dipendenze\n // dell'effect sotto, ma restano comunque props di ResultDestinationPicker dentro\n // `panelExtra`: un riferimento nuovo a ogni render sarebbe fragile (basterebbe aggiungerli\n // un giorno alle dipendenze per riaprire lo stesso loop).\n const matchingAnchors = useMemo(\n () => sidebar.componentAnchors.filter((a) => a.subagentId === manifest.id && a.onResult),\n [sidebar.componentAnchors, manifest.id],\n );\n const liveAnchorDestination = useMemo(\n () => buildLiveAnchorDestination(matchingAnchors, params, taskId),\n [matchingAnchors, params, taskId],\n );\n const pickerExtraDestinations = useMemo(\n () => (liveAnchorDestination ? [liveAnchorDestination, ...(extraDestinations ?? [])] : extraDestinations),\n [liveAnchorDestination, extraDestinations],\n );\n\n // A subagent whose chats belong to an entity (CR-003) saves the chat after every turn: it is in\n // the history and is resumed on that entity, without the host having to commit it.\n const chatEntityId = manifest.chatEntityId?.(params);\n const wasRunningRef = useRef(false);\n useEffect(() => {\n const ended = wasRunningRef.current && !agent.running;\n wasRunningRef.current = agent.running;\n if (!ended || !chatEntityId || !scope) return;\n const snap = agent.snapshot();\n if (!snap) return;\n saveEntityChat({\n scopeId: scope.id, subagentId: manifest.id, entityId: chatEntityId, params, title: deriveHistoryTitle(manifest, params),\n history: snap.history,\n activity: [...(precedingActivity ?? []), ...snap.activity],\n });\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [agent.running]);\n\n const dismiss = () => {\n dismissedRef.current = true;\n if (agent.running) agent.stop();\n sidebar.closeTask(taskId);\n };\n // The controls of the host for this subagent: actions of the prompt and what it shows above.\n const useControls = useRef<UseSubagentControls>(getSubagentControls(manifest.id) ?? noControls).current;\n const controls = useControls({ params, taskId, running: agent.running, close: dismiss });\n\n const handleDestinationApplied = (destinationId: string) => {\n setAppliedIds((prev) => (prev.includes(destinationId) ? prev : [...prev, destinationId]));\n };\n\n // Nessun gate su agent.canContinue — Agentico è l'unica UI per qualunque subagent (nessuna\n // modale con una propria UI indipendente esiste più), quindi il task va registrato subito,\n // altrimenti senza provider AI configurato (o prima che il catalogo modelli si risolva) non\n // comparirebbe mai, e il pannello sembrerebbe non aver reagito.\n //\n // `panelExtra` (non il return JSX di questo componente!) è come il ResultDestinationPicker\n // arriva dentro il pannello Agentico — questo componente è montato come sibling PRIMA del\n // pannello visibile (vedi Agentico.tsx, per sopravvivere al toggle di isOpen), quindi il\n // suo return finirebbe renderizzato FUORI dal pannello, come una colonna a sé — bug osservato\n // e corretto passando il picker attraverso lo slot esplicito del task invece che nel JSX.\n // `result`/`appliedIds` sono nelle dipendenze apposta: senza, il task registrato al primo\n // giro non includerebbe mai il picker (o i suoi aggiornamenti) una volta arrivato il risultato.\n useEffect(() => {\n if (dismissedRef.current) return;\n sidebar.openTask({\n // Titolo specifico (es. \"consul-etica.it\", non \"Theme extractor\") invece del nome\n // generico del subagent — più utile nell'header di Agentico.\n id: taskId, title: deriveHistoryTitle(manifest, params), icon: manifest.icon, agent, onApplied: (r: unknown) => setResult(r), precedingActivity,\n panelExtraAnchorIndex: panelExtraAnchorIndexRef.current,\n onDismiss: dismiss,\n // Saved at every turn: leaving it asks nothing (see Agentico.tsx, \"new chat\").\n ...(chatEntityId ? { savedToDisk: true } : {}),\n panelActions: controls.content,\n composerActions: controls.actions,\n panelExtra: result ? (\n <>\n {selectionModule && selection ? (\n <selectionModule.Report result={result} selection={selection} onChange={handleSelectionChange} />\n ) : reportModule?.kind === 'editable' ? (\n <reportModule.Report result={result} onResultChange={handleResultChange} />\n ) : (\n // Nessun report registrato per questo subagent (oggi: theme, redirects) —\n // anteprima generica, mai un apply diretto senza che l'utente veda prima\n // cosa sta per applicare (vedi GenericResultPreview.tsx).\n <GenericResultPreview result={filteredResult} />\n )}\n {autoApplyError && (\n <p className=\"text-xs text-destructive\">Salvataggio automatico non riuscito: {autoApplyError}</p>\n )}\n <ResultDestinationPicker\n manifest={manifest} result={filteredResult} meta={params} taskId={taskId}\n initiallyApplied={appliedIds} onApplied={handleDestinationApplied}\n extraDestinations={pickerExtraDestinations}\n />\n </>\n ) : undefined,\n });\n // BUG FISSATO: stesso problema di OrchestratorRunner.tsx — `agent.modelsByProvider`/\n // `agent.hasProviders` mancavano qui, quindi il catalogo modelli risolto in modo\n // asincrono non faceva mai ri-registrare l'`agent` fresco in sidebar.openTask, e la\n // tendina di selezione modello restava vuota per l'intera durata del task.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [agent.activity, agent.running, agent.error, agent.canContinue, agent.selectedModel, agent.modelsByProvider, agent.hasProviders, agent.remoteModels, sidebar.openTask, sidebar.componentAnchors, taskId, manifest, params, result, filteredResult, selection, appliedIds, extraDestinations, precedingActivity, autoApplyError, controls.content, controls.actions]);\n\n useEffect(() => {\n return () => { sidebar.closeTask(taskId); };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [sidebar.closeTask, taskId]);\n\n return null;\n}\n","import React, { useEffect, useMemo, useRef, useState } from 'react';\nimport { useAgenticoI18n } from '../i18n';\nimport { useAgent } from './useAgent';\nimport { useAgentico, ORCHESTRATOR_TASK_ID } from './AgenticoContext';\nimport { createOrchestratorAgentDefinition } from '../domain/orchestrator/agenticoOrchestratorAgent';\nimport { findSubagentManifest } from '../domain/subagentRegistry';\nimport type { OrchestratorHandoff } from '../domain/orchestrator/prompt';\nimport { SubagentRunner } from './SubagentRunner';\n\n/** Monta la conversazione dell'orchestratore Agentico — il \"meta-agente\" che legge il catalogo\n * CLI dei subagent (deterministico, nessuna chiamata LLM, vedi createOrchestratorAgentDefinition\n * buildContext) e conversa con l'utente finché non ha raccolto abbastanza parametri per invocare\n * un subagent reale. Quando arriva un risultato `{ kind: 'handoff' }` monta un SubagentRunner per\n * quel subagent, gli passa il transcript dell'orchestratore com'era in quel momento\n * (`precedingActivity`) e chiude subito il proprio task — l'utente vede UNA chat continua che\n * scivola dalla raccolta parametri all'esecuzione del subagent, mai due task da switchare a\n * mano (decisione esplicita, vedi piano). */\nexport function OrchestratorRunner({ userGoal }: { userGoal: string }) {\n const t = useAgenticoI18n();\n // Built once per mount with the labels of the current locale (useAgent keeps the first definition).\n const definition = useMemo(() => createOrchestratorAgentDefinition({\n unavailable: t.orchestratorUnavailable,\n continueWithoutAi: t.orchestratorContinueWithoutAi,\n preparing: t.orchestratorPreparing,\n collecting: t.orchestratorCollecting,\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }), []);\n const agent = useAgent(definition, { defaultEnabled: true });\n const sidebar = useAgentico();\n const [handoff, setHandoff] = useState<OrchestratorHandoff | null>(null);\n const startedRef = useRef(false);\n const readyToStart = !agent.hasProviders || agent.selectedModel !== '';\n\n // Catturate una sola volta al mount (come userGoal, un prop) — non rilette live da\n // sidebar.pendingExtraDestinations più avanti: questo componente resta montato per tutta la\n // conversazione, che può durare più turni fino all'handoff, e nel frattempo un'altra\n // startOrchestrator() altrove potrebbe aver sovrascritto quel valore nel context.\n const extraDestinationsRef = useRef(sidebar.pendingExtraDestinations);\n // Stesso principio per gli allegati del messaggio di apertura (grezzi — la conversione ad\n // AIAttachment avviene in useAgent.ts) — servono solo al primissimo run(), mai riletti dopo.\n const attachmentsRef = useRef(sidebar.pendingAttachments);\n // Stesso principio per il modello scelto nel composer \"starter\" — passato come override a\n // run() (vedi useAgent.ts) invece di un setSelectedModel() che non farebbe in tempo a\n // committare prima della chiamata nello stesso effect.\n const modelRef = useRef(sidebar.pendingModel);\n // Id del subagent verso cui la pagina/componente che ha avviato questa conversazione era\n // scoped (es. ThemeIndexList ancorato a 'theme', o /tools/redirects col vecchio meccanismo a\n // pageContextCard) — catturato UNA VOLTA al mount, come gli altri ref sopra. Permette a\n // IndexList (redirects) di mostrare \"in corso\" per quel subagent già durante la raccolta\n // parametri, non solo dopo l'handoff (vedi AgenticoContext.AgenticoTask.scopedSubagentId).\n // Un target esplicito da startOrchestrator(goal, {scopedSubagentId}) vince su quello\n // derivato da un componentAnchor live (vedi AgenticoContext.pendingScopedSubagentId) — un\n // click one-shot (es. \"Importa HTML/JSX\") non monta alcun anchor, ma sa comunque già a\n // priori quale subagent servirà.\n const scopedSubagentIdRef = useRef(sidebar.pendingScopedSubagentId ?? sidebar.scopedSubagentId ?? undefined);\n // Vedi PendingCarryOver (AgenticoContext.tsx) — catturato una volta al mount come gli altri\n // ref sopra: se startOrchestrator() ha trovato un task esistente per lo stesso subagent,\n // questo giro deve CONTINUARE quella conversazione (stesso id di tab, transcript prepeso)\n // invece di aprirne una scollegata — vedi il return sotto.\n const carryOverRef = useRef(sidebar.pendingCarryOver);\n // true dopo che l'utente ha chiuso la tab di questo task dalla tab bar — impedisce\n // all'effect sotto di ri-registrarlo (sidebar.openTask) al prossimo cambiamento di\n // agent.activity/running (es. l'abort innescato dalla chiusura stessa produce un ultimo\n // aggiornamento \"Interrotto dall'utente\"): senza questo guard la tab riapparirebbe da sola\n // subito dopo essere stata chiusa (bug osservato in fase di sviluppo di questa feature).\n const dismissedRef = useRef(false);\n\n const handleResult = (r: unknown) => {\n if (r && typeof r === 'object' && (r as { kind?: string }).kind === 'handoff') {\n setHandoff(r as OrchestratorHandoff);\n // Il lavoro dell'orchestratore è finito qui — libera subito ORCHESTRATOR_TASK_ID\n // (invece di aspettare lo smontaggio, che con questo componente non avviene mai\n // finché pendingGoal resta impostato) cosi un futuro startOrchestrator() vede\n // correttamente \"nessuna conversazione in corso\" invece di restare bloccato per il\n // resto della sessione su questa, già conclusa (bug osservato, vedi piano). Il\n // SubagentRunner montato sotto non è toccato: ha un id/task proprio, indipendente.\n sidebar.closeTask(ORCHESTRATOR_TASK_ID);\n }\n };\n\n useEffect(() => {\n if (startedRef.current || !readyToStart) return;\n startedRef.current = true;\n // `model: modelRef.current` overrides `selectedModel` for THIS run() call only (see the\n // comment on modelRef above and useAgent.ts's own doc comment on `opts.model`) — a\n // setSelectedModel() here wouldn't commit in time for run() to read it in the same\n // effect. But that override is scoped to this one call: every later interaction — the\n // \"Riprova\" button, any follow-up sendMessage(), a later round-trip of a NEW run() — reads\n // `agent.selectedModel` fresh, which without this call still sits at whatever\n // useAIModelCatalog() picked as its own default (a separate instance from the starter\n // composer's, never told about the user's actual choice). Calling it too, alongside the\n // override, costs nothing now and fixes every call after this one — this was the bug: the\n // model the user picked silently reverted to the provider default the moment the\n // conversation needed a second API call (a retry, a tool round-trip, anything past the\n // opening message).\n if (modelRef.current) agent.setSelectedModel(modelRef.current);\n // pageContext letto QUI, non prima — \"dove si trova l'utente\" al momento in cui la\n // conversazione parte davvero, non a mount-time del componente (che potrebbe precedere\n // di un istante la registrazione del contesto da parte della pagina chiamante).\n void agent.run(\n { userGoal, pageContext: sidebar.pageContext, scopedSubagentId: scopedSubagentIdRef.current ?? null },\n { attachments: attachmentsRef.current, model: modelRef.current, openingMessage: userGoal },\n ).then((r) => { if (r) handleResult(r); });\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [readyToStart]);\n\n // Il pannello Agentico è l'UNICA UI per qualunque subagent — se il gate fosse su\n // canContinue, senza un provider AI configurato (o prima che\n // il catalogo modelli si risolva) il task non comparirebbe MAI — il pannello si aprirebbe\n // vuoto, indistinguibile da \"non è successo niente\". Il task si registra quindi subito, a\n // ogni cambiamento rilevante di stato, e mostra il fallback deterministico se l'AI non è\n // disponibile invece di sparire.\n useEffect(() => {\n if (dismissedRef.current) return;\n sidebar.openTask({\n id: ORCHESTRATOR_TASK_ID, title: 'Agentico', icon: 'bot', agent, onApplied: handleResult,\n scopedSubagentId: scopedSubagentIdRef.current,\n // Il carry-over (vedi PendingCarryOver in AgenticoContext.tsx) va prepeso GIÀ QUI,\n // non solo sul SubagentRunner post-handoff\n // più sotto — altrimenti il trascritto precedente sparisce per tutta la durata del\n // turno di raccolta parametri dell'orchestratore stesso (il \"Sto pensando...\" che\n // parte SUBITO dopo l'invio) e riappare solo se/quando arriva un handoff, bug\n // segnalato: \"appena premo invio la chat precedente non c'è più\".\n precedingActivity: carryOverRef.current?.activity,\n onDismiss: () => {\n dismissedRef.current = true;\n if (agent.running) agent.stop();\n sidebar.closeTask(ORCHESTRATOR_TASK_ID);\n },\n });\n // BUG FISSATO: `agent.modelsByProvider`/`agent.hasProviders` mancavano da questo array —\n // il catalogo modelli si popola in modo asincrono (useAIModelCatalog dentro useAgent) e\n // quell'aggiornamento non faceva mai ri-registrare l'`agent` fresco in sidebar.openTask,\n // finché non cambiava anche uno degli altri campi osservati. Il task restava agganciato\n // per sempre allo snapshot di `agent` preso all'ultimo re-render \"rilevante\" precedente —\n // con `modelsByProvider` ancora vuoto se il catalogo non si era risolto in tempo — e la\n // tendina di selezione modello in Agentico spariva per l'intera durata della\n // conversazione, anche dopo che il catalogo (verificato via localStorage `ai.models.*`)\n // aveva correttamente 73 modelli disponibili.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [agent.activity, agent.running, agent.error, agent.canContinue, agent.selectedModel, agent.modelsByProvider, agent.hasProviders, sidebar.openTask]);\n\n useEffect(() => {\n return () => { sidebar.closeTask(ORCHESTRATOR_TASK_ID); };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [sidebar.closeTask]);\n\n // Ricalcolato SOLO quando agent.activity cambia davvero (carryOverRef.current è un ref,\n // stabile per tutta la vita del componente dopo il mount) — uno spread nudo qui creava un\n // array NUOVO a ogni render di questo componente, che SubagentRunner tiene nelle sue\n // dependency dell'effect di registrazione (sidebar.openTask): una reference sempre \"diversa\"\n // faceva ripartire quell'effect a ogni render, che aggiornando il context ri-renderizzava\n // anche questo componente, ricreando lo spread da capo — loop infinito (\"Maximum update\n // depth exceeded\", bug segnalato).\n const combinedPrecedingActivity = useMemo(\n () => [...(carryOverRef.current?.activity ?? []), ...agent.activity],\n [agent.activity],\n );\n\n if (!handoff) return null;\n const manifest = findSubagentManifest(handoff.subagentId);\n if (!manifest) return null;\n return (\n <SubagentRunner\n manifest={manifest} params={handoff.params}\n extraDestinations={extraDestinationsRef.current}\n model={modelRef.current || undefined}\n // Se c'è un carry-over (stesso subagent, tab precedente ancora aperta), riusa il\n // SUO id — il nuovo SubagentRunner aggiorna quella tab in place invece di aprirne\n // una seconda — e prepende la SUA intera conversazione (già comprensiva del suo\n // stesso precedingActivity) prima di quella di questo giro. Senza carry-over,\n // comportamento invariato: id fresco, solo il transcript di QUESTO orchestratore.\n taskId={carryOverRef.current?.taskId}\n precedingActivity={combinedPrecedingActivity}\n />\n );\n}\n","import React, { useEffect, useMemo, useRef, useState } from 'react';\nimport { ActionButton } from '@llmnative/react';\nimport { useAgent } from './useAgent';\nimport { useAgenticoI18n } from '../i18n';\nimport { useAgentico } from './AgenticoContext';\nimport type { SubagentManifest } from '../domain/subagentManifest';\nimport type { ResultDestination } from '../domain/resultDestination';\nimport { ResultDestinationPicker } from './ResultDestinationPicker';\nimport { getSubagentReport, type SubagentSelection } from './reports/subagentReportRegistry';\nimport { GenericResultPreview } from './reports/GenericResultPreview';\nimport { buildLiveAnchorDestination } from './liveAnchorDestination';\n\n/** Percorso \"senza AI\" per un subagent già scoped (un solo target possibile, niente da\n * instradare — vedi SubagentManifest.manualEntry e AgenticoContext.startManualEntry), montato\n * quando l'orchestratore stesso non riesce a partire (AI giù) ma il chiamante sa già a priori\n * quale subagent servirà. Mostra il prompt statico del manifest; sul primo testo inviato\n * dall'utente costruisce i TParams DIRETTAMENTE dal testo grezzo (`manualEntry.fromRawText`) e\n * chiama `agent.run()` con l'AI disabilitata a priori — `buildContext` (sempre deterministico)\n * gira comunque, ma la chiamata AI viene saltata del tutto (vedi useAgent.run, ramo\n * `!enabled`), zero round-trip di rete, zero possibilità di fallire per un provider giù.\n *\n * Il risultato è mostrato in anteprima (report del subagent se registrato, altrimenti\n * `GenericResultPreview`) più `ResultDestinationPicker` — STESSO identico schema di\n * SubagentRunner, mai un apply automatico appena arriva il risultato (bug segnalato: prima\n * consegnava subito a un anchor live o alla prima extraDestination, senza che l'utente vedesse\n * nulla prima). Un componentAnchor live (es. ThemeEditor per theme) è trattato come una\n * destinazione in più tra quelle proposte (vedi liveAnchorDestination.ts), non un canale a\n * parte. */\nexport function ManualEntryRunner({\n manifest, extraDestinations,\n}: {\n manifest: SubagentManifest<any, any, any>;\n extraDestinations?: ResultDestination[];\n}) {\n const agent = useAgent(manifest.definition, { defaultEnabled: false });\n const t = useAgenticoI18n();\n const sidebar = useAgentico();\n const taskId = useRef(`manual-${manifest.id}-${crypto.randomUUID()}`).current;\n const [result, setResult] = useState<unknown>(null);\n const [appliedIds, setAppliedIds] = useState<string[]>([]);\n const reportModule = getSubagentReport(manifest.id);\n const selectionModule = reportModule?.kind === 'selection' ? reportModule : null;\n const [selection, setSelection] = useState<SubagentSelection | null>(null);\n // Ultimi TParams usati con successo — riusati sia per costruire `meta` per\n // ResultDestinationPicker sia da \"Riprendi con AI\" (AgenticoContext.resumeWithAI) come punto\n // di partenza per un vero SubagentRunner, invece di far ridigitare da capo lo stesso testo.\n const [lastParams, setLastParams] = useState<Record<string, unknown> | null>(null);\n const dismissedRef = useRef(false);\n\n const handleSubmit = (rawText: string) => {\n if (!manifest.manualEntry) return;\n let params;\n try {\n params = manifest.manualEntry.fromRawText(rawText);\n } catch (err) {\n // Difensivo — il composer (Chatbot) blocca già l'invio di testo vuoto/whitespace,\n // stessa condizione che fromRawText tipicamente rifiuta; nessun canale per mostrare\n // un errore sincrono qui (niente sessione ancora avviata su cui appoggiarlo).\n console.error('[ManualEntryRunner] manualEntry.fromRawText ha rifiutato il testo:', err);\n return;\n }\n setLastParams(params as Record<string, unknown>);\n setAppliedIds([]);\n void agent.run(params, { openingMessage: rawText }).then((r) => { if (r) setResult(r); });\n };\n\n useEffect(() => {\n if (result && selectionModule) setSelection(selectionModule.defaultSelection(result));\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [result, selectionModule]);\n\n const handleSelectionChange = (key: string, value: boolean | string) => {\n setSelection((prev) => (prev ? { ...prev, [key]: value } : prev));\n };\n\n const handleResultChange = (next: unknown) => setResult(next);\n\n // Memoizzato: applySelection ritorna sempre un oggetto NUOVO (spread) — senza useMemo,\n // ogni render produrrebbe un filteredResult diverso, e finendo nelle dipendenze dell'effect\n // di sidebar.openTask sotto, quell'effect non si stabilizzerebbe mai (loop di re-render\n // infinito, \"Maximum update depth exceeded\" — bug osservato). Stesso identico principio già\n // documentato in SubagentRunner.tsx.\n const filteredResult = useMemo(\n () => (result && selectionModule && selection ? selectionModule.applySelection(result, selection) : result),\n [result, selectionModule, selection],\n );\n\n const handleDestinationApplied = (destinationId: string) => {\n setAppliedIds((prev) => (prev.includes(destinationId) ? prev : [...prev, destinationId]));\n };\n\n // Memoizzati per lo stesso motivo — non sono nelle dipendenze dell'effect sotto (solo\n // `sidebar.componentAnchors`/`lastParams` lo sono), ma restano comunque props di\n // ResultDestinationPicker dentro `panelExtra`: un riferimento nuovo a ogni render vi\n // propagherebbe un re-render inutile a ogni giro, oltre a essere inutilmente fragile se in\n // futuro qualcuno li aggiungesse alle dipendenze.\n const matchingAnchors = useMemo(\n () => sidebar.componentAnchors.filter((a) => a.subagentId === manifest.id && a.onResult),\n [sidebar.componentAnchors, manifest.id],\n );\n const liveAnchorDestination = useMemo(\n () => (lastParams ? buildLiveAnchorDestination(matchingAnchors, lastParams, taskId) : null),\n [matchingAnchors, lastParams, taskId],\n );\n const pickerExtraDestinations = useMemo(\n () => (liveAnchorDestination ? [liveAnchorDestination, ...(extraDestinations ?? [])] : extraDestinations),\n [liveAnchorDestination, extraDestinations],\n );\n\n useEffect(() => {\n if (dismissedRef.current) return;\n sidebar.openTask({\n id: taskId, title: manifest.label, icon: manifest.icon, agent,\n onApplied: (r: unknown) => setResult(r),\n manualEntry: manifest.manualEntry ? { prompt: manifest.manualEntry.prompt, onSubmit: handleSubmit } : undefined,\n onDismiss: () => {\n dismissedRef.current = true;\n if (agent.running) agent.stop();\n sidebar.closeTask(taskId);\n },\n panelExtra: result ? (\n <div className=\"space-y-2\">\n {selectionModule && selection ? (\n <selectionModule.Report result={result} selection={selection} onChange={handleSelectionChange} />\n ) : reportModule?.kind === 'editable' ? (\n <reportModule.Report result={result} onResultChange={handleResultChange} />\n ) : (\n <GenericResultPreview result={filteredResult} />\n )}\n <ResultDestinationPicker\n manifest={manifest} result={filteredResult} meta={lastParams ?? {}} taskId={taskId}\n initiallyApplied={appliedIds} onApplied={handleDestinationApplied}\n extraDestinations={pickerExtraDestinations}\n />\n {/* Il \"contrario\" di startManualEntry — niente da rifare, solo il canale che\n cambia: gli stessi `lastParams` avviano un vero SubagentRunner (AI\n abilitata) al posto di questo task. Nascosto se non esiste nemmeno un\n provider configurato: riprovare con AI fallirebbe di nuovo allo stesso\n modo, senza aggiungere nulla. */}\n {agent.hasProviders && (\n <ActionButton\n label={t.resumeWithAi}\n variant=\"outline-secondary\"\n className=\"text-xs\"\n onClick={() => { if (lastParams) sidebar.resumeWithAI(manifest.id, lastParams, extraDestinations); }}\n />\n )}\n </div>\n ) : undefined,\n });\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [agent.activity, agent.running, agent.error, agent.hasProviders, sidebar.componentAnchors, result, filteredResult, selection, appliedIds, lastParams]);\n\n useEffect(() => {\n return () => { sidebar.closeTask(taskId); };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n return null;\n}\n","import React, { useEffect, useMemo, useRef, useState } from 'react';\nimport { ActionButton, Badge, Chatbot, Dropdown, DropdownItem, Icon, interpolate, useI18n } from '@llmnative/react';\nimport type { AIModelDescriptor, ChatbotModelOption } from '@llmnative/react';\nimport { useAgentico } from './AgenticoContext';\nimport { AgentActivityLog } from './AgentActivityLog';\nimport { AgenticoToggle } from './AgenticoToggle';\nimport { OrchestratorRunner } from './OrchestratorRunner';\nimport { SubagentRunner } from './SubagentRunner';\nimport { ManualEntryRunner } from './ManualEntryRunner';\nimport { ORCHESTRATOR_TASK_ID } from './AgenticoContext';\nimport type { AgenticoTask } from './AgenticoContext';\nimport { useAgenticoI18n } from '../i18n';\nimport { useAIModelCatalog } from './useAIModelCatalog';\nimport type { SubagentUsageCard } from '../domain/subagentUsageCard';\nimport { findSubagentManifest } from '../domain/subagentRegistry';\nimport { entityChatTaskId, listEntityChats, removeEntityChat } from '../domain/entityChatStore';\n\n/** Larghezza fissa del pannello — passata anche come `rightInset` alle Modal che devono\n * lasciargli spazio invece di sovrapporlo (vedi Modal.rightInset nel framework). */\nexport const AGENTICO_WIDTH = 420;\n\n/** `modelsByProvider` di useAgent è raggruppato per provider — Chatbot vuole una lista\n * piatta `{label, value, pricing, group}`, stesso appiattimento che il framework fa internamente\n * in usePromptCapabilities per il proprio model picker (Prompt.tsx). `group` = nome del provider\n * (header sticky nel picker), quindi `label` è solo il modello. `pricing: null` = prezzo\n * cercato ma non trovato: il picker lo segnala sulla riga invece di tacere. */\nfunction flattenModelOptions(modelsByProvider: Record<string, AIModelDescriptor[]>): ChatbotModelOption[] {\n return Object.values(modelsByProvider).flat().map((m) => ({\n label: m.model,\n value: m.id,\n pricing: m.pricing ?? null,\n group: m.providerLabel ?? m.provider,\n }));\n}\n\n/** Composer del follow-up — monta `Chatbot` (CR-071, `@llmnative/react`): stesso widget di\n * `Prompt` in modalità RUN, ma qui il testo digitato è esattamente quello inviato a ogni\n * turno (nessun template autorato/campo risultato separato — quella semantica resta\n * specifica di Prompt). Chatbot si limita a raccogliere testo/allegati/modello e a\n * restituirli via `onSubmit`; la \"politica\" di cosa farne — chiamare `agent.sendMessage`,\n * accodare il risultato alla history — resta qui, mai responsabilità di Chatbot. */\nfunction AgenticoComposer({ task }: { task: AgenticoTask }) {\n const t = useAgenticoI18n();\n const [text, setText] = useState('');\n const { agent, onApplied, manualEntry } = task;\n const running = agent.running;\n\n const handleSubmit = (payload: { text: string; files: File[]; model: string }) => {\n setText('');\n // Percorso \"senza AI\" (vedi ManualEntryRunner/SubagentManifest.manualEntry) — il testo\n // costruisce i parametri DIRETTAMENTE, mai una chiamata al modello: niente\n // agent.sendMessage qui, `manualEntry.onSubmit` gestisce tutto (run con AI disabilitata,\n // auto-apply, log). Allegati/modello ignorati in questo ramo — non c'è alcuna chiamata\n // AI a cui passarli.\n if (manualEntry) { manualEntry.onSubmit(payload.text); return; }\n void agent.sendMessage(payload.text, { attachments: payload.files, model: payload.model }).then((result) => {\n if (result) onApplied(result);\n });\n };\n\n // BUG FISSATO: senza questa condizione, `selectedModel === ''` (nessun default automatico\n // più, vedi useAIModelCatalog.ts) non impediva comunque l'invio — il messaggio partiva con\n // `model: ''`, e `AIProvider.complete()` (framework, shared.ts:242, `request.model ||\n // this.defaultModel`) rimetteva dentro il default hardcoded silenziosamente, vanificando la\n // scelta esplicita richiesta all'utente. Bloccare qui l'invio finché un provider esiste ma\n // nessun modello è ancora selezionato è l'unico modo per far sì che quel fallback non scatti\n // mai più nei flussi Agentico. Nessuna chiamata AI in un task `manualEntry` (vedi handleSubmit\n // sopra): la scelta modello non ha senso qui, non deve mai bloccare l'invio.\n // A remote task (CR-003) answers with its own agent: no attachments, and its models are the\n // ones its executor offers (none = no picker), never required.\n const noModel = Boolean(manualEntry) || (agent.remote && agent.remoteModels.length === 0);\n const modelRequired = !noModel && !agent.remote && agent.hasProviders && !agent.selectedModel;\n const models = noModel ? [] : agent.remote ? agent.remoteModels : flattenModelOptions(agent.modelsByProvider);\n\n return (\n <Chatbot\n value={text}\n onChange={setText}\n placeholder={manualEntry?.prompt ?? t.messagePlaceholder}\n running={running}\n onStop={agent.stop}\n disabled={!text.trim() || modelRequired}\n onSubmit={handleSubmit}\n attachments={!manualEntry && !agent.remote}\n models={models}\n selectedModel={noModel ? '' : agent.selectedModel}\n actions={task.composerActions}\n onModelChange={agent.setSelectedModel}\n modelPlaceholder={t.selectModelPlaceholder}\n minHeight={44}\n maxHeight={220}\n />\n );\n}\n\n/** Il composer quando non c'è ancora un task attivo — il testo AVVIA una conversazione\n * (useAgentico().startOrchestrator) invece di proseguirla, quindi non c'è ancora un `agent`\n * da cui leggere il catalogo modelli: usa useAIModelCatalog direttamente (stessa fonte dati\n * di useAgent, vedi quel file) cosi la scelta del modello è possibile ANCHE prima che\n * l'orchestratore parta, non solo nei turni di follow-up. */\nfunction AgenticoStarterComposer() {\n const { startOrchestrator } = useAgentico();\n const t = useAgenticoI18n();\n const [text, setText] = useState('');\n const { hasProviders, modelsByProvider, selectedModel, setSelectedModel } = useAIModelCatalog();\n\n const handleSubmit = (payload: { text: string; files: File[]; model: string }) => {\n if (!payload.text.trim()) return;\n startOrchestrator(payload.text.trim(), {\n attachments: payload.files.length > 0 ? payload.files : undefined,\n model: payload.model || undefined,\n });\n setText('');\n };\n\n // Stesso motivo di AgenticoComposer sopra: senza questo, l'avvio partirebbe con\n // `model: undefined` e farebbe scattare comunque il default hardcoded del provider dentro\n // AIProvider.complete().\n const modelRequired = hasProviders && !selectedModel;\n\n return (\n <Chatbot\n value={text}\n onChange={setText}\n placeholder={t.orchestratorStarterPlaceholder}\n disabled={!text.trim() || modelRequired}\n onSubmit={handleSubmit}\n attachments\n models={flattenModelOptions(modelsByProvider)}\n selectedModel={selectedModel}\n onModelChange={setSelectedModel}\n modelPlaceholder={t.selectModelPlaceholder}\n minHeight={44}\n maxHeight={220}\n />\n );\n}\n\n/** \"2 h ago\": how long ago, in the language of the app (CR-003). */\nfunction relativeTime(at: number, locale: string, now = Date.now()): string {\n const seconds = Math.round((at - now) / 1000);\n const format = new Intl.RelativeTimeFormat(locale, { numeric: 'auto', style: 'short' });\n for (const [unit, size] of [['day', 86_400], ['hour', 3_600], ['minute', 60]] as const) {\n if (Math.abs(seconds) >= size) return format.format(Math.round(seconds / size), unit);\n }\n return format.format(0, 'minute');\n}\n\n/** Card \"pronto per una nuova istanza di X\" — render deterministico della stessa\n * SubagentUsageCard passata al modello via pageContext (vedi useAgenticoPageContext, usata da\n * pagine come redirects/ToolPage.tsx), qui come lista scansionabile di campi invece che come\n * frase di prosa: un\n * badge required/optional per campo, non un paragrafo da cui l'utente (o un LLM) deve dedurre\n * cosa serve davvero. Sostituisce il vecchio `<p>{pageContext}</p>` SOLO quando c'è una card —\n * altrove resta il testo semplice (vedi il chiamante). */\nfunction SubagentUsageCardView({ card }: { card: SubagentUsageCard }) {\n const t = useAgenticoI18n();\n // humanSummaryOnly (vedi SubagentCommand.humanSummaryOnly): un comando i cui campi tecnici\n // non rispecchiano come un umano pensa l'input (es. redirects: 4 campi separati ma un umano\n // ragiona in termini di \"cosa mando\", con una fonte sola spesso sufficiente per più campi)\n // mostra SOLO il racconto in prosa — l'elenco a badge richiesto/opzionale e l'esempio in\n // sintassi CLI implicherebbero erroneamente un modulo da compilare campo per campo.\n if (card.humanSummaryOnly) {\n return (\n <div className=\"space-y-2 rounded-lg border border-border/60 p-3\">\n <p className=\"text-xs font-medium text-foreground\">\"{card.label}\"</p>\n <p className=\"text-xs text-muted-foreground\">{card.humanSummary}</p>\n </div>\n );\n }\n\n return (\n <div className=\"space-y-2 rounded-lg border border-border/60 p-3\">\n <p className=\"text-xs font-medium text-foreground\">{interpolate(t.newInstance, { label: card.label })}</p>\n <p className=\"text-xs text-muted-foreground\">{card.humanSummary}</p>\n {card.fields.length > 0 && (\n <ul className=\"space-y-1\">\n {card.fields.map((f) => (\n <li key={f.key} className=\"flex items-start gap-1.5 text-xs\">\n <Badge variant={f.required ? 'primary' : 'secondary'} className=\"mt-px shrink-0\">\n {f.required ? 'richiesto' : 'opzionale'}\n </Badge>\n <span className=\"min-w-0\">\n <span className=\"font-mono text-foreground\">{f.key}</span>\n {f.humanDescription ? <span className=\"text-muted-foreground\"> — {f.humanDescription}</span> : null}\n </span>\n </li>\n ))}\n </ul>\n )}\n {card.example && (\n <p className=\"text-xs text-muted-foreground\">Esempio: <span className=\"font-mono\">{card.example}</span></p>\n )}\n </div>\n );\n}\n\n/** Messaggio semplice per un anchor componente↔subagent (SubagentComponentContext.userMessage) —\n * sostituisce la card a campi per QUESTO meccanismo: niente badge required/opzionale, il\n * componente vivo si popola da solo quando arriva un risultato (vedi SubagentRunner), non c'è un\n * modulo di parametri da compilare a vista. Più righe se sono ancorati più subagent\n * contemporaneamente (vedi AgenticoContext.anchoredUserMessages). */\nfunction AnchoredUserMessageView({ messages }: { messages: string[] }) {\n return (\n <div className=\"space-y-2 rounded-lg border border-border/60 p-3\">\n {messages.map((m, i) => (\n <p key={i} className=\"text-xs text-muted-foreground\">{m}</p>\n ))}\n </div>\n );\n}\n\n/** Dropdown dello storico chat — TUTTE le chat salvate del tenant, mescolate across subagent\n * (theme, redirects, html-to-liquid: la label del subagent nella riga distingue da quale\n * viene), più recenti prima (vedi entityChatStore.listEntityChats). Un semplice filtro\n * testuale locale, non una ricerca full-text: basta per il volume atteso (una entry per\n * entità, non per esecuzione).\n *\n * `Dropdown`/`DropdownItem` del framework, non un overlay scritto a mano — posizionamento,\n * chiusura al click fuori/Escape, portal: tutto già risolto lì, riscriverlo qui sarebbe stato\n * puro doppione (bug corretto: la prima versione di questo pannello aveva un backdrop e un\n * `<button>` grezzi invece di riusare questi componenti). */\nfunction AgenticoHistoryDropdown({\n scopeId, currentTaskId, open, onOpenChange, onSelect, onNewChat, onArchivedCurrent,\n}: {\n scopeId: string;\n /** The task open in the panel: its chat is marked in the list (CR-003). */\n currentTaskId: string | null;\n open: boolean;\n onOpenChange: (open: boolean) => void;\n onSelect: (subagentId: string, entityId: string, snapshot: ReturnType<typeof listEntityChats>[number]) => void;\n onNewChat: () => void;\n /** The chat open in the panel was archived: the panel lets it go. */\n onArchivedCurrent: () => void;\n}) {\n const t = useAgenticoI18n();\n const { locale } = useI18n();\n const [search, setSearch] = useState('');\n // Bumped when a chat is archived, to read the list again.\n const [version, setVersion] = useState(0);\n const entries = useMemo(() => (open ? listEntityChats(scopeId) : []), [scopeId, open, version]);\n\n /** Archives a chat (CR-003): the host first (e.g. it closes a remote session, with its own\n * question if it has one), then off the history. A refusal of the host keeps the chat. */\n const archive = async (entry: ReturnType<typeof listEntityChats>[number]) => {\n const manifest = findSubagentManifest(entry.subagentId);\n const question = await manifest?.confirmArchive?.(entry.entityId, entry.params);\n if (question && !window.confirm(question)) return;\n try {\n await manifest?.onArchive?.(entry.entityId, entry.params);\n } catch {\n return;\n }\n removeEntityChat(scopeId, entry.subagentId, entry.entityId);\n if (currentTaskId === entityChatTaskId(entry.subagentId, entry.entityId)) onArchivedCurrent();\n setVersion((value) => value + 1);\n };\n const filtered = useMemo(() => {\n const q = search.trim().toLowerCase();\n if (!q) return entries;\n return entries.filter((e) => {\n const label = findSubagentManifest(e.subagentId)?.label ?? e.subagentId;\n return e.title.toLowerCase().includes(q) || label.toLowerCase().includes(q);\n });\n }, [entries, search]);\n\n return (\n <Dropdown\n trigger={{ icon: 'history', title: t.chatHistory }}\n // Il default del tema per un trigger Dropdown è pensato per un select-like button\n // (bordo, sfondo) — questo è un'icona isolata nell'header, stesso trattamento di\n // AgenticoToggle qui accanto: nessun bordo, solo hover.\n triggerClassName=\"!h-9 !w-9 !p-0 rounded-md bg-transparent text-muted-foreground hover:bg-accent hover:text-foreground\"\n open={open}\n onOpenChange={onOpenChange}\n position=\"end\"\n strategy=\"absolute\"\n menuClassName=\"w-[380px] max-h-[70vh] overflow-y-auto\"\n header={\n <div className=\"flex items-center gap-2 px-1 pb-1.5\">\n <Icon name=\"search\" size={14} className=\"shrink-0 text-muted-foreground\" />\n <input\n autoFocus\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n placeholder={t.searchChats}\n className=\"min-w-0 flex-1 bg-transparent text-sm outline-none placeholder:text-muted-foreground\"\n />\n <ActionButton icon=\"square-pen\" ariaLabel={t.newChat} title={t.newChat} variant=\"link\" className=\"shrink-0\" onClick={onNewChat} />\n </div>\n }\n >\n {filtered.length === 0 ? (\n <p className=\"px-2 py-3 text-center text-xs text-muted-foreground\">\n {entries.length === 0 ? t.noSavedChats : t.noResults}\n </p>\n ) : (\n filtered.map((entry) => {\n const manifest = findSubagentManifest(entry.subagentId);\n const current = currentTaskId === entityChatTaskId(entry.subagentId, entry.entityId);\n return (\n <DropdownItem\n key={`${entry.subagentId}:${entry.entityId}`}\n // The chat open in the panel is marked: the person sees where they are.\n icon={current ? 'check' : manifest?.icon ?? 'bot'}\n className={current ? 'bg-accent font-medium' : undefined}\n actions={[{ icon: 'archive', label: t.archiveChat, onClick: () => void archive(entry) }]}\n onClick={() => onSelect(entry.subagentId, entry.entityId, entry)}\n >\n <span className=\"flex min-w-0 flex-1 items-center justify-between gap-2\">\n <span className=\"min-w-0 truncate\">\n {/* `entry.title` è assente su una chat salvata PRIMA che questo campo esistesse\n (localStorage non si aggiorna da solo con lo schema — bug segnalato: la riga\n mostrava \"Theme extractor ·\" senza nulla dopo) — ripiega sull'id dell'entità\n invece di un vuoto silenzioso. */}\n <span className=\"text-muted-foreground\">{manifest?.label ?? entry.subagentId}</span> · {entry.title || entry.entityId}\n </span>\n <span className=\"shrink-0 text-xs text-muted-foreground\" title={new Date(entry.savedAt).toLocaleString(locale)}>{relativeTime(entry.savedAt, locale)}</span>\n </span>\n </DropdownItem>\n );\n })\n )}\n </Dropdown>\n );\n}\n\n/** Agentico — il pannello globale, persistente e non-overlay per le sessioni agente — affianca\n * il CMS (che si stringe) invece di coprirlo, cosi la modale che ha avviato l'agente resta\n * aperta e utilizzabile accanto. Header: titolo dell'UNICO task attivo (mai una tab bar — \"una\n * chat unica\", decisione esplicita: avviarne uno nuovo sostituisce quello in corso, vedi\n * AgenticoContext.openTask) — NESSUNA ×: si apre/chiude solo con AgenticoToggle (nell'Header del\n * CMS, o come `closeSlot` di una Modal right), la stessa icona ovunque, mai una propria qui.\n * Body: lo stesso log di attività del pannello embedded (AgentActivityLog), o un placeholder\n * neutro se non c'è ancora nessun task — il pannello può restare aperto vuoto, non sparisce da\n * solo quando il task finisce. Footer: il composer di follow-up (vedi AgenticoComposer sopra),\n * nascosto quando non c'è un task attivo.\n *\n * Solo la sessione aperta in questa sessione browser (registry in memoria via AgenticoContext)\n * — nessuna persistenza cross-sessione (Fase 3, deferita).\n *\n * Nota sul naming: \"Agentico\" è il nome dell'orchestratore — questo pannello, il suo contesto\n * (AgenticoContext), il suo toggle (AgenticoToggle) — non degli agenti verticali che esegue\n * (theme-import, html-to-liquid, ...), che restano nel proprio dominio senza questo\n * prefisso: sono contenuto che Agentico orchestra, non parte della sua identità. */\nexport function Agentico() {\n const {\n task, isOpen, pendingGoal, orchestratorRunId, pageContext, pageContextCard,\n anchoredUserMessages, pendingResume, resumeRunId, dismissForeignTask, resumeEntityChat,\n pendingManualEntry, manualEntryRunId, startManualEntry, pendingExtraDestinations,\n pendingResumeWithAI, resumeWithAIRunId, pendingStart, startRunId,\n scope,\n } = useAgentico();\n\n const t = useAgenticoI18n();\n const bodyRef = useRef<HTMLDivElement | null>(null);\n const [historyOpen, setHistoryOpen] = useState(false);\n\n // \"Nuova chat\" — congeda il task corrente (se c'è) e torna al composer \"starter\" del\n // contesto attuale, che già riflette dove ci si trova (vedi anchoredUserMessages/\n // pageContext) — nessun significato fisso, cambia da solo in base a dove si è. Un avviso\n // SOLO se la conversazione lasciata indietro non è mai stata salvata (vedi\n // AgenticoTask.savedToDisk): senza salvataggio andrebbe persa per sempre, nessun \"torna\n // indietro\" per scelta esplicita — una già salvata resta comunque recuperabile dallo\n // storico, quindi scartarla senza chiedere è sicuro.\n const handleNewChat = () => {\n if (task && !task.agent.running && !task.savedToDisk) {\n if (!window.confirm(t.unsavedConversationConfirm)) return;\n }\n dismissForeignTask();\n setHistoryOpen(false);\n };\n\n // Il transcript dell'orchestratore (se presente) viene PRIMA del log del subagent — vedi\n // AgenticoTask.precedingActivity: una chat unica e continua, mai due task da switchare.\n const displayedActivity = task ? [...(task.precedingActivity ?? []), ...task.agent.activity] : [];\n const activityCount = displayedActivity.length;\n // Punto della timeline in cui interlacciare `panelExtra` (vedi AgenticoTask.panelExtraAnchorIndex)\n // — assente = comportamento invariato, sempre in fondo. Clamp difensivo: un indice ricevuto\n // da un giro precedente potrebbe restare più corto/lungo del log corrente per un frame\n // (es. subito dopo che `stop()`/un nuovo run tronca `agent.activity`).\n const panelExtraAnchorIndex = Math.max(0, Math.min(task?.panelExtraAnchorIndex ?? activityCount, activityCount));\n const activityBeforePanelExtra = displayedActivity.slice(0, panelExtraAnchorIndex);\n const activityAfterPanelExtra = displayedActivity.slice(panelExtraAnchorIndex);\n const hasError = !!(task && !task.agent.running && task.agent.error);\n // Il fallback dell'orchestratore (agenticoOrchestratorAgent.ts) non è mai un vero handoff —\n // solo un messaggio da leggere, non può indovinare l'intento senza AI. Quando invece il\n // target è già noto (scopedSubagentId esplicito, vedi startOrchestrator/TemplateImportButton)\n // e quel subagent dichiara un manualEntry, \"Continua senza AI\" può fare MEGLIO che mostrare\n // un messaggio generico: salta dritto al percorso manuale (vedi ManualEntryRunner) invece di\n // chiamare runFallback() sull'agent dell'orchestratore stesso.\n const scopedManifestForError = task?.id === ORCHESTRATOR_TASK_ID && task.scopedSubagentId\n ? findSubagentManifest(task.scopedSubagentId)\n : undefined;\n const manualEntryManifestForError = scopedManifestForError?.manualEntry ? scopedManifestForError : null;\n // Se l'utente è vicino al fondo (entro una tolleranza) o no — aggiornato da un listener di\n // scroll vero, non ricalcolato dentro l'effect sotto: lì il DOM ha GIÀ la nuova voce quando\n // gira, quindi \"distanza dal fondo\" rifletterebbe la nuova altezza invece della posizione in\n // cui si trovava l'utente PRIMA che arrivasse (bug segnalato: durante \"sto pensando...\" non\n // si riusciva a scrollare in alto per rileggere una chat ripresa — l'auto-scroll ririportava\n // giù a ogni singola voce d'attività, decine al secondo mentre l'agente lavora).\n const isNearBottomRef = useRef(true);\n useEffect(() => {\n const el = bodyRef.current;\n if (!el) return;\n const handleScroll = () => {\n isNearBottomRef.current = el.scrollHeight - el.scrollTop - el.clientHeight < 80;\n };\n el.addEventListener('scroll', handleScroll);\n return () => el.removeEventListener('scroll', handleScroll);\n }, [task?.id]);\n\n // Nuovo task selezionato/ripreso — sempre in fondo, a prescindere da dove si era scrollati\n // nel task PRECEDENTE (se ce n'era uno).\n useEffect(() => {\n const el = bodyRef.current;\n if (!el) return;\n isNearBottomRef.current = true;\n el.scrollTop = el.scrollHeight;\n }, [task?.id]);\n\n // Scende in fondo ogni volta che compare una nuova voce (nuovo messaggio utente, nuovo\n // turno \"sto pensando\", nuova tool call) o l'errore finale — cosi l'ultimo messaggio è\n // sempre a vista senza dover scrollare a mano, come una normale chat. SOLO se l'utente era\n // già vicino al fondo: chi ha scrollato in alto per rileggere (es. la cronologia ripresa)\n // non viene più riportato giù a forza mentre l'agente genera ancora.\n // A reply that grows in place (a streamed turn, CR-003) does not add an entry: its length\n // counts too, so the panel follows the text as it is written.\n const lastDetailLength = displayedActivity.at(-1)?.detail?.length ?? 0;\n useEffect(() => {\n const el = bodyRef.current;\n if (!el || !isNearBottomRef.current) return;\n el.scrollTop = el.scrollHeight;\n }, [activityCount, hasError, lastDetailLength]);\n\n // The content also grows after the text arrived (Markdown laid out, a block opened): whenever\n // its height changes, the panel follows it if the person was at the bottom (CR-003).\n const contentRef = useRef<HTMLDivElement | null>(null);\n useEffect(() => {\n const el = bodyRef.current;\n const content = contentRef.current;\n if (!el || !content || typeof ResizeObserver === 'undefined') return;\n const observer = new ResizeObserver(() => {\n if (isNearBottomRef.current) el.scrollTop = el.scrollHeight;\n });\n observer.observe(content);\n return () => observer.disconnect();\n }, [task?.id, isOpen]);\n\n // Montato SEMPRE quando c'è un goal in sospeso, indipendentemente da `isOpen` — se lo\n // gatassimo dietro `!isOpen return null` sotto, chiudere il pannello smonterebbe\n // OrchestratorRunner (e a cascata l'eventuale SubagentRunner), perdendo il riferimento vivo\n // all'agent in corso — stesso motivo per cui AgenticoContext (vedi commento su\n // AgenticoTask.agent) richiede che il componente proprietario resti montato.\n // `key={orchestratorRunId}` forza React a smontare/rimontare un'istanza FRESCA a ogni nuova\n // conversazione avviata per davvero (vedi AgenticoContext) — senza, un secondo\n // startOrchestrator() riuserebbe l'istanza precedente (il cui `handoff` interno è già\n // impostato), ignorando silenziosamente il nuovo goal.\n const orchestrator = pendingGoal ? <OrchestratorRunner key={orchestratorRunId} userGoal={pendingGoal} /> : null;\n\n // Stesso principio di `orchestrator` sopra — montato SEMPRE quando c'è un resume in\n // sospeso, indipendentemente da `isOpen`, per non perdere il riferimento vivo all'agent non\n // appena il pannello si richiude. `taskId` stabile per entità (non un uuid fresco): riaprire\n // il resume della STESSA entità aggiorna la stessa tab invece di duplicarla.\n const resumeManifest = pendingResume ? findSubagentManifest(pendingResume.subagentId) : undefined;\n const resumeRunner = pendingResume && resumeManifest ? (\n <SubagentRunner\n key={resumeRunId}\n manifest={resumeManifest}\n params={pendingResume.snapshot.params}\n taskId={entityChatTaskId(pendingResume.subagentId, pendingResume.entityId)}\n resumeSeed={{ history: pendingResume.snapshot.history, activity: pendingResume.snapshot.activity }}\n />\n ) : null;\n\n // Stesso principio di `orchestrator`/`resumeRunner` sopra — percorso \"senza AI\" per un\n // subagent già noto (vedi AgenticoContext.startManualEntry/ManualEntryRunner), montato SEMPRE\n // indipendentemente da `isOpen`.\n const manualEntryManifest = pendingManualEntry ? findSubagentManifest(pendingManualEntry.subagentId) : undefined;\n const manualEntryRunner = pendingManualEntry && manualEntryManifest ? (\n <ManualEntryRunner\n key={manualEntryRunId}\n manifest={manualEntryManifest}\n extraDestinations={pendingManualEntry.extraDestinations}\n />\n ) : null;\n\n // \"Riprendi con AI\" (vedi AgenticoContext.resumeWithAI) — il contrario di manualEntryRunner:\n // un vero SubagentRunner (AI abilitata di default), avviato con gli ultimi parametri usati\n // nel percorso senza AI. Stesso identico trattamento \"montato sempre, indipendentemente da\n // isOpen\" di tutti gli altri runner sopra.\n const resumeWithAIManifest = pendingResumeWithAI ? findSubagentManifest(pendingResumeWithAI.subagentId) : undefined;\n const resumeWithAIRunner = pendingResumeWithAI && resumeWithAIManifest ? (\n <SubagentRunner\n key={resumeWithAIRunId}\n manifest={resumeWithAIManifest}\n params={pendingResumeWithAI.params}\n extraDestinations={pendingResumeWithAI.extraDestinations}\n />\n ) : null;\n\n // A subagent started by a page (AgenticoContext.startSubagent, CR-003) — mounted always, as\n // every runner above.\n const startManifest = pendingStart ? findSubagentManifest(pendingStart.subagentId) : undefined;\n const startRunner = pendingStart && startManifest ? (\n <SubagentRunner\n key={startRunId}\n manifest={startManifest}\n params={pendingStart.params}\n taskId={pendingStart.taskId}\n openingMessage={pendingStart.openingMessage}\n />\n ) : null;\n\n if (!isOpen) return <>{orchestrator}{resumeRunner}{manualEntryRunner}{resumeWithAIRunner}{startRunner}</>;\n\n return (\n <>\n {orchestrator}\n {resumeRunner}\n {manualEntryRunner}\n {resumeWithAIRunner}\n {startRunner}\n <div\n style={{ width: AGENTICO_WIDTH }}\n className=\"relative flex h-full shrink-0 flex-col border-l border-border bg-background\"\n >\n {/* Stessi `px-4 py-3` + `border-b/60` dell'header di Modal.tsx (framework) — la\n modale di import tema è affiancata a questo pannello, deve avere\n esattamente la stessa altezza di testata, non una vicina. L'icona robot è ORA\n sempre presente qui (non più solo nell'Header generale/come closeSlot delle\n modali) — il pannello \"sembra\" un sidebar/modale a sé, quindi deve avere la sua\n stessa affordance di apertura/chiusura sul proprio header, indipendentemente da\n cosa gli sta affianco (decisione esplicita, corregge l'asimmetria precedente). */}\n <div className=\"flex min-h-[60px] items-center gap-1 border-b border-border/60 px-4 py-3\">\n {/* `leading-tight` (1.25), non `leading-none` (1): line-height 1 ritagliava il\n discendente della \"g\" di \"Agentico\" su alcuni font — l'altezza della riga è\n comunque garantita da `items-center` sul flex padre, non serve forzarla a 1.\n Un solo task alla volta in tutta l'app (mai una tab bar, decisione esplicita:\n \"una chat unica\") — il titolo mostra semplicemente cosa sta facendo ora\n l'unica conversazione viva, o \"Agentico\" quando non ce n'è nessuna. */}\n <span className=\"min-w-0 flex-1 truncate text-lg font-semibold leading-tight\">\n {task ? task.title : 'Agentico'}\n </span>\n {/* Storico — TUTTE le chat salvate dello scope (vedi AgenticoHistoryDropdown),\n mescolate across subagent, più recenti prima. Nessuna dipendenza da\n `isOpen`/task: uno stato locale a sé, sempre disponibile col pannello aperto. */}\n {scope && (\n <AgenticoHistoryDropdown\n scopeId={scope.id}\n currentTaskId={task?.id ?? null}\n open={historyOpen}\n onOpenChange={setHistoryOpen}\n onNewChat={handleNewChat}\n onSelect={(subagentId, entityId, snapshot) => resumeEntityChat(subagentId, entityId, snapshot)}\n onArchivedCurrent={dismissForeignTask}\n />\n )}\n <AgenticoToggle />\n </div>\n\n {/* Niente `pt-*` qui — un padding-top sul contenitore di scroll resterebbe SEMPRE\n visibile sopra un elemento sticky `top-0` (lo spazio di offset per lo sticky è\n misurato dentro il padding-box di questo contenitore), lasciando lo spazietto tra\n header e bolla utente segnalato dall'utente. Lo spazio in alto si ottiene invece\n dentro il contenuto stesso (vedi il `pt-2` di AgentActivityLog), che scorre via\n normalmente invece di restare incollato all'header. */}\n <div ref={bodyRef} className=\"flex-1 overflow-y-auto px-3 pb-2\">\n {/* `pt-2` qui, non sul contenitore di scroll sopra — questo div scorre via\n normalmente coi contenuti, cosi la bolla sticky dell'utente (dentro\n AgentActivityLog) può incollarsi davvero a filo con l'header una volta\n superato, invece di lasciare sempre visibile lo spazio di questo padding. */}\n <div ref={contentRef} className=\"pt-2\">\n {task ? (\n <>\n {/* Prompt statico del percorso \"senza AI\" (vedi\n SubagentManifest.manualEntry) — mostrato SOLO finché non c'è\n ancora attività: una volta inviato il primo testo, la bolla utente\n + il risultato raccontano da soli cosa sta succedendo, questo testo\n diventerebbe solo rumore duplicato. */}\n {task.manualEntry && displayedActivity.length === 0 && (\n <p className=\"text-xs text-muted-foreground\">{task.manualEntry.prompt}</p>\n )}\n <AgentActivityLog activity={activityBeforePanelExtra} />\n {task.panelExtra && <div className=\"mt-3\">{task.panelExtra}</div>}\n {/* Conversazione successiva a `panelExtra` (retry, follow-up) — un\n secondo <ul> (AgentActivityLog ne apre uno suo), non un problema:\n lo sticky delle bolle utente è per-<li> rispetto allo scroll\n container di questo pannello, non al singolo <ul> che le contiene. */}\n {activityAfterPanelExtra.length > 0 && <AgentActivityLog activity={activityAfterPanelExtra} />}\n {!task.agent.running && task.agent.error && (\n <div className=\"mt-2 flex items-center gap-2\">\n <p className=\"flex-1 text-xs text-destructive\">{task.agent.error}</p>\n {/* Nessun \"retry dell'ultimo turno\" nativo in useAgent — un\n nuovo messaggio \"Riprova.\" nella STESSA conversazione\n (history/tool-result già raccolti restano disponibili al\n modello) è l'approssimazione più semplice, stesso\n percorso di chi ridigita a mano in chat. */}\n <ActionButton\n label={t.retry}\n variant=\"secondary\"\n className=\"shrink-0\"\n onClick={() => { void task.agent.sendMessage(t.retryMessage).then((r) => { if (r) task.onApplied(r); }); }}\n />\n {/* Ogni subagent ha un fallback deterministico a costo zero\n (vedi AgentDefinition.fallback) — quando la chiamata al\n modello fallisce (rete/provider giù, non solo AI\n disabilitata) l'utente non deve restare bloccato dietro\n \"Riprova\": può proseguire subito col risultato\n deterministico, generico per costruzione a QUALUNQUE\n subagent (nessuna label/logica specifica qui). */}\n {/* A remote agent has no deterministic result to fall back on. */}\n {!task.agent.remote && <ActionButton\n label={t.continueWithoutAi}\n variant=\"secondary\"\n className=\"shrink-0\"\n onClick={() => {\n if (manualEntryManifestForError) {\n startManualEntry(manualEntryManifestForError.id, pendingExtraDestinations);\n return;\n }\n const r = task.agent.runFallback();\n if (r) task.onApplied(r);\n }}\n />}\n </div>\n )}\n </>\n ) : anchoredUserMessages.length > 0 ? (\n // Uno o più componenti vivi sono ancorati a un subagent (vedi\n // useSubagentComponentContext) — un semplice messaggio per l'utente, non\n // un modulo di campi: il componente si popola da solo quando arriva un\n // risultato, non c'è nulla da compilare a vista qui.\n <AnchoredUserMessageView messages={anchoredUserMessages} />\n ) : pageContextCard ? (\n // Card strutturata (vedi SubagentUsageCardView sopra) quando il pageContext\n // registrato riguarda un singolo subagent-comando (es. redirects/ToolPage.tsx)\n // — zero costo/nessuna chiamata AI, e a differenza del vecchio paragrafo di prosa non\n // lascia all'utente il compito di dedurre quali campi servono per partire.\n <SubagentUsageCardView card={pageContextCard} />\n ) : (\n // Nessuna card disponibile (es. ToolsPage/RedirectsPage, pagine senza un\n // singolo subagent-comando a cui riferirsi) — resta il testo semplice del\n // pageContext, o il generico \"dimmi cosa vuoi fare\" se non c'è nemmeno quello.\n <p className=\"text-xs text-muted-foreground\">{pageContext ?? t.orchestratorStarterHint}</p>\n )}\n </div>\n </div>\n\n {/* Stessi `px-4 py-3` + `border-t/60` del footer di Modal.tsx — stessa altezza di\n partenza del footer della modale adiacente (textarea a una riga, vedi sopra).\n Sempre presente col pannello aperto (non solo quando c'è un task attivo, a\n differenza di prima): il composer resta \"stuck to bottom\" identico in entrambi i\n casi — solo il body sopra cambia (log live o l'hint), mai il composer\n stesso a spostarsi al centro del pannello. */}\n <div className=\"border-t border-border/60 px-4 py-3\">\n {/* What the controls of the host show (registerSubagentControls, CR-003), right above the composer. */}\n {task?.panelActions && <div className=\"mb-2\">{task.panelActions}</div>}\n {task\n ? <AgenticoComposer key={task.id} task={task} />\n : <AgenticoStarterComposer />}\n </div>\n </div>\n </>\n );\n}\n","import { useEffect, useRef } from 'react';\nimport { useAgentico } from './AgenticoContext';\nimport type { SubagentUsageCard } from '../domain/subagentUsageCard';\n\n/** Registra \"dove si trova l'utente adesso\" per Agentico — stesso pattern effect-based di\n * usePageBreadcrumb/usePageLeading (PageActionsContext.tsx): si registra al mount, si toglie da\n * solo allo smontaggio. Più istanze possono essere montate insieme (es. ToolsPage generico +\n * ToolHistoryPanel specifico sopra) — vince l'ultima registrata, vedi AgenticoContext.setPageContext.\n * `text === null` non registra nulla (utile per un contesto calcolato che a volte non è pronto).\n * `card` — opzionale, SOLO per la UI del pannello Agentico (vedi Agentico.tsx): quando presente\n * rimpiazza il paragrafo di prosa con una card strutturata (campi richiesti/opzionali), mentre\n * `text` resta comunque quello che arriva al prompt dell'orchestratore (vedi\n * AgenticoContext.pageContext) — due destinazioni diverse dello stesso \"dove si trova l'utente\",\n * mai lo stesso testo forzato a servire entrambe. */\nexport function useAgenticoPageContext(text: string | null, card?: SubagentUsageCard | null): void {\n const { setPageContext } = useAgentico();\n const id = useRef(crypto.randomUUID()).current;\n\n useEffect(() => {\n setPageContext(id, text, card ?? null);\n return () => setPageContext(id, null);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [text, card]);\n}\n","import { useEffect, useRef } from 'react';\nimport { useAgentico } from './AgenticoContext';\n\n/** Registra \"questo componente è il subagent X\" per Agentico — chiamato DAL componente\n * portabile stesso (es. ThemeIndexList/ThemeEditor), mai dalla pagina che lo ospita: chi monta\n * il componente ottiene gratis il contesto/hint/scoping corretto, senza ricostruirlo pagina per\n * pagina (vedi ../domain/subagentManifest.ts SubagentManifest.componentContexts per\n * dove vivono `agenticoHint`/`userMessage`).\n *\n * `componentKey` deve combaciare con una chiave di `manifest.componentContexts` — se non\n * combacia, l'anchor si registra comunque (per `onResult`) ma senza hint/messaggio (nessun\n * crash, solo nessun aggancio di prosa per l'LLM/l'utente).\n *\n * `onResult`, opzionale — chiamato da SubagentRunner quando arriva un risultato per questo\n * `subagentId` MENTRE questo componente è montato: il componente popola se stesso (Form/stato\n * locale), mai un salvataggio automatico — l'utente resta libero di rivedere e premere Salva\n * come per un edit manuale. `meta` è il TParams con cui il subagent è stato lanciato (es.\n * `{ url }` per theme) — utile quando il risultato stesso non porta un dato che serve per\n * popolare il componente (es. l'URL sorgente, per derivare un nome di default). `taskId` è l'id\n * del task Agentico che ha prodotto questo risultato — un componente che vuole poter persistere\n * la chat al proprio salvataggio (vedi AgenticoContext.commitEntityChat) lo tiene da parte in un\n * ref, altrimenti può ignorarlo. Anche una pagina\n * non-portabile (es. SiteEditorPage, per i soli campi di site-branding dentro lo stesso\n * risultato) può chiamare questo hook con lo stesso `subagentId` di un componente già montato —\n * più listener indipendenti sullo stesso risultato, ciascuno responsabile della propria fetta di\n * dati. */\nexport function useSubagentComponentContext(\n subagentId: string,\n componentKey: string,\n onResult?: (result: unknown, meta: Record<string, unknown>, taskId: string) => void,\n /** Fatti live sull'entità che QUESTA istanza ha aperta ORA (es. `Il tema aperto si chiama\n * \"ducatimilano.it\".`) — accodati all'agenticoHint statico del manifest nel prompt\n * dell'orchestratore (vedi AgenticoContext.anchorTexts), cosi un \"estrai il tema\" senza URL\n * esplicito può dedurlo dal nome/hostname già noto invece di richiederlo sempre da capo.\n * Assente/undefined = comportamento invariato. A differenza di `onResult` (in un ref, mai\n * causa di ri-registrazione) QUESTO è una dipendenza vera dell'effect sotto: deve\n * ri-registrare quando cambia (es. il nome dell'entità arriva dopo un fetch asincrono). */\n contextInfo?: string,\n /** `false` = non registra nulla (o smonta l'anchor già registrato) — per un componente che\n * resta montato anche quando \"non è più lui\" ad ancorarsi (es. RedirectsIndexList, che non\n * smonta quando mostra il dettaglio di un host: senza questo, il suo anchor 'indexList'\n * resterebbe attivo ANCHE mentre RedirectsEditor registra 'editor' per lo stesso\n * subagentId, consegnando un risultato a entrambi). Default `true` — comportamento invariato\n * per chi non lo passa. */\n enabled: boolean = true,\n /** Id dell'entità a cui QUESTA istanza è vincolata (es. l'hostId di RedirectsEditor) — vedi\n * ComponentAnchor.pinnedEntityId per il perché (backstop deterministico contro un salvataggio\n * su un'entità sbagliata, non solo un'istruzione nel prompt). Assente = nessun vincolo,\n * comportamento invariato. */\n pinnedEntityId?: string,\n): void {\n const { registerComponentAnchor } = useAgentico();\n const id = useRef(crypto.randomUUID()).current;\n // Ref, non una dipendenza diretta dell'effect sotto — altrimenti una onResult ricreata a\n // ogni render (comune, spesso una closure inline) farebbe ri-registrare l'anchor a ogni\n // render invece che solo quando cambia davvero subagentId/componentKey.\n const onResultRef = useRef(onResult);\n onResultRef.current = onResult;\n\n useEffect(() => {\n if (!enabled) return;\n registerComponentAnchor(id, {\n subagentId,\n componentKey,\n contextInfo,\n pinnedEntityId,\n onResult: (result: unknown, meta: Record<string, unknown>, taskId: string) => onResultRef.current?.(result, meta, taskId),\n });\n return () => registerComponentAnchor(id, null);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [subagentId, componentKey, contextInfo, enabled, pinnedEntityId]);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["registry","zodToJsonSchema","useI18n","createContext","useState","useRef","useCallback","useMemo","useContext","useState","Code","_Fragment","ChatMarkdown","Icon","useRef","interpolate","Tree","ChatReasoning","ChatQuestions","ActionButton","useAIProviderRegistry","useState","PromptUtils","useState","useRef","useAIProviderRegistry","parseAIModelRef","interpolate","useProxy","useState","interpolate","registry","useState","Code","useProxy","useRef","useState","useMemo","_Fragment","useMemo","useState","useRef","useRef","useState","useMemo","ActionButton","useState","Chatbot","interpolate","Badge","useI18n","useMemo","Dropdown","Icon","ActionButton","DropdownItem","useRef","_Fragment","useRef","useRef","__create","__defProp","__getOwnPropDesc","__getOwnPropNames","__getProtoOf","__hasOwnProp","__copyProps","from","keys","i","n","key","to","except","k","desc","__toESM","mod","isNodeMode","target","zod_to_json_schema","_llmnative_react","react","react_jsx_runtime","DEFAULT_TEXT_PART_ID","DEFAULT_REASONING_PART_ID","upsertMessagePart","at","parts","p","part","closed","closeReasoning","defaultText","messagePartsText","formatQuestionAnswers","answers","answer","questions","defineSubagent","config","registry$2","registerSubagents","manifests","getSubagentRegistry","findSubagentManifest","m","id","factories","registerCrossAgentTools","toolFactories","createCrossAgentTools","factory","context","toJsonSchema","rest","schema","toMcpTool","inputSchema","outputSchema","describeCommandFields","command","properties","required","def","description","agenticoPromptsEn","agenticoPromptsIt","activePrompts","configureAgenticoPrompts","prompts","getAgenticoPrompts","resetAgenticoPrompts","renderInputSchemaUsage","commandName","fields","f","renderCommand","usage","paramLines","subagentId","indent","line","pad","spaces","text","renderSubagentCli","manifest","c","renderRegistryCli","registry","SUMMARY_MICRO_PROMPT","MICRO_PROMPT_REGISTRY","applyMicroPrompts","ids","prompt","fragments","t","extractErrorMessage","err","e","inner","extractJsonBlock","fenced","raw","stripped","extractFirstBalancedJsonValue","start","open","close","depth","inString","escaped","ch","JSON_EXPORT_DESTINATION_ID","jsonExportDestination","blob","result","url","a","hostDestinations","registerDestinations","destinations","getDestinationRegistry","storageKey","configureEntityChatStore","options","normalize","entry","record","scopeId","_legacy","readAll","parsed","writeAll","entries","entityId","getEntityChat","saveEntityChat","snapshot","next","removeEntityChat","listEntityChats","b","entityChatTaskId","agenticoDe","agenticoEn","agenticoIt","agenticoTranslations","useAgenticoI18n","fromHost","dict","ORCHESTRATOR_TASK_ID","AgenticoContext","AgenticoProvider","task","setTaskState","taskRef","isOpen","setIsOpen","openTask","current","merged","closeTask","prev","toggleOpen","v","openPanel","pendingGoal","setPendingGoal","pendingExtraDestinations","setPendingExtraDestinations","pendingAttachments","setPendingAttachments","pendingModel","setPendingModel","orchestratorRunId","setOrchestratorRunId","pendingCarryOver","setPendingCarryOver","pendingScopedSubagentId","setPendingScopedSubagentId","pageContextEntries","setPageContextEntries","setPageContext","lastPageContextEntry","componentAnchorEntries","setComponentAnchorEntries","registerComponentAnchor","anchor","componentAnchors","uniqueAnchors","bySubagent","anchorTexts","ctx","agenticoHint","x","anchoredUserMessages","pageContext","list","pageContextCard","uniqueAnchoredSubagentIds","scopedSubagentId","startOrchestrator","existingTask","goal","commitEntityChat","taskId","snap","pendingResume","setPendingResume","resumeRunId","setResumeRunId","resumeEntityChat","pendingManualEntry","setPendingManualEntry","manualEntryRunId","setManualEntryRunId","startManualEntry","pendingResumeWithAI","setPendingResumeWithAI","resumeWithAIRunId","setResumeWithAIRunId","resumeWithAI","pendingStart","setPendingStart","startRunId","setStartRunId","startSubagent","dismissForeignTask","value","scope","useAgentico","splitUnfencedJsonSegments","end","candidate","segments","parseDetailSegments","fenceRe","lastIndex","match","DetailCodeBlock","expanded","setExpanded","content","DetailContent","seg","ActivityStatusIcon","status","thinkingDisplayLabel","USER_BUBBLE_COLLAPSED_MAX_HEIGHT","UserMessageBubble","contentRef","overflowing","setOverflowing","el","collapsed","name","STEP_ICONS","StepsGroup","running","steps","s","failed","summary","step","MessageParts","blocks","last","answerable","block","ActivityRow","showStatus","codeSegments","textSegments","label","AgentActivityLog","activity","turns","turn","AgenticoToggle","className","SELECTED_MODEL_STORAGE_KEY","readStoredModel","writeStoredModel","useAIModelCatalog","hasProviders","modelsByProvider","setModelsByProvider","modelsLoading","setModelsLoading","selectedModel","setSelectedModelState","cancelled","catalog","flat","error","MAX_TOOL_ROUNDTRIPS","MAX_INLINE_TEXT_ATTACHMENT_BASE64_LENGTH","filterInlineAttachments","attachments","useAgent","enabled","setEnabled","defaultEnabled","setRunning","setError","canContinue","setCanContinue","setActivityState","activityRef","setActivity","pendingQuestionsRef","labels","labelsRef","sessionRef","runningRef","abortControllerRef","stop","ref","provider","executor","definition","remote","remoteModels","setRemoteModels","remoteModel","setRemoteModelState","remoteModelRef","setRemoteModel","model","offerRemoteModels","params","runRemoteTurn","session","controller","thinkingId","reasoningStarted","secondsSince","updateParts","change","detail","upsert","textWithId","onText","partial","onReasoning","onStep","turnQuestions","questionsCount","rejectPendingQuestions","pending","onQuestions","finish","finalText","askedText","reply","answerQuestions","entryId","partId","markAnswered","runTurn","effectiveRef","overrideModel","effectiveProvider","aiAttachments","attachmentsHistoryText","inlineAttachments","toolDefs","toolsByName","setEntryStatus","toolNames","history","promptText","userText","roundTrip","toolIntentLabels","tool","call","toolResults","output","closingId","closingResult","setSelectedModel","opts","attachmentsRef","tools","hasProvider","fallbackResult","file","seed","describeOrchestratorResponseContract","rule","buildOrchestratorPrompt","pageContextSection","cliCatalog","userGoal","parseOrchestratorResponse","handoff","offeredSubagents","createOrchestratorAgentDefinition","allCrossAgentTools","scopedManifest","crossAgentTools","handoffTools","handoffParams","input","deriveHistoryTitle","ResultDestinationPicker","sidebar","proxyFetch","setPending","done","setDone","initiallyApplied","destinationMap","d","extraDestinations","run","destinationId","destination","outcome","meta","onApplied","registry$1","registerSubagentReports","reports","getSubagentReport","GenericResultPreview","buildLiveAnchorDestination","matching","registerSubagentControls","controls","getSubagentControls","noControls","SubagentRunner","taskIdProp","agent","setResult","appliedIds","setAppliedIds","autoApplyError","setAutoApplyError","reportModule","selectionModule","selection","setSelection","startedRef","panelExtraAnchorIndexRef","anchoredResultRef","precedingActivity","dismissedRef","readyToStart","resumeSeed","r","autoIds","pinnedEntityId","handleSelectionChange","handleResultChange","filteredResult","matchingAnchors","liveAnchorDestination","pickerExtraDestinations","chatEntityId","wasRunningRef","ended","dismiss","useControls","handleDestinationApplied","OrchestratorRunner","setHandoff","extraDestinationsRef","modelRef","scopedSubagentIdRef","carryOverRef","handleResult","combinedPrecedingActivity","ManualEntryRunner","lastParams","setLastParams","handleSubmit","rawText","AGENTICO_WIDTH","flattenModelOptions","AgenticoComposer","setText","manualEntry","payload","noModel","modelRequired","models","AgenticoStarterComposer","relativeTime","seconds","now","format","locale","unit","size","SubagentUsageCardView","card","AnchoredUserMessageView","messages","AgenticoHistoryDropdown","search","setSearch","version","setVersion","archive","question","currentTaskId","onArchivedCurrent","filtered","q","onNewChat","onSelect","Agentico","bodyRef","historyOpen","setHistoryOpen","handleNewChat","displayedActivity","activityCount","panelExtraAnchorIndex","activityBeforePanelExtra","activityAfterPanelExtra","hasError","liveEntryIds","scopedManifestForError","manualEntryManifestForError","isNearBottomRef","handleScroll","lastDetailLength","observer","orchestrator","resumeManifest","resumeRunner","manualEntryManifest","manualEntryRunner","resumeWithAIManifest","resumeWithAIRunner","startManifest","startRunner","useAgenticoPageContext","useSubagentComponentContext","onResultRef","onResult","componentKey","contextInfo"],"sources":["../src/domain/messageParts.ts","../src/domain/defineSubagent.ts","../src/domain/subagentRegistry.ts","../src/domain/crossAgentTools.ts","../src/domain/mcpTool.ts","../src/domain/subagentUsageCard.ts","../src/domain/prompts.ts","../src/domain/cli/renderManifestCli.ts","../src/domain/microPrompts.ts","../src/domain/extractErrorMessage.ts","../src/domain/extractJsonBlock.ts","../src/domain/jsonExportDestination.ts","../src/domain/destinationRegistry.ts","../src/domain/entityChatStore.ts","../src/i18n/de.ts","../src/i18n/en.ts","../src/i18n/it.ts","../src/i18n/index.ts","../src/presentation/AgenticoContext.tsx","../src/presentation/AgentActivityLog.tsx","../src/presentation/AgenticoToggle.tsx","../src/presentation/useAIModelCatalog.ts","../src/presentation/useAgent.ts","../src/domain/orchestrator/prompt.ts","../src/domain/orchestrator/agenticoOrchestratorAgent.ts","../src/presentation/deriveHistoryTitle.ts","../src/presentation/ResultDestinationPicker.tsx","../src/presentation/reports/subagentReportRegistry.tsx","../src/presentation/reports/GenericResultPreview.tsx","../src/presentation/liveAnchorDestination.ts","../src/presentation/subagentControls.ts","../src/presentation/SubagentRunner.tsx","../src/presentation/OrchestratorRunner.tsx","../src/presentation/ManualEntryRunner.tsx","../src/presentation/Agentico.tsx","../src/presentation/useAgenticoPageContext.ts","../src/presentation/useSubagentComponentContext.ts"],"sourcesContent":["// The parts of a message of an agent (CR-004), on the model of the Vercel AI SDK and AG-UI: what a\n// remote executor reports during a turn (text, reasoning, steps, questions) is kept as a list of\n// parts, in the order they arrive, each shown by its own component. Plain data: saved with the chat\n// (EntityChatSnapshot) and reloaded as it is.\n\n/** A choice of a question (the shape of the `question` tool of opencode and of MCP elicitation). */\nexport interface AgentQuestionOption {\n /** What the answer says: it is the value returned when chosen. */\n label: string;\n /** A line under the label. */\n description?: string;\n}\n\n/** A question of the agent to the person. */\nexport interface AgentQuestion {\n /** Returned with the answer, to match it to the question. */\n id?: string;\n /** Short title: the label of its tab. */\n header?: string;\n question: string;\n /** The choices. Without them the question takes a free-text answer. */\n options?: AgentQuestionOption[];\n /** More than one choice. */\n multiple?: boolean;\n /** The choice the agent recommends: its index (from 0) or its label. */\n recommended?: number | string;\n /** Offers \"Other\" with a free-text answer (default: yes). */\n custom?: boolean;\n}\n\n/** The answer to one question: the chosen labels and/or the free text, in the order of the questions. */\nexport interface AgentQuestionAnswer {\n id?: string;\n header?: string;\n values: string[];\n}\n\n/** The answers to a set of questions, as a turn gives them to the executor. `id` is the id of the\n * questions part (the one given to `onQuestions`, or the one Agentico made up). */\nexport interface AgentQuestionsAnswers {\n id: string;\n answers: AgentQuestionAnswer[];\n}\n\n/** Text of the reply, as Markdown. `streaming` while it is still arriving. */\nexport interface AgentTextPart {\n type: 'text';\n id: string;\n text: string;\n streaming?: boolean;\n}\n\n/** The reasoning of the model, as Markdown, shown closed behind \"Reasoned for 12 s\". */\nexport interface AgentReasoningPart {\n type: 'reasoning';\n id: string;\n text: string;\n streaming?: boolean;\n /** Seconds the model reasoned: given by the executor, or measured by Agentico. */\n duration?: number;\n}\n\n/** A step of the agent (a tool it ran, a file it wrote). Consecutive steps are shown as one group. */\nexport interface AgentToolPart {\n type: 'tool';\n id: string;\n label: string;\n status: 'running' | 'done' | 'error' | 'stopped';\n}\n\n/** Questions to the person, answered with a click. `answers` once answered (shown read-only). */\nexport interface AgentQuestionsPart {\n type: 'questions';\n id: string;\n questions: AgentQuestion[];\n answers?: AgentQuestionAnswer[];\n}\n\nexport type AgentMessagePart = AgentTextPart | AgentReasoningPart | AgentToolPart | AgentQuestionsPart;\n\n/** The id of the text part of an executor that calls `onText` without an id (the reply of CR-003). */\nexport const DEFAULT_TEXT_PART_ID = 'text';\n/** The id of the reasoning part of an executor that calls `onReasoning` without an id. */\nexport const DEFAULT_REASONING_PART_ID = 'reasoning';\n\n/** Adds `part`, or replaces the part of the same type and id. A new step or reasoning goes above the\n * default text part, so the reply (as in CR-003) stays below the work that led to it; anything else\n * is appended. `closeReasoning` closes the reasoning still streaming when a part of another type\n * arrives (the model moved on), with its duration. */\nexport function upsertMessagePart(\n parts: AgentMessagePart[],\n part: AgentMessagePart,\n closeReasoning?: (part: AgentReasoningPart) => AgentReasoningPart,\n): AgentMessagePart[] {\n const at = parts.findIndex((p) => p.type === part.type && p.id === part.id);\n if (at >= 0) return parts.map((p, i) => (i === at ? part : p));\n const closed = part.type === 'reasoning' || !closeReasoning\n ? parts\n : parts.map((p) => (p.type === 'reasoning' && p.streaming ? closeReasoning(p) : p));\n const defaultText = closed.findIndex((p) => p.type === 'text' && p.id === DEFAULT_TEXT_PART_ID);\n if ((part.type === 'tool' || part.type === 'reasoning') && defaultText >= 0) {\n return [...closed.slice(0, defaultText), part, ...closed.slice(defaultText)];\n }\n return [...closed, part];\n}\n\n/** The text of a message: its text parts, one paragraph each (what `AgentActivityEntry.detail` holds). */\nexport function messagePartsText(parts: readonly AgentMessagePart[]): string {\n return parts.filter((p): p is AgentTextPart => p.type === 'text').map((p) => p.text).filter(Boolean).join('\\n\\n');\n}\n\n/** The answers as one line per question (\"Database: Postgres\"), for the history of the chat. */\nexport function formatQuestionAnswers(questions: readonly AgentQuestion[], answers: readonly AgentQuestionAnswer[]): string {\n return answers.map((answer, i) => {\n const title = answer.header ?? questions[i]?.header ?? questions[i]?.question ?? answer.id ?? `${i + 1}`;\n return `${title}: ${answer.values.join(', ')}`;\n }).join('\\n');\n}\n","// Punto unico che uno sviluppatore deve passare per creare un subagent — sostituisce\n// l'oggetto letterale libero usato finora (vedi themeSubagentManifest.ts prima della\n// migrazione): qui i generici TParams/TContext/TResult sono dedotti da `definition`, e\n// TypeScript segnala subito se `toParams` non ritorna il TParams che l'AgentDefinition si\n// aspetta, o se manca un campo del manifest — lo stesso livello di guida che darebbe\n// \"estendere una classe\", senza introdurre eredità in un codebase che non la usa altrove.\nimport type { AgentDefinition } from './agentDefinition';\nimport type { SubagentManifest } from './subagentManifest';\n\nexport function defineSubagent<TParams, TContext, TResult>(\n config: Omit<SubagentManifest<TParams, TContext, TResult>, 'definition'> & {\n definition: AgentDefinition<TParams, TContext, TResult>;\n },\n): SubagentManifest<TParams, TContext, TResult> {\n return config;\n}\n","// Il registro dei subagent — store mutabile popolato UNA VOLTA dall'host all'avvio (vedi\n// registerSubagents sotto), MAI un array statico che importa manifest concreti da qui: Agentico\n// (questo modulo) non deve conoscere quali subagent esistono in un progetto che lo consuma —\n// altrimenti un'estrazione futura in un pacchetto a sé porterebbe con sé una dipendenza diretta\n// su theme/redirects/html-to-liquid, specifici di QUESTO CMS. L'host (vedi\n// src/application/context/CmsProviders.tsx) importa i propri manifest e li registra qui a\n// livello di modulo, prima che qualunque componente sotto AgenticoProvider possa leggerli.\nimport type { SubagentManifest } from './subagentManifest';\n\nlet registry: ReadonlyArray<SubagentManifest<any, any, any>> = [];\n\n/** Chiamata dall'host UNA VOLTA, a livello di modulo (non dentro un componente React) — un\n * secondo giro SOSTITUISCE il registro precedente, non lo somma: niente doppie registrazioni\n * accidentali (es. da un hot-reload) da dover deduplicare a valle. */\nexport function registerSubagents(manifests: ReadonlyArray<SubagentManifest<any, any, any>>): void {\n registry = manifests;\n}\n\n/** Letto da codice che ha bisogno dell'INTERO elenco (es. la Grid di ToolsPage, il catalogo CLI\n * dell'orchestratore) — mai per costruzione statica a import-time: se questo ritorna `[]` è\n * quasi sempre perché `registerSubagents` non è ancora stato chiamato (o il modulo che lo chiama\n * non è ancora stato importato) prima di questo punto, non un progetto senza subagent. */\nexport function getSubagentRegistry(): ReadonlyArray<SubagentManifest<any, any, any>> {\n if (registry.length === 0 && typeof console !== 'undefined') {\n console.warn('[agentico] getSubagentRegistry() letto a registro vuoto — registerSubagents() non è ancora stato chiamato?');\n }\n return registry;\n}\n\nexport function findSubagentManifest(id: string): SubagentManifest<any, any, any> | undefined {\n return registry.find((m) => m.id === id);\n}\n","// Cross-agent tools: data-collection tools the orchestrator can call while it gathers the\n// parameters of a subagent command (read a sitemap, list the columns of an attached CSV, scrape\n// a page…). Agentico ships none of them: the host registers a factory once at startup, exactly\n// like registerSubagents().\n//\n// Tools often produce large values (hundreds of paths) that the model would have to copy again\n// into the final command, and models truncate long arrays when they do. So a tool can also write\n// its complete output into `collected`, a per-conversation scratchpad; the subagent the\n// conversation hands off to reads it back through SubagentManifest.withCollected.\nimport type { AgentTool, LatestAttachmentsRef } from './agentDefinition';\n\nexport interface CrossAgentToolContext {\n /** Raw attachments of the current turn — for tools that read files too big for the prompt. */\n attachmentsRef: LatestAttachmentsRef | undefined;\n /** Per-conversation scratchpad shared with the subagent manifests (see withCollected). */\n collected: Record<string, unknown>;\n /** CORS-safe fetch of the host app (the framework's proxyFetch). */\n proxyFetch: typeof fetch;\n}\n\nexport type CrossAgentToolFactory = (context: CrossAgentToolContext) => AgentTool[];\n\nlet factories: CrossAgentToolFactory[] = [];\n\n/** Registers the host's cross-agent tools. Called once at startup; a later call replaces the set. */\nexport function registerCrossAgentTools(...toolFactories: CrossAgentToolFactory[]): void {\n factories = toolFactories;\n}\n\nexport function createCrossAgentTools(context: CrossAgentToolContext): AgentTool[] {\n return factories.flatMap((factory) => factory(context));\n}\n","// Punto unico di conversione Zod → JSON Schema — usato SOLO ai due bordi che hanno\n// davvero bisogno della forma serializzata (il provider AI in useAgent.ts, che parla\n// JSON Schema sul wire come ogni function-calling API; il render CLI in\n// cli/renderManifestCli.ts). Ovunque altro nel dominio (AgentTool, SubagentCommand) lo\n// schema resta un vero z.ZodType — un solo formato \"di verità\", mai due dialetti.\n//\n// La forma `McpTool` è quella del formato MCP Tool (Model Context Protocol) —\n// name/description/inputSchema JSON-Schema (+ outputSchema opzionale): non un'invenzione\n// interna, così ogni subagent resta esponibile 1:1 come vero MCP tool in futuro, senza\n// ulteriore lavoro di adattamento.\nimport { z } from 'zod';\nimport { zodToJsonSchema } from 'zod-to-json-schema';\n\nexport interface McpTool {\n name: string;\n description: string;\n inputSchema: Record<string, unknown>;\n outputSchema?: Record<string, unknown>;\n}\n\nexport function toJsonSchema(schema: z.ZodType): Record<string, unknown> {\n // zodToJsonSchema inserisce sempre `$schema` (un meta-riferimento al draft, non parte\n // dello schema stesso) alla radice dell'output. Nel wire format function-calling\n // (OpenAI-compatible), quel campo finisce dentro `tools[].function.parameters` — dove\n // non è previsto — e alcuni backend (verificato con OpenCode Zen: falliva su OGNI\n // modello, in modo consistente, non transitorio) rispondono 400/500 non appena\n // `parameters` lo contiene.\n const { $schema, ...rest } = zodToJsonSchema(schema, { target: 'jsonSchema7', $refStrategy: 'none' }) as Record<string, unknown>;\n return rest;\n}\n\nexport function toMcpTool(name: string, description: string, inputSchema: z.ZodType, outputSchema?: z.ZodType): McpTool {\n return {\n name,\n description,\n inputSchema: toJsonSchema(inputSchema),\n ...(outputSchema ? { outputSchema: toJsonSchema(outputSchema) } : {}),\n };\n}\n","// Card deterministica \"cosa serve per avviare una nuova istanza di questo subagent\" — stessa\n// fonte (Zod → JSON Schema, vedi mcpTool.ts) già usata dal render CLI (cli/renderManifestCli.ts)\n// e dal wire del provider AI (function calling in useAgent.ts), qui proiettata per l'UI di\n// Agentico (ToolHistoryPanel/Agentico.tsx) invece che per testo --help o per il tool schema. Un\n// solo posto che estrae \"quali campi, obbligatori o no, con che descrizione\" da un\n// SubagentCommand — mai duplicato a mano in prosa per ogni subagent.\nimport type { SubagentCommand, SubagentManifest } from './subagentManifest';\nimport { toJsonSchema } from './mcpTool';\n\nexport interface SubagentUsageField {\n key: string;\n type: string;\n required: boolean;\n /** Tecnica, machine-facing — la `.describe()` Zod tale e quale, stessa fonte del JSON\n * Schema/CLI help. Verbosa ed enumerativa per progettazione: un LLM sbaglia meno con casi\n * limite espliciti, un umano di passaggio no. */\n description: string;\n /** Human-facing, per la card di Agentico — `command.humanFieldHints[key]` se presente,\n * altrimenti ripiega su `description` (mai vuota, mai un secondo giro di scrittura\n * obbligato per ogni subagent). */\n humanDescription: string;\n}\n\nexport interface SubagentUsageCard {\n subagentId: string;\n label: string;\n commandName: string;\n /** Tecnica, machine-facing — vedi SubagentCommand.summary. */\n summary: string;\n /** Human-facing — `command.humanSummary` se presente, altrimenti ripiega su `summary`. */\n humanSummary: string;\n fields: SubagentUsageField[];\n example?: string;\n /** Quando true, il consumer UI (SubagentUsageCardView) mostra SOLO `humanSummary` — niente\n * `fields`/`example` (pensati per l'occhio tecnico) — vedi SubagentCommand.humanSummaryOnly\n * per il perché. */\n humanSummaryOnly: boolean;\n}\n\n/** Estrae la lista di campi (nome/tipo/obbligatorietà/descrizione) dallo z.ZodType di un\n * comando — riusata sia qui sia da renderManifestCli.ts, che altrimenti duplicherebbe la stessa\n * lettura di `properties`/`required` dal JSON Schema. */\nexport function describeCommandFields(command: SubagentCommand): SubagentUsageField[] {\n const schema = toJsonSchema(command.inputSchema);\n const properties = (schema.properties as Record<string, Record<string, unknown>> | undefined) ?? {};\n const required = new Set((schema.required as string[] | undefined) ?? []);\n return Object.entries(properties).map(([key, def]) => {\n const description = typeof def.description === 'string' ? def.description : '';\n return {\n key,\n type: typeof def.type === 'string' ? def.type : 'string',\n required: required.has(key),\n description,\n humanDescription: command.humanFieldHints?.[key] ?? description,\n };\n });\n}\n\n/** Il comando primario di un manifest (oggi quasi sempre l'unico, vedi SubagentManifest.commands)\n * proiettato in una card — `null` solo se un manifest non ne dichiara nessuno (non dovrebbe\n * succedere per un subagent registrato, ma il chiamante UI non deve crashare se succede). */\nexport function buildSubagentUsageCard(manifest: SubagentManifest<any, any, any>): SubagentUsageCard | null {\n const command = manifest.commands[0];\n if (!command) return null;\n return {\n subagentId: manifest.id,\n label: manifest.label,\n commandName: command.name,\n summary: command.summary,\n humanSummary: command.humanSummary ?? command.summary,\n fields: describeCommandFields(command),\n example: command.example,\n humanSummaryOnly: command.humanSummaryOnly ?? false,\n };\n}\n","// Model-facing text: what Agentico itself sends to the AI provider (orchestrator prompt, response\n// contract, closing-summary micro-prompt, the \"stop calling tools\" nudge, the queued handoff note).\n// User-facing text lives in the `agentico` i18n namespace instead (i18n/), never here.\n//\n// One pack per language. The host picks one and extends it once at startup with\n// configureAgenticoPrompts() — e.g. its own orchestrator intro and the rules that only make sense\n// for its own subagents and cross-agent tools. Agentico never mentions a concrete subagent here.\n\nexport interface AgenticoPrompts {\n /** First paragraph of the orchestrator prompt: who the orchestrator is and what it does. */\n orchestratorIntro: string;\n /** Bullet rules of the orchestrator response contract (generic ones first, host ones after). */\n responseRules: string[];\n responseRulesHeading: string;\n toolsHeading: string;\n pageContextHeading: string;\n pageContextIntro: string;\n userRequestHeading: string;\n /** CLI catalog shown when no subagent is registered. */\n noToolsAvailable: string;\n /** Sent to the model when it keeps calling tools past the round-trip limit. */\n stopCallingTools: string;\n /** Returned to the model by a handoff tool: the work is queued, not done. `{label}` = subagent. */\n handoffQueued: string;\n /** Closing-summary micro-prompt (see microPrompts.ts). */\n closingSummary: string;\n /** Page context preamble when several subagents are anchored on the same page. */\n multipleCandidates: string;\n}\n\nexport const agenticoPromptsEn: AgenticoPrompts = {\n orchestratorIntro:\n 'You are Agentico, the orchestrator of this application\\'s tools. Your job is to understand what the user wants to achieve and, once every required parameter is collected, start the right tool among those listed below — do not carry it out yourself \"in words\", invoke it as a tool.',\n responseRules: [\n 'BEFORE asking for any parameter, check the \"Page context\" below (if present): if a parameter required by the command is already there or can be deduced from it with reasonable certainty, USE it directly — do not ask the user again. Only ask for parameters that cannot be derived from the context.',\n 'The \"<subagent>_<command>\" command you close with is the one that does the real work (analysis, matching, generation, extraction — whatever its description says) — NEVER try to produce that result yourself in chat once the raw data is collected.',\n 'If parameters are really missing (not deducible from the context nor from a data-collection tool) for one of the listed commands, ask in natural language, one question at a time, until you have everything.',\n 'When you have ALL required parameters of ONE command, actually invoke the tool with that name (\"<subagent>_<command>\") passing the parameters — do not just describe it, and do not replace the work that command is designed to do.',\n 'Invoke at most one command per turn.',\n 'If the user\\'s request is not covered by any of the listed tools, say so clearly instead of inventing one.',\n 'IMPORTANT: when you invoke a \"<subagent>_<command>\" command, your comment in THIS turn must only announce the START of the work — NEVER describe its outcome as already obtained, and NEVER say that something \"opened\" or \"is ready to review\". The real work starts AFTER this turn, in a session you will never see: any description of the outcome at this point would be invented, even if the tool answered without errors.',\n 'After invoking a command, do not spontaneously suggest other subagents unrelated to the current request — the user will open a new conversation if they need something else.',\n ],\n responseRulesHeading: '## How to answer',\n toolsHeading: '## Available tools',\n pageContextHeading: '## Page context',\n pageContextIntro: 'The user is interacting from this point of the app — use it to understand what they refer to when the request is not explicit:',\n userRequestHeading: '## User request',\n noToolsAvailable: 'No tools available.',\n stopCallingTools: 'Stop calling tools. Answer NOW with the final result, in the required format, using everything collected so far.',\n handoffQueued: '{label} has been queued for the real execution — the result is not available in this conversation yet.',\n closingSummary: [\n 'When you are done answering (with or without tools in this turn),',\n 'ALWAYS close your text answer with a concise but complete summary of what',\n 'you did — a few lines, a bullet list when there is more than one action, no',\n 'needless repetition of the original task. The summary goes AFTER the JSON',\n 'block requested above, never before it and never instead of it.',\n ].join(' '),\n multipleCandidates: 'Several subagents are candidates on this page — ask the user which one they mean if their message does not make it clear, before going on.',\n};\n\nexport const agenticoPromptsIt: AgenticoPrompts = {\n orchestratorIntro:\n 'Sei Agentico, l\\'orchestratore dei tool di questa applicazione. Il tuo compito è capire cosa vuole ottenere l\\'utente e, una volta raccolti tutti i parametri necessari, avviare il tool giusto tra quelli elencati sotto — non eseguirlo tu stesso \"a parole\", invocalo come strumento.',\n responseRules: [\n 'PRIMA di chiedere qualunque parametro, controlla il \"Contesto pagina\" (se presente) qui sotto: se un parametro richiesto dal comando è già lì o si può dedurre da lì con ragionevole certezza (es. l\\'utente sta guardando lo storico di un tool specifico, o un\\'esecuzione passata con parametri simili), USALO direttamente — non chiederlo di nuovo all\\'utente. Chiedi solo i parametri che NON emergono in nessun modo dal contesto.',\n 'Il comando \"<subagent>_<comando>\" con cui chiudi è quello che fa il lavoro di analisi vero (matching, generazione, estrazione — qualunque cosa il suo comando descriva) — MAI provare a produrlo tu stesso in chat una volta raccolti i dati grezzi.',\n 'Se mancano davvero parametri (non deducibili dal contesto né da un tool di raccolta dati) per uno dei comandi elencati, fai domande in linguaggio naturale, una alla volta, finché non hai raccolto tutto.',\n 'Quando hai TUTTI i parametri richiesti di UN comando (raccolti dall\\'utente, dedotti dal contesto, o ottenuti da un tool di raccolta dati), invoca davvero lo strumento con quel nome (\"<subagent>_<comando>\") passandogli i parametri — non limitarti a descriverlo a parole, e non sostituirti al lavoro che quel comando è progettato per fare.',\n 'Invoca al massimo un comando per turno.',\n 'Se la richiesta dell\\'utente non è coperta da nessuno dei tool elencati, dillo chiaramente invece di inventarne uno.',\n 'IMPORTANTE: quando invochi un comando \"<subagent>_<comando>\", il tuo commento in QUESTO turno deve annunciare solo l\\'AVVIO del lavoro (es. \"Avvio l\\'estrazione…\", \"Sto preparando il mapping…\") — MAI descriverne l\\'esito come già ottenuto (colori, font, loghi, mapping, o qualunque altro dato del risultato) e MAI dire che qualcosa \"si è aperto\" o \"è pronto da rivedere\". Il vero lavoro parte DOPO questo turno, in una sessione che tu non vedrai mai: qualunque tua descrizione dell\\'esito a questo punto sarebbe un\\'invenzione, anche se il tool ha risposto senza errori.',\n 'Dopo aver invocato un comando, non proporre spontaneamente altri subagent scollegati dalla richiesta corrente — l\\'utente aprirà lui una nuova conversazione se gli servirà altro.',\n ],\n responseRulesHeading: '## Come rispondere',\n toolsHeading: '## Tool disponibili',\n pageContextHeading: '## Contesto pagina',\n pageContextIntro: 'L\\'utente sta interagendo da questo punto dell\\'app — usalo per capire a cosa si riferisce se non è esplicito nella richiesta:',\n userRequestHeading: '## Richiesta dell\\'utente',\n noToolsAvailable: 'Nessun tool disponibile.',\n stopCallingTools: 'Basta chiamare tool. Rispondi ORA con il risultato finale, nel formato richiesto, usando tutte le informazioni raccolte finora.',\n handoffQueued: '{label} è stato messo in coda per l\\'esecuzione reale — il risultato non è ancora disponibile in questa conversazione.',\n closingSummary: [\n 'Quando hai finito di rispondere (con o senza aver usato dei tool in questo turno),',\n 'chiudi SEMPRE la tua risposta testuale con un riepilogo conciso ma esaustivo di cosa',\n 'hai fatto — poche righe, elenco puntato se le azioni sono più di una, niente',\n 'ripetizioni superflue del compito originale. Il riepilogo va DOPO il blocco JSON',\n 'richiesto sopra, mai prima e mai al posto suo.',\n ].join(' '),\n multipleCandidates: 'Più subagent sono candidati su questa pagina — chiedi all\\'utente quale intende se non è chiaro dal suo messaggio, prima di procedere.',\n};\n\nlet activePrompts: AgenticoPrompts = agenticoPromptsEn;\n\n/** Sets the model-facing pack once at startup — e.g. `configureAgenticoPrompts({ ...agenticoPromptsIt,\n * responseRules: [...agenticoPromptsIt.responseRules, ...hostRules] })`. Fields left out keep their value. */\nexport function configureAgenticoPrompts(prompts: Partial<AgenticoPrompts>): void {\n activePrompts = { ...activePrompts, ...prompts };\n}\n\nexport function getAgenticoPrompts(): AgenticoPrompts {\n return activePrompts;\n}\n\n/** Restores the English defaults (tests). */\nexport function resetAgenticoPrompts(): void {\n activePrompts = agenticoPromptsEn;\n}\n","// Rendering deterministico (nessuna chiamata LLM) di un SubagentManifest in testo \"CLI --help\".\n// Usato dall'orchestratore Agentico (buildContext, che gira SEMPRE — vedi agentDefinition.ts)\n// per il discovery: il modello legge questo testo, non JSON Schema grezzo — più denso, più\n// leggibile, e allineato al vocabolario \"usage/example\" con cui i modelli sono più addestrati a\n// ragionare su strumenti a riga di comando.\nimport type { SubagentCommand, SubagentManifest } from '../subagentManifest';\nimport { describeCommandFields } from '../subagentUsageCard';\nimport { getAgenticoPrompts } from '../prompts';\n\nfunction renderInputSchemaUsage(commandName: string, fields: ReturnType<typeof describeCommandFields>): string {\n const flags = fields.map((f) => (f.required ? `--${f.key} <${f.type}>` : `[--${f.key} <${f.type}>]`));\n return `${commandName} ${flags.join(' ')}`.trim();\n}\n\nfunction renderCommand(subagentId: string, command: SubagentCommand): string {\n // command.inputSchema è Zod (sorgente unica) — la lista di campi (nome/tipo/obbligatorietà/\n // descrizione) è la stessa estratta da subagentUsageCard.ts per la card UI di Agentico,\n // mai duplicata a mano qui.\n const fields = describeCommandFields(command);\n const usage = renderInputSchemaUsage(command.name, fields);\n const paramLines = fields.map((f) =>\n ` --${f.key} <${f.type}> (${f.required ? 'required' : 'optional'})${f.description ? ` — ${f.description}` : ''}`);\n\n return [\n `COMMAND: ${subagentId} ${command.name}`,\n ` ${command.summary}`,\n '',\n ` USAGE`,\n ` ${subagentId} ${usage}`,\n paramLines.length ? '' : '',\n paramLines.length ? ` PARAMS` : '',\n ...paramLines,\n '',\n ` OUTPUT`,\n indent(command.describeOutput(), 4),\n command.example ? '' : '',\n command.example ? ` EXAMPLE` : '',\n command.example ? ` ${command.example}` : '',\n ].filter((line) => line !== '').join('\\n');\n}\n\nfunction indent(text: string, spaces: number): string {\n const pad = ' '.repeat(spaces);\n return text.split('\\n').map((line) => (line ? pad + line : line)).join('\\n');\n}\n\n/** Help completo di UN subagent — tutte le sue command, la sua descrizione. */\nexport function renderSubagentCli<TParams, TContext, TResult>(\n manifest: SubagentManifest<TParams, TContext, TResult>,\n): string {\n return [\n `NAME`,\n ` ${manifest.id} — ${manifest.label}`,\n '',\n `DESCRIPTION`,\n indent(manifest.description, 2),\n '',\n ...manifest.commands.map((c) => renderCommand(manifest.id, c)),\n ].join('\\n');\n}\n\n/** Indice sintetico di TUTTI i subagent registrati — una riga per subagent, i suoi comandi\n * elencati per nome. Questa è la vista che l'orchestratore Agentico usa come punto di partenza\n * (\"cosa esiste\") prima di approfondire un singolo subagent con renderSubagentCli(). */\nexport function renderRegistryCli(registry: ReadonlyArray<SubagentManifest<any, any, any>>): string {\n if (registry.length === 0) return getAgenticoPrompts().noToolsAvailable;\n return registry\n .map((m) => `${m.id} [${m.commands.map((c) => c.name).join('|')}] — ${m.label}: ${m.commands.map((c) => c.summary).join(' / ')}`)\n .join('\\n');\n}\n","// Layer di micro-prompt trasversali — frammenti di istruzione che si aggiungono in coda al\n// prompt VERTICALE di un agente specifico (theme-extract, html-to-liquid, e i prossimi)\n// SENZA che quell'agente debba conoscerne il testo o duplicarlo nel proprio buildPrompt/\n// buildFollowUpPrompt. Applicati centralmente da useAgent (l'unico esecutore, vedi\n// agentDefinition.ts) — un agente si limita a dichiarare QUALI vuole via\n// `AgentDefinition.microPromptIds`, un elenco di id che referenzia questo registro.\n//\n// Perché un registro per id invece di far comporre il testo direttamente a ogni agente: un\n// domani, un nuovo comportamento trasversale (es. \"rispondi sempre in italiano\", \"cita sempre\n// le fonti quando disponibili\") si aggiunge in un solo posto qui sotto e diventa disponibile a\n// ogni agente esistente e futuro con una sola riga (`microPromptIds: [...]`), invece di dover\n// toccare il buildPrompt di ciascuno.\nimport { getAgenticoPrompts } from './prompts';\n\nexport interface MicroPrompt {\n id: string;\n text: string;\n}\n\n/** Chiede al modello di chiudere SEMPRE la propria risposta testuale con un riepilogo\n * conciso di cosa ha fatto — utile perché l'utente altrimenti vede solo il log delle tool\n * call/turni \"sto pensando\" (già deterministico, mai narrato dal modello) ma nessuna sintesi\n * finale in linguaggio naturale di cosa è stato effettivamente ottenuto.\n *\n * Il riepilogo va DOPO il blocco ```json — mai prima, mai a comporlo — cosi\n * extractJsonBlock() lo trova comunque cercando il fence ovunque nel testo, e il riepilogo\n * stesso resta visibile all'utente come testo libero accanto al JSON (vedi DetailContent in\n * AgentActivityLog.tsx, che già sa render testo + blocco codice misti nello stesso turno). */\nexport const SUMMARY_MICRO_PROMPT: MicroPrompt = {\n id: 'closing-summary',\n // Read from the active prompt pack (prompts.ts) at use time, so the host's language applies.\n get text() {\n return getAgenticoPrompts().closingSummary;\n },\n};\n\n/** Registro dei micro-prompt disponibili, per id. */\nexport const MICRO_PROMPT_REGISTRY: Record<string, MicroPrompt> = {\n [SUMMARY_MICRO_PROMPT.id]: SUMMARY_MICRO_PROMPT,\n};\n\n/** Applica in coda al prompt verticale i micro-prompt richiesti per id — no-op se l'agente\n * non ne richiede nessuno (o richiede un id sconosciuto, ignorato silenziosamente: un id\n * scritto male non deve far fallire l'intero prompt). */\nexport function applyMicroPrompts(prompt: string, ids: string[] | undefined): string {\n if (!ids || ids.length === 0) return prompt;\n const fragments = ids.map((id) => MICRO_PROMPT_REGISTRY[id]?.text).filter((t): t is string => !!t);\n if (fragments.length === 0) return prompt;\n return `${prompt}\\n\\n${fragments.join('\\n')}`;\n}\n","// Normalizza un errore di chiamata AI in un messaggio leggibile — condiviso da ogni chiamante\n// di useAgent (prima duplicato identico in SiteEditorPage.tsx). I provider AI (OpenAI,\n// Anthropic, Gemini) non lanciano sempre un vero Error: spesso è un oggetto con la forma\n// { error: { message } } propagato as-is dalla loro risposta HTTP.\nexport function extractErrorMessage(err: unknown): string {\n if (err instanceof Error) return err.message;\n if (err !== null && typeof err === 'object') {\n const e = err as Record<string, unknown>;\n // Forma comune quanto { error: { message } } — alcuni provider (visto con OpenCode\n // locale) rispondono con { error: \"testo\" } invece che { error: { message: \"testo\" } }.\n if (typeof e.error === 'string') return e.error;\n if (e.error && typeof e.error === 'object') {\n const inner = e.error as Record<string, unknown>;\n if (typeof inner.message === 'string') return inner.message;\n }\n if (typeof e.message === 'string') return e.message;\n if (typeof e.status === 'number') return `HTTP ${e.status}`;\n // Forma sconosciuta — meglio un dump JSON leggibile (e diagnosticabile) che\n // \"[object Object]\" da String(err) sul default toString() di un plain object.\n try { return JSON.stringify(err); } catch { /* fall through */ }\n }\n return String(err);\n}\n","// Estrae un blocco JSON dalla risposta grezza di un agente — condiviso da ogni parseResponse\n// che si aspetta un ```json ... ``` (theme-extract, html-to-liquid, e i prossimi).\n//\n// Cerca il fence OVUNQUE nel testo, non solo se occupa l'intera risposta: un micro-prompt\n// trasversale (vedi microPrompts.ts — es. \"chiudi sempre con un riepilogo\") può far si che il\n// modello aggiunga prosa prima o dopo il blocco JSON. Il vecchio comportamento (strip dei soli\n// fence iniziale/finale, poi JSON.parse sull'intera stringa) romperebbe silenziosamente il\n// parsing non appena comparisse anche una sola riga di prosa attorno al JSON.\nexport function extractJsonBlock(raw: string): string {\n const fenced = raw.match(/```(?:json)?\\s*\\n?([\\s\\S]*?)```/i);\n if (fenced) return fenced[1].trim();\n // Nessun fence trovato — il modello a volte lo dimentica proprio (l'istruzione \"output\n // ONLY the raw JSON\" non è sempre rispettata su turni di follow-up lunghi) e scrive il JSON\n // valido seguito, SENZA delimitatori, da un paragrafo di riepilogo in prosa (bug osservato:\n // JSON.parse falliva con \"Unexpected non-whitespace character AFTER json\" — cioè il JSON\n // stesso era completo e corretto, il problema era solo il testo attaccato subito dopo).\n // Isoliamo il primo valore JSON bilanciato (oggetto o array) contando parentesi/graffe e\n // ignorando quelle dentro le stringhe, e tagliamo lì — qualunque prosa dopo viene scartata.\n const stripped = raw.replace(/^```json\\s*/i, '').replace(/^```\\s*/i, '').replace(/```\\s*$/, '').trim();\n const balanced = extractFirstBalancedJsonValue(stripped);\n return balanced ?? stripped;\n}\n\nfunction extractFirstBalancedJsonValue(text: string): string | null {\n const start = text.search(/[{[]/);\n if (start === -1) return null;\n\n const open = text[start];\n const close = open === '{' ? '}' : ']';\n let depth = 0;\n let inString = false;\n let escaped = false;\n\n for (let i = start; i < text.length; i++) {\n const ch = text[i];\n if (inString) {\n if (escaped) escaped = false;\n else if (ch === '\\\\') escaped = true;\n else if (ch === '\"') inString = false;\n continue;\n }\n if (ch === '\"') { inString = true; continue; }\n if (ch === open) depth++;\n else if (ch === close) {\n depth--;\n if (depth === 0) return text.slice(start, i + 1);\n }\n }\n return null;\n}\n","// Destinazione generica — funziona per QUALUNQUE TResult, sempre disponibile (vedi\n// ResultDestinationPicker, che la elenca oltre a manifest.destinationIds, non al posto loro).\nimport type { ResultDestination } from './resultDestination';\n\nexport const JSON_EXPORT_DESTINATION_ID = 'json-export';\n\nexport const jsonExportDestination: ResultDestination<unknown> = {\n id: JSON_EXPORT_DESTINATION_ID,\n label: 'Export as JSON', // shown through the agentico.exportJson translation by the picker\n async apply(result) {\n const blob = new Blob([JSON.stringify(result, null, 2)], { type: 'application/json' });\n const url = URL.createObjectURL(blob);\n try {\n const a = document.createElement('a');\n a.href = url;\n a.download = `tool-result-${Date.now()}.json`;\n a.click();\n } finally {\n URL.revokeObjectURL(url);\n }\n },\n};\n","// Registro delle destinazioni per id, letto da ResultDestinationPicker per risolvere\n// `manifest.destinationIds` in `ResultDestination` reali — stesso principio di\n// subagentRegistry.ts: Agentico possiede SOLO `jsonExportDestination` (generica, funziona per\n// qualunque TResult), mai una destinazione specifica di un subagent concreto (es.\n// componentLibraryDestination, specifica di html-to-liquid). L'host la registra una volta\n// all'avvio (vedi CmsProviders.tsx), Agentico si limita a fondere quel registro con la propria\n// voce generica sempre presente.\nimport type { ResultDestination } from './resultDestination';\nimport { jsonExportDestination } from './jsonExportDestination';\n\nlet hostDestinations: Record<string, ResultDestination<any>> = {};\n\n/** Chiamata dall'host UNA VOLTA, a livello di modulo — un secondo giro SOSTITUISCE il registro\n * precedente, stesso principio di registerSubagents. */\nexport function registerDestinations(destinations: Record<string, ResultDestination<any>>): void {\n hostDestinations = destinations;\n}\n\nexport function getDestinationRegistry(): Record<string, ResultDestination<any>> {\n return { [jsonExportDestination.id]: jsonExportDestination, ...hostDestinations };\n}\n","// Chat Agentico legata a UN'entità salvata (es. un tema) — scrive SOLO al salvataggio riuscito\n// dell'entità che quella conversazione ha popolato (vedi AgenticoContext.commitEntityChat), e la\n// persiste per intero: `history` (AIConversationTurn[], dato puro) permette un resume vero — il\n// modello può continuare da lì — non solo un replay in sola lettura.\n//\n// Nessuna scrittura = nessuna chat mai salvata per quell'entità (creazione manuale, o\n// un'estrazione abbandonata senza salvare — sparisce da sola, mai persistita). Una sola chat per\n// entità: un nuovo salvataggio con una nuova estrazione associata SOVRASCRIVE quella precedente\n// (decisione esplicita — non una cronologia di tentativi). Un salvataggio SENZA nessuna\n// estrazione coinvolta (edit manuale) non tocca lo store — vedi commitEntityChat, che è un no-op\n// quando non c'è un taskId da cui pescare uno snapshot.\nimport type { AIConversationTurn } from '@llmnative/react';\nimport type { AgentMessagePart } from './messageParts';\n\n/** Stessa forma di AgentActivityEntry (agentico/presentation/useAgent.ts) — ridefinita qui invece\n * di importata: il domain layer non dipende dal presentation layer, e questi campi sono\n * semplici dati (mai funzioni), quindi TypeScript accetta un AgentActivityEntry[] ovunque sia\n * richiesto questo tipo per compatibilità strutturale, senza bisogno di un cast esplicito. */\nexport interface EntityChatActivityEntry {\n id: string;\n kind: 'user' | 'thinking' | 'tool';\n label: string;\n status: 'running' | 'done' | 'error' | 'stopped';\n detail?: string;\n toolNames?: string[];\n attachmentNames?: string[];\n /** The message in parts (CR-004): saved and reloaded as they are. Absent in chats saved before\n * 0.3.0, which show `detail` as before. */\n parts?: AgentMessagePart[];\n}\n\nexport interface EntityChatSnapshot {\n /** Host scope (tenant, company…) the chat belongs to — see AgenticoScope. */\n scopeId: string;\n subagentId: string;\n entityId: string;\n /** TParams grezzo (pre-manifest.toParams, es. `{ url }` per theme) con cui la sessione\n * originale era stata avviata — necessario per ricostruire context/tools al resume (vedi\n * useAgent.resume/SubagentRunner.resumeSeed): senza, `manifest.toParams` non avrebbe nulla\n * da validare/convertire per riavviare la sessione. */\n params: Record<string, unknown>;\n history: AIConversationTurn[];\n activity: EntityChatActivityEntry[];\n savedAt: number;\n /** Etichetta human-friendly dell'entità (es. il nome del tema) — usata dallo storico chat\n * di Agentico (vedi Agentico.tsx) per mostrare \"cosa\" senza dover rileggere l'entità stessa\n * dal suo DataProviderAdapter (che questo store, domain-side, non possiede). */\n title: string;\n}\n\nlet storageKey = 'llmnative.agentico.entityChat';\n\n/** Sets the localStorage key of the chat store — call once at startup, before any read. */\nexport function configureEntityChatStore(options: { storageKey: string }): void {\n storageKey = options.storageKey;\n}\n\n// Snapshots written before the scope rename carry `tenantId`: read them as `scopeId`.\nfunction normalize(entry: unknown): EntityChatSnapshot | null {\n if (!entry || typeof entry !== 'object') return null;\n const record = entry as Partial<EntityChatSnapshot> & { tenantId?: string };\n if (typeof record.entityId !== 'string') return null;\n const scopeId = record.scopeId ?? record.tenantId;\n if (typeof scopeId !== 'string') return null;\n const { tenantId: _legacy, ...rest } = record;\n return { ...rest, scopeId } as EntityChatSnapshot;\n}\n\nfunction readAll(): EntityChatSnapshot[] {\n try {\n const raw = localStorage.getItem(storageKey);\n if (!raw) return [];\n const parsed: unknown = JSON.parse(raw);\n return Array.isArray(parsed) ? parsed.map(normalize).filter((e): e is EntityChatSnapshot => e !== null) : [];\n } catch {\n return [];\n }\n}\n\nfunction writeAll(entries: EntityChatSnapshot[]): void {\n try {\n localStorage.setItem(storageKey, JSON.stringify(entries));\n } catch { /* quota superata o localStorage non disponibile — non fatale, è un extra */ }\n}\n\nconst key = (scopeId: string, subagentId: string, entityId: string) => `${scopeId}:${subagentId}:${entityId}`;\n\n/** La chat attualmente associata a un'entità — `null` se non ne è mai stata salvata una (o è\n * stata rimossa, vedi removeEntityChat). */\nexport function getEntityChat(scopeId: string, subagentId: string, entityId: string): EntityChatSnapshot | null {\n const k = key(scopeId, subagentId, entityId);\n return readAll().find((e) => key(e.scopeId, e.subagentId, e.entityId) === k) ?? null;\n}\n\n/** Sovrascrive (o crea) la chat associata a questa entità — vedi il commento in testa al file\n * per perché è sempre un overwrite, mai un accumulo. */\nexport function saveEntityChat(snapshot: Omit<EntityChatSnapshot, 'savedAt'>): void {\n const k = key(snapshot.scopeId, snapshot.subagentId, snapshot.entityId);\n const next = readAll().filter((e) => key(e.scopeId, e.subagentId, e.entityId) !== k);\n next.push({ ...snapshot, savedAt: Date.now() });\n writeAll(next);\n}\n\n/** Rimuove la chat di un'entità — chiamata quando l'entità stessa viene eliminata, altrimenti\n * resterebbe un'entry orfana senza nulla a cui riattaccarsi. */\nexport function removeEntityChat(scopeId: string, subagentId: string, entityId: string): void {\n const k = key(scopeId, subagentId, entityId);\n writeAll(readAll().filter((e) => key(e.scopeId, e.subagentId, e.entityId) !== k));\n}\n\n/** Tutte le chat salvate di uno scope, più recenti prima — per lo storico di Agentico\n * (Agentico.tsx), che le mescola across subagent (theme, redirects, html-to-liquid) in un'unica\n * lista ordinata per data. */\nexport function listEntityChats(scopeId: string): EntityChatSnapshot[] {\n return readAll().filter((e) => e.scopeId === scopeId).sort((a, b) => b.savedAt - a.savedAt);\n}\n\n/** The id of the Agentico task of the chat of an entity: the same when the chat is resumed from\n * the history and when a page starts it again (AgenticoContext.startSubagent), so it stays one\n * task (CR-003). */\nexport const entityChatTaskId = (subagentId: string, entityId: string) => `entitychat-${subagentId}-${entityId}`;\n","import type { AgenticoDict } from './types';\n\nexport const agenticoDe: AgenticoDict = {\n modelLabel: 'Modell',\n modelsLoading: 'Wird geladen…',\n noModelsAvailable: 'Keine Modelle verfügbar',\n noProviderConnected: 'Kein AI-Provider verbunden',\n selectModelPlaceholder: 'Modell wählen…',\n orchestratorStarterHint: 'Sag Agentico, was du erreichen willst — es wählt das passende Werkzeug und fragt nach den nötigen Angaben.',\n orchestratorStarterPlaceholder: 'Beschreibe, was du tun möchtest…',\n destinationPickerTitle: 'Ergebnis speichern in…',\n destinationApplying: 'Wird gespeichert…',\n destinationNoScope: 'Kein Ziel ausgewählt — vor dem Speichern eines auswählen.',\n unknownDestination: 'Unbekanntes Ziel: {id}',\n exportJson: 'Als JSON exportieren',\n orchestratorUnavailable: 'AI nicht verfügbar — öffne das benötigte Werkzeug direkt auf seiner Seite.',\n orchestratorContinueWithoutAi: 'Weiter ohne AI.',\n orchestratorPreparing: '{label} wird vorbereitet…',\n orchestratorCollecting: 'Parameter für {label} werden gesammelt…',\n thinking: 'Denke nach…',\n stopped: 'Vom Benutzer abgebrochen.',\n toolRequest: 'Werkzeugausführung angefordert',\n modelRequestError: 'Fehler bei der Anfrage an das Modell',\n noProviderResponse: 'Keine Antwort vom AI-Provider.',\n noActiveSession: 'Keine aktive Sitzung — zuerst run() aufrufen.',\n requestInProgress: 'Eine andere Anfrage läuft bereits.',\n invalidResponse: 'Ungültige AI-Antwort.',\n responseReceived: 'Antwort erhalten',\n noProviderAvailable: 'Kein AI-Provider verfügbar.',\n tooManyToolTurns: 'Zu viele aufeinanderfolgende Werkzeugaufrufe — das bisher gesammelte deterministische Ergebnis wird verwendet.',\n aiDisabledFallback: 'AI deaktiviert — das deterministische Ergebnis wird verwendet.',\n continueWithoutAiFallback: 'Weiter ohne AI — das deterministische Ergebnis wird verwendet.',\n oneStep: '1 Schritt',\n steps: '{count} Schritte',\n stepsLabel: 'Schritte des Agenten',\n newInstance: 'Neue Instanz von \"{label}\"',\n chatHistory: 'Chatverlauf',\n newChat: 'Neuer Chat',\n searchChats: 'Chats durchsuchen…',\n messagePlaceholder: 'Nachricht schreiben…',\n unsavedConversationConfirm: 'Diese Unterhaltung wurde nicht gespeichert und geht verloren. Fortfahren?',\n noSavedChats: 'Noch keine gespeicherten Chats.',\n archiveChat: 'Archivieren',\n noResults: 'Keine Ergebnisse.',\n retry: 'Erneut versuchen',\n retryMessage: 'Erneut versuchen.',\n continueWithoutAi: 'Ohne AI fortfahren',\n resumeWithAi: 'Mit AI fortsetzen',\n openPanel: 'Agentenbereich öffnen',\n closePanel: 'Agentenbereich schließen',\n};\n","import type { AgenticoDict } from './types';\n\nexport const agenticoEn: AgenticoDict = {\n modelLabel: 'Model',\n modelsLoading: 'Loading…',\n noModelsAvailable: 'No models available',\n noProviderConnected: 'No AI provider connected',\n selectModelPlaceholder: 'Choose a model…',\n orchestratorStarterHint: 'Tell Agentico what you want to do — it will pick the right tool and ask for what it needs.',\n orchestratorStarterPlaceholder: 'Describe what you want to do…',\n destinationPickerTitle: 'Save result to…',\n destinationApplying: 'Saving…',\n destinationNoScope: 'Nothing selected to save into — select one before saving.',\n unknownDestination: 'Unknown destination: {id}',\n exportJson: 'Export as JSON',\n orchestratorUnavailable: 'AI not available — open the tool you need directly from its page.',\n orchestratorContinueWithoutAi: 'Continuing without AI.',\n orchestratorPreparing: 'Preparing {label}…',\n orchestratorCollecting: 'Collecting the parameters for {label}…',\n thinking: 'Thinking…',\n stopped: 'Stopped by the user.',\n toolRequest: 'Requested to run some tools',\n modelRequestError: 'Error in the request to the model',\n noProviderResponse: 'No response from the AI provider.',\n noActiveSession: 'No active session — call run() first.',\n requestInProgress: 'Another request is already running.',\n invalidResponse: 'Invalid AI response.',\n responseReceived: 'Response received',\n noProviderAvailable: 'No AI provider available.',\n tooManyToolTurns: 'Too many consecutive tool-calling turns — using the deterministic result collected so far.',\n aiDisabledFallback: 'AI disabled — using the deterministic result.',\n continueWithoutAiFallback: 'Continuing without AI — using the deterministic result.',\n oneStep: '1 step',\n steps: '{count} steps',\n stepsLabel: 'Steps of the agent',\n newInstance: 'New \"{label}\" instance',\n chatHistory: 'Chat history',\n newChat: 'New chat',\n searchChats: 'Search chats…',\n messagePlaceholder: 'Write a message…',\n unsavedConversationConfirm: 'This conversation has not been saved and will be lost. Continue?',\n noSavedChats: 'No saved chats yet.',\n archiveChat: 'Archive',\n noResults: 'No results.',\n retry: 'Retry',\n retryMessage: 'Retry.',\n continueWithoutAi: 'Continue without AI',\n resumeWithAi: 'Resume with AI',\n openPanel: 'Open agent panel',\n closePanel: 'Close agent panel',\n};\n","import type { AgenticoDict } from './types';\n\nexport const agenticoIt: AgenticoDict = {\n modelLabel: 'Modello',\n modelsLoading: 'Caricamento…',\n noModelsAvailable: 'Nessun modello disponibile',\n noProviderConnected: 'Nessun provider AI connesso',\n selectModelPlaceholder: 'Scegli un modello…',\n orchestratorStarterHint: 'Dì ad Agentico cosa vuoi fare — sceglierà lo strumento giusto e ti chiederà cosa serve.',\n orchestratorStarterPlaceholder: 'Descrivi cosa vuoi fare…',\n destinationPickerTitle: 'Salva risultato in…',\n destinationApplying: 'Salvataggio…',\n destinationNoScope: 'Nessuna destinazione selezionata — selezionane una prima di salvare.',\n unknownDestination: 'Destinazione sconosciuta: {id}',\n exportJson: 'Esporta come JSON',\n orchestratorUnavailable: 'AI non disponibile — apri il tool che ti serve direttamente dalla sua pagina.',\n orchestratorContinueWithoutAi: 'Continuo senza AI.',\n orchestratorPreparing: 'Preparo {label}…',\n orchestratorCollecting: 'Sto raccogliendo i parametri per {label}…',\n thinking: 'Sto pensando…',\n stopped: \"Interrotto dall'utente.\",\n toolRequest: 'Richiesta di eseguire dei tool',\n modelRequestError: 'Errore nella richiesta al modello',\n noProviderResponse: 'Nessuna risposta dal provider AI.',\n noActiveSession: 'Nessuna sessione attiva — chiama prima run().',\n requestInProgress: \"Un'altra richiesta è già in corso.\",\n invalidResponse: 'Risposta AI non valida.',\n responseReceived: 'Risposta ricevuta',\n noProviderAvailable: 'Nessun provider AI disponibile.',\n tooManyToolTurns: 'Troppi turni di tool-calling consecutivi — uso il risultato deterministico raccolto finora.',\n aiDisabledFallback: 'AI disabilitata — uso il risultato deterministico.',\n continueWithoutAiFallback: 'Continuo senza AI — uso il risultato deterministico.',\n oneStep: '1 passo',\n steps: '{count} passi',\n stepsLabel: \"Passi dell'agente\",\n newInstance: 'Nuova istanza di \"{label}\"',\n chatHistory: 'Storico chat',\n newChat: 'Nuova chat',\n searchChats: 'Cerca nelle chat…',\n messagePlaceholder: 'Scrivi un messaggio…',\n unsavedConversationConfirm: 'Questa conversazione non è stata salvata e andrà persa. Continuare?',\n noSavedChats: 'Nessuna chat salvata ancora.',\n archiveChat: 'Archivia',\n noResults: 'Nessun risultato.',\n retry: 'Riprova',\n retryMessage: 'Riprova.',\n continueWithoutAi: 'Continua senza AI',\n resumeWithAi: 'Riprendi con AI',\n openPanel: 'Apri pannello agente',\n closePanel: 'Chiudi pannello agente',\n};\n","import { useI18n } from '@llmnative/react';\nimport { agenticoDe } from './de';\nimport { agenticoEn } from './en';\nimport { agenticoIt } from './it';\nimport type { AgenticoDict } from './types';\n\nexport type { AgenticoDict } from './types';\nexport { agenticoDe, agenticoEn, agenticoIt };\n\n/** Merge into <App i18n={{ translations }}> — e.g. `it: { ...locales.it, ...agenticoTranslations.it, ...yourIt }`. */\nexport const agenticoTranslations = {\n en: { agentico: agenticoEn },\n it: { agentico: agenticoIt },\n de: { agentico: agenticoDe },\n};\n\n/** Agentico strings for the active locale: what the host registered, on top of English. */\nexport function useAgenticoI18n(): AgenticoDict {\n const { dict } = useI18n();\n const fromHost = (dict as { agentico?: Partial<AgenticoDict> }).agentico;\n return fromHost ? { ...agenticoEn, ...fromHost } : agenticoEn;\n}\n","import React, { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react';\r\nimport type { AIModelDescriptor, ChatbotAction, DataProviderAdapter, StorageProviderAdapter } from '@llmnative/react';\r\nimport type { AgentActivityEntry, AgentConversation } from './useAgent';\r\nimport type { ResultDestination } from '../domain/resultDestination';\r\nimport type { SubagentUsageCard } from '../domain/subagentUsageCard';\r\nimport { findSubagentManifest } from '../domain/subagentRegistry';\r\nimport { saveEntityChat, type EntityChatSnapshot } from '../domain/entityChatStore';\r\nimport { getAgenticoPrompts } from '../domain/prompts';\r\n\r\n/** Where results are saved and chats are kept: the host's current tenant, company, workspace…\r\n * `null` while the host has none selected — destinations then refuse to save. */\r\nexport interface AgenticoScope {\r\n id: string;\r\n dataProvider: DataProviderAdapter;\r\n storageProvider: StorageProviderAdapter | null;\r\n}\r\n\r\n/** Cattura la conversazione di un task subagent già esistente da \"continuare\" invece di forkare\r\n * — vedi startOrchestrator sotto. `taskId` viene riusato dal nuovo SubagentRunner (invece di un\r\n * uuid fresco) cosi il task esistente si aggiorna in place; `activity` (precedingActivity +\r\n * agent.activity del vecchio task, già concatenati) viene prepesa al transcript del nuovo giro —\r\n * l'utente legge tutto come UNA chat continua, mai due sessioni scollegate per lo stesso\r\n * subagent. */\r\nexport interface PendingCarryOver {\r\n taskId: string;\r\n activity: AgentActivityEntry[];\r\n}\r\n\r\n/** Id fisso del task dell'orchestratore Agentico — al massimo UNA conversazione orchestratore\r\n * per volta, come per qualunque altro task (vedi AgenticoTask sotto: un solo task alla volta in\r\n * tutta l'app, mai una tab bar da switchare). Un id fisso permette a startOrchestrator() di\r\n * riconoscere \"ce n'è già uno aperto\" senza dover tenere un riferimento separato. */\r\nexport const ORCHESTRATOR_TASK_ID = 'agentico-orchestrator';\r\n\r\n/** Una sessione agente aperta nel pannello globale — riferimento vivo all'oggetto restituito\r\n * da useAgent() nel componente che l'ha avviata (NON una copia: quel componente deve restare\r\n * montato perché questo riferimento resti valido, vedi Agentico.tsx). Solo le sessioni aperte in\r\n * questa sessione browser — nessuna persistenza su Firestore (Fase 3, deferita).\r\n *\r\n * Al massimo UN task alla volta in tutta l'app (decisione esplicita: \"una chat unica\", niente\r\n * tab multiple per run paralleli) — avviarne uno nuovo mentre un altro è in corso lo sostituisce\r\n * (vedi openTask sotto), mai lo affianca. */\r\nexport interface AgenticoTask<TResult = unknown> {\r\n id: string;\r\n /** Titolo mostrato nell'header del pannello — es. l'URL importato, il nome del componente. */\r\n title: string;\r\n /** Nome icona (@llmnative/react Icon) mostrata nell'header — 'bot' per l'orchestratore\r\n * (OrchestratorRunner, stessa identità di AgenticoToggle), manifest.icon per un subagent\r\n * (SubagentRunner). */\r\n icon: string;\r\n /** SOLO sul task dell'orchestratore — id del subagent verso cui la pagina/componente che ha\r\n * avviato questa conversazione era scoped (catturato da OrchestratorRunner al mount, da\r\n * sidebar.scopedSubagentId). Permette a un indice come redirects/IndexList.tsx di mostrare\r\n * \"in corso\" per QUESTO subagent anche prima dell'handoff (mentre l'orchestratore sta ancora\r\n * raccogliendo parametri/chiamando tool di raccolta dati). Assente sui task dei subagent\r\n * stessi (quelli sono già inequivocabilmente scoped dal proprio id\r\n * `subagent-${subagentId}-...`). */\r\n scopedSubagentId?: string;\r\n /** Presente SOLO dopo un commitEntityChat riuscito su questo task — questa conversazione è\r\n * genuinamente su disco (vedi entityChatStore), non solo in memoria. Usato dal bottone\r\n * \"nuova chat\" (vedi Agentico.tsx) per decidere se avvertire l'utente prima di scartarla:\r\n * senza salvataggio andrebbe persa per sempre (nessun \"torna indietro\", scelta esplicita). */\r\n savedToDisk?: boolean;\r\n agent: AgentConversation<TResult> & {\r\n hasProviders: boolean;\r\n selectedModel: string;\r\n setSelectedModel: (id: string) => void;\r\n modelsByProvider: Record<string, AIModelDescriptor[]>;\r\n };\r\n onApplied: (result: TResult) => void;\r\n /** Transcript di UNA conversazione precedente da mostrare \"prima\" di `agent.activity` nello\r\n * stesso log, senza soluzione di continuità — usato dal task del subagent per prependere la\r\n * conversazione dell'orchestratore che lo ha invocato (vedi OrchestratorRunner/SubagentRunner):\r\n * l'utente deve vivere Agentico come UNA chat discorsiva unica, mai accorgersi che ci sono\r\n * due \"attori\" distinti dietro le quinte. */\r\n precedingActivity?: AgentActivityEntry[];\r\n /** Contenuto extra renderizzato nel body del pannello, sotto il log di attività (es.\r\n * ResultDestinationPicker una volta che un subagent ha prodotto un risultato senza un\r\n * componente vivo ad ascoltarlo — vedi SubagentRunner). Deve restare dentro il pannello\r\n * Agentico, non un elemento reso altrove nell'albero: chi possiede il task\r\n * (SubagentRunner/OrchestratorRunner) è montato come sibling PRIMA del pannello visibile in\r\n * Agentico.tsx (per sopravvivere al toggle di isOpen), quindi il proprio JSX di ritorno\r\n * finirebbe fuori dal pannello se non passato tramite questo slot esplicito. */\r\n panelExtra?: React.ReactNode;\r\n /** Presente SOLO sul task di un ManualEntryRunner — quando impostato, AgenticoComposer\r\n * (Agentico.tsx) instrada il testo digitato QUI invece che ad `agent.sendMessage`: niente\r\n * chiamata AI, `onSubmit` costruisce i TParams dal testo grezzo e avvia il subagent con\r\n * l'AI disabilitata (vedi SubagentManifest.manualEntry). `prompt` è il messaggio statico\r\n * mostrato quando non c'è ancora nessuna attività (invece del solito pageContext/hint). */\r\n manualEntry?: { prompt: string; onSubmit: (rawText: string) => void };\r\n /** Quante voci di `precedingActivity + agent.activity` (in ordine, vedi Agentico.tsx)\r\n * precedono `panelExtra` nella timeline — cioè QUANTE erano già maturate quando il\r\n * risultato mostrato in panelExtra è stato prodotto. Senza questo indice, panelExtra veniva\r\n * sempre reso dopo l'INTERO log corrente, quindi ogni messaggio di follow-up successivo\r\n * (es. un retry finito in errore) appariva SOPRA un risultato che in realtà era arrivato\r\n * PRIMA — invertendo la cronologia di una chat che l'utente si aspetta scorra\r\n * top=vecchio/bottom=nuovo (bug segnalato). Assente = comportamento invariato, sempre in\r\n * fondo (equivalente a passare la lunghezza corrente del log). */\r\n panelExtraAnchorIndex?: number;\r\n /** What the host shows at the bottom of the panel, above the composer, while the task is open\r\n * (see registerSubagentControls, CR-003). */\r\n panelActions?: React.ReactNode;\r\n /** Buttons of the host in the composer (see registerSubagentControls, CR-003). */\r\n composerActions?: ChatbotAction[];\r\n /** Chiamato quando questo task viene sostituito da uno nuovo (vedi openTask) o quando\r\n * l'utente lo interrompe — SEMPRE definito da chi possiede il task\r\n * (OrchestratorRunner/SubagentRunner), mai un fallback generico su closeTask: quel\r\n * componente resta montato anche a task chiuso, quindi il proprio effect che ri-registra il\r\n * task a ogni cambiamento di agent.activity/running lo RIAPRIREBBE da solo (es.\r\n * l'aggiornamento \"Interrotto dall'utente\" dopo uno stop()) se non gli si dice\r\n * esplicitamente di smettere — vedi il ref `dismissedRef` in entrambi i chiamanti. */\r\n onDismiss: () => void;\r\n}\r\n\r\n/** Un punto di ancoraggio semantico subagent↔componente attivo in questo momento — vedi\r\n * useSubagentComponentContext (presentation/hooks) per come si registra, e\r\n * SubagentManifest.componentContexts per dove vivono i testi (agenticoHint/userMessage). */\r\nexport interface ComponentAnchor {\r\n subagentId: string;\r\n componentKey: string;\r\n /** Chiamato da SubagentRunner quando arriva un risultato per `subagentId` mentre questo\r\n * anchor è montato — il componente/pagina popola se stesso, mai un salvataggio automatico.\r\n * `meta` è il TParams con cui il subagent è stato lanciato (es. `{ url }` per theme).\r\n * `taskId` è l'id del task Agentico che ha prodotto questo risultato — un componente che\r\n * vuole poter persistere la chat al proprio salvataggio (vedi commitEntityChat sotto) lo\r\n * tiene da parte, altrimenti può ignorarlo. */\r\n onResult?: (result: unknown, meta: Record<string, unknown>, taskId: string) => void;\r\n /** Fatti live su QUESTA istanza dell'entità aperta (es. \"Il tema aperto si chiama\r\n * ducatimilano.it\"), aggiunti in coda all'`agenticoHint` statico del manifest (vedi\r\n * anchorTexts) — l'`agenticoHint` è prosa fissa scritta una volta nel manifest, non può\r\n * sapere quale entità specifica è aperta ORA. Assente = comportamento invariato (solo il\r\n * testo statico). Vedi useSubagentComponentContext per come un componente lo valorizza. */\r\n contextInfo?: string;\r\n /** Id dell'entità a cui QUESTA istanza è vincolata (es. l'hostId di RedirectsEditor) — a\r\n * differenza di `contextInfo` (prosa, solo per il prompt), è un valore STRUTTURATO che\r\n * SubagentRunner inoltra a `ResultDestination.apply()` (vedi `meta.pinnedEntityId`) perché una\r\n * destinazione con logica di identità propria (es. redirectsSaveDestination, che calcola\r\n * l'hostId di destinazione dal risultato) possa rifiutare un salvataggio il cui hostId\r\n * risolto NON combacia — un backstop deterministico, non affidato solo all'istruzione nel\r\n * prompt (che può comunque essere ignorata dal modello). Assente = nessun controllo\r\n * aggiuntivo, comportamento invariato (es. RedirectsIndexList, che non è vincolato a un solo\r\n * host). */\r\n pinnedEntityId?: string;\r\n}\r\n\r\ninterface AgenticoContextValue {\r\n /** The host scope passed to <AgenticoProvider scope>. */\r\n scope: AgenticoScope | null;\r\n task: AgenticoTask | null;\r\n /** Visibilità del pannello — completamente indipendente dal task (vedi AgenticoToggle:\r\n * icona persistente stile \"estensione del browser\"). Nessuna chiamata a `openTask` la forza\r\n * mai a true: un fetch/run() NON apre da solo il pannello (solo l'icona anima per segnalare\r\n * che l'agente sta lavorando) — solo l'utente, cliccando il toggle, decide di vederlo. */\r\n isOpen: boolean;\r\n /** Crea o sostituisce il task attivo (upsert idempotente se stesso id, altrimenti sostituisce:\r\n * un task con un id DIVERSO da quello corrente ne causa la chiusura esplicita via\r\n * `onDismiss()` prima di installare il nuovo — mai due conversazioni vive insieme, vedi\r\n * AgenticoTask). Richiamalo a ogni render, l'oggetto `agent` è nuovo a ogni render dell'hook\r\n * useAgent. */\r\n openTask: <TResult>(task: AgenticoTask<TResult>) => void;\r\n closeTask: (id: string) => void;\r\n /** Mostra/nasconde il pannello — l'unica azione che tocca `isOpen` insieme a `openPanel`\r\n * (vedi commento su `isOpen`). Usato da AgenticoToggle. */\r\n toggleOpen: () => void;\r\n /** Goal in attesa che un OrchestratorRunner catturi al mount e lanci — settato SOLO se non\r\n * esiste già un task orchestratore (vedi startOrchestrator). `null` quando non c'è nessun\r\n * avvio in sospeso. */\r\n pendingGoal: string | null;\r\n /** Cambia a ogni conversazione orchestratore avviata per davvero — usato solo come React\r\n * `key` sull'elemento <OrchestratorRunner>, mai letto per altro. */\r\n orchestratorRunId: string | null;\r\n /** Chiamato da ToolsPage (\"Usa agent\" su una card), dall'input vuoto del pannello Agentico, o\r\n * da una pagina che vuole avviare una conversazione già scoped (es. SiteEditorPage) — apre il\r\n * pannello e, se non c'è già una conversazione orchestratore in corso, la avvia con questo\r\n * goal. Se ce n'è già una, la porta semplicemente a fuoco: NON inietta il nuovo goal in\r\n * quella conversazione (limite noto, accettato per l'MVP).\r\n * `options.scopedSubagentId` — presente quando il chiamante sa GIÀ, senza bisogno di AI,\r\n * quale subagent servirà (es. il bottone \"Importa HTML/JSX\", sempre `html-to-liquid`) — a\r\n * differenza dello scoping derivato da un componentAnchor (vedi `scopedSubagentId` sotto,\r\n * che richiede un ancoraggio live), questo vale per un click one-shot che non monta alcun\r\n * anchor. Usato SOLO per abilitare il percorso \"Continua senza AI\" → manuale (vedi\r\n * startManualEntry/ManualEntryRunner) quando l'orchestratore stesso non riesce a partire:\r\n * senza sapere il target, quel bottone non potrebbe offrire altro che il messaggio generico\r\n * di fallback dell'orchestratore (non un vero handoff, non può indovinare l'intento). */\r\n startOrchestrator: (goal: string, options?: { extraDestinations?: ResultDestination[]; attachments?: File[]; model?: string; scopedSubagentId?: string }) => void;\r\n /** Destinazioni scoped alla pagina che ha avviato l'ultimo startOrchestrator() con\r\n * `options.extraDestinations` — catturate da OrchestratorRunner al momento dell'handoff e\r\n * inoltrate al SubagentRunner che monta, insieme a `pendingGoal`. `undefined` per i\r\n * chiamanti che non ne passano. */\r\n pendingExtraDestinations: ResultDestination[] | undefined;\r\n /** File allegati al messaggio che ha avviato l'ultima conversazione orchestratore — catturati\r\n * da OrchestratorRunner al mount, insieme a `pendingGoal`. */\r\n pendingAttachments: File[] | undefined;\r\n /** Modello scelto dall'utente nel composer \"starter\", prima ancora che esista una sessione\r\n * useAgent — catturato da OrchestratorRunner al mount, insieme a `pendingGoal`. */\r\n pendingModel: string | undefined;\r\n /** Vedi PendingCarryOver — catturato da OrchestratorRunner al mount insieme a pendingGoal,\r\n * `null` quando startOrchestrator() non ha trovato nessun task da continuare. */\r\n pendingCarryOver: PendingCarryOver | null;\r\n /** Vedi `options.scopedSubagentId` di startOrchestrator sopra — catturato da OrchestratorRunner\r\n * al mount, insieme agli altri pending*. `undefined` = nessun target esplicito (comportamento\r\n * invariato, l'orchestratore deriva lo scope SOLO da un componentAnchor live, se presente). */\r\n pendingScopedSubagentId: string | undefined;\r\n /** Testo \"dove si trova l'utente adesso\" per contestualizzare l'orchestratore — se ci sono\r\n * ancoraggi componente↔subagent attivi (vedi componentAnchors sotto) deriva da quelli\r\n * (agenticoHint, combinato se ne sono attivi più di uno di subagent diversi), altrimenti\r\n * ripiega sul vecchio meccanismo a stack per-pagina (useAgenticoPageContext, ancora usato da\r\n * pagine non migrate a componentContexts). Letto una sola volta da OrchestratorRunner al\r\n * momento di run(). */\r\n pageContext: string | null;\r\n /** Card strutturata (campi richiesti/opzionali) — SOLO dal vecchio meccanismo a stack\r\n * per-pagina (mai per un anchor component-based, che usa invece `anchoredUserMessages` sotto,\r\n * un semplice messaggio invece di un modulo a campi). `null` quando non applicabile. */\r\n pageContextCard: SubagentUsageCard | null;\r\n /** Registra/rimuove (text === null) il contesto di UNA pagina/pannello identificata da `id`\r\n * — usare tramite l'hook useAgenticoPageContext, non direttamente. Vedi pageContext sopra per\r\n * come si combina con componentAnchors. */\r\n setPageContext: (id: string, text: string | null, card?: SubagentUsageCard | null) => void;\r\n /** Id del subagent a cui l'orchestratore deve restare scoped — deriva da componentAnchors\r\n * quando esattamente UN subagent è ancorato, altrimenti (0 o >1 subagent distinti) ripiega\r\n * sul vecchio pageContextCard/`null`. Sostituisce il vecchio `pageContextCard?.subagentId`\r\n * come UNICA fonte per OrchestratorRunner/startOrchestrator. */\r\n scopedSubagentId: string | null;\r\n /** Ancoraggi componente↔subagent attualmente montati, deduplicati per subagentId (l'ultimo\r\n * registrato per uno stesso subagent vince) — usato da SubagentRunner per instradare un\r\n * risultato al componente vivo invece di mostrare sempre ResultDestinationPicker. */\r\n componentAnchors: ComponentAnchor[];\r\n /** Messaggi utente (SubagentComponentContext.userMessage) per gli anchor attivi — mostrati\r\n * da Agentico.tsx al posto della vecchia card a campi quando ci sono anchor component-based.\r\n * Vuoto se nessun anchor attivo, o se un anchor non ha un componentContexts corrispondente\r\n * (es. SiteEditorPage, che ascolta un risultato senza fornire un proprio messaggio). */\r\n anchoredUserMessages: string[];\r\n /** Registra/rimuove (anchor === null) un ancoraggio componente↔subagent identificato da `id`\r\n * — usare tramite l'hook useSubagentComponentContext, non direttamente. */\r\n registerComponentAnchor: (id: string, anchor: ComponentAnchor | null) => void;\r\n /** Apre il pannello incondizionatamente — a differenza di toggleOpen(), non dipende dal\r\n * valore corrente di isOpen. */\r\n openPanel: () => void;\r\n /** Persiste (sovrascrivendo un'eventuale chat già salvata per la STESSA entità, vedi\r\n * entityChatStore) la conversazione del task `taskId`, keyed su `${scopeId}:${subagentId}:\r\n * ${entityId}` — chiamato da un componente (es. ThemeEditor) subito dopo un salvataggio\r\n * riuscito. No-op se `taskId` è assente (nessuna estrazione coinvolta in questo salvataggio,\r\n * es. edit manuale — un'eventuale chat già salvata in precedenza per questa entità resta\r\n * intatta) o se il task `taskId` non è più quello corrente (l'utente ha aperto un'altra\r\n * conversazione nel frattempo — niente si salva, per non attaccare a un'entità la chat\r\n * sbagliata). */\r\n commitEntityChat: (subagentId: string, entityId: string, taskId: string | null | undefined, scopeId: string | undefined, params: Record<string, unknown>, title: string) => void;\r\n /** Riprende una chat salvata (vedi entityChatStore) — apre il pannello e monta un\r\n * SubagentRunner con `resumeSeed` valorizzato invece di un run/handoff dall'orchestratore\r\n * (vedi Agentico.tsx). Sostituisce (mai affianca) qualunque task corrente, stesso principio\r\n * di startOrchestrator/openTask — una chat unica alla volta in tutta l'app. */\r\n resumeEntityChat: (subagentId: string, entityId: string, snapshot: EntityChatSnapshot) => void;\r\n /** Presente = un resumeEntityChat() è in sospeso, letto da Agentico.tsx per montare il\r\n * SubagentRunner col resumeSeed giusto — analogo a pendingGoal per l'orchestratore. */\r\n pendingResume: { subagentId: string; entityId: string; snapshot: EntityChatSnapshot } | null;\r\n /** Cambia a ogni resumeEntityChat() — usato solo come React `key` sul SubagentRunner di\r\n * resume (vedi Agentico.tsx), stesso principio di orchestratorRunId. */\r\n resumeRunId: string | null;\r\n /** Presente = un startManualEntry() è in sospeso, letto da Agentico.tsx per montare un\r\n * ManualEntryRunner per QUESTO subagent — percorso \"senza AI\" per quando l'orchestratore\r\n * stesso non riesce a partire ma il target è già noto (vedi\r\n * SubagentManifest.manualEntry/pendingScopedSubagentId). Analogo a pendingGoal/pendingResume. */\r\n pendingManualEntry: { subagentId: string; extraDestinations?: ResultDestination[] } | null;\r\n /** Cambia a ogni startManualEntry() — React `key` sul ManualEntryRunner, stesso principio di\r\n * orchestratorRunId/resumeRunId. */\r\n manualEntryRunId: string | null;\r\n /** Avvia il percorso \"senza AI\" per un subagent già noto — chiamato dal bottone \"Continua\r\n * senza AI\" quando la conversazione orchestratore è scoped e non può proseguire (vedi\r\n * Agentico.tsx). Congeda subito il task orchestratore corrente, stesso principio di un vero\r\n * handoff AI-driven (OrchestratorRunner.handleResult). */\r\n startManualEntry: (subagentId: string, extraDestinations?: ResultDestination[]) => void;\r\n /** Presente = un resumeWithAI() è in sospeso, letto da Agentico.tsx per montare un vero\r\n * SubagentRunner (AI abilitata) per QUESTO subagent con QUESTI parametri — il \"contrario\" di\r\n * startManualEntry: dopo aver lavorato nel percorso senza AI, l'utente può far raffinare\r\n * dall'AI lo stesso input, invece di restare bloccato lì per sempre. */\r\n pendingResumeWithAI: { subagentId: string; params: Record<string, unknown>; extraDestinations?: ResultDestination[] } | null;\r\n /** Cambia a ogni resumeWithAI() — React `key` sul SubagentRunner risultante, stesso principio\r\n * di manualEntryRunId. */\r\n resumeWithAIRunId: string | null;\r\n /** Passa da un task ManualEntryRunner (senza AI) a un vero SubagentRunner (AI abilitata) per\r\n * lo STESSO subagent, riusando `params` (gli ultimi usati con successo nel percorso senza\r\n * AI) come punto di partenza — mai una conversazione persa, solo il canale che cambia.\r\n * Generico per costruzione: nessuna conoscenza di QUALE subagent, funziona per ogni\r\n * manifest con `manualEntry` dichiarato, non solo html-to-liquid. */\r\n resumeWithAI: (subagentId: string, params: Record<string, unknown>, extraDestinations?: ResultDestination[]) => void;\r\n /** Congeda il task corrente — usato da un componente che ha già stabilito (in base al\r\n * proprio subagentId/entità) che il task visibile non è il suo (vedi ThemeEditor/\r\n * ThemeIndexList). Regola unica, nessuna eccezione: quale chat si vede dipende SOLO dal\r\n * componente/entità a cui si è agganciati in questo momento — niente puntatori \"torna\r\n * indietro\", niente eccezione per un task ancora in corso (deliberatamente semplice,\r\n * raffinabile in seguito). No-op se non c'è alcun task. */\r\n dismissForeignTask: () => void;\r\n /** Starts a subagent from a page, without the orchestrator (CR-003): it replaces the current\r\n * task. `taskId` keeps one task per entity (start it again = the same task); `openingMessage`\r\n * is shown as the first message of the person and given to the first turn. */\r\n startSubagent: (subagentId: string, params: Record<string, unknown>, options?: { taskId?: string; openingMessage?: string }) => void;\r\n /** Present = a startSubagent() is pending, read by Agentico.tsx to mount its SubagentRunner. */\r\n pendingStart: { subagentId: string; params: Record<string, unknown>; taskId?: string; openingMessage?: string } | null;\r\n /** Changes at every startSubagent() — React `key` of that SubagentRunner. */\r\n startRunId: string | null;\r\n}\r\n\r\nconst AgenticoContext = createContext<AgenticoContextValue | null>(null);\r\n\r\nexport function AgenticoProvider({ children, scope = null }: { children: React.ReactNode; scope?: AgenticoScope | null }) {\r\n const [task, setTaskState] = useState<AgenticoTask | null>(null);\r\n // Stabile per tutta la vita del provider (vedi openTask sotto) — leggere `task` da una ref\r\n // invece che dalle dipendenze di useCallback evita che openTask cambi identità ogni volta che\r\n // il task cambia, che altrimenti farebbe ripartire in loop gli effect di registrazione di\r\n // OrchestratorRunner/SubagentRunner (le cui dipendenze includono sidebar.openTask) — stesso\r\n // bug class già osservato altrove in questo file (vedi filteredResult in SubagentRunner.tsx).\r\n const taskRef = useRef<AgenticoTask | null>(null);\r\n useEffect(() => { taskRef.current = task; }, [task]);\r\n const [isOpen, setIsOpen] = useState(false);\r\n\r\n const openTask = useCallback(<TResult,>(next: AgenticoTask<TResult>) => {\r\n const current = taskRef.current;\r\n if (current && current.id !== next.id) current.onDismiss();\r\n // Chi possiede il task (SubagentRunner) ricostruisce l'intero oggetto a ogni\r\n // ri-registrazione (un cambio qualunque di agent.activity/running/...) SENZA sapere\r\n // nulla di `savedToDisk` (settato altrove, da commitEntityChat) — senza questo merge,\r\n // un follow-up in chat DOPO il salvataggio cancellerebbe silenziosamente il flag appena\r\n // impostato (bug osservato: il dialogo \"nuova chat\" tornava a chiedere conferma per una\r\n // chat già salvata). Preservato SOLO quando è genuinamente lo STESSO task (stesso id) —\r\n // un vero task nuovo riparte da zero, correttamente non salvato.\r\n const merged = (current && current.id === next.id && current.savedToDisk && next.savedToDisk === undefined)\r\n ? { ...next, savedToDisk: true }\r\n : next;\r\n setTaskState(merged as AgenticoTask);\r\n }, []);\r\n\r\n // Chiudere il task NON nasconde il pannello — il pannello è un \"posto persistente\", come\r\n // l'icona di un'estensione del browser: resta dov'è (magari vuoto) finché non è l'utente\r\n // stesso a nasconderlo col toggle globale.\r\n const closeTask = useCallback((id: string) => {\r\n setTaskState((prev) => (prev && prev.id === id ? null : prev));\r\n }, []);\r\n\r\n const toggleOpen = useCallback(() => setIsOpen((v) => !v), []);\r\n const openPanel = useCallback(() => setIsOpen(true), []);\r\n\r\n const [pendingGoal, setPendingGoal] = useState<string | null>(null);\r\n const [pendingExtraDestinations, setPendingExtraDestinations] = useState<ResultDestination[] | undefined>(undefined);\r\n const [pendingAttachments, setPendingAttachments] = useState<File[] | undefined>(undefined);\r\n const [pendingModel, setPendingModel] = useState<string | undefined>(undefined);\r\n // Cambia a ogni conversazione orchestratore avviata per davvero (ramo \"else\" sotto) — usato\r\n // come React `key` sull'elemento <OrchestratorRunner> (vedi Agentico.tsx), cosi un nuovo\r\n // startOrchestrator() smonta e rimonta un'istanza FRESCA invece di riusare quella precedente.\r\n const [orchestratorRunId, setOrchestratorRunId] = useState<string | null>(null);\r\n const [pendingCarryOver, setPendingCarryOver] = useState<PendingCarryOver | null>(null);\r\n const [pendingScopedSubagentId, setPendingScopedSubagentId] = useState<string | undefined>(undefined);\r\n\r\n // ── Contesto pagina, vecchio meccanismo a stack (useAgenticoPageContext) ──────────────────\r\n const [pageContextEntries, setPageContextEntries] = useState<Array<{ id: string; text: string; card: SubagentUsageCard | null }>>([]);\r\n const setPageContext = useCallback((id: string, text: string | null, card: SubagentUsageCard | null = null) => {\r\n setPageContextEntries((prev) => {\r\n const next = prev.filter((e) => e.id !== id);\r\n return text ? [...next, { id, text, card }] : next;\r\n });\r\n }, []);\r\n const lastPageContextEntry = pageContextEntries.length > 0 ? pageContextEntries[pageContextEntries.length - 1] : null;\r\n\r\n // ── Ancoraggi componente↔subagent, nuovo meccanismo (useSubagentComponentContext) ────────\r\n const [componentAnchorEntries, setComponentAnchorEntries] = useState<Array<{ id: string } & ComponentAnchor>>([]);\r\n const registerComponentAnchor = useCallback((id: string, anchor: ComponentAnchor | null) => {\r\n setComponentAnchorEntries((prev) => {\r\n const next = prev.filter((e) => e.id !== id);\r\n return anchor ? [...next, { id, ...anchor }] : next;\r\n });\r\n }, []);\r\n // NON deduplicato per subagentId — a differenza del vecchio stack pageContextEntries, QUI\r\n // più anchor per lo STESSO subagentId sono un caso reale e voluto (es. ThemeEditor, per i\r\n // campi tema, e SiteEditorPage, per i soli campi di site-branding, entrambi ancorati a\r\n // 'theme' mentre la modale è aperta — vedi SiteEditorPage.tsx): ognuno deve ricevere il\r\n // risultato, mai solo \"l'ultimo registrato\" (bug corretto: un dedup qui faceva sparire in\r\n // silenzio uno dei due listener). Il dedup per hint/scoping (sotto) resta separato.\r\n const componentAnchors = useMemo(\r\n () => componentAnchorEntries.map((e) => ({ subagentId: e.subagentId, componentKey: e.componentKey, onResult: e.onResult, pinnedEntityId: e.pinnedEntityId })),\r\n [componentAnchorEntries],\r\n );\r\n\r\n // QUI sì, deduplicato per subagentId — un hint/messaggio per subagent ancorato, non uno per\r\n // OGNI componente che lo ascolta (altrimenti un anchor \"solo ascolto\" senza propri\r\n // agenticoHint/userMessage, come SiteEditorPage, o due componenti sullo stesso subagent\r\n // produrrebbero hint duplicati/nessuno). L'ultimo registrato vince, stesso principio del\r\n // vecchio stack pageContextEntries.\r\n const uniqueAnchors = useMemo(() => {\r\n const bySubagent = new Map<string, ComponentAnchor>();\r\n for (const e of componentAnchorEntries) bySubagent.set(e.subagentId, { subagentId: e.subagentId, componentKey: e.componentKey, onResult: e.onResult, contextInfo: e.contextInfo });\r\n return [...bySubagent.values()];\r\n }, [componentAnchorEntries]);\r\n\r\n // Testi (agenticoHint/userMessage) per ogni anchor attivo il cui manifest dichiara davvero un\r\n // componentContexts per quella chiave — un anchor \"solo ascolto\" (es. SiteEditorPage, che\r\n // ascolta 'theme' senza essere uno dei suoi componenti dichiarati) non produce testo qui, ma\r\n // resta comunque nella lista `componentAnchors` sopra per il routing di onResult.\r\n const anchorTexts = useMemo(() => {\r\n return uniqueAnchors\r\n .map((a) => {\r\n const manifest = findSubagentManifest(a.subagentId);\r\n const ctx = manifest?.componentContexts?.[a.componentKey];\r\n if (!ctx) return null;\r\n // `contextInfo` (fatti live sull'entità aperta ORA, es. \"il tema aperto si\r\n // chiama ducatimilano.it\") in coda al testo statico del manifest — mai al posto\r\n // suo: l'hint spiega COSA fare, contextInfo COSA sa già il componente.\r\n const agenticoHint = a.contextInfo ? `${ctx.agenticoHint}\\n${a.contextInfo}` : ctx.agenticoHint;\r\n return { subagentId: a.subagentId, agenticoHint, userMessage: ctx.userMessage };\r\n })\r\n .filter((x): x is { subagentId: string; agenticoHint: string; userMessage: string } => x !== null);\r\n }, [uniqueAnchors]);\r\n\r\n const anchoredUserMessages = useMemo(() => anchorTexts.map((t) => t.userMessage), [anchorTexts]);\r\n\r\n // Testo combinato per il prompt dell'orchestratore — un solo hint se un solo subagent è\r\n // ancorato, altrimenti una premessa esplicita che elenca tutti i candidati e istruisce il\r\n // modello a chiedere all'utente quale intende prima di procedere (mai un tentativo silenzioso\r\n // di indovinare quale dei due l'utente vuole davvero).\r\n const anchoredPageContextText = useMemo(() => {\r\n if (anchorTexts.length === 0) return null;\r\n if (anchorTexts.length === 1) return anchorTexts[0].agenticoHint;\r\n const list = anchorTexts.map((t, i) => `${i + 1}. ${t.agenticoHint}`).join('\\n');\r\n return `${getAgenticoPrompts().multipleCandidates}\\n\\n${list}`;\r\n }, [anchorTexts]);\r\n\r\n const pageContext = anchoredPageContextText ?? lastPageContextEntry?.text ?? null;\r\n // Una card a campi (SubagentUsageCardView) ha senso solo per il vecchio meccanismo — un\r\n // anchor component-based mostra invece `anchoredUserMessages` (una frase, non un modulo).\r\n const pageContextCard = anchorTexts.length > 0 ? null : (lastPageContextEntry?.card ?? null);\r\n // Un solo subagent ancorato → scope diretto; zero o più di uno (ambiguo, l'LLM deve chiedere)\r\n // → ripiega sul vecchio pageContextCard (pagine non ancora migrate) o null.\r\n const uniqueAnchoredSubagentIds = useMemo(() => [...new Set(componentAnchors.map((a) => a.subagentId))], [componentAnchors]);\r\n const scopedSubagentId = uniqueAnchoredSubagentIds.length === 1\r\n ? uniqueAnchoredSubagentIds[0]\r\n : (uniqueAnchoredSubagentIds.length === 0 ? (lastPageContextEntry?.card?.subagentId ?? null) : null);\r\n\r\n const startOrchestrator = useCallback((goal: string, options?: { extraDestinations?: ResultDestination[]; attachments?: File[]; model?: string; scopedSubagentId?: string }) => {\r\n openPanel();\r\n const current = taskRef.current;\r\n if (current?.id === ORCHESTRATOR_TASK_ID) return; // già in corso, la porta a fuoco basta aprire il pannello\r\n\r\n // \"Stesso contesto\" = la pagina che ha invocato questa richiesta è scoped allo stesso\r\n // subagent del task già esistente (es. richiedi di nuovo \"estrai tema\" mentre il task\r\n // precedente per lo stesso subagent è ancora aperto) — in quel caso continua QUELLA\r\n // conversazione (stesso id, transcript prepeso) invece di aprirne una scollegata.\r\n const existingTask = (scopedSubagentId && current?.id.startsWith(`subagent-${scopedSubagentId}-`)) ? current : undefined;\r\n setPendingCarryOver(existingTask ? {\r\n taskId: existingTask.id,\r\n activity: [...(existingTask.precedingActivity ?? []), ...existingTask.agent.activity],\r\n } : null);\r\n setPendingGoal(goal);\r\n setPendingExtraDestinations(options?.extraDestinations);\r\n setPendingAttachments(options?.attachments);\r\n setPendingModel(options?.model);\r\n setPendingScopedSubagentId(options?.scopedSubagentId);\r\n setOrchestratorRunId(crypto.randomUUID());\r\n }, [openPanel, scopedSubagentId]);\r\n\r\n const commitEntityChat = useCallback((subagentId: string, entityId: string, taskId: string | null | undefined, scopeId: string | undefined, params: Record<string, unknown>, title: string) => {\r\n if (!taskId || !scopeId) return; // nessuna estrazione coinvolta in questo salvataggio\r\n const current = taskRef.current;\r\n if (!current || current.id !== taskId) return; // il task che ha popolato il draft non è più quello vivo\r\n const snap = current.agent.snapshot();\r\n if (!snap) return;\r\n // `current.precedingActivity` (vedi PendingCarryOver/orchestrator→subagent handoff) è il\r\n // trascritto prepeso SOLO per la visualizzazione (displayedActivity in Agentico.tsx) —\r\n // l'`activity` salvata qui deve includerlo esplicitamente, altrimenti un salvataggio dopo\r\n // aver continuato una conversazione già in corso perderebbe comunque la parte precedente.\r\n saveEntityChat({\r\n scopeId, subagentId, entityId, params, title,\r\n history: snap.history,\r\n activity: [...(current.precedingActivity ?? []), ...snap.activity],\r\n });\r\n setTaskState((prev) => (prev && prev.id === taskId ? { ...prev, savedToDisk: true } : prev));\r\n }, []);\r\n\r\n const [pendingResume, setPendingResume] = useState<{ subagentId: string; entityId: string; snapshot: EntityChatSnapshot } | null>(null);\r\n const [resumeRunId, setResumeRunId] = useState<string | null>(null);\r\n\r\n const resumeEntityChat = useCallback((subagentId: string, entityId: string, snapshot: EntityChatSnapshot) => {\r\n openPanel();\r\n setPendingResume({ subagentId, entityId, snapshot });\r\n setResumeRunId(crypto.randomUUID());\r\n }, [openPanel]);\r\n\r\n const [pendingManualEntry, setPendingManualEntry] = useState<{ subagentId: string; extraDestinations?: ResultDestination[] } | null>(null);\r\n const [manualEntryRunId, setManualEntryRunId] = useState<string | null>(null);\r\n\r\n const startManualEntry = useCallback((subagentId: string, extraDestinations?: ResultDestination[]) => {\r\n openPanel();\r\n // Stesso identico trattamento di un vero handoff AI-driven (OrchestratorRunner.\r\n // handleResult, ramo 'handoff'): closeTask diretto, MAI onDismiss — quando il\r\n // ManualEntryRunner monta ed esegue il proprio openTask, `taskRef.current` è già `null`\r\n // (svuotato qui), quindi il ramo \"sostituisci il task precedente\" di openTask non scatta\r\n // nemmeno: nessun problema, l'agent dell'orchestratore semplicemente non produce più\r\n // attività dopo questo punto, quindi il suo effect di registrazione non riparte da solo.\r\n closeTask(ORCHESTRATOR_TASK_ID);\r\n setPendingManualEntry({ subagentId, extraDestinations });\r\n setManualEntryRunId(crypto.randomUUID());\r\n }, [openPanel, closeTask]);\r\n\r\n const [pendingResumeWithAI, setPendingResumeWithAI] = useState<{ subagentId: string; params: Record<string, unknown>; extraDestinations?: ResultDestination[] } | null>(null);\r\n const [resumeWithAIRunId, setResumeWithAIRunId] = useState<string | null>(null);\r\n\r\n const resumeWithAI = useCallback((subagentId: string, params: Record<string, unknown>, extraDestinations?: ResultDestination[]) => {\r\n // Smonta SUBITO il ManualEntryRunner corrente (invece di un closeTask diretto, come in\r\n // startManualEntry sopra) — a differenza dell'orchestratore dopo un handoff,\r\n // ManualEntryRunner resta un componente vivo che si ri-registra a ogni cambio del proprio\r\n // stato (result/applyLabel/...): un semplice closeTask() qui verrebbe subito vanificato\r\n // dal suo prossimo re-render. Smontarlo (pendingManualEntry → null) fa scattare il suo\r\n // effect di cleanup (closeTask sul PROPRIO id) prima che il nuovo SubagentRunner monti e\r\n // si registri — nessuna corsa tra i due task.\r\n setPendingManualEntry(null);\r\n setPendingResumeWithAI({ subagentId, params, extraDestinations });\r\n setResumeWithAIRunId(crypto.randomUUID());\r\n }, []);\r\n\r\n const [pendingStart, setPendingStart] = useState<{ subagentId: string; params: Record<string, unknown>; taskId?: string; openingMessage?: string } | null>(null);\r\n const [startRunId, setStartRunId] = useState<string | null>(null);\r\n\r\n const startSubagent = useCallback((subagentId: string, params: Record<string, unknown>, options?: { taskId?: string; openingMessage?: string }) => {\r\n openPanel();\r\n setPendingStart({ subagentId, params, ...options });\r\n setStartRunId(crypto.randomUUID());\r\n }, [openPanel]);\r\n\r\n const dismissForeignTask = useCallback(() => {\r\n const current = taskRef.current;\r\n if (!current) return;\r\n current.onDismiss();\r\n }, []);\r\n\r\n const value = useMemo(\r\n () => ({\r\n scope, task, isOpen, openTask, closeTask, toggleOpen, pendingGoal, startOrchestrator,\r\n pageContext, pageContextCard, setPageContext, scopedSubagentId, pendingScopedSubagentId,\r\n componentAnchors, anchoredUserMessages, registerComponentAnchor,\r\n openPanel, pendingExtraDestinations,\r\n pendingAttachments, pendingModel, orchestratorRunId, pendingCarryOver, commitEntityChat,\r\n resumeEntityChat, pendingResume, resumeRunId, dismissForeignTask,\r\n pendingManualEntry, manualEntryRunId, startManualEntry,\r\n pendingResumeWithAI, resumeWithAIRunId, resumeWithAI,\r\n startSubagent, pendingStart, startRunId,\r\n }),\r\n [scope, task, isOpen, openTask, closeTask, toggleOpen, pendingGoal, startOrchestrator, pageContext, pageContextCard, setPageContext, scopedSubagentId, pendingScopedSubagentId, componentAnchors, anchoredUserMessages, registerComponentAnchor, openPanel, pendingExtraDestinations, pendingAttachments, pendingModel, orchestratorRunId, pendingCarryOver, commitEntityChat, resumeEntityChat, pendingResume, resumeRunId, dismissForeignTask, pendingManualEntry, manualEntryRunId, startManualEntry, pendingResumeWithAI, resumeWithAIRunId, resumeWithAI, startSubagent, pendingStart, startRunId],\r\n );\r\n\r\n return (\r\n <AgenticoContext.Provider value={value}>\r\n {children}\r\n </AgenticoContext.Provider>\r\n );\r\n}\r\n\r\nexport function useAgentico() {\r\n const context = useContext(AgenticoContext);\r\n\r\n if (!context) {\r\n throw new Error('useAgentico must be used inside AgenticoProvider');\r\n }\r\n\r\n return context;\r\n}\r\n","import { useLayoutEffect, useRef, useState } from 'react';\nimport { ChatMarkdown, ChatQuestions, ChatReasoning, Code, Icon, Tree, interpolate } from '@llmnative/react';\nimport type { AgentActivityEntry } from './useAgent';\nimport type { AgentMessagePart, AgentQuestionAnswer, AgentToolPart } from '../domain/messageParts';\nimport { useAgenticoI18n, type AgenticoDict } from '../i18n';\n\n/** Un blocco ```lang ... ``` dentro al testo, o testo semplice attorno — il modello spesso\n * mescola un commento libero con un blocco di codice/JSON (vedi lo screenshot che ha portato\n * a questo componente: la risposta di un tool con un blocco ```json grezzo). */\ntype DetailSegment = { type: 'text'; content: string } | { type: 'code'; content: string; language?: string };\n\n/** Individua un blob JSON bilanciato dentro un pezzo di testo SENZA fence attorno (il modello a\n * volte lo dimentica del tutto sui turni di follow-up, non solo sul primo — bug segnalato: un\n * `{\"matches\": [...]}` grezzo comparso come muro di testo in chat, niente collapse, niente\n * evidenziazione). Stesso algoritmo a conteggio graffe/parentesi di extractJsonBlock (ignora\n * quelle dentro le stringhe) — soglia di lunghezza minima per non trattare come \"codice\" un\n * oggetto minuscolo citato di sfuggita in una frase. */\nfunction splitUnfencedJsonSegments(text: string): DetailSegment[] {\n const start = text.search(/[{[]/);\n if (start === -1) return [{ type: 'text', content: text }];\n\n const open = text[start];\n const close = open === '{' ? '}' : ']';\n let depth = 0;\n let inString = false;\n let escaped = false;\n let end = -1;\n for (let i = start; i < text.length; i++) {\n const ch = text[i];\n if (inString) {\n if (escaped) escaped = false;\n else if (ch === '\\\\') escaped = true;\n else if (ch === '\"') inString = false;\n continue;\n }\n if (ch === '\"') { inString = true; continue; }\n if (ch === open) depth++;\n else if (ch === close) { depth--; if (depth === 0) { end = i; break; } }\n }\n\n const candidate = end === -1 ? null : text.slice(start, end + 1);\n if (!candidate || candidate.length < 60) return [{ type: 'text', content: text }];\n try { JSON.parse(candidate); } catch { return [{ type: 'text', content: text }]; }\n\n const segments: DetailSegment[] = [];\n if (start > 0) segments.push({ type: 'text', content: text.slice(0, start) });\n segments.push({ type: 'code', content: candidate, language: 'json' });\n const rest = text.slice(end + 1);\n if (rest.trim()) segments.push(...splitUnfencedJsonSegments(rest));\n return segments;\n}\n\n/** Divide `detail` in segmenti testo/codice sui fence ```lang\\n...``` — non un parser\n * markdown completo, solo quel tanto che serve a non mostrare i backtick letterali all'utente\n * e a poter passare il contenuto del blocco al componente Code (evidenziazione + copia) del\n * framework invece che a un <p> piatto. Ogni segmento di testo risultante passa POI per\n * `splitUnfencedJsonSegments`, cosi un blob JSON senza fence attorno riceve comunque lo stesso\n * trattamento (collassato/espandibile) di uno fenced, invece di restare testo piatto. */\nfunction parseDetailSegments(text: string): DetailSegment[] {\n const segments: DetailSegment[] = [];\n const fenceRe = /```(\\w+)?\\n?([\\s\\S]*?)```/g;\n let lastIndex = 0;\n let match: RegExpExecArray | null;\n while ((match = fenceRe.exec(text))) {\n if (match.index > lastIndex) segments.push(...splitUnfencedJsonSegments(text.slice(lastIndex, match.index)));\n segments.push({ type: 'code', content: match[2].replace(/\\n$/, ''), language: match[1] });\n lastIndex = fenceRe.lastIndex;\n }\n if (lastIndex < text.length) segments.push(...splitUnfencedJsonSegments(text.slice(lastIndex)));\n return segments;\n}\n\n/** Blocco di codice nel log — SEMPRE visibile non appena la riga che lo contiene lo è (niente\n * doppio collapse: non serve un primo click solo per farlo apparire, quello era il problema\n * del vecchio testo ```json grezzo in riga). Il collapse che resta è solo quello proprio del\n * blocco: parte alto ~3-4 righe (evidenziazione Prism del framework), un clic lo espande a\n * un'altezza maggiore con scroll interno se il contenuto non ci sta comunque. Copia solo da\n * espanso — da collassato il bottone verrebbe tagliato dal max-height comunque. */\nfunction DetailCodeBlock({ content, language }: { content: string; language?: string }) {\n const [expanded, setExpanded] = useState(false);\n return (\n <div\n className={`relative cursor-pointer rounded-md ${expanded ? 'max-h-80 overflow-y-auto' : 'max-h-28 overflow-hidden'}`}\n onClick={() => setExpanded((v) => !v)}\n title={expanded ? 'Clic per comprimere' : 'Clic per espandere'}\n >\n <Code language={language as never} showCopy={expanded} background=\"transparent\" className=\"!text-[11px]\">\n {content}\n </Code>\n {!expanded && (\n <div className=\"pointer-events-none absolute inset-x-0 bottom-0 h-6 bg-gradient-to-t from-muted/30 to-transparent\" />\n )}\n </div>\n );\n}\n\n/** Il contenuto di `detail` — testo (Markdown, con `ChatMarkdown` del framework, CR-004) più\n * eventuali blocchi di codice, ciascuno reso col syntax-highlighter del framework (collassabile,\n * vedi DetailCodeBlock) invece che coi backtick grezzi. */\nfunction DetailContent({ text, tone }: { text: string; tone?: 'default' | 'muted' }) {\n const segments = parseDetailSegments(text);\n return (\n <>\n {segments.map((seg, i) => seg.type === 'code'\n ? <DetailCodeBlock key={i} content={seg.content} language={seg.language} />\n : (seg.content.trim() && <ChatMarkdown key={i} content={seg.content.trim()} tone={tone} className=\"break-words text-xs leading-relaxed\" />)\n )}\n </>\n );\n}\n\n/** Icona di stato di una voce di attività — icon-system del framework (Lucide), mai emoji.\n * \"running\" resta lo spinner a bordo rotante già usato da LoadingButton in tutto il CMS,\n * non un'icona Lucide: stessa identica animazione di ogni altro stato di caricamento. */\nfunction ActivityStatusIcon({ status }: { status: AgentActivityEntry['status'] }) {\n if (status === 'running') {\n return <span className=\"inline-block h-3 w-3 shrink-0 animate-spin rounded-full border-2 border-current border-t-transparent\" />;\n }\n if (status === 'done') return <Icon name=\"check\" size={12} className=\"shrink-0 text-green-600\" />;\n if (status === 'stopped') return <Icon name=\"circle-slash\" size={12} className=\"shrink-0 text-muted-foreground\" />;\n return <Icon name=\"x\" size={12} className=\"shrink-0 text-destructive\" />;\n}\n\n/** \"Sto pensando…\" è transitorio — SOLO mentre quel turno è `running`. Appena arriva la\n * risposta, la riga smette di dire \"sto pensando\" (che a quel punto sarebbe falso: ha già\n * finito). Nessuna anteprima-prima-riga qui: la prosa (se c'è) è sempre mostrata per intero\n * subito sotto (vedi ActivityRow), quindi un'etichetta che ne anticipi un pezzo sarebbe solo\n * la stessa frase ripetuta due volte a distanza di un rigo — esattamente il riepilogo del\n * micro-prompt \"closing-summary\" che, prima di questa versione, veniva sia troncato in\n * etichetta SIA ripetuto per intero subito sotto una volta espanso. */\nfunction thinkingDisplayLabel(entry: AgentActivityEntry, t: AgenticoDict): string {\n if (entry.status === 'running') return t.thinking;\n if (entry.status === 'error') return t.modelRequestError;\n if (entry.status === 'stopped') return t.stopped;\n if (!entry.detail) {\n // Il modello ha richiesto tool_calls senza scrivere testo libero — dice direttamente\n // lo scopo generale dell'azione (frasi già complete, vedi AgentTool.describeIntent)\n // invece del generico \"Richiesta di eseguire dei tool\". Nessun prefisso \"Eseguo:\"\n // aggiunto qui: ogni frase è già completa di suo, e i parametri della chiamata\n // specifica sono già sulla riga della tool call subito sotto — ripeterli qui sarebbe\n // di nuovo la stessa duplicazione che questo campo esiste per evitare.\n if (entry.toolNames && entry.toolNames.length > 0) return entry.toolNames.join(' · ');\n return t.toolRequest;\n }\n return t.responseReceived;\n}\n\n/** Altezza da collassato, in px — abbastanza per ~4 righe a text-xs. Sopra questa soglia la\n * bolla utente collassa di default (fade + maniglia \"espandi\" sotto, stesso pattern di\n * DetailCodeBlock) invece di occupare tutto lo spazio verticale disponibile: la bolla è\n * `sticky top-0`, quindi un messaggio lungo resterebbe agganciato in cima schiacciando via\n * la vista sull'attività/risposta dell'AI che scorre sotto — esattamente il problema segnalato. */\nconst USER_BUBBLE_COLLAPSED_MAX_HEIGHT = 96;\n\n/** Bolla utente — collassabile SOLO se il testo eccede l'altezza di soglia (misurato via ref,\n * niente stima a priori sul numero di caratteri: dipende da wrap/allegati/lingua). L'unica zona\n * cliccabile per espandere/comprimere è la maniglia dedicata in fondo alla bolla (chevron),\n * MAI l'intera bolla — stesso motivo per cui ChatGPT/Claude fanno lo stesso: il testo dentro\n * deve restare selezionabile col mouse senza che un click/trascinamento apra o chiuda la bolla.\n * Non inventato da zero: è il pattern \"show more/less con maniglia separata\" già standard nei\n * chatbot conversazionali per i turni utente lunghi.\n *\n * Sticky in cima (CR-004) ma solo dentro il suo turno (vedi AgentActivityLog): quando il turno\n * finisce di scorrere, la bolla se ne va con lui invece di restare sotto quella del turno dopo\n * (prima tutte le bolle erano sticky nella stessa lista, e una bolla più alta spuntava sotto\n * una più bassa, sopra il testo). Sfondo pieno e `z-20`: il testo che scorre sotto (tabelle,\n * codice) non le passa sopra. */\nfunction UserMessageBubble({ entry }: { entry: AgentActivityEntry }) {\n const contentRef = useRef<HTMLDivElement>(null);\n const [expanded, setExpanded] = useState(false);\n const [overflowing, setOverflowing] = useState(false);\n\n useLayoutEffect(() => {\n const el = contentRef.current;\n if (!el) return;\n setOverflowing(el.scrollHeight > USER_BUBBLE_COLLAPSED_MAX_HEIGHT + 1);\n }, [entry.detail, entry.attachmentNames]);\n\n const collapsed = overflowing && !expanded;\n\n return (\n <div className=\"sticky top-0 z-20 flex justify-end border-b border-border/40 bg-background py-1.5\">\n <div className=\"max-w-[85%] rounded-lg bg-primary/10 text-xs text-foreground\">\n <div\n ref={contentRef}\n className={`space-y-1 px-2.5 pt-1.5 ${collapsed ? 'overflow-hidden' : ''}`}\n style={collapsed ? { maxHeight: USER_BUBBLE_COLLAPSED_MAX_HEIGHT } : undefined}\n >\n {entry.attachmentNames && entry.attachmentNames.length > 0 && (\n // Pillole SOPRA il testo, non dentro DetailContent — sono metadati del\n // messaggio (cosa era allegato), non parte del testo scritto dall'utente,\n // niente markdown/segmenti codice da applicargli.\n <div className=\"flex flex-wrap gap-1\">\n {entry.attachmentNames.map((name, i) => (\n <span key={i} className=\"inline-flex items-center gap-1 rounded-md bg-primary/15 px-1.5 py-0.5 text-[11px] text-foreground\">\n <Icon name=\"paperclip\" size={10} className=\"shrink-0\" />\n <span className=\"max-w-[160px] truncate\">{name}</span>\n </span>\n ))}\n </div>\n )}\n <DetailContent text={entry.detail ?? ''} />\n </div>\n {overflowing && (\n // Riquadro cliccabile a sé, non un overlay sopra il testo — un `padding`\n // generoso (non un filo di 1px) rende la maniglia facile da colpire senza\n // dover mirare con precisione, mantenendo comunque il testo sopra intoccato.\n <button\n type=\"button\"\n onClick={() => setExpanded((v) => !v)}\n className=\"relative flex w-full items-center justify-center rounded-b-lg py-1 text-muted-foreground hover:bg-primary/15\"\n title={expanded ? 'Comprimi' : 'Espandi'}\n >\n {collapsed && (\n <div className=\"pointer-events-none absolute inset-x-0 -top-4 h-4 bg-gradient-to-t from-primary/10 to-transparent\" />\n )}\n <Icon name={expanded ? 'chevron-up' : 'chevron-down'} size={12} />\n </button>\n )}\n </div>\n </div>\n );\n}\n\n/** The icon of a step in its group: the framework Icon names, coloured by the Tree as muted. */\nconst STEP_ICONS: Record<AgentToolPart['status'], string> = {\n running: 'loader-circle',\n done: 'check',\n error: 'x',\n stopped: 'circle-slash',\n};\n\n/** Consecutive steps of a remote agent (CR-004), as one collapsible group: a `Tree` of the\n * framework with one branch, closed by default. Its line says the step running now, or how many\n * steps there were; open, it lists them with their status. */\nfunction StepsGroup({ steps }: { steps: AgentToolPart[] }) {\n const t = useAgenticoI18n();\n const running = steps.filter((s) => s.status === 'running').at(-1);\n const failed = steps.some((s) => s.status === 'error');\n const summary = running?.label ?? (steps.length === 1 ? t.oneStep : interpolate(t.steps, { count: steps.length }));\n return (\n <Tree\n ariaLabel={t.stepsLabel}\n selectedId={null}\n itemClassName=\"py-0.5 text-xs text-muted-foreground\"\n nodes={[{\n id: `steps-${steps[0]?.id ?? ''}`,\n label: summary,\n icon: running ? 'loader-circle' : failed ? 'circle-alert' : 'list-checks',\n children: steps.map((step) => ({\n id: step.id,\n label: <span className={step.status === 'error' ? 'text-destructive' : undefined}>{step.label}</span>,\n icon: STEP_ICONS[step.status],\n })),\n }]}\n />\n );\n}\n\n/** What the log needs to let the person answer questions (CR-004): the entries of the live\n * conversation (`canAnswer`) and where the answers go. Without it, unanswered questions are shown\n * disabled. */\nexport interface AgentActivityLogQuestions {\n canAnswer: (entryId: string) => boolean;\n onAnswer: (entryId: string, partId: string, answers: AgentQuestionAnswer[]) => Promise<unknown>;\n}\n\n/** A message in parts (CR-004), in the order the parts arrived: text with `ChatMarkdown`\n * (repaired while it streams), reasoning with `ChatReasoning`, consecutive steps as one group,\n * questions with `ChatQuestions` (read-only once answered). */\nfunction MessageParts({ entry, questions }: { entry: AgentActivityEntry; questions?: AgentActivityLogQuestions }) {\n const parts = entry.parts ?? [];\n const blocks: Array<{ key: string; part: Exclude<AgentMessagePart, AgentToolPart> } | { key: string; steps: AgentToolPart[] }> = [];\n for (const part of parts) {\n const last = blocks.at(-1);\n if (part.type === 'tool') {\n if (last && 'steps' in last) last.steps.push(part);\n else blocks.push({ key: `tool-${part.id}`, steps: [part] });\n } else {\n blocks.push({ key: `${part.type}-${part.id}`, part });\n }\n }\n const answerable = Boolean(questions?.canAnswer(entry.id));\n return (\n <div className=\"space-y-2\">\n {blocks.map((block) => {\n if ('steps' in block) return <StepsGroup key={block.key} steps={block.steps} />;\n const { part } = block;\n if (part.type === 'text') {\n return part.text.trim()\n ? <ChatMarkdown key={block.key} content={part.text} streaming={part.streaming} className=\"break-words text-xs leading-relaxed text-foreground\" />\n : null;\n }\n if (part.type === 'reasoning') {\n return <ChatReasoning key={block.key} content={part.text} streaming={part.streaming} duration={part.duration} className=\"my-0\" />;\n }\n return (\n <ChatQuestions\n key={block.key}\n questions={part.questions}\n {...(part.answers ? { answers: part.answers } : {})}\n disabled={!part.answers && !answerable}\n onSubmit={questions && answerable ? (answers) => questions.onAnswer(entry.id, part.id, answers) : undefined}\n className=\"text-xs\"\n />\n );\n })}\n </div>\n );\n}\n\nfunction ActivityRow({ entry, questions }: { entry: AgentActivityEntry; questions?: AgentActivityLogQuestions }) {\n const t = useAgenticoI18n();\n\n // A message in parts (remote agent, CR-004): the parts say it all; a status line only while\n // nothing arrived yet, or when the turn stopped or failed.\n if (entry.kind === 'thinking' && entry.parts) {\n const showStatus = entry.status === 'error' || entry.status === 'stopped' || (entry.status === 'running' && entry.parts.length === 0);\n return (\n <li className=\"text-xs text-muted-foreground\">\n {entry.parts.length > 0 && <MessageParts entry={entry} questions={questions} />}\n {showStatus && (\n <div className={`flex items-start gap-1.5 ${entry.parts.length > 0 ? 'mt-1' : ''}`}>\n <span className=\"pt-px\"><ActivityStatusIcon status={entry.status} /></span>\n <span className=\"min-w-0 flex-1 whitespace-pre-wrap break-words\">{thinkingDisplayLabel(entry, t)}</span>\n </div>\n )}\n </li>\n );\n }\n\n const segments = entry.kind === 'thinking' && entry.detail ? parseDetailSegments(entry.detail) : [];\n const codeSegments = segments.filter((s): s is Extract<DetailSegment, { type: 'code' }> => s.type === 'code');\n const textSegments = segments.filter((s) => s.type === 'text' && s.content.trim());\n const label = entry.kind === 'thinking' ? thinkingDisplayLabel(entry, t) : entry.label;\n\n return (\n <li className=\"text-xs text-muted-foreground\">\n {/* `items-start`, non `items-center`: l'etichetta va a capo invece di troncarsi\n (niente `truncate`/ellissi), e le righe successive devono restare allineate\n sotto la prima, non sotto l'icona di stato — l'icona (larghezza fissa via\n shrink-0) e il testo (flex-1) restano comunque due colonne separate qualunque\n sia l'altezza del testo. */}\n <div className=\"flex items-start gap-1.5\">\n <span className=\"pt-px\"><ActivityStatusIcon status={entry.status} /></span>\n <span className=\"min-w-0 flex-1 whitespace-pre-wrap break-words\">{label}</span>\n </div>\n {codeSegments.length > 0 && (\n <div className=\"mt-1 ml-[18px] space-y-1\">\n {codeSegments.map((seg, i) => <DetailCodeBlock key={i} content={seg.content} language={seg.language} />)}\n </div>\n )}\n {textSegments.length > 0 && (\n // Testo libero, non incastonato in un box — stessa resa delle altre righe\n // (\"Sto ricontrollando…\" ecc.), niente bordo/sfondo/scroll proprio: scorre con\n // lo scroll principale del pannello, non con un secondo scroll interno. Markdown\n // GFM con `ChatMarkdown` del framework (CR-004: tabelle, elenchi, titoli).\n <div className=\"mt-1 ml-[18px] space-y-2\">\n {textSegments.map((seg, i) => (\n <ChatMarkdown key={i} content={seg.content.trim()} tone=\"muted\" className=\"break-words text-xs leading-relaxed\" />\n ))}\n </div>\n )}\n </li>\n );\n}\n\n/** Il log di attività di un agente, usato dal pannello globale (Agentico) — ogni agente ora\n * ci passa attraverso, il pannello embedded legacy (AgentFollowUp) è stato ritirato una volta\n * migrato anche l'ultimo consumer (import HTML/JSX, vedi ComponentEditPage.tsx). Un solo posto\n * per l'icon-system e la logica \"sto pensando\" transitorio/collassabile.\n *\n * One `<li>` per turn (CR-004): the message of the person, sticky at the top while its turn\n * scrolls, then what the agent did and said. Entries before the first message of the person\n * (an opening without text) form a turn of their own, without a bubble. */\nexport function AgentActivityLog({ activity, questions }: { activity: AgentActivityEntry[]; questions?: AgentActivityLogQuestions }) {\n if (activity.length === 0) return null;\n const turns: Array<{ user?: AgentActivityEntry; entries: AgentActivityEntry[] }> = [];\n for (const entry of activity) {\n if (entry.kind === 'user' || turns.length === 0) turns.push(entry.kind === 'user' ? { user: entry, entries: [] } : { entries: [entry] });\n else turns[turns.length - 1].entries.push(entry);\n }\n return (\n <ul className=\"space-y-0.5\">\n {turns.map((turn) => (\n <li key={turn.user?.id ?? turn.entries[0]?.id}>\n {turn.user && <UserMessageBubble entry={turn.user} />}\n {turn.entries.length > 0 && (\n <ul className=\"space-y-0.5\">\n {turn.entries.map((entry) => <ActivityRow key={entry.id} entry={entry} questions={questions} />)}\n </ul>\n )}\n </li>\n ))}\n </ul>\n );\n}\n","import React from 'react';\nimport { ActionButton } from '@llmnative/react';\nimport { useAgentico } from './AgenticoContext';\nimport { useAgenticoI18n } from '../i18n';\n\n/** L'unica icona che apre/chiude il pannello agente globale, Agentico (Agentico.tsx) — stile\n * icona-estensione-del-browser: sempre nello stesso angolo, sempre lo stesso significato, sia\n * che tu stia guardando il CMS a schermo pieno (nell'Header) sia che ci sia una modale right\n * aperta sopra (come `closeSlot` di quella Modal, al posto della sua ×). Un'unica istanza di\n * questo componente — mai una ricreata ad hoc — cosi il comportamento resta identico ovunque\n * compaia. `ActionButton` del framework (non un <button> a mano) per ereditare `cursor-pointer`\n * e lo stato hover/focus-visible già standard su ogni bottone del CMS dalla classe condivisa\n * `.btn` — solo sfondo/dimensione sono nostri via className, nessuna variante colore predefinita\n * si adatta a un'icona-toggle isolata come questa.\n *\n * Animata (icona pulsante) quando l'unico task attivo ha l'agente al lavoro — anche se il\n * pannello è chiuso, cosi si vede che sta succedendo qualcosa senza doverlo aprire apposta\n * (l'agente NON apre più da solo il pannello durante un run(), vedi AgenticoContext). */\nexport function AgenticoToggle({ className = '' }: { className?: string }) {\n const { isOpen, toggleOpen, task } = useAgentico();\n const t = useAgenticoI18n();\n const running = task?.agent.running ?? false;\n\n return (\n <ActionButton\n icon=\"bot\"\n ariaLabel={isOpen ? t.closePanel : t.openPanel}\n title={isOpen ? t.closePanel : t.openPanel}\n onClick={toggleOpen}\n iconClassName={running ? 'animate-pulse' : undefined}\n className={`!h-9 !w-9 !p-0 rounded-md ${\n isOpen ? 'bg-primary/10 text-primary hover:bg-primary/15' : 'bg-transparent text-muted-foreground hover:bg-accent hover:text-foreground'\n } ${className}`}\n />\n );\n}\n","// Estratto da useAgent.ts — la sola parte \"catalogo modelli disponibili per il tenant\",\n// riusabile anche da chi non ha (ancora) una sessione useAgent attiva, es.\n// AgenticoStarterComposer (Agentico.tsx): l'utente deve poter scegliere il modello ANCHE\n// prima che l'orchestratore parta, non solo nei turni di follow-up.\nimport { useEffect, useState } from 'react';\nimport { getAIModelCatalog, useAIProviderRegistry } from '@llmnative/react';\nimport type { AIModelDescriptor, AIProviderAdapter } from '@llmnative/react';\n\nexport interface UseAIModelCatalogResult {\n hasProviders: boolean;\n modelsByProvider: Record<string, AIModelDescriptor[]>;\n modelsLoading: boolean;\n selectedModel: string;\n setSelectedModel: (id: string) => void;\n}\n\n// Un solo modello selezionato per l'intera app (non per subagent/tab) — tutti gli usi\n// dell'hook (starter composer, sessione useAgent attiva, ...) devono vedere la stessa scelta,\n// quindi una chiave globale condivisa, non una per istanza.\nconst SELECTED_MODEL_STORAGE_KEY = 'llmnative-cms.selectedAIModel';\n\nfunction readStoredModel(): string {\n try {\n return localStorage.getItem(SELECTED_MODEL_STORAGE_KEY) ?? '';\n } catch {\n return ''; // storage non disponibile (privacy mode, ecc.) — nessuna persistenza, non un errore fatale\n }\n}\n\nfunction writeStoredModel(id: string): void {\n try {\n if (id) localStorage.setItem(SELECTED_MODEL_STORAGE_KEY, id);\n else localStorage.removeItem(SELECTED_MODEL_STORAGE_KEY);\n } catch {\n // idem — persistenza best-effort\n }\n}\n\nexport function useAIModelCatalog(): UseAIModelCatalogResult {\n const registry: Record<string, AIProviderAdapter> = useAIProviderRegistry()?.registry ?? {};\n const hasProviders = Object.keys(registry).length > 0;\n const [modelsByProvider, setModelsByProvider] = useState<Record<string, AIModelDescriptor[]>>({});\n const [modelsLoading, setModelsLoading] = useState(false);\n const [selectedModel, setSelectedModelState] = useState(readStoredModel);\n\n const setSelectedModel = (id: string) => {\n writeStoredModel(id);\n setSelectedModelState(id);\n };\n\n useEffect(() => {\n if (!hasProviders) { setModelsByProvider({}); return; }\n let cancelled = false;\n setModelsLoading(true);\n getAIModelCatalog(registry).then((catalog) => {\n if (cancelled) return;\n setModelsByProvider(catalog.modelsByProvider);\n const flat = Object.values(catalog.modelsByProvider).flat();\n // BUG FISSATO: prima, se la selezione corrente non era più nel catalogo, si\n // ripiegava sul `defaultModel` hardcoded del primo provider (o sul primo modello\n // qualunque) — un default scritto a mano nel file del provider framework, che\n // diventa obsoleto ogni volta che il catalogo dei modelli disponibili cambia\n // upstream (i modelli AI cambiano molto più spesso di quanto venga aggiornato il\n // codice). Nessun auto-fallback: se la selezione non è più valida, resta vuota —\n // l'utente è costretto a sceglierne uno esplicitamente dal menu (stesso principio\n // già in uso per il caso \"nessuna selezione ancora fatta\", vedi readyToStart in\n // OrchestratorRunner.tsx/SubagentRunner.tsx, che già gestiva selectedModel === '').\n // Stesso trattamento per il valore riletto da localStorage all'avvio: se il modello\n // salvato non esiste più nel catalogo (rimosso/rinominato upstream), si scarta e si\n // ripulisce anche lo storage — mai un ID stantio che farebbe fallire ogni richiesta\n // in silenzio finché l'utente non lo cambia a mano.\n setSelectedModelState((prev) => {\n if (flat.some((m) => m.id === prev)) return prev;\n writeStoredModel('');\n return '';\n });\n }).catch((error) => {\n // BUG FISSATO: questo catch azzerava modelsByProvider SENZA loggare nulla — un\n // fallimento qui (getAIModelCatalog rigettato) sembrava dal lato utente \"nessun\n // errore in console, tendina modello sparita\" senza alcuna pista per diagnosticarlo.\n // getAIModelCatalog ora isola i fallimenti per singolo provider (framework,\n // shared.ts), quindi questo ramo dovrebbe restare eccezionale — ma se scatta\n // comunque, deve almeno essere visibile.\n console.error('useAIModelCatalog: getAIModelCatalog failed', error);\n if (!cancelled) setModelsByProvider({});\n })\n .finally(() => { if (!cancelled) setModelsLoading(false); });\n return () => { cancelled = true; };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [hasProviders, JSON.stringify(Object.keys(registry).sort())]);\n\n return { hasProviders, modelsByProvider, modelsLoading, selectedModel, setSelectedModel };\n}\n","// Hook condiviso per ogni feature \"opt-in AI\" del CMS (import tema, import HTML/JSX, e i\n// prossimi) — un solo posto da correggere se il meccanismo cambia (vedi il bug del registry\n// sempre popolato risolto in react/src/providers/manifest.ts — prima avrebbe dovuto essere\n// corretto in due punti diversi di questo repo).\n//\n// `useAgent` è l'UNICO esecutore: consuma un `AgentDefinition` (../domain/agentDefinition) — il\n// chiamante non costruisce mai il prompt né chiama il provider direttamente. Il controller\n// dell'agente (`buildContext`) gira sempre, con o senza AI abilitata; `run()` ritorna il\n// fallback deterministico quando l'AI è spenta, non disponibile, o la sua risposta non è\n// valida — mai un errore non gestito per il chiamante.\n//\n// Conversazione multi-turno: `run()` avvia una sessione (controller + primo prompt),\n// `sendMessage()` la continua — l'utente affina la richiesta (\"controlla anche /about\",\n// \"riprova con questo markup\") e il modello può invocare i tool dell'agente (getTools) con i\n// parametri che l'utente ha suggerito, invece di dover essere ririchiamato da zero dal\n// chiamante. Il loop tool_use → execute → continua è interamente interno a questo hook.\nimport { useEffect, useRef, useState } from 'react';\nimport { parseAIModelRef, PromptUtils, useAIProviderRegistry } from '@llmnative/react';\nimport type { AIAttachment, AIConversationTurn, AIModelDescriptor, AIProviderAdapter, AIToolResult } from '@llmnative/react';\nimport type { AgentDefinition, AgentExecutorPartOptions, AgentExecutorStep, AgentTool, LatestAttachmentsRef } from '../domain/agentDefinition';\nimport {\n DEFAULT_REASONING_PART_ID, DEFAULT_TEXT_PART_ID, formatQuestionAnswers, messagePartsText, upsertMessagePart,\n type AgentMessagePart, type AgentQuestion, type AgentQuestionAnswer, type AgentQuestionsAnswers, type AgentReasoningPart,\n} from '../domain/messageParts';\nimport { extractErrorMessage } from '../domain/extractErrorMessage';\nimport { applyMicroPrompts } from '../domain/microPrompts';\nimport { toJsonSchema } from '../domain/mcpTool';\nimport { useAIModelCatalog } from './useAIModelCatalog';\nimport { getAgenticoPrompts } from '../domain/prompts';\nimport { useAgenticoI18n } from '../i18n';\n\n/** Numero massimo di round-trip tool_calls consecutivi in un singolo turno — argine contro\n * un modello che continua a invocare tool senza mai arrivare a una risposta testuale. Un\n * task legittimo (es. verificare 5-6 URL candidati per loghi/favicon) può richiederne\n * parecchi: il limite è un argine contro un loop infinito, non un tetto stretto al lavoro\n * reale — vedi il turno di chiusura forzata sotto per cosa succede quando lo si raggiunge\n * comunque con lavoro legittimo ancora in corso. */\nconst MAX_TOOL_ROUNDTRIPS = 20;\n\n/** Soglia oltre la quale un allegato testuale NON viene più incollato nel messaggio al\n * provider (né persistito in history, vedi attachmentsHistoryText sotto) — solo esclusa dalla\n * chiamata AI, resta comunque raggiungibile per intero da un tool via LatestAttachmentsRef\n * (vedi agentDefinition.ts). ~260KB di base64 ≈ ~195KB di testo decodificato: un CSV di\n * migliaia di righe (bug osservato: un file da 7MB mandava in 400 l'upstream, ben oltre\n * qualunque finestra di contesto) resta gestibile SOLO se un tool lo legge direttamente,\n * mai incollandolo per intero nel prompt. */\nconst MAX_INLINE_TEXT_ATTACHMENT_BASE64_LENGTH = 260_000;\n\n/** Allegati non testuali (immagini, ecc.) passano sempre — solo i testuali oltre soglia\n * vengono esclusi dal prompt/dalla history (vedi sopra), mai dal ref grezzo per i tool. */\nfunction filterInlineAttachments(attachments: AIAttachment[]): AIAttachment[] {\n return attachments.filter((a) => !PromptUtils.isTextAttachment(a.mimeType) || a.base64.length <= MAX_INLINE_TEXT_ATTACHMENT_BASE64_LENGTH);\n}\n\n/** Una voce del log di attività — o un messaggio dell'utente (il testo di sendMessage(),\n * mostrato subito com'è, mai \"sto pensando\" perché non è il modello a scriverlo), o una tool\n * call (etichetta SEMPRE deterministica/locale, vedi AgentTool.describeInvocation: mai testo\n * generato chiedendo al modello di narrare se stesso, name+input sono già strutturati nella\n * risposta del provider — stesso approccio degli IDE agentici, che renderizzano le tool call\n * dal client, non dal testo del modello), o un turno \"sto pensando\" (una chiamata al modello,\n * testo libero in `detail` — QUESTO è l'unico testo scritto davvero dal modello nel log,\n * collassato a una riga dal chiamante). */\nexport interface AgentActivityEntry {\n id: string;\n /** 'user' = il testo passato a sendMessage(), mostrato come bolla di chat (vedi\n * AgentActivityLog). 'thinking' = una chiamata al modello (etichetta transitoria, vedi\n * AgentActivityLog: \"Sto pensando…\" mentre gira, sostituita da un'anteprima di `detail` a\n * risposta arrivata — mai testo che persiste invariato). 'tool' = una tool call,\n * etichetta sempre statica (AgentTool.describeInvocation). */\n kind: 'user' | 'thinking' | 'tool';\n label: string;\n status: 'running' | 'done' | 'error' | 'stopped';\n /** Sui turni \"sto pensando\" — il testo che il modello ha scritto in quel turno (commento\n * prima di un tool_calls, o la risposta finale). Sui messaggi utente — il testo inviato\n * a sendMessage(), integrale (non troncato, non collassabile: è già quello che l'utente\n * ha scritto, non c'è \"ragionamento\" da nascondere dietro un collapse). */\n detail?: string;\n /** Solo sui turni \"sto pensando\" che hanno richiesto tool_calls SENZA testo libero\n * (`detail` assente) — le frasi discorsive (deterministiche, vedi AgentTool.describeIntent)\n * sullo SCOPO GENERALE dei tool che quel turno sta per invocare (mai i parametri della\n * chiamata specifica, quelli sono già sulla riga della tool call sotto — vedi\n * describeIntent per il perché) cosi la UI dice qualcosa di concreto invece del generico\n * \"Richiesta di eseguire dei tool\" (vedi thinkingDisplayLabel in AgentActivityLog.tsx). */\n toolNames?: string[];\n /** Solo su 'user' — nomi dei file allegati a QUESTO messaggio (run()/sendMessage()\n * opts.attachments), mostrati come pillole sopra il testo (vedi AgentActivityLog) — senza,\n * l'utente non ha modo di verificare \"ho davvero attaccato quel file?\" scorrendo indietro,\n * doveva fidarsi a memoria (bug osservato: un allegato sembrava \"sparito\" perché non\n * c'era traccia visiva che fosse mai arrivato). */\n attachmentNames?: string[];\n /** On the turns of a remote agent (CR-004): the message in parts (text, reasoning, steps,\n * questions), in the order they arrived. `detail` still holds its text. Absent on the turns of\n * the AI provider and on chats saved before 0.3.0, shown from `detail` as before. */\n parts?: AgentMessagePart[];\n}\n\n/** Stato di gating/selezione — non generico, condiviso dal componente <AgentControls />\n * (che non ha bisogno di conoscere TParams/TResult, solo di disegnare switch + select). */\nexport interface AgentControlState {\n enabled: boolean;\n setEnabled: (v: boolean) => void;\n hasProviders: boolean;\n selectedModel: string;\n setSelectedModel: (id: string) => void;\n modelsByProvider: Record<string, AIModelDescriptor[]>;\n modelsLoading: boolean;\n}\n\nexport interface UseAgentResult<TParams, TResult> extends AgentControlState {\n running: boolean;\n error: string | null;\n /** true dopo che run() ha eseguito almeno un turno con AI abilitata — solo allora la\n * conversazione ha una history su cui sendMessage() può continuare. */\n canContinue: boolean;\n /** true when the turns go to `AgentDefinition.executor` (CR-003): no attachments, no\n * deterministic fallback; the models are the executor's (`remoteModels`), not the catalog. */\n remote: boolean;\n /** The models a remote executor offers (`AgentExecutor.models`); `selectedModel` is one of them. */\n remoteModels: Array<{ label: string; value: string; group?: string }>;\n /** Log delle tool call della sessione corrente, più recenti in fondo — azzerato a ogni\n * run(), accumulato attraverso i sendMessage() successivi. */\n activity: AgentActivityEntry[];\n /** Esegue il controller (sempre) e, se abilitato e disponibile un provider, il prompt\n * dell'agente. Ritorna `null` solo se il controller stesso fallisce (es. fetch di rete) —\n * in ogni altro caso ritorna almeno il fallback deterministico dell'agente. Avvia una\n * nuova sessione conversazionale (azzera la history di eventuali run precedenti).\n * `opts.attachments` — file allegati dall'utente al messaggio di apertura, convertiti in\n * `AIAttachment` (`PromptUtils.fileToAttachment`) e passati al provider — è il provider\n * stesso (react/src/providers/ai/*) a decidere se un mimetype testuale va decodificato e\n * inserito come blocco di testo o trattato come documento nativo (CR-071bis): questo hook\n * non fa più quella distinzione, per non duplicare una logica che deve restare unica e\n * per-provider dentro il framework. `opts.model` — sovrascrive\n * `selectedModel` SOLO per questa chiamata (mai una `setSelectedModel` seguita subito da\n * `run()`: lo stato React non sarebbe ancora committato nello stesso tick — vedi Chatbot\n * in Agentico.tsx, che riporta il modello scelto nel payload di ogni submit invece di\n * affidarsi a un `setSelectedModel` sincrono). `opts.openingMessage` — testo da mostrare\n * SUBITO come bolla \"Tu\" in cima all'activity log (stesso trattamento del testo di\n * sendMessage(), vedi sotto): senza, il messaggio che ha avviato la conversazione (es. il\n * goal scritto nel composer \"starter\" di Agentico, magari con un allegato) non appariva mai\n * nel log — solo i turni di sendMessage() successivi lo facevano, rendendo il primo\n * messaggio \"invisibile\" e facile da dimenticare fosse mai stato inviato (bug osservato).\n * Opzionale perché non ogni chiamante di run() è una chat (es. i subagent lanciati da\n * modale/form come theme/html-to-liquid non hanno un \"messaggio utente\" testuale da\n * mostrare). */\n run: (params: TParams, opts?: { attachments?: File[]; model?: string; openingMessage?: string }) => Promise<TResult | null>;\n /** Continua la conversazione avviata da run() con un messaggio dell'utente — il modello\n * può invocare i tool dell'agente con i parametri suggeriti nel messaggio. Richiede che\n * run() sia già stato eseguito con AI abilitata (altrimenti `error` viene impostato).\n * `opts` — stesso trattamento di run(), vedi sopra. */\n sendMessage: (text: string, opts?: { attachments?: File[]; model?: string }) => Promise<TResult | null>;\n /** Interrompe il turno in corso (run() o sendMessage()) — annulla la chiamata AI\n * attualmente in volo. Nessun effetto se non c'è nulla in corso. Le tool call già in\n * esecuzione in quel momento completano comunque (sono fetch brevi); il turno si ferma\n * al prossimo checkpoint (subito se si stava aspettando il modello, altrimenti dopo che\n * il round di tool in corso ha finito). */\n stop: () => void;\n /** Ricarica una sessione già esistente (vedi entityChatStore/AgenticoContext.resumeEntityChat)\n * invece di iniziarne una nuova — NON invia alcun prompt al modello, si limita a ricostruire\n * context/tools (via buildContext/getTools, come run()) e a installare `seed.history`/\n * `seed.activity` cosi un sendMessage() successivo continui esattamente come se la\n * conversazione non fosse mai stata interrotta. `params` deve avere la stessa forma con cui\n * la sessione originale era stata avviata (lo stesso TParams grezzo salvato nello snapshot). */\n resume: (params: TParams, seed: { history: AIConversationTurn[]; activity: AgentActivityEntry[] }) => Promise<void>;\n /** Esegue direttamente il fallback deterministico dell'agente, bypassando l'AI — per il\n * bottone \"Continua senza AI\" mostrato accanto a \"Riprova\" quando la chiamata al modello\n * fallisce (rete/provider giù, non una risposta non valida: quel caso ricade già da solo\n * sul fallback dentro runTurn). Richiede che run() sia già stato eseguito almeno una volta\n * in questa sessione (altrimenti non c'è `params`/`context` da cui derivarlo) — `null` se\n * chiamato prima. Sincrono: `AgentDefinition.fallback` non è mai una Promise (è sempre\n * derivato da `buildContext`, già risolto a questo punto). */\n runFallback: () => TResult | null;\n /** Foto corrente, serializzabile, della conversazione — `history` (turni grezzi\n * `AIConversationTurn[]`, dato puro senza funzioni/riferimenti al provider) più `activity`\n * (lo stesso log mostrato in UI). Usata da AgenticoContext.commitEntityChat per persistere\n * la chat che ha prodotto un'entità appena salvata (vedi entityChatStore) — a differenza di\n * `activity` da solo, `history` è ciò che serve per un vero resume (un nuovo giro può\n * continuare la conversazione col modello, non solo mostrarne il transcript in sola\n * lettura). `null` se run() non è mai stato eseguito in questa sessione. Una funzione, non\n * un valore memoizzato: deve leggere `sessionRef`/`activity` correnti al momento della\n * chiamata (tipicamente un click su Salva, molto dopo l'ultimo turno), mai una chiusura\n * stantia catturata al mount. */\n snapshot: () => { history: AIConversationTurn[]; activity: AgentActivityEntry[] } | null;\n /** Answers the questions part `partId` of the entry `entryId` (CR-004). If the turn that asked\n * is still waiting, its `onQuestions` promise resolves; otherwise a new turn of the executor\n * carries them (`AgentExecutorTurn.answers`, with empty `text`). Either way the part keeps the\n * answers and shows them read-only. Resolves with the result of that new turn, if any; rejects\n * (leaving the form open) when another turn is running or the entry is not of this conversation. */\n answerQuestions: (entryId: string, partId: string, answers: AgentQuestionAnswer[]) => Promise<TResult | null>;\n}\n\n/** Solo la fetta di UseAgentResult che serve a un consumer del pannello globale (Agentico.tsx)\n * per continuare una conversazione già avviata da run() — TParams non compare in nessuno dei\n * membri qui sotto, quindi `never` è innocuo. */\nexport type AgentConversation<TResult> = Pick<UseAgentResult<never, TResult>, 'canContinue' | 'remote' | 'remoteModels' | 'running' | 'error' | 'activity' | 'sendMessage' | 'stop' | 'runFallback' | 'snapshot' | 'answerQuestions'>;\n\nexport function useAgent<TParams, TContext, TResult>(\n definition: AgentDefinition<TParams, TContext, TResult>,\n options: { defaultEnabled?: boolean } = {},\n): UseAgentResult<TParams, TResult> {\n const { defaultEnabled = false } = options;\n const [enabled, setEnabled] = useState(defaultEnabled);\n const { hasProviders, modelsByProvider, modelsLoading, selectedModel, setSelectedModel } = useAIModelCatalog();\n const [running, setRunning] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [canContinue, setCanContinue] = useState(false);\n const [activity, setActivityState] = useState<AgentActivityEntry[]>([]);\n // The log as last set, readable outside a render (answerQuestions looks its entry up there).\n const activityRef = useRef<AgentActivityEntry[]>([]);\n const setActivity = (next: AgentActivityEntry[] | ((prev: AgentActivityEntry[]) => AgentActivityEntry[])) => {\n setActivityState((prev) => {\n const value = typeof next === 'function' ? next(prev) : next;\n activityRef.current = value;\n return value;\n });\n };\n // Questions asked by a turn still running, by `${entryId}:${partId}`: answering resolves them.\n const pendingQuestionsRef = useRef(new Map<string, { resolve: (answers: AgentQuestionAnswer[]) => void; reject: (reason: unknown) => void }>());\n const labels = useAgenticoI18n();\n const labelsRef = useRef(labels);\n labelsRef.current = labels;\n\n // Stato della sessione conversazionale corrente — non ricalcolabile dal render (params/\n // context/tools di run() devono sopravvivere invariati fino al prossimo run()), quindi\n // ref e non state: sendMessage() li legge senza dover essere lui stesso il motivo del\n // render che li ha popolati.\n // `logId` — generato una sola volta per sessione (run()), non per turno: è quello che\n // permette al proxy dev di accodare request/response di TUTTI i turni della stessa\n // conversazione (run() + ogni sendMessage() successivo) allo stesso file di log (vedi\n // AICompleteRequest.logId e react/src/providers/proxy/vite.ts).\n // `attachmentsRef` — LatestAttachmentsRef (vedi agentDefinition.ts), creato una volta per\n // sessione e passato a getTools(): un tool come extract_csv_column lo legge a ogni\n // esecuzione, sempre aggiornato con gli allegati grezzi del turno CORRENTE (vedi runTurn).\n const sessionRef = useRef<{ params: TParams; context: TContext; tools: AgentTool[]; history: AIConversationTurn[]; logId: string; attachmentsRef: LatestAttachmentsRef } | null>(null);\n // Guardia di rientranza — un ref, non lo state `running`: lo stato React è aggiornato in\n // batch e non è garantito visibile sincronamente a una seconda chiamata di run()/\n // sendMessage() scattata nello stesso tick (es. doppio click prima del re-render).\n const runningRef = useRef(false);\n // Il turno in corso (se c'è) — stop() lo annulla. Un ref, non state: creato e distrutto\n // dentro runTurn, mai il motivo di un render.\n const abortControllerRef = useRef<AbortController | null>(null);\n const stop = () => { abortControllerRef.current?.abort(); };\n\n // Il catalogo modelli (fetch, selezione di default) vive in useAIModelCatalog — condiviso\n // con AgenticoStarterComposer (Agentico.tsx), che ne ha bisogno PRIMA che esista una\n // sessione useAgent. Qui serve solo il registry grezzo per risolvere provider/ref.\n const registry: Record<string, AIProviderAdapter> = useAIProviderRegistry()?.registry ?? {};\n\n const ref = enabled && selectedModel ? parseAIModelRef(selectedModel) : null;\n const provider = ref ? registry[ref.provider] : null;\n const executor = definition.executor;\n const remote = Boolean(executor);\n // The model of a remote executor, picked among its own options: a ref too, so a turn reads the\n // last pick even within the same render.\n const [remoteModels, setRemoteModels] = useState<UseAgentResult<TParams, TResult>['remoteModels']>([]);\n const [remoteModel, setRemoteModelState] = useState('');\n const remoteModelRef = useRef('');\n const setRemoteModel = (model: string) => {\n remoteModelRef.current = model;\n setRemoteModelState(model);\n };\n const offerRemoteModels = (params: TParams) => {\n setRemoteModels(executor?.models?.(params) ?? []);\n setRemoteModel(executor?.defaultModel?.(params) ?? '');\n };\n\n /** A turn of a remote agent (CR-003): the text of the person goes to the executor as typed and\n * its message grows in one \"thinking\" entry, in parts (CR-004): text, reasoning, steps and\n * questions, in the order they arrive. No provider, no tool loop, no fallback. `answers` = the\n * turn carries the answers to the questions of an earlier turn (`historyText` is how the history\n * of the chat records them). */\n const runRemoteTurn = async (\n text: string,\n first: boolean,\n model?: string,\n answers?: AgentQuestionsAnswers & { historyText: string },\n ): Promise<TResult | null> => {\n const session = sessionRef.current;\n if (!session || !executor) { setError(labelsRef.current.noActiveSession); return null; }\n const controller = new AbortController();\n abortControllerRef.current = controller;\n const thinkingId = crypto.randomUUID();\n setActivity((prev) => [...prev, { id: thinkingId, kind: 'thinking', label: labelsRef.current.thinking, status: 'running', parts: [] }]);\n\n // When each reasoning part started, to say how long the model reasoned.\n const reasoningStarted = new Map<string, number>();\n const secondsSince = (id: string) => Math.max(1, Math.round((Date.now() - (reasoningStarted.get(id) ?? Date.now())) / 1000));\n const closeReasoning = (part: AgentReasoningPart): AgentReasoningPart => ({ ...part, streaming: false, duration: part.duration ?? secondsSince(part.id) });\n const updateParts = (change: (parts: AgentMessagePart[]) => AgentMessagePart[], status?: AgentActivityEntry['status']) =>\n setActivity((prev) => prev.map((a) => {\n if (a.id !== thinkingId) return a;\n const parts = change(a.parts ?? []);\n const detail = messagePartsText(parts);\n return { ...a, parts, ...(status ? { status } : {}), ...(detail ? { detail } : {}) };\n }));\n const upsert = (part: AgentMessagePart) => updateParts((parts) => upsertMessagePart(parts, part, closeReasoning));\n\n // A text part with an id of the host: the reply is then in its parts, not set again from reply.text.\n let textWithId = false;\n const onText = (partial: string, part?: AgentExecutorPartOptions) => {\n if (part?.id && part.id !== DEFAULT_TEXT_PART_ID) textWithId = true;\n upsert({ type: 'text', id: part?.id ?? DEFAULT_TEXT_PART_ID, text: partial, streaming: !part?.done });\n };\n const onReasoning = (partial: string, part?: AgentExecutorPartOptions & { duration?: number }) => {\n const id = part?.id ?? DEFAULT_REASONING_PART_ID;\n if (!reasoningStarted.has(id)) reasoningStarted.set(id, Date.now());\n upsert({\n type: 'reasoning', id, text: partial, streaming: !part?.done,\n ...(part?.duration !== undefined ? { duration: part.duration } : part?.done ? { duration: secondsSince(id) } : {}),\n });\n };\n const onStep = (step: AgentExecutorStep) => upsert({ type: 'tool', id: step.id, label: step.label, status: step.status });\n\n // The questions of this turn still waiting for their answers.\n const turnQuestions = new Set<string>();\n let questionsCount = 0;\n const rejectPendingQuestions = () => {\n for (const key of turnQuestions) {\n const pending = pendingQuestionsRef.current.get(key);\n pendingQuestionsRef.current.delete(key);\n pending?.reject(new DOMException('The turn was stopped.', 'AbortError'));\n }\n turnQuestions.clear();\n };\n controller.signal.addEventListener('abort', rejectPendingQuestions);\n const onQuestions = (questions: AgentQuestion[], part?: { id?: string }) => {\n questionsCount += 1;\n const id = part?.id ?? `questions-${questionsCount}`;\n upsert({ type: 'questions', id, questions });\n const key = `${thinkingId}:${id}`;\n if (controller.signal.aborted) return Promise.reject(new DOMException('The turn was stopped.', 'AbortError'));\n turnQuestions.add(key);\n return new Promise<AgentQuestionAnswer[]>((resolve, reject) => {\n pendingQuestionsRef.current.set(key, { resolve, reject });\n });\n };\n\n /** End of the turn: nothing streams any more, and answers to its questions now go to a new turn. */\n const finish = (status: AgentActivityEntry['status'], finalText?: string) => {\n updateParts((parts) => {\n const next = finalText && !textWithId\n ? upsertMessagePart(parts, { type: 'text', id: DEFAULT_TEXT_PART_ID, text: finalText, streaming: false }, closeReasoning)\n : parts;\n return next.map((p) => {\n if (p.type === 'reasoning' && p.streaming) return closeReasoning(p);\n if (p.type === 'text' && p.streaming) return { ...p, streaming: false };\n if (p.type === 'tool' && p.status === 'running' && status !== 'done') return { ...p, status: 'stopped' as const };\n return p;\n });\n }, status);\n controller.signal.removeEventListener('abort', rejectPendingQuestions);\n for (const key of turnQuestions) pendingQuestionsRef.current.delete(key);\n turnQuestions.clear();\n };\n\n const askedText = text || answers?.historyText || '';\n try {\n const reply = await executor.turn({\n params: session.params, context: session.context, text, first, signal: controller.signal,\n ...((model || remoteModelRef.current) ? { model: model || remoteModelRef.current } : {}),\n ...(answers ? { answers: { id: answers.id, answers: answers.answers } } : {}),\n onText, onReasoning, onStep, onQuestions,\n });\n finish('done', reply.text);\n session.history = [\n ...session.history,\n ...(askedText ? [{ role: 'user' as const, content: askedText }] : []),\n { role: 'assistant' as const, content: reply.text },\n ];\n return reply.result ?? null;\n } catch (err) {\n session.history = [...session.history, ...(askedText ? [{ role: 'user' as const, content: askedText }] : [])];\n if (controller.signal.aborted) {\n finish('stopped');\n return null;\n }\n finish('error');\n setError(extractErrorMessage(err));\n return null;\n } finally {\n abortControllerRef.current = null;\n }\n };\n\n const answerQuestions = async (entryId: string, partId: string, answers: AgentQuestionAnswer[]): Promise<TResult | null> => {\n const entry = activityRef.current.find((a) => a.id === entryId);\n const part = entry?.parts?.find((p) => p.type === 'questions' && p.id === partId);\n if (!part || part.type !== 'questions') {\n setError(labelsRef.current.noActiveSession);\n throw new Error(labelsRef.current.noActiveSession);\n }\n const markAnswered = () => setActivity((prev) => prev.map((a) => (a.id !== entryId ? a : {\n ...a, parts: a.parts?.map((p) => (p.type === 'questions' && p.id === partId ? { ...p, answers } : p)),\n })));\n const key = `${entryId}:${partId}`;\n const pending = pendingQuestionsRef.current.get(key);\n if (pending) {\n pendingQuestionsRef.current.delete(key);\n markAnswered();\n pending.resolve(answers);\n return null;\n }\n if (runningRef.current) {\n setError(labelsRef.current.requestInProgress);\n throw new Error(labelsRef.current.requestInProgress);\n }\n if (!remote || !sessionRef.current) {\n setError(labelsRef.current.noActiveSession);\n throw new Error(labelsRef.current.noActiveSession);\n }\n runningRef.current = true;\n setRunning(true);\n setError(null);\n markAnswered();\n try {\n return await runRemoteTurn('', false, undefined, { id: partId, answers, historyText: formatQuestionAnswers(part.questions, answers) });\n } finally {\n runningRef.current = false;\n setRunning(false);\n }\n };\n\n /** Un turno: manda `userText` (eventualmente vuoto — vedi sotto) più la history/i tool\n * della sessione, esegue in loop i tool_calls richiesti dal modello finché non arriva un\n * turno testuale, poi ritorna il risultato parsato. `userText` vuoto significa \"nessun\n * testo nuovo, si continua solo perché il turno precedente era un tool_result\" — i\n * provider adapter (react/src/providers/ai/*) sanno omettere il turno finale in quel caso.\n * `aiAttachments` (allegati binari, già convertiti — vedi run()/sendMessage()) accompagna\n * SOLO la primissima chiamata al provider di questo turno: la history non ha un concetto\n * di allegato persistente per round-trip successivi, ed è comunque lì che vive il vero\n * messaggio dell'utente. `overrideModel` — vedi run()/sendMessage(): risolto localmente\n * qui (mai da `selectedModel` di stato, che potrebbe non aver ancora fatto commit). */\n const runTurn = async (userText: string, aiAttachments?: AIAttachment[], overrideModel?: string): Promise<TResult | null> => {\n const session = sessionRef.current;\n if (!session) { setError(labelsRef.current.noActiveSession); return null; }\n const effectiveRef = overrideModel ? parseAIModelRef(overrideModel) : ref;\n const effectiveProvider = effectiveRef ? registry[effectiveRef.provider] : null;\n if (!effectiveProvider || !effectiveRef) { setError(labelsRef.current.noProviderAvailable); return null; }\n\n // Il ref grezzo per i tool (vedi LatestAttachmentsRef) ACCUMULA gli allegati di TUTTA\n // la conversazione, non solo del turno corrente — sovrascriverlo a ogni turno faceva\n // sparire un allegato grande (che non è mai incollato in history, vedi sotto) non\n // appena l'utente scriveva un messaggio successivo SENZA riallegare nulla: il modello si\n // ritrovava a dover chiedere \"puoi riallegare il CSV?\" anche se l'utente l'aveva già\n // fornito due turni prima (bug osservato). Aggiornato PRIMA di filtrare per il provider:\n // un tool deve poter leggere anche ciò che al modello non arriva mai.\n if (aiAttachments?.length) {\n session.attachmentsRef.current = [...(session.attachmentsRef.current ?? []), ...aiAttachments];\n }\n\n // Gli allegati binari (aiAttachments) accompagnano SOLO la chiamata live del primo\n // round-trip (vedi sotto) — il provider li traduce nel blocco nativo giusto (image/\n // document/text) per QUESTA chiamata, ma AIConversationTurn non ha un campo \"allegato\"\n // persistente: senza questo, il contenuto del file spariva dalla history non appena il\n // turno finiva, rendendolo irraggiungibile a qualunque sendMessage() successivo (bug\n // osservato: \"gli old path sono quelli che ti ho allegato\" in un turno 3 non trovava più\n // nulla). Per i mimetype testuali (CSV/TXT/JSON/...) sotto soglia, il fix è incorporare\n // lo stesso testo decodificato DENTRO il contenuto persistito in history — da lì in\n // avanti è testo normale, sopravvive a ogni turno futuro esattamente come se l'utente\n // l'avesse scritto a mano. Sopra soglia (vedi MAX_INLINE_TEXT_ATTACHMENT_BASE64_LENGTH),\n // MAI incollato: un CSV da alcuni MB manderebbe in errore l'upstream per dimensione della\n // richiesta (bug osservato) — resta raggiungibile SOLO da un tool via attachmentsRef. MA\n // un marker SENZA contenuto va comunque persistito in history anche per questi — senza,\n // il modello non ha alcuna traccia testuale che un file sia stato allegato (bug osservato:\n // un CSV di traffico reale, quasi sempre sopra soglia, veniva richiesto di nuovo\n // all'utente in un turno successivo perché \"per il modello\" non era mai arrivato nulla —\n // l'unico segnale della sua esistenza era la chip UI dell'allegato, che non raggiunge mai\n // il prompt). I binari (immagini, PDF) restano turn-scoped: non c'è un modo sensato di\n // \"incorporarli come testo\" nella history.\n const textAttachments = (aiAttachments ?? []).filter((a) => PromptUtils.isTextAttachment(a.mimeType));\n const attachmentsHistoryText = textAttachments\n .map((a) => (a.base64.length <= MAX_INLINE_TEXT_ATTACHMENT_BASE64_LENGTH\n ? `\\n\\n[File allegato: ${a.name}]\\n${PromptUtils.decodeBase64Text(a.base64)}`\n : `\\n\\n[File allegato: ${a.name} — troppo grande per essere incollato qui. Usa uno dei tool di lettura file disponibili (es. list_csv_columns/extract_csv_column) per leggerne il contenuto prima di chiedere altro all'utente.]`))\n .join('');\n const inlineAttachments = filterInlineAttachments(aiAttachments ?? []);\n\n // t.inputSchema è Zod (sorgente unica) — il provider AI parla JSON Schema sul wire\n // come ogni function-calling API: questo è l'unico punto in cui la forma serializzata\n // serve davvero (vedi mcpTool.ts).\n const toolDefs = session.tools.map((t) => ({ name: t.name, description: t.description, inputSchema: toJsonSchema(t.inputSchema) }));\n const toolsByName = new Map(session.tools.map((t) => [t.name, t]));\n\n const controller = new AbortController();\n abortControllerRef.current = controller;\n\n /** Aggiorna una voce di activity per id — usata sia per il turno \"sto pensando\" di\n * ogni round sia per le singole tool call. */\n const setEntryStatus = (id: string, status: AgentActivityEntry['status'], detail?: string, toolNames?: string[]) =>\n setActivity((prev) => prev.map((a) => (a.id === id ? {\n ...a, status,\n ...(detail !== undefined ? { detail } : {}),\n ...(toolNames !== undefined ? { toolNames } : {}),\n } : a)));\n\n try {\n let history = session.history;\n let promptText = userText;\n\n for (let roundTrip = 0; roundTrip < MAX_TOOL_ROUNDTRIPS; roundTrip++) {\n // Un turno \"sto pensando\" per OGNI chiamata al modello, non solo per le tool\n // call — prima l'attesa della risposta AI (spesso la più lunga, specie tra un\n // round di tool e il successivo) non aveva nessun indicatore visibile.\n const thinkingId = crypto.randomUUID();\n setActivity((prev) => [...prev, { id: thinkingId, kind: 'thinking', label: labelsRef.current.thinking, status: 'running' }]);\n\n // provider.complete() può sia risolvere a `null` (fetch.ts inghiotte molti\n // errori di rete e li normalizza così) SIA rigettare (es. il dev-proxy\n // risponde con uno status non-ok quando la connessione a monte viene\n // interrotta da un abort — capita spesso con un errore \"CORS\"-shaped invece\n // di un pulito AbortError). Un try/catch qui è obbligatorio: senza, un throw\n // scavalca l'aggiornamento della voce \"sto pensando\", che resta bloccata su\n // \"running\" per sempre — esattamente il sintomo di un abort non gestito.\n let result;\n try {\n result = await effectiveProvider.complete({\n prompt: promptText,\n model: effectiveRef.model,\n history,\n tools: toolDefs.length ? toolDefs : undefined,\n signal: controller.signal,\n attachments: roundTrip === 0 ? inlineAttachments : undefined,\n logId: session.logId,\n });\n } catch (err) {\n // Must include the pending user turn, not just `history` as of the START of\n // this round — otherwise a failure on round 0 (the common case: provider\n // rate-limited/unavailable) leaves `session.history` at `[]`, and the next\n // sendMessage('Riprova.') from Agentico.tsx's retry button finds no trace of\n // the original goal or its orchestrator prompt. The model then sees only\n // \"Riprova.\" + the tool catalog, with nothing to retry — hence answering as\n // a fresh, contextless assistant instead of resuming the actual request.\n session.history = [\n ...history,\n ...(promptText ? [{ role: 'user' as const, content: promptText + attachmentsHistoryText }] : []),\n ];\n if (controller.signal.aborted) {\n // Niente setError qui: la voce di activity già dice \"Interrotto\n // dall'utente.\" con l'icona giusta — un secondo messaggio identico\n // sotto sarebbe una pura duplicazione.\n setEntryStatus(thinkingId, 'stopped');\n return definition.fallback(session.params, session.context);\n }\n setEntryStatus(thinkingId, 'error');\n setError(extractErrorMessage(err));\n return null;\n }\n // Persisti la history maturata FINORA (inclusi eventuali round-trip di tool già\n // eseguiti con effetti reali) prima di ritornare — altrimenti un sendMessage()\n // successivo riparte dalla history pre-turno, \"dimenticando\" tool call già\n // avvenute e rischiando di farle rieseguire dal modello. Include anche il turno\n // utente pendente di QUESTO round (stesso motivo del ramo catch sopra) — se\n // `result` risulta falsy sotto, questa è l'ultima history persistita prima del\n // return, e deve comunque contenere cosa l'utente ha chiesto in questo round.\n session.history = [\n ...history,\n ...(promptText ? [{ role: 'user' as const, content: promptText + attachmentsHistoryText }] : []),\n ];\n\n if (!result) {\n if (controller.signal.aborted) {\n setEntryStatus(thinkingId, 'stopped');\n return definition.fallback(session.params, session.context);\n }\n setEntryStatus(thinkingId, 'error');\n setError(labelsRef.current.noProviderResponse);\n return null;\n }\n\n if (result.type === 'text') {\n setEntryStatus(thinkingId, 'done', result.text);\n session.history = [\n ...history,\n ...(promptText ? [{ role: 'user' as const, content: promptText + attachmentsHistoryText }] : []),\n { role: 'assistant' as const, content: result.text },\n ];\n const parsed = definition.parseResponse(result.text, session.params, session.context);\n if (!parsed) {\n // `parseResponse` non riporta MAI il motivo esatto (ogni subagent ha il\n // proprio parser/schema — vedi es. parseRedirectsResponse, che logga già\n // l'errore di JSON.parse specifico prima di arrivare qui) — questo è il\n // log generico, comune a QUALUNQUE subagent, che risponde alla domanda\n // \"cosa ha scritto davvero il modello?\" senza dover riprodurre la\n // conversazione. Preview head/tail (non il testo intero, potenzialmente\n // enorme — es. centinaia di redirect) perché un troncamento upstream\n // (limite di token in output) si vede quasi sempre alla FINE della\n // risposta, non all'inizio.\n console.error(\n '[useAgent] parseResponse ha restituito null — la risposta del modello non è nel formato atteso da questo subagent. Uso il fallback deterministico.',\n {\n responseLength: result.text.length,\n responsePreview: result.text.length > 2000\n ? `${result.text.slice(0, 1000)}\\n…[troncato qui nel log, ${result.text.length} caratteri totali]…\\n${result.text.slice(-1000)}`\n : result.text,\n params: session.params,\n },\n );\n setError(labelsRef.current.invalidResponse);\n return definition.fallback(session.params, session.context);\n }\n return parsed;\n }\n\n // Etichette deterministiche (mai narrate dal modello) dei tool che sta per\n // invocare. Due testi distinti per due righe distinte, non lo stesso\n // riciclato — altrimenti il turno \"sto pensando\" e la tool call subito sotto\n // dicono LETTERALMENTE la stessa frase, una sopra l'altra (il difetto\n // segnalato: \"Eseguo: Ricontrollo https://…\" seguito da \"Ricontrollo\n // https://…\"): describeIntent (scopo generale, no parametri) per la riga \"sto\n // pensando\" quando non ha scritto testo libero; describeInvocation (con i\n // parametri della chiamata specifica) per la riga della singola tool call.\n const toolIntentLabels = result.toolCalls.map((call) => {\n const tool = toolsByName.get(call.name);\n return tool?.describeIntent ?? tool?.describeInvocation?.(call.input) ?? `Chiamata: ${call.name}`;\n });\n setEntryStatus(thinkingId, 'done', result.text || undefined, result.text ? undefined : toolIntentLabels);\n\n const toolResults: AIToolResult[] = await Promise.all(result.toolCalls.map(async (call) => {\n const tool = toolsByName.get(call.name);\n const label = tool?.describeInvocation?.(call.input) ?? `Chiamata: ${call.name}`;\n setActivity((prev) => [...prev, { id: call.id, kind: 'tool', label, status: 'running' }]);\n\n if (!tool) {\n setEntryStatus(call.id, 'error');\n return { toolCallId: call.id, name: call.name, output: `Tool sconosciuto: ${call.name}`, isError: true };\n }\n try {\n const output = await tool.execute(call.input);\n setEntryStatus(call.id, 'done');\n return { toolCallId: call.id, name: call.name, output };\n } catch (err) {\n setEntryStatus(call.id, 'error');\n return { toolCallId: call.id, name: call.name, output: extractErrorMessage(err), isError: true };\n }\n }));\n\n history = [\n ...history,\n ...(promptText ? [{ role: 'user' as const, content: promptText + attachmentsHistoryText }] : []),\n { role: 'assistant' as const, content: result.text, toolCalls: result.toolCalls },\n { role: 'tool_result' as const, results: toolResults },\n ];\n promptText = '';\n }\n\n // Budget esaurito ma il modello voleva ancora invocare tool: il lavoro fatto finora\n // (i risultati dei tool già eseguiti sono già in `history`) non va buttato via con\n // un errore secco — un ultimo turno SENZA tool disponibili forza una risposta\n // finale usando tutto quello raccolto fino a qui.\n const closingId = crypto.randomUUID();\n setActivity((prev) => [...prev, { id: closingId, kind: 'thinking', label: labelsRef.current.thinking, status: 'running' }]);\n let closingResult;\n try {\n closingResult = await effectiveProvider.complete({\n // Anche questo turno passa dai micro-prompt (vedi run()/sendMessage() sopra)\n // — altrimenti un run che esaurisce il budget di round-trip (tanti tool_calls\n // di fila, es. molti URL di font/asset da ricontrollare) salta il riepilogo\n // finale senza che nessuno se ne accorga: era esattamente questo il path che\n // lo perdeva, non un rifiuto del modello.\n prompt: applyMicroPrompts(\n getAgenticoPrompts().stopCallingTools,\n definition.microPromptIds,\n ),\n model: effectiveRef.model,\n history,\n signal: controller.signal,\n logId: session.logId,\n });\n } catch {\n closingResult = null;\n }\n if (closingResult?.type === 'text') {\n setEntryStatus(closingId, 'done', closingResult.text);\n session.history = [...history, { role: 'assistant', content: closingResult.text }];\n const parsed = definition.parseResponse(closingResult.text, session.params, session.context);\n if (parsed) return parsed;\n } else {\n setEntryStatus(closingId, controller.signal.aborted ? 'stopped' : 'error');\n session.history = history;\n }\n\n // Niente setError se interrotto dall'utente: la voce di activity già dice\n // \"Interrotto dall'utente.\" con l'icona giusta, un secondo messaggio sotto\n // sarebbe una pura duplicazione.\n if (!controller.signal.aborted) {\n setError(labelsRef.current.tooManyToolTurns);\n }\n return definition.fallback(session.params, session.context);\n } finally {\n abortControllerRef.current = null;\n }\n };\n\n const run = async (params: TParams, opts?: { attachments?: File[]; model?: string; openingMessage?: string }): Promise<TResult | null> => {\n if (runningRef.current) { setError(labelsRef.current.requestInProgress); return null; }\n runningRef.current = true;\n setError(null);\n // Stesso trattamento del testo di sendMessage() sotto — mostrato subito, prima di\n // aspettare la risposta, cosi il messaggio che ha avviato la conversazione non \"scompare\"\n // dal log (vedi il commento su opts.openingMessage in UseAgentResult.run).\n setActivity(opts?.openingMessage\n ? [{\n id: crypto.randomUUID(), kind: 'user', label: 'Tu', status: 'done', detail: opts.openingMessage,\n ...(opts.attachments?.length ? { attachmentNames: opts.attachments.map((f) => f.name) } : {}),\n }]\n : []);\n setRunning(true);\n try {\n const context = await definition.buildContext(params);\n const attachmentsRef: LatestAttachmentsRef = { current: undefined };\n const tools = definition.getTools?.(params, context, attachmentsRef) ?? [];\n sessionRef.current = { params, context, tools, history: [], logId: crypto.randomUUID(), attachmentsRef };\n\n if (remote) {\n setCanContinue(true);\n offerRemoteModels(params);\n return await runRemoteTurn(opts?.openingMessage ?? '', true);\n }\n\n const overrideModel = opts?.model || undefined;\n const hasProvider = overrideModel ? Boolean(registry[parseAIModelRef(overrideModel)?.provider ?? '']) : Boolean(provider);\n if (!enabled || !hasProvider) {\n setCanContinue(false);\n const fallbackResult = definition.fallback(params, context);\n // Stessa conferma di runFallback() (vedi describeFallback) — senza, un run con\n // AI disabilitata a priori (es. ManualEntryRunner) produrrebbe il risultato ma\n // zero riga di log a dirlo: indistinguibile da \"non è successo niente\" finché\n // non arriva panelExtra (bug della stessa famiglia di quello risolto in\n // runFallback, qui sul percorso \"mai nemmeno tentato\" invece che \"fallito\").\n setActivity((prev) => [...prev, {\n id: crypto.randomUUID(), kind: 'tool', status: 'done',\n label: definition.describeFallback?.(fallbackResult) ?? labelsRef.current.aiDisabledFallback,\n }]);\n return fallbackResult;\n }\n\n setCanContinue(true);\n const aiAttachments = opts?.attachments?.length\n ? await Promise.all(opts.attachments.map((file) => PromptUtils.fileToAttachment(file)))\n : undefined;\n const prompt = applyMicroPrompts(definition.buildPrompt(params, context), definition.microPromptIds);\n return await runTurn(prompt, aiAttachments, overrideModel);\n } catch (err) {\n setError(extractErrorMessage(err));\n return null;\n } finally {\n runningRef.current = false;\n setRunning(false);\n }\n };\n\n const resume = async (params: TParams, seed: { history: AIConversationTurn[]; activity: AgentActivityEntry[] }): Promise<void> => {\n if (runningRef.current) { setError(labelsRef.current.requestInProgress); return; }\n runningRef.current = true;\n setError(null);\n setActivity(seed.activity);\n // `running`, come in run() — buildContext() per theme rifà una vera fetch di rete\n // (extractSiteSnapshot): senza, `task.agent.running` resterebbe falsamente `false` per\n // tutta quella finestra, e qualunque logica che vi si affida (spinner del composer,\n // un dismiss/replace concorrente che si fida di \"running\" per non interrompere un\n // lavoro in corso) lo leggerebbe come già inattivo mentre sta ancora lavorando.\n setRunning(true);\n try {\n const context = await definition.buildContext(params);\n const attachmentsRef: LatestAttachmentsRef = { current: undefined };\n const tools = definition.getTools?.(params, context, attachmentsRef) ?? [];\n sessionRef.current = { params, context, tools, history: seed.history, logId: crypto.randomUUID(), attachmentsRef };\n if (remote) offerRemoteModels(params);\n setCanContinue(remote || (enabled && Boolean(provider)));\n } catch (err) {\n setError(extractErrorMessage(err));\n } finally {\n runningRef.current = false;\n setRunning(false);\n }\n };\n\n const sendMessage = async (text: string, opts?: { attachments?: File[]; model?: string }): Promise<TResult | null> => {\n if (runningRef.current) { setError(labelsRef.current.requestInProgress); return null; }\n setError(null);\n if (!text.trim()) { setError('Messaggio vuoto.'); return null; }\n const session = sessionRef.current;\n if (!session) { setError(labelsRef.current.noActiveSession); return null; }\n runningRef.current = true;\n setRunning(true);\n // Mostrato subito, prima di aspettare la risposta — è quello che l'utente ha appena\n // scritto, non c'è nulla da attendere per saperlo (a differenza del turno \"sto\n // pensando\" del modello, che invece parte vuoto e si riempie solo a risposta arrivata).\n setActivity((prev) => [...prev, {\n id: crypto.randomUUID(), kind: 'user', label: 'Tu', status: 'done', detail: text,\n ...(opts?.attachments?.length ? { attachmentNames: opts.attachments.map((f) => f.name) } : {}),\n }]);\n try {\n if (remote) return await runRemoteTurn(text, false, opts?.model || undefined);\n const aiAttachments = opts?.attachments?.length\n ? await Promise.all(opts.attachments.map((file) => PromptUtils.fileToAttachment(file)))\n : undefined;\n const prompt = applyMicroPrompts(definition.buildFollowUpPrompt(text, session.params, session.context), definition.microPromptIds);\n return await runTurn(prompt, aiAttachments, opts?.model || undefined);\n } catch (err) {\n setError(extractErrorMessage(err));\n return null;\n } finally {\n runningRef.current = false;\n setRunning(false);\n }\n };\n\n const runFallback = (): TResult | null => {\n const session = sessionRef.current;\n if (!session) return null;\n setError(null);\n const result = definition.fallback(session.params, session.context);\n // kind 'tool', non 'thinking' — thinkingDisplayLabel (AgentActivityLog.tsx) ricalcola\n // sempre la sua etichetta da status/detail per i turni \"sto pensando\" (mai quella\n // passata qui), mentre le righe 'tool' mostrano `label` verbatim: esattamente la resa\n // semplice \"icona + una frase\" che serve per una singola riga di log deterministica.\n // `describeFallback` (se l'agente lo implementa) sostituisce l'etichetta generica con\n // una specifica al risultato appena prodotto — vedi il commento su\n // AgentDefinition.describeFallback per il perché serve DAVVERO per l'orchestratore\n // (senza, il suo messaggio di fallback sparirebbe senza che l'utente lo veda mai).\n setActivity((prev) => [...prev, {\n id: crypto.randomUUID(), kind: 'tool', status: 'done',\n label: definition.describeFallback?.(result) ?? labelsRef.current.continueWithoutAiFallback,\n }]);\n return result;\n };\n\n // Non un useCallback — deve chiudere sempre su `activity`/`sessionRef` del render CORRENTE,\n // letta però solo quando qualcuno la invoca davvero (un salvataggio, evento raro innescato\n // dall'utente, non un percorso caldo): niente da memoizzare, il costo di ricrearla ad ogni\n // render è trascurabile.\n const snapshot = (): { history: AIConversationTurn[]; activity: AgentActivityEntry[] } | null => (\n sessionRef.current ? { history: sessionRef.current.history, activity } : null\n );\n\n return {\n enabled, setEnabled, hasProviders,\n // A remote executor picks among its own models, not the AI catalog of the app.\n selectedModel: remote ? remoteModel : selectedModel,\n setSelectedModel: remote ? setRemoteModel : setSelectedModel,\n modelsByProvider, modelsLoading, running, error, canContinue, remote, remoteModels, activity, run, resume, sendMessage, stop, runFallback, snapshot, answerQuestions,\n };\n}\n","// Prompt + parsing per l'orchestratore Agentico — vedi\n// ./agenticoOrchestratorAgent.ts per l'AgentDefinition che li usa.\n//\n// A differenza di theme/html-to-liquid, qui non c'è un contratto JSON da far rispettare: il\n// modello o continua a conversare in prosa libera (per raccogliere parametri) o invoca uno dei\n// tool generati dal registry (../subagentRegistry.ts) — la vera \"risposta\n// strutturata\" arriva dalla tool call, non dal testo. Vedi agenticoOrchestratorAgent.ts per come\n// il tool eseguito scrive nel context (`handoff`) che parseOrchestratorResponse legge qui.\nimport { getAgenticoPrompts } from '../prompts';\n\nexport interface OrchestratorHandoff {\n subagentId: string;\n command: string;\n params: Record<string, unknown>;\n}\n\nexport type OrchestratorResult =\n | { kind: 'chat'; message: string }\n | ({ kind: 'handoff' } & OrchestratorHandoff);\n\nexport function describeOrchestratorResponseContract(): string {\n const prompts = getAgenticoPrompts();\n return [prompts.responseRulesHeading, ...prompts.responseRules.map((rule) => `- ${rule}`)].join('\\n');\n}\n\nexport function buildOrchestratorPrompt(userGoal: string, cliCatalog: string, pageContext?: string | null): string {\n const prompts = getAgenticoPrompts();\n const pageContextSection = pageContext\n ? `\\n${prompts.pageContextHeading}\\n${prompts.pageContextIntro}\\n${pageContext}\\n`\n : '';\n\n return `${prompts.orchestratorIntro}\n\n${prompts.toolsHeading}\n${cliCatalog}\n${pageContextSection}\n${describeOrchestratorResponseContract()}\n\n${prompts.userRequestHeading}\n${userGoal}`;\n}\n\n/** `handoff` arriva dal context della sessione (settato dall'execute() del tool invocato in\n * questo turno, vedi agenticoOrchestratorAgent.ts) — se presente ha sempre priorità sul testo\n * libero del modello, che a quel punto è solo un commento discorsivo sul tool appena invocato. */\nexport function parseOrchestratorResponse(raw: string, handoff: OrchestratorHandoff | null): OrchestratorResult {\n if (handoff) return { kind: 'handoff', ...handoff };\n return { kind: 'chat', message: raw };\n}\n","// AgentDefinition of the Agentico orchestrator — see ../agentDefinition.ts for the contract. Its\r\n// \"result\" is never used by the user directly: either conversation text (kind: 'chat', still\r\n// collecting parameters) or a handoff (kind: 'handoff') that the caller (Agentico.tsx) turns into\r\n// a SubagentRunner on the chosen subagent.\r\n//\r\n// Agentico knows no concrete subagent or tool: the handoff tools are generated from the registry\r\n// (registerSubagents) and the data-collection tools come from the host (registerCrossAgentTools).\r\nimport { interpolate, proxyFetch } from '@llmnative/react';\r\nimport type { AgentDefinition, AgentTool } from '../agentDefinition';\r\nimport { getSubagentRegistry, findSubagentManifest } from '../subagentRegistry';\r\nimport { renderRegistryCli } from '../cli/renderManifestCli';\r\nimport { createCrossAgentTools } from '../crossAgentTools';\r\nimport { getAgenticoPrompts } from '../prompts';\r\nimport {\r\n buildOrchestratorPrompt,\r\n describeOrchestratorResponseContract,\r\n parseOrchestratorResponse,\r\n type OrchestratorHandoff,\r\n type OrchestratorResult,\r\n} from './prompt';\r\n\r\nexport interface OrchestratorAgentParams {\r\n /** The opening message — the user's free request, or a goal pre-seeded from a subagent card. */\r\n userGoal: string;\r\n /** \"Where the user is now\" (see useAgenticoPageContext), captured once at run(). */\r\n pageContext: string | null;\r\n /** Subagent the starting page is pinned to, `null` for a generic chat. Restricts the\r\n * cross-agent tools to the ones that subagent declares (SubagentManifest.crossAgentToolIds). */\r\n scopedSubagentId: string | null;\r\n}\r\n\r\nexport interface OrchestratorAgentContext {\r\n cliCatalog: string;\r\n /** Written by the execute() of the handoff tool invoked in this turn and read by parseResponse:\r\n * the only way a tool call outcome reaches parseResponse, which only sees the final text.\r\n * Reset every turn — a handoff is valid only for the turn that invoked it. */\r\n handoff: { current: OrchestratorHandoff | null };\r\n /** Complete outputs of the cross-agent tools of this conversation (see crossAgentTools.ts). */\r\n collected: Record<string, unknown>;\r\n}\r\n\r\n/** User-facing labels of the orchestrator (from the `agentico` i18n namespace). */\r\nexport interface OrchestratorLabels {\r\n /** Chat message used when the AI is not available. */\r\n unavailable: string;\r\n /** Log line of \"continue without AI\". */\r\n continueWithoutAi: string;\r\n /** Tool call label while a handoff is prepared — `{label}` = subagent label. */\r\n preparing: string;\r\n /** \"Thinking\" label of the turn that requested the handoff — `{label}` = subagent label. */\r\n collecting: string;\r\n}\r\n\r\n/** The subagents the orchestrator offers: all of them, but one for a page only (onlyWhenAnchored,\r\n * CR-003) only when the conversation is scoped to it — in its catalog and in its tools alike. */\r\nfunction offeredSubagents(scopedSubagentId: string | null) {\r\n return getSubagentRegistry().filter((manifest) => !manifest.onlyWhenAnchored || scopedSubagentId === manifest.id);\r\n}\r\n\r\nexport function createOrchestratorAgentDefinition(\r\n labels: OrchestratorLabels,\r\n): AgentDefinition<OrchestratorAgentParams, OrchestratorAgentContext, OrchestratorResult> {\r\n return {\r\n buildContext(params) {\r\n return {\r\n cliCatalog: renderRegistryCli(offeredSubagents(params.scopedSubagentId)),\r\n handoff: { current: null },\r\n collected: {},\r\n };\r\n },\r\n\r\n buildPrompt({ userGoal, pageContext }, { cliCatalog }) {\r\n return buildOrchestratorPrompt(userGoal, cliCatalog, pageContext);\r\n },\r\n\r\n buildFollowUpPrompt(userText, _params, { cliCatalog }) {\r\n return `${userText}\\n\\n${describeOrchestratorResponseContract()}\\n\\n${getAgenticoPrompts().toolsHeading}\\n${cliCatalog}`;\r\n },\r\n\r\n parseResponse(raw, _params, context) {\r\n const handoff = context.handoff.current;\r\n context.handoff.current = null; // consumed — must not survive into the next turn\r\n return parseOrchestratorResponse(raw, handoff);\r\n },\r\n\r\n fallback() {\r\n return { kind: 'chat', message: labels.unavailable };\r\n },\r\n\r\n // The fallback is never a real handoff (it cannot guess the subagent without AI), only a\r\n // message to read: without this hook \"continue without AI\" would show nothing.\r\n describeFallback(result) {\r\n return result.kind === 'chat' ? result.message : labels.continueWithoutAi;\r\n },\r\n\r\n // One handoff tool per (subagent, command) of the registry, generated — registering a\r\n // subagent makes it invocable here. execute() does NOT run the subagent (that would nest a\r\n // multi-turn AI conversation in a tool call): it only stores the parameters for parseResponse.\r\n getTools(params, context, attachmentsRef): AgentTool[] {\r\n const allCrossAgentTools = createCrossAgentTools({ attachmentsRef, collected: context.collected, proxyFetch });\r\n\r\n // A conversation pinned to a subagent only sees the cross-agent tools it declares; a\r\n // generic chat (not yet scoped) sees them all, since the target is still unknown.\r\n const scopedManifest = params.scopedSubagentId ? findSubagentManifest(params.scopedSubagentId) : null;\r\n const crossAgentTools = scopedManifest\r\n ? allCrossAgentTools.filter((tool) => (scopedManifest.crossAgentToolIds ?? []).includes(tool.name))\r\n : allCrossAgentTools;\r\n\r\n const handoffTools: AgentTool[] = offeredSubagents(params.scopedSubagentId).flatMap((manifest) =>\r\n manifest.commands.map((command) => ({\r\n name: `${manifest.id}_${command.name}`,\r\n description: `${manifest.label}: ${command.summary}`,\r\n inputSchema: command.inputSchema,\r\n describeInvocation: () => interpolate(labels.preparing, { label: manifest.label }),\r\n describeIntent: interpolate(labels.collecting, { label: manifest.label }),\r\n async execute(input) {\r\n // What the cross-agent tools collected always wins over what the model copied.\r\n const handoffParams = manifest.withCollected ? manifest.withCollected({ ...input }, context.collected) : input;\r\n context.handoff.current = { subagentId: manifest.id, command: command.name, params: handoffParams };\r\n // NEVER `{ ready: true }`-like: a model seeing \"ready\" narrates a finished\r\n // result in this same turn, before the real work (mounted after this turn)\r\n // has even started. The payload must read unmistakably as \"queued\".\r\n return { status: 'queued', note: interpolate(getAgenticoPrompts().handoffQueued, { label: manifest.label }) };\r\n },\r\n })),\r\n );\r\n\r\n return [...crossAgentTools, ...handoffTools];\r\n },\r\n };\r\n}\r\n","import type { SubagentManifest } from '../domain/subagentManifest';\n\n/** Etichetta breve mostrata nell'header di Agentico/nello storico chat per un task — Agentico\n * stesso non conosce la forma di TParams di nessun subagent specifico (nessun `params.url`/\n * `params.siteHostname` hard-codato qui): ogni manifest dichiara il proprio\n * `deriveHistoryTitle` se ha un campo sensato da mostrare (vedi\n * SubagentManifest.deriveHistoryTitle), altrimenti si ripiega sul nome generico del subagent\n * (`manifest.label`) — mai un tentativo di indovinare quale campo di `params` sia \"il titolo\".\n * File a sé (non dentro SubagentRunner.tsx/ResultDestinationPicker.tsx) perché entrambi lo\n * consumano — tenerlo in uno dei due avrebbe creato un import circolare tra loro. */\nexport function deriveHistoryTitle(manifest: SubagentManifest<any, any, any>, params: Record<string, unknown>): string {\n return manifest.deriveHistoryTitle?.(params) ?? manifest.label;\n}\n","import React, { useState } from 'react';\nimport { useProxy } from '@llmnative/react';\nimport type { SubagentManifest } from '../domain/subagentManifest';\nimport { getDestinationRegistry } from '../domain/destinationRegistry';\nimport { JSON_EXPORT_DESTINATION_ID, jsonExportDestination } from '../domain/jsonExportDestination';\nimport type { ResultDestination } from '../domain/resultDestination';\nimport { interpolate } from '@llmnative/react';\nimport { useAgenticoI18n } from '../i18n';\nimport { useAgentico } from './AgenticoContext';\nimport { deriveHistoryTitle } from './deriveHistoryTitle';\n\n/** Mostrata da SubagentRunner una volta che il subagent ha prodotto un risultato — un bottone\n * per destinazione dichiarata dal manifest (`destinationIds`) più \"Esporta come JSON\", sempre\n * disponibile per qualunque risultato (vedi jsonExportDestination). Le destinazioni tenant-scoped\n * (theme, component-library) scrivono SOLO nel tenant attualmente attivo — vedi\n * DestinationContext, nessuna scelta di un tenant diverso qui (decisione esplicita, vedi piano). */\nexport function ResultDestinationPicker({\n manifest, result, meta, taskId, initiallyApplied, onApplied, extraDestinations,\n}: {\n manifest: SubagentManifest<any, any, any>;\n result: unknown;\n meta: Record<string, unknown>;\n /** Id del task Agentico che ha prodotto questo risultato — passato a `commitEntityChat`\n * quando un apply esplicito (click qui) restituisce un `id` (vedi sotto). Assente = nessuna\n * persistenza dello storico per questo click (nessun task noto da cui pescare uno snapshot),\n * comportamento invariato per un eventuale chiamante futuro senza un task Agentico dietro. */\n taskId?: string;\n /** Destinazioni già applicate in precedenza (es. riaprendo un'esecuzione dallo storico) —\n * mostrate subito come spuntate invece di far credere che vadano riapplicate da zero. */\n initiallyApplied?: string[];\n /** Chiamato dopo un apply riuscito — usato da SubagentRunner per aggiornare la voce di\n * storico (appliedDestinationIds), assente quando il chiamante non ne ha bisogno (es. una\n * destinazione già storicizzata che si vuole solo ri-esportare). */\n onApplied?: (destinationId: string) => void;\n /** Destinazioni scoped alla pagina/istanza corrente (es. \"Applica a questo Site aperto\"),\n * mai registrate nel destinationRegistry condiviso perché chiudono su stato React di UNA\n * pagina specifica — mostrate accanto a quelle del registry, non al loro posto. */\n extraDestinations?: ResultDestination[];\n}) {\n const t = useAgenticoI18n();\n const sidebar = useAgentico();\n const scope = sidebar.scope;\n const proxyFetch = useProxy();\n const [pending, setPending] = useState<string | null>(null);\n const [done, setDone] = useState<Set<string>>(new Set(initiallyApplied ?? []));\n const [error, setError] = useState<string | null>(null);\n\n // Mappa per id, non una semplice concatenazione: un extraDestination con lo STESSO id di\n // una destinazione del registry la SOVRASCRIVE nella stessa posizione invece di duplicarla —\n // utile a una pagina che vuole avvolgere una destinazione generica con un'informazione\n // scoped a sé (es. \"collega anche al Site aperto\") che il registry tenant-wide non può\n // conoscere da solo.\n const registry = getDestinationRegistry();\n const destinationMap = new Map<string, ResultDestination>();\n manifest.destinationIds.forEach((id: string) => { const d = registry[id]; if (d) destinationMap.set(id, d); });\n (extraDestinations ?? []).forEach((d) => destinationMap.set(d.id, d));\n destinationMap.set(jsonExportDestination.id, jsonExportDestination);\n const destinations = Array.from(destinationMap.values());\n\n const run = async (destinationId: string) => {\n setPending(destinationId);\n setError(null);\n try {\n const destination = destinationMap.get(destinationId);\n if (!destination) throw new Error(interpolate(t.unknownDestination, { id: destinationId }));\n if (destinationId !== JSON_EXPORT_DESTINATION_ID && !scope) {\n throw new Error(t.destinationNoScope);\n }\n const outcome = await destination.apply(result, {\n scopeId: scope?.id ?? '',\n // json-export (the only destination allowed without a scope) never reads it.\n dataProvider: scope?.dataProvider as NonNullable<typeof scope>['dataProvider'],\n storageProvider: scope?.storageProvider ?? null,\n proxyFetch,\n }, meta);\n setDone((prev) => new Set(prev).add(destinationId));\n onApplied?.(destinationId);\n // Un apply riuscito CON un id è, per definizione, l'entità che questo click ha\n // scritto — stessa persistenza dello storico già fatta per l'auto-apply\n // (SubagentRunner.tsx), qui per il caso \"l'utente ha cliccato lui stesso il\n // bottone\" (es. \"Applica a questo Component\" di html-to-liquid, che non ha\n // autoApplyDestinationIds): senza questo, quella chat non finiva MAI nello storico\n // (bug segnalato — solo theme/redirects, che la persistono per conto proprio,\n // comparivano).\n if (outcome?.id) {\n sidebar.commitEntityChat(manifest.id, outcome.id, taskId, scope?.id, meta, deriveHistoryTitle(manifest, meta));\n }\n } catch (err) {\n setError(err instanceof Error ? err.message : String(err));\n } finally {\n setPending(null);\n }\n };\n\n return (\n <div className=\"space-y-2\">\n <p className=\"text-xs font-medium text-muted-foreground\">{t.destinationPickerTitle}</p>\n <div className=\"flex flex-wrap gap-2\">\n {destinations.map((destination) => (\n <button\n key={destination.id}\n type=\"button\"\n disabled={pending !== null}\n onClick={() => void run(destination.id)}\n className=\"cursor-pointer rounded-md border border-input bg-background px-2.5 py-1.5 text-xs font-medium transition-colors hover:bg-accent hover:text-accent-foreground disabled:cursor-not-allowed disabled:opacity-60\"\n >\n {pending === destination.id ? t.destinationApplying : `${done.has(destination.id) ? '✓ ' : ''}${destination.id === JSON_EXPORT_DESTINATION_ID ? t.exportJson : destination.label}`}\n </button>\n ))}\n </div>\n {error && <p className=\"text-xs text-destructive\">{error}</p>}\n </div>\n );\n}\n","import type { ComponentType } from 'react';\n\n/** Booleano = includi/escludi (colori, font — un solo valore risolto per chiave, niente tra cui\n * scegliere). Stringa = candidato scelto per uno slot con più opzioni recuperate (logo/favicon/\n * OG image — '' significa \"Nessuno\"). Vedi LiquidResultReport per l'uso concreto (theme non ha\n * più un report qui: il risultato popola direttamente il ThemeEditor aperto, vedi\n * SubagentRunner.componentAnchors — nessuna selezione intermedia). */\nexport type SubagentSelection = Record<string, boolean | string>;\n\n/** Un subagent \"a selezione\" (branding, html-to-liquid): il risultato è generato UNA volta,\n * l'utente sceglie solo quali campi/candidati includere prima di spedirlo a una destinazione —\n * mai una modifica diretta del contenuto del risultato. */\nexport interface SelectionReportModule<TResult = any> {\n kind: 'selection';\n Report: ComponentType<{\n result: TResult;\n selection: SubagentSelection;\n onChange: (key: string, value: boolean | string) => void;\n }>;\n /** Solo le chiavi per campi effettivamente presenti nel risultato — l'assenza di una chiave\n * significa \"nessuna checkbox per questo campo\", non \"deselezionato\". Parte sempre a true\n * (comportamento opt-out, come il vecchio flusso di import pre-AI). */\n defaultSelection(result: TResult): SubagentSelection;\n /** Copia di result con i campi deselezionati azzerati/filtrati — usata SOLO per alimentare\n * ResultDestinationPicker, mai per il Report stesso (che mostra sempre il result grezzo +\n * stato checkbox, altrimenti un campo deselezionato sparirebbe invece di restare spuntabile). */\n applySelection(result: TResult, selection: SubagentSelection): TResult;\n}\n\n/** Un subagent \"editabile\": il risultato STESSO è l'oggetto che l'utente continua a modificare\n * nel tempo (righe di una griglia, non un opt-in/opt-out su campi noti a priori) — non c'è nulla\n * da \"filtrare\" prima di una destinazione. Il `Report` di un modulo così presuppone SEMPRE un\n * `<Form>` vivo intorno a sé (i suoi campi editabili sono `Input`/`Select` legati a\n * `useFormContext`) — per questo NON va registrato qui se quel Form lo possiede già un componente\n * component-anchor sempre montato (es. redirects: `RedirectsEditor` monta il proprio Report\n * dentro il proprio `<Form>` con un import diretto, mai tramite questo registro). Registrarlo qui\n * lo esporrebbe ANCHE al fallback \"nessun anchor vivo\" di SubagentRunner (vedi sotto), che lo\n * renderizza SENZA alcun Form intorno — crash reale osservato (`useFormContext must be used\n * within a FormContext.Provider`, root-caused a redirects registrato qui mentre l'utente naviga\n * via dall'editor senza salvare: un task ancora vivo con un risultato in sospeso, nessun anchor\n * per riceverlo, fallback che monta il Report nudo). Un modulo \"editabile\" appartiene qui SOLO se\n * il subagent NON garantisce un componente-anchor sempre vivo per ricevere il risultato. */\nexport interface EditableReportModule<TResult = any> {\n kind: 'editable';\n Report: ComponentType<{\n result: TResult;\n onResultChange: (next: TResult) => void;\n }>;\n}\n\n/** Registro presentation-side per id di subagent (stesso principio filesystem-first/id-keyed di\n * destinationRegistry.ts) — MAI un campo su SubagentManifest: quel\n * contratto vive nel domain layer, presentation-agnostico per design (nessun file in domain/\n * importa React in questo repo), mentre un report è JSX puro. Un subagent senza voce qui\n * semplicemente non mostra nessun report nel fallback \"nessun anchor vivo\" di SubagentRunner —\n * comportamento identico a oggi per redirects (il suo report vive SOLO dentro RedirectsEditor,\n * mai qui, vedi il commento su EditableReportModule sopra), mai un errore.\n * Estendere per un futuro subagent: un modulo + una riga qui SOLO se non ha un componente-anchor\n * sempre vivo che possa possedere il proprio Form. */\nexport type SubagentReportModule<TResult = any> = SelectionReportModule<TResult> | EditableReportModule<TResult>;\n\n// Store mutabile, non un letterale statico — Agentico (questo file) non deve MAI importare un\n// report di un subagent concreto (es. LiquidResultReport, specifico di html-to-liquid): stessa\n// inversione di dipendenza di subagentRegistry.ts/registerSubagents, qui applicata al registro\n// dei report React invece che ai manifest. L'host (il CMS) registra i propri report una volta\n// sola all'avvio (vedi CmsProviders.tsx) — un id senza voce registrata resta \"nessun report\" per\n// il fallback \"nessun anchor vivo\" di SubagentRunner, mai un errore (comportamento invariato).\nconst registry: Record<string, SubagentReportModule> = {};\n\nexport function registerSubagentReports(reports: Record<string, SubagentReportModule>): void {\n Object.assign(registry, reports);\n}\n\nexport function getSubagentReport(subagentId: string): SubagentReportModule | undefined {\n return registry[subagentId];\n}\n","import React, { useState } from 'react';\nimport { Code } from '@llmnative/react';\n\n/** Anteprima di riserva per un subagent SENZA un report registrato (oggi: theme, redirects) —\n * mostrata nella stessa identica posizione/momento di un vero `SubagentReportModule.Report`\n * (prima del bottone \"Applica\"), cosi ogni subagent, con o senza report dedicato, con o senza\n * AI, passa dallo STESSO schema \"vedi il risultato → poi applica\", mai un apply automatico\n * silenzioso. Un JSON grezzo non è bello quanto uno swatch di colori o una griglia editabile,\n * ma è sempre meglio di applicare alla cieca — un report dedicato resta la via preferita quando\n * ne vale la pena scriverne uno (vedi subagentReportRegistry.tsx). */\nexport function GenericResultPreview({ result }: { result: unknown }) {\n const [expanded, setExpanded] = useState(false);\n return (\n <div\n className={`relative cursor-pointer rounded-md border border-border/60 ${expanded ? 'max-h-80 overflow-y-auto' : 'max-h-28 overflow-hidden'}`}\n onClick={() => setExpanded((v) => !v)}\n title={expanded ? 'Clic per comprimere' : 'Clic per espandere'}\n >\n <Code language=\"json\" showCopy={expanded} background=\"transparent\" className=\"!text-[11px]\">\n {JSON.stringify(result, null, 2)}\n </Code>\n {!expanded && (\n <div className=\"pointer-events-none absolute inset-x-0 bottom-0 h-6 bg-gradient-to-t from-muted/30 to-transparent\" />\n )}\n </div>\n );\n}\n","import type { ComponentAnchor } from './AgenticoContext';\nimport type { ResultDestination } from '../domain/resultDestination';\n\n/** Consegna a un componentAnchor live (es. ThemeEditor per theme) trattata come UNA destinazione\n * qualunque tra quelle proposte da ResultDestinationPicker — non più un canale a parte con\n * consegna automatica silenziosa (bug segnalato: il risultato finiva nel componente PRIMA che\n * l'utente vedesse alcuna anteprima o cliccasse alcunché). Stesso schema per ogni subagent, con\n * o senza AI: risultato → anteprima in chat → click esplicito → applica. Nessun `id` restituito\n * (l'entità non è ancora persistita finché l'utente non salva lui stesso nel componente aperto —\n * `commitEntityChat` si occupa di quel momento separatamente, dal Save del componente stesso). */\nexport function buildLiveAnchorDestination(\n matching: ComponentAnchor[],\n meta: Record<string, unknown>,\n taskId: string,\n): ResultDestination | null {\n if (matching.length === 0) return null;\n return {\n id: 'live-component',\n label: 'Applica al componente aperto',\n async apply(result) {\n matching.forEach((a) => a.onResult?.(result, meta, taskId));\n },\n };\n}\n","import type { ReactNode } from 'react';\nimport type { ChatbotAction } from '@llmnative/react';\n\n/** What the controls of a subagent receive (CR-003). */\nexport interface SubagentControlsProps {\n /** The raw params the subagent was started with. */\n params: Record<string, unknown>;\n /** The id of the task in the Agentico panel. */\n taskId: string;\n /** A turn is running: actions that would interrupt it should wait. */\n running: boolean;\n /** Ends the task (stops a running turn and closes it). */\n close: () => void;\n}\n\n/** The controls of a task: actions of the prompt, and what the host shows above it (a modal, a message). */\nexport interface SubagentControls {\n /** Buttons of the composer, next to the attachments (`ChatbotAction` with `onClick`). */\n actions?: ChatbotAction[];\n /** Shown above the composer while the task is open. */\n content?: ReactNode;\n}\n\n/**\n * A React hook of the host: called by the runner of each task of its subagent, on every render.\n * Its `actions` and `content` must keep their identity while nothing changes (`useMemo`): a new\n * one registers the task again, and a new one at every render would never stop.\n */\nexport type UseSubagentControls = (props: SubagentControlsProps) => SubagentControls;\n\n// Keyed by subagent id, presentation side as the report registry: Agentico never imports a\n// concrete one. The hook of a subagent never changes, so a runner always calls the same one.\nconst registry: Record<string, UseSubagentControls> = {};\n\nexport function registerSubagentControls(controls: Record<string, UseSubagentControls>): void {\n Object.assign(registry, controls);\n}\n\nexport function getSubagentControls(subagentId: string): UseSubagentControls | undefined {\n return registry[subagentId];\n}\n","import React, { useEffect, useMemo, useRef, useState } from 'react';\nimport { useProxy } from '@llmnative/react';\nimport type { AIConversationTurn } from '@llmnative/react';\nimport { useAgent, type AgentActivityEntry } from './useAgent';\nimport { useAgentico } from './AgenticoContext';\nimport type { SubagentManifest } from '../domain/subagentManifest';\nimport { getDestinationRegistry } from '../domain/destinationRegistry';\nimport type { ResultDestination } from '../domain/resultDestination';\nimport { ResultDestinationPicker } from './ResultDestinationPicker';\nimport { getSubagentReport, type SubagentSelection } from './reports/subagentReportRegistry';\nimport { GenericResultPreview } from './reports/GenericResultPreview';\nimport { deriveHistoryTitle } from './deriveHistoryTitle';\nimport { buildLiveAnchorDestination } from './liveAnchorDestination';\nimport { getSubagentControls, type UseSubagentControls } from './subagentControls';\nimport { saveEntityChat } from '../domain/entityChatStore';\n\n/** Monta il vero subagent (useAgent(manifest.definition)) e lo esegue una volta al mount con i\n * parametri già raccolti dall'orchestratore Agentico — nessun passo utente aggiuntivo, la\n * conversazione di raccolta parametri è già avvenuta a monte (vedi Agentico.tsx). Si registra\n * nel pannello Agentico con id stabile via useRef + cleanup closeTask allo smontaggio, con il\n * transcript dell'orchestratore (`precedingActivity`) prepeso al proprio log — l'utente vede\n * UNA chat continua, mai due task distinti da switchare (vedi OrchestratorRunner).\n *\n * Con `resumeSeed` valorizzato, il primo giro chiama `agent.resume(...)` invece di\n * `agent.run(...)` — riprende una chat già salvata (vedi entityChatStore/\n * AgenticoContext.resumeEntityChat) invece di avviarne una nuova. Tutto il resto (consegna del\n * risultato ai componentAnchors, ResultDestinationPicker di riserva, registrazione del task) è\n * IDENTICO a un run normale — un follow-up dopo un resume produce un risultato esattamente come\n * un follow-up dopo un run, nessuna duplicazione di logica per il caso \"ripreso\". */\nconst noControls: UseSubagentControls = () => ({});\n\nexport function SubagentRunner({\n manifest, params, extraDestinations, precedingActivity, taskId: taskIdProp, resumeSeed, model, openingMessage,\n}: {\n manifest: SubagentManifest<any, any, any>;\n params: Record<string, unknown>;\n /** Destinazioni scoped alla pagina che ha avviato questo run (es. \"Applica a questo Site\n * aperto\") — inoltrate tale e quali a ResultDestinationPicker, vedi quel file. */\n extraDestinations?: ResultDestination[];\n /** Transcript dell'orchestratore all'handoff — vedi AgenticoTask.precedingActivity. Non ha\n * senso insieme a `resumeSeed` (una chat ripresa non ha un orchestratore a monte in questo\n * giro) — semplicemente non passato dal chiamante in quel caso. */\n precedingActivity?: AgentActivityEntry[];\n /** Id di tab da RIUSARE invece di generarne uno fresco — vedi PendingCarryOver\n * (AgenticoContext.tsx)/OrchestratorRunner.tsx: quando presente, questo run \"continua\" una\n * tab già esistente (stessa posizione, stesso id) invece di aprirne una seconda per lo\n * stesso subagent. Assente = comportamento invariato, id fresco per ogni run. */\n taskId?: string;\n /** Presente = riprendi una chat già salvata invece di avviarne una nuova (vedi\n * AgenticoContext.resumeEntityChat) — `params` deve comunque essere la stessa forma raccolta\n * originariamente (manifest.toParams(params) viene chiamato in entrambi i casi). */\n resumeSeed?: { history: AIConversationTurn[]; activity: AgentActivityEntry[] };\n /** Modello scelto nel composer \"starter\" a monte (OrchestratorRunner.tsx's modelRef) — questo\n * componente monta la sua PROPRIA istanza di useAgent/useAIModelCatalog, indipendente da\n * quella dell'orchestratore che ha appena fatto l'handoff, quindi senza propagarlo qui\n * esplicitamente `agent.selectedModel` si auto-inizializzerebbe al default del provider\n * invece di continuare con la scelta dell'utente (stesso bug, stessa causa, di\n * OrchestratorRunner — vedi il commento lì). Non passato per un `resumeSeed` — lì non c'è una\n * scelta \"di questo giro\" da propagare, si continua semplicemente la chat salvata così com'era. */\n model?: string;\n /** The first message of the person, when a page starts the subagent directly\n * (AgenticoContext.startSubagent, CR-003): shown in the log and given to the first turn. */\n openingMessage?: string;\n}) {\n const agent = useAgent(manifest.definition, { defaultEnabled: true });\n const sidebar = useAgentico();\n const scope = sidebar.scope;\n const proxyFetch = useProxy();\n const taskId = useRef(taskIdProp ?? `subagent-${manifest.id}-${crypto.randomUUID()}`).current;\n const [result, setResult] = useState<unknown>(null);\n const [appliedIds, setAppliedIds] = useState<string[]>([]);\n const [autoApplyError, setAutoApplyError] = useState<string | null>(null);\n const reportModule = getSubagentReport(manifest.id);\n const selectionModule = reportModule?.kind === 'selection' ? reportModule : null;\n const [selection, setSelection] = useState<SubagentSelection | null>(null);\n const startedRef = useRef(false);\n // Punto della timeline (precedingActivity + agent.activity, stesso ordine di Agentico.tsx) in\n // cui è arrivato l'ULTIMO risultato NUOVO — passato come panelExtraAnchorIndex a openTask\n // sotto, cosi un follow-up successivo (retry finito in errore, nuovo messaggio) appare\n // SOTTO il pannello risultati invece di scavalcarlo (bug segnalato: il pannello sembrava\n // \"risalire\" sopra conversazione più recente perché veniva sempre reso in fondo al log,\n // indipendentemente da quando il risultato che mostra fosse realmente arrivato).\n const panelExtraAnchorIndexRef = useRef(0);\n const anchoredResultRef = useRef<unknown>(null);\n if (result && result !== anchoredResultRef.current) {\n anchoredResultRef.current = result;\n panelExtraAnchorIndexRef.current = (precedingActivity?.length ?? 0) + agent.activity.length;\n }\n // Stesso guard di OrchestratorRunner — vedi il commento lì per il perché è necessario\n // (senza, la tab chiusa dall'utente riapparirebbe da sola al prossimo aggiornamento di\n // agent.activity/running, es. l'abort innescato dalla chiusura stessa).\n const dismissedRef = useRef(false);\n\n // Aspetta che il catalogo modelli sia risolto (async, vedi useAgent.ts) prima di lanciare\n // run() — altrimenti run() partirebbe con selectedModel ancora vuoto e cadrebbe sempre sul\n // fallback deterministico, anche quando un provider è configurato. Se non c'è nessun\n // provider (`!hasProviders`), non arriverà mai un selectedModel: si parte comunque subito,\n // run() userà il fallback come previsto.\n // A remote subagent (CR-003) has no model to wait for.\n const readyToStart = agent.remote || !agent.hasProviders || agent.selectedModel !== '';\n\n useEffect(() => {\n if (startedRef.current || !readyToStart) return;\n startedRef.current = true;\n if (resumeSeed) {\n void agent.resume(manifest.toParams(params), resumeSeed);\n } else {\n // Same reasoning as OrchestratorRunner.tsx: `model` overrides `selectedModel` for\n // this run() call only, so setSelectedModel() also needs calling here for every\n // later interaction (a follow-up message, a retry) to keep reading the user's actual\n // choice instead of this instance's own just-resolved default.\n if (model) agent.setSelectedModel(model);\n void agent.run(manifest.toParams(params), { model, openingMessage }).then((r) => { if (r) setResult(r); });\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [readyToStart]);\n\n // Auto-apply — per un subagent come redirects il risultato STESSO è il dato definitivo\n // (docs/30-redirects-system.md), non un export opzionale: ogni volta che `result` cambia\n // (run iniziale o un edit successivo in RedirectMappingReport), le destinazioni elencate in\n // `autoApplyDestinationIds` vengono applicate da sole, senza che l'utente clicchi nulla in\n // ResultDestinationPicker (che resta comunque disponibile per un secondo apply esplicito,\n // es. ri-esportare un CSV). Richiede uno `scope` (AgenticoProvider) — nessun apply silenzioso\n // verso un provider ancora placeholder.\n useEffect(() => {\n if (!result || !scope) return;\n const autoIds = manifest.autoApplyDestinationIds ?? [];\n // Se un componente vivo per QUESTO subagent dichiara un `pinnedEntityId` (es.\n // RedirectsEditor, aperto su un host specifico — vedi ComponentAnchor.pinnedEntityId),\n // lo si inoltra alla destinazione: una destinazione con logica di identità propria (es.\n // redirectsSaveDestination) può rifiutare un salvataggio il cui id risolto non combacia,\n // invece di scrivere silenziosamente sull'entità sbagliata perché il modello ha ignorato\n // il vincolo nel prompt. Nessun anchor pinnato (es. run partito da un contesto senza\n // componente vivo) → `undefined`, nessun controllo aggiuntivo.\n const pinnedEntityId = sidebar.componentAnchors.find((a) => a.subagentId === manifest.id && a.pinnedEntityId)?.pinnedEntityId;\n const registry = getDestinationRegistry();\n autoIds.forEach((id) => {\n const destination = registry[id];\n if (!destination) return;\n setAutoApplyError(null);\n destination.apply(result, {\n scopeId: scope.id,\n dataProvider: scope.dataProvider,\n storageProvider: scope.storageProvider,\n proxyFetch,\n }, { ...params, pinnedEntityId })\n // Un auto-apply riuscito CON un id (es. redirects: {id: hostId}) è, per\n // definizione, l'entità che questo giro ha scritto — commitEntityChat generico\n // qui invece che duplicato nel componente di ogni subagent (a differenza di\n // theme, che non ha auto-apply e deve chiamarlo lui stesso dal proprio Save):\n // qualunque futuro subagent con autoApplyDestinationIds ottiene la persistenza\n // chat gratis, senza scriverne una riga in più.\n .then((outcome) => {\n if (outcome?.id) {\n sidebar.commitEntityChat(manifest.id, outcome.id, taskId, scope.id, params, deriveHistoryTitle(manifest, params));\n }\n })\n // Un auto-apply fallito (es. hostname non risolvibile) non deve restare\n // invisibile solo perché non l'ha innescato un click esplicito — bug osservato:\n // \"Tools > Redirects\" restava vuoto dopo un'elaborazione riuscita, senza alcun\n // segnale che il salvataggio non fosse mai avvenuto.\n .catch((err) => setAutoApplyError(err instanceof Error ? err.message : String(err)));\n });\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [result, agent.activity, scope]);\n\n // Inizializza la selezione (tutti i campi opzionali spuntati) non appena arriva un risultato\n // per un subagent con report registrato — si resetta anche se `result` cambia riferimento\n // (es. un refinement successivo produce un nuovo oggetto), così l'utente rivede sempre uno\n // stato \"tutto incluso\" di fronte a un risultato nuovo invece di ereditare vecchie esclusioni.\n useEffect(() => {\n if (result && selectionModule) setSelection(selectionModule.defaultSelection(result));\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [result, selectionModule]);\n\n const handleSelectionChange = (key: string, value: boolean | string) => {\n setSelection((prev) => (prev ? { ...prev, [key]: value } : prev));\n };\n\n // Un report \"editabile\" (redirects) rimpiazza direttamente `result` — l'effect di auto-apply\n // sopra reagisce a `result` e ripropaga da solo, nessun secondo canale da aggiungere qui.\n const handleResultChange = (next: unknown) => setResult(next);\n\n // Memoizzato: applySelection ritorna sempre un oggetto NUOVO (spread), quindi senza\n // useMemo ogni render di questo componente produrrebbe un filteredResult diverso — ed\n // essendo nelle dipendenze dell'effect di sidebar.openTask sotto, l'effect non si sarebbe\n // mai stabilizzato: openTask cambia il task nel context → il context re-renderizza i\n // consumer (anche questo componente) → filteredResult ricalcolato con un nuovo riferimento\n // → l'effect riparte da capo. Loop infinito osservato in console (\"Maximum update depth\n // exceeded\"), corretto memoizzando sulle dipendenze effettive.\n const filteredResult = useMemo(\n () => (result && selectionModule && selection ? selectionModule.applySelection(result, selection) : result),\n [result, selectionModule, selection],\n );\n\n // Un componentAnchor live (vedi useSubagentComponentContext) per QUESTO subagent è trattato\n // come UNA destinazione qualunque — MAI più una consegna automatica silenziosa appena\n // arriva il risultato (bug segnalato: l'utente non vedeva nessuna anteprima prima che il\n // componente si popolasse da solo). Più anchor possono ascoltare lo STESSO subagentId (es.\n // ThemeEditor per i campi tema + SiteEditorPage per i soli campi di site-branding) — tutti\n // ricevuti insieme da un solo click su \"Applica al componente aperto\" (vedi\n // liveAnchorDestination.ts). Nessun anchor combacia (es. run partito da un contesto senza\n // componente vivo) → quella destinazione semplicemente non compare tra le altre.\n // Memoizzati per lo stesso motivo di filteredResult sopra — non sono nelle dipendenze\n // dell'effect sotto, ma restano comunque props di ResultDestinationPicker dentro\n // `panelExtra`: un riferimento nuovo a ogni render sarebbe fragile (basterebbe aggiungerli\n // un giorno alle dipendenze per riaprire lo stesso loop).\n const matchingAnchors = useMemo(\n () => sidebar.componentAnchors.filter((a) => a.subagentId === manifest.id && a.onResult),\n [sidebar.componentAnchors, manifest.id],\n );\n const liveAnchorDestination = useMemo(\n () => buildLiveAnchorDestination(matchingAnchors, params, taskId),\n [matchingAnchors, params, taskId],\n );\n const pickerExtraDestinations = useMemo(\n () => (liveAnchorDestination ? [liveAnchorDestination, ...(extraDestinations ?? [])] : extraDestinations),\n [liveAnchorDestination, extraDestinations],\n );\n\n // A subagent whose chats belong to an entity (CR-003) saves the chat after every turn: it is in\n // the history and is resumed on that entity, without the host having to commit it.\n const chatEntityId = manifest.chatEntityId?.(params);\n const wasRunningRef = useRef(false);\n useEffect(() => {\n const ended = wasRunningRef.current && !agent.running;\n wasRunningRef.current = agent.running;\n if (!ended || !chatEntityId || !scope) return;\n const snap = agent.snapshot();\n if (!snap) return;\n saveEntityChat({\n scopeId: scope.id, subagentId: manifest.id, entityId: chatEntityId, params, title: deriveHistoryTitle(manifest, params),\n history: snap.history,\n activity: [...(precedingActivity ?? []), ...snap.activity],\n });\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [agent.running]);\n\n const dismiss = () => {\n dismissedRef.current = true;\n if (agent.running) agent.stop();\n sidebar.closeTask(taskId);\n };\n // The controls of the host for this subagent: actions of the prompt and what it shows above.\n const useControls = useRef<UseSubagentControls>(getSubagentControls(manifest.id) ?? noControls).current;\n const controls = useControls({ params, taskId, running: agent.running, close: dismiss });\n\n const handleDestinationApplied = (destinationId: string) => {\n setAppliedIds((prev) => (prev.includes(destinationId) ? prev : [...prev, destinationId]));\n };\n\n // Nessun gate su agent.canContinue — Agentico è l'unica UI per qualunque subagent (nessuna\n // modale con una propria UI indipendente esiste più), quindi il task va registrato subito,\n // altrimenti senza provider AI configurato (o prima che il catalogo modelli si risolva) non\n // comparirebbe mai, e il pannello sembrerebbe non aver reagito.\n //\n // `panelExtra` (non il return JSX di questo componente!) è come il ResultDestinationPicker\n // arriva dentro il pannello Agentico — questo componente è montato come sibling PRIMA del\n // pannello visibile (vedi Agentico.tsx, per sopravvivere al toggle di isOpen), quindi il\n // suo return finirebbe renderizzato FUORI dal pannello, come una colonna a sé — bug osservato\n // e corretto passando il picker attraverso lo slot esplicito del task invece che nel JSX.\n // `result`/`appliedIds` sono nelle dipendenze apposta: senza, il task registrato al primo\n // giro non includerebbe mai il picker (o i suoi aggiornamenti) una volta arrivato il risultato.\n useEffect(() => {\n if (dismissedRef.current) return;\n sidebar.openTask({\n // Titolo specifico (es. \"consul-etica.it\", non \"Theme extractor\") invece del nome\n // generico del subagent — più utile nell'header di Agentico.\n id: taskId, title: deriveHistoryTitle(manifest, params), icon: manifest.icon, agent, onApplied: (r: unknown) => setResult(r), precedingActivity,\n panelExtraAnchorIndex: panelExtraAnchorIndexRef.current,\n onDismiss: dismiss,\n // Saved at every turn: leaving it asks nothing (see Agentico.tsx, \"new chat\").\n ...(chatEntityId ? { savedToDisk: true } : {}),\n panelActions: controls.content,\n composerActions: controls.actions,\n panelExtra: result ? (\n <>\n {selectionModule && selection ? (\n <selectionModule.Report result={result} selection={selection} onChange={handleSelectionChange} />\n ) : reportModule?.kind === 'editable' ? (\n <reportModule.Report result={result} onResultChange={handleResultChange} />\n ) : (\n // Nessun report registrato per questo subagent (oggi: theme, redirects) —\n // anteprima generica, mai un apply diretto senza che l'utente veda prima\n // cosa sta per applicare (vedi GenericResultPreview.tsx).\n <GenericResultPreview result={filteredResult} />\n )}\n {autoApplyError && (\n <p className=\"text-xs text-destructive\">Salvataggio automatico non riuscito: {autoApplyError}</p>\n )}\n <ResultDestinationPicker\n manifest={manifest} result={filteredResult} meta={params} taskId={taskId}\n initiallyApplied={appliedIds} onApplied={handleDestinationApplied}\n extraDestinations={pickerExtraDestinations}\n />\n </>\n ) : undefined,\n });\n // BUG FISSATO: stesso problema di OrchestratorRunner.tsx — `agent.modelsByProvider`/\n // `agent.hasProviders` mancavano qui, quindi il catalogo modelli risolto in modo\n // asincrono non faceva mai ri-registrare l'`agent` fresco in sidebar.openTask, e la\n // tendina di selezione modello restava vuota per l'intera durata del task.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [agent.activity, agent.running, agent.error, agent.canContinue, agent.selectedModel, agent.modelsByProvider, agent.hasProviders, agent.remoteModels, sidebar.openTask, sidebar.componentAnchors, taskId, manifest, params, result, filteredResult, selection, appliedIds, extraDestinations, precedingActivity, autoApplyError, controls.content, controls.actions]);\n\n useEffect(() => {\n return () => { sidebar.closeTask(taskId); };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [sidebar.closeTask, taskId]);\n\n return null;\n}\n","import React, { useEffect, useMemo, useRef, useState } from 'react';\nimport { useAgenticoI18n } from '../i18n';\nimport { useAgent } from './useAgent';\nimport { useAgentico, ORCHESTRATOR_TASK_ID } from './AgenticoContext';\nimport { createOrchestratorAgentDefinition } from '../domain/orchestrator/agenticoOrchestratorAgent';\nimport { findSubagentManifest } from '../domain/subagentRegistry';\nimport type { OrchestratorHandoff } from '../domain/orchestrator/prompt';\nimport { SubagentRunner } from './SubagentRunner';\n\n/** Monta la conversazione dell'orchestratore Agentico — il \"meta-agente\" che legge il catalogo\n * CLI dei subagent (deterministico, nessuna chiamata LLM, vedi createOrchestratorAgentDefinition\n * buildContext) e conversa con l'utente finché non ha raccolto abbastanza parametri per invocare\n * un subagent reale. Quando arriva un risultato `{ kind: 'handoff' }` monta un SubagentRunner per\n * quel subagent, gli passa il transcript dell'orchestratore com'era in quel momento\n * (`precedingActivity`) e chiude subito il proprio task — l'utente vede UNA chat continua che\n * scivola dalla raccolta parametri all'esecuzione del subagent, mai due task da switchare a\n * mano (decisione esplicita, vedi piano). */\nexport function OrchestratorRunner({ userGoal }: { userGoal: string }) {\n const t = useAgenticoI18n();\n // Built once per mount with the labels of the current locale (useAgent keeps the first definition).\n const definition = useMemo(() => createOrchestratorAgentDefinition({\n unavailable: t.orchestratorUnavailable,\n continueWithoutAi: t.orchestratorContinueWithoutAi,\n preparing: t.orchestratorPreparing,\n collecting: t.orchestratorCollecting,\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }), []);\n const agent = useAgent(definition, { defaultEnabled: true });\n const sidebar = useAgentico();\n const [handoff, setHandoff] = useState<OrchestratorHandoff | null>(null);\n const startedRef = useRef(false);\n const readyToStart = !agent.hasProviders || agent.selectedModel !== '';\n\n // Catturate una sola volta al mount (come userGoal, un prop) — non rilette live da\n // sidebar.pendingExtraDestinations più avanti: questo componente resta montato per tutta la\n // conversazione, che può durare più turni fino all'handoff, e nel frattempo un'altra\n // startOrchestrator() altrove potrebbe aver sovrascritto quel valore nel context.\n const extraDestinationsRef = useRef(sidebar.pendingExtraDestinations);\n // Stesso principio per gli allegati del messaggio di apertura (grezzi — la conversione ad\n // AIAttachment avviene in useAgent.ts) — servono solo al primissimo run(), mai riletti dopo.\n const attachmentsRef = useRef(sidebar.pendingAttachments);\n // Stesso principio per il modello scelto nel composer \"starter\" — passato come override a\n // run() (vedi useAgent.ts) invece di un setSelectedModel() che non farebbe in tempo a\n // committare prima della chiamata nello stesso effect.\n const modelRef = useRef(sidebar.pendingModel);\n // Id del subagent verso cui la pagina/componente che ha avviato questa conversazione era\n // scoped (es. ThemeIndexList ancorato a 'theme', o /tools/redirects col vecchio meccanismo a\n // pageContextCard) — catturato UNA VOLTA al mount, come gli altri ref sopra. Permette a\n // IndexList (redirects) di mostrare \"in corso\" per quel subagent già durante la raccolta\n // parametri, non solo dopo l'handoff (vedi AgenticoContext.AgenticoTask.scopedSubagentId).\n // Un target esplicito da startOrchestrator(goal, {scopedSubagentId}) vince su quello\n // derivato da un componentAnchor live (vedi AgenticoContext.pendingScopedSubagentId) — un\n // click one-shot (es. \"Importa HTML/JSX\") non monta alcun anchor, ma sa comunque già a\n // priori quale subagent servirà.\n const scopedSubagentIdRef = useRef(sidebar.pendingScopedSubagentId ?? sidebar.scopedSubagentId ?? undefined);\n // Vedi PendingCarryOver (AgenticoContext.tsx) — catturato una volta al mount come gli altri\n // ref sopra: se startOrchestrator() ha trovato un task esistente per lo stesso subagent,\n // questo giro deve CONTINUARE quella conversazione (stesso id di tab, transcript prepeso)\n // invece di aprirne una scollegata — vedi il return sotto.\n const carryOverRef = useRef(sidebar.pendingCarryOver);\n // true dopo che l'utente ha chiuso la tab di questo task dalla tab bar — impedisce\n // all'effect sotto di ri-registrarlo (sidebar.openTask) al prossimo cambiamento di\n // agent.activity/running (es. l'abort innescato dalla chiusura stessa produce un ultimo\n // aggiornamento \"Interrotto dall'utente\"): senza questo guard la tab riapparirebbe da sola\n // subito dopo essere stata chiusa (bug osservato in fase di sviluppo di questa feature).\n const dismissedRef = useRef(false);\n\n const handleResult = (r: unknown) => {\n if (r && typeof r === 'object' && (r as { kind?: string }).kind === 'handoff') {\n setHandoff(r as OrchestratorHandoff);\n // Il lavoro dell'orchestratore è finito qui — libera subito ORCHESTRATOR_TASK_ID\n // (invece di aspettare lo smontaggio, che con questo componente non avviene mai\n // finché pendingGoal resta impostato) cosi un futuro startOrchestrator() vede\n // correttamente \"nessuna conversazione in corso\" invece di restare bloccato per il\n // resto della sessione su questa, già conclusa (bug osservato, vedi piano). Il\n // SubagentRunner montato sotto non è toccato: ha un id/task proprio, indipendente.\n sidebar.closeTask(ORCHESTRATOR_TASK_ID);\n }\n };\n\n useEffect(() => {\n if (startedRef.current || !readyToStart) return;\n startedRef.current = true;\n // `model: modelRef.current` overrides `selectedModel` for THIS run() call only (see the\n // comment on modelRef above and useAgent.ts's own doc comment on `opts.model`) — a\n // setSelectedModel() here wouldn't commit in time for run() to read it in the same\n // effect. But that override is scoped to this one call: every later interaction — the\n // \"Riprova\" button, any follow-up sendMessage(), a later round-trip of a NEW run() — reads\n // `agent.selectedModel` fresh, which without this call still sits at whatever\n // useAIModelCatalog() picked as its own default (a separate instance from the starter\n // composer's, never told about the user's actual choice). Calling it too, alongside the\n // override, costs nothing now and fixes every call after this one — this was the bug: the\n // model the user picked silently reverted to the provider default the moment the\n // conversation needed a second API call (a retry, a tool round-trip, anything past the\n // opening message).\n if (modelRef.current) agent.setSelectedModel(modelRef.current);\n // pageContext letto QUI, non prima — \"dove si trova l'utente\" al momento in cui la\n // conversazione parte davvero, non a mount-time del componente (che potrebbe precedere\n // di un istante la registrazione del contesto da parte della pagina chiamante).\n void agent.run(\n { userGoal, pageContext: sidebar.pageContext, scopedSubagentId: scopedSubagentIdRef.current ?? null },\n { attachments: attachmentsRef.current, model: modelRef.current, openingMessage: userGoal },\n ).then((r) => { if (r) handleResult(r); });\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [readyToStart]);\n\n // Il pannello Agentico è l'UNICA UI per qualunque subagent — se il gate fosse su\n // canContinue, senza un provider AI configurato (o prima che\n // il catalogo modelli si risolva) il task non comparirebbe MAI — il pannello si aprirebbe\n // vuoto, indistinguibile da \"non è successo niente\". Il task si registra quindi subito, a\n // ogni cambiamento rilevante di stato, e mostra il fallback deterministico se l'AI non è\n // disponibile invece di sparire.\n useEffect(() => {\n if (dismissedRef.current) return;\n sidebar.openTask({\n id: ORCHESTRATOR_TASK_ID, title: 'Agentico', icon: 'bot', agent, onApplied: handleResult,\n scopedSubagentId: scopedSubagentIdRef.current,\n // Il carry-over (vedi PendingCarryOver in AgenticoContext.tsx) va prepeso GIÀ QUI,\n // non solo sul SubagentRunner post-handoff\n // più sotto — altrimenti il trascritto precedente sparisce per tutta la durata del\n // turno di raccolta parametri dell'orchestratore stesso (il \"Sto pensando...\" che\n // parte SUBITO dopo l'invio) e riappare solo se/quando arriva un handoff, bug\n // segnalato: \"appena premo invio la chat precedente non c'è più\".\n precedingActivity: carryOverRef.current?.activity,\n onDismiss: () => {\n dismissedRef.current = true;\n if (agent.running) agent.stop();\n sidebar.closeTask(ORCHESTRATOR_TASK_ID);\n },\n });\n // BUG FISSATO: `agent.modelsByProvider`/`agent.hasProviders` mancavano da questo array —\n // il catalogo modelli si popola in modo asincrono (useAIModelCatalog dentro useAgent) e\n // quell'aggiornamento non faceva mai ri-registrare l'`agent` fresco in sidebar.openTask,\n // finché non cambiava anche uno degli altri campi osservati. Il task restava agganciato\n // per sempre allo snapshot di `agent` preso all'ultimo re-render \"rilevante\" precedente —\n // con `modelsByProvider` ancora vuoto se il catalogo non si era risolto in tempo — e la\n // tendina di selezione modello in Agentico spariva per l'intera durata della\n // conversazione, anche dopo che il catalogo (verificato via localStorage `ai.models.*`)\n // aveva correttamente 73 modelli disponibili.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [agent.activity, agent.running, agent.error, agent.canContinue, agent.selectedModel, agent.modelsByProvider, agent.hasProviders, sidebar.openTask]);\n\n useEffect(() => {\n return () => { sidebar.closeTask(ORCHESTRATOR_TASK_ID); };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [sidebar.closeTask]);\n\n // Ricalcolato SOLO quando agent.activity cambia davvero (carryOverRef.current è un ref,\n // stabile per tutta la vita del componente dopo il mount) — uno spread nudo qui creava un\n // array NUOVO a ogni render di questo componente, che SubagentRunner tiene nelle sue\n // dependency dell'effect di registrazione (sidebar.openTask): una reference sempre \"diversa\"\n // faceva ripartire quell'effect a ogni render, che aggiornando il context ri-renderizzava\n // anche questo componente, ricreando lo spread da capo — loop infinito (\"Maximum update\n // depth exceeded\", bug segnalato).\n const combinedPrecedingActivity = useMemo(\n () => [...(carryOverRef.current?.activity ?? []), ...agent.activity],\n [agent.activity],\n );\n\n if (!handoff) return null;\n const manifest = findSubagentManifest(handoff.subagentId);\n if (!manifest) return null;\n return (\n <SubagentRunner\n manifest={manifest} params={handoff.params}\n extraDestinations={extraDestinationsRef.current}\n model={modelRef.current || undefined}\n // Se c'è un carry-over (stesso subagent, tab precedente ancora aperta), riusa il\n // SUO id — il nuovo SubagentRunner aggiorna quella tab in place invece di aprirne\n // una seconda — e prepende la SUA intera conversazione (già comprensiva del suo\n // stesso precedingActivity) prima di quella di questo giro. Senza carry-over,\n // comportamento invariato: id fresco, solo il transcript di QUESTO orchestratore.\n taskId={carryOverRef.current?.taskId}\n precedingActivity={combinedPrecedingActivity}\n />\n );\n}\n","import React, { useEffect, useMemo, useRef, useState } from 'react';\nimport { ActionButton } from '@llmnative/react';\nimport { useAgent } from './useAgent';\nimport { useAgenticoI18n } from '../i18n';\nimport { useAgentico } from './AgenticoContext';\nimport type { SubagentManifest } from '../domain/subagentManifest';\nimport type { ResultDestination } from '../domain/resultDestination';\nimport { ResultDestinationPicker } from './ResultDestinationPicker';\nimport { getSubagentReport, type SubagentSelection } from './reports/subagentReportRegistry';\nimport { GenericResultPreview } from './reports/GenericResultPreview';\nimport { buildLiveAnchorDestination } from './liveAnchorDestination';\n\n/** Percorso \"senza AI\" per un subagent già scoped (un solo target possibile, niente da\n * instradare — vedi SubagentManifest.manualEntry e AgenticoContext.startManualEntry), montato\n * quando l'orchestratore stesso non riesce a partire (AI giù) ma il chiamante sa già a priori\n * quale subagent servirà. Mostra il prompt statico del manifest; sul primo testo inviato\n * dall'utente costruisce i TParams DIRETTAMENTE dal testo grezzo (`manualEntry.fromRawText`) e\n * chiama `agent.run()` con l'AI disabilitata a priori — `buildContext` (sempre deterministico)\n * gira comunque, ma la chiamata AI viene saltata del tutto (vedi useAgent.run, ramo\n * `!enabled`), zero round-trip di rete, zero possibilità di fallire per un provider giù.\n *\n * Il risultato è mostrato in anteprima (report del subagent se registrato, altrimenti\n * `GenericResultPreview`) più `ResultDestinationPicker` — STESSO identico schema di\n * SubagentRunner, mai un apply automatico appena arriva il risultato (bug segnalato: prima\n * consegnava subito a un anchor live o alla prima extraDestination, senza che l'utente vedesse\n * nulla prima). Un componentAnchor live (es. ThemeEditor per theme) è trattato come una\n * destinazione in più tra quelle proposte (vedi liveAnchorDestination.ts), non un canale a\n * parte. */\nexport function ManualEntryRunner({\n manifest, extraDestinations,\n}: {\n manifest: SubagentManifest<any, any, any>;\n extraDestinations?: ResultDestination[];\n}) {\n const agent = useAgent(manifest.definition, { defaultEnabled: false });\n const t = useAgenticoI18n();\n const sidebar = useAgentico();\n const taskId = useRef(`manual-${manifest.id}-${crypto.randomUUID()}`).current;\n const [result, setResult] = useState<unknown>(null);\n const [appliedIds, setAppliedIds] = useState<string[]>([]);\n const reportModule = getSubagentReport(manifest.id);\n const selectionModule = reportModule?.kind === 'selection' ? reportModule : null;\n const [selection, setSelection] = useState<SubagentSelection | null>(null);\n // Ultimi TParams usati con successo — riusati sia per costruire `meta` per\n // ResultDestinationPicker sia da \"Riprendi con AI\" (AgenticoContext.resumeWithAI) come punto\n // di partenza per un vero SubagentRunner, invece di far ridigitare da capo lo stesso testo.\n const [lastParams, setLastParams] = useState<Record<string, unknown> | null>(null);\n const dismissedRef = useRef(false);\n\n const handleSubmit = (rawText: string) => {\n if (!manifest.manualEntry) return;\n let params;\n try {\n params = manifest.manualEntry.fromRawText(rawText);\n } catch (err) {\n // Difensivo — il composer (Chatbot) blocca già l'invio di testo vuoto/whitespace,\n // stessa condizione che fromRawText tipicamente rifiuta; nessun canale per mostrare\n // un errore sincrono qui (niente sessione ancora avviata su cui appoggiarlo).\n console.error('[ManualEntryRunner] manualEntry.fromRawText ha rifiutato il testo:', err);\n return;\n }\n setLastParams(params as Record<string, unknown>);\n setAppliedIds([]);\n void agent.run(params, { openingMessage: rawText }).then((r) => { if (r) setResult(r); });\n };\n\n useEffect(() => {\n if (result && selectionModule) setSelection(selectionModule.defaultSelection(result));\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [result, selectionModule]);\n\n const handleSelectionChange = (key: string, value: boolean | string) => {\n setSelection((prev) => (prev ? { ...prev, [key]: value } : prev));\n };\n\n const handleResultChange = (next: unknown) => setResult(next);\n\n // Memoizzato: applySelection ritorna sempre un oggetto NUOVO (spread) — senza useMemo,\n // ogni render produrrebbe un filteredResult diverso, e finendo nelle dipendenze dell'effect\n // di sidebar.openTask sotto, quell'effect non si stabilizzerebbe mai (loop di re-render\n // infinito, \"Maximum update depth exceeded\" — bug osservato). Stesso identico principio già\n // documentato in SubagentRunner.tsx.\n const filteredResult = useMemo(\n () => (result && selectionModule && selection ? selectionModule.applySelection(result, selection) : result),\n [result, selectionModule, selection],\n );\n\n const handleDestinationApplied = (destinationId: string) => {\n setAppliedIds((prev) => (prev.includes(destinationId) ? prev : [...prev, destinationId]));\n };\n\n // Memoizzati per lo stesso motivo — non sono nelle dipendenze dell'effect sotto (solo\n // `sidebar.componentAnchors`/`lastParams` lo sono), ma restano comunque props di\n // ResultDestinationPicker dentro `panelExtra`: un riferimento nuovo a ogni render vi\n // propagherebbe un re-render inutile a ogni giro, oltre a essere inutilmente fragile se in\n // futuro qualcuno li aggiungesse alle dipendenze.\n const matchingAnchors = useMemo(\n () => sidebar.componentAnchors.filter((a) => a.subagentId === manifest.id && a.onResult),\n [sidebar.componentAnchors, manifest.id],\n );\n const liveAnchorDestination = useMemo(\n () => (lastParams ? buildLiveAnchorDestination(matchingAnchors, lastParams, taskId) : null),\n [matchingAnchors, lastParams, taskId],\n );\n const pickerExtraDestinations = useMemo(\n () => (liveAnchorDestination ? [liveAnchorDestination, ...(extraDestinations ?? [])] : extraDestinations),\n [liveAnchorDestination, extraDestinations],\n );\n\n useEffect(() => {\n if (dismissedRef.current) return;\n sidebar.openTask({\n id: taskId, title: manifest.label, icon: manifest.icon, agent,\n onApplied: (r: unknown) => setResult(r),\n manualEntry: manifest.manualEntry ? { prompt: manifest.manualEntry.prompt, onSubmit: handleSubmit } : undefined,\n onDismiss: () => {\n dismissedRef.current = true;\n if (agent.running) agent.stop();\n sidebar.closeTask(taskId);\n },\n panelExtra: result ? (\n <div className=\"space-y-2\">\n {selectionModule && selection ? (\n <selectionModule.Report result={result} selection={selection} onChange={handleSelectionChange} />\n ) : reportModule?.kind === 'editable' ? (\n <reportModule.Report result={result} onResultChange={handleResultChange} />\n ) : (\n <GenericResultPreview result={filteredResult} />\n )}\n <ResultDestinationPicker\n manifest={manifest} result={filteredResult} meta={lastParams ?? {}} taskId={taskId}\n initiallyApplied={appliedIds} onApplied={handleDestinationApplied}\n extraDestinations={pickerExtraDestinations}\n />\n {/* Il \"contrario\" di startManualEntry — niente da rifare, solo il canale che\n cambia: gli stessi `lastParams` avviano un vero SubagentRunner (AI\n abilitata) al posto di questo task. Nascosto se non esiste nemmeno un\n provider configurato: riprovare con AI fallirebbe di nuovo allo stesso\n modo, senza aggiungere nulla. */}\n {agent.hasProviders && (\n <ActionButton\n label={t.resumeWithAi}\n variant=\"outline-secondary\"\n className=\"text-xs\"\n onClick={() => { if (lastParams) sidebar.resumeWithAI(manifest.id, lastParams, extraDestinations); }}\n />\n )}\n </div>\n ) : undefined,\n });\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [agent.activity, agent.running, agent.error, agent.hasProviders, sidebar.componentAnchors, result, filteredResult, selection, appliedIds, lastParams]);\n\n useEffect(() => {\n return () => { sidebar.closeTask(taskId); };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n return null;\n}\n","import React, { useEffect, useMemo, useRef, useState } from 'react';\nimport { ActionButton, Badge, Chatbot, Dropdown, DropdownItem, Icon, interpolate, useI18n } from '@llmnative/react';\nimport type { AIModelDescriptor, ChatbotModelOption } from '@llmnative/react';\nimport { useAgentico } from './AgenticoContext';\nimport { AgentActivityLog, type AgentActivityLogQuestions } from './AgentActivityLog';\nimport { AgenticoToggle } from './AgenticoToggle';\nimport { OrchestratorRunner } from './OrchestratorRunner';\nimport { SubagentRunner } from './SubagentRunner';\nimport { ManualEntryRunner } from './ManualEntryRunner';\nimport { ORCHESTRATOR_TASK_ID } from './AgenticoContext';\nimport type { AgenticoTask } from './AgenticoContext';\nimport { useAgenticoI18n } from '../i18n';\nimport { useAIModelCatalog } from './useAIModelCatalog';\nimport type { SubagentUsageCard } from '../domain/subagentUsageCard';\nimport { findSubagentManifest } from '../domain/subagentRegistry';\nimport { entityChatTaskId, listEntityChats, removeEntityChat } from '../domain/entityChatStore';\n\n/** Larghezza fissa del pannello — passata anche come `rightInset` alle Modal che devono\n * lasciargli spazio invece di sovrapporlo (vedi Modal.rightInset nel framework). */\nexport const AGENTICO_WIDTH = 420;\n\n/** `modelsByProvider` di useAgent è raggruppato per provider — Chatbot vuole una lista\n * piatta `{label, value, pricing, group}`, stesso appiattimento che il framework fa internamente\n * in usePromptCapabilities per il proprio model picker (Prompt.tsx). `group` = nome del provider\n * (header sticky nel picker), quindi `label` è solo il modello. `pricing: null` = prezzo\n * cercato ma non trovato: il picker lo segnala sulla riga invece di tacere. */\nfunction flattenModelOptions(modelsByProvider: Record<string, AIModelDescriptor[]>): ChatbotModelOption[] {\n return Object.values(modelsByProvider).flat().map((m) => ({\n label: m.model,\n value: m.id,\n pricing: m.pricing ?? null,\n group: m.providerLabel ?? m.provider,\n }));\n}\n\n/** Composer del follow-up — monta `Chatbot` (CR-071, `@llmnative/react`): stesso widget di\n * `Prompt` in modalità RUN, ma qui il testo digitato è esattamente quello inviato a ogni\n * turno (nessun template autorato/campo risultato separato — quella semantica resta\n * specifica di Prompt). Chatbot si limita a raccogliere testo/allegati/modello e a\n * restituirli via `onSubmit`; la \"politica\" di cosa farne — chiamare `agent.sendMessage`,\n * accodare il risultato alla history — resta qui, mai responsabilità di Chatbot. */\nfunction AgenticoComposer({ task }: { task: AgenticoTask }) {\n const t = useAgenticoI18n();\n const [text, setText] = useState('');\n const { agent, onApplied, manualEntry } = task;\n const running = agent.running;\n\n const handleSubmit = (payload: { text: string; files: File[]; model: string }) => {\n setText('');\n // Percorso \"senza AI\" (vedi ManualEntryRunner/SubagentManifest.manualEntry) — il testo\n // costruisce i parametri DIRETTAMENTE, mai una chiamata al modello: niente\n // agent.sendMessage qui, `manualEntry.onSubmit` gestisce tutto (run con AI disabilitata,\n // auto-apply, log). Allegati/modello ignorati in questo ramo — non c'è alcuna chiamata\n // AI a cui passarli.\n if (manualEntry) { manualEntry.onSubmit(payload.text); return; }\n void agent.sendMessage(payload.text, { attachments: payload.files, model: payload.model }).then((result) => {\n if (result) onApplied(result);\n });\n };\n\n // BUG FISSATO: senza questa condizione, `selectedModel === ''` (nessun default automatico\n // più, vedi useAIModelCatalog.ts) non impediva comunque l'invio — il messaggio partiva con\n // `model: ''`, e `AIProvider.complete()` (framework, shared.ts:242, `request.model ||\n // this.defaultModel`) rimetteva dentro il default hardcoded silenziosamente, vanificando la\n // scelta esplicita richiesta all'utente. Bloccare qui l'invio finché un provider esiste ma\n // nessun modello è ancora selezionato è l'unico modo per far sì che quel fallback non scatti\n // mai più nei flussi Agentico. Nessuna chiamata AI in un task `manualEntry` (vedi handleSubmit\n // sopra): la scelta modello non ha senso qui, non deve mai bloccare l'invio.\n // A remote task (CR-003) answers with its own agent: no attachments, and its models are the\n // ones its executor offers (none = no picker), never required.\n const noModel = Boolean(manualEntry) || (agent.remote && agent.remoteModels.length === 0);\n const modelRequired = !noModel && !agent.remote && agent.hasProviders && !agent.selectedModel;\n const models = noModel ? [] : agent.remote ? agent.remoteModels : flattenModelOptions(agent.modelsByProvider);\n\n return (\n <Chatbot\n value={text}\n onChange={setText}\n placeholder={manualEntry?.prompt ?? t.messagePlaceholder}\n running={running}\n onStop={agent.stop}\n disabled={!text.trim() || modelRequired}\n onSubmit={handleSubmit}\n attachments={!manualEntry && !agent.remote}\n models={models}\n selectedModel={noModel ? '' : agent.selectedModel}\n actions={task.composerActions}\n onModelChange={agent.setSelectedModel}\n modelPlaceholder={t.selectModelPlaceholder}\n minHeight={44}\n maxHeight={220}\n />\n );\n}\n\n/** Il composer quando non c'è ancora un task attivo — il testo AVVIA una conversazione\n * (useAgentico().startOrchestrator) invece di proseguirla, quindi non c'è ancora un `agent`\n * da cui leggere il catalogo modelli: usa useAIModelCatalog direttamente (stessa fonte dati\n * di useAgent, vedi quel file) cosi la scelta del modello è possibile ANCHE prima che\n * l'orchestratore parta, non solo nei turni di follow-up. */\nfunction AgenticoStarterComposer() {\n const { startOrchestrator } = useAgentico();\n const t = useAgenticoI18n();\n const [text, setText] = useState('');\n const { hasProviders, modelsByProvider, selectedModel, setSelectedModel } = useAIModelCatalog();\n\n const handleSubmit = (payload: { text: string; files: File[]; model: string }) => {\n if (!payload.text.trim()) return;\n startOrchestrator(payload.text.trim(), {\n attachments: payload.files.length > 0 ? payload.files : undefined,\n model: payload.model || undefined,\n });\n setText('');\n };\n\n // Stesso motivo di AgenticoComposer sopra: senza questo, l'avvio partirebbe con\n // `model: undefined` e farebbe scattare comunque il default hardcoded del provider dentro\n // AIProvider.complete().\n const modelRequired = hasProviders && !selectedModel;\n\n return (\n <Chatbot\n value={text}\n onChange={setText}\n placeholder={t.orchestratorStarterPlaceholder}\n disabled={!text.trim() || modelRequired}\n onSubmit={handleSubmit}\n attachments\n models={flattenModelOptions(modelsByProvider)}\n selectedModel={selectedModel}\n onModelChange={setSelectedModel}\n modelPlaceholder={t.selectModelPlaceholder}\n minHeight={44}\n maxHeight={220}\n />\n );\n}\n\n/** \"2 h ago\": how long ago, in the language of the app (CR-003). */\nfunction relativeTime(at: number, locale: string, now = Date.now()): string {\n const seconds = Math.round((at - now) / 1000);\n const format = new Intl.RelativeTimeFormat(locale, { numeric: 'auto', style: 'short' });\n for (const [unit, size] of [['day', 86_400], ['hour', 3_600], ['minute', 60]] as const) {\n if (Math.abs(seconds) >= size) return format.format(Math.round(seconds / size), unit);\n }\n return format.format(0, 'minute');\n}\n\n/** Card \"pronto per una nuova istanza di X\" — render deterministico della stessa\n * SubagentUsageCard passata al modello via pageContext (vedi useAgenticoPageContext, usata da\n * pagine come redirects/ToolPage.tsx), qui come lista scansionabile di campi invece che come\n * frase di prosa: un\n * badge required/optional per campo, non un paragrafo da cui l'utente (o un LLM) deve dedurre\n * cosa serve davvero. Sostituisce il vecchio `<p>{pageContext}</p>` SOLO quando c'è una card —\n * altrove resta il testo semplice (vedi il chiamante). */\nfunction SubagentUsageCardView({ card }: { card: SubagentUsageCard }) {\n const t = useAgenticoI18n();\n // humanSummaryOnly (vedi SubagentCommand.humanSummaryOnly): un comando i cui campi tecnici\n // non rispecchiano come un umano pensa l'input (es. redirects: 4 campi separati ma un umano\n // ragiona in termini di \"cosa mando\", con una fonte sola spesso sufficiente per più campi)\n // mostra SOLO il racconto in prosa — l'elenco a badge richiesto/opzionale e l'esempio in\n // sintassi CLI implicherebbero erroneamente un modulo da compilare campo per campo.\n if (card.humanSummaryOnly) {\n return (\n <div className=\"space-y-2 rounded-lg border border-border/60 p-3\">\n <p className=\"text-xs font-medium text-foreground\">\"{card.label}\"</p>\n <p className=\"text-xs text-muted-foreground\">{card.humanSummary}</p>\n </div>\n );\n }\n\n return (\n <div className=\"space-y-2 rounded-lg border border-border/60 p-3\">\n <p className=\"text-xs font-medium text-foreground\">{interpolate(t.newInstance, { label: card.label })}</p>\n <p className=\"text-xs text-muted-foreground\">{card.humanSummary}</p>\n {card.fields.length > 0 && (\n <ul className=\"space-y-1\">\n {card.fields.map((f) => (\n <li key={f.key} className=\"flex items-start gap-1.5 text-xs\">\n <Badge variant={f.required ? 'primary' : 'secondary'} className=\"mt-px shrink-0\">\n {f.required ? 'richiesto' : 'opzionale'}\n </Badge>\n <span className=\"min-w-0\">\n <span className=\"font-mono text-foreground\">{f.key}</span>\n {f.humanDescription ? <span className=\"text-muted-foreground\"> — {f.humanDescription}</span> : null}\n </span>\n </li>\n ))}\n </ul>\n )}\n {card.example && (\n <p className=\"text-xs text-muted-foreground\">Esempio: <span className=\"font-mono\">{card.example}</span></p>\n )}\n </div>\n );\n}\n\n/** Messaggio semplice per un anchor componente↔subagent (SubagentComponentContext.userMessage) —\n * sostituisce la card a campi per QUESTO meccanismo: niente badge required/opzionale, il\n * componente vivo si popola da solo quando arriva un risultato (vedi SubagentRunner), non c'è un\n * modulo di parametri da compilare a vista. Più righe se sono ancorati più subagent\n * contemporaneamente (vedi AgenticoContext.anchoredUserMessages). */\nfunction AnchoredUserMessageView({ messages }: { messages: string[] }) {\n return (\n <div className=\"space-y-2 rounded-lg border border-border/60 p-3\">\n {messages.map((m, i) => (\n <p key={i} className=\"text-xs text-muted-foreground\">{m}</p>\n ))}\n </div>\n );\n}\n\n/** Dropdown dello storico chat — TUTTE le chat salvate del tenant, mescolate across subagent\n * (theme, redirects, html-to-liquid: la label del subagent nella riga distingue da quale\n * viene), più recenti prima (vedi entityChatStore.listEntityChats). Un semplice filtro\n * testuale locale, non una ricerca full-text: basta per il volume atteso (una entry per\n * entità, non per esecuzione).\n *\n * `Dropdown`/`DropdownItem` del framework, non un overlay scritto a mano — posizionamento,\n * chiusura al click fuori/Escape, portal: tutto già risolto lì, riscriverlo qui sarebbe stato\n * puro doppione (bug corretto: la prima versione di questo pannello aveva un backdrop e un\n * `<button>` grezzi invece di riusare questi componenti). */\nfunction AgenticoHistoryDropdown({\n scopeId, currentTaskId, open, onOpenChange, onSelect, onNewChat, onArchivedCurrent,\n}: {\n scopeId: string;\n /** The task open in the panel: its chat is marked in the list (CR-003). */\n currentTaskId: string | null;\n open: boolean;\n onOpenChange: (open: boolean) => void;\n onSelect: (subagentId: string, entityId: string, snapshot: ReturnType<typeof listEntityChats>[number]) => void;\n onNewChat: () => void;\n /** The chat open in the panel was archived: the panel lets it go. */\n onArchivedCurrent: () => void;\n}) {\n const t = useAgenticoI18n();\n const { locale } = useI18n();\n const [search, setSearch] = useState('');\n // Bumped when a chat is archived, to read the list again.\n const [version, setVersion] = useState(0);\n const entries = useMemo(() => (open ? listEntityChats(scopeId) : []), [scopeId, open, version]);\n\n /** Archives a chat (CR-003): the host first (e.g. it closes a remote session, with its own\n * question if it has one), then off the history. A refusal of the host keeps the chat. */\n const archive = async (entry: ReturnType<typeof listEntityChats>[number]) => {\n const manifest = findSubagentManifest(entry.subagentId);\n const question = await manifest?.confirmArchive?.(entry.entityId, entry.params);\n if (question && !window.confirm(question)) return;\n try {\n await manifest?.onArchive?.(entry.entityId, entry.params);\n } catch {\n return;\n }\n removeEntityChat(scopeId, entry.subagentId, entry.entityId);\n if (currentTaskId === entityChatTaskId(entry.subagentId, entry.entityId)) onArchivedCurrent();\n setVersion((value) => value + 1);\n };\n const filtered = useMemo(() => {\n const q = search.trim().toLowerCase();\n if (!q) return entries;\n return entries.filter((e) => {\n const label = findSubagentManifest(e.subagentId)?.label ?? e.subagentId;\n return e.title.toLowerCase().includes(q) || label.toLowerCase().includes(q);\n });\n }, [entries, search]);\n\n return (\n <Dropdown\n trigger={{ icon: 'history', title: t.chatHistory }}\n // Il default del tema per un trigger Dropdown è pensato per un select-like button\n // (bordo, sfondo) — questo è un'icona isolata nell'header, stesso trattamento di\n // AgenticoToggle qui accanto: nessun bordo, solo hover.\n triggerClassName=\"!h-9 !w-9 !p-0 rounded-md bg-transparent text-muted-foreground hover:bg-accent hover:text-foreground\"\n open={open}\n onOpenChange={onOpenChange}\n position=\"end\"\n strategy=\"absolute\"\n menuClassName=\"w-[380px] max-h-[70vh] overflow-y-auto\"\n header={\n <div className=\"flex items-center gap-2 px-1 pb-1.5\">\n <Icon name=\"search\" size={14} className=\"shrink-0 text-muted-foreground\" />\n <input\n autoFocus\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n placeholder={t.searchChats}\n className=\"min-w-0 flex-1 bg-transparent text-sm outline-none placeholder:text-muted-foreground\"\n />\n <ActionButton icon=\"square-pen\" ariaLabel={t.newChat} title={t.newChat} variant=\"link\" className=\"shrink-0\" onClick={onNewChat} />\n </div>\n }\n >\n {filtered.length === 0 ? (\n <p className=\"px-2 py-3 text-center text-xs text-muted-foreground\">\n {entries.length === 0 ? t.noSavedChats : t.noResults}\n </p>\n ) : (\n filtered.map((entry) => {\n const manifest = findSubagentManifest(entry.subagentId);\n const current = currentTaskId === entityChatTaskId(entry.subagentId, entry.entityId);\n return (\n <DropdownItem\n key={`${entry.subagentId}:${entry.entityId}`}\n // The chat open in the panel is marked: the person sees where they are.\n icon={current ? 'check' : manifest?.icon ?? 'bot'}\n className={current ? 'bg-accent font-medium' : undefined}\n actions={[{ icon: 'archive', label: t.archiveChat, onClick: () => void archive(entry) }]}\n onClick={() => onSelect(entry.subagentId, entry.entityId, entry)}\n >\n <span className=\"flex min-w-0 flex-1 items-center justify-between gap-2\">\n <span className=\"min-w-0 truncate\">\n {/* `entry.title` è assente su una chat salvata PRIMA che questo campo esistesse\n (localStorage non si aggiorna da solo con lo schema — bug segnalato: la riga\n mostrava \"Theme extractor ·\" senza nulla dopo) — ripiega sull'id dell'entità\n invece di un vuoto silenzioso. */}\n <span className=\"text-muted-foreground\">{manifest?.label ?? entry.subagentId}</span> · {entry.title || entry.entityId}\n </span>\n <span className=\"shrink-0 text-xs text-muted-foreground\" title={new Date(entry.savedAt).toLocaleString(locale)}>{relativeTime(entry.savedAt, locale)}</span>\n </span>\n </DropdownItem>\n );\n })\n )}\n </Dropdown>\n );\n}\n\n/** Agentico — il pannello globale, persistente e non-overlay per le sessioni agente — affianca\n * il CMS (che si stringe) invece di coprirlo, cosi la modale che ha avviato l'agente resta\n * aperta e utilizzabile accanto. Header: titolo dell'UNICO task attivo (mai una tab bar — \"una\n * chat unica\", decisione esplicita: avviarne uno nuovo sostituisce quello in corso, vedi\n * AgenticoContext.openTask) — NESSUNA ×: si apre/chiude solo con AgenticoToggle (nell'Header del\n * CMS, o come `closeSlot` di una Modal right), la stessa icona ovunque, mai una propria qui.\n * Body: lo stesso log di attività del pannello embedded (AgentActivityLog), o un placeholder\n * neutro se non c'è ancora nessun task — il pannello può restare aperto vuoto, non sparisce da\n * solo quando il task finisce. Footer: il composer di follow-up (vedi AgenticoComposer sopra),\n * nascosto quando non c'è un task attivo.\n *\n * Solo la sessione aperta in questa sessione browser (registry in memoria via AgenticoContext)\n * — nessuna persistenza cross-sessione (Fase 3, deferita).\n *\n * Nota sul naming: \"Agentico\" è il nome dell'orchestratore — questo pannello, il suo contesto\n * (AgenticoContext), il suo toggle (AgenticoToggle) — non degli agenti verticali che esegue\n * (theme-import, html-to-liquid, ...), che restano nel proprio dominio senza questo\n * prefisso: sono contenuto che Agentico orchestra, non parte della sua identità. */\nexport function Agentico() {\n const {\n task, isOpen, pendingGoal, orchestratorRunId, pageContext, pageContextCard,\n anchoredUserMessages, pendingResume, resumeRunId, dismissForeignTask, resumeEntityChat,\n pendingManualEntry, manualEntryRunId, startManualEntry, pendingExtraDestinations,\n pendingResumeWithAI, resumeWithAIRunId, pendingStart, startRunId,\n scope,\n } = useAgentico();\n\n const t = useAgenticoI18n();\n const bodyRef = useRef<HTMLDivElement | null>(null);\n const [historyOpen, setHistoryOpen] = useState(false);\n\n // \"Nuova chat\" — congeda il task corrente (se c'è) e torna al composer \"starter\" del\n // contesto attuale, che già riflette dove ci si trova (vedi anchoredUserMessages/\n // pageContext) — nessun significato fisso, cambia da solo in base a dove si è. Un avviso\n // SOLO se la conversazione lasciata indietro non è mai stata salvata (vedi\n // AgenticoTask.savedToDisk): senza salvataggio andrebbe persa per sempre, nessun \"torna\n // indietro\" per scelta esplicita — una già salvata resta comunque recuperabile dallo\n // storico, quindi scartarla senza chiedere è sicuro.\n const handleNewChat = () => {\n if (task && !task.agent.running && !task.savedToDisk) {\n if (!window.confirm(t.unsavedConversationConfirm)) return;\n }\n dismissForeignTask();\n setHistoryOpen(false);\n };\n\n // Il transcript dell'orchestratore (se presente) viene PRIMA del log del subagent — vedi\n // AgenticoTask.precedingActivity: una chat unica e continua, mai due task da switchare.\n const displayedActivity = task ? [...(task.precedingActivity ?? []), ...task.agent.activity] : [];\n const activityCount = displayedActivity.length;\n // Punto della timeline in cui interlacciare `panelExtra` (vedi AgenticoTask.panelExtraAnchorIndex)\n // — assente = comportamento invariato, sempre in fondo. Clamp difensivo: un indice ricevuto\n // da un giro precedente potrebbe restare più corto/lungo del log corrente per un frame\n // (es. subito dopo che `stop()`/un nuovo run tronca `agent.activity`).\n const panelExtraAnchorIndex = Math.max(0, Math.min(task?.panelExtraAnchorIndex ?? activityCount, activityCount));\n const activityBeforePanelExtra = displayedActivity.slice(0, panelExtraAnchorIndex);\n const activityAfterPanelExtra = displayedActivity.slice(panelExtraAnchorIndex);\n const hasError = !!(task && !task.agent.running && task.agent.error);\n // Questions of the agent (CR-004): answerable in the entries of the live conversation only (not\n // in the transcript that precedes it); the result of the turn that carries them is applied\n // like the one of a message.\n const liveEntryIds = new Set(task?.agent.activity.map((entry) => entry.id) ?? []);\n const questions: AgentActivityLogQuestions | undefined = task ? {\n canAnswer: (entryId) => liveEntryIds.has(entryId),\n onAnswer: (entryId, partId, answers) => task.agent.answerQuestions(entryId, partId, answers).then((result) => {\n if (result) task.onApplied(result);\n }),\n } : undefined;\n // Il fallback dell'orchestratore (agenticoOrchestratorAgent.ts) non è mai un vero handoff —\n // solo un messaggio da leggere, non può indovinare l'intento senza AI. Quando invece il\n // target è già noto (scopedSubagentId esplicito, vedi startOrchestrator/TemplateImportButton)\n // e quel subagent dichiara un manualEntry, \"Continua senza AI\" può fare MEGLIO che mostrare\n // un messaggio generico: salta dritto al percorso manuale (vedi ManualEntryRunner) invece di\n // chiamare runFallback() sull'agent dell'orchestratore stesso.\n const scopedManifestForError = task?.id === ORCHESTRATOR_TASK_ID && task.scopedSubagentId\n ? findSubagentManifest(task.scopedSubagentId)\n : undefined;\n const manualEntryManifestForError = scopedManifestForError?.manualEntry ? scopedManifestForError : null;\n // Se l'utente è vicino al fondo (entro una tolleranza) o no — aggiornato da un listener di\n // scroll vero, non ricalcolato dentro l'effect sotto: lì il DOM ha GIÀ la nuova voce quando\n // gira, quindi \"distanza dal fondo\" rifletterebbe la nuova altezza invece della posizione in\n // cui si trovava l'utente PRIMA che arrivasse (bug segnalato: durante \"sto pensando...\" non\n // si riusciva a scrollare in alto per rileggere una chat ripresa — l'auto-scroll ririportava\n // giù a ogni singola voce d'attività, decine al secondo mentre l'agente lavora).\n const isNearBottomRef = useRef(true);\n useEffect(() => {\n const el = bodyRef.current;\n if (!el) return;\n const handleScroll = () => {\n isNearBottomRef.current = el.scrollHeight - el.scrollTop - el.clientHeight < 80;\n };\n el.addEventListener('scroll', handleScroll);\n return () => el.removeEventListener('scroll', handleScroll);\n }, [task?.id]);\n\n // Nuovo task selezionato/ripreso — sempre in fondo, a prescindere da dove si era scrollati\n // nel task PRECEDENTE (se ce n'era uno).\n useEffect(() => {\n const el = bodyRef.current;\n if (!el) return;\n isNearBottomRef.current = true;\n el.scrollTop = el.scrollHeight;\n }, [task?.id]);\n\n // Scende in fondo ogni volta che compare una nuova voce (nuovo messaggio utente, nuovo\n // turno \"sto pensando\", nuova tool call) o l'errore finale — cosi l'ultimo messaggio è\n // sempre a vista senza dover scrollare a mano, come una normale chat. SOLO se l'utente era\n // già vicino al fondo: chi ha scrollato in alto per rileggere (es. la cronologia ripresa)\n // non viene più riportato giù a forza mentre l'agente genera ancora.\n // A reply that grows in place (a streamed turn, CR-003) does not add an entry: its length\n // counts too, so the panel follows the text as it is written.\n const lastDetailLength = displayedActivity.at(-1)?.detail?.length ?? 0;\n useEffect(() => {\n const el = bodyRef.current;\n if (!el || !isNearBottomRef.current) return;\n el.scrollTop = el.scrollHeight;\n }, [activityCount, hasError, lastDetailLength]);\n\n // The content also grows after the text arrived (Markdown laid out, a block opened): whenever\n // its height changes, the panel follows it if the person was at the bottom (CR-003).\n const contentRef = useRef<HTMLDivElement | null>(null);\n useEffect(() => {\n const el = bodyRef.current;\n const content = contentRef.current;\n if (!el || !content || typeof ResizeObserver === 'undefined') return;\n const observer = new ResizeObserver(() => {\n if (isNearBottomRef.current) el.scrollTop = el.scrollHeight;\n });\n observer.observe(content);\n return () => observer.disconnect();\n }, [task?.id, isOpen]);\n\n // Montato SEMPRE quando c'è un goal in sospeso, indipendentemente da `isOpen` — se lo\n // gatassimo dietro `!isOpen return null` sotto, chiudere il pannello smonterebbe\n // OrchestratorRunner (e a cascata l'eventuale SubagentRunner), perdendo il riferimento vivo\n // all'agent in corso — stesso motivo per cui AgenticoContext (vedi commento su\n // AgenticoTask.agent) richiede che il componente proprietario resti montato.\n // `key={orchestratorRunId}` forza React a smontare/rimontare un'istanza FRESCA a ogni nuova\n // conversazione avviata per davvero (vedi AgenticoContext) — senza, un secondo\n // startOrchestrator() riuserebbe l'istanza precedente (il cui `handoff` interno è già\n // impostato), ignorando silenziosamente il nuovo goal.\n const orchestrator = pendingGoal ? <OrchestratorRunner key={orchestratorRunId} userGoal={pendingGoal} /> : null;\n\n // Stesso principio di `orchestrator` sopra — montato SEMPRE quando c'è un resume in\n // sospeso, indipendentemente da `isOpen`, per non perdere il riferimento vivo all'agent non\n // appena il pannello si richiude. `taskId` stabile per entità (non un uuid fresco): riaprire\n // il resume della STESSA entità aggiorna la stessa tab invece di duplicarla.\n const resumeManifest = pendingResume ? findSubagentManifest(pendingResume.subagentId) : undefined;\n const resumeRunner = pendingResume && resumeManifest ? (\n <SubagentRunner\n key={resumeRunId}\n manifest={resumeManifest}\n params={pendingResume.snapshot.params}\n taskId={entityChatTaskId(pendingResume.subagentId, pendingResume.entityId)}\n resumeSeed={{ history: pendingResume.snapshot.history, activity: pendingResume.snapshot.activity }}\n />\n ) : null;\n\n // Stesso principio di `orchestrator`/`resumeRunner` sopra — percorso \"senza AI\" per un\n // subagent già noto (vedi AgenticoContext.startManualEntry/ManualEntryRunner), montato SEMPRE\n // indipendentemente da `isOpen`.\n const manualEntryManifest = pendingManualEntry ? findSubagentManifest(pendingManualEntry.subagentId) : undefined;\n const manualEntryRunner = pendingManualEntry && manualEntryManifest ? (\n <ManualEntryRunner\n key={manualEntryRunId}\n manifest={manualEntryManifest}\n extraDestinations={pendingManualEntry.extraDestinations}\n />\n ) : null;\n\n // \"Riprendi con AI\" (vedi AgenticoContext.resumeWithAI) — il contrario di manualEntryRunner:\n // un vero SubagentRunner (AI abilitata di default), avviato con gli ultimi parametri usati\n // nel percorso senza AI. Stesso identico trattamento \"montato sempre, indipendentemente da\n // isOpen\" di tutti gli altri runner sopra.\n const resumeWithAIManifest = pendingResumeWithAI ? findSubagentManifest(pendingResumeWithAI.subagentId) : undefined;\n const resumeWithAIRunner = pendingResumeWithAI && resumeWithAIManifest ? (\n <SubagentRunner\n key={resumeWithAIRunId}\n manifest={resumeWithAIManifest}\n params={pendingResumeWithAI.params}\n extraDestinations={pendingResumeWithAI.extraDestinations}\n />\n ) : null;\n\n // A subagent started by a page (AgenticoContext.startSubagent, CR-003) — mounted always, as\n // every runner above.\n const startManifest = pendingStart ? findSubagentManifest(pendingStart.subagentId) : undefined;\n const startRunner = pendingStart && startManifest ? (\n <SubagentRunner\n key={startRunId}\n manifest={startManifest}\n params={pendingStart.params}\n taskId={pendingStart.taskId}\n openingMessage={pendingStart.openingMessage}\n />\n ) : null;\n\n if (!isOpen) return <>{orchestrator}{resumeRunner}{manualEntryRunner}{resumeWithAIRunner}{startRunner}</>;\n\n return (\n <>\n {orchestrator}\n {resumeRunner}\n {manualEntryRunner}\n {resumeWithAIRunner}\n {startRunner}\n <div\n style={{ width: AGENTICO_WIDTH }}\n className=\"relative flex h-full shrink-0 flex-col border-l border-border bg-background\"\n >\n {/* Stessi `px-4 py-3` + `border-b/60` dell'header di Modal.tsx (framework) — la\n modale di import tema è affiancata a questo pannello, deve avere\n esattamente la stessa altezza di testata, non una vicina. L'icona robot è ORA\n sempre presente qui (non più solo nell'Header generale/come closeSlot delle\n modali) — il pannello \"sembra\" un sidebar/modale a sé, quindi deve avere la sua\n stessa affordance di apertura/chiusura sul proprio header, indipendentemente da\n cosa gli sta affianco (decisione esplicita, corregge l'asimmetria precedente). */}\n <div className=\"flex min-h-[60px] items-center gap-1 border-b border-border/60 px-4 py-3\">\n {/* `leading-tight` (1.25), non `leading-none` (1): line-height 1 ritagliava il\n discendente della \"g\" di \"Agentico\" su alcuni font — l'altezza della riga è\n comunque garantita da `items-center` sul flex padre, non serve forzarla a 1.\n Un solo task alla volta in tutta l'app (mai una tab bar, decisione esplicita:\n \"una chat unica\") — il titolo mostra semplicemente cosa sta facendo ora\n l'unica conversazione viva, o \"Agentico\" quando non ce n'è nessuna. */}\n <span className=\"min-w-0 flex-1 truncate text-lg font-semibold leading-tight\">\n {task ? task.title : 'Agentico'}\n </span>\n {/* Storico — TUTTE le chat salvate dello scope (vedi AgenticoHistoryDropdown),\n mescolate across subagent, più recenti prima. Nessuna dipendenza da\n `isOpen`/task: uno stato locale a sé, sempre disponibile col pannello aperto. */}\n {scope && (\n <AgenticoHistoryDropdown\n scopeId={scope.id}\n currentTaskId={task?.id ?? null}\n open={historyOpen}\n onOpenChange={setHistoryOpen}\n onNewChat={handleNewChat}\n onSelect={(subagentId, entityId, snapshot) => resumeEntityChat(subagentId, entityId, snapshot)}\n onArchivedCurrent={dismissForeignTask}\n />\n )}\n <AgenticoToggle />\n </div>\n\n {/* Niente `pt-*` qui — un padding-top sul contenitore di scroll resterebbe SEMPRE\n visibile sopra un elemento sticky `top-0` (lo spazio di offset per lo sticky è\n misurato dentro il padding-box di questo contenitore), lasciando lo spazietto tra\n header e bolla utente segnalato dall'utente. Lo spazio in alto si ottiene invece\n dentro il contenuto stesso (vedi il `pt-2` di AgentActivityLog), che scorre via\n normalmente invece di restare incollato all'header. */}\n <div ref={bodyRef} className=\"flex-1 overflow-y-auto px-3 pb-2\">\n {/* `pt-2` qui, non sul contenitore di scroll sopra — questo div scorre via\n normalmente coi contenuti, cosi la bolla sticky dell'utente (dentro\n AgentActivityLog) può incollarsi davvero a filo con l'header una volta\n superato, invece di lasciare sempre visibile lo spazio di questo padding. */}\n <div ref={contentRef} className=\"pt-2\">\n {task ? (\n <>\n {/* Prompt statico del percorso \"senza AI\" (vedi\n SubagentManifest.manualEntry) — mostrato SOLO finché non c'è\n ancora attività: una volta inviato il primo testo, la bolla utente\n + il risultato raccontano da soli cosa sta succedendo, questo testo\n diventerebbe solo rumore duplicato. */}\n {task.manualEntry && displayedActivity.length === 0 && (\n <p className=\"text-xs text-muted-foreground\">{task.manualEntry.prompt}</p>\n )}\n <AgentActivityLog activity={activityBeforePanelExtra} questions={questions} />\n {task.panelExtra && <div className=\"mt-3\">{task.panelExtra}</div>}\n {/* Conversazione successiva a `panelExtra` (retry, follow-up) — un\n secondo <ul> (AgentActivityLog ne apre uno suo), non un problema:\n lo sticky delle bolle utente è per-<li> rispetto allo scroll\n container di questo pannello, non al singolo <ul> che le contiene. */}\n {activityAfterPanelExtra.length > 0 && <AgentActivityLog activity={activityAfterPanelExtra} questions={questions} />}\n {!task.agent.running && task.agent.error && (\n <div className=\"mt-2 flex items-center gap-2\">\n <p className=\"flex-1 text-xs text-destructive\">{task.agent.error}</p>\n {/* Nessun \"retry dell'ultimo turno\" nativo in useAgent — un\n nuovo messaggio \"Riprova.\" nella STESSA conversazione\n (history/tool-result già raccolti restano disponibili al\n modello) è l'approssimazione più semplice, stesso\n percorso di chi ridigita a mano in chat. */}\n <ActionButton\n label={t.retry}\n variant=\"secondary\"\n className=\"shrink-0\"\n onClick={() => { void task.agent.sendMessage(t.retryMessage).then((r) => { if (r) task.onApplied(r); }); }}\n />\n {/* Ogni subagent ha un fallback deterministico a costo zero\n (vedi AgentDefinition.fallback) — quando la chiamata al\n modello fallisce (rete/provider giù, non solo AI\n disabilitata) l'utente non deve restare bloccato dietro\n \"Riprova\": può proseguire subito col risultato\n deterministico, generico per costruzione a QUALUNQUE\n subagent (nessuna label/logica specifica qui). */}\n {/* A remote agent has no deterministic result to fall back on. */}\n {!task.agent.remote && <ActionButton\n label={t.continueWithoutAi}\n variant=\"secondary\"\n className=\"shrink-0\"\n onClick={() => {\n if (manualEntryManifestForError) {\n startManualEntry(manualEntryManifestForError.id, pendingExtraDestinations);\n return;\n }\n const r = task.agent.runFallback();\n if (r) task.onApplied(r);\n }}\n />}\n </div>\n )}\n </>\n ) : anchoredUserMessages.length > 0 ? (\n // Uno o più componenti vivi sono ancorati a un subagent (vedi\n // useSubagentComponentContext) — un semplice messaggio per l'utente, non\n // un modulo di campi: il componente si popola da solo quando arriva un\n // risultato, non c'è nulla da compilare a vista qui.\n <AnchoredUserMessageView messages={anchoredUserMessages} />\n ) : pageContextCard ? (\n // Card strutturata (vedi SubagentUsageCardView sopra) quando il pageContext\n // registrato riguarda un singolo subagent-comando (es. redirects/ToolPage.tsx)\n // — zero costo/nessuna chiamata AI, e a differenza del vecchio paragrafo di prosa non\n // lascia all'utente il compito di dedurre quali campi servono per partire.\n <SubagentUsageCardView card={pageContextCard} />\n ) : (\n // Nessuna card disponibile (es. ToolsPage/RedirectsPage, pagine senza un\n // singolo subagent-comando a cui riferirsi) — resta il testo semplice del\n // pageContext, o il generico \"dimmi cosa vuoi fare\" se non c'è nemmeno quello.\n <p className=\"text-xs text-muted-foreground\">{pageContext ?? t.orchestratorStarterHint}</p>\n )}\n </div>\n </div>\n\n {/* Stessi `px-4 py-3` + `border-t/60` del footer di Modal.tsx — stessa altezza di\n partenza del footer della modale adiacente (textarea a una riga, vedi sopra).\n Sempre presente col pannello aperto (non solo quando c'è un task attivo, a\n differenza di prima): il composer resta \"stuck to bottom\" identico in entrambi i\n casi — solo il body sopra cambia (log live o l'hint), mai il composer\n stesso a spostarsi al centro del pannello. */}\n <div className=\"border-t border-border/60 px-4 py-3\">\n {/* What the controls of the host show (registerSubagentControls, CR-003), right above the composer. */}\n {task?.panelActions && <div className=\"mb-2\">{task.panelActions}</div>}\n {task\n ? <AgenticoComposer key={task.id} task={task} />\n : <AgenticoStarterComposer />}\n </div>\n </div>\n </>\n );\n}\n","import { useEffect, useRef } from 'react';\nimport { useAgentico } from './AgenticoContext';\nimport type { SubagentUsageCard } from '../domain/subagentUsageCard';\n\n/** Registra \"dove si trova l'utente adesso\" per Agentico — stesso pattern effect-based di\n * usePageBreadcrumb/usePageLeading (PageActionsContext.tsx): si registra al mount, si toglie da\n * solo allo smontaggio. Più istanze possono essere montate insieme (es. ToolsPage generico +\n * ToolHistoryPanel specifico sopra) — vince l'ultima registrata, vedi AgenticoContext.setPageContext.\n * `text === null` non registra nulla (utile per un contesto calcolato che a volte non è pronto).\n * `card` — opzionale, SOLO per la UI del pannello Agentico (vedi Agentico.tsx): quando presente\n * rimpiazza il paragrafo di prosa con una card strutturata (campi richiesti/opzionali), mentre\n * `text` resta comunque quello che arriva al prompt dell'orchestratore (vedi\n * AgenticoContext.pageContext) — due destinazioni diverse dello stesso \"dove si trova l'utente\",\n * mai lo stesso testo forzato a servire entrambe. */\nexport function useAgenticoPageContext(text: string | null, card?: SubagentUsageCard | null): void {\n const { setPageContext } = useAgentico();\n const id = useRef(crypto.randomUUID()).current;\n\n useEffect(() => {\n setPageContext(id, text, card ?? null);\n return () => setPageContext(id, null);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [text, card]);\n}\n","import { useEffect, useRef } from 'react';\nimport { useAgentico } from './AgenticoContext';\n\n/** Registra \"questo componente è il subagent X\" per Agentico — chiamato DAL componente\n * portabile stesso (es. ThemeIndexList/ThemeEditor), mai dalla pagina che lo ospita: chi monta\n * il componente ottiene gratis il contesto/hint/scoping corretto, senza ricostruirlo pagina per\n * pagina (vedi ../domain/subagentManifest.ts SubagentManifest.componentContexts per\n * dove vivono `agenticoHint`/`userMessage`).\n *\n * `componentKey` deve combaciare con una chiave di `manifest.componentContexts` — se non\n * combacia, l'anchor si registra comunque (per `onResult`) ma senza hint/messaggio (nessun\n * crash, solo nessun aggancio di prosa per l'LLM/l'utente).\n *\n * `onResult`, opzionale — chiamato da SubagentRunner quando arriva un risultato per questo\n * `subagentId` MENTRE questo componente è montato: il componente popola se stesso (Form/stato\n * locale), mai un salvataggio automatico — l'utente resta libero di rivedere e premere Salva\n * come per un edit manuale. `meta` è il TParams con cui il subagent è stato lanciato (es.\n * `{ url }` per theme) — utile quando il risultato stesso non porta un dato che serve per\n * popolare il componente (es. l'URL sorgente, per derivare un nome di default). `taskId` è l'id\n * del task Agentico che ha prodotto questo risultato — un componente che vuole poter persistere\n * la chat al proprio salvataggio (vedi AgenticoContext.commitEntityChat) lo tiene da parte in un\n * ref, altrimenti può ignorarlo. Anche una pagina\n * non-portabile (es. SiteEditorPage, per i soli campi di site-branding dentro lo stesso\n * risultato) può chiamare questo hook con lo stesso `subagentId` di un componente già montato —\n * più listener indipendenti sullo stesso risultato, ciascuno responsabile della propria fetta di\n * dati. */\nexport function useSubagentComponentContext(\n subagentId: string,\n componentKey: string,\n onResult?: (result: unknown, meta: Record<string, unknown>, taskId: string) => void,\n /** Fatti live sull'entità che QUESTA istanza ha aperta ORA (es. `Il tema aperto si chiama\n * \"ducatimilano.it\".`) — accodati all'agenticoHint statico del manifest nel prompt\n * dell'orchestratore (vedi AgenticoContext.anchorTexts), cosi un \"estrai il tema\" senza URL\n * esplicito può dedurlo dal nome/hostname già noto invece di richiederlo sempre da capo.\n * Assente/undefined = comportamento invariato. A differenza di `onResult` (in un ref, mai\n * causa di ri-registrazione) QUESTO è una dipendenza vera dell'effect sotto: deve\n * ri-registrare quando cambia (es. il nome dell'entità arriva dopo un fetch asincrono). */\n contextInfo?: string,\n /** `false` = non registra nulla (o smonta l'anchor già registrato) — per un componente che\n * resta montato anche quando \"non è più lui\" ad ancorarsi (es. RedirectsIndexList, che non\n * smonta quando mostra il dettaglio di un host: senza questo, il suo anchor 'indexList'\n * resterebbe attivo ANCHE mentre RedirectsEditor registra 'editor' per lo stesso\n * subagentId, consegnando un risultato a entrambi). Default `true` — comportamento invariato\n * per chi non lo passa. */\n enabled: boolean = true,\n /** Id dell'entità a cui QUESTA istanza è vincolata (es. l'hostId di RedirectsEditor) — vedi\n * ComponentAnchor.pinnedEntityId per il perché (backstop deterministico contro un salvataggio\n * su un'entità sbagliata, non solo un'istruzione nel prompt). Assente = nessun vincolo,\n * comportamento invariato. */\n pinnedEntityId?: string,\n): void {\n const { registerComponentAnchor } = useAgentico();\n const id = useRef(crypto.randomUUID()).current;\n // Ref, non una dipendenza diretta dell'effect sotto — altrimenti una onResult ricreata a\n // ogni render (comune, spesso una closure inline) farebbe ri-registrare l'anchor a ogni\n // render invece che solo quando cambia davvero subagentId/componentKey.\n const onResultRef = useRef(onResult);\n onResultRef.current = onResult;\n\n useEffect(() => {\n if (!enabled) return;\n registerComponentAnchor(id, {\n subagentId,\n componentKey,\n contextInfo,\n pinnedEntityId,\n onResult: (result: unknown, meta: Record<string, unknown>, taskId: string) => onResultRef.current?.(result, meta, taskId),\n });\n return () => registerComponentAnchor(id, null);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [subagentId, componentKey, contextInfo, enabled, pinnedEntityId]);\n}\n"],"mappings":"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"}