@llmnative/agentico 0.1.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/README.md +6 -1
- package/dist/index.js +13 -10
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1054 -680
- package/dist/index.mjs.map +1 -1
- package/dist/types/domain/agentDefinition.d.ts +79 -0
- package/dist/types/domain/entityChatStore.d.ts +9 -0
- package/dist/types/domain/messageParts.d.ts +79 -0
- package/dist/types/domain/subagentManifest.d.ts +14 -0
- package/dist/types/i18n/types.d.ts +8 -0
- package/dist/types/index.d.ts +4 -2
- package/dist/types/presentation/AgentActivityLog.d.ts +16 -4
- package/dist/types/presentation/AgenticoContext.d.ts +22 -1
- package/dist/types/presentation/SubagentRunner.d.ts +4 -14
- package/dist/types/presentation/subagentControls.d.ts +28 -0
- package/dist/types/presentation/useAgent.d.ts +21 -1
- package/llms.txt +9 -3
- package/package.json +3 -3
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { z } from 'zod';
|
|
2
2
|
import type { AIAttachment } from '@llmnative/react';
|
|
3
|
+
import type { AgentQuestion, AgentQuestionAnswer, AgentQuestionsAnswers } from './messageParts';
|
|
3
4
|
/** Rif mutabile, sempre aggiornato da useAgent.ts all'inizio di ogni turno (roundTrip 0) con
|
|
4
5
|
* gli allegati grezzi (base64) di QUESTO turno — mai un valore statico catturato una volta:
|
|
5
6
|
* un tool creato da getTools() (chiamato una sola volta per sessione) deve comunque vedere gli
|
|
@@ -80,4 +81,82 @@ export interface AgentDefinition<TParams, TContext, TResult> {
|
|
|
80
81
|
* agente: qui si dichiara solo QUALI si vogliono, mai il loro testo. Assente o [] =
|
|
81
82
|
* nessuno. */
|
|
82
83
|
microPromptIds?: string[];
|
|
84
|
+
/** Present = the turns of this agent are answered by the host, not by the AI provider of
|
|
85
|
+
* Agentico (CR-003): a remote agent with its own memory, for example an opencode session on
|
|
86
|
+
* a server. `useAgent` then builds no prompt, needs no model and never falls back: it gives
|
|
87
|
+
* the executor the text of the person and shows what it reports. `buildPrompt`,
|
|
88
|
+
* `buildFollowUpPrompt`, `parseResponse` and `fallback` stay required by the type but are not
|
|
89
|
+
* called. */
|
|
90
|
+
executor?: AgentExecutor<TParams, TContext, TResult>;
|
|
91
|
+
}
|
|
92
|
+
/** One step of a remote agent, shown as a line of the activity log and updated by `id`
|
|
93
|
+
* (e.g. "wrote GLOSSARY.md"). */
|
|
94
|
+
export interface AgentExecutorStep {
|
|
95
|
+
id: string;
|
|
96
|
+
label: string;
|
|
97
|
+
status: 'running' | 'done' | 'error';
|
|
98
|
+
}
|
|
99
|
+
/** One turn given to an `AgentExecutor`. */
|
|
100
|
+
export interface AgentExecutorTurn<TParams, TContext> {
|
|
101
|
+
params: TParams;
|
|
102
|
+
context: TContext;
|
|
103
|
+
/** What the person wrote: the opening message on the first turn (possibly empty), then
|
|
104
|
+
* each follow-up, as typed — never a prompt built by Agentico. */
|
|
105
|
+
text: string;
|
|
106
|
+
/** true on the turn of `run()`, false on every follow-up. */
|
|
107
|
+
first: boolean;
|
|
108
|
+
/** Aborted when the person stops the turn. */
|
|
109
|
+
signal: AbortSignal;
|
|
110
|
+
/** The model the person picked among `AgentExecutor.models`, if the executor offers any. */
|
|
111
|
+
model?: string;
|
|
112
|
+
/** Present when the person answered questions of an earlier turn (CR-004): the turn carries
|
|
113
|
+
* them, structured, with `text` empty. The executor decides how to forward them (e.g. to the
|
|
114
|
+
* `question` tool of its session). Questions answered while their own turn is still running
|
|
115
|
+
* resolve the promise of `onQuestions` instead. */
|
|
116
|
+
answers?: AgentQuestionsAnswers;
|
|
117
|
+
/** The reply so far, whole (not a delta): shown as it grows, as Markdown. Without `part.id` it
|
|
118
|
+
* is the one reply of the turn (CR-003), kept below the steps and the reasoning; with an id it is
|
|
119
|
+
* a text part of its own, in the order it first arrived (CR-004). `part.done` ends its streaming. */
|
|
120
|
+
onText(text: string, part?: AgentExecutorPartOptions): void;
|
|
121
|
+
/** The reasoning of the model so far, whole (not a delta), as Markdown: shown closed behind
|
|
122
|
+
* "Reasoned for 12 s" (CR-004). Without `part.id` it is one reasoning part per turn. It ends with
|
|
123
|
+
* `part.done`, when another kind of part arrives, or with the turn; `part.duration` (seconds)
|
|
124
|
+
* replaces the time Agentico measures. */
|
|
125
|
+
onReasoning(text: string, part?: AgentExecutorPartOptions & {
|
|
126
|
+
duration?: number;
|
|
127
|
+
}): void;
|
|
128
|
+
/** A step of the agent: added the first time its id is seen, updated after. Consecutive steps
|
|
129
|
+
* are shown as one collapsible group. */
|
|
130
|
+
onStep(step: AgentExecutorStep): void;
|
|
131
|
+
/** Questions to the person, answered with a click (CR-004). The promise resolves with the
|
|
132
|
+
* answers if they come while this turn is still running (e.g. the session waits for them);
|
|
133
|
+
* once the turn has ended (or the chat was reloaded), the answers arrive as a new turn with
|
|
134
|
+
* `answers` instead. It rejects when the person stops the turn. `part.id` names the set
|
|
135
|
+
* (default: made up by Agentico) and is the `answers.id` of that later turn. */
|
|
136
|
+
onQuestions(questions: AgentQuestion[], part?: {
|
|
137
|
+
id?: string;
|
|
138
|
+
}): Promise<AgentQuestionAnswer[]>;
|
|
139
|
+
}
|
|
140
|
+
/** Which part an `onText`/`onReasoning` call updates, and whether it is complete. */
|
|
141
|
+
export interface AgentExecutorPartOptions {
|
|
142
|
+
/** The part to update: created the first time it is seen. */
|
|
143
|
+
id?: string;
|
|
144
|
+
/** The part is complete: no longer streaming. */
|
|
145
|
+
done?: boolean;
|
|
146
|
+
}
|
|
147
|
+
/** The host side of a remote agent: one call per turn, resolved when its reply is complete. */
|
|
148
|
+
export interface AgentExecutor<TParams, TContext, TResult> {
|
|
149
|
+
turn(turn: AgentExecutorTurn<TParams, TContext>): Promise<{
|
|
150
|
+
text: string;
|
|
151
|
+
result?: TResult;
|
|
152
|
+
}>;
|
|
153
|
+
/** The models the person can pick in the composer, at any time: what one means is the host's
|
|
154
|
+
* (e.g. the model of the remote session from the next message). Absent = no picker. */
|
|
155
|
+
models?(params: TParams): Array<{
|
|
156
|
+
label: string;
|
|
157
|
+
value: string;
|
|
158
|
+
group?: string;
|
|
159
|
+
}>;
|
|
160
|
+
/** The model picked at first, e.g. the one the remote session runs with. */
|
|
161
|
+
defaultModel?(params: TParams): string | undefined;
|
|
83
162
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { AIConversationTurn } from '@llmnative/react';
|
|
2
|
+
import type { AgentMessagePart } from './messageParts';
|
|
2
3
|
/** Stessa forma di AgentActivityEntry (agentico/presentation/useAgent.ts) — ridefinita qui invece
|
|
3
4
|
* di importata: il domain layer non dipende dal presentation layer, e questi campi sono
|
|
4
5
|
* semplici dati (mai funzioni), quindi TypeScript accetta un AgentActivityEntry[] ovunque sia
|
|
@@ -10,6 +11,10 @@ export interface EntityChatActivityEntry {
|
|
|
10
11
|
status: 'running' | 'done' | 'error' | 'stopped';
|
|
11
12
|
detail?: string;
|
|
12
13
|
toolNames?: string[];
|
|
14
|
+
attachmentNames?: string[];
|
|
15
|
+
/** The message in parts (CR-004): saved and reloaded as they are. Absent in chats saved before
|
|
16
|
+
* 0.3.0, which show `detail` as before. */
|
|
17
|
+
parts?: AgentMessagePart[];
|
|
13
18
|
}
|
|
14
19
|
export interface EntityChatSnapshot {
|
|
15
20
|
/** Host scope (tenant, company…) the chat belongs to — see AgenticoScope. */
|
|
@@ -46,3 +51,7 @@ export declare function removeEntityChat(scopeId: string, subagentId: string, en
|
|
|
46
51
|
* (Agentico.tsx), che le mescola across subagent (theme, redirects, html-to-liquid) in un'unica
|
|
47
52
|
* lista ordinata per data. */
|
|
48
53
|
export declare function listEntityChats(scopeId: string): EntityChatSnapshot[];
|
|
54
|
+
/** The id of the Agentico task of the chat of an entity: the same when the chat is resumed from
|
|
55
|
+
* the history and when a page starts it again (AgenticoContext.startSubagent), so it stays one
|
|
56
|
+
* task (CR-003). */
|
|
57
|
+
export declare const entityChatTaskId: (subagentId: string, entityId: string) => string;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/** A choice of a question (the shape of the `question` tool of opencode and of MCP elicitation). */
|
|
2
|
+
export interface AgentQuestionOption {
|
|
3
|
+
/** What the answer says: it is the value returned when chosen. */
|
|
4
|
+
label: string;
|
|
5
|
+
/** A line under the label. */
|
|
6
|
+
description?: string;
|
|
7
|
+
}
|
|
8
|
+
/** A question of the agent to the person. */
|
|
9
|
+
export interface AgentQuestion {
|
|
10
|
+
/** Returned with the answer, to match it to the question. */
|
|
11
|
+
id?: string;
|
|
12
|
+
/** Short title: the label of its tab. */
|
|
13
|
+
header?: string;
|
|
14
|
+
question: string;
|
|
15
|
+
/** The choices. Without them the question takes a free-text answer. */
|
|
16
|
+
options?: AgentQuestionOption[];
|
|
17
|
+
/** More than one choice. */
|
|
18
|
+
multiple?: boolean;
|
|
19
|
+
/** The choice the agent recommends: its index (from 0) or its label. */
|
|
20
|
+
recommended?: number | string;
|
|
21
|
+
/** Offers "Other" with a free-text answer (default: yes). */
|
|
22
|
+
custom?: boolean;
|
|
23
|
+
}
|
|
24
|
+
/** The answer to one question: the chosen labels and/or the free text, in the order of the questions. */
|
|
25
|
+
export interface AgentQuestionAnswer {
|
|
26
|
+
id?: string;
|
|
27
|
+
header?: string;
|
|
28
|
+
values: string[];
|
|
29
|
+
}
|
|
30
|
+
/** The answers to a set of questions, as a turn gives them to the executor. `id` is the id of the
|
|
31
|
+
* questions part (the one given to `onQuestions`, or the one Agentico made up). */
|
|
32
|
+
export interface AgentQuestionsAnswers {
|
|
33
|
+
id: string;
|
|
34
|
+
answers: AgentQuestionAnswer[];
|
|
35
|
+
}
|
|
36
|
+
/** Text of the reply, as Markdown. `streaming` while it is still arriving. */
|
|
37
|
+
export interface AgentTextPart {
|
|
38
|
+
type: 'text';
|
|
39
|
+
id: string;
|
|
40
|
+
text: string;
|
|
41
|
+
streaming?: boolean;
|
|
42
|
+
}
|
|
43
|
+
/** The reasoning of the model, as Markdown, shown closed behind "Reasoned for 12 s". */
|
|
44
|
+
export interface AgentReasoningPart {
|
|
45
|
+
type: 'reasoning';
|
|
46
|
+
id: string;
|
|
47
|
+
text: string;
|
|
48
|
+
streaming?: boolean;
|
|
49
|
+
/** Seconds the model reasoned: given by the executor, or measured by Agentico. */
|
|
50
|
+
duration?: number;
|
|
51
|
+
}
|
|
52
|
+
/** A step of the agent (a tool it ran, a file it wrote). Consecutive steps are shown as one group. */
|
|
53
|
+
export interface AgentToolPart {
|
|
54
|
+
type: 'tool';
|
|
55
|
+
id: string;
|
|
56
|
+
label: string;
|
|
57
|
+
status: 'running' | 'done' | 'error' | 'stopped';
|
|
58
|
+
}
|
|
59
|
+
/** Questions to the person, answered with a click. `answers` once answered (shown read-only). */
|
|
60
|
+
export interface AgentQuestionsPart {
|
|
61
|
+
type: 'questions';
|
|
62
|
+
id: string;
|
|
63
|
+
questions: AgentQuestion[];
|
|
64
|
+
answers?: AgentQuestionAnswer[];
|
|
65
|
+
}
|
|
66
|
+
export type AgentMessagePart = AgentTextPart | AgentReasoningPart | AgentToolPart | AgentQuestionsPart;
|
|
67
|
+
/** The id of the text part of an executor that calls `onText` without an id (the reply of CR-003). */
|
|
68
|
+
export declare const DEFAULT_TEXT_PART_ID = "text";
|
|
69
|
+
/** The id of the reasoning part of an executor that calls `onReasoning` without an id. */
|
|
70
|
+
export declare const DEFAULT_REASONING_PART_ID = "reasoning";
|
|
71
|
+
/** Adds `part`, or replaces the part of the same type and id. A new step or reasoning goes above the
|
|
72
|
+
* default text part, so the reply (as in CR-003) stays below the work that led to it; anything else
|
|
73
|
+
* is appended. `closeReasoning` closes the reasoning still streaming when a part of another type
|
|
74
|
+
* arrives (the model moved on), with its duration. */
|
|
75
|
+
export declare function upsertMessagePart(parts: AgentMessagePart[], part: AgentMessagePart, closeReasoning?: (part: AgentReasoningPart) => AgentReasoningPart): AgentMessagePart[];
|
|
76
|
+
/** The text of a message: its text parts, one paragraph each (what `AgentActivityEntry.detail` holds). */
|
|
77
|
+
export declare function messagePartsText(parts: readonly AgentMessagePart[]): string;
|
|
78
|
+
/** The answers as one line per question ("Database: Postgres"), for the history of the chat. */
|
|
79
|
+
export declare function formatQuestionAnswers(questions: readonly AgentQuestion[], answers: readonly AgentQuestionAnswer[]): string;
|
|
@@ -157,6 +157,20 @@ export interface SubagentManifest<TParams = any, TContext = any, TResult = any>
|
|
|
157
157
|
* orchestratorHint/humanSummary* per il vecchio meccanismo a livello di pagina, ancora usato
|
|
158
158
|
* da redirects/html-to-liquid finché non vengono migrati). */
|
|
159
159
|
componentContexts?: Record<string, SubagentComponentContext>;
|
|
160
|
+
/** The entity a chat of this subagent belongs to, from its raw params (e.g. the project it
|
|
161
|
+
* designs): when set, SubagentRunner saves the chat after every turn under it (see
|
|
162
|
+
* entityChatStore), so it is in the history and can be resumed on that entity (CR-003).
|
|
163
|
+
* Absent = saved only when the host commits it (commitEntityChat), as before. */
|
|
164
|
+
chatEntityId?(params: Record<string, unknown>): string | undefined;
|
|
165
|
+
/** true = the orchestrator offers this subagent only when the conversation is scoped to it,
|
|
166
|
+
* i.e. a component anchored to it is on the page (CR-003): a subagent that makes sense on
|
|
167
|
+
* one page only (e.g. the design of the project open there). Absent = offered everywhere. */
|
|
168
|
+
onlyWhenAnchored?: boolean;
|
|
169
|
+
/** Archiving a chat of this subagent from the history (CR-003): what the host does besides
|
|
170
|
+
* taking it off the history, e.g. close the remote session. A rejection keeps the chat. */
|
|
171
|
+
onArchive?(entityId: string, params: Record<string, unknown>): Promise<void> | void;
|
|
172
|
+
/** The question asked before archiving, if any (e.g. "close it without a pull request?"). */
|
|
173
|
+
confirmArchive?(entityId: string, params: Record<string, unknown>): Promise<string | undefined> | string | undefined;
|
|
160
174
|
}
|
|
161
175
|
/** Coppia di messaggi per UN punto di ancoraggio componente↔subagent (vedi
|
|
162
176
|
* SubagentManifest.componentContexts) — due pubblici diversi per lo stesso aggancio, mai lo
|
|
@@ -31,6 +31,12 @@ export interface AgenticoDict {
|
|
|
31
31
|
tooManyToolTurns: string;
|
|
32
32
|
aiDisabledFallback: string;
|
|
33
33
|
continueWithoutAiFallback: string;
|
|
34
|
+
/** The group of the steps of the agent, when it holds one step. */
|
|
35
|
+
oneStep: string;
|
|
36
|
+
/** The group of the steps of the agent. `{count}` = number of steps */
|
|
37
|
+
steps: string;
|
|
38
|
+
/** Accessible name of the group of the steps. */
|
|
39
|
+
stepsLabel: string;
|
|
34
40
|
/** `{label}` = subagent label */
|
|
35
41
|
newInstance: string;
|
|
36
42
|
chatHistory: string;
|
|
@@ -39,6 +45,8 @@ export interface AgenticoDict {
|
|
|
39
45
|
messagePlaceholder: string;
|
|
40
46
|
unsavedConversationConfirm: string;
|
|
41
47
|
noSavedChats: string;
|
|
48
|
+
/** Archives a chat of the history (CR-003). */
|
|
49
|
+
archiveChat: string;
|
|
42
50
|
noResults: string;
|
|
43
51
|
retry: string;
|
|
44
52
|
/** User message sent to the model by the retry button */
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import './i18n/types';
|
|
2
|
-
export type { AgentDefinition, AgentTool, LatestAttachmentsRef } from './domain/agentDefinition';
|
|
2
|
+
export type { AgentDefinition, AgentTool, LatestAttachmentsRef, AgentExecutor, AgentExecutorTurn, AgentExecutorStep, AgentExecutorPartOptions } from './domain/agentDefinition';
|
|
3
|
+
export { DEFAULT_TEXT_PART_ID, DEFAULT_REASONING_PART_ID, upsertMessagePart, messagePartsText, formatQuestionAnswers, type AgentMessagePart, type AgentTextPart, type AgentReasoningPart, type AgentToolPart, type AgentQuestionsPart, type AgentQuestion, type AgentQuestionOption, type AgentQuestionAnswer, type AgentQuestionsAnswers, } from './domain/messageParts';
|
|
3
4
|
export type { SubagentCommand, SubagentManifest, SubagentComponentContext } from './domain/subagentManifest';
|
|
4
5
|
export { defineSubagent } from './domain/defineSubagent';
|
|
5
6
|
export { registerSubagents, getSubagentRegistry, findSubagentManifest } from './domain/subagentRegistry';
|
|
@@ -14,7 +15,7 @@ export { extractJsonBlock } from './domain/extractJsonBlock';
|
|
|
14
15
|
export type { ResultDestination, DestinationContext, ResultDestinationOutcome } from './domain/resultDestination';
|
|
15
16
|
export { jsonExportDestination, JSON_EXPORT_DESTINATION_ID } from './domain/jsonExportDestination';
|
|
16
17
|
export { registerDestinations, getDestinationRegistry } from './domain/destinationRegistry';
|
|
17
|
-
export { configureEntityChatStore, saveEntityChat, getEntityChat, listEntityChats, removeEntityChat, type EntityChatSnapshot, } from './domain/entityChatStore';
|
|
18
|
+
export { configureEntityChatStore, saveEntityChat, getEntityChat, listEntityChats, removeEntityChat, entityChatTaskId, type EntityChatSnapshot, } from './domain/entityChatStore';
|
|
18
19
|
export { agenticoTranslations, agenticoEn, agenticoIt, agenticoDe, useAgenticoI18n, type AgenticoDict } from './i18n';
|
|
19
20
|
export { Agentico, AGENTICO_WIDTH } from './presentation/Agentico';
|
|
20
21
|
export { AgenticoToggle } from './presentation/AgenticoToggle';
|
|
@@ -23,3 +24,4 @@ export { useAgent, type AgentActivityEntry, type AgentConversation, type UseAgen
|
|
|
23
24
|
export { useAgenticoPageContext } from './presentation/useAgenticoPageContext';
|
|
24
25
|
export { useSubagentComponentContext } from './presentation/useSubagentComponentContext';
|
|
25
26
|
export { registerSubagentReports, getSubagentReport, type EditableReportModule, type SubagentReportModule, type SubagentSelection, } from './presentation/reports/subagentReportRegistry';
|
|
27
|
+
export { registerSubagentControls, getSubagentControls, type SubagentControls, type SubagentControlsProps, type UseSubagentControls } from './presentation/subagentControls';
|
|
@@ -1,9 +1,21 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
1
|
import type { AgentActivityEntry } from './useAgent';
|
|
2
|
+
import type { AgentQuestionAnswer } from '../domain/messageParts';
|
|
3
|
+
/** What the log needs to let the person answer questions (CR-004): the entries of the live
|
|
4
|
+
* conversation (`canAnswer`) and where the answers go. Without it, unanswered questions are shown
|
|
5
|
+
* disabled. */
|
|
6
|
+
export interface AgentActivityLogQuestions {
|
|
7
|
+
canAnswer: (entryId: string) => boolean;
|
|
8
|
+
onAnswer: (entryId: string, partId: string, answers: AgentQuestionAnswer[]) => Promise<unknown>;
|
|
9
|
+
}
|
|
3
10
|
/** Il log di attività di un agente, usato dal pannello globale (Agentico) — ogni agente ora
|
|
4
11
|
* ci passa attraverso, il pannello embedded legacy (AgentFollowUp) è stato ritirato una volta
|
|
5
12
|
* migrato anche l'ultimo consumer (import HTML/JSX, vedi ComponentEditPage.tsx). Un solo posto
|
|
6
|
-
* per l'icon-system e la logica "sto pensando" transitorio/collassabile.
|
|
7
|
-
|
|
13
|
+
* per l'icon-system e la logica "sto pensando" transitorio/collassabile.
|
|
14
|
+
*
|
|
15
|
+
* One `<li>` per turn (CR-004): the message of the person, sticky at the top while its turn
|
|
16
|
+
* scrolls, then what the agent did and said. Entries before the first message of the person
|
|
17
|
+
* (an opening without text) form a turn of their own, without a bubble. */
|
|
18
|
+
export declare function AgentActivityLog({ activity, questions }: {
|
|
8
19
|
activity: AgentActivityEntry[];
|
|
9
|
-
|
|
20
|
+
questions?: AgentActivityLogQuestions;
|
|
21
|
+
}): import("react").JSX.Element | null;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import type { AIModelDescriptor, DataProviderAdapter, StorageProviderAdapter } from '@llmnative/react';
|
|
2
|
+
import type { AIModelDescriptor, ChatbotAction, DataProviderAdapter, StorageProviderAdapter } from '@llmnative/react';
|
|
3
3
|
import type { AgentActivityEntry, AgentConversation } from './useAgent';
|
|
4
4
|
import type { ResultDestination } from '../domain/resultDestination';
|
|
5
5
|
import type { SubagentUsageCard } from '../domain/subagentUsageCard';
|
|
@@ -94,6 +94,11 @@ export interface AgenticoTask<TResult = unknown> {
|
|
|
94
94
|
* top=vecchio/bottom=nuovo (bug segnalato). Assente = comportamento invariato, sempre in
|
|
95
95
|
* fondo (equivalente a passare la lunghezza corrente del log). */
|
|
96
96
|
panelExtraAnchorIndex?: number;
|
|
97
|
+
/** What the host shows at the bottom of the panel, above the composer, while the task is open
|
|
98
|
+
* (see registerSubagentControls, CR-003). */
|
|
99
|
+
panelActions?: React.ReactNode;
|
|
100
|
+
/** Buttons of the host in the composer (see registerSubagentControls, CR-003). */
|
|
101
|
+
composerActions?: ChatbotAction[];
|
|
97
102
|
/** Chiamato quando questo task viene sostituito da uno nuovo (vedi openTask) o quando
|
|
98
103
|
* l'utente lo interrompe — SEMPRE definito da chi possiede il task
|
|
99
104
|
* (OrchestratorRunner/SubagentRunner), mai un fallback generico su closeTask: quel
|
|
@@ -296,6 +301,22 @@ interface AgenticoContextValue {
|
|
|
296
301
|
* indietro", niente eccezione per un task ancora in corso (deliberatamente semplice,
|
|
297
302
|
* raffinabile in seguito). No-op se non c'è alcun task. */
|
|
298
303
|
dismissForeignTask: () => void;
|
|
304
|
+
/** Starts a subagent from a page, without the orchestrator (CR-003): it replaces the current
|
|
305
|
+
* task. `taskId` keeps one task per entity (start it again = the same task); `openingMessage`
|
|
306
|
+
* is shown as the first message of the person and given to the first turn. */
|
|
307
|
+
startSubagent: (subagentId: string, params: Record<string, unknown>, options?: {
|
|
308
|
+
taskId?: string;
|
|
309
|
+
openingMessage?: string;
|
|
310
|
+
}) => void;
|
|
311
|
+
/** Present = a startSubagent() is pending, read by Agentico.tsx to mount its SubagentRunner. */
|
|
312
|
+
pendingStart: {
|
|
313
|
+
subagentId: string;
|
|
314
|
+
params: Record<string, unknown>;
|
|
315
|
+
taskId?: string;
|
|
316
|
+
openingMessage?: string;
|
|
317
|
+
} | null;
|
|
318
|
+
/** Changes at every startSubagent() — React `key` of that SubagentRunner. */
|
|
319
|
+
startRunId: string | null;
|
|
299
320
|
}
|
|
300
321
|
export declare function AgenticoProvider({ children, scope }: {
|
|
301
322
|
children: React.ReactNode;
|
|
@@ -2,20 +2,7 @@ import type { AIConversationTurn } from '@llmnative/react';
|
|
|
2
2
|
import { type AgentActivityEntry } from './useAgent';
|
|
3
3
|
import type { SubagentManifest } from '../domain/subagentManifest';
|
|
4
4
|
import type { ResultDestination } from '../domain/resultDestination';
|
|
5
|
-
|
|
6
|
-
* parametri già raccolti dall'orchestratore Agentico — nessun passo utente aggiuntivo, la
|
|
7
|
-
* conversazione di raccolta parametri è già avvenuta a monte (vedi Agentico.tsx). Si registra
|
|
8
|
-
* nel pannello Agentico con id stabile via useRef + cleanup closeTask allo smontaggio, con il
|
|
9
|
-
* transcript dell'orchestratore (`precedingActivity`) prepeso al proprio log — l'utente vede
|
|
10
|
-
* UNA chat continua, mai due task distinti da switchare (vedi OrchestratorRunner).
|
|
11
|
-
*
|
|
12
|
-
* Con `resumeSeed` valorizzato, il primo giro chiama `agent.resume(...)` invece di
|
|
13
|
-
* `agent.run(...)` — riprende una chat già salvata (vedi entityChatStore/
|
|
14
|
-
* AgenticoContext.resumeEntityChat) invece di avviarne una nuova. Tutto il resto (consegna del
|
|
15
|
-
* risultato ai componentAnchors, ResultDestinationPicker di riserva, registrazione del task) è
|
|
16
|
-
* IDENTICO a un run normale — un follow-up dopo un resume produce un risultato esattamente come
|
|
17
|
-
* un follow-up dopo un run, nessuna duplicazione di logica per il caso "ripreso". */
|
|
18
|
-
export declare function SubagentRunner({ manifest, params, extraDestinations, precedingActivity, taskId: taskIdProp, resumeSeed, model, }: {
|
|
5
|
+
export declare function SubagentRunner({ manifest, params, extraDestinations, precedingActivity, taskId: taskIdProp, resumeSeed, model, openingMessage, }: {
|
|
19
6
|
manifest: SubagentManifest<any, any, any>;
|
|
20
7
|
params: Record<string, unknown>;
|
|
21
8
|
/** Destinazioni scoped alla pagina che ha avviato questo run (es. "Applica a questo Site
|
|
@@ -45,4 +32,7 @@ export declare function SubagentRunner({ manifest, params, extraDestinations, pr
|
|
|
45
32
|
* OrchestratorRunner — vedi il commento lì). Non passato per un `resumeSeed` — lì non c'è una
|
|
46
33
|
* scelta "di questo giro" da propagare, si continua semplicemente la chat salvata così com'era. */
|
|
47
34
|
model?: string;
|
|
35
|
+
/** The first message of the person, when a page starts the subagent directly
|
|
36
|
+
* (AgenticoContext.startSubagent, CR-003): shown in the log and given to the first turn. */
|
|
37
|
+
openingMessage?: string;
|
|
48
38
|
}): null;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import type { ChatbotAction } from '@llmnative/react';
|
|
3
|
+
/** What the controls of a subagent receive (CR-003). */
|
|
4
|
+
export interface SubagentControlsProps {
|
|
5
|
+
/** The raw params the subagent was started with. */
|
|
6
|
+
params: Record<string, unknown>;
|
|
7
|
+
/** The id of the task in the Agentico panel. */
|
|
8
|
+
taskId: string;
|
|
9
|
+
/** A turn is running: actions that would interrupt it should wait. */
|
|
10
|
+
running: boolean;
|
|
11
|
+
/** Ends the task (stops a running turn and closes it). */
|
|
12
|
+
close: () => void;
|
|
13
|
+
}
|
|
14
|
+
/** The controls of a task: actions of the prompt, and what the host shows above it (a modal, a message). */
|
|
15
|
+
export interface SubagentControls {
|
|
16
|
+
/** Buttons of the composer, next to the attachments (`ChatbotAction` with `onClick`). */
|
|
17
|
+
actions?: ChatbotAction[];
|
|
18
|
+
/** Shown above the composer while the task is open. */
|
|
19
|
+
content?: ReactNode;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* A React hook of the host: called by the runner of each task of its subagent, on every render.
|
|
23
|
+
* Its `actions` and `content` must keep their identity while nothing changes (`useMemo`): a new
|
|
24
|
+
* one registers the task again, and a new one at every render would never stop.
|
|
25
|
+
*/
|
|
26
|
+
export type UseSubagentControls = (props: SubagentControlsProps) => SubagentControls;
|
|
27
|
+
export declare function registerSubagentControls(controls: Record<string, UseSubagentControls>): void;
|
|
28
|
+
export declare function getSubagentControls(subagentId: string): UseSubagentControls | undefined;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { AIConversationTurn, AIModelDescriptor } from '@llmnative/react';
|
|
2
2
|
import type { AgentDefinition } from '../domain/agentDefinition';
|
|
3
|
+
import { type AgentMessagePart, type AgentQuestionAnswer } from '../domain/messageParts';
|
|
3
4
|
/** Una voce del log di attività — o un messaggio dell'utente (il testo di sendMessage(),
|
|
4
5
|
* mostrato subito com'è, mai "sto pensando" perché non è il modello a scriverlo), o una tool
|
|
5
6
|
* call (etichetta SEMPRE deterministica/locale, vedi AgentTool.describeInvocation: mai testo
|
|
@@ -36,6 +37,10 @@ export interface AgentActivityEntry {
|
|
|
36
37
|
* doveva fidarsi a memoria (bug osservato: un allegato sembrava "sparito" perché non
|
|
37
38
|
* c'era traccia visiva che fosse mai arrivato). */
|
|
38
39
|
attachmentNames?: string[];
|
|
40
|
+
/** On the turns of a remote agent (CR-004): the message in parts (text, reasoning, steps,
|
|
41
|
+
* questions), in the order they arrived. `detail` still holds its text. Absent on the turns of
|
|
42
|
+
* the AI provider and on chats saved before 0.3.0, shown from `detail` as before. */
|
|
43
|
+
parts?: AgentMessagePart[];
|
|
39
44
|
}
|
|
40
45
|
/** Stato di gating/selezione — non generico, condiviso dal componente <AgentControls />
|
|
41
46
|
* (che non ha bisogno di conoscere TParams/TResult, solo di disegnare switch + select). */
|
|
@@ -54,6 +59,15 @@ export interface UseAgentResult<TParams, TResult> extends AgentControlState {
|
|
|
54
59
|
/** true dopo che run() ha eseguito almeno un turno con AI abilitata — solo allora la
|
|
55
60
|
* conversazione ha una history su cui sendMessage() può continuare. */
|
|
56
61
|
canContinue: boolean;
|
|
62
|
+
/** true when the turns go to `AgentDefinition.executor` (CR-003): no attachments, no
|
|
63
|
+
* deterministic fallback; the models are the executor's (`remoteModels`), not the catalog. */
|
|
64
|
+
remote: boolean;
|
|
65
|
+
/** The models a remote executor offers (`AgentExecutor.models`); `selectedModel` is one of them. */
|
|
66
|
+
remoteModels: Array<{
|
|
67
|
+
label: string;
|
|
68
|
+
value: string;
|
|
69
|
+
group?: string;
|
|
70
|
+
}>;
|
|
57
71
|
/** Log delle tool call della sessione corrente, più recenti in fondo — azzerato a ogni
|
|
58
72
|
* run(), accumulato attraverso i sendMessage() successivi. */
|
|
59
73
|
activity: AgentActivityEntry[];
|
|
@@ -130,11 +144,17 @@ export interface UseAgentResult<TParams, TResult> extends AgentControlState {
|
|
|
130
144
|
history: AIConversationTurn[];
|
|
131
145
|
activity: AgentActivityEntry[];
|
|
132
146
|
} | null;
|
|
147
|
+
/** Answers the questions part `partId` of the entry `entryId` (CR-004). If the turn that asked
|
|
148
|
+
* is still waiting, its `onQuestions` promise resolves; otherwise a new turn of the executor
|
|
149
|
+
* carries them (`AgentExecutorTurn.answers`, with empty `text`). Either way the part keeps the
|
|
150
|
+
* answers and shows them read-only. Resolves with the result of that new turn, if any; rejects
|
|
151
|
+
* (leaving the form open) when another turn is running or the entry is not of this conversation. */
|
|
152
|
+
answerQuestions: (entryId: string, partId: string, answers: AgentQuestionAnswer[]) => Promise<TResult | null>;
|
|
133
153
|
}
|
|
134
154
|
/** Solo la fetta di UseAgentResult che serve a un consumer del pannello globale (Agentico.tsx)
|
|
135
155
|
* per continuare una conversazione già avviata da run() — TParams non compare in nessuno dei
|
|
136
156
|
* membri qui sotto, quindi `never` è innocuo. */
|
|
137
|
-
export type AgentConversation<TResult> = Pick<UseAgentResult<never, TResult>, 'canContinue' | 'running' | 'error' | 'activity' | 'sendMessage' | 'stop' | 'runFallback' | 'snapshot'>;
|
|
157
|
+
export type AgentConversation<TResult> = Pick<UseAgentResult<never, TResult>, 'canContinue' | 'remote' | 'remoteModels' | 'running' | 'error' | 'activity' | 'sendMessage' | 'stop' | 'runFallback' | 'snapshot' | 'answerQuestions'>;
|
|
138
158
|
export declare function useAgent<TParams, TContext, TResult>(definition: AgentDefinition<TParams, TContext, TResult>, options?: {
|
|
139
159
|
defaultEnabled?: boolean;
|
|
140
160
|
}): UseAgentResult<TParams, TResult>;
|
package/llms.txt
CHANGED
|
@@ -6,9 +6,11 @@
|
|
|
6
6
|
|
|
7
7
|
## Host responsibilities (startup)
|
|
8
8
|
|
|
9
|
-
- `registerSubagents(manifests)` — SubagentManifest: id, label, icon, description, indexPath, commands (name, summary, Zod inputSchema, describeOutput), definition (AgentDefinition), toParams, destinationIds, optional crossAgentToolIds, withCollected, manualEntry, componentContexts.
|
|
9
|
+
- `registerSubagents(manifests)` — SubagentManifest: id, label, icon, description, indexPath, commands (name, summary, Zod inputSchema, describeOutput), definition (AgentDefinition), toParams, destinationIds, optional crossAgentToolIds, withCollected, manualEntry, componentContexts, chatEntityId (chat saved at every turn under that entity), onlyWhenAnchored (offered by the orchestrator only when scoped to it).
|
|
10
10
|
- `registerDestinations({ id: ResultDestination })` — apply(result, { scopeId, dataProvider, storageProvider, proxyFetch }, meta).
|
|
11
11
|
- `registerSubagentReports({ subagentId: SubagentReportModule })`.
|
|
12
|
+
- `registerSubagentControls({ subagentId: useControls })` — a hook of the host per task (props: params, taskId, running, close) returning `{ actions?: ChatbotAction[] (with onClick), content?: ReactNode }`, memoized: actions in the prompt, content above it.
|
|
13
|
+
- `SubagentManifest.onArchive(entityId, params)` / `confirmArchive(entityId, params)` — archiving a chat from the history (hover icon).
|
|
12
14
|
- `registerCrossAgentTools((ctx) => AgentTool[])` — ctx: attachmentsRef, collected (per-conversation scratchpad), proxyFetch.
|
|
13
15
|
- `configureAgenticoPrompts(partial)` — model-facing pack; `agenticoPromptsEn` (default) / `agenticoPromptsIt`.
|
|
14
16
|
- `configureEntityChatStore({ storageKey })`.
|
|
@@ -25,11 +27,15 @@
|
|
|
25
27
|
|
|
26
28
|
## Hooks
|
|
27
29
|
|
|
28
|
-
- `useAgentico()` — panel state, `startOrchestrator(goal, opts)`, `commitEntityChat(subagentId, entityId, taskId, scopeId, params, title)`, `scope`.
|
|
30
|
+
- `useAgentico()` — panel state, `startOrchestrator(goal, opts)`, `startSubagent(subagentId, params, { taskId?, openingMessage? })` (use `entityChatTaskId(subagentId, entityId)` as taskId), `resumeEntityChat`, `commitEntityChat(subagentId, entityId, taskId, scopeId, params, title)`, `scope`.
|
|
29
31
|
- `useAgenticoPageContext(text, card?)` — what the user is looking at.
|
|
30
32
|
- `useSubagentComponentContext(subagentId, componentKey)` — anchor a component to a subagent.
|
|
31
33
|
- `useAgent(definition)` — run an AgentDefinition directly.
|
|
32
34
|
|
|
33
35
|
## AgentDefinition
|
|
34
36
|
|
|
35
|
-
buildContext (always runs) · buildPrompt · buildFollowUpPrompt · parseResponse (null → fallback) · fallback (required) · describeFallback? · getTools? · microPromptIds?
|
|
37
|
+
buildContext (always runs) · buildPrompt · buildFollowUpPrompt · parseResponse (null → fallback) · fallback (required) · describeFallback? · getTools? · microPromptIds? · executor?
|
|
38
|
+
|
|
39
|
+
`executor.turn({ params, context, text, first, signal, model?, onText(whole reply so far), onStep({ id, label, status }) }) → { text, result? }`; `executor.models(params)` / `defaultModel(params)` put the models of the executor in the composer: the turns go to the host instead of the AI provider; `text` is what the person typed (opening message on the first turn). The conversation is `remote: true`: no attachments, no fallback; the picker shows `remoteModels`.
|
|
40
|
+
|
|
41
|
+
Message parts (0.3.0): the turn also has `onText(text, { id?, done? })` (without id: the one reply, kept below steps and reasoning; with id: a text part of its own), `onReasoning(text, { id?, done?, duration? })` (whole text so far; shown closed, "Reasoned for N s", duration measured if not given), `onQuestions(questions, { id? }) → Promise<answers>` (questions `{ id?, header?, question, options?: [{ label, description? }], multiple?, recommended?, custom? }`; answers `[{ id?, header?, values: string[] }]`). Answers given while the turn runs resolve that promise; given later (or after a reload) they start a new turn with `text: ''` and `answers: { id, answers }` (id = the questions part id, default `questions-N`). Parts are kept in `AgentActivityEntry.parts` (`text` | `reasoning` | `tool` | `questions`, `detail` = the text) and saved with the chat; old chats without parts show `detail`. Rendered with `ChatMarkdown`, `ChatReasoning`, steps grouped in a `Tree`, `ChatQuestions` (needs `@llmnative/react` 1.34). `useAgent().answerQuestions(entryId, partId, answers)`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@llmnative/agentico",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Agentic chat orchestrator for @llmnative/react apps: a persistent panel that talks with the user and hands the work to host-registered subagents.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
},
|
|
49
49
|
"packageManager": "npm@11.11.0",
|
|
50
50
|
"peerDependencies": {
|
|
51
|
-
"@llmnative/react": "^1.
|
|
51
|
+
"@llmnative/react": "^1.34.0",
|
|
52
52
|
"react": "^19.2.0",
|
|
53
53
|
"react-dom": "^19.2.0",
|
|
54
54
|
"zod": "^3.24.1"
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
"zod-to-json-schema": "^3.24.1"
|
|
58
58
|
},
|
|
59
59
|
"devDependencies": {
|
|
60
|
-
"@llmnative/react": "^1.
|
|
60
|
+
"@llmnative/react": "^1.34.0",
|
|
61
61
|
"@phosphor-icons/react": "^2.1.10",
|
|
62
62
|
"@testing-library/jest-dom": "^7.0.1",
|
|
63
63
|
"@testing-library/react": "^16.3.3",
|