@adaptic/maestro 1.7.3 → 1.8.1

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.
Files changed (36) hide show
  1. package/.claude/commands/init-maestro.md +15 -2
  2. package/.gitignore +7 -0
  3. package/README.md +62 -11
  4. package/bin/maestro.mjs +338 -2
  5. package/bin/maestro.test.mjs +299 -0
  6. package/docs/guides/poller-daemon-setup.md +21 -8
  7. package/docs/runbooks/perpetual-operations.md +19 -15
  8. package/docs/runbooks/recovery-and-failover.md +42 -0
  9. package/lib/cadence-bus.mjs +625 -0
  10. package/lib/cadence-bus.test.mjs +354 -0
  11. package/package.json +6 -1
  12. package/scaffold/CLAUDE.md +11 -7
  13. package/scripts/cadence/cadence-status.mjs +36 -0
  14. package/scripts/cadence/enqueue-cadence-tick.mjs +158 -0
  15. package/scripts/cadence/enqueue-cadence-tick.test.mjs +154 -0
  16. package/scripts/cadence/launchd-cadence-wrapper.sh +85 -0
  17. package/scripts/daemon/cadence-consumer.mjs +439 -0
  18. package/scripts/daemon/cadence-consumer.test.mjs +397 -0
  19. package/scripts/daemon/cadence-handlers.mjs +263 -0
  20. package/scripts/daemon/maestro-daemon.mjs +20 -0
  21. package/scripts/local-triggers/generate-plists.sh +62 -17
  22. package/scripts/local-triggers/generate-plists.test.mjs +254 -0
  23. package/scripts/local-triggers/plists/.gitkeep +0 -0
  24. package/scripts/local-triggers/run-trigger.sh +22 -3
  25. package/scripts/local-triggers/plists/ai.adaptic.sophie-backlog-executor.plist +0 -21
  26. package/scripts/local-triggers/plists/ai.adaptic.sophie-daemon.plist +0 -32
  27. package/scripts/local-triggers/plists/ai.adaptic.sophie-inbox-processor.plist +0 -21
  28. package/scripts/local-triggers/plists/ai.adaptic.sophie-meeting-action-capture.plist +0 -21
  29. package/scripts/local-triggers/plists/ai.adaptic.sophie-meeting-prep.plist +0 -21
  30. package/scripts/local-triggers/plists/ai.adaptic.sophie-midday-sweep.plist +0 -26
  31. package/scripts/local-triggers/plists/ai.adaptic.sophie-quarterly-self-assessment.plist +0 -62
  32. package/scripts/local-triggers/plists/ai.adaptic.sophie-weekly-engineering-health.plist +0 -28
  33. package/scripts/local-triggers/plists/ai.adaptic.sophie-weekly-execution.plist +0 -28
  34. package/scripts/local-triggers/plists/ai.adaptic.sophie-weekly-hiring.plist +0 -28
  35. package/scripts/local-triggers/plists/ai.adaptic.sophie-weekly-priorities.plist +0 -28
  36. package/scripts/local-triggers/plists/ai.adaptic.sophie-weekly-strategic-memo.plist +0 -28
@@ -6,25 +6,68 @@
6
6
  # Reads config/agent.ts to extract the agent's first name and generates
7
7
  # properly configured plist files for all scheduled triggers and the daemon.
8
8
  #
9
+ # Architecture marker:
10
+ # MAESTRO_PLIST_ARCH=cadence-bus
11
+ #
12
+ # This generator emits the *cadence-bus* plist architecture (introduced in
13
+ # maestro 1.8). Scheduled cadence ticks invoke a lightweight Node enqueue
14
+ # script (scripts/cadence/enqueue-cadence-tick.mjs) that drops an event onto
15
+ # state/cadence-bus/. The persistent maestro-daemon.mjs consumes the bus and
16
+ # decides — per cadence — whether to handle the tick inline or escalate to a
17
+ # managed sub-session. Launchd NEVER spawns a fresh Claude Code session for
18
+ # routine cadence ticks anymore.
19
+ #
20
+ # Doctor verifies the `MAESTRO_PLIST_ARCH=cadence-bus` marker is present in
21
+ # installed plists, and flags any plist that still runs run-trigger.sh
22
+ # directly as the legacy spawn-per-tick pattern.
23
+ #
9
24
  # Usage:
10
25
  # ./scripts/local-triggers/generate-plists.sh
11
26
  #
