@gajae-code/coding-agent 0.12.4 → 0.12.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 +36 -0
- package/README.md +4 -1
- package/dist/types/async/job-manager.d.ts +9 -1
- package/dist/types/eval/backend.d.ts +2 -2
- package/dist/types/exec/bash-executor.d.ts +10 -8
- package/dist/types/extensibility/extensions/runner.d.ts +9 -7
- package/dist/types/gjc-runtime/launch-tmux.d.ts +22 -3
- package/dist/types/gjc-runtime/psmux-detect.d.ts +3 -0
- package/dist/types/gjc-runtime/team-launch.d.ts +3 -1
- package/dist/types/gjc-runtime/team-runtime.d.ts +14 -2
- package/dist/types/gjc-runtime/team-store.d.ts +0 -4
- package/dist/types/gjc-runtime/team-workers.d.ts +5 -0
- package/dist/types/gjc-runtime/tmux-common.d.ts +1 -0
- package/dist/types/gjc-runtime/tmux-provider-context.d.ts +57 -0
- package/dist/types/gjc-runtime/tmux-sessions.d.ts +8 -1
- package/dist/types/gjc-runtime/ultragoal-change-set.d.ts +3 -0
- package/dist/types/gjc-runtime/ultragoal-review-source.d.ts +83 -0
- package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +57 -4
- package/dist/types/internal-urls/registry-helpers.d.ts +18 -0
- package/dist/types/modes/components/gjc-bundle-settings.d.ts +1 -0
- package/dist/types/modes/components/plugin-settings.d.ts +2 -0
- package/dist/types/modes/components/settings-selector.d.ts +2 -0
- package/dist/types/sdk/broker/broker.d.ts +18 -0
- package/dist/types/sdk/broker/lifecycle-ledger.d.ts +13 -0
- package/dist/types/sdk/broker/lifecycle.d.ts +1 -0
- package/dist/types/sdk/bus/chat-daemon-control.d.ts +5 -2
- package/dist/types/sdk/bus/index.d.ts +4 -0
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +7 -3
- package/dist/types/sdk/bus/telegram-daemon.d.ts +25 -3
- package/dist/types/sdk/bus/topic-registry.d.ts +63 -3
- package/dist/types/sdk/host/host.d.ts +23 -2
- package/dist/types/sdk/host/reverse-leases.d.ts +7 -4
- package/dist/types/session/agent-session.d.ts +5 -4
- package/dist/types/session/artifacts.d.ts +8 -5
- package/dist/types/session/attempt-record-store.d.ts +19 -0
- package/dist/types/session/client-bridge.d.ts +14 -0
- package/dist/types/session/internal/managed-session-storage.d.ts +7 -23
- package/dist/types/session/messages.d.ts +4 -1
- package/dist/types/session/session-manager.d.ts +7 -5
- package/dist/types/session/session-storage.d.ts +8 -0
- package/dist/types/session/streaming-output.d.ts +32 -0
- package/dist/types/task/executor.d.ts +1 -1
- package/dist/types/task/index.d.ts +2 -1
- package/dist/types/task/receipt.d.ts +3 -0
- package/dist/types/task/types.d.ts +194 -0
- package/dist/types/task/worktree.d.ts +13 -6
- package/dist/types/tools/bash.d.ts +1 -1
- package/dist/types/tools/fetch.d.ts +3 -0
- package/dist/types/tools/output-meta.d.ts +37 -5
- package/dist/types/tools/subagent.d.ts +2 -0
- package/dist/types/tools/tool-result.d.ts +1 -0
- package/dist/types/utils/artifact-id.d.ts +2 -0
- package/dist/types/utils/git.d.ts +1 -0
- package/package.json +8 -8
- package/scripts/capture-sticky-viewport-showcase.ts +131 -5
- package/scripts/verify-gjc-sdk-canonicalization.ts +128 -183
- package/scripts/verify-sticky-viewport-showcase.ts +620 -93
- package/src/async/job-manager.ts +13 -5
- package/src/config/model-profiles.ts +8 -0
- package/src/config/settings-schema.ts +17 -3
- package/src/cursor.ts +20 -13
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +5 -2
- package/src/eval/backend.ts +2 -2
- package/src/exec/bash-executor.ts +425 -151
- package/src/extensibility/extensions/runner.ts +80 -6
- package/src/extensibility/extensions/wrapper.ts +22 -15
- package/src/extensibility/runtime-skill-discovery.ts +4 -6
- package/src/gjc-runtime/launch-tmux.ts +309 -293
- package/src/gjc-runtime/managed-owner-supervisor.ts +6 -2
- package/src/gjc-runtime/psmux-detect.ts +52 -6
- package/src/gjc-runtime/team-launch.ts +50 -2
- package/src/gjc-runtime/team-runtime.ts +716 -251
- package/src/gjc-runtime/team-store.ts +25 -7
- package/src/gjc-runtime/team-workers.ts +161 -1
- package/src/gjc-runtime/tmux-common.ts +16 -4
- package/src/gjc-runtime/tmux-gc.ts +37 -5
- package/src/gjc-runtime/tmux-owner-isolation.ts +2 -0
- package/src/gjc-runtime/tmux-provider-context.ts +451 -0
- package/src/gjc-runtime/tmux-sessions.ts +409 -156
- package/src/gjc-runtime/ultragoal-change-set.ts +90 -44
- package/src/gjc-runtime/ultragoal-receipt-freshness.ts +7 -0
- package/src/gjc-runtime/ultragoal-review-source.ts +378 -0
- package/src/gjc-runtime/ultragoal-runtime.ts +640 -159
- package/src/gjc-runtime/workflow-manifest.generated.json +40 -1
- package/src/gjc-runtime/workflow-manifest.ts +12 -0
- package/src/internal-urls/agent-protocol.ts +98 -38
- package/src/internal-urls/artifact-protocol.ts +28 -17
- package/src/internal-urls/docs-index.generated.ts +6 -6
- package/src/internal-urls/local-protocol.ts +39 -18
- package/src/internal-urls/registry-helpers.ts +62 -0
- package/src/modes/acp/acp-agent.ts +51 -13
- package/src/modes/acp/acp-client-bridge.ts +154 -11
- package/src/modes/components/gjc-bundle-settings.ts +2 -0
- package/src/modes/components/plugin-settings.ts +5 -0
- package/src/modes/components/settings-selector.ts +4 -0
- package/src/modes/controllers/selector-controller.ts +1 -0
- package/src/prompts/tools/bash.md +2 -2
- package/src/prompts/tools/monitor.md +4 -4
- package/src/sdk/acp/adapter.ts +200 -9
- package/src/sdk/broker/broker.ts +93 -10
- package/src/sdk/broker/lifecycle-ledger.ts +227 -11
- package/src/sdk/broker/lifecycle.ts +692 -98
- package/src/sdk/bus/chat-daemon-control.ts +33 -5
- package/src/sdk/bus/index.ts +347 -14
- package/src/sdk/bus/telegram-daemon-contract.ts +7 -3
- package/src/sdk/bus/telegram-daemon.ts +283 -44
- package/src/sdk/bus/topic-registry.ts +133 -10
- package/src/sdk/host/host.ts +187 -9
- package/src/sdk/host/reverse-leases.ts +249 -42
- package/src/sdk/session.ts +8 -6
- package/src/session/agent-session.ts +930 -354
- package/src/session/artifacts.ts +211 -74
- package/src/session/attempt-record-store.ts +106 -0
- package/src/session/client-bridge.ts +15 -0
- package/src/session/internal/managed-session-scope.ts +332 -198
- package/src/session/internal/managed-session-storage.ts +162 -450
- package/src/session/messages.ts +32 -7
- package/src/session/session-manager.ts +31 -28
- package/src/session/session-storage.ts +190 -53
- package/src/session/streaming-output.ts +198 -40
- package/src/task/executor.ts +5 -2
- package/src/task/index.ts +277 -64
- package/src/task/provider-retry-status.ts +4 -1
- package/src/task/receipt.ts +26 -2
- package/src/task/render.ts +42 -1
- package/src/task/types.ts +52 -0
- package/src/task/worktree.ts +122 -43
- package/src/tools/bash.ts +818 -239
- package/src/tools/eval.ts +99 -4
- package/src/tools/fetch.ts +77 -15
- package/src/tools/find.ts +7 -3
- package/src/tools/monitor.ts +7 -1
- package/src/tools/output-meta.ts +706 -116
- package/src/tools/search.ts +9 -4
- package/src/tools/subagent-render.ts +5 -2
- package/src/tools/subagent.ts +8 -0
- package/src/tools/tool-result.ts +1 -0
- package/src/utils/artifact-id.ts +6 -0
- package/src/utils/git.ts +2 -0
package/src/tools/eval.ts
CHANGED
|
@@ -11,8 +11,17 @@ import type { RenderResultOptions } from "../extensibility/custom-tools/types";
|
|
|
11
11
|
import { truncateToVisualLines } from "../modes/components/visual-truncate";
|
|
12
12
|
import { getMarkdownTheme, type Theme } from "../modes/theme/theme";
|
|
13
13
|
import evalDescription from "../prompts/tools/eval.md" with { type: "text" };
|
|
14
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
DEFAULT_ARTIFACT_MAX_BYTES,
|
|
16
|
+
DEFAULT_MAX_BYTES,
|
|
17
|
+
OutputSink,
|
|
18
|
+
type OutputSummary,
|
|
19
|
+
TailBuffer,
|
|
20
|
+
type TerminalArtifactPublisher,
|
|
21
|
+
truncateHeadBytes,
|
|
22
|
+
} from "../session/streaming-output";
|
|
15
23
|
import { getTreeBranch, getTreeContinuePrefix, renderCodeCell } from "../tui";
|
|
24
|
+
import { isValidArtifactId } from "../utils/artifact-id";
|
|
16
25
|
import { resolveEvalBackends, type ToolSession } from ".";
|
|
17
26
|
import {
|
|
18
27
|
formatStyledTruncationWarning,
|
|
@@ -163,6 +172,55 @@ interface ResolvedEvalCell {
|
|
|
163
172
|
resolved: ResolvedBackend;
|
|
164
173
|
}
|
|
165
174
|
|
|
175
|
+
const EVAL_ARTIFACT_ALLOCATION_WAIT_MS = 500;
|
|
176
|
+
const EVAL_ARTIFACT_ALLOCATION_LIMIT = 64;
|
|
177
|
+
const pendingEvalArtifactAllocations = new Set<Promise<unknown>>();
|
|
178
|
+
|
|
179
|
+
async function allocateEvalArtifact(
|
|
180
|
+
session: ToolSession,
|
|
181
|
+
signal?: AbortSignal,
|
|
182
|
+
): Promise<{ path?: string; id?: string; diagnostic?: string }> {
|
|
183
|
+
if (!session.allocateOutputArtifact) return {};
|
|
184
|
+
if (signal?.aborted) throw new ToolAbortError();
|
|
185
|
+
if (pendingEvalArtifactAllocations.size >= EVAL_ARTIFACT_ALLOCATION_LIMIT) {
|
|
186
|
+
return { diagnostic: "pending Eval artifact allocation limit reached" };
|
|
187
|
+
}
|
|
188
|
+
const allocation = Promise.resolve().then(() => session.allocateOutputArtifact!("eval"));
|
|
189
|
+
pendingEvalArtifactAllocations.add(allocation);
|
|
190
|
+
void allocation.then(
|
|
191
|
+
() => pendingEvalArtifactAllocations.delete(allocation),
|
|
192
|
+
() => pendingEvalArtifactAllocations.delete(allocation),
|
|
193
|
+
);
|
|
194
|
+
const aborted = Promise.withResolvers<{ status: "aborted" }>();
|
|
195
|
+
const onAbort = () => aborted.resolve({ status: "aborted" });
|
|
196
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
197
|
+
try {
|
|
198
|
+
const outcome = await Promise.race([
|
|
199
|
+
allocation.then(
|
|
200
|
+
value => ({ status: "ok" as const, value }),
|
|
201
|
+
error => ({
|
|
202
|
+
status: "failed" as const,
|
|
203
|
+
diagnostic: error instanceof Error ? error.message : String(error),
|
|
204
|
+
}),
|
|
205
|
+
),
|
|
206
|
+
Bun.sleep(EVAL_ARTIFACT_ALLOCATION_WAIT_MS).then(() => ({
|
|
207
|
+
status: "timeout" as const,
|
|
208
|
+
diagnostic: `did not settle within ${EVAL_ARTIFACT_ALLOCATION_WAIT_MS}ms`,
|
|
209
|
+
})),
|
|
210
|
+
aborted.promise,
|
|
211
|
+
]);
|
|
212
|
+
if (outcome.status === "aborted") throw new ToolAbortError();
|
|
213
|
+
if (outcome.status !== "ok") return { diagnostic: outcome.diagnostic.slice(0, 512) };
|
|
214
|
+
const { path, id } = outcome.value ?? {};
|
|
215
|
+
if (!path || !isValidArtifactId(id)) {
|
|
216
|
+
return { diagnostic: "artifact storage is unavailable or returned an invalid artifact id" };
|
|
217
|
+
}
|
|
218
|
+
return { path, id };
|
|
219
|
+
} finally {
|
|
220
|
+
signal?.removeEventListener("abort", onAbort);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
166
224
|
function uniqueEvalLanguages(cells: ResolvedEvalCell[]): EvalLanguage[] {
|
|
167
225
|
return [...new Set(cells.map(cell => cell.resolved.backend.id))];
|
|
168
226
|
}
|
|
@@ -265,10 +323,12 @@ export class EvalTool implements AgentTool<typeof evalSchema> {
|
|
|
265
323
|
const sessionAbortController = new AbortController();
|
|
266
324
|
let outputSink: OutputSink | undefined;
|
|
267
325
|
let outputSummary: OutputSummary | undefined;
|
|
326
|
+
let artifactAllocationDiagnostic: string | undefined;
|
|
268
327
|
let outputDumped = false;
|
|
269
328
|
const finalizeOutput = async (): Promise<OutputSummary | undefined> => {
|
|
270
329
|
if (outputDumped || !outputSink) return outputSummary;
|
|
271
330
|
outputSummary = await outputSink.dump();
|
|
331
|
+
if (artifactAllocationDiagnostic) outputSummary.artifactFailureDiagnostic = artifactAllocationDiagnostic;
|
|
272
332
|
outputDumped = true;
|
|
273
333
|
return outputSummary;
|
|
274
334
|
};
|
|
@@ -334,11 +394,34 @@ export class EvalTool implements AgentTool<typeof evalSchema> {
|
|
|
334
394
|
|
|
335
395
|
const sessionFile = session.getSessionFile?.() ?? undefined;
|
|
336
396
|
const kernelOwnerId = session.getEvalKernelOwnerId?.() ?? undefined;
|
|
337
|
-
const
|
|
397
|
+
const artifactManager = session.getArtifactManager?.();
|
|
398
|
+
const managedArtifactPublisher: TerminalArtifactPublisher | undefined = artifactManager?.getManagedStore()
|
|
399
|
+
? Object.assign(
|
|
400
|
+
async (content: string) => {
|
|
401
|
+
const contentBytes = Buffer.byteLength(content, "utf8");
|
|
402
|
+
const artifactId = await artifactManager.save(content, "eval");
|
|
403
|
+
if (contentBytes <= DEFAULT_ARTIFACT_MAX_BYTES) {
|
|
404
|
+
return { status: "published" as const, artifactId };
|
|
405
|
+
}
|
|
406
|
+
const retainedBytes = truncateHeadBytes(content, DEFAULT_ARTIFACT_MAX_BYTES).bytes;
|
|
407
|
+
return {
|
|
408
|
+
status: "published" as const,
|
|
409
|
+
artifactId,
|
|
410
|
+
omittedBytes: contentBytes - retainedBytes,
|
|
411
|
+
};
|
|
412
|
+
},
|
|
413
|
+
{ owner: artifactManager },
|
|
414
|
+
)
|
|
415
|
+
: undefined;
|
|
416
|
+
const allocation = managedArtifactPublisher ? {} : await allocateEvalArtifact(session, signal);
|
|
417
|
+
artifactAllocationDiagnostic = allocation.diagnostic;
|
|
418
|
+
const { path: artifactPath, id: artifactId } = allocation;
|
|
419
|
+
if (signal?.aborted) throw new ToolAbortError();
|
|
338
420
|
session.assertEvalExecutionAllowed?.();
|
|
339
421
|
outputSink = new OutputSink({
|
|
340
422
|
artifactPath,
|
|
341
423
|
artifactId,
|
|
424
|
+
artifactPublisher: managedArtifactPublisher,
|
|
342
425
|
headBytes: resolveOutputSinkHeadBytes(session.settings),
|
|
343
426
|
maxColumns: resolveOutputMaxColumns(session.settings),
|
|
344
427
|
onChunk: chunk => {
|
|
@@ -376,8 +459,6 @@ export class EvalTool implements AgentTool<typeof evalSchema> {
|
|
|
376
459
|
session,
|
|
377
460
|
deadlineMs,
|
|
378
461
|
reset: cell.reset,
|
|
379
|
-
artifactPath,
|
|
380
|
-
artifactId,
|
|
381
462
|
onChunk: chunk => {
|
|
382
463
|
outputSink!.push(chunk);
|
|
383
464
|
},
|
|
@@ -545,6 +626,7 @@ async function summarizeFinal(
|
|
|
545
626
|
const outputBytes = Buffer.byteLength(combinedOutput, "utf-8");
|
|
546
627
|
const missingLines = Math.max(0, rawSummary.totalLines - rawSummary.outputLines);
|
|
547
628
|
const missingBytes = Math.max(0, rawSummary.totalBytes - rawSummary.outputBytes);
|
|
629
|
+
const artifactRepresentationIncomplete = rawSummary.output !== combinedOutput || rawSummary.artifactId !== undefined;
|
|
548
630
|
return {
|
|
549
631
|
output: combinedOutput,
|
|
550
632
|
truncated: rawSummary.truncated,
|
|
@@ -553,6 +635,19 @@ async function summarizeFinal(
|
|
|
553
635
|
outputLines,
|
|
554
636
|
outputBytes,
|
|
555
637
|
artifactId: rawSummary.artifactId,
|
|
638
|
+
artifactVerified: rawSummary.artifactVerified,
|
|
639
|
+
artifactTruncatedBytes: rawSummary.artifactTruncatedBytes,
|
|
640
|
+
sourceTruncatedBytes: rawSummary.sourceTruncatedBytes,
|
|
641
|
+
sourceCaptureIncomplete:
|
|
642
|
+
rawSummary.sourceCaptureIncomplete === true || artifactRepresentationIncomplete || undefined,
|
|
643
|
+
headRange: rawSummary.headRange,
|
|
644
|
+
tailRange: rawSummary.tailRange,
|
|
645
|
+
elidedBytes: rawSummary.elidedBytes,
|
|
646
|
+
elidedLines: rawSummary.elidedLines,
|
|
647
|
+
columnDroppedBytes: rawSummary.columnDroppedBytes,
|
|
648
|
+
columnTruncatedLines: rawSummary.columnTruncatedLines,
|
|
649
|
+
columnMax: rawSummary.columnMax,
|
|
650
|
+
artifactFailureDiagnostic: rawSummary.artifactFailureDiagnostic,
|
|
556
651
|
};
|
|
557
652
|
}
|
|
558
653
|
|
package/src/tools/fetch.ts
CHANGED
|
@@ -10,9 +10,15 @@ import type { RenderResultOptions } from "../extensibility/custom-tools/types";
|
|
|
10
10
|
import { type Theme, theme } from "../modes/theme/theme";
|
|
11
11
|
import type { ToolSession } from "../sdk";
|
|
12
12
|
import type { AgentStorage } from "../session/agent-storage";
|
|
13
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
DEFAULT_ARTIFACT_MAX_BYTES,
|
|
15
|
+
DEFAULT_MAX_BYTES,
|
|
16
|
+
type TruncationDirection,
|
|
17
|
+
truncateContent,
|
|
18
|
+
} from "../session/streaming-output";
|
|
14
19
|
import { renderStatusLine } from "../tui";
|
|
15
20
|
import { CachedOutputBlock } from "../tui/output-block";
|
|
21
|
+
import { isValidArtifactId } from "../utils/artifact-id";
|
|
16
22
|
import { formatDimensionNote, resizeImage } from "../utils/image-resize";
|
|
17
23
|
import { parseHtmlLazy } from "../utils/linkedom";
|
|
18
24
|
import { INSANE_NOTES } from "../web/insane/bridge";
|
|
@@ -22,7 +28,7 @@ import type { RenderResult } from "../web/scrapers/types";
|
|
|
22
28
|
import { finalizeOutput, loadPage, looksLikeHtml, MAX_OUTPUT_CHARS } from "../web/scrapers/types";
|
|
23
29
|
import { convertWithMarkit, fetchBinary } from "../web/scrapers/utils";
|
|
24
30
|
import { applyListLimit } from "./list-limit";
|
|
25
|
-
import {
|
|
31
|
+
import { formatArtifactEvidenceNotice, type OutputMeta } from "./output-meta";
|
|
26
32
|
import { formatExpandHint, getDomain, replaceTabs } from "./render-utils";
|
|
27
33
|
import { ToolAbortError, ToolError } from "./tool-errors";
|
|
28
34
|
import { toolResult } from "./tool-result";
|
|
@@ -1144,6 +1150,9 @@ export interface ReadUrlToolDetails {
|
|
|
1144
1150
|
|
|
1145
1151
|
interface ReadUrlCacheEntry {
|
|
1146
1152
|
artifactId?: string;
|
|
1153
|
+
artifactVerified?: boolean;
|
|
1154
|
+
artifactFailureDiagnostic?: string;
|
|
1155
|
+
artifactStorageIncomplete?: boolean;
|
|
1147
1156
|
details: ReadUrlToolDetails;
|
|
1148
1157
|
image?: FetchImagePayload;
|
|
1149
1158
|
/** Complete URL output, including its preamble and body. */
|
|
@@ -1180,7 +1189,7 @@ async function materializeReadUrlCacheEntry(
|
|
|
1180
1189
|
entry: ReadUrlCacheEntry,
|
|
1181
1190
|
): Promise<ReadUrlCacheEntry | null> {
|
|
1182
1191
|
if (entry.output.length > 0) return entry;
|
|
1183
|
-
if (entry.artifactId) {
|
|
1192
|
+
if (isValidArtifactId(entry.artifactId) && entry.artifactVerified === true) {
|
|
1184
1193
|
const artifactOutput = await readArtifactOutput(session, entry.artifactId);
|
|
1185
1194
|
if (artifactOutput !== null) {
|
|
1186
1195
|
// Artifacts retain their trust-boundary wrapper. Keep the raw preamble
|
|
@@ -1188,6 +1197,7 @@ async function materializeReadUrlCacheEntry(
|
|
|
1188
1197
|
return {
|
|
1189
1198
|
...entry,
|
|
1190
1199
|
output: artifactOutput,
|
|
1200
|
+
artifactVerified: true,
|
|
1191
1201
|
...(entry.wrappedPreambleChars !== undefined ? { wrappedPreambleChars: entry.wrappedPreambleChars } : {}),
|
|
1192
1202
|
};
|
|
1193
1203
|
}
|
|
@@ -1195,17 +1205,60 @@ async function materializeReadUrlCacheEntry(
|
|
|
1195
1205
|
return null;
|
|
1196
1206
|
}
|
|
1197
1207
|
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1208
|
+
const READ_URL_ARTIFACT_WAIT_MS = 500;
|
|
1209
|
+
const READ_URL_ARTIFACT_PENDING_LIMIT = 64;
|
|
1210
|
+
const pendingReadUrlArtifactWrites = new Set<Promise<unknown>>();
|
|
1211
|
+
|
|
1212
|
+
async function persistReadUrlArtifact(
|
|
1213
|
+
session: ToolSession,
|
|
1214
|
+
output: string,
|
|
1215
|
+
): Promise<{ artifactId?: string; diagnostic?: string; storageIncomplete?: boolean }> {
|
|
1216
|
+
if (pendingReadUrlArtifactWrites.size >= READ_URL_ARTIFACT_PENDING_LIMIT) {
|
|
1217
|
+
return { diagnostic: "pending URL artifact persistence limit reached" };
|
|
1218
|
+
}
|
|
1219
|
+
const promise = Promise.resolve().then(async () => {
|
|
1220
|
+
const wrapped = wrapUntrustedContent(output);
|
|
1221
|
+
const artifactManager = session.getArtifactManager?.();
|
|
1222
|
+
if (artifactManager?.getManagedStore()) {
|
|
1223
|
+
const artifactId = await artifactManager.save(wrapped, "read");
|
|
1224
|
+
const contentBytes = Buffer.byteLength(wrapped, "utf8");
|
|
1225
|
+
return {
|
|
1226
|
+
artifactId,
|
|
1227
|
+
storageIncomplete: contentBytes > DEFAULT_ARTIFACT_MAX_BYTES || undefined,
|
|
1228
|
+
};
|
|
1229
|
+
}
|
|
1230
|
+
const { path: artifactPath, id } = (await session.allocateOutputArtifact?.("read")) ?? {};
|
|
1231
|
+
if (!artifactPath || !isValidArtifactId(id)) {
|
|
1232
|
+
return { diagnostic: "artifact storage is unavailable or returned an invalid artifact id" };
|
|
1233
|
+
}
|
|
1234
|
+
await Bun.write(artifactPath, wrapped);
|
|
1235
|
+
return { artifactId: id };
|
|
1236
|
+
});
|
|
1237
|
+
pendingReadUrlArtifactWrites.add(promise);
|
|
1238
|
+
void promise.then(
|
|
1239
|
+
() => pendingReadUrlArtifactWrites.delete(promise),
|
|
1240
|
+
() => pendingReadUrlArtifactWrites.delete(promise),
|
|
1241
|
+
);
|
|
1242
|
+
return Promise.race([
|
|
1243
|
+
promise.catch(error => ({
|
|
1244
|
+
diagnostic: error instanceof Error ? error.message.slice(0, 512) : String(error).slice(0, 512),
|
|
1245
|
+
})),
|
|
1246
|
+
Bun.sleep(READ_URL_ARTIFACT_WAIT_MS).then(() => ({
|
|
1247
|
+
diagnostic: `did not settle within ${READ_URL_ARTIFACT_WAIT_MS}ms`,
|
|
1248
|
+
})),
|
|
1249
|
+
]);
|
|
1203
1250
|
}
|
|
1204
1251
|
|
|
1205
1252
|
async function ensureReadUrlCacheArtifact(session: ToolSession, entry: ReadUrlCacheEntry): Promise<ReadUrlCacheEntry> {
|
|
1206
|
-
if (entry.artifactId) return entry;
|
|
1207
|
-
const
|
|
1208
|
-
return
|
|
1253
|
+
if (isValidArtifactId(entry.artifactId) && entry.artifactVerified === true) return entry;
|
|
1254
|
+
const persisted = await persistReadUrlArtifact(session, entry.output);
|
|
1255
|
+
return {
|
|
1256
|
+
...entry,
|
|
1257
|
+
artifactId: persisted.artifactId,
|
|
1258
|
+
artifactVerified: persisted.artifactId !== undefined,
|
|
1259
|
+
artifactFailureDiagnostic: persisted.diagnostic,
|
|
1260
|
+
artifactStorageIncomplete: persisted.storageIncomplete,
|
|
1261
|
+
};
|
|
1209
1262
|
}
|
|
1210
1263
|
|
|
1211
1264
|
function cacheReadUrlEntry(session: ToolSession, requestedUrl: string, raw: boolean, entry: ReadUrlCacheEntry): void {
|
|
@@ -1230,10 +1283,13 @@ async function buildReadUrlCacheEntry(
|
|
|
1230
1283
|
const storage = session.settings.getStorage();
|
|
1231
1284
|
const result = await renderUrl(url, effectiveTimeout, raw, session.settings, signal, storage);
|
|
1232
1285
|
const built = buildUrlReadOutput(result, result.content);
|
|
1233
|
-
const
|
|
1286
|
+
const persistedArtifact = options?.ensureArtifact ? await persistReadUrlArtifact(session, built.output) : {};
|
|
1234
1287
|
|
|
1235
1288
|
return {
|
|
1236
|
-
artifactId,
|
|
1289
|
+
artifactId: persistedArtifact.artifactId,
|
|
1290
|
+
artifactVerified: persistedArtifact.artifactId !== undefined,
|
|
1291
|
+
artifactFailureDiagnostic: persistedArtifact.diagnostic,
|
|
1292
|
+
artifactStorageIncomplete: persistedArtifact.storageIncomplete,
|
|
1237
1293
|
details: {
|
|
1238
1294
|
kind: "url",
|
|
1239
1295
|
url: result.url,
|
|
@@ -1371,7 +1427,7 @@ export async function executeReadUrl(
|
|
|
1371
1427
|
direction: effectiveDirection,
|
|
1372
1428
|
});
|
|
1373
1429
|
const needsArtifact = truncation.truncated;
|
|
1374
|
-
if (needsArtifact && !cacheEntry.artifactId) {
|
|
1430
|
+
if (needsArtifact && (!isValidArtifactId(cacheEntry.artifactId) || cacheEntry.artifactVerified !== true)) {
|
|
1375
1431
|
cacheEntry = await ensureReadUrlCacheArtifact(session, cacheEntry);
|
|
1376
1432
|
cacheReadUrlEntry(session, params.path, params.raw ?? false, cacheEntry);
|
|
1377
1433
|
}
|
|
@@ -1398,6 +1454,9 @@ export async function executeReadUrl(
|
|
|
1398
1454
|
resultBuilder.truncation(truncation, {
|
|
1399
1455
|
direction: effectiveDirection === "both" ? "middle" : effectiveDirection === "last" ? "tail" : "head",
|
|
1400
1456
|
artifactId: cacheEntry.artifactId,
|
|
1457
|
+
artifactVerified: cacheEntry.artifactVerified === true || undefined,
|
|
1458
|
+
artifactFailureDiagnostic: cacheEntry.artifactFailureDiagnostic,
|
|
1459
|
+
sourceCaptureIncomplete: cacheEntry.details.truncated || cacheEntry.artifactStorageIncomplete || undefined,
|
|
1401
1460
|
...(effectiveDirection !== "head" ? { maxBytes: DEFAULT_MAX_BYTES } : {}),
|
|
1402
1461
|
});
|
|
1403
1462
|
} else if (cacheEntry.details.truncated) {
|
|
@@ -1501,7 +1560,10 @@ export function renderReadUrlResult(
|
|
|
1501
1560
|
metadataLines.push(`${uiTheme.fg("muted", "Chars:")} ${charCount}`);
|
|
1502
1561
|
if (truncated) {
|
|
1503
1562
|
metadataLines.push(uiTheme.fg("warning", `${uiTheme.status.warning} Output truncated`));
|
|
1504
|
-
if (truncation?.artifactId
|
|
1563
|
+
if (truncation?.artifactId && truncation.artifactVerified === true) {
|
|
1564
|
+
const artifactNotice = formatArtifactEvidenceNotice(truncation);
|
|
1565
|
+
if (artifactNotice) metadataLines.push(uiTheme.fg("warning", artifactNotice));
|
|
1566
|
+
}
|
|
1505
1567
|
}
|
|
1506
1568
|
if (hasNotes) {
|
|
1507
1569
|
metadataLines.push(`${uiTheme.fg("muted", "Notes:")} ${details.notes.join("; ")}`);
|
package/src/tools/find.ts
CHANGED
|
@@ -14,7 +14,7 @@ import { type TruncationResult, truncateHead } from "../session/streaming-output
|
|
|
14
14
|
import { Ellipsis, fileHyperlink, renderFileList, renderStatusLine, renderTreeList, truncateToWidth } from "../tui";
|
|
15
15
|
import type { ToolSession } from ".";
|
|
16
16
|
import { applyListLimit } from "./list-limit";
|
|
17
|
-
import {
|
|
17
|
+
import { formatArtifactEvidenceNotice, type OutputMeta } from "./output-meta";
|
|
18
18
|
import {
|
|
19
19
|
formatPathRelativeToCwd,
|
|
20
20
|
hasGlobPathChars,
|
|
@@ -510,8 +510,12 @@ export const findToolRenderer = {
|
|
|
510
510
|
if (details?.resultLimitReached) truncationReasons.push(`limit ${details.resultLimitReached} results`);
|
|
511
511
|
if (limits?.resultLimit) truncationReasons.push(`limit ${limits.resultLimit.reached} results`);
|
|
512
512
|
if (truncation) truncationReasons.push(truncation.truncatedBy === "lines" ? "line limit" : "size limit");
|
|
513
|
-
const
|
|
514
|
-
|
|
513
|
+
const artifactEvidence = details?.meta?.truncation
|
|
514
|
+
? formatArtifactEvidenceNotice(details.meta.truncation)
|
|
515
|
+
: truncation && "artifactId" in truncation
|
|
516
|
+
? formatArtifactEvidenceNotice(truncation as NonNullable<OutputMeta["truncation"]>)
|
|
517
|
+
: undefined;
|
|
518
|
+
if (artifactEvidence) truncationReasons.push(artifactEvidence);
|
|
515
519
|
|
|
516
520
|
const extraLines: string[] = [];
|
|
517
521
|
if (truncationReasons.length > 0) {
|
package/src/tools/monitor.ts
CHANGED
|
@@ -51,6 +51,7 @@ const MONITOR_LABEL_MAX = 120;
|
|
|
51
51
|
const MAX_PENDING_MONITOR_NOTIFICATIONS = 3;
|
|
52
52
|
const MONITOR_NOTIFICATION_LINE_MAX_BYTES = 16 * 1024;
|
|
53
53
|
const MONITOR_NOTIFICATION_LINE_MAX_LINES = 20;
|
|
54
|
+
const PERSISTENT_MONITOR_DEBOUNCE_MS = 250;
|
|
54
55
|
|
|
55
56
|
function buildMonitorLabel(params: MonitorParams): string {
|
|
56
57
|
const base = `[monitor:${params.kind}] ${params.description}`;
|
|
@@ -124,6 +125,7 @@ export class MonitorTool implements AgentTool<typeof monitorSchema, MonitorToolD
|
|
|
124
125
|
const controller = { closed: false };
|
|
125
126
|
let currentJobId = "";
|
|
126
127
|
let sequence = 0;
|
|
128
|
+
let flushTimer: NodeJS.Timeout | undefined;
|
|
127
129
|
let latestLine: string | undefined;
|
|
128
130
|
let coalescedCount = 0;
|
|
129
131
|
let flushScheduled = false;
|
|
@@ -136,6 +138,10 @@ export class MonitorTool implements AgentTool<typeof monitorSchema, MonitorToolD
|
|
|
136
138
|
(message.details as { taskId?: string } | undefined)?.taskId === currentJobId;
|
|
137
139
|
const flushLatest = () => {
|
|
138
140
|
if (!persistent || latestLine === undefined) return;
|
|
141
|
+
if (flushTimer) {
|
|
142
|
+
clearTimeout(flushTimer);
|
|
143
|
+
flushTimer = undefined;
|
|
144
|
+
}
|
|
139
145
|
const line = latestLine;
|
|
140
146
|
const count = coalescedCount;
|
|
141
147
|
latestLine = undefined;
|
|
@@ -205,7 +211,7 @@ export class MonitorTool implements AgentTool<typeof monitorSchema, MonitorToolD
|
|
|
205
211
|
coalescedCount += flushScheduled ? 1 : 0;
|
|
206
212
|
if (flushScheduled) return;
|
|
207
213
|
flushScheduled = true;
|
|
208
|
-
|
|
214
|
+
flushTimer = setTimeout(flushLatest, PERSISTENT_MONITOR_DEBOUNCE_MS);
|
|
209
215
|
};
|
|
210
216
|
const monitorJob = await bash.startMonitorJob(
|
|
211
217
|
{ command: params.command, timeout: params.timeout },
|