@gajae-code/coding-agent 0.15.6 → 0.16.1
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 +99 -0
- package/dist/types/async/job-manager.d.ts +32 -1
- package/dist/types/browser-backend.d.ts +12 -0
- package/dist/types/cli/args.d.ts +2 -0
- package/dist/types/cli/root-flags.d.ts +14 -0
- package/dist/types/cli/update-cli.d.ts +14 -0
- package/dist/types/cli.d.ts +7 -0
- package/dist/types/commands/launch.d.ts +10 -0
- package/dist/types/commands/sdk.d.ts +2 -0
- package/dist/types/config/autorouting-tier-map.d.ts +24 -0
- package/dist/types/config/billing-path.d.ts +25 -0
- package/dist/types/config/config-schema-version.d.ts +13 -1
- package/dist/types/config/file-lock.d.ts +5 -0
- package/dist/types/config/model-registry.d.ts +22 -0
- package/dist/types/config/settings-schema.d.ts +27 -8
- package/dist/types/config/settings.d.ts +2 -0
- package/dist/types/coordinator-mcp/projection-scan.d.ts +8 -0
- package/dist/types/crash/record-loader.d.ts +15 -0
- package/dist/types/extensibility/extensions/types.d.ts +8 -3
- package/dist/types/gjc-runtime/launch-worktree-reservation.d.ts +21 -0
- package/dist/types/gjc-runtime/launch-worktree.d.ts +25 -1
- package/dist/types/gjc-runtime/managed-owner-supervisor.d.ts +2 -0
- package/dist/types/gjc-runtime/session-state-lock.d.ts +22 -0
- package/dist/types/gjc-runtime/session-state-sidecar.d.ts +66 -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/main.d.ts +1 -0
- 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/acp/acp-agent.d.ts +21 -21
- package/dist/types/modes/components/frictionless-onboarding-selector.d.ts +10 -0
- package/dist/types/modes/components/model-selector.d.ts +4 -0
- package/dist/types/modes/components/oauth-selector.d.ts +1 -0
- package/dist/types/modes/components/session-selector.d.ts +1 -1
- package/dist/types/modes/components/sessions-dashboard.d.ts +2 -1
- 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/interactive-mode.d.ts +16 -2
- package/dist/types/modes/print-mode.d.ts +17 -0
- package/dist/types/modes/theme/theme.d.ts +2 -1
- package/dist/types/modes/types.d.ts +2 -0
- package/dist/types/modes/ui-language.d.ts +7 -1
- package/dist/types/sdk/broker/broker.d.ts +79 -2
- package/dist/types/sdk/broker/endpoint-authority.d.ts +24 -0
- package/dist/types/sdk/broker/ensure.d.ts +3 -0
- package/dist/types/sdk/broker/index.d.ts +3 -0
- package/dist/types/sdk/broker/lifecycle-ledger.d.ts +6 -0
- package/dist/types/sdk/broker/lifecycle.d.ts +40 -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/startup-budget.d.ts +52 -0
- package/dist/types/sdk/broker/state-version.d.ts +21 -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/discord-daemon.d.ts +1 -1
- package/dist/types/sdk/bus/index.d.ts +9 -1
- package/dist/types/sdk/bus/kind-aware-reconciliation.d.ts +1 -1
- package/dist/types/sdk/bus/prompt-reconciliation.d.ts +1 -1
- package/dist/types/sdk/bus/slack-conversation.d.ts +1 -1
- package/dist/types/sdk/bus/slack-daemon.d.ts +1 -1
- 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 +12 -5
- package/dist/types/sdk/cli/session-cli.d.ts +20 -3
- package/dist/types/sdk/client/client.d.ts +3 -2
- 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/events.d.ts +1 -0
- package/dist/types/sdk/host/host.d.ts +10 -0
- package/dist/types/sdk/host/query/cursor.d.ts +4 -2
- package/dist/types/sdk/host/query/handlers.d.ts +1 -0
- package/dist/types/sdk/host/query/revision-store.d.ts +1 -0
- package/dist/types/sdk/host/session-runtime.d.ts +70 -17
- 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 +3 -1
- package/dist/types/sdk/prompt-deadline-manager.d.ts +2 -5
- package/dist/types/sdk/receipt-state.d.ts +2 -0
- package/dist/types/sdk/router/session-router.d.ts +6 -0
- package/dist/types/sdk/session.d.ts +16 -0
- package/dist/types/sdk/startup-capability.d.ts +2 -1
- package/dist/types/sdk/turn-result.d.ts +6 -0
- package/dist/types/session/agent-session.d.ts +52 -7
- package/dist/types/session/credential-store-errors.d.ts +1 -0
- package/dist/types/session/fold-coordinator.d.ts +29 -2
- package/dist/types/session/session-manager.d.ts +79 -2
- package/dist/types/session/yield-queue.d.ts +5 -4
- package/dist/types/setup/frictionless-onboarding.d.ts +1 -0
- package/dist/types/tools/ask.d.ts +1 -0
- package/dist/types/tools/bash.d.ts +16 -0
- package/dist/types/tools/browser/launch.d.ts +10 -0
- package/dist/types/tools/browser/registry.d.ts +8 -0
- package/dist/types/tools/gh.d.ts +8 -0
- package/dist/types/tools/index.d.ts +10 -10
- package/dist/types/tools/job.d.ts +3 -1
- package/dist/types/utils/external-editor.d.ts +7 -0
- package/examples/extensions/README.md +1 -1
- package/examples/extensions/pirate.ts +7 -4
- package/package.json +7 -7
- package/scripts/generate-sdk-operation-inventory.ts +13 -1
- package/scripts/verify-gjc-sdk-canonicalization.ts +35 -1
- package/src/async/job-manager.ts +107 -9
- package/src/browser-backend.ts +29 -0
- package/src/cli/accounts-cli.ts +9 -5
- package/src/cli/args.ts +17 -0
- package/src/cli/fast-help.ts +1 -0
- package/src/cli/plugin-cli.ts +29 -0
- package/src/cli/root-flags.ts +5 -0
- package/src/cli/update-cli.ts +140 -23
- package/src/commands/launch.ts +86 -18
- package/src/commands/sdk.ts +135 -28
- package/src/config/autorouting-tier-map.ts +12 -0
- package/src/config/billing-path.ts +42 -0
- package/src/config/config-schema-version.ts +18 -1
- package/src/config/file-lock.ts +158 -83
- package/src/config/model-preset-registry.ts +178 -23
- package/src/config/model-profiles.ts +5 -5
- package/src/config/model-registry.ts +291 -42
- package/src/config/model-resolver.ts +5 -0
- package/src/config/settings-schema.ts +27 -8
- package/src/config/settings.ts +51 -15
- package/src/coordinator-mcp/projection-scan.ts +27 -6
- package/src/coordinator-mcp/question-state.ts +9 -11
- package/src/coordinator-mcp/server.ts +115 -21
- package/src/crash/index-store.ts +2 -0
- package/src/crash/record-loader.ts +10 -0
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +1 -1
- package/src/extensibility/extensions/runner.ts +27 -0
- package/src/extensibility/extensions/types.ts +8 -3
- package/src/extensibility/gjc-plugins/installer.ts +51 -11
- package/src/extensibility/runtime-skill-discovery.ts +110 -3
- package/src/extensibility/skills.ts +22 -12
- package/src/gjc-runtime/launch-worktree-reservation.ts +242 -0
- package/src/gjc-runtime/launch-worktree.ts +285 -4
- package/src/gjc-runtime/managed-owner-supervisor.ts +63 -51
- package/src/gjc-runtime/session-state-lock.ts +213 -14
- package/src/gjc-runtime/session-state-sidecar.ts +938 -39
- package/src/gjc-runtime/tmux-owner-isolation-cli.ts +4 -7
- package/src/gjc-runtime/tmux-owner-isolation.ts +18 -3
- package/src/gjc-runtime/tmux-sessions.ts +252 -13
- package/src/hooks/skill-state.ts +6 -0
- package/src/internal-urls/docs-index.generated.ts +15 -15
- package/src/internal-urls/issue-pr-protocol.ts +4 -2
- package/src/main.ts +61 -3
- 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 +610 -161
- package/src/modes/components/footer.ts +4 -2
- package/src/modes/components/frictionless-onboarding-selector.ts +48 -2
- package/src/modes/components/model-selector.ts +76 -8
- package/src/modes/components/oauth-selector.ts +6 -1
- package/src/modes/components/session-selector.ts +23 -15
- package/src/modes/components/sessions-dashboard.ts +8 -5
- 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 +23 -9
- package/src/modes/components/tool-status-header.ts +151 -15
- package/src/modes/controllers/command-controller.ts +1 -1
- package/src/modes/controllers/event-controller.ts +30 -4
- package/src/modes/controllers/extension-ui-controller.ts +13 -13
- package/src/modes/controllers/input-controller.ts +30 -2
- package/src/modes/controllers/selector-controller.ts +200 -98
- package/src/modes/interactive-mode.ts +36 -12
- package/src/modes/print-mode.ts +64 -0
- package/src/modes/prompt-action-autocomplete.ts +30 -4
- package/src/modes/runtime-init.ts +12 -9
- package/src/modes/theme/theme.ts +5 -0
- package/src/modes/types.ts +2 -0
- package/src/modes/ui-language.ts +49 -2
- package/src/modes/utils/ui-helpers.ts +22 -3
- package/src/prompts/agent-fragments/browser-aside.md +22 -0
- package/src/prompts/system/master-mode.md +28 -0
- package/src/prompts/tools/ask.md +1 -1
- package/src/prompts/tools/bash.md +1 -0
- package/src/prompts/tools/job.md +1 -1
- package/src/sdk/acp/adapter.ts +5 -0
- package/src/sdk/broker/broker.ts +1646 -27
- package/src/sdk/broker/endpoint-authority.ts +86 -0
- package/src/sdk/broker/ensure.ts +81 -3
- package/src/sdk/broker/index.ts +3 -0
- package/src/sdk/broker/lifecycle-ledger.ts +6 -0
- package/src/sdk/broker/lifecycle.ts +440 -75
- 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 +591 -0
- package/src/sdk/broker/startup-budget.ts +144 -2
- package/src/sdk/broker/state-version.ts +43 -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 +136 -18
- package/src/sdk/bus/discord-daemon.ts +50 -10
- package/src/sdk/bus/index.ts +84 -7
- package/src/sdk/bus/kind-aware-reconciliation.ts +296 -48
- package/src/sdk/bus/lifecycle-commands.ts +4 -0
- package/src/sdk/bus/notification-service.ts +22 -5
- package/src/sdk/bus/prompt-reconciliation.ts +7 -2
- package/src/sdk/bus/reconciliation-store.ts +55 -31
- package/src/sdk/bus/slack-conversation.ts +1 -1
- package/src/sdk/bus/slack-daemon.ts +72 -38
- 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 +41 -15
- package/src/sdk/cli/session-cli.ts +297 -26
- package/src/sdk/client/client.ts +14 -8
- package/src/sdk/host/control/dispatch.ts +101 -40
- 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/events.ts +1 -0
- package/src/sdk/host/host.ts +29 -2
- package/src/sdk/host/query/cursor.ts +52 -7
- package/src/sdk/host/query/handlers.ts +33 -12
- package/src/sdk/host/query/revision-store.ts +12 -0
- package/src/sdk/host/session-runtime.test.ts +673 -14
- package/src/sdk/host/session-runtime.ts +674 -103
- package/src/sdk/host/surface-policy.ts +3 -3
- package/src/sdk/lifecycle/broker-client.ts +28 -0
- package/src/sdk/lifecycle/service.ts +279 -20
- package/src/sdk/lifecycle-session.ts +17 -4
- package/src/sdk/prompt-deadline-manager.ts +2 -1
- package/src/sdk/protocol/operation-inventory.generated.json +105 -3
- package/src/sdk/protocol/operation-registry.ts +14 -4
- package/src/sdk/receipt-state.ts +10 -0
- package/src/sdk/router/session-router.ts +219 -118
- package/src/sdk/session.ts +416 -195
- package/src/sdk/startup-capability.ts +1 -0
- package/src/sdk/turn-result.ts +22 -0
- package/src/session/agent-session.ts +1628 -318
- package/src/session/blob-store.ts +15 -5
- package/src/session/credential-store-errors.ts +2 -0
- package/src/session/fold-coordinator.ts +40 -3
- package/src/session/internal/managed-session-storage.ts +153 -78
- package/src/session/session-manager.ts +850 -97
- package/src/session/yield-queue.ts +24 -10
- package/src/setup/frictionless-onboarding.ts +2 -0
- package/src/slash-commands/builtin-registry.ts +41 -0
- package/src/task/executor.ts +9 -4
- package/src/telemetry/events.ts +1138 -12
- package/src/tools/ask-contract.test.ts +64 -0
- package/src/tools/ask-contract.ts +36 -1
- package/src/tools/ask.ts +10 -8
- package/src/tools/bash-interactive.ts +11 -2
- package/src/tools/bash.ts +412 -75
- package/src/tools/browser/launch.ts +37 -0
- package/src/tools/browser/registry.ts +99 -9
- package/src/tools/cron.ts +6 -4
- package/src/tools/descriptors.test.ts +15 -0
- package/src/tools/descriptors.ts +5 -1
- package/src/tools/gh.ts +104 -48
- package/src/tools/github-cache.ts +2 -2
- package/src/tools/index.ts +10 -6
- package/src/tools/job.ts +4 -1
- package/src/tools/monitor.ts +12 -9
- package/src/tools/resolve.ts +30 -10
- package/src/tools/tool-catalog.generated.ts +2 -2
- package/src/utils/external-editor.ts +72 -1
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local interactive-master CLI surface.
|
|
3
|
+
*
|
|
4
|
+
* `gjc sdk spawn` is legal only inside a live master session: the master's Bash
|
|
5
|
+
* tool threads the transient capability only through a strict direct spawn
|
|
6
|
+
* command, and the Broker still verifies it against the live effective host
|
|
7
|
+
* before any effect.
|
|
8
|
+
* No output path may echo the task or capability.
|
|
9
|
+
*/
|
|
10
|
+
import { randomUUID } from "node:crypto";
|
|
11
|
+
import * as path from "node:path";
|
|
12
|
+
import { getAgentDir } from "@gajae-code/utils";
|
|
13
|
+
import { type IndexedSession, isSessionAuthorityEligible, SessionIndex } from "../broker/session-index";
|
|
14
|
+
import { SdkClientError } from "../client";
|
|
15
|
+
import { dispatchSpawnGlobal } from "../lifecycle/broker-client";
|
|
16
|
+
|
|
17
|
+
const MASTER_CAPABILITY_ENV = "GJC_MASTER_CAPABILITY";
|
|
18
|
+
const MASTER_SESSION_ENV = "GJC_SESSION_ID";
|
|
19
|
+
const SPAWN_TIMEOUT_MS = 120_000;
|
|
20
|
+
|
|
21
|
+
export class SdkMasterCliError extends Error {
|
|
22
|
+
constructor(
|
|
23
|
+
readonly code: string,
|
|
24
|
+
message: string,
|
|
25
|
+
readonly exitCode: 1 | 2 = 1,
|
|
26
|
+
) {
|
|
27
|
+
super(message);
|
|
28
|
+
this.name = "SdkMasterCliError";
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface SdkSpawnArgs {
|
|
33
|
+
cwd?: string;
|
|
34
|
+
prompt?: string;
|
|
35
|
+
model?: string;
|
|
36
|
+
profile?: string;
|
|
37
|
+
agentDir?: string;
|
|
38
|
+
idempotencyKey?: string;
|
|
39
|
+
json?: boolean;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** The allowlisted spawn result projection rendered to the operator. */
|
|
43
|
+
export interface SdkSpawnRendered {
|
|
44
|
+
code: string;
|
|
45
|
+
claimId?: string;
|
|
46
|
+
sessionId?: string;
|
|
47
|
+
substrateKind?: string;
|
|
48
|
+
seed?: { phase?: string; clientRef?: string; commandId?: string; turnId?: string; status?: string };
|
|
49
|
+
idempotencyKey?: string;
|
|
50
|
+
error?: { code: string; message: string };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface SdkSpawnDependencies {
|
|
54
|
+
env: Record<string, string | undefined>;
|
|
55
|
+
dispatch: (agentDir: string, input: Record<string, unknown>, idempotencyKey: string) => Promise<unknown>;
|
|
56
|
+
resolveAttestationEpoch: (agentDir: string, ownerSessionId: string) => Promise<string | undefined>;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async function brokerSpawnDispatch(
|
|
60
|
+
agentDir: string,
|
|
61
|
+
input: Record<string, unknown>,
|
|
62
|
+
idempotencyKey: string,
|
|
63
|
+
): Promise<unknown> {
|
|
64
|
+
return await dispatchSpawnGlobal(agentDir, input, idempotencyKey, SPAWN_TIMEOUT_MS);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Select the newest retained master attestation for an owner session.
|
|
69
|
+
*
|
|
70
|
+
* `listSessionIdentities()` can retain both the direct-role row and one or
|
|
71
|
+
* more endpoint generations. Its iteration order is a projection detail, not
|
|
72
|
+
* authority ordering, so selecting the first matching row can resurrect an
|
|
73
|
+
* older epoch after a master relaunch. The broker's monotonically increasing
|
|
74
|
+
* `indexSeq` is the authoritative retained ordering.
|
|
75
|
+
*/
|
|
76
|
+
export function selectNewestMasterAttestationEpoch(
|
|
77
|
+
rows: readonly IndexedSession[],
|
|
78
|
+
ownerSessionId: string,
|
|
79
|
+
): string | undefined {
|
|
80
|
+
const processIdentity = (row: IndexedSession): string | undefined => row.hostIncarnation ?? row.processIncarnation;
|
|
81
|
+
const matchesAttestation = (
|
|
82
|
+
left: NonNullable<IndexedSession["masterRole"]>,
|
|
83
|
+
right: NonNullable<IndexedSession["masterRole"]>,
|
|
84
|
+
) =>
|
|
85
|
+
left.version === right.version &&
|
|
86
|
+
left.ownerSessionId === right.ownerSessionId &&
|
|
87
|
+
left.launchPid === right.launchPid &&
|
|
88
|
+
left.launchProcessIncarnation === right.launchProcessIncarnation &&
|
|
89
|
+
left.role === right.role &&
|
|
90
|
+
left.attestationEpoch === right.attestationEpoch;
|
|
91
|
+
const hasDirectAttestation = (effective: IndexedSession): boolean =>
|
|
92
|
+
rows.some(
|
|
93
|
+
row =>
|
|
94
|
+
row.sessionId === effective.sessionId &&
|
|
95
|
+
row.endpointGeneration === 0 &&
|
|
96
|
+
row.pid === effective.masterRole?.launchPid &&
|
|
97
|
+
processIdentity(row) === effective.masterRole?.launchProcessIncarnation &&
|
|
98
|
+
row.masterRole !== undefined &&
|
|
99
|
+
effective.masterRole !== undefined &&
|
|
100
|
+
matchesAttestation(row.masterRole, effective.masterRole),
|
|
101
|
+
);
|
|
102
|
+
let newest: { indexSeq: number; epoch: string } | undefined;
|
|
103
|
+
for (const row of rows) {
|
|
104
|
+
const attestation = row.masterRole;
|
|
105
|
+
if (
|
|
106
|
+
!Number.isSafeInteger(row.indexSeq) ||
|
|
107
|
+
attestation === undefined ||
|
|
108
|
+
attestation.version !== 2 ||
|
|
109
|
+
attestation.role !== "master" ||
|
|
110
|
+
attestation.ownerSessionId !== ownerSessionId ||
|
|
111
|
+
row.endpointGeneration <= 0 ||
|
|
112
|
+
row.live !== true ||
|
|
113
|
+
row.terminal ||
|
|
114
|
+
row.terminalUncertain ||
|
|
115
|
+
!isSessionAuthorityEligible(row) ||
|
|
116
|
+
attestation.launchPid !== row.pid ||
|
|
117
|
+
attestation.launchProcessIncarnation !== processIdentity(row) ||
|
|
118
|
+
!hasDirectAttestation(row) ||
|
|
119
|
+
typeof attestation.attestationEpoch !== "string" ||
|
|
120
|
+
attestation.attestationEpoch.length === 0 ||
|
|
121
|
+
attestation.attestationEpoch.length > 512 ||
|
|
122
|
+
!/^[A-Za-z0-9_-]+$/u.test(attestation.attestationEpoch)
|
|
123
|
+
)
|
|
124
|
+
continue;
|
|
125
|
+
if (newest === undefined || row.indexSeq > newest.indexSeq)
|
|
126
|
+
newest = { indexSeq: row.indexSeq, epoch: attestation.attestationEpoch };
|
|
127
|
+
}
|
|
128
|
+
return newest?.epoch;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
async function indexAttestationEpoch(agentDir: string, ownerSessionId: string): Promise<string | undefined> {
|
|
132
|
+
const index = await new SessionIndex(agentDir).open();
|
|
133
|
+
return selectNewestMasterAttestationEpoch(index.listSessionIdentities(), ownerSessionId);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function record(value: unknown): Record<string, unknown> | undefined {
|
|
137
|
+
return value !== null && typeof value === "object" && !Array.isArray(value)
|
|
138
|
+
? (value as Record<string, unknown>)
|
|
139
|
+
: undefined;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function opaque(value: unknown): string | undefined {
|
|
143
|
+
return typeof value === "string" && value.length > 0 && value.length <= 512 ? value : undefined;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/** Projects an arbitrary Broker response onto the allowlisted render shape. */
|
|
147
|
+
export function safeSpawnRender(response: unknown): { rendered: SdkSpawnRendered; exitCode: 0 | 1 } {
|
|
148
|
+
const outer = record(response);
|
|
149
|
+
if (outer?.ok === true) {
|
|
150
|
+
const result = record(outer.result);
|
|
151
|
+
const seed = record(result?.seed);
|
|
152
|
+
return {
|
|
153
|
+
rendered: {
|
|
154
|
+
code: opaque(result?.code) ?? "spawn_accepted",
|
|
155
|
+
...(opaque(result?.claimId) === undefined ? {} : { claimId: opaque(result?.claimId) }),
|
|
156
|
+
...(opaque(result?.sessionId) === undefined ? {} : { sessionId: opaque(result?.sessionId) }),
|
|
157
|
+
...(opaque(result?.substrateKind) === undefined ? {} : { substrateKind: opaque(result?.substrateKind) }),
|
|
158
|
+
...(seed === undefined
|
|
159
|
+
? {}
|
|
160
|
+
: {
|
|
161
|
+
seed: {
|
|
162
|
+
...(opaque(seed.phase) === undefined ? {} : { phase: opaque(seed.phase) }),
|
|
163
|
+
...(opaque(seed.clientRef) === undefined ? {} : { clientRef: opaque(seed.clientRef) }),
|
|
164
|
+
...(opaque(seed.commandId) === undefined ? {} : { commandId: opaque(seed.commandId) }),
|
|
165
|
+
...(opaque(seed.turnId) === undefined ? {} : { turnId: opaque(seed.turnId) }),
|
|
166
|
+
...(opaque(seed.status) === undefined ? {} : { status: opaque(seed.status) }),
|
|
167
|
+
},
|
|
168
|
+
}),
|
|
169
|
+
},
|
|
170
|
+
exitCode: 0,
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
const error = record(outer?.error);
|
|
174
|
+
const code = opaque(error?.code) ?? "spawn_failed";
|
|
175
|
+
const retryIdempotencyKey = code === "uncertain_after_send" ? opaque(error?.idempotencyKey) : undefined;
|
|
176
|
+
// Error messages are Broker-typed and never quote request input; an uncertain
|
|
177
|
+
// post-send result may additionally expose its non-secret replay key.
|
|
178
|
+
const message =
|
|
179
|
+
typeof error?.message === "string" && error.message.length <= 512 ? error.message : "session.spawn failed";
|
|
180
|
+
return {
|
|
181
|
+
rendered: {
|
|
182
|
+
code,
|
|
183
|
+
...(retryIdempotencyKey === undefined ? {} : { idempotencyKey: retryIdempotencyKey }),
|
|
184
|
+
error: { code, message },
|
|
185
|
+
},
|
|
186
|
+
exitCode: 1,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export function renderSpawnTable(rendered: SdkSpawnRendered): string {
|
|
191
|
+
const lines = [`Result: ${rendered.code}`];
|
|
192
|
+
if (rendered.claimId) lines.push(`Claim: ${rendered.claimId}`);
|
|
193
|
+
if (rendered.sessionId) lines.push(`Child session: ${rendered.sessionId}`);
|
|
194
|
+
if (rendered.substrateKind) lines.push(`Substrate: ${rendered.substrateKind}`);
|
|
195
|
+
if (rendered.seed?.phase) lines.push(`Seed phase: ${rendered.seed.phase}`);
|
|
196
|
+
if (rendered.seed?.status) lines.push(`Seed status: ${rendered.seed.status}`);
|
|
197
|
+
if (rendered.error) lines.push(`Error: ${rendered.error.code}: ${rendered.error.message}`);
|
|
198
|
+
if (rendered.idempotencyKey) lines.push(`Retry idempotency key: ${rendered.idempotencyKey}`);
|
|
199
|
+
|
|
200
|
+
return lines.join("\n");
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Runs one local master spawn. A caller-provided idempotency key replays the
|
|
205
|
+
* matching durable claim; otherwise each invocation receives a fresh key.
|
|
206
|
+
*/
|
|
207
|
+
export async function runSdkSpawn(
|
|
208
|
+
args: SdkSpawnArgs,
|
|
209
|
+
dependencies: Partial<SdkSpawnDependencies> = {},
|
|
210
|
+
): Promise<{ rendered: SdkSpawnRendered; exitCode: 0 | 1 }> {
|
|
211
|
+
const deps: SdkSpawnDependencies = {
|
|
212
|
+
env: dependencies.env ?? process.env,
|
|
213
|
+
dispatch: dependencies.dispatch ?? brokerSpawnDispatch,
|
|
214
|
+
resolveAttestationEpoch: dependencies.resolveAttestationEpoch ?? indexAttestationEpoch,
|
|
215
|
+
};
|
|
216
|
+
if (!args.cwd) throw new SdkMasterCliError("invalid_input", "sdk spawn requires --cwd <dir>.", 2);
|
|
217
|
+
if (!args.prompt) throw new SdkMasterCliError("invalid_input", "sdk spawn requires --prompt <task>.", 2);
|
|
218
|
+
const capability = deps.env[MASTER_CAPABILITY_ENV];
|
|
219
|
+
const ownerSessionId = deps.env[MASTER_SESSION_ENV];
|
|
220
|
+
if (!capability || !ownerSessionId)
|
|
221
|
+
throw new SdkMasterCliError(
|
|
222
|
+
"master_context_required",
|
|
223
|
+
"sdk spawn is available only inside a live interactive master session.",
|
|
224
|
+
1,
|
|
225
|
+
);
|
|
226
|
+
const agentDir = args.agentDir ?? getAgentDir();
|
|
227
|
+
const attestationEpoch = await deps.resolveAttestationEpoch(agentDir, ownerSessionId);
|
|
228
|
+
if (!attestationEpoch)
|
|
229
|
+
throw new SdkMasterCliError(
|
|
230
|
+
"master_context_required",
|
|
231
|
+
"No master role attestation exists for this session; relaunch with gjc --master.",
|
|
232
|
+
1,
|
|
233
|
+
);
|
|
234
|
+
const idempotencyKey = args.idempotencyKey ?? randomUUID();
|
|
235
|
+
try {
|
|
236
|
+
const response = await deps.dispatch(
|
|
237
|
+
agentDir,
|
|
238
|
+
{
|
|
239
|
+
task: args.prompt,
|
|
240
|
+
masterCapability: capability,
|
|
241
|
+
ownerSessionId,
|
|
242
|
+
attestationEpoch,
|
|
243
|
+
cwd: args.cwd === undefined ? undefined : path.resolve(args.cwd),
|
|
244
|
+
...(args.model === undefined ? {} : { modelId: args.model }),
|
|
245
|
+
...(args.profile === undefined ? {} : { modelPreset: args.profile }),
|
|
246
|
+
},
|
|
247
|
+
idempotencyKey,
|
|
248
|
+
);
|
|
249
|
+
return safeSpawnRender(response);
|
|
250
|
+
} catch (error) {
|
|
251
|
+
if (error instanceof SdkClientError && error.code === "uncertain_after_send") {
|
|
252
|
+
return safeSpawnRender({
|
|
253
|
+
ok: false,
|
|
254
|
+
error: { code: error.code, message: error.message, idempotencyKey },
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
throw error;
|
|
258
|
+
}
|
|
259
|
+
}
|
package/src/sdk/cli/rows.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Versioned DTO rows for `gjc sdk session` semantic verbs (row DTO
|
|
2
|
+
* Versioned DTO rows for `gjc sdk session` semantic verbs (row DTO v2, DR-12).
|
|
3
3
|
*
|
|
4
4
|
* Every semantic verb renders its output through these rows so machine
|
|
5
5
|
* consumers get one deterministic, versioned envelope per verb. Credentials
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* rendering.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
export const SESSION_ROWS_VERSION =
|
|
11
|
+
export const SESSION_ROWS_VERSION = 2;
|
|
12
12
|
|
|
13
13
|
export type SdkSessionActivityState = "active" | "idle";
|
|
14
14
|
|
|
@@ -19,7 +19,7 @@ export interface SdkSessionActivityV1 {
|
|
|
19
19
|
|
|
20
20
|
export interface SdkSessionRowV1 {
|
|
21
21
|
sessionId: string;
|
|
22
|
-
locator: {
|
|
22
|
+
locator: { cwd: string; worktreeRoot: string | null; stateRoot: string };
|
|
23
23
|
endpointGeneration: number;
|
|
24
24
|
/** Process-bound incarnation of the hosting process (C1); absent for legacy-provenance rows. */
|
|
25
25
|
hostIncarnation?: string;
|
|
@@ -41,16 +41,20 @@ export interface SdkSessionRowV1 {
|
|
|
41
41
|
ambiguous?: boolean;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
export interface
|
|
44
|
+
export interface SdkRingPositionV1 {
|
|
45
45
|
revision: number;
|
|
46
46
|
generation: number;
|
|
47
47
|
seq: number;
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
+
export interface SdkCheckpointRecordV1 extends SdkRingPositionV1 {
|
|
51
|
+
idle: boolean;
|
|
52
|
+
}
|
|
53
|
+
|
|
50
54
|
export interface SdkRetentionGapV1 {
|
|
51
55
|
code: "retention_gap";
|
|
52
56
|
missing?: { from: number; to: number };
|
|
53
|
-
resync?:
|
|
57
|
+
resync?: SdkRingPositionV1;
|
|
54
58
|
}
|
|
55
59
|
|
|
56
60
|
/** One tail item: a retained transcript entry or a live event-ring event. */
|
|
@@ -58,6 +62,8 @@ export interface SdkTailItemV1 {
|
|
|
58
62
|
/** "transcript" for retained transcript entries, otherwise the event-ring kind. */
|
|
59
63
|
kind: string;
|
|
60
64
|
id?: string;
|
|
65
|
+
/** Durable revision at emission; (revision,generation,seq) is unique per session. generation:seq alone is revision-local. */
|
|
66
|
+
revision?: number;
|
|
61
67
|
generation?: number;
|
|
62
68
|
seq?: number;
|
|
63
69
|
payload: unknown;
|
|
@@ -98,12 +104,15 @@ function optionalFiniteNumber(value: unknown): number | undefined {
|
|
|
98
104
|
return typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
99
105
|
}
|
|
100
106
|
|
|
101
|
-
function locatorOf(row: Record<string, unknown>): {
|
|
107
|
+
function locatorOf(row: Record<string, unknown>): { cwd: string; worktreeRoot: string | null; stateRoot: string } {
|
|
102
108
|
const locator = isRecord(row.locator) ? row.locator : {};
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
109
|
+
const cwd = optionalString(locator.cwd);
|
|
110
|
+
const stateRoot = optionalString(locator.stateRoot);
|
|
111
|
+
const worktreeRoot = locator.worktreeRoot;
|
|
112
|
+
if (!cwd || !stateRoot || (worktreeRoot !== null && optionalString(worktreeRoot) === undefined)) {
|
|
113
|
+
throw new Error("session row has an invalid locator v2");
|
|
114
|
+
}
|
|
115
|
+
return { cwd, worktreeRoot: worktreeRoot === null ? null : optionalString(worktreeRoot)!, stateRoot };
|
|
107
116
|
}
|
|
108
117
|
|
|
109
118
|
function activityOf(row: Record<string, unknown>): SdkSessionActivityV1 | undefined {
|
|
@@ -162,6 +171,7 @@ export function toCheckpointRecordV1(value: unknown): SdkCheckpointRecordV1 | un
|
|
|
162
171
|
const revision = value.revision;
|
|
163
172
|
const generation = value.generation;
|
|
164
173
|
const seq = value.seq;
|
|
174
|
+
const idle = value.idle;
|
|
165
175
|
if (
|
|
166
176
|
typeof revision !== "number" ||
|
|
167
177
|
!Number.isSafeInteger(revision) ||
|
|
@@ -171,10 +181,11 @@ export function toCheckpointRecordV1(value: unknown): SdkCheckpointRecordV1 | un
|
|
|
171
181
|
generation < 0 ||
|
|
172
182
|
typeof seq !== "number" ||
|
|
173
183
|
!Number.isSafeInteger(seq) ||
|
|
174
|
-
seq < 0
|
|
184
|
+
seq < 0 ||
|
|
185
|
+
typeof idle !== "boolean"
|
|
175
186
|
)
|
|
176
187
|
return undefined;
|
|
177
|
-
return { revision, generation, seq };
|
|
188
|
+
return { revision, generation, seq, idle };
|
|
178
189
|
}
|
|
179
190
|
|
|
180
191
|
export function toRetentionGapV1(value: unknown): SdkRetentionGapV1 | undefined {
|
|
@@ -186,15 +197,28 @@ export function toRetentionGapV1(value: unknown): SdkRetentionGapV1 | undefined
|
|
|
186
197
|
if (typeof from === "number" && Number.isSafeInteger(from) && typeof to === "number" && Number.isSafeInteger(to))
|
|
187
198
|
gap.missing = { from, to };
|
|
188
199
|
}
|
|
189
|
-
|
|
190
|
-
|
|
200
|
+
if (isRecord(value.resync)) {
|
|
201
|
+
const { revision, generation, seq } = value.resync;
|
|
202
|
+
if (
|
|
203
|
+
typeof revision === "number" &&
|
|
204
|
+
Number.isSafeInteger(revision) &&
|
|
205
|
+
revision >= 0 &&
|
|
206
|
+
typeof generation === "number" &&
|
|
207
|
+
Number.isSafeInteger(generation) &&
|
|
208
|
+
generation >= 0 &&
|
|
209
|
+
typeof seq === "number" &&
|
|
210
|
+
Number.isSafeInteger(seq) &&
|
|
211
|
+
seq >= 0
|
|
212
|
+
)
|
|
213
|
+
gap.resync = { revision, generation, seq };
|
|
214
|
+
}
|
|
191
215
|
return gap;
|
|
192
216
|
}
|
|
193
217
|
|
|
194
218
|
/** Projects a transcript entry or ring event into a tail item with dedupe keys. */
|
|
195
219
|
export function toTailItemV1(
|
|
196
220
|
value: unknown,
|
|
197
|
-
fallback: { kind: string; generation?: number; seq?: number },
|
|
221
|
+
fallback: { kind: string; revision?: number; generation?: number; seq?: number },
|
|
198
222
|
): SdkTailItemV1 {
|
|
199
223
|
if (!isRecord(value)) return { kind: fallback.kind, payload: stripSecretFields(value) };
|
|
200
224
|
const kind = optionalString(value.kind) ?? fallback.kind;
|
|
@@ -204,10 +228,12 @@ export function toTailItemV1(
|
|
|
204
228
|
const item: SdkTailItemV1 = {
|
|
205
229
|
kind,
|
|
206
230
|
...(payloadId !== undefined ? { id: payloadId } : id !== undefined ? { id } : {}),
|
|
231
|
+
...(typeof value.revision === "number" ? { revision: value.revision } : {}),
|
|
207
232
|
...(typeof value.generation === "number" ? { generation: value.generation } : {}),
|
|
208
233
|
...(typeof value.seq === "number" ? { seq: value.seq } : {}),
|
|
209
234
|
payload: stripSecretFields(payload),
|
|
210
235
|
};
|
|
236
|
+
if (fallback.revision !== undefined && item.revision === undefined) item.revision = fallback.revision;
|
|
211
237
|
if (fallback.generation !== undefined && item.generation === undefined) item.generation = fallback.generation;
|
|
212
238
|
if (fallback.seq !== undefined && item.seq === undefined) item.seq = fallback.seq;
|
|
213
239
|
return item;
|