12
- # Called by: init-agent.sh (Step 5)
27
+ # Called by: init-agent.sh (Step 5) and by `maestro upgrade` migration.
13
28
  # =============================================================================
14
29
 
15
30
  set -e
16
31
 
32
+ # Plist architecture marker — embedded as a comment in every generated plist.
33
+ MAESTRO_PLIST_ARCH="cadence-bus"
34
+ MAESTRO_PLIST_ARCH_VERSION="1"
35
+
17
36
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
18
37
  AGENT_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)"
19
38
  PLIST_DIR="$SCRIPT_DIR/plists"
20
- AGENT_CONFIG="$AGENT_DIR/config/agent.ts"
21
39
 
22
- # Extract agent first name from config/agent.ts
23
- if [ -f "$AGENT_CONFIG" ]; then
24
- AGENT_FIRST=$(grep "firstName:" "$AGENT_CONFIG" | head -1 | sed "s/.*firstName:[[:space:]]*['\"]//; s/['\"].*//" | tr '[:upper:]' '[:lower:]')
40
+ # Resolve the agent's first name. Order of preference:
41
+ # 1. config/agent.json — single source of truth (SOT) introduced when
42
+ # maestro split identity out of TypeScript into JSON. Use jq if
43
+ # available, otherwise a small awk fallback.
44
+ # 2. config/agent.ts — legacy path; only works if firstName is
45
+ # defined inline (string literal), not as a type-only declaration
46
+ # like `firstName: string;`.
47
+ # 3. Directory name — last-resort fallback (e.g. ~/ravi-ai → ravi).
48
+ AGENT_JSON="$AGENT_DIR/config/agent.json"
49
+ AGENT_TS="$AGENT_DIR/config/agent.ts"
50
+ AGENT_FIRST=""
51
+
52
+ if [ -z "$AGENT_FIRST" ] && [ -f "$AGENT_JSON" ]; then
53
+ if command -v jq >/dev/null 2>&1; then
54
+ AGENT_FIRST=$(jq -r '.firstName // empty' "$AGENT_JSON" 2>/dev/null | tr '[:upper:]' '[:lower:]')
55
+ else
56
+ # Awk fallback — handle "firstName": "value" with optional whitespace
57
+ AGENT_FIRST=$(awk -F'"' '/"firstName"[[:space:]]*:/ { print tolower($4); exit }' "$AGENT_JSON")
58
+ fi
59
+ fi
60
+
61
+ if [ -z "$AGENT_FIRST" ] && [ -f "$AGENT_TS" ]; then
62
+ # Only accept a quoted inline value — `firstName: string;` (type
63
+ # declaration) must not match. The grep requires a quote on the line.
64
+ AGENT_FIRST=$(grep "firstName:[[:space:]]*['\"]" "$AGENT_TS" \
65
+ | head -1 \
66
+ | sed "s/.*firstName:[[:space:]]*['\"]//; s/['\"].*//" \
67
+ | tr '[:upper:]' '[:lower:]')
25
68
  fi
26
69
 
27
- # Fall back to directory name if config not set or UNCONFIGURED
70
+ # Fall back to directory name if config not set or UNCONFIGURED.
28
71
  if [ -z "$AGENT_FIRST" ] || [ "$AGENT_FIRST" = "unconfigured" ]; then
29
72
  AGENT_FIRST=$(basename "$AGENT_DIR" | sed 's/-ai$//')
30
73
  fi
@@ -57,6 +100,8 @@ generate_plist() {
57
100
 
58
101
  cat > "$FILE" << PLIST_HEADER
59
102
  <?xml version="1.0" encoding="UTF-8"?>
103
+ <!-- maestro-plist-arch: $MAESTRO_PLIST_ARCH v$MAESTRO_PLIST_ARCH_VERSION -->
104
+ <!-- generated-by: scripts/local-triggers/generate-plists.sh -->
60
105
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
61
106
  <plist version="1.0">
62
107
  <dict>
@@ -161,16 +206,16 @@ PLIST_FOOTER
161
206
  echo " Generated: $LABEL"
162
207
  }
163
208
 
164
- # ── Helper: trigger plist (runs claude with a trigger prompt) ────────────────
209
+ # ── Helper: trigger plist (enqueues a cadence tick) ──────────────────────────
165
210
  #
