@the-open-engine/zeroshot 6.38.1 → 6.39.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/README.md +6 -0
- package/cli/event-copy.js +8 -7
- package/cli/event-copy.ts +12 -0
- package/cli/index.js +67 -48
- package/cli/json-export.js +61 -64
- package/cli/json-export.ts +112 -0
- package/cli/message-formatter-utils.js +68 -56
- package/cli/message-formatter-utils.ts +121 -0
- package/cli/message-formatters-normal.js +124 -259
- package/cli/message-formatters-normal.ts +250 -0
- package/cli/message-formatters-watch.js +81 -84
- package/cli/message-formatters-watch.ts +189 -0
- package/lib/cluster/generated/protocol-schema.cjs +1 -1
- package/lib/cluster/generated/protocol-schema.mjs +1 -1
- package/lib/cluster/generated/protocol.d.cts +8 -0
- package/lib/cluster/generated/protocol.d.mts +8 -0
- package/lib/cluster/generated/protocol.d.ts +8 -0
- package/lib/clusters-registry.d.ts +23 -0
- package/lib/clusters-registry.js +17 -29
- package/lib/completion.d.ts +12 -0
- package/lib/completion.js +84 -94
- package/lib/compose-utils.d.ts +32 -0
- package/lib/compose-utils.js +57 -72
- package/lib/detached-startup.d.ts +84 -0
- package/lib/detached-startup.js +232 -302
- package/lib/docker-config.d.ts +43 -0
- package/lib/docker-config.js +150 -325
- package/lib/docker-env-auth.d.ts +17 -0
- package/lib/docker-env-auth.js +75 -0
- package/lib/git-remote-utils.d.ts +48 -0
- package/lib/git-remote-utils.js +147 -213
- package/lib/id-detector.d.ts +15 -0
- package/lib/id-detector.js +37 -41
- package/lib/path-check.d.ts +30 -0
- package/lib/path-check.js +42 -45
- package/lib/process-liveness.d.ts +16 -0
- package/lib/process-liveness.js +19 -12
- package/lib/provider-credential-path.d.ts +16 -0
- package/lib/provider-credential-path.js +31 -29
- package/lib/provider-defaults.d.ts +30 -0
- package/lib/provider-defaults.js +47 -42
- package/lib/provider-detection.d.ts +11 -0
- package/lib/provider-detection.js +91 -54
- package/lib/provider-names.d.ts +38 -0
- package/lib/provider-names.js +56 -64
- package/lib/repo-settings.d.ts +28 -0
- package/lib/repo-settings.js +83 -59
- package/lib/run-mode.d.ts +21 -0
- package/lib/run-mode.js +21 -20
- package/lib/run-plan.d.ts +28 -0
- package/lib/run-plan.js +17 -16
- package/lib/settings/claude-auth.d.ts +35 -0
- package/lib/settings/claude-auth.js +30 -47
- package/lib/settings-defaults.d.ts +20 -0
- package/lib/settings-defaults.js +149 -0
- package/lib/settings-error.d.ts +7 -0
- package/lib/settings-error.js +8 -0
- package/lib/settings-issue-providers.d.ts +10 -0
- package/lib/settings-issue-providers.js +12 -0
- package/lib/settings-models.d.ts +22 -0
- package/lib/settings-models.js +93 -0
- package/lib/settings-storage.d.ts +23 -0
- package/lib/settings-storage.js +205 -0
- package/lib/settings-validation.d.ts +7 -0
- package/lib/settings-validation.js +182 -0
- package/lib/settings.d.ts +29 -0
- package/lib/settings.js +41 -736
- package/lib/setup-apply-types.d.ts +66 -0
- package/lib/setup-apply-types.js +2 -0
- package/lib/setup-apply.d.ts +39 -0
- package/lib/setup-apply.js +267 -264
- package/lib/setup-journal.d.ts +41 -0
- package/lib/setup-journal.js +119 -76
- package/lib/setup-plan-types.d.ts +102 -0
- package/lib/setup-plan-types.js +2 -0
- package/lib/setup-plan.d.ts +30 -0
- package/lib/setup-plan.js +300 -324
- package/lib/setup-undo.d.ts +44 -0
- package/lib/setup-undo.js +61 -74
- package/lib/start-cluster-config.d.ts +26 -0
- package/lib/start-cluster-config.js +56 -0
- package/lib/start-cluster-environment.d.ts +41 -0
- package/lib/start-cluster-environment.js +134 -0
- package/lib/start-cluster-run-options.d.ts +54 -0
- package/lib/start-cluster-run-options.js +148 -0
- package/lib/start-cluster.d.ts +64 -0
- package/lib/start-cluster.js +98 -454
- package/lib/stream-json-parser.d.ts +17 -0
- package/lib/stream-json-parser.js +51 -52
- package/npm-shrinkwrap.json +6 -8
- package/package.json +14 -8
- package/scripts/postinstall.js +27 -0
- package/scripts/rust-distribution.js +36 -45
- package/src/agent/agent-command-proofs-context.js +35 -45
- package/src/agent/agent-command-proofs-context.ts +87 -0
- package/src/agent/agent-config-settings.js +19 -0
- package/src/agent/agent-config-settings.ts +41 -0
- package/src/agent/agent-config.js +122 -231
- package/src/agent/agent-config.ts +285 -0
- package/src/agent/agent-context-builder.js +38 -273
- package/src/agent/agent-context-builder.ts +51 -0
- package/src/agent/agent-context-environment-sections.js +105 -0
- package/src/agent/agent-context-environment-sections.ts +134 -0
- package/src/agent/agent-context-packs.js +165 -0
- package/src/agent/agent-context-packs.ts +296 -0
- package/src/agent/agent-context-prompt-sections.js +100 -0
- package/src/agent/agent-context-prompt-sections.ts +148 -0
- package/src/agent/agent-context-sections.js +25 -334
- package/src/agent/agent-context-sections.ts +36 -0
- package/src/agent/agent-context-sources.js +96 -149
- package/src/agent/agent-context-sources.ts +253 -0
- package/src/agent/agent-context-types.js +2 -0
- package/src/agent/agent-context-types.ts +132 -0
- package/src/agent/agent-context-validation-sections.js +73 -0
- package/src/agent/agent-context-validation-sections.ts +129 -0
- package/src/agent/agent-input-injector.js +99 -118
- package/src/agent/agent-input-injector.ts +195 -0
- package/src/agent/agent-liveness-poll.js +74 -78
- package/src/agent/agent-liveness-poll.ts +163 -0
- package/src/agent/agent-quality-gate-schema.js +77 -86
- package/src/agent/agent-quality-gate-schema.ts +135 -0
- package/src/agent/agent-quality-gates-context.js +44 -50
- package/src/agent/agent-quality-gates-context.ts +91 -0
- package/src/agent/agent-stuck-detector.js +171 -239
- package/src/agent/agent-stuck-detector.ts +288 -0
- package/src/agent/agent-trigger-evaluator.js +29 -46
- package/src/agent/agent-trigger-evaluator.ts +98 -0
- package/src/agent/context-metrics.js +110 -141
- package/src/agent/context-metrics.ts +261 -0
- package/src/agent/context-pack-builder.js +72 -358
- package/src/agent/context-pack-builder.ts +105 -0
- package/src/agent/context-pack-core.js +70 -0
- package/src/agent/context-pack-core.ts +96 -0
- package/src/agent/context-pack-selection.js +119 -0
- package/src/agent/context-pack-selection.ts +187 -0
- package/src/agent/context-pack-truncation.js +139 -0
- package/src/agent/context-pack-truncation.ts +208 -0
- package/src/agent/context-pack-types.js +2 -0
- package/src/agent/context-pack-types.ts +98 -0
- package/src/agent/context-replay-policy.js +30 -39
- package/src/agent/context-replay-policy.ts +71 -0
- package/src/agent/critical-agent-policy.js +6 -10
- package/src/agent/critical-agent-policy.ts +20 -0
- package/src/agent/guidance-queue.js +74 -87
- package/src/agent/guidance-queue.ts +166 -0
- package/src/agent/output-extraction-error-detail.js +45 -0
- package/src/agent/output-extraction-error-detail.ts +49 -0
- package/src/agent/output-extraction-failures.js +125 -0
- package/src/agent/output-extraction-failures.ts +149 -0
- package/src/agent/output-extraction-json.js +234 -0
- package/src/agent/output-extraction-json.ts +235 -0
- package/src/agent/output-extraction-pi.js +124 -0
- package/src/agent/output-extraction-pi.ts +137 -0
- package/src/agent/output-extraction-types.js +2 -0
- package/src/agent/output-extraction-types.ts +49 -0
- package/src/agent/output-extraction.js +17 -617
- package/src/agent/output-extraction.ts +30 -0
- package/src/agent/output-reformatter-errors.js +83 -0
- package/src/agent/output-reformatter-errors.ts +106 -0
- package/src/agent/output-reformatter-types.js +2 -0
- package/src/agent/output-reformatter-types.ts +86 -0
- package/src/agent/output-reformatter.js +140 -185
- package/src/agent/output-reformatter.ts +238 -0
- package/src/agent/pi-terminal-lifecycle-types.js +2 -0
- package/src/agent/pi-terminal-lifecycle-types.ts +41 -0
- package/src/agent/pi-terminal-lifecycle.js +186 -206
- package/src/agent/pi-terminal-lifecycle.ts +297 -0
- package/src/agent/provider-control-plane.js +22 -21
- package/src/agent/provider-control-plane.ts +52 -0
- package/src/agent/provider-terminal-failure-types.js +2 -0
- package/src/agent/provider-terminal-failure-types.ts +60 -0
- package/src/agent/provider-terminal-failure.js +194 -191
- package/src/agent/provider-terminal-failure.ts +262 -0
- package/src/agent/rate-limit-backoff.js +31 -74
- package/src/agent/rate-limit-backoff.ts +71 -0
- package/src/agent/schema-utils.js +85 -125
- package/src/agent/schema-utils.ts +148 -0
- package/src/agent/structured-output-error.js +34 -33
- package/src/agent/structured-output-error.ts +105 -0
- package/src/agent/validation-platform.js +27 -27
- package/src/agent/validation-platform.ts +49 -0
- package/src/agents/git-pusher-template.js +17 -34
- package/src/attach/attach-client.js +232 -405
- package/src/attach/attach-client.ts +300 -0
- package/src/attach/attach-server-cleanup.js +43 -0
- package/src/attach/attach-server-cleanup.ts +39 -0
- package/src/attach/attach-server-clients.js +117 -0
- package/src/attach/attach-server-clients.ts +122 -0
- package/src/attach/attach-server-events.js +38 -0
- package/src/attach/attach-server-events.ts +36 -0
- package/src/attach/attach-server-pty.js +45 -0
- package/src/attach/attach-server-pty.ts +64 -0
- package/src/attach/attach-server-runtime.js +73 -0
- package/src/attach/attach-server-runtime.ts +62 -0
- package/src/attach/attach-server-socket.js +28 -0
- package/src/attach/attach-server-socket.ts +23 -0
- package/src/attach/attach-server-types.js +2 -0
- package/src/attach/attach-server-types.ts +82 -0
- package/src/attach/attach-server.js +106 -523
- package/src/attach/attach-server.ts +158 -0
- package/src/attach/index.js +18 -35
- package/src/attach/index.ts +18 -0
- package/src/attach/protocol.js +95 -184
- package/src/attach/protocol.ts +181 -0
- package/src/attach/ring-buffer.js +76 -102
- package/src/attach/ring-buffer.ts +109 -0
- package/src/attach/send-input.js +64 -75
- package/src/attach/send-input.ts +94 -0
- package/src/attach/socket-discovery.js +164 -218
- package/src/attach/socket-discovery.ts +230 -0
- package/src/attach/socket-paths.js +42 -61
- package/src/attach/socket-paths.ts +81 -0
- package/src/claude-credentials.js +51 -54
- package/src/claude-credentials.ts +89 -0
- package/src/cluster/generated/protocol-schema.ts +1 -1
- package/src/cluster/generated/protocol.ts +2 -1
- package/src/command-proofs.js +110 -141
- package/src/command-proofs.ts +216 -0
- package/src/config-router.js +81 -73
- package/src/config-router.ts +131 -0
- package/src/copy-containment.js +191 -0
- package/src/copy-containment.ts +259 -0
- package/src/copy-worker.js +53 -30
- package/src/copy-worker.ts +88 -0
- package/src/darwin-keychain-boundary.js +80 -111
- package/src/darwin-keychain-boundary.ts +148 -0
- package/src/guidance-topics.js +4 -5
- package/src/guidance-topics.ts +10 -0
- package/src/input-helpers.js +33 -54
- package/src/input-helpers.ts +58 -0
- package/src/isolation-manager.js +59 -20
- package/src/ledger-sequence.js +45 -48
- package/src/ledger-sequence.ts +63 -0
- package/src/legacy-lib/clusters-registry.ts +40 -0
- package/src/legacy-lib/completion.ts +148 -0
- package/src/legacy-lib/compose-utils.ts +102 -0
- package/src/legacy-lib/detached-startup.ts +451 -0
- package/src/legacy-lib/docker-config.ts +282 -0
- package/src/legacy-lib/docker-env-auth.ts +116 -0
- package/src/legacy-lib/git-remote-utils.ts +244 -0
- package/src/legacy-lib/id-detector.ts +57 -0
- package/src/legacy-lib/js-yaml.d.ts +3 -0
- package/src/legacy-lib/omelette.d.ts +15 -0
- package/src/legacy-lib/path-check.ts +89 -0
- package/src/legacy-lib/process-liveness.ts +36 -0
- package/src/legacy-lib/provider-credential-path.ts +57 -0
- package/src/legacy-lib/provider-defaults.ts +114 -0
- package/src/legacy-lib/provider-detection.ts +67 -0
- package/src/legacy-lib/provider-names.ts +126 -0
- package/src/legacy-lib/repo-settings.ts +88 -0
- package/src/legacy-lib/run-mode.ts +58 -0
- package/src/legacy-lib/run-plan.ts +49 -0
- package/src/legacy-lib/settings/claude-auth.ts +80 -0
- package/src/legacy-lib/settings-defaults.ts +212 -0
- package/src/legacy-lib/settings-error.ts +8 -0
- package/src/legacy-lib/settings-issue-providers.ts +18 -0
- package/src/legacy-lib/settings-models.ts +124 -0
- package/src/legacy-lib/settings-storage.ts +277 -0
- package/src/legacy-lib/settings-validation.ts +220 -0
- package/src/legacy-lib/settings.ts +87 -0
- package/src/legacy-lib/setup-apply-types.ts +70 -0
- package/src/legacy-lib/setup-apply.ts +456 -0
- package/src/legacy-lib/setup-journal.ts +185 -0
- package/src/legacy-lib/setup-plan-types.ts +96 -0
- package/src/legacy-lib/setup-plan.ts +500 -0
- package/src/legacy-lib/setup-undo.ts +142 -0
- package/src/legacy-lib/start-cluster-config.ts +124 -0
- package/src/legacy-lib/start-cluster-environment.ts +166 -0
- package/src/legacy-lib/start-cluster-run-options.ts +281 -0
- package/src/legacy-lib/start-cluster.ts +237 -0
- package/src/legacy-lib/stream-json-parser.ts +88 -0
- package/src/message-buffer.js +55 -71
- package/src/message-buffer.ts +107 -0
- package/src/message-bus-bridge.js +92 -133
- package/src/message-bus-bridge.ts +149 -0
- package/src/name-generator-shared.js +220 -0
- package/src/name-generator-shared.ts +231 -0
- package/src/name-generator.js +8 -217
- package/src/name-generator.ts +27 -0
- package/src/omp-blob-root.js +83 -64
- package/src/omp-blob-root.ts +116 -0
- package/src/omp-config-overlay.js +65 -38
- package/src/omp-config-overlay.ts +110 -0
- package/src/omp-execution-fingerprint.js +38 -43
- package/src/omp-execution-fingerprint.ts +92 -0
- package/src/omp-session-limits.js +10 -15
- package/src/omp-session-limits.ts +53 -0
- package/src/omp-session-partition.js +2 -2
- package/src/omp-session-verifier.js +1 -1
- package/src/orchestrator.js +32 -7
- package/src/pr-body-template.js +71 -0
- package/src/providers/anthropic/index.js +3 -1
- package/src/providers/anthropic/index.ts +13 -0
- package/src/providers/capabilities.js +19 -27
- package/src/providers/capabilities.ts +44 -0
- package/src/providers/google/index.js +3 -1
- package/src/providers/google/index.ts +13 -0
- package/src/providers/openai/index.js +3 -1
- package/src/providers/openai/index.ts +13 -0
- package/src/providers/opencode/index.js +3 -1
- package/src/providers/opencode/index.ts +13 -0
- package/src/quality-gates.js +67 -127
- package/src/quality-gates.ts +149 -0
- package/src/repo-settings-access.js +40 -0
- package/src/repo-settings-access.ts +64 -0
- package/src/schemas/sub-cluster.js +127 -183
- package/src/schemas/sub-cluster.ts +238 -0
- package/src/state-snapshot-normalization.js +224 -0
- package/src/state-snapshot-normalization.ts +222 -0
- package/src/state-snapshot.js +192 -359
- package/src/state-snapshot.ts +291 -0
- package/src/state-snapshotter.js +118 -130
- package/src/state-snapshotter.ts +167 -0
- package/src/sub-cluster-wrapper.js +4 -13
- package/src/task-run-model-args.js +87 -124
- package/src/task-run-model-args.ts +204 -0
- package/src/task-runner.js +5 -25
- package/src/task-runner.ts +28 -0
- package/src/task-startup-error.js +73 -58
- package/src/task-startup-error.ts +126 -0
- package/src/template-validation/consensus-gate-contracts.js +2 -0
- package/src/template-validation/consensus-gate-contracts.ts +70 -0
- package/src/template-validation/consensus-gate-scenarios.js +76 -0
- package/src/template-validation/consensus-gate-scenarios.ts +130 -0
- package/src/template-validation/consensus-gate-stage.js +34 -0
- package/src/template-validation/consensus-gate-stage.ts +56 -0
- package/src/template-validation/random-topology-contracts.js +2 -0
- package/src/template-validation/random-topology-contracts.ts +54 -0
- package/src/template-validation/random-topology-dispatch.js +25 -0
- package/src/template-validation/random-topology-dispatch.ts +33 -0
- package/src/template-validation/random-topology-message-loop.js +53 -0
- package/src/template-validation/random-topology-message-loop.ts +73 -0
- package/src/template-validation/random-topology-operations.js +79 -0
- package/src/template-validation/random-topology-operations.ts +101 -0
- package/src/template-validation/random-topology-runtime.js +25 -0
- package/src/template-validation/random-topology-runtime.ts +43 -0
- package/src/template-validation/random-topology-scenario.js +52 -0
- package/src/template-validation/random-topology-scenario.ts +57 -0
- package/src/template-validation/random-topology-schema.js +159 -0
- package/src/template-validation/random-topology-schema.ts +190 -0
- package/src/template-validation/random-topology-state.js +28 -0
- package/src/template-validation/random-topology-state.ts +30 -0
- package/src/template-validation/random-topology-trigger-evaluation.js +45 -0
- package/src/template-validation/random-topology-trigger-evaluation.ts +65 -0
- package/src/template-validation/random-topology-trigger.js +75 -0
- package/src/template-validation/random-topology-trigger.ts +88 -0
- package/src/template-validation/report-formatter.js +40 -46
- package/src/template-validation/report-formatter.ts +101 -0
- package/src/template-validation/simulate-consensus-gates.js +111 -302
- package/src/template-validation/simulate-consensus-gates.ts +196 -0
- package/src/template-validation/simulate-random-topology.js +26 -519
- package/src/template-validation/simulate-random-topology.ts +57 -0
- package/src/template-validation/simulate-two-stage-validation.js +41 -265
- package/src/template-validation/simulate-two-stage-validation.ts +55 -0
- package/src/template-validation/simulation-agent-runtime.js +25 -0
- package/src/template-validation/simulation-agent-runtime.ts +40 -0
- package/src/template-validation/simulation-agent.js +29 -0
- package/src/template-validation/simulation-agent.ts +73 -0
- package/src/template-validation/simulation-runtime.js +34 -0
- package/src/template-validation/simulation-runtime.ts +72 -0
- package/src/template-validation/two-stage-contracts.js +2 -0
- package/src/template-validation/two-stage-contracts.ts +41 -0
- package/src/template-validation/two-stage-inputs.js +21 -0
- package/src/template-validation/two-stage-inputs.ts +29 -0
- package/src/template-validation/two-stage-results.js +30 -0
- package/src/template-validation/two-stage-results.ts +40 -0
- package/src/template-validation/two-stage-scenario.js +86 -0
- package/src/template-validation/two-stage-scenario.ts +120 -0
- package/src/worktree-claude-config.js +182 -220
- package/src/worktree-claude-config.ts +272 -0
- package/src/worktree-tooling-env.js +103 -125
- package/src/worktree-tooling-env.ts +142 -0
- package/task-lib/completion.js +2 -3
- package/task-lib/completion.ts +9 -0
- package/task-lib/config.js +1 -4
- package/task-lib/config.ts +11 -0
- package/task-lib/name-generator.js +4 -226
- package/task-lib/name-generator.ts +11 -0
- package/task-lib/omp-session-cleanup.js +1 -1
- package/task-lib/omp-storage-root.js +8 -12
- package/task-lib/omp-storage-root.ts +62 -0
- package/task-lib/process-termination.js +143 -166
- package/task-lib/process-termination.ts +268 -0
|
@@ -0,0 +1,451 @@
|
|
|
1
|
+
import fs = require('fs');
|
|
2
|
+
import os = require('os');
|
|
3
|
+
import path = require('path');
|
|
4
|
+
|
|
5
|
+
interface LockOptions {
|
|
6
|
+
lockfilePath: string;
|
|
7
|
+
stale: number;
|
|
8
|
+
retries: {
|
|
9
|
+
retries: number;
|
|
10
|
+
minTimeout: number;
|
|
11
|
+
maxTimeout: number;
|
|
12
|
+
randomize: boolean;
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
type ReleaseLock = () => Promise<void>;
|
|
17
|
+
|
|
18
|
+
interface LockfileFacade {
|
|
19
|
+
lock(filePath: string, options: LockOptions): Promise<ReleaseLock>;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
interface RunOptions extends Record<string, unknown> {
|
|
23
|
+
autoMerge?: unknown;
|
|
24
|
+
closeIssue?: unknown;
|
|
25
|
+
docker?: unknown;
|
|
26
|
+
mergeQueue?: unknown;
|
|
27
|
+
pr?: unknown;
|
|
28
|
+
prBase?: unknown;
|
|
29
|
+
prBody?: unknown;
|
|
30
|
+
ship?: unknown;
|
|
31
|
+
worktree?: unknown;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
interface RunPlan {
|
|
35
|
+
isolation: 'none' | 'worktree' | 'docker';
|
|
36
|
+
delivery: 'none' | 'pr' | 'ship';
|
|
37
|
+
autoMerge: boolean;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
interface RunPlanFacade {
|
|
41
|
+
resolveRunPlan(options?: RunOptions): Readonly<RunPlan>;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
interface ProcessLivenessFacade {
|
|
45
|
+
isProcessRunning(pid: unknown): boolean;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
interface IsolationManagerFacade {
|
|
49
|
+
getDetachedSetupResources(clusterId: string): unknown;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
interface ClusterRecord extends Record<string, unknown> {
|
|
53
|
+
createdAt?: number;
|
|
54
|
+
id?: string;
|
|
55
|
+
pid?: number | null;
|
|
56
|
+
resumeDaemonPid?: number | null;
|
|
57
|
+
setupLogPath?: string | null;
|
|
58
|
+
state?: string;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
type ClusterRegistry = Record<string, ClusterRecord>;
|
|
62
|
+
type ClusterUpdater = (clusters: ClusterRegistry) => ClusterRegistry | void;
|
|
63
|
+
|
|
64
|
+
interface DetachedSetupParams {
|
|
65
|
+
clusterId: string;
|
|
66
|
+
pid?: number;
|
|
67
|
+
storageDir?: string;
|
|
68
|
+
logPath?: string | null;
|
|
69
|
+
worktree?: unknown;
|
|
70
|
+
runOptions?: RunOptions;
|
|
71
|
+
cwd?: string;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
interface ClusterIdentityParams {
|
|
75
|
+
clusterId: string;
|
|
76
|
+
storageDir?: string;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
interface SetupFailureParams extends ClusterIdentityParams {
|
|
80
|
+
error: unknown;
|
|
81
|
+
logPath?: string | null;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
interface ResumePatchParams extends ClusterIdentityParams {
|
|
85
|
+
daemonPid: number;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
interface RegistrationWaitParams extends ClusterIdentityParams {
|
|
89
|
+
timeoutMs?: number;
|
|
90
|
+
pollMs?: number;
|
|
91
|
+
daemonPid?: number;
|
|
92
|
+
logPath?: string | null;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
interface OwnershipWaitParams extends ResumePatchParams {
|
|
96
|
+
timeoutMs?: number;
|
|
97
|
+
pollMs?: number;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
101
|
+
const lockfile: LockfileFacade = require('proper-lockfile');
|
|
102
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
103
|
+
const runPlan: RunPlanFacade = require('./run-plan');
|
|
104
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
105
|
+
const processLiveness: ProcessLivenessFacade = require('./process-liveness');
|
|
106
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
107
|
+
const IsolationManager: IsolationManagerFacade = require('../src/isolation-manager');
|
|
108
|
+
|
|
109
|
+
const { resolveRunPlan } = runPlan;
|
|
110
|
+
const { isProcessRunning } = processLiveness;
|
|
111
|
+
|
|
112
|
+
const DEFAULT_WAIT_TIMEOUT_SECONDS = 180;
|
|
113
|
+
const DEFAULT_WAIT_POLL_MS = 1000;
|
|
114
|
+
const CLUSTERS_LOCK_STALE_MS = 5000;
|
|
115
|
+
const DEFAULT_OWNERSHIP_TIMEOUT_MS = 10000;
|
|
116
|
+
|
|
117
|
+
function isClusterRegistry(value: unknown): value is ClusterRegistry {
|
|
118
|
+
return value !== null && typeof value === 'object';
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function hasErrorCode(error: unknown, code: string): boolean {
|
|
122
|
+
return error !== null && typeof error === 'object' && 'code' in error && error.code === code;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function getStorageDir(storageDir?: string | null): string {
|
|
126
|
+
return storageDir || path.join(os.homedir(), '.zeroshot');
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function getClustersFilePath(storageDir?: string | null): string {
|
|
130
|
+
return path.join(getStorageDir(storageDir), 'clusters.json');
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function resolveWaitTimeoutMs(waitTimeoutSeconds: unknown): number {
|
|
134
|
+
const parsed = Number(waitTimeoutSeconds);
|
|
135
|
+
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
136
|
+
return DEFAULT_WAIT_TIMEOUT_SECONDS * 1000;
|
|
137
|
+
}
|
|
138
|
+
return Math.floor(parsed * 1000);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Kept as an alias: this module used to carry its own copy of this check.
|
|
142
|
+
const isProcessAlive = isProcessRunning;
|
|
143
|
+
|
|
144
|
+
function isClusterRegistered(clusterId: string, storageDir?: string): boolean {
|
|
145
|
+
const clustersFile = getClustersFilePath(storageDir);
|
|
146
|
+
if (!fs.existsSync(clustersFile)) {
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
try {
|
|
151
|
+
const raw = fs.readFileSync(clustersFile, 'utf8');
|
|
152
|
+
const parsed: unknown = JSON.parse(raw);
|
|
153
|
+
return isClusterRegistry(parsed) && Boolean(parsed[clusterId]);
|
|
154
|
+
} catch {
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function ensureStorageDir(storageDir?: string): string {
|
|
160
|
+
const resolvedStorageDir = getStorageDir(storageDir);
|
|
161
|
+
fs.mkdirSync(resolvedStorageDir, { recursive: true });
|
|
162
|
+
return resolvedStorageDir;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function ensureClustersFile(storageDir?: string): string {
|
|
166
|
+
const resolvedStorageDir = ensureStorageDir(storageDir);
|
|
167
|
+
const clustersFile = getClustersFilePath(resolvedStorageDir);
|
|
168
|
+
try {
|
|
169
|
+
fs.writeFileSync(clustersFile, '{}', { flag: 'wx', mode: 0o600 });
|
|
170
|
+
} catch (error: unknown) {
|
|
171
|
+
if (!hasErrorCode(error, 'EEXIST')) {
|
|
172
|
+
throw error;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return clustersFile;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function readClustersFile(clustersFile: string): ClusterRegistry {
|
|
179
|
+
try {
|
|
180
|
+
const parsed: unknown = JSON.parse(fs.readFileSync(clustersFile, 'utf8'));
|
|
181
|
+
return isClusterRegistry(parsed) ? parsed : {};
|
|
182
|
+
} catch {
|
|
183
|
+
return {};
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
async function updateClustersFile(
|
|
188
|
+
storageDir: string | undefined,
|
|
189
|
+
updater: ClusterUpdater
|
|
190
|
+
): Promise<ClusterRegistry> {
|
|
191
|
+
const clustersFile = ensureClustersFile(storageDir);
|
|
192
|
+
const lockfilePath = path.join(path.dirname(clustersFile), 'clusters.json.lock');
|
|
193
|
+
let release: ReleaseLock | undefined;
|
|
194
|
+
|
|
195
|
+
try {
|
|
196
|
+
release = await lockfile.lock(clustersFile, {
|
|
197
|
+
lockfilePath,
|
|
198
|
+
stale: CLUSTERS_LOCK_STALE_MS,
|
|
199
|
+
retries: {
|
|
200
|
+
retries: 20,
|
|
201
|
+
minTimeout: 100,
|
|
202
|
+
maxTimeout: 250,
|
|
203
|
+
randomize: true,
|
|
204
|
+
},
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
const clusters = readClustersFile(clustersFile);
|
|
208
|
+
const updated = updater(clusters) || clusters;
|
|
209
|
+
fs.writeFileSync(clustersFile, JSON.stringify(updated, null, 2));
|
|
210
|
+
return updated;
|
|
211
|
+
} finally {
|
|
212
|
+
if (release) {
|
|
213
|
+
await release();
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
async function registerDetachedSetupCluster({
|
|
219
|
+
clusterId,
|
|
220
|
+
pid,
|
|
221
|
+
storageDir,
|
|
222
|
+
logPath,
|
|
223
|
+
worktree,
|
|
224
|
+
runOptions = {},
|
|
225
|
+
cwd,
|
|
226
|
+
}: DetachedSetupParams): Promise<void> {
|
|
227
|
+
const plan = resolveRunPlan(runOptions);
|
|
228
|
+
const setupResources =
|
|
229
|
+
plan.isolation === 'docker' ? IsolationManager.getDetachedSetupResources(clusterId) : null;
|
|
230
|
+
await updateClustersFile(storageDir, (clusters) => {
|
|
231
|
+
clusters[clusterId] = {
|
|
232
|
+
id: clusterId,
|
|
233
|
+
state: 'setup',
|
|
234
|
+
createdAt: Date.now(),
|
|
235
|
+
pid: typeof pid === 'number' && Number.isInteger(pid) ? pid : null,
|
|
236
|
+
setupLogPath: logPath || null,
|
|
237
|
+
setupStartedAt: Date.now(),
|
|
238
|
+
setupStage: 'starting',
|
|
239
|
+
autoPr: plan.delivery !== 'none',
|
|
240
|
+
prOptions: plan.delivery !== 'none'
|
|
241
|
+
? {
|
|
242
|
+
prBase: runOptions.prBase,
|
|
243
|
+
prBody: typeof runOptions.prBody === 'string' ? runOptions.prBody : null,
|
|
244
|
+
mergeQueue: runOptions.mergeQueue || false,
|
|
245
|
+
closeIssue: runOptions.closeIssue || null,
|
|
246
|
+
autoMerge: plan.autoMerge,
|
|
247
|
+
cwd: cwd || null,
|
|
248
|
+
}
|
|
249
|
+
: null,
|
|
250
|
+
worktree: worktree || null,
|
|
251
|
+
config: null,
|
|
252
|
+
issue: null,
|
|
253
|
+
isolation: null,
|
|
254
|
+
agentStates: [],
|
|
255
|
+
failureInfo: null,
|
|
256
|
+
setupResources,
|
|
257
|
+
provisional: true,
|
|
258
|
+
};
|
|
259
|
+
return clusters;
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
async function removeDetachedSetupCluster({
|
|
264
|
+
clusterId,
|
|
265
|
+
storageDir,
|
|
266
|
+
}: ClusterIdentityParams): Promise<void> {
|
|
267
|
+
await updateClustersFile(storageDir, (clusters) => {
|
|
268
|
+
delete clusters[clusterId];
|
|
269
|
+
return clusters;
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
async function markDetachedSetupFailed({
|
|
274
|
+
clusterId,
|
|
275
|
+
storageDir,
|
|
276
|
+
error,
|
|
277
|
+
logPath,
|
|
278
|
+
}: SetupFailureParams): Promise<void> {
|
|
279
|
+
await updateClustersFile(storageDir, (clusters) => {
|
|
280
|
+
const existing = clusters[clusterId] || { id: clusterId, createdAt: Date.now() };
|
|
281
|
+
clusters[clusterId] = {
|
|
282
|
+
...existing,
|
|
283
|
+
state: 'failed',
|
|
284
|
+
pid: null,
|
|
285
|
+
setupLogPath: existing.setupLogPath || logPath || null,
|
|
286
|
+
setupFinishedAt: Date.now(),
|
|
287
|
+
failureInfo: {
|
|
288
|
+
type: 'setup',
|
|
289
|
+
error: error instanceof Error ? error.message : String(error),
|
|
290
|
+
timestamp: Date.now(),
|
|
291
|
+
},
|
|
292
|
+
provisional: true,
|
|
293
|
+
};
|
|
294
|
+
return clusters;
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
function sleep(ms: number): Promise<void> {
|
|
299
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
function formatLogHint(logPath?: string | null): string {
|
|
303
|
+
return logPath ? ` Check log: ${logPath}` : '';
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
async function waitForClusterRegistration({
|
|
307
|
+
clusterId,
|
|
308
|
+
timeoutMs,
|
|
309
|
+
pollMs = DEFAULT_WAIT_POLL_MS,
|
|
310
|
+
storageDir,
|
|
311
|
+
daemonPid,
|
|
312
|
+
logPath,
|
|
313
|
+
}: RegistrationWaitParams): Promise<{ ready: true; elapsedMs: number }> {
|
|
314
|
+
const effectiveTimeoutMs =
|
|
315
|
+
typeof timeoutMs === 'number' && Number.isFinite(timeoutMs) && timeoutMs > 0
|
|
316
|
+
? timeoutMs
|
|
317
|
+
: DEFAULT_WAIT_TIMEOUT_SECONDS * 1000;
|
|
318
|
+
const startedAt = Date.now();
|
|
319
|
+
const deadline = startedAt + effectiveTimeoutMs;
|
|
320
|
+
const logHint = formatLogHint(logPath);
|
|
321
|
+
|
|
322
|
+
while (Date.now() < deadline) {
|
|
323
|
+
if (isClusterRegistered(clusterId, storageDir)) {
|
|
324
|
+
return { ready: true, elapsedMs: Date.now() - startedAt };
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
if (daemonPid && !isProcessAlive(daemonPid)) {
|
|
328
|
+
throw new Error(
|
|
329
|
+
`Detached daemon exited before cluster "${clusterId}" registered in storage.${logHint}`
|
|
330
|
+
);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
await sleep(pollMs);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
const timeoutSeconds = Math.ceil(effectiveTimeoutMs / 1000);
|
|
337
|
+
throw new Error(
|
|
338
|
+
`Timed out after ${timeoutSeconds}s waiting for cluster "${clusterId}" to appear in status/list.${logHint}`
|
|
339
|
+
);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Atomically claim the right to resume-daemon an existing cluster record.
|
|
344
|
+
* Ownership for the handoff is tracked separately from cluster.pid/state so
|
|
345
|
+
* orchestrator.resume() can still perform its own eligibility check.
|
|
346
|
+
*/
|
|
347
|
+
async function patchDetachedResumeCluster({
|
|
348
|
+
clusterId,
|
|
349
|
+
daemonPid,
|
|
350
|
+
storageDir,
|
|
351
|
+
}: ResumePatchParams): Promise<void> {
|
|
352
|
+
await updateClustersFile(storageDir, (clusters) => {
|
|
353
|
+
const existing = clusters[clusterId];
|
|
354
|
+
if (!existing) {
|
|
355
|
+
throw new Error(`Cannot start resume daemon: cluster "${clusterId}" not found in registry`);
|
|
356
|
+
}
|
|
357
|
+
if (existing.resumeDaemonPid && isProcessRunning(existing.resumeDaemonPid)) {
|
|
358
|
+
const ownership = `Cluster "${clusterId}" already has a live resume daemon`;
|
|
359
|
+
throw new Error(
|
|
360
|
+
`${ownership} (PID ${existing.resumeDaemonPid}); refusing to start a second one`
|
|
361
|
+
);
|
|
362
|
+
}
|
|
363
|
+
if (existing.state === 'running' && existing.pid && isProcessRunning(existing.pid)) {
|
|
364
|
+
throw new Error(
|
|
365
|
+
`Cluster "${clusterId}" is already running (PID ${existing.pid}); refusing to start a second resume daemon`
|
|
366
|
+
);
|
|
367
|
+
}
|
|
368
|
+
clusters[clusterId] = { ...existing, resumeDaemonPid: daemonPid };
|
|
369
|
+
return clusters;
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Undo a resume handoff that did not complete so a dead daemon cannot leave
|
|
375
|
+
* the cluster stuck in a running state.
|
|
376
|
+
*/
|
|
377
|
+
async function revertDetachedResumeCluster({
|
|
378
|
+
clusterId,
|
|
379
|
+
storageDir,
|
|
380
|
+
error,
|
|
381
|
+
}: SetupFailureParams): Promise<void> {
|
|
382
|
+
await updateClustersFile(storageDir, (clusters) => {
|
|
383
|
+
const existing = clusters[clusterId];
|
|
384
|
+
if (!existing) return clusters;
|
|
385
|
+
clusters[clusterId] = {
|
|
386
|
+
...existing,
|
|
387
|
+
state: 'failed',
|
|
388
|
+
pid: null,
|
|
389
|
+
resumeDaemonPid: null,
|
|
390
|
+
failureInfo: {
|
|
391
|
+
type: 'resume-daemon',
|
|
392
|
+
error: error instanceof Error ? error.message : String(error),
|
|
393
|
+
timestamp: Date.now(),
|
|
394
|
+
},
|
|
395
|
+
};
|
|
396
|
+
return clusters;
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
function getRegisteredResumeDaemonPid(clusterId: string, storageDir?: string): number | null {
|
|
401
|
+
const clustersFile = getClustersFilePath(storageDir);
|
|
402
|
+
if (!fs.existsSync(clustersFile)) {
|
|
403
|
+
return null;
|
|
404
|
+
}
|
|
405
|
+
try {
|
|
406
|
+
const parsed: unknown = JSON.parse(fs.readFileSync(clustersFile, 'utf8'));
|
|
407
|
+
if (!isClusterRegistry(parsed)) {
|
|
408
|
+
return null;
|
|
409
|
+
}
|
|
410
|
+
return parsed[clusterId]?.resumeDaemonPid ?? null;
|
|
411
|
+
} catch {
|
|
412
|
+
return null;
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/**
|
|
417
|
+
* Daemon-side half of the resume handoff: wait until the registry shows this
|
|
418
|
+
* process's PID as resumeDaemonPid before touching the cluster.
|
|
419
|
+
*/
|
|
420
|
+
async function waitForResumeOwnership({
|
|
421
|
+
clusterId,
|
|
422
|
+
daemonPid,
|
|
423
|
+
storageDir,
|
|
424
|
+
timeoutMs = DEFAULT_OWNERSHIP_TIMEOUT_MS,
|
|
425
|
+
pollMs = DEFAULT_WAIT_POLL_MS,
|
|
426
|
+
}: OwnershipWaitParams): Promise<boolean> {
|
|
427
|
+
const deadline = Date.now() + timeoutMs;
|
|
428
|
+
while (Date.now() < deadline) {
|
|
429
|
+
if (getRegisteredResumeDaemonPid(clusterId, storageDir) === daemonPid) {
|
|
430
|
+
return true;
|
|
431
|
+
}
|
|
432
|
+
await sleep(pollMs);
|
|
433
|
+
}
|
|
434
|
+
return getRegisteredResumeDaemonPid(clusterId, storageDir) === daemonPid;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
export = {
|
|
438
|
+
DEFAULT_WAIT_TIMEOUT_SECONDS,
|
|
439
|
+
getClustersFilePath,
|
|
440
|
+
getRegisteredResumeDaemonPid,
|
|
441
|
+
isClusterRegistered,
|
|
442
|
+
isProcessAlive,
|
|
443
|
+
markDetachedSetupFailed,
|
|
444
|
+
patchDetachedResumeCluster,
|
|
445
|
+
registerDetachedSetupCluster,
|
|
446
|
+
removeDetachedSetupCluster,
|
|
447
|
+
resolveWaitTimeoutMs,
|
|
448
|
+
revertDetachedResumeCluster,
|
|
449
|
+
waitForClusterRegistration,
|
|
450
|
+
waitForResumeOwnership,
|
|
451
|
+
};
|