@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
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-process reservation for a deterministic launch-worktree path.
|
|
3
|
+
*
|
|
4
|
+
* The durable session index is the long-lived ownership record, but a newly
|
|
5
|
+
* planned worktree has no index row yet. This reservation closes that interval:
|
|
6
|
+
* it is acquired before preparation and removed after `host_registered` is
|
|
7
|
+
* durably appended to the index.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
11
|
+
import type { Dirent } from "node:fs";
|
|
12
|
+
import * as fs from "node:fs/promises";
|
|
13
|
+
import * as path from "node:path";
|
|
14
|
+
import { resolveEquivalentPath } from "@gajae-code/utils";
|
|
15
|
+
import {
|
|
16
|
+
type FileLockGcObservation,
|
|
17
|
+
processStartTime,
|
|
18
|
+
readFileLockObservationForGc,
|
|
19
|
+
removeFileLockDirForGc,
|
|
20
|
+
} from "../config/file-lock";
|
|
21
|
+
import { processIncarnation } from "../sdk/broker/process-incarnation";
|
|
22
|
+
|
|
23
|
+
const RESERVATION_VERSION = 1;
|
|
24
|
+
const RESERVATION_DIRECTORY = "launch-worktree-reservations";
|
|
25
|
+
|
|
26
|
+
interface LaunchWorktreeReservationRecord {
|
|
27
|
+
version: typeof RESERVATION_VERSION;
|
|
28
|
+
worktreePath: string;
|
|
29
|
+
pid: number;
|
|
30
|
+
start_time?: string;
|
|
31
|
+
processIncarnation: string | null;
|
|
32
|
+
timestamp: number;
|
|
33
|
+
reservationId: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface LaunchWorktreeReservation {
|
|
37
|
+
release(): Promise<void>;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function canonicalWorktreePath(worktreePath: string): string {
|
|
41
|
+
return resolveEquivalentPath(path.resolve(worktreePath));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function reservationRoot(agentDir: string): string {
|
|
45
|
+
return path.join(agentDir, "sdk", "sessions", RESERVATION_DIRECTORY);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function launchWorktreeReservationDirectory(agentDir: string, worktreePath: string): string {
|
|
49
|
+
const digest = createHash("sha256").update(canonicalWorktreePath(worktreePath)).digest("hex");
|
|
50
|
+
return path.join(reservationRoot(agentDir), `${digest}.lock`);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Test seam for seeding a dead-owner reservation at the exact production path. */
|
|
54
|
+
export function launchWorktreeReservationDirectoryForTest(agentDir: string, worktreePath: string): string {
|
|
55
|
+
return launchWorktreeReservationDirectory(agentDir, worktreePath);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function isErrno(error: unknown, code: string): boolean {
|
|
59
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function isReservationRecord(value: unknown): value is LaunchWorktreeReservationRecord {
|
|
63
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
|
|
64
|
+
const record = value as Partial<LaunchWorktreeReservationRecord>;
|
|
65
|
+
return (
|
|
66
|
+
record.version === RESERVATION_VERSION &&
|
|
67
|
+
typeof record.worktreePath === "string" &&
|
|
68
|
+
record.worktreePath.length > 0 &&
|
|
69
|
+
typeof record.pid === "number" &&
|
|
70
|
+
Number.isSafeInteger(record.pid) &&
|
|
71
|
+
record.pid > 0 &&
|
|
72
|
+
(record.start_time === undefined || (typeof record.start_time === "string" && record.start_time.length > 0)) &&
|
|
73
|
+
(record.processIncarnation === null ||
|
|
74
|
+
(typeof record.processIncarnation === "string" && record.processIncarnation.length > 0)) &&
|
|
75
|
+
typeof record.timestamp === "number" &&
|
|
76
|
+
Number.isFinite(record.timestamp) &&
|
|
77
|
+
typeof record.reservationId === "string" &&
|
|
78
|
+
record.reservationId.length > 0
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async function readReservation(lockDir: string): Promise<LaunchWorktreeReservationRecord | null> {
|
|
83
|
+
let value: unknown;
|
|
84
|
+
try {
|
|
85
|
+
value = await Bun.file(path.join(lockDir, "info")).json();
|
|
86
|
+
} catch (error) {
|
|
87
|
+
if (isErrno(error, "ENOENT") || error instanceof SyntaxError) return null;
|
|
88
|
+
throw error;
|
|
89
|
+
}
|
|
90
|
+
return isReservationRecord(value) ? value : null;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function ownerDefinitelyExited(record: LaunchWorktreeReservationRecord): boolean {
|
|
94
|
+
try {
|
|
95
|
+
process.kill(record.pid, 0);
|
|
96
|
+
} catch (error) {
|
|
97
|
+
return isErrno(error, "ESRCH");
|
|
98
|
+
}
|
|
99
|
+
if (record.processIncarnation !== null) {
|
|
100
|
+
const actualIncarnation = processIncarnation(record.pid);
|
|
101
|
+
if (actualIncarnation !== undefined) return actualIncarnation !== record.processIncarnation;
|
|
102
|
+
}
|
|
103
|
+
if (record.start_time) {
|
|
104
|
+
const actualStartTime = processStartTime(record.pid);
|
|
105
|
+
if (actualStartTime !== null) return actualStartTime !== record.start_time;
|
|
106
|
+
}
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async function removeReservation(
|
|
111
|
+
lockDir: string,
|
|
112
|
+
record: LaunchWorktreeReservationRecord,
|
|
113
|
+
): Promise<"removed" | "kept"> {
|
|
114
|
+
// Capture the exact on-disk identity BEFORE authorizing removal: the generic
|
|
115
|
+
// GC remover only deletes a tree whose pre-verdict identity it was handed,
|
|
116
|
+
// so a successor reservation published at the same path is never reaped.
|
|
117
|
+
let observation: FileLockGcObservation | null;
|
|
118
|
+
try {
|
|
119
|
+
observation = await readFileLockObservationForGc(lockDir);
|
|
120
|
+
} catch {
|
|
121
|
+
return "kept";
|
|
122
|
+
}
|
|
123
|
+
if (!observation) return "kept";
|
|
124
|
+
let onDisk: unknown;
|
|
125
|
+
try {
|
|
126
|
+
onDisk = JSON.parse(observation.bytes);
|
|
127
|
+
} catch {
|
|
128
|
+
return "kept";
|
|
129
|
+
}
|
|
130
|
+
if (
|
|
131
|
+
!isReservationRecord(onDisk) ||
|
|
132
|
+
onDisk.reservationId !== record.reservationId ||
|
|
133
|
+
onDisk.pid !== record.pid ||
|
|
134
|
+
onDisk.timestamp !== record.timestamp ||
|
|
135
|
+
canonicalWorktreePath(onDisk.worktreePath) !== canonicalWorktreePath(record.worktreePath)
|
|
136
|
+
)
|
|
137
|
+
return "kept";
|
|
138
|
+
const result = await removeFileLockDirForGc(lockDir, observation.info, observation.identity);
|
|
139
|
+
return result === "removed" ? "removed" : "kept";
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
async function writeReservation(lockDir: string, record: LaunchWorktreeReservationRecord): Promise<void> {
|
|
143
|
+
try {
|
|
144
|
+
await Bun.write(path.join(lockDir, "info"), JSON.stringify(record));
|
|
145
|
+
} catch (error) {
|
|
146
|
+
await fs.rm(lockDir, { recursive: true, force: true }).catch(() => undefined);
|
|
147
|
+
throw error;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
async function tryPublishReservation(lockDir: string, record: LaunchWorktreeReservationRecord): Promise<boolean> {
|
|
152
|
+
const pendingDir = `${lockDir}.pending.${process.pid}.${randomUUID()}`;
|
|
153
|
+
await fs.mkdir(pendingDir, { mode: 0o700 });
|
|
154
|
+
try {
|
|
155
|
+
await writeReservation(pendingDir, record);
|
|
156
|
+
try {
|
|
157
|
+
await fs.rename(pendingDir, lockDir);
|
|
158
|
+
return true;
|
|
159
|
+
} catch (error) {
|
|
160
|
+
if (isErrno(error, "EEXIST") || isErrno(error, "ENOTEMPTY")) return false;
|
|
161
|
+
if (isErrno(error, "EPERM")) {
|
|
162
|
+
try {
|
|
163
|
+
await fs.stat(lockDir);
|
|
164
|
+
return false;
|
|
165
|
+
} catch (statError) {
|
|
166
|
+
if (!isErrno(statError, "ENOENT")) throw statError;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
throw error;
|
|
170
|
+
}
|
|
171
|
+
} finally {
|
|
172
|
+
await fs.rm(pendingDir, { recursive: true, force: true }).catch(() => undefined);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Atomically claim `worktreePath`, returning `null` only when another live or
|
|
178
|
+
* unverified launch already owns the same deterministic target. A dead or
|
|
179
|
+
* PID-reused owner is reclaimed before retrying the exclusive directory create.
|
|
180
|
+
*/
|
|
181
|
+
export async function reserveLaunchWorktree(
|
|
182
|
+
agentDir: string,
|
|
183
|
+
worktreePath: string,
|
|
184
|
+
): Promise<LaunchWorktreeReservation | null> {
|
|
185
|
+
const canonicalPath = canonicalWorktreePath(worktreePath);
|
|
186
|
+
const lockDir = launchWorktreeReservationDirectory(agentDir, canonicalPath);
|
|
187
|
+
await fs.mkdir(path.dirname(lockDir), { recursive: true, mode: 0o700 });
|
|
188
|
+
const ownStartTime = processStartTime(process.pid) ?? undefined;
|
|
189
|
+
const ownIncarnation = processIncarnation(process.pid) ?? null;
|
|
190
|
+
|
|
191
|
+
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
192
|
+
const record: LaunchWorktreeReservationRecord = {
|
|
193
|
+
version: RESERVATION_VERSION,
|
|
194
|
+
worktreePath: canonicalPath,
|
|
195
|
+
pid: process.pid,
|
|
196
|
+
...(ownStartTime ? { start_time: ownStartTime } : {}),
|
|
197
|
+
processIncarnation: ownIncarnation,
|
|
198
|
+
timestamp: Date.now(),
|
|
199
|
+
reservationId: randomUUID(),
|
|
200
|
+
};
|
|
201
|
+
if (await tryPublishReservation(lockDir, record)) {
|
|
202
|
+
return {
|
|
203
|
+
async release(): Promise<void> {
|
|
204
|
+
await removeReservation(lockDir, record);
|
|
205
|
+
},
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const existing = await readReservation(lockDir);
|
|
210
|
+
if (
|
|
211
|
+
!existing ||
|
|
212
|
+
canonicalWorktreePath(existing.worktreePath) !== canonicalPath ||
|
|
213
|
+
!ownerDefinitelyExited(existing)
|
|
214
|
+
)
|
|
215
|
+
return null;
|
|
216
|
+
if ((await removeReservation(lockDir, existing)) !== "removed") return null;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/** Remove the transient reservation only after the matching worktree is indexed. */
|
|
223
|
+
export async function releaseLaunchWorktreeReservationAfterRegistration(
|
|
224
|
+
agentDir: string,
|
|
225
|
+
worktreePath: string,
|
|
226
|
+
): Promise<void> {
|
|
227
|
+
const canonicalPath = canonicalWorktreePath(worktreePath);
|
|
228
|
+
let entries: Dirent[];
|
|
229
|
+
try {
|
|
230
|
+
entries = await fs.readdir(reservationRoot(agentDir), { withFileTypes: true });
|
|
231
|
+
} catch (error) {
|
|
232
|
+
if (isErrno(error, "ENOENT")) return;
|
|
233
|
+
throw error;
|
|
234
|
+
}
|
|
235
|
+
for (const entry of entries) {
|
|
236
|
+
if (!entry.isDirectory() || !entry.name.endsWith(".lock")) continue;
|
|
237
|
+
const lockDir = path.join(reservationRoot(agentDir), entry.name);
|
|
238
|
+
const record = await readReservation(lockDir);
|
|
239
|
+
if (!record || canonicalWorktreePath(record.worktreePath) !== canonicalPath) continue;
|
|
240
|
+
await removeReservation(lockDir, record);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
@@ -15,6 +15,8 @@ export const MANAGED_OWNER_GENERATION_ENV = "GJC_TMUX_OWNER_GENERATION";
|
|
|
15
15
|
export const MANAGED_OWNER_STATE_DIR_ENV = "GJC_TMUX_OWNER_STATE_DIR";
|
|
16
16
|
export const MANAGED_OWNER_RUN_ID_ENV = "GJC_MANAGED_OWNER_RUN_ID";
|
|
17
17
|
export const MANAGED_OWNER_INCARNATION_ENV = "GJC_MANAGED_OWNER_INCARNATION";
|
|
18
|
+
/** Suppresses command-derived durable artifacts for Broker-owned opaque spawn children. */
|
|
19
|
+
export const MANAGED_OWNER_REDACT_COMMAND_ENV = "GJC_MANAGED_OWNER_REDACT_COMMAND";
|
|
18
20
|
|
|
19
21
|
let bootstrapSigtermPending = false;
|
|
20
22
|
const captureBootstrapSigterm = () => {
|
|
@@ -140,26 +142,32 @@ export async function runManagedOwnerSupervisor(): Promise<void> {
|
|
|
140
142
|
if (!supervisorStartTime) throw new Error("managed_owner_supervisor_start_time_unavailable");
|
|
141
143
|
await fs.mkdir(root, { recursive: true, mode: 0o700 });
|
|
142
144
|
const childToken = crypto.randomUUID();
|
|
143
|
-
const
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
145
|
+
const redactCommand = process.env[MANAGED_OWNER_REDACT_COMMAND_ENV] === "1";
|
|
146
|
+
const binding: ManagedOwnerBinding | null = redactCommand
|
|
147
|
+
? null
|
|
148
|
+
: {
|
|
149
|
+
schema_version: 2,
|
|
150
|
+
generation,
|
|
151
|
+
session_id: sessionId,
|
|
152
|
+
run_id: runId,
|
|
153
|
+
endpoint_incarnation: incarnation,
|
|
154
|
+
child_token: childToken,
|
|
155
|
+
command,
|
|
156
|
+
command_sha256: commandDigest(command),
|
|
157
|
+
supervisor_pid: process.pid,
|
|
158
|
+
supervisor_start_time: supervisorStartTime,
|
|
159
|
+
created_at: new Date().toISOString(),
|
|
160
|
+
};
|
|
161
|
+
if (binding) await writeDurableExclusive(path.join(root, `child-${childToken}.binding.json`), binding);
|
|
162
|
+
const childEnvironment: NodeJS.ProcessEnv = { ...process.env, [MANAGED_OWNER_CHILD_TOKEN_ENV]: childToken };
|
|
163
|
+
delete childEnvironment[MANAGED_OWNER_COMMAND_ENV];
|
|
164
|
+
delete childEnvironment[MANAGED_OWNER_REDACT_COMMAND_ENV];
|
|
157
165
|
const child = Bun.spawn({
|
|
158
166
|
cmd: command,
|
|
159
167
|
stdin: "inherit",
|
|
160
168
|
stdout: "inherit",
|
|
161
169
|
stderr: "inherit",
|
|
162
|
-
env:
|
|
170
|
+
env: childEnvironment,
|
|
163
171
|
});
|
|
164
172
|
const childStartTime = await managedOwnerProcessProvenance(child.pid);
|
|
165
173
|
if (!childStartTime) throw new Error("managed_owner_child_start_time_unavailable");
|
|
@@ -167,24 +175,26 @@ export async function runManagedOwnerSupervisor(): Promise<void> {
|
|
|
167
175
|
if (!childProcess) {
|
|
168
176
|
const exitCode = await child.exited;
|
|
169
177
|
if (child.signalCode === "SIGABRT") {
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
178
|
+
if (binding) {
|
|
179
|
+
const receipt: ManagedOwnerSigabrtReceipt = {
|
|
180
|
+
schema_version: 2,
|
|
181
|
+
generation,
|
|
182
|
+
session_id: sessionId,
|
|
183
|
+
run_id: runId,
|
|
184
|
+
endpoint_incarnation: incarnation,
|
|
185
|
+
child_token: childToken,
|
|
186
|
+
command_sha256: binding.command_sha256,
|
|
187
|
+
supervisor_pid: process.pid,
|
|
188
|
+
supervisor_start_time: supervisorStartTime,
|
|
189
|
+
child_pid: child.pid,
|
|
190
|
+
child_start_time: childStartTime,
|
|
191
|
+
signal: "SIGABRT",
|
|
192
|
+
signal_number: 6,
|
|
193
|
+
exit_code: exitCode,
|
|
194
|
+
received_at: new Date().toISOString(),
|
|
195
|
+
};
|
|
196
|
+
await writeDurableExclusive(path.join(root, `sigabrt-${childToken}.receipt.json`), receipt);
|
|
197
|
+
}
|
|
188
198
|
process.exitCode = 134;
|
|
189
199
|
return;
|
|
190
200
|
}
|
|
@@ -246,24 +256,26 @@ export async function runManagedOwnerSupervisor(): Promise<void> {
|
|
|
246
256
|
});
|
|
247
257
|
}
|
|
248
258
|
if (child.signalCode === "SIGABRT") {
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
259
|
+
if (binding) {
|
|
260
|
+
const receipt: ManagedOwnerSigabrtReceipt = {
|
|
261
|
+
schema_version: 2,
|
|
262
|
+
generation,
|
|
263
|
+
session_id: sessionId,
|
|
264
|
+
run_id: runId,
|
|
265
|
+
endpoint_incarnation: incarnation,
|
|
266
|
+
child_token: childToken,
|
|
267
|
+
command_sha256: binding.command_sha256,
|
|
268
|
+
supervisor_pid: process.pid,
|
|
269
|
+
supervisor_start_time: supervisorStartTime,
|
|
270
|
+
child_pid: child.pid,
|
|
271
|
+
child_start_time: childStartTime,
|
|
272
|
+
signal: "SIGABRT",
|
|
273
|
+
signal_number: 6,
|
|
274
|
+
exit_code: exitCode,
|
|
275
|
+
received_at: new Date().toISOString(),
|
|
276
|
+
};
|
|
277
|
+
await writeDurableExclusive(path.join(root, `sigabrt-${childToken}.receipt.json`), receipt);
|
|
278
|
+
}
|
|
267
279
|
process.exitCode = 134;
|
|
268
280
|
return;
|
|
269
281
|
}
|