@gajae-code/coding-agent 0.16.4 → 0.16.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 +54 -0
- package/dist/types/capability/types.d.ts +4 -0
- package/dist/types/cli/customize-doctor.d.ts +22 -0
- package/dist/types/config/file-lock.d.ts +31 -3
- package/dist/types/coordinator-mcp/question-state.d.ts +7 -0
- package/dist/types/coordinator-mcp/server.d.ts +7 -0
- package/dist/types/discovery/helpers.d.ts +33 -0
- package/dist/types/exec/bash-executor.d.ts +17 -1
- package/dist/types/exec/bash-shell-guardian.d.ts +15 -0
- package/dist/types/exec/bash-shell-runtime-entry.d.ts +2 -0
- package/dist/types/exec/bash-shell-signals.d.ts +2 -0
- package/dist/types/exec/bash-shell-supervisor-entry.d.ts +1 -0
- package/dist/types/exec/bash-shell-supervisor.d.ts +10 -0
- package/dist/types/exec/bash-shell-worker-entry.d.ts +2 -0
- package/dist/types/exec/bash-shell-worker-protocol.d.ts +63 -0
- package/dist/types/exec/bash-shell-worker.d.ts +1 -0
- package/dist/types/exec/isolated-shell.d.ts +19 -0
- package/dist/types/extensibility/extensions/function-hooks-internal.d.ts +9 -0
- package/dist/types/extensibility/extensions/function-hooks.d.ts +169 -0
- package/dist/types/extensibility/extensions/index.d.ts +1 -0
- package/dist/types/extensibility/extensions/runner.d.ts +46 -6
- package/dist/types/extensibility/extensions/types.d.ts +16 -0
- package/dist/types/extensibility/extensions/wrapper.d.ts +3 -1
- package/dist/types/extensibility/gjc-plugins/constrained-hooks.d.ts +35 -2
- package/dist/types/extensibility/gjc-plugins/types.d.ts +25 -0
- package/dist/types/extensibility/hooks/types.d.ts +4 -0
- package/dist/types/extensibility/runtime-skill-discovery.d.ts +4 -1
- package/dist/types/extensibility/skill-management.d.ts +9 -4
- package/dist/types/extensibility/skills.d.ts +2 -0
- package/dist/types/gjc-runtime/empty-delete-gc.d.ts +4 -1
- package/dist/types/gjc-runtime/linux-proc.d.ts +2 -0
- package/dist/types/gjc-runtime/session-state-lock.d.ts +12 -0
- package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +41 -3
- package/dist/types/gjc-runtime/ultragoal-succession.d.ts +251 -0
- package/dist/types/modes/components/bash-execution.d.ts +1 -0
- package/dist/types/modes/components/execution-shared.d.ts +1 -0
- package/dist/types/modes/interactive-mode.d.ts +2 -0
- package/dist/types/modes/types.d.ts +6 -0
- package/dist/types/sdk/broker/ensure.d.ts +32 -0
- package/dist/types/sdk/broker/lifecycle-ledger.d.ts +0 -1
- package/dist/types/sdk/broker/lifecycle.d.ts +6 -0
- package/dist/types/sdk/broker/startup-failure.d.ts +4 -1
- package/dist/types/sdk/bus/chat-daemon-control.d.ts +2 -0
- package/dist/types/sdk/bus/chat-effect-journal.d.ts +17 -2
- package/dist/types/sdk/bus/conversation-store.d.ts +11 -0
- package/dist/types/sdk/bus/lifecycle-commands.d.ts +8 -8
- package/dist/types/sdk/bus/slack-daemon.d.ts +1 -1
- package/dist/types/sdk/bus/topic-registry.d.ts +8 -0
- package/dist/types/sdk/client/client.d.ts +6 -0
- package/dist/types/sdk/host/host.d.ts +3 -1
- package/dist/types/sdk/host/session-runtime.d.ts +4 -2
- package/dist/types/sdk/lifecycle/service.d.ts +1 -0
- package/dist/types/sdk/router/session-router.d.ts +10 -2
- package/dist/types/sdk/session.d.ts +5 -1
- package/dist/types/session/agent-session.d.ts +2 -0
- package/dist/types/session/fallback-chain-controller.d.ts +15 -0
- package/dist/types/session/internal/managed-session-storage.d.ts +7 -0
- package/dist/types/session/messages.d.ts +1 -0
- package/dist/types/session/session-manager.d.ts +3 -1
- package/dist/types/setup/paseo/announce.d.ts +4 -3
- package/dist/types/skill-state/canonical-skills.d.ts +4 -0
- package/dist/types/system-prompt.d.ts +8 -0
- package/dist/types/task/executor.d.ts +3 -0
- package/dist/types/tools/bash.d.ts +1 -0
- package/dist/types/tools/github-cache.d.ts +2 -1
- package/dist/types/tools/index.d.ts +2 -0
- package/package.json +9 -7
- package/scripts/generate-sdk-operation-inventory.ts +2 -0
- package/src/capability/index.ts +14 -2
- package/src/capability/types.ts +4 -0
- package/src/cli/customize-doctor.ts +155 -12
- package/src/cli.ts +22 -0
- package/src/commands/sdk.ts +74 -31
- package/src/config/file-lock-gc.ts +40 -2
- package/src/config/file-lock.ts +198 -14
- package/src/config/model-preset-registry.ts +115 -16
- package/src/config/model-registry.ts +16 -12
- package/src/coordinator-mcp/question-state.ts +33 -2
- package/src/coordinator-mcp/server.ts +477 -101
- package/src/crash/sanitize.ts +1 -1
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +22 -0
- package/src/discovery/builtin.ts +50 -62
- package/src/discovery/helpers.ts +297 -32
- package/src/exec/bash-executor.ts +330 -50
- package/src/exec/bash-shell-guardian.ts +388 -0
- package/src/exec/bash-shell-runtime-entry.ts +5 -0
- package/src/exec/bash-shell-signals.ts +29 -0
- package/src/exec/bash-shell-supervisor-entry.ts +3 -0
- package/src/exec/bash-shell-supervisor.ts +359 -0
- package/src/exec/bash-shell-worker-entry.ts +5 -0
- package/src/exec/bash-shell-worker-protocol.ts +36 -0
- package/src/exec/bash-shell-worker.ts +116 -0
- package/src/exec/isolated-shell.ts +393 -0
- package/src/extensibility/extensions/function-hooks-internal.ts +73 -0
- package/src/extensibility/extensions/function-hooks.ts +959 -0
- package/src/extensibility/extensions/index.ts +22 -0
- package/src/extensibility/extensions/loader.ts +53 -2
- package/src/extensibility/extensions/runner.ts +895 -264
- package/src/extensibility/extensions/types.ts +42 -0
- package/src/extensibility/extensions/wrapper.ts +56 -15
- package/src/extensibility/gjc-plugins/compiler.ts +20 -2
- package/src/extensibility/gjc-plugins/constrained-hooks.ts +321 -23
- package/src/extensibility/gjc-plugins/registry.ts +113 -1
- package/src/extensibility/gjc-plugins/runtime-quarantine.ts +20 -2
- package/src/extensibility/gjc-plugins/schema.ts +121 -4
- package/src/extensibility/gjc-plugins/types.ts +25 -0
- package/src/extensibility/hooks/loader.ts +133 -16
- package/src/extensibility/hooks/types.ts +28 -0
- package/src/extensibility/runtime-skill-discovery.ts +80 -47
- package/src/extensibility/skill-management.ts +200 -52
- package/src/extensibility/skills.ts +29 -9
- package/src/gjc-runtime/empty-delete-gc.ts +94 -4
- package/src/gjc-runtime/gc-runtime.ts +29 -0
- package/src/gjc-runtime/linux-proc.ts +14 -0
- package/src/gjc-runtime/session-state-lock.ts +51 -8
- package/src/gjc-runtime/session-state-sidecar.ts +27 -11
- package/src/gjc-runtime/state-writer.ts +30 -29
- package/src/gjc-runtime/ultragoal-runtime.ts +189 -47
- package/src/gjc-runtime/ultragoal-succession.ts +1667 -0
- package/src/gjc-runtime/workflow-manifest.generated.json +52 -1
- package/src/gjc-runtime/workflow-manifest.ts +16 -0
- package/src/harness-control-plane/session-lease.ts +2 -2
- package/src/internal-urls/docs-index.generated.ts +6 -6
- package/src/lsp/config.ts +10 -3
- package/src/lsp/index.ts +1 -1
- package/src/lsp/path-trust.ts +69 -14
- package/src/main.ts +6 -1
- package/src/modes/acp/acp-agent.ts +16 -3
- package/src/modes/components/bash-execution.ts +4 -1
- package/src/modes/components/execution-shared.ts +4 -1
- package/src/modes/components/session-observer-overlay.ts +1 -1
- package/src/modes/components/tool-status-header.ts +3 -1
- package/src/modes/controllers/command-controller.ts +1 -0
- package/src/modes/controllers/event-controller.ts +3 -2
- package/src/modes/controllers/extension-ui-controller.ts +1 -0
- package/src/modes/controllers/input-controller.ts +6 -3
- package/src/modes/interactive-mode.ts +6 -5
- package/src/modes/print-mode.ts +5 -1
- package/src/modes/prompt-action-autocomplete.ts +54 -27
- package/src/modes/runtime-init.ts +2 -0
- package/src/modes/types.ts +16 -3
- package/src/modes/utils/ui-helpers.ts +1 -0
- package/src/prompts/tools/subagent.md +1 -0
- package/src/runtime-mcp/manager.ts +20 -2
- package/src/runtime-mcp/pool.test.ts +20 -11
- package/src/sdk/broker/broker.ts +119 -28
- package/src/sdk/broker/ensure.ts +248 -41
- package/src/sdk/broker/lifecycle-ledger.ts +7 -17
- package/src/sdk/broker/lifecycle.ts +56 -6
- package/src/sdk/broker/startup-failure.ts +15 -6
- package/src/sdk/bus/chat-daemon-control.ts +3 -1
- package/src/sdk/bus/chat-daemon-runtime.ts +35 -5
- package/src/sdk/bus/chat-effect-journal.ts +132 -7
- package/src/sdk/bus/conversation-store.ts +29 -0
- package/src/sdk/bus/lifecycle-commands.ts +17 -11
- package/src/sdk/bus/slack-conversation.ts +1 -0
- package/src/sdk/bus/slack-daemon.ts +387 -48
- package/src/sdk/bus/telegram-daemon.ts +16 -2
- package/src/sdk/bus/topic-registry.ts +19 -0
- package/src/sdk/cli/master-cli.ts +1 -1
- package/src/sdk/cli/session-cli.ts +2 -2
- package/src/sdk/client/client.ts +66 -17
- package/src/sdk/host/host.ts +29 -9
- package/src/sdk/host/query/revision-store.ts +19 -13
- package/src/sdk/host/reverse-leases.ts +7 -4
- package/src/sdk/host/session-runtime.test.ts +50 -8
- package/src/sdk/host/session-runtime.ts +158 -51
- package/src/sdk/lifecycle/service.ts +4 -0
- package/src/sdk/protocol/operation-inventory.generated.json +11 -0
- package/src/sdk/router/session-router.ts +89 -38
- package/src/sdk/session.ts +155 -26
- package/src/sdk/transport/relay.ts +35 -13
- package/src/session/agent-session.ts +117 -52
- package/src/session/fallback-chain-controller.ts +38 -0
- package/src/session/internal/managed-session-storage.ts +19 -5
- package/src/session/messages.ts +5 -0
- package/src/session/session-manager.ts +29 -5
- package/src/session-import/redact.ts +5 -1
- package/src/setup/paseo/announce.ts +49 -43
- package/src/setup/paseo/setup-deps.ts +1 -1
- package/src/skill-state/canonical-skills.ts +12 -0
- package/src/skills/skills.test.ts +41 -21
- package/src/system-prompt.ts +25 -3
- package/src/task/executor.ts +5 -0
- package/src/task/index.ts +4 -0
- package/src/tools/bash.ts +28 -11
- package/src/tools/gh.ts +26 -3
- package/src/tools/github-cache.ts +68 -16
- package/src/tools/index.ts +2 -0
- package/src/tools/job.ts +13 -2
- package/src/tools/read.ts +14 -2
- package/src/tools/render-utils.ts +1 -3
- package/src/tools/resource-gc.ts +28 -8
- package/src/tools/skill-discovery.ts +5 -0
- package/src/tools/skill.ts +15 -0
- package/src/tools/subagent.ts +3 -2
- package/src/tools/tool-catalog.generated.ts +2 -2
- package/src/tools/ultragoal-ask-guard.ts +2 -2
|
@@ -439,6 +439,7 @@ import {
|
|
|
439
439
|
type ConfiguredFallbackChain,
|
|
440
440
|
cappedExponentialWithFullJitter,
|
|
441
441
|
compactionRetryDelay,
|
|
442
|
+
describeCompactionCandidateFailures,
|
|
442
443
|
effectiveFallbackDelay,
|
|
443
444
|
FallbackChainController,
|
|
444
445
|
type FallbackChainRuntimeState,
|
|
@@ -2791,7 +2792,7 @@ export class AgentSession {
|
|
|
2791
2792
|
this.#activeAttemptScope = undefined;
|
|
2792
2793
|
this.#activeLogicalRunId = undefined;
|
|
2793
2794
|
}
|
|
2794
|
-
#acceptSdkAttemptRun(handle: AttemptRunHandle, sdkRunToken?: string): void {
|
|
2795
|
+
#acceptSdkAttemptRun(handle: AttemptRunHandle, sdkRunToken?: string, sdkRunTokens?: string[]): void {
|
|
2795
2796
|
const predecessorScope = this.#activeAttemptScope;
|
|
2796
2797
|
const predecessorSdkRunToken =
|
|
2797
2798
|
predecessorScope === undefined ? undefined : this.#sdkRunTokensByAttemptScope.get(predecessorScope);
|
|
@@ -2804,6 +2805,11 @@ export class AgentSession {
|
|
|
2804
2805
|
if (sdkRunToken !== undefined) {
|
|
2805
2806
|
this.#activeSdkRunToken = sdkRunToken;
|
|
2806
2807
|
this.#sdkRunTokensByAttemptScope.set(handle.scope, sdkRunToken);
|
|
2808
|
+
const inheritedCohort =
|
|
2809
|
+
predecessorScope !== undefined && predecessorSdkRunToken === sdkRunToken
|
|
2810
|
+
? this.#sdkRunCohortsByAttemptScope.get(predecessorScope)
|
|
2811
|
+
: undefined;
|
|
2812
|
+
this.#sdkRunCohortsByAttemptScope.set(handle.scope, sdkRunTokens ?? inheritedCohort ?? [sdkRunToken]);
|
|
2807
2813
|
}
|
|
2808
2814
|
if (carryRecoverySkip) this.#skipPostPromptRecoveryWaitByAttemptScope.add(handle.scope);
|
|
2809
2815
|
}
|
|
@@ -3108,6 +3114,7 @@ export class AgentSession {
|
|
|
3108
3114
|
#sdkRunTokensByQueuedMessage = new WeakMap<AgentMessage, string>();
|
|
3109
3115
|
#skipPostPromptRecoveryWaitByAttemptScope = new WeakSet<AttemptScope>();
|
|
3110
3116
|
#sdkRunTokensByAttemptScope = new WeakMap<AttemptScope, string>();
|
|
3117
|
+
#sdkRunCohortsByAttemptScope = new WeakMap<AttemptScope, string[]>();
|
|
3111
3118
|
#activeSdkRunToken: string | undefined;
|
|
3112
3119
|
#activeAttemptScope: AttemptScope | undefined;
|
|
3113
3120
|
#attemptAuthority!: AttemptScopeAuthority;
|
|
@@ -4578,7 +4585,7 @@ export class AgentSession {
|
|
|
4578
4585
|
this.#transformContext = config.transformContext ?? (messages => messages);
|
|
4579
4586
|
const configuredOnPayload = config.onPayload;
|
|
4580
4587
|
this.#onPayload = configuredOnPayload
|
|
4581
|
-
? (payload, model, scope) => configuredOnPayload(payload, model, scope)
|
|
4588
|
+
? (payload, model, scope, signal) => configuredOnPayload(payload, model, scope, signal)
|
|
4582
4589
|
: undefined;
|
|
4583
4590
|
this.rawSseDebugBuffer = config.rawSseDebugBuffer ?? new RawSseDebugBuffer();
|
|
4584
4591
|
// Avoid wrapping in an `async` closure when no user callback is configured: the
|
|
@@ -4587,9 +4594,9 @@ export class AgentSession {
|
|
|
4587
4594
|
// shows up as ~3.5% self time in streaming profiles.
|
|
4588
4595
|
const configuredOnResponse = config.onResponse;
|
|
4589
4596
|
this.#onResponse = configuredOnResponse
|
|
4590
|
-
? async (response, model, scope) => {
|
|
4597
|
+
? async (response, model, scope, signal) => {
|
|
4591
4598
|
this.rawSseDebugBuffer.recordResponse(response, model);
|
|
4592
|
-
await configuredOnResponse(response, model, scope);
|
|
4599
|
+
await configuredOnResponse(response, model, scope, signal);
|
|
4593
4600
|
}
|
|
4594
4601
|
: (response, model, _scope) => {
|
|
4595
4602
|
this.rawSseDebugBuffer.recordResponse(response, model);
|
|
@@ -6925,12 +6932,15 @@ export class AgentSession {
|
|
|
6925
6932
|
assistantMsg.stopReason !== "aborted" &&
|
|
6926
6933
|
this.#retryAttempt > 0
|
|
6927
6934
|
) {
|
|
6935
|
+
// Clear ownership before notifying observers: agent_end may run while
|
|
6936
|
+
// the success event is awaiting its subscribers.
|
|
6937
|
+
const attempt = this.#retryAttempt;
|
|
6938
|
+
this.#retryAttempt = 0;
|
|
6928
6939
|
await this.#emitSessionEvent({
|
|
6929
6940
|
type: "auto_retry_end",
|
|
6930
6941
|
success: true,
|
|
6931
|
-
attempt
|
|
6942
|
+
attempt,
|
|
6932
6943
|
});
|
|
6933
|
-
this.#retryAttempt = 0;
|
|
6934
6944
|
// Settle the retry gate here, colocated with the success event, rather
|
|
6935
6945
|
// than relying on the generic #resolveRetry() at the end of the
|
|
6936
6946
|
// agent_end branch. That tail resolver is bypassed by every early
|
|
@@ -7530,15 +7540,19 @@ export class AgentSession {
|
|
|
7530
7540
|
const inheritedSdkRunToken = options?.continueQueuedOnly
|
|
7531
7541
|
? undefined
|
|
7532
7542
|
: (options?.sdkRunToken ?? scheduledSdkRunToken);
|
|
7533
|
-
const
|
|
7543
|
+
const consumedSdkRunTokens = acceptance.consumedQueuedMessages
|
|
7534
7544
|
.map(message => this.#sdkRunTokensByQueuedMessage.get(message))
|
|
7535
|
-
.
|
|
7536
|
-
const sdkRunToken =
|
|
7545
|
+
.filter((token): token is string => token !== undefined);
|
|
7546
|
+
const sdkRunToken = consumedSdkRunTokens[0] ?? inheritedSdkRunToken;
|
|
7537
7547
|
this.#fireQueuedPromotionHooks(acceptance.consumedQueuedMessages, {
|
|
7538
7548
|
startsOwnRun: startsOwn,
|
|
7539
7549
|
});
|
|
7540
7550
|
if (startsOwn) this.#activeSdkRunToken = sdkRunToken;
|
|
7541
|
-
this.#acceptSdkAttemptRun(
|
|
7551
|
+
this.#acceptSdkAttemptRun(
|
|
7552
|
+
handle,
|
|
7553
|
+
sdkRunToken,
|
|
7554
|
+
consumedSdkRunTokens.length > 0 ? consumedSdkRunTokens : undefined,
|
|
7555
|
+
);
|
|
7542
7556
|
options?.onRunAccepted?.(handle);
|
|
7543
7557
|
// Keep the queued token available through the acceptance callback;
|
|
7544
7558
|
// SDK follow-up owners bind it to the new attempt scope there.
|
|
@@ -8674,6 +8688,9 @@ export class AgentSession {
|
|
|
8674
8688
|
{
|
|
8675
8689
|
type: "agent_start",
|
|
8676
8690
|
...(sdkRunToken ? { sdkRunToken } : {}),
|
|
8691
|
+
...(deliveryScope
|
|
8692
|
+
? { sdkRunTokens: this.#sdkRunCohortsByAttemptScope.get(deliveryScope as AttemptScope) }
|
|
8693
|
+
: {}),
|
|
8677
8694
|
},
|
|
8678
8695
|
undefined,
|
|
8679
8696
|
deliveryScope,
|
|
@@ -9549,6 +9566,11 @@ export class AgentSession {
|
|
|
9549
9566
|
});
|
|
9550
9567
|
}
|
|
9551
9568
|
|
|
9569
|
+
/** Test seam: await all currently admitted coordinator sidecar writes. */
|
|
9570
|
+
async awaitCoordinatorRuntimeStatePersistenceForTests(): Promise<void> {
|
|
9571
|
+
await this.#coordinatorPersistQueue;
|
|
9572
|
+
await this.#drainUnbarrieredCoordinatorPersists();
|
|
9573
|
+
}
|
|
9552
9574
|
queueCoordinatorRuntimeStatePersistForTests(event: AgentSessionEvent, gate: Promise<void>): Promise<void> {
|
|
9553
9575
|
this.#agentEventAdmission.set(event, {
|
|
9554
9576
|
persistGeneration: this.#coordinatorPersistGeneration,
|
|
@@ -10275,11 +10297,12 @@ export class AgentSession {
|
|
|
10275
10297
|
let wrappersByVersion = this.#guardedToolWrapperCache.get(tool);
|
|
10276
10298
|
const cached = wrappersByVersion?.get(cacheKey);
|
|
10277
10299
|
if (cached) return cached as T;
|
|
10278
|
-
const
|
|
10300
|
+
const innerTool = tool instanceof ExtensionToolWrapper ? tool.getInnerTool() : tool;
|
|
10301
|
+
const guarded = this.#wrapToolForCwdTransitionFence(
|
|
10279
10302
|
this.#wrapToolForWorkflowMutationGuard(
|
|
10280
10303
|
this.#wrapToolForAcpPermission(
|
|
10281
10304
|
guardToolForUltragoalAsk(
|
|
10282
|
-
|
|
10305
|
+
innerTool,
|
|
10283
10306
|
() => this.sessionManager.getCwd(),
|
|
10284
10307
|
() => ({
|
|
10285
10308
|
activeSkillState: this.getActiveSkillState(),
|
|
@@ -10290,6 +10313,7 @@ export class AgentSession {
|
|
|
10290
10313
|
),
|
|
10291
10314
|
),
|
|
10292
10315
|
);
|
|
10316
|
+
const wrapped = (this.#extensionRunner ? new ExtensionToolWrapper(guarded, this.#extensionRunner) : guarded) as T;
|
|
10293
10317
|
// The object published into `agent.state.tools` — and therefore the object the
|
|
10294
10318
|
// agent loop actually dispatches to — is this guard wrapper, not the registry
|
|
10295
10319
|
// entry. A wrapper built from a proven built-in inherits that proof; one built
|
|
@@ -10970,10 +10994,10 @@ export class AgentSession {
|
|
|
10970
10994
|
preparedOptions.onPayload = sessionOnPayload;
|
|
10971
10995
|
} else {
|
|
10972
10996
|
const requestOnPayload = options.onPayload;
|
|
10973
|
-
preparedOptions.onPayload = async (payload, model, callbackScope) => {
|
|
10974
|
-
const sessionPayload = await sessionOnPayload(payload, model, callbackScope);
|
|
10997
|
+
preparedOptions.onPayload = async (payload, model, callbackScope, signal) => {
|
|
10998
|
+
const sessionPayload = await sessionOnPayload(payload, model, callbackScope, signal);
|
|
10975
10999
|
const sessionResolvedPayload = sessionPayload ?? payload;
|
|
10976
|
-
const requestPayload = await requestOnPayload(sessionResolvedPayload, model, callbackScope);
|
|
11000
|
+
const requestPayload = await requestOnPayload(sessionResolvedPayload, model, callbackScope, signal);
|
|
10977
11001
|
return requestPayload ?? sessionResolvedPayload;
|
|
10978
11002
|
};
|
|
10979
11003
|
}
|
|
@@ -10984,9 +11008,9 @@ export class AgentSession {
|
|
|
10984
11008
|
preparedOptions.onResponse = sessionOnResponse;
|
|
10985
11009
|
} else {
|
|
10986
11010
|
const requestOnResponse = options.onResponse;
|
|
10987
|
-
preparedOptions.onResponse = async (response, model, callbackScope) => {
|
|
10988
|
-
await sessionOnResponse(response, model, callbackScope);
|
|
10989
|
-
await requestOnResponse(response, model, callbackScope);
|
|
11011
|
+
preparedOptions.onResponse = async (response, model, callbackScope, signal) => {
|
|
11012
|
+
await sessionOnResponse(response, model, callbackScope, signal);
|
|
11013
|
+
await requestOnResponse(response, model, callbackScope, signal);
|
|
10990
11014
|
};
|
|
10991
11015
|
}
|
|
10992
11016
|
}
|
|
@@ -11867,6 +11891,11 @@ export class AgentSession {
|
|
|
11867
11891
|
const releaseStartupPromptWaiter = this.#reserveStartupPromptWaiter();
|
|
11868
11892
|
try {
|
|
11869
11893
|
await this.#promptInternal(text, options, releaseStartupPromptWaiter);
|
|
11894
|
+
// Agent-core converts listener failures into an aborted response. Keep a
|
|
11895
|
+
// rejected persistence fence typed at the public prompt boundary instead
|
|
11896
|
+
// of reporting the misleading provider-level "Request was aborted".
|
|
11897
|
+
const appendRejection = this.sessionManager.getAppendIdentityMismatch();
|
|
11898
|
+
if (appendRejection) throw appendRejection;
|
|
11870
11899
|
} finally {
|
|
11871
11900
|
releaseStartupPromptWaiter();
|
|
11872
11901
|
}
|
|
@@ -12458,6 +12487,14 @@ export class AgentSession {
|
|
|
12458
12487
|
hasPendingNextTurnMessages = pendingNextTurnMessageCount > 0;
|
|
12459
12488
|
const promptAttribution: "user" | "agent" | undefined =
|
|
12460
12489
|
"attribution" in message ? message.attribution : undefined;
|
|
12490
|
+
let effectivePrompt = expandedText;
|
|
12491
|
+
const messageImages =
|
|
12492
|
+
"content" in message && Array.isArray(message.content)
|
|
12493
|
+
? message.content.filter((content): content is ImageContent => content.type === "image")
|
|
12494
|
+
: undefined;
|
|
12495
|
+
let effectiveImages =
|
|
12496
|
+
options?.images ?? (messageImages && messageImages.length > 0 ? messageImages : undefined);
|
|
12497
|
+
let promptMessage = message;
|
|
12461
12498
|
let phaseACompleted = false;
|
|
12462
12499
|
let fileMentionMessages: AgentMessage[] = [];
|
|
12463
12500
|
let beforeAgentStartResultMessages: BeforeAgentStartInternalMessage[] = [];
|
|
@@ -12478,7 +12515,49 @@ export class AgentSession {
|
|
|
12478
12515
|
if (!phaseACompleted) {
|
|
12479
12516
|
phaseACompleted = true;
|
|
12480
12517
|
// Phase A (one-time side-effectful products; runs once).
|
|
12481
|
-
const
|
|
12518
|
+
const beforeAgentStartSystemPrompt = await this.#buildSystemPromptForAgentStart(effectivePrompt);
|
|
12519
|
+
hindsightRecall = this.getHindsightSessionState()?.getRecallSnippetForInjection();
|
|
12520
|
+
planReferenceMessage = await this.#buildPlanReferenceMessage();
|
|
12521
|
+
// Emit before_agent_start extension event. Race hook completion with
|
|
12522
|
+
// prompt cancellation so a wedged hook cannot retain SDK prompt authority.
|
|
12523
|
+
if (this.#extensionRunner?.hasHandlers("before_agent_start")) this.#markRetryReplayUnsafe();
|
|
12524
|
+
if (this.#extensionRunner) {
|
|
12525
|
+
const result = await this.#awaitPromptPreflight(
|
|
12526
|
+
generation,
|
|
12527
|
+
preflightSignal,
|
|
12528
|
+
this.#extensionRunner.emitBeforeAgentStart(
|
|
12529
|
+
effectivePrompt,
|
|
12530
|
+
effectiveImages,
|
|
12531
|
+
beforeAgentStartSystemPrompt,
|
|
12532
|
+
preflightSignal,
|
|
12533
|
+
),
|
|
12534
|
+
);
|
|
12535
|
+
if (result?.messages) beforeAgentStartResultMessages = [...result.messages];
|
|
12536
|
+
if (result?.prompt !== undefined) effectivePrompt = result.prompt;
|
|
12537
|
+
if (result && Object.hasOwn(result, "images")) effectiveImages = result.images;
|
|
12538
|
+
if (result?.prompt !== undefined || (result && Object.hasOwn(result, "images"))) {
|
|
12539
|
+
if (
|
|
12540
|
+
promptMessage.role === "user" ||
|
|
12541
|
+
promptMessage.role === "developer" ||
|
|
12542
|
+
promptMessage.role === "custom"
|
|
12543
|
+
) {
|
|
12544
|
+
promptMessage = {
|
|
12545
|
+
...promptMessage,
|
|
12546
|
+
content: [{ type: "text", text: effectivePrompt }, ...(effectiveImages ?? [])],
|
|
12547
|
+
};
|
|
12548
|
+
}
|
|
12549
|
+
}
|
|
12550
|
+
if (result?.systemPrompt !== undefined) {
|
|
12551
|
+
this.agent.setSystemPrompt(result.systemPrompt);
|
|
12552
|
+
} else if (result?.prompt !== undefined) {
|
|
12553
|
+
this.agent.setSystemPrompt(await this.#buildSystemPromptForAgentStart(effectivePrompt));
|
|
12554
|
+
} else {
|
|
12555
|
+
this.agent.setSystemPrompt(beforeAgentStartSystemPrompt);
|
|
12556
|
+
}
|
|
12557
|
+
} else {
|
|
12558
|
+
this.agent.setSystemPrompt(beforeAgentStartSystemPrompt);
|
|
12559
|
+
}
|
|
12560
|
+
const fileMentions = extractFileMentions(effectivePrompt);
|
|
12482
12561
|
if (fileMentions.length > 0) {
|
|
12483
12562
|
const cwd = this.sessionManager.getCwd();
|
|
12484
12563
|
// Collect resolved paths already shown (read or mentioned) in the
|
|
@@ -12506,31 +12585,6 @@ export class AgentSession {
|
|
|
12506
12585
|
recentlyShownPaths,
|
|
12507
12586
|
});
|
|
12508
12587
|
}
|
|
12509
|
-
const beforeAgentStartSystemPrompt = await this.#buildSystemPromptForAgentStart(expandedText);
|
|
12510
|
-
hindsightRecall = this.getHindsightSessionState()?.getRecallSnippetForInjection();
|
|
12511
|
-
planReferenceMessage = await this.#buildPlanReferenceMessage();
|
|
12512
|
-
// Emit before_agent_start extension event. Race hook completion with
|
|
12513
|
-
// prompt cancellation so a wedged hook cannot retain SDK prompt authority.
|
|
12514
|
-
if (this.#extensionRunner?.hasHandlers("before_agent_start")) this.#markRetryReplayUnsafe();
|
|
12515
|
-
if (this.#extensionRunner) {
|
|
12516
|
-
const result = await this.#awaitPromptPreflight(
|
|
12517
|
-
generation,
|
|
12518
|
-
preflightSignal,
|
|
12519
|
-
this.#extensionRunner.emitBeforeAgentStart(
|
|
12520
|
-
expandedText,
|
|
12521
|
-
options?.images,
|
|
12522
|
-
beforeAgentStartSystemPrompt,
|
|
12523
|
-
),
|
|
12524
|
-
);
|
|
12525
|
-
if (result?.messages) beforeAgentStartResultMessages = [...result.messages];
|
|
12526
|
-
if (result?.systemPrompt !== undefined) {
|
|
12527
|
-
this.agent.setSystemPrompt(result.systemPrompt);
|
|
12528
|
-
} else {
|
|
12529
|
-
this.agent.setSystemPrompt(beforeAgentStartSystemPrompt);
|
|
12530
|
-
}
|
|
12531
|
-
} else {
|
|
12532
|
-
this.agent.setSystemPrompt(beforeAgentStartSystemPrompt);
|
|
12533
|
-
}
|
|
12534
12588
|
// Invoke first-party internal before-agent-start contributors. These
|
|
12535
12589
|
// run alongside the extension runner (not via user-loaded hooks) and
|
|
12536
12590
|
// append through the same custom-message attribution path. Errors are nonfatal.
|
|
@@ -12541,8 +12595,8 @@ export class AgentSession {
|
|
|
12541
12595
|
generation,
|
|
12542
12596
|
preflightSignal,
|
|
12543
12597
|
contributor({
|
|
12544
|
-
prompt:
|
|
12545
|
-
images:
|
|
12598
|
+
prompt: effectivePrompt,
|
|
12599
|
+
images: effectiveImages,
|
|
12546
12600
|
sessionId: this.sessionId,
|
|
12547
12601
|
}),
|
|
12548
12602
|
);
|
|
@@ -12600,7 +12654,7 @@ export class AgentSession {
|
|
|
12600
12654
|
}
|
|
12601
12655
|
|
|
12602
12656
|
const promptIndex = messages.length;
|
|
12603
|
-
messages.push(
|
|
12657
|
+
messages.push(promptMessage);
|
|
12604
12658
|
|
|
12605
12659
|
// Re-present captured pending next-turn messages (never drained here).
|
|
12606
12660
|
// Reclassify deferred envelopes before injecting them into the new
|
|
@@ -19435,10 +19489,10 @@ export class AgentSession {
|
|
|
19435
19489
|
isError: message.isError,
|
|
19436
19490
|
content: this.#normalizeProviderReplayValue(message.content),
|
|
19437
19491
|
};
|
|
19438
|
-
case "
|
|
19492
|
+
case "pythonExecution":
|
|
19439
19493
|
return {
|
|
19440
19494
|
role: message.role,
|
|
19441
|
-
|
|
19495
|
+
code: message.code,
|
|
19442
19496
|
output: message.output,
|
|
19443
19497
|
exitCode: message.exitCode,
|
|
19444
19498
|
cancelled: message.cancelled,
|
|
@@ -19456,12 +19510,13 @@ export class AgentSession {
|
|
|
19456
19510
|
: undefined,
|
|
19457
19511
|
excludeFromContext: message.excludeFromContext,
|
|
19458
19512
|
};
|
|
19459
|
-
case "
|
|
19513
|
+
case "bashExecution":
|
|
19460
19514
|
return {
|
|
19461
19515
|
role: message.role,
|
|
19462
|
-
|
|
19516
|
+
command: message.command,
|
|
19463
19517
|
output: message.output,
|
|
19464
19518
|
exitCode: message.exitCode,
|
|
19519
|
+
signal: message.signal,
|
|
19465
19520
|
cancelled: message.cancelled,
|
|
19466
19521
|
meta: message.meta
|
|
19467
19522
|
? {
|
|
@@ -20160,6 +20215,10 @@ export class AgentSession {
|
|
|
20160
20215
|
const telemetry = resolveTelemetry(this.agent.telemetry, this.sessionId);
|
|
20161
20216
|
let compactResult: CompactionResult | undefined;
|
|
20162
20217
|
let lastError: unknown;
|
|
20218
|
+
// Every candidate that was tried and failed, in order. Only the last failure
|
|
20219
|
+
// used to reach the user, which named whichever same-provider fallback the
|
|
20220
|
+
// chain ended on and hid that the session model itself had already failed.
|
|
20221
|
+
const candidateFailures: Array<{ model: string; message: string }> = [];
|
|
20163
20222
|
|
|
20164
20223
|
for (const candidate of candidates) {
|
|
20165
20224
|
const apiKey = await this.#modelRegistry.getApiKey(candidate, this.credentialSessionId);
|
|
@@ -20193,6 +20252,7 @@ export class AgentSession {
|
|
|
20193
20252
|
const message = error instanceof Error ? error.message : String(error);
|
|
20194
20253
|
if (this.#isCompactionAuthFailure(error)) {
|
|
20195
20254
|
lastError = this.#buildCompactionAuthError();
|
|
20255
|
+
candidateFailures.push({ model: `${candidate.provider}/${candidate.id}`, message });
|
|
20196
20256
|
break;
|
|
20197
20257
|
}
|
|
20198
20258
|
const retryAfterMs = this.#parseRetryAfterMsFromError(message);
|
|
@@ -20205,6 +20265,7 @@ export class AgentSession {
|
|
|
20205
20265
|
isUsageLimitError(message));
|
|
20206
20266
|
if (!shouldRetry) {
|
|
20207
20267
|
lastError = error;
|
|
20268
|
+
candidateFailures.push({ model: `${candidate.provider}/${candidate.id}`, message });
|
|
20208
20269
|
break;
|
|
20209
20270
|
}
|
|
20210
20271
|
|
|
@@ -20229,6 +20290,7 @@ export class AgentSession {
|
|
|
20229
20290
|
model: `${candidate.provider}/${candidate.id}`,
|
|
20230
20291
|
});
|
|
20231
20292
|
lastError = error;
|
|
20293
|
+
candidateFailures.push({ model: `${candidate.provider}/${candidate.id}`, message });
|
|
20232
20294
|
break; // Exit retry loop, continue to next candidate
|
|
20233
20295
|
}
|
|
20234
20296
|
// No more candidates - we have to wait
|
|
@@ -20254,7 +20316,9 @@ export class AgentSession {
|
|
|
20254
20316
|
|
|
20255
20317
|
if (!compactResult) {
|
|
20256
20318
|
if (lastError) {
|
|
20257
|
-
throw
|
|
20319
|
+
throw candidateFailures.length > 1
|
|
20320
|
+
? describeCompactionCandidateFailures(candidateFailures, lastError)
|
|
20321
|
+
: lastError;
|
|
20258
20322
|
}
|
|
20259
20323
|
throw new Error("Compaction failed: no available model");
|
|
20260
20324
|
}
|
|
@@ -22329,6 +22393,7 @@ export class AgentSession {
|
|
|
22329
22393
|
command,
|
|
22330
22394
|
output: result.output,
|
|
22331
22395
|
exitCode: result.exitCode,
|
|
22396
|
+
signal: result.signal,
|
|
22332
22397
|
cancelled: result.cancelled,
|
|
22333
22398
|
truncated: result.truncated,
|
|
22334
22399
|
meta,
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { cleanReason } from "@gajae-code/ai/auth-broker/redact";
|
|
1
2
|
import type { FallbackTriggerClass } from "@gajae-code/ai/utils/fallback-transport";
|
|
2
3
|
|
|
3
4
|
/** Immutable configured fallback intent. Transient attempt state never belongs here. */
|
|
@@ -265,6 +266,43 @@ export function compactionRetryDelay(
|
|
|
265
266
|
return Math.max(0, bounded);
|
|
266
267
|
}
|
|
267
268
|
|
|
269
|
+
export interface CompactionCandidateFailure {
|
|
270
|
+
readonly model: string;
|
|
271
|
+
readonly message: string;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function sanitizeCompactionCandidateFailureMessage(message: string): string {
|
|
275
|
+
const cleaned = cleanReason(message) ?? "Compaction candidate failed.";
|
|
276
|
+
return cleaned.replace(/\b[a-z][a-z0-9+.-]*:\/\/[^\s<>'"]+/gi, "[redacted URL]");
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Error for an auto-compaction run whose every candidate failed.
|
|
281
|
+
*
|
|
282
|
+
* The candidate chain starts at the session model and ends on a same-provider
|
|
283
|
+
* largest-context fallback, so surfacing only the final error named a model
|
|
284
|
+
* the user never chose and hid that their own model had already failed the
|
|
285
|
+
* same way. The message leads with the first failure (the one the user can act
|
|
286
|
+
* on), then lists every candidate that was tried with its own error. `cause`
|
|
287
|
+
* is the final error so callers that classify by type keep the same object.
|
|
288
|
+
*/
|
|
289
|
+
export function describeCompactionCandidateFailures(
|
|
290
|
+
failures: readonly CompactionCandidateFailure[],
|
|
291
|
+
lastError: unknown,
|
|
292
|
+
): Error {
|
|
293
|
+
if (failures.length === 0)
|
|
294
|
+
return new Error("Compaction failed: no candidate failure details available.", { cause: lastError });
|
|
295
|
+
const first = failures[0]!;
|
|
296
|
+
const firstMessage = sanitizeCompactionCandidateFailureMessage(first.message);
|
|
297
|
+
const lines = failures.map(
|
|
298
|
+
failure => ` ${failure.model}: ${sanitizeCompactionCandidateFailureMessage(failure.message)}`,
|
|
299
|
+
);
|
|
300
|
+
return new Error(
|
|
301
|
+
`${firstMessage} (${first.model}); ${failures.length} compaction candidates failed:\n${lines.join("\n")}`,
|
|
302
|
+
{ cause: lastError },
|
|
303
|
+
);
|
|
304
|
+
}
|
|
305
|
+
|
|
268
306
|
/** Retry-After is intentionally uncapped. */
|
|
269
307
|
export function effectiveFallbackDelay(
|
|
270
308
|
baseDelayMs: number,
|
|
@@ -52,6 +52,20 @@ const LOCK_LEASE_MS = 60_000;
|
|
|
52
52
|
const LOCK_HEARTBEAT_MS = 10_000;
|
|
53
53
|
const LOCK_WAIT_MS = 5_000;
|
|
54
54
|
|
|
55
|
+
/** A pre-mutation append fence rejection: no bytes from this append were written. */
|
|
56
|
+
export class ManagedAppendIdentityMismatchError extends Error {
|
|
57
|
+
readonly code = "managed_append_identity_mismatch";
|
|
58
|
+
readonly relativePath: string;
|
|
59
|
+
readonly operatorMessage =
|
|
60
|
+
"Session was resumed by another process; this resume did not run. Retry, or resume a different session.";
|
|
61
|
+
|
|
62
|
+
constructor(relativePath: string) {
|
|
63
|
+
super("managed_append_identity_mismatch");
|
|
64
|
+
this.name = "ManagedAppendIdentityMismatchError";
|
|
65
|
+
this.relativePath = relativePath;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
55
69
|
export class ManagedPublishError extends Error {
|
|
56
70
|
readonly classification:
|
|
57
71
|
| "destination_conflict"
|
|
@@ -1806,7 +1820,7 @@ export class ManagedSessionDescendantStore {
|
|
|
1806
1820
|
current.size.toString() === expected.size &&
|
|
1807
1821
|
current.sha256 === expected.sha256;
|
|
1808
1822
|
if (!identityMatches) {
|
|
1809
|
-
throw new
|
|
1823
|
+
throw new ManagedAppendIdentityMismatchError(relativePath);
|
|
1810
1824
|
}
|
|
1811
1825
|
// mtime/ctime drift on the same file object is harmless. The append
|
|
1812
1826
|
// helper performs its own descriptor-bound mutation check.
|
|
@@ -1820,7 +1834,7 @@ export class ManagedSessionDescendantStore {
|
|
|
1820
1834
|
return managedAppendReceiptFromIdentity(appended);
|
|
1821
1835
|
}
|
|
1822
1836
|
const current = this.captureBoundedAppendExpectation(relativePath);
|
|
1823
|
-
if (!current) throw new
|
|
1837
|
+
if (!current) throw new ManagedAppendIdentityMismatchError(relativePath);
|
|
1824
1838
|
if (
|
|
1825
1839
|
current.dev !== expected.dev ||
|
|
1826
1840
|
current.ino !== expected.ino ||
|
|
@@ -1828,7 +1842,7 @@ export class ManagedSessionDescendantStore {
|
|
|
1828
1842
|
current.size !== expected.size ||
|
|
1829
1843
|
current.sha256 !== expected.sha256
|
|
1830
1844
|
)
|
|
1831
|
-
throw new
|
|
1845
|
+
throw new ManagedAppendIdentityMismatchError(relativePath);
|
|
1832
1846
|
const descriptorMatches =
|
|
1833
1847
|
current.dev === expected.dev &&
|
|
1834
1848
|
current.ino === expected.ino &&
|
|
@@ -1862,7 +1876,7 @@ export class ManagedSessionDescendantStore {
|
|
|
1862
1876
|
expected: ManagedFileIdentity,
|
|
1863
1877
|
): ManagedAppendReceipt {
|
|
1864
1878
|
const bounded = this.captureBoundedAppendExpectation(relativePath);
|
|
1865
|
-
if (!bounded) throw new
|
|
1879
|
+
if (!bounded) throw new ManagedAppendIdentityMismatchError(relativePath);
|
|
1866
1880
|
const descriptorMatches =
|
|
1867
1881
|
bounded.dev === expected.dev.toString() &&
|
|
1868
1882
|
bounded.ino === expected.ino.toString() &&
|
|
@@ -1882,7 +1896,7 @@ export class ManagedSessionDescendantStore {
|
|
|
1882
1896
|
bounded.size !== String(expected.size) ||
|
|
1883
1897
|
bounded.sha256 !== expected.sha256
|
|
1884
1898
|
)
|
|
1885
|
-
throw new
|
|
1899
|
+
throw new ManagedAppendIdentityMismatchError(relativePath);
|
|
1886
1900
|
}
|
|
1887
1901
|
return this.appendExpectedSync(relativePath, bytes, bounded);
|
|
1888
1902
|
}
|
package/src/session/messages.ts
CHANGED
|
@@ -212,6 +212,7 @@ export interface BashExecutionMessage {
|
|
|
212
212
|
command: string;
|
|
213
213
|
output: string;
|
|
214
214
|
exitCode: number | undefined;
|
|
215
|
+
signal?: string;
|
|
215
216
|
cancelled: boolean;
|
|
216
217
|
truncated: boolean;
|
|
217
218
|
meta?: OutputMeta;
|
|
@@ -313,6 +314,10 @@ export function bashExecutionToText(msg: BashExecutionMessage): string {
|
|
|
313
314
|
}
|
|
314
315
|
if (msg.cancelled) {
|
|
315
316
|
text += "\n\n(command cancelled)";
|
|
317
|
+
} else if (msg.signal) {
|
|
318
|
+
text += `\n\nCommand terminated by ${msg.signal}${
|
|
319
|
+
msg.exitCode !== null && msg.exitCode !== undefined ? ` (exit code ${msg.exitCode})` : ""
|
|
320
|
+
}`;
|
|
316
321
|
} else if (msg.exitCode !== null && msg.exitCode !== undefined && msg.exitCode !== 0) {
|
|
317
322
|
text += `\n\nCommand exited with code ${msg.exitCode}`;
|
|
318
323
|
}
|
|
@@ -112,6 +112,7 @@ import {
|
|
|
112
112
|
captureManagedFileNoFollow,
|
|
113
113
|
fsyncManagedArtifactTree,
|
|
114
114
|
MANAGED_ARTIFACT_MAX_FILE_BYTES,
|
|
115
|
+
ManagedAppendIdentityMismatchError,
|
|
115
116
|
type ManagedAppendReceipt,
|
|
116
117
|
type ManagedBoundedAppendExpectation,
|
|
117
118
|
ManagedCommittedMutationError,
|
|
@@ -11873,6 +11874,10 @@ export class SessionManager {
|
|
|
11873
11874
|
return false;
|
|
11874
11875
|
}
|
|
11875
11876
|
|
|
11877
|
+
/** Ownership loss remains observable even when an agent event listener aborts the turn. */
|
|
11878
|
+
getAppendIdentityMismatch(): ManagedAppendIdentityMismatchError | undefined {
|
|
11879
|
+
return this.#persistError instanceof ManagedAppendIdentityMismatchError ? this.#persistError : undefined;
|
|
11880
|
+
}
|
|
11876
11881
|
#recordPersistError(err: unknown): Error {
|
|
11877
11882
|
const normalized = toError(err);
|
|
11878
11883
|
if (!this.#persistError) this.#persistError = normalized;
|
|
@@ -16255,7 +16260,11 @@ export class SessionManager {
|
|
|
16255
16260
|
}
|
|
16256
16261
|
}
|
|
16257
16262
|
const outcome = await this.closeStrict();
|
|
16258
|
-
return priorFlushError &&
|
|
16263
|
+
return priorFlushError &&
|
|
16264
|
+
!(priorFlushError instanceof ManagedAppendIdentityMismatchError) &&
|
|
16265
|
+
outcome.kind === "closed"
|
|
16266
|
+
? { kind: "close_unknown", error: priorFlushError }
|
|
16267
|
+
: outcome;
|
|
16259
16268
|
}
|
|
16260
16269
|
|
|
16261
16270
|
/**
|
|
@@ -16280,6 +16289,21 @@ export class SessionManager {
|
|
|
16280
16289
|
});
|
|
16281
16290
|
}
|
|
16282
16291
|
if (preparedCleanupError) return { kind: "close_failed_retryable", error: preparedCleanupError };
|
|
16292
|
+
await this.#persistChain;
|
|
16293
|
+
// Managed appends are synchronous and reject identity drift before writing.
|
|
16294
|
+
// Disown the stale transcript, not an uncertain OS writer: never rewrite a
|
|
16295
|
+
// successor owned by another process while disposing this rejected resume.
|
|
16296
|
+
if (
|
|
16297
|
+
this.destination.kind === "managed" &&
|
|
16298
|
+
!this.#persistWriter &&
|
|
16299
|
+
this.#persistError instanceof ManagedAppendIdentityMismatchError
|
|
16300
|
+
) {
|
|
16301
|
+
this.#needsFullRewriteOnNextPersist = false;
|
|
16302
|
+
this.#strictResumeMutationPending = false;
|
|
16303
|
+
this.#managedPersistExpectedIdentity = undefined;
|
|
16304
|
+
this.#persistError = undefined;
|
|
16305
|
+
this.#persistErrorReported = false;
|
|
16306
|
+
}
|
|
16283
16307
|
let priorPersistError = this.#persistError;
|
|
16284
16308
|
if (this.#needsFullRewriteOnNextPersist && (!this.#readOnlyResume || this.#strictResumeMutationPending)) {
|
|
16285
16309
|
await this.#persistChain.catch(() => {});
|
|
@@ -17379,10 +17403,7 @@ export class SessionManager {
|
|
|
17379
17403
|
receipt = store.appendExpectedIdentitySync(relativePath, bytes, this.#managedPersistExpectedIdentity);
|
|
17380
17404
|
} catch (err) {
|
|
17381
17405
|
const predecessorMissing = store.descriptorExpected(relativePath) === null;
|
|
17382
|
-
if (
|
|
17383
|
-
!isEnoent(err) &&
|
|
17384
|
-
(!(err instanceof Error) || err.message !== "managed_append_identity_mismatch" || !predecessorMissing)
|
|
17385
|
-
)
|
|
17406
|
+
if (!isEnoent(err) && (!(err instanceof ManagedAppendIdentityMismatchError) || !predecessorMissing))
|
|
17386
17407
|
throw err;
|
|
17387
17408
|
// Appending only the new records would create a truncated transcript.
|
|
17388
17409
|
// Recreate the missing file from the complete resident entry set instead.
|
|
@@ -17724,6 +17745,9 @@ export class SessionManager {
|
|
|
17724
17745
|
if (entry.type === "label") this.#labelRevision++;
|
|
17725
17746
|
if (sidecarAppendCharge > 0) activeRuntime?.accountant.release(sidecarAppendCharge);
|
|
17726
17747
|
if (sidecarTailCharge > 0) activeRuntime?.tailCache.release(sidecarTailCharge);
|
|
17748
|
+
// The pre-write fence is an expected ownership loss, not an uncertain
|
|
17749
|
+
// append. Preserve its type after rolling back the rejected entry.
|
|
17750
|
+
if (error instanceof ManagedAppendIdentityMismatchError) throw error;
|
|
17727
17751
|
throw new SessionAppendPersistenceError(
|
|
17728
17752
|
priorPersistenceError ? "prior_failure" : "current_append",
|
|
17729
17753
|
residentEntry.id,
|
|
@@ -59,7 +59,11 @@ const REDACTION_RULES: readonly RedactionRule[] = [
|
|
|
59
59
|
// Long hex secrets (64+ hex chars: webhook secrets, signing keys).
|
|
60
60
|
{ id: "hex-secret", pattern: new RegExp(`\\b${HEX}{64,}\\b`, "g") },
|
|
61
61
|
// Basic-auth credentials embedded in URLs (https://user:pass@host).
|
|
62
|
-
|
|
62
|
+
// The scheme repetition is bounded. Unbounded `[a-z0-9+.-]*` in front of the
|
|
63
|
+
// literal `://` re-tries every prefix of a long alphabetic run before failing,
|
|
64
|
+
// which is quadratic in the input length: 200 KB of ordinary prose costs ~10s.
|
|
65
|
+
// IANA's longest registered scheme is well under 16 characters.
|
|
66
|
+
{ id: "url-credential", pattern: /([a-z][a-z0-9+.-]{0,15}:\/\/)[^/\s:@]{1,256}:[^/\s@]{1,256}@/gi },
|
|
63
67
|
// Sensitive env/KEY assignments: OPENAI_API_KEY=..., token: ..., password = ...
|
|
64
68
|
// The name prefix is optional so a bare sensitive name (`password: …`) also matches.
|
|
65
69
|
{
|