@cluesmith/codev 3.2.4 → 3.3.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/dashboard-dist/assets/index-BZRTEMtH.css +32 -0
- package/dashboard-dist/assets/{index-BZ1x7x1X.js → index-JCbkpAcV.js} +33 -33
- package/dashboard-dist/assets/index-JCbkpAcV.js.map +1 -0
- package/dashboard-dist/index.html +2 -2
- package/dist/agent-farm/cli.d.ts.map +1 -1
- package/dist/agent-farm/cli.js +142 -0
- package/dist/agent-farm/cli.js.map +1 -1
- package/dist/agent-farm/commands/cleanup.d.ts.map +1 -1
- package/dist/agent-farm/commands/cleanup.js +16 -0
- package/dist/agent-farm/commands/cleanup.js.map +1 -1
- package/dist/agent-farm/commands/inbox.d.ts +42 -0
- package/dist/agent-farm/commands/inbox.d.ts.map +1 -0
- package/dist/agent-farm/commands/inbox.js +129 -0
- package/dist/agent-farm/commands/inbox.js.map +1 -0
- package/dist/agent-farm/commands/interrupt.d.ts +20 -0
- package/dist/agent-farm/commands/interrupt.d.ts.map +1 -0
- package/dist/agent-farm/commands/interrupt.js +65 -0
- package/dist/agent-farm/commands/interrupt.js.map +1 -0
- package/dist/agent-farm/commands/reset/constants.d.ts +52 -0
- package/dist/agent-farm/commands/reset/constants.d.ts.map +1 -0
- package/dist/agent-farm/commands/reset/constants.js +52 -0
- package/dist/agent-farm/commands/reset/constants.js.map +1 -0
- package/dist/agent-farm/commands/reset/context.d.ts +230 -0
- package/dist/agent-farm/commands/reset/context.d.ts.map +1 -0
- package/dist/agent-farm/commands/reset/context.js +428 -0
- package/dist/agent-farm/commands/reset/context.js.map +1 -0
- package/dist/agent-farm/commands/reset/index.d.ts +192 -0
- package/dist/agent-farm/commands/reset/index.d.ts.map +1 -0
- package/dist/agent-farm/commands/reset/index.js +488 -0
- package/dist/agent-farm/commands/reset/index.js.map +1 -0
- package/dist/agent-farm/commands/reset/receipt.d.ts +97 -0
- package/dist/agent-farm/commands/reset/receipt.d.ts.map +1 -0
- package/dist/agent-farm/commands/reset/receipt.js +149 -0
- package/dist/agent-farm/commands/reset/receipt.js.map +1 -0
- package/dist/agent-farm/commands/reset/reorient.d.ts +117 -0
- package/dist/agent-farm/commands/reset/reorient.d.ts.map +1 -0
- package/dist/agent-farm/commands/reset/reorient.js +313 -0
- package/dist/agent-farm/commands/reset/reorient.js.map +1 -0
- package/dist/agent-farm/commands/reset.d.ts +19 -0
- package/dist/agent-farm/commands/reset.d.ts.map +1 -0
- package/dist/agent-farm/commands/reset.js +292 -0
- package/dist/agent-farm/commands/reset.js.map +1 -0
- package/dist/agent-farm/commands/send.d.ts.map +1 -1
- package/dist/agent-farm/commands/send.js +49 -8
- package/dist/agent-farm/commands/send.js.map +1 -1
- package/dist/agent-farm/commands/spawn-worktree.d.ts +105 -6
- package/dist/agent-farm/commands/spawn-worktree.d.ts.map +1 -1
- package/dist/agent-farm/commands/spawn-worktree.js +289 -66
- package/dist/agent-farm/commands/spawn-worktree.js.map +1 -1
- package/dist/agent-farm/commands/spawn.d.ts.map +1 -1
- package/dist/agent-farm/commands/spawn.js +13 -1
- package/dist/agent-farm/commands/spawn.js.map +1 -1
- package/dist/agent-farm/commands/status.d.ts.map +1 -1
- package/dist/agent-farm/commands/status.js +80 -8
- package/dist/agent-farm/commands/status.js.map +1 -1
- package/dist/agent-farm/db/index.d.ts +1 -1
- package/dist/agent-farm/db/index.d.ts.map +1 -1
- package/dist/agent-farm/db/index.js +80 -1
- package/dist/agent-farm/db/index.js.map +1 -1
- package/dist/agent-farm/db/mailbox.d.ts +222 -0
- package/dist/agent-farm/db/mailbox.d.ts.map +1 -0
- package/dist/agent-farm/db/mailbox.js +304 -0
- package/dist/agent-farm/db/mailbox.js.map +1 -0
- package/dist/agent-farm/db/schema.d.ts +1 -1
- package/dist/agent-farm/db/schema.d.ts.map +1 -1
- package/dist/agent-farm/db/schema.js +35 -0
- package/dist/agent-farm/db/schema.js.map +1 -1
- package/dist/agent-farm/db/types.d.ts +45 -0
- package/dist/agent-farm/db/types.d.ts.map +1 -1
- package/dist/agent-farm/db/types.js.map +1 -1
- package/dist/agent-farm/lib/builder-lookup.d.ts +1 -1
- package/dist/agent-farm/lib/builder-lookup.js +2 -2
- package/dist/agent-farm/lib/builder-lookup.js.map +1 -1
- package/dist/agent-farm/lib/reconnect-backoff.d.ts +54 -0
- package/dist/agent-farm/lib/reconnect-backoff.d.ts.map +1 -0
- package/dist/agent-farm/lib/reconnect-backoff.js +47 -0
- package/dist/agent-farm/lib/reconnect-backoff.js.map +1 -0
- package/dist/agent-farm/lib/tower-client.d.ts +16 -6
- package/dist/agent-farm/lib/tower-client.d.ts.map +1 -1
- package/dist/agent-farm/lib/tower-client.js +37 -6
- package/dist/agent-farm/lib/tower-client.js.map +1 -1
- package/dist/agent-farm/lib/tunnel-client.d.ts +69 -1
- package/dist/agent-farm/lib/tunnel-client.d.ts.map +1 -1
- package/dist/agent-farm/lib/tunnel-client.js +218 -27
- package/dist/agent-farm/lib/tunnel-client.js.map +1 -1
- package/dist/agent-farm/servers/cron-delivery.d.ts +72 -0
- package/dist/agent-farm/servers/cron-delivery.d.ts.map +1 -0
- package/dist/agent-farm/servers/cron-delivery.js +80 -0
- package/dist/agent-farm/servers/cron-delivery.js.map +1 -0
- package/dist/agent-farm/servers/delayed-send.d.ts +85 -0
- package/dist/agent-farm/servers/delayed-send.d.ts.map +1 -0
- package/dist/agent-farm/servers/delayed-send.js +165 -0
- package/dist/agent-farm/servers/delayed-send.js.map +1 -0
- package/dist/agent-farm/servers/gate-profiles.d.ts +67 -0
- package/dist/agent-farm/servers/gate-profiles.d.ts.map +1 -0
- package/dist/agent-farm/servers/gate-profiles.js +113 -0
- package/dist/agent-farm/servers/gate-profiles.js.map +1 -0
- package/dist/agent-farm/servers/mailbox-delivery.d.ts +370 -0
- package/dist/agent-farm/servers/mailbox-delivery.d.ts.map +1 -0
- package/dist/agent-farm/servers/mailbox-delivery.js +617 -0
- package/dist/agent-farm/servers/mailbox-delivery.js.map +1 -0
- package/dist/agent-farm/servers/mailbox-wiring.d.ts +125 -0
- package/dist/agent-farm/servers/mailbox-wiring.d.ts.map +1 -0
- package/dist/agent-farm/servers/mailbox-wiring.js +425 -0
- package/dist/agent-farm/servers/mailbox-wiring.js.map +1 -0
- package/dist/agent-farm/servers/message-write.d.ts +53 -1
- package/dist/agent-farm/servers/message-write.d.ts.map +1 -1
- package/dist/agent-farm/servers/message-write.js +63 -0
- package/dist/agent-farm/servers/message-write.js.map +1 -1
- package/dist/agent-farm/servers/overview.d.ts.map +1 -1
- package/dist/agent-farm/servers/overview.js +23 -4
- package/dist/agent-farm/servers/overview.js.map +1 -1
- package/dist/agent-farm/servers/render-gate.d.ts +141 -0
- package/dist/agent-farm/servers/render-gate.d.ts.map +1 -0
- package/dist/agent-farm/servers/render-gate.js +251 -0
- package/dist/agent-farm/servers/render-gate.js.map +1 -0
- package/dist/agent-farm/servers/session-submit.d.ts +90 -0
- package/dist/agent-farm/servers/session-submit.d.ts.map +1 -0
- package/dist/agent-farm/servers/session-submit.js +127 -0
- package/dist/agent-farm/servers/session-submit.js.map +1 -0
- package/dist/agent-farm/servers/tower-cron.d.ts +10 -7
- package/dist/agent-farm/servers/tower-cron.d.ts.map +1 -1
- package/dist/agent-farm/servers/tower-cron.js +58 -53
- package/dist/agent-farm/servers/tower-cron.js.map +1 -1
- package/dist/agent-farm/servers/tower-instances.d.ts +14 -2
- package/dist/agent-farm/servers/tower-instances.d.ts.map +1 -1
- package/dist/agent-farm/servers/tower-instances.js +78 -16
- package/dist/agent-farm/servers/tower-instances.js.map +1 -1
- package/dist/agent-farm/servers/tower-messages.d.ts +38 -1
- package/dist/agent-farm/servers/tower-messages.d.ts.map +1 -1
- package/dist/agent-farm/servers/tower-messages.js +106 -1
- package/dist/agent-farm/servers/tower-messages.js.map +1 -1
- package/dist/agent-farm/servers/tower-routes.d.ts +16 -4
- package/dist/agent-farm/servers/tower-routes.d.ts.map +1 -1
- package/dist/agent-farm/servers/tower-routes.js +670 -105
- package/dist/agent-farm/servers/tower-routes.js.map +1 -1
- package/dist/agent-farm/servers/tower-server.js +163 -31
- package/dist/agent-farm/servers/tower-server.js.map +1 -1
- package/dist/agent-farm/servers/tower-terminals.d.ts +17 -2
- package/dist/agent-farm/servers/tower-terminals.d.ts.map +1 -1
- package/dist/agent-farm/servers/tower-terminals.js +104 -122
- package/dist/agent-farm/servers/tower-terminals.js.map +1 -1
- package/dist/agent-farm/servers/tower-tunnel.d.ts.map +1 -1
- package/dist/agent-farm/servers/tower-tunnel.js +63 -4
- package/dist/agent-farm/servers/tower-tunnel.js.map +1 -1
- package/dist/agent-farm/servers/tower-types.d.ts +2 -1
- package/dist/agent-farm/servers/tower-types.d.ts.map +1 -1
- package/dist/agent-farm/servers/tower-utils.d.ts +64 -1
- package/dist/agent-farm/servers/tower-utils.d.ts.map +1 -1
- package/dist/agent-farm/servers/tower-utils.js +168 -1
- package/dist/agent-farm/servers/tower-utils.js.map +1 -1
- package/dist/agent-farm/servers/tower-websocket.d.ts.map +1 -1
- package/dist/agent-farm/servers/tower-websocket.js +6 -21
- package/dist/agent-farm/servers/tower-websocket.js.map +1 -1
- package/dist/agent-farm/servers/write-queue.d.ts +35 -0
- package/dist/agent-farm/servers/write-queue.d.ts.map +1 -0
- package/dist/agent-farm/servers/write-queue.js +56 -0
- package/dist/agent-farm/servers/write-queue.js.map +1 -0
- package/dist/agent-farm/types.d.ts +39 -0
- package/dist/agent-farm/types.d.ts.map +1 -1
- package/dist/agent-farm/utils/agent-names.d.ts +2 -2
- package/dist/agent-farm/utils/agent-names.d.ts.map +1 -1
- package/dist/agent-farm/utils/agent-names.js +3 -3
- package/dist/agent-farm/utils/agent-names.js.map +1 -1
- package/dist/agent-farm/utils/architect-name.d.ts +2 -2
- package/dist/agent-farm/utils/architect-name.d.ts.map +1 -1
- package/dist/agent-farm/utils/architect-name.js +3 -3
- package/dist/agent-farm/utils/architect-name.js.map +1 -1
- package/dist/agent-farm/utils/config.d.ts +13 -0
- package/dist/agent-farm/utils/config.d.ts.map +1 -1
- package/dist/agent-farm/utils/config.js +33 -1
- package/dist/agent-farm/utils/config.js.map +1 -1
- package/dist/agent-farm/utils/harness.d.ts +103 -7
- package/dist/agent-farm/utils/harness.d.ts.map +1 -1
- package/dist/agent-farm/utils/harness.js +126 -19
- package/dist/agent-farm/utils/harness.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +6 -32
- package/dist/cli.js.map +1 -1
- package/dist/commands/consult/cli-options.d.ts +34 -0
- package/dist/commands/consult/cli-options.d.ts.map +1 -0
- package/dist/commands/consult/cli-options.js +67 -0
- package/dist/commands/consult/cli-options.js.map +1 -0
- package/dist/commands/consult/index.d.ts +56 -2
- package/dist/commands/consult/index.d.ts.map +1 -1
- package/dist/commands/consult/index.js +272 -28
- package/dist/commands/consult/index.js.map +1 -1
- package/dist/commands/consult/metrics.d.ts +42 -0
- package/dist/commands/consult/metrics.d.ts.map +1 -1
- package/dist/commands/consult/metrics.js +129 -9
- package/dist/commands/consult/metrics.js.map +1 -1
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +71 -19
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/porch/checks.d.ts +23 -0
- package/dist/commands/porch/checks.d.ts.map +1 -1
- package/dist/commands/porch/checks.js +60 -0
- package/dist/commands/porch/checks.js.map +1 -1
- package/dist/commands/porch/config.d.ts +20 -0
- package/dist/commands/porch/config.d.ts.map +1 -1
- package/dist/commands/porch/config.js +20 -0
- package/dist/commands/porch/config.js.map +1 -1
- package/dist/commands/porch/index.js +21 -28
- package/dist/commands/porch/index.js.map +1 -1
- package/dist/commands/porch/next.d.ts.map +1 -1
- package/dist/commands/porch/next.js +2 -41
- package/dist/commands/porch/next.js.map +1 -1
- package/dist/lib/config.d.ts +69 -3
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +67 -3
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/consult-lanes.d.ts +149 -0
- package/dist/lib/consult-lanes.d.ts.map +1 -0
- package/dist/lib/consult-lanes.js +360 -0
- package/dist/lib/consult-lanes.js.map +1 -0
- package/dist/lib/github.js +1 -1
- package/dist/lib/github.js.map +1 -1
- package/dist/lib/prompt-behavior-metrics.d.ts +67 -0
- package/dist/lib/prompt-behavior-metrics.d.ts.map +1 -0
- package/dist/lib/prompt-behavior-metrics.js +290 -0
- package/dist/lib/prompt-behavior-metrics.js.map +1 -0
- package/dist/lib/skeleton.d.ts +22 -1
- package/dist/lib/skeleton.d.ts.map +1 -1
- package/dist/lib/skeleton.js +120 -0
- package/dist/lib/skeleton.js.map +1 -1
- package/dist/lib/test-env.d.ts +74 -0
- package/dist/lib/test-env.d.ts.map +1 -0
- package/dist/lib/test-env.js +92 -0
- package/dist/lib/test-env.js.map +1 -0
- package/dist/terminal/pty-manager.d.ts +2 -0
- package/dist/terminal/pty-manager.d.ts.map +1 -1
- package/dist/terminal/pty-manager.js +16 -6
- package/dist/terminal/pty-manager.js.map +1 -1
- package/dist/terminal/pty-session.d.ts +131 -0
- package/dist/terminal/pty-session.d.ts.map +1 -1
- package/dist/terminal/pty-session.js +232 -39
- package/dist/terminal/pty-session.js.map +1 -1
- package/dist/terminal/ring-buffer.d.ts +39 -6
- package/dist/terminal/ring-buffer.d.ts.map +1 -1
- package/dist/terminal/ring-buffer.js +74 -6
- package/dist/terminal/ring-buffer.js.map +1 -1
- package/dist/terminal/session-manager.d.ts +53 -0
- package/dist/terminal/session-manager.d.ts.map +1 -1
- package/dist/terminal/session-manager.js +94 -9
- package/dist/terminal/session-manager.js.map +1 -1
- package/dist/terminal/session-screen.d.ts +91 -0
- package/dist/terminal/session-screen.d.ts.map +1 -0
- package/dist/terminal/session-screen.js +135 -0
- package/dist/terminal/session-screen.js.map +1 -0
- package/dist/terminal/shellper-main.js +2 -1
- package/dist/terminal/shellper-main.js.map +1 -1
- package/dist/terminal/shellper-process.d.ts +1 -1
- package/dist/terminal/shellper-process.d.ts.map +1 -1
- package/dist/terminal/shellper-process.js +17 -8
- package/dist/terminal/shellper-process.js.map +1 -1
- package/dist/terminal/shellper-replay-buffer.d.ts +69 -7
- package/dist/terminal/shellper-replay-buffer.d.ts.map +1 -1
- package/dist/terminal/shellper-replay-buffer.js +167 -31
- package/dist/terminal/shellper-replay-buffer.js.map +1 -1
- package/package.json +8 -6
- package/skeleton/.claude/skills/afx/SKILL.md +14 -1
- package/skeleton/.claude/skills/arch-init/SKILL.md +22 -9
- package/skeleton/.claude/skills/arch-save/SKILL.md +183 -0
- package/skeleton/.claude/skills/codev/SKILL.md +31 -0
- package/skeleton/.claude/skills/runnable-worktrees/SKILL.md +119 -0
- package/skeleton/.codex/skills/afx/SKILL.md +14 -1
- package/skeleton/.codex/skills/arch-init/SKILL.md +22 -9
- package/skeleton/.codex/skills/arch-save/SKILL.md +183 -0
- package/skeleton/.codex/skills/codev/SKILL.md +31 -0
- package/skeleton/.codex/skills/runnable-worktrees/SKILL.md +119 -0
- package/skeleton/protocols/air/builder-prompt.md +28 -47
- package/skeleton/protocols/air/consult-types/impl-review.md +17 -34
- package/skeleton/protocols/air/consult-types/pr-review.md +14 -37
- package/skeleton/protocols/air/prompts/implement.md +20 -73
- package/skeleton/protocols/air/prompts/pr.md +22 -54
- package/skeleton/protocols/air/protocol.md +40 -71
- package/skeleton/protocols/aspir/builder-prompt.md +29 -66
- package/skeleton/protocols/aspir/consult-types/impl-review.md +17 -44
- package/skeleton/protocols/aspir/consult-types/phase-review.md +17 -44
- package/skeleton/protocols/aspir/consult-types/plan-review.md +14 -38
- package/skeleton/protocols/aspir/consult-types/pr-review.md +13 -39
- package/skeleton/protocols/aspir/consult-types/spec-review.md +13 -33
- package/skeleton/protocols/aspir/prompts/implement.md +27 -196
- package/skeleton/protocols/aspir/prompts/plan.md +21 -85
- package/skeleton/protocols/aspir/prompts/review.md +29 -228
- package/skeleton/protocols/aspir/prompts/specify.md +25 -111
- package/skeleton/protocols/aspir/protocol.json +10 -0
- package/skeleton/protocols/aspir/protocol.md +36 -82
- package/skeleton/protocols/bugfix/builder-prompt.md +23 -45
- package/skeleton/protocols/bugfix/consult-types/impl-review.md +21 -45
- package/skeleton/protocols/bugfix/consult-types/pr-review.md +18 -52
- package/skeleton/protocols/bugfix/prompts/fix.md +16 -61
- package/skeleton/protocols/bugfix/prompts/investigate.md +18 -62
- package/skeleton/protocols/bugfix/prompts/pr.md +22 -57
- package/skeleton/protocols/bugfix/protocol.md +44 -50
- package/skeleton/protocols/experiment/builder-prompt.md +25 -58
- package/skeleton/protocols/experiment/protocol.md +22 -188
- package/skeleton/protocols/experiment/templates/notes.md +11 -71
- package/skeleton/protocols/maintain/builder-prompt.md +24 -45
- package/skeleton/protocols/maintain/consult-types/impl-review.md +17 -44
- package/skeleton/protocols/maintain/consult-types/pr-review.md +13 -39
- package/skeleton/protocols/maintain/prompts/maintain.md +1 -1
- package/skeleton/protocols/maintain/protocol.md +32 -266
- package/skeleton/protocols/maintain/templates/maintenance-run.md +11 -0
- package/skeleton/protocols/pir/builder-prompt.md +30 -64
- package/skeleton/protocols/pir/consult-types/impl-review.md +17 -50
- package/skeleton/protocols/pir/consult-types/pr-review.md +11 -40
- package/skeleton/protocols/pir/prompts/implement.md +1 -2
- package/skeleton/protocols/pir/prompts/plan.md +0 -3
- package/skeleton/protocols/pir/prompts/review.md +1 -1
- package/skeleton/protocols/pir/protocol.md +54 -177
- package/skeleton/protocols/research/builder-prompt.md +27 -58
- package/skeleton/protocols/research/protocol.md +28 -153
- package/skeleton/protocols/spike/builder-prompt.md +30 -48
- package/skeleton/protocols/spike/protocol.md +24 -107
- package/skeleton/protocols/spike/templates/findings.md +9 -39
- package/skeleton/protocols/spir/builder-prompt.md +33 -62
- package/skeleton/protocols/spir/consult-types/impl-review.md +17 -44
- package/skeleton/protocols/spir/consult-types/phase-review.md +17 -44
- package/skeleton/protocols/spir/consult-types/plan-review.md +14 -38
- package/skeleton/protocols/spir/consult-types/pr-review.md +13 -39
- package/skeleton/protocols/spir/consult-types/spec-review.md +13 -33
- package/skeleton/protocols/spir/prompts/implement.md +26 -195
- package/skeleton/protocols/spir/prompts/plan.md +20 -84
- package/skeleton/protocols/spir/prompts/review.md +28 -227
- package/skeleton/protocols/spir/prompts/specify.md +24 -110
- package/skeleton/protocols/spir/protocol.json +10 -0
- package/skeleton/protocols/spir/protocol.md +84 -633
- package/skeleton/protocols/spir/templates/plan.md +28 -147
- package/skeleton/protocols/spir/templates/review.md +21 -80
- package/skeleton/protocols/spir/templates/spec.md +24 -118
- package/skeleton/resources/commands/agent-farm.md +259 -0
- package/skeleton/resources/commands/consult.md +236 -8
- package/skeleton/resources/commands/overview.md +1 -0
- package/skeleton/roles/architect.md +67 -317
- package/skeleton/roles/builder.md +77 -191
- package/skeleton/templates/AGENTS.md +2 -0
- package/skeleton/templates/CLAUDE.md +2 -0
- package/templates/tower.html +39 -2
- package/dashboard-dist/assets/index-BZ1x7x1X.js.map +0 -1
- package/dashboard-dist/assets/index-DB1KBSNE.css +0 -32
- package/dist/agent-farm/servers/send-buffer.d.ts +0 -59
- package/dist/agent-farm/servers/send-buffer.d.ts.map +0 -1
- package/dist/agent-farm/servers/send-buffer.js +0 -116
- package/dist/agent-farm/servers/send-buffer.js.map +0 -1
- package/skeleton/porch/prompts/defend.md +0 -108
- package/skeleton/porch/prompts/diagnose.md +0 -70
- package/skeleton/porch/prompts/evaluate.md +0 -132
- package/skeleton/porch/prompts/fix.md +0 -59
- package/skeleton/porch/prompts/implement.md +0 -88
- package/skeleton/porch/prompts/plan.md +0 -74
- package/skeleton/porch/prompts/pr.md +0 -84
- package/skeleton/porch/prompts/review.md +0 -249
- package/skeleton/porch/prompts/specify.md +0 -53
- package/skeleton/porch/prompts/test.md +0 -63
- package/skeleton/protocols/aspir/templates/plan.md +0 -184
- package/skeleton/protocols/aspir/templates/review.md +0 -120
- package/skeleton/protocols/aspir/templates/spec.md +0 -154
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cron message delivery through the mailbox + gate (Spec 1313, Phase 6).
|
|
3
|
+
*
|
|
4
|
+
* Cron is today the most unguarded message writer: it wrote straight to the PTY with
|
|
5
|
+
* no idle check and logged "delivered" unconditionally. This phase makes it an
|
|
6
|
+
* ordinary mailbox sender — every cron notification is persisted and then delivered
|
|
7
|
+
* through the SAME single gated path (`deliverAgentMailSerialized`) that `handleSend`
|
|
8
|
+
* and the backstop drainer use, so a cron message can never land mid-draft or fuse
|
|
9
|
+
* with a human's half-typed line. There is no force path: a busy/menu/wrapper screen
|
|
10
|
+
* holds the message for the backstop, exactly like any other send.
|
|
11
|
+
*
|
|
12
|
+
* Two cron-specific twists on top of the shared path:
|
|
13
|
+
* - **Supersede key = task name** (Baked Decision 6): a newer run of a task replaces
|
|
14
|
+
* its own older *held* row instead of queueing a backlog. Supersede keys are
|
|
15
|
+
* cron-only in this project — no non-cron send ever supplies one.
|
|
16
|
+
* - **Honest run outcome**: the caller logs the real fate (`delivered` / `held` /
|
|
17
|
+
* `superseded`) rather than an unconditional "delivered".
|
|
18
|
+
*
|
|
19
|
+
* This module holds the registry-free orchestration core behind the Phase-4
|
|
20
|
+
* {@link DeliveryPorts} seam, so it is unit-testable against a real mailbox DB with
|
|
21
|
+
* fake edges (no live Tower). The identity resolution that needs the live routing
|
|
22
|
+
* registry (`resolveTarget` + the architect reverse-map + the dead-session registry
|
|
23
|
+
* fallback) lives in `tower-routes.ts`'s `deliverCronMessage`, which calls this.
|
|
24
|
+
*/
|
|
25
|
+
import { supersede, getById, countHeldWithKey } from '../db/mailbox.js';
|
|
26
|
+
import { deliverAgentMailSerialized } from './mailbox-delivery.js';
|
|
27
|
+
/** The pseudo-agent identity every cron notification is sent as. */
|
|
28
|
+
export const CRON_SENDER = 'af-cron';
|
|
29
|
+
/**
|
|
30
|
+
* Persist a cron notification (superseding any still-held row from an earlier run of
|
|
31
|
+
* the same task) and attempt one gated delivery, returning the run's real outcome.
|
|
32
|
+
*
|
|
33
|
+
* The corruption-safety is entirely inherited from {@link deliverAgentMailSerialized}:
|
|
34
|
+
* the body is only ever written to a render-verified empty prompt, and the per-agent
|
|
35
|
+
* serializer means a concurrent send can never interleave with this write. A busy or
|
|
36
|
+
* unclassifiable screen simply leaves the row held for the backstop — there is no
|
|
37
|
+
* force path here, by construction.
|
|
38
|
+
*/
|
|
39
|
+
export async function deliverCronMail(ports, db, target) {
|
|
40
|
+
const { workspacePath, toAgent, supersedeKey } = target;
|
|
41
|
+
// Did an earlier run of this task leave a row still held? Read it BEFORE the
|
|
42
|
+
// supersede, with no await between, so the pair is atomic on the synchronous DB
|
|
43
|
+
// handle (see countHeldWithKey). This only informs the log word — the "no backlog"
|
|
44
|
+
// correctness comes from supersede() being atomic regardless.
|
|
45
|
+
const replacedPrior = countHeldWithKey(db, workspacePath, supersedeKey) > 0;
|
|
46
|
+
const row = supersede(db, workspacePath, supersedeKey, {
|
|
47
|
+
workspacePath,
|
|
48
|
+
toAgent,
|
|
49
|
+
terminalId: target.terminalId,
|
|
50
|
+
body: target.body,
|
|
51
|
+
formattedMessage: target.formattedMessage,
|
|
52
|
+
fromAgent: CRON_SENDER,
|
|
53
|
+
fromWorkspace: workspacePath,
|
|
54
|
+
}, ports.now());
|
|
55
|
+
// The held set changed (a new row enqueued, possibly replacing a prior held one) →
|
|
56
|
+
// refresh the indicator count (Spec 1313, Phase 7). A clean delivery below fires it
|
|
57
|
+
// again when the row leaves the set; both are cheap, idempotent refetch triggers.
|
|
58
|
+
ports.onHeldStateChange();
|
|
59
|
+
try {
|
|
60
|
+
await deliverAgentMailSerialized(ports, db, workspacePath, toAgent);
|
|
61
|
+
}
|
|
62
|
+
catch (err) {
|
|
63
|
+
// A gate/write error leaves the row HELD (markDelivered only runs on a completed
|
|
64
|
+
// write); the backstop drainer retries. Mirrors handleSend — never throws upward.
|
|
65
|
+
ports.log(`[cron] delivery attempt errored for ${toAgent} (row ${row.id.slice(0, 8)}… stays held): ${String(err)}`);
|
|
66
|
+
}
|
|
67
|
+
const stored = getById(db, row.id);
|
|
68
|
+
if (stored?.status === 'delivered') {
|
|
69
|
+
return { outcome: 'delivered', reason: null, mailboxId: row.id };
|
|
70
|
+
}
|
|
71
|
+
// Held. `reason` is set by the delivery pass when it holds; default to `busy` for
|
|
72
|
+
// the rare case where an older row for the same agent delivered first and left ours
|
|
73
|
+
// queued behind it (its Enter makes the line busy for the next pass anyway).
|
|
74
|
+
return {
|
|
75
|
+
outcome: replacedPrior ? 'superseded' : 'held',
|
|
76
|
+
reason: stored?.reason ?? 'busy',
|
|
77
|
+
mailboxId: row.id,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=cron-delivery.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cron-delivery.js","sourceRoot":"","sources":["../../../src/agent-farm/servers/cron-delivery.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAGH,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAExE,OAAO,EAAE,0BAA0B,EAAsB,MAAM,uBAAuB,CAAC;AAEvF,oEAAoE;AACpE,MAAM,CAAC,MAAM,WAAW,GAAG,SAAS,CAAC;AAoCrC;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,KAAoB,EACpB,EAAqB,EACrB,MAAkB;IAElB,MAAM,EAAE,aAAa,EAAE,OAAO,EAAE,YAAY,EAAE,GAAG,MAAM,CAAC;IAExD,6EAA6E;IAC7E,gFAAgF;IAChF,mFAAmF;IACnF,8DAA8D;IAC9D,MAAM,aAAa,GAAG,gBAAgB,CAAC,EAAE,EAAE,aAAa,EAAE,YAAY,CAAC,GAAG,CAAC,CAAC;IAC5E,MAAM,GAAG,GAAG,SAAS,CACnB,EAAE,EACF,aAAa,EACb,YAAY,EACZ;QACE,aAAa;QACb,OAAO;QACP,UAAU,EAAE,MAAM,CAAC,UAAU;QAC7B,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,gBAAgB,EAAE,MAAM,CAAC,gBAAgB;QACzC,SAAS,EAAE,WAAW;QACtB,aAAa,EAAE,aAAa;KAC7B,EACD,KAAK,CAAC,GAAG,EAAE,CACZ,CAAC;IACF,mFAAmF;IACnF,oFAAoF;IACpF,kFAAkF;IAClF,KAAK,CAAC,iBAAiB,EAAE,CAAC;IAE1B,IAAI,CAAC;QACH,MAAM,0BAA0B,CAAC,KAAK,EAAE,EAAE,EAAE,aAAa,EAAE,OAAO,CAAC,CAAC;IACtE,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,iFAAiF;QACjF,kFAAkF;QAClF,KAAK,CAAC,GAAG,CAAC,uCAAuC,OAAO,SAAS,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,kBAAkB,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IACtH,CAAC;IAED,MAAM,MAAM,GAAG,OAAO,CAAC,EAAE,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;IACnC,IAAI,MAAM,EAAE,MAAM,KAAK,WAAW,EAAE,CAAC;QACnC,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC;IACnE,CAAC;IACD,kFAAkF;IAClF,oFAAoF;IACpF,6EAA6E;IAC7E,OAAO;QACL,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,MAAM;QAC9C,MAAM,EAAE,MAAM,EAAE,MAAM,IAAI,MAAM;QAChC,SAAS,EAAE,GAAG,CAAC,EAAE;KAClB,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Due-time timer for the delayed `afx send --delay --interrupt` path (Spec 1307,
|
|
3
|
+
* reshaped by Spec 1313 round 3).
|
|
4
|
+
*
|
|
5
|
+
* ## What this module does NOW (and no longer does)
|
|
6
|
+
*
|
|
7
|
+
* The message *body* of every `--delay` send is persisted to the durable mailbox
|
|
8
|
+
* at REQUEST time, with a `not_before` due time, by `handleDelayedSend` in
|
|
9
|
+
* tower-routes.ts — NOT here, and NOT at fire time. The gated backstop drainer
|
|
10
|
+
* delivers that row once `not_before` passes, so a plain `--delay` keeps no timer
|
|
11
|
+
* at all and survives a Tower restart by construction.
|
|
12
|
+
*
|
|
13
|
+
* This module holds a due-time timer used by ONE caller: the delayed-`--interrupt`
|
|
14
|
+
* path. When it fires it writes only the Ctrl+C that ends the current turn — no
|
|
15
|
+
* message body, and it marks nothing `delivered`. The body then lands through the
|
|
16
|
+
* same render gate every send uses, after the ^C ends the turn.
|
|
17
|
+
*
|
|
18
|
+
* ## Why the registry exists at all
|
|
19
|
+
*
|
|
20
|
+
* A bare `setTimeout` would work until Tower shuts down, at which point the
|
|
21
|
+
* process would either hang on a pending timer or exit with a ^C half-scheduled
|
|
22
|
+
* and no record of it. The registry makes shutdown explicit.
|
|
23
|
+
*
|
|
24
|
+
* ## Shutdown drops the ^C nudge, never the message
|
|
25
|
+
*
|
|
26
|
+
* A pre-due `--delay` message is a persisted mailbox row: it survives a restart
|
|
27
|
+
* and delivers when the target's prompt is next clean. Only the in-memory ^C
|
|
28
|
+
* nudge is dropped on shutdown — recoverable (a human re-interrupts if it matters),
|
|
29
|
+
* and matching the documented "only the interrupt semantics gracefully degrade"
|
|
30
|
+
* boundary. This is the CONSCIOUS reversal of Spec 1307's original body-drop-on-
|
|
31
|
+
* restart trade (see review 1313): the render gate now supplies the protection that
|
|
32
|
+
* trade wanted — a post-restart delivery still only lands on a render-verified empty
|
|
33
|
+
* prompt, and a stale pending row is visible and cancellable in `afx inbox`.
|
|
34
|
+
*/
|
|
35
|
+
/**
|
|
36
|
+
* Upper bound on `--delay`, in seconds.
|
|
37
|
+
*
|
|
38
|
+
* One hour. Not a meaningful workflow limit — it exists so a typo (`--delay
|
|
39
|
+
* 1500` when 15 was meant) cannot park a message far in the future unnoticed.
|
|
40
|
+
* Under Spec 1313 round 3 a parked send IS listable and cancellable via
|
|
41
|
+
* `afx inbox` / `afx inbox dismiss` (it is a durable mailbox row), but the
|
|
42
|
+
* ceiling stays as a cheap guard against the typo case.
|
|
43
|
+
*/
|
|
44
|
+
export declare const MAX_DELAY_SECONDS = 3600;
|
|
45
|
+
/**
|
|
46
|
+
* Validate a delay in seconds, returning null when acceptable or an error
|
|
47
|
+
* string naming the problem.
|
|
48
|
+
*
|
|
49
|
+
* `Number.isInteger` rather than a bare comparison chain: `NaN > 0` and
|
|
50
|
+
* `NaN <= 0` are both false, so a NaN slips through any single comparison
|
|
51
|
+
* written the obvious way and yields a `setTimeout` that fires immediately —
|
|
52
|
+
* silently converting a delayed send into an immediate one. Infinity is
|
|
53
|
+
* rejected for the same class of reason.
|
|
54
|
+
*/
|
|
55
|
+
export declare function validateDelaySeconds(value: unknown): string | null;
|
|
56
|
+
/**
|
|
57
|
+
* Schedule `deliver` to run after `delaySeconds`.
|
|
58
|
+
*
|
|
59
|
+
* The callback is responsible for re-resolving the session and re-deciding how
|
|
60
|
+
* to deliver; this module guarantees only *when* it is invoked, and that it is
|
|
61
|
+
* invoked at most once.
|
|
62
|
+
*/
|
|
63
|
+
export declare function scheduleDelayedSend(delaySeconds: number, terminalId: string,
|
|
64
|
+
/**
|
|
65
|
+
* Invoked when the send comes due. Receives `isStillLive`, which it must
|
|
66
|
+
* re-check at the moment it actually writes (inside the submission lock): a
|
|
67
|
+
* delivery can acquire the lock only AFTER a shutdown that fired while it
|
|
68
|
+
* queued, and the generation check below only guards the moment BEFORE it
|
|
69
|
+
* enters the lock. Return value is ignored.
|
|
70
|
+
*/
|
|
71
|
+
deliver: (isStillLive: () => boolean) => unknown): void;
|
|
72
|
+
/**
|
|
73
|
+
* Cancel every pending delayed send without delivering. Returns the count of
|
|
74
|
+
* still-timing sends dropped, so shutdown can log it rather than losing
|
|
75
|
+
* messages silently.
|
|
76
|
+
*
|
|
77
|
+
* Covers two states: sends still on their timer (cleared here) and sends whose
|
|
78
|
+
* timer has fired but whose delivery has not started — invalidated by bumping
|
|
79
|
+
* `generation`, which the delivery callback re-checks. A delivery already
|
|
80
|
+
* writing when this runs is NOT interrupted; see `generation`'s note.
|
|
81
|
+
*/
|
|
82
|
+
export declare function shutdownDelayedSends(): number;
|
|
83
|
+
/** Number of pending delayed sends. Diagnostics and tests. */
|
|
84
|
+
export declare function pendingDelayedSendCount(): number;
|
|
85
|
+
//# sourceMappingURL=delayed-send.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delayed-send.d.ts","sourceRoot":"","sources":["../../../src/agent-farm/servers/delayed-send.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAwCH;;;;;;;;GAQG;AACH,eAAO,MAAM,iBAAiB,OAAO,CAAC;AAEtC;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAWlE;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM;AAClB;;;;;;GAMG;AACH,OAAO,EAAE,CAAC,WAAW,EAAE,MAAM,OAAO,KAAK,OAAO,GAC/C,IAAI,CAmCN;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,CAQ7C;AAED,8DAA8D;AAC9D,wBAAgB,uBAAuB,IAAI,MAAM,CAEhD"}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Due-time timer for the delayed `afx send --delay --interrupt` path (Spec 1307,
|
|
3
|
+
* reshaped by Spec 1313 round 3).
|
|
4
|
+
*
|
|
5
|
+
* ## What this module does NOW (and no longer does)
|
|
6
|
+
*
|
|
7
|
+
* The message *body* of every `--delay` send is persisted to the durable mailbox
|
|
8
|
+
* at REQUEST time, with a `not_before` due time, by `handleDelayedSend` in
|
|
9
|
+
* tower-routes.ts — NOT here, and NOT at fire time. The gated backstop drainer
|
|
10
|
+
* delivers that row once `not_before` passes, so a plain `--delay` keeps no timer
|
|
11
|
+
* at all and survives a Tower restart by construction.
|
|
12
|
+
*
|
|
13
|
+
* This module holds a due-time timer used by ONE caller: the delayed-`--interrupt`
|
|
14
|
+
* path. When it fires it writes only the Ctrl+C that ends the current turn — no
|
|
15
|
+
* message body, and it marks nothing `delivered`. The body then lands through the
|
|
16
|
+
* same render gate every send uses, after the ^C ends the turn.
|
|
17
|
+
*
|
|
18
|
+
* ## Why the registry exists at all
|
|
19
|
+
*
|
|
20
|
+
* A bare `setTimeout` would work until Tower shuts down, at which point the
|
|
21
|
+
* process would either hang on a pending timer or exit with a ^C half-scheduled
|
|
22
|
+
* and no record of it. The registry makes shutdown explicit.
|
|
23
|
+
*
|
|
24
|
+
* ## Shutdown drops the ^C nudge, never the message
|
|
25
|
+
*
|
|
26
|
+
* A pre-due `--delay` message is a persisted mailbox row: it survives a restart
|
|
27
|
+
* and delivers when the target's prompt is next clean. Only the in-memory ^C
|
|
28
|
+
* nudge is dropped on shutdown — recoverable (a human re-interrupts if it matters),
|
|
29
|
+
* and matching the documented "only the interrupt semantics gracefully degrade"
|
|
30
|
+
* boundary. This is the CONSCIOUS reversal of Spec 1307's original body-drop-on-
|
|
31
|
+
* restart trade (see review 1313): the render gate now supplies the protection that
|
|
32
|
+
* trade wanted — a post-restart delivery still only lands on a render-verified empty
|
|
33
|
+
* prompt, and a stale pending row is visible and cancellable in `afx inbox`.
|
|
34
|
+
*/
|
|
35
|
+
const pending = new Set();
|
|
36
|
+
/*
|
|
37
|
+
* NOTE: this module no longer serialises deliveries. It used to hold a
|
|
38
|
+
* per-terminal promise chain; Spec 1273's `submitToSession` now owns that, and
|
|
39
|
+
* every due message re-enters the mailbox delivery path, which submits under the
|
|
40
|
+
* lock. One mechanism, not two — per the architect's ruling that this project
|
|
41
|
+
* adopts the primitive rather than keeping a rival.
|
|
42
|
+
*/
|
|
43
|
+
/**
|
|
44
|
+
* Incremented by every shutdown. Each scheduled send captures the value current
|
|
45
|
+
* when it was scheduled and re-checks it at delivery.
|
|
46
|
+
*
|
|
47
|
+
* Clearing the pending timers is not sufficient on its own. A message whose
|
|
48
|
+
* timer has already fired is out of `pending` but its delivery may not have
|
|
49
|
+
* started yet — it can be waiting on the session's `submitToSession` lock
|
|
50
|
+
* behind an in-flight write. That queued delivery would otherwise run AFTER
|
|
51
|
+
* shutdown, which is exactly what "shutdown drops pending delayed sends"
|
|
52
|
+
* promises it will not. The generation check re-read at delivery time makes
|
|
53
|
+
* such an already-scheduled delivery a no-op.
|
|
54
|
+
*
|
|
55
|
+
* Honest bound: a delivery that has ALREADY begun its write when shutdown fires
|
|
56
|
+
* still completes — the lock does not interrupt a write in progress. "Drops on
|
|
57
|
+
* shutdown" therefore means "does not START anything new," not "aborts what is
|
|
58
|
+
* mid-flight." See the shutdown function.
|
|
59
|
+
*/
|
|
60
|
+
let generation = 0;
|
|
61
|
+
/**
|
|
62
|
+
* Upper bound on `--delay`, in seconds.
|
|
63
|
+
*
|
|
64
|
+
* One hour. Not a meaningful workflow limit — it exists so a typo (`--delay
|
|
65
|
+
* 1500` when 15 was meant) cannot park a message far in the future unnoticed.
|
|
66
|
+
* Under Spec 1313 round 3 a parked send IS listable and cancellable via
|
|
67
|
+
* `afx inbox` / `afx inbox dismiss` (it is a durable mailbox row), but the
|
|
68
|
+
* ceiling stays as a cheap guard against the typo case.
|
|
69
|
+
*/
|
|
70
|
+
export const MAX_DELAY_SECONDS = 3600;
|
|
71
|
+
/**
|
|
72
|
+
* Validate a delay in seconds, returning null when acceptable or an error
|
|
73
|
+
* string naming the problem.
|
|
74
|
+
*
|
|
75
|
+
* `Number.isInteger` rather than a bare comparison chain: `NaN > 0` and
|
|
76
|
+
* `NaN <= 0` are both false, so a NaN slips through any single comparison
|
|
77
|
+
* written the obvious way and yields a `setTimeout` that fires immediately —
|
|
78
|
+
* silently converting a delayed send into an immediate one. Infinity is
|
|
79
|
+
* rejected for the same class of reason.
|
|
80
|
+
*/
|
|
81
|
+
export function validateDelaySeconds(value) {
|
|
82
|
+
if (typeof value !== 'number' || !Number.isInteger(value)) {
|
|
83
|
+
return `delay must be a whole number of seconds, got '${String(value)}'`;
|
|
84
|
+
}
|
|
85
|
+
if (value <= 0) {
|
|
86
|
+
return `delay must be greater than zero, got ${value}`;
|
|
87
|
+
}
|
|
88
|
+
if (value > MAX_DELAY_SECONDS) {
|
|
89
|
+
return `delay must be at most ${MAX_DELAY_SECONDS} seconds (1 hour), got ${value}`;
|
|
90
|
+
}
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Schedule `deliver` to run after `delaySeconds`.
|
|
95
|
+
*
|
|
96
|
+
* The callback is responsible for re-resolving the session and re-deciding how
|
|
97
|
+
* to deliver; this module guarantees only *when* it is invoked, and that it is
|
|
98
|
+
* invoked at most once.
|
|
99
|
+
*/
|
|
100
|
+
export function scheduleDelayedSend(delaySeconds, terminalId,
|
|
101
|
+
/**
|
|
102
|
+
* Invoked when the send comes due. Receives `isStillLive`, which it must
|
|
103
|
+
* re-check at the moment it actually writes (inside the submission lock): a
|
|
104
|
+
* delivery can acquire the lock only AFTER a shutdown that fired while it
|
|
105
|
+
* queued, and the generation check below only guards the moment BEFORE it
|
|
106
|
+
* enters the lock. Return value is ignored.
|
|
107
|
+
*/
|
|
108
|
+
deliver) {
|
|
109
|
+
const entry = {
|
|
110
|
+
terminalId,
|
|
111
|
+
dueAt: Date.now() + delaySeconds * 1000,
|
|
112
|
+
// Assigned below; the object must exist first so the callback can
|
|
113
|
+
// deregister itself by identity.
|
|
114
|
+
timer: undefined,
|
|
115
|
+
};
|
|
116
|
+
const scheduledGeneration = generation;
|
|
117
|
+
entry.timer = setTimeout(() => {
|
|
118
|
+
// Deregister BEFORE delivering. If delivery throws, the entry must not be
|
|
119
|
+
// left behind as a phantom pending send that shutdown would then report.
|
|
120
|
+
pending.delete(entry);
|
|
121
|
+
void (async () => {
|
|
122
|
+
// Generation re-checked at DELIVERY time, not timer time: delivery now
|
|
123
|
+
// queues behind the session's submission lock, so the wait to actually
|
|
124
|
+
// write can outlast a shutdown.
|
|
125
|
+
if (generation !== scheduledGeneration)
|
|
126
|
+
return;
|
|
127
|
+
try {
|
|
128
|
+
// Passed through to the write site, where it is re-checked while the
|
|
129
|
+
// lock is held — closing the shutdown-during-lock-wait window.
|
|
130
|
+
await deliver(() => generation === scheduledGeneration);
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
// The mailbox delivery path logs a write failure at its own site with
|
|
134
|
+
// terminal context; this catch is a last-resort guard so an unexpected
|
|
135
|
+
// throw cannot become an unhandled rejection that takes Tower down over
|
|
136
|
+
// one undeliverable message.
|
|
137
|
+
}
|
|
138
|
+
})();
|
|
139
|
+
}, delaySeconds * 1000);
|
|
140
|
+
pending.add(entry);
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Cancel every pending delayed send without delivering. Returns the count of
|
|
144
|
+
* still-timing sends dropped, so shutdown can log it rather than losing
|
|
145
|
+
* messages silently.
|
|
146
|
+
*
|
|
147
|
+
* Covers two states: sends still on their timer (cleared here) and sends whose
|
|
148
|
+
* timer has fired but whose delivery has not started — invalidated by bumping
|
|
149
|
+
* `generation`, which the delivery callback re-checks. A delivery already
|
|
150
|
+
* writing when this runs is NOT interrupted; see `generation`'s note.
|
|
151
|
+
*/
|
|
152
|
+
export function shutdownDelayedSends() {
|
|
153
|
+
const count = pending.size;
|
|
154
|
+
for (const entry of pending) {
|
|
155
|
+
clearTimeout(entry.timer);
|
|
156
|
+
}
|
|
157
|
+
pending.clear();
|
|
158
|
+
generation++;
|
|
159
|
+
return count;
|
|
160
|
+
}
|
|
161
|
+
/** Number of pending delayed sends. Diagnostics and tests. */
|
|
162
|
+
export function pendingDelayedSendCount() {
|
|
163
|
+
return pending.size;
|
|
164
|
+
}
|
|
165
|
+
//# sourceMappingURL=delayed-send.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delayed-send.js","sourceRoot":"","sources":["../../../src/agent-farm/servers/delayed-send.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAWH,MAAM,OAAO,GAAG,IAAI,GAAG,EAAsB,CAAC;AAE9C;;;;;;GAMG;AAEH;;;;;;;;;;;;;;;;GAgBG;AACH,IAAI,UAAU,GAAG,CAAC,CAAC;AAEnB;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,IAAI,CAAC;AAEtC;;;;;;;;;GASG;AACH,MAAM,UAAU,oBAAoB,CAAC,KAAc;IACjD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC;QAC1D,OAAO,iDAAiD,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC;IAC3E,CAAC;IACD,IAAI,KAAK,IAAI,CAAC,EAAE,CAAC;QACf,OAAO,wCAAwC,KAAK,EAAE,CAAC;IACzD,CAAC;IACD,IAAI,KAAK,GAAG,iBAAiB,EAAE,CAAC;QAC9B,OAAO,yBAAyB,iBAAiB,0BAA0B,KAAK,EAAE,CAAC;IACrF,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,mBAAmB,CACjC,YAAoB,EACpB,UAAkB;AAClB;;;;;;GAMG;AACH,OAAgD;IAEhD,MAAM,KAAK,GAAuB;QAChC,UAAU;QACV,KAAK,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,YAAY,GAAG,IAAI;QACvC,kEAAkE;QAClE,iCAAiC;QACjC,KAAK,EAAE,SAAqD;KAC7D,CAAC;IAEF,MAAM,mBAAmB,GAAG,UAAU,CAAC;IAEvC,KAAK,CAAC,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;QAC5B,0EAA0E;QAC1E,yEAAyE;QACzE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAEtB,KAAK,CAAC,KAAK,IAAI,EAAE;YACf,uEAAuE;YACvE,uEAAuE;YACvE,gCAAgC;YAChC,IAAI,UAAU,KAAK,mBAAmB;gBAAE,OAAO;YAC/C,IAAI,CAAC;gBACH,qEAAqE;gBACrE,+DAA+D;gBAC/D,MAAM,OAAO,CAAC,GAAG,EAAE,CAAC,UAAU,KAAK,mBAAmB,CAAC,CAAC;YAC1D,CAAC;YAAC,MAAM,CAAC;gBACP,sEAAsE;gBACtE,uEAAuE;gBACvE,wEAAwE;gBACxE,6BAA6B;YAC/B,CAAC;QACH,CAAC,CAAC,EAAE,CAAC;IACP,CAAC,EAAE,YAAY,GAAG,IAAI,CAAC,CAAC;IAExB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;AACrB,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,oBAAoB;IAClC,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC;IAC3B,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,YAAY,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAC5B,CAAC;IACD,OAAO,CAAC,KAAK,EAAE,CAAC;IAChB,UAAU,EAAE,CAAC;IACb,OAAO,KAAK,CAAC;AACf,CAAC;AAED,8DAA8D;AAC9D,MAAM,UAAU,uBAAuB;IACrC,OAAO,OAAO,CAAC,IAAI,CAAC;AACtB,CAAC"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Render-gate classifier profiles (Spec 1313, Phase 2).
|
|
3
|
+
*
|
|
4
|
+
* A profile tells {@link classifyScreen} how to find and bound a given app's
|
|
5
|
+
* composer. Profiles are per-app *data* by design (spike constraint 9): a TUI
|
|
6
|
+
* layout change is a profile drift the smoke suite catches, never a silent
|
|
7
|
+
* misdelivery — an unmatched marker classifies NOT clean.
|
|
8
|
+
*
|
|
9
|
+
* Measured apps have a profile: claude, codex (spike g2), and agy (Spec 1313
|
|
10
|
+
* Phase 3 measurement — its own marker `> ` and a color-keyed placeholder rule,
|
|
11
|
+
* because agy renders its idle hint in palette-8 gray, not SGR-dim). Everything
|
|
12
|
+
* else — gemini, opencode, an unknown binary, or a launch we can't identify —
|
|
13
|
+
* resolves to `null`, and the caller holds the message with reason `no-profile`.
|
|
14
|
+
* This is the strict app-identity table the spike mandates (constraint 10): we
|
|
15
|
+
* deliberately do NOT reuse `resolveHarness`, whose claude fallback would make an
|
|
16
|
+
* agy terminal masquerade as claude and receive claude's (wrong) profile — a
|
|
17
|
+
* correctness bug, since agy's screens classify by an entirely different rule.
|
|
18
|
+
*/
|
|
19
|
+
import type { GateProfile } from './render-gate.js';
|
|
20
|
+
/** claude composer profile (marker ❯, dim placeholder — measured, spike g2). */
|
|
21
|
+
export declare const CLAUDE_PROFILE: GateProfile;
|
|
22
|
+
/** codex composer profile (marker ›, dim placeholder — measured, spike g2). */
|
|
23
|
+
export declare const CODEX_PROFILE: GateProfile;
|
|
24
|
+
/**
|
|
25
|
+
* agy composer profile (Spec 1313 Phase 3 — net-new measurement). agy breaks the
|
|
26
|
+
* dim-placeholder assumption: its idle mode-hint (`Accept-edits mode: …`) renders
|
|
27
|
+
* at NORMAL intensity but in **palette-8 (gray)**, while user-typed text is
|
|
28
|
+
* default-fg — so the placeholder signal is a foreground COLOR, not SGR-dim
|
|
29
|
+
* (`placeholderFgPalette: 8`). Consequences, all measured: idle → clean (the
|
|
30
|
+
* gray hint is ignored), draft → busy (default-fg text counted), and the
|
|
31
|
+
* per-folder trust dialog → busy (its selected `> Yes, I trust this folder`
|
|
32
|
+
* option is palette-12, counted) — so a blind Enter never confirms filesystem
|
|
33
|
+
* trust. Region bounds reuse the shared rule-line/status patterns (agy brackets
|
|
34
|
+
* its composer with `─────` rules, like claude).
|
|
35
|
+
*/
|
|
36
|
+
export declare const AGY_PROFILE: GateProfile;
|
|
37
|
+
/**
|
|
38
|
+
* The identity signals a caller extracts from a live session. A `PtySession`
|
|
39
|
+
* satisfies this structurally via its `command` / `launchArgs` getters (the
|
|
40
|
+
* Spec 1313 identity seam); tests pass a plain object.
|
|
41
|
+
*
|
|
42
|
+
* `label` is intentionally not used for matching: for a builder it is the
|
|
43
|
+
* builder id (e.g. `spir-1313`), for an architect the architect name — neither
|
|
44
|
+
* names the agent. The authoritative signal is the launch `command`.
|
|
45
|
+
*/
|
|
46
|
+
export interface AppIdentity {
|
|
47
|
+
command: string;
|
|
48
|
+
args?: string[];
|
|
49
|
+
label?: string;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Map a session's identity to its classifier profile, or `null` when the app is
|
|
53
|
+
* unknown/unmeasured (→ caller holds with `no-profile`).
|
|
54
|
+
*
|
|
55
|
+
* Resolution is strict: the launch `command`'s basename must match a measured
|
|
56
|
+
* agent. agy is matched directly (its binary is `agy`/`antigravity`), because the
|
|
57
|
+
* shared {@link detectHarnessFromCommand} does not recognize it and we will not
|
|
58
|
+
* extend that resolver — its claude fallback is exactly the misidentification the
|
|
59
|
+
* gate must avoid (constraint 10). claude/codex resolve via that helper. Wrapped
|
|
60
|
+
* launches — a builder run through `.builder-start.sh` whose `command` is the
|
|
61
|
+
* shell, not the agent — resolve to `null` here; the delivery wiring (Phase 4)
|
|
62
|
+
* supplies the resolved agent command for those (it already reads the launch
|
|
63
|
+
* script to identify the harness, as `afx reset` does). Fail-safe by
|
|
64
|
+
* construction: an unresolved identity is held and surfaced, never guessed.
|
|
65
|
+
*/
|
|
66
|
+
export declare function resolveProfile(identity: AppIdentity): GateProfile | null;
|
|
67
|
+
//# sourceMappingURL=gate-profiles.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gate-profiles.d.ts","sourceRoot":"","sources":["../../../src/agent-farm/servers/gate-profiles.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAIH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AA+BpD,gFAAgF;AAChF,eAAO,MAAM,cAAc,EAAE,WAI5B,CAAC;AAEF,+EAA+E;AAC/E,eAAO,MAAM,aAAa,EAAE,WAI3B,CAAC;AASF;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,WAAW,EAAE,WAKzB,CAAC;AAQF;;;;;;;;GAQG;AACH,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,WAAW,GAAG,WAAW,GAAG,IAAI,CAMxE"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Render-gate classifier profiles (Spec 1313, Phase 2).
|
|
3
|
+
*
|
|
4
|
+
* A profile tells {@link classifyScreen} how to find and bound a given app's
|
|
5
|
+
* composer. Profiles are per-app *data* by design (spike constraint 9): a TUI
|
|
6
|
+
* layout change is a profile drift the smoke suite catches, never a silent
|
|
7
|
+
* misdelivery — an unmatched marker classifies NOT clean.
|
|
8
|
+
*
|
|
9
|
+
* Measured apps have a profile: claude, codex (spike g2), and agy (Spec 1313
|
|
10
|
+
* Phase 3 measurement — its own marker `> ` and a color-keyed placeholder rule,
|
|
11
|
+
* because agy renders its idle hint in palette-8 gray, not SGR-dim). Everything
|
|
12
|
+
* else — gemini, opencode, an unknown binary, or a launch we can't identify —
|
|
13
|
+
* resolves to `null`, and the caller holds the message with reason `no-profile`.
|
|
14
|
+
* This is the strict app-identity table the spike mandates (constraint 10): we
|
|
15
|
+
* deliberately do NOT reuse `resolveHarness`, whose claude fallback would make an
|
|
16
|
+
* agy terminal masquerade as claude and receive claude's (wrong) profile — a
|
|
17
|
+
* correctness bug, since agy's screens classify by an entirely different rule.
|
|
18
|
+
*/
|
|
19
|
+
import { basename } from 'node:path';
|
|
20
|
+
import { detectHarnessFromCommand } from '../utils/harness.js';
|
|
21
|
+
/**
|
|
22
|
+
* Marker family shared by the measured TUIs: both render a `❯`/`›` prompt glyph
|
|
23
|
+
* at the start of the composer input row. Kept in one place but referenced
|
|
24
|
+
* per-profile so a future app whose marker diverges gets its own pattern without
|
|
25
|
+
* disturbing the others.
|
|
26
|
+
*/
|
|
27
|
+
const COMPOSER_MARKER = /^[❯›]/;
|
|
28
|
+
/**
|
|
29
|
+
* Lines that END the composer region — the rule line claude draws beneath its
|
|
30
|
+
* input (`─────`) and the status line codex draws (model / reasoning / cwd, e.g.
|
|
31
|
+
* ` gpt-5.6-sol high: … ~/repo`). Scanning stops at the first such line so
|
|
32
|
+
* status chrome below the composer is never miscounted as user text. Both
|
|
33
|
+
* patterns are carried by both profiles (harmless: a claude screen has no
|
|
34
|
+
* `gpt|high:|~/` status line, a codex screen has no long rule line under input),
|
|
35
|
+
* exactly as the validated spike classifier applied them.
|
|
36
|
+
*
|
|
37
|
+
* Load-bearing since the Spec 1313 render-gate hardening: when NONE of these matches
|
|
38
|
+
* below the marker, the gate now HOLDS (`no-region-end`) rather than scanning to the
|
|
39
|
+
* screen bottom — so this list is the sole lower-bound signal, and it is FAIL-SAFE but
|
|
40
|
+
* DRIFT-FRAGILE. The rule pattern requires the line to *start* with `─/━/╌/┄`; a claude
|
|
41
|
+
* reversion to a rounded box (`╰────╯`, note `╰`/`└` are ignorable glyphs but NOT in
|
|
42
|
+
* this class) or an indented rule would stop matching and hold every send to that app.
|
|
43
|
+
* That is the safe direction (never a false-clean), and a sustained hold now escalates
|
|
44
|
+
* to liveness telemetry (mailbox-delivery `recordStreak`), but broaden this list ONLY
|
|
45
|
+
* from a real capture — a too-loose pattern that matches draft content is a false-clean.
|
|
46
|
+
*/
|
|
47
|
+
const REGION_END_PATTERNS = [/^[─━╌┄]{5,}/, /^\s{2,}(gpt|high:|~\/)/];
|
|
48
|
+
/** claude composer profile (marker ❯, dim placeholder — measured, spike g2). */
|
|
49
|
+
export const CLAUDE_PROFILE = {
|
|
50
|
+
app: 'claude',
|
|
51
|
+
markerPattern: COMPOSER_MARKER,
|
|
52
|
+
regionEndPatterns: REGION_END_PATTERNS,
|
|
53
|
+
};
|
|
54
|
+
/** codex composer profile (marker ›, dim placeholder — measured, spike g2). */
|
|
55
|
+
export const CODEX_PROFILE = {
|
|
56
|
+
app: 'codex',
|
|
57
|
+
markerPattern: COMPOSER_MARKER,
|
|
58
|
+
regionEndPatterns: REGION_END_PATTERNS,
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* agy (Antigravity CLI 1.1.8) composer marker: a `> ` prompt glyph at the input
|
|
62
|
+
* row start — a different glyph from claude/codex's `❯`/`›`, so its own pattern.
|
|
63
|
+
* (Measured, Spec 1313 Phase 3; the marker cell renders palette-12 bright-blue.)
|
|
64
|
+
*/
|
|
65
|
+
const AGY_MARKER = /^> /;
|
|
66
|
+
/**
|
|
67
|
+
* agy composer profile (Spec 1313 Phase 3 — net-new measurement). agy breaks the
|
|
68
|
+
* dim-placeholder assumption: its idle mode-hint (`Accept-edits mode: …`) renders
|
|
69
|
+
* at NORMAL intensity but in **palette-8 (gray)**, while user-typed text is
|
|
70
|
+
* default-fg — so the placeholder signal is a foreground COLOR, not SGR-dim
|
|
71
|
+
* (`placeholderFgPalette: 8`). Consequences, all measured: idle → clean (the
|
|
72
|
+
* gray hint is ignored), draft → busy (default-fg text counted), and the
|
|
73
|
+
* per-folder trust dialog → busy (its selected `> Yes, I trust this folder`
|
|
74
|
+
* option is palette-12, counted) — so a blind Enter never confirms filesystem
|
|
75
|
+
* trust. Region bounds reuse the shared rule-line/status patterns (agy brackets
|
|
76
|
+
* its composer with `─────` rules, like claude).
|
|
77
|
+
*/
|
|
78
|
+
export const AGY_PROFILE = {
|
|
79
|
+
app: 'agy',
|
|
80
|
+
markerPattern: AGY_MARKER,
|
|
81
|
+
regionEndPatterns: REGION_END_PATTERNS,
|
|
82
|
+
placeholderFgPalette: 8,
|
|
83
|
+
};
|
|
84
|
+
/** Registry keyed by the harness name `detectHarnessFromCommand` returns. */
|
|
85
|
+
const PROFILES_BY_HARNESS = {
|
|
86
|
+
claude: CLAUDE_PROFILE,
|
|
87
|
+
codex: CODEX_PROFILE,
|
|
88
|
+
};
|
|
89
|
+
/**
|
|
90
|
+
* Map a session's identity to its classifier profile, or `null` when the app is
|
|
91
|
+
* unknown/unmeasured (→ caller holds with `no-profile`).
|
|
92
|
+
*
|
|
93
|
+
* Resolution is strict: the launch `command`'s basename must match a measured
|
|
94
|
+
* agent. agy is matched directly (its binary is `agy`/`antigravity`), because the
|
|
95
|
+
* shared {@link detectHarnessFromCommand} does not recognize it and we will not
|
|
96
|
+
* extend that resolver — its claude fallback is exactly the misidentification the
|
|
97
|
+
* gate must avoid (constraint 10). claude/codex resolve via that helper. Wrapped
|
|
98
|
+
* launches — a builder run through `.builder-start.sh` whose `command` is the
|
|
99
|
+
* shell, not the agent — resolve to `null` here; the delivery wiring (Phase 4)
|
|
100
|
+
* supplies the resolved agent command for those (it already reads the launch
|
|
101
|
+
* script to identify the harness, as `afx reset` does). Fail-safe by
|
|
102
|
+
* construction: an unresolved identity is held and surfaced, never guessed.
|
|
103
|
+
*/
|
|
104
|
+
export function resolveProfile(identity) {
|
|
105
|
+
const base = basename(identity.command).toLowerCase();
|
|
106
|
+
if (base.includes('agy') || base.includes('antigravity'))
|
|
107
|
+
return AGY_PROFILE;
|
|
108
|
+
const harness = detectHarnessFromCommand(identity.command);
|
|
109
|
+
if (harness && harness in PROFILES_BY_HARNESS)
|
|
110
|
+
return PROFILES_BY_HARNESS[harness];
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=gate-profiles.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gate-profiles.js","sourceRoot":"","sources":["../../../src/agent-farm/servers/gate-profiles.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AACrC,OAAO,EAAE,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AAG/D;;;;;GAKG;AACH,MAAM,eAAe,GAAG,OAAO,CAAC;AAEhC;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,mBAAmB,GAAG,CAAC,aAAa,EAAE,wBAAwB,CAAC,CAAC;AAEtE,gFAAgF;AAChF,MAAM,CAAC,MAAM,cAAc,GAAgB;IACzC,GAAG,EAAE,QAAQ;IACb,aAAa,EAAE,eAAe;IAC9B,iBAAiB,EAAE,mBAAmB;CACvC,CAAC;AAEF,+EAA+E;AAC/E,MAAM,CAAC,MAAM,aAAa,GAAgB;IACxC,GAAG,EAAE,OAAO;IACZ,aAAa,EAAE,eAAe;IAC9B,iBAAiB,EAAE,mBAAmB;CACvC,CAAC;AAEF;;;;GAIG;AACH,MAAM,UAAU,GAAG,KAAK,CAAC;AAEzB;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,WAAW,GAAgB;IACtC,GAAG,EAAE,KAAK;IACV,aAAa,EAAE,UAAU;IACzB,iBAAiB,EAAE,mBAAmB;IACtC,oBAAoB,EAAE,CAAC;CACxB,CAAC;AAEF,6EAA6E;AAC7E,MAAM,mBAAmB,GAAgC;IACvD,MAAM,EAAE,cAAc;IACtB,KAAK,EAAE,aAAa;CACrB,CAAC;AAiBF;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,cAAc,CAAC,QAAqB;IAClD,MAAM,IAAI,GAAG,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,WAAW,EAAE,CAAC;IACtD,IAAI,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC;QAAE,OAAO,WAAW,CAAC;IAC7E,MAAM,OAAO,GAAG,wBAAwB,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IAC3D,IAAI,OAAO,IAAI,OAAO,IAAI,mBAAmB;QAAE,OAAO,mBAAmB,CAAC,OAAO,CAAC,CAAC;IACnF,OAAO,IAAI,CAAC;AACd,CAAC"}
|