@gajae-code/coding-agent 0.12.6 → 0.12.8
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 +22 -1
- package/dist/types/async/job-manager.d.ts +36 -1
- package/dist/types/commands/notify.d.ts +4 -0
- package/dist/types/config/file-lock.d.ts +3 -0
- package/dist/types/config/model-discovery-manager.d.ts +6 -1
- package/dist/types/config/model-registry.d.ts +2 -0
- package/dist/types/config/resolve-config-value.d.ts +2 -1
- package/dist/types/config/settings-schema.d.ts +17 -1
- package/dist/types/eval/backend.d.ts +2 -2
- package/dist/types/exec/bash-executor.d.ts +8 -10
- package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +0 -36
- package/dist/types/internal-urls/registry-helpers.d.ts +0 -18
- package/dist/types/main.d.ts +1 -0
- package/dist/types/modes/components/hook-selector.d.ts +20 -0
- package/dist/types/modes/components/notifications-settings-editor.d.ts +53 -4
- package/dist/types/modes/controllers/extension-ui-controller.d.ts +1 -1
- package/dist/types/modes/controllers/selector-controller.d.ts +4 -2
- package/dist/types/sdk/broker/broker.d.ts +5 -1
- package/dist/types/sdk/broker/lifecycle-ledger.d.ts +1 -0
- package/dist/types/sdk/bus/chat-daemon-control.d.ts +11 -3
- package/dist/types/sdk/bus/config.d.ts +75 -113
- package/dist/types/sdk/bus/discord-live-provider.d.ts +8 -2
- package/dist/types/sdk/bus/discord-provider.d.ts +19 -0
- package/dist/types/sdk/bus/index.d.ts +1 -1
- package/dist/types/sdk/bus/notification-orchestration.d.ts +127 -18
- package/dist/types/sdk/bus/notification-service.d.ts +31 -4
- package/dist/types/sdk/bus/session-control.d.ts +8 -3
- package/dist/types/sdk/bus/slack-live-provider.d.ts +9 -2
- package/dist/types/sdk/bus/slack-provider.d.ts +22 -0
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +9 -4
- package/dist/types/sdk/client/discovery.d.ts +2 -1
- package/dist/types/sdk/host/host.d.ts +2 -23
- package/dist/types/sdk/host/query/handlers.d.ts +2 -0
- package/dist/types/sdk/host/reverse-leases.d.ts +4 -7
- package/dist/types/sdk/index.d.ts +1 -0
- package/dist/types/sdk/providers.d.ts +22 -0
- package/dist/types/sdk/session-directory.d.ts +1 -1
- package/dist/types/session/artifacts.d.ts +5 -8
- package/dist/types/session/client-bridge.d.ts +0 -14
- package/dist/types/session/internal/managed-session-scope.d.ts +17 -1
- package/dist/types/session/internal/managed-session-storage.d.ts +11 -4
- package/dist/types/session/internal/native-publish-outcome.d.ts +1 -0
- package/dist/types/session/internal/session-open-errors.d.ts +5 -0
- package/dist/types/session/messages.d.ts +1 -4
- package/dist/types/session/session-manager.d.ts +7 -6
- package/dist/types/session/session-storage.d.ts +2 -0
- package/dist/types/session/streaming-output.d.ts +0 -32
- package/dist/types/task/receipt.d.ts +1 -2
- package/dist/types/task/types.d.ts +43 -178
- package/dist/types/tools/bash.d.ts +1 -1
- package/dist/types/tools/fetch.d.ts +0 -3
- package/dist/types/tools/output-meta.d.ts +5 -37
- package/dist/types/tools/subagent.d.ts +15 -0
- package/dist/types/tools/todo-write.d.ts +3 -0
- package/dist/types/tools/tool-result.d.ts +0 -1
- package/package.json +10 -8
- package/scripts/build-sdk-package-smoke.ts +51 -0
- package/scripts/capture-notifications-settings-showcase.ts +1 -1
- package/src/async/job-manager.ts +430 -46
- package/src/cli/notify-cli.ts +204 -94
- package/src/commands/notify.ts +19 -8
- package/src/config/file-lock.ts +6 -0
- package/src/config/model-discovery-manager.ts +50 -8
- package/src/config/model-registry.ts +862 -75
- package/src/config/resolve-config-value.ts +10 -8
- package/src/config/settings-schema.ts +25 -15
- package/src/config/settings.ts +15 -15
- package/src/cursor.ts +13 -20
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +2 -3
- package/src/eval/backend.ts +2 -2
- package/src/exec/bash-executor.ts +151 -425
- package/src/gjc-runtime/session-state-sidecar.ts +12 -2
- package/src/gjc-runtime/ultragoal-runtime.ts +3 -280
- package/src/gjc-runtime/workflow-manifest.generated.json +1 -40
- package/src/gjc-runtime/workflow-manifest.ts +0 -12
- package/src/internal-urls/agent-protocol.ts +38 -98
- package/src/internal-urls/artifact-protocol.ts +17 -28
- package/src/internal-urls/docs-index.generated.ts +7 -7
- package/src/internal-urls/registry-helpers.ts +0 -62
- package/src/main.ts +43 -7
- package/src/modes/DESIGN.md +11 -11
- package/src/modes/acp/acp-agent.ts +0 -24
- package/src/modes/acp/acp-client-bridge.ts +11 -154
- package/src/modes/components/hook-selector.ts +155 -44
- package/src/modes/components/model-selector.ts +242 -49
- package/src/modes/components/notifications-settings-editor.ts +631 -85
- package/src/modes/controllers/extension-ui-controller.ts +114 -15
- package/src/modes/controllers/selector-controller.ts +464 -50
- package/src/prompts/tools/bash.md +2 -2
- package/src/sdk/acp/adapter.ts +9 -200
- package/src/sdk/broker/broker.ts +54 -10
- package/src/sdk/broker/lifecycle-ledger.ts +34 -1
- package/src/sdk/broker/lifecycle.ts +47 -50
- package/src/sdk/bus/chat-daemon-cli.ts +12 -5
- package/src/sdk/bus/chat-daemon-control.ts +35 -8
- package/src/sdk/bus/chat-daemon-runtime.ts +16 -2
- package/src/sdk/bus/config.ts +534 -286
- package/src/sdk/bus/discord-live-provider.ts +57 -2
- package/src/sdk/bus/discord-provider.ts +21 -0
- package/src/sdk/bus/index.ts +105 -359
- package/src/sdk/bus/notification-orchestration.ts +371 -68
- package/src/sdk/bus/notification-service.ts +431 -130
- package/src/sdk/bus/session-control.ts +119 -44
- package/src/sdk/bus/slack-live-provider.ts +78 -2
- package/src/sdk/bus/slack-provider.ts +24 -0
- package/src/sdk/bus/telegram-daemon-cli.ts +9 -2
- package/src/sdk/bus/telegram-daemon-contract.ts +9 -4
- package/src/sdk/bus/telegram-daemon-control.ts +3 -3
- package/src/sdk/bus/telegram-daemon.ts +23 -15
- package/src/sdk/client/discovery.ts +10 -4
- package/src/sdk/host/host.ts +9 -187
- package/src/sdk/host/query/handlers.ts +20 -1
- package/src/sdk/host/reverse-leases.ts +42 -249
- package/src/sdk/index.ts +1 -0
- package/src/sdk/protocol/operation-inventory.generated.json +18 -0
- package/src/sdk/protocol/operation-registry.ts +5 -2
- package/src/sdk/providers.ts +51 -0
- package/src/sdk/session-directory.ts +1 -0
- package/src/sdk/session.ts +10 -7
- package/src/session/agent-session.ts +59 -202
- package/src/session/artifacts.ts +74 -211
- package/src/session/client-bridge.ts +0 -15
- package/src/session/internal/managed-session-scope.ts +981 -450
- package/src/session/internal/managed-session-storage.ts +362 -124
- package/src/session/internal/native-publish-outcome.ts +3 -1
- package/src/session/internal/session-open-errors.ts +11 -0
- package/src/session/messages.ts +7 -32
- package/src/session/session-manager.ts +48 -15
- package/src/session/session-storage.ts +72 -5
- package/src/session/streaming-output.ts +40 -198
- package/src/slash-commands/builtin-registry.ts +82 -4
- package/src/task/index.ts +237 -65
- package/src/task/receipt.ts +2 -4
- package/src/task/render.ts +0 -30
- package/src/task/types.ts +10 -34
- package/src/tools/ask.ts +1 -1
- package/src/tools/bash.ts +239 -818
- package/src/tools/eval.ts +4 -99
- package/src/tools/fetch.ts +15 -77
- package/src/tools/find.ts +3 -7
- package/src/tools/output-meta.ts +114 -696
- package/src/tools/search.ts +4 -9
- package/src/tools/subagent.ts +97 -75
- package/src/tools/telegram-send.ts +4 -2
- package/src/tools/todo-write.ts +47 -0
- package/src/tools/tool-result.ts +0 -1
- package/dist/types/gjc-runtime/ultragoal-review-source.d.ts +0 -83
- package/dist/types/utils/artifact-id.d.ts +0 -2
- package/src/gjc-runtime/ultragoal-review-source.ts +0 -378
- package/src/utils/artifact-id.ts +0 -6
|
@@ -349,12 +349,7 @@ import {
|
|
|
349
349
|
import { assertEditableFile } from "../tools/auto-generated-guard";
|
|
350
350
|
import { releaseTabsForOwner } from "../tools/browser/tab-supervisor";
|
|
351
351
|
import type { CheckpointState } from "../tools/checkpoint";
|
|
352
|
-
import {
|
|
353
|
-
createArtifactFailurePreview,
|
|
354
|
-
type OutputMeta,
|
|
355
|
-
outputMeta,
|
|
356
|
-
wrapToolWithMetaNotice,
|
|
357
|
-
} from "../tools/output-meta";
|
|
352
|
+
import { outputMeta, wrapToolWithMetaNotice } from "../tools/output-meta";
|
|
358
353
|
import { normalizeLocalScheme, resolveReadPath, resolveToCwd } from "../tools/path-utils";
|
|
359
354
|
import { registerResourceGcSession } from "../tools/resource-gc";
|
|
360
355
|
import { getLatestTodoPhasesFromEntries, type TodoItem, type TodoPhase } from "../tools/todo-write";
|
|
@@ -1062,102 +1057,6 @@ function boundAgentBashArtifactSaveDiagnostic(error: unknown): string {
|
|
|
1062
1057
|
return truncateHeadBytes(normalized, AGENT_BASH_ARTIFACT_SAVE_DIAGNOSTIC_MAX_BYTES).text;
|
|
1063
1058
|
}
|
|
1064
1059
|
|
|
1065
|
-
const PRE_ADMISSION_ARTIFACT_SAVE_WAIT_MS = 500;
|
|
1066
|
-
const PRE_ADMISSION_ARTIFACT_MAX_PENDING_SAVES = 2;
|
|
1067
|
-
|
|
1068
|
-
type PreAdmissionArtifactSaveOutcome =
|
|
1069
|
-
| { kind: "saved"; saved: Awaited<ReturnType<SessionManager["saveArtifactReceipt"]>> }
|
|
1070
|
-
| { kind: "failed"; error: unknown };
|
|
1071
|
-
|
|
1072
|
-
interface PreAdmissionArtifactSaveAdmission {
|
|
1073
|
-
readonly pending: Set<Promise<PreAdmissionArtifactSaveOutcome>>;
|
|
1074
|
-
}
|
|
1075
|
-
|
|
1076
|
-
/**
|
|
1077
|
-
* Pre-admission spills are owned by their concrete SessionManager. A stalled
|
|
1078
|
-
* save remains admitted until its promise settles, so timeout does not turn
|
|
1079
|
-
* each subsequent tool result into another retained full payload.
|
|
1080
|
-
*/
|
|
1081
|
-
const preAdmissionArtifactSaveAdmissions = new WeakMap<SessionManager, PreAdmissionArtifactSaveAdmission>();
|
|
1082
|
-
|
|
1083
|
-
function preAdmissionArtifactSaveAdmissionFor(sessionManager: SessionManager): PreAdmissionArtifactSaveAdmission {
|
|
1084
|
-
let admission = preAdmissionArtifactSaveAdmissions.get(sessionManager);
|
|
1085
|
-
if (!admission) {
|
|
1086
|
-
admission = { pending: new Set() };
|
|
1087
|
-
preAdmissionArtifactSaveAdmissions.set(sessionManager, admission);
|
|
1088
|
-
}
|
|
1089
|
-
return admission;
|
|
1090
|
-
}
|
|
1091
|
-
|
|
1092
|
-
function beginPreAdmissionArtifactSave(
|
|
1093
|
-
sessionManager: SessionManager,
|
|
1094
|
-
content: string,
|
|
1095
|
-
toolType: string,
|
|
1096
|
-
): { kind: "capacity" } | { kind: "started"; outcome: Promise<PreAdmissionArtifactSaveOutcome> } {
|
|
1097
|
-
const admission = preAdmissionArtifactSaveAdmissionFor(sessionManager);
|
|
1098
|
-
if (admission.pending.size >= PRE_ADMISSION_ARTIFACT_MAX_PENDING_SAVES) return { kind: "capacity" };
|
|
1099
|
-
|
|
1100
|
-
let outcome: Promise<PreAdmissionArtifactSaveOutcome>;
|
|
1101
|
-
try {
|
|
1102
|
-
outcome = sessionManager.saveArtifactReceipt(content, toolType).then(
|
|
1103
|
-
saved => ({ kind: "saved" as const, saved }),
|
|
1104
|
-
error => ({ kind: "failed" as const, error }),
|
|
1105
|
-
);
|
|
1106
|
-
} catch (error) {
|
|
1107
|
-
return { kind: "started", outcome: Promise.resolve({ kind: "failed", error }) };
|
|
1108
|
-
}
|
|
1109
|
-
admission.pending.add(outcome);
|
|
1110
|
-
void outcome.then(
|
|
1111
|
-
() => admission.pending.delete(outcome),
|
|
1112
|
-
() => admission.pending.delete(outcome),
|
|
1113
|
-
);
|
|
1114
|
-
return { kind: "started", outcome };
|
|
1115
|
-
}
|
|
1116
|
-
|
|
1117
|
-
function replaceToolResultWithArtifactFailurePreview(
|
|
1118
|
-
message: ToolResultMessage,
|
|
1119
|
-
fullText: string,
|
|
1120
|
-
maxInlineBytes: number | undefined,
|
|
1121
|
-
diagnostic: string,
|
|
1122
|
-
): void {
|
|
1123
|
-
const preview = createArtifactFailurePreview(fullText, maxInlineBytes, diagnostic);
|
|
1124
|
-
const existingDetails =
|
|
1125
|
-
message.details && typeof message.details === "object" ? (message.details as Record<string, unknown>) : {};
|
|
1126
|
-
const existingMeta =
|
|
1127
|
-
existingDetails.meta && typeof existingDetails.meta === "object" ? (existingDetails.meta as OutputMeta) : {};
|
|
1128
|
-
const existingTruncation = existingMeta.truncation;
|
|
1129
|
-
const totalLines = fullText.length > 0 ? fullText.split("\n").length : 0;
|
|
1130
|
-
const outputLines = preview.text.length > 0 ? preview.text.split("\n").length : 0;
|
|
1131
|
-
const outputBytes = Buffer.byteLength(preview.text, "utf-8");
|
|
1132
|
-
const truncation: NonNullable<OutputMeta["truncation"]> = {
|
|
1133
|
-
...(existingTruncation ?? {}),
|
|
1134
|
-
direction: "tail",
|
|
1135
|
-
truncatedBy: "bytes",
|
|
1136
|
-
noticeOwner: undefined,
|
|
1137
|
-
totalLines,
|
|
1138
|
-
totalBytes: Buffer.byteLength(fullText, "utf-8"),
|
|
1139
|
-
outputLines,
|
|
1140
|
-
outputBytes,
|
|
1141
|
-
maxBytes: maxInlineBytes,
|
|
1142
|
-
shownRange: undefined,
|
|
1143
|
-
headRange: undefined,
|
|
1144
|
-
tailRange: undefined,
|
|
1145
|
-
nextOffset: undefined,
|
|
1146
|
-
artifactId: undefined,
|
|
1147
|
-
artifactVerified: false,
|
|
1148
|
-
sourceCaptureIncomplete: true,
|
|
1149
|
-
artifactFailureDiagnostic: diagnostic,
|
|
1150
|
-
};
|
|
1151
|
-
message.details = {
|
|
1152
|
-
...existingDetails,
|
|
1153
|
-
meta: { ...existingMeta, truncation },
|
|
1154
|
-
};
|
|
1155
|
-
message.content = [
|
|
1156
|
-
...message.content.filter((block): block is ImageContent => block.type === "image"),
|
|
1157
|
-
{ type: "text", text: preview.text },
|
|
1158
|
-
];
|
|
1159
|
-
}
|
|
1160
|
-
|
|
1161
1060
|
function summarizeAgentBashArtifactSave(
|
|
1162
1061
|
artifactId: string,
|
|
1163
1062
|
originalText: string,
|
|
@@ -1970,13 +1869,6 @@ export class AgentSession {
|
|
|
1970
1869
|
this.#retryReplayEpoch++;
|
|
1971
1870
|
this.#retryReplayUnsafeEpoch = undefined;
|
|
1972
1871
|
this.#firstEventTimeoutRetryStartedAt = Date.now();
|
|
1973
|
-
if (
|
|
1974
|
-
this.#extensionRunner?.hasHandlers("context") ||
|
|
1975
|
-
this.#extensionRunner?.hasHandlers("before_provider_request") ||
|
|
1976
|
-
this.#extensionRunner?.hasHandlers("after_provider_response")
|
|
1977
|
-
) {
|
|
1978
|
-
this.#markRetryReplayUnsafe();
|
|
1979
|
-
}
|
|
1980
1872
|
}
|
|
1981
1873
|
|
|
1982
1874
|
#markRetryReplayUnsafe(): void {
|
|
@@ -1986,6 +1878,14 @@ export class AgentSession {
|
|
|
1986
1878
|
get #hasCleanRetryReplaySafety(): boolean {
|
|
1987
1879
|
return this.#retryReplayEpoch > 0 && this.#retryReplayUnsafeEpoch !== this.#retryReplayEpoch;
|
|
1988
1880
|
}
|
|
1881
|
+
#bindAttemptScope(scope: AttemptScope | undefined): void {
|
|
1882
|
+
if (!scope) return;
|
|
1883
|
+
this.#attemptRecordStore.register(scope);
|
|
1884
|
+
this.#attemptRecordStore.establishClean(scope);
|
|
1885
|
+
}
|
|
1886
|
+
#isRetryScopeClean(scope: AttemptScope | undefined): boolean {
|
|
1887
|
+
return scope !== undefined && this.#attemptRecordStore.isClean(scope);
|
|
1888
|
+
}
|
|
1989
1889
|
#prePromptContextCheckPromise: Promise<void> | undefined = undefined;
|
|
1990
1890
|
/** Display-only context snapshot; pre-prompt compaction estimates deliberately remain uncached. */
|
|
1991
1891
|
#contextUsageCache: { key: string; value: ContextUsage } | undefined;
|
|
@@ -1999,6 +1899,8 @@ export class AgentSession {
|
|
|
1999
1899
|
// Handoff state
|
|
2000
1900
|
#handoffAbortController: AbortController | undefined = undefined;
|
|
2001
1901
|
#skipPostTurnMaintenanceAssistantTimestamp: number | undefined = undefined;
|
|
1902
|
+
/** Scope cleanliness captured after message_end extension delivery and before terminal agent_end retirement. */
|
|
1903
|
+
#assistantAttemptScopes = new WeakMap<AssistantMessage, { scope?: AttemptScope; wasClean: boolean }>();
|
|
2002
1904
|
|
|
2003
1905
|
// Retry state
|
|
2004
1906
|
#retryAbortController: AbortController | undefined = undefined;
|
|
@@ -2707,6 +2609,7 @@ export class AgentSession {
|
|
|
2707
2609
|
if (this.#extensionRunner && typeof this.#extensionRunner.setAttemptRecordStore === "function") {
|
|
2708
2610
|
this.#extensionRunner.setAttemptRecordStore(this.#attemptRecordStore);
|
|
2709
2611
|
}
|
|
2612
|
+
this.agent.setMainAttemptScopeObserver(scope => this.#bindAttemptScope(scope));
|
|
2710
2613
|
this.#skills = config.skills ?? [];
|
|
2711
2614
|
this.#skillWarnings = config.skillWarnings ?? [];
|
|
2712
2615
|
this.#customCommands = config.customCommands ?? [];
|
|
@@ -3782,101 +3685,39 @@ export class AgentSession {
|
|
|
3782
3685
|
const textParts = message.content.flatMap(block => (block.type === "text" ? [block.text] : []));
|
|
3783
3686
|
if (textParts.length === 0) return;
|
|
3784
3687
|
const fullText = textParts.join("\n");
|
|
3785
|
-
const configuredInlineKiB = this.settings.get("tools.maxInlineResultBytes");
|
|
3786
|
-
const maxInlineBytes =
|
|
3787
|
-
configuredInlineKiB > 0 ? Math.max(1024, Math.floor(configuredInlineKiB * 1024)) : undefined;
|
|
3788
|
-
const totalBytes = Buffer.byteLength(fullText, "utf-8");
|
|
3789
|
-
const exceedsInlineCap = maxInlineBytes !== undefined && totalBytes > maxInlineBytes;
|
|
3790
3688
|
const contextWindow = this.model?.contextWindow;
|
|
3791
3689
|
const thresholdTokens = Math.min(
|
|
3792
3690
|
8_000,
|
|
3793
3691
|
contextWindow && contextWindow > 0 ? Math.floor(contextWindow * 0.05) : 8_000,
|
|
3794
3692
|
);
|
|
3795
|
-
|
|
3796
|
-
// before token heuristics can opt this result out of spilling.
|
|
3797
|
-
if (!exceedsInlineCap && (thresholdTokens <= 0 || estimateTextTokensHeuristic(fullText) <= thresholdTokens))
|
|
3798
|
-
return;
|
|
3693
|
+
if (thresholdTokens <= 0 || estimateTextTokensHeuristic(fullText) <= thresholdTokens) return;
|
|
3799
3694
|
|
|
3800
|
-
const saveAdmission = beginPreAdmissionArtifactSave(this.sessionManager, fullText, "tool-result");
|
|
3801
|
-
if (saveAdmission.kind === "capacity") {
|
|
3802
|
-
replaceToolResultWithArtifactFailurePreview(
|
|
3803
|
-
message,
|
|
3804
|
-
fullText,
|
|
3805
|
-
maxInlineBytes,
|
|
3806
|
-
"artifact save capacity exhausted; the previous save is still settling",
|
|
3807
|
-
);
|
|
3808
|
-
return;
|
|
3809
|
-
}
|
|
3810
3695
|
try {
|
|
3811
|
-
const
|
|
3812
|
-
|
|
3813
|
-
Bun.sleep(PRE_ADMISSION_ARTIFACT_SAVE_WAIT_MS).then(() => ({ kind: "timeout" as const })),
|
|
3814
|
-
]);
|
|
3815
|
-
if (saveOutcome.kind !== "saved" || !saveOutcome.saved) {
|
|
3816
|
-
const diagnostic =
|
|
3817
|
-
saveOutcome.kind === "timeout"
|
|
3818
|
-
? `did not settle within ${PRE_ADMISSION_ARTIFACT_SAVE_WAIT_MS}ms`
|
|
3819
|
-
: saveOutcome.kind === "failed"
|
|
3820
|
-
? boundAgentBashArtifactSaveDiagnostic(saveOutcome.error)
|
|
3821
|
-
: "artifact storage is unavailable";
|
|
3822
|
-
replaceToolResultWithArtifactFailurePreview(message, fullText, maxInlineBytes, diagnostic);
|
|
3823
|
-
return;
|
|
3824
|
-
}
|
|
3825
|
-
const saved = saveOutcome.saved;
|
|
3826
|
-
const existingDetails = message.details;
|
|
3827
|
-
const detailRecord =
|
|
3828
|
-
existingDetails && typeof existingDetails === "object" ? (existingDetails as Record<string, unknown>) : {};
|
|
3829
|
-
const existingMeta =
|
|
3830
|
-
detailRecord.meta && typeof detailRecord.meta === "object" ? (detailRecord.meta as OutputMeta) : {};
|
|
3831
|
-
const existingTruncation = existingMeta.truncation;
|
|
3832
|
-
const priorLoss = existingTruncation !== undefined;
|
|
3696
|
+
const artifactId = await this.sessionManager.saveArtifact(fullText, "tool-result");
|
|
3697
|
+
if (!artifactId) return;
|
|
3833
3698
|
const digest = crypto.createHash("sha256").update(fullText).digest("hex");
|
|
3834
|
-
const
|
|
3835
|
-
const preview = createPreAdmissionArtifactSpillPreview(fullText, saved.id, digest, {
|
|
3836
|
-
maxInlineBytes,
|
|
3837
|
-
fullOutput: saved.complete && !priorLoss,
|
|
3838
|
-
});
|
|
3699
|
+
const preview = createPreAdmissionArtifactSpillPreview(fullText, artifactId, digest);
|
|
3839
3700
|
const spillMeta = outputMeta()
|
|
3840
3701
|
.truncationFromText(preview, {
|
|
3841
3702
|
direction: "middle",
|
|
3842
3703
|
totalLines: fullText.split("\n").length,
|
|
3843
|
-
totalBytes,
|
|
3844
|
-
artifactId
|
|
3845
|
-
artifactVerified: true,
|
|
3846
|
-
sourceCaptureIncomplete,
|
|
3847
|
-
...(maxInlineBytes !== undefined ? { maxBytes: maxInlineBytes } : {}),
|
|
3704
|
+
totalBytes: Buffer.byteLength(fullText, "utf-8"),
|
|
3705
|
+
artifactId,
|
|
3848
3706
|
})
|
|
3849
3707
|
.get();
|
|
3850
|
-
const
|
|
3851
|
-
const
|
|
3852
|
-
|
|
3853
|
-
const
|
|
3854
|
-
|
|
3855
|
-
|
|
3856
|
-
|
|
3857
|
-
|
|
3858
|
-
artifactVerified: true,
|
|
3859
|
-
artifactTruncatedBytes,
|
|
3860
|
-
sourceTruncatedBytes: existingTruncation?.sourceTruncatedBytes,
|
|
3861
|
-
sourceCaptureIncomplete,
|
|
3862
|
-
columnDroppedBytes: existingTruncation?.columnDroppedBytes,
|
|
3863
|
-
shownRange: sourceCaptureIncomplete ? undefined : spillTruncation.shownRange,
|
|
3864
|
-
headRange: sourceCaptureIncomplete ? undefined : spillTruncation.headRange,
|
|
3865
|
-
tailRange: sourceCaptureIncomplete ? undefined : spillTruncation.tailRange,
|
|
3866
|
-
nextOffset: sourceCaptureIncomplete ? undefined : spillTruncation.nextOffset,
|
|
3867
|
-
}
|
|
3868
|
-
: existingTruncation;
|
|
3869
|
-
message.details = {
|
|
3870
|
-
...detailRecord,
|
|
3871
|
-
meta: { ...existingMeta, ...spillMeta, truncation: mergedTruncation },
|
|
3872
|
-
};
|
|
3708
|
+
const existingDetails = message.details;
|
|
3709
|
+
const detailRecord =
|
|
3710
|
+
existingDetails && typeof existingDetails === "object" ? (existingDetails as Record<string, unknown>) : {};
|
|
3711
|
+
const existingMeta =
|
|
3712
|
+
detailRecord.meta && typeof detailRecord.meta === "object"
|
|
3713
|
+
? (detailRecord.meta as Record<string, unknown>)
|
|
3714
|
+
: {};
|
|
3715
|
+
message.details = { ...detailRecord, meta: { ...existingMeta, ...spillMeta } };
|
|
3873
3716
|
message.content = [
|
|
3874
3717
|
...message.content.filter((block): block is ImageContent => block.type === "image"),
|
|
3875
3718
|
{ type: "text", text: preview },
|
|
3876
3719
|
];
|
|
3877
3720
|
} catch (error) {
|
|
3878
|
-
const diagnostic = boundAgentBashArtifactSaveDiagnostic(error);
|
|
3879
|
-
replaceToolResultWithArtifactFailurePreview(message, fullText, maxInlineBytes, diagnostic);
|
|
3880
3721
|
logger.warn("Failed to spill oversized tool result before context admission", {
|
|
3881
3722
|
toolName: message.toolName,
|
|
3882
3723
|
error: error instanceof Error ? error.message : String(error),
|
|
@@ -3902,11 +3743,7 @@ export class AgentSession {
|
|
|
3902
3743
|
/** Internal handler for agent events - shared by subscribe and reconnect */
|
|
3903
3744
|
#handleAgentEvent = async (event: AgentEvent, activePromptHandle?: string): Promise<void> => {
|
|
3904
3745
|
const attemptScope = (event as AgentEvent & { scope?: AttemptScope }).scope;
|
|
3905
|
-
|
|
3906
|
-
this.#attemptRecordStore.register(attemptScope);
|
|
3907
|
-
this.#attemptRecordStore.establishClean(attemptScope);
|
|
3908
|
-
}
|
|
3909
|
-
if (this.#extensionRunner?.hasHandlers(event.type)) this.#markRetryReplayUnsafe();
|
|
3746
|
+
|
|
3910
3747
|
if (
|
|
3911
3748
|
event.type === "tool_execution_start" ||
|
|
3912
3749
|
event.type === "tool_execution_update" ||
|
|
@@ -4110,6 +3947,12 @@ export class AgentSession {
|
|
|
4110
3947
|
}
|
|
4111
3948
|
|
|
4112
3949
|
await this.#emitSessionEvent(displayEvent);
|
|
3950
|
+
if (event.type === "message_end" && event.message.role === "assistant") {
|
|
3951
|
+
this.#assistantAttemptScopes.set(event.message, {
|
|
3952
|
+
scope: attemptScope,
|
|
3953
|
+
wasClean: this.#isRetryScopeClean(attemptScope),
|
|
3954
|
+
});
|
|
3955
|
+
}
|
|
4113
3956
|
if (
|
|
4114
3957
|
displayEvent !== event &&
|
|
4115
3958
|
displayEvent.type === "message_end" &&
|
|
@@ -4484,7 +4327,14 @@ export class AgentSession {
|
|
|
4484
4327
|
if (this.#isRetryableError(msg)) {
|
|
4485
4328
|
const transportFailure = (msg as AssistantMessage & { transportFailure?: TransportFailureFacts })
|
|
4486
4329
|
.transportFailure;
|
|
4487
|
-
const
|
|
4330
|
+
const messageScope = this.#assistantAttemptScopes.get(msg);
|
|
4331
|
+
const didRetry = await this.#handleRetryableError(
|
|
4332
|
+
msg,
|
|
4333
|
+
false,
|
|
4334
|
+
transportFailure,
|
|
4335
|
+
messageScope?.scope ?? event.scope,
|
|
4336
|
+
messageScope?.wasClean ?? false,
|
|
4337
|
+
);
|
|
4488
4338
|
if (didRetry) return; // Retry was initiated, don't proceed to compaction
|
|
4489
4339
|
}
|
|
4490
4340
|
if (this.#retryAttempt > 0) {
|
|
@@ -6001,6 +5851,7 @@ export class AgentSession {
|
|
|
6001
5851
|
this.#abortActiveMidRunBarriers();
|
|
6002
5852
|
this.abortCompaction();
|
|
6003
5853
|
this.agent.abort();
|
|
5854
|
+
this.agent.setMainAttemptScopeObserver(undefined);
|
|
6004
5855
|
// Disconnect the Agent event bridge NOW — before the maintenance join and the
|
|
6005
5856
|
// bounded idle / forceAbort below — so no agent_end emitted during teardown
|
|
6006
5857
|
// (including the one forceAbort emits) can re-enter #handleAgentEvent and start
|
|
@@ -14736,6 +14587,8 @@ export class AgentSession {
|
|
|
14736
14587
|
outcome.failure.message,
|
|
14737
14588
|
true,
|
|
14738
14589
|
outcome.failure.transportFailure,
|
|
14590
|
+
outcome.scope,
|
|
14591
|
+
this.#isRetryScopeClean(outcome.scope),
|
|
14739
14592
|
) as Promise<ManagedAttemptDecision>;
|
|
14740
14593
|
}
|
|
14741
14594
|
|
|
@@ -14896,6 +14749,8 @@ export class AgentSession {
|
|
|
14896
14749
|
message: AssistantMessage,
|
|
14897
14750
|
managedOutcome = false,
|
|
14898
14751
|
transportFailure?: TransportFailureFacts,
|
|
14752
|
+
scope?: AttemptScope,
|
|
14753
|
+
scopeWasClean = this.#isRetryScopeClean(scope),
|
|
14899
14754
|
): Promise<boolean | ManagedAttemptDecision> {
|
|
14900
14755
|
const controller = this.#defaultFallbackChain();
|
|
14901
14756
|
const managedFallback = controller.chain.entries.length > 1;
|
|
@@ -14910,12 +14765,16 @@ export class AgentSession {
|
|
|
14910
14765
|
if (!managedFallback && !retrySettings.enabled) return false;
|
|
14911
14766
|
const classification = this.#classifyErrorForRetry(message);
|
|
14912
14767
|
const firstEventTimeout = classification === "first_event_timeout";
|
|
14913
|
-
if (!managedFallback && firstEventTimeout) {
|
|
14914
|
-
this.#firstEventTimeoutRetryStartedAt
|
|
14768
|
+
if (!managedFallback && firstEventTimeout && this.#retryAttempt === 0) {
|
|
14769
|
+
this.#firstEventTimeoutRetryStartedAt = Date.now();
|
|
14915
14770
|
}
|
|
14916
|
-
|
|
14917
|
-
|
|
14918
|
-
|
|
14771
|
+
const requiresScopedFirstEventTimeout = managedFallback || !legacyRetryConfigured;
|
|
14772
|
+
if (
|
|
14773
|
+
firstEventTimeout &&
|
|
14774
|
+
(assistantMessageHasVisibleOrToolContent(message) ||
|
|
14775
|
+
(this.#retryAttempt > 0 && !this.#hasCleanRetryReplaySafety) ||
|
|
14776
|
+
(requiresScopedFirstEventTimeout && (!this.#isTypedFirstEventTimeout(message) || !scope || !scopeWasClean)))
|
|
14777
|
+
) {
|
|
14919
14778
|
return managedOutcome
|
|
14920
14779
|
? {
|
|
14921
14780
|
type: "terminal",
|
|
@@ -14943,14 +14802,12 @@ export class AgentSession {
|
|
|
14943
14802
|
// before the failure. This bypasses #hasCleanRetryReplaySafety because the
|
|
14944
14803
|
// content-free check is the replay-safety guarantee for credential rotation.
|
|
14945
14804
|
const canReplayRotatedCredential = credentialRotated;
|
|
14946
|
-
// Bare defaults
|
|
14947
|
-
//
|
|
14948
|
-
//
|
|
14805
|
+
// Bare defaults retain their narrow watchdog and Codex admissions. A
|
|
14806
|
+
// first-event timeout adds the typed, content-free, current-clean-scope
|
|
14807
|
+
// requirement above; other transient watchdogs preserve legacy behavior.
|
|
14949
14808
|
if (!managedFallback && !legacyRetryConfigured && !canReplayRotatedCredential) {
|
|
14950
14809
|
const bareDefaultCodexOverload = isBareDefaultCodexOverload(message);
|
|
14951
14810
|
const bareDefaultWrappedFirstEventTimeout = isBareDefaultWrappedFirstEventTimeout(message);
|
|
14952
|
-
// Content-free Codex overload is replay-safe by the same reasoning as
|
|
14953
|
-
// credential rotation: no partial output means no observable state.
|
|
14954
14811
|
const canReplayCodexOverload = bareDefaultCodexOverload;
|
|
14955
14812
|
if (
|
|
14956
14813
|
(!canReplayCodexOverload &&
|
|
@@ -14959,7 +14816,7 @@ export class AgentSession {
|
|
|
14959
14816
|
(hasBareDefaultRetryDisqualifyingFacts(message) ||
|
|
14960
14817
|
(classification !== "transient" && classification !== "first_event_timeout") ||
|
|
14961
14818
|
!BARE_DEFAULT_WATCHDOG_ERROR.test(message.errorMessage ?? ""))) ||
|
|
14962
|
-
(!canReplayCodexOverload && !this.#hasCleanRetryReplaySafety)
|
|
14819
|
+
(!canReplayCodexOverload && !firstEventTimeout && !this.#hasCleanRetryReplaySafety)
|
|
14963
14820
|
) {
|
|
14964
14821
|
return false;
|
|
14965
14822
|
}
|