@gajae-code/coding-agent 0.15.5 → 0.16.0
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 +85 -1
- package/dist/types/async/job-manager.d.ts +83 -0
- package/dist/types/cli/args.d.ts +2 -0
- package/dist/types/cli/auth-gateway-cli.d.ts +17 -0
- package/dist/types/cli/model-presets-cli.d.ts +9 -0
- package/dist/types/cli/root-flags.d.ts +14 -0
- package/dist/types/cli/update-cli.d.ts +15 -0
- package/dist/types/cli.d.ts +7 -0
- package/dist/types/commands/auth-gateway.d.ts +3 -0
- package/dist/types/commands/gc.d.ts +13 -0
- package/dist/types/commands/launch.d.ts +10 -0
- package/dist/types/commands/model-presets.d.ts +21 -0
- package/dist/types/config/autorouting-tier-map.d.ts +120 -0
- package/dist/types/config/file-lock.d.ts +54 -2
- package/dist/types/config/internal/model-preset-registry-test-state.d.ts +4 -0
- package/dist/types/config/internal/model-preset-registry-test-support.d.ts +3 -0
- package/dist/types/config/keybindings.d.ts +3 -2
- package/dist/types/config/model-preset-registry.d.ts +368 -0
- package/dist/types/config/model-profile-activation.d.ts +39 -0
- package/dist/types/config/model-profiles.d.ts +5 -3
- package/dist/types/config/model-registry.d.ts +17 -5
- package/dist/types/config/settings-schema.d.ts +19 -1
- package/dist/types/config/settings.d.ts +2 -0
- package/dist/types/coordinator-mcp/model-preset.d.ts +7 -5
- package/dist/types/coordinator-mcp/projection-scan.d.ts +54 -0
- package/dist/types/crash/record-loader.d.ts +15 -0
- package/dist/types/extensibility/extensions/types.d.ts +2 -5
- package/dist/types/gjc-runtime/empty-delete-gc.d.ts +58 -0
- package/dist/types/gjc-runtime/gc-runtime.d.ts +2 -0
- package/dist/types/gjc-runtime/launch-worktree-reservation.d.ts +21 -0
- package/dist/types/gjc-runtime/managed-owner-supervisor.d.ts +2 -0
- package/dist/types/gjc-runtime/session-state-lock.d.ts +35 -6
- package/dist/types/gjc-runtime/session-state-sidecar.d.ts +72 -1
- package/dist/types/gjc-runtime/tmux-owner-isolation.d.ts +1 -0
- package/dist/types/gjc-runtime/tmux-sessions.d.ts +27 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/main.d.ts +3 -1
- package/dist/types/master-mode/context.d.ts +21 -0
- package/dist/types/master-mode/first-request.d.ts +34 -0
- package/dist/types/master-mode/snapshot.d.ts +32 -0
- package/dist/types/modes/components/frictionless-onboarding-selector.d.ts +10 -0
- package/dist/types/modes/components/jobs-overlay-model.d.ts +4 -2
- package/dist/types/modes/components/oauth-selector.d.ts +1 -0
- package/dist/types/modes/components/status-line/model-name.d.ts +18 -0
- package/dist/types/modes/components/status-line/priority-row.d.ts +34 -0
- package/dist/types/modes/components/status-line/segments.d.ts +10 -0
- package/dist/types/modes/jobs-observer.d.ts +36 -0
- package/dist/types/modes/print-mode.d.ts +17 -0
- package/dist/types/modes/ui-language.d.ts +7 -1
- package/dist/types/sdk/broker/broker.d.ts +80 -1
- package/dist/types/sdk/broker/endpoint-authority.d.ts +24 -0
- package/dist/types/sdk/broker/index.d.ts +3 -0
- package/dist/types/sdk/broker/lifecycle.d.ts +26 -0
- package/dist/types/sdk/broker/master-capability.d.ts +10 -0
- package/dist/types/sdk/broker/session-index.d.ts +40 -11
- package/dist/types/sdk/broker/session-scope.d.ts +72 -0
- package/dist/types/sdk/broker/spawn-authority.d.ts +201 -0
- package/dist/types/sdk/broker/spawn-substrate.d.ts +30 -0
- package/dist/types/sdk/broker/state-version.d.ts +4 -1
- package/dist/types/sdk/broker/worktree-occupancy.d.ts +28 -0
- package/dist/types/sdk/bus/chat-daemon-control.d.ts +3 -0
- package/dist/types/sdk/bus/index.d.ts +6 -0
- package/dist/types/sdk/bus/kind-aware-reconciliation.d.ts +3 -0
- package/dist/types/sdk/bus/prompt-reconciliation.d.ts +4 -0
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +1 -1
- package/dist/types/sdk/cli/index.d.ts +1 -0
- package/dist/types/sdk/cli/master-cli.d.ts +63 -0
- package/dist/types/sdk/cli/rows.d.ts +4 -3
- package/dist/types/sdk/cli/session-cli.d.ts +18 -2
- package/dist/types/sdk/client/client.d.ts +3 -2
- package/dist/types/sdk/client/discovery.d.ts +1 -0
- package/dist/types/sdk/host/control/dispatch.d.ts +10 -0
- package/dist/types/sdk/host/control/index.d.ts +1 -1
- package/dist/types/sdk/host/control/operations.d.ts +2 -0
- package/dist/types/sdk/host/control/runtime-gate.d.ts +21 -7
- package/dist/types/sdk/host/host.d.ts +10 -0
- package/dist/types/sdk/host/model-pin.d.ts +7 -2
- package/dist/types/sdk/host/sdk-run-capability.d.ts +1 -0
- package/dist/types/sdk/host/session-runtime.d.ts +67 -1
- package/dist/types/sdk/lifecycle/broker-client.d.ts +6 -0
- package/dist/types/sdk/lifecycle/service.d.ts +15 -3
- package/dist/types/sdk/lifecycle-session.d.ts +2 -0
- package/dist/types/sdk/prompt-deadline-manager.d.ts +11 -1
- package/dist/types/sdk/protocol/adapter-validation.d.ts +2 -0
- package/dist/types/sdk/router/session-router.d.ts +11 -0
- package/dist/types/sdk/session.d.ts +16 -0
- package/dist/types/sdk/startup-capability.d.ts +2 -1
- package/dist/types/session/account-inventory.d.ts +1 -0
- package/dist/types/session/agent-session.d.ts +24 -7
- package/dist/types/session/credential-store-errors.d.ts +1 -0
- package/dist/types/session/fold-coordinator.d.ts +201 -0
- package/dist/types/session/sdk-run-capability-internal.d.ts +7 -0
- package/dist/types/session/sdk-run-capability.d.ts +2 -0
- package/dist/types/session/session-manager.d.ts +65 -0
- package/dist/types/session/yield-queue.d.ts +5 -4
- package/dist/types/setup/frictionless-onboarding.d.ts +1 -0
- package/dist/types/telemetry/control.d.ts +11 -0
- package/dist/types/telemetry/events.d.ts +20 -0
- package/dist/types/telemetry/index.d.ts +3 -0
- package/dist/types/telemetry/transport.d.ts +22 -0
- package/dist/types/tools/bash-interactive.d.ts +23 -0
- package/dist/types/tools/bash.d.ts +4 -0
- package/dist/types/tools/gh.d.ts +8 -0
- package/dist/types/tools/index.d.ts +10 -5
- package/dist/types/web/search/providers/base.d.ts +9 -4
- package/dist/types/web/search/providers/xai.d.ts +2 -1
- package/dist/types/web/search/types.d.ts +14 -0
- package/package.json +10 -7
- package/scripts/generate-hotkeys-docs.ts +4 -0
- package/scripts/generate-sdk-operation-inventory.ts +9 -3
- package/scripts/verify-gjc-sdk-canonicalization.ts +35 -1
- package/src/async/job-manager.ts +671 -39
- package/src/cli/accounts-cli.ts +9 -5
- package/src/cli/args.ts +17 -0
- package/src/cli/auth-broker-cli.ts +40 -12
- package/src/cli/auth-gateway-cli.ts +273 -97
- package/src/cli/config-cli.ts +2 -2
- package/src/cli/fast-help.ts +4 -0
- package/src/cli/model-presets-cli.ts +100 -0
- package/src/cli/root-flags.ts +5 -0
- package/src/cli/update-cli.ts +117 -16
- package/src/cli.ts +6 -2
- package/src/commands/auth-gateway.ts +12 -8
- package/src/commands/gc.ts +19 -0
- package/src/commands/launch.ts +86 -18
- package/src/commands/model-presets.ts +24 -0
- package/src/commands/sdk.ts +113 -21
- package/src/config/autorouting-tier-map.ts +44 -0
- package/src/config/file-lock-gc.ts +9 -4
- package/src/config/file-lock.ts +890 -99
- package/src/config/internal/model-preset-registry-test-state.ts +28 -0
- package/src/config/internal/model-preset-registry-test-support.ts +11 -0
- package/src/config/keybindings.ts +6 -2
- package/src/config/model-preset-registry.ts +2795 -0
- package/src/config/model-profile-activation.ts +115 -28
- package/src/config/model-profile-contract.ts +4 -1
- package/src/config/model-profiles.ts +56 -14
- package/src/config/model-registry.ts +1044 -122
- package/src/config/model-resolver.ts +5 -0
- package/src/config/settings-schema.ts +19 -1
- package/src/config/settings.ts +11 -1
- package/src/coordinator-mcp/model-preset.ts +32 -17
- package/src/coordinator-mcp/projection-scan.ts +592 -0
- package/src/coordinator-mcp/question-state.ts +9 -11
- package/src/coordinator-mcp/server.ts +111 -42
- package/src/coordinator-mcp/session-reaper.ts +9 -3
- package/src/crash/index-store.ts +2 -0
- package/src/crash/record-loader.ts +10 -0
- package/src/extensibility/extensions/loader.ts +0 -1
- package/src/extensibility/extensions/types.ts +2 -3
- package/src/extensibility/gjc-plugins/runtime-adapters.ts +1 -0
- package/src/gjc-runtime/empty-delete-gc.ts +301 -0
- package/src/gjc-runtime/gc-render.ts +29 -0
- package/src/gjc-runtime/gc-runtime.ts +174 -11
- package/src/gjc-runtime/launch-worktree-reservation.ts +242 -0
- package/src/gjc-runtime/managed-owner-supervisor.ts +63 -51
- package/src/gjc-runtime/session-state-lock.ts +1321 -147
- package/src/gjc-runtime/session-state-sidecar.ts +1004 -25
- package/src/gjc-runtime/tmux-owner-isolation-cli.ts +4 -7
- package/src/gjc-runtime/tmux-owner-isolation.ts +2 -2
- package/src/gjc-runtime/tmux-sessions.ts +252 -13
- package/src/hooks/skill-state.ts +6 -0
- package/src/index.ts +1 -0
- package/src/internal-urls/docs-index.generated.ts +14 -13
- package/src/internal-urls/issue-pr-protocol.ts +4 -2
- package/src/lsp/config.ts +4 -3
- package/src/lsp/path-trust.ts +12 -6
- package/src/main.ts +66 -4
- package/src/master-mode/context.ts +50 -0
- package/src/master-mode/first-request.ts +65 -0
- package/src/master-mode/snapshot.ts +205 -0
- package/src/modes/acp/acp-agent.ts +616 -161
- package/src/modes/components/footer.ts +4 -2
- package/src/modes/components/frictionless-onboarding-selector.ts +48 -2
- package/src/modes/components/jobs-overlay-model.ts +99 -16
- package/src/modes/components/model-selector.ts +158 -18
- package/src/modes/components/oauth-selector.ts +6 -1
- package/src/modes/components/settings-selector.ts +1 -0
- package/src/modes/components/status-line/model-name.ts +53 -0
- package/src/modes/components/status-line/priority-row.ts +107 -0
- package/src/modes/components/status-line/segments.ts +29 -10
- package/src/modes/components/tool-status-header.ts +151 -15
- package/src/modes/controllers/extension-ui-controller.ts +1 -1
- package/src/modes/controllers/input-controller.ts +51 -6
- package/src/modes/controllers/selector-controller.ts +191 -96
- package/src/modes/interactive-mode.ts +14 -1
- package/src/modes/jobs-observer.ts +149 -16
- package/src/modes/print-mode.ts +64 -0
- package/src/modes/runtime-init.ts +1 -1
- package/src/modes/ui-language.ts +49 -2
- package/src/modes/utils/injected-user-submission.ts +15 -1
- package/src/prompts/system/master-mode.md +28 -0
- package/src/prompts/tools/bash.md +2 -2
- package/src/prompts/tools/fold-receipt.md +3 -0
- package/src/prompts/tools/job.md +1 -1
- package/src/sdk/acp/adapter.ts +17 -2
- package/src/sdk/broker/broker.ts +1671 -34
- package/src/sdk/broker/endpoint-authority.ts +86 -0
- package/src/sdk/broker/ensure.ts +5 -0
- package/src/sdk/broker/index.ts +3 -0
- package/src/sdk/broker/lifecycle.ts +217 -46
- package/src/sdk/broker/master-capability.ts +232 -0
- package/src/sdk/broker/session-index.ts +121 -17
- package/src/sdk/broker/session-scope.ts +272 -0
- package/src/sdk/broker/spawn-authority.ts +1031 -0
- package/src/sdk/broker/spawn-substrate.ts +548 -0
- package/src/sdk/broker/state-version.ts +26 -14
- package/src/sdk/broker/transport.ts +1 -0
- package/src/sdk/broker/worktree-occupancy.ts +90 -0
- package/src/sdk/bus/chat-daemon-control.ts +5 -2
- package/src/sdk/bus/chat-daemon-runtime.ts +46 -5
- package/src/sdk/bus/index.ts +51 -4
- package/src/sdk/bus/kind-aware-reconciliation.ts +36 -6
- package/src/sdk/bus/prompt-reconciliation.ts +28 -4
- package/src/sdk/bus/slack-conversation.ts +5 -1
- package/src/sdk/bus/telegram-daemon-contract.ts +1 -1
- package/src/sdk/cli/index.ts +1 -0
- package/src/sdk/cli/master-cli.ts +259 -0
- package/src/sdk/cli/rows.ts +11 -8
- package/src/sdk/cli/session-cli.ts +276 -6
- package/src/sdk/client/client.ts +14 -8
- package/src/sdk/client/discovery.ts +3 -1
- package/src/sdk/host/control/dispatch.ts +101 -37
- package/src/sdk/host/control/index.ts +2 -0
- package/src/sdk/host/control/operations.ts +2 -0
- package/src/sdk/host/control/runtime-gate.ts +203 -11
- package/src/sdk/host/host.ts +27 -2
- package/src/sdk/host/model-pin.ts +185 -25
- package/src/sdk/host/sdk-run-capability.ts +1 -0
- package/src/sdk/host/session-runtime.test.ts +581 -43
- package/src/sdk/host/session-runtime.ts +655 -101
- package/src/sdk/lifecycle/broker-client.ts +28 -0
- package/src/sdk/lifecycle/service.ts +279 -20
- package/src/sdk/lifecycle-session.ts +10 -2
- package/src/sdk/prompt-deadline-manager.ts +16 -1
- package/src/sdk/protocol/adapter-validation.ts +27 -0
- package/src/sdk/protocol/operation-inventory.generated.json +53 -2
- package/src/sdk/protocol/operation-registry.ts +17 -4
- package/src/sdk/router/session-router.ts +294 -154
- package/src/sdk/session.ts +440 -167
- package/src/sdk/startup-capability.ts +1 -0
- package/src/session/account-inventory.ts +27 -11
- package/src/session/agent-session.ts +1292 -180
- package/src/session/blob-store.ts +15 -5
- package/src/session/credential-store-errors.ts +2 -0
- package/src/session/fold-coordinator.ts +433 -0
- package/src/session/internal/managed-session-storage.ts +153 -78
- package/src/session/sdk-run-capability-internal.ts +15 -0
- package/src/session/sdk-run-capability.ts +2 -0
- package/src/session/session-manager.ts +342 -28
- package/src/session/yield-queue.ts +24 -10
- package/src/setup/frictionless-onboarding.ts +2 -0
- package/src/setup/provider-presets.json +1 -1
- package/src/slash-commands/helpers/credential-switch.ts +6 -1
- package/src/task/executor.ts +19 -3
- package/src/telemetry/control.ts +26 -0
- package/src/telemetry/events.ts +1268 -0
- package/src/telemetry/index.ts +3 -0
- package/src/telemetry/transport.ts +87 -0
- package/src/tools/bash-interactive.ts +182 -85
- package/src/tools/bash.ts +1028 -109
- package/src/tools/gh.ts +104 -48
- package/src/tools/github-cache.ts +2 -2
- package/src/tools/index.ts +11 -5
- package/src/tools/tool-catalog.generated.ts +1 -1
- package/src/web/search/provider.ts +120 -11
- package/src/web/search/providers/anthropic.ts +89 -17
- package/src/web/search/providers/base.ts +9 -4
- package/src/web/search/providers/gemini.ts +87 -15
- package/src/web/search/providers/openai-compatible.ts +23 -8
- package/src/web/search/providers/xai.ts +98 -12
- package/src/web/search/types.ts +16 -0
|
@@ -69,7 +69,7 @@ import {
|
|
|
69
69
|
} from "./event-webhook";
|
|
70
70
|
import {
|
|
71
71
|
type CoordinatorModelProfileLoader,
|
|
72
|
-
|
|
72
|
+
createCoordinatorModelProfileLoader,
|
|
73
73
|
resolveCoordinatorMpreset,
|
|
74
74
|
} from "./model-preset";
|
|
75
75
|
import {
|
|
@@ -83,6 +83,7 @@ import {
|
|
|
83
83
|
requireCoordinatorMutation,
|
|
84
84
|
safeOpenCoordinatorArtifact,
|
|
85
85
|
} from "./policy";
|
|
86
|
+
import { listCoordinatorJsonFilesWithRetry } from "./projection-scan";
|
|
86
87
|
import {
|
|
87
88
|
answerBindingMatches,
|
|
88
89
|
buildCoordinatorAskAnswerSchema,
|
|
@@ -834,7 +835,7 @@ function toolSchema(name: CoordinatorToolName): {
|
|
|
834
835
|
properties: {
|
|
835
836
|
after_seq: { type: "integer", minimum: 0 },
|
|
836
837
|
session_id: sessionId,
|
|
837
|
-
event_types: { type: "array", items: { type: "string" } },
|
|
838
|
+
event_types: { type: "array", items: { type: "string", enum: [...COORDINATOR_EVENT_KINDS] } },
|
|
838
839
|
timeout_ms: {
|
|
839
840
|
type: "number",
|
|
840
841
|
description: "Bounded event long-poll timeout in milliseconds, capped at 30 seconds.",
|
|
@@ -1497,26 +1498,17 @@ function publicSdkAcknowledgement(result: RuntimePromptAcknowledgement): Record<
|
|
|
1497
1498
|
}
|
|
1498
1499
|
|
|
1499
1500
|
async function listJsonFiles(dir: string): Promise<unknown[]> {
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
if ((error as NodeJS.ErrnoException).code === "ENOENT") return null;
|
|
1510
|
-
logger.warn("Coordinator projection read failed", { path: entry, error: String(error) });
|
|
1511
|
-
throw error;
|
|
1512
|
-
}
|
|
1513
|
-
}),
|
|
1514
|
-
);
|
|
1515
|
-
return values.filter(value => value !== null);
|
|
1516
|
-
} catch (error) {
|
|
1517
|
-
if ((error as NodeJS.ErrnoException).code === "ENOENT") return [];
|
|
1518
|
-
throw error;
|
|
1501
|
+
const scan = await listCoordinatorJsonFilesWithRetry(dir);
|
|
1502
|
+
if (scan.capped) throw new Error("coordinator_projection_scan_incomplete");
|
|
1503
|
+
if (scan.skippedEmpty > 0 || scan.skippedDebris > 0) {
|
|
1504
|
+
logger.warn("Coordinator projection scan skipped debris", {
|
|
1505
|
+
dir,
|
|
1506
|
+
parsed: scan.parsed,
|
|
1507
|
+
skippedDebris: scan.skippedDebris,
|
|
1508
|
+
skippedEmpty: scan.skippedEmpty,
|
|
1509
|
+
});
|
|
1519
1510
|
}
|
|
1511
|
+
return scan.values;
|
|
1520
1512
|
}
|
|
1521
1513
|
|
|
1522
1514
|
const COORDINATOR_STATUS_EVENT_LIMIT = 100;
|
|
@@ -1538,7 +1530,10 @@ function brokerSessionId(record: Record<string, unknown>): string | null {
|
|
|
1538
1530
|
}
|
|
1539
1531
|
|
|
1540
1532
|
function brokerSessionScope(record: Record<string, unknown>): string | null {
|
|
1541
|
-
|
|
1533
|
+
// Locator v2 records the canonical workspace directory as `cwd`; the removed
|
|
1534
|
+
// `repo` field is never translated, so reading it here silently dropped every
|
|
1535
|
+
// broker row and failed open with an empty scoped listing.
|
|
1536
|
+
return firstString(asRecord(record.locator) ?? {}, ["cwd"]);
|
|
1542
1537
|
}
|
|
1543
1538
|
|
|
1544
1539
|
function sameCanonicalPath(left: string, right: string, platform: NodeJS.Platform): boolean {
|
|
@@ -2642,11 +2637,6 @@ async function writeActiveTurn(namespaceDir: string, turn: TurnRecord): Promise<
|
|
|
2642
2637
|
});
|
|
2643
2638
|
}
|
|
2644
2639
|
|
|
2645
|
-
async function clearActiveTurn(namespaceDir: string, turn: TurnRecord): Promise<void> {
|
|
2646
|
-
const active = asRecord(await readJsonFile(activeTurnFile(namespaceDir, turn.session_id)));
|
|
2647
|
-
if (active?.turn_id === turn.turn_id) await removeCoordinatorFile(activeTurnFile(namespaceDir, turn.session_id));
|
|
2648
|
-
}
|
|
2649
|
-
|
|
2650
2640
|
async function readSessionState(namespaceDir: string, sessionId: string): Promise<CoordinatorSessionState | null> {
|
|
2651
2641
|
return (await readJsonFile(sessionStateFile(namespaceDir, sessionId))) as CoordinatorSessionState | null;
|
|
2652
2642
|
}
|
|
@@ -2666,9 +2656,6 @@ async function writeSessionStateUnlocked(
|
|
|
2666
2656
|
} = {},
|
|
2667
2657
|
): Promise<CoordinatorSessionState> {
|
|
2668
2658
|
const persisted = await readSessionState(namespaceDir, sessionId);
|
|
2669
|
-
const session = asRecord(
|
|
2670
|
-
await readJsonFile(path.join(namespaceDir, "sessions", `${safeExternalId("session", sessionId)}.json`)),
|
|
2671
|
-
);
|
|
2672
2659
|
// An overwrite is a canonical lifecycle repair, not proof that tool activity ended.
|
|
2673
2660
|
const previous = options.overwrite ? null : persisted;
|
|
2674
2661
|
const hasCurrentTurn = Object.hasOwn(options, "currentTurnId");
|
|
@@ -3260,7 +3247,7 @@ export function createCoordinatorMcpServer(options: CoordinatorMcpServerOptions
|
|
|
3260
3247
|
await routerReady;
|
|
3261
3248
|
}
|
|
3262
3249
|
const platform = options.platform ?? process.platform;
|
|
3263
|
-
const loadModelProfiles = services.resolveModelProfiles ??
|
|
3250
|
+
const loadModelProfiles = services.resolveModelProfiles ?? createCoordinatorModelProfileLoader(routerAgentDir);
|
|
3264
3251
|
// Every authoritative projection is scoped by the collision-resistant namespace identity.
|
|
3265
3252
|
const namespaceDir = coordinatorNamespacePath(config);
|
|
3266
3253
|
// The prior human-readable directory is migration input only; it is never authority.
|
|
@@ -5052,6 +5039,58 @@ export function createCoordinatorMcpServer(options: CoordinatorMcpServerOptions
|
|
|
5052
5039
|
const keyDigest = createHash("sha256").update(idempotencyKey).digest("hex");
|
|
5053
5040
|
return path.join(namespaceDir, "idempotency", `${keyDigest}.json`);
|
|
5054
5041
|
}
|
|
5042
|
+
const idempotencyFlights = new Map<string, { requestDigest: string; promise: Promise<Record<string, unknown>> }>();
|
|
5043
|
+
type IdempotencyFlightAdmission =
|
|
5044
|
+
| { kind: "conflict"; response: Record<string, unknown> }
|
|
5045
|
+
| { kind: "joined"; promise: Promise<Record<string, unknown>> }
|
|
5046
|
+
| { kind: "owner"; run: (operation: Promise<Record<string, unknown>>) => Promise<Record<string, unknown>> };
|
|
5047
|
+
function admitIdempotencyFlight(
|
|
5048
|
+
tool: string,
|
|
5049
|
+
idempotencyKey: string,
|
|
5050
|
+
canonicalArgs: Record<string, unknown>,
|
|
5051
|
+
): IdempotencyFlightAdmission {
|
|
5052
|
+
const keyDigest = createHash("sha256").update(idempotencyKey).digest("hex");
|
|
5053
|
+
const requestDigest = createHash("sha256")
|
|
5054
|
+
.update(canonicalJson({ tool, args: canonicalArgs }))
|
|
5055
|
+
.digest("hex");
|
|
5056
|
+
const existingFlight = idempotencyFlights.get(keyDigest);
|
|
5057
|
+
if (existingFlight) {
|
|
5058
|
+
if (existingFlight.requestDigest !== requestDigest)
|
|
5059
|
+
return {
|
|
5060
|
+
kind: "conflict",
|
|
5061
|
+
response: {
|
|
5062
|
+
ok: false,
|
|
5063
|
+
error: { code: "idempotency_conflict", message: "idempotency key was used with a different request" },
|
|
5064
|
+
},
|
|
5065
|
+
};
|
|
5066
|
+
return { kind: "joined", promise: existingFlight.promise };
|
|
5067
|
+
}
|
|
5068
|
+
const result = Promise.withResolvers<Record<string, unknown>>();
|
|
5069
|
+
const flight = { requestDigest, promise: result.promise };
|
|
5070
|
+
idempotencyFlights.set(keyDigest, flight);
|
|
5071
|
+
return {
|
|
5072
|
+
kind: "owner",
|
|
5073
|
+
run: async operation => {
|
|
5074
|
+
void operation.then(result.resolve, result.reject);
|
|
5075
|
+
try {
|
|
5076
|
+
return await result.promise;
|
|
5077
|
+
} finally {
|
|
5078
|
+
if (idempotencyFlights.get(keyDigest) === flight) idempotencyFlights.delete(keyDigest);
|
|
5079
|
+
}
|
|
5080
|
+
},
|
|
5081
|
+
};
|
|
5082
|
+
}
|
|
5083
|
+
async function withIdempotencyFlight(
|
|
5084
|
+
tool: string,
|
|
5085
|
+
idempotencyKey: string,
|
|
5086
|
+
canonicalArgs: Record<string, unknown>,
|
|
5087
|
+
operation: () => Promise<Record<string, unknown>>,
|
|
5088
|
+
): Promise<Record<string, unknown>> {
|
|
5089
|
+
const admission = admitIdempotencyFlight(tool, idempotencyKey, canonicalArgs);
|
|
5090
|
+
if (admission.kind === "conflict") return admission.response;
|
|
5091
|
+
if (admission.kind === "joined") return await admission.promise;
|
|
5092
|
+
return await admission.run(operation());
|
|
5093
|
+
}
|
|
5055
5094
|
async function withOrderedSessionStateLocks<T>(
|
|
5056
5095
|
lockFiles: readonly string[],
|
|
5057
5096
|
operation: () => Promise<T>,
|
|
@@ -5083,6 +5122,7 @@ export function createCoordinatorMcpServer(options: CoordinatorMcpServerOptions
|
|
|
5083
5122
|
recoverInProgress = false,
|
|
5084
5123
|
isNonterminal: (response: Record<string, unknown>) => boolean = isRouterRequestAmbiguous,
|
|
5085
5124
|
lockAlreadyHeld = false,
|
|
5125
|
+
flightAlreadyOwned = false,
|
|
5086
5126
|
): Promise<Record<string, unknown>> {
|
|
5087
5127
|
const keyDigest = createHash("sha256").update(idempotencyKey).digest("hex");
|
|
5088
5128
|
const requestDigest = createHash("sha256")
|
|
@@ -5187,7 +5227,11 @@ export function createCoordinatorMcpServer(options: CoordinatorMcpServerOptions
|
|
|
5187
5227
|
});
|
|
5188
5228
|
return response;
|
|
5189
5229
|
};
|
|
5190
|
-
|
|
5230
|
+
const executeWithLock = async (): Promise<Record<string, unknown>> =>
|
|
5231
|
+
lockAlreadyHeld ? await execute() : await withSessionStateLock(lockFile, execute);
|
|
5232
|
+
return flightAlreadyOwned
|
|
5233
|
+
? await executeWithLock()
|
|
5234
|
+
: await withIdempotencyFlight(tool, idempotencyKey, canonicalArgs, executeWithLock);
|
|
5191
5235
|
}
|
|
5192
5236
|
|
|
5193
5237
|
async function brokerSession(
|
|
@@ -5289,11 +5333,28 @@ export function createCoordinatorMcpServer(options: CoordinatorMcpServerOptions
|
|
|
5289
5333
|
sessionId,
|
|
5290
5334
|
);
|
|
5291
5335
|
} catch (closeError) {
|
|
5292
|
-
|
|
5293
|
-
|
|
5294
|
-
|
|
5295
|
-
|
|
5296
|
-
|
|
5336
|
+
let listed: Record<string, unknown>;
|
|
5337
|
+
try {
|
|
5338
|
+
listed = await paginatedBrokerSessionList(cwd, { cwd });
|
|
5339
|
+
} catch (listError) {
|
|
5340
|
+
throw new SdkClientError(
|
|
5341
|
+
UNOBSERVED_COMPENSATION_CODE,
|
|
5342
|
+
`Coordinator creation failed after remote session ${sessionId} was created, and compensation failed.`,
|
|
5343
|
+
{ primary: error, compensation: closeError, list: listError, session_id: sessionId },
|
|
5344
|
+
);
|
|
5345
|
+
}
|
|
5346
|
+
const rows = jsonRecords(Array.isArray(listed.sessions) ? listed.sessions : []);
|
|
5347
|
+
const row = rows.find(candidate => brokerSessionId(candidate) === sessionId);
|
|
5348
|
+
const deadUncertain =
|
|
5349
|
+
row !== undefined &&
|
|
5350
|
+
row.live !== true &&
|
|
5351
|
+
(row.terminalUncertain === true || (row as Record<string, unknown>).terminal_uncertain === true);
|
|
5352
|
+
if (!deadUncertain)
|
|
5353
|
+
throw new SdkClientError(
|
|
5354
|
+
UNOBSERVED_COMPENSATION_CODE,
|
|
5355
|
+
`Coordinator creation failed after remote session ${sessionId} was created, and compensation failed.`,
|
|
5356
|
+
{ primary: error, compensation: closeError, session_id: sessionId },
|
|
5357
|
+
);
|
|
5297
5358
|
}
|
|
5298
5359
|
throw error;
|
|
5299
5360
|
}
|
|
@@ -6721,7 +6782,7 @@ export function createCoordinatorMcpServer(options: CoordinatorMcpServerOptions
|
|
|
6721
6782
|
// any stale pointer directly: once the turn projection is rewritten
|
|
6722
6783
|
// terminal, readActiveTurn() intentionally rejects it and cannot
|
|
6723
6784
|
// discover the legacy active-turn file to clear.
|
|
6724
|
-
await
|
|
6785
|
+
await removeCoordinatorFile(activeTurnFile(namespaceDir, sessionId));
|
|
6725
6786
|
}
|
|
6726
6787
|
// A runtime sidecar can advance a live session while the canonical WAL still
|
|
6727
6788
|
// needs projection repair. Preserve that observed lifecycle state (and its
|
|
@@ -8412,6 +8473,9 @@ export function createCoordinatorMcpServer(options: CoordinatorMcpServerOptions
|
|
|
8412
8473
|
}
|
|
8413
8474
|
if (name === "gjc_coordinator_start_session") {
|
|
8414
8475
|
requireCoordinatorMutation(config, "sessions", args);
|
|
8476
|
+
const suppliedPrompt = Object.hasOwn(args, "prompt");
|
|
8477
|
+
if (suppliedPrompt && (typeof args.prompt !== "string" || args.prompt.trim().length === 0))
|
|
8478
|
+
return { ok: false, error: { code: "invalid_input", message: "Prompt must not be empty." } };
|
|
8415
8479
|
const idempotencyKey = requiredIdempotencyKey(args);
|
|
8416
8480
|
const cwd = await canonicalBrokerWorkspace(await assertCoordinatorWorkdir(config, args.cwd));
|
|
8417
8481
|
const mpresetResolution = await resolveCoordinatorMpreset(args.mpreset, loadModelProfiles);
|
|
@@ -8434,7 +8498,7 @@ export function createCoordinatorMcpServer(options: CoordinatorMcpServerOptions
|
|
|
8434
8498
|
error: { code: "unknown_model", message: modelResolution.error },
|
|
8435
8499
|
};
|
|
8436
8500
|
}
|
|
8437
|
-
const prompt =
|
|
8501
|
+
const prompt = suppliedPrompt ? (args.prompt as string) : null;
|
|
8438
8502
|
/**
|
|
8439
8503
|
* A prepared session is deliberately not ready for input: its readiness
|
|
8440
8504
|
* is withheld until an operator-supplied thread is bound and activation
|
|
@@ -8785,7 +8849,10 @@ export function createCoordinatorMcpServer(options: CoordinatorMcpServerOptions
|
|
|
8785
8849
|
code === "live_session"
|
|
8786
8850
|
);
|
|
8787
8851
|
};
|
|
8788
|
-
|
|
8852
|
+
const flight = admitIdempotencyFlight(name, retirementKey, canonicalArgs);
|
|
8853
|
+
if (flight.kind === "conflict") return flight.response;
|
|
8854
|
+
if (flight.kind === "joined") return await flight.promise;
|
|
8855
|
+
const retirementOperation = withOrderedSessionStateLocks(
|
|
8789
8856
|
[idempotencyLockFile(creationKey), idempotencyLockFile(retirementKey)],
|
|
8790
8857
|
async () =>
|
|
8791
8858
|
await withToolIdempotency(
|
|
@@ -8969,8 +9036,10 @@ export function createCoordinatorMcpServer(options: CoordinatorMcpServerOptions
|
|
|
8969
9036
|
true,
|
|
8970
9037
|
isRetirementRetryable,
|
|
8971
9038
|
true,
|
|
9039
|
+
true,
|
|
8972
9040
|
),
|
|
8973
9041
|
);
|
|
9042
|
+
return await flight.run(retirementOperation);
|
|
8974
9043
|
}
|
|
8975
9044
|
if (name === "gjc_coordinator_activate_session") {
|
|
8976
9045
|
requireCoordinatorMutation(config, "sessions", args);
|
|
@@ -9079,10 +9148,10 @@ export function createCoordinatorMcpServer(options: CoordinatorMcpServerOptions
|
|
|
9079
9148
|
}
|
|
9080
9149
|
if (name === "gjc_coordinator_send_prompt") {
|
|
9081
9150
|
requireCoordinatorMutation(config, "sessions", args);
|
|
9151
|
+
if (typeof args.prompt !== "string" || args.prompt.trim().length === 0)
|
|
9152
|
+
return { ok: false, error: { code: "invalid_input", message: "Prompt must not be empty." } };
|
|
9082
9153
|
const idempotencyKey = requiredIdempotencyKey(args);
|
|
9083
9154
|
const sessionId = safeExternalId("session", args.session_id);
|
|
9084
|
-
if (typeof args.prompt !== "string" || args.prompt.length === 0)
|
|
9085
|
-
return { ok: false, error: { code: "invalid_input", message: "prompt is required" } };
|
|
9086
9155
|
const prompt = args.prompt;
|
|
9087
9156
|
return await withToolIdempotency(
|
|
9088
9157
|
name,
|
|
@@ -100,9 +100,15 @@ export function createSessionReaper(deps: SessionReaperDeps, policy: SessionReap
|
|
|
100
100
|
function schedule(gen: number): void {
|
|
101
101
|
timer = setTimeout(() => {
|
|
102
102
|
if (gen !== generation) return; // stale timer from a prior start()
|
|
103
|
-
void sweepOnce()
|
|
104
|
-
|
|
105
|
-
|
|
103
|
+
void sweepOnce()
|
|
104
|
+
.catch(error => {
|
|
105
|
+
// A refused sweep (e.g. a fail-closed capped projection scan) must not
|
|
106
|
+
// become an unhandled rejection; log and keep the schedule alive.
|
|
107
|
+
logger.warn(`session-reaper: sweep refused: ${error instanceof Error ? error.message : String(error)}`);
|
|
108
|
+
})
|
|
109
|
+
.finally(() => {
|
|
110
|
+
if (gen === generation) schedule(gen);
|
|
111
|
+
});
|
|
106
112
|
}, sweepIntervalMs);
|
|
107
113
|
// The reaper must never keep the coordinator process alive by itself.
|
|
108
114
|
(timer as { unref?: () => void }).unref?.();
|
package/src/crash/index-store.ts
CHANGED
|
@@ -613,6 +613,7 @@ export function applyCrashEvent(index: CrashIndex, event: CrashEvent, now: numbe
|
|
|
613
613
|
}
|
|
614
614
|
|
|
615
615
|
// occurrence
|
|
616
|
+
if (event.provenance === "eval" || event.provenance === "bun_test") return false;
|
|
616
617
|
// Clear before deduplication: a replayed occurrence after main-index publish
|
|
617
618
|
// but before retirement-sidecar publish must still supersede a stale tombstone.
|
|
618
619
|
index.retiredFingerprints = index.retiredFingerprints.filter(fingerprint => fingerprint !== event.fingerprint);
|
|
@@ -698,6 +699,7 @@ async function recoverAndRecomputeRetainedCounts(index: CrashIndex, crashLogPath
|
|
|
698
699
|
const recordsByFingerprint = new Map<string, LoadedCrashRecord[]>();
|
|
699
700
|
const seenRecordIds = new Set<string>();
|
|
700
701
|
for (const record of parseRecoverableCrashRecords(contents)) {
|
|
702
|
+
if (record.provenance === "eval" || record.provenance === "bun_test") continue;
|
|
701
703
|
if (seenRecordIds.has(record.recordId)) continue;
|
|
702
704
|
seenRecordIds.add(record.recordId);
|
|
703
705
|
const records = recordsByFingerprint.get(record.fingerprint) ?? [];
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
CRASH_FINGERPRINT_VERSION,
|
|
16
16
|
CRASH_RECORD_MARKER,
|
|
17
17
|
type CrashFingerprint,
|
|
18
|
+
type CrashProvenance,
|
|
18
19
|
computeCrashFingerprint,
|
|
19
20
|
parseCrashRecordMarker,
|
|
20
21
|
} from "@gajae-code/utils";
|
|
@@ -33,6 +34,7 @@ export interface LoadedCrashRecord {
|
|
|
33
34
|
readonly body: string;
|
|
34
35
|
/** The `Name: message` part of the header line. */
|
|
35
36
|
readonly headline: string;
|
|
37
|
+
readonly provenance?: CrashProvenance;
|
|
36
38
|
}
|
|
37
39
|
|
|
38
40
|
interface ParsedCrashRecord {
|
|
@@ -78,6 +80,7 @@ function parseCandidates(contents: string): ParsedCrashRecord[] {
|
|
|
78
80
|
let at = 0;
|
|
79
81
|
let buffer: string[] = [];
|
|
80
82
|
let started = false;
|
|
83
|
+
let provenance: CrashProvenance | undefined;
|
|
81
84
|
let pending: ParsedCrashRecord | undefined;
|
|
82
85
|
for (const line of contents.split("\n")) {
|
|
83
86
|
if (pending) {
|
|
@@ -92,6 +95,12 @@ function parseCandidates(contents: string): ParsedCrashRecord[] {
|
|
|
92
95
|
continue;
|
|
93
96
|
}
|
|
94
97
|
headline = header[2] ?? "";
|
|
98
|
+
const label = line.slice(line.indexOf("[") + 1, line.indexOf("]"));
|
|
99
|
+
const provenanceValue = label
|
|
100
|
+
.split(";")
|
|
101
|
+
.find(part => part.startsWith("provenance="))
|
|
102
|
+
?.slice(11);
|
|
103
|
+
provenance = provenanceValue === "eval" || provenanceValue === "bun_test" ? provenanceValue : undefined;
|
|
95
104
|
at = parsedAt;
|
|
96
105
|
buffer = [];
|
|
97
106
|
started = true;
|
|
@@ -116,6 +125,7 @@ function parseCandidates(contents: string): ParsedCrashRecord[] {
|
|
|
116
125
|
messageClass: fingerprint?.messageClass ?? "",
|
|
117
126
|
body: lines.join("\n").trimEnd(),
|
|
118
127
|
headline,
|
|
128
|
+
...(provenance === undefined ? {} : { provenance }),
|
|
119
129
|
},
|
|
120
130
|
complete: false,
|
|
121
131
|
bound: marker.version === CRASH_FINGERPRINT_VERSION && fingerprint !== undefined,
|
|
@@ -345,7 +345,6 @@ class ConcreteExtensionAPI implements ExtensionAPI {
|
|
|
345
345
|
onQueuedPromoted?: (promotion: { startsOwnRun?: boolean; removed?: boolean }) => void;
|
|
346
346
|
onDispatchDisposition?: (promotion: { startsOwnRun: boolean }) => void;
|
|
347
347
|
preflightSignal?: AbortSignal;
|
|
348
|
-
sdkRunToken?: string;
|
|
349
348
|
},
|
|
350
349
|
): Promise<void> {
|
|
351
350
|
return Promise.resolve(this.runtime.sendUserMessage(content, options));
|
|
@@ -536,7 +536,7 @@ export interface ExtensionContext {
|
|
|
536
536
|
onPreflightAcceptCommit?: () => void | Promise<void>;
|
|
537
537
|
onSkillPrepared?: (meta: { name: string; path: string; lineCount?: number; cleanedArgs?: string }) => void;
|
|
538
538
|
preflightSignal?: AbortSignal;
|
|
539
|
-
|
|
539
|
+
sdkRunCapability?: unknown;
|
|
540
540
|
},
|
|
541
541
|
): Promise<unknown>;
|
|
542
542
|
setPlanMode?(on: boolean): unknown;
|
|
@@ -1316,7 +1316,6 @@ export interface ExtensionAPI {
|
|
|
1316
1316
|
onDispatchDisposition?: (promotion: { startsOwnRun: boolean }) => void;
|
|
1317
1317
|
preflightSignal?: AbortSignal;
|
|
1318
1318
|
/** Internal SDK correlation owner for an exact queued follow-up. */
|
|
1319
|
-
sdkRunToken?: string;
|
|
1320
1319
|
},
|
|
1321
1320
|
): Promise<void>;
|
|
1322
1321
|
|
|
@@ -1546,7 +1545,6 @@ export type SendUserMessageHandler = (
|
|
|
1546
1545
|
onQueuedPromoted?: (promotion: { startsOwnRun?: boolean; removed?: boolean }) => void;
|
|
1547
1546
|
preflightSignal?: AbortSignal;
|
|
1548
1547
|
/** Internal SDK correlation owner for an exact queued follow-up. */
|
|
1549
|
-
sdkRunToken?: string;
|
|
1550
1548
|
},
|
|
1551
1549
|
) => void | Promise<void>;
|
|
1552
1550
|
|
|
@@ -1708,6 +1706,7 @@ export interface ExtensionContextActions {
|
|
|
1708
1706
|
onPreflightAcceptCommit?: () => void | Promise<void>;
|
|
1709
1707
|
onSkillPrepared?: (meta: { name: string; path: string; lineCount?: number; cleanedArgs?: string }) => void;
|
|
1710
1708
|
preflightSignal?: AbortSignal;
|
|
1709
|
+
sdkRunCapability?: unknown;
|
|
1711
1710
|
},
|
|
1712
1711
|
) => Promise<unknown>;
|
|
1713
1712
|
setPlanMode?: (on: boolean) => unknown;
|
|
@@ -453,6 +453,7 @@ export async function buildPluginMcpConfigs(input: { cwd: string }): Promise<{
|
|
|
453
453
|
command: cfg.command,
|
|
454
454
|
args: cfg.args,
|
|
455
455
|
cwd: cfg.cwd ? nodePath.resolve(entry.pluginRoot, cfg.cwd) : entry.pluginRoot,
|
|
456
|
+
timeout: 5_000,
|
|
456
457
|
// Third-party plugin MCP processes must not inherit host secrets;
|
|
457
458
|
// only a minimal OS allowlist (PATH/HOME/temp/locale) is provided.
|
|
458
459
|
noInheritEnv: true,
|