@gajae-code/coding-agent 0.11.3 → 0.11.5
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 +47 -0
- package/dist/types/cli/notify-cli.d.ts +5 -1
- package/dist/types/cli/worktree-cli.d.ts +8 -25
- package/dist/types/commands/gc.d.ts +4 -0
- package/dist/types/commands/worktree.d.ts +7 -32
- package/dist/types/config/settings-schema.d.ts +20 -0
- package/dist/types/export/html/template.generated.d.ts +1 -1
- package/dist/types/gjc-runtime/deep-interview-state.d.ts +28 -0
- package/dist/types/gjc-runtime/gc-runtime.d.ts +9 -0
- package/dist/types/gjc-runtime/launch-tmux.d.ts +3 -0
- package/dist/types/gjc-runtime/managed-owner-admission.d.ts +42 -0
- package/dist/types/gjc-runtime/managed-owner-supervisor.d.ts +41 -0
- package/dist/types/gjc-runtime/state-argv.d.ts +38 -0
- package/dist/types/gjc-runtime/tmux-owner-isolation.d.ts +9 -0
- package/dist/types/gjc-runtime/ultragoal-owner-loss-recovery.d.ts +94 -0
- package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +1 -0
- package/dist/types/harness-control-plane/owner.d.ts +3 -1
- package/dist/types/internal-urls/local-protocol.d.ts +18 -0
- package/dist/types/main.d.ts +2 -2
- package/dist/types/modes/components/ansi-display-validator.d.ts +2 -0
- package/dist/types/modes/components/custom-editor.d.ts +8 -3
- package/dist/types/modes/components/notifications-settings-editor.d.ts +4 -0
- package/dist/types/modes/components/session-observer-overlay.d.ts +3 -1
- package/dist/types/modes/components/settings-selector.d.ts +2 -0
- package/dist/types/modes/components/tool-transcript-format.d.ts +77 -0
- package/dist/types/modes/components/transcript-viewer-overlay.d.ts +3 -0
- package/dist/types/modes/controllers/goal-mode-controller.d.ts +8 -1
- package/dist/types/modes/controllers/input-controller.d.ts +8 -2
- package/dist/types/modes/controllers/selector-controller.d.ts +15 -0
- package/dist/types/modes/shared/agent-wire/workflow-gate-broker.d.ts +3 -0
- package/dist/types/modes/shared/agent-wire/workflow-gate-types.d.ts +16 -0
- package/dist/types/plan-mode/approved-plan.d.ts +1 -0
- package/dist/types/runtime-mcp/manager.d.ts +2 -0
- package/dist/types/runtime-mcp/oauth-discovery.d.ts +12 -1
- package/dist/types/sdk/broker/broker.d.ts +1 -0
- package/dist/types/sdk/broker/discovery.d.ts +10 -0
- package/dist/types/sdk/broker/ensure.d.ts +24 -1
- package/dist/types/sdk/broker/internal.d.ts +3 -0
- package/dist/types/sdk/broker/session-index.d.ts +12 -0
- package/dist/types/sdk/broker/state-version.d.ts +2 -1
- package/dist/types/sdk/bus/chat-daemon-cli.d.ts +3 -1
- package/dist/types/sdk/bus/chat-daemon-control.d.ts +39 -4
- package/dist/types/sdk/bus/config-commands.d.ts +8 -0
- package/dist/types/sdk/bus/config.d.ts +22 -0
- package/dist/types/sdk/bus/index.d.ts +162 -2
- package/dist/types/sdk/bus/notification-service.d.ts +71 -8
- package/dist/types/sdk/bus/session-control.d.ts +11 -8
- package/dist/types/sdk/bus/telegram-daemon-cli.d.ts +5 -0
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +6 -3
- package/dist/types/sdk/bus/telegram-daemon-control.d.ts +23 -4
- package/dist/types/sdk/bus/telegram-daemon.d.ts +214 -20
- package/dist/types/sdk/session.d.ts +16 -1
- package/dist/types/secrets/index.d.ts +4 -2
- package/dist/types/secrets/obfuscator.d.ts +1 -1
- package/dist/types/session/agent-session.d.ts +31 -2
- package/dist/types/session/artifacts.d.ts +30 -8
- package/dist/types/session/internal/managed-session-scope.d.ts +7 -0
- package/dist/types/session/internal/managed-session-storage.d.ts +63 -6
- package/dist/types/session/session-manager.d.ts +88 -18
- package/dist/types/session/session-storage.d.ts +27 -0
- package/dist/types/session/streaming-output.d.ts +4 -0
- package/dist/types/task/executor.d.ts +9 -0
- package/dist/types/tool-discovery/tool-index.d.ts +2 -0
- package/dist/types/tools/ask.d.ts +87 -12
- package/dist/types/tools/bash-skill-urls.d.ts +2 -2
- package/dist/types/tools/browser/screenshot-format.d.ts +8 -0
- package/dist/types/tools/browser/tab-protocol.d.ts +11 -0
- package/dist/types/tools/browser/tab-supervisor.d.ts +1 -1
- package/dist/types/tools/browser/tab-worker-path-resolver.d.ts +2 -0
- package/dist/types/tools/browser/tab-worker-smoke.d.ts +16 -0
- package/dist/types/tools/index.d.ts +4 -0
- package/dist/types/tools/path-utils.d.ts +9 -14
- package/dist/types/tools/render-utils.d.ts +1 -8
- package/dist/types/tools/resource-gc.d.ts +14 -0
- package/dist/types/utils/image-loading.d.ts +23 -0
- package/dist/types/utils/image-resize.d.ts +2 -0
- package/dist/types/utils/pasted-image-loading.d.ts +39 -0
- package/dist/types/utils/pasted-image-path.d.ts +21 -19
- package/dist/types/web/scrapers/html-to-markdown.d.ts +5 -0
- package/dist/types/web/scrapers/types.d.ts +1 -5
- package/package.json +25 -9
- package/scripts/generate-sdk-operation-inventory.ts +2 -0
- package/scripts/run-threat-model-check.ts +60 -0
- package/src/cli/notify-cli.ts +40 -18
- package/src/cli/worktree-cli.ts +9 -287
- package/src/cli.ts +10 -9
- package/src/commands/gc.ts +11 -1
- package/src/commands/sdk.ts +10 -4
- package/src/commands/worktree.ts +10 -53
- package/src/config/model-profiles.ts +15 -15
- package/src/config/settings-schema.ts +17 -0
- package/src/daemon/runtime.ts +2 -2
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +70 -49
- package/src/defaults/gjc/skills/ralplan/SKILL.md +0 -6
- package/src/defaults/gjc/skills/team/SKILL.md +7 -9
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +0 -2
- package/src/eval/js/shared/runtime.ts +1 -1
- package/src/eval/py/index.ts +2 -2
- package/src/export/html/template.generated.ts +1 -1
- package/src/extensibility/plugins/marketplace/manager.ts +94 -10
- package/src/gjc-runtime/deep-interview-recorder.ts +7 -0
- package/src/gjc-runtime/deep-interview-runtime.ts +10 -0
- package/src/gjc-runtime/deep-interview-state.ts +127 -3
- package/src/gjc-runtime/gc-render.ts +19 -1
- package/src/gjc-runtime/gc-runtime.ts +68 -3
- package/src/gjc-runtime/launch-tmux.ts +61 -6
- package/src/gjc-runtime/managed-owner-admission.ts +352 -0
- package/src/gjc-runtime/managed-owner-supervisor.ts +162 -0
- package/src/gjc-runtime/state-argv.ts +292 -0
- package/src/gjc-runtime/state-runtime.ts +80 -222
- package/src/gjc-runtime/team-runtime.ts +1 -1
- package/src/gjc-runtime/tmux-owner-isolation.ts +117 -0
- package/src/gjc-runtime/ultragoal-owner-loss-recovery.ts +328 -0
- package/src/gjc-runtime/ultragoal-runtime.ts +14 -0
- package/src/harness-control-plane/owner.ts +59 -3
- package/src/hindsight/client.ts +65 -2
- package/src/internal-urls/agent-protocol.ts +76 -5
- package/src/internal-urls/docs-index.generated.ts +7 -6
- package/src/internal-urls/local-protocol.ts +416 -18
- package/src/lsp/index.ts +4 -88
- package/src/main.ts +37 -13
- package/src/modes/acp/acp-agent.ts +8 -0
- package/src/modes/components/ansi-display-validator.ts +54 -0
- package/src/modes/components/custom-editor.ts +103 -32
- package/src/modes/components/custom-provider-wizard.ts +26 -1
- package/src/modes/components/notifications-settings-editor.ts +26 -2
- package/src/modes/components/session-observer-overlay.ts +40 -37
- package/src/modes/components/settings-selector.ts +3 -1
- package/src/modes/components/tool-transcript-format.ts +393 -0
- package/src/modes/components/transcript-viewer-overlay.ts +63 -1
- package/src/modes/controllers/goal-mode-controller.ts +20 -1
- package/src/modes/controllers/input-controller.ts +98 -37
- package/src/modes/controllers/plan-mode-controller.ts +2 -0
- package/src/modes/controllers/runtime-mcp-command-controller.ts +3 -0
- package/src/modes/controllers/selector-controller.ts +71 -9
- package/src/modes/interactive-mode.ts +14 -2
- package/src/modes/shared/agent-wire/deep-interview-gate.ts +31 -1
- package/src/modes/shared/agent-wire/workflow-gate-broker.ts +17 -1
- package/src/modes/shared/agent-wire/workflow-gate-schema.ts +4 -2
- package/src/modes/shared/agent-wire/workflow-gate-types.ts +120 -3
- package/src/plan-mode/approved-plan.ts +3 -1
- package/src/prompts/agents/architect.md +1 -0
- package/src/prompts/agents/critic.md +1 -0
- package/src/prompts/system/custom-system-prompt.md +1 -1
- package/src/prompts/system/system-prompt.md +2 -1
- package/src/prompts/tools/lsp.md +3 -3
- package/src/runtime-mcp/manager.ts +16 -1
- package/src/runtime-mcp/oauth-discovery.ts +116 -26
- package/src/sdk/broker/broker.ts +139 -32
- package/src/sdk/broker/discovery.ts +125 -0
- package/src/sdk/broker/ensure.ts +103 -19
- package/src/sdk/broker/internal.ts +10 -0
- package/src/sdk/broker/session-index.ts +309 -38
- package/src/sdk/broker/state-version.ts +5 -2
- package/src/sdk/bus/chat-daemon-cli.ts +72 -10
- package/src/sdk/bus/chat-daemon-control.ts +526 -130
- package/src/sdk/bus/chat-daemon-runtime.ts +1 -0
- package/src/sdk/bus/config-commands.ts +22 -0
- package/src/sdk/bus/config.ts +37 -0
- package/src/sdk/bus/index.ts +384 -92
- package/src/sdk/bus/lifecycle-control-runtime.ts +53 -3
- package/src/sdk/bus/notification-service.ts +284 -62
- package/src/sdk/bus/recent-activity.ts +14 -5
- package/src/sdk/bus/session-control.ts +150 -44
- package/src/sdk/bus/telegram-daemon-cli.ts +20 -14
- package/src/sdk/bus/telegram-daemon-contract.ts +6 -3
- package/src/sdk/bus/telegram-daemon-control.ts +294 -76
- package/src/sdk/bus/telegram-daemon.ts +1842 -281
- package/src/sdk/protocol/operation-inventory.generated.json +11 -0
- package/src/sdk/session.ts +155 -27
- package/src/secrets/index.ts +53 -10
- package/src/secrets/obfuscator.ts +21 -18
- package/src/secrets/regex.ts +2 -0
- package/src/session/agent-session.ts +495 -253
- package/src/session/artifacts.ts +188 -23
- package/src/session/context-estimation.ts +7 -2
- package/src/session/internal/managed-session-scope.ts +215 -39
- package/src/session/internal/managed-session-storage.ts +1152 -46
- package/src/session/session-manager.ts +1055 -148
- package/src/session/session-storage.ts +109 -11
- package/src/session/streaming-output.ts +6 -0
- package/src/task/executor.ts +71 -26
- package/src/task/index.ts +63 -20
- package/src/tool-discovery/tool-index.ts +18 -0
- package/src/tools/ask.ts +329 -57
- package/src/tools/bash-allowed-prefixes.ts +79 -29
- package/src/tools/bash-skill-urls.ts +14 -3
- package/src/tools/bash.ts +2 -2
- package/src/tools/browser/launch.ts +3 -1
- package/src/tools/browser/readable.ts +1 -1
- package/src/tools/browser/screenshot-format.ts +34 -0
- package/src/tools/browser/tab-protocol.ts +3 -0
- package/src/tools/browser/tab-supervisor.ts +125 -122
- package/src/tools/browser/tab-worker-entry.ts +33 -14
- package/src/tools/browser/tab-worker-path-resolver.ts +67 -0
- package/src/tools/browser/tab-worker-smoke.ts +87 -0
- package/src/tools/browser/tab-worker.ts +7 -5
- package/src/tools/index.ts +4 -0
- package/src/tools/path-utils.ts +42 -35
- package/src/tools/plan-mode-guard.ts +1 -0
- package/src/tools/read.ts +7 -1
- package/src/tools/render-utils.ts +1 -27
- package/src/tools/resource-gc.ts +123 -34
- package/src/tools/sqlite-reader.ts +3 -2
- package/src/tools/subagent.ts +2 -0
- package/src/utils/image-loading.ts +85 -34
- package/src/utils/image-resize.ts +16 -9
- package/src/utils/pasted-image-loading.ts +514 -0
- package/src/utils/pasted-image-path.ts +223 -98
- package/src/web/scrapers/html-to-markdown.ts +68 -0
- package/src/web/scrapers/readthedocs.ts +1 -1
- package/src/web/scrapers/types.ts +1 -68
- package/src/web/search/providers/openai-compatible.ts +76 -23
|
@@ -29,9 +29,13 @@ import {
|
|
|
29
29
|
import { renderCliWriteReceipt } from "./cli-write-receipt";
|
|
30
30
|
import { applyAmbiguityFloorToEnvelope } from "./deep-interview-ambiguity";
|
|
31
31
|
import {
|
|
32
|
+
assertDeepInterviewEnvelopeInputLimits,
|
|
33
|
+
assertDeepInterviewInputWithinLimit,
|
|
32
34
|
assertDeepInterviewIntentManifest,
|
|
33
35
|
assertDeepInterviewIntentReview,
|
|
36
|
+
assertDeepInterviewStructuredResponseWithinLimit,
|
|
34
37
|
type DeepInterviewIntentManifest,
|
|
38
|
+
MAX_DEEP_INTERVIEW_STRUCTURED_RESPONSE_LENGTH,
|
|
35
39
|
mergeDeepInterviewEnvelope,
|
|
36
40
|
normalizeDeepInterviewEnvelope,
|
|
37
41
|
} from "./deep-interview-state";
|
|
@@ -42,6 +46,7 @@ import {
|
|
|
42
46
|
SessionResolutionError,
|
|
43
47
|
writeSessionActivityMarker,
|
|
44
48
|
} from "./session-resolution";
|
|
49
|
+
import { classifyStateArgv, firstStateFlagValue, type StateAction, type StateArgvClassification } from "./state-argv";
|
|
45
50
|
import { renderStateGraph, type StateGraphFormat } from "./state-graph";
|
|
46
51
|
import { migrateAndPersistLegacyState, migrateWorkflowState } from "./state-migrations";
|
|
47
52
|
import {
|
|
@@ -72,7 +77,7 @@ import {
|
|
|
72
77
|
writeGuardedWorkflowEnvelopeAtomic,
|
|
73
78
|
} from "./state-writer";
|
|
74
79
|
import { assertSafePathComponent, CommandError, flagValue, hasFlag, isPlainObject } from "./workflow-cli-common";
|
|
75
|
-
import { getSkillManifest, isKnownWorkflowState, isValidTransition
|
|
80
|
+
import { getSkillManifest, isKnownWorkflowState, isValidTransition } from "./workflow-manifest";
|
|
76
81
|
|
|
77
82
|
/**
|
|
78
83
|
* Native implementation of the `gjc state read|write|clear` command surface.
|
|
@@ -99,136 +104,9 @@ class StateCommandError extends CommandError {
|
|
|
99
104
|
}
|
|
100
105
|
|
|
101
106
|
const GRAPH_FORMATS = new Set(["ascii", "mermaid", "dot"]);
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
"--session-id",
|
|
106
|
-
"--thread-id",
|
|
107
|
-
"--turn-id",
|
|
108
|
-
"--to",
|
|
109
|
-
"--skill",
|
|
110
|
-
"--format",
|
|
111
|
-
"--older-than",
|
|
112
|
-
"--status",
|
|
113
|
-
"--fields",
|
|
114
|
-
"--since",
|
|
115
|
-
"--limit",
|
|
116
|
-
]);
|
|
117
|
-
const ACTION_NAMES = new Set([
|
|
118
|
-
"read",
|
|
119
|
-
"write",
|
|
120
|
-
"clear",
|
|
121
|
-
"contract",
|
|
122
|
-
"handoff",
|
|
123
|
-
"graph",
|
|
124
|
-
"prune",
|
|
125
|
-
"gc",
|
|
126
|
-
"migrate",
|
|
127
|
-
"status",
|
|
128
|
-
"doctor",
|
|
129
|
-
]);
|
|
130
|
-
const BOOLEAN_FLAGS = new Set([
|
|
131
|
-
"--json",
|
|
132
|
-
"--replace",
|
|
133
|
-
"--hard",
|
|
134
|
-
"--dry-run",
|
|
135
|
-
"--migrate",
|
|
136
|
-
"--compact",
|
|
137
|
-
"--history",
|
|
138
|
-
"--force",
|
|
139
|
-
]);
|
|
140
|
-
const VERB_SPECIFIC_FLAGS = new Set([
|
|
141
|
-
"--skill",
|
|
142
|
-
"--format",
|
|
143
|
-
"--older-than",
|
|
144
|
-
"--status",
|
|
145
|
-
"--fields",
|
|
146
|
-
"--since",
|
|
147
|
-
"--limit",
|
|
148
|
-
"--history",
|
|
149
|
-
]);
|
|
150
|
-
|
|
151
|
-
function flagName(arg: string): string | undefined {
|
|
152
|
-
if (!arg.startsWith("--")) return undefined;
|
|
153
|
-
const equalsIndex = arg.indexOf("=");
|
|
154
|
-
return equalsIndex >= 0 ? arg.slice(0, equalsIndex) : arg;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
function manifestFlagNames(action: ParsedInvocation["action"], positionalSkill: string | undefined): Set<string> {
|
|
158
|
-
const names = new Set<string>();
|
|
159
|
-
const skills =
|
|
160
|
-
positionalSkill && KNOWN_MODES.includes(positionalSkill)
|
|
161
|
-
? [positionalSkill as CanonicalGjcWorkflowSkill]
|
|
162
|
-
: CANONICAL_GJC_WORKFLOW_SKILLS;
|
|
163
|
-
for (const skill of skills) {
|
|
164
|
-
for (const arg of typedArgsFor(skill, action)) names.add(`--${arg.name}`);
|
|
165
|
-
}
|
|
166
|
-
return names;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
function assertKnownFlags(args: readonly string[], parsed: ParsedInvocation): void {
|
|
170
|
-
const manifestFlags = manifestFlagNames(parsed.action, parsed.positionalSkill);
|
|
171
|
-
for (const arg of args) {
|
|
172
|
-
const flag = flagName(arg);
|
|
173
|
-
if (!flag) continue;
|
|
174
|
-
if (
|
|
175
|
-
FLAGS_WITH_VALUES.has(flag) ||
|
|
176
|
-
BOOLEAN_FLAGS.has(flag) ||
|
|
177
|
-
VERB_SPECIFIC_FLAGS.has(flag) ||
|
|
178
|
-
manifestFlags.has(flag)
|
|
179
|
-
) {
|
|
180
|
-
continue;
|
|
181
|
-
}
|
|
182
|
-
throw new StateCommandError(2, `unknown gjc state flag: ${flag}`);
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
interface ParsedInvocation {
|
|
187
|
-
action:
|
|
188
|
-
| "read"
|
|
189
|
-
| "write"
|
|
190
|
-
| "clear"
|
|
191
|
-
| "contract"
|
|
192
|
-
| "handoff"
|
|
193
|
-
| "graph"
|
|
194
|
-
| "prune"
|
|
195
|
-
| "gc"
|
|
196
|
-
| "migrate"
|
|
197
|
-
| "status"
|
|
198
|
-
| "doctor";
|
|
199
|
-
positionalSkill?: string;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
function parsePositionalArgs(args: readonly string[]): ParsedInvocation {
|
|
203
|
-
let skipNext = false;
|
|
204
|
-
const positional: string[] = [];
|
|
205
|
-
for (const arg of args) {
|
|
206
|
-
if (skipNext) {
|
|
207
|
-
skipNext = false;
|
|
208
|
-
continue;
|
|
209
|
-
}
|
|
210
|
-
if (FLAGS_WITH_VALUES.has(arg)) {
|
|
211
|
-
skipNext = true;
|
|
212
|
-
continue;
|
|
213
|
-
}
|
|
214
|
-
if (!arg.startsWith("-")) positional.push(arg);
|
|
215
|
-
}
|
|
216
|
-
// Documented argv shapes:
|
|
217
|
-
// gjc state read|write|clear|contract ...
|
|
218
|
-
// gjc state <skill> read|write|contract ...
|
|
219
|
-
const first = positional[0];
|
|
220
|
-
const second = positional[1];
|
|
221
|
-
if (first && ACTION_NAMES.has(first)) {
|
|
222
|
-
return { action: first as ParsedInvocation["action"], positionalSkill: second };
|
|
223
|
-
}
|
|
224
|
-
if (first && second && ACTION_NAMES.has(second)) {
|
|
225
|
-
return { action: second as ParsedInvocation["action"], positionalSkill: first };
|
|
226
|
-
}
|
|
227
|
-
// `gjc state <skill>` alone defaults to read for that skill.
|
|
228
|
-
if (first && !second) {
|
|
229
|
-
return { action: "read", positionalSkill: first };
|
|
230
|
-
}
|
|
231
|
-
return { action: "read" };
|
|
107
|
+
function assertKnownFlags(classification: StateArgvClassification): void {
|
|
108
|
+
const [unknownFlag] = classification.unknownFlags;
|
|
109
|
+
if (unknownFlag) throw new StateCommandError(2, `unknown gjc state flag: ${unknownFlag}`);
|
|
232
110
|
}
|
|
233
111
|
|
|
234
112
|
function isKnownMode(mode: string): mode is CanonicalGjcWorkflowSkill {
|
|
@@ -279,19 +157,16 @@ interface ResolvedSelectors {
|
|
|
279
157
|
// `clear` resolves like a read (explicit -> payload -> env -> latest-activity marker)
|
|
280
158
|
// per the spec: read/status/clear may fall back to the most-recent session. Commands
|
|
281
159
|
// that create or mutate new state roots still require an explicit/env session id.
|
|
282
|
-
const WRITE_SESSION_ACTIONS = new Set<
|
|
160
|
+
const WRITE_SESSION_ACTIONS = new Set<StateAction>(["write", "handoff", "prune", "migrate"]);
|
|
283
161
|
|
|
284
|
-
async function resolveSelectors(
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
positionalSkill: string | undefined,
|
|
288
|
-
action: ParsedInvocation["action"],
|
|
289
|
-
): Promise<ResolvedSelectors> {
|
|
290
|
-
const payload = await readInputJson(flagValue(args, "--input"), cwd);
|
|
162
|
+
async function resolveSelectors(args: readonly string[], cwd: string, action: StateAction): Promise<ResolvedSelectors> {
|
|
163
|
+
const classification = classifyStateArgv(args);
|
|
164
|
+
const payload = await readInputJson(firstStateFlagValue(classification, "--input"), cwd);
|
|
291
165
|
|
|
166
|
+
const [modeCandidate, positionalCandidate] = classification.runtimeSelectorCandidates;
|
|
292
167
|
const candidates: Array<string | undefined> = [
|
|
293
|
-
|
|
294
|
-
|
|
168
|
+
modeCandidate?.value,
|
|
169
|
+
positionalCandidate?.value,
|
|
295
170
|
typeof payload?.mode === "string" ? (payload.mode as string).trim() || undefined : undefined,
|
|
296
171
|
typeof payload?.skill === "string" ? (payload.skill as string).trim() || undefined : undefined,
|
|
297
172
|
];
|
|
@@ -1121,6 +996,7 @@ async function reconcileWorkflowSkillStateUnlocked(
|
|
|
1121
996
|
): Promise<{ stateFile: string }> {
|
|
1122
997
|
const { cwd, mode, threadId, turnId, active, payload } = options;
|
|
1123
998
|
const filePath = modeStateFile(cwd, mode, sessionId);
|
|
999
|
+
if (mode === "deep-interview") assertDeepInterviewStructuredResponseWithinLimit(payload);
|
|
1124
1000
|
const existingRead = await readExistingStateForMutation(filePath);
|
|
1125
1001
|
const existingPayload = existingRead.kind === "valid" ? existingRead.value : {};
|
|
1126
1002
|
const nowIsoStr = nowIso();
|
|
@@ -1157,6 +1033,7 @@ async function reconcileWorkflowSkillStateUnlocked(
|
|
|
1157
1033
|
unknown
|
|
1158
1034
|
>)
|
|
1159
1035
|
: mergeWithNullDelete(existingPayload, payload);
|
|
1036
|
+
if (mode === "deep-interview") assertDeepInterviewEnvelopeInputLimits(merged);
|
|
1160
1037
|
merged.skill = mode;
|
|
1161
1038
|
merged.current_phase = trimmedPhase;
|
|
1162
1039
|
merged.active = active;
|
|
@@ -1232,12 +1109,8 @@ export async function readWorkflowStateJson(
|
|
|
1232
1109
|
return (await readJsonFile(modeStateFile(cwd, skill, session.gjcSessionId))) ?? {};
|
|
1233
1110
|
}
|
|
1234
1111
|
|
|
1235
|
-
async function handleRead(
|
|
1236
|
-
args
|
|
1237
|
-
cwd: string,
|
|
1238
|
-
positionalSkill: string | undefined,
|
|
1239
|
-
): Promise<StateCommandResult> {
|
|
1240
|
-
const selectors = await resolveSelectors(args, cwd, positionalSkill, "read");
|
|
1112
|
+
async function handleRead(args: readonly string[], cwd: string): Promise<StateCommandResult> {
|
|
1113
|
+
const selectors = await resolveSelectors(args, cwd, "read");
|
|
1241
1114
|
const mode = selectors.mode ?? (await inferModeFromActiveState(cwd, selectors.gjcSessionId));
|
|
1242
1115
|
const fields = parseFieldsFlag(args);
|
|
1243
1116
|
if (mode) {
|
|
@@ -1276,12 +1149,8 @@ async function handleRead(
|
|
|
1276
1149
|
return { status: 0, stdout: `${JSON.stringify(existing ?? {}, null, 2)}\n` };
|
|
1277
1150
|
}
|
|
1278
1151
|
|
|
1279
|
-
async function handleStatus(
|
|
1280
|
-
args
|
|
1281
|
-
cwd: string,
|
|
1282
|
-
positionalSkill: string | undefined,
|
|
1283
|
-
): Promise<StateCommandResult> {
|
|
1284
|
-
const selectors = await resolveSelectors(args, cwd, positionalSkill, "read");
|
|
1152
|
+
async function handleStatus(args: readonly string[], cwd: string): Promise<StateCommandResult> {
|
|
1153
|
+
const selectors = await resolveSelectors(args, cwd, "read");
|
|
1285
1154
|
const mode = selectors.mode ?? (await inferModeFromActiveState(cwd, selectors.gjcSessionId));
|
|
1286
1155
|
if (!mode) {
|
|
1287
1156
|
throw new StateCommandError(
|
|
@@ -1303,12 +1172,8 @@ async function handleStatus(
|
|
|
1303
1172
|
};
|
|
1304
1173
|
}
|
|
1305
1174
|
|
|
1306
|
-
async function handleWrite(
|
|
1307
|
-
args
|
|
1308
|
-
cwd: string,
|
|
1309
|
-
positionalSkill: string | undefined,
|
|
1310
|
-
): Promise<StateCommandResult> {
|
|
1311
|
-
const selectors = await resolveSelectors(args, cwd, positionalSkill, "write");
|
|
1175
|
+
async function handleWrite(args: readonly string[], cwd: string): Promise<StateCommandResult> {
|
|
1176
|
+
const selectors = await resolveSelectors(args, cwd, "write");
|
|
1312
1177
|
const { gjcSessionId: sessionId, threadId, turnId, payload } = selectors;
|
|
1313
1178
|
if (!payload) throw new StateCommandError(2, "gjc state write requires --input '<json>'");
|
|
1314
1179
|
const mode = selectors.mode ?? (await inferModeFromActiveState(cwd, sessionId));
|
|
@@ -1318,6 +1183,13 @@ async function handleWrite(
|
|
|
1318
1183
|
"gjc state write requires --mode <skill>, positional <skill>, input.skill, or an active workflow in the current session active state",
|
|
1319
1184
|
);
|
|
1320
1185
|
|
|
1186
|
+
if (mode === "deep-interview") {
|
|
1187
|
+
try {
|
|
1188
|
+
assertDeepInterviewStructuredResponseWithinLimit(payload);
|
|
1189
|
+
} catch (error) {
|
|
1190
|
+
throw new StateCommandError(2, error instanceof Error ? error.message : String(error));
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1321
1193
|
const filePath = modeStateFile(cwd, mode, sessionId);
|
|
1322
1194
|
const forced = hasFlag(args, "--force");
|
|
1323
1195
|
return await withWorkflowStateLock(
|
|
@@ -1360,6 +1232,11 @@ async function handleWrite(
|
|
|
1360
1232
|
merged = applyAmbiguityFloorToEnvelope(
|
|
1361
1233
|
mergeDeepInterviewEnvelope(existingPayload, payload, { replace: hasFlag(args, "--replace") }),
|
|
1362
1234
|
).envelope;
|
|
1235
|
+
try {
|
|
1236
|
+
assertDeepInterviewEnvelopeInputLimits(merged);
|
|
1237
|
+
} catch (error) {
|
|
1238
|
+
throw new StateCommandError(2, error instanceof Error ? error.message : String(error));
|
|
1239
|
+
}
|
|
1363
1240
|
} else if (hasFlag(args, "--replace")) {
|
|
1364
1241
|
merged = { ...payload };
|
|
1365
1242
|
} else {
|
|
@@ -1465,12 +1342,8 @@ async function handleWrite(
|
|
|
1465
1342
|
);
|
|
1466
1343
|
}
|
|
1467
1344
|
|
|
1468
|
-
async function handleClear(
|
|
1469
|
-
args
|
|
1470
|
-
cwd: string,
|
|
1471
|
-
positionalSkill: string | undefined,
|
|
1472
|
-
): Promise<StateCommandResult> {
|
|
1473
|
-
const selectors = await resolveSelectors(args, cwd, positionalSkill, "clear");
|
|
1345
|
+
async function handleClear(args: readonly string[], cwd: string): Promise<StateCommandResult> {
|
|
1346
|
+
const selectors = await resolveSelectors(args, cwd, "clear");
|
|
1474
1347
|
const { gjcSessionId: sessionId, threadId, turnId } = selectors;
|
|
1475
1348
|
const mode = selectors.mode ?? (await inferModeFromActiveState(cwd, sessionId));
|
|
1476
1349
|
if (!mode)
|
|
@@ -1564,6 +1437,25 @@ async function handleClear(
|
|
|
1564
1437
|
const DEEP_INTERVIEW_INTENT_ID_RE = /(?:artifact|surface|integration|constraint):[a-z0-9][a-z0-9._/-]{0,127}/g;
|
|
1565
1438
|
|
|
1566
1439
|
async function assertDeepInterviewHandoffReady(state: Record<string, unknown>): Promise<void> {
|
|
1440
|
+
const specPath = typeof state.spec_path === "string" ? state.spec_path : undefined;
|
|
1441
|
+
const expectedSha = typeof state.spec_sha256 === "string" ? state.spec_sha256 : undefined;
|
|
1442
|
+
let content: string | undefined;
|
|
1443
|
+
if (specPath) {
|
|
1444
|
+
try {
|
|
1445
|
+
content = await fs.readFile(specPath, "utf-8");
|
|
1446
|
+
} catch (error) {
|
|
1447
|
+
throw new StateCommandError(
|
|
1448
|
+
2,
|
|
1449
|
+
`deep-interview handoff cannot read persisted spec: ${error instanceof Error ? error.message : String(error)}`,
|
|
1450
|
+
);
|
|
1451
|
+
}
|
|
1452
|
+
const boundedContent = content.endsWith("\n") ? content.slice(0, -1) : content;
|
|
1453
|
+
assertDeepInterviewInputWithinLimit(
|
|
1454
|
+
boundedContent,
|
|
1455
|
+
MAX_DEEP_INTERVIEW_STRUCTURED_RESPONSE_LENGTH,
|
|
1456
|
+
"persisted deep-interview spec",
|
|
1457
|
+
);
|
|
1458
|
+
}
|
|
1567
1459
|
const envelope = normalizeDeepInterviewEnvelope(state);
|
|
1568
1460
|
const inner = envelope.state;
|
|
1569
1461
|
if (!inner) return;
|
|
@@ -1573,19 +1465,8 @@ async function assertDeepInterviewHandoffReady(state: Record<string, unknown>):
|
|
|
1573
1465
|
return;
|
|
1574
1466
|
}
|
|
1575
1467
|
assertDeepInterviewIntentManifest(inner.intent_contract);
|
|
1576
|
-
|
|
1577
|
-
const expectedSha = typeof state.spec_sha256 === "string" ? state.spec_sha256 : undefined;
|
|
1578
|
-
if (!specPath || !expectedSha)
|
|
1468
|
+
if (!specPath || !expectedSha || content === undefined)
|
|
1579
1469
|
throw new StateCommandError(2, "deep-interview handoff requires a persisted intent-validated spec");
|
|
1580
|
-
let content: string;
|
|
1581
|
-
try {
|
|
1582
|
-
content = await fs.readFile(specPath, "utf-8");
|
|
1583
|
-
} catch (error) {
|
|
1584
|
-
throw new StateCommandError(
|
|
1585
|
-
2,
|
|
1586
|
-
`deep-interview handoff cannot read persisted spec: ${error instanceof Error ? error.message : String(error)}`,
|
|
1587
|
-
);
|
|
1588
|
-
}
|
|
1589
1470
|
if (createHash("sha256").update(content).digest("hex") !== expectedSha)
|
|
1590
1471
|
throw new StateCommandError(2, "deep-interview handoff spec hash mismatch");
|
|
1591
1472
|
const observedIds = [...new Set(content.match(DEEP_INTERVIEW_INTENT_ID_RE) ?? [])].sort();
|
|
@@ -1631,12 +1512,8 @@ async function assertDeepInterviewHandoffReady(state: Record<string, unknown>):
|
|
|
1631
1512
|
* the phase remains in `skill-active-state.json` until a chain call (or
|
|
1632
1513
|
* explicit `clear`) demotes it.
|
|
1633
1514
|
*/
|
|
1634
|
-
async function handleHandoffUnlocked(
|
|
1635
|
-
args
|
|
1636
|
-
cwd: string,
|
|
1637
|
-
positionalSkill: string | undefined,
|
|
1638
|
-
): Promise<StateCommandResult> {
|
|
1639
|
-
const selectors = await resolveSelectors(args, cwd, positionalSkill, "handoff");
|
|
1515
|
+
async function handleHandoffUnlocked(args: readonly string[], cwd: string): Promise<StateCommandResult> {
|
|
1516
|
+
const selectors = await resolveSelectors(args, cwd, "handoff");
|
|
1640
1517
|
const { gjcSessionId: sessionId, threadId, turnId } = selectors;
|
|
1641
1518
|
const caller = selectors.mode ?? (await inferModeFromActiveState(cwd, sessionId));
|
|
1642
1519
|
if (!caller) {
|
|
@@ -1944,12 +1821,8 @@ async function handleHandoffUnlocked(
|
|
|
1944
1821
|
};
|
|
1945
1822
|
}
|
|
1946
1823
|
|
|
1947
|
-
async function handleHandoff(
|
|
1948
|
-
args
|
|
1949
|
-
cwd: string,
|
|
1950
|
-
positionalSkill: string | undefined,
|
|
1951
|
-
): Promise<StateCommandResult> {
|
|
1952
|
-
const selectors = await resolveSelectors(args, cwd, positionalSkill, "handoff");
|
|
1824
|
+
async function handleHandoff(args: readonly string[], cwd: string): Promise<StateCommandResult> {
|
|
1825
|
+
const selectors = await resolveSelectors(args, cwd, "handoff");
|
|
1953
1826
|
// Serialize concurrent handoffs on a dedicated sentinel lock, NOT on the
|
|
1954
1827
|
// derived `skill-active-state.json` cache. The inner transaction
|
|
1955
1828
|
// (applyHandoffToActiveState / syncSkillActiveState -> rebuildActiveSnapshot)
|
|
@@ -1959,15 +1832,11 @@ async function handleHandoff(
|
|
|
1959
1832
|
// `{ cwd }` so the sentinel resolves against the handoff cwd rather than
|
|
1960
1833
|
// `process.cwd()`.
|
|
1961
1834
|
const handoffLock = path.join(sessionStateDir(cwd, selectors.gjcSessionId), "handoff");
|
|
1962
|
-
return withWorkflowStateLock(handoffLock, async () => handleHandoffUnlocked(args, cwd
|
|
1835
|
+
return withWorkflowStateLock(handoffLock, async () => handleHandoffUnlocked(args, cwd), { cwd });
|
|
1963
1836
|
}
|
|
1964
1837
|
|
|
1965
|
-
async function handleContract(
|
|
1966
|
-
args
|
|
1967
|
-
cwd: string,
|
|
1968
|
-
positionalSkill: string | undefined,
|
|
1969
|
-
): Promise<StateCommandResult> {
|
|
1970
|
-
const { mode } = await resolveSelectors(args, cwd, positionalSkill, "read");
|
|
1838
|
+
async function handleContract(args: readonly string[], cwd: string): Promise<StateCommandResult> {
|
|
1839
|
+
const { mode } = await resolveSelectors(args, cwd, "read");
|
|
1971
1840
|
if (!mode) {
|
|
1972
1841
|
throw new StateCommandError(2, "gjc state contract requires --mode <skill>, positional <skill>, or input.skill");
|
|
1973
1842
|
}
|
|
@@ -2195,12 +2064,8 @@ async function handleGraph(
|
|
|
2195
2064
|
};
|
|
2196
2065
|
}
|
|
2197
2066
|
|
|
2198
|
-
async function handlePrune(
|
|
2199
|
-
args
|
|
2200
|
-
cwd: string,
|
|
2201
|
-
positionalSkill: string | undefined,
|
|
2202
|
-
): Promise<StateCommandResult> {
|
|
2203
|
-
const selectors = await resolveSelectors(args, cwd, positionalSkill, "prune");
|
|
2067
|
+
async function handlePrune(args: readonly string[], cwd: string): Promise<StateCommandResult> {
|
|
2068
|
+
const selectors = await resolveSelectors(args, cwd, "prune");
|
|
2204
2069
|
const mode = selectors.mode ?? (await inferModeFromActiveState(cwd, selectors.gjcSessionId));
|
|
2205
2070
|
if (!mode) {
|
|
2206
2071
|
throw new StateCommandError(
|
|
@@ -2265,12 +2130,8 @@ async function handleGc(
|
|
|
2265
2130
|
return { status: 0, stdout: `${JSON.stringify(summary, null, 2)}\n` };
|
|
2266
2131
|
}
|
|
2267
2132
|
|
|
2268
|
-
async function handleMigrate(
|
|
2269
|
-
args
|
|
2270
|
-
cwd: string,
|
|
2271
|
-
positionalSkill: string | undefined,
|
|
2272
|
-
): Promise<StateCommandResult> {
|
|
2273
|
-
const selectors = await resolveSelectors(args, cwd, positionalSkill, "migrate");
|
|
2133
|
+
async function handleMigrate(args: readonly string[], cwd: string): Promise<StateCommandResult> {
|
|
2134
|
+
const selectors = await resolveSelectors(args, cwd, "migrate");
|
|
2274
2135
|
const mode = selectors.mode ?? (await inferModeFromActiveState(cwd, selectors.gjcSessionId));
|
|
2275
2136
|
if (!mode) {
|
|
2276
2137
|
throw new StateCommandError(
|
|
@@ -2301,34 +2162,31 @@ async function handleMigrate(
|
|
|
2301
2162
|
|
|
2302
2163
|
export async function runNativeStateCommand(args: string[], cwd = process.cwd()): Promise<StateCommandResult> {
|
|
2303
2164
|
try {
|
|
2304
|
-
const parsed =
|
|
2305
|
-
assertKnownFlags(
|
|
2306
|
-
switch (parsed.
|
|
2165
|
+
const parsed = classifyStateArgv(args);
|
|
2166
|
+
assertKnownFlags(parsed);
|
|
2167
|
+
switch (parsed.effectiveAction) {
|
|
2307
2168
|
case "read":
|
|
2308
|
-
|
|
2309
|
-
return await handleRead(args, cwd, parsed.positionalSkill);
|
|
2169
|
+
return await handleRead(args, cwd);
|
|
2310
2170
|
case "write":
|
|
2311
|
-
return await handleWrite(args, cwd
|
|
2171
|
+
return await handleWrite(args, cwd);
|
|
2312
2172
|
case "clear":
|
|
2313
|
-
return await handleClear(args, cwd
|
|
2173
|
+
return await handleClear(args, cwd);
|
|
2314
2174
|
case "contract":
|
|
2315
|
-
return await handleContract(args, cwd
|
|
2175
|
+
return await handleContract(args, cwd);
|
|
2316
2176
|
case "status":
|
|
2317
|
-
return await handleStatus(args, cwd
|
|
2177
|
+
return await handleStatus(args, cwd);
|
|
2318
2178
|
case "doctor":
|
|
2319
2179
|
return await handleDoctor(args, cwd, parsed.positionalSkill);
|
|
2320
2180
|
case "handoff":
|
|
2321
|
-
return await handleHandoff(args, cwd
|
|
2181
|
+
return await handleHandoff(args, cwd);
|
|
2322
2182
|
case "graph":
|
|
2323
2183
|
return await handleGraph(args, cwd, parsed.positionalSkill);
|
|
2324
2184
|
case "prune":
|
|
2325
|
-
return await handlePrune(args, cwd
|
|
2185
|
+
return await handlePrune(args, cwd);
|
|
2326
2186
|
case "gc":
|
|
2327
2187
|
return await handleGc(args, cwd, parsed.positionalSkill);
|
|
2328
2188
|
case "migrate":
|
|
2329
|
-
return await handleMigrate(args, cwd
|
|
2330
|
-
default:
|
|
2331
|
-
return { status: 2, stderr: `Unknown gjc state command: ${parsed.action}\n` };
|
|
2189
|
+
return await handleMigrate(args, cwd);
|
|
2332
2190
|
}
|
|
2333
2191
|
} catch (error) {
|
|
2334
2192
|
if (error instanceof CommandError) return { status: error.exitStatus, stderr: `${error.message}\n` };
|
|
@@ -1885,7 +1885,7 @@ async function startTmuxSession(
|
|
|
1885
1885
|
for (const worker of config.workers) {
|
|
1886
1886
|
const splitDirection: string = worker.index === 1 ? "-h" : "-v";
|
|
1887
1887
|
const splitTarget: string =
|
|
1888
|
-
worker.index === 1 ? config.
|
|
1888
|
+
worker.index === 1 ? config.tmux_target : (rightStackRootPaneId ?? config.tmux_target);
|
|
1889
1889
|
const workerCommand = buildWorkerCommand(config, worker);
|
|
1890
1890
|
const workerCwd = worker.worktree_path ?? config.leader.cwd;
|
|
1891
1891
|
const useSendKeysFallback = shouldDispatchWorkerWithSendKeys(config.tmux_command);
|
|
@@ -11,6 +11,7 @@ import * as fsSync from "node:fs";
|
|
|
11
11
|
import * as fs from "node:fs/promises";
|
|
12
12
|
|
|
13
13
|
import * as path from "node:path";
|
|
14
|
+
import { openRecoveryFsRoot } from "@gajae-code/natives";
|
|
14
15
|
import { isCompiledBinary } from "@gajae-code/utils/env";
|
|
15
16
|
import { parseLinuxProcStartTime } from "./linux-proc";
|
|
16
17
|
|
|
@@ -1046,6 +1047,122 @@ function sameOwnerGenerationBaseline(left: OwnerGenerationBaseline, right: Owner
|
|
|
1046
1047
|
);
|
|
1047
1048
|
}
|
|
1048
1049
|
|
|
1050
|
+
export interface ManagedOwnerPredecessorEvidence {
|
|
1051
|
+
generation: string;
|
|
1052
|
+
sessionId: string;
|
|
1053
|
+
runId: string;
|
|
1054
|
+
incarnation: string;
|
|
1055
|
+
predecessorToken: string;
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
function exactManagedOwnerJson(authority: ReturnType<typeof openRecoveryFsRoot>, name: string): unknown {
|
|
1059
|
+
const first = authority.read(name, 64 * 1024);
|
|
1060
|
+
if (!first.ok || !first.data) throw new Error("managed_owner_replacement_evidence_unavailable");
|
|
1061
|
+
const second = authority.read(name, 64 * 1024);
|
|
1062
|
+
if (
|
|
1063
|
+
!second.ok ||
|
|
1064
|
+
!second.data ||
|
|
1065
|
+
Buffer.compare(Buffer.from(first.data), Buffer.from(second.data)) !== 0 ||
|
|
1066
|
+
JSON.stringify(first.identity) !== JSON.stringify(second.identity)
|
|
1067
|
+
)
|
|
1068
|
+
throw new Error("managed_owner_replacement_evidence_changed");
|
|
1069
|
+
const content = Buffer.from(first.data).toString("utf8");
|
|
1070
|
+
if (!content.endsWith("\n") || content.indexOf("\n") !== content.length - 1)
|
|
1071
|
+
throw new Error("managed_owner_replacement_evidence_untrusted");
|
|
1072
|
+
return JSON.parse(content);
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
/** Resolve one SIGABRT predecessor from the retained exact prior-generation root. */
|
|
1076
|
+
export function resolveManagedOwnerPredecessorSync(
|
|
1077
|
+
stateDir: string,
|
|
1078
|
+
sessionId: string,
|
|
1079
|
+
baseline: OwnerGenerationBaseline,
|
|
1080
|
+
): ManagedOwnerPredecessorEvidence | undefined {
|
|
1081
|
+
if (baseline.state === "absent") return undefined;
|
|
1082
|
+
const root = lifecyclePaths(stateDir, sessionId, baseline.generation).root;
|
|
1083
|
+
let entries: string[];
|
|
1084
|
+
try {
|
|
1085
|
+
entries = fsSync.readdirSync(root);
|
|
1086
|
+
} catch {
|
|
1087
|
+
throw new Error("managed_owner_replacement_evidence_unavailable");
|
|
1088
|
+
}
|
|
1089
|
+
const bindings = new Set<string>();
|
|
1090
|
+
const receipts = new Set<string>();
|
|
1091
|
+
for (const entry of entries) {
|
|
1092
|
+
const binding = /^child-([^/]+)\.binding\.json$/.exec(entry);
|
|
1093
|
+
const receipt = /^sigabrt-([^/]+)\.receipt\.json$/.exec(entry);
|
|
1094
|
+
if (entry.startsWith("child-") && !binding) throw new Error("managed_owner_replacement_evidence_untrusted");
|
|
1095
|
+
if (entry.startsWith("sigabrt-") && !receipt) throw new Error("managed_owner_replacement_evidence_untrusted");
|
|
1096
|
+
if (binding) bindings.add(binding[1]!);
|
|
1097
|
+
if (receipt) receipts.add(receipt[1]!);
|
|
1098
|
+
}
|
|
1099
|
+
if (bindings.size === 0 && receipts.size === 0) return undefined;
|
|
1100
|
+
const tokens = [...bindings].filter(token => receipts.has(token));
|
|
1101
|
+
if (tokens.length !== 1 || receipts.size !== 1) throw new Error("managed_owner_replacement_evidence_ambiguous");
|
|
1102
|
+
const predecessorToken = tokens[0]!;
|
|
1103
|
+
if (!/^[A-Za-z0-9._-]+$/.test(predecessorToken)) throw new Error("managed_owner_replacement_evidence_untrusted");
|
|
1104
|
+
const authority = openRecoveryFsRoot(root);
|
|
1105
|
+
try {
|
|
1106
|
+
const binding = exactManagedOwnerJson(authority, `child-${predecessorToken}.binding.json`) as Record<
|
|
1107
|
+
string,
|
|
1108
|
+
unknown
|
|
1109
|
+
>;
|
|
1110
|
+
const receipt = exactManagedOwnerJson(authority, `sigabrt-${predecessorToken}.receipt.json`) as Record<
|
|
1111
|
+
string,
|
|
1112
|
+
unknown
|
|
1113
|
+
>;
|
|
1114
|
+
const command = binding.command;
|
|
1115
|
+
const runId = binding.run_id;
|
|
1116
|
+
const incarnation = binding.endpoint_incarnation;
|
|
1117
|
+
const commandDigest =
|
|
1118
|
+
Array.isArray(command) && command.length > 0 && command.every(value => typeof value === "string" && value)
|
|
1119
|
+
? crypto.createHash("sha256").update(JSON.stringify(command)).digest("hex")
|
|
1120
|
+
: "";
|
|
1121
|
+
const valid =
|
|
1122
|
+
binding.schema_version === 2 &&
|
|
1123
|
+
binding.generation === baseline.generation &&
|
|
1124
|
+
binding.session_id === sessionId &&
|
|
1125
|
+
typeof runId === "string" &&
|
|
1126
|
+
runId.length > 0 &&
|
|
1127
|
+
typeof incarnation === "string" &&
|
|
1128
|
+
incarnation.length > 0 &&
|
|
1129
|
+
binding.child_token === predecessorToken &&
|
|
1130
|
+
binding.command_sha256 === commandDigest &&
|
|
1131
|
+
typeof binding.supervisor_pid === "number" &&
|
|
1132
|
+
Number.isSafeInteger(binding.supervisor_pid) &&
|
|
1133
|
+
binding.supervisor_pid > 0 &&
|
|
1134
|
+
typeof binding.supervisor_start_time === "string" &&
|
|
1135
|
+
typeof binding.created_at === "string" &&
|
|
1136
|
+
receipt.schema_version === 2 &&
|
|
1137
|
+
receipt.generation === binding.generation &&
|
|
1138
|
+
receipt.session_id === binding.session_id &&
|
|
1139
|
+
receipt.run_id === runId &&
|
|
1140
|
+
receipt.endpoint_incarnation === incarnation &&
|
|
1141
|
+
receipt.child_token === binding.child_token &&
|
|
1142
|
+
receipt.command_sha256 === binding.command_sha256 &&
|
|
1143
|
+
receipt.supervisor_pid === binding.supervisor_pid &&
|
|
1144
|
+
receipt.supervisor_start_time === binding.supervisor_start_time &&
|
|
1145
|
+
typeof receipt.child_pid === "number" &&
|
|
1146
|
+
Number.isSafeInteger(receipt.child_pid) &&
|
|
1147
|
+
receipt.child_pid > 0 &&
|
|
1148
|
+
typeof receipt.child_start_time === "string" &&
|
|
1149
|
+
typeof receipt.received_at === "string" &&
|
|
1150
|
+
(receipt.exit_code === null || Number.isSafeInteger(receipt.exit_code)) &&
|
|
1151
|
+
receipt.signal === "SIGABRT" &&
|
|
1152
|
+
receipt.signal_number === 6;
|
|
1153
|
+
if (!valid) throw new Error("managed_owner_replacement_evidence_untrusted");
|
|
1154
|
+
return {
|
|
1155
|
+
generation: baseline.generation,
|
|
1156
|
+
sessionId,
|
|
1157
|
+
runId,
|
|
1158
|
+
incarnation,
|
|
1159
|
+
predecessorToken,
|
|
1160
|
+
};
|
|
1161
|
+
} finally {
|
|
1162
|
+
authority.close();
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1165
|
+
|
|
1049
1166
|
export async function captureOwnerGenerationBaseline(
|
|
1050
1167
|
stateDir: string,
|
|
1051
1168
|
sessionId: string,
|