@myagentroam/agent 0.9.62 → 0.9.65
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/LICENSE +202 -0
- package/NOTICE +4 -0
- package/dist/cli/codex-auth-file.js +268 -3
- package/dist/cli/main.js +431 -2
- package/dist/error.js +66 -2
- package/dist/host/contracts.js +20 -2
- package/dist/host/local-agent-host.js +146 -2
- package/dist/host/local-process-executor.js +39 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +12 -2
- package/dist/model/anthropic-messages.js +474 -2
- package/dist/model/anthropic-thinking.js +33 -2
- package/dist/model/catalog-file.js +88 -2
- package/dist/model/configuration.js +137 -2
- package/dist/model/contracts.d.ts +2 -0
- package/dist/model/contracts.js +1 -2
- package/dist/model/http.js +529 -2
- package/dist/model/openai-responses.d.ts +2 -0
- package/dist/model/openai-responses.js +1602 -2
- package/dist/model/runtime-credential.js +35 -2
- package/dist/model/sse.js +199 -2
- package/dist/prompts/compact.js +25 -2
- package/dist/prompts/core.js +37 -2
- package/dist/prompts/execution-budget.js +10 -2
- package/dist/prompts/index.d.ts +3 -2
- package/dist/prompts/index.js +47 -2
- package/dist/prompts/modes.js +25 -2
- package/dist/prompts/output.js +1 -2
- package/dist/prompts/resources.d.ts +1 -1
- package/dist/prompts/resources.js +31 -2
- package/dist/prompts/subagent.d.ts +6 -0
- package/dist/prompts/subagent.js +29 -2
- package/dist/prompts/title.js +9 -2
- package/dist/prompts/workflow.js +32 -2
- package/dist/runtime/async-queue.js +62 -2
- package/dist/runtime/builtin-skills.js +19 -2
- package/dist/runtime/byted-ark-imagegen-skill.js +18 -2
- package/dist/runtime/codex-imagegen-skill.js +35 -2
- package/dist/runtime/compact.js +225 -2
- package/dist/runtime/context-gc-runtime.d.ts +35 -0
- package/dist/runtime/context-gc-runtime.js +163 -0
- package/dist/runtime/context-gc-tokens.d.ts +24 -0
- package/dist/runtime/context-gc-tokens.js +83 -0
- package/dist/runtime/context-gc.d.ts +66 -0
- package/dist/runtime/context-gc.js +909 -0
- package/dist/runtime/context-projection.d.ts +23 -0
- package/dist/runtime/context-projection.js +203 -0
- package/dist/runtime/environment-context.js +41 -2
- package/dist/runtime/execution-policy.js +13 -2
- package/dist/runtime/instructions.js +298 -2
- package/dist/runtime/normalize.js +40 -2
- package/dist/runtime/plan-output.js +95 -2
- package/dist/runtime/rollout-budget.d.ts +2 -0
- package/dist/runtime/rollout-budget.js +92 -2
- package/dist/runtime/token-budget.d.ts +2 -0
- package/dist/runtime/token-budget.js +44 -2
- package/dist/sdk/agent.js +1804 -2
- package/dist/sdk/types.d.ts +15 -5
- package/dist/sdk/types.js +1 -2
- package/dist/session/catalog.d.ts +5 -4
- package/dist/session/catalog.js +133 -2
- package/dist/session/context-gc-checkpoint.d.ts +148 -0
- package/dist/session/context-gc-checkpoint.js +665 -0
- package/dist/session/context-gc-replacements.d.ts +52 -0
- package/dist/session/context-gc-replacements.js +226 -0
- package/dist/session/context-gc-subagent.d.ts +43 -0
- package/dist/session/context-gc-subagent.js +267 -0
- package/dist/session/jsonl-store.d.ts +42 -4
- package/dist/session/jsonl-store.js +1657 -2
- package/dist/subagent/scheduler.d.ts +35 -2
- package/dist/subagent/scheduler.js +249 -2
- package/dist/subagent/session-controller.d.ts +94 -0
- package/dist/subagent/session-controller.js +675 -0
- package/dist/tools/agent-cancel.js +28 -2
- package/dist/tools/{task.d.ts → agent-message.d.ts} +2 -2
- package/dist/tools/agent-message.js +51 -0
- package/dist/tools/agent-start.js +84 -2
- package/dist/tools/agent-wait.js +53 -2
- package/dist/tools/apply-patch.js +478 -2
- package/dist/tools/code-mode.js +370 -2
- package/dist/tools/codex-image-gen.js +312 -2
- package/dist/tools/exec.js +455 -2
- package/dist/tools/execution-limits.d.ts +1 -0
- package/dist/tools/execution-limits.js +44 -2
- package/dist/tools/local-registry.js +233 -2
- package/dist/tools/local-workspace-tools.js +28 -2
- package/dist/tools/question.js +124 -2
- package/dist/tools/read-context.d.ts +16 -0
- package/dist/tools/read-context.js +85 -0
- package/dist/tools/read.d.ts +13 -0
- package/dist/tools/read.js +294 -2
- package/dist/tools/registry.js +76 -2
- package/dist/tools/runtime-context.d.ts +9 -2
- package/dist/tools/runtime-context.js +1 -2
- package/dist/tools/runtime.js +21 -2
- package/dist/tools/scheduler.js +41 -2
- package/dist/tools/shared/path-resolver.js +133 -2
- package/dist/tools/skill.js +37 -2
- package/dist/tools/todo-read.js +10 -2
- package/dist/tools/todo-write.js +65 -2
- package/dist/tools/todo.js +67 -2
- package/dist/tools/view-image.js +81 -2
- package/dist/tools/web-fetch.js +11 -2
- package/dist/tools/web-search.js +14 -2
- package/package.json +9 -7
- package/README.md +0 -3
- package/dist/runtime/prompt.d.ts +0 -9
- package/dist/runtime/prompt.js +0 -2
- package/dist/tools/shared/secret-redaction.d.ts +0 -4
- package/dist/tools/shared/secret-redaction.js +0 -2
- package/dist/tools/task-cancel.d.ts +0 -7
- package/dist/tools/task-cancel.js +0 -2
- package/dist/tools/task-output.d.ts +0 -7
- package/dist/tools/task-output.js +0 -2
- package/dist/tools/task-tools.d.ts +0 -36
- package/dist/tools/task-tools.js +0 -2
- package/dist/tools/task.js +0 -2
- package/dist/tools/todo-tools.d.ts +0 -25
- package/dist/tools/todo-tools.js +0 -2
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
export interface ContextGcPatchFile {
|
|
2
|
+
readonly path: string;
|
|
3
|
+
readonly targetPath?: string;
|
|
4
|
+
readonly operation: 'added' | 'modified' | 'deleted' | 'moved';
|
|
5
|
+
readonly addedLines: number;
|
|
6
|
+
readonly deletedLines: number;
|
|
7
|
+
}
|
|
8
|
+
export interface ContextGcPatchResult {
|
|
9
|
+
readonly files: readonly ContextGcPatchFile[];
|
|
10
|
+
readonly totals: {
|
|
11
|
+
readonly files: number;
|
|
12
|
+
readonly addedLines: number;
|
|
13
|
+
readonly deletedLines: number;
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
export type ContextGcExecResult = {
|
|
17
|
+
readonly source: 'STRUCTURED';
|
|
18
|
+
readonly status: 'running' | 'completed' | 'cancelled';
|
|
19
|
+
readonly processId?: string;
|
|
20
|
+
readonly exitCode?: number | null;
|
|
21
|
+
readonly truncated: boolean;
|
|
22
|
+
readonly stdoutBytes: number;
|
|
23
|
+
readonly stderrBytes: number;
|
|
24
|
+
readonly stdoutDroppedBytes: number;
|
|
25
|
+
readonly stderrDroppedBytes: number;
|
|
26
|
+
readonly stdout: string;
|
|
27
|
+
readonly stderr: string;
|
|
28
|
+
} | {
|
|
29
|
+
readonly source: 'MODEL_CONTEXT_TRUNCATED';
|
|
30
|
+
readonly status: 'completed' | 'cancelled';
|
|
31
|
+
readonly processId?: string;
|
|
32
|
+
readonly exitCode?: number | null;
|
|
33
|
+
readonly truncated: boolean;
|
|
34
|
+
readonly stdoutBytes: number;
|
|
35
|
+
readonly stderrBytes: number;
|
|
36
|
+
readonly stdoutDroppedBytes: number;
|
|
37
|
+
readonly stderrDroppedBytes: number;
|
|
38
|
+
readonly modelContextContent: string;
|
|
39
|
+
};
|
|
40
|
+
export declare function contextGcPatchResultFromCompletedPayload(payload: unknown): ContextGcPatchResult | undefined;
|
|
41
|
+
export declare function contextGcApplyPatchInputReplacement(arguments_: unknown, result: ContextGcPatchResult, evidenceSequence: number): string;
|
|
42
|
+
export declare function contextGcStaleReadReplacement(input: {
|
|
43
|
+
readonly path: string;
|
|
44
|
+
readonly content: string;
|
|
45
|
+
readonly evidenceSequence: number;
|
|
46
|
+
readonly supersededByCallId: string;
|
|
47
|
+
readonly operation: Exclude<ContextGcPatchFile['operation'], 'added'>;
|
|
48
|
+
}): string;
|
|
49
|
+
export declare function contextGcExecInputReplacement(arguments_: unknown): unknown;
|
|
50
|
+
export declare function contextGcExecOutputReplacement(result: ContextGcExecResult): string;
|
|
51
|
+
export declare function normalizeContextGcExecResult(modelContent: unknown, completedPayload: unknown): ContextGcExecResult | undefined;
|
|
52
|
+
export declare function parseContextGcExecResult(value: unknown): ContextGcExecResult | undefined;
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
const RAW_MODEL_CONTEXT_PREVIEW_CHARACTERS = 256;
|
|
3
|
+
export function contextGcPatchResultFromCompletedPayload(payload) {
|
|
4
|
+
if (!isRecord(payload) || payload.type !== 'tool.completed')
|
|
5
|
+
return undefined;
|
|
6
|
+
const data = payload.data;
|
|
7
|
+
if (!isRecord(data) || !Array.isArray(data.files) || !isRecord(data.totals))
|
|
8
|
+
return undefined;
|
|
9
|
+
const files = data.files.flatMap((file) => isRecord(file) &&
|
|
10
|
+
isNonemptyString(file.path) &&
|
|
11
|
+
(file.operation === 'added' ||
|
|
12
|
+
file.operation === 'modified' ||
|
|
13
|
+
file.operation === 'deleted' ||
|
|
14
|
+
file.operation === 'moved') &&
|
|
15
|
+
(file.targetPath === undefined || isNonemptyString(file.targetPath)) &&
|
|
16
|
+
isNonnegativeInteger(file.addedLines) &&
|
|
17
|
+
isNonnegativeInteger(file.deletedLines)
|
|
18
|
+
? [
|
|
19
|
+
{
|
|
20
|
+
path: file.path,
|
|
21
|
+
...(typeof file.targetPath === 'string' ? { targetPath: file.targetPath } : {}),
|
|
22
|
+
operation: file.operation,
|
|
23
|
+
addedLines: file.addedLines,
|
|
24
|
+
deletedLines: file.deletedLines
|
|
25
|
+
}
|
|
26
|
+
]
|
|
27
|
+
: []);
|
|
28
|
+
if (files.length !== data.files.length ||
|
|
29
|
+
files.length === 0 ||
|
|
30
|
+
!isNonnegativeInteger(data.totals.files) ||
|
|
31
|
+
!isNonnegativeInteger(data.totals.addedLines) ||
|
|
32
|
+
!isNonnegativeInteger(data.totals.deletedLines) ||
|
|
33
|
+
data.totals.files !== files.length ||
|
|
34
|
+
data.totals.addedLines !== files.reduce((total, file) => total + file.addedLines, 0) ||
|
|
35
|
+
data.totals.deletedLines !== files.reduce((total, file) => total + file.deletedLines, 0))
|
|
36
|
+
return undefined;
|
|
37
|
+
return {
|
|
38
|
+
files,
|
|
39
|
+
totals: {
|
|
40
|
+
files: data.totals.files,
|
|
41
|
+
addedLines: data.totals.addedLines,
|
|
42
|
+
deletedLines: data.totals.deletedLines
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
export function contextGcApplyPatchInputReplacement(arguments_, result, evidenceSequence) {
|
|
47
|
+
const original = typeof arguments_ === 'string' ? arguments_ : stableJson(arguments_);
|
|
48
|
+
const summary = result.files
|
|
49
|
+
.map((file) => `${file.operation} ${file.path}${file.targetPath ? ` -> ${file.targetPath}` : ''} (+${file.addedLines}/-${file.deletedLines})`)
|
|
50
|
+
.join('; ');
|
|
51
|
+
return `<context_gc kind="successful_apply_patch_input" originalBytes="${Buffer.byteLength(original)}" originalSha256="${sha256(original)}" evidenceSequence="${evidenceSequence}">Applied successfully: ${escapeXml(summary)}. Totals: ${result.totals.files} files, +${result.totals.addedLines}/-${result.totals.deletedLines}. The original patch body was reclaimed.</context_gc>`;
|
|
52
|
+
}
|
|
53
|
+
export function contextGcStaleReadReplacement(input) {
|
|
54
|
+
return `<context_gc kind="stale_read" path="${escapeXml(input.path)}" originalBytes="${Buffer.byteLength(input.content)}" originalSha256="${sha256(input.content)}" supersededByCallId="${escapeXml(input.supersededByCallId)}" operation="${input.operation}" evidenceSequence="${input.evidenceSequence}">This read predates a successful apply_patch. Re-read the path before relying on its contents.</context_gc>`;
|
|
55
|
+
}
|
|
56
|
+
export function contextGcExecInputReplacement(arguments_) {
|
|
57
|
+
if (!isRecord(arguments_))
|
|
58
|
+
return `<context_gc kind="terminal_exec_input" originalSha256="${sha256(stableJson(arguments_))}" />`;
|
|
59
|
+
const action = typeof arguments_.action === 'string' ? arguments_.action : 'unknown';
|
|
60
|
+
const original = stableJson(arguments_);
|
|
61
|
+
const retained = {
|
|
62
|
+
action,
|
|
63
|
+
contextGc: {
|
|
64
|
+
kind: 'terminal_exec_input',
|
|
65
|
+
originalBytes: Buffer.byteLength(original),
|
|
66
|
+
originalSha256: sha256(original)
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
for (const key of ['cwd', 'processId', 'timeoutMs', 'yieldMs'])
|
|
70
|
+
if (arguments_[key] !== undefined)
|
|
71
|
+
retained[key] = arguments_[key];
|
|
72
|
+
if (typeof arguments_.command === 'string')
|
|
73
|
+
retained.commandPrefix = [...arguments_.command].slice(0, 256).join('');
|
|
74
|
+
if (typeof arguments_.data === 'string')
|
|
75
|
+
retained.dataPrefix = [...arguments_.data].slice(0, 256).join('');
|
|
76
|
+
return retained;
|
|
77
|
+
}
|
|
78
|
+
export function contextGcExecOutputReplacement(result) {
|
|
79
|
+
if (result.source === 'MODEL_CONTEXT_TRUNCATED')
|
|
80
|
+
return JSON.stringify({
|
|
81
|
+
status: result.status,
|
|
82
|
+
...(result.processId === undefined ? {} : { processId: result.processId }),
|
|
83
|
+
...(result.exitCode === undefined ? {} : { exitCode: result.exitCode }),
|
|
84
|
+
truncated: result.truncated,
|
|
85
|
+
stdoutBytes: result.stdoutBytes,
|
|
86
|
+
stderrBytes: result.stderrBytes,
|
|
87
|
+
stdoutDroppedBytes: result.stdoutDroppedBytes,
|
|
88
|
+
stderrDroppedBytes: result.stderrDroppedBytes,
|
|
89
|
+
modelContextTruncated: true,
|
|
90
|
+
modelContextBytes: Buffer.byteLength(result.modelContextContent),
|
|
91
|
+
modelContextSha256: sha256(result.modelContextContent),
|
|
92
|
+
modelContextPreview: unicodeHeadTail(result.modelContextContent, RAW_MODEL_CONTEXT_PREVIEW_CHARACTERS),
|
|
93
|
+
contextGc: 'The model-visible terminal result was truncated after execution and is no longer valid JSON. Detailed terminal input/output was reclaimed after the process reached a stable terminal state. Re-run the command if full output is required.'
|
|
94
|
+
});
|
|
95
|
+
const stdout = result.stdout;
|
|
96
|
+
const stderr = result.stderr;
|
|
97
|
+
return JSON.stringify({
|
|
98
|
+
status: result.status,
|
|
99
|
+
...(result.processId === undefined ? {} : { processId: result.processId }),
|
|
100
|
+
...(result.exitCode === undefined ? {} : { exitCode: result.exitCode }),
|
|
101
|
+
truncated: result.truncated === true,
|
|
102
|
+
stdoutBytes: nonnegativeNumber(result.stdoutBytes) ?? Buffer.byteLength(stdout),
|
|
103
|
+
stderrBytes: nonnegativeNumber(result.stderrBytes) ?? Buffer.byteLength(stderr),
|
|
104
|
+
stdoutDroppedBytes: nonnegativeNumber(result.stdoutDroppedBytes) ?? 0,
|
|
105
|
+
stderrDroppedBytes: nonnegativeNumber(result.stderrDroppedBytes) ?? 0,
|
|
106
|
+
stdoutSha256: sha256(stdout),
|
|
107
|
+
stderrSha256: sha256(stderr),
|
|
108
|
+
stdoutPreview: unicodeHeadTail(stdout, 512),
|
|
109
|
+
stderrPreview: unicodeHeadTail(stderr, 512),
|
|
110
|
+
contextGc: 'Detailed terminal input/output was reclaimed after the process reached a stable terminal state. Re-run the command if full output is required.'
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
export function normalizeContextGcExecResult(modelContent, completedPayload) {
|
|
114
|
+
const parsed = parseContextGcExecResult(modelContent);
|
|
115
|
+
if (parsed)
|
|
116
|
+
return parsed;
|
|
117
|
+
if (typeof modelContent !== 'string' ||
|
|
118
|
+
!isRecord(completedPayload) ||
|
|
119
|
+
completedPayload.type !== 'tool.completed' ||
|
|
120
|
+
completedPayload.toolName !== 'exec' ||
|
|
121
|
+
completedPayload.truncated !== true)
|
|
122
|
+
return undefined;
|
|
123
|
+
const data = completedPayload.data;
|
|
124
|
+
if (!isRecord(data) || (data.status !== 'completed' && data.status !== 'cancelled'))
|
|
125
|
+
return undefined;
|
|
126
|
+
const metadata = parseContextGcExecMetadata(data);
|
|
127
|
+
return metadata
|
|
128
|
+
? {
|
|
129
|
+
source: 'MODEL_CONTEXT_TRUNCATED',
|
|
130
|
+
...metadata,
|
|
131
|
+
status: data.status,
|
|
132
|
+
modelContextContent: modelContent
|
|
133
|
+
}
|
|
134
|
+
: undefined;
|
|
135
|
+
}
|
|
136
|
+
export function parseContextGcExecResult(value) {
|
|
137
|
+
if (typeof value !== 'string')
|
|
138
|
+
return undefined;
|
|
139
|
+
try {
|
|
140
|
+
const parsed = JSON.parse(value);
|
|
141
|
+
if (!isRecord(parsed) ||
|
|
142
|
+
(parsed.status !== 'running' &&
|
|
143
|
+
parsed.status !== 'completed' &&
|
|
144
|
+
parsed.status !== 'cancelled') ||
|
|
145
|
+
typeof parsed.stdout !== 'string' ||
|
|
146
|
+
typeof parsed.stderr !== 'string')
|
|
147
|
+
return undefined;
|
|
148
|
+
const metadata = parseContextGcExecMetadata(parsed);
|
|
149
|
+
return metadata
|
|
150
|
+
? {
|
|
151
|
+
source: 'STRUCTURED',
|
|
152
|
+
...metadata,
|
|
153
|
+
status: parsed.status,
|
|
154
|
+
stdout: parsed.stdout,
|
|
155
|
+
stderr: parsed.stderr
|
|
156
|
+
}
|
|
157
|
+
: undefined;
|
|
158
|
+
}
|
|
159
|
+
catch {
|
|
160
|
+
return undefined;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
function parseContextGcExecMetadata(value) {
|
|
164
|
+
if (typeof value.truncated !== 'boolean' ||
|
|
165
|
+
(value.processId !== undefined &&
|
|
166
|
+
(typeof value.processId !== 'string' || value.processId.length === 0)) ||
|
|
167
|
+
(value.exitCode !== undefined &&
|
|
168
|
+
value.exitCode !== null &&
|
|
169
|
+
(typeof value.exitCode !== 'number' || !Number.isSafeInteger(value.exitCode))) ||
|
|
170
|
+
!isNonnegativeInteger(value.stdoutBytes) ||
|
|
171
|
+
!isNonnegativeInteger(value.stderrBytes) ||
|
|
172
|
+
!isNonnegativeInteger(value.stdoutDroppedBytes) ||
|
|
173
|
+
!isNonnegativeInteger(value.stderrDroppedBytes) ||
|
|
174
|
+
value.stdoutDroppedBytes > value.stdoutBytes ||
|
|
175
|
+
value.stderrDroppedBytes > value.stderrBytes)
|
|
176
|
+
return undefined;
|
|
177
|
+
return {
|
|
178
|
+
...(typeof value.processId === 'string' ? { processId: value.processId } : {}),
|
|
179
|
+
...(value.exitCode === undefined ? {} : { exitCode: value.exitCode }),
|
|
180
|
+
truncated: value.truncated,
|
|
181
|
+
stdoutBytes: value.stdoutBytes,
|
|
182
|
+
stderrBytes: value.stderrBytes,
|
|
183
|
+
stdoutDroppedBytes: value.stdoutDroppedBytes,
|
|
184
|
+
stderrDroppedBytes: value.stderrDroppedBytes
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
function unicodeHeadTail(value, count) {
|
|
188
|
+
const characters = [...value];
|
|
189
|
+
if (characters.length <= count * 2)
|
|
190
|
+
return value;
|
|
191
|
+
return `${characters.slice(0, count).join('')}\n...[context GC omitted middle]...\n${characters
|
|
192
|
+
.slice(-count)
|
|
193
|
+
.join('')}`;
|
|
194
|
+
}
|
|
195
|
+
function nonnegativeNumber(value) {
|
|
196
|
+
return typeof value === 'number' && Number.isFinite(value) && value >= 0 ? value : undefined;
|
|
197
|
+
}
|
|
198
|
+
function sha256(value) {
|
|
199
|
+
return createHash('sha256').update(value).digest('hex');
|
|
200
|
+
}
|
|
201
|
+
function escapeXml(value) {
|
|
202
|
+
return value
|
|
203
|
+
.replaceAll('&', '&')
|
|
204
|
+
.replaceAll('"', '"')
|
|
205
|
+
.replaceAll('<', '<')
|
|
206
|
+
.replaceAll('>', '>');
|
|
207
|
+
}
|
|
208
|
+
function stableJson(value) {
|
|
209
|
+
if (Array.isArray(value))
|
|
210
|
+
return `[${value.map(stableJson).join(',')}]`;
|
|
211
|
+
if (value !== null && typeof value === 'object')
|
|
212
|
+
return `{${Object.entries(value)
|
|
213
|
+
.sort(([left], [right]) => left.localeCompare(right))
|
|
214
|
+
.map(([key, entry]) => `${JSON.stringify(key)}:${stableJson(entry)}`)
|
|
215
|
+
.join(',')}}`;
|
|
216
|
+
return JSON.stringify(value) ?? 'undefined';
|
|
217
|
+
}
|
|
218
|
+
function isRecord(value) {
|
|
219
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
220
|
+
}
|
|
221
|
+
function isNonemptyString(value) {
|
|
222
|
+
return typeof value === 'string' && value.length > 0;
|
|
223
|
+
}
|
|
224
|
+
function isNonnegativeInteger(value) {
|
|
225
|
+
return Number.isSafeInteger(value) && Number(value) >= 0;
|
|
226
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export type ContextGcSubagentToolName = 'agent_start' | 'agent_wait' | 'agent_message' | 'agent_cancel';
|
|
2
|
+
export interface ContextGcSubagentSnapshot {
|
|
3
|
+
readonly agentId: string;
|
|
4
|
+
readonly description: string;
|
|
5
|
+
readonly status: 'queued' | 'running' | 'completed' | 'failed' | 'cancelled' | 'interrupted';
|
|
6
|
+
readonly modelId?: string;
|
|
7
|
+
readonly modelName?: string;
|
|
8
|
+
readonly reasoningEffort?: string;
|
|
9
|
+
readonly latestMessage?: {
|
|
10
|
+
readonly itemId: string;
|
|
11
|
+
readonly turnId: string;
|
|
12
|
+
readonly phase: 'commentary' | 'final_answer';
|
|
13
|
+
readonly text: string;
|
|
14
|
+
readonly timestamp: string;
|
|
15
|
+
readonly truncated?: boolean;
|
|
16
|
+
};
|
|
17
|
+
readonly summary?: string;
|
|
18
|
+
readonly changedFiles?: readonly string[];
|
|
19
|
+
readonly verification?: readonly string[];
|
|
20
|
+
readonly evidence?: readonly {
|
|
21
|
+
readonly path: string;
|
|
22
|
+
readonly startLine?: number;
|
|
23
|
+
readonly endLine?: number;
|
|
24
|
+
}[];
|
|
25
|
+
readonly errorCode?: string;
|
|
26
|
+
readonly errorMessage?: string;
|
|
27
|
+
readonly messageId?: string;
|
|
28
|
+
readonly turnId?: string;
|
|
29
|
+
readonly deliveryStatus?: 'accepted' | 'applied' | 'cancelled' | 'failed';
|
|
30
|
+
}
|
|
31
|
+
export declare function normalizeContextGcSubagentSnapshot(modelContent: unknown, completedPayload: unknown, toolName: ContextGcSubagentToolName): ContextGcSubagentSnapshot | undefined;
|
|
32
|
+
export declare function contextGcCompletedSubagentTurnId(snapshot: ContextGcSubagentSnapshot): string | undefined;
|
|
33
|
+
export declare function contextGcSubagentSnapshotTurnId(snapshot: ContextGcSubagentSnapshot): string | undefined;
|
|
34
|
+
export declare function contextGcSubagentNeedsRetainedPriorFinal(snapshot: ContextGcSubagentSnapshot): boolean;
|
|
35
|
+
export declare function contextGcSubagentPriorFinalIsRetained(source: ContextGcSubagentSnapshot, retained: ContextGcSubagentSnapshot | undefined): boolean;
|
|
36
|
+
export declare function contextGcSubagentSnapshotIsSuperseded(source: ContextGcSubagentSnapshot, terminal: ContextGcSubagentSnapshot, retainedPriorFinal?: ContextGcSubagentSnapshot): boolean;
|
|
37
|
+
export declare function contextGcSubagentOutputReplacement(input: {
|
|
38
|
+
readonly sourceContent: string;
|
|
39
|
+
readonly source: ContextGcSubagentSnapshot;
|
|
40
|
+
readonly terminal: ContextGcSubagentSnapshot;
|
|
41
|
+
readonly terminalCallId: string;
|
|
42
|
+
readonly evidenceSequence: number;
|
|
43
|
+
}): string;
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
export function normalizeContextGcSubagentSnapshot(modelContent, completedPayload, toolName) {
|
|
3
|
+
if (typeof modelContent !== 'string' ||
|
|
4
|
+
!isRecord(completedPayload) ||
|
|
5
|
+
completedPayload.type !== 'tool.completed' ||
|
|
6
|
+
completedPayload.toolName !== toolName ||
|
|
7
|
+
completedPayload.truncated === true)
|
|
8
|
+
return undefined;
|
|
9
|
+
const fromContent = parseContextGcSubagentSnapshot(modelContent);
|
|
10
|
+
const fromData = parseContextGcSubagentSnapshot(completedPayload.data);
|
|
11
|
+
return fromContent && fromData && stableJson(fromContent) === stableJson(fromData)
|
|
12
|
+
? fromContent
|
|
13
|
+
: undefined;
|
|
14
|
+
}
|
|
15
|
+
export function contextGcCompletedSubagentTurnId(snapshot) {
|
|
16
|
+
if (snapshot.status !== 'completed' ||
|
|
17
|
+
!isNonemptyString(snapshot.summary) ||
|
|
18
|
+
snapshot.latestMessage?.phase !== 'final_answer' ||
|
|
19
|
+
(snapshot.deliveryStatus !== undefined && snapshot.deliveryStatus !== 'applied'))
|
|
20
|
+
return undefined;
|
|
21
|
+
const turnId = snapshot.latestMessage.turnId;
|
|
22
|
+
return snapshot.turnId === undefined || snapshot.turnId === turnId ? turnId : undefined;
|
|
23
|
+
}
|
|
24
|
+
export function contextGcSubagentSnapshotTurnId(snapshot) {
|
|
25
|
+
return snapshot.turnId ?? snapshot.latestMessage?.turnId;
|
|
26
|
+
}
|
|
27
|
+
export function contextGcSubagentNeedsRetainedPriorFinal(snapshot) {
|
|
28
|
+
return (snapshot.latestMessage?.phase === 'final_answer' &&
|
|
29
|
+
snapshot.latestMessage.turnId !== contextGcSubagentSnapshotTurnId(snapshot));
|
|
30
|
+
}
|
|
31
|
+
export function contextGcSubagentPriorFinalIsRetained(source, retained) {
|
|
32
|
+
if (!contextGcSubagentNeedsRetainedPriorFinal(source))
|
|
33
|
+
return true;
|
|
34
|
+
return (retained !== undefined &&
|
|
35
|
+
retained.agentId === source.agentId &&
|
|
36
|
+
retained.description === source.description &&
|
|
37
|
+
contextGcCompletedSubagentTurnId(retained) === source.latestMessage.turnId &&
|
|
38
|
+
stableJson(retained.latestMessage) === stableJson(source.latestMessage));
|
|
39
|
+
}
|
|
40
|
+
export function contextGcSubagentSnapshotIsSuperseded(source, terminal, retainedPriorFinal) {
|
|
41
|
+
const terminalTurnId = contextGcCompletedSubagentTurnId(terminal);
|
|
42
|
+
if (!terminalTurnId ||
|
|
43
|
+
source.agentId !== terminal.agentId ||
|
|
44
|
+
contextGcSubagentSnapshotTurnId(source) !== terminalTurnId ||
|
|
45
|
+
source.description !== terminal.description ||
|
|
46
|
+
!optionalStringMatches(source.modelId, terminal.modelId) ||
|
|
47
|
+
!optionalStringMatches(source.modelName, terminal.modelName) ||
|
|
48
|
+
!optionalStringMatches(source.reasoningEffort, terminal.reasoningEffort))
|
|
49
|
+
return false;
|
|
50
|
+
if (source.status === 'completed')
|
|
51
|
+
return stableJson(source) === stableJson(terminal);
|
|
52
|
+
if ((source.status !== 'queued' && source.status !== 'running') ||
|
|
53
|
+
source.errorCode !== undefined ||
|
|
54
|
+
source.errorMessage !== undefined ||
|
|
55
|
+
!optionalStringMatches(source.summary, terminal.summary) ||
|
|
56
|
+
!contextGcSubagentPriorFinalIsRetained(source, retainedPriorFinal) ||
|
|
57
|
+
(source.latestMessage?.phase === 'final_answer' &&
|
|
58
|
+
source.latestMessage.turnId === contextGcSubagentSnapshotTurnId(source)))
|
|
59
|
+
return false;
|
|
60
|
+
if (source.messageId !== undefined &&
|
|
61
|
+
((source.deliveryStatus !== 'accepted' && source.deliveryStatus !== 'applied') ||
|
|
62
|
+
terminal.messageId !== source.messageId ||
|
|
63
|
+
terminal.turnId !== source.turnId ||
|
|
64
|
+
terminal.deliveryStatus !== 'applied'))
|
|
65
|
+
return false;
|
|
66
|
+
return (stringArrayIsCovered(source.changedFiles, terminal.changedFiles) &&
|
|
67
|
+
stringArrayIsCovered(source.verification, terminal.verification) &&
|
|
68
|
+
evidenceIsCovered(source.evidence, terminal.evidence));
|
|
69
|
+
}
|
|
70
|
+
export function contextGcSubagentOutputReplacement(input) {
|
|
71
|
+
const latestMessage = input.source.latestMessage;
|
|
72
|
+
return JSON.stringify({
|
|
73
|
+
agentId: input.source.agentId,
|
|
74
|
+
description: input.source.description,
|
|
75
|
+
status: input.source.status,
|
|
76
|
+
...(input.source.modelId === undefined ? {} : { modelId: input.source.modelId }),
|
|
77
|
+
...(input.source.modelName === undefined ? {} : { modelName: input.source.modelName }),
|
|
78
|
+
...(input.source.reasoningEffort === undefined
|
|
79
|
+
? {}
|
|
80
|
+
: { reasoningEffort: input.source.reasoningEffort }),
|
|
81
|
+
...(input.source.messageId === undefined ? {} : { messageId: input.source.messageId }),
|
|
82
|
+
...(input.source.turnId === undefined ? {} : { turnId: input.source.turnId }),
|
|
83
|
+
...(input.source.deliveryStatus === undefined
|
|
84
|
+
? {}
|
|
85
|
+
: { deliveryStatus: input.source.deliveryStatus }),
|
|
86
|
+
...(latestMessage === undefined
|
|
87
|
+
? {}
|
|
88
|
+
: {
|
|
89
|
+
latestMessage: {
|
|
90
|
+
itemId: latestMessage.itemId,
|
|
91
|
+
turnId: latestMessage.turnId,
|
|
92
|
+
phase: latestMessage.phase,
|
|
93
|
+
timestamp: latestMessage.timestamp,
|
|
94
|
+
...(latestMessage.truncated === undefined
|
|
95
|
+
? {}
|
|
96
|
+
: { truncated: latestMessage.truncated }),
|
|
97
|
+
textBytes: Buffer.byteLength(latestMessage.text),
|
|
98
|
+
textSha256: sha256(latestMessage.text)
|
|
99
|
+
}
|
|
100
|
+
}),
|
|
101
|
+
contextGc: {
|
|
102
|
+
kind: 'superseded_subagent_snapshot',
|
|
103
|
+
originalBytes: Buffer.byteLength(input.sourceContent),
|
|
104
|
+
originalSha256: sha256(input.sourceContent),
|
|
105
|
+
terminalStatus: input.terminal.status,
|
|
106
|
+
terminalTurnId: contextGcCompletedSubagentTurnId(input.terminal),
|
|
107
|
+
terminalCallId: input.terminalCallId,
|
|
108
|
+
evidenceSequence: input.evidenceSequence,
|
|
109
|
+
note: 'This intermediate child-agent snapshot was superseded by retained completed evidence; complete Turn results remain in retained completed snapshots.'
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
function parseContextGcSubagentSnapshot(value) {
|
|
114
|
+
let parsed = value;
|
|
115
|
+
if (typeof value === 'string')
|
|
116
|
+
try {
|
|
117
|
+
parsed = JSON.parse(value);
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
return undefined;
|
|
121
|
+
}
|
|
122
|
+
if (!isRecord(parsed) ||
|
|
123
|
+
Object.keys(parsed).some((key) => ![
|
|
124
|
+
'agentId',
|
|
125
|
+
'description',
|
|
126
|
+
'status',
|
|
127
|
+
'modelId',
|
|
128
|
+
'modelName',
|
|
129
|
+
'reasoningEffort',
|
|
130
|
+
'latestMessage',
|
|
131
|
+
'summary',
|
|
132
|
+
'changedFiles',
|
|
133
|
+
'verification',
|
|
134
|
+
'evidence',
|
|
135
|
+
'errorCode',
|
|
136
|
+
'errorMessage',
|
|
137
|
+
'messageId',
|
|
138
|
+
'turnId',
|
|
139
|
+
'deliveryStatus'
|
|
140
|
+
].includes(key)) ||
|
|
141
|
+
!isNonemptyString(parsed.agentId) ||
|
|
142
|
+
!isNonemptyString(parsed.description) ||
|
|
143
|
+
(parsed.status !== 'queued' &&
|
|
144
|
+
parsed.status !== 'running' &&
|
|
145
|
+
parsed.status !== 'completed' &&
|
|
146
|
+
parsed.status !== 'failed' &&
|
|
147
|
+
parsed.status !== 'cancelled' &&
|
|
148
|
+
parsed.status !== 'interrupted') ||
|
|
149
|
+
!isOptionalNonemptyString(parsed.modelId) ||
|
|
150
|
+
!isOptionalNonemptyString(parsed.modelName) ||
|
|
151
|
+
!isOptionalNonemptyString(parsed.reasoningEffort) ||
|
|
152
|
+
!isOptionalString(parsed.summary) ||
|
|
153
|
+
!isOptionalString(parsed.errorCode) ||
|
|
154
|
+
!isOptionalString(parsed.errorMessage) ||
|
|
155
|
+
!isOptionalNonemptyString(parsed.messageId) ||
|
|
156
|
+
!isOptionalNonemptyString(parsed.turnId) ||
|
|
157
|
+
(parsed.deliveryStatus !== undefined &&
|
|
158
|
+
parsed.deliveryStatus !== 'accepted' &&
|
|
159
|
+
parsed.deliveryStatus !== 'applied' &&
|
|
160
|
+
parsed.deliveryStatus !== 'cancelled' &&
|
|
161
|
+
parsed.deliveryStatus !== 'failed') ||
|
|
162
|
+
!isOptionalStringArray(parsed.changedFiles) ||
|
|
163
|
+
!isOptionalStringArray(parsed.verification) ||
|
|
164
|
+
!isOptionalSubagentEvidence(parsed.evidence))
|
|
165
|
+
return undefined;
|
|
166
|
+
const receiptCount = [parsed.messageId, parsed.turnId, parsed.deliveryStatus].filter((item) => item !== undefined).length;
|
|
167
|
+
if (receiptCount !== 0 && receiptCount !== 3)
|
|
168
|
+
return undefined;
|
|
169
|
+
const latestMessage = parseContextGcSubagentLatestMessage(parsed.latestMessage);
|
|
170
|
+
if (parsed.latestMessage !== undefined && !latestMessage)
|
|
171
|
+
return undefined;
|
|
172
|
+
return {
|
|
173
|
+
agentId: parsed.agentId,
|
|
174
|
+
description: parsed.description,
|
|
175
|
+
status: parsed.status,
|
|
176
|
+
...(typeof parsed.modelId === 'string' ? { modelId: parsed.modelId } : {}),
|
|
177
|
+
...(typeof parsed.modelName === 'string' ? { modelName: parsed.modelName } : {}),
|
|
178
|
+
...(typeof parsed.reasoningEffort === 'string'
|
|
179
|
+
? { reasoningEffort: parsed.reasoningEffort }
|
|
180
|
+
: {}),
|
|
181
|
+
...(latestMessage ? { latestMessage } : {}),
|
|
182
|
+
...(typeof parsed.summary === 'string' ? { summary: parsed.summary } : {}),
|
|
183
|
+
...(Array.isArray(parsed.changedFiles) ? { changedFiles: [...parsed.changedFiles] } : {}),
|
|
184
|
+
...(Array.isArray(parsed.verification) ? { verification: [...parsed.verification] } : {}),
|
|
185
|
+
...(Array.isArray(parsed.evidence)
|
|
186
|
+
? { evidence: parsed.evidence.map((item) => ({ ...item })) }
|
|
187
|
+
: {}),
|
|
188
|
+
...(typeof parsed.errorCode === 'string' ? { errorCode: parsed.errorCode } : {}),
|
|
189
|
+
...(typeof parsed.errorMessage === 'string' ? { errorMessage: parsed.errorMessage } : {}),
|
|
190
|
+
...(typeof parsed.messageId === 'string' ? { messageId: parsed.messageId } : {}),
|
|
191
|
+
...(typeof parsed.turnId === 'string' ? { turnId: parsed.turnId } : {}),
|
|
192
|
+
...(typeof parsed.deliveryStatus === 'string'
|
|
193
|
+
? {
|
|
194
|
+
deliveryStatus: parsed.deliveryStatus
|
|
195
|
+
}
|
|
196
|
+
: {})
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
function parseContextGcSubagentLatestMessage(value) {
|
|
200
|
+
if (!isRecord(value) ||
|
|
201
|
+
Object.keys(value).some((key) => !['itemId', 'turnId', 'phase', 'text', 'timestamp', 'truncated'].includes(key)) ||
|
|
202
|
+
!isNonemptyString(value.itemId) ||
|
|
203
|
+
!isNonemptyString(value.turnId) ||
|
|
204
|
+
(value.phase !== 'commentary' && value.phase !== 'final_answer') ||
|
|
205
|
+
typeof value.text !== 'string' ||
|
|
206
|
+
!isNonemptyString(value.timestamp) ||
|
|
207
|
+
(value.truncated !== undefined && typeof value.truncated !== 'boolean'))
|
|
208
|
+
return undefined;
|
|
209
|
+
return {
|
|
210
|
+
itemId: value.itemId,
|
|
211
|
+
turnId: value.turnId,
|
|
212
|
+
phase: value.phase,
|
|
213
|
+
text: value.text,
|
|
214
|
+
timestamp: value.timestamp,
|
|
215
|
+
...(typeof value.truncated === 'boolean' ? { truncated: value.truncated } : {})
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
function isOptionalSubagentEvidence(value) {
|
|
219
|
+
return (value === undefined ||
|
|
220
|
+
(Array.isArray(value) &&
|
|
221
|
+
value.every((item) => isRecord(item) &&
|
|
222
|
+
Object.keys(item).every((key) => ['path', 'startLine', 'endLine'].includes(key)) &&
|
|
223
|
+
isNonemptyString(item.path) &&
|
|
224
|
+
(item.startLine === undefined || isPositiveInteger(item.startLine)) &&
|
|
225
|
+
(item.endLine === undefined || isPositiveInteger(item.endLine)))));
|
|
226
|
+
}
|
|
227
|
+
function isOptionalStringArray(value) {
|
|
228
|
+
return (value === undefined || (Array.isArray(value) && value.every((item) => typeof item === 'string')));
|
|
229
|
+
}
|
|
230
|
+
function isOptionalString(value) {
|
|
231
|
+
return value === undefined || typeof value === 'string';
|
|
232
|
+
}
|
|
233
|
+
function isOptionalNonemptyString(value) {
|
|
234
|
+
return value === undefined || isNonemptyString(value);
|
|
235
|
+
}
|
|
236
|
+
function optionalStringMatches(source, terminal) {
|
|
237
|
+
return source === undefined || source === terminal;
|
|
238
|
+
}
|
|
239
|
+
function stringArrayIsCovered(source, terminal) {
|
|
240
|
+
return source === undefined || source.every((item) => terminal?.includes(item) === true);
|
|
241
|
+
}
|
|
242
|
+
function evidenceIsCovered(source, terminal) {
|
|
243
|
+
return (source === undefined ||
|
|
244
|
+
source.every((item) => terminal?.some((candidate) => stableJson(candidate) === stableJson(item))));
|
|
245
|
+
}
|
|
246
|
+
function sha256(value) {
|
|
247
|
+
return createHash('sha256').update(value).digest('hex');
|
|
248
|
+
}
|
|
249
|
+
function stableJson(value) {
|
|
250
|
+
if (Array.isArray(value))
|
|
251
|
+
return `[${value.map(stableJson).join(',')}]`;
|
|
252
|
+
if (value !== null && typeof value === 'object')
|
|
253
|
+
return `{${Object.entries(value)
|
|
254
|
+
.sort(([left], [right]) => left.localeCompare(right))
|
|
255
|
+
.map(([key, entry]) => `${JSON.stringify(key)}:${stableJson(entry)}`)
|
|
256
|
+
.join(',')}}`;
|
|
257
|
+
return JSON.stringify(value) ?? 'undefined';
|
|
258
|
+
}
|
|
259
|
+
function isRecord(value) {
|
|
260
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
261
|
+
}
|
|
262
|
+
function isNonemptyString(value) {
|
|
263
|
+
return typeof value === 'string' && value.length > 0;
|
|
264
|
+
}
|
|
265
|
+
function isPositiveInteger(value) {
|
|
266
|
+
return Number.isSafeInteger(value) && Number(value) > 0;
|
|
267
|
+
}
|