@gaunt-sloth/agent 2.0.0-alpha.9 → 2.0.0-beta.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 +73 -23
- package/cli-acp.js +21 -24
- package/dist/builtInToolsConfig.d.ts +17 -3
- package/dist/builtInToolsConfig.js +43 -41
- package/dist/builtInToolsConfig.js.map +1 -1
- package/dist/core/debugCapture.d.ts +7 -49
- package/dist/core/debugCapture.js +1 -1
- package/dist/core/debugCapture.js.map +1 -1
- package/dist/core/resolveAgentFactory.d.ts +10 -9
- package/dist/core/resolveAgentFactory.js +11 -12
- package/dist/core/resolveAgentFactory.js.map +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.js +7 -5
- package/dist/index.js.map +1 -1
- package/dist/mcp/OAuthClientProviderImpl.js.map +1 -1
- package/dist/mcp/tlsTrust.d.ts +61 -0
- package/dist/mcp/tlsTrust.js +109 -0
- package/dist/mcp/tlsTrust.js.map +1 -0
- package/dist/middleware/binaryContentInjectionMiddleware.d.ts +8 -1
- package/dist/middleware/binaryContentInjectionMiddleware.js +11 -2
- package/dist/middleware/binaryContentInjectionMiddleware.js.map +1 -1
- package/dist/middleware/frontendImageInjectionMiddleware.d.ts +107 -0
- package/dist/middleware/frontendImageInjectionMiddleware.js +167 -0
- package/dist/middleware/frontendImageInjectionMiddleware.js.map +1 -0
- package/dist/middleware/registry.js +49 -1
- package/dist/middleware/registry.js.map +1 -1
- package/dist/middleware/types.d.ts +16 -2
- package/dist/modules/a2a/A2AClientWrapper.d.ts +56 -1
- package/dist/modules/a2a/A2AClientWrapper.js +131 -31
- package/dist/modules/a2a/A2AClientWrapper.js.map +1 -1
- package/dist/modules/acp/acpAgentApp.d.ts +57 -0
- package/dist/modules/acp/acpAgentApp.js +378 -0
- package/dist/modules/acp/acpAgentApp.js.map +1 -0
- package/dist/modules/acp/acpAgentAppV1.d.ts +52 -0
- package/dist/modules/acp/acpAgentAppV1.js +307 -0
- package/dist/modules/acp/acpAgentAppV1.js.map +1 -0
- package/dist/modules/acp/acpCommon.d.ts +167 -0
- package/dist/modules/acp/acpCommon.js +282 -0
- package/dist/modules/acp/acpCommon.js.map +1 -0
- package/dist/modules/acp/acpPermissions.d.ts +65 -0
- package/dist/modules/acp/acpPermissions.js +162 -0
- package/dist/modules/acp/acpPermissions.js.map +1 -0
- package/dist/modules/acp/acpPermissionsV1.d.ts +45 -0
- package/dist/modules/acp/acpPermissionsV1.js +110 -0
- package/dist/modules/acp/acpPermissionsV1.js.map +1 -0
- package/dist/modules/acp/acpRouter.d.ts +41 -0
- package/dist/modules/acp/acpRouter.js +48 -0
- package/dist/modules/acp/acpRouter.js.map +1 -0
- package/dist/modules/acp/acpStdio.d.ts +47 -0
- package/dist/modules/acp/acpStdio.js +71 -0
- package/dist/modules/acp/acpStdio.js.map +1 -0
- package/dist/modules/acp/acpToolCalls.d.ts +93 -0
- package/dist/modules/acp/acpToolCalls.js +193 -0
- package/dist/modules/acp/acpToolCalls.js.map +1 -0
- package/dist/modules/acp/acpUpdates.d.ts +48 -0
- package/dist/modules/acp/acpUpdates.js +142 -0
- package/dist/modules/acp/acpUpdates.js.map +1 -0
- package/dist/modules/acp/acpUpdatesV1.d.ts +61 -0
- package/dist/modules/acp/acpUpdatesV1.js +162 -0
- package/dist/modules/acp/acpUpdatesV1.js.map +1 -0
- package/dist/modules/apiAgUiModule.d.ts +87 -0
- package/dist/modules/apiAgUiModule.js +193 -28
- package/dist/modules/apiAgUiModule.js.map +1 -1
- package/dist/modules/interactiveSessionModule.js +497 -51
- package/dist/modules/interactiveSessionModule.js.map +1 -1
- package/dist/modules/slashCommands.d.ts +582 -0
- package/dist/modules/slashCommands.js +1001 -0
- package/dist/modules/slashCommands.js.map +1 -0
- package/dist/resolvers.js +119 -9
- package/dist/resolvers.js.map +1 -1
- package/dist/tools/GthCustomToolkit.js +95 -11
- package/dist/tools/GthCustomToolkit.js.map +1 -1
- package/dist/tools/GthDevToolkit.d.ts +8 -5
- package/dist/tools/GthDevToolkit.js +89 -43
- package/dist/tools/GthDevToolkit.js.map +1 -1
- package/dist/tools/GthFileSystemToolkit.d.ts +63 -0
- package/dist/tools/GthFileSystemToolkit.js +506 -105
- package/dist/tools/GthFileSystemToolkit.js.map +1 -1
- package/dist/tools/McpResourceTool.d.ts +31 -0
- package/dist/tools/McpResourceTool.js +106 -0
- package/dist/tools/McpResourceTool.js.map +1 -0
- package/dist/tools/gthChecklistTool.d.ts +30 -0
- package/dist/tools/gthChecklistTool.js +80 -0
- package/dist/tools/gthChecklistTool.js.map +1 -0
- package/dist/tools/gthGrepTool.d.ts +54 -0
- package/dist/tools/gthGrepTool.js +482 -0
- package/dist/tools/gthGrepTool.js.map +1 -0
- package/dist/tools/shell/env.js +1 -1
- package/dist/tools/shell/env.js.map +1 -1
- package/dist/tools/shell/outputBuffer.js.map +1 -1
- package/dist/tools/shell/workDir.d.ts +1 -10
- package/dist/tools/shell/workDir.js +10 -36
- package/dist/tools/shell/workDir.js.map +1 -1
- package/dist/utils/mcpAuthError.d.ts +36 -0
- package/dist/utils/mcpAuthError.js +89 -0
- package/dist/utils/mcpAuthError.js.map +1 -0
- package/package.json +13 -13
- package/dist/core/GthDeepAgent.d.ts +0 -181
- package/dist/core/GthDeepAgent.js +0 -682
- package/dist/core/GthDeepAgent.js.map +0 -1
- package/dist/core/deepAgentPermissions.d.ts +0 -115
- package/dist/core/deepAgentPermissions.js +0 -282
- package/dist/core/deepAgentPermissions.js.map +0 -1
- package/dist/core/gthAcpServer.d.ts +0 -27
- package/dist/core/gthAcpServer.js +0 -82
- package/dist/core/gthAcpServer.js.map +0 -1
- package/dist/core/gthDeepAgentFactory.d.ts +0 -8
- package/dist/core/gthDeepAgentFactory.js +0 -9
- package/dist/core/gthDeepAgentFactory.js.map +0 -1
- package/dist/modules/acpModule.d.ts +0 -40
- package/dist/modules/acpModule.js +0 -78
- package/dist/modules/acpModule.js.map +0 -1
- package/dist/tools/shell/allowlist.d.ts +0 -11
- package/dist/tools/shell/allowlist.js +0 -12
- package/dist/tools/shell/allowlist.js.map +0 -1
- package/dist/tools/shell/arity.d.ts +0 -11
- package/dist/tools/shell/arity.js +0 -12
- package/dist/tools/shell/arity.js.map +0 -1
- package/dist/tools/shell/hardline.d.ts +0 -15
- package/dist/tools/shell/hardline.js +0 -88
- package/dist/tools/shell/hardline.js.map +0 -1
- package/dist/tools/shell/normalize.d.ts +0 -10
- package/dist/tools/shell/normalize.js +0 -11
- package/dist/tools/shell/normalize.js.map +0 -1
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @packageDocumentation
|
|
3
|
+
* Translates the agent runtime's typed {@link AgentStreamEvent} stream into ACP **v2**
|
|
4
|
+
* `session/update` payloads. (`acpUpdatesV1.ts` is the v1 half; both extend the tool-call tracker
|
|
5
|
+
* in `acpToolCalls.ts`, which is where the parts that are not about the dialect live.)
|
|
6
|
+
*
|
|
7
|
+
* Kept as a pure, stateful-but-transport-free mapper rather than inlined in the request handlers
|
|
8
|
+
* for two reasons. It is the half of the ACP surface with real logic — message identity, tool-call
|
|
9
|
+
* upserts, which events open and close a run — so it is the half worth testing without a
|
|
10
|
+
* connection. And the `session/update` **upsert semantics** live here and nowhere else: what the
|
|
11
|
+
* mapper omits is what a client must leave unchanged, so a mapper that re-sent a full replacement
|
|
12
|
+
* on every update would silently erase fields a client had already rendered.
|
|
13
|
+
*
|
|
14
|
+
* ## The upsert contract this mapper is written against
|
|
15
|
+
*
|
|
16
|
+
* A client applies updates per id, in arrival order: an omitted field leaves the stored value
|
|
17
|
+
* unchanged, `null` clears it, a concrete value replaces it, and a chunk appends. The first
|
|
18
|
+
* `tool_call_update` a client sees for a `toolCallId` CREATES the tool call. So the mapper sends
|
|
19
|
+
* the descriptive fields **once**, on the creating update, and every later update for that call
|
|
20
|
+
* carries only what actually changed — which is what makes a client's rendering of a running tool
|
|
21
|
+
* call correct rather than flickering back to a bare id.
|
|
22
|
+
*/
|
|
23
|
+
import type { SessionUpdate } from '@agentclientprotocol/sdk/experimental/v2';
|
|
24
|
+
import type { AgentStreamEvent } from '@gaunt-sloth/core/core/types.js';
|
|
25
|
+
import { AcpToolCallTracker } from '#src/modules/acp/acpToolCalls.js';
|
|
26
|
+
/**
|
|
27
|
+
* Turns one agent run's event stream into ACP v2 `session/update` payloads.
|
|
28
|
+
*
|
|
29
|
+
* One instance per prompt turn: it holds the message identity of the assistant text run and of the
|
|
30
|
+
* reasoning run, plus the accumulated argument text per tool call. A fresh instance per turn is
|
|
31
|
+
* what makes a new turn a new `messageId`, which is how a client tells two messages apart.
|
|
32
|
+
*/
|
|
33
|
+
export declare class AcpUpdateMapper extends AcpToolCallTracker {
|
|
34
|
+
/**
|
|
35
|
+
* `messageId` of the assistant text message currently being streamed, or `null` when no text run
|
|
36
|
+
* is open. Cleared whenever something else interrupts the text (a tool call, a reasoning block),
|
|
37
|
+
* so the text that resumes afterwards is a NEW message rather than an append to the one the
|
|
38
|
+
* client already considers finished.
|
|
39
|
+
*/
|
|
40
|
+
private assistantMessageId;
|
|
41
|
+
/** `messageId` of the reasoning message currently being streamed, or `null` outside one. */
|
|
42
|
+
private thoughtMessageId;
|
|
43
|
+
/**
|
|
44
|
+
* The `session/update` payloads one runtime event produces — usually one, sometimes none
|
|
45
|
+
* (`tool_args`, which only accumulates; the reasoning boundaries, which only move state).
|
|
46
|
+
*/
|
|
47
|
+
map(event: AgentStreamEvent): SessionUpdate[];
|
|
48
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @packageDocumentation
|
|
3
|
+
* Translates the agent runtime's typed {@link AgentStreamEvent} stream into ACP **v2**
|
|
4
|
+
* `session/update` payloads. (`acpUpdatesV1.ts` is the v1 half; both extend the tool-call tracker
|
|
5
|
+
* in `acpToolCalls.ts`, which is where the parts that are not about the dialect live.)
|
|
6
|
+
*
|
|
7
|
+
* Kept as a pure, stateful-but-transport-free mapper rather than inlined in the request handlers
|
|
8
|
+
* for two reasons. It is the half of the ACP surface with real logic — message identity, tool-call
|
|
9
|
+
* upserts, which events open and close a run — so it is the half worth testing without a
|
|
10
|
+
* connection. And the `session/update` **upsert semantics** live here and nowhere else: what the
|
|
11
|
+
* mapper omits is what a client must leave unchanged, so a mapper that re-sent a full replacement
|
|
12
|
+
* on every update would silently erase fields a client had already rendered.
|
|
13
|
+
*
|
|
14
|
+
* ## The upsert contract this mapper is written against
|
|
15
|
+
*
|
|
16
|
+
* A client applies updates per id, in arrival order: an omitted field leaves the stored value
|
|
17
|
+
* unchanged, `null` clears it, a concrete value replaces it, and a chunk appends. The first
|
|
18
|
+
* `tool_call_update` a client sees for a `toolCallId` CREATES the tool call. So the mapper sends
|
|
19
|
+
* the descriptive fields **once**, on the creating update, and every later update for that call
|
|
20
|
+
* carries only what actually changed — which is what makes a client's rendering of a running tool
|
|
21
|
+
* call correct rather than flickering back to a bare id.
|
|
22
|
+
*/
|
|
23
|
+
import { randomUUID } from 'node:crypto';
|
|
24
|
+
import { AcpToolCallTracker, toolKindFor } from '#src/modules/acp/acpToolCalls.js';
|
|
25
|
+
/** One text content block, the shape both message chunks and tool content wrap. */
|
|
26
|
+
function textBlock(text) {
|
|
27
|
+
return { type: 'text', text };
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Turns one agent run's event stream into ACP v2 `session/update` payloads.
|
|
31
|
+
*
|
|
32
|
+
* One instance per prompt turn: it holds the message identity of the assistant text run and of the
|
|
33
|
+
* reasoning run, plus the accumulated argument text per tool call. A fresh instance per turn is
|
|
34
|
+
* what makes a new turn a new `messageId`, which is how a client tells two messages apart.
|
|
35
|
+
*/
|
|
36
|
+
export class AcpUpdateMapper extends AcpToolCallTracker {
|
|
37
|
+
/**
|
|
38
|
+
* `messageId` of the assistant text message currently being streamed, or `null` when no text run
|
|
39
|
+
* is open. Cleared whenever something else interrupts the text (a tool call, a reasoning block),
|
|
40
|
+
* so the text that resumes afterwards is a NEW message rather than an append to the one the
|
|
41
|
+
* client already considers finished.
|
|
42
|
+
*/
|
|
43
|
+
assistantMessageId = null;
|
|
44
|
+
/** `messageId` of the reasoning message currently being streamed, or `null` outside one. */
|
|
45
|
+
thoughtMessageId = null;
|
|
46
|
+
/**
|
|
47
|
+
* The `session/update` payloads one runtime event produces — usually one, sometimes none
|
|
48
|
+
* (`tool_args`, which only accumulates; the reasoning boundaries, which only move state).
|
|
49
|
+
*/
|
|
50
|
+
map(event) {
|
|
51
|
+
switch (event.type) {
|
|
52
|
+
case 'text': {
|
|
53
|
+
this.assistantMessageId ??= randomUUID();
|
|
54
|
+
return [
|
|
55
|
+
{
|
|
56
|
+
sessionUpdate: 'agent_message_chunk',
|
|
57
|
+
messageId: this.assistantMessageId,
|
|
58
|
+
content: textBlock(event.delta),
|
|
59
|
+
},
|
|
60
|
+
];
|
|
61
|
+
}
|
|
62
|
+
case 'reasoning_start': {
|
|
63
|
+
this.assistantMessageId = null;
|
|
64
|
+
this.thoughtMessageId = randomUUID();
|
|
65
|
+
return [];
|
|
66
|
+
}
|
|
67
|
+
case 'reasoning_delta': {
|
|
68
|
+
this.thoughtMessageId ??= randomUUID();
|
|
69
|
+
return [
|
|
70
|
+
{
|
|
71
|
+
sessionUpdate: 'agent_thought_chunk',
|
|
72
|
+
messageId: this.thoughtMessageId,
|
|
73
|
+
content: textBlock(event.delta),
|
|
74
|
+
},
|
|
75
|
+
];
|
|
76
|
+
}
|
|
77
|
+
case 'reasoning_end': {
|
|
78
|
+
this.thoughtMessageId = null;
|
|
79
|
+
return [];
|
|
80
|
+
}
|
|
81
|
+
case 'tool_start': {
|
|
82
|
+
// A tool call ends the open text run: the text that follows the tool is a separate message.
|
|
83
|
+
this.assistantMessageId = null;
|
|
84
|
+
this.trackToolStart(event.id, event.name);
|
|
85
|
+
// The CREATING update — the first one a client sees for this id. Everything descriptive is
|
|
86
|
+
// sent here and never resent, because from here on omission means "unchanged".
|
|
87
|
+
return [
|
|
88
|
+
{
|
|
89
|
+
sessionUpdate: 'tool_call_update',
|
|
90
|
+
toolCallId: event.id,
|
|
91
|
+
name: event.name,
|
|
92
|
+
title: event.name,
|
|
93
|
+
kind: toolKindFor(event.name),
|
|
94
|
+
status: 'pending',
|
|
95
|
+
},
|
|
96
|
+
];
|
|
97
|
+
}
|
|
98
|
+
case 'tool_args': {
|
|
99
|
+
this.appendToolArgs(event.id, event.delta);
|
|
100
|
+
return [];
|
|
101
|
+
}
|
|
102
|
+
case 'tool_end': {
|
|
103
|
+
const rawInput = this.rawInputFor(event.id);
|
|
104
|
+
// Status only (plus the arguments, once they are complete). No title, no kind, no name —
|
|
105
|
+
// the client keeps the ones the creating update set.
|
|
106
|
+
return [
|
|
107
|
+
{
|
|
108
|
+
sessionUpdate: 'tool_call_update',
|
|
109
|
+
toolCallId: event.id,
|
|
110
|
+
status: 'in_progress',
|
|
111
|
+
...(rawInput === undefined ? {} : { rawInput }),
|
|
112
|
+
},
|
|
113
|
+
];
|
|
114
|
+
}
|
|
115
|
+
case 'tool_output': {
|
|
116
|
+
// Live output from an executing tool. A CHUNK, not an update: it appends to whatever the
|
|
117
|
+
// client has for this call, where a `tool_call_update` carrying `content` would replace it.
|
|
118
|
+
if (event.id === undefined)
|
|
119
|
+
return [];
|
|
120
|
+
return [
|
|
121
|
+
{
|
|
122
|
+
sessionUpdate: 'tool_call_content_chunk',
|
|
123
|
+
toolCallId: event.id,
|
|
124
|
+
content: { type: 'content', content: textBlock(event.chunk) },
|
|
125
|
+
},
|
|
126
|
+
];
|
|
127
|
+
}
|
|
128
|
+
case 'tool_result': {
|
|
129
|
+
this.trackToolSettled(event.id);
|
|
130
|
+
return [
|
|
131
|
+
{
|
|
132
|
+
sessionUpdate: 'tool_call_update',
|
|
133
|
+
toolCallId: event.id,
|
|
134
|
+
status: event.isError ? 'failed' : 'completed',
|
|
135
|
+
content: [{ type: 'content', content: textBlock(event.content) }],
|
|
136
|
+
},
|
|
137
|
+
];
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=acpUpdates.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"acpUpdates.js","sourceRoot":"","sources":["../../../src/modules/acp/acpUpdates.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @packageDocumentation
|
|
3
|
+
* Translates the agent runtime's typed {@link AgentStreamEvent} stream into ACP **v1**
|
|
4
|
+
* `session/update` payloads. (`acpUpdates.ts` is the v2 half.)
|
|
5
|
+
*
|
|
6
|
+
* ## Where v1 differs, and why the mapper could not be shared
|
|
7
|
+
*
|
|
8
|
+
* The two dialects report the same events, but not with the same messages, and each difference
|
|
9
|
+
* changes what a conforming client ends up rendering:
|
|
10
|
+
*
|
|
11
|
+
* - **v1 has a distinct `tool_call` update that CREATES a tool call**, where v2 folded creation into
|
|
12
|
+
* the first `tool_call_update`. Sending only `tool_call_update`s on v1 would leave a client
|
|
13
|
+
* patching a call it was never told about.
|
|
14
|
+
* - **v1 has no `tool_call_content_chunk`.** Its `tool_call_update.content` REPLACES the whole
|
|
15
|
+
* collection, so live tool output is streamed by accumulating it here and resending the
|
|
16
|
+
* collection — the opposite of v2, where each chunk appends and only a `tool_call_update` replaces.
|
|
17
|
+
* - **v1 has no `state_update`.** There is nowhere to report `running` / `requires_action` / `idle`,
|
|
18
|
+
* and no notification carries the stop reason: the turn's outcome is the `session/prompt`
|
|
19
|
+
* RESPONSE. See `acpAgentAppV1.ts`.
|
|
20
|
+
* - **A prompt turn does not echo the user's message.** v1 reserves replay for `session/load`; a
|
|
21
|
+
* client already renders what it sent, so echoing it would draw the message twice.
|
|
22
|
+
*
|
|
23
|
+
* What is NOT duplicated is tool-call identity — the kind hint, the argument reassembly, and the
|
|
24
|
+
* pairing that lets a permission request name the call it is about all come from
|
|
25
|
+
* {@link AcpToolCallTracker}.
|
|
26
|
+
*/
|
|
27
|
+
import type { SessionUpdate } from '@agentclientprotocol/sdk';
|
|
28
|
+
import type { AgentStreamEvent } from '@gaunt-sloth/core/core/types.js';
|
|
29
|
+
import { AcpToolCallTracker } from '#src/modules/acp/acpToolCalls.js';
|
|
30
|
+
/**
|
|
31
|
+
* Turns one agent run's event stream into ACP v1 `session/update` payloads.
|
|
32
|
+
*
|
|
33
|
+
* One instance per prompt turn: it holds the message identity of the assistant text run and of the
|
|
34
|
+
* reasoning run, the accumulated argument text per tool call, and the output accumulated for each
|
|
35
|
+
* running tool. A fresh instance per turn is what makes a new turn a new `messageId`, which is how
|
|
36
|
+
* a client tells two messages apart.
|
|
37
|
+
*/
|
|
38
|
+
export declare class AcpV1UpdateMapper extends AcpToolCallTracker {
|
|
39
|
+
/**
|
|
40
|
+
* `messageId` of the assistant text message currently being streamed, or `null` when no text run
|
|
41
|
+
* is open. Cleared whenever something else interrupts the text (a tool call, a reasoning block),
|
|
42
|
+
* so the text that resumes afterwards is a NEW message rather than an append to the one the
|
|
43
|
+
* client already considers finished.
|
|
44
|
+
*/
|
|
45
|
+
private assistantMessageId;
|
|
46
|
+
/** `messageId` of the reasoning message currently being streamed, or `null` outside one. */
|
|
47
|
+
private thoughtMessageId;
|
|
48
|
+
/**
|
|
49
|
+
* Live output accumulated per tool call, because v1 can only REPLACE a tool call's content.
|
|
50
|
+
*
|
|
51
|
+
* With no append-a-chunk update in the dialect, the only way to show a tool's output as it
|
|
52
|
+
* arrives is to resend everything seen so far; keeping the collection here is what makes each
|
|
53
|
+
* replacement a superset of the last rather than a flicker back to the newest line alone.
|
|
54
|
+
*/
|
|
55
|
+
private readonly toolOutput;
|
|
56
|
+
/**
|
|
57
|
+
* The `session/update` payloads one runtime event produces — usually one, sometimes none
|
|
58
|
+
* (`tool_args`, which only accumulates; the reasoning boundaries, which only move state).
|
|
59
|
+
*/
|
|
60
|
+
map(event: AgentStreamEvent): SessionUpdate[];
|
|
61
|
+
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @packageDocumentation
|
|
3
|
+
* Translates the agent runtime's typed {@link AgentStreamEvent} stream into ACP **v1**
|
|
4
|
+
* `session/update` payloads. (`acpUpdates.ts` is the v2 half.)
|
|
5
|
+
*
|
|
6
|
+
* ## Where v1 differs, and why the mapper could not be shared
|
|
7
|
+
*
|
|
8
|
+
* The two dialects report the same events, but not with the same messages, and each difference
|
|
9
|
+
* changes what a conforming client ends up rendering:
|
|
10
|
+
*
|
|
11
|
+
* - **v1 has a distinct `tool_call` update that CREATES a tool call**, where v2 folded creation into
|
|
12
|
+
* the first `tool_call_update`. Sending only `tool_call_update`s on v1 would leave a client
|
|
13
|
+
* patching a call it was never told about.
|
|
14
|
+
* - **v1 has no `tool_call_content_chunk`.** Its `tool_call_update.content` REPLACES the whole
|
|
15
|
+
* collection, so live tool output is streamed by accumulating it here and resending the
|
|
16
|
+
* collection — the opposite of v2, where each chunk appends and only a `tool_call_update` replaces.
|
|
17
|
+
* - **v1 has no `state_update`.** There is nowhere to report `running` / `requires_action` / `idle`,
|
|
18
|
+
* and no notification carries the stop reason: the turn's outcome is the `session/prompt`
|
|
19
|
+
* RESPONSE. See `acpAgentAppV1.ts`.
|
|
20
|
+
* - **A prompt turn does not echo the user's message.** v1 reserves replay for `session/load`; a
|
|
21
|
+
* client already renders what it sent, so echoing it would draw the message twice.
|
|
22
|
+
*
|
|
23
|
+
* What is NOT duplicated is tool-call identity — the kind hint, the argument reassembly, and the
|
|
24
|
+
* pairing that lets a permission request name the call it is about all come from
|
|
25
|
+
* {@link AcpToolCallTracker}.
|
|
26
|
+
*/
|
|
27
|
+
import { randomUUID } from 'node:crypto';
|
|
28
|
+
import { AcpToolCallTracker, toolKindFor } from '#src/modules/acp/acpToolCalls.js';
|
|
29
|
+
/** One text content block, the shape both message chunks and tool content wrap. */
|
|
30
|
+
function textBlock(text) {
|
|
31
|
+
return { type: 'text', text };
|
|
32
|
+
}
|
|
33
|
+
/** One tool-call content entry wrapping a text block. */
|
|
34
|
+
function toolText(text) {
|
|
35
|
+
return { type: 'content', content: textBlock(text) };
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Turns one agent run's event stream into ACP v1 `session/update` payloads.
|
|
39
|
+
*
|
|
40
|
+
* One instance per prompt turn: it holds the message identity of the assistant text run and of the
|
|
41
|
+
* reasoning run, the accumulated argument text per tool call, and the output accumulated for each
|
|
42
|
+
* running tool. A fresh instance per turn is what makes a new turn a new `messageId`, which is how
|
|
43
|
+
* a client tells two messages apart.
|
|
44
|
+
*/
|
|
45
|
+
export class AcpV1UpdateMapper extends AcpToolCallTracker {
|
|
46
|
+
/**
|
|
47
|
+
* `messageId` of the assistant text message currently being streamed, or `null` when no text run
|
|
48
|
+
* is open. Cleared whenever something else interrupts the text (a tool call, a reasoning block),
|
|
49
|
+
* so the text that resumes afterwards is a NEW message rather than an append to the one the
|
|
50
|
+
* client already considers finished.
|
|
51
|
+
*/
|
|
52
|
+
assistantMessageId = null;
|
|
53
|
+
/** `messageId` of the reasoning message currently being streamed, or `null` outside one. */
|
|
54
|
+
thoughtMessageId = null;
|
|
55
|
+
/**
|
|
56
|
+
* Live output accumulated per tool call, because v1 can only REPLACE a tool call's content.
|
|
57
|
+
*
|
|
58
|
+
* With no append-a-chunk update in the dialect, the only way to show a tool's output as it
|
|
59
|
+
* arrives is to resend everything seen so far; keeping the collection here is what makes each
|
|
60
|
+
* replacement a superset of the last rather than a flicker back to the newest line alone.
|
|
61
|
+
*/
|
|
62
|
+
toolOutput = new Map();
|
|
63
|
+
/**
|
|
64
|
+
* The `session/update` payloads one runtime event produces — usually one, sometimes none
|
|
65
|
+
* (`tool_args`, which only accumulates; the reasoning boundaries, which only move state).
|
|
66
|
+
*/
|
|
67
|
+
map(event) {
|
|
68
|
+
switch (event.type) {
|
|
69
|
+
case 'text': {
|
|
70
|
+
this.assistantMessageId ??= randomUUID();
|
|
71
|
+
return [
|
|
72
|
+
{
|
|
73
|
+
sessionUpdate: 'agent_message_chunk',
|
|
74
|
+
messageId: this.assistantMessageId,
|
|
75
|
+
content: textBlock(event.delta),
|
|
76
|
+
},
|
|
77
|
+
];
|
|
78
|
+
}
|
|
79
|
+
case 'reasoning_start': {
|
|
80
|
+
this.assistantMessageId = null;
|
|
81
|
+
this.thoughtMessageId = randomUUID();
|
|
82
|
+
return [];
|
|
83
|
+
}
|
|
84
|
+
case 'reasoning_delta': {
|
|
85
|
+
this.thoughtMessageId ??= randomUUID();
|
|
86
|
+
return [
|
|
87
|
+
{
|
|
88
|
+
sessionUpdate: 'agent_thought_chunk',
|
|
89
|
+
messageId: this.thoughtMessageId,
|
|
90
|
+
content: textBlock(event.delta),
|
|
91
|
+
},
|
|
92
|
+
];
|
|
93
|
+
}
|
|
94
|
+
case 'reasoning_end': {
|
|
95
|
+
this.thoughtMessageId = null;
|
|
96
|
+
return [];
|
|
97
|
+
}
|
|
98
|
+
case 'tool_start': {
|
|
99
|
+
// A tool call ends the open text run: the text that follows the tool is a separate message.
|
|
100
|
+
this.assistantMessageId = null;
|
|
101
|
+
this.trackToolStart(event.id, event.name);
|
|
102
|
+
this.toolOutput.set(event.id, []);
|
|
103
|
+
// v1's CREATE. `title` is required here, and everything descriptive is sent with it; the
|
|
104
|
+
// later `tool_call_update`s carry only what changed.
|
|
105
|
+
return [
|
|
106
|
+
{
|
|
107
|
+
sessionUpdate: 'tool_call',
|
|
108
|
+
toolCallId: event.id,
|
|
109
|
+
name: event.name,
|
|
110
|
+
title: event.name,
|
|
111
|
+
kind: toolKindFor(event.name),
|
|
112
|
+
status: 'pending',
|
|
113
|
+
},
|
|
114
|
+
];
|
|
115
|
+
}
|
|
116
|
+
case 'tool_args': {
|
|
117
|
+
this.appendToolArgs(event.id, event.delta);
|
|
118
|
+
return [];
|
|
119
|
+
}
|
|
120
|
+
case 'tool_end': {
|
|
121
|
+
const rawInput = this.rawInputFor(event.id);
|
|
122
|
+
// Status only (plus the arguments, once they are complete). No title, no kind, no name —
|
|
123
|
+
// the client keeps the ones the creating update set.
|
|
124
|
+
return [
|
|
125
|
+
{
|
|
126
|
+
sessionUpdate: 'tool_call_update',
|
|
127
|
+
toolCallId: event.id,
|
|
128
|
+
status: 'in_progress',
|
|
129
|
+
...(rawInput === undefined ? {} : { rawInput }),
|
|
130
|
+
},
|
|
131
|
+
];
|
|
132
|
+
}
|
|
133
|
+
case 'tool_output': {
|
|
134
|
+
if (event.id === undefined)
|
|
135
|
+
return [];
|
|
136
|
+
const collected = this.toolOutput.get(event.id) ?? [];
|
|
137
|
+
collected.push(toolText(event.chunk));
|
|
138
|
+
this.toolOutput.set(event.id, collected);
|
|
139
|
+
// The whole collection, because v1 replaces rather than appends. Copied so a later push
|
|
140
|
+
// cannot mutate an update already handed to the transport.
|
|
141
|
+
return [
|
|
142
|
+
{ sessionUpdate: 'tool_call_update', toolCallId: event.id, content: [...collected] },
|
|
143
|
+
];
|
|
144
|
+
}
|
|
145
|
+
case 'tool_result': {
|
|
146
|
+
this.trackToolSettled(event.id);
|
|
147
|
+
this.toolOutput.delete(event.id);
|
|
148
|
+
// The result REPLACES whatever live output was showing — it is the authoritative record of
|
|
149
|
+
// what the tool produced, and the same thing v2's final update does.
|
|
150
|
+
return [
|
|
151
|
+
{
|
|
152
|
+
sessionUpdate: 'tool_call_update',
|
|
153
|
+
toolCallId: event.id,
|
|
154
|
+
status: event.isError ? 'failed' : 'completed',
|
|
155
|
+
content: [toolText(event.content)],
|
|
156
|
+
},
|
|
157
|
+
];
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
//# sourceMappingURL=acpUpdatesV1.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"acpUpdatesV1.js","sourceRoot":"","sources":["../../../src/modules/acp/acpUpdatesV1.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,2 +1,89 @@
|
|
|
1
1
|
import { GthConfig } from '@gaunt-sloth/core/config.js';
|
|
2
|
+
import type { BaseMessage } from '@langchain/core/messages';
|
|
3
|
+
/** An AG-UI wire message as received on the run input (the shape {@link convertMessage} accepts). */
|
|
4
|
+
type AgUiWireMessage = {
|
|
5
|
+
role: string;
|
|
6
|
+
content?: string;
|
|
7
|
+
id: string;
|
|
8
|
+
toolCalls?: Array<{
|
|
9
|
+
id: string;
|
|
10
|
+
type: string;
|
|
11
|
+
function: {
|
|
12
|
+
name: string;
|
|
13
|
+
arguments: string;
|
|
14
|
+
};
|
|
15
|
+
}>;
|
|
16
|
+
toolCallId?: string;
|
|
17
|
+
};
|
|
18
|
+
/** Per-message options for {@link convertMessage}. */
|
|
19
|
+
interface ConvertMessageOptions {
|
|
20
|
+
/**
|
|
21
|
+
* Whether an assistant text-emitted tool call may be PROMOTED to a native tool_call for this
|
|
22
|
+
* message. Defaults to `true` (the EXT-35 behaviour). {@link convertMessages} sets this to `false`
|
|
23
|
+
* for a DANGLING history call (one not followed by its tool result) so a stalled replayed call
|
|
24
|
+
* stays plain text — see EXT-43 and that function's doc.
|
|
25
|
+
*/
|
|
26
|
+
allowTextCallPromotion?: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* RC-32: the tool NAME to stamp on a `role:'tool'` message, resolved by
|
|
29
|
+
* {@link convertMessages} from the parenting assistant `tool_call`. The AG-UI wire format does
|
|
30
|
+
* not carry it, and a `ToolMessage` without a name is invisible to every result-inspecting
|
|
31
|
+
* middleware — `frontend-image-injection` keys on `msg.name === 'capture_image'`, so a replayed
|
|
32
|
+
* capture silently stopped producing a vision block on every turn after the capture itself.
|
|
33
|
+
* Absent on the standalone {@link convertMessage} path (a queued resume message has no history
|
|
34
|
+
* to resolve against), where the name is simply unknown.
|
|
35
|
+
*/
|
|
36
|
+
toolName?: string;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Convert AG-UI message format to LangChain BaseMessage.
|
|
40
|
+
*
|
|
41
|
+
* `allowedToolNames` is the set of tool names bound to this run (config.tools + any run-input
|
|
42
|
+
* client tools). It gates EXT-35 plain-text tool-call repair on the assistant branch: an incoming
|
|
43
|
+
* assistant message with NO native `toolCalls` whose content is a STANDALONE text-emitted call
|
|
44
|
+
* (bracket / `<function=…>` / Harmony — the dialects small/local models produce) is promoted to a
|
|
45
|
+
* native tool_call so a replayed history turn is a real tool call rather than inert prose. An empty
|
|
46
|
+
* (or absent) allow-list promotes nothing — the prose-safe default. This runs alongside
|
|
47
|
+
* {@link parseToolArguments} (which rescues malformed args on an ALREADY-native tool_call).
|
|
48
|
+
*
|
|
49
|
+
* EXT-43: `options.allowTextCallPromotion` (default `true`) lets a caller suppress promotion for a
|
|
50
|
+
* single message; {@link convertMessages} uses it to leave a DANGLING history call as text.
|
|
51
|
+
*/
|
|
52
|
+
export declare function convertMessage(msg: AgUiWireMessage, allowedToolNames?: Set<string>, options?: ConvertMessageOptions): BaseMessage;
|
|
53
|
+
/**
|
|
54
|
+
* Convert a whole AG-UI history array to LangChain messages, applying TWO symmetric replay guards
|
|
55
|
+
* so a poisoned history can never abort every subsequent turn on the thread.
|
|
56
|
+
*
|
|
57
|
+
* EXT-43 (forward, dangling-CALL): EXT-35's per-message promotion is unconditional, which is correct
|
|
58
|
+
* for a call that WILL be executed this turn. But when replaying HISTORY, promoting a STALLED text
|
|
59
|
+
* call (one the client recorded but that never ran) yields an `AIMessage` with `tool_calls` and NO
|
|
60
|
+
* following `tool_result` — a shape a strict provider (Anthropic) 400s on, where the pre-EXT-35
|
|
61
|
+
* plain text was valid. So promotion is allowed ONLY when the assistant message is immediately
|
|
62
|
+
* followed by a `tool` result message; a dangling call stays plain text (`allowTextCallPromotion:
|
|
63
|
+
* false`).
|
|
64
|
+
*
|
|
65
|
+
* RC-18 (backward, orphan-RESULT): the mirror image. A replayed `role:'tool'` message whose matching
|
|
66
|
+
* `tool_call` id is absent from EVERY PRECEDING assistant message is an ORPHAN — converting it to a
|
|
67
|
+
* `ToolMessage` yields a tool result with no preceding `AIMessage.tool_calls`, which the same strict
|
|
68
|
+
* provider 400s on (`Invalid parameter: messages with role 'tool' must be a response to a preceding
|
|
69
|
+
* message with 'tool_calls'`, INVALID_TOOL_RESULTS). Such orphans arise when a terminal
|
|
70
|
+
* (`returnDirect`) tool call's result is reconstructed by the client without its parenting assistant
|
|
71
|
+
* `tool_call`. We DROP the orphan (match on tool_call_id, NOT adjacency; keep genuine pairs; do NOT
|
|
72
|
+
* fabricate a synthetic call — mirroring EXT-43's demote-don't-invent spirit). Ids are accumulated
|
|
73
|
+
* in iteration order, so a result whose matching call appears only LATER is still an orphan.
|
|
74
|
+
*
|
|
75
|
+
* RC-32 (tool NAME restoration): the AG-UI wire message for a `tool` result carries `toolCallId`
|
|
76
|
+
* but no tool name, so a naively-converted `ToolMessage` has none — and every middleware that
|
|
77
|
+
* inspects results by name is blind to it. `frontend-image-injection` keys on
|
|
78
|
+
* `msg.name === 'capture_image'`, so a captured photo reached the model on the resume turn (where
|
|
79
|
+
* the graph builds the ToolMessage itself, with a name) and then vanished from every later turn,
|
|
80
|
+
* leaving the model to answer questions about a picture it could no longer see. The name is
|
|
81
|
+
* recoverable from the parenting assistant `tool_call`, which this function already walks for the
|
|
82
|
+
* RC-18 guard, so it is resolved there and stamped back on.
|
|
83
|
+
*
|
|
84
|
+
* The live middleware path (`GthLangChainAgent`, fixing the CURRENT turn) is unaffected — the two
|
|
85
|
+
* guards and the name restoration are history-replay only.
|
|
86
|
+
*/
|
|
87
|
+
export declare function convertMessages(messages: AgUiWireMessage[], allowedToolNames?: Set<string>): BaseMessage[];
|
|
2
88
|
export declare function startAgUiServer(config: GthConfig, port: number): Promise<void>;
|
|
89
|
+
export {};
|