@sentropic/h2a-cli 0.75.0 → 0.78.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +1 -37
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -41
- package/dist/index.js.map +1 -1
- package/package.json +4 -15
- package/dist/bin.d.ts +0 -3
- package/dist/bin.d.ts.map +0 -1
- package/dist/bin.js +0 -83
- package/dist/bin.js.map +0 -1
- package/dist/cli-contract.d.ts +0 -62
- package/dist/cli-contract.d.ts.map +0 -1
- package/dist/cli-contract.js +0 -794
- package/dist/cli-contract.js.map +0 -1
- package/dist/cli.d.ts +0 -254
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js +0 -4672
- package/dist/cli.js.map +0 -1
- package/dist/hosts/agy.d.ts +0 -14
- package/dist/hosts/agy.d.ts.map +0 -1
- package/dist/hosts/agy.js +0 -47
- package/dist/hosts/agy.js.map +0 -1
- package/dist/hosts/claude.d.ts +0 -12
- package/dist/hosts/claude.d.ts.map +0 -1
- package/dist/hosts/claude.js +0 -46
- package/dist/hosts/claude.js.map +0 -1
- package/dist/hosts/codex.d.ts +0 -65
- package/dist/hosts/codex.d.ts.map +0 -1
- package/dist/hosts/codex.js +0 -45
- package/dist/hosts/codex.js.map +0 -1
- package/dist/hosts/gemini.d.ts +0 -12
- package/dist/hosts/gemini.d.ts.map +0 -1
- package/dist/hosts/gemini.js +0 -46
- package/dist/hosts/gemini.js.map +0 -1
- package/dist/hosts/plugin.d.ts +0 -151
- package/dist/hosts/plugin.d.ts.map +0 -1
- package/dist/hosts/plugin.js +0 -188
- package/dist/hosts/plugin.js.map +0 -1
- package/dist/mcp.d.ts +0 -2
- package/dist/mcp.d.ts.map +0 -1
- package/dist/mcp.js +0 -32
- package/dist/mcp.js.map +0 -1
- package/dist/runtime/blockage/index.d.ts +0 -3
- package/dist/runtime/blockage/index.d.ts.map +0 -1
- package/dist/runtime/blockage/index.js +0 -3
- package/dist/runtime/blockage/index.js.map +0 -1
- package/dist/runtime/blockage/notifiers.d.ts +0 -52
- package/dist/runtime/blockage/notifiers.d.ts.map +0 -1
- package/dist/runtime/blockage/notifiers.js +0 -74
- package/dist/runtime/blockage/notifiers.js.map +0 -1
- package/dist/runtime/blockage/registry.d.ts +0 -30
- package/dist/runtime/blockage/registry.d.ts.map +0 -1
- package/dist/runtime/blockage/registry.js +0 -86
- package/dist/runtime/blockage/registry.js.map +0 -1
- package/dist/runtime/deploy/k8s-sidecar.d.ts +0 -92
- package/dist/runtime/deploy/k8s-sidecar.d.ts.map +0 -1
- package/dist/runtime/deploy/k8s-sidecar.js +0 -166
- package/dist/runtime/deploy/k8s-sidecar.js.map +0 -1
- package/dist/runtime/deploy/k8s-tenant.d.ts +0 -86
- package/dist/runtime/deploy/k8s-tenant.d.ts.map +0 -1
- package/dist/runtime/deploy/k8s-tenant.js +0 -255
- package/dist/runtime/deploy/k8s-tenant.js.map +0 -1
- package/dist/runtime/drive/inbox-wake.d.ts +0 -36
- package/dist/runtime/drive/inbox-wake.d.ts.map +0 -1
- package/dist/runtime/drive/inbox-wake.js +0 -50
- package/dist/runtime/drive/inbox-wake.js.map +0 -1
- package/dist/runtime/drive/index.d.ts +0 -173
- package/dist/runtime/drive/index.d.ts.map +0 -1
- package/dist/runtime/drive/index.js +0 -503
- package/dist/runtime/drive/index.js.map +0 -1
- package/dist/runtime/drumbeat/deciders.d.ts +0 -32
- package/dist/runtime/drumbeat/deciders.d.ts.map +0 -1
- package/dist/runtime/drumbeat/deciders.js +0 -65
- package/dist/runtime/drumbeat/deciders.js.map +0 -1
- package/dist/runtime/drumbeat/decisions.d.ts +0 -19
- package/dist/runtime/drumbeat/decisions.d.ts.map +0 -1
- package/dist/runtime/drumbeat/decisions.js +0 -34
- package/dist/runtime/drumbeat/decisions.js.map +0 -1
- package/dist/runtime/drumbeat/inbox-relance.d.ts +0 -29
- package/dist/runtime/drumbeat/inbox-relance.d.ts.map +0 -1
- package/dist/runtime/drumbeat/inbox-relance.js +0 -101
- package/dist/runtime/drumbeat/inbox-relance.js.map +0 -1
- package/dist/runtime/drumbeat/index.d.ts +0 -8
- package/dist/runtime/drumbeat/index.d.ts.map +0 -1
- package/dist/runtime/drumbeat/index.js +0 -8
- package/dist/runtime/drumbeat/index.js.map +0 -1
- package/dist/runtime/drumbeat/registry.d.ts +0 -60
- package/dist/runtime/drumbeat/registry.d.ts.map +0 -1
- package/dist/runtime/drumbeat/registry.js +0 -115
- package/dist/runtime/drumbeat/registry.js.map +0 -1
- package/dist/runtime/drumbeat/relaunchers.d.ts +0 -111
- package/dist/runtime/drumbeat/relaunchers.d.ts.map +0 -1
- package/dist/runtime/drumbeat/relaunchers.js +0 -260
- package/dist/runtime/drumbeat/relaunchers.js.map +0 -1
- package/dist/runtime/drumbeat/scan.d.ts +0 -36
- package/dist/runtime/drumbeat/scan.d.ts.map +0 -1
- package/dist/runtime/drumbeat/scan.js +0 -41
- package/dist/runtime/drumbeat/scan.js.map +0 -1
- package/dist/runtime/drumbeat/watch.d.ts +0 -74
- package/dist/runtime/drumbeat/watch.d.ts.map +0 -1
- package/dist/runtime/drumbeat/watch.js +0 -216
- package/dist/runtime/drumbeat/watch.js.map +0 -1
- package/dist/runtime/escalation/index.d.ts +0 -2
- package/dist/runtime/escalation/index.d.ts.map +0 -1
- package/dist/runtime/escalation/index.js +0 -2
- package/dist/runtime/escalation/index.js.map +0 -1
- package/dist/runtime/escalation/registry.d.ts +0 -44
- package/dist/runtime/escalation/registry.d.ts.map +0 -1
- package/dist/runtime/escalation/registry.js +0 -79
- package/dist/runtime/escalation/registry.js.map +0 -1
- package/dist/runtime/governance/claims.d.ts +0 -43
- package/dist/runtime/governance/claims.d.ts.map +0 -1
- package/dist/runtime/governance/claims.js +0 -93
- package/dist/runtime/governance/claims.js.map +0 -1
- package/dist/runtime/governance/conductor.d.ts +0 -79
- package/dist/runtime/governance/conductor.d.ts.map +0 -1
- package/dist/runtime/governance/conductor.js +0 -106
- package/dist/runtime/governance/conductor.js.map +0 -1
- package/dist/runtime/governance/index.d.ts +0 -5
- package/dist/runtime/governance/index.d.ts.map +0 -1
- package/dist/runtime/governance/index.js +0 -5
- package/dist/runtime/governance/index.js.map +0 -1
- package/dist/runtime/governance/launch-check.d.ts +0 -93
- package/dist/runtime/governance/launch-check.d.ts.map +0 -1
- package/dist/runtime/governance/launch-check.js +0 -113
- package/dist/runtime/governance/launch-check.js.map +0 -1
- package/dist/runtime/governance/spawns.d.ts +0 -50
- package/dist/runtime/governance/spawns.d.ts.map +0 -1
- package/dist/runtime/governance/spawns.js +0 -91
- package/dist/runtime/governance/spawns.js.map +0 -1
- package/dist/runtime/identity/bindings.d.ts +0 -84
- package/dist/runtime/identity/bindings.d.ts.map +0 -1
- package/dist/runtime/identity/bindings.js +0 -112
- package/dist/runtime/identity/bindings.js.map +0 -1
- package/dist/runtime/identity/index.d.ts +0 -11
- package/dist/runtime/identity/index.d.ts.map +0 -1
- package/dist/runtime/identity/index.js +0 -6
- package/dist/runtime/identity/index.js.map +0 -1
- package/dist/runtime/identity/live.d.ts +0 -26
- package/dist/runtime/identity/live.d.ts.map +0 -1
- package/dist/runtime/identity/live.js +0 -232
- package/dist/runtime/identity/live.js.map +0 -1
- package/dist/runtime/identity/migration.d.ts +0 -55
- package/dist/runtime/identity/migration.d.ts.map +0 -1
- package/dist/runtime/identity/migration.js +0 -100
- package/dist/runtime/identity/migration.js.map +0 -1
- package/dist/runtime/identity/readers.d.ts +0 -42
- package/dist/runtime/identity/readers.d.ts.map +0 -1
- package/dist/runtime/identity/readers.js +0 -248
- package/dist/runtime/identity/readers.js.map +0 -1
- package/dist/runtime/identity/resolver.d.ts +0 -50
- package/dist/runtime/identity/resolver.d.ts.map +0 -1
- package/dist/runtime/identity/resolver.js +0 -61
- package/dist/runtime/identity/resolver.js.map +0 -1
- package/dist/runtime/identity/workspace-id.d.ts +0 -43
- package/dist/runtime/identity/workspace-id.d.ts.map +0 -1
- package/dist/runtime/identity/workspace-id.js +0 -138
- package/dist/runtime/identity/workspace-id.js.map +0 -1
- package/dist/runtime/local-files/index.d.ts +0 -8
- package/dist/runtime/local-files/index.d.ts.map +0 -1
- package/dist/runtime/local-files/index.js +0 -8
- package/dist/runtime/local-files/index.js.map +0 -1
- package/dist/runtime/local-files/lease.d.ts +0 -62
- package/dist/runtime/local-files/lease.d.ts.map +0 -1
- package/dist/runtime/local-files/lease.js +0 -249
- package/dist/runtime/local-files/lease.js.map +0 -1
- package/dist/runtime/local-files/locks.d.ts +0 -43
- package/dist/runtime/local-files/locks.d.ts.map +0 -1
- package/dist/runtime/local-files/locks.js +0 -197
- package/dist/runtime/local-files/locks.js.map +0 -1
- package/dist/runtime/local-files/migrate.d.ts +0 -31
- package/dist/runtime/local-files/migrate.d.ts.map +0 -1
- package/dist/runtime/local-files/migrate.js +0 -104
- package/dist/runtime/local-files/migrate.js.map +0 -1
- package/dist/runtime/local-files/paths.d.ts +0 -146
- package/dist/runtime/local-files/paths.d.ts.map +0 -1
- package/dist/runtime/local-files/paths.js +0 -233
- package/dist/runtime/local-files/paths.js.map +0 -1
- package/dist/runtime/local-files/presence.d.ts +0 -80
- package/dist/runtime/local-files/presence.d.ts.map +0 -1
- package/dist/runtime/local-files/presence.js +0 -250
- package/dist/runtime/local-files/presence.js.map +0 -1
- package/dist/runtime/local-files/schema.d.ts +0 -47
- package/dist/runtime/local-files/schema.d.ts.map +0 -1
- package/dist/runtime/local-files/schema.js +0 -77
- package/dist/runtime/local-files/schema.js.map +0 -1
- package/dist/runtime/local-files/store.d.ts +0 -132
- package/dist/runtime/local-files/store.d.ts.map +0 -1
- package/dist/runtime/local-files/store.js +0 -997
- package/dist/runtime/local-files/store.js.map +0 -1
- package/dist/runtime/loop/index.d.ts +0 -73
- package/dist/runtime/loop/index.d.ts.map +0 -1
- package/dist/runtime/loop/index.js +0 -107
- package/dist/runtime/loop/index.js.map +0 -1
- package/dist/runtime/mcp/handlers.d.ts +0 -196
- package/dist/runtime/mcp/handlers.d.ts.map +0 -1
- package/dist/runtime/mcp/handlers.js +0 -1025
- package/dist/runtime/mcp/handlers.js.map +0 -1
- package/dist/runtime/mcp/index.d.ts +0 -7
- package/dist/runtime/mcp/index.d.ts.map +0 -1
- package/dist/runtime/mcp/index.js +0 -7
- package/dist/runtime/mcp/index.js.map +0 -1
- package/dist/runtime/mcp/notifications.d.ts +0 -47
- package/dist/runtime/mcp/notifications.d.ts.map +0 -1
- package/dist/runtime/mcp/notifications.js +0 -204
- package/dist/runtime/mcp/notifications.js.map +0 -1
- package/dist/runtime/mcp/server.d.ts +0 -45
- package/dist/runtime/mcp/server.d.ts.map +0 -1
- package/dist/runtime/mcp/server.js +0 -94
- package/dist/runtime/mcp/server.js.map +0 -1
- package/dist/runtime/mcp/sessions.d.ts +0 -68
- package/dist/runtime/mcp/sessions.d.ts.map +0 -1
- package/dist/runtime/mcp/sessions.js +0 -152
- package/dist/runtime/mcp/sessions.js.map +0 -1
- package/dist/runtime/mcp/stdio.d.ts +0 -72
- package/dist/runtime/mcp/stdio.d.ts.map +0 -1
- package/dist/runtime/mcp/stdio.js +0 -274
- package/dist/runtime/mcp/stdio.js.map +0 -1
- package/dist/runtime/mcp/tools.d.ts +0 -19
- package/dist/runtime/mcp/tools.d.ts.map +0 -1
- package/dist/runtime/mcp/tools.js +0 -525
- package/dist/runtime/mcp/tools.js.map +0 -1
- package/dist/runtime/mcp-http/app.d.ts +0 -33
- package/dist/runtime/mcp-http/app.d.ts.map +0 -1
- package/dist/runtime/mcp-http/app.js +0 -162
- package/dist/runtime/mcp-http/app.js.map +0 -1
- package/dist/runtime/mcp-http/hosted-mcp-server.d.ts +0 -14
- package/dist/runtime/mcp-http/hosted-mcp-server.d.ts.map +0 -1
- package/dist/runtime/mcp-http/hosted-mcp-server.js +0 -38
- package/dist/runtime/mcp-http/hosted-mcp-server.js.map +0 -1
- package/dist/runtime/mcp-http/index.d.ts +0 -18
- package/dist/runtime/mcp-http/index.d.ts.map +0 -1
- package/dist/runtime/mcp-http/index.js +0 -18
- package/dist/runtime/mcp-http/index.js.map +0 -1
- package/dist/runtime/mcp-http/main.d.ts +0 -2
- package/dist/runtime/mcp-http/main.d.ts.map +0 -1
- package/dist/runtime/mcp-http/main.js +0 -14
- package/dist/runtime/mcp-http/main.js.map +0 -1
- package/dist/runtime/mcp-http/oauth/broker-login.d.ts +0 -50
- package/dist/runtime/mcp-http/oauth/broker-login.d.ts.map +0 -1
- package/dist/runtime/mcp-http/oauth/broker-login.js +0 -41
- package/dist/runtime/mcp-http/oauth/broker-login.js.map +0 -1
- package/dist/runtime/mcp-http/oauth/broker-routes.d.ts +0 -45
- package/dist/runtime/mcp-http/oauth/broker-routes.d.ts.map +0 -1
- package/dist/runtime/mcp-http/oauth/broker-routes.js +0 -54
- package/dist/runtime/mcp-http/oauth/broker-routes.js.map +0 -1
- package/dist/runtime/mcp-http/oauth/config.d.ts +0 -49
- package/dist/runtime/mcp-http/oauth/config.d.ts.map +0 -1
- package/dist/runtime/mcp-http/oauth/config.js +0 -69
- package/dist/runtime/mcp-http/oauth/config.js.map +0 -1
- package/dist/runtime/mcp-http/oauth/crypto.d.ts +0 -14
- package/dist/runtime/mcp-http/oauth/crypto.d.ts.map +0 -1
- package/dist/runtime/mcp-http/oauth/crypto.js +0 -29
- package/dist/runtime/mcp-http/oauth/crypto.js.map +0 -1
- package/dist/runtime/mcp-http/oauth/file-store.d.ts +0 -48
- package/dist/runtime/mcp-http/oauth/file-store.d.ts.map +0 -1
- package/dist/runtime/mcp-http/oauth/file-store.js +0 -101
- package/dist/runtime/mcp-http/oauth/file-store.js.map +0 -1
- package/dist/runtime/mcp-http/oauth/hono-oauth-router.d.ts +0 -5
- package/dist/runtime/mcp-http/oauth/hono-oauth-router.d.ts.map +0 -1
- package/dist/runtime/mcp-http/oauth/hono-oauth-router.js +0 -92
- package/dist/runtime/mcp-http/oauth/hono-oauth-router.js.map +0 -1
- package/dist/runtime/mcp-http/oauth/oidc-rp.d.ts +0 -56
- package/dist/runtime/mcp-http/oauth/oidc-rp.d.ts.map +0 -1
- package/dist/runtime/mcp-http/oauth/oidc-rp.js +0 -63
- package/dist/runtime/mcp-http/oauth/oidc-rp.js.map +0 -1
- package/dist/runtime/mcp-http/oauth/redirect-uri.d.ts +0 -11
- package/dist/runtime/mcp-http/oauth/redirect-uri.d.ts.map +0 -1
- package/dist/runtime/mcp-http/oauth/redirect-uri.js +0 -29
- package/dist/runtime/mcp-http/oauth/redirect-uri.js.map +0 -1
- package/dist/runtime/mcp-http/oauth/single-tenant-provider.d.ts +0 -70
- package/dist/runtime/mcp-http/oauth/single-tenant-provider.d.ts.map +0 -1
- package/dist/runtime/mcp-http/oauth/single-tenant-provider.js +0 -247
- package/dist/runtime/mcp-http/oauth/single-tenant-provider.js.map +0 -1
- package/dist/runtime/mcp-http/oauth/tenancy.d.ts +0 -3
- package/dist/runtime/mcp-http/oauth/tenancy.d.ts.map +0 -1
- package/dist/runtime/mcp-http/oauth/tenancy.js +0 -18
- package/dist/runtime/mcp-http/oauth/tenancy.js.map +0 -1
- package/dist/runtime/mcp-http/readonly-allowlist.d.ts +0 -24
- package/dist/runtime/mcp-http/readonly-allowlist.d.ts.map +0 -1
- package/dist/runtime/mcp-http/readonly-allowlist.js +0 -43
- package/dist/runtime/mcp-http/readonly-allowlist.js.map +0 -1
- package/dist/runtime/mcp-http/serve.d.ts +0 -39
- package/dist/runtime/mcp-http/serve.d.ts.map +0 -1
- package/dist/runtime/mcp-http/serve.js +0 -100
- package/dist/runtime/mcp-http/serve.js.map +0 -1
- package/dist/runtime/mirror/accept.d.ts +0 -57
- package/dist/runtime/mirror/accept.d.ts.map +0 -1
- package/dist/runtime/mirror/accept.js +0 -70
- package/dist/runtime/mirror/accept.js.map +0 -1
- package/dist/runtime/mirror/build.d.ts +0 -44
- package/dist/runtime/mirror/build.d.ts.map +0 -1
- package/dist/runtime/mirror/build.js +0 -37
- package/dist/runtime/mirror/build.js.map +0 -1
- package/dist/runtime/mirror/index.d.ts +0 -9
- package/dist/runtime/mirror/index.d.ts.map +0 -1
- package/dist/runtime/mirror/index.js +0 -9
- package/dist/runtime/mirror/index.js.map +0 -1
- package/dist/runtime/mirror/serve.d.ts +0 -21
- package/dist/runtime/mirror/serve.d.ts.map +0 -1
- package/dist/runtime/mirror/serve.js +0 -141
- package/dist/runtime/mirror/serve.js.map +0 -1
- package/dist/runtime/nhi.d.ts +0 -15
- package/dist/runtime/nhi.d.ts.map +0 -1
- package/dist/runtime/nhi.js +0 -26
- package/dist/runtime/nhi.js.map +0 -1
- package/dist/runtime/remote/accept.d.ts +0 -50
- package/dist/runtime/remote/accept.d.ts.map +0 -1
- package/dist/runtime/remote/accept.js +0 -54
- package/dist/runtime/remote/accept.js.map +0 -1
- package/dist/runtime/remote/client.d.ts +0 -27
- package/dist/runtime/remote/client.d.ts.map +0 -1
- package/dist/runtime/remote/client.js +0 -34
- package/dist/runtime/remote/client.js.map +0 -1
- package/dist/runtime/remote/index.d.ts +0 -5
- package/dist/runtime/remote/index.d.ts.map +0 -1
- package/dist/runtime/remote/index.js +0 -5
- package/dist/runtime/remote/index.js.map +0 -1
- package/dist/runtime/remote/serve.d.ts +0 -27
- package/dist/runtime/remote/serve.d.ts.map +0 -1
- package/dist/runtime/remote/serve.js +0 -26
- package/dist/runtime/remote/serve.js.map +0 -1
- package/dist/runtime/remote/server.d.ts +0 -36
- package/dist/runtime/remote/server.d.ts.map +0 -1
- package/dist/runtime/remote/server.js +0 -100
- package/dist/runtime/remote/server.js.map +0 -1
- package/dist/runtime/sysml/client.d.ts +0 -44
- package/dist/runtime/sysml/client.d.ts.map +0 -1
- package/dist/runtime/sysml/client.js +0 -52
- package/dist/runtime/sysml/client.js.map +0 -1
- package/dist/runtime/sysml/disclosure.d.ts +0 -23
- package/dist/runtime/sysml/disclosure.d.ts.map +0 -1
- package/dist/runtime/sysml/disclosure.js +0 -53
- package/dist/runtime/sysml/disclosure.js.map +0 -1
- package/dist/runtime/sysml/index.d.ts +0 -4
- package/dist/runtime/sysml/index.d.ts.map +0 -1
- package/dist/runtime/sysml/index.js +0 -4
- package/dist/runtime/sysml/index.js.map +0 -1
- package/dist/runtime/sysml/verify.d.ts +0 -32
- package/dist/runtime/sysml/verify.d.ts.map +0 -1
- package/dist/runtime/sysml/verify.js +0 -67
- package/dist/runtime/sysml/verify.js.map +0 -1
- package/dist/runtime/upgrade/index.d.ts +0 -96
- package/dist/runtime/upgrade/index.d.ts.map +0 -1
- package/dist/runtime/upgrade/index.js +0 -185
- package/dist/runtime/upgrade/index.js.map +0 -1
- package/dist/runtime/version/agent-version.d.ts +0 -15
- package/dist/runtime/version/agent-version.d.ts.map +0 -1
- package/dist/runtime/version/agent-version.js +0 -53
- package/dist/runtime/version/agent-version.js.map +0 -1
- package/skills/h2a/SKILL.md +0 -345
|
@@ -1,260 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Drumbeat relauncher adapters (DEC-091, slice D3). D2 shipped the dumb daemon
|
|
3
|
-
* + the `H2ARelauncher` interface with only a logging adapter; D3 supplies the
|
|
4
|
-
* two **local** adapters that actually revive a stalled agent:
|
|
5
|
-
*
|
|
6
|
-
* - **local-tmux**: `tmux send-keys` the resume/launch command into the pane
|
|
7
|
-
* captured at launch (DEC-085 `launchContext.tmux`). The natural revive for a
|
|
8
|
-
* user-local agent — it lands back in its original terminal.
|
|
9
|
-
* - **headless**: detached background respawn of the captured command + a
|
|
10
|
-
* notification (the PRINCIPAL escalation is wired fully in D7).
|
|
11
|
-
*
|
|
12
|
-
* `chainRelauncher` composes them (try tmux, fall back to headless) — the
|
|
13
|
-
* "local-tmux adapter + headless fallback" of D3. The actual process I/O is
|
|
14
|
-
* behind an injectable `RelauncherRuntime` so the adapters are unit-testable
|
|
15
|
-
* with no real tmux or child processes. The remote adapter is D4.
|
|
16
|
-
*/
|
|
17
|
-
import { spawn, spawnSync } from "node:child_process";
|
|
18
|
-
import { randomBytes } from "node:crypto";
|
|
19
|
-
import { H2A_DRUMBEAT_RESUME_BODY_KIND, createEnvelope } from "@sentropic/h2a";
|
|
20
|
-
import { createLocalStore } from "../local-files/store.js";
|
|
21
|
-
import { sendRemoteEnvelope } from "../remote/client.js";
|
|
22
|
-
export const defaultRelauncherRuntime = {
|
|
23
|
-
run(file, args) {
|
|
24
|
-
const res = spawnSync(file, [...args], { stdio: "ignore" });
|
|
25
|
-
return res.status === 0;
|
|
26
|
-
},
|
|
27
|
-
capture(file, args) {
|
|
28
|
-
try {
|
|
29
|
-
const res = spawnSync(file, [...args], {
|
|
30
|
-
encoding: "utf8",
|
|
31
|
-
stdio: ["ignore", "pipe", "ignore"]
|
|
32
|
-
});
|
|
33
|
-
return res.status === 0 ? res.stdout : undefined;
|
|
34
|
-
}
|
|
35
|
-
catch {
|
|
36
|
-
return undefined;
|
|
37
|
-
}
|
|
38
|
-
},
|
|
39
|
-
spawnDetached(command, options) {
|
|
40
|
-
try {
|
|
41
|
-
const child = typeof command === "string"
|
|
42
|
-
? spawn(command, {
|
|
43
|
-
cwd: options.cwd,
|
|
44
|
-
shell: true,
|
|
45
|
-
detached: true,
|
|
46
|
-
stdio: "ignore"
|
|
47
|
-
})
|
|
48
|
-
: spawn(command.file, [...command.args], {
|
|
49
|
-
cwd: command.cwd ?? options.cwd,
|
|
50
|
-
detached: true,
|
|
51
|
-
stdio: "ignore"
|
|
52
|
-
});
|
|
53
|
-
child.unref();
|
|
54
|
-
return child.pid !== undefined;
|
|
55
|
-
}
|
|
56
|
-
catch {
|
|
57
|
-
return false;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
};
|
|
61
|
-
const deferredRelaunchers = new WeakSet();
|
|
62
|
-
function markDeferred(relauncher, deferred) {
|
|
63
|
-
if (deferred) {
|
|
64
|
-
deferredRelaunchers.add(relauncher);
|
|
65
|
-
}
|
|
66
|
-
else {
|
|
67
|
-
deferredRelaunchers.delete(relauncher);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
/**
|
|
71
|
-
* Build a tmux target-pane spec from a captured `launchContext.tmux`. With a
|
|
72
|
-
* window it is `session:window.pane`; otherwise the pane is assumed to be a
|
|
73
|
-
* unique tmux pane id (`%N`, captured via `#{pane_id}` by the D6 plugins) and
|
|
74
|
-
* targeted directly.
|
|
75
|
-
*/
|
|
76
|
-
export function tmuxTarget(t) {
|
|
77
|
-
return t.window !== undefined ? `${t.session}:${t.window}.${t.pane}` : t.pane;
|
|
78
|
-
}
|
|
79
|
-
/**
|
|
80
|
-
* Type `text` into a tmux pane AND submit it. Sends the text literally (`-l`, so
|
|
81
|
-
* spaces / special chars aren't interpreted as tmux key names) then Enter as a
|
|
82
|
-
* SEPARATE send-keys. A combined `send-keys "text" Enter` (or one without `-l`)
|
|
83
|
-
* leaves the line in the input but **never commits** on modern TUIs
|
|
84
|
-
* (codex/claude) — the bug behind "the line is written but no Enter fires". A
|
|
85
|
-
* second Enter covers TUIs that buffer the first. The single shared submit path
|
|
86
|
-
* for both the drumbeat relauncher and the EVO-1 wake/drive local-tmux driver.
|
|
87
|
-
*/
|
|
88
|
-
export function tmuxSendSubmit(runtime, target, text) {
|
|
89
|
-
const typed = runtime.run("tmux", ["send-keys", "-t", target, "-l", text]);
|
|
90
|
-
const enter1 = runtime.run("tmux", ["send-keys", "-t", target, "Enter"]);
|
|
91
|
-
const enter2 = runtime.run("tmux", ["send-keys", "-t", target, "Enter"]);
|
|
92
|
-
return typed && enter1 && enter2;
|
|
93
|
-
}
|
|
94
|
-
/**
|
|
95
|
-
* Default window (ms): if a tmux client attached to the pane's session was
|
|
96
|
-
* active within this window, a wake/relance is DEFERRED so it does not clobber
|
|
97
|
-
* a human typing in the pane. Override via `H2A_WAKE_DEFER_ACTIVITY_MS` (0 = off).
|
|
98
|
-
*/
|
|
99
|
-
export const H2A_WAKE_DEFER_ACTIVITY_DEFAULT_MS = 4000;
|
|
100
|
-
function activityWindowMs() {
|
|
101
|
-
const raw = process.env.H2A_WAKE_DEFER_ACTIVITY_MS;
|
|
102
|
-
if (raw !== undefined) {
|
|
103
|
-
const n = Number.parseInt(raw, 10);
|
|
104
|
-
if (Number.isFinite(n) && n >= 0)
|
|
105
|
-
return n;
|
|
106
|
-
}
|
|
107
|
-
return H2A_WAKE_DEFER_ACTIVITY_DEFAULT_MS;
|
|
108
|
-
}
|
|
109
|
-
/**
|
|
110
|
-
* True iff a tmux CLIENT attached to `target`'s session had activity within the
|
|
111
|
-
* defer window — i.e. a human is (probably) typing/looking RIGHT NOW, so a wake
|
|
112
|
-
* that types into the pane would clobber their in-progress input. TUI-agnostic
|
|
113
|
-
* (uses tmux `client_activity`, not any TUI's composer state). Fail-OPEN: if the
|
|
114
|
-
* probe can't be read (no `capture`, no tmux, detached/no client), returns false
|
|
115
|
-
* so waking still works — the guard only fires on a POSITIVE recent-activity read.
|
|
116
|
-
*/
|
|
117
|
-
export function paneHasRecentHumanActivity(runtime, target, opts = {}) {
|
|
118
|
-
if (!runtime.capture)
|
|
119
|
-
return false;
|
|
120
|
-
const windowMs = opts.windowMs ?? activityWindowMs();
|
|
121
|
-
if (windowMs <= 0)
|
|
122
|
-
return false; // disabled
|
|
123
|
-
const now = opts.now ?? Date.now();
|
|
124
|
-
const sessionRaw = runtime.capture("tmux", [
|
|
125
|
-
"display-message",
|
|
126
|
-
"-p",
|
|
127
|
-
"-t",
|
|
128
|
-
target,
|
|
129
|
-
"#{session_name}"
|
|
130
|
-
]);
|
|
131
|
-
const session = sessionRaw?.trim();
|
|
132
|
-
if (!session)
|
|
133
|
-
return false;
|
|
134
|
-
const clientsRaw = runtime.capture("tmux", [
|
|
135
|
-
"list-clients",
|
|
136
|
-
"-t",
|
|
137
|
-
`=${session}`,
|
|
138
|
-
"-F",
|
|
139
|
-
"#{client_activity}"
|
|
140
|
-
]);
|
|
141
|
-
if (!clientsRaw)
|
|
142
|
-
return false; // no attached client → no human → don't defer
|
|
143
|
-
let mostRecentSec = 0;
|
|
144
|
-
for (const line of clientsRaw.split("\n")) {
|
|
145
|
-
const epoch = Number.parseInt(line.trim(), 10);
|
|
146
|
-
if (Number.isFinite(epoch) && epoch > mostRecentSec)
|
|
147
|
-
mostRecentSec = epoch;
|
|
148
|
-
}
|
|
149
|
-
if (mostRecentSec === 0)
|
|
150
|
-
return false;
|
|
151
|
-
const ageMs = now - mostRecentSec * 1000; // client_activity is epoch SECONDS
|
|
152
|
-
return ageMs >= 0 && ageMs < windowMs;
|
|
153
|
-
}
|
|
154
|
-
/** Revive a stalled agent by sending its resume/launch command into its pane. */
|
|
155
|
-
export function localTmuxRelauncher(options = {}) {
|
|
156
|
-
const runtime = options.runtime ?? defaultRelauncherRuntime;
|
|
157
|
-
const relauncher = {
|
|
158
|
-
relance(finding) {
|
|
159
|
-
markDeferred(relauncher, false);
|
|
160
|
-
const lc = finding.launchContext;
|
|
161
|
-
if (!lc?.tmux)
|
|
162
|
-
return false; // not a tmux-launched agent — let the chain fall through
|
|
163
|
-
const command = lc.resumeCommand ?? lc.command;
|
|
164
|
-
const target = tmuxTarget(lc.tmux);
|
|
165
|
-
if (paneHasRecentHumanActivity(runtime, target)) {
|
|
166
|
-
markDeferred(relauncher, true);
|
|
167
|
-
options.log?.(`drumbeat[local-tmux]: relance ${finding.instance} → ${target} DEFERRED (human active in pane)`);
|
|
168
|
-
return false;
|
|
169
|
-
}
|
|
170
|
-
// literal command + separate Enter(s) — a combined `send-keys cmd Enter`
|
|
171
|
-
// doesn't commit in a TUI (the reported "line written, no Enter") and
|
|
172
|
-
// without -l the command keys can be misread as tmux key names.
|
|
173
|
-
const ok = tmuxSendSubmit(runtime, target, command);
|
|
174
|
-
options.log?.(`drumbeat[local-tmux]: relance ${finding.instance} → tmux send-keys -t ${target} (${ok ? "ok" : "failed"})`);
|
|
175
|
-
return ok;
|
|
176
|
-
}
|
|
177
|
-
};
|
|
178
|
-
return relauncher;
|
|
179
|
-
}
|
|
180
|
-
/** Fallback: detached background respawn of the captured command + notify. */
|
|
181
|
-
export function headlessRelauncher(options = {}) {
|
|
182
|
-
const runtime = options.runtime ?? defaultRelauncherRuntime;
|
|
183
|
-
return {
|
|
184
|
-
relance(finding) {
|
|
185
|
-
const lc = finding.launchContext;
|
|
186
|
-
const command = lc?.resumeCommand ?? lc?.command;
|
|
187
|
-
if (!command)
|
|
188
|
-
return false; // nothing to respawn
|
|
189
|
-
const ok = runtime.spawnDetached(command, { cwd: lc?.cwd });
|
|
190
|
-
const note = `drumbeat[headless]: relance ${finding.instance} → detached "${command}" (${ok ? "spawned" : "failed"})`;
|
|
191
|
-
options.log?.(note);
|
|
192
|
-
runtime.notify?.(note);
|
|
193
|
-
return ok;
|
|
194
|
-
}
|
|
195
|
-
};
|
|
196
|
-
}
|
|
197
|
-
/** Try each adapter in order; the first to issue a relance wins (D3 fallback). */
|
|
198
|
-
export function chainRelauncher(...relaunchers) {
|
|
199
|
-
return {
|
|
200
|
-
async relance(finding) {
|
|
201
|
-
for (const r of relaunchers) {
|
|
202
|
-
if (await r.relance(finding))
|
|
203
|
-
return true;
|
|
204
|
-
if (deferredRelaunchers.has(r))
|
|
205
|
-
return false;
|
|
206
|
-
}
|
|
207
|
-
return false;
|
|
208
|
-
}
|
|
209
|
-
};
|
|
210
|
-
}
|
|
211
|
-
function envelopeId(now) {
|
|
212
|
-
return `env:${now}:${randomBytes(4).toString("hex")}`;
|
|
213
|
-
}
|
|
214
|
-
/**
|
|
215
|
-
* D4 remote adapter: sign a `drumbeat.resume` envelope and deliver it to the
|
|
216
|
-
* target instance's declared remote endpoint. It never executes remote code.
|
|
217
|
-
*/
|
|
218
|
-
export function remoteRelauncher(options) {
|
|
219
|
-
const store = options.root ? createLocalStore({ root: options.root }) : undefined;
|
|
220
|
-
const resolveEndpoint = options.resolveEndpoint ??
|
|
221
|
-
((instance) => store?.findInstance(instance)?.endpoints.find((e) => e.kind === "remote")?.uri);
|
|
222
|
-
const sendImpl = options.sendImpl ?? sendRemoteEnvelope;
|
|
223
|
-
return {
|
|
224
|
-
async relance(finding) {
|
|
225
|
-
const endpoint = await resolveEndpoint(finding.instance);
|
|
226
|
-
if (!endpoint) {
|
|
227
|
-
options.log?.(`drumbeat[remote]: no remote endpoint for ${finding.instance}`);
|
|
228
|
-
return false;
|
|
229
|
-
}
|
|
230
|
-
const now = options.now?.() ?? Date.now();
|
|
231
|
-
const envelope = createEnvelope({
|
|
232
|
-
id: envelopeId(now),
|
|
233
|
-
type: "event",
|
|
234
|
-
actor: options.actor,
|
|
235
|
-
target: { instance: finding.instance },
|
|
236
|
-
body: {
|
|
237
|
-
kind: H2A_DRUMBEAT_RESUME_BODY_KIND,
|
|
238
|
-
target: finding.instance,
|
|
239
|
-
reason: finding.reason,
|
|
240
|
-
requestedBy: options.actor.instance
|
|
241
|
-
},
|
|
242
|
-
createdAt: new Date(now).toISOString()
|
|
243
|
-
});
|
|
244
|
-
try {
|
|
245
|
-
const result = await sendImpl(endpoint, envelope, {
|
|
246
|
-
by: options.actor.instance,
|
|
247
|
-
privateKeyPem: options.privateKeyPem
|
|
248
|
-
});
|
|
249
|
-
const ok = result.status >= 200 && result.status < 300;
|
|
250
|
-
options.log?.(`drumbeat[remote]: relay ${finding.instance} -> ${endpoint} (${result.status}${ok ? " ok" : " failed"})`);
|
|
251
|
-
return ok;
|
|
252
|
-
}
|
|
253
|
-
catch (error) {
|
|
254
|
-
options.log?.(`drumbeat[remote]: relay ${finding.instance} -> ${endpoint} failed (${error.message})`);
|
|
255
|
-
return false;
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
};
|
|
259
|
-
}
|
|
260
|
-
//# sourceMappingURL=relaunchers.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"relaunchers.js","sourceRoot":"","sources":["../../../src/runtime/drumbeat/relaunchers.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Drumbeat scan (DEC-086, slice D2). Reads the durable registry and decides,
|
|
3
|
-
* per stopped agent, whether it is a relance candidate, has exhausted its
|
|
4
|
-
* relance budget (→ escalate, D7), or finished (→ ignore/clear).
|
|
5
|
-
*/
|
|
6
|
-
import type { H2AWorkStatus } from "@sentropic/h2a";
|
|
7
|
-
import { type H2ADrumbeatEntry } from "./registry.js";
|
|
8
|
-
/** Default number of relances before escalating to the PRINCIPAL (DEC-084). */
|
|
9
|
-
export declare const H2A_DEFAULT_MAX_RELANCES = 3;
|
|
10
|
-
export type H2ADrumbeatReason = "stopped" | "out-of-tokens";
|
|
11
|
-
export interface H2ADrumbeatFinding {
|
|
12
|
-
readonly instance: string;
|
|
13
|
-
readonly reason: H2ADrumbeatReason;
|
|
14
|
-
readonly workStatus: H2AWorkStatus;
|
|
15
|
-
readonly launchContext?: H2ADrumbeatEntry["launchContext"];
|
|
16
|
-
readonly relanceCount: number;
|
|
17
|
-
}
|
|
18
|
-
export interface H2ADrumbeatScanResult {
|
|
19
|
-
/** Stopped agents that should be relanced. */
|
|
20
|
-
readonly findings: readonly H2ADrumbeatFinding[];
|
|
21
|
-
/** Entries that hit the relance cap — hand to escalation (D7). */
|
|
22
|
-
readonly exhausted: readonly H2ADrumbeatEntry[];
|
|
23
|
-
}
|
|
24
|
-
export interface ScanDrumbeatOptions {
|
|
25
|
-
readonly maxRelances?: number;
|
|
26
|
-
/** Instances already handled by a pre-scan D4 inbox consume pass. */
|
|
27
|
-
readonly skipInstances?: readonly string[];
|
|
28
|
-
}
|
|
29
|
-
/**
|
|
30
|
-
* Classify the registry: `done` entries are skipped (a clean finish);
|
|
31
|
-
* entries at/over `maxRelances` are `exhausted` (escalate, don't loop);
|
|
32
|
-
* everything else is a relance `finding` (reason `out-of-tokens` when that is
|
|
33
|
-
* the recorded status, else `stopped`).
|
|
34
|
-
*/
|
|
35
|
-
export declare function scanDrumbeat(root: string, options?: ScanDrumbeatOptions): H2ADrumbeatScanResult;
|
|
36
|
-
//# sourceMappingURL=scan.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"scan.d.ts","sourceRoot":"","sources":["../../../src/runtime/drumbeat/scan.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAEpD,OAAO,EAAgB,KAAK,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAEpE,+EAA+E;AAC/E,eAAO,MAAM,wBAAwB,IAAI,CAAC;AAE1C,MAAM,MAAM,iBAAiB,GAAG,SAAS,GAAG,eAAe,CAAC;AAE5D,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,iBAAiB,CAAC;IACnC,QAAQ,CAAC,UAAU,EAAE,aAAa,CAAC;IACnC,QAAQ,CAAC,aAAa,CAAC,EAAE,gBAAgB,CAAC,eAAe,CAAC,CAAC;IAC3D,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC/B;AAED,MAAM,WAAW,qBAAqB;IACpC,8CAA8C;IAC9C,QAAQ,CAAC,QAAQ,EAAE,SAAS,kBAAkB,EAAE,CAAC;IACjD,kEAAkE;IAClE,QAAQ,CAAC,SAAS,EAAE,SAAS,gBAAgB,EAAE,CAAC;CACjD;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,qEAAqE;IACrE,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC5C;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,mBAAwB,GAChC,qBAAqB,CAsBvB"}
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Drumbeat scan (DEC-086, slice D2). Reads the durable registry and decides,
|
|
3
|
-
* per stopped agent, whether it is a relance candidate, has exhausted its
|
|
4
|
-
* relance budget (→ escalate, D7), or finished (→ ignore/clear).
|
|
5
|
-
*/
|
|
6
|
-
import { listDrumbeat } from "./registry.js";
|
|
7
|
-
/** Default number of relances before escalating to the PRINCIPAL (DEC-084). */
|
|
8
|
-
export const H2A_DEFAULT_MAX_RELANCES = 3;
|
|
9
|
-
/**
|
|
10
|
-
* Classify the registry: `done` entries are skipped (a clean finish);
|
|
11
|
-
* entries at/over `maxRelances` are `exhausted` (escalate, don't loop);
|
|
12
|
-
* everything else is a relance `finding` (reason `out-of-tokens` when that is
|
|
13
|
-
* the recorded status, else `stopped`).
|
|
14
|
-
*/
|
|
15
|
-
export function scanDrumbeat(root, options = {}) {
|
|
16
|
-
const maxRelances = options.maxRelances ?? H2A_DEFAULT_MAX_RELANCES;
|
|
17
|
-
const skip = new Set(options.skipInstances ?? []);
|
|
18
|
-
const findings = [];
|
|
19
|
-
const exhausted = [];
|
|
20
|
-
for (const entry of listDrumbeat(root)) {
|
|
21
|
-
if (skip.has(entry.instance))
|
|
22
|
-
continue;
|
|
23
|
-
if (entry.workStatus === "done")
|
|
24
|
-
continue;
|
|
25
|
-
if (entry.terminal)
|
|
26
|
-
continue; // D5: a watchdog terminal action already resolved this
|
|
27
|
-
if (entry.relanceCount >= maxRelances) {
|
|
28
|
-
exhausted.push(entry);
|
|
29
|
-
continue;
|
|
30
|
-
}
|
|
31
|
-
findings.push({
|
|
32
|
-
instance: entry.instance,
|
|
33
|
-
reason: entry.workStatus === "out-of-tokens" ? "out-of-tokens" : "stopped",
|
|
34
|
-
workStatus: entry.workStatus,
|
|
35
|
-
...(entry.launchContext ? { launchContext: entry.launchContext } : {}),
|
|
36
|
-
relanceCount: entry.relanceCount
|
|
37
|
-
});
|
|
38
|
-
}
|
|
39
|
-
return { findings, exhausted };
|
|
40
|
-
}
|
|
41
|
-
//# sourceMappingURL=scan.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"scan.js","sourceRoot":"","sources":["../../../src/runtime/drumbeat/scan.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,EAAE,YAAY,EAAyB,MAAM,eAAe,CAAC;AAEpE,+EAA+E;AAC/E,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,CAAC;AAyB1C;;;;;GAKG;AACH,MAAM,UAAU,YAAY,CAC1B,IAAY,EACZ,UAA+B,EAAE;IAEjC,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,wBAAwB,CAAC;IACpE,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,aAAa,IAAI,EAAE,CAAC,CAAC;IAClD,MAAM,QAAQ,GAAyB,EAAE,CAAC;IAC1C,MAAM,SAAS,GAAuB,EAAE,CAAC;IACzC,KAAK,MAAM,KAAK,IAAI,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC;QACvC,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC;YAAE,SAAS;QACvC,IAAI,KAAK,CAAC,UAAU,KAAK,MAAM;YAAE,SAAS;QAC1C,IAAI,KAAK,CAAC,QAAQ;YAAE,SAAS,CAAC,uDAAuD;QACrF,IAAI,KAAK,CAAC,YAAY,IAAI,WAAW,EAAE,CAAC;YACtC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACtB,SAAS;QACX,CAAC;QACD,QAAQ,CAAC,IAAI,CAAC;YACZ,QAAQ,EAAE,KAAK,CAAC,QAAQ;YACxB,MAAM,EAAE,KAAK,CAAC,UAAU,KAAK,eAAe,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,SAAS;YAC1E,UAAU,EAAE,KAAK,CAAC,UAAU;YAC5B,GAAG,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,KAAK,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACtE,YAAY,EAAE,KAAK,CAAC,YAAY;SACjC,CAAC,CAAC;IACL,CAAC;IACD,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;AACjC,CAAC"}
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Drumbeat daemon core (DEC-086, slice D2): the dumb, always-on anchor that
|
|
3
|
-
* `/loop` provides only on Claude. It scans the registry and, for each stalled
|
|
4
|
-
* agent, calls a **relauncher adapter** (concrete adapters — local-tmux,
|
|
5
|
-
* remote, headless — are slice D3/D4). It performs no LLM work and never
|
|
6
|
-
* relaunches by itself; it just detects + dispatches + caps the relances.
|
|
7
|
-
*/
|
|
8
|
-
import type { H2AReflexiveDecision } from "@sentropic/h2a";
|
|
9
|
-
import { type H2ADrumbeatFinding, type ScanDrumbeatOptions } from "./scan.js";
|
|
10
|
-
import { type H2ADrumbeatEntry } from "./registry.js";
|
|
11
|
-
import type { ReflexiveDecider } from "./deciders.js";
|
|
12
|
-
/** Adapter that actually re-invokes a stalled agent (slice D3/D4). */
|
|
13
|
-
export interface H2ARelauncher {
|
|
14
|
-
/** Returns true if a relance was issued (counts against the cap). */
|
|
15
|
-
relance(finding: H2ADrumbeatFinding): boolean | Promise<boolean>;
|
|
16
|
-
}
|
|
17
|
-
/** A relauncher that only logs — the D2 default until real adapters land. */
|
|
18
|
-
export declare function loggingRelauncher(write: (line: string) => void): H2ARelauncher;
|
|
19
|
-
export interface DrumbeatTickOptions extends ScanDrumbeatOptions {
|
|
20
|
-
/** D4: optional pre-scan hook, used by watch to consume remote resume inboxes. */
|
|
21
|
-
beforeScan?: () => readonly string[] | Promise<readonly string[]>;
|
|
22
|
-
/** Hook for entries that hit the relance cap → escalate to PRINCIPAL (D7). */
|
|
23
|
-
onExhausted?: (entry: H2ADrumbeatEntry) => void | Promise<void>;
|
|
24
|
-
now?: number;
|
|
25
|
-
/** D5: reflexive watchdog. Consulted once `relanceCount >= deciderAfter`. */
|
|
26
|
-
decider?: ReflexiveDecider;
|
|
27
|
-
/** D5: minimum relanceCount before consulting the decider (default 1). */
|
|
28
|
-
deciderAfter?: number;
|
|
29
|
-
/** D5: apply the decision (true) or just log it and do the safe default (false). */
|
|
30
|
-
enforce?: boolean;
|
|
31
|
-
/** D5: label recorded in the decision log (default "logging"). */
|
|
32
|
-
deciderLabel?: string;
|
|
33
|
-
/** D5: effect hooks (cli.ts wires escalation), keeping watch.ts store-free. */
|
|
34
|
-
onEscalate?: (finding: H2ADrumbeatFinding, decision: H2AReflexiveDecision) => void | Promise<void>;
|
|
35
|
-
onReroute?: (finding: H2ADrumbeatFinding, decision: H2AReflexiveDecision) => void | Promise<void>;
|
|
36
|
-
/**
|
|
37
|
-
* WP-7: store root for the reach-guard. When provided, each finding is
|
|
38
|
-
* checked via `reachGuard` before reaching: phantom / ambiguous / malformed
|
|
39
|
-
* targets are SKIPPED (never relanced). When absent the guard is bypassed
|
|
40
|
-
* and the tick behaves exactly as today — backward-compatible.
|
|
41
|
-
*/
|
|
42
|
-
root?: string;
|
|
43
|
-
/**
|
|
44
|
-
* WP-7: optional log sink for reach-guard skip messages. Defaults to a
|
|
45
|
-
* no-op; pass a `(line: string) => void` to surface the skip reason.
|
|
46
|
-
*/
|
|
47
|
-
log?: (line: string) => void;
|
|
48
|
-
/**
|
|
49
|
-
* Gov D2/D4: the instance id of the agent running this drumbeat. When
|
|
50
|
-
* absent, BOTH governance checks (D2 conductor-ownership, D4 cross-workspace
|
|
51
|
-
* CoI advisory) are SKIPPED entirely — fully backward-compatible; all
|
|
52
|
-
* existing callers that do not pass this option are unaffected.
|
|
53
|
-
*/
|
|
54
|
-
selfInstance?: string;
|
|
55
|
-
}
|
|
56
|
-
export interface DrumbeatTickResult {
|
|
57
|
-
readonly relanced: readonly string[];
|
|
58
|
-
readonly exhausted: readonly string[];
|
|
59
|
-
}
|
|
60
|
-
/** One scan→dispatch pass (no timers) — the testable unit of the loop. */
|
|
61
|
-
export declare function drumbeatTick(root: string, relauncher: H2ARelauncher, options?: DrumbeatTickOptions): Promise<DrumbeatTickResult>;
|
|
62
|
-
export interface DrumbeatWatchOptions extends DrumbeatTickOptions {
|
|
63
|
-
/** Beat interval in ms. Default 30s. */
|
|
64
|
-
intervalMs?: number;
|
|
65
|
-
/** Stop the loop. */
|
|
66
|
-
signal?: AbortSignal;
|
|
67
|
-
onTick?: (result: DrumbeatTickResult) => void | Promise<void>;
|
|
68
|
-
}
|
|
69
|
-
/**
|
|
70
|
-
* Run the drumbeat loop until `signal` aborts. Each beat: scan → relance via
|
|
71
|
-
* the adapter → cap. The anchor that codex/agy/gemini lack.
|
|
72
|
-
*/
|
|
73
|
-
export declare function runDrumbeatWatch(root: string, relauncher: H2ARelauncher, options?: DrumbeatWatchOptions): Promise<void>;
|
|
74
|
-
//# sourceMappingURL=watch.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"watch.d.ts","sourceRoot":"","sources":["../../../src/runtime/drumbeat/watch.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAsB,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAO/E,OAAO,EAAgB,KAAK,kBAAkB,EAAE,KAAK,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAC5F,OAAO,EAIL,KAAK,gBAAgB,EACtB,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAGtD,sEAAsE;AACtE,MAAM,WAAW,aAAa;IAC5B,qEAAqE;IACrE,OAAO,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CAClE;AAED,6EAA6E;AAC7E,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG,aAAa,CAS9E;AAED,MAAM,WAAW,mBAAoB,SAAQ,mBAAmB;IAC9D,kFAAkF;IAClF,UAAU,CAAC,EAAE,MAAM,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,SAAS,MAAM,EAAE,CAAC,CAAC;IAClE,8EAA8E;IAC9E,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,gBAAgB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAChE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,6EAA6E;IAC7E,OAAO,CAAC,EAAE,gBAAgB,CAAC;IAC3B,0EAA0E;IAC1E,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,oFAAoF;IACpF,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,kEAAkE;IAClE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,+EAA+E;IAC/E,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,kBAAkB,EAAE,QAAQ,EAAE,oBAAoB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnG,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,kBAAkB,EAAE,QAAQ,EAAE,oBAAoB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClG;;;;;OAKG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7B;;;;;OAKG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,QAAQ,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;CACvC;AAED,0EAA0E;AAC1E,wBAAsB,YAAY,CAChC,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,aAAa,EACzB,OAAO,GAAE,mBAAwB,GAChC,OAAO,CAAC,kBAAkB,CAAC,CAmL7B;AAED,MAAM,WAAW,oBAAqB,SAAQ,mBAAmB;IAC/D,wCAAwC;IACxC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,qBAAqB;IACrB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,kBAAkB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/D;AAiBD;;;GAGG;AACH,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,aAAa,EACzB,OAAO,GAAE,oBAAyB,GACjC,OAAO,CAAC,IAAI,CAAC,CAQf"}
|
|
@@ -1,216 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Drumbeat daemon core (DEC-086, slice D2): the dumb, always-on anchor that
|
|
3
|
-
* `/loop` provides only on Claude. It scans the registry and, for each stalled
|
|
4
|
-
* agent, calls a **relauncher adapter** (concrete adapters — local-tmux,
|
|
5
|
-
* remote, headless — are slice D3/D4). It performs no LLM work and never
|
|
6
|
-
* relaunches by itself; it just detects + dispatches + caps the relances.
|
|
7
|
-
*/
|
|
8
|
-
import { canonicalAddress } from "../local-files/index.js";
|
|
9
|
-
import { reachGuard } from "../local-files/paths.js";
|
|
10
|
-
import { listPresence } from "../local-files/presence.js";
|
|
11
|
-
import { createLocalStore } from "../local-files/store.js";
|
|
12
|
-
import { conductorFor } from "../governance/conductor.js";
|
|
13
|
-
import { scanDrumbeat } from "./scan.js";
|
|
14
|
-
import { markRelanced, clearDrumbeatEntry, markDrumbeatTerminal } from "./registry.js";
|
|
15
|
-
import { recordDrumbeatDecision } from "./decisions.js";
|
|
16
|
-
/** A relauncher that only logs — the D2 default until real adapters land. */
|
|
17
|
-
export function loggingRelauncher(write) {
|
|
18
|
-
return {
|
|
19
|
-
relance(finding) {
|
|
20
|
-
write(`drumbeat: would relance ${finding.instance} (reason=${finding.reason}, tries=${finding.relanceCount})`);
|
|
21
|
-
return true;
|
|
22
|
-
}
|
|
23
|
-
};
|
|
24
|
-
}
|
|
25
|
-
/** One scan→dispatch pass (no timers) — the testable unit of the loop. */
|
|
26
|
-
export async function drumbeatTick(root, relauncher, options = {}) {
|
|
27
|
-
const preRelanced = await options.beforeScan?.();
|
|
28
|
-
const { findings, exhausted } = scanDrumbeat(root, {
|
|
29
|
-
maxRelances: options.maxRelances,
|
|
30
|
-
skipInstances: [...(options.skipInstances ?? []), ...(preRelanced ?? [])]
|
|
31
|
-
});
|
|
32
|
-
// WP-7: Build reach-guard sets once per tick (avoid repeated I/O).
|
|
33
|
-
// When `options.root` is absent the guard is skipped — backward-compatible.
|
|
34
|
-
let guardRoot = options.root;
|
|
35
|
-
let liveInstances;
|
|
36
|
-
let registeredInstances;
|
|
37
|
-
let allSessions;
|
|
38
|
-
if (guardRoot !== undefined) {
|
|
39
|
-
allSessions = listPresence(guardRoot, options.now !== undefined ? { now: options.now } : {});
|
|
40
|
-
liveInstances = allSessions.map((s) => s.instance);
|
|
41
|
-
registeredInstances = createLocalStore({ root: guardRoot })
|
|
42
|
-
.listInstances()
|
|
43
|
-
.map((i) => i.instance);
|
|
44
|
-
}
|
|
45
|
-
// Gov D2/D4: resolve wSelf (self's workspace) once per tick if selfInstance known.
|
|
46
|
-
// undefined = unknown/skip advisory.
|
|
47
|
-
const selfInstance = options.selfInstance;
|
|
48
|
-
let wSelf;
|
|
49
|
-
if (selfInstance !== undefined && guardRoot !== undefined) {
|
|
50
|
-
const selfSessions = (allSessions ?? []).filter((s) => canonicalAddress(s.instance) === canonicalAddress(selfInstance));
|
|
51
|
-
if (selfSessions.length > 0 && selfSessions[0].workspace?.id !== undefined) {
|
|
52
|
-
wSelf = selfSessions[0].workspace.id;
|
|
53
|
-
}
|
|
54
|
-
else {
|
|
55
|
-
try {
|
|
56
|
-
const reg = createLocalStore({ root: guardRoot }).findInstance(selfInstance);
|
|
57
|
-
if (reg?.workspace?.id !== undefined) {
|
|
58
|
-
wSelf = reg.workspace.id;
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
catch {
|
|
62
|
-
// if registry unreadable, wSelf stays undefined → D4 advisory skipped
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
const relanced = [];
|
|
67
|
-
const k = options.deciderAfter ?? 1;
|
|
68
|
-
for (const finding of findings) {
|
|
69
|
-
// ── REACH-GUARD CHOKEPOINT ──────────────────────────────────────────────
|
|
70
|
-
// Governance gates (D2 conductor-ownership, D4 CoI advisory) hook HERE so
|
|
71
|
-
// every reach path inherits them.
|
|
72
|
-
if (guardRoot !== undefined) {
|
|
73
|
-
const guard = reachGuard({
|
|
74
|
-
target: finding.instance,
|
|
75
|
-
liveInstances: liveInstances,
|
|
76
|
-
registeredInstances: registeredInstances
|
|
77
|
-
});
|
|
78
|
-
if (!guard.ok) {
|
|
79
|
-
options.log?.(`drumbeat: skipping relance of ${finding.instance} — ${guard.reason}`);
|
|
80
|
-
continue;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
// ── GOV D2: conductor owns relances (opt-in, default-allow) ────────────
|
|
84
|
-
// Suppression only when ALL of:
|
|
85
|
-
// (a) selfInstance and options.root are known
|
|
86
|
-
// (b) wTarget (the finding's workspace) is resolvable
|
|
87
|
-
// (c) a live conductor is claimed for that workspace
|
|
88
|
-
// (d) self is NOT that conductor
|
|
89
|
-
// (e) relanceCount === 0 (fresh stall — failsafe: if relanceCount >= 1
|
|
90
|
-
// the conductor has already missed the window; peer steps in so
|
|
91
|
-
// a workspace is never frozen by a stuck conductor)
|
|
92
|
-
// Also used by D4 CoI advisory below.
|
|
93
|
-
let wTarget;
|
|
94
|
-
if (selfInstance !== undefined && guardRoot !== undefined) {
|
|
95
|
-
// Resolve finding's workspace from live sessions first, then registry.
|
|
96
|
-
const targetSession = (allSessions ?? []).find((s) => canonicalAddress(s.instance) === canonicalAddress(finding.instance));
|
|
97
|
-
if (targetSession?.workspace?.id !== undefined) {
|
|
98
|
-
wTarget = targetSession.workspace.id;
|
|
99
|
-
}
|
|
100
|
-
else {
|
|
101
|
-
try {
|
|
102
|
-
const reg = createLocalStore({ root: guardRoot }).findInstance(finding.instance);
|
|
103
|
-
if (reg?.workspace?.id !== undefined) {
|
|
104
|
-
wTarget = reg.workspace.id;
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
catch {
|
|
108
|
-
// registry unreadable → wTarget stays undefined → ALLOW
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
if (wTarget !== undefined) {
|
|
112
|
-
const res = conductorFor({
|
|
113
|
-
root: guardRoot,
|
|
114
|
-
workspaceId: wTarget,
|
|
115
|
-
...(options.now !== undefined ? { now: options.now } : {})
|
|
116
|
-
});
|
|
117
|
-
if (res.conductor !== null &&
|
|
118
|
-
canonicalAddress(res.conductor) !== canonicalAddress(selfInstance) &&
|
|
119
|
-
finding.relanceCount === 0) {
|
|
120
|
-
options.log?.(`drumbeat: ${finding.instance} in ${wTarget} owned by conductor ${res.conductor}; deferring relance`);
|
|
121
|
-
continue; // D2 suppression
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
// ── END GOV D2 ──────────────────────────────────────────────────────────
|
|
126
|
-
// ── GOV D4: cross-workspace CoI advisory (warn only, never skip) ────────
|
|
127
|
-
if (selfInstance !== undefined &&
|
|
128
|
-
wSelf !== undefined &&
|
|
129
|
-
wTarget !== undefined &&
|
|
130
|
-
canonicalAddress(wSelf) !== canonicalAddress(wTarget)) {
|
|
131
|
-
options.log?.(`drumbeat: cross-workspace relance of ${finding.instance} (workspace ${wTarget}) by ${selfInstance} (workspace ${wSelf}) — no target-conductor mandate (advisory)`);
|
|
132
|
-
}
|
|
133
|
-
// ── END GOV D4 ──────────────────────────────────────────────────────────
|
|
134
|
-
// ────────────────────────────────────────────────────────────────────────
|
|
135
|
-
// Cost guard: relance cheaply until K relances, then consult the decider.
|
|
136
|
-
if (!options.decider || finding.relanceCount < k) {
|
|
137
|
-
if (await relauncher.relance(finding)) {
|
|
138
|
-
markRelanced(root, finding.instance, options.now);
|
|
139
|
-
relanced.push(finding.instance);
|
|
140
|
-
}
|
|
141
|
-
continue;
|
|
142
|
-
}
|
|
143
|
-
const decision = await options.decider.decide(finding);
|
|
144
|
-
let applied = decision.action;
|
|
145
|
-
if (!options.enforce) {
|
|
146
|
-
applied = "relance"; // advisory-first: log the verdict, do the safe default
|
|
147
|
-
}
|
|
148
|
-
else if (decision.action === "finish" && finding.workStatus !== "done") {
|
|
149
|
-
applied = "escalate"; // `done` is the only safe-to-finish status (else never abandon)
|
|
150
|
-
}
|
|
151
|
-
switch (applied) {
|
|
152
|
-
case "relance":
|
|
153
|
-
if (await relauncher.relance(finding)) {
|
|
154
|
-
markRelanced(root, finding.instance, options.now);
|
|
155
|
-
relanced.push(finding.instance);
|
|
156
|
-
}
|
|
157
|
-
break;
|
|
158
|
-
case "finish":
|
|
159
|
-
clearDrumbeatEntry(root, finding.instance); // workStatus === "done": genuinely complete
|
|
160
|
-
break;
|
|
161
|
-
case "escalate":
|
|
162
|
-
await options.onEscalate?.(finding, decision);
|
|
163
|
-
markDrumbeatTerminal(root, finding.instance, "escalate", options.now);
|
|
164
|
-
break;
|
|
165
|
-
case "reroute":
|
|
166
|
-
await options.onReroute?.(finding, decision);
|
|
167
|
-
markDrumbeatTerminal(root, finding.instance, "reroute", options.now);
|
|
168
|
-
break;
|
|
169
|
-
}
|
|
170
|
-
recordDrumbeatDecision(root, {
|
|
171
|
-
instance: finding.instance,
|
|
172
|
-
decided: decision.action,
|
|
173
|
-
applied,
|
|
174
|
-
...(decision.reason ? { reason: decision.reason } : {}),
|
|
175
|
-
decider: options.deciderLabel ?? "logging",
|
|
176
|
-
enforced: options.enforce === true,
|
|
177
|
-
at: new Date(options.now ?? Date.now()).toISOString()
|
|
178
|
-
});
|
|
179
|
-
}
|
|
180
|
-
const escalated = [];
|
|
181
|
-
for (const entry of exhausted) {
|
|
182
|
-
await options.onExhausted?.(entry);
|
|
183
|
-
escalated.push(entry.instance);
|
|
184
|
-
}
|
|
185
|
-
return { relanced, exhausted: escalated };
|
|
186
|
-
}
|
|
187
|
-
function delay(ms, signal) {
|
|
188
|
-
return new Promise((resolve) => {
|
|
189
|
-
if (signal?.aborted)
|
|
190
|
-
return resolve();
|
|
191
|
-
const timer = setTimeout(() => {
|
|
192
|
-
signal?.removeEventListener("abort", onAbort);
|
|
193
|
-
resolve();
|
|
194
|
-
}, ms);
|
|
195
|
-
function onAbort() {
|
|
196
|
-
clearTimeout(timer);
|
|
197
|
-
resolve();
|
|
198
|
-
}
|
|
199
|
-
signal?.addEventListener("abort", onAbort, { once: true });
|
|
200
|
-
});
|
|
201
|
-
}
|
|
202
|
-
/**
|
|
203
|
-
* Run the drumbeat loop until `signal` aborts. Each beat: scan → relance via
|
|
204
|
-
* the adapter → cap. The anchor that codex/agy/gemini lack.
|
|
205
|
-
*/
|
|
206
|
-
export async function runDrumbeatWatch(root, relauncher, options = {}) {
|
|
207
|
-
const intervalMs = options.intervalMs ?? 30_000;
|
|
208
|
-
while (!options.signal?.aborted) {
|
|
209
|
-
const result = await drumbeatTick(root, relauncher, options);
|
|
210
|
-
await options.onTick?.(result);
|
|
211
|
-
if (options.signal?.aborted)
|
|
212
|
-
break;
|
|
213
|
-
await delay(intervalMs, options.signal);
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
//# sourceMappingURL=watch.js.map
|