@gajae-code/coding-agent 0.11.4 → 0.11.6
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/CHANGELOG.md +23 -0
- package/dist/types/cli/notify-cli.d.ts +5 -1
- package/dist/types/config/settings-schema.d.ts +31 -1
- package/dist/types/export/html/template.generated.d.ts +1 -1
- package/dist/types/gjc-runtime/deep-interview-state.d.ts +28 -0
- package/dist/types/gjc-runtime/launch-tmux.d.ts +3 -0
- package/dist/types/gjc-runtime/managed-owner-admission.d.ts +42 -0
- package/dist/types/gjc-runtime/managed-owner-supervisor.d.ts +41 -0
- package/dist/types/gjc-runtime/tmux-owner-isolation.d.ts +9 -0
- package/dist/types/gjc-runtime/ultragoal-owner-loss-recovery.d.ts +94 -0
- package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +1 -0
- package/dist/types/harness-control-plane/owner.d.ts +3 -1
- package/dist/types/internal-urls/local-protocol.d.ts +18 -0
- package/dist/types/main.d.ts +2 -2
- package/dist/types/modes/components/ansi-display-validator.d.ts +2 -0
- package/dist/types/modes/components/notifications-settings-editor.d.ts +4 -0
- package/dist/types/modes/components/session-observer-overlay.d.ts +3 -1
- package/dist/types/modes/components/settings-selector.d.ts +2 -0
- package/dist/types/modes/components/tool-transcript-format.d.ts +58 -0
- package/dist/types/modes/components/transcript-viewer-overlay.d.ts +3 -0
- package/dist/types/modes/controllers/goal-mode-controller.d.ts +8 -1
- package/dist/types/modes/controllers/selector-controller.d.ts +15 -0
- package/dist/types/modes/shared/agent-wire/workflow-gate-broker.d.ts +3 -0
- package/dist/types/modes/shared/agent-wire/workflow-gate-types.d.ts +16 -0
- package/dist/types/plan-mode/approved-plan.d.ts +1 -0
- package/dist/types/runtime-mcp/manager.d.ts +2 -0
- package/dist/types/runtime-mcp/oauth-discovery.d.ts +12 -1
- package/dist/types/sdk/bus/chat-daemon-cli.d.ts +3 -1
- package/dist/types/sdk/bus/chat-daemon-control.d.ts +42 -4
- package/dist/types/sdk/bus/config-commands.d.ts +8 -0
- package/dist/types/sdk/bus/config.d.ts +22 -0
- package/dist/types/sdk/bus/index.d.ts +162 -2
- package/dist/types/sdk/bus/notification-service.d.ts +71 -8
- package/dist/types/sdk/bus/session-control.d.ts +11 -8
- package/dist/types/sdk/bus/telegram-daemon-cli.d.ts +5 -0
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +11 -3
- package/dist/types/sdk/bus/telegram-daemon-control.d.ts +23 -4
- package/dist/types/sdk/bus/telegram-daemon.d.ts +214 -20
- package/dist/types/sdk/session.d.ts +16 -1
- package/dist/types/secrets/index.d.ts +4 -2
- package/dist/types/secrets/obfuscator.d.ts +1 -1
- package/dist/types/session/agent-session.d.ts +31 -2
- package/dist/types/session/artifacts.d.ts +30 -8
- package/dist/types/session/internal/managed-session-scope.d.ts +7 -0
- package/dist/types/session/internal/managed-session-storage.d.ts +63 -6
- package/dist/types/session/session-manager.d.ts +98 -18
- package/dist/types/session/session-storage.d.ts +27 -0
- package/dist/types/session/streaming-output.d.ts +4 -0
- package/dist/types/session/yield-queue.d.ts +8 -0
- package/dist/types/task/executor.d.ts +9 -0
- package/dist/types/tool-discovery/tool-index.d.ts +2 -0
- package/dist/types/tools/ask.d.ts +48 -0
- package/dist/types/tools/bash-skill-urls.d.ts +2 -2
- package/dist/types/tools/index.d.ts +4 -0
- package/package.json +9 -9
- package/scripts/generate-sdk-operation-inventory.ts +3 -0
- package/src/cli/notify-cli.ts +40 -18
- package/src/commands/sdk.ts +7 -2
- package/src/config/settings-schema.ts +31 -1
- package/src/daemon/runtime.ts +2 -2
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +70 -49
- package/src/defaults/gjc/skills/ralplan/SKILL.md +0 -6
- package/src/defaults/gjc/skills/team/SKILL.md +7 -9
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +0 -2
- package/src/eval/py/index.ts +2 -2
- package/src/export/html/template.generated.ts +1 -1
- package/src/extensibility/plugins/marketplace/manager.ts +94 -10
- package/src/gjc-runtime/deep-interview-recorder.ts +7 -0
- package/src/gjc-runtime/deep-interview-runtime.ts +10 -0
- package/src/gjc-runtime/deep-interview-state.ts +127 -3
- package/src/gjc-runtime/launch-tmux.ts +61 -6
- package/src/gjc-runtime/managed-owner-admission.ts +352 -0
- package/src/gjc-runtime/managed-owner-supervisor.ts +162 -0
- package/src/gjc-runtime/state-runtime.ts +38 -12
- package/src/gjc-runtime/team-runtime.ts +1 -1
- package/src/gjc-runtime/tmux-owner-isolation.ts +117 -0
- package/src/gjc-runtime/ultragoal-owner-loss-recovery.ts +328 -0
- package/src/gjc-runtime/ultragoal-runtime.ts +14 -0
- package/src/harness-control-plane/owner.ts +59 -3
- package/src/hindsight/client.ts +65 -2
- package/src/internal-urls/agent-protocol.ts +76 -5
- package/src/internal-urls/docs-index.generated.ts +8 -7
- package/src/internal-urls/local-protocol.ts +416 -18
- package/src/lsp/index.ts +4 -88
- package/src/main.ts +36 -12
- package/src/modes/acp/acp-agent.ts +8 -0
- package/src/modes/components/ansi-display-validator.ts +54 -0
- package/src/modes/components/notifications-settings-editor.ts +26 -2
- package/src/modes/components/session-observer-overlay.ts +40 -24
- package/src/modes/components/settings-selector.ts +3 -1
- package/src/modes/components/tool-transcript-format.ts +321 -0
- package/src/modes/components/transcript-viewer-overlay.ts +39 -17
- package/src/modes/controllers/command-controller.ts +15 -0
- package/src/modes/controllers/extension-ui-controller.ts +14 -2
- package/src/modes/controllers/goal-mode-controller.ts +20 -1
- package/src/modes/controllers/plan-mode-controller.ts +2 -0
- package/src/modes/controllers/runtime-mcp-command-controller.ts +3 -0
- package/src/modes/controllers/selector-controller.ts +70 -8
- package/src/modes/interactive-mode.ts +1 -0
- package/src/modes/runtime-init.ts +16 -2
- package/src/modes/shared/agent-wire/deep-interview-gate.ts +31 -1
- package/src/modes/shared/agent-wire/workflow-gate-broker.ts +17 -1
- package/src/modes/shared/agent-wire/workflow-gate-schema.ts +4 -2
- package/src/modes/shared/agent-wire/workflow-gate-types.ts +120 -3
- package/src/plan-mode/approved-plan.ts +3 -1
- package/src/prompts/agents/architect.md +1 -0
- package/src/prompts/agents/critic.md +1 -0
- package/src/prompts/system/custom-system-prompt.md +1 -1
- package/src/prompts/system/system-prompt.md +2 -1
- package/src/prompts/tools/lsp.md +3 -3
- package/src/runtime-mcp/manager.ts +16 -1
- package/src/runtime-mcp/oauth-discovery.ts +116 -26
- package/src/sdk/broker/session-index.ts +122 -64
- package/src/sdk/bus/chat-daemon-cli.ts +72 -10
- package/src/sdk/bus/chat-daemon-control.ts +538 -130
- package/src/sdk/bus/chat-daemon-runtime.ts +1 -0
- package/src/sdk/bus/config-commands.ts +22 -0
- package/src/sdk/bus/config.ts +37 -0
- package/src/sdk/bus/index.ts +388 -94
- package/src/sdk/bus/lifecycle-control-runtime.ts +53 -3
- package/src/sdk/bus/notification-service.ts +284 -62
- package/src/sdk/bus/recent-activity.ts +14 -5
- package/src/sdk/bus/session-control.ts +150 -44
- package/src/sdk/bus/telegram-daemon-cli.ts +20 -14
- package/src/sdk/bus/telegram-daemon-contract.ts +11 -3
- package/src/sdk/bus/telegram-daemon-control.ts +312 -76
- package/src/sdk/bus/telegram-daemon.ts +1916 -285
- package/src/sdk/host/control/dispatch.ts +14 -5
- package/src/sdk/protocol/operation-inventory.generated.json +29 -0
- package/src/sdk/session.ts +155 -27
- package/src/secrets/index.ts +53 -10
- package/src/secrets/obfuscator.ts +21 -18
- package/src/secrets/regex.ts +2 -0
- package/src/session/agent-session.ts +1447 -852
- package/src/session/artifacts.ts +188 -23
- package/src/session/context-estimation.ts +7 -2
- package/src/session/internal/managed-session-scope.ts +200 -38
- package/src/session/internal/managed-session-storage.ts +1152 -46
- package/src/session/session-manager.ts +1121 -148
- package/src/session/session-storage.ts +109 -11
- package/src/session/streaming-output.ts +6 -0
- package/src/session/yield-queue.ts +20 -0
- package/src/slash-commands/builtin-registry.ts +36 -0
- package/src/task/executor.ts +71 -26
- package/src/task/index.ts +63 -20
- package/src/tool-discovery/tool-index.ts +18 -0
- package/src/tools/ask-answer-registry.ts +15 -1
- package/src/tools/ask.ts +129 -32
- package/src/tools/bash-skill-urls.ts +14 -3
- package/src/tools/bash.ts +2 -2
- package/src/tools/index.ts +4 -0
- package/src/tools/plan-mode-guard.ts +1 -0
- package/src/tools/sqlite-reader.ts +3 -2
- package/src/tools/subagent.ts +2 -0
package/src/session/artifacts.ts
CHANGED
|
@@ -4,10 +4,61 @@
|
|
|
4
4
|
* Artifacts are stored in a directory alongside the session file,
|
|
5
5
|
* accessible via artifact:// URLs.
|
|
6
6
|
*/
|
|
7
|
+
|
|
8
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
7
9
|
import * as fs from "node:fs/promises";
|
|
8
|
-
import * as path from "node:path";
|
|
9
10
|
|
|
11
|
+
import * as path from "node:path";
|
|
12
|
+
import {
|
|
13
|
+
ensureManagedDirectory,
|
|
14
|
+
type ManagedSessionDescendantStore,
|
|
15
|
+
publishManagedFileNoReplace,
|
|
16
|
+
} from "./internal/managed-session-storage";
|
|
10
17
|
import { DEFAULT_ARTIFACT_MAX_BYTES, truncateHeadBytes } from "./streaming-output";
|
|
18
|
+
|
|
19
|
+
export interface ManagedOutputGeneration {
|
|
20
|
+
outputFilename: string;
|
|
21
|
+
metadataFilename: string;
|
|
22
|
+
outputSizeBytes: number;
|
|
23
|
+
outputSha256: string;
|
|
24
|
+
metadataSizeBytes: number;
|
|
25
|
+
metadataSha256: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function sha256(bytes: Uint8Array): string {
|
|
29
|
+
return createHash("sha256").update(bytes).digest("hex");
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function isSafeFilename(filename: string): boolean {
|
|
33
|
+
return /^[a-zA-Z0-9_.-]+$/.test(filename);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function parseManagedOutputGeneration(value: Uint8Array, outputFilenamePrefix: string): ManagedOutputGeneration | null {
|
|
37
|
+
try {
|
|
38
|
+
const parsed = JSON.parse(Buffer.from(value).toString("utf8")) as Partial<ManagedOutputGeneration>;
|
|
39
|
+
if (
|
|
40
|
+
typeof parsed.outputFilename !== "string" ||
|
|
41
|
+
typeof parsed.metadataFilename !== "string" ||
|
|
42
|
+
typeof parsed.outputSizeBytes !== "number" ||
|
|
43
|
+
typeof parsed.outputSha256 !== "string" ||
|
|
44
|
+
typeof parsed.metadataSizeBytes !== "number" ||
|
|
45
|
+
typeof parsed.metadataSha256 !== "string" ||
|
|
46
|
+
!isSafeFilename(parsed.outputFilename) ||
|
|
47
|
+
!isSafeFilename(parsed.metadataFilename) ||
|
|
48
|
+
!parsed.outputFilename.startsWith(`${outputFilenamePrefix}.`) ||
|
|
49
|
+
!parsed.outputFilename.endsWith(".output") ||
|
|
50
|
+
parsed.metadataFilename !== `${parsed.outputFilename}.meta.json`
|
|
51
|
+
)
|
|
52
|
+
return null;
|
|
53
|
+
return parsed as ManagedOutputGeneration;
|
|
54
|
+
} catch {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function sameGeneration(left: ManagedOutputGeneration, right: ManagedOutputGeneration): boolean {
|
|
60
|
+
return left.outputFilename === right.outputFilename && left.metadataFilename === right.metadataFilename;
|
|
61
|
+
}
|
|
11
62
|
export interface ArtifactSaveOptions {
|
|
12
63
|
maxBytes?: number;
|
|
13
64
|
}
|
|
@@ -25,14 +76,16 @@ export interface ArtifactSaveOptions {
|
|
|
25
76
|
export class ArtifactManager {
|
|
26
77
|
#nextId = 0;
|
|
27
78
|
readonly #dir: string;
|
|
79
|
+
readonly #store: ManagedSessionDescendantStore | undefined;
|
|
28
80
|
#dirCreated = false;
|
|
29
81
|
#initialized = false;
|
|
30
82
|
|
|
31
83
|
/**
|
|
32
84
|
* @param dir Directory that will hold artifact files. Created lazily on first save.
|
|
33
85
|
*/
|
|
34
|
-
constructor(
|
|
35
|
-
this.#
|
|
86
|
+
constructor(target: string | ManagedSessionDescendantStore) {
|
|
87
|
+
this.#store = typeof target === "string" ? undefined : target;
|
|
88
|
+
this.#dir = typeof target === "string" ? target : target.dir;
|
|
36
89
|
}
|
|
37
90
|
|
|
38
91
|
/**
|
|
@@ -43,9 +96,26 @@ export class ArtifactManager {
|
|
|
43
96
|
return this.#dir;
|
|
44
97
|
}
|
|
45
98
|
|
|
99
|
+
getManagedRootAuthority() {
|
|
100
|
+
return this.#store?.rootAuthority;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
getManagedSubtreeRootAuthority() {
|
|
104
|
+
return this.#store?.subtreeRootAuthority;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
getManagedStore(): ManagedSessionDescendantStore | undefined {
|
|
108
|
+
return this.#store;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
assertManagedBinding(): void {
|
|
112
|
+
this.#store?.assertBound();
|
|
113
|
+
}
|
|
114
|
+
|
|
46
115
|
async #ensureDir(): Promise<void> {
|
|
47
116
|
if (!this.#dirCreated) {
|
|
48
|
-
|
|
117
|
+
if (this.#store) this.#store.ensureDirectory();
|
|
118
|
+
else ensureManagedDirectory(this.#dir);
|
|
49
119
|
this.#dirCreated = true;
|
|
50
120
|
}
|
|
51
121
|
if (!this.#initialized) {
|
|
@@ -54,6 +124,102 @@ export class ArtifactManager {
|
|
|
54
124
|
}
|
|
55
125
|
}
|
|
56
126
|
|
|
127
|
+
#filename(id: string, toolType: string): string {
|
|
128
|
+
if (!/^[a-zA-Z0-9_-]+$/.test(toolType)) throw new Error("Unsafe artifact tool type");
|
|
129
|
+
return `${id}.${toolType}.log`;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
async #publish(content: string, filename: string): Promise<void> {
|
|
133
|
+
if (this.#store) await this.#store.publishNoReplace(filename, Buffer.from(content, "utf8"));
|
|
134
|
+
else await publishManagedFileNoReplace(path.join(this.#dir, filename), Buffer.from(content, "utf8"));
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
async replaceNamed(filename: string, content: string): Promise<void> {
|
|
138
|
+
if (!/^[a-zA-Z0-9_.-]+$/.test(filename)) throw new Error("Unsafe named artifact");
|
|
139
|
+
await this.#ensureDir();
|
|
140
|
+
if (this.#store) await this.#store.replace(filename, Buffer.from(content, "utf8"));
|
|
141
|
+
else await Bun.write(path.join(this.#dir, filename), content);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async replaceNamedBytes(filename: string, bytes: Uint8Array): Promise<void> {
|
|
145
|
+
if (!/^[a-zA-Z0-9_.-]+$/.test(filename)) throw new Error("Unsafe named artifact");
|
|
146
|
+
await this.#ensureDir();
|
|
147
|
+
if (this.#store) await this.#store.replace(filename, bytes);
|
|
148
|
+
else await Bun.write(path.join(this.#dir, filename), bytes);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
async publishManagedOutputGeneration(
|
|
152
|
+
selectorFilename: string,
|
|
153
|
+
outputFilenamePrefix: string,
|
|
154
|
+
outputBytes: Uint8Array,
|
|
155
|
+
metadataBytes: Uint8Array,
|
|
156
|
+
): Promise<void> {
|
|
157
|
+
if (!isSafeFilename(selectorFilename) || !isSafeFilename(outputFilenamePrefix)) {
|
|
158
|
+
throw new Error("Unsafe managed output generation");
|
|
159
|
+
}
|
|
160
|
+
await this.#ensureDir();
|
|
161
|
+
if (!this.#store) throw new Error("Managed output generation requires retained authority");
|
|
162
|
+
|
|
163
|
+
const priorSelector = this.#store.readExpected(selectorFilename);
|
|
164
|
+
const priorGeneration = priorSelector
|
|
165
|
+
? parseManagedOutputGeneration(priorSelector.bytes, outputFilenamePrefix)
|
|
166
|
+
: null;
|
|
167
|
+
const generationId = randomUUID();
|
|
168
|
+
const outputFilename = `${outputFilenamePrefix}.${generationId}.output`;
|
|
169
|
+
const metadataFilename = `${outputFilename}.meta.json`;
|
|
170
|
+
const generation: ManagedOutputGeneration = {
|
|
171
|
+
outputFilename,
|
|
172
|
+
metadataFilename,
|
|
173
|
+
outputSizeBytes: outputBytes.byteLength,
|
|
174
|
+
outputSha256: sha256(outputBytes),
|
|
175
|
+
metadataSizeBytes: metadataBytes.byteLength,
|
|
176
|
+
metadataSha256: sha256(metadataBytes),
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
// Immutable generations are not visible until the selector is replaced.
|
|
180
|
+
await this.#store.publishNoReplace(outputFilename, outputBytes);
|
|
181
|
+
await this.#store.publishNoReplace(metadataFilename, metadataBytes);
|
|
182
|
+
const stagedOutput = this.#store.readExpected(outputFilename);
|
|
183
|
+
const stagedMetadata = this.#store.readExpected(metadataFilename);
|
|
184
|
+
if (
|
|
185
|
+
!stagedOutput ||
|
|
186
|
+
!stagedMetadata ||
|
|
187
|
+
stagedOutput.bytes.byteLength !== generation.outputSizeBytes ||
|
|
188
|
+
stagedMetadata.bytes.byteLength !== generation.metadataSizeBytes ||
|
|
189
|
+
sha256(stagedOutput.bytes) !== generation.outputSha256 ||
|
|
190
|
+
sha256(stagedMetadata.bytes) !== generation.metadataSha256
|
|
191
|
+
) {
|
|
192
|
+
throw new Error("managed_output_generation_verification_failed");
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
await this.#store.replace(selectorFilename, Buffer.from(JSON.stringify(generation), "utf8"));
|
|
196
|
+
const publishedSelector = this.#store.readExpected(selectorFilename);
|
|
197
|
+
const publishedGeneration = publishedSelector
|
|
198
|
+
? parseManagedOutputGeneration(publishedSelector.bytes, outputFilenamePrefix)
|
|
199
|
+
: null;
|
|
200
|
+
if (!publishedGeneration || !sameGeneration(publishedGeneration, generation)) {
|
|
201
|
+
throw new Error("managed_output_selector_verification_failed");
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Cleanup cannot affect the selected generation or publication outcome.
|
|
205
|
+
if (priorGeneration && !sameGeneration(priorGeneration, generation)) {
|
|
206
|
+
for (const filename of [priorGeneration.outputFilename, priorGeneration.metadataFilename]) {
|
|
207
|
+
try {
|
|
208
|
+
const previous = this.#store.readExpected(filename);
|
|
209
|
+
if (previous) this.#store.removeExpected(filename, previous);
|
|
210
|
+
} catch {
|
|
211
|
+
// Retain unreachable generations for a later safe cleanup.
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
async publishNamedNoReplace(filename: string, bytes: Uint8Array): Promise<void> {
|
|
217
|
+
if (!/^[a-zA-Z0-9_.-]+$/.test(filename)) throw new Error("Unsafe named artifact");
|
|
218
|
+
await this.#ensureDir();
|
|
219
|
+
if (this.#store) await this.#store.publishNoReplace(filename, bytes);
|
|
220
|
+
else await publishManagedFileNoReplace(path.join(this.#dir, filename), bytes);
|
|
221
|
+
}
|
|
222
|
+
|
|
57
223
|
/**
|
|
58
224
|
* Scan existing artifact files to find the next available ID.
|
|
59
225
|
* This ensures we don't overwrite artifacts when resuming a session.
|
|
@@ -81,37 +247,36 @@ export class ArtifactManager {
|
|
|
81
247
|
}
|
|
82
248
|
|
|
83
249
|
/**
|
|
84
|
-
*
|
|
250
|
+
* Reserve an artifact ID without exposing a writable managed pathname.
|
|
85
251
|
*
|
|
86
|
-
*
|
|
252
|
+
* Streaming callers that only understand bare paths fail closed; use `save`
|
|
253
|
+
* for terminally published artifact content.
|
|
87
254
|
*/
|
|
88
|
-
async allocatePath(toolType: string): Promise<{ id: string; path
|
|
255
|
+
async allocatePath(toolType: string): Promise<{ id: string; path?: string }> {
|
|
89
256
|
await this.#ensureDir();
|
|
90
257
|
const id = String(this.allocateId());
|
|
91
|
-
|
|
92
|
-
return { id, path: path.join(this.#dir, filename) };
|
|
258
|
+
if (this.#store) return { id };
|
|
259
|
+
return { id, path: path.join(this.#dir, this.#filename(id, toolType)) };
|
|
93
260
|
}
|
|
94
261
|
|
|
95
262
|
/**
|
|
96
263
|
* Save content as an artifact and return the artifact ID.
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
* @param toolType Tool name for file extension (e.g., "bash", "read")
|
|
100
|
-
* @returns Artifact ID (numeric string)
|
|
264
|
+
* Content is written to a private temporary inode, synced, and linked into
|
|
265
|
+
* the artifact directory only after the complete terminal payload exists.
|
|
101
266
|
*/
|
|
102
267
|
async save(content: string, toolType: string, options: ArtifactSaveOptions = {}): Promise<string> {
|
|
103
|
-
|
|
268
|
+
await this.#ensureDir();
|
|
269
|
+
const id = String(this.allocateId());
|
|
104
270
|
const maxBytes = Math.max(0, options.maxBytes ?? DEFAULT_ARTIFACT_MAX_BYTES);
|
|
105
271
|
const contentBytes = Buffer.byteLength(content, "utf-8");
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
}
|
|
272
|
+
const published =
|
|
273
|
+
contentBytes > maxBytes
|
|
274
|
+
? (() => {
|
|
275
|
+
const truncated = truncateHeadBytes(content, maxBytes);
|
|
276
|
+
return `${truncated.text}\n[artifact truncated after ${truncated.bytes} bytes; omitted at least ${contentBytes - truncated.bytes} bytes]\n`;
|
|
277
|
+
})()
|
|
278
|
+
: content;
|
|
279
|
+
await this.#publish(published, this.#filename(id, toolType));
|
|
115
280
|
return id;
|
|
116
281
|
}
|
|
117
282
|
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* as well, so every surface reports the same numbers.
|
|
8
8
|
*/
|
|
9
9
|
import { estimateTextTokensHeuristic } from "@gajae-code/agent-core/compaction";
|
|
10
|
+
import { toolWireSchema } from "@gajae-code/ai/utils/schema/wire";
|
|
10
11
|
import type { Skill } from "../extensibility/skills";
|
|
11
12
|
import type { Tool } from "../tools";
|
|
12
13
|
import type { AgentSession } from "./agent-session";
|
|
@@ -28,9 +29,13 @@ export function estimateToolSchemaTokens(
|
|
|
28
29
|
for (const tool of tools) {
|
|
29
30
|
fragments.push(tool.name, tool.description);
|
|
30
31
|
try {
|
|
31
|
-
fragments.push(JSON.stringify(tool
|
|
32
|
+
fragments.push(JSON.stringify(toolWireSchema(tool as never)));
|
|
32
33
|
} catch {
|
|
33
|
-
|
|
34
|
+
try {
|
|
35
|
+
fragments.push(JSON.stringify(tool.parameters ?? {}));
|
|
36
|
+
} catch {
|
|
37
|
+
// Schema may contain functions or cycles; ignore.
|
|
38
|
+
}
|
|
34
39
|
}
|
|
35
40
|
}
|
|
36
41
|
return estimateTextTokensHeuristic(fragments);
|