@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,86 @@
|
|
|
1
|
+
import * as fsSync from "node:fs";
|
|
2
|
+
import * as fs from "node:fs/promises";
|
|
3
|
+
|
|
4
|
+
const MAX_ENDPOINT_BYTES = 4_096n;
|
|
5
|
+
|
|
6
|
+
export type EndpointFileRead = {
|
|
7
|
+
source: string;
|
|
8
|
+
dev: bigint;
|
|
9
|
+
ino: bigint;
|
|
10
|
+
nlink: bigint;
|
|
11
|
+
size: bigint;
|
|
12
|
+
mtimeNs: bigint;
|
|
13
|
+
mtimeMs: number;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export type EndpointAuthorityFilesystem = Pick<typeof fs, "open">;
|
|
17
|
+
|
|
18
|
+
export type IndexedEndpointAuthority = {
|
|
19
|
+
endpointMtimeMs?: number;
|
|
20
|
+
endpointFileId?: string;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
function readFlags(): number {
|
|
24
|
+
return fsSync.constants.O_RDONLY | (fsSync.constants.O_NOFOLLOW ?? 0) | (fsSync.constants.O_NONBLOCK ?? 0);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Reads one endpoint from the descriptor opened for its pathname. The opened
|
|
29
|
+
* object must remain a small, singly-linked regular file with stable identity
|
|
30
|
+
* and metadata for the whole read; a pathname replacement cannot affect the
|
|
31
|
+
* bytes returned by this function.
|
|
32
|
+
*/
|
|
33
|
+
export async function readEndpointFile(
|
|
34
|
+
filePath: string,
|
|
35
|
+
filesystem: EndpointAuthorityFilesystem = fs,
|
|
36
|
+
): Promise<EndpointFileRead | undefined> {
|
|
37
|
+
let handle: fs.FileHandle | undefined;
|
|
38
|
+
try {
|
|
39
|
+
handle = await filesystem.open(filePath, readFlags());
|
|
40
|
+
const before = await handle.stat({ bigint: true });
|
|
41
|
+
if (!before.isFile() || before.nlink !== 1n || before.size > MAX_ENDPOINT_BYTES) return undefined;
|
|
42
|
+
if (before.size > BigInt(Number.MAX_SAFE_INTEGER)) return undefined;
|
|
43
|
+
const bytes = Buffer.alloc(Number(before.size));
|
|
44
|
+
let offset = 0;
|
|
45
|
+
while (offset < bytes.length) {
|
|
46
|
+
const { bytesRead } = await handle.read(bytes, offset, bytes.length - offset, offset);
|
|
47
|
+
if (bytesRead <= 0) return undefined;
|
|
48
|
+
offset += bytesRead;
|
|
49
|
+
}
|
|
50
|
+
const after = await handle.stat({ bigint: true });
|
|
51
|
+
if (
|
|
52
|
+
before.dev !== after.dev ||
|
|
53
|
+
before.ino !== after.ino ||
|
|
54
|
+
before.nlink !== after.nlink ||
|
|
55
|
+
before.size !== after.size ||
|
|
56
|
+
before.mtimeNs !== after.mtimeNs
|
|
57
|
+
)
|
|
58
|
+
return undefined;
|
|
59
|
+
return {
|
|
60
|
+
source: bytes.toString("utf8"),
|
|
61
|
+
dev: before.dev,
|
|
62
|
+
ino: before.ino,
|
|
63
|
+
nlink: before.nlink,
|
|
64
|
+
size: before.size,
|
|
65
|
+
mtimeNs: before.mtimeNs,
|
|
66
|
+
mtimeMs: Number(before.mtimeNs) / 1_000_000,
|
|
67
|
+
};
|
|
68
|
+
} catch {
|
|
69
|
+
return undefined;
|
|
70
|
+
} finally {
|
|
71
|
+
await handle?.close().catch(() => undefined);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** Match a descriptor-bound endpoint read to the index representation. */
|
|
76
|
+
export function matchesIndexedEndpointFile(
|
|
77
|
+
file: Pick<EndpointFileRead, "dev" | "ino" | "mtimeMs">,
|
|
78
|
+
authority: IndexedEndpointAuthority,
|
|
79
|
+
): boolean {
|
|
80
|
+
return (
|
|
81
|
+
authority.endpointMtimeMs !== undefined &&
|
|
82
|
+
Number.isFinite(authority.endpointMtimeMs) &&
|
|
83
|
+
Math.abs(file.mtimeMs - authority.endpointMtimeMs) <= 0.001 &&
|
|
84
|
+
(authority.endpointFileId === undefined || authority.endpointFileId === `${file.dev}:${file.ino}`)
|
|
85
|
+
);
|
|
86
|
+
}
|
package/src/sdk/broker/ensure.ts
CHANGED
|
@@ -3,9 +3,21 @@ import { randomUUID } from "node:crypto";
|
|
|
3
3
|
import type { FileHandle } from "node:fs/promises";
|
|
4
4
|
import * as fs from "node:fs/promises";
|
|
5
5
|
import path from "node:path";
|
|
6
|
+
import packageJson from "../../../package.json" with { type: "json" };
|
|
7
|
+
import { SdkClient } from "../client/client";
|
|
6
8
|
import { type BrokerDiscovery, brokerProcessIncarnation, readBrokerDiscovery } from "./discovery";
|
|
7
9
|
import { resolveSdkInternalSpawnCommand, type SdkInternalSpawnCommand } from "./runtime";
|
|
8
10
|
import { BrokerStartupError, clearBrokerStartupFailureMarker, readBrokerStartupFailureMarker } from "./startup-failure";
|
|
11
|
+
|
|
12
|
+
function resolveExpectedBrokerGeneration(): string {
|
|
13
|
+
const v = (packageJson as { version?: unknown }).version;
|
|
14
|
+
return typeof v === "string" && v.length > 0 ? v : "unknown";
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function isBrokerGenerationCompatible(discovery: BrokerDiscovery | null): boolean {
|
|
18
|
+
if (!discovery) return false;
|
|
19
|
+
return discovery.packageGeneration === resolveExpectedBrokerGeneration();
|
|
20
|
+
}
|
|
9
21
|
export interface EnsureBrokerSettings {
|
|
10
22
|
agentDir: string;
|
|
11
23
|
heartbeatTtlMs?: number;
|
|
@@ -232,6 +244,11 @@ function registerBrokerOwner(
|
|
|
232
244
|
function brokerSpawnEnvironment(command: SdkInternalSpawnCommand, override?: NodeJS.ProcessEnv): NodeJS.ProcessEnv {
|
|
233
245
|
const environment = { ...(override ?? command.env) };
|
|
234
246
|
delete environment.BUN_OPTIONS;
|
|
247
|
+
// The master capability is a transient in-memory dispatch input. A broker
|
|
248
|
+
// cold-started from the master's own Bash environment would otherwise inherit
|
|
249
|
+
// it and pass it on to every substrate child it later launches, so it is
|
|
250
|
+
// stripped at the lifecycle boundary exactly as lifecycle children strip it.
|
|
251
|
+
delete environment.GJC_MASTER_CAPABILITY;
|
|
235
252
|
if (command.kind === "bun-source") {
|
|
236
253
|
delete environment.PI_COMPILED;
|
|
237
254
|
delete environment.GJC_COMPILED;
|
|
@@ -243,6 +260,47 @@ function fixtureLeaseUnavailable(): Error {
|
|
|
243
260
|
return new Error("fixture_broker_lease_unavailable");
|
|
244
261
|
}
|
|
245
262
|
|
|
263
|
+
const STALE_BROKER_SHUTDOWN_TIMEOUT_MS = 5_000;
|
|
264
|
+
const STALE_BROKER_POLL_MS = 50;
|
|
265
|
+
|
|
266
|
+
type BrokerOwnerIdentity = Pick<BrokerDiscovery, "ownerId" | "pid" | "incarnation">;
|
|
267
|
+
|
|
268
|
+
function sameBrokerOwner(left: BrokerOwnerIdentity | null, right: BrokerOwnerIdentity): boolean {
|
|
269
|
+
return Boolean(
|
|
270
|
+
left && left.ownerId === right.ownerId && left.pid === right.pid && left.incarnation === right.incarnation,
|
|
271
|
+
);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
async function retireStaleBrokerGeneration(stale: BrokerDiscovery, settings: EnsureBrokerSettings): Promise<void> {
|
|
275
|
+
let shutdownSucceeded = false;
|
|
276
|
+
try {
|
|
277
|
+
const current = await readBrokerDiscovery(settings.agentDir, settings.heartbeatTtlMs);
|
|
278
|
+
if (!current || !sameBrokerOwner(current, stale)) return;
|
|
279
|
+
const client = await SdkClient.connect(current.url, current.token, { timeoutMs: 2_000 });
|
|
280
|
+
try {
|
|
281
|
+
await client.global("broker.shutdown", {});
|
|
282
|
+
shutdownSucceeded = true;
|
|
283
|
+
} finally {
|
|
284
|
+
await client.close().catch(() => {});
|
|
285
|
+
}
|
|
286
|
+
} catch {}
|
|
287
|
+
if (!shutdownSucceeded) {
|
|
288
|
+
try {
|
|
289
|
+
if (brokerProcessIncarnation(stale.pid) === stale.incarnation) {
|
|
290
|
+
try {
|
|
291
|
+
process.kill(stale.pid, "SIGTERM");
|
|
292
|
+
} catch {}
|
|
293
|
+
}
|
|
294
|
+
} catch {}
|
|
295
|
+
}
|
|
296
|
+
// Wait for the stale identity to disappear (owner-fenced: pid+incarnation).
|
|
297
|
+
const deadline = Date.now() + STALE_BROKER_SHUTDOWN_TIMEOUT_MS;
|
|
298
|
+
while (Date.now() < deadline) {
|
|
299
|
+
const current = await readBrokerDiscovery(settings.agentDir, settings.heartbeatTtlMs);
|
|
300
|
+
if (!sameBrokerOwner(current, stale)) break;
|
|
301
|
+
await sleep(STALE_BROKER_POLL_MS);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
246
304
|
function createFixtureLeaseFromChild(child: ChildProcess, terminate: () => Promise<void>): ExactFixtureBrokerLease {
|
|
247
305
|
let termination: Promise<void> | undefined;
|
|
248
306
|
const hasExited = (): boolean => child.exitCode !== null || child.signalCode !== null || child.pid === undefined;
|
|
@@ -295,11 +353,24 @@ async function ensureBrokerOnce(settings: EnsureBrokerSettings, initiator: Ensur
|
|
|
295
353
|
if (priorOwner.canReuse(existing)) return { kind: "prior-local-owner", discovery: existing!, owner: priorOwner };
|
|
296
354
|
await priorOwner.stop();
|
|
297
355
|
const discoveredAfterCleanup = await readBrokerDiscovery(settings.agentDir, settings.heartbeatTtlMs);
|
|
298
|
-
if (discoveredAfterCleanup
|
|
356
|
+
if (discoveredAfterCleanup && isBrokerGenerationCompatible(discoveredAfterCleanup))
|
|
357
|
+
return { kind: "external-discovery", discovery: discoveredAfterCleanup };
|
|
358
|
+
// A stale-generation discovery after cleanup must not be reused — fall through to replacement.
|
|
359
|
+
if (!discoveredAfterCleanup) {
|
|
360
|
+
// no discovery left after cleanup — fall through to spawn
|
|
361
|
+
} else {
|
|
362
|
+
await retireStaleBrokerGeneration(discoveredAfterCleanup, settings);
|
|
363
|
+
const afterRetire = await readBrokerDiscovery(settings.agentDir, settings.heartbeatTtlMs);
|
|
364
|
+
if (afterRetire && isBrokerGenerationCompatible(afterRetire))
|
|
365
|
+
return { kind: "external-discovery", discovery: afterRetire };
|
|
366
|
+
}
|
|
299
367
|
} else if (existing) {
|
|
300
|
-
return { kind: "external-discovery", discovery: existing };
|
|
368
|
+
if (isBrokerGenerationCompatible(existing)) return { kind: "external-discovery", discovery: existing };
|
|
369
|
+
await retireStaleBrokerGeneration(existing, settings);
|
|
370
|
+
const afterRetire = await readBrokerDiscovery(settings.agentDir, settings.heartbeatTtlMs);
|
|
371
|
+
if (afterRetire && isBrokerGenerationCompatible(afterRetire))
|
|
372
|
+
return { kind: "external-discovery", discovery: afterRetire };
|
|
301
373
|
}
|
|
302
|
-
|
|
303
374
|
const command = resolveSdkInternalSpawnCommand("broker-internal");
|
|
304
375
|
const spawnLog = await openBrokerSpawnLog(settings.agentDir);
|
|
305
376
|
// A stale marker must never be misattributed to this spawn; clear it first.
|
|
@@ -472,6 +543,13 @@ export function startFixtureBrokerCommandWithLeaseForTest(command: FixtureBroker
|
|
|
472
543
|
export function brokerOwnerForTest(agentDir: string): BrokerOwner | undefined {
|
|
473
544
|
return owners.get(agentDir);
|
|
474
545
|
}
|
|
546
|
+
/** Test hook: verifies the complete broker owner identity used during stale retirement. */
|
|
547
|
+
export function brokerOwnerIdentityMatchesForTest(
|
|
548
|
+
left: BrokerOwnerIdentity | null,
|
|
549
|
+
right: BrokerOwnerIdentity,
|
|
550
|
+
): boolean {
|
|
551
|
+
return sameBrokerOwner(left, right);
|
|
552
|
+
}
|
|
475
553
|
/** Test hook: drives the detached-broker reap on a controllable child surface. */
|
|
476
554
|
export function reapSpawnedBrokerForTest(child: ChildProcess, timing: ReapTiming = DEFAULT_REAP_TIMING): Promise<void> {
|
|
477
555
|
return reapSpawnedBroker(child, timing);
|
package/src/sdk/broker/index.ts
CHANGED
|
@@ -4,6 +4,9 @@ export * from "./ensure";
|
|
|
4
4
|
export * from "./identity";
|
|
5
5
|
export * from "./lifecycle";
|
|
6
6
|
export * from "./lifecycle-ledger";
|
|
7
|
+
export * from "./master-capability";
|
|
7
8
|
export * from "./session-index";
|
|
9
|
+
export * from "./spawn-authority";
|
|
10
|
+
export * from "./spawn-substrate";
|
|
8
11
|
export * from "./startup-budget";
|
|
9
12
|
export * from "./transport";
|
|
@@ -55,6 +55,7 @@ export interface LifecycleDurableEffectsReceipt {
|
|
|
55
55
|
cwdDigest: string;
|
|
56
56
|
created: boolean;
|
|
57
57
|
reused: boolean;
|
|
58
|
+
createdBranch: boolean;
|
|
58
59
|
branchDigest?: string;
|
|
59
60
|
};
|
|
60
61
|
transcript?: {
|
|
@@ -62,6 +63,11 @@ export interface LifecycleDurableEffectsReceipt {
|
|
|
62
63
|
contentDigest: string;
|
|
63
64
|
};
|
|
64
65
|
startup?: LifecycleStartupFailureReceipt;
|
|
66
|
+
timings?: {
|
|
67
|
+
worktreePreparationMs?: number;
|
|
68
|
+
dependencyPreparationMs?: number;
|
|
69
|
+
spawnAuthorizedAtOffsetMs?: number;
|
|
70
|
+
};
|
|
65
71
|
digest?: string;
|
|
66
72
|
}
|
|
67
73
|
|