@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,313 @@
|
|
|
1
|
+
# The front-door session — operator guide
|
|
2
|
+
|
|
3
|
+
Every seat runs two launchd jobs and one brain. This page is for the person
|
|
4
|
+
who has to look after a seat: what is running, how to look at it, how to stop
|
|
5
|
+
and restart it, and how to read `maestro session status` and `maestro doctor`.
|
|
6
|
+
The design is `docs/superpowers/specs/2026-09-08-front-door-session-design.md`;
|
|
7
|
+
this is the operator's view of §3 of it.
|
|
8
|
+
|
|
9
|
+
## 1. What runs on a seat
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
launchd (gui/<uid>)
|
|
13
|
+
├── ai.maestro.<first>-daemon KeepAlive scripts/daemon/maestro-daemon.mjs ← plumbing
|
|
14
|
+
│ the one SSE stream to os.cohortapp.com · presence beat every 30 s ·
|
|
15
|
+
│ Slack/Telegram/WhatsApp/orgmail adapters · classify + hydrate inbound
|
|
16
|
+
│ → state/inbox/cohort/*.yaml · cadence bus
|
|
17
|
+
└── ai.maestro.<first>-session KeepAlive scripts/session/supervisor.sh ← brain
|
|
18
|
+
screen -D -m -S maestro-<first> claude --name <first>-main --resume <id> …
|
|
19
|
+
└── a persistent monitor inside the session: scripts/session/feed.mjs
|
|
20
|
+
heartbeat every 15 s · tails the inbox + handoffs
|
|
21
|
+
→ one JSON line per event → the session acts
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
**The daemon** is the plumbing and is unchanged in what it holds: the single
|
|
25
|
+
allowed SSE connection per seat with its durable cursor, the presence beat,
|
|
26
|
+
the channel adapters, the directedness/self-echo/dedupe pipeline. It never
|
|
27
|
+
reasons.
|
|
28
|
+
|
|
29
|
+
**The session** is the agent's front door. It is a normal interactive Claude
|
|
30
|
+
Code session (`claude --name <first>-main`) kept alive inside a terminal
|
|
31
|
+
multiplexer (`screen` ships with macOS; `tmux` is preferred when installed).
|
|
32
|
+
Every inbound Cohort event — DMs, space messages, threads, inbound email,
|
|
33
|
+
comments on files and boards, huddle calls — reaches it as a JSON line from
|
|
34
|
+
the feed, and it either answers in the same turn, runs a dynamic workflow, or
|
|
35
|
+
hands the heavy work to a peer session it spawns. Meaty asks go onto the
|
|
36
|
+
relevant Board; the session works its board items proactively when idle.
|
|
37
|
+
|
|
38
|
+
**Who receives inbound.** While the session's heartbeat
|
|
39
|
+
(`state/session/heartbeat.json`, written every 15 s) is fresher than 90 s, the
|
|
40
|
+
daemon leaves Cohort inbox items in place for the session to claim and writes
|
|
41
|
+
escalate/guarded cadence ticks as handoffs (`state/session/handoffs/`). When the
|
|
42
|
+
heartbeat is stale, the daemon falls back to today's `claude --print` dispatch
|
|
43
|
+
for both. Liveness is measured, never assumed, so nothing is dropped in either
|
|
44
|
+
direction. The beat reports which lane is active: `machine.frontDoor`
|
|
45
|
+
(`"session"` when the job exists, `"daemon"` otherwise) and
|
|
46
|
+
`machine.sessionLive`. They ride `machine` — the beat field hq stores as an
|
|
47
|
+
open record — rather than `session`, which hq validates strictly and which
|
|
48
|
+
stays `null` while no work session runs.
|
|
49
|
+
|
|
50
|
+
**Exactly one.** The supervisor takes an O_EXCL lock (`lib/singleton.js`,
|
|
51
|
+
name `session`); a second supervisor exits 0 so launchd does not thrash. An
|
|
52
|
+
orphaned mux session with no lock holder is adopted, not duplicated.
|
|
53
|
+
|
|
54
|
+
**Continuity.** The session id is stable (`state/session/main-session.json`);
|
|
55
|
+
every relaunch is `claude --resume <id>`, so the session keeps its memory
|
|
56
|
+
across restarts and reboots. If a resume fails within 20 s the supervisor
|
|
57
|
+
rotates to a fresh id (at most three times an hour, then it sleeps).
|
|
58
|
+
|
|
59
|
+
**Relaunch only on death.** The supervisor runs the multiplexer in the
|
|
60
|
+
foreground and exits 75 when the session ends; launchd's
|
|
61
|
+
`KeepAlive {SuccessfulExit:false}` + `ThrottleInterval 30` bring it back.
|
|
62
|
+
There is no cron, no periodic kill, no memory watchdog SIGTERM for the main
|
|
63
|
+
session (`memory-watchdog.sh` spares `<first>-main` and the feed).
|
|
64
|
+
|
|
65
|
+
**Auth.** The supervisor sources `.env` the same way `launchd-wrapper.sh`
|
|
66
|
+
does, so the seat's `CLAUDE_CODE_OAUTH_TOKEN` (from `claude setup-token`)
|
|
67
|
+
reaches `claude`. The keychain login is not relied on.
|
|
68
|
+
|
|
69
|
+
## 2. Install and first start
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
cd ~/<agent-name>
|
|
73
|
+
maestro session install # renders + bootstraps ai.maestro.<first>-session
|
|
74
|
+
maestro session status # expect: lock held · mux maestro-<first> · heartbeat N s ago (live)
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
`init-agent.sh` and `generate-plists.sh` render the same plist; `install`
|
|
78
|
+
is the idempotent front door for it. It needs the SDK installed **globally**
|
|
79
|
+
(`npm i -g @cohortapp/agent-sdk`) because the session, like any Claude Code
|
|
80
|
+
session on the seat, resolves `maestro` and `cohort-mcp` from PATH — `maestro
|
|
81
|
+
doctor` checks both.
|
|
82
|
+
|
|
83
|
+
## 3. How to attach
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
maestro session attach # opens the live session in this terminal
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Detach with `Ctrl-A D` (screen) or `Ctrl-B D` (tmux). Detaching does not stop
|
|
90
|
+
anything. You are looking at the agent's own working session: type into it
|
|
91
|
+
the way you would into any Claude Code session, but remember that whatever it
|
|
92
|
+
sends outbound goes through the send-gate and the persona audit like any
|
|
93
|
+
other turn. Over SSH this is the same command; Tailscale SSH
|
|
94
|
+
(`scripts/setup/configure-macos.sh --tailscale-ssh`) is how an operator
|
|
95
|
+
reaches a seat.
|
|
96
|
+
|
|
97
|
+
## 4. How to stop, start and restart
|
|
98
|
+
|
|
99
|
+
| You want | Run | What happens |
|
|
100
|
+
| --- | --- | --- |
|
|
101
|
+
| Restart on the current code (or after an upgrade) | `maestro session restart` | writes `state/session/restart-requested`; the session finishes its turn, exits the mux, the supervisor exits 75, launchd relaunches it with `--resume` |
|
|
102
|
+
| Stop it and keep it stopped | `maestro session stop` | `launchctl bootout` of the session job; the daemon's `--print` lane takes over inbound within 90 s |
|
|
103
|
+
| Start it again | `maestro session start` | `launchctl bootstrap` of the job |
|
|
104
|
+
| Stop the whole seat | `touch .emergency-stop` | daemon exits clean and stays down; stop the session separately with `session stop` |
|
|
105
|
+
|
|
106
|
+
Never `launchctl kickstart -k` the session job from a script or a cadence:
|
|
107
|
+
it is long-lived interactive state. The hourly autoupdate job knows this — it
|
|
108
|
+
restarts the daemon and health-gates it, but for the session it only writes
|
|
109
|
+
`state/session/upgrade-notice.json`; the session restarts itself at an idle
|
|
110
|
+
moment via `maestro session restart` and comes back on the new code.
|
|
111
|
+
|
|
112
|
+
One consequence of `maestro upgrade` reconciling launchd (§4a): every
|
|
113
|
+
generated job that is installed but **not loaded** is bootstrapped again on
|
|
114
|
+
the next upgrade, the session job included. `maestro session stop` therefore
|
|
115
|
+
holds until the next hour's autoupdate lands a new version; to keep a seat's
|
|
116
|
+
session down for longer, use the autoupdate kill-switch
|
|
117
|
+
(`touch ~/.maestro-no-autoupdate`) or remove the session plist. The
|
|
118
|
+
kill-switch and the emergency stop (`.emergency-stop`, `state/EMERGENCY_STOP`)
|
|
119
|
+
also **hold** the launchd step of a by-hand `maestro upgrade`: plists are
|
|
120
|
+
still written, nothing is bootstrapped, and each held row says which marker
|
|
121
|
+
held it. `scripts/session/supervisor.sh` refuses to start under an emergency
|
|
122
|
+
stop as well (exit 0, so launchd does not relaunch it), which covers the one
|
|
123
|
+
path the hold cannot — the next login re-bootstrapping `~/Library/LaunchAgents`.
|
|
124
|
+
|
|
125
|
+
## 4a. How a seat upgrades itself
|
|
126
|
+
|
|
127
|
+
The hourly `ai.maestro.<first>-autoupdate` job
|
|
128
|
+
(`scripts/local-triggers/autoupdate.sh`) installs a strictly-newer SDK into
|
|
129
|
+
the agent dir and runs **that package's** `maestro upgrade`. So on the first
|
|
130
|
+
hop from any older version it is the *new* `upgrade` that runs, and it puts
|
|
131
|
+
the seat on the new architecture by itself — the "seat reconcile" steps at
|
|
132
|
+
the end of every non-dry upgrade, each fail-open and each reported in the
|
|
133
|
+
summary and in `.maestro/upgrade-result.json` `{from, to, at, steps}`:
|
|
134
|
+
|
|
135
|
+
| Step | What it does |
|
|
136
|
+
| --- | --- |
|
|
137
|
+
| `plists` | `scripts/local-triggers/generate-plists.sh`, unconditionally |
|
|
138
|
+
| `launchd` | installs every generated plist missing from `~/Library/LaunchAgents` (mode 600, `launchctl bootstrap gui/<uid>`; `launchctl load` only when launchd does not already hold the label) — this is how the session job reaches an existing seat; **rewrites** changed ones (file only — launchd keeps the loaded definition until the next login or an explicit `bootout` + `bootstrap`; nothing is ever `kickstart`ed, least of all the `-autoupdate` job this upgrade runs inside); bootstraps installed-but-unloaded ones; never `bootout`s anything, orphans are only reported. "Loaded" is measured in the GUI domain (`launchctl print gui/<uid>`), so the answer is right over ssh too. Held entirely (files written, nothing started) under an emergency stop or the autoupdate kill-switch |
|
|
139
|
+
| `globalSetup` | `maestro global-setup` — identity block, cohort MCP server, session settings, global skills |
|
|
140
|
+
| `globalInstall` | `npm i -g @cohortapp/agent-sdk@<this version>` so `maestro` / `cohort` / `cohort-mcp` on PATH match the agent dir (`MAESTRO_SKIP_GLOBAL_INSTALL=1` skips) |
|
|
141
|
+
| `verify` | the `maestro upgrade --verify` report, below |
|
|
142
|
+
|
|
143
|
+
autoupdate then kickstarts the daemon and health-gates it: the daemon
|
|
144
|
+
process must be alive with a fresh `org-mesh connected` / `[daemon] Running`
|
|
145
|
+
line, and — when a `-session` plist was generated — the session label must be
|
|
146
|
+
in `launchctl list` with a live supervisor process. A missing session is
|
|
147
|
+
logged as `reconcile-failed` and does **not** roll back (the daemon's
|
|
148
|
+
`--print` lane is the front door meanwhile; doctor says what to do). Every
|
|
149
|
+
launchd and `pgrep` question is scoped to *this* agent (`ai.maestro.<first>-*`
|
|
150
|
+
and its own agent dir), so two seats on one Mac never gate on — or restart —
|
|
151
|
+
each other.
|
|
152
|
+
|
|
153
|
+
An unhealthy daemon **rolls back**: the previous SDK is reinstalled into the
|
|
154
|
+
agent dir and *that* package's `upgrade` runs, the global install is put back
|
|
155
|
+
to the previous version, and a `-session` job that **this run** installed is
|
|
156
|
+
booted out and its plist removed (a pre-existing session job is the
|
|
157
|
+
operator's and is never touched). What a rollback does *not* undo: files the
|
|
158
|
+
new version added that the old manifest never listed stay on disk, and the
|
|
159
|
+
old `upgrade` rewrites the manifest without `sdkVersion` — `maestro doctor` /
|
|
160
|
+
`upgrade --verify` report both on the next healthy hop. A version that failed
|
|
161
|
+
the health gate here is then **held**: `state/autoupdate/last.json`
|
|
162
|
+
(`{from, to, at, ok, healthy, reason}`) is read at the top of every run and a
|
|
163
|
+
`to` it records as `unhealthy-rolled-back` / `rollback-unhealthy` is not
|
|
164
|
+
retried for 24 h (`MAESTRO_AUTOUPDATE_FAILED_HOLD_S`; delete the file to retry
|
|
165
|
+
now) — without that memory a bad release would install, reconcile, roll back
|
|
166
|
+
and refresh the global install twice an hour until someone touched the
|
|
167
|
+
kill-switch. The beat reports `last.json` as `machine.upgrade` next to
|
|
168
|
+
`sdkVersion` in the Fleet view, so a held seat shows its failure and its age.
|
|
169
|
+
Two runs cannot overlap (`state/locks/autoupdate/`, stale after two hours),
|
|
170
|
+
and `npm view` / `npm install` are retried three times with backoff.
|
|
171
|
+
|
|
172
|
+
`maestro upgrade --verify` (also printed by `maestro doctor`) answers "is
|
|
173
|
+
this seat wholly on one version?": the global bin, the agent dir's package,
|
|
174
|
+
`.maestro/shipped-manifest.json`, the running daemon
|
|
175
|
+
(`state/dashboards/daemon-health.yaml` `sdk_version` — the version of the
|
|
176
|
+
*copied* `scripts/daemon` the process started on, read from the manifest
|
|
177
|
+
before `node_modules`, so a daemon restarted between autoupdate's `npm
|
|
178
|
+
install` and the file merge does not report the version it is not yet
|
|
179
|
+
running), every generated plist installed **and** loaded (in the GUI domain),
|
|
180
|
+
and the global-setup markers. It exits 1 on any mismatch and names the fix
|
|
181
|
+
per row.
|
|
182
|
+
|
|
183
|
+
## 5. How to read status
|
|
184
|
+
|
|
185
|
+
`maestro session status` prints:
|
|
186
|
+
|
|
187
|
+
- **lock** — who holds `state/session.lock` (pid, identity) or "free";
|
|
188
|
+
- **mux** — `maestro-<first>` present or not, which multiplexer;
|
|
189
|
+
- **session id** and how many times it has been resumed;
|
|
190
|
+
- **heartbeat** — age in seconds and `live` / `stale` against the 90 s bar;
|
|
191
|
+
- **open handoffs** — cadence ticks waiting in `state/session/handoffs/`;
|
|
192
|
+
- **peers** — sessions it has spawned (`maestro session peers` lists them
|
|
193
|
+
and prunes dead ones).
|
|
194
|
+
|
|
195
|
+
`--brief` gives the one-liner the SessionStart hook shows every new session.
|
|
196
|
+
|
|
197
|
+
`maestro doctor` adds the fleet view of the same facts and a remedy per line:
|
|
198
|
+
the Claude auth mode and what `claude auth status` reports (the CLI reports
|
|
199
|
+
that a credential is present — it does not validate a token; a bad token
|
|
200
|
+
shows up on the first real spawn, in `logs/sessions` and on the beat as
|
|
201
|
+
`relogin_required`); where each Cohort value
|
|
202
|
+
came from (`config/org.yaml` beats the environment beats `.env`) and which
|
|
203
|
+
values lost; whether `COHORT_ORG_ID` is the org's ID or its slug; the global
|
|
204
|
+
SDK install and whether it is current; Tailscale SSH; and the session job —
|
|
205
|
+
installed / loaded / heartbeat live. The rungs read, in order:
|
|
206
|
+
|
|
207
|
+
```
|
|
208
|
+
✓ Claude auth: subscription OAuth token (CLAUDE_CODE_OAUTH_TOKEN + MAESTRO_PREFER_SUBSCRIPTION_AUTH=1)
|
|
209
|
+
✓ Claude auth (subscription — OAuth token): CLI reports a login for the token; the token itself is only proven by a real spawn (a rejection shows in logs/sessions and on the beat as relogin_required)
|
|
210
|
+
✓ Cohort base ← org.yaml (https://os.cohortapp.com)
|
|
211
|
+
✓ Cohort orgId ← org.yaml (org_default_adaptic)
|
|
212
|
+
⚠ Cohort orgId: org.yaml wins over .env COHORT_ORG_ID=adaptic — the losing value is ignored
|
|
213
|
+
✓ COHORT_ORG_ID is the org ID (org_default_adaptic) — matches the server
|
|
214
|
+
✓ @cohortapp/agent-sdk 2.12.0 installed globally (npm latest 2.12.0)
|
|
215
|
+
✓ Tailscale SSH is on (RunSSH=true)
|
|
216
|
+
✓ Main session job ai.maestro.ethan-session loaded, heartbeat live (12 s ago)
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
From the Fleet view in Cohort the same seat shows `hostname`, `tailnetIp`,
|
|
220
|
+
`sdkVersion`, `frontDoor` and `sessionLive` off its beat — `sdkVersion` per
|
|
221
|
+
seat is how a fleet-wide rollout is confirmed after a publish.
|
|
222
|
+
|
|
223
|
+
## 6. What the session does with an event
|
|
224
|
+
|
|
225
|
+
Each line from the feed is one unit of work. The policy the session follows
|
|
226
|
+
(skill `inbound-triage`):
|
|
227
|
+
|
|
228
|
+
1. **Answer in the turn** when the ask is answerable in one reply from what
|
|
229
|
+
the agent already knows. `maestro inbox reply <id>` routes through the same
|
|
230
|
+
send-gate and audit as every other outbound path.
|
|
231
|
+
2. Otherwise **acknowledge in-channel in the same turn**, file the ask on the
|
|
232
|
+
conversation's board (`maestro board track <inbox-id> --stage accepted
|
|
233
|
+
--title … --why …`), and either run the work as a dynamic workflow inside
|
|
234
|
+
the session or spawn a peer (`maestro session spawn --name <slug> "<prompt>"`).
|
|
235
|
+
Report back in-channel and `board track --stage done` on completion.
|
|
236
|
+
3. **Handoffs** (`{"type":"handoff", …}`) are cadence ticks the daemon has
|
|
237
|
+
handed over; the session runs the rendered prompt and `maestro session ack
|
|
238
|
+
<tickId>`. A handoff not acked within 30 minutes is re-enqueued by the
|
|
239
|
+
daemon on the legacy lane, so a wedged session cannot starve a cadence.
|
|
240
|
+
4. A claimed inbox item neither replied nor done within 20 minutes is
|
|
241
|
+
re-opened by the daemon's assurance sweep and re-emitted (or dispatched
|
|
242
|
+
legacy if the session is no longer live).
|
|
243
|
+
|
|
244
|
+
To the humans it talks to, the agent is one persona. Peer sessions and
|
|
245
|
+
sub-agents are "my team" / "a colleague"; the persona audit hook blocks any
|
|
246
|
+
outbound text that says otherwise.
|
|
247
|
+
|
|
248
|
+
## 6a. DESIGN.md and the parallelism directive
|
|
249
|
+
|
|
250
|
+
Two things reach every prompt a seat runs, and neither is left to a session to
|
|
251
|
+
remember.
|
|
252
|
+
|
|
253
|
+
**DESIGN.md and PRODUCT.md.** `maestro design sync` reads the workspace's SAVED
|
|
254
|
+
brand foundation (`branding.getFoundation` — never a draft) and renders it into
|
|
255
|
+
`DESIGN.md` (colour, type, radius, spacing and component tokens, in the
|
|
256
|
+
google-labs-code/design.md frontmatter shape, with `{colors.primary}`-style
|
|
257
|
+
refs) and `PRODUCT.md` (positioning, voice, tone, lexicon) in the agent dir,
|
|
258
|
+
plus the raw snapshot at `state/design/foundation.json`. The daemon re-runs the
|
|
259
|
+
same code path every 15 minutes and rewrites the files when the foundation's
|
|
260
|
+
version has moved, so a seat's design files follow the humans editing the
|
|
261
|
+
foundation without anyone re-running a command.
|
|
262
|
+
|
|
263
|
+
Both files are GENERATED. An edit to them is overwritten on the next sync;
|
|
264
|
+
foundation changes are human-gated and go through the `design_propose_change`
|
|
265
|
+
tool. The write is byte-idempotent — an unchanged foundation touches no mtime,
|
|
266
|
+
so the poll never churns the tree, and the render stamp is kept per output
|
|
267
|
+
directory so a `--out` run and the daemon's poll of the agent dir cannot
|
|
268
|
+
invalidate each other's files. Every posture here is fail-open: an un-enrolled
|
|
269
|
+
seat writes nothing, and a failed read keeps the last-good files rather than
|
|
270
|
+
blanking them. Design skills are told to read `$AGENT_ROOT/DESIGN.md` when the
|
|
271
|
+
working directory is not the agent dir.
|
|
272
|
+
|
|
273
|
+
A workspace that has never SAVED a foundation writes nothing at all, and the
|
|
274
|
+
CLI says why. hq answers such a read with its own product defaults and
|
|
275
|
+
`unversioned: true` — a complete, plausible palette that is nobody's brand — and
|
|
276
|
+
a DESIGN.md rendered from it would ground every design skill on the seat in the
|
|
277
|
+
wrong palette with a generated file's authority behind it.
|
|
278
|
+
|
|
279
|
+
maestro design sync # into the agent dir
|
|
280
|
+
maestro design sync --out docs/brand --json
|
|
281
|
+
|
|
282
|
+
**The parallelism directive.** `lib/prompts/parallelism.mjs` holds one constant,
|
|
283
|
+
prepended exactly once (it is idempotent, and the seams compose) to every
|
|
284
|
+
peer-session prompt by `maestro session spawn`, to every sub-session prompt
|
|
285
|
+
built by the daemon's prompt-builder, and to every cadence trigger prompt on the
|
|
286
|
+
escalate/guarded lane — both when a sub-session is spawned for it and when a
|
|
287
|
+
live main session is handed it (`renderCadencePromptBody`, one seam for both, so
|
|
288
|
+
the two lanes send the same bytes). It grants the standing permission to
|
|
289
|
+
dispatch independent tasks as one parallel batch, and carries the safety rule
|
|
290
|
+
that makes that safe on a shared checkout: check the planned file scope first,
|
|
291
|
+
and never assign two agents to edit the same file — sequence or re-scope
|
|
292
|
+
instead. Sessions ran plans strictly serially before it existed, because
|
|
293
|
+
nothing in the prompt had ever said they need not.
|
|
294
|
+
|
|
295
|
+
## 7. Files
|
|
296
|
+
|
|
297
|
+
| Path | What |
|
|
298
|
+
| --- | --- |
|
|
299
|
+
| `state/session/heartbeat.json` | `{pid, ppid, sessionId, name, ts, feedVersion}`, every 15 s |
|
|
300
|
+
| `state/session/main-session.json` | `{sessionId, createdAt, resumes}` — the stable id |
|
|
301
|
+
| `state/session/handoffs/<tickId>.json` | cadence ticks waiting for the session; `done/` when acked |
|
|
302
|
+
| `state/session/peers.json` | spawned peer sessions |
|
|
303
|
+
| `state/session/feed-seen.json` | the feed's seen-set (no replay after a monitor restart) |
|
|
304
|
+
| `state/session/restart-requested` | `maestro session restart` marker |
|
|
305
|
+
| `state/session/upgrade-notice.json` | written by autoupdate; the session restarts itself when idle |
|
|
306
|
+
| `state/session.lock` | the singleton lock |
|
|
307
|
+
| `state/inbox/cohort/*.yaml` | inbound items (`.dispatched` = claimed, `.processed` = done, `.deferred`) |
|
|
308
|
+
| `DESIGN.md`, `PRODUCT.md` | generated from the workspace brand foundation by `maestro design sync` and the daemon's 15-minute poll; edits are overwritten |
|
|
309
|
+
| `state/design/foundation.json` | `{fetchedAt, renderedAt, version, versionLabel, foundation}` — the last synced foundation; the refresh gate reads it back |
|
|
310
|
+
| `state/org/board-mine.json` | `{ts, items}` — the daemon's 5-minute cache of hq `board.mine` (this agent's items across every board); read by the SessionStart primer, the `session_status` MCP tool and `maestro board mine` |
|
|
311
|
+
| `.maestro/upgrade-result.json` | `{from, to, at, steps:{plists, launchd, globalSetup, globalInstall, verify}}` — what the last `maestro upgrade` did to the seat |
|
|
312
|
+
| `state/autoupdate/last.json` | `{from, to, at, ok, healthy, reason}` — the last autoupdate attempt; the beat's `machine.upgrade` |
|
|
313
|
+
| `state/locks/autoupdate/` | autoupdate's overlap lock (pid inside; stale after 2 h) |
|
package/docs/guides/mac-mini.md
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
# Mac mini bring-up — an always-on Cohort agent, end to end
|
|
2
2
|
|
|
3
3
|
The one-page operator runbook: from a boxed Apple-silicon Mac mini to a fully
|
|
4
|
-
enrolled AI colleague on `os.cohortapp.com` — messaging, board work,
|
|
5
|
-
workspace email address
|
|
6
|
-
fleet mechanics live in the
|
|
7
|
-
|
|
4
|
+
enrolled AI colleague on `os.cohortapp.com` — messaging, board work, a real
|
|
5
|
+
workspace email address and an always-on **main session** as its front door —
|
|
6
|
+
verified by `cohort doctor`. OS-level hardening and fleet mechanics live in the
|
|
7
|
+
deeper runbook: [Mac Mini Bootstrap](../runbooks/mac-mini-bootstrap.md); the
|
|
8
|
+
session itself is described in [Front-door Session](front-door-session.md)
|
|
9
|
+
(this page links both rather than duplicating them).
|
|
10
|
+
|
|
11
|
+
The sequence, in one line: **token → create → setup → init-agent →
|
|
12
|
+
session install → tailscale-ssh → doctor.**
|
|
8
13
|
|
|
9
14
|
## 1. Hardware / prereqs
|
|
10
15
|
|
|
@@ -22,13 +27,27 @@ sudo pmset -a sleep 0 displaysleep 0 autorestart 1
|
|
|
22
27
|
xcode-select --install
|
|
23
28
|
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
|
|
24
29
|
brew install node@20 git python@3.12
|
|
25
|
-
npm i -g @anthropic-ai/claude-code
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
npm i -g @cohortapp/agent-sdk
|
|
30
|
+
npm i -g @anthropic-ai/claude-code
|
|
31
|
+
npm i -g @cohortapp/agent-sdk # GLOBAL on purpose: every Claude Code session on
|
|
32
|
+
# this seat needs `maestro` + `cohort-mcp` on PATH
|
|
29
33
|
```
|
|
30
34
|
|
|
31
|
-
## 3.
|
|
35
|
+
## 3. The Claude token — one auth story for the whole seat
|
|
36
|
+
|
|
37
|
+
Every `claude` the seat runs (the daemon's `--print` lane, the main session,
|
|
38
|
+
cadence sub-sessions) authenticates with a **long-lived subscription OAuth
|
|
39
|
+
token**, never the interactive keychain login (which expires headlessly and
|
|
40
|
+
has stranded seats before). Mint it on **any** machine where you are logged
|
|
41
|
+
in to Claude Code with the Max subscription — your laptop is fine:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
claude setup-token # prints one token; copy it
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Keep it in your clipboard / password manager for step 5. It is per seat: mint
|
|
48
|
+
a fresh one whenever `cohort doctor` reports the token as rejected.
|
|
49
|
+
|
|
50
|
+
## 4. Create the agent repo
|
|
32
51
|
|
|
33
52
|
```bash
|
|
34
53
|
cohort create <agent-name>
|
|
@@ -40,7 +59,7 @@ including `.mcp.json`, which exposes the `cohort-mcp` org tool surface to
|
|
|
40
59
|
interactive Claude sessions, and the PreToolUse hooks that keep its outbound
|
|
41
60
|
writes on the CLI lane).
|
|
42
61
|
|
|
43
|
-
##
|
|
62
|
+
## 5. Enroll against `https://os.cohortapp.com` — and paste the token
|
|
44
63
|
|
|
45
64
|
Two lanes — pick one:
|
|
46
65
|
|
|
@@ -49,9 +68,14 @@ Two lanes — pick one:
|
|
|
49
68
|
|
|
50
69
|
```bash
|
|
51
70
|
export COHORT_API_KEY=nlk_… COHORT_ORG_ID=<org-ID> COHORT_AGENT_ID=<member-slug>
|
|
52
|
-
cohort setup
|
|
71
|
+
cohort setup # the model section (order 30) asks for the token from step 3
|
|
72
|
+
# headless / one-paste bootstrap: the token comes from the environment instead of a prompt
|
|
73
|
+
CLAUDE_CODE_OAUTH_TOKEN=<token> cohort setup --headless
|
|
53
74
|
```
|
|
54
75
|
|
|
76
|
+
Either way `.env` ends up with `CLAUDE_CODE_OAUTH_TOKEN` +
|
|
77
|
+
`MAESTRO_PREFER_SUBSCRIPTION_AUTH=1`, mode 600. No `ANTHROPIC_API_KEY`.
|
|
78
|
+
|
|
55
79
|
> **`COHORT_ORG_ID` is the org's ID, not its slug** — this line said
|
|
56
80
|
> `<org-slug>` and that would 401 every call. The value rides as `x-org-id`
|
|
57
81
|
> (`lib/org/client.mjs#baseHeaders`) and hq compares it with strict equality
|
|
@@ -68,12 +92,13 @@ Two lanes — pick one:
|
|
|
68
92
|
- **Pre-auth pairing**: `cohort pair <code>` → an org admin approves the
|
|
69
93
|
handshake in Cohort (`pairing.request`/`pairing.approve`).
|
|
70
94
|
|
|
71
|
-
##
|
|
95
|
+
## 6. Wizard
|
|
72
96
|
|
|
73
|
-
`cohort setup` walks the sections in order — the org trio
|
|
97
|
+
`cohort setup` walks the sections in order — the auth section and the org trio are:
|
|
74
98
|
|
|
75
99
|
| Order | Section | What it does |
|
|
76
100
|
| --- | --- | --- |
|
|
101
|
+
| 30 | `model` | Claude auth — **`oauth-token` by default**: paste the `claude setup-token` output; writes `CLAUDE_CODE_OAUTH_TOKEN` + `MAESTRO_PREFER_SUBSCRIPTION_AUTH=1`, chmods `.env` 600 |
|
|
77
102
|
| 75 | `org` | endpoint + token (the enrollment SoT) |
|
|
78
103
|
| 76 | `messaging` | messaging.read/write + calling.write scopes, home channels |
|
|
79
104
|
| 77 | `orgmail` | **workspace mailbox** — writes `config/orgmail.yaml` |
|
|
@@ -82,28 +107,65 @@ For 77 to go live, an admin must have verified a domain and assigned this
|
|
|
82
107
|
agent a mailbox in Cohort → Settings → Email first (the wizard's verify step
|
|
83
108
|
probes `email.inbox` and WARNS until then — it never blocks setup).
|
|
84
109
|
|
|
85
|
-
##
|
|
110
|
+
## 7. Always-on (launchd): the daemon, then the main session
|
|
86
111
|
|
|
87
112
|
```bash
|
|
88
|
-
scripts/setup/init-agent.sh # deps → state dirs → generate-plists.sh → launchctl
|
|
113
|
+
scripts/setup/init-agent.sh # deps → state dirs → generate-plists.sh → launchctl bootstrap
|
|
114
|
+
maestro session install # the front-door main session job (KeepAlive)
|
|
115
|
+
maestro session status # lock holder, mux name, heartbeat age — expect "live"
|
|
89
116
|
launchctl list | grep ai.maestro
|
|
90
117
|
```
|
|
91
118
|
|
|
92
|
-
Expect the daemon
|
|
93
|
-
|
|
94
|
-
|
|
119
|
+
Expect the daemon, the **session**, the cadence triggers and the hourly
|
|
120
|
+
autoupdate job. The daemon plist is KeepAlive `{SuccessfulExit:false,
|
|
121
|
+
Crashed:true}`; the session plist is KeepAlive `{SuccessfulExit:false}` with a
|
|
122
|
+
30 s throttle — it relaunches only on death, never on a schedule. Once the
|
|
123
|
+
session's heartbeat is live the daemon stops spawning `--print` sessions for
|
|
124
|
+
Cohort inbox items and hands them to the session instead; if the heartbeat
|
|
125
|
+
goes stale (> 90 s) the daemon falls back to its own lane, so nothing is ever
|
|
126
|
+
dropped. `maestro session attach` opens the live session in your terminal
|
|
127
|
+
(`Ctrl-A D` / `Ctrl-B D` detaches); see [Front-door Session](front-door-session.md).
|
|
128
|
+
|
|
129
|
+
## 8. Remote operator access — Tailscale SSH
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
scripts/setup/configure-macos.sh --tailscale-ssh # tailscale set --ssh + Remote Login on
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
This is what makes every later step remote (`ssh <agent-user>@<tailnet-host>`
|
|
136
|
+
from an operator machine, no `authorized_keys` to manage). Allow it in the
|
|
137
|
+
tailnet ACL once for the fleet; `cohort doctor` warns while it is off.
|
|
95
138
|
|
|
96
|
-
##
|
|
139
|
+
## 9. Verify — `cohort doctor` is the gate
|
|
97
140
|
|
|
98
141
|
```bash
|
|
99
142
|
cohort doctor
|
|
100
143
|
```
|
|
101
144
|
|
|
102
|
-
All green includes
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
145
|
+
All green includes, in this order:
|
|
146
|
+
|
|
147
|
+
- **Claude auth** — the mode in effect (`subscription OAuth token` is the
|
|
148
|
+
expected line) and what `claude auth status` reports. The CLI reports that
|
|
149
|
+
a credential is PRESENT — it answers "logged in" for any token value — so a
|
|
150
|
+
bad token is only caught by the first real spawn (`logs/sessions`, and the
|
|
151
|
+
beat flips to `relogin_required`); a `claude -p "ping"` after doctor is the
|
|
152
|
+
cheap way to prove it. "keychain login"
|
|
153
|
+
or "nothing configured" means step 3/5 was skipped.
|
|
154
|
+
- **Cohort sources** — for each of `base` / `orgId` / `token` / `agentId`,
|
|
155
|
+
which file won (`config/org.yaml` beats the environment beats `.env`) and
|
|
156
|
+
every value that lost. If you edited `.env` and nothing changed, this line
|
|
157
|
+
is why.
|
|
158
|
+
- **`COHORT_ORG_ID`** checked against the server: "you set the slug" names the
|
|
159
|
+
real ID to use.
|
|
160
|
+
- **Global SDK install** — `@cohortapp/agent-sdk` present globally and current
|
|
161
|
+
against npm; `maestro` / `cohort-mcp` resolve on PATH.
|
|
162
|
+
- **Tailscale SSH** on (step 8).
|
|
163
|
+
- **Main session job** installed, loaded, heartbeat live (step 7).
|
|
164
|
+
- The **Cohort connectivity** block: enrollment resolved → directory reachable
|
|
165
|
+
(latency) → protocol version aligned (the server's `x-org-protocol` header;
|
|
166
|
+
a drift warning means run `cohort upgrade`) → **key paired to a workforce
|
|
167
|
+
member** (the `messaging.channels` gate) → **workspace mailbox reachable**
|
|
168
|
+
(when `config/orgmail.yaml` exists).
|
|
107
169
|
|
|
108
170
|
Then two live probes:
|
|
109
171
|
|
|
@@ -115,15 +177,25 @@ Then two live probes:
|
|
|
115
177
|
tick ≤60s). First-contact recipients park as approvals in /decisions — by
|
|
116
178
|
design.
|
|
117
179
|
|
|
118
|
-
##
|
|
180
|
+
## 10. Operations
|
|
119
181
|
|
|
120
182
|
- **Kill switch**: touch `.emergency-stop` in the repo root (human-only). The
|
|
121
183
|
daemon exits clean, so launchd stops the restart treadmill. Resume with
|
|
122
|
-
`scripts/resume-operations.sh`.
|
|
184
|
+
`scripts/resume-operations.sh`. The main session is stopped separately with
|
|
185
|
+
`maestro session stop` (and comes back with `maestro session start`).
|
|
123
186
|
- **Email-only kill switch**: delete/rename `config/orgmail.yaml` and restart —
|
|
124
187
|
the gate-file loop skips the platform; server-side an admin can flip the
|
|
125
188
|
mailbox to Disabled in Settings → Email.
|
|
126
|
-
- **Upgrade**:
|
|
127
|
-
|
|
189
|
+
- **Upgrade**: automatic — the hourly `ai.maestro.<first>-autoupdate` job installs
|
|
190
|
+
a newer SDK, runs `cohort upgrade` (which also regenerates and installs the
|
|
191
|
+
launchd jobs, re-runs `global-setup` and refreshes the global npm install —
|
|
192
|
+
`docs/guides/front-door-session.md` §4a), restarts the daemon and
|
|
193
|
+
health-gates it. It does **not** restart the main session; it writes
|
|
194
|
+
`state/session/upgrade-notice.json` and the session restarts itself at an
|
|
195
|
+
idle moment. By hand: `npm i -g @cohortapp/agent-sdk@latest && cohort upgrade`,
|
|
196
|
+
then `maestro session restart`; `maestro upgrade --verify` confirms the seat
|
|
197
|
+
is wholly on one version. Verified fleet-wide by `sdkVersion` and
|
|
198
|
+
`machine.upgrade` on each seat's beat (the Fleet view). A doctor "protocol
|
|
199
|
+
drift" warning is the cue.
|
|
128
200
|
- **Health**: `scripts/healthcheck.sh`; logs under `logs/` (`logs/audit/` carries
|
|
129
201
|
the per-action JSONL rows from BOTH the native executor and the MCP plane).
|
|
@@ -71,7 +71,7 @@ context from Cohort and shape the agent's config from it.
|
|
|
71
71
|
| Var | Required | Purpose |
|
|
72
72
|
| --- | --- | --- |
|
|
73
73
|
| `COHORT_API_KEY` | yes | Bearer OrgApiKey for the org-data DTO reads AND the `/v1` read/RPC surface. hq's documented agent var. (`COHORT_API_TOKEN` / `COHORT_TOKEN` are accepted for back-compat.) |
|
|
74
|
-
| `COHORT_ORG_ID` | yes |
|
|
74
|
+
| `COHORT_ORG_ID` | yes | The org **ID** (`org_default_<slug>`), never the bare slug — hq compares the `x-org-id` pin with strict equality against the ID, so a slug 401s every `/v1` call (`maestro setup` warns, `maestro doctor` checks it against the server). Also sent as `?slug=` on legacy org-data reads. |
|
|
75
75
|
| `COHORT_AGENT_ID` | optional | This agent's member slug. When set it selects `members/<slug>` + keys relationships; when absent the pull resolves via `whoami`. |
|
|
76
76
|
| `COHORT_BASE` / `COHORT_API_URL` | optional | Server origin. Falls back to the persisted `config/org.yaml` endpoint. |
|
|
77
77
|
| `COHORT_AGENT_EMAIL` | optional | Email hint for `whoami` resolution when no `COHORT_AGENT_ID` is given. |
|
|
@@ -52,7 +52,7 @@ core runner.
|
|
|
52
52
|
|---------|-------|-------------|
|
|
53
53
|
| `identity` | 10 | Agent name and title, the **function × altitude** archetype, and the principal. Writes `config/agent.json`. |
|
|
54
54
|
| `company` | 20 | Company-context interview — name, website, industry, one-line + detailed overview, stage/size, footprint, regulation, top priorities, and key people. Writes `config/company.json`. |
|
|
55
|
-
| `model` | 30 |
|
|
55
|
+
| `model` | 30 | Claude auth. Three modes, **`oauth-token` the default**: run `claude setup-token` on any logged-in machine and paste the result (headless: `CLAUDE_CODE_OAUTH_TOKEN` in the environment) → `.env` gets `CLAUDE_CODE_OAUTH_TOKEN` + `MAESTRO_PREFER_SUBSCRIPTION_AUTH=1`; or this machine's keychain login; or a pasted Anthropic API key. Chmods `.env` 600. |
|
|
56
56
|
| `comms` | 45 | Wires each messaging channel — Slack, Gmail, SMS, WhatsApp, Telegram, voice — into `.env`/gate files and verifies inbound. |
|
|
57
57
|
| `tools` | 50 | Selects which channels and MCP servers the agent should run. |
|
|
58
58
|
| `operating-model` | 60 | Deterministically generates the operating charter, a seeded WBS backlog (≥5 open items), 40–60 sub-agents + skills + workflows + MCP servers + event-routing, archetype cadences, the autonomy policy, the communication profile, and the launchd plists. |
|
|
@@ -102,14 +102,17 @@ re-running `maestro setup` (or `maestro setup enrich`) without the flag.
|
|
|
102
102
|
## 5. The verify probe and capability table
|
|
103
103
|
|
|
104
104
|
The final `verify` section runs a live check and prints an `N/M` capability table.
|
|
105
|
-
It aggregates every other section's `verify()` plus
|
|
106
|
-
|
|
105
|
+
It aggregates every other section's `verify()` plus the Claude auth probe
|
|
106
|
+
(`claude auth status` in subscription mode — which reports that a credential
|
|
107
|
+
is present, not that it is valid; an API ping in api-key mode, which does
|
|
108
|
+
prove the key — the same probe `maestro doctor` and the fleet beat use) and
|
|
109
|
+
the completeness gate. Per enabled channel/MCP it runs the right liveness check
|
|
107
110
|
(`auth.test` for Slack, an IMAP login for Gmail, a Twilio lookup, a relay
|
|
108
111
|
`/health`, etc.), and prints a remedy line under any failure:
|
|
109
112
|
|
|
110
113
|
```
|
|
111
114
|
Capability summary — 7/9 checks passing
|
|
112
|
-
✓ Claude
|
|
115
|
+
✓ Claude auth (subscription — OAuth token)
|
|
113
116
|
✓ identity complete
|
|
114
117
|
✓ Slack auth.test
|
|
115
118
|
✗ Gmail IMAP login [comms]
|
|
@@ -118,7 +121,8 @@ Capability summary — 7/9 checks passing
|
|
|
118
121
|
```
|
|
119
122
|
|
|
120
123
|
Setup exits **non-zero only on hard failures** — no identity, an unresolvable
|
|
121
|
-
archetype, or
|
|
124
|
+
archetype, or Claude auth being rejected (a probe that cannot run — no CLI, no
|
|
125
|
+
network — is reported as "could not verify" and does not fail the run). Optional channels and best-effort
|
|
122
126
|
enrichment are reported but never fail the run. The same completeness gate backs
|
|
123
127
|
`maestro doctor`, so `doctor` and `setup --status` agree on what "done" means.
|
|
124
128
|
|
|
@@ -55,7 +55,17 @@ machine's `config/org.yaml` pins `server.url` (e.g.
|
|
|
55
55
|
service changes the `*.up.railway.app` host and strands the fleet. Move the
|
|
56
56
|
fleet to a stable custom domain first (§2), then rename freely.
|
|
57
57
|
|
|
58
|
-
>
|
|
58
|
+
> ✅ **RESOLVED 2026-09-08.** The working host is **`https://os.cohortapp.com`** —
|
|
59
|
+
> the org plane (`/api/v1/*`, the `/v1` RPC binding, `presence.beat`, the
|
|
60
|
+
> agent SSE stream) is served by the product app `cohort-app` on the
|
|
61
|
+
> `cohort-os` Railway project, not by a separate org-server service. There is
|
|
62
|
+
> nothing to repoint at `neolith-production`. `DEPLOYMENT.md` §5.1 now templates
|
|
63
|
+
> `os.cohortapp.com`; `.env.example` defaults `COHORT_BASE` to it; `maestro
|
|
64
|
+
> doctor` prints the effective base per seat (org.yaml vs .env vs env) so a
|
|
65
|
+
> straggler pinning the dead host is one `doctor` run away from being found.
|
|
66
|
+
> The paragraph below is kept as the record of what was observed.
|
|
67
|
+
>
|
|
68
|
+
> ⚠︎ **2026-08-16 — this warning appears to have come true (resolved above).**
|
|
59
69
|
> `https://neolith.up.railway.app` is now UNROUTED: Railway's edge returns
|
|
60
70
|
> `x-railway-fallback: true` with a 404 on `/`, `/v1` and `/v1/ops`, meaning no
|
|
61
71
|
> service is attached to that hostname. Contrast the app, whose rename DID
|