@adaptic/maestro 1.10.2 → 1.10.4

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/bin/maestro.mjs CHANGED
@@ -625,6 +625,8 @@ rubrics: []
625
625
  "slack:events:stop": "./scripts/slack-events-ctl.sh stop",
626
626
  "slack:events:status": "./scripts/slack-events-ctl.sh status",
627
627
  upgrade: "npx @adaptic/maestro upgrade",
628
+ init: "npx @adaptic/maestro init",
629
+ doctor: "npx @adaptic/maestro doctor",
628
630
  },
629
631
  dependencies: {
630
632
  "@google/genai": "^1.42.0",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adaptic/maestro",
3
- "version": "1.10.2",
3
+ "version": "1.10.4",
4
4
  "description": "Maestro — Autonomous AI agent operating system. Deploy AI employees on dedicated Mac minis.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -109,9 +109,13 @@ test("guarded cadence completes inline when there's no work", async () => {
109
109
  test("guarded cadence escalates when guard says work is pending", async () => {
110
110
  const root = await makeAgentRoot();
111
111
  // Seed an inbox item so the inbox-processor guard returns "escalate".
112
+ // Also set INBOX_CADENCE_ESCALATE=1 because the default behaviour is
113
+ // now inline (reactive daemon owns inbox dispatch — see cadence-handlers.mjs).
112
114
  mkdirSync(join(root, "state/inbox/slack"), { recursive: true });
113
115
  writeFileSync(join(root, "state/inbox/slack/item.json"), JSON.stringify({ id: "msg1" }));
114
116
  plantPrompt(root, "inbox-processor");
117
+ const prevEnv = process.env.INBOX_CADENCE_ESCALATE;
118
+ process.env.INBOX_CADENCE_ESCALATE = "1";
115
119
  const spawnCalls = [];
116
120
  const consumer = startConsumer({
117
121
  agentRoot: root,
@@ -131,6 +135,8 @@ test("guarded cadence escalates when guard says work is pending", async () => {
131
135
  } finally {
132
136
  await consumer.stop();
133
137
  await rmRoot(root);
138
+ if (prevEnv === undefined) delete process.env.INBOX_CADENCE_ESCALATE;
139
+ else process.env.INBOX_CADENCE_ESCALATE = prevEnv;
134
140
  }
135
141
  });
136
142
 
@@ -431,9 +437,13 @@ test("explicit reason is attached to event metadata before escalation", async ()
431
437
  // but for audit visibility we attach guard data to event.metadata.
432
438
  const root = await makeAgentRoot();
433
439
  // Force a guard:escalate outcome via inbox-processor with pending items.
440
+ // Default behaviour is now inline (reactive daemon owns inbox), so
441
+ // override with INBOX_CADENCE_ESCALATE=1.
434
442
  mkdirSync(join(root, "state/inbox/internal"), { recursive: true });
435
443
  writeFileSync(join(root, "state/inbox/internal/x.json"), "{}");
436
444
  plantPrompt(root, "inbox-processor");
445
+ const prevEnv = process.env.INBOX_CADENCE_ESCALATE;
446
+ process.env.INBOX_CADENCE_ESCALATE = "1";
437
447
  const seen = [];
438
448
  const consumer = startConsumer({
439
449
  agentRoot: root, pollMs: 50,
@@ -462,5 +472,7 @@ test("explicit reason is attached to event metadata before escalation", async ()
462
472
  } finally {
463
473
  await consumer.stop();
464
474
  await rmRoot(root);
475
+ if (prevEnv === undefined) delete process.env.INBOX_CADENCE_ESCALATE;
476
+ else process.env.INBOX_CADENCE_ESCALATE = prevEnv;
465
477
  }
466
478
  });
@@ -109,10 +109,19 @@ async function handleStaleRecovery({ event, agentRoot }) {
109
109
 
110
110
  /**
111
111
  * inbox-processor guard:
112
- * - Count items under state/inbox/{slack,gmail,calendar,internal,sms,whatsapp}
113
- * that have NOT been moved to state/inbox/processed/.
114
- * - If 0, complete inline (saves a Claude Code spawn).
115
- * - If >0, escalate so a sub-session can classify and route them.
112
+ * The reactive daemon (agent-daemon.mjs) continuously polls inbound
113
+ * services + drains state/inbox/ + dispatches sessions via classifier
114
+ * and dispatcher. Running the cadence-bus inbox-processor cadence on
115
+ * top causes DOUBLE responses to the same message observed in
116
+ * ravi-ai when a CEO DM got TWO substantive replies (one from each
117
+ * path) on 2026-05-12.
118
+ *
119
+ * So this guard now always returns inline. The reactive daemon owns
120
+ * inbox dispatch. Operators who want the cadence-only mode (no reactive
121
+ * daemon) can set INBOX_CADENCE_ESCALATE=1 in .env to flip behaviour.
122
+ *
123
+ * Pre-check is still done (count pending items) so the inline result
124
+ * carries useful telemetry, but escalation is suppressed.
116
125
  */
117
126
  async function guardInboxProcessor({ agentRoot }) {
118
127
  const inboxRoot = join(agentRoot, "state", "inbox");
@@ -121,10 +130,19 @@ async function guardInboxProcessor({ agentRoot }) {
121
130
  for (const src of sources) {
122
131
  pending += countFiles(join(inboxRoot, src), /\.(json|ya?ml)$/i);
123
132
  }
124
- if (pending === 0) {
125
- return { ok: true, decision: "inline", reason: "inbox empty", pending: 0 };
133
+ if (process.env.INBOX_CADENCE_ESCALATE === "1") {
134
+ if (pending === 0) {
135
+ return { ok: true, decision: "inline", reason: "inbox empty", pending: 0 };
136
+ }
137
+ return { ok: true, decision: "escalate", pending };
126
138
  }
127
- return { ok: true, decision: "escalate", pending };
139
+ return {
140
+ ok: true,
141
+ decision: "inline",
142
+ reason: "reactive-daemon-owns-inbox",
143
+ pending,
144
+ note: "Set INBOX_CADENCE_ESCALATE=1 to override.",
145
+ };
128
146
  }
129
147
 
130
148
  /**