166
- # Routes through scripts/daemon/launchd-wrapper-generic.sh so the trigger's
167
- # stdout/stderr lands on the SSD (when /Volumes/{name}-SSD is mounted) and
168
- # CLAUDE_CODE_TMPDIR is set for any Claude Code session it spawns.
211
+ # Cadence-bus architecture: launchd invokes the lightweight Node enqueue
212
+ # script (scripts/cadence/enqueue-cadence-tick.mjs) which drops a JSON event
213
+ # onto state/cadence-bus/inbox/. The persistent maestro-daemon.mjs consumes
214
+ # the bus and either handles the tick inline or escalates to a managed
215
+ # sub-session.
169
216
  #
170
- # IMPORTANT: run-trigger.sh expects the trigger NAME (e.g. "meeting-prep"),
171
- # not the full path to the .md file. The script constructs the full path
172
- # itself. Passing the full path causes a doubled path bug:
173
- # /agent/schedules/triggers//agent/schedules/triggers/meeting-prep.md.md
217
+ # This deliberately does NOT spawn Claude Code per tick anymore. The legacy
218
+ # run-trigger.sh is preserved for manual one-shot invocations only.
174
219
 
175
220
  generate_trigger_plist() {
176
221
  local TRIGGER_NAME="$1"
@@ -178,11 +223,11 @@ generate_trigger_plist() {
178
223
  local INTERVAL="$3"
179
224
 
180
225
  local LABEL="ai.adaptic.${AGENT_FIRST}-${TRIGGER_NAME}"
181
- local RUN_TRIGGER="$AGENT_DIR/scripts/local-triggers/run-trigger.sh"
182
- local WRAPPER="$AGENT_DIR/scripts/daemon/launchd-wrapper-generic.sh"
226
+ local ENQUEUE="$AGENT_DIR/scripts/cadence/enqueue-cadence-tick.mjs"
227
+ local WRAPPER="$AGENT_DIR/scripts/cadence/launchd-cadence-wrapper.sh"
183
228
 
184
229
  generate_plist "$LABEL" \
185
- "$WRAPPER|$RUN_TRIGGER|$TRIGGER_NAME" \
230
+ "$WRAPPER|$ENQUEUE|$TRIGGER_NAME|--source=launchd|--quiet" \
186
231
  "$SCHEDULE" \
187
232
  "$INTERVAL" \
188
233
  ""
@@ -0,0 +1,254 @@
1
+ /**
2
+ * generate-plists.test.mjs — Coverage for scripts/local-triggers/generate-plists.sh
3
+ *
4
+ * Builds a fixture agent repo (minimal config/agent.ts + script tree),
5
+ * runs the generator, and asserts every emitted plist matches the
6
+ * cadence-bus architecture marker and never references the legacy
7
+ * run-trigger.sh spawn-per-tick pattern.
8
+ */
9
+
10
+ import { test } from "node:test";
11
+ import assert from "node:assert/strict";
12
+ import { promises as fsp } from "fs";
13
+ import { readFileSync, readdirSync, writeFileSync, mkdirSync, copyFileSync } from "node:fs";
14
+ import { tmpdir } from "os";
15
+ import { join, resolve, dirname } from "path";
16
+ import { fileURLToPath } from "node:url";
17
+ import { spawnSync } from "node:child_process";
18
+
19
+ const __dirname = dirname(fileURLToPath(import.meta.url));
20
+ const MAESTRO_ROOT = resolve(__dirname, "..", "..");
21
+ const GENERATOR = resolve(__dirname, "generate-plists.sh");
22
+
23
+ async function makeAgent(firstName) {
24
+ const root = join(
25
+ tmpdir(),
26
+ `plist-test-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`
27
+ );
28
+ await fsp.mkdir(root, { recursive: true });
29
+
30
+ // Minimal agent identity.
31
+ await fsp.mkdir(join(root, "config"), { recursive: true });
32
+ writeFileSync(
33
+ join(root, "config/agent.ts"),
34
+ `export const AGENT = { firstName: '${firstName}', lastName: 'Tester' };\n`
35
+ );
36
+ writeFileSync(join(root, "package.json"), '{"name":"t"}');
37
+
38
+ // Copy the scripts the generator references.
39
+ await fsp.mkdir(join(root, "scripts/local-triggers"), { recursive: true });
40
+ await fsp.mkdir(join(root, "scripts/daemon"), { recursive: true });
41
+ await fsp.mkdir(join(root, "scripts/cadence"), { recursive: true });
42
+ copyFileSync(GENERATOR, join(root, "scripts/local-triggers/generate-plists.sh"));
43
+ copyFileSync(
44
+ join(MAESTRO_ROOT, "scripts/local-triggers/run-trigger.sh"),
45
+ join(root, "scripts/local-triggers/run-trigger.sh"),
46
+ );
47
+ copyFileSync(
48
+ join(MAESTRO_ROOT, "scripts/daemon/launchd-wrapper.sh"),
49
+ join(root, "scripts/daemon/launchd-wrapper.sh"),
50
+ );
51
+ copyFileSync(
52
+ join(MAESTRO_ROOT, "scripts/daemon/launchd-wrapper-generic.sh"),
53
+ join(root, "scripts/daemon/launchd-wrapper-generic.sh"),
54
+ );
55
+ copyFileSync(
56
+ join(MAESTRO_ROOT, "scripts/cadence/launchd-cadence-wrapper.sh"),
57
+ join(root, "scripts/cadence/launchd-cadence-wrapper.sh"),
58
+ );
59
+ copyFileSync(
60
+ join(MAESTRO_ROOT, "scripts/cadence/enqueue-cadence-tick.mjs"),
61
+ join(root, "scripts/cadence/enqueue-cadence-tick.mjs"),
62
+ );
63
+
64
+ // Make scripts executable.
65
+ for (const p of [
66
+ "scripts/local-triggers/generate-plists.sh",
67
+ "scripts/local-triggers/run-trigger.sh",
68
+ "scripts/daemon/launchd-wrapper.sh",
69
+ "scripts/daemon/launchd-wrapper-generic.sh",
70
+ "scripts/cadence/launchd-cadence-wrapper.sh",
71
+ ]) {
72
+ await fsp.chmod(join(root, p), 0o755);
73
+ }
74
+ return root;
75
+ }
76
+
77
+ async function rmRoot(path) {
78
+ try { await fsp.rm(path, { recursive: true, force: true }); } catch { /* */ }
79
+ }
80
+
81
+ function runGenerator(agentRoot) {
82
+ return spawnSync("/bin/bash", [join(agentRoot, "scripts/local-triggers/generate-plists.sh")], {
83
+ cwd: agentRoot,
84
+ encoding: "utf-8",
85
+ env: { ...process.env },
86
+ });
87
+ }
88
+
89
+ function listPlists(agentRoot) {
90
+ const dir = join(agentRoot, "scripts/local-triggers/plists");
91
+ return readdirSync(dir).filter((n) => n.endsWith(".plist"));
92
+ }
93
+
94
+ // ---------------------------------------------------------------------------
95
+ // Tests
96
+ // ---------------------------------------------------------------------------
97
+
98
+ test("generator emits 13 plists with the agent's first name", async () => {
99
+ const root = await makeAgent("alice");
100
+ try {
101
+ const r = runGenerator(root);
102
+ assert.equal(r.status, 0, r.stderr);
103
+ const plists = listPlists(root);
104
+ assert.equal(plists.length, 13);
105
+ for (const p of plists) {
106
+ assert.match(p, /^ai\.adaptic\.alice-/);
107
+ }
108
+ } finally { await rmRoot(root); }
109
+ });
110
+
111
+ test("every trigger plist carries the cadence-bus architecture marker", async () => {
112
+ const root = await makeAgent("bob");
113
+ try {
114
+ runGenerator(root);
115
+ const dir = join(root, "scripts/local-triggers/plists");
116
+ for (const name of listPlists(root)) {
117
+ const body = readFileSync(join(dir, name), "utf-8");
118
+ assert.match(body, /maestro-plist-arch: cadence-bus/, `${name} missing arch marker`);
119
+ }
120
+ } finally { await rmRoot(root); }
121
+ });
122
+
123
+ test("NO trigger plist invokes run-trigger.sh", async () => {
124
+ const root = await makeAgent("carol");
125
+ try {
126
+ runGenerator(root);
127
+ const dir = join(root, "scripts/local-triggers/plists");
128
+ for (const name of listPlists(root)) {
129
+ const body = readFileSync(join(dir, name), "utf-8");
130
+ // Exception: the daemon/poll-relay plists don't run triggers at all.
131
+ // The trigger plists must invoke enqueue-cadence-tick.mjs.
132
+ if (name.endsWith("-daemon.plist") || name.endsWith("-poll-relay.plist")) {
133
+ continue;
134
+ }
135
+ assert.ok(!body.includes("run-trigger.sh"),
136
+ `${name} still references run-trigger.sh:\n${body}`);
137
+ assert.ok(body.includes("enqueue-cadence-tick.mjs"),
138
+ `${name} should invoke enqueue script`);
139
+ }
140
+ } finally { await rmRoot(root); }
141
+ });
142
+
143
+ test("trigger plists pass --source=launchd to the enqueue script", async () => {
144
+ const root = await makeAgent("dana");
145
+ try {
146
+ runGenerator(root);
147
+ const dir = join(root, "scripts/local-triggers/plists");
148
+ const triggerPlist = join(dir, "ai.adaptic.dana-inbox-processor.plist");
149
+ const body = readFileSync(triggerPlist, "utf-8");
150
+ assert.match(body, /<string>--source=launchd<\/string>/);
151
+ assert.match(body, /<string>inbox-processor<\/string>/);
152
+ // StartInterval 300 (every 5 min).
153
+ assert.match(body, /<integer>300<\/integer>/);
154
+ } finally { await rmRoot(root); }
155
+ });
156
+
157
+ test("daemon plist remains a KeepAlive job (not a cadence enqueue)", async () => {
158
+ const root = await makeAgent("erin");
159
+ try {
160
+ runGenerator(root);
161
+ const path = join(root, "scripts/local-triggers/plists/ai.adaptic.erin-daemon.plist");
162
+ const body = readFileSync(path, "utf-8");
163
+ assert.match(body, /<key>KeepAlive<\/key>\s*<true\/>/);
164
+ assert.match(body, /launchd-wrapper\.sh/);
165
+ assert.ok(!body.includes("enqueue-cadence-tick.mjs"),
166
+ "daemon plist should not enqueue ticks — it IS the consumer");
167
+ } finally { await rmRoot(root); }
168
+ });
169
+
170
+ test("generator reads firstName from agent.json when agent.ts is type-only (SOT layout)", async () => {
171
+ // Reproduce the SOT layout where agent.ts only declares interfaces
172
+ // (containing `firstName: string;` as a type, not a value) and the
173
+ // actual identity lives in agent.json. The legacy resolver matched the
174
+ // interface line and produced labels like
175
+ // `ai.adaptic. firstname: string;-…` — guard against regression.
176
+ const root = join(
177
+ tmpdir(),
178
+ `plist-sot-test-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`
179
+ );
180
+ await fsp.mkdir(root, { recursive: true });
181
+ // Manually wire the same fixture-builder used by other tests, except
182
+ // overwrite agent.ts to look like a type-only TypeScript file and add
183
+ // a real agent.json.
184
+ await fsp.mkdir(join(root, "config"), { recursive: true });
185
+ writeFileSync(
186
+ join(root, "config/agent.ts"),
187
+ `// Type declarations only — value lives in agent.json
188
+ export interface PrincipalConfig { firstName: string; }
189
+ export interface AgentConfig { firstName: string; }
190
+ `,
191
+ );
192
+ writeFileSync(
193
+ join(root, "config/agent.json"),
194
+ JSON.stringify({ firstName: "Sigrid", lastName: "Test" }) + "\n",
195
+ );
196
+ writeFileSync(join(root, "package.json"), '{"name":"t"}');
197
+
198
+ await fsp.mkdir(join(root, "scripts/local-triggers"), { recursive: true });
199
+ await fsp.mkdir(join(root, "scripts/daemon"), { recursive: true });
200
+ await fsp.mkdir(join(root, "scripts/cadence"), { recursive: true });
201
+ // Same set of helper scripts as makeAgent().
202
+ const { copyFileSync } = await import("node:fs");
203
+ copyFileSync(GENERATOR, join(root, "scripts/local-triggers/generate-plists.sh"));
204
+ copyFileSync(
205
+ join(MAESTRO_ROOT, "scripts/local-triggers/run-trigger.sh"),
206
+ join(root, "scripts/local-triggers/run-trigger.sh"),
207
+ );
208
+ copyFileSync(
209
+ join(MAESTRO_ROOT, "scripts/daemon/launchd-wrapper.sh"),
210
+ join(root, "scripts/daemon/launchd-wrapper.sh"),
211
+ );
212
+ copyFileSync(
213
+ join(MAESTRO_ROOT, "scripts/daemon/launchd-wrapper-generic.sh"),
214
+ join(root, "scripts/daemon/launchd-wrapper-generic.sh"),
215
+ );
216
+ copyFileSync(
217
+ join(MAESTRO_ROOT, "scripts/cadence/launchd-cadence-wrapper.sh"),
218
+ join(root, "scripts/cadence/launchd-cadence-wrapper.sh"),
219
+ );
220
+ copyFileSync(
221
+ join(MAESTRO_ROOT, "scripts/cadence/enqueue-cadence-tick.mjs"),
222
+ join(root, "scripts/cadence/enqueue-cadence-tick.mjs"),
223
+ );
224
+ await fsp.chmod(join(root, "scripts/local-triggers/generate-plists.sh"), 0o755);
225
+
226
+ try {
227
+ const r = runGenerator(root);
228
+ assert.equal(r.status, 0, r.stderr);
229
+ const plists = listPlists(root);
230
+ assert.ok(plists.length >= 1);
231
+ for (const p of plists) {
232
+ assert.match(p, /^ai\.adaptic\.sigrid-/, `expected "sigrid" label, got: ${p}`);
233
+ assert.ok(!p.includes("string"),
234
+ `label leaked the TypeScript type: ${p}`);
235
+ }
236
+ } finally { await rmRoot(root); }
237
+ });
238
+
239
+ test("regeneration clears stale plists", async () => {
240
+ const root = await makeAgent("frank");
241
+ try {
242
+ // Plant a stale plist for a long-gone agent.
243
+ const dir = join(root, "scripts/local-triggers/plists");
244
+ mkdirSync(dir, { recursive: true });
245
+ writeFileSync(join(dir, "ai.adaptic.OLD-AGENT-stale.plist"), "<plist/>");
246
+ runGenerator(root);
247
+ const remaining = listPlists(root);
248
+ assert.ok(!remaining.includes("ai.adaptic.OLD-AGENT-stale.plist"),
249
+ "generator must clear stale plists");
250
+ for (const p of remaining) {
251
+ assert.match(p, /^ai\.adaptic\.frank-/);
252
+ }
253
+ } finally { await rmRoot(root); }
254
+ });
File without changes
@@ -1,9 +1,28 @@
1
1
  #!/bin/bash
2
- # Run a local scheduled trigger via Claude Code CLI
2
+ # Run a local scheduled trigger via Claude Code CLI (legacy / manual path).
3
+ #
3
4
  # Usage: run-trigger.sh <trigger-name>
4
5
  #
5
- # Reads the prompt from schedules/triggers/<trigger-name>.md
6
- # and runs it as a non-interactive Claude Code session.
6
+ # Reads the prompt from schedules/triggers/<trigger-name>.md and runs it as
7
+ # a non-interactive `claude --print` session.
8
+ #
9
+ # ⚠ LEGACY PATH — DO NOT USE FROM LAUNCHD ⚠
10
+ #
11
+ # As of the cadence-bus architecture (maestro 1.8), scheduled cadence ticks
12
+ # go through scripts/cadence/enqueue-cadence-tick.mjs instead of running
13
+ # claude directly. The persistent maestro-daemon.mjs consumes events from
14
+ # state/cadence-bus/ and decides whether to handle them inline or escalate
15
+ # to a managed sub-session.
16
+ #
17
+ # This script remains available for two specific purposes:
18
+ # 1. Manual one-shot invocation by an operator who wants to run a single
19
+ # cadence prompt immediately, without waiting for the daemon.
20
+ # 2. Compatibility with very old launchd plists during migration. Doctor
21
+ # / upgrade will flag plists that still call this script and rewrite
22
+ # them through the cadence bus.
23
+ #
24
+ # Prefer instead:
25
+ # node scripts/cadence/enqueue-cadence-tick.mjs <trigger-name> --source=manual
7
26
 
8
27
  set -e
9
28
 
@@ -1,21 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
- <plist version="1.0">
4
- <dict>
5
- <key>Label</key>
6
- <string>ai.adaptic.sophie-backlog-executor</string>
7
- <key>ProgramArguments</key>
8
- <array>
9
- <string>/Users/sophie/sophie-ai/scripts/local-triggers/run-trigger.sh</string>
10
- <string>backlog-executor</string>
11
- </array>
12
- <key>StartInterval</key>
13
- <integer>600</integer>
14
- <key>WorkingDirectory</key>
15
- <string>/Users/sophie/sophie-ai</string>
16
- <key>StandardOutPath</key>
17
- <string>/Users/sophie/sophie-ai/logs/workflows/launchd-backlog-executor-stdout.log</string>
18
- <key>StandardErrorPath</key>
19
- <string>/Users/sophie/sophie-ai/logs/workflows/launchd-backlog-executor-stderr.log</string>
20
- </dict>
21
- </plist>
@@ -1,32 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
- <plist version="1.0">
4
- <dict>
5
- <key>Label</key>
6
- <string>ai.adaptic.sophie-daemon</string>
7
-
8
- <key>ProgramArguments</key>
9
- <array>
10
- <string>/opt/homebrew/bin/node</string>
11
- <string>/Users/sophie/sophie-ai/scripts/daemon/sophie-daemon.mjs</string>
12
- </array>
13
-
14
- <key>WorkingDirectory</key>
15
- <string>/Users/sophie/sophie-ai</string>
16
-
17
- <key>KeepAlive</key>
18
- <true/>
19
-
20
- <key>RunAtLoad</key>
21
- <true/>
22
-
23
- <key>ThrottleInterval</key>
24
- <integer>5</integer>
25
-
26
- <key>StandardOutPath</key>
27
- <string>/Users/sophie/sophie-ai/logs/daemon/launchd-stdout.log</string>
28
-
29
- <key>StandardErrorPath</key>
30
- <string>/Users/sophie/sophie-ai/logs/daemon/launchd-stderr.log</string>
31
- </dict>
32
- </plist>
@@ -1,21 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
- <plist version="1.0">
4
- <dict>
5
- <key>Label</key>
6
- <string>ai.adaptic.sophie-inbox-processor</string>
7
- <key>ProgramArguments</key>
8
- <array>
9
- <string>/Users/sophie/sophie-ai/scripts/local-triggers/run-trigger.sh</string>
10
- <string>inbox-processor</string>
11
- </array>
12
- <key>StartInterval</key>
13
- <integer>300</integer>
14
- <key>WorkingDirectory</key>
15
- <string>/Users/sophie/sophie-ai</string>
16
- <key>StandardOutPath</key>
17
- <string>/Users/sophie/sophie-ai/logs/workflows/launchd-inbox-processor-stdout.log</string>
18
- <key>StandardErrorPath</key>
19
- <string>/Users/sophie/sophie-ai/logs/workflows/launchd-inbox-processor-stderr.log</string>
20
- </dict>
21
- </plist>
@@ -1,21 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
- <plist version="1.0">
4
- <dict>
5
- <key>Label</key>
6
- <string>ai.adaptic.sophie-meeting-action-capture</string>
7
- <key>ProgramArguments</key>
8
- <array>
9
- <string>/Users/sophie/sophie-ai/scripts/local-triggers/run-trigger.sh</string>
10
- <string>meeting-action-capture</string>
11
- </array>
12
- <key>StartInterval</key>
13
- <integer>1800</integer>
14
- <key>WorkingDirectory</key>
15
- <string>/Users/sophie/sophie-ai</string>
16
- <key>StandardOutPath</key>
17
- <string>/Users/sophie/sophie-ai/logs/workflows/launchd-meeting-action-capture-stdout.log</string>
18
- <key>StandardErrorPath</key>
19
- <string>/Users/sophie/sophie-ai/logs/workflows/launchd-meeting-action-capture-stderr.log</string>
20
- </dict>
21
- </plist>
@@ -1,21 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
- <plist version="1.0">
4
- <dict>
5
- <key>Label</key>
6
- <string>ai.adaptic.sophie-meeting-prep</string>
7
- <key>ProgramArguments</key>
8
- <array>
9
- <string>/Users/sophie/sophie-ai/scripts/local-triggers/run-trigger.sh</string>
10
- <string>meeting-prep</string>
11
- </array>
12
- <key>StartInterval</key>
13
- <integer>900</integer>
14
- <key>WorkingDirectory</key>
15
- <string>/Users/sophie/sophie-ai</string>
16
- <key>StandardOutPath</key>
17
- <string>/Users/sophie/sophie-ai/logs/workflows/launchd-meeting-prep-stdout.log</string>
18
- <key>StandardErrorPath</key>
19
- <string>/Users/sophie/sophie-ai/logs/workflows/launchd-meeting-prep-stderr.log</string>
20
- </dict>
21
- </plist>
@@ -1,26 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
- <plist version="1.0">
4
- <dict>
5
- <key>Label</key>
6
- <string>ai.adaptic.sophie-midday-sweep</string>
7
- <key>ProgramArguments</key>
8
- <array>
9
- <string>/Users/sophie/sophie-ai/scripts/local-triggers/run-trigger.sh</string>
10
- <string>daily-midday-sweep</string>
11
- </array>
12
- <key>StartCalendarInterval</key>
13
- <dict>
14
- <key>Hour</key>
15
- <integer>12</integer>
16
- <key>Minute</key>
17
- <integer>0</integer>
18
- </dict>
19
- <key>WorkingDirectory</key>
20
- <string>/Users/sophie/sophie-ai</string>
21
- <key>StandardOutPath</key>
22
- <string>/Users/sophie/sophie-ai/logs/workflows/launchd-midday-sweep-stdout.log</string>
23
- <key>StandardErrorPath</key>
24
- <string>/Users/sophie/sophie-ai/logs/workflows/launchd-midday-sweep-stderr.log</string>
25
- </dict>
26
- </plist>
@@ -1,62 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
- <plist version="1.0">
4
- <dict>
5
- <key>Label</key>
6
- <string>ai.adaptic.sophie-quarterly-self-assessment</string>
7
- <key>ProgramArguments</key>
8
- <array>
9
- <string>/Users/sophie/sophie-ai/scripts/local-triggers/run-trigger.sh</string>
10
- <string>quarterly-self-assessment</string>
11
- </array>
12
- <key>StartCalendarInterval</key>
13
- <array>
14
- <dict>
15
- <key>Month</key>
16
- <integer>1</integer>
17
- <key>Weekday</key>
18
- <integer>1</integer>
19
- <key>Hour</key>
20
- <integer>9</integer>
21
- <key>Minute</key>
22
- <integer>30</integer>
23
- </dict>
24
- <dict>
25
- <key>Month</key>
26
- <integer>4</integer>
27
- <key>Weekday</key>
28
- <integer>1</integer>
29
- <key>Hour</key>
30
- <integer>9</integer>
31
- <key>Minute</key>
32
- <integer>30</integer>
33
- </dict>
34
- <dict>
35
- <key>Month</key>
36
- <integer>7</integer>
37
- <key>Weekday</key>
38
- <integer>1</integer>
39
- <key>Hour</key>
40
- <integer>9</integer>
41
- <key>Minute</key>
42
- <integer>30</integer>
43
- </dict>
44
- <dict>
45
- <key>Month</key>
46
- <integer>10</integer>
47
- <key>Weekday</key>
48
- <integer>1</integer>
49
- <key>Hour</key>
50
- <integer>9</integer>
51
- <key>Minute</key>
52
- <integer>30</integer>
53
- </dict>
54
- </array>
55
- <key>WorkingDirectory</key>
56
- <string>/Users/sophie/sophie-ai</string>
57
- <key>StandardOutPath</key>
58
- <string>/Users/sophie/sophie-ai/logs/workflows/launchd-quarterly-self-assessment-stdout.log</string>
59
- <key>StandardErrorPath</key>
60
- <string>/Users/sophie/sophie-ai/logs/workflows/launchd-quarterly-self-assessment-stderr.log</string>
61
- </dict>
62
- </plist>
@@ -1,28 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
- <plist version="1.0">
4
- <dict>
5
- <key>Label</key>
6
- <string>ai.adaptic.sophie-weekly-engineering-health</string>
7
- <key>ProgramArguments</key>
8
- <array>
9
- <string>/Users/sophie/sophie-ai/scripts/local-triggers/run-trigger.sh</string>
10
- <string>weekly-engineering-health</string>
11
- </array>
12
- <key>StartCalendarInterval</key>
13
- <dict>
14
- <key>Weekday</key>
15
- <integer>3</integer>
16
- <key>Hour</key>
17
- <integer>10</integer>
18
- <key>Minute</key>
19
- <integer>0</integer>
20
- </dict>
21
- <key>WorkingDirectory</key>
22
- <string>/Users/sophie/sophie-ai</string>
23
- <key>StandardOutPath</key>
24
- <string>/Users/sophie/sophie-ai/logs/workflows/launchd-engineering-health-stdout.log</string>
25
- <key>StandardErrorPath</key>
26
- <string>/Users/sophie/sophie-ai/logs/workflows/launchd-engineering-health-stderr.log</string>
27
- </dict>
28
- </plist>