@aiden-ade/sandbox-agent 0.1.59 → 0.1.60
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/dist/__tests__/binding-switch-git.test.d.ts +2 -0
- package/dist/__tests__/binding-switch-git.test.d.ts.map +1 -0
- package/dist/__tests__/binding-switch-git.test.js +610 -0
- package/dist/__tests__/binding-switch-git.test.js.map +1 -0
- package/dist/__tests__/cli-ensure-install.test.d.ts +2 -0
- package/dist/__tests__/cli-ensure-install.test.d.ts.map +1 -0
- package/dist/__tests__/cli-ensure-install.test.js +109 -0
- package/dist/__tests__/cli-ensure-install.test.js.map +1 -0
- package/dist/__tests__/cli-executable.test.d.ts +2 -0
- package/dist/__tests__/cli-executable.test.d.ts.map +1 -0
- package/dist/__tests__/cli-executable.test.js +129 -0
- package/dist/__tests__/cli-executable.test.js.map +1 -0
- package/dist/__tests__/cli-help.test.d.ts +2 -0
- package/dist/__tests__/cli-help.test.d.ts.map +1 -0
- package/dist/__tests__/cli-help.test.js +34 -0
- package/dist/__tests__/cli-help.test.js.map +1 -0
- package/dist/__tests__/computer-readiness.test.d.ts +2 -0
- package/dist/__tests__/computer-readiness.test.d.ts.map +1 -0
- package/dist/__tests__/computer-readiness.test.js +63 -0
- package/dist/__tests__/computer-readiness.test.js.map +1 -0
- package/dist/__tests__/core-agent-workflow-context.test.d.ts +2 -0
- package/dist/__tests__/core-agent-workflow-context.test.d.ts.map +1 -0
- package/dist/__tests__/core-agent-workflow-context.test.js +139 -0
- package/dist/__tests__/core-agent-workflow-context.test.js.map +1 -0
- package/dist/__tests__/daemon-reconcile-ask.test.d.ts +2 -0
- package/dist/__tests__/daemon-reconcile-ask.test.d.ts.map +1 -0
- package/dist/__tests__/daemon-reconcile-ask.test.js +86 -0
- package/dist/__tests__/daemon-reconcile-ask.test.js.map +1 -0
- package/dist/__tests__/daemon-socket.integration.test.d.ts +2 -0
- package/dist/__tests__/daemon-socket.integration.test.d.ts.map +1 -0
- package/dist/__tests__/daemon-socket.integration.test.js +450 -0
- package/dist/__tests__/daemon-socket.integration.test.js.map +1 -0
- package/dist/__tests__/daemon.test.d.ts +2 -0
- package/dist/__tests__/daemon.test.d.ts.map +1 -0
- package/dist/__tests__/daemon.test.js +3975 -0
- package/dist/__tests__/daemon.test.js.map +1 -0
- package/dist/__tests__/ensure-shared-checkout-branch.test.d.ts +2 -0
- package/dist/__tests__/ensure-shared-checkout-branch.test.d.ts.map +1 -0
- package/dist/__tests__/ensure-shared-checkout-branch.test.js +129 -0
- package/dist/__tests__/ensure-shared-checkout-branch.test.js.map +1 -0
- package/dist/__tests__/event-outbox-persist-failure.test.d.ts +2 -0
- package/dist/__tests__/event-outbox-persist-failure.test.d.ts.map +1 -0
- package/dist/__tests__/event-outbox-persist-failure.test.js +84 -0
- package/dist/__tests__/event-outbox-persist-failure.test.js.map +1 -0
- package/dist/__tests__/event-outbox.test.d.ts +2 -0
- package/dist/__tests__/event-outbox.test.d.ts.map +1 -0
- package/dist/__tests__/event-outbox.test.js +256 -0
- package/dist/__tests__/event-outbox.test.js.map +1 -0
- package/dist/__tests__/fake-provider-cli.test.d.ts +2 -0
- package/dist/__tests__/fake-provider-cli.test.d.ts.map +1 -0
- package/dist/__tests__/fake-provider-cli.test.js +46 -0
- package/dist/__tests__/fake-provider-cli.test.js.map +1 -0
- package/dist/__tests__/generated-images.test.d.ts +2 -0
- package/dist/__tests__/generated-images.test.d.ts.map +1 -0
- package/dist/__tests__/generated-images.test.js +132 -0
- package/dist/__tests__/generated-images.test.js.map +1 -0
- package/dist/__tests__/lifecycle-logger.test.d.ts +2 -0
- package/dist/__tests__/lifecycle-logger.test.d.ts.map +1 -0
- package/dist/__tests__/lifecycle-logger.test.js +49 -0
- package/dist/__tests__/lifecycle-logger.test.js.map +1 -0
- package/dist/__tests__/local-skill-scan.test.d.ts +2 -0
- package/dist/__tests__/local-skill-scan.test.d.ts.map +1 -0
- package/dist/__tests__/local-skill-scan.test.js +91 -0
- package/dist/__tests__/local-skill-scan.test.js.map +1 -0
- package/dist/__tests__/local-workspace-migration.test.d.ts +2 -0
- package/dist/__tests__/local-workspace-migration.test.d.ts.map +1 -0
- package/dist/__tests__/local-workspace-migration.test.js +195 -0
- package/dist/__tests__/local-workspace-migration.test.js.map +1 -0
- package/dist/__tests__/mcp-inspect-timeout.test.d.ts +2 -0
- package/dist/__tests__/mcp-inspect-timeout.test.d.ts.map +1 -0
- package/dist/__tests__/mcp-inspect-timeout.test.js +97 -0
- package/dist/__tests__/mcp-inspect-timeout.test.js.map +1 -0
- package/dist/__tests__/mcp-readiness.test.d.ts +2 -0
- package/dist/__tests__/mcp-readiness.test.d.ts.map +1 -0
- package/dist/__tests__/mcp-readiness.test.js +267 -0
- package/dist/__tests__/mcp-readiness.test.js.map +1 -0
- package/dist/__tests__/mcp-registration-cli.test.d.ts +2 -0
- package/dist/__tests__/mcp-registration-cli.test.d.ts.map +1 -0
- package/dist/__tests__/mcp-registration-cli.test.js +71 -0
- package/dist/__tests__/mcp-registration-cli.test.js.map +1 -0
- package/dist/__tests__/mcp-registration.test.d.ts +2 -0
- package/dist/__tests__/mcp-registration.test.d.ts.map +1 -0
- package/dist/__tests__/mcp-registration.test.js +426 -0
- package/dist/__tests__/mcp-registration.test.js.map +1 -0
- package/dist/__tests__/model-scan-runner.test.d.ts +2 -0
- package/dist/__tests__/model-scan-runner.test.d.ts.map +1 -0
- package/dist/__tests__/model-scan-runner.test.js +67 -0
- package/dist/__tests__/model-scan-runner.test.js.map +1 -0
- package/dist/__tests__/network-diagnostics.test.d.ts +2 -0
- package/dist/__tests__/network-diagnostics.test.d.ts.map +1 -0
- package/dist/__tests__/network-diagnostics.test.js +26 -0
- package/dist/__tests__/network-diagnostics.test.js.map +1 -0
- package/dist/__tests__/observable-map.test.d.ts +2 -0
- package/dist/__tests__/observable-map.test.d.ts.map +1 -0
- package/dist/__tests__/observable-map.test.js +54 -0
- package/dist/__tests__/observable-map.test.js.map +1 -0
- package/dist/__tests__/provider-auth-preflight.test.d.ts +2 -0
- package/dist/__tests__/provider-auth-preflight.test.d.ts.map +1 -0
- package/dist/__tests__/provider-auth-preflight.test.js +123 -0
- package/dist/__tests__/provider-auth-preflight.test.js.map +1 -0
- package/dist/__tests__/provider-model-discovery.test.d.ts +2 -0
- package/dist/__tests__/provider-model-discovery.test.d.ts.map +1 -0
- package/dist/__tests__/provider-model-discovery.test.js +472 -0
- package/dist/__tests__/provider-model-discovery.test.js.map +1 -0
- package/dist/__tests__/remove-worktrees-if-clean.test.d.ts +2 -0
- package/dist/__tests__/remove-worktrees-if-clean.test.d.ts.map +1 -0
- package/dist/__tests__/remove-worktrees-if-clean.test.js +221 -0
- package/dist/__tests__/remove-worktrees-if-clean.test.js.map +1 -0
- package/dist/__tests__/resume-fallback-env.test.d.ts +2 -0
- package/dist/__tests__/resume-fallback-env.test.d.ts.map +1 -0
- package/dist/__tests__/resume-fallback-env.test.js +28 -0
- package/dist/__tests__/resume-fallback-env.test.js.map +1 -0
- package/dist/__tests__/run-journal.test.d.ts +2 -0
- package/dist/__tests__/run-journal.test.d.ts.map +1 -0
- package/dist/__tests__/run-journal.test.js +121 -0
- package/dist/__tests__/run-journal.test.js.map +1 -0
- package/dist/__tests__/run-start-gate.test.d.ts +2 -0
- package/dist/__tests__/run-start-gate.test.d.ts.map +1 -0
- package/dist/__tests__/run-start-gate.test.js +29 -0
- package/dist/__tests__/run-start-gate.test.js.map +1 -0
- package/dist/__tests__/runtime-capabilities-sync.test.d.ts +2 -0
- package/dist/__tests__/runtime-capabilities-sync.test.d.ts.map +1 -0
- package/dist/__tests__/runtime-capabilities-sync.test.js +66 -0
- package/dist/__tests__/runtime-capabilities-sync.test.js.map +1 -0
- package/dist/__tests__/runtime-config.test.d.ts +2 -0
- package/dist/__tests__/runtime-config.test.d.ts.map +1 -0
- package/dist/__tests__/runtime-config.test.js +102 -0
- package/dist/__tests__/runtime-config.test.js.map +1 -0
- package/dist/__tests__/runtime-presenter-heartbeat.test.d.ts +2 -0
- package/dist/__tests__/runtime-presenter-heartbeat.test.d.ts.map +1 -0
- package/dist/__tests__/runtime-presenter-heartbeat.test.js +159 -0
- package/dist/__tests__/runtime-presenter-heartbeat.test.js.map +1 -0
- package/dist/__tests__/sandbox-outbox.test.d.ts +2 -0
- package/dist/__tests__/sandbox-outbox.test.d.ts.map +1 -0
- package/dist/__tests__/sandbox-outbox.test.js +214 -0
- package/dist/__tests__/sandbox-outbox.test.js.map +1 -0
- package/dist/__tests__/sandbox-workflow-context.test.d.ts +2 -0
- package/dist/__tests__/sandbox-workflow-context.test.d.ts.map +1 -0
- package/dist/__tests__/sandbox-workflow-context.test.js +396 -0
- package/dist/__tests__/sandbox-workflow-context.test.js.map +1 -0
- package/dist/__tests__/service-manager.test.d.ts +2 -0
- package/dist/__tests__/service-manager.test.d.ts.map +1 -0
- package/dist/__tests__/service-manager.test.js +50 -0
- package/dist/__tests__/service-manager.test.js.map +1 -0
- package/dist/__tests__/sleep-inhibitor.test.d.ts +2 -0
- package/dist/__tests__/sleep-inhibitor.test.d.ts.map +1 -0
- package/dist/__tests__/sleep-inhibitor.test.js +236 -0
- package/dist/__tests__/sleep-inhibitor.test.js.map +1 -0
- package/dist/__tests__/status-local-workspace.test.d.ts +2 -0
- package/dist/__tests__/status-local-workspace.test.d.ts.map +1 -0
- package/dist/__tests__/status-local-workspace.test.js +121 -0
- package/dist/__tests__/status-local-workspace.test.js.map +1 -0
- package/dist/__tests__/suspension-detector.test.d.ts +2 -0
- package/dist/__tests__/suspension-detector.test.d.ts.map +1 -0
- package/dist/__tests__/suspension-detector.test.js +112 -0
- package/dist/__tests__/suspension-detector.test.js.map +1 -0
- package/dist/__tests__/web-presenter-parent-events.test.d.ts +2 -0
- package/dist/__tests__/web-presenter-parent-events.test.d.ts.map +1 -0
- package/dist/__tests__/web-presenter-parent-events.test.js +130 -0
- package/dist/__tests__/web-presenter-parent-events.test.js.map +1 -0
- package/dist/__tests__/workspace-relocation.test.d.ts +2 -0
- package/dist/__tests__/workspace-relocation.test.d.ts.map +1 -0
- package/dist/__tests__/workspace-relocation.test.js +66 -0
- package/dist/__tests__/workspace-relocation.test.js.map +1 -0
- package/dist/__tests__/workspace-run-lease.test.d.ts +2 -0
- package/dist/__tests__/workspace-run-lease.test.d.ts.map +1 -0
- package/dist/__tests__/workspace-run-lease.test.js +230 -0
- package/dist/__tests__/workspace-run-lease.test.js.map +1 -0
- package/dist/__tests__/ws-client-heartbeat-volatile.test.d.ts +2 -0
- package/dist/__tests__/ws-client-heartbeat-volatile.test.d.ts.map +1 -0
- package/dist/__tests__/ws-client-heartbeat-volatile.test.js +61 -0
- package/dist/__tests__/ws-client-heartbeat-volatile.test.js.map +1 -0
- package/dist/__tests__/ws-client-heartbeat.integration.test.d.ts +2 -0
- package/dist/__tests__/ws-client-heartbeat.integration.test.d.ts.map +1 -0
- package/dist/__tests__/ws-client-heartbeat.integration.test.js +109 -0
- package/dist/__tests__/ws-client-heartbeat.integration.test.js.map +1 -0
- package/dist/__tests__/ws-client-user-message-ack.integration.test.d.ts +2 -0
- package/dist/__tests__/ws-client-user-message-ack.integration.test.d.ts.map +1 -0
- package/dist/__tests__/ws-client-user-message-ack.integration.test.js +167 -0
- package/dist/__tests__/ws-client-user-message-ack.integration.test.js.map +1 -0
- package/dist/aiden-system-prompt.d.ts +6 -0
- package/dist/aiden-system-prompt.d.ts.map +1 -0
- package/dist/aiden-system-prompt.js +6 -0
- package/dist/aiden-system-prompt.js.map +1 -0
- package/dist/binding-switch-git.d.ts +26 -0
- package/dist/binding-switch-git.d.ts.map +1 -0
- package/dist/binding-switch-git.js +973 -0
- package/dist/binding-switch-git.js.map +1 -0
- package/dist/cli-ensure-install.d.ts +39 -0
- package/dist/cli-ensure-install.d.ts.map +1 -0
- package/dist/cli-ensure-install.js +133 -0
- package/dist/cli-ensure-install.js.map +1 -0
- package/dist/cli-executable.d.ts +97 -0
- package/dist/cli-executable.d.ts.map +1 -0
- package/dist/cli-executable.js +432 -0
- package/dist/cli-executable.js.map +1 -0
- package/dist/cli-help.d.ts +7 -0
- package/dist/cli-help.d.ts.map +1 -0
- package/dist/cli-help.js +49 -0
- package/dist/cli-help.js.map +1 -0
- package/dist/computer-readiness.d.ts +42 -0
- package/dist/computer-readiness.d.ts.map +1 -0
- package/dist/computer-readiness.js +191 -0
- package/dist/computer-readiness.js.map +1 -0
- package/dist/core-agent.d.ts +47 -0
- package/dist/core-agent.d.ts.map +1 -0
- package/dist/core-agent.js +364 -0
- package/dist/core-agent.js.map +1 -0
- package/dist/daemon.d.ts +392 -0
- package/dist/daemon.d.ts.map +1 -0
- package/dist/daemon.js +4711 -0
- package/dist/daemon.js.map +1 -0
- package/dist/event-outbox.d.ts +83 -0
- package/dist/event-outbox.d.ts.map +1 -0
- package/dist/event-outbox.js +440 -0
- package/dist/event-outbox.js.map +1 -0
- package/dist/fake-provider-cli.d.ts +13 -0
- package/dist/fake-provider-cli.d.ts.map +1 -0
- package/dist/fake-provider-cli.js +53 -0
- package/dist/fake-provider-cli.js.map +1 -0
- package/dist/generated-images.d.ts +2 -0
- package/dist/generated-images.d.ts.map +1 -0
- package/dist/generated-images.js +2 -0
- package/dist/generated-images.js.map +1 -0
- package/dist/index.cjs +129 -29
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +209 -0
- package/dist/index.js.map +1 -0
- package/dist/lifecycle-logger.d.ts +25 -0
- package/dist/lifecycle-logger.d.ts.map +1 -0
- package/dist/lifecycle-logger.js +73 -0
- package/dist/lifecycle-logger.js.map +1 -0
- package/dist/local-skill-scan.d.ts +21 -0
- package/dist/local-skill-scan.d.ts.map +1 -0
- package/dist/local-skill-scan.js +109 -0
- package/dist/local-skill-scan.js.map +1 -0
- package/dist/local-workspace-migration.d.ts +68 -0
- package/dist/local-workspace-migration.d.ts.map +1 -0
- package/dist/local-workspace-migration.js +157 -0
- package/dist/local-workspace-migration.js.map +1 -0
- package/dist/mcp-readiness.d.ts +59 -0
- package/dist/mcp-readiness.d.ts.map +1 -0
- package/dist/mcp-readiness.js +202 -0
- package/dist/mcp-readiness.js.map +1 -0
- package/dist/mcp-registration.d.ts +75 -0
- package/dist/mcp-registration.d.ts.map +1 -0
- package/dist/mcp-registration.js +563 -0
- package/dist/mcp-registration.js.map +1 -0
- package/dist/model-scan-runner.d.ts +15 -0
- package/dist/model-scan-runner.d.ts.map +1 -0
- package/dist/model-scan-runner.js +51 -0
- package/dist/model-scan-runner.js.map +1 -0
- package/dist/network-diagnostics.d.ts +8 -0
- package/dist/network-diagnostics.d.ts.map +1 -0
- package/dist/network-diagnostics.js +56 -0
- package/dist/network-diagnostics.js.map +1 -0
- package/dist/observable-map.d.ts +25 -0
- package/dist/observable-map.d.ts.map +1 -0
- package/dist/observable-map.js +42 -0
- package/dist/observable-map.js.map +1 -0
- package/dist/provider-auth-preflight.d.ts +22 -0
- package/dist/provider-auth-preflight.d.ts.map +1 -0
- package/dist/provider-auth-preflight.js +110 -0
- package/dist/provider-auth-preflight.js.map +1 -0
- package/dist/provider-model-discovery.d.ts +38 -0
- package/dist/provider-model-discovery.d.ts.map +1 -0
- package/dist/provider-model-discovery.js +519 -0
- package/dist/provider-model-discovery.js.map +1 -0
- package/dist/resume-fallback-env.d.ts +20 -0
- package/dist/resume-fallback-env.d.ts.map +1 -0
- package/dist/resume-fallback-env.js +30 -0
- package/dist/resume-fallback-env.js.map +1 -0
- package/dist/run-journal.d.ts +53 -0
- package/dist/run-journal.d.ts.map +1 -0
- package/dist/run-journal.js +193 -0
- package/dist/run-journal.js.map +1 -0
- package/dist/run-start-gate.d.ts +9 -0
- package/dist/run-start-gate.d.ts.map +1 -0
- package/dist/run-start-gate.js +24 -0
- package/dist/run-start-gate.js.map +1 -0
- package/dist/runtime-backends.d.ts +2 -0
- package/dist/runtime-backends.d.ts.map +1 -0
- package/dist/runtime-backends.js +2 -0
- package/dist/runtime-backends.js.map +1 -0
- package/dist/runtime-capabilities-sync.d.ts +33 -0
- package/dist/runtime-capabilities-sync.d.ts.map +1 -0
- package/dist/runtime-capabilities-sync.js +56 -0
- package/dist/runtime-capabilities-sync.js.map +1 -0
- package/dist/runtime-config.d.ts +24 -0
- package/dist/runtime-config.d.ts.map +1 -0
- package/dist/runtime-config.js +124 -0
- package/dist/runtime-config.js.map +1 -0
- package/dist/sandbox-outbox.d.ts +122 -0
- package/dist/sandbox-outbox.d.ts.map +1 -0
- package/dist/sandbox-outbox.js +180 -0
- package/dist/sandbox-outbox.js.map +1 -0
- package/dist/sandbox.d.ts +44 -0
- package/dist/sandbox.d.ts.map +1 -0
- package/dist/sandbox.js +438 -0
- package/dist/sandbox.js.map +1 -0
- package/dist/service-manager.d.ts +28 -0
- package/dist/service-manager.d.ts.map +1 -0
- package/dist/service-manager.js +259 -0
- package/dist/service-manager.js.map +1 -0
- package/dist/sleep-inhibitor.d.ts +111 -0
- package/dist/sleep-inhibitor.d.ts.map +1 -0
- package/dist/sleep-inhibitor.js +412 -0
- package/dist/sleep-inhibitor.js.map +1 -0
- package/dist/suspension-detector.d.ts +48 -0
- package/dist/suspension-detector.d.ts.map +1 -0
- package/dist/suspension-detector.js +89 -0
- package/dist/suspension-detector.js.map +1 -0
- package/dist/types.d.ts +6 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -0
- package/dist/updater.d.ts +15 -0
- package/dist/updater.d.ts.map +1 -0
- package/dist/updater.js +50 -0
- package/dist/updater.js.map +1 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +2 -0
- package/dist/version.js.map +1 -0
- package/dist/web-presenter.d.ts +55 -0
- package/dist/web-presenter.d.ts.map +1 -0
- package/dist/web-presenter.js +265 -0
- package/dist/web-presenter.js.map +1 -0
- package/dist/workspace-relocation.d.ts +22 -0
- package/dist/workspace-relocation.d.ts.map +1 -0
- package/dist/workspace-relocation.js +90 -0
- package/dist/workspace-relocation.js.map +1 -0
- package/dist/workspace-run-lease.d.ts +97 -0
- package/dist/workspace-run-lease.d.ts.map +1 -0
- package/dist/workspace-run-lease.js +241 -0
- package/dist/workspace-run-lease.js.map +1 -0
- package/dist/ws-client.d.ts +194 -0
- package/dist/ws-client.d.ts.map +1 -0
- package/dist/ws-client.js +282 -0
- package/dist/ws-client.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,412 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-platform sleep prevention for the duration of active local agent runs.
|
|
3
|
+
*
|
|
4
|
+
* Why this exists: a local run executes in-process with the daemon. When the
|
|
5
|
+
* machine idle-sleeps, the socket, the runtime heartbeat and the provider CLI
|
|
6
|
+
* all freeze together; the backend watchdog then fails the run as "Agent
|
|
7
|
+
* unresponsive". Holding an OS-level sleep assertion while at least one run is
|
|
8
|
+
* active removes the most common cause of that failure.
|
|
9
|
+
*
|
|
10
|
+
* Design constraints learned the hard way:
|
|
11
|
+
*
|
|
12
|
+
* - **Never throw, never reject a run.** Sleep prevention is best-effort. A
|
|
13
|
+
* missing `caffeinate`/`systemd-inhibit` binary, a sandboxed environment, or
|
|
14
|
+
* a spawn failure must degrade to "machine may sleep", never to a failed run.
|
|
15
|
+
* - **Self-heal.** The helper process can die mid-run (OOM, user kill, session
|
|
16
|
+
* teardown). Acquiring once at run start is not enough, so a low-frequency
|
|
17
|
+
* watchdog re-checks and respawns while runs are active.
|
|
18
|
+
* - **Never outlive the daemon.** A leaked `caffeinate` keeps a laptop awake
|
|
19
|
+
* forever, which is a far worse bug than the one being fixed. Every backend
|
|
20
|
+
* is tied to this process's lifetime by the OS itself (see below), in
|
|
21
|
+
* addition to explicit release.
|
|
22
|
+
*
|
|
23
|
+
* Platform coverage:
|
|
24
|
+
*
|
|
25
|
+
* | Platform | Mechanism |
|
|
26
|
+
* |----------|------------------------------------------------------|
|
|
27
|
+
* | macOS | `caffeinate -i -s -w <pid>` |
|
|
28
|
+
* | Linux | `systemd-inhibit`, falling back to `gnome-session-inhibit` |
|
|
29
|
+
* | Windows | desktop: Electron's `powerSaveBlocker`, registered externally. Standalone/headless daemon (e.g. installed as a Windows Scheduled Task via service-manager.ts): a PowerShell `SetThreadExecutionState` helper, used only when nothing else is already registered |
|
|
30
|
+
*
|
|
31
|
+
* `-w <pid>` (macOS) and the `kill -0`/`Get-Process` guard loops (Linux,
|
|
32
|
+
* Windows) make the OS release the assertion when *this* process exits, even
|
|
33
|
+
* on SIGKILL where no JS cleanup handler can run. That is the Node-reachable
|
|
34
|
+
* equivalent of the `PR_SET_PDEATHSIG` guard the Codex sleep-inhibitor uses.
|
|
35
|
+
*
|
|
36
|
+
* Known limitation: on macOS the `-s` assertion is only honoured while the
|
|
37
|
+
* machine is on AC power (see `man caffeinate`). A laptop on battery with the
|
|
38
|
+
* lid closed will still sleep, and no userspace assertion can prevent that.
|
|
39
|
+
* Surviving that case is the job of wake-recovery, not of this module.
|
|
40
|
+
*/
|
|
41
|
+
import { spawn } from "node:child_process";
|
|
42
|
+
/**
|
|
43
|
+
* Set to `1`/`true` to disable sleep prevention entirely. An escape hatch for
|
|
44
|
+
* users who would rather have their machine sleep than have runs survive.
|
|
45
|
+
*/
|
|
46
|
+
export const SLEEP_PREVENTION_DISABLED_ENV = "ALAN_DISABLE_SLEEP_PREVENTION";
|
|
47
|
+
/** How often to verify the OS assertion is still held while runs are active. */
|
|
48
|
+
const HEALTH_CHECK_INTERVAL_MS = 30_000;
|
|
49
|
+
const noopLog = () => { };
|
|
50
|
+
export function isSleepPreventionDisabled(env = process.env) {
|
|
51
|
+
const raw = env[SLEEP_PREVENTION_DISABLED_ENV];
|
|
52
|
+
if (raw === undefined)
|
|
53
|
+
return false;
|
|
54
|
+
const normalized = raw.trim().toLowerCase();
|
|
55
|
+
return normalized === "1" || normalized === "true" || normalized === "yes";
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Backend driven by a long-lived helper process.
|
|
59
|
+
*
|
|
60
|
+
* The assertion is held for exactly as long as the helper lives, so lifecycle
|
|
61
|
+
* management reduces to "is the child still running?".
|
|
62
|
+
*/
|
|
63
|
+
class ChildProcessBackend {
|
|
64
|
+
child = null;
|
|
65
|
+
/**
|
|
66
|
+
* Set once a candidate command is proven absent (ENOENT). Prevents a spawn
|
|
67
|
+
* storm from the health watchdog on machines that simply lack the binary.
|
|
68
|
+
*/
|
|
69
|
+
unavailable = false;
|
|
70
|
+
log;
|
|
71
|
+
constructor(log) {
|
|
72
|
+
this.log = log;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Let a multi-candidate backend react when its active helper terminates.
|
|
76
|
+
* This is not called for an intentional release because `release()` clears
|
|
77
|
+
* the active child before signalling it.
|
|
78
|
+
*/
|
|
79
|
+
onUnexpectedTermination() { }
|
|
80
|
+
isHealthy() {
|
|
81
|
+
if (this.unavailable)
|
|
82
|
+
return true; // nothing to heal; already degraded and logged
|
|
83
|
+
const child = this.child;
|
|
84
|
+
return child !== null && child.exitCode === null && child.signalCode === null;
|
|
85
|
+
}
|
|
86
|
+
acquire() {
|
|
87
|
+
if (this.unavailable)
|
|
88
|
+
return;
|
|
89
|
+
if (this.isHealthy() && this.child !== null)
|
|
90
|
+
return;
|
|
91
|
+
// A dead child is cleared before respawning so a failed spawn cannot leave
|
|
92
|
+
// a stale handle that later reports healthy.
|
|
93
|
+
this.child = null;
|
|
94
|
+
const command = this.command();
|
|
95
|
+
if (command === null) {
|
|
96
|
+
this.unavailable = true;
|
|
97
|
+
this.log(`sleep-inhibitor ${this.name} unavailable: no supported command`);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
let child;
|
|
101
|
+
try {
|
|
102
|
+
child = spawn(command.file, command.args, { stdio: "ignore", detached: false });
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
this.unavailable = true;
|
|
106
|
+
this.log(`sleep-inhibitor ${this.name} spawn threw: ${describeError(error)}`);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
child.on("error", (error) => {
|
|
110
|
+
const wasActiveChild = this.child === child;
|
|
111
|
+
if (wasActiveChild)
|
|
112
|
+
this.child = null;
|
|
113
|
+
// ENOENT means the binary does not exist on this machine — retrying is
|
|
114
|
+
// pointless. Any other error may be transient, so stay eligible.
|
|
115
|
+
if (error.code === "ENOENT")
|
|
116
|
+
this.unavailable = true;
|
|
117
|
+
this.log(`sleep-inhibitor ${this.name} failed: ${describeError(error)}`);
|
|
118
|
+
if (wasActiveChild)
|
|
119
|
+
this.onUnexpectedTermination();
|
|
120
|
+
});
|
|
121
|
+
child.on("exit", (code, signal) => {
|
|
122
|
+
const wasActiveChild = this.child === child;
|
|
123
|
+
if (wasActiveChild)
|
|
124
|
+
this.child = null;
|
|
125
|
+
this.log(`sleep-inhibitor ${this.name} exited code=${code} signal=${signal}`);
|
|
126
|
+
if (wasActiveChild)
|
|
127
|
+
this.onUnexpectedTermination();
|
|
128
|
+
});
|
|
129
|
+
this.child = child;
|
|
130
|
+
this.log(`sleep-inhibitor ${this.name} acquired pid=${child.pid}`);
|
|
131
|
+
}
|
|
132
|
+
release() {
|
|
133
|
+
const child = this.child;
|
|
134
|
+
if (child === null)
|
|
135
|
+
return;
|
|
136
|
+
this.child = null;
|
|
137
|
+
try {
|
|
138
|
+
child.kill("SIGTERM");
|
|
139
|
+
}
|
|
140
|
+
catch (error) {
|
|
141
|
+
this.log(`sleep-inhibitor ${this.name} release failed: ${describeError(error)}`);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* macOS: `caffeinate -i -s -w <pid>`.
|
|
147
|
+
*
|
|
148
|
+
* `-i` blocks idle system sleep, `-s` additionally blocks system sleep while on
|
|
149
|
+
* AC power, and `-w <pid>` makes the OS drop both the moment this process exits
|
|
150
|
+
* — the orphan guard that survives SIGKILL.
|
|
151
|
+
*/
|
|
152
|
+
class MacCaffeinateBackend extends ChildProcessBackend {
|
|
153
|
+
name = "caffeinate";
|
|
154
|
+
command() {
|
|
155
|
+
return { file: "/usr/bin/caffeinate", args: ["-i", "-s", "-w", String(process.pid)] };
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Linux: `systemd-inhibit`, falling back to `gnome-session-inhibit`.
|
|
160
|
+
*
|
|
161
|
+
* The inhibited command polls `kill -0 <pid>` so the helper self-terminates
|
|
162
|
+
* once this process is gone, mirroring the macOS `-w` guarantee.
|
|
163
|
+
*/
|
|
164
|
+
class LinuxIdleInhibitBackend extends ChildProcessBackend {
|
|
165
|
+
name = "systemd-inhibit";
|
|
166
|
+
candidateIndex = 0;
|
|
167
|
+
candidateCount = 2;
|
|
168
|
+
command() {
|
|
169
|
+
const guard = `while kill -0 ${process.pid} 2>/dev/null; do sleep 5; done`;
|
|
170
|
+
const candidates = [
|
|
171
|
+
{
|
|
172
|
+
file: "systemd-inhibit",
|
|
173
|
+
args: [
|
|
174
|
+
"--what=idle:sleep",
|
|
175
|
+
"--mode=block",
|
|
176
|
+
"--who=alan-agent",
|
|
177
|
+
"--why=Alan is running an active agent run",
|
|
178
|
+
"--",
|
|
179
|
+
"sh",
|
|
180
|
+
"-c",
|
|
181
|
+
guard,
|
|
182
|
+
],
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
file: "gnome-session-inhibit",
|
|
186
|
+
args: [
|
|
187
|
+
"--inhibit",
|
|
188
|
+
"idle",
|
|
189
|
+
"--reason",
|
|
190
|
+
"Alan is running an active agent run",
|
|
191
|
+
"sh",
|
|
192
|
+
"-c",
|
|
193
|
+
guard,
|
|
194
|
+
],
|
|
195
|
+
},
|
|
196
|
+
];
|
|
197
|
+
return candidates[this.candidateIndex] ?? null;
|
|
198
|
+
}
|
|
199
|
+
acquire() {
|
|
200
|
+
// Advance past a candidate that proved missing, then retry the next one.
|
|
201
|
+
const before = this.candidateIndex;
|
|
202
|
+
super.acquire();
|
|
203
|
+
if (this.unavailable && this.candidateIndex === before) {
|
|
204
|
+
this.tryNextCandidate();
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
onUnexpectedTermination() {
|
|
208
|
+
// `systemd-inhibit` can exist but still exit immediately when logind's
|
|
209
|
+
// system D-Bus is unavailable (containers, remote sessions, or partial
|
|
210
|
+
// desktop environments). Treat that the same as a missing binary and try
|
|
211
|
+
// the session-level GNOME inhibitor immediately.
|
|
212
|
+
this.tryNextCandidate();
|
|
213
|
+
}
|
|
214
|
+
tryNextCandidate() {
|
|
215
|
+
this.candidateIndex += 1;
|
|
216
|
+
if (this.candidateIndex >= this.candidateCount)
|
|
217
|
+
return;
|
|
218
|
+
this.unavailable = false;
|
|
219
|
+
super.acquire();
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Windows: a hidden PowerShell helper calling `SetThreadExecutionState`.
|
|
224
|
+
*
|
|
225
|
+
* `ES_CONTINUOUS | ES_SYSTEM_REQUIRED` (0x80000001) is the same request the
|
|
226
|
+
* daemon's own process would make if Node could call the Win32 API directly;
|
|
227
|
+
* P/Invoke via `Add-Type` is the subprocess-reachable equivalent, matching the
|
|
228
|
+
* macOS/Linux backends above. The state is scoped to the calling thread, so
|
|
229
|
+
* Windows clears it the instant this helper process exits — no separate
|
|
230
|
+
* release step is needed on top of killing the child, mirroring the `-w`/
|
|
231
|
+
* `kill -0` orphan guards on the other platforms.
|
|
232
|
+
*
|
|
233
|
+
* This only runs when nothing else already holds a Windows sleep assertion
|
|
234
|
+
* for this daemon (see `createPlatformBackends`) — the desktop app's Electron
|
|
235
|
+
* `powerSaveBlocker` covers the common case, and duplicating it would just
|
|
236
|
+
* spawn a redundant `powershell.exe` per run for no additional protection.
|
|
237
|
+
*/
|
|
238
|
+
class WindowsPowerRequestBackend extends ChildProcessBackend {
|
|
239
|
+
name = "windows-power-request";
|
|
240
|
+
command() {
|
|
241
|
+
const guard = `while (Get-Process -Id ${process.pid} -ErrorAction SilentlyContinue) ` +
|
|
242
|
+
"{ Start-Sleep -Seconds 5 }";
|
|
243
|
+
const script = [
|
|
244
|
+
"Add-Type -Namespace Alan -Name Power -MemberDefinition " +
|
|
245
|
+
"'[DllImport(\"kernel32.dll\")] public static extern uint SetThreadExecutionState(uint esFlags);';",
|
|
246
|
+
"[Alan.Power]::SetThreadExecutionState(0x80000001) | Out-Null;",
|
|
247
|
+
guard,
|
|
248
|
+
].join(" ");
|
|
249
|
+
return {
|
|
250
|
+
file: "powershell.exe",
|
|
251
|
+
args: ["-NoProfile", "-NonInteractive", "-WindowStyle", "Hidden", "-Command", script],
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Backends contributed by the embedding application.
|
|
257
|
+
*
|
|
258
|
+
* The desktop app registers Electron's `powerSaveBlocker` here. When present,
|
|
259
|
+
* it takes priority over `WindowsPowerRequestBackend` (see
|
|
260
|
+
* `createPlatformBackends`) so a desktop-wrapped run doesn't spawn a
|
|
261
|
+
* redundant PowerShell helper alongside it.
|
|
262
|
+
*/
|
|
263
|
+
const externalBackends = new Set();
|
|
264
|
+
/**
|
|
265
|
+
* Register an additional backend. Returns a disposer that unregisters it.
|
|
266
|
+
*
|
|
267
|
+
* Registration must happen before the daemon starts to affect its inhibitor;
|
|
268
|
+
* inhibitors snapshot the registry at construction time.
|
|
269
|
+
*/
|
|
270
|
+
export function registerSleepInhibitorBackend(backend) {
|
|
271
|
+
externalBackends.add(backend);
|
|
272
|
+
return () => {
|
|
273
|
+
externalBackends.delete(backend);
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
function createPlatformBackends(log, hasExternalBackend) {
|
|
277
|
+
switch (process.platform) {
|
|
278
|
+
case "darwin":
|
|
279
|
+
return [new MacCaffeinateBackend(log)];
|
|
280
|
+
case "linux":
|
|
281
|
+
return [new LinuxIdleInhibitBackend(log)];
|
|
282
|
+
case "win32":
|
|
283
|
+
// A registered external backend (desktop's powerSaveBlocker) already
|
|
284
|
+
// covers this; only spawn the standalone PowerShell helper when the
|
|
285
|
+
// daemon is running headless (e.g. as a Windows Scheduled Task via
|
|
286
|
+
// service-manager.ts) with nothing else holding a sleep assertion.
|
|
287
|
+
return hasExternalBackend ? [] : [new WindowsPowerRequestBackend(log)];
|
|
288
|
+
default:
|
|
289
|
+
return [];
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Holds an OS sleep assertion while `setActive(true)` is in effect.
|
|
294
|
+
*
|
|
295
|
+
* Mirrors the Codex `SleepInhibitor` contract: idempotent, self-healing, and
|
|
296
|
+
* silent on platforms where nothing is available.
|
|
297
|
+
*/
|
|
298
|
+
export class SleepInhibitor {
|
|
299
|
+
backends;
|
|
300
|
+
log;
|
|
301
|
+
enabled;
|
|
302
|
+
active = false;
|
|
303
|
+
healthTimer = null;
|
|
304
|
+
exitHandler = null;
|
|
305
|
+
disposed = false;
|
|
306
|
+
constructor(options = {}) {
|
|
307
|
+
this.log = options.log ?? noopLog;
|
|
308
|
+
this.enabled = !isSleepPreventionDisabled(options.env ?? process.env);
|
|
309
|
+
this.backends = this.enabled
|
|
310
|
+
? [...createPlatformBackends(this.log, externalBackends.size > 0), ...externalBackends]
|
|
311
|
+
: [];
|
|
312
|
+
if (!this.enabled) {
|
|
313
|
+
this.log(`sleep-inhibitor disabled via ${SLEEP_PREVENTION_DISABLED_ENV}`);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
isActive() {
|
|
317
|
+
return this.active;
|
|
318
|
+
}
|
|
319
|
+
/** Names of the backends this inhibitor will drive. Exposed for diagnostics. */
|
|
320
|
+
backendNames() {
|
|
321
|
+
return this.backends.map((backend) => backend.name);
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Turn sleep prevention on or off. Safe to call repeatedly with the same
|
|
325
|
+
* value; only edges do work.
|
|
326
|
+
*/
|
|
327
|
+
setActive(active) {
|
|
328
|
+
if (this.disposed)
|
|
329
|
+
return;
|
|
330
|
+
if (this.active === active) {
|
|
331
|
+
// Even on a repeated `true`, re-verify: a helper may have died since the
|
|
332
|
+
// last call and the watchdog may not have fired yet.
|
|
333
|
+
if (active)
|
|
334
|
+
this.healBackends();
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
this.active = active;
|
|
338
|
+
if (active)
|
|
339
|
+
this.start();
|
|
340
|
+
else
|
|
341
|
+
this.stop();
|
|
342
|
+
}
|
|
343
|
+
/** Release everything and detach timers/handlers. Idempotent. */
|
|
344
|
+
dispose() {
|
|
345
|
+
if (this.disposed)
|
|
346
|
+
return;
|
|
347
|
+
this.disposed = true;
|
|
348
|
+
this.active = false;
|
|
349
|
+
this.stop();
|
|
350
|
+
}
|
|
351
|
+
start() {
|
|
352
|
+
if (this.backends.length === 0)
|
|
353
|
+
return;
|
|
354
|
+
this.healBackends();
|
|
355
|
+
if (this.healthTimer === null) {
|
|
356
|
+
this.healthTimer = setInterval(() => this.healBackends(), HEALTH_CHECK_INTERVAL_MS);
|
|
357
|
+
// Never keep the process alive purely to hold a sleep assertion.
|
|
358
|
+
this.healthTimer.unref?.();
|
|
359
|
+
}
|
|
360
|
+
if (this.exitHandler === null) {
|
|
361
|
+
// Best-effort synchronous cleanup. The OS-level parent guards above are
|
|
362
|
+
// the real safety net; this just releases promptly on a clean exit.
|
|
363
|
+
this.exitHandler = () => {
|
|
364
|
+
for (const backend of this.backends)
|
|
365
|
+
backend.release();
|
|
366
|
+
};
|
|
367
|
+
process.on("exit", this.exitHandler);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
stop() {
|
|
371
|
+
if (this.healthTimer !== null) {
|
|
372
|
+
clearInterval(this.healthTimer);
|
|
373
|
+
this.healthTimer = null;
|
|
374
|
+
}
|
|
375
|
+
if (this.exitHandler !== null) {
|
|
376
|
+
process.removeListener("exit", this.exitHandler);
|
|
377
|
+
this.exitHandler = null;
|
|
378
|
+
}
|
|
379
|
+
for (const backend of this.backends) {
|
|
380
|
+
try {
|
|
381
|
+
backend.release();
|
|
382
|
+
}
|
|
383
|
+
catch (error) {
|
|
384
|
+
this.log(`sleep-inhibitor release error: ${describeError(error)}`);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
/**
|
|
389
|
+
* Acquire anything not currently held. This is the self-healing step: a
|
|
390
|
+
* backend whose helper died is respawned here.
|
|
391
|
+
*
|
|
392
|
+
* Healthy backends are skipped rather than re-acquired, so concurrent runs
|
|
393
|
+
* can never stack assertions even if a backend's own `acquire()` is not
|
|
394
|
+
* defensively idempotent.
|
|
395
|
+
*/
|
|
396
|
+
healBackends() {
|
|
397
|
+
for (const backend of this.backends) {
|
|
398
|
+
try {
|
|
399
|
+
if (backend.isHealthy())
|
|
400
|
+
continue;
|
|
401
|
+
backend.acquire();
|
|
402
|
+
}
|
|
403
|
+
catch (error) {
|
|
404
|
+
this.log(`sleep-inhibitor ${backend.name} acquire error: ${describeError(error)}`);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
function describeError(error) {
|
|
410
|
+
return error instanceof Error ? error.message : String(error);
|
|
411
|
+
}
|
|
412
|
+
//# sourceMappingURL=sleep-inhibitor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sleep-inhibitor.js","sourceRoot":"","sources":["../src/sleep-inhibitor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Detects that this process was suspended (machine sleep, VM pause, laptop lid
|
|
3
|
+
* close) rather than merely idle.
|
|
4
|
+
*
|
|
5
|
+
* Why the daemon needs to know: when the host sleeps, the socket, the runtime
|
|
6
|
+
* heartbeat and the provider CLI all freeze together. Nothing is emitted, so
|
|
7
|
+
* from the backend's point of view the run simply went silent, and the
|
|
8
|
+
* watchdog fails it as "Agent unresponsive". Sleep prevention (see
|
|
9
|
+
* `sleep-inhibitor.ts`) removes the common case, but it cannot win against a
|
|
10
|
+
* lid close on battery or a forced sleep — so the daemon must be able to say
|
|
11
|
+
* "I was frozen, that silence was not my fault" once it wakes.
|
|
12
|
+
*
|
|
13
|
+
* Detection method: schedule a timer at a known interval and compare the
|
|
14
|
+
* wall-clock gap against the interval. Timers do not fire while the process is
|
|
15
|
+
* suspended, so a gap far larger than the interval means the process was not
|
|
16
|
+
* running. This is the same inter-tick gap technique the API watchdog uses to
|
|
17
|
+
* notice that *it* was suspended (`isWakeGapSweep`), applied at the client.
|
|
18
|
+
*
|
|
19
|
+
* A plain "wall clock jumped" check would also fire on NTP corrections and
|
|
20
|
+
* timezone changes; keying off missed timer ticks avoids both, because a clock
|
|
21
|
+
* adjustment does not stop timers from firing.
|
|
22
|
+
*/
|
|
23
|
+
export interface SuspensionDetectorOptions {
|
|
24
|
+
/** Called once per detected suspension, with how long the process was frozen. */
|
|
25
|
+
onSuspensionDetected: (gapMs: number) => void;
|
|
26
|
+
checkIntervalMs?: number;
|
|
27
|
+
thresholdMs?: number;
|
|
28
|
+
/** Injectable for tests. */
|
|
29
|
+
now?: () => number;
|
|
30
|
+
setIntervalFn?: typeof setInterval;
|
|
31
|
+
clearIntervalFn?: typeof clearInterval;
|
|
32
|
+
}
|
|
33
|
+
export declare class SuspensionDetector {
|
|
34
|
+
private readonly onSuspensionDetected;
|
|
35
|
+
private readonly checkIntervalMs;
|
|
36
|
+
private readonly thresholdMs;
|
|
37
|
+
private readonly now;
|
|
38
|
+
private readonly setIntervalFn;
|
|
39
|
+
private readonly clearIntervalFn;
|
|
40
|
+
private timer;
|
|
41
|
+
private lastTickAtMs;
|
|
42
|
+
constructor(options: SuspensionDetectorOptions);
|
|
43
|
+
start(): void;
|
|
44
|
+
stop(): void;
|
|
45
|
+
isRunning(): boolean;
|
|
46
|
+
private tick;
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=suspension-detector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"suspension-detector.d.ts","sourceRoot":"","sources":["../src/suspension-detector.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAgBH,MAAM,WAAW,yBAAyB;IACxC,iFAAiF;IACjF,oBAAoB,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9C,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4BAA4B;IAC5B,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,OAAO,WAAW,CAAC;IACnC,eAAe,CAAC,EAAE,OAAO,aAAa,CAAC;CACxC;AAED,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAA0B;IAC/D,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;IACzC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;IACnC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAqB;IACnD,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAuB;IACvD,OAAO,CAAC,KAAK,CAA+C;IAC5D,OAAO,CAAC,YAAY,CAAuB;gBAE/B,OAAO,EAAE,yBAAyB;IAS9C,KAAK,IAAI,IAAI;IAQb,IAAI,IAAI,IAAI;IAOZ,SAAS,IAAI,OAAO;IAIpB,OAAO,CAAC,IAAI;CAiBb"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Detects that this process was suspended (machine sleep, VM pause, laptop lid
|
|
3
|
+
* close) rather than merely idle.
|
|
4
|
+
*
|
|
5
|
+
* Why the daemon needs to know: when the host sleeps, the socket, the runtime
|
|
6
|
+
* heartbeat and the provider CLI all freeze together. Nothing is emitted, so
|
|
7
|
+
* from the backend's point of view the run simply went silent, and the
|
|
8
|
+
* watchdog fails it as "Agent unresponsive". Sleep prevention (see
|
|
9
|
+
* `sleep-inhibitor.ts`) removes the common case, but it cannot win against a
|
|
10
|
+
* lid close on battery or a forced sleep — so the daemon must be able to say
|
|
11
|
+
* "I was frozen, that silence was not my fault" once it wakes.
|
|
12
|
+
*
|
|
13
|
+
* Detection method: schedule a timer at a known interval and compare the
|
|
14
|
+
* wall-clock gap against the interval. Timers do not fire while the process is
|
|
15
|
+
* suspended, so a gap far larger than the interval means the process was not
|
|
16
|
+
* running. This is the same inter-tick gap technique the API watchdog uses to
|
|
17
|
+
* notice that *it* was suspended (`isWakeGapSweep`), applied at the client.
|
|
18
|
+
*
|
|
19
|
+
* A plain "wall clock jumped" check would also fire on NTP corrections and
|
|
20
|
+
* timezone changes; keying off missed timer ticks avoids both, because a clock
|
|
21
|
+
* adjustment does not stop timers from firing.
|
|
22
|
+
*/
|
|
23
|
+
/** Timer cadence. Small enough to bound detection latency, large enough to be free. */
|
|
24
|
+
const DEFAULT_CHECK_INTERVAL_MS = 15_000;
|
|
25
|
+
/**
|
|
26
|
+
* Overshoot beyond the interval that counts as a suspension.
|
|
27
|
+
*
|
|
28
|
+
* Ordinary event-loop congestion and timer coalescing routinely add seconds;
|
|
29
|
+
* they must not be reported as sleep. A full minute of overshoot cannot be
|
|
30
|
+
* explained that way, and anything shorter than this is well inside the
|
|
31
|
+
* backend's 5-minute unresponsive window anyway, so reporting it would add
|
|
32
|
+
* noise without preventing a single failed run.
|
|
33
|
+
*/
|
|
34
|
+
const DEFAULT_THRESHOLD_MS = 60_000;
|
|
35
|
+
export class SuspensionDetector {
|
|
36
|
+
onSuspensionDetected;
|
|
37
|
+
checkIntervalMs;
|
|
38
|
+
thresholdMs;
|
|
39
|
+
now;
|
|
40
|
+
setIntervalFn;
|
|
41
|
+
clearIntervalFn;
|
|
42
|
+
timer = null;
|
|
43
|
+
lastTickAtMs = null;
|
|
44
|
+
constructor(options) {
|
|
45
|
+
this.onSuspensionDetected = options.onSuspensionDetected;
|
|
46
|
+
this.checkIntervalMs = options.checkIntervalMs ?? DEFAULT_CHECK_INTERVAL_MS;
|
|
47
|
+
this.thresholdMs = options.thresholdMs ?? DEFAULT_THRESHOLD_MS;
|
|
48
|
+
this.now = options.now ?? Date.now;
|
|
49
|
+
this.setIntervalFn = options.setIntervalFn ?? setInterval;
|
|
50
|
+
this.clearIntervalFn = options.clearIntervalFn ?? clearInterval;
|
|
51
|
+
}
|
|
52
|
+
start() {
|
|
53
|
+
if (this.timer !== null)
|
|
54
|
+
return;
|
|
55
|
+
this.lastTickAtMs = this.now();
|
|
56
|
+
this.timer = this.setIntervalFn(() => this.tick(), this.checkIntervalMs);
|
|
57
|
+
// Detection must never be the reason the process stays alive.
|
|
58
|
+
this.timer.unref?.();
|
|
59
|
+
}
|
|
60
|
+
stop() {
|
|
61
|
+
if (this.timer === null)
|
|
62
|
+
return;
|
|
63
|
+
this.clearIntervalFn(this.timer);
|
|
64
|
+
this.timer = null;
|
|
65
|
+
this.lastTickAtMs = null;
|
|
66
|
+
}
|
|
67
|
+
isRunning() {
|
|
68
|
+
return this.timer !== null;
|
|
69
|
+
}
|
|
70
|
+
tick() {
|
|
71
|
+
const nowMs = this.now();
|
|
72
|
+
const previousMs = this.lastTickAtMs;
|
|
73
|
+
this.lastTickAtMs = nowMs;
|
|
74
|
+
if (previousMs === null)
|
|
75
|
+
return;
|
|
76
|
+
// Overshoot beyond the scheduled cadence, i.e. time the process was not running.
|
|
77
|
+
const overshootMs = nowMs - previousMs - this.checkIntervalMs;
|
|
78
|
+
if (overshootMs < this.thresholdMs)
|
|
79
|
+
return;
|
|
80
|
+
try {
|
|
81
|
+
this.onSuspensionDetected(overshootMs);
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
// A reporting failure must never break the detection loop; the next
|
|
85
|
+
// suspension still gets reported.
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=suspension-detector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"suspension-detector.js","sourceRoot":"","sources":["../src/suspension-detector.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,uFAAuF;AACvF,MAAM,yBAAyB,GAAG,MAAM,CAAC;AAEzC;;;;;;;;GAQG;AACH,MAAM,oBAAoB,GAAG,MAAM,CAAC;AAapC,MAAM,OAAO,kBAAkB;IACZ,oBAAoB,CAA0B;IAC9C,eAAe,CAAS;IACxB,WAAW,CAAS;IACpB,GAAG,CAAe;IAClB,aAAa,CAAqB;IAClC,eAAe,CAAuB;IAC/C,KAAK,GAA0C,IAAI,CAAC;IACpD,YAAY,GAAkB,IAAI,CAAC;IAE3C,YAAY,OAAkC;QAC5C,IAAI,CAAC,oBAAoB,GAAG,OAAO,CAAC,oBAAoB,CAAC;QACzD,IAAI,CAAC,eAAe,GAAG,OAAO,CAAC,eAAe,IAAI,yBAAyB,CAAC;QAC5E,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,oBAAoB,CAAC;QAC/D,IAAI,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC;QACnC,IAAI,CAAC,aAAa,GAAG,OAAO,CAAC,aAAa,IAAI,WAAW,CAAC;QAC1D,IAAI,CAAC,eAAe,GAAG,OAAO,CAAC,eAAe,IAAI,aAAa,CAAC;IAClE,CAAC;IAED,KAAK;QACH,IAAI,IAAI,CAAC,KAAK,KAAK,IAAI;YAAE,OAAO;QAChC,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC/B,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,IAAI,CAAC,eAAe,CAAC,CAAC;QACzE,8DAA8D;QAC7D,IAAI,CAAC,KAAgC,CAAC,KAAK,EAAE,EAAE,CAAC;IACnD,CAAC;IAED,IAAI;QACF,IAAI,IAAI,CAAC,KAAK,KAAK,IAAI;YAAE,OAAO;QAChC,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACjC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;QAClB,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;IAC3B,CAAC;IAED,SAAS;QACP,OAAO,IAAI,CAAC,KAAK,KAAK,IAAI,CAAC;IAC7B,CAAC;IAEO,IAAI;QACV,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACzB,MAAM,UAAU,GAAG,IAAI,CAAC,YAAY,CAAC;QACrC,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC;QAC1B,IAAI,UAAU,KAAK,IAAI;YAAE,OAAO;QAEhC,iFAAiF;QACjF,MAAM,WAAW,GAAG,KAAK,GAAG,UAAU,GAAG,IAAI,CAAC,eAAe,CAAC;QAC9D,IAAI,WAAW,GAAG,IAAI,CAAC,WAAW;YAAE,OAAO;QAE3C,IAAI,CAAC;YACH,IAAI,CAAC,oBAAoB,CAAC,WAAW,CAAC,CAAC;QACzC,CAAC;QAAC,MAAM,CAAC;YACP,oEAAoE;YACpE,kCAAkC;QACpC,CAAC;IACH,CAAC;CACF"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,cAAc,uBAAuB,CAAC"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,cAAc,uBAAuB,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface UpdatePayload {
|
|
2
|
+
devMode: boolean;
|
|
3
|
+
version?: string;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Install the new agent version via npm.
|
|
7
|
+
* Returns true if the install succeeded.
|
|
8
|
+
*/
|
|
9
|
+
export declare function installUpdate(payload: UpdatePayload): Promise<boolean>;
|
|
10
|
+
/**
|
|
11
|
+
* Spawn a new agent process with the same env vars, then exit this process.
|
|
12
|
+
* Preserves the provider session ID so the new agent can resume Claude context.
|
|
13
|
+
*/
|
|
14
|
+
export declare function respawnAgent(lastProviderSessionId?: string): void;
|
|
15
|
+
//# sourceMappingURL=updater.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"updater.d.ts","sourceRoot":"","sources":["../src/updater.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;GAGG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,OAAO,CAAC,CAe5E;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,qBAAqB,CAAC,EAAE,MAAM,GAAG,IAAI,CAmBjE"}
|
package/dist/updater.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Self-update logic for the sandbox agent.
|
|
3
|
+
* In prod/staging, installs the new version from npm.
|
|
4
|
+
*/
|
|
5
|
+
import { execSync, spawn } from "node:child_process";
|
|
6
|
+
const DEFAULT_PATH = "/home/user/.local/node/bin:/home/user/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin";
|
|
7
|
+
/**
|
|
8
|
+
* Install the new agent version via npm.
|
|
9
|
+
* Returns true if the install succeeded.
|
|
10
|
+
*/
|
|
11
|
+
export async function installUpdate(payload) {
|
|
12
|
+
const version = payload.version ?? "latest";
|
|
13
|
+
try {
|
|
14
|
+
console.info(`[updater] Installing @aiden-ade/sandbox-agent@${version} via npm`);
|
|
15
|
+
execSync(`npm install -g @aiden-ade/sandbox-agent@${version}`, {
|
|
16
|
+
timeout: 60_000,
|
|
17
|
+
env: { ...process.env, PATH: DEFAULT_PATH },
|
|
18
|
+
stdio: "pipe",
|
|
19
|
+
});
|
|
20
|
+
console.info("[updater] npm install complete");
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
console.error("[updater] npm install failed:", error.message);
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Spawn a new agent process with the same env vars, then exit this process.
|
|
30
|
+
* Preserves the provider session ID so the new agent can resume Claude context.
|
|
31
|
+
*/
|
|
32
|
+
export function respawnAgent(lastProviderSessionId) {
|
|
33
|
+
const env = { ...process.env };
|
|
34
|
+
if (lastProviderSessionId) {
|
|
35
|
+
env.AIDEN_PROVIDER_SESSION_ID = lastProviderSessionId;
|
|
36
|
+
}
|
|
37
|
+
console.info("[updater] Spawning new agent process");
|
|
38
|
+
const child = spawn("aiden-agent", [], {
|
|
39
|
+
detached: true,
|
|
40
|
+
stdio: "ignore",
|
|
41
|
+
env: { ...env, PATH: DEFAULT_PATH },
|
|
42
|
+
});
|
|
43
|
+
child.unref();
|
|
44
|
+
// Give the new process a moment to start before we exit
|
|
45
|
+
setTimeout(() => {
|
|
46
|
+
console.info("[updater] Old agent exiting after respawn");
|
|
47
|
+
process.exit(0);
|
|
48
|
+
}, 500);
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=updater.js.map
|