@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
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/upgrade/verify.mjs — `maestro upgrade --verify` (design 2026-09-08,
|
|
3
|
+
* WP-M6 step 5): is this seat wholly on ONE SDK version, with the launchd job
|
|
4
|
+
* set and the global Claude Code config that version expects?
|
|
5
|
+
*
|
|
6
|
+
* The rows:
|
|
7
|
+
* · global bin version — `npm ls -g @cohortapp/agent-sdk`
|
|
8
|
+
* · local package — <agent>/node_modules/@cohortapp/agent-sdk/package.json
|
|
9
|
+
* · shipped manifest — .maestro/shipped-manifest.json `sdkVersion`
|
|
10
|
+
* · running daemon — state/dashboards/daemon-health.yaml `sdk_version`
|
|
11
|
+
* · plists — every generated label installed AND loaded
|
|
12
|
+
* · global-setup — identity block, collective block, cohort MCP entry,
|
|
13
|
+
* session settings, machine pointer, global skills
|
|
14
|
+
*
|
|
15
|
+
* `buildVerifyReport` is PURE over collected values; `collectVerifyInputs` is
|
|
16
|
+
* the edge (files + `npm ls -g` + `launchctl list`, all fail-open);
|
|
17
|
+
* `runVerify` glues them. A `fail` row is a mismatch → the CLI exits 1.
|
|
18
|
+
* `doctor` prints the same rows.
|
|
19
|
+
*
|
|
20
|
+
* @module lib/upgrade/verify
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
"use strict";
|
|
24
|
+
|
|
25
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
26
|
+
import { join, resolve } from "node:path";
|
|
27
|
+
import { homedir } from "node:os";
|
|
28
|
+
import { createRequire } from "node:module";
|
|
29
|
+
import { readGlobalVersion, SKIP_ENV, SDK_PACKAGE } from "./global-refresh.mjs";
|
|
30
|
+
import { readLaunchdState, defaultExec } from "./launchd-reconcile.mjs";
|
|
31
|
+
import { IDENTITY_BEGIN, CLAUDE_MD_BEGIN, mergeSessionSettings } from "../collective/global-config.mjs";
|
|
32
|
+
import { GLOBAL_SKILL_PREFIX } from "../collective/global-skills.mjs";
|
|
33
|
+
|
|
34
|
+
/** The skill every seat must carry for the front-door session to work. */
|
|
35
|
+
export const REQUIRED_SKILL = "main-session";
|
|
36
|
+
|
|
37
|
+
// ---------------------------------------------------------------------------
|
|
38
|
+
// Pure
|
|
39
|
+
// ---------------------------------------------------------------------------
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Parse the two lines verify needs out of daemon-health.yaml. The dashboard
|
|
43
|
+
* is a flat `key: value` list at the top level (health.mjs).
|
|
44
|
+
* @param {string} yaml
|
|
45
|
+
* @returns {{version:string, updatedAt:string, pid:number|null}}
|
|
46
|
+
*/
|
|
47
|
+
export function parseDaemonHealth(yaml) {
|
|
48
|
+
const s = String(yaml || "");
|
|
49
|
+
const grab = (k) => { const m = s.match(new RegExp(`^${k}:\\s*(.+)$`, "m")); return m ? m[1].trim() : ""; };
|
|
50
|
+
const pid = Number(grab("pid"));
|
|
51
|
+
return { version: grab("sdk_version"), updatedAt: grab("updated"), pid: Number.isFinite(pid) && pid > 0 ? pid : null };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Build the report. Pure.
|
|
56
|
+
*
|
|
57
|
+
* @param {object} i
|
|
58
|
+
* @param {string} i.expected the SDK version running verify
|
|
59
|
+
* @param {string} [i.globalVersion] "" = not installed globally
|
|
60
|
+
* @param {boolean} [i.skipGlobal] MAESTRO_SKIP_GLOBAL_INSTALL=1 → warn, not fail
|
|
61
|
+
* @param {string} [i.localVersion] "" = no agent-dir dependency (framework checkout)
|
|
62
|
+
* @param {string} [i.manifestVersion] "" = pre-2.12 manifest or none
|
|
63
|
+
* @param {boolean} [i.manifestPresent]
|
|
64
|
+
* @param {{version:string, updatedAt:string}} [i.daemon]
|
|
65
|
+
* @param {boolean} [i.daemonRestartPending] true right after upgrade(): autoupdate restarts it next
|
|
66
|
+
* @param {{generated:string[], installed:string[], loaded:string[], launchctlOk?:boolean}} [i.plists]
|
|
67
|
+
* @param {{identityConfigured:boolean, identity:boolean, collective:boolean, mcp:boolean, settings:boolean, pointer:boolean, skills:boolean}} [i.markers]
|
|
68
|
+
* @returns {{ok:boolean, expected:string, rows:Array<{level:"ok"|"warn"|"fail", name:string, msg:string}>, mismatches:string[]}}
|
|
69
|
+
*/
|
|
70
|
+
export function buildVerifyReport(i = {}) {
|
|
71
|
+
const expected = String(i.expected || "").trim();
|
|
72
|
+
const rows = [];
|
|
73
|
+
const row = (level, name, msg) => rows.push({ level, name, msg });
|
|
74
|
+
|
|
75
|
+
// Global bin.
|
|
76
|
+
const g = String(i.globalVersion || "").trim();
|
|
77
|
+
if (g === expected && expected) row("ok", "global", `global ${SDK_PACKAGE} ${g} (maestro / cohort / cohort-mcp on PATH match the agent dir)`);
|
|
78
|
+
else if (i.skipGlobal) row("warn", "global", `global ${SDK_PACKAGE} ${g || "not installed"} vs ${expected} — ${SKIP_ENV}=1, refresh skipped on purpose`);
|
|
79
|
+
else if (!g) row("fail", "global", `${SDK_PACKAGE} is not installed globally — npm i -g ${SDK_PACKAGE}@${expected} --no-audit --no-fund`);
|
|
80
|
+
else row("fail", "global", `global ${SDK_PACKAGE} is ${g}, agent dir is ${expected} — npm i -g ${SDK_PACKAGE}@${expected} --no-audit --no-fund`);
|
|
81
|
+
|
|
82
|
+
// Local package.
|
|
83
|
+
const l = String(i.localVersion || "").trim();
|
|
84
|
+
if (!l) row("warn", "local", `no ${SDK_PACKAGE} under node_modules (a framework checkout runs its own code)`);
|
|
85
|
+
else if (l === expected) row("ok", "local", `agent dir ${SDK_PACKAGE} ${l}`);
|
|
86
|
+
else row("fail", "local", `agent dir ${SDK_PACKAGE} is ${l} but this upgrade is ${expected} — npm install ${SDK_PACKAGE}@${expected} --save`);
|
|
87
|
+
|
|
88
|
+
// Shipped manifest.
|
|
89
|
+
const m = String(i.manifestVersion || "").trim();
|
|
90
|
+
if (!i.manifestPresent) row("warn", "manifest", ".maestro/shipped-manifest.json absent — no upgrade has completed here yet");
|
|
91
|
+
else if (!m) row("warn", "manifest", ".maestro/shipped-manifest.json carries no sdkVersion (written by a pre-2.12 upgrade); the next upgrade records it");
|
|
92
|
+
else if (m === expected) row("ok", "manifest", `shipped manifest ${m}`);
|
|
93
|
+
else row("fail", "manifest", `shipped manifest says ${m}, expected ${expected} — the framework files on disk are from another version; run maestro upgrade`);
|
|
94
|
+
|
|
95
|
+
// Running daemon.
|
|
96
|
+
const d = i.daemon || {};
|
|
97
|
+
const dv = String(d.version || "").trim();
|
|
98
|
+
if (!dv) row("warn", "daemon", "daemon has not reported sdk_version (state/dashboards/daemon-health.yaml) — older daemon or not running yet");
|
|
99
|
+
else if (dv === expected) row("ok", "daemon", `running daemon ${dv}${d.updatedAt ? ` (health written ${d.updatedAt})` : ""}`);
|
|
100
|
+
else if (i.daemonRestartPending) row("warn", "daemon", `running daemon is ${dv}; ${expected} takes effect when autoupdate kickstarts it`);
|
|
101
|
+
else row("fail", "daemon", `running daemon is ${dv}, expected ${expected} — launchctl kickstart -k gui/$(id -u)/ai.maestro.<first>-daemon`);
|
|
102
|
+
|
|
103
|
+
// Plists.
|
|
104
|
+
const p = i.plists || {};
|
|
105
|
+
const gen = (p.generated || []);
|
|
106
|
+
const inst = new Set(p.installed || []);
|
|
107
|
+
const loaded = new Set(p.loaded || []);
|
|
108
|
+
if (gen.length === 0) row("warn", "plists", "no generated plists (scripts/local-triggers/plists is empty) — run scripts/local-triggers/generate-plists.sh");
|
|
109
|
+
else {
|
|
110
|
+
const missing = gen.filter((x) => !inst.has(x));
|
|
111
|
+
const unloaded = gen.filter((x) => inst.has(x) && !loaded.has(x));
|
|
112
|
+
if (missing.length) row("fail", "plists", `${missing.length} generated plist(s) not installed: ${missing.join(", ")} — run maestro upgrade (it installs them)`);
|
|
113
|
+
if (unloaded.length) {
|
|
114
|
+
if (p.launchctlOk === false) row("warn", "plists", `launchctl list unavailable — could not confirm ${unloaded.length} installed job(s) are loaded`);
|
|
115
|
+
else row("fail", "plists", `${unloaded.length} installed plist(s) not loaded: ${unloaded.join(", ")} — launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/<label>.plist`);
|
|
116
|
+
}
|
|
117
|
+
if (!missing.length && !unloaded.length) row("ok", "plists", `${gen.length} generated plist(s) installed and loaded`);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Global-setup markers.
|
|
121
|
+
const k = i.markers || {};
|
|
122
|
+
if (k.identityConfigured === false) row("warn", "global-setup", "identity block skipped: config/agent.json has no name yet (maestro setup)");
|
|
123
|
+
else if (!k.identity) row("fail", "global-setup", "~/.claude/CLAUDE.md has no maestro:identity block — maestro global-setup");
|
|
124
|
+
const rest = [["collective", "~/.claude/CLAUDE.md maestro:collective block"], ["mcp", "~/.claude.json cohort MCP server for this agent dir"], ["settings", "~/.claude/settings.json front-door additions"], ["pointer", "~/.claude/maestro-agent.json pointer to this agent dir"], ["skills", `~/.claude/skills/${GLOBAL_SKILL_PREFIX}${REQUIRED_SKILL}/SKILL.md`]];
|
|
125
|
+
const missingMarkers = rest.filter(([key]) => !k[key]).map(([, label]) => label);
|
|
126
|
+
if (missingMarkers.length) row("fail", "global-setup", `missing: ${missingMarkers.join("; ")} — maestro global-setup`);
|
|
127
|
+
else if (k.identity || k.identityConfigured === false) row("ok", "global-setup", "identity, MCP, settings, pointer and skills present");
|
|
128
|
+
|
|
129
|
+
const mismatches = rows.filter((r) => r.level === "fail").map((r) => r.name);
|
|
130
|
+
return { ok: mismatches.length === 0, expected, rows, mismatches: [...new Set(mismatches)] };
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/** Render rows for a terminal. */
|
|
134
|
+
export function formatVerifyReport(report) {
|
|
135
|
+
const mark = { ok: "✓", warn: "⚠", fail: "✗" };
|
|
136
|
+
const lines = report.rows.map((r) => `${mark[r.level] || "·"} ${r.msg}`);
|
|
137
|
+
lines.push(report.ok ? `verify: seat is on ${report.expected}` : `verify: MISMATCH (${report.mismatches.join(", ")})`);
|
|
138
|
+
return lines;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// ---------------------------------------------------------------------------
|
|
142
|
+
// Edge
|
|
143
|
+
// ---------------------------------------------------------------------------
|
|
144
|
+
|
|
145
|
+
function safeRead(p) { try { return readFileSync(p, "utf8"); } catch { return ""; /* absent → "" */ } }
|
|
146
|
+
function safeJson(p) { try { return JSON.parse(readFileSync(p, "utf8")); } catch { return null; /* absent/corrupt → null */ } }
|
|
147
|
+
|
|
148
|
+
/** The agent dir's own @cohortapp/agent-sdk dependency version, "" when none. */
|
|
149
|
+
export function readLocalVersion(cwd) {
|
|
150
|
+
try {
|
|
151
|
+
const req = createRequire(join(resolve(cwd), "package.json"));
|
|
152
|
+
const pkg = JSON.parse(readFileSync(req.resolve(`${SDK_PACKAGE}/package.json`), "utf8"));
|
|
153
|
+
return typeof pkg.version === "string" ? pkg.version : "";
|
|
154
|
+
} catch {
|
|
155
|
+
// No agent package.json to resolve from (or the dep is not declared): read
|
|
156
|
+
// the node_modules copy directly; absent → "" and the row degrades to warn.
|
|
157
|
+
const p = join(resolve(cwd), "node_modules", ...SDK_PACKAGE.split("/"), "package.json");
|
|
158
|
+
const j = safeJson(p);
|
|
159
|
+
return j && typeof j.version === "string" ? j.version : "";
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Read everything `buildVerifyReport` needs. Every read is fail-open.
|
|
165
|
+
* @param {{cwd:string, home?:string, env?:object, exec?:Function, expected:string, daemonRestartPending?:boolean}} o
|
|
166
|
+
*/
|
|
167
|
+
export function collectVerifyInputs(o = {}) {
|
|
168
|
+
const cwd = resolve(o.cwd || process.cwd());
|
|
169
|
+
const home = o.home || homedir();
|
|
170
|
+
const env = o.env || process.env;
|
|
171
|
+
const exec = o.exec || defaultExec;
|
|
172
|
+
const skipGlobal = String(env[SKIP_ENV] || "") === "1";
|
|
173
|
+
const claudeDir = join(home, ".claude");
|
|
174
|
+
|
|
175
|
+
const manifest = safeJson(join(cwd, ".maestro", "shipped-manifest.json"));
|
|
176
|
+
const daemon = parseDaemonHealth(safeRead(join(cwd, "state", "dashboards", "daemon-health.yaml")));
|
|
177
|
+
const plists = readLaunchdState({ generatedDir: join(cwd, "scripts", "local-triggers", "plists"), launchAgentsDir: join(home, "Library", "LaunchAgents"), exec });
|
|
178
|
+
|
|
179
|
+
const agentJson = safeJson(join(cwd, "config", "agent.json"));
|
|
180
|
+
const identityConfigured = !!(agentJson && (agentJson.fullName || agentJson.firstName));
|
|
181
|
+
const md = safeRead(join(claudeDir, "CLAUDE.md"));
|
|
182
|
+
const claudeJson = safeJson(join(home, ".claude.json"));
|
|
183
|
+
const mcpEntry = claudeJson && claudeJson.mcpServers && claudeJson.mcpServers.cohort;
|
|
184
|
+
const settings = safeJson(join(claudeDir, "settings.json"));
|
|
185
|
+
const pointer = safeJson(join(claudeDir, "maestro-agent.json"));
|
|
186
|
+
const markers = {
|
|
187
|
+
identityConfigured,
|
|
188
|
+
identity: md.includes(IDENTITY_BEGIN),
|
|
189
|
+
collective: md.includes(CLAUDE_MD_BEGIN),
|
|
190
|
+
mcp: !!(mcpEntry && mcpEntry.env && String(mcpEntry.env.COHORT_AGENT_ROOT || "") === cwd),
|
|
191
|
+
settings: !!settings && mergeSessionSettings(settings, { agentRoot: cwd }).changes.length === 0,
|
|
192
|
+
pointer: !!(pointer && String(pointer.agentRepo || "") === cwd),
|
|
193
|
+
skills: existsSync(join(claudeDir, "skills", `${GLOBAL_SKILL_PREFIX}${REQUIRED_SKILL}`, "SKILL.md")),
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
return {
|
|
197
|
+
expected: o.expected,
|
|
198
|
+
globalVersion: skipGlobal ? "" : readGlobalVersion({ exec }),
|
|
199
|
+
skipGlobal,
|
|
200
|
+
localVersion: readLocalVersion(cwd),
|
|
201
|
+
manifestPresent: !!manifest,
|
|
202
|
+
manifestVersion: manifest && typeof manifest.sdkVersion === "string" ? manifest.sdkVersion : "",
|
|
203
|
+
daemon,
|
|
204
|
+
daemonRestartPending: !!o.daemonRestartPending,
|
|
205
|
+
plists,
|
|
206
|
+
markers,
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/** Collect + build. */
|
|
211
|
+
export function runVerify(o = {}) {
|
|
212
|
+
return buildVerifyReport(collectVerifyInputs(o));
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
export default { buildVerifyReport, formatVerifyReport, collectVerifyInputs, runVerify, parseDaemonHealth, readLocalVersion, REQUIRED_SKILL };
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* verify.test.mjs — buildVerifyReport is pure; collectVerifyInputs reads a
|
|
3
|
+
* temp HOME + agent dir with a recorded exec (never the real npm/launchctl).
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { test } from "node:test";
|
|
7
|
+
import assert from "node:assert/strict";
|
|
8
|
+
import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from "node:fs";
|
|
9
|
+
import { join } from "node:path";
|
|
10
|
+
import { tmpdir } from "node:os";
|
|
11
|
+
import { buildVerifyReport, formatVerifyReport, parseDaemonHealth, collectVerifyInputs, runVerify } from "./verify.mjs";
|
|
12
|
+
import { IDENTITY_BEGIN, IDENTITY_END, CLAUDE_MD_BEGIN, CLAUDE_MD_END, mergeSessionSettings } from "../collective/global-config.mjs";
|
|
13
|
+
|
|
14
|
+
const G = ["ai.maestro.ivy-daemon", "ai.maestro.ivy-session"];
|
|
15
|
+
const allMarkers = { identityConfigured: true, identity: true, collective: true, mcp: true, settings: true, pointer: true, skills: true };
|
|
16
|
+
|
|
17
|
+
function good(over = {}) {
|
|
18
|
+
return {
|
|
19
|
+
expected: "2.12.0", globalVersion: "2.12.0", localVersion: "2.12.0", manifestPresent: true, manifestVersion: "2.12.0",
|
|
20
|
+
daemon: { version: "2.12.0", updatedAt: "2026-09-08T12:00:00Z" },
|
|
21
|
+
plists: { generated: G, installed: G, loaded: G, launchctlOk: true }, markers: allMarkers, ...over,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const byName = (r) => Object.fromEntries(r.rows.map((x) => [x.name, x]));
|
|
26
|
+
|
|
27
|
+
test("a seat wholly on the expected version is ok with no mismatches", () => {
|
|
28
|
+
const r = buildVerifyReport(good());
|
|
29
|
+
assert.equal(r.ok, true, JSON.stringify(r.rows));
|
|
30
|
+
assert.deepEqual(r.mismatches, []);
|
|
31
|
+
assert.ok(r.rows.every((x) => x.level === "ok"));
|
|
32
|
+
assert.match(formatVerifyReport(r).pop(), /seat is on 2\.12\.0/);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test("global bin behind → fail; under MAESTRO_SKIP_GLOBAL_INSTALL it is a warn", () => {
|
|
36
|
+
const r = buildVerifyReport(good({ globalVersion: "2.11.17" }));
|
|
37
|
+
assert.equal(r.ok, false);
|
|
38
|
+
assert.deepEqual(r.mismatches, ["global"]);
|
|
39
|
+
assert.match(byName(r).global.msg, /npm i -g @cohortapp\/agent-sdk@2\.12\.0 --no-audit --no-fund/);
|
|
40
|
+
const s = buildVerifyReport(good({ globalVersion: "", skipGlobal: true }));
|
|
41
|
+
assert.equal(s.ok, true);
|
|
42
|
+
assert.equal(byName(s).global.level, "warn");
|
|
43
|
+
assert.equal(buildVerifyReport(good({ globalVersion: "" })).mismatches[0], "global");
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test("local / manifest / daemon: missing is a warn, a different version is a fail; daemonRestartPending downgrades the daemon row", () => {
|
|
47
|
+
assert.equal(byName(buildVerifyReport(good({ localVersion: "" }))).local.level, "warn");
|
|
48
|
+
assert.deepEqual(buildVerifyReport(good({ localVersion: "2.11.17" })).mismatches, ["local"]);
|
|
49
|
+
assert.equal(byName(buildVerifyReport(good({ manifestPresent: false }))).manifest.level, "warn");
|
|
50
|
+
assert.equal(byName(buildVerifyReport(good({ manifestVersion: "" }))).manifest.level, "warn");
|
|
51
|
+
assert.deepEqual(buildVerifyReport(good({ manifestVersion: "2.11.17" })).mismatches, ["manifest"]);
|
|
52
|
+
assert.equal(byName(buildVerifyReport(good({ daemon: { version: "" } }))).daemon.level, "warn");
|
|
53
|
+
assert.deepEqual(buildVerifyReport(good({ daemon: { version: "2.11.17" } })).mismatches, ["daemon"]);
|
|
54
|
+
const pending = buildVerifyReport(good({ daemon: { version: "2.11.17" }, daemonRestartPending: true }));
|
|
55
|
+
assert.equal(pending.ok, true);
|
|
56
|
+
assert.match(byName(pending).daemon.msg, /autoupdate kickstarts/);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
test("plists: a generated label that is not installed, or installed but not loaded, is a fail naming the label", () => {
|
|
60
|
+
const notInstalled = buildVerifyReport(good({ plists: { generated: G, installed: [G[0]], loaded: [G[0]], launchctlOk: true } }));
|
|
61
|
+
assert.deepEqual(notInstalled.mismatches, ["plists"]);
|
|
62
|
+
assert.match(byName(notInstalled).plists.msg, /not installed: ai\.maestro\.ivy-session/);
|
|
63
|
+
const notLoaded = buildVerifyReport(good({ plists: { generated: G, installed: G, loaded: [G[0]], launchctlOk: true } }));
|
|
64
|
+
assert.match(byName(notLoaded).plists.msg, /not loaded: ai\.maestro\.ivy-session/);
|
|
65
|
+
const noLaunchctl = buildVerifyReport(good({ plists: { generated: G, installed: G, loaded: [], launchctlOk: false } }));
|
|
66
|
+
assert.equal(noLaunchctl.ok, true, "cannot prove loaded without launchctl → warn");
|
|
67
|
+
assert.equal(byName(buildVerifyReport(good({ plists: { generated: [], installed: [], loaded: [] } }))).plists.level, "warn");
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test("global-setup markers: any missing marker is a fail; an unconfigured identity is a warn", () => {
|
|
71
|
+
const r = buildVerifyReport(good({ markers: { ...allMarkers, mcp: false, skills: false } }));
|
|
72
|
+
assert.deepEqual(r.mismatches, ["global-setup"]);
|
|
73
|
+
assert.match(byName(r)["global-setup"].msg, /cohort MCP server/);
|
|
74
|
+
assert.match(byName(r)["global-setup"].msg, /maestro-main-session\/SKILL\.md/);
|
|
75
|
+
const unconf = buildVerifyReport(good({ markers: { ...allMarkers, identityConfigured: false, identity: false } }));
|
|
76
|
+
assert.equal(unconf.ok, true);
|
|
77
|
+
assert.equal(unconf.rows.filter((x) => x.name === "global-setup")[0].level, "warn");
|
|
78
|
+
const noId = buildVerifyReport(good({ markers: { ...allMarkers, identity: false } }));
|
|
79
|
+
assert.equal(noId.ok, false);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test("parseDaemonHealth reads sdk_version / updated / pid from the flat YAML the daemon writes", () => {
|
|
83
|
+
const y = "updated: 2026-09-08T12:00:00.000Z\nuptime_s: 40\nsdk_version: 2.12.0\npid: 4242\ndispatcher:\n {\n }\n";
|
|
84
|
+
assert.deepEqual(parseDaemonHealth(y), { version: "2.12.0", updatedAt: "2026-09-08T12:00:00.000Z", pid: 4242 });
|
|
85
|
+
assert.deepEqual(parseDaemonHealth(""), { version: "", updatedAt: "", pid: null });
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// ---------------------------------------------------------------------------
|
|
89
|
+
// collectVerifyInputs — the edge over a temp HOME + agent dir
|
|
90
|
+
// ---------------------------------------------------------------------------
|
|
91
|
+
|
|
92
|
+
function seat({ version = "2.12.0", withSetup = true } = {}) {
|
|
93
|
+
const root = mkdtempSync(join(tmpdir(), "verify-"));
|
|
94
|
+
const cwd = join(root, "ivy-ai");
|
|
95
|
+
const home = join(root, "home");
|
|
96
|
+
const claudeDir = join(home, ".claude");
|
|
97
|
+
mkdirSync(join(cwd, "config"), { recursive: true });
|
|
98
|
+
mkdirSync(join(cwd, "node_modules", "@cohortapp", "agent-sdk"), { recursive: true });
|
|
99
|
+
mkdirSync(join(cwd, ".maestro"), { recursive: true });
|
|
100
|
+
mkdirSync(join(cwd, "state", "dashboards"), { recursive: true });
|
|
101
|
+
mkdirSync(join(cwd, "scripts", "local-triggers", "plists"), { recursive: true });
|
|
102
|
+
mkdirSync(join(home, "Library", "LaunchAgents"), { recursive: true });
|
|
103
|
+
mkdirSync(join(claudeDir, "skills", "maestro-main-session"), { recursive: true });
|
|
104
|
+
writeFileSync(join(cwd, "config", "agent.json"), JSON.stringify({ firstName: "Ivy", fullName: "Ivy Tester" }));
|
|
105
|
+
writeFileSync(join(cwd, "package.json"), JSON.stringify({ name: "ivy-ai" }));
|
|
106
|
+
writeFileSync(join(cwd, "node_modules", "@cohortapp", "agent-sdk", "package.json"), JSON.stringify({ name: "@cohortapp/agent-sdk", version }));
|
|
107
|
+
writeFileSync(join(cwd, ".maestro", "shipped-manifest.json"), JSON.stringify({ version: 1, sdkVersion: version, files: [] }));
|
|
108
|
+
writeFileSync(join(cwd, "state", "dashboards", "daemon-health.yaml"), `updated: 2026-09-08T12:00:00Z\nsdk_version: ${version}\n`);
|
|
109
|
+
for (const l of G) {
|
|
110
|
+
writeFileSync(join(cwd, "scripts", "local-triggers", "plists", `${l}.plist`), `<plist>${l}</plist>`);
|
|
111
|
+
writeFileSync(join(home, "Library", "LaunchAgents", `${l}.plist`), `<plist>${l}</plist>`);
|
|
112
|
+
}
|
|
113
|
+
if (withSetup) {
|
|
114
|
+
writeFileSync(join(claudeDir, "CLAUDE.md"), `${CLAUDE_MD_BEGIN}\nx\n${CLAUDE_MD_END}\n${IDENTITY_BEGIN}\nid\n${IDENTITY_END}\n`);
|
|
115
|
+
writeFileSync(join(home, ".claude.json"), JSON.stringify({ mcpServers: { cohort: { command: "cohort-mcp", args: [], env: { COHORT_AGENT_ROOT: cwd } } } }));
|
|
116
|
+
writeFileSync(join(claudeDir, "settings.json"), JSON.stringify(mergeSessionSettings({}, { agentRoot: cwd }).settings));
|
|
117
|
+
writeFileSync(join(claudeDir, "maestro-agent.json"), JSON.stringify({ agentRepo: cwd }));
|
|
118
|
+
writeFileSync(join(claudeDir, "skills", "maestro-main-session", "SKILL.md"), "# skill");
|
|
119
|
+
}
|
|
120
|
+
return { root, cwd, home };
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function execFor({ globalVersion = "2.12.0", loaded = G } = {}) {
|
|
124
|
+
const calls = [];
|
|
125
|
+
const exec = (cmd, args) => {
|
|
126
|
+
calls.push([cmd, ...args]);
|
|
127
|
+
if (cmd === "npm") return { status: 0, stdout: JSON.stringify({ dependencies: { "@cohortapp/agent-sdk": { version: globalVersion } } }), stderr: "" };
|
|
128
|
+
if (cmd === "launchctl") return { status: 0, stdout: `PID\tStatus\tLabel\n${loaded.map((l) => `1\t0\t${l}`).join("\n")}\n`, stderr: "" };
|
|
129
|
+
return { status: 1, stdout: "", stderr: "" };
|
|
130
|
+
};
|
|
131
|
+
exec.calls = calls;
|
|
132
|
+
return exec;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
test("collectVerifyInputs reads a fully-set-up seat as all-ok; runVerify agrees", () => {
|
|
136
|
+
const s = seat();
|
|
137
|
+
try {
|
|
138
|
+
const exec = execFor();
|
|
139
|
+
const inputs = collectVerifyInputs({ cwd: s.cwd, home: s.home, env: {}, exec, expected: "2.12.0" });
|
|
140
|
+
assert.equal(inputs.globalVersion, "2.12.0");
|
|
141
|
+
assert.equal(inputs.localVersion, "2.12.0");
|
|
142
|
+
assert.equal(inputs.manifestVersion, "2.12.0");
|
|
143
|
+
assert.equal(inputs.daemon.version, "2.12.0");
|
|
144
|
+
assert.deepEqual(inputs.plists.generated.sort(), G);
|
|
145
|
+
assert.deepEqual(inputs.markers, { identityConfigured: true, identity: true, collective: true, mcp: true, settings: true, pointer: true, skills: true });
|
|
146
|
+
const r = runVerify({ cwd: s.cwd, home: s.home, env: {}, exec, expected: "2.12.0" });
|
|
147
|
+
assert.equal(r.ok, true, JSON.stringify(r.rows));
|
|
148
|
+
assert.ok(exec.calls.some((c) => c[0] === "npm" && c[1] === "ls" && c[2] === "-g"));
|
|
149
|
+
} finally { rmSync(s.root, { recursive: true, force: true }); }
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
test("collectVerifyInputs: a seat with no global-setup and a stale global bin reports both mismatches; MAESTRO_SKIP_GLOBAL_INSTALL skips the npm read", () => {
|
|
153
|
+
const s = seat({ withSetup: false });
|
|
154
|
+
try {
|
|
155
|
+
const exec = execFor({ globalVersion: "2.11.17", loaded: [G[0]] });
|
|
156
|
+
const r = runVerify({ cwd: s.cwd, home: s.home, env: {}, exec, expected: "2.12.0" });
|
|
157
|
+
assert.equal(r.ok, false);
|
|
158
|
+
assert.deepEqual(r.mismatches.sort(), ["global", "global-setup", "plists"]);
|
|
159
|
+
const skipExec = execFor();
|
|
160
|
+
const inputs = collectVerifyInputs({ cwd: s.cwd, home: s.home, env: { MAESTRO_SKIP_GLOBAL_INSTALL: "1" }, exec: skipExec, expected: "2.12.0" });
|
|
161
|
+
assert.equal(inputs.skipGlobal, true);
|
|
162
|
+
assert.ok(!skipExec.calls.some((c) => c[0] === "npm"), "no npm call under the skip flag");
|
|
163
|
+
} finally { rmSync(s.root, { recursive: true, force: true }); }
|
|
164
|
+
});
|
package/lib/voice/outbound.mjs
CHANGED
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
*/
|
|
30
30
|
|
|
31
31
|
import { writeFileSync, mkdirSync, existsSync, readFileSync } from "node:fs";
|
|
32
|
+
import { writeJsonAtomic } from "../fs-atomic.mjs";
|
|
32
33
|
import { join, resolve } from "node:path";
|
|
33
34
|
import { tmpdir } from "node:os";
|
|
34
35
|
import { synthesizeSpeech, pickDefaultVoice, ttsCostEstimate } from "./tts.mjs";
|
|
@@ -335,14 +336,14 @@ export async function placeOutboundCall(opts = {}) {
|
|
|
335
336
|
// Persist brief locally (always — for audit + later post-call processing)
|
|
336
337
|
const briefsDir = join(agentRoot, "state/voice/outbound-briefs");
|
|
337
338
|
mkdirSync(briefsDir, { recursive: true });
|
|
338
|
-
|
|
339
|
+
writeJsonAtomic(join(briefsDir, `${callId}.json`), {
|
|
339
340
|
call_id: callId,
|
|
340
341
|
to: opts.to,
|
|
341
342
|
brief: opts.brief,
|
|
342
343
|
voice: opts.voice || "cedar",
|
|
343
344
|
transport,
|
|
344
345
|
created_at: new Date().toISOString(),
|
|
345
|
-
}
|
|
346
|
+
});
|
|
346
347
|
|
|
347
348
|
// For media_streams, we use URL-based TwiML — Twilio fetches the TwiML
|
|
348
349
|
// from /twilio/twiml when the call connects, instead of us passing it
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
import { spawn } from "node:child_process";
|
|
37
37
|
import { writeFileSync, readFileSync, mkdirSync, existsSync, appendFileSync } from "node:fs";
|
|
38
38
|
import { join, resolve, dirname } from "node:path";
|
|
39
|
+
import { writeJsonAtomic } from "../fs-atomic.mjs";
|
|
39
40
|
|
|
40
41
|
const BRIEF_PROMPT = `You are an executive operator processing the transcript of a voice call.
|
|
41
42
|
Produce a structured post-call brief in JSON only — no preamble, no commentary.
|
|
@@ -145,7 +146,7 @@ export async function generateBrief(opts = {}) {
|
|
|
145
146
|
const mdPath = join(briefDir, `${date}-${safeCallId}.md`);
|
|
146
147
|
const jsonPath = join(briefDir, `${date}-${safeCallId}.json`);
|
|
147
148
|
writeFileSync(mdPath, briefMd);
|
|
148
|
-
|
|
149
|
+
writeJsonAtomic(jsonPath, briefJson);
|
|
149
150
|
|
|
150
151
|
// Push extracted action items to action-stack queue (best-effort)
|
|
151
152
|
await pushActionItems({ agentRoot, brief: briefJson, callId, peer });
|
|
@@ -38,16 +38,21 @@ export const SESSION_ROTATION_DEFAULTS = Object.freeze({
|
|
|
38
38
|
* recent_latencies_ms: number[], // last N turn latencies
|
|
39
39
|
* }
|
|
40
40
|
* @param {object} opts Optional overrides for the defaults.
|
|
41
|
+
* @param {number} [opts.now] Injectable clock (ms epoch); defaults to Date.now().
|
|
41
42
|
* @returns {{ rotate: boolean, reason?: string }}
|
|
42
43
|
*/
|
|
43
44
|
export function shouldRotate(state, opts = {}) {
|
|
44
45
|
const cfg = { ...SESSION_ROTATION_DEFAULTS, ...opts };
|
|
45
46
|
if (!state) return { rotate: false };
|
|
47
|
+
// The clock is an input: without it the max_duration rule could only be
|
|
48
|
+
// exercised by waiting out a real session. Defaulted, so every existing
|
|
49
|
+
// caller is unchanged.
|
|
50
|
+
const now = typeof opts.now === "number" ? opts.now : Date.now();
|
|
46
51
|
|
|
47
52
|
if ((state.turns || 0) >= cfg.max_turns) {
|
|
48
53
|
return { rotate: true, reason: "max_turns" };
|
|
49
54
|
}
|
|
50
|
-
const ageMs =
|
|
55
|
+
const ageMs = now - (state.started_at || now);
|
|
51
56
|
if (ageMs >= cfg.max_duration_ms) {
|
|
52
57
|
return { rotate: true, reason: "max_duration" };
|
|
53
58
|
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for shouldRotate — the voice-session rotation decision.
|
|
3
|
+
*
|
|
4
|
+
* These tests exist because the clock became an argument. Before that, the
|
|
5
|
+
* `max_duration` rule read `Date.now()` internally, so the only way to observe
|
|
6
|
+
* it firing was to hold a real voice session open for four minutes. That is not
|
|
7
|
+
* a test anyone writes, which is why there were none.
|
|
8
|
+
*
|
|
9
|
+
* @module lib/voice/session-rotation.test
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { describe, it } from "node:test";
|
|
13
|
+
import assert from "node:assert/strict";
|
|
14
|
+
import {
|
|
15
|
+
shouldRotate,
|
|
16
|
+
SESSION_ROTATION_DEFAULTS,
|
|
17
|
+
} from "./session-rotation.mjs";
|
|
18
|
+
|
|
19
|
+
const T0 = 1_700_000_000_000; // a fixed instant; any would do
|
|
20
|
+
const { max_duration_ms: MAX_MS, max_turns: MAX_TURNS } = SESSION_ROTATION_DEFAULTS;
|
|
21
|
+
|
|
22
|
+
/** A session started at T0 with no turns and no latency history. */
|
|
23
|
+
const fresh = (over = {}) => ({
|
|
24
|
+
turns: 0,
|
|
25
|
+
started_at: T0,
|
|
26
|
+
recent_latencies_ms: [],
|
|
27
|
+
...over,
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
describe("shouldRotate: no state", () => {
|
|
31
|
+
it("does not rotate when there is no session", () => {
|
|
32
|
+
assert.deepEqual(shouldRotate(null), { rotate: false });
|
|
33
|
+
assert.deepEqual(shouldRotate(undefined), { rotate: false });
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
describe("shouldRotate: the duration boundary", () => {
|
|
38
|
+
it("holds one millisecond before the limit", () => {
|
|
39
|
+
const r = shouldRotate(fresh(), { now: T0 + MAX_MS - 1 });
|
|
40
|
+
assert.equal(r.rotate, false);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it("rotates exactly ON the limit", () => {
|
|
44
|
+
const r = shouldRotate(fresh(), { now: T0 + MAX_MS });
|
|
45
|
+
assert.deepEqual(r, { rotate: true, reason: "max_duration" });
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it("rotates well past the limit", () => {
|
|
49
|
+
const r = shouldRotate(fresh(), { now: T0 + MAX_MS * 10 });
|
|
50
|
+
assert.equal(r.reason, "max_duration");
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it("honours an overridden limit", () => {
|
|
54
|
+
const r = shouldRotate(fresh(), { now: T0 + 100, max_duration_ms: 50 });
|
|
55
|
+
assert.deepEqual(r, { rotate: true, reason: "max_duration" });
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it("treats a missing started_at as age zero rather than as epoch", () => {
|
|
59
|
+
// The pre-existing semantics: `now - (started_at || now)` === 0. Pinned so a
|
|
60
|
+
// future refactor cannot silently turn an unknown start into a 55-year age.
|
|
61
|
+
const r = shouldRotate({ turns: 0, recent_latencies_ms: [] }, { now: T0 });
|
|
62
|
+
assert.equal(r.rotate, false);
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
describe("shouldRotate: turn count takes precedence", () => {
|
|
67
|
+
it("rotates on max_turns even inside the duration window", () => {
|
|
68
|
+
const r = shouldRotate(fresh({ turns: MAX_TURNS }), { now: T0 });
|
|
69
|
+
assert.deepEqual(r, { rotate: true, reason: "max_turns" });
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it("holds one turn below the limit", () => {
|
|
73
|
+
assert.equal(shouldRotate(fresh({ turns: MAX_TURNS - 1 }), { now: T0 }).rotate, false);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it("reports max_turns, not max_duration, when both are exceeded", () => {
|
|
77
|
+
const r = shouldRotate(fresh({ turns: MAX_TURNS }), { now: T0 + MAX_MS * 5 });
|
|
78
|
+
assert.equal(r.reason, "max_turns");
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
describe("shouldRotate: latency drift", () => {
|
|
83
|
+
it("ignores latency until there are five samples", () => {
|
|
84
|
+
const slow = [9999, 9999, 9999, 9999];
|
|
85
|
+
assert.equal(shouldRotate(fresh({ recent_latencies_ms: slow }), { now: T0 }).rotate, false);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it("rotates once five samples drift past p95", () => {
|
|
89
|
+
const slow = [9999, 9999, 9999, 9999, 9999];
|
|
90
|
+
const r = shouldRotate(fresh({ recent_latencies_ms: slow }), { now: T0 });
|
|
91
|
+
assert.deepEqual(r, { rotate: true, reason: "latency_drift" });
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it("does not rotate on five fast samples", () => {
|
|
95
|
+
const fast = [10, 12, 11, 9, 13];
|
|
96
|
+
assert.equal(shouldRotate(fresh({ recent_latencies_ms: fast }), { now: T0 }).rotate, false);
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
describe("shouldRotate: determinism", () => {
|
|
101
|
+
it("is a pure function of (state, opts) — same inputs, same answer", () => {
|
|
102
|
+
const state = fresh({ turns: 3, recent_latencies_ms: [10, 20, 30, 40, 50] });
|
|
103
|
+
const a = shouldRotate(state, { now: T0 + 1000 });
|
|
104
|
+
const b = shouldRotate(state, { now: T0 + 1000 });
|
|
105
|
+
assert.deepEqual(a, b);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it("does not mutate the state it is given", () => {
|
|
109
|
+
const state = fresh({ turns: 2, recent_latencies_ms: [5, 4, 3, 2, 1] });
|
|
110
|
+
const before = JSON.stringify(state);
|
|
111
|
+
shouldRotate(state, { now: T0 + 5 });
|
|
112
|
+
assert.equal(JSON.stringify(state), before);
|
|
113
|
+
});
|
|
114
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cohortapp/agent-sdk",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.13.0",
|
|
4
4
|
"description": "Cohort Agent SDK — autonomous AI colleague runtime. Deploy senior AI colleagues on dedicated Mac minis, wired to the Cohort operating surface.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -121,7 +121,7 @@
|
|
|
121
121
|
"scripts": {
|
|
122
122
|
"test": "node scripts/ci/run-tests.mjs",
|
|
123
123
|
"test:coverage": "node scripts/ci/run-coverage.mjs",
|
|
124
|
-
"test:list": "node --test bin/maestro.test.mjs lib/action-executor.test.mjs lib/archetype.test.mjs lib/autonomy.test.mjs lib/backlog.test.mjs lib/budget-guard.test.mjs lib/cadence-bus-schedule.test.mjs lib/cadence-bus.test.mjs lib/cadences.test.mjs lib/capability.test.mjs lib/channels/base-adapter.test.mjs lib/channels/channels.test.mjs lib/channels/contract.test.mjs lib/channels/inbox-item.test.mjs lib/channels/pairing.test.mjs lib/channels/slack-adapter.test.mjs lib/channels/telegram-adapter.test.mjs lib/channels/whatsapp/baileys-typing.test.mjs lib/charter.test.mjs lib/claude-bin.test.mjs lib/collective/capture.test.mjs lib/collective/cards.test.mjs lib/collective/config.test.mjs lib/collective/global-config.test.mjs lib/collective/presence.test.mjs lib/collective/recall.test.mjs lib/comms.test.mjs lib/fs-atomic.test.mjs lib/fs-ownership.test.mjs lib/learning/config.test.mjs lib/learning/counters.test.mjs lib/learning/curator-consolidate.test.mjs lib/learning/curator.test.mjs lib/learning/reflect.test.mjs lib/learning/session-index.test.mjs lib/learning/skill-writer.test.mjs lib/model-router.test.mjs lib/model-router/catalog.test.mjs lib/model-router/economics.test.mjs lib/model-router/failover.test.mjs lib/model-router/health.test.mjs lib/model-router/integration.test.mjs lib/model-router/integration-coverage.test.mjs lib/model-router/ledger.test.mjs lib/model-router/llm-task.test.mjs lib/model-router/org-credentials.test.mjs lib/model-router/reconcile.test.mjs lib/model-router/repair.test.mjs lib/model-router/spawn.test.mjs lib/model-router/taxonomy.test.mjs lib/org/approvals.test.mjs lib/org/board.test.mjs lib/org/client.test.mjs lib/org/cost-sync.test.mjs lib/org/handoff.test.mjs lib/org/keys.test.mjs lib/org/knowledge.test.mjs lib/org/activity.test.mjs lib/org/leases.test.mjs lib/org/mesh.test.mjs lib/org/messaging.test.mjs lib/org/cohort-client.test.mjs lib/org/policy.test.mjs lib/org/registry.test.mjs lib/org/tool-surface.test.mjs lib/org/ui-parity.test.mjs lib/org/doctor.test.mjs lib/org/verify.test.mjs lib/mcp/server.test.mjs lib/channels/orgmail/adapter.test.mjs lib/rag/rag.test.mjs lib/rate-guard.test.mjs lib/render.test.mjs lib/resource-governor.test.mjs lib/security/coerce-args.test.mjs lib/security/dangerous-tools.test.mjs lib/security/external-content.test.mjs lib/security/redact.test.mjs lib/security/secret-equal.test.mjs lib/session-permissions.test.mjs lib/setup/completeness.test.mjs lib/setup/context-pack.test.mjs lib/setup/enrich.test.mjs lib/setup/enroll-from-cohort.test.mjs lib/setup/integration.test.mjs lib/setup/io.test.mjs lib/setup/runner.test.mjs lib/setup/sections/identity.test.mjs lib/setup/sections/learning.test.mjs lib/setup/sections/messaging.test.mjs lib/setup/sections/org.test.mjs lib/setup/sections/orgmail.test.mjs lib/setup/sections/recovery.test.mjs lib/setup/sections/subagents.test.mjs lib/setup/sot.test.mjs lib/setup/state.test.mjs lib/singleton.test.mjs lib/subagents/schema.test.mjs lib/subagents/refs.test.mjs lib/subagents/manifest.test.mjs lib/subagents/lock.test.mjs lib/subagents/resolve.test.mjs lib/subagents/gap.test.mjs lib/subagents/client.test.mjs lib/subagents/cli.test.mjs lib/telemetry/alerts.test.mjs lib/telemetry/collect.test.mjs lib/tool-definitions.test.mjs lib/voice/outbound.test.mjs lib/voice/stt.test.mjs lib/voice/voice.test.mjs scripts/cadence/enqueue-cadence-tick.test.mjs scripts/ci/check-no-build-artifacts.test.mjs scripts/ci/check-no-residual-identity.test.mjs scripts/ci/check.test.mjs scripts/collective/hook-runner.test.mjs scripts/cost/fleet-digest.test.mjs scripts/daemon/cadence-consumer-governance.test.mjs scripts/daemon/cadence-consumer.test.mjs scripts/daemon/cadence-handlers.test.mjs scripts/daemon/classify-kind.test.mjs scripts/daemon/dispatcher-cooldown.test.mjs scripts/daemon/dispatcher-governance.test.mjs scripts/daemon/inbox-deferral.test.mjs scripts/daemon/lib/session-router.test.mjs scripts/daemon/prompt-builder.test.mjs scripts/daemon/typing-registry.test.mjs scripts/hooks/pre-send-audit.test.mjs scripts/local-triggers/generate-plists.test.mjs scripts/poller/inbox-scan-poller.test.mjs scripts/poller/lib/cloud-relay-dedup.test.mjs scripts/poller/slack-socket-mode.test.mjs scripts/setup/gen-subagent-manifest.test.mjs scripts/setup/init-agent.test.mjs services/webhook-relay/server.test.mjs",
|
|
124
|
+
"test:list": "node --test bin/maestro.test.mjs lib/action-executor.test.mjs lib/archetype.test.mjs lib/autonomy.test.mjs lib/backlog.test.mjs lib/budget-guard.test.mjs lib/cadence-bus-schedule.test.mjs lib/cadence-bus.test.mjs lib/cadences.test.mjs lib/capability.test.mjs lib/channels/base-adapter.test.mjs lib/channels/channels.test.mjs lib/channels/contract.test.mjs lib/channels/inbox-item.test.mjs lib/channels/pairing.test.mjs lib/channels/slack-adapter.test.mjs lib/channels/telegram-adapter.test.mjs lib/channels/whatsapp/baileys-typing.test.mjs lib/charter.test.mjs lib/claude-bin.test.mjs lib/collective/capture.test.mjs lib/collective/cards.test.mjs lib/collective/config.test.mjs lib/collective/global-config.test.mjs lib/collective/presence.test.mjs lib/collective/recall.test.mjs lib/comms.test.mjs lib/fs-atomic.test.mjs lib/fs-ownership.test.mjs lib/learning/config.test.mjs lib/learning/counters.test.mjs lib/learning/curator-consolidate.test.mjs lib/learning/curator.test.mjs lib/learning/reflect.test.mjs lib/learning/session-index.test.mjs lib/learning/skill-writer.test.mjs lib/model-router.test.mjs lib/model-router/catalog.test.mjs lib/model-router/economics.test.mjs lib/model-router/failover.test.mjs lib/model-router/health.test.mjs lib/model-router/integration.test.mjs lib/model-router/integration-coverage.test.mjs lib/model-router/ledger.test.mjs lib/model-router/llm-task.test.mjs lib/model-router/org-credentials.test.mjs lib/model-router/reconcile.test.mjs lib/model-router/repair.test.mjs lib/model-router/spawn.test.mjs lib/model-router/taxonomy.test.mjs lib/org/approvals.test.mjs lib/org/board.test.mjs lib/org/client.test.mjs lib/org/cost-sync.test.mjs lib/org/handoff.test.mjs lib/org/keys.test.mjs lib/org/knowledge.test.mjs lib/org/activity.test.mjs lib/org/leases.test.mjs lib/org/mesh.test.mjs lib/org/messaging.test.mjs lib/org/cohort-client.test.mjs lib/org/policy.test.mjs lib/org/registry.test.mjs lib/org/tool-surface.test.mjs lib/org/ui-parity.test.mjs lib/org/doctor.test.mjs lib/org/verify.test.mjs lib/mcp/server.test.mjs lib/channels/orgmail/adapter.test.mjs lib/rag/rag.test.mjs lib/rate-guard.test.mjs lib/render.test.mjs lib/resource-governor.test.mjs lib/security/coerce-args.test.mjs lib/security/dangerous-tools.test.mjs lib/security/external-content.test.mjs lib/security/redact.test.mjs lib/security/secret-equal.test.mjs lib/session-permissions.test.mjs lib/setup/completeness.test.mjs lib/setup/context-pack.test.mjs lib/setup/enrich.test.mjs lib/setup/enroll-from-cohort.test.mjs lib/setup/integration.test.mjs lib/setup/io.test.mjs lib/setup/runner.test.mjs lib/setup/sections/identity.test.mjs lib/setup/sections/learning.test.mjs lib/setup/sections/messaging.test.mjs lib/setup/sections/org.test.mjs lib/setup/sections/orgmail.test.mjs lib/setup/sections/recovery.test.mjs lib/setup/sections/subagents.test.mjs lib/setup/sot.test.mjs lib/setup/state.test.mjs lib/singleton.test.mjs lib/subagents/schema.test.mjs lib/subagents/refs.test.mjs lib/subagents/manifest.test.mjs lib/subagents/lock.test.mjs lib/subagents/resolve.test.mjs lib/subagents/gap.test.mjs lib/subagents/client.test.mjs lib/subagents/cli.test.mjs lib/telemetry/alerts.test.mjs lib/telemetry/collect.test.mjs lib/tool-definitions.test.mjs lib/voice/session-rotation.test.mjs lib/voice/outbound.test.mjs lib/voice/stt.test.mjs lib/voice/voice.test.mjs scripts/cadence/enqueue-cadence-tick.test.mjs scripts/ci/check-durable-write-seam.test.mjs scripts/ci/check-no-build-artifacts.test.mjs scripts/ci/check-no-residual-identity.test.mjs scripts/ci/check.test.mjs scripts/collective/hook-runner.test.mjs scripts/cost/fleet-digest.test.mjs scripts/daemon/cadence-consumer-governance.test.mjs scripts/daemon/cadence-consumer.test.mjs scripts/daemon/cadence-handlers.test.mjs scripts/daemon/classify-kind.test.mjs scripts/daemon/dispatcher-cooldown.test.mjs scripts/daemon/dispatcher-governance.test.mjs scripts/daemon/inbox-deferral.test.mjs scripts/daemon/lib/session-router.test.mjs scripts/daemon/prompt-builder.test.mjs scripts/daemon/typing-registry.test.mjs scripts/hooks/pre-send-audit.test.mjs scripts/local-triggers/generate-plists.test.mjs scripts/poller/inbox-scan-poller.test.mjs scripts/poller/lib/cloud-relay-dedup.test.mjs scripts/poller/slack-socket-mode.test.mjs scripts/setup/gen-subagent-manifest.test.mjs scripts/setup/init-agent.test.mjs services/webhook-relay/server.test.mjs lib/cadence-bus-requeue.test.mjs lib/cli/inbox.test.mjs lib/cli/session-ack.test.mjs lib/org/mesh-directives.test.mjs lib/session/frontdoor.test.mjs lib/session/handoffs.test.mjs lib/session/inbox-claims.test.mjs scripts/daemon/agent-daemon-frontdoor.test.mjs scripts/daemon/cadence-consumer-frontdoor.test.mjs scripts/daemon/dispatcher-resume.test.mjs scripts/daemon/inbox-deferral-session.test.mjs scripts/watchdog/memory-watchdog.test.mjs lib/upgrade/launchd-reconcile.test.mjs lib/upgrade/global-refresh.test.mjs lib/upgrade/verify.test.mjs lib/upgrade/post-steps.test.mjs lib/org/board-mine-cache.test.mjs scripts/daemon/agent-daemon-board-mine.test.mjs scripts/local-triggers/autoupdate.test.mjs scripts/daemon/sdk-version.test.mjs",
|
|
125
125
|
"test:setup": "node --test lib/setup/io.test.mjs lib/setup/state.test.mjs lib/setup/sot.test.mjs lib/setup/completeness.test.mjs lib/setup/context-pack.test.mjs lib/setup/enrich.test.mjs lib/setup/runner.test.mjs lib/setup/integration.test.mjs lib/setup/sections/subagents.test.mjs",
|
|
126
126
|
"check": "node scripts/ci/check.mjs",
|
|
127
127
|
"conformance:org": "node scripts/ci/conformance-org-api.mjs",
|
|
@@ -136,7 +136,7 @@
|
|
|
136
136
|
"test:org": "node --test $(find lib/org -name '*.test.mjs')",
|
|
137
137
|
"test:pack": "node scripts/ci/check-tarball-fidelity.mjs",
|
|
138
138
|
"test:plists": "node --test scripts/local-triggers/generate-plists.test.mjs",
|
|
139
|
-
"prepublishOnly": "
|
|
139
|
+
"prepublishOnly": "npm run check && npm test && npm run test:pack"
|
|
140
140
|
},
|
|
141
141
|
"engines": {
|
|
142
142
|
"node": ">=20.0.0"
|