@gajae-code/coding-agent 0.18.5 → 0.18.7
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 +140 -0
- package/dist/types/capability/types.d.ts +4 -0
- package/dist/types/cli-commands.d.ts +10 -0
- package/dist/types/cli-ordinary.d.ts +13 -0
- package/dist/types/cli.d.ts +12 -3
- package/dist/types/commands/contribution-prep.d.ts +0 -3
- package/dist/types/commands/sdk.d.ts +3 -0
- package/dist/types/commands/update.d.ts +1 -0
- package/dist/types/config/model-profile-activation.d.ts +19 -0
- package/dist/types/config/settings.d.ts +4 -0
- package/dist/types/coordinator-mcp/session-reaper.d.ts +5 -0
- package/dist/types/discovery/helpers.d.ts +2 -2
- package/dist/types/edit/modes/replace.d.ts +3 -3
- package/dist/types/edit/streaming.d.ts +2 -2
- package/dist/types/exec/bash-shell-supervisor.d.ts +12 -0
- package/dist/types/runtime-mcp/elicitation.d.ts +12 -0
- package/dist/types/sdk/broker/discovery.d.ts +8 -0
- package/dist/types/sdk/broker/ensure.d.ts +1 -0
- package/dist/types/sdk/broker/lifecycle-ledger.d.ts +4 -0
- package/dist/types/sdk/broker/lifecycle.d.ts +9 -0
- package/dist/types/sdk/broker/runtime.d.ts +1 -1
- package/dist/types/sdk/broker/task-artifact-owner-validation.d.ts +55 -0
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +1 -1
- package/dist/types/sdk/bus/telegram-daemon.d.ts +4 -2
- package/dist/types/sdk/cli/diagnostics-cli.d.ts +18 -0
- package/dist/types/sdk/diagnostics/observe-broker.d.ts +185 -0
- package/dist/types/sdk/host/control/operations.d.ts +1 -1
- package/dist/types/sdk/host/host.d.ts +6 -0
- package/dist/types/sdk/index.d.ts +1 -0
- package/dist/types/sdk/router/session-router.d.ts +9 -0
- package/dist/types/sdk/session.d.ts +13 -0
- package/dist/types/session/agent-session.d.ts +5 -1
- package/dist/types/session/internal/managed-session-storage.d.ts +11 -1
- package/dist/types/session/internal/task-artifact-owner-access.d.ts +17 -0
- package/dist/types/session/internal/task-artifact-owner-transcript.d.ts +3 -0
- package/dist/types/session/session-retirement.d.ts +47 -0
- package/dist/types/session/session-storage.d.ts +0 -46
- package/dist/types/session/session-transcript-header.d.ts +2 -0
- package/dist/types/session/task-artifact-owner-codec.d.ts +107 -0
- package/dist/types/session/task-artifact-owner-retirement.d.ts +7 -0
- package/dist/types/session/task-artifact-owner.d.ts +10 -0
- package/dist/types/tools/ask-contract.d.ts +75 -254
- package/dist/types/tools/bash.d.ts +1 -1
- package/package.json +7 -7
- package/scripts/build-sdk-package-smoke.ts +5 -0
- package/scripts/resident-memory-bench.ts +174 -20
- package/src/capability/index.ts +1 -0
- package/src/capability/types.ts +4 -0
- package/src/cli/config-cli.ts +8 -18
- package/src/cli/public-command-entry.ts +5 -0
- package/src/cli/ssh-cli.ts +6 -3
- package/src/cli/web-search-cli.ts +9 -3
- package/src/cli-commands.ts +14 -0
- package/src/cli-main.ts +7 -8
- package/src/cli-ordinary.ts +197 -0
- package/src/cli.ts +25 -182
- package/src/commands/contribution-prep.ts +1 -3
- package/src/commands/sdk.ts +171 -21
- package/src/commands/update.ts +15 -1
- package/src/config/file-lock.ts +91 -2
- package/src/config/model-profile-activation.ts +70 -4
- package/src/config/model-profile-contract.ts +4 -1
- package/src/config/model-profiles.ts +23 -31
- package/src/config/model-resolver.ts +9 -1
- package/src/config/settings.ts +44 -4
- package/src/coordinator-mcp/event-webhook.ts +19 -14
- package/src/coordinator-mcp/policy.ts +14 -4
- package/src/coordinator-mcp/server.ts +142 -7
- package/src/coordinator-mcp/session-reaper.ts +9 -1
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +1 -1
- package/src/discovery/helpers.ts +9 -3
- package/src/discovery/ssh.ts +8 -3
- package/src/edit/modes/replace.ts +6 -6
- package/src/edit/streaming.ts +15 -15
- package/src/exec/bash-shell-supervisor.ts +60 -3
- package/src/gjc-runtime/gc-runtime.ts +2 -1
- package/src/hindsight/config.ts +9 -3
- package/src/internal-urls/docs-index.generated.ts +11 -10
- package/src/memories/index.ts +8 -2
- package/src/modes/acp/acp-agent.ts +724 -370
- package/src/modes/components/tool-execution.ts +1 -1
- package/src/prompts/tools/ask.md +2 -0
- package/src/runtime-mcp/client.ts +13 -4
- package/src/runtime-mcp/elicitation.ts +142 -0
- package/src/sdk/broker/broker.ts +54 -1
- package/src/sdk/broker/daemon-entry.ts +67 -6
- package/src/sdk/broker/discovery.ts +8 -0
- package/src/sdk/broker/ensure.ts +156 -20
- package/src/sdk/broker/lifecycle-ledger.ts +110 -12
- package/src/sdk/broker/lifecycle.ts +334 -348
- package/src/sdk/broker/runtime.ts +5 -1
- package/src/sdk/broker/task-artifact-owner-validation.ts +259 -0
- package/src/sdk/broker/transport.ts +5 -1
- package/src/sdk/bus/chat-daemon-control.ts +2 -2
- package/src/sdk/bus/chat-daemon-runtime.ts +3 -0
- package/src/sdk/bus/index.ts +3 -3
- package/src/sdk/bus/telegram-daemon-contract.ts +1 -1
- package/src/sdk/bus/telegram-daemon-control.ts +15 -12
- package/src/sdk/bus/telegram-daemon.ts +38 -3
- package/src/sdk/cli/diagnostics-cli.ts +247 -0
- package/src/sdk/diagnostics/observe-broker.ts +654 -0
- package/src/sdk/host/control/dispatch.ts +52 -4
- package/src/sdk/host/control/operations.ts +1 -1
- package/src/sdk/host/host.ts +6 -0
- package/src/sdk/host/session-runtime.test.ts +83 -4
- package/src/sdk/host/session-runtime.ts +15 -7
- package/src/sdk/index.ts +1 -0
- package/src/sdk/lifecycle-session.ts +1 -0
- package/src/sdk/mcp/server.ts +15 -3
- package/src/sdk/protocol/operation-registry.ts +1 -1
- package/src/sdk/router/session-router.ts +26 -3
- package/src/sdk/session.ts +51 -8
- package/src/session/agent-session.ts +169 -43
- package/src/session/blob-store.ts +78 -33
- package/src/session/internal/managed-session-storage.ts +203 -4
- package/src/session/internal/task-artifact-owner-access.ts +229 -0
- package/src/session/internal/task-artifact-owner-transcript.ts +68 -0
- package/src/session/session-retirement.ts +155 -0
- package/src/session/session-storage.ts +1 -160
- package/src/session/session-transcript-header.ts +17 -0
- package/src/session/task-artifact-owner-codec.ts +578 -0
- package/src/session/task-artifact-owner-retirement.ts +475 -0
- package/src/session/task-artifact-owner.ts +289 -0
- package/src/setup/hermes/templates/operator-instructions.v1.md +19 -1
- package/src/setup/hermes-setup.ts +4 -1
- package/src/slash-commands/builtin-registry.ts +8 -5
- package/src/tools/ask-contract.test.ts +76 -1
- package/src/tools/ask-contract.ts +57 -6
- package/src/tools/ask.ts +16 -7
- package/src/tools/bash.ts +38 -29
- package/src/tools/descriptors.test.ts +29 -0
- package/src/tools/tool-catalog.generated.ts +7 -4
- package/src/web/search/index.ts +2 -2
|
@@ -127,12 +127,14 @@ import {
|
|
|
127
127
|
streamSimple,
|
|
128
128
|
} from "@gajae-code/ai/core";
|
|
129
129
|
import { normalizeAnthropicBaseUrl } from "@gajae-code/ai/providers/anthropic";
|
|
130
|
+
import { isExplicitCodexTerminalVeto } from "@gajae-code/ai/providers/openai-codex-responses";
|
|
130
131
|
import {
|
|
131
132
|
type AuthDisposition,
|
|
132
133
|
beginAttempt,
|
|
133
134
|
classifyFallbackTrigger,
|
|
134
135
|
EMPTY_RESPONSE_PROVIDER_CODE,
|
|
135
136
|
type FallbackAttemptToken,
|
|
137
|
+
PROVIDER_PROTOCOL_MISMATCH_ERROR_CODE,
|
|
136
138
|
type FallbackTriggerClass,
|
|
137
139
|
SERVER_OVERLOADED_PROVIDER_CODE,
|
|
138
140
|
STREAM_FIRST_EVENT_TIMEOUT_PROVIDER_CODE,
|
|
@@ -1210,7 +1212,10 @@ export interface TrackedSendUserMessageOptions extends SendUserMessageOptions {
|
|
|
1210
1212
|
trackSubmission: true;
|
|
1211
1213
|
}
|
|
1212
1214
|
|
|
1213
|
-
type SendUserMessageDispatchOptions = SendUserMessageOptions & {
|
|
1215
|
+
type SendUserMessageDispatchOptions = SendUserMessageOptions & {
|
|
1216
|
+
trackSubmission?: boolean;
|
|
1217
|
+
expectedSdkRunToken?: string;
|
|
1218
|
+
};
|
|
1214
1219
|
|
|
1215
1220
|
type InternalPromptOptions = PromptOptions & { sdkRunToken?: string };
|
|
1216
1221
|
type InternalCustomMessageOptions = Pick<
|
|
@@ -1419,8 +1424,13 @@ const BARE_DEFAULT_WATCHDOG_ERROR =
|
|
|
1419
1424
|
const PROVIDER_FIRST_EVENT_TIMEOUT_ERROR = "Provider stream timed out while waiting for the first event";
|
|
1420
1425
|
const WRAPPED_PROVIDER_FIRST_EVENT_TIMEOUT_ERROR = `Error: ${PROVIDER_FIRST_EVENT_TIMEOUT_ERROR}`;
|
|
1421
1426
|
const PROVIDER_FIRST_EVENT_TIMEOUT_WITHOUT_ARTICLE_ERROR = "Provider stream timed out while waiting for first event";
|
|
1422
|
-
const
|
|
1423
|
-
|
|
1427
|
+
const BARE_DEFAULT_CODEX_RETRYABLE_ERROR =
|
|
1428
|
+
/^Codex error event(?:: .*)? \(code=(?:server_is_overloaded|server_error|internal_error)(?:, [^)]+)*\)$/;
|
|
1429
|
+
const BARE_DEFAULT_CODEX_RETRYABLE_CODES = new Set(["server_is_overloaded", "server_error", "internal_error"]);
|
|
1430
|
+
const BARE_DEFAULT_CODEX_OVERLOAD_ERROR =
|
|
1431
|
+
/^Codex error event(?:: .*)? \(code=server_is_overloaded(?:, [^)]+)*\)$/;
|
|
1432
|
+
const BARE_DEFAULT_OVERLOAD_CODES = new Set([SERVER_OVERLOADED_PROVIDER_CODE]);
|
|
1433
|
+
/** Anthropic's typed capacity-overload `error.type`, the only Anthropic overload code admitted below. */
|
|
1424
1434
|
const ANTHROPIC_OVERLOADED_ERROR_TYPE = "overloaded_error";
|
|
1425
1435
|
const KIMI_CODE_FIRST_EVENT_TIMEOUT_MESSAGES = {
|
|
1426
1436
|
"anthropic-messages": new Set([
|
|
@@ -1501,8 +1511,8 @@ function isBareDefaultMessageOnlyFirstEventTimeout(message: AssistantMessage): b
|
|
|
1501
1511
|
);
|
|
1502
1512
|
}
|
|
1503
1513
|
/**
|
|
1504
|
-
* True when the attempt's structured evidence is exactly
|
|
1505
|
-
*
|
|
1514
|
+
* True when the attempt's structured evidence is exactly an admitted provider
|
|
1515
|
+
* code and nothing else: no status, no retained retry header,
|
|
1506
1516
|
* no second typed code, no error kind, and none of the transport's other
|
|
1507
1517
|
* observations (request size, first-event timing, endpoint class, provider retry
|
|
1508
1518
|
* ceiling). The code arrives statuslessly (a Codex error event, an HTTP 200
|
|
@@ -1511,15 +1521,18 @@ function isBareDefaultMessageOnlyFirstEventTimeout(message: AssistantMessage): b
|
|
|
1511
1521
|
* attempt is no longer provably that bare capacity rejection. The code is
|
|
1512
1522
|
* compared case-sensitively, matching the parser and the transport gate.
|
|
1513
1523
|
*/
|
|
1514
|
-
function isExactTypedOverloadFacts(
|
|
1524
|
+
function isExactTypedOverloadFacts(
|
|
1525
|
+
message: AssistantMessage,
|
|
1526
|
+
acceptedProviderCodes: ReadonlySet<string> = BARE_DEFAULT_OVERLOAD_CODES,
|
|
1527
|
+
): boolean {
|
|
1515
1528
|
if (message.errorKind !== undefined || message.errorStatus !== undefined) return false;
|
|
1516
1529
|
const facts = message.transportFailure;
|
|
1517
1530
|
if (!facts) return false;
|
|
1518
1531
|
return (
|
|
1519
|
-
facts.providerCode
|
|
1532
|
+
acceptedProviderCodes.has(facts.providerCode ?? "") &&
|
|
1520
1533
|
facts.status === undefined &&
|
|
1521
1534
|
facts.anthropicErrorType === undefined &&
|
|
1522
|
-
(facts.openaiErrorCode === undefined || facts.openaiErrorCode ===
|
|
1535
|
+
(facts.openaiErrorCode === undefined || facts.openaiErrorCode === facts.providerCode) &&
|
|
1523
1536
|
(facts.headers === undefined || Object.keys(facts.headers).length === 0) &&
|
|
1524
1537
|
facts.requestBytes === undefined &&
|
|
1525
1538
|
facts.firstEventElapsedMs === undefined &&
|
|
@@ -1545,11 +1558,41 @@ function isStatuslessTypedOverloadFacts(facts: TransportFailureFacts | undefined
|
|
|
1545
1558
|
);
|
|
1546
1559
|
}
|
|
1547
1560
|
|
|
1561
|
+
// Deterministic auth/request/model diagnostics must surface even when a provider
|
|
1562
|
+
// labels the failure with a transient code.
|
|
1563
|
+
const TERMINAL_ERROR_MESSAGE =
|
|
1564
|
+
/unauthorized|forbidden|authentication_error|permission_error|permission denied|invalid api key|invalid_request_error|invalid request|bad request|bad_request|validation_error|unprocessable|payload too large|payment required|insufficient_quota|insufficient credits|missing required (parameter|field)|invalid schema|invalid tool_choice|unsupported (parameter|value|model)|model_not_found|no such model|unknown model|does not (exist|support)|request was aborted|request aborted|the user aborted/i;
|
|
1565
|
+
|
|
1566
|
+
function isTerminalCodexProviderFailure(message: AssistantMessage): boolean {
|
|
1567
|
+
if (message.api !== "openai-codex-responses") return false;
|
|
1568
|
+
if (isExplicitCodexTerminalVeto(message.transportFailure?.providerCode, message.errorMessage)) return true;
|
|
1569
|
+
return (
|
|
1570
|
+
(BARE_DEFAULT_CODEX_RETRYABLE_CODES.has(message.transportFailure?.providerCode ?? "") ||
|
|
1571
|
+
BARE_DEFAULT_CODEX_RETRYABLE_ERROR.test(message.errorMessage ?? "")) &&
|
|
1572
|
+
TERMINAL_ERROR_MESSAGE.test(message.errorMessage ?? "")
|
|
1573
|
+
);
|
|
1574
|
+
}
|
|
1575
|
+
|
|
1548
1576
|
function isBareDefaultCodexOverload(message: AssistantMessage): boolean {
|
|
1549
1577
|
return (
|
|
1550
1578
|
message.api === "openai-codex-responses" &&
|
|
1551
|
-
|
|
1552
|
-
(
|
|
1579
|
+
!TERMINAL_ERROR_MESSAGE.test(message.errorMessage ?? "") &&
|
|
1580
|
+
!isExplicitCodexTerminalVeto(undefined, message.errorMessage) &&
|
|
1581
|
+
(isExactTypedOverloadFacts(message, BARE_DEFAULT_OVERLOAD_CODES) ||
|
|
1582
|
+
(BARE_DEFAULT_CODEX_OVERLOAD_ERROR.test(message.errorMessage ?? "") &&
|
|
1583
|
+
!hasBareDefaultRetryDisqualifyingFacts(message))) &&
|
|
1584
|
+
!assistantMessageHasVisibleOrToolContent(message)
|
|
1585
|
+
);
|
|
1586
|
+
}
|
|
1587
|
+
|
|
1588
|
+
function isBareDefaultCodexProviderFailure(message: AssistantMessage): boolean {
|
|
1589
|
+
return (
|
|
1590
|
+
message.api === "openai-codex-responses" &&
|
|
1591
|
+
!TERMINAL_ERROR_MESSAGE.test(message.errorMessage ?? "") &&
|
|
1592
|
+
!isExplicitCodexTerminalVeto(message.transportFailure?.providerCode, message.errorMessage) &&
|
|
1593
|
+
(isExactTypedOverloadFacts(message, BARE_DEFAULT_CODEX_RETRYABLE_CODES) ||
|
|
1594
|
+
(BARE_DEFAULT_CODEX_RETRYABLE_ERROR.test(message.errorMessage ?? "") &&
|
|
1595
|
+
!hasBareDefaultRetryDisqualifyingFacts(message))) &&
|
|
1553
1596
|
!assistantMessageHasVisibleOrToolContent(message)
|
|
1554
1597
|
);
|
|
1555
1598
|
}
|
|
@@ -1609,7 +1652,10 @@ function isBareDefaultAnthropicOverload(message: AssistantMessage): boolean {
|
|
|
1609
1652
|
function assistantMessageHasVisibleOrToolContent(message: AssistantMessage): boolean {
|
|
1610
1653
|
return message.content.some(content => {
|
|
1611
1654
|
if (content.type === "text") return content.text.length > 0;
|
|
1612
|
-
|
|
1655
|
+
if (content.type === "thinking") {
|
|
1656
|
+
return content.thinking.length > 0 || (content.thinkingSignature?.length ?? 0) > 0;
|
|
1657
|
+
}
|
|
1658
|
+
return content.type === "redactedThinking" || content.type === "toolCall";
|
|
1613
1659
|
});
|
|
1614
1660
|
}
|
|
1615
1661
|
|
|
@@ -5077,6 +5123,7 @@ export class AgentSession {
|
|
|
5077
5123
|
if (lease) this.#deferredAgentEndLeases.delete(pending);
|
|
5078
5124
|
this.#startAgentEndPublication(pending, lease);
|
|
5079
5125
|
}
|
|
5126
|
+
|
|
5080
5127
|
#startAgentEndPublication(pending: AgentSessionEvent, lease?: RunResourceProducerLease): void {
|
|
5081
5128
|
this.#agentEndPublicationInFlight++;
|
|
5082
5129
|
const pendingScope = (pending as AgentSessionEvent & { scope?: AttemptScopeRef }).scope as
|
|
@@ -6166,7 +6213,10 @@ export class AgentSession {
|
|
|
6166
6213
|
path.basename(sessionModeStatePath(this.sessionManager.getCwd(), currentSessionId, active.skill)),
|
|
6167
6214
|
);
|
|
6168
6215
|
const raw = fs.readFileSync(filePath, "utf-8");
|
|
6169
|
-
const parsed = JSON.parse(raw) as { state?: { intent_contract?: unknown } };
|
|
6216
|
+
const parsed = JSON.parse(raw) as { current_phase?: unknown; state?: { intent_contract?: unknown } };
|
|
6217
|
+
// A persisted final spec moves to handoff. Its next-workflow/approval
|
|
6218
|
+
// asks are not interview rounds and must not require recorder metadata.
|
|
6219
|
+
if (parsed.current_phase !== "interviewing") return undefined;
|
|
6170
6220
|
const intentContract = parsed.state?.intent_contract;
|
|
6171
6221
|
if (intentContract === undefined) return "topology";
|
|
6172
6222
|
assertDeepInterviewIntentManifest(intentContract);
|
|
@@ -6318,7 +6368,10 @@ export class AgentSession {
|
|
|
6318
6368
|
},
|
|
6319
6369
|
};
|
|
6320
6370
|
}
|
|
6321
|
-
const transformedMessages = await this.#transformContext(
|
|
6371
|
+
const transformedMessages = await this.#transformContext(
|
|
6372
|
+
this.#withoutEphemeralCustomMessages(this.messages),
|
|
6373
|
+
options.signal,
|
|
6374
|
+
);
|
|
6322
6375
|
const convertedMessages = await this.#convertToLlm(transformedMessages);
|
|
6323
6376
|
const providerMessages = this.model
|
|
6324
6377
|
? normalizeMessagesForProvider(convertedMessages, this.model)
|
|
@@ -12297,9 +12350,16 @@ export class AgentSession {
|
|
|
12297
12350
|
return this.agent.activeResourceRunId;
|
|
12298
12351
|
}
|
|
12299
12352
|
|
|
12300
|
-
/**
|
|
12353
|
+
/**
|
|
12354
|
+
* All messages including custom types like BashExecutionMessage. Request-scoped
|
|
12355
|
+
* ephemeral copies are retained only in `agent.state.messages` to keep the provider
|
|
12356
|
+
* cache prefix stable; they are never part of this public view.
|
|
12357
|
+
*/
|
|
12301
12358
|
get messages(): AgentMessage[] {
|
|
12302
|
-
|
|
12359
|
+
const messages = this.agent.state.messages;
|
|
12360
|
+
return messages.some(message => message.role === "custom" && this.#isEphemeralCustomMessageType(message.customType))
|
|
12361
|
+
? this.#withoutEphemeralCustomMessages(messages)
|
|
12362
|
+
: messages;
|
|
12303
12363
|
}
|
|
12304
12364
|
get transcriptPromptGeneration(): number {
|
|
12305
12365
|
return this.#promptGeneration;
|
|
@@ -12389,9 +12449,16 @@ export class AgentSession {
|
|
|
12389
12449
|
let continuationSdkRunToken: string | undefined;
|
|
12390
12450
|
try {
|
|
12391
12451
|
const volatileProjectContextMessage = await this.#buildVolatileProjectContextMessage();
|
|
12392
|
-
this
|
|
12452
|
+
if (!this.#isLatestRetainedEphemeralCopy(volatileProjectContextMessage)) {
|
|
12453
|
+
this.agent.appendMessage(volatileProjectContextMessage);
|
|
12454
|
+
}
|
|
12393
12455
|
const untrustedMcpServerInstructionsMessage = this.#buildUntrustedMcpServerInstructionsMessage();
|
|
12394
|
-
if (
|
|
12456
|
+
if (
|
|
12457
|
+
untrustedMcpServerInstructionsMessage &&
|
|
12458
|
+
!this.#isLatestRetainedEphemeralCopy(untrustedMcpServerInstructionsMessage)
|
|
12459
|
+
) {
|
|
12460
|
+
this.agent.appendMessage(untrustedMcpServerInstructionsMessage);
|
|
12461
|
+
}
|
|
12395
12462
|
const hindsightState = this.getHindsightSessionState();
|
|
12396
12463
|
await hindsightState?.maybeRecallOnAgentStart();
|
|
12397
12464
|
hindsightRecall = hindsightState?.getRecallSnippetForInjection();
|
|
@@ -12429,7 +12496,6 @@ export class AgentSession {
|
|
|
12429
12496
|
});
|
|
12430
12497
|
if (continuationSdkRunToken === undefined) await this.#waitForPostPromptRecovery();
|
|
12431
12498
|
} finally {
|
|
12432
|
-
this.#removeEphemeralCustomMessages();
|
|
12433
12499
|
await this.#settleEndedInFlight(
|
|
12434
12500
|
inFlightPrompt,
|
|
12435
12501
|
continuationSdkRunToken === undefined ? "full" : "publication",
|
|
@@ -13293,6 +13359,21 @@ export class AgentSession {
|
|
|
13293
13359
|
);
|
|
13294
13360
|
}
|
|
13295
13361
|
|
|
13362
|
+
/**
|
|
13363
|
+
* Sent ephemeral copies stay in live state so the provider prefix is stable. A new
|
|
13364
|
+
* copy is only worth appending when its provider-visible content changed; an
|
|
13365
|
+
* identical one would just repeat tokens that still count against the window.
|
|
13366
|
+
*/
|
|
13367
|
+
#isLatestRetainedEphemeralCopy(message: CustomMessage): boolean {
|
|
13368
|
+
const messages = this.agent.state.messages;
|
|
13369
|
+
for (let index = messages.length - 1; index >= 0; index--) {
|
|
13370
|
+
const candidate = messages[index];
|
|
13371
|
+
if (candidate?.role !== "custom" || candidate.customType !== message.customType) continue;
|
|
13372
|
+
return JSON.stringify(candidate.content) === JSON.stringify(message.content);
|
|
13373
|
+
}
|
|
13374
|
+
return false;
|
|
13375
|
+
}
|
|
13376
|
+
|
|
13296
13377
|
#removeEphemeralCustomMessages(): void {
|
|
13297
13378
|
const messages = this.agent.state.messages;
|
|
13298
13379
|
const withoutEphemeralMessages = this.#withoutEphemeralCustomMessages(messages);
|
|
@@ -13988,8 +14069,6 @@ export class AgentSession {
|
|
|
13988
14069
|
throw new Error(formatNoCredentialOnboardingError(this.model.provider));
|
|
13989
14070
|
}
|
|
13990
14071
|
|
|
13991
|
-
this.#removeEphemeralCustomMessages();
|
|
13992
|
-
|
|
13993
14072
|
// Check if we need to compact before sending (catches aborted responses)
|
|
13994
14073
|
const lastAssistant = this.#findLastAssistantMessage();
|
|
13995
14074
|
if (lastAssistant && !options?.skipCompactionCheck) {
|
|
@@ -14164,9 +14243,16 @@ export class AgentSession {
|
|
|
14164
14243
|
messages.push(goalModeMessage);
|
|
14165
14244
|
}
|
|
14166
14245
|
const volatileProjectContextMessage = await this.#buildVolatileProjectContextMessage();
|
|
14167
|
-
|
|
14246
|
+
if (!this.#isLatestRetainedEphemeralCopy(volatileProjectContextMessage)) {
|
|
14247
|
+
messages.push(volatileProjectContextMessage);
|
|
14248
|
+
}
|
|
14168
14249
|
const untrustedMcpServerInstructionsMessage = this.#buildUntrustedMcpServerInstructionsMessage();
|
|
14169
|
-
if (
|
|
14250
|
+
if (
|
|
14251
|
+
untrustedMcpServerInstructionsMessage &&
|
|
14252
|
+
!this.#isLatestRetainedEphemeralCopy(untrustedMcpServerInstructionsMessage)
|
|
14253
|
+
) {
|
|
14254
|
+
messages.push(untrustedMcpServerInstructionsMessage);
|
|
14255
|
+
}
|
|
14170
14256
|
|
|
14171
14257
|
// Roster: one Phase A claim, revalidated and reused on every attempt;
|
|
14172
14258
|
// a superseded claim releases and the prompt proceeds without it.
|
|
@@ -14328,7 +14414,6 @@ export class AgentSession {
|
|
|
14328
14414
|
return;
|
|
14329
14415
|
throw error;
|
|
14330
14416
|
} finally {
|
|
14331
|
-
this.#removeEphemeralCustomMessages();
|
|
14332
14417
|
if (rosterClaim) {
|
|
14333
14418
|
this.agent.replaceMessages(
|
|
14334
14419
|
this.agent.state.messages.filter(
|
|
@@ -14606,6 +14691,7 @@ export class AgentSession {
|
|
|
14606
14691
|
onQueuedAfterAdmission?: (message: AgentMessage, cancelQueued: () => boolean) => void;
|
|
14607
14692
|
external?: boolean;
|
|
14608
14693
|
sdkRunToken?: string;
|
|
14694
|
+
expectedSdkRunToken?: string;
|
|
14609
14695
|
forceOneAtATime?: boolean;
|
|
14610
14696
|
allowDuringSessionTransition?: boolean;
|
|
14611
14697
|
allowCancelAndSubmit?: boolean;
|
|
@@ -14622,11 +14708,18 @@ export class AgentSession {
|
|
|
14622
14708
|
const message = { role: "user" as const, content, attribution: "user" as const, timestamp: Date.now() };
|
|
14623
14709
|
// Enqueue-time admission: the Agent only accepts a steer into a live,
|
|
14624
14710
|
// non-aborted run. Anything else (idle, unwinding after agent_end, another
|
|
14625
|
-
// prompt still in preflight, aborting) has no run to steer
|
|
14626
|
-
//
|
|
14627
|
-
//
|
|
14711
|
+
// prompt still in preflight, aborting) has no run to steer. Ordinary steers
|
|
14712
|
+
// become a sequential follow-up owned by the next turn; SDK turn-bound
|
|
14713
|
+
// steers fail closed rather than being delivered to a successor run.
|
|
14714
|
+
if (
|
|
14715
|
+
options?.expectedSdkRunToken !== undefined &&
|
|
14716
|
+
this.#activeSdkRunToken !== options.expectedSdkRunToken
|
|
14717
|
+
)
|
|
14718
|
+
throw Object.assign(new Error("The expected SDK run is not active."), { code: "turn_not_active" });
|
|
14628
14719
|
const admission = this.agent.steer(message, options?.forceOneAtATime ? { forceOneAtATime: true } : undefined);
|
|
14629
14720
|
if (!admission.admitted) {
|
|
14721
|
+
if (options?.expectedSdkRunToken !== undefined)
|
|
14722
|
+
throw Object.assign(new Error("The expected SDK run is not active."), { code: "turn_not_active" });
|
|
14630
14723
|
const queuedFollowUp = await this.#queueFollowUpAfterReservation(message, displayText, {
|
|
14631
14724
|
forceOneAtATime: options?.forceOneAtATime,
|
|
14632
14725
|
claimsGenuineUserIntent: options?.claimsGenuineUserIntent,
|
|
@@ -15779,6 +15872,7 @@ export class AgentSession {
|
|
|
15779
15872
|
onPromoted: onQueuedPromoted,
|
|
15780
15873
|
external: true,
|
|
15781
15874
|
sdkRunToken: internalOptions?.sdkRunToken,
|
|
15875
|
+
expectedSdkRunToken: internalOptions?.expectedSdkRunToken,
|
|
15782
15876
|
forceOneAtATime: options?.queuePolicy === "sequential",
|
|
15783
15877
|
onQueuedAfterAdmission: onTrackedQueued,
|
|
15784
15878
|
allowDuringSessionTransition: this.#hasCommittedSuccessorTrackedAdmission(options),
|
|
@@ -16145,6 +16239,9 @@ export class AgentSession {
|
|
|
16145
16239
|
this.#cachedWorkspaceTree = undefined;
|
|
16146
16240
|
this.#cachedWorkspaceTreeAt = 0;
|
|
16147
16241
|
this.#pendingWorkspaceTreeRescope = true;
|
|
16242
|
+
// Sent copies name the abandoned root; the rescope already rebuilds the stable
|
|
16243
|
+
// prompt, so there is no cache prefix left to preserve by keeping them.
|
|
16244
|
+
this.#removeEphemeralCustomMessages();
|
|
16148
16245
|
}
|
|
16149
16246
|
|
|
16150
16247
|
/** Skill loading warnings captured by SDK */
|
|
@@ -20282,7 +20379,7 @@ export class AgentSession {
|
|
|
20282
20379
|
}
|
|
20283
20380
|
|
|
20284
20381
|
const handoffText = await generateHandoff(
|
|
20285
|
-
this.agent.state.messages,
|
|
20382
|
+
this.#withoutEphemeralCustomMessages(this.agent.state.messages),
|
|
20286
20383
|
model,
|
|
20287
20384
|
apiKey,
|
|
20288
20385
|
{
|
|
@@ -20516,7 +20613,7 @@ export class AgentSession {
|
|
|
20516
20613
|
sessionId: this.sessionId,
|
|
20517
20614
|
cwd: this.sessionManager.getCwd(),
|
|
20518
20615
|
sessionFile: this.sessionFile,
|
|
20519
|
-
messages: this.agent.state.messages,
|
|
20616
|
+
messages: this.#withoutEphemeralCustomMessages(this.agent.state.messages),
|
|
20520
20617
|
customInstructions: options.customInstructions,
|
|
20521
20618
|
},
|
|
20522
20619
|
options,
|
|
@@ -22699,6 +22796,7 @@ export class AgentSession {
|
|
|
22699
22796
|
willRetry && !continuationSkipReason
|
|
22700
22797
|
? await this.#scheduleOverflowRetryContinuation(generation, options?.resourceRunId)
|
|
22701
22798
|
: false;
|
|
22799
|
+
|
|
22702
22800
|
await this.#emitSessionEvent({
|
|
22703
22801
|
type: "auto_compaction_end",
|
|
22704
22802
|
action,
|
|
@@ -22707,6 +22805,7 @@ export class AgentSession {
|
|
|
22707
22805
|
willRetry: overflowContinuationScheduled,
|
|
22708
22806
|
continuationSkipReason,
|
|
22709
22807
|
});
|
|
22808
|
+
|
|
22710
22809
|
if (autoCompactionSignal.aborted) return { kind: "aborted", source: "signal" };
|
|
22711
22810
|
|
|
22712
22811
|
if (willRetry) {
|
|
@@ -22930,6 +23029,7 @@ export class AgentSession {
|
|
|
22930
23029
|
}
|
|
22931
23030
|
|
|
22932
23031
|
#isRetryableError(message: AssistantMessage): boolean {
|
|
23032
|
+
if (isTerminalCodexProviderFailure(message)) return false;
|
|
22933
23033
|
if (this.#isTerminalProviderFirstEventTimeout(message)) return false;
|
|
22934
23034
|
if (message.errorMessage?.startsWith("Model fallback chain exhausted;")) return false;
|
|
22935
23035
|
if (message.errorMessage?.startsWith("Managed fallback retried the escaped non-ASCII")) return false;
|
|
@@ -22940,6 +23040,11 @@ export class AgentSession {
|
|
|
22940
23040
|
return false;
|
|
22941
23041
|
const contextWindow = this.model?.contextWindow ?? 0;
|
|
22942
23042
|
if (classifyContextOverflow(message, transportFailure, contextWindow)) return false;
|
|
23043
|
+
if (
|
|
23044
|
+
(isBareDefaultCodexOverload(message) || isBareDefaultCodexProviderFailure(message)) &&
|
|
23045
|
+
this.settings.get("retry.enabled") !== false
|
|
23046
|
+
)
|
|
23047
|
+
return true;
|
|
22943
23048
|
const managedFallback = this.#defaultFallbackChain().chain.entries.length > 1;
|
|
22944
23049
|
// An account-specific model rejection that cannot rotate to another
|
|
22945
23050
|
// credential stays terminal only on the session's own retry path; managed
|
|
@@ -23052,9 +23157,7 @@ export class AgentSession {
|
|
|
23052
23157
|
// Errors that will never succeed on retry (auth/permission, malformed
|
|
23053
23158
|
// request, unknown/unsupported model). These surface immediately rather
|
|
23054
23159
|
// than retry forever.
|
|
23055
|
-
return
|
|
23056
|
-
errorMessage,
|
|
23057
|
-
);
|
|
23160
|
+
return TERMINAL_ERROR_MESSAGE.test(errorMessage);
|
|
23058
23161
|
}
|
|
23059
23162
|
|
|
23060
23163
|
#extractExplicitHttpStatusFromErrorMessage(errorMessage: string): number | undefined {
|
|
@@ -23082,6 +23185,9 @@ export class AgentSession {
|
|
|
23082
23185
|
// Without this the message carries no transport facts and would fall
|
|
23083
23186
|
// through to "unknown", which is admitted for bounded retry (#5627).
|
|
23084
23187
|
if (message.errorCode === REPETITION_GUARD_ERROR_CODE) return "terminal";
|
|
23188
|
+
// A 2xx body in the wrong protocol replays identically; its embedded body text
|
|
23189
|
+
// (e.g. "HTTP 503") must not be reparsed as a transient failure.
|
|
23190
|
+
if (message.errorCode === PROVIDER_PROTOCOL_MISMATCH_ERROR_CODE) return "terminal";
|
|
23085
23191
|
if (message.errorKind === "local_snapshot_failure") return "local_snapshot";
|
|
23086
23192
|
if (message.errorKind === "local_buffer_overflow") return "local_buffer_overflow";
|
|
23087
23193
|
if (this.#isTypedFirstEventTimeout(message)) return "first_event_timeout";
|
|
@@ -24240,6 +24346,13 @@ export class AgentSession {
|
|
|
24240
24346
|
const classification = this.#classifyErrorForRetry(message);
|
|
24241
24347
|
const localSnapshot = classification === "local_snapshot";
|
|
24242
24348
|
const localBufferOverflow = classification === "local_buffer_overflow";
|
|
24349
|
+
// Managed outcomes can reach retry handling without the ordinary admission
|
|
24350
|
+
// check. Do not let retained transport facts bypass the same terminal veto.
|
|
24351
|
+
if (isTerminalCodexProviderFailure(message)) {
|
|
24352
|
+
return managedOutcome
|
|
24353
|
+
? { type: "terminal", terminal: { stopReason: "error", messages: [message] } }
|
|
24354
|
+
: false;
|
|
24355
|
+
}
|
|
24243
24356
|
if (retryCancelled()) {
|
|
24244
24357
|
return managedOutcome ? { type: "terminal", terminal: { stopReason: "cancelled" } } : false;
|
|
24245
24358
|
}
|
|
@@ -24301,6 +24414,9 @@ export class AgentSession {
|
|
|
24301
24414
|
isBareDefaultCodexOverload(message) ||
|
|
24302
24415
|
isBareDefaultAnthropicOverload(message) ||
|
|
24303
24416
|
isBareDefaultOpenAIResponsesOverload(message);
|
|
24417
|
+
const canReplayBareDefaultCodexFailure = isBareDefaultCodexProviderFailure(message);
|
|
24418
|
+
const boundBareDefaultCodexFailure = canReplayBareDefaultCodexFailure && !legacyRetryConfigured;
|
|
24419
|
+
const canReplayCodexProviderOverload = isBareDefaultCodexOverload(message);
|
|
24304
24420
|
const reportedRetryMaxAttempts = transportFailure?.retryMaxAttempts;
|
|
24305
24421
|
if (reportedRetryMaxAttempts !== undefined) {
|
|
24306
24422
|
this.#providerRetryMaxAttempts = Math.min(
|
|
@@ -24340,19 +24456,26 @@ export class AgentSession {
|
|
|
24340
24456
|
}
|
|
24341
24457
|
: false;
|
|
24342
24458
|
}
|
|
24459
|
+
const fallbackTrigger = this.#fallbackTriggerFor(message, !managedFallback, transportFailure);
|
|
24343
24460
|
const trigger:
|
|
24344
24461
|
| { class: FallbackTriggerClass; retryAfterMs?: number; authDisposition?: AuthDisposition }
|
|
24345
|
-
| undefined =
|
|
24462
|
+
| undefined = canReplayCodexProviderOverload || canReplayBareDefaultCodexFailure
|
|
24463
|
+
? { class: "server" }
|
|
24464
|
+
: fallbackTrigger;
|
|
24346
24465
|
// OpenAI's typed statusless capacity-overload code (issue #5018) must not
|
|
24347
24466
|
// gain managed-chain retry/advance authority from its new facts. Before
|
|
24348
24467
|
// the code survived transport, this failure reached the session as an
|
|
24349
24468
|
// ordinary committed error and surfaced immediately, so mirror that
|
|
24350
|
-
// behavior with the existing exhaustion decision.
|
|
24469
|
+
// behavior with the existing exhaustion decision. However, when called from
|
|
24470
|
+
// the agent_end path (managedOutcome=false), we must return false to allow
|
|
24471
|
+
// proper session termination handling.
|
|
24351
24472
|
if (managedFallback && isStatuslessTypedOverloadFacts(transportFailure)) {
|
|
24352
|
-
return
|
|
24353
|
-
|
|
24354
|
-
|
|
24355
|
-
|
|
24473
|
+
return managedOutcome
|
|
24474
|
+
? this.#managedFallbackExhaustionDecision(
|
|
24475
|
+
message,
|
|
24476
|
+
message.errorMessage || "Model fallback attempt failed",
|
|
24477
|
+
)
|
|
24478
|
+
: false;
|
|
24356
24479
|
}
|
|
24357
24480
|
if (!trigger) {
|
|
24358
24481
|
return managedOutcome
|
|
@@ -24442,6 +24565,7 @@ export class AgentSession {
|
|
|
24442
24565
|
if (!managedFallback && !legacyRetryConfigured && !canReplayRotatedCredential && !canReplayEmptyResponse) {
|
|
24443
24566
|
if (
|
|
24444
24567
|
(!canReplayProviderOverload &&
|
|
24568
|
+
!canReplayBareDefaultCodexFailure &&
|
|
24445
24569
|
!canReplayUnexpectedSocketClose &&
|
|
24446
24570
|
!this.#isTypedFirstEventTimeout(message) &&
|
|
24447
24571
|
!messageOnlyWatchdogTimeout &&
|
|
@@ -24460,6 +24584,7 @@ export class AgentSession {
|
|
|
24460
24584
|
!managedFallback &&
|
|
24461
24585
|
classification === "transient" &&
|
|
24462
24586
|
!canReplayProviderOverload &&
|
|
24587
|
+
!boundBareDefaultCodexFailure &&
|
|
24463
24588
|
!canReplayUnexpectedSocketClose &&
|
|
24464
24589
|
!this.#isIdleStreamStallErrorMessage(message.errorMessage ?? "");
|
|
24465
24590
|
|
|
@@ -26036,7 +26161,8 @@ export class AgentSession {
|
|
|
26036
26161
|
|
|
26037
26162
|
/** Build a background snapshot with in-flight assistant and optional context. */
|
|
26038
26163
|
#buildEphemeralSnapshot(promptText: string, prependMessages?: AgentMessage[]): AgentMessage[] {
|
|
26039
|
-
|
|
26164
|
+
// Side requests run in a fresh provider session with no prefix to preserve.
|
|
26165
|
+
const messages = this.#withoutEphemeralCustomMessages(this.messages);
|
|
26040
26166
|
const streaming = this.agent.state.streamMessage;
|
|
26041
26167
|
if (streaming && streaming.role === "assistant") {
|
|
26042
26168
|
const preservedBlocks: AssistantMessage["content"] = [];
|
|
@@ -27268,7 +27394,7 @@ export class AgentSession {
|
|
|
27268
27394
|
const branchEntries = this.sessionManager.getBranch();
|
|
27269
27395
|
const latestCompaction = getLatestCompactionEntry(branchEntries);
|
|
27270
27396
|
const boundaryTs = latestCompaction ? new Date(latestCompaction.timestamp).getTime() : 0;
|
|
27271
|
-
const anchor = this.#findAnchorableUsageIndex(this.messages, boundaryTs);
|
|
27397
|
+
const anchor = this.#findAnchorableUsageIndex(this.agent.state.messages, boundaryTs);
|
|
27272
27398
|
let value: ContextUsage;
|
|
27273
27399
|
if (latestCompaction && !anchor) {
|
|
27274
27400
|
value = { tokens: null, contextWindow, percent: null, source: "unknown" };
|
|
@@ -27291,7 +27417,7 @@ export class AgentSession {
|
|
|
27291
27417
|
}
|
|
27292
27418
|
|
|
27293
27419
|
#contextUsageCacheKey(model: Model, contextWindow: number): string {
|
|
27294
|
-
const messages = this.messages;
|
|
27420
|
+
const messages = this.agent.state.messages;
|
|
27295
27421
|
const lastMessage = messages[messages.length - 1];
|
|
27296
27422
|
// Entry and leaf revisions change whenever the active branch changes, avoiding getBranch() on warm reads.
|
|
27297
27423
|
const revision = this.sessionManager.revisionSnapshot();
|
|
@@ -27440,7 +27566,7 @@ export class AgentSession {
|
|
|
27440
27566
|
* raw promptTokens/estimatedTokens quotient). Clamped to [0.5, 2] downstream.
|
|
27441
27567
|
*/
|
|
27442
27568
|
#computeCompactionTokenCorrectionRatio(): number | undefined {
|
|
27443
|
-
const messages = this.messages;
|
|
27569
|
+
const messages = this.agent.state.messages;
|
|
27444
27570
|
let lastUsageIndex = -1;
|
|
27445
27571
|
let lastUsage: Usage | undefined;
|
|
27446
27572
|
for (let i = messages.length - 1; i >= 0; i--) {
|
|
@@ -27490,7 +27616,7 @@ export class AgentSession {
|
|
|
27490
27616
|
tokens: number;
|
|
27491
27617
|
anchored: boolean;
|
|
27492
27618
|
} {
|
|
27493
|
-
const messages = this.messages;
|
|
27619
|
+
const messages = this.agent.state.messages;
|
|
27494
27620
|
let anchor = knownAnchor;
|
|
27495
27621
|
if (boundaryTs === undefined) {
|
|
27496
27622
|
const latestCompaction = getLatestCompactionEntry(this.sessionManager.getBranch());
|
|
@@ -27666,7 +27792,7 @@ export class AgentSession {
|
|
|
27666
27792
|
*/
|
|
27667
27793
|
formatSessionAsText(): string {
|
|
27668
27794
|
return formatSessionDumpText({
|
|
27669
|
-
messages: this.messages,
|
|
27795
|
+
messages: this.#withoutEphemeralCustomMessages(this.messages),
|
|
27670
27796
|
systemPrompt: this.agent.state.systemPrompt,
|
|
27671
27797
|
model: this.agent.state.model,
|
|
27672
27798
|
thinkingLevel: this.#thinkingLevel,
|