@cohortapp/agent-sdk 2.11.15 → 2.13.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/.env.example +37 -22
- package/README.md +2 -0
- package/bin/maestro.mjs +117 -39
- package/bin/maestro.test.mjs +175 -5
- package/docs/guides/front-door-session.md +313 -0
- package/docs/guides/mac-mini.md +100 -28
- package/docs/guides/org-onboarding.md +1 -1
- package/docs/guides/setup-wizard.md +9 -5
- package/docs/runbooks/cohort-cutover.md +11 -1
- package/docs/runbooks/mac-mini-bootstrap.md +38 -63
- package/lib/cadence-bus-requeue.test.mjs +83 -0
- package/lib/cadence-bus.mjs +43 -7
- package/lib/channels/inbox-item.mjs +59 -2
- package/lib/cli/board.mjs +285 -0
- package/lib/cli/board.test.mjs +227 -0
- package/lib/cli/design.mjs +185 -0
- package/lib/cli/design.test.mjs +270 -0
- package/lib/cli/doctor-checks.mjs +441 -0
- package/lib/cli/doctor-checks.test.mjs +336 -0
- package/lib/cli/global-setup-extras.mjs +454 -0
- package/lib/cli/global-setup-extras.test.mjs +462 -0
- package/lib/cli/inbox.mjs +304 -0
- package/lib/cli/inbox.test.mjs +230 -0
- package/lib/cli/session-ack.mjs +63 -0
- package/lib/cli/session-ack.test.mjs +63 -0
- package/lib/cli/session.mjs +760 -0
- package/lib/cli/session.test.mjs +613 -0
- package/lib/collective/global-config.mjs +209 -6
- package/lib/collective/global-config.test.mjs +145 -0
- package/lib/collective/global-skills.mjs +145 -0
- package/lib/collective/global-skills.test.mjs +126 -0
- package/lib/collective/presence.mjs +4 -3
- package/lib/collective/vendor-skills.mjs +305 -0
- package/lib/collective/vendor-skills.test.mjs +306 -0
- package/lib/comms/send-gate.mjs +115 -0
- package/lib/comms/send-gate.test.mjs +113 -0
- package/lib/design/design-md.mjs +793 -0
- package/lib/design/design-md.test.mjs +318 -0
- package/lib/design/fixtures/DESIGN.golden.md +238 -0
- package/lib/design/fixtures/PRODUCT.golden.md +67 -0
- package/lib/design/fixtures/foundation.json +133 -0
- package/lib/design/refresh-gate.mjs +154 -0
- package/lib/design/refresh-gate.test.mjs +144 -0
- package/lib/design/write.mjs +275 -0
- package/lib/design/write.test.mjs +241 -0
- package/lib/feature-init.mjs +2 -2
- package/lib/mcp/server.test.mjs +9 -4
- package/lib/model-router/spawn.test.mjs +21 -0
- package/lib/org/board-mine-cache.mjs +99 -0
- package/lib/org/board-mine-cache.test.mjs +53 -0
- package/lib/org/board.mjs +11 -0
- package/lib/org/board.test.mjs +11 -1
- package/lib/org/client.mjs +36 -0
- package/lib/org/client.test.mjs +46 -0
- package/lib/org/inbound/directedness.mjs +18 -2
- package/lib/org/inbound/directedness.test.mjs +58 -0
- package/lib/org/inbound/index.mjs +8 -1
- package/lib/org/inbound/index.test.mjs +22 -0
- package/lib/org/mesh-directives.test.mjs +110 -0
- package/lib/org/mesh.mjs +61 -1
- package/lib/org/protocol.checksum +1 -1
- package/lib/org/protocol.mjs +52 -0
- package/lib/org/protocol.test.mjs +12 -1
- package/lib/org/registry.mjs +3 -2
- package/lib/org/tool-surface.mjs +120 -0
- package/lib/org/tool-surface.test.mjs +118 -5
- package/lib/prompts/parallelism.mjs +79 -0
- package/lib/prompts/parallelism.test.mjs +177 -0
- package/lib/security/external-content.mjs +1 -1
- package/lib/security/external-content.test.mjs +17 -0
- package/lib/session/config.mjs +137 -0
- package/lib/session/config.test.mjs +92 -0
- package/lib/session/feed-core.mjs +229 -0
- package/lib/session/feed-core.test.mjs +198 -0
- package/lib/session/first-run.mjs +126 -0
- package/lib/session/first-run.test.mjs +121 -0
- package/lib/session/frontdoor.mjs +266 -0
- package/lib/session/frontdoor.test.mjs +205 -0
- package/lib/session/handoffs.mjs +295 -0
- package/lib/session/handoffs.test.mjs +183 -0
- package/lib/session/identity.mjs +220 -0
- package/lib/session/identity.test.mjs +180 -0
- package/lib/session/inbox-claims.mjs +434 -0
- package/lib/session/inbox-claims.test.mjs +286 -0
- package/lib/session/launch-args.mjs +161 -0
- package/lib/session/launch-args.test.mjs +157 -0
- package/lib/session/liveness.mjs +174 -0
- package/lib/session/liveness.test.mjs +100 -0
- package/lib/session/status-summary.mjs +172 -0
- package/lib/session/status-summary.test.mjs +118 -0
- package/lib/session-permissions.mjs +39 -3
- package/lib/session-permissions.test.mjs +20 -0
- package/lib/setup/claude-probe.mjs +161 -24
- package/lib/setup/claude-probe.test.mjs +187 -0
- package/lib/setup/sections/learning.mjs +2 -1
- package/lib/setup/sections/model.mjs +104 -24
- package/lib/setup/sections/model.test.mjs +240 -0
- package/lib/setup/sections/org.mjs +27 -2
- package/lib/setup/sections/org.test.mjs +35 -2
- package/lib/setup/sections/verify.mjs +5 -0
- package/lib/setup/state.mjs +30 -10
- package/lib/setup/state.test.mjs +24 -1
- package/lib/singleton.js +11 -3
- package/lib/singleton.test.mjs +16 -0
- package/lib/subagents/lock.mjs +1 -1
- package/lib/telemetry/collect.mjs +270 -6
- package/lib/telemetry/collect.test.mjs +196 -1
- package/lib/upgrade/global-refresh.mjs +108 -0
- package/lib/upgrade/global-refresh.test.mjs +65 -0
- package/lib/upgrade/launchd-reconcile.mjs +327 -0
- package/lib/upgrade/launchd-reconcile.test.mjs +272 -0
- package/lib/upgrade/post-steps.mjs +151 -0
- package/lib/upgrade/post-steps.test.mjs +200 -0
- package/lib/upgrade/verify.mjs +215 -0
- package/lib/upgrade/verify.test.mjs +164 -0
- package/lib/voice/outbound.mjs +3 -2
- package/lib/voice/post-call-brief.mjs +2 -1
- package/lib/voice/session-rotation.mjs +6 -1
- package/lib/voice/session-rotation.test.mjs +114 -0
- package/package.json +3 -3
- package/plugins/maestro-skills/plugin.json +25 -1
- package/plugins/maestro-skills/skills/board-work.md +63 -0
- package/plugins/maestro-skills/skills/cohort-design.md +153 -0
- package/plugins/maestro-skills/skills/inbound-triage.md +80 -0
- package/plugins/maestro-skills/skills/main-session.md +102 -0
- package/plugins/maestro-skills/skills/peer-sessions.md +65 -0
- package/plugins/maestro-skills/skills/persona-discipline.md +75 -0
- package/plugins/maestro-skills/vendor/emilkowalski/LICENSE +21 -0
- package/plugins/maestro-skills/vendor/emilkowalski/UPSTREAM.json +70 -0
- package/plugins/maestro-skills/vendor/emilkowalski/skills/animate/RECIPES.md +324 -0
- package/plugins/maestro-skills/vendor/emilkowalski/skills/animate/SKILL.md +199 -0
- package/plugins/maestro-skills/vendor/emilkowalski/skills/animation-vocabulary/SKILL.md +173 -0
- package/plugins/maestro-skills/vendor/emilkowalski/skills/apple-design/SKILL.md +282 -0
- package/plugins/maestro-skills/vendor/emilkowalski/skills/emil-design-eng/SKILL.md +674 -0
- package/plugins/maestro-skills/vendor/emilkowalski/skills/find-animation-opportunities/SKILL.md +132 -0
- package/plugins/maestro-skills/vendor/emilkowalski/skills/improve-animations/AUDIT.md +115 -0
- package/plugins/maestro-skills/vendor/emilkowalski/skills/improve-animations/PLAN-TEMPLATE.md +73 -0
- package/plugins/maestro-skills/vendor/emilkowalski/skills/improve-animations/SKILL.md +101 -0
- package/plugins/maestro-skills/vendor/emilkowalski/skills/prototype/PICKER.md +197 -0
- package/plugins/maestro-skills/vendor/emilkowalski/skills/prototype/SKILL.md +90 -0
- package/plugins/maestro-skills/vendor/emilkowalski/skills/review-animations/SKILL.md +112 -0
- package/plugins/maestro-skills/vendor/emilkowalski/skills/review-animations/STANDARDS.md +187 -0
- package/plugins/maestro-skills/vendor/impeccable/LICENSE +191 -0
- package/plugins/maestro-skills/vendor/impeccable/NOTICE.md +11 -0
- package/plugins/maestro-skills/vendor/impeccable/SKILL.md +86 -0
- package/plugins/maestro-skills/vendor/impeccable/UPSTREAM.json +201 -0
- package/plugins/maestro-skills/vendor/impeccable/agents/impeccable-asset-producer.md +42 -0
- package/plugins/maestro-skills/vendor/impeccable/agents/impeccable-documenter.md +29 -0
- package/plugins/maestro-skills/vendor/impeccable/agents/impeccable-finish-reviewer.md +43 -0
- package/plugins/maestro-skills/vendor/impeccable/agents/impeccable-manual-edit-applier.md +97 -0
- package/plugins/maestro-skills/vendor/impeccable/reference/adapt.md +312 -0
- package/plugins/maestro-skills/vendor/impeccable/reference/adapt.native.md +58 -0
- package/plugins/maestro-skills/vendor/impeccable/reference/android.md +46 -0
- package/plugins/maestro-skills/vendor/impeccable/reference/animate.md +89 -0
- package/plugins/maestro-skills/vendor/impeccable/reference/audit.md +136 -0
- package/plugins/maestro-skills/vendor/impeccable/reference/audit.native.md +139 -0
- package/plugins/maestro-skills/vendor/impeccable/reference/bolder.md +33 -0
- package/plugins/maestro-skills/vendor/impeccable/reference/clarify.md +94 -0
- package/plugins/maestro-skills/vendor/impeccable/reference/colorize.md +86 -0
- package/plugins/maestro-skills/vendor/impeccable/reference/craft-floor.md +44 -0
- package/plugins/maestro-skills/vendor/impeccable/reference/craft.md +5 -0
- package/plugins/maestro-skills/vendor/impeccable/reference/critique.md +806 -0
- package/plugins/maestro-skills/vendor/impeccable/reference/degraded/asset-producer.md +37 -0
- package/plugins/maestro-skills/vendor/impeccable/reference/degraded/documenter.md +24 -0
- package/plugins/maestro-skills/vendor/impeccable/reference/degraded/finish-reviewer.md +38 -0
- package/plugins/maestro-skills/vendor/impeccable/reference/degraded/manual-edit-applier.md +92 -0
- package/plugins/maestro-skills/vendor/impeccable/reference/delight.md +70 -0
- package/plugins/maestro-skills/vendor/impeccable/reference/distill.md +111 -0
- package/plugins/maestro-skills/vendor/impeccable/reference/doctor.md +54 -0
- package/plugins/maestro-skills/vendor/impeccable/reference/document.md +416 -0
- package/plugins/maestro-skills/vendor/impeccable/reference/extract.md +69 -0
- package/plugins/maestro-skills/vendor/impeccable/reference/harden.md +336 -0
- package/plugins/maestro-skills/vendor/impeccable/reference/hooks.md +111 -0
- package/plugins/maestro-skills/vendor/impeccable/reference/init.md +131 -0
- package/plugins/maestro-skills/vendor/impeccable/reference/ios.md +51 -0
- package/plugins/maestro-skills/vendor/impeccable/reference/layout.md +84 -0
- package/plugins/maestro-skills/vendor/impeccable/reference/live-setup.md +104 -0
- package/plugins/maestro-skills/vendor/impeccable/reference/live.md +325 -0
- package/plugins/maestro-skills/vendor/impeccable/reference/new-work.md +147 -0
- package/plugins/maestro-skills/vendor/impeccable/reference/onboard.md +234 -0
- package/plugins/maestro-skills/vendor/impeccable/reference/operate.md +61 -0
- package/plugins/maestro-skills/vendor/impeccable/reference/optimize.md +258 -0
- package/plugins/maestro-skills/vendor/impeccable/reference/overdrive.md +127 -0
- package/plugins/maestro-skills/vendor/impeccable/reference/polish.md +105 -0
- package/plugins/maestro-skills/vendor/impeccable/reference/quieter.md +99 -0
- package/plugins/maestro-skills/vendor/impeccable/reference/routing.md +24 -0
- package/plugins/maestro-skills/vendor/impeccable/reference/shape.md +59 -0
- package/plugins/maestro-skills/vendor/impeccable/reference/typeset.md +80 -0
- package/plugins/maestro-skills/vendor/impeccable/reference/visualize.md +46 -0
- package/plugins/maestro-skills/vendor/taste-skill/LICENSE +21 -0
- package/plugins/maestro-skills/vendor/taste-skill/UPSTREAM.json +37 -0
- package/plugins/maestro-skills/vendor/taste-skill/skills/minimalist-skill/SKILL.md +85 -0
- package/plugins/maestro-skills/vendor/taste-skill/skills/redesign-skill/SKILL.md +178 -0
- package/plugins/maestro-skills/vendor/taste-skill/skills/soft-skill/SKILL.md +98 -0
- package/plugins/maestro-skills/vendor/taste-skill/skills/taste-skill/SKILL.md +1206 -0
- package/plugins/maestro-skills/vendor/unlazy/LICENSE +21 -0
- package/plugins/maestro-skills/vendor/unlazy/SECURITY.md +72 -0
- package/plugins/maestro-skills/vendor/unlazy/SKILL.md +104 -0
- package/plugins/maestro-skills/vendor/unlazy/UPSTREAM.json +94 -0
- package/plugins/maestro-skills/vendor/unlazy/references/dispatch.md +82 -0
- package/plugins/maestro-skills/vendor/unlazy/references/gates.md +149 -0
- package/plugins/maestro-skills/vendor/unlazy/references/method.md +49 -0
- package/plugins/maestro-skills/vendor/unlazy/references/orchestration.md +107 -0
- package/plugins/maestro-skills/vendor/unlazy/references/parallel.md +133 -0
- package/plugins/maestro-skills/vendor/unlazy/references/token-economy.md +48 -0
- package/plugins/maestro-skills/vendor/unlazy/scripts/dispatch-check.mjs +139 -0
- package/plugins/maestro-skills/vendor/unlazy/scripts/gate-check.mjs +960 -0
- package/plugins/maestro-skills/vendor/unlazy/scripts/gate-lint.mjs +245 -0
- package/plugins/maestro-skills/vendor/unlazy/scripts/lib/check-supervisor.mjs +46 -0
- package/plugins/maestro-skills/vendor/unlazy/scripts/lib/dispatch.mjs +293 -0
- package/plugins/maestro-skills/vendor/unlazy/scripts/lib/gates.mjs +953 -0
- package/plugins/maestro-skills/vendor/unlazy/scripts/lib/process-tree.mjs +161 -0
- package/plugins/maestro-skills/vendor/unlazy/scripts/lib/regex-worker.mjs +9 -0
- package/plugins/maestro-skills/vendor/unlazy/templates/PLAN.md +116 -0
- package/plugins/maestro-skills/vendor/unlazy/templates/gates-leaf.md +51 -0
- package/plugins/maestro-skills/vendor/unlazy/templates/gates-node.md +51 -0
- package/scaffold/CLAUDE.md +24 -0
- package/scripts/ci/check-durable-write-seam.mjs +147 -0
- package/scripts/ci/check-durable-write-seam.test.mjs +90 -0
- package/scripts/ci/check-skill-packs.mjs +388 -0
- package/scripts/ci/check-skill-packs.test.mjs +495 -0
- package/scripts/ci/check.mjs +6 -0
- package/scripts/collective/hook-runner.mjs +39 -4
- package/scripts/collective/hook-runner.test.mjs +85 -2
- package/scripts/daemon/agent-daemon-board-mine.test.mjs +96 -0
- package/scripts/daemon/agent-daemon-design.test.mjs +238 -0
- package/scripts/daemon/agent-daemon-frontdoor.test.mjs +60 -0
- package/scripts/daemon/agent-daemon.mjs +249 -10
- package/scripts/daemon/agent-daemon.test.mjs +73 -0
- package/scripts/daemon/assurance-e2e.test.mjs +141 -6
- package/scripts/daemon/assurance.mjs +461 -37
- package/scripts/daemon/assurance.test.mjs +408 -43
- package/scripts/daemon/cadence-consumer-frontdoor.test.mjs +393 -0
- package/scripts/daemon/cadence-consumer.mjs +289 -89
- package/scripts/daemon/cadence-handlers.mjs +53 -0
- package/scripts/daemon/classifier.mjs +1 -1
- package/scripts/daemon/dispatcher-resume.test.mjs +166 -0
- package/scripts/daemon/dispatcher.mjs +127 -19
- package/scripts/daemon/health.mjs +12 -1
- package/scripts/daemon/inbox-deferral-session.test.mjs +49 -0
- package/scripts/daemon/inbox-deferral.mjs +6 -0
- package/scripts/daemon/lib/self-echo.mjs +201 -0
- package/scripts/daemon/lib/self-echo.test.mjs +153 -0
- package/scripts/daemon/maestro-daemon.mjs +3 -0
- package/scripts/daemon/prompt-builder.mjs +19 -3
- package/scripts/daemon/responder.mjs +51 -40
- package/scripts/daemon/sdk-version.mjs +51 -0
- package/scripts/daemon/sdk-version.test.mjs +31 -0
- package/scripts/hooks/pre-send-audit.sh +97 -4
- package/scripts/hooks/pre-send-audit.test.mjs +140 -1
- package/scripts/local-triggers/autoupdate.sh +243 -19
- package/scripts/local-triggers/autoupdate.test.mjs +518 -0
- package/scripts/local-triggers/generate-plists.sh +24 -1
- package/scripts/local-triggers/generate-plists.test.mjs +49 -11
- package/scripts/org/send-orgmail.first-contact.test.mjs +102 -0
- package/scripts/org/send-orgmail.mjs +27 -3
- package/scripts/poller/inbox-privilege-injection.test.mjs +167 -0
- package/scripts/poller/slack-poller.mjs +13 -1
- package/scripts/poller/utils.mjs +46 -1
- package/scripts/poller-launchd/install.sh +19 -11
- package/scripts/poller-launchd/install.test.mjs +243 -0
- package/scripts/poller-launchd/launchd-poller-wrapper.sh +92 -0
- package/scripts/poller-launchd/migrate.sh +66 -0
- package/scripts/poller-launchd/poller.plist.template +4 -2
- package/scripts/session/feed.mjs +237 -0
- package/scripts/session/feed.test.mjs +196 -0
- package/scripts/session/supervisor-sh.test.mjs +218 -0
- package/scripts/session/supervisor.mjs +328 -0
- package/scripts/session/supervisor.sh +141 -0
- package/scripts/session/supervisor.test.mjs +482 -0
- package/scripts/setup/configure-macos.sh +250 -55
- package/scripts/setup/configure-macos.test.mjs +306 -0
- package/scripts/setup/init-agent.sh +112 -7
- package/scripts/setup/init-agent.test.mjs +220 -1
- package/scripts/vendor/skill-packs.mjs +354 -0
- package/scripts/vendor/sync-skill-packs.mjs +242 -0
- package/scripts/vendor/sync-skill-packs.test.mjs +103 -0
- package/scripts/watchdog/memory-watchdog.sh +37 -1
- package/scripts/watchdog/memory-watchdog.test.mjs +64 -0
- package/scripts/setup/boot-claude-session.sh +0 -94
|
@@ -47,6 +47,17 @@ const AGENT_DIR = mkdtempSync(join(tmpdir(), "maestro-assurance-e2e-"));
|
|
|
47
47
|
process.env.AGENT_DIR = AGENT_DIR;
|
|
48
48
|
process.env.AGENT_ROOT = AGENT_DIR;
|
|
49
49
|
process.env.MAESTRO_DAEMON_NO_AUTOSTART = "1";
|
|
50
|
+
// HERMETICITY. `receipts.recordOutbound` attributes a send to an obligation and
|
|
51
|
+
// a session from AMBIENT ENV when the caller does not name them — the seam that
|
|
52
|
+
// lets a CLI send lane inside a spawned session attribute itself. Run this file
|
|
53
|
+
// from inside a live maestro session and those variables are inherited, so the
|
|
54
|
+
// receipt SCENARIO 2 writes to prove "the session answered" is stamped with a
|
|
55
|
+
// DIFFERENT debt's key and reads back as `attributed-elsewhere`. The scenario
|
|
56
|
+
// then fails on a real machine and passes on a bare CI runner, which is the
|
|
57
|
+
// wrong way round for a test to be environment-sensitive.
|
|
58
|
+
delete process.env.MAESTRO_OBLIGATION_KEY;
|
|
59
|
+
delete process.env.MAESTRO_SESSION_ID;
|
|
60
|
+
delete process.env.AGENT_SESSION_ID;
|
|
50
61
|
|
|
51
62
|
let daemon, assurance, receipts;
|
|
52
63
|
|
|
@@ -103,10 +114,16 @@ function transport(virtualClock = () => null) {
|
|
|
103
114
|
};
|
|
104
115
|
}
|
|
105
116
|
|
|
106
|
-
/** The daemon's ack seam, shaped exactly like responder.sendHoldingMessage
|
|
107
|
-
|
|
117
|
+
/** The daemon's ack seam, shaped exactly like responder.sendHoldingMessage:
|
|
118
|
+
* the line is GENERATED (here through the REAL generateAck with an injected
|
|
119
|
+
* runner — no spawn in tests), and a failed generation means silence, never
|
|
120
|
+
* a canned fallback. */
|
|
121
|
+
function ackSeam(runImpl) {
|
|
108
122
|
return async (item, classResult) => {
|
|
109
|
-
const text = assurance.
|
|
123
|
+
const text = await assurance.generateAck(item, classResult, {
|
|
124
|
+
runImpl: runImpl || (async () => "Taking the git fixes now — push coming shortly."),
|
|
125
|
+
});
|
|
126
|
+
if (text == null) return { sent: false, holdingText: null, error: "ack_generation_failed", genFailed: true };
|
|
110
127
|
heard("ack", text);
|
|
111
128
|
return { sent: true, holdingText: text, via: "fake" };
|
|
112
129
|
};
|
|
@@ -136,6 +153,24 @@ function makeItem(id, content) {
|
|
|
136
153
|
return item;
|
|
137
154
|
}
|
|
138
155
|
|
|
156
|
+
/**
|
|
157
|
+
* Every scenario injects both seams that would otherwise spawn: `dispatch` (no
|
|
158
|
+
* child process) and `sendHoldingMessage` (the ack line comes from an injected
|
|
159
|
+
* runner). No scenario in this file ever needs the real `claude` binary.
|
|
160
|
+
*
|
|
161
|
+
* The daemon's ack path is nevertheless gated on `claudeAvailable()` — a
|
|
162
|
+
* NECESSARY production fail-safe (never post "on it" when the CLI that would
|
|
163
|
+
* answer cannot start). Left to the real probe, that gate reads a fact about
|
|
164
|
+
* the MACHINE, not about the scenario: these tests pass on a developer box with
|
|
165
|
+
* the CLI installed and suppress every acknowledgement on a CI runner without
|
|
166
|
+
* it. That is precisely how SCENARIOS 2, 3 and 6 came to be red on main and
|
|
167
|
+
* green everywhere they were written. The premise is declared here instead.
|
|
168
|
+
*
|
|
169
|
+
* The gate's false branch is not untested by this — see "the ack is suppressed
|
|
170
|
+
* when the CLI is unavailable" at the end of this file.
|
|
171
|
+
*/
|
|
172
|
+
const CLI_PRESENT = () => true;
|
|
173
|
+
|
|
139
174
|
const COMPLEX = {
|
|
140
175
|
priority: "critical", action: "respond", model: "opus", category: "action_required",
|
|
141
176
|
directed_at_agent: true, summary: "Fix the issues you just noted and push the fixes to git",
|
|
@@ -152,6 +187,7 @@ test("SCENARIO 1 — a fast ask gets a direct answer, with no pointless holding
|
|
|
152
187
|
|
|
153
188
|
let holdingCalls = 0;
|
|
154
189
|
await daemon.processItem(item, "cohort", {
|
|
190
|
+
claudeAvailable: CLI_PRESENT,
|
|
155
191
|
classify: async () => ({ priority: "normal", action: "respond", model: "sonnet", category: "question", directed_at_agent: true, summary: "board call time" }),
|
|
156
192
|
isQuickReply: () => true,
|
|
157
193
|
sendQuickResponse: async (it) => {
|
|
@@ -181,6 +217,7 @@ test("SCENARIO 2 — a slow ask is acknowledged at once, updated while it runs,
|
|
|
181
217
|
|
|
182
218
|
let onCloseHook = null;
|
|
183
219
|
await daemon.processItem(item, "cohort", {
|
|
220
|
+
claudeAvailable: CLI_PRESENT,
|
|
184
221
|
classify: async () => COMPLEX,
|
|
185
222
|
isQuickReply: () => false,
|
|
186
223
|
sendHoldingMessage: ackSeam(),
|
|
@@ -189,9 +226,10 @@ test("SCENARIO 2 — a slow ask is acknowledged at once, updated while it runs,
|
|
|
189
226
|
|
|
190
227
|
const ack = transcript.find((m) => m.kind === "ack");
|
|
191
228
|
assert.ok(ack, "the acknowledgement must go out");
|
|
192
|
-
assert.ok(ack.ms < 1000, `acknowledged in ${ack.ms}ms
|
|
193
|
-
assert.match(ack.text, /
|
|
194
|
-
assert.
|
|
229
|
+
assert.ok(ack.ms < 1000, `acknowledged in ${ack.ms}ms`);
|
|
230
|
+
assert.match(ack.text, /git fixes/i, "the ack is bespoke to the actual ask, not a rotation pick");
|
|
231
|
+
assert.doesNotMatch(ack.text, /^(on it|looking now)[.!—]?\s*$/i, "never the retired canned lines");
|
|
232
|
+
assert.ok(ack.text.length <= 120, "the ack is one short human line, not a templated paragraph");
|
|
195
233
|
|
|
196
234
|
const key = assurance.openObligations()[0].key;
|
|
197
235
|
note("obligation opened and acknowledged; session running");
|
|
@@ -230,6 +268,7 @@ test("SCENARIO 3 — a timing-out session tells the human what happened and that
|
|
|
230
268
|
|
|
231
269
|
let onCloseHook = null;
|
|
232
270
|
await daemon.processItem(item, "cohort", {
|
|
271
|
+
claudeAvailable: CLI_PRESENT,
|
|
233
272
|
classify: async () => ({ ...COMPLEX, summary: "Reconcile July spend against forecast" }),
|
|
234
273
|
isQuickReply: () => false,
|
|
235
274
|
sendHoldingMessage: ackSeam(),
|
|
@@ -265,6 +304,7 @@ test("SCENARIO 4 — a crashing session tells the human it failed, stops, and es
|
|
|
265
304
|
|
|
266
305
|
let onCloseHook = null;
|
|
267
306
|
await daemon.processItem(item, "cohort", {
|
|
307
|
+
claudeAvailable: CLI_PRESENT,
|
|
268
308
|
classify: async () => ({ ...COMPLEX, summary: "Draft the Q3 investor update" }),
|
|
269
309
|
isQuickReply: () => false,
|
|
270
310
|
sendHoldingMessage: ackSeam(),
|
|
@@ -299,6 +339,7 @@ test("SCENARIO 5 — a session that exits 0 without replying no longer passes as
|
|
|
299
339
|
|
|
300
340
|
let onCloseHook = null;
|
|
301
341
|
await daemon.processItem(item, "cohort", {
|
|
342
|
+
claudeAvailable: CLI_PRESENT,
|
|
302
343
|
classify: async () => ({ ...COMPLEX, summary: "Check whether the migration landed cleanly" }),
|
|
303
344
|
isQuickReply: () => false,
|
|
304
345
|
sendHoldingMessage: ackSeam(),
|
|
@@ -333,6 +374,7 @@ test("SCENARIO 6 — a debt orphaned by a daemon restart is spoken to on the nex
|
|
|
333
374
|
const item = makeItem("ORPHAN-1", ask);
|
|
334
375
|
|
|
335
376
|
await daemon.processItem(item, "cohort", {
|
|
377
|
+
claudeAvailable: CLI_PRESENT,
|
|
336
378
|
classify: async () => ({ ...COMPLEX, summary: "Fix DNS on the staging domain" }),
|
|
337
379
|
isQuickReply: () => false,
|
|
338
380
|
sendHoldingMessage: ackSeam(),
|
|
@@ -357,6 +399,65 @@ test("SCENARIO 6 — a debt orphaned by a daemon restart is spoken to on the nex
|
|
|
357
399
|
note("previously: the typing dot stopped and that was the last the human ever heard", 30_000);
|
|
358
400
|
});
|
|
359
401
|
|
|
402
|
+
// ===========================================================================
|
|
403
|
+
// SCENARIO 6b — A PEER AGENT'S ACK ARRIVES AS INBOUND. The dispatch gate reads
|
|
404
|
+
// it, and NEITHER an ack goes out NOR a session spawns — the machinery half of
|
|
405
|
+
// the 25/27/30 Aug loop, closed at the gate.
|
|
406
|
+
// ===========================================================================
|
|
407
|
+
|
|
408
|
+
test("SCENARIO 6b — an ack-shaped one-liner from a peer agent seat spawns nothing and acks nothing", async () => {
|
|
409
|
+
const echoAck = "On it — taking a look now.";
|
|
410
|
+
beginScenario("SCENARIO 6b — PEER AGENT ACK (emitter-class inbound)", echoAck);
|
|
411
|
+
const item = {
|
|
412
|
+
id: "PEER-ACK-1",
|
|
413
|
+
raw_ref: "cohort:dm-a016-a039:PEER-ACK-1",
|
|
414
|
+
message_id: "PEER-ACK-1",
|
|
415
|
+
service: "cohort",
|
|
416
|
+
channel: "dm-a016-a039", // an agent↔agent DM — the structural seat signal
|
|
417
|
+
channel_id: "CDM-PEER-ACK-1",
|
|
418
|
+
is_dm: true,
|
|
419
|
+
sender: "Piotr Novak",
|
|
420
|
+
sender_id: "A039",
|
|
421
|
+
content: echoAck,
|
|
422
|
+
};
|
|
423
|
+
writeFileSync(join(AGENT_DIR, "state", "inbox", "cohort", "PEER-ACK-1.yaml"), "id: PEER-ACK-1\n");
|
|
424
|
+
|
|
425
|
+
let holdingCalls = 0;
|
|
426
|
+
let dispatched = 0;
|
|
427
|
+
await daemon.processItem(item, "cohort", {
|
|
428
|
+
claudeAvailable: CLI_PRESENT,
|
|
429
|
+
classify: async () => ({ ...COMPLEX, summary: "peer says it's on it" }),
|
|
430
|
+
isQuickReply: () => false,
|
|
431
|
+
sendHoldingMessage: async () => { holdingCalls++; return { sent: true, holdingText: "x" }; },
|
|
432
|
+
dispatch: () => { dispatched++; },
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
assert.equal(holdingCalls, 0, "no acknowledgement at machinery");
|
|
436
|
+
assert.equal(dispatched, 0, "and no session burned answering a reflex");
|
|
437
|
+
assert.equal(transcript.length, 0, "the peer seat hears nothing");
|
|
438
|
+
assert.ok(!assurance.openObligations().some((o) => o.key === item.raw_ref), "no debt opened for emitter-class inbound");
|
|
439
|
+
// The refusal is on the audit trail — same log family as the self-echo drops.
|
|
440
|
+
const { readFileSync } = await import("node:fs");
|
|
441
|
+
const drops = readFileSync(join(AGENT_DIR, "logs", "daemon", "dispatch-gate-drops.jsonl"), "utf-8")
|
|
442
|
+
.split("\n").filter(Boolean).map((l) => JSON.parse(l));
|
|
443
|
+
const drop = drops.find((d) => d.raw_ref === item.raw_ref);
|
|
444
|
+
assert.ok(drop, "the gate refusal is audited");
|
|
445
|
+
assert.equal(drop.reason, "peer-ack-shaped");
|
|
446
|
+
note("gate refusal audited to logs/daemon/dispatch-gate-drops.jsonl; item retired");
|
|
447
|
+
|
|
448
|
+
// The CONTRAST: the same shape from a human sender still gets the full path.
|
|
449
|
+
const humanItem = makeItem("HUMAN-TERSE-1", "On it — will do.");
|
|
450
|
+
let humanDispatched = 0;
|
|
451
|
+
await daemon.processItem(humanItem, "cohort", {
|
|
452
|
+
claudeAvailable: CLI_PRESENT,
|
|
453
|
+
classify: async () => COMPLEX,
|
|
454
|
+
isQuickReply: () => false,
|
|
455
|
+
sendHoldingMessage: ackSeam(async () => "Got it — I'll pick this up now."),
|
|
456
|
+
dispatch: () => { humanDispatched++; },
|
|
457
|
+
});
|
|
458
|
+
assert.equal(humanDispatched, 1, "a human's terse line is still worked on");
|
|
459
|
+
});
|
|
460
|
+
|
|
360
461
|
// ===========================================================================
|
|
361
462
|
// SCENARIO 7 — TWO ASKS, ONE ROOM. The owner sends a second thing before the
|
|
362
463
|
// first is done. One session answers; the other exits 0 in silence.
|
|
@@ -420,3 +521,37 @@ test("SCENARIO 7 — a second ask in the same DM is not discharged by the first
|
|
|
420
521
|
assert.match(rescue.text, /race between the poller's lock release/, "and B's actual work reaches the human");
|
|
421
522
|
note("previously: B closed as `answered` on A's receipt — zero messages, ask gone");
|
|
422
523
|
});
|
|
524
|
+
|
|
525
|
+
// ===========================================================================
|
|
526
|
+
// THE GATE'S OTHER SIDE — the fail-safe every scenario above suppresses.
|
|
527
|
+
// ===========================================================================
|
|
528
|
+
|
|
529
|
+
/**
|
|
530
|
+
* Mutation proof for `CLI_PRESENT`. Every scenario above declares the CLI
|
|
531
|
+
* present so that the ack path is exercised rather than skipped; this asserts
|
|
532
|
+
* the branch they are stepping over still behaves. Flip `CLI_PRESENT` to
|
|
533
|
+
* `() => false` and the scenarios go red; delete the `claudeAvailable` seam
|
|
534
|
+
* from THIS test and it goes red on any box with the CLI installed.
|
|
535
|
+
*/
|
|
536
|
+
test("the ack is suppressed when the CLI is unavailable — but the debt is still opened", async () => {
|
|
537
|
+
const ask = "rebuild the index and tell me when it's done";
|
|
538
|
+
beginScenario("CLI UNAVAILABLE — silence with a tracked debt, never a promise", ask);
|
|
539
|
+
const item = makeItem("NO-CLI-1", ask);
|
|
540
|
+
|
|
541
|
+
let dispatched = 0;
|
|
542
|
+
await daemon.processItem(item, "cohort", {
|
|
543
|
+
claudeAvailable: () => false,
|
|
544
|
+
classify: async () => COMPLEX,
|
|
545
|
+
isQuickReply: () => false,
|
|
546
|
+
sendHoldingMessage: ackSeam(),
|
|
547
|
+
dispatch: () => { dispatched++; },
|
|
548
|
+
});
|
|
549
|
+
|
|
550
|
+
assert.equal(transcript.filter((m) => m.kind === "ack").length, 0,
|
|
551
|
+
"never promise a reply the CLI that would write it cannot start");
|
|
552
|
+
const rec = assurance.openObligations().find((o) => o.key === item.raw_ref);
|
|
553
|
+
assert.ok(rec, "the debt is opened anyway — silence is compensated by the sweep, not forgotten");
|
|
554
|
+
assert.equal(rec.state, "open");
|
|
555
|
+
assert.equal(dispatched, 1, "the work is still attempted; only the promise is withheld");
|
|
556
|
+
note("no ack, obligation open: the assurance sweep owns the compensation");
|
|
557
|
+
});
|