@rubytech/create-maxy-code 0.1.544 → 0.1.546

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 (108) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/lib/mcp-spawn-tee/dist/index.js +11 -0
  3. package/payload/platform/lib/mcp-spawn-tee/dist/index.js.map +1 -1
  4. package/payload/platform/lib/mcp-spawn-tee/src/__tests__/spawn-tee.test.ts +39 -0
  5. package/payload/platform/lib/mcp-spawn-tee/src/index.ts +12 -0
  6. package/payload/platform/plugins/admin/lib/mcp-spawn-tee/index.js +11 -0
  7. package/payload/platform/plugins/admin/skills/channel-instruction/SKILL.md +26 -12
  8. package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +3 -1
  9. package/payload/platform/plugins/admin/skills/whats-new/SKILL.md +9 -0
  10. package/payload/platform/plugins/aeo/lib/mcp-spawn-tee/index.js +11 -0
  11. package/payload/platform/plugins/browser/lib/mcp-spawn-tee/index.js +11 -0
  12. package/payload/platform/plugins/connector/lib/mcp-spawn-tee/index.js +11 -0
  13. package/payload/platform/plugins/contacts/lib/mcp-spawn-tee/index.js +11 -0
  14. package/payload/platform/plugins/docs/references/channel-wake-and-prompt.md +2 -0
  15. package/payload/platform/plugins/email/lib/mcp-spawn-tee/index.js +11 -0
  16. package/payload/platform/plugins/filesystem/lib/mcp-spawn-tee/index.js +11 -0
  17. package/payload/platform/plugins/google/lib/mcp-spawn-tee/index.js +11 -0
  18. package/payload/platform/plugins/graph-viewer/lib/mcp-spawn-tee/index.js +11 -0
  19. package/payload/platform/plugins/ledger/lib/mcp-spawn-tee/index.js +11 -0
  20. package/payload/platform/plugins/memory/lib/mcp-spawn-tee/index.js +11 -0
  21. package/payload/platform/plugins/outlook/lib/mcp-spawn-tee/index.js +11 -0
  22. package/payload/platform/plugins/quickbooks/lib/mcp-spawn-tee/index.js +11 -0
  23. package/payload/platform/plugins/replicate/lib/mcp-spawn-tee/index.js +11 -0
  24. package/payload/platform/plugins/scheduling/PLUGIN.md +1 -1
  25. package/payload/platform/plugins/scheduling/lib/mcp-spawn-tee/index.js +11 -0
  26. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/availability-snapshot.test.d.ts +2 -0
  27. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/availability-snapshot.test.d.ts.map +1 -0
  28. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/availability-snapshot.test.js +80 -0
  29. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/availability-snapshot.test.js.map +1 -0
  30. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/publish-availability-logging.test.d.ts +2 -0
  31. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/publish-availability-logging.test.d.ts.map +1 -0
  32. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/publish-availability-logging.test.js +32 -0
  33. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/publish-availability-logging.test.js.map +1 -0
  34. package/payload/platform/plugins/scheduling/mcp/dist/lib/availability-snapshot.d.ts +67 -0
  35. package/payload/platform/plugins/scheduling/mcp/dist/lib/availability-snapshot.d.ts.map +1 -0
  36. package/payload/platform/plugins/scheduling/mcp/dist/lib/availability-snapshot.js +88 -0
  37. package/payload/platform/plugins/scheduling/mcp/dist/lib/availability-snapshot.js.map +1 -0
  38. package/payload/platform/plugins/scheduling/mcp/dist/scripts/publish-availability.d.ts +2 -1
  39. package/payload/platform/plugins/scheduling/mcp/dist/scripts/publish-availability.d.ts.map +1 -1
  40. package/payload/platform/plugins/scheduling/mcp/dist/scripts/publish-availability.js +59 -54
  41. package/payload/platform/plugins/scheduling/mcp/dist/scripts/publish-availability.js.map +1 -1
  42. package/payload/platform/plugins/storage-broker/lib/mcp-spawn-tee/index.js +11 -0
  43. package/payload/platform/plugins/telegram/lib/mcp-spawn-tee/index.js +11 -0
  44. package/payload/platform/plugins/url-get/lib/mcp-spawn-tee/index.js +11 -0
  45. package/payload/platform/plugins/voice-mirror/lib/mcp-spawn-tee/index.js +11 -0
  46. package/payload/platform/plugins/whatsapp/lib/mcp-spawn-tee/index.js +11 -0
  47. package/payload/platform/plugins/work/lib/mcp-spawn-tee/index.js +11 -0
  48. package/payload/platform/plugins/workflows/lib/mcp-spawn-tee/index.js +11 -0
  49. package/payload/platform/services/claude-session-manager/dist/channel-mcp.d.ts +5 -0
  50. package/payload/platform/services/claude-session-manager/dist/channel-mcp.d.ts.map +1 -1
  51. package/payload/platform/services/claude-session-manager/dist/channel-mcp.js +16 -2
  52. package/payload/platform/services/claude-session-manager/dist/channel-mcp.js.map +1 -1
  53. package/payload/platform/services/claude-session-manager/dist/channel-spawn-tee.d.ts +34 -0
  54. package/payload/platform/services/claude-session-manager/dist/channel-spawn-tee.d.ts.map +1 -0
  55. package/payload/platform/services/claude-session-manager/dist/channel-spawn-tee.js +41 -0
  56. package/payload/platform/services/claude-session-manager/dist/channel-spawn-tee.js.map +1 -0
  57. package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
  58. package/payload/platform/services/claude-session-manager/dist/http-server.js +26 -1
  59. package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
  60. package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts.map +1 -1
  61. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js +11 -0
  62. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js.map +1 -1
  63. package/payload/platform/services/claude-session-manager/dist/telegram-channel-mcp.d.ts +5 -0
  64. package/payload/platform/services/claude-session-manager/dist/telegram-channel-mcp.d.ts.map +1 -1
  65. package/payload/platform/services/claude-session-manager/dist/telegram-channel-mcp.js +16 -2
  66. package/payload/platform/services/claude-session-manager/dist/telegram-channel-mcp.js.map +1 -1
  67. package/payload/platform/services/claude-session-manager/dist/wa-channel-mcp.d.ts +6 -0
  68. package/payload/platform/services/claude-session-manager/dist/wa-channel-mcp.d.ts.map +1 -1
  69. package/payload/platform/services/claude-session-manager/dist/wa-channel-mcp.js +17 -2
  70. package/payload/platform/services/claude-session-manager/dist/wa-channel-mcp.js.map +1 -1
  71. package/payload/platform/services/claude-session-manager/dist/webchat-channel-mcp.d.ts +5 -0
  72. package/payload/platform/services/claude-session-manager/dist/webchat-channel-mcp.d.ts.map +1 -1
  73. package/payload/platform/services/claude-session-manager/dist/webchat-channel-mcp.js +16 -2
  74. package/payload/platform/services/claude-session-manager/dist/webchat-channel-mcp.js.map +1 -1
  75. package/payload/platform/services/telegram-channel/dist/instructions.d.ts +1 -0
  76. package/payload/platform/services/telegram-channel/dist/instructions.d.ts.map +1 -1
  77. package/payload/platform/services/telegram-channel/dist/instructions.js +47 -0
  78. package/payload/platform/services/telegram-channel/dist/instructions.js.map +1 -1
  79. package/payload/platform/services/telegram-channel/dist/notification.d.ts +8 -14
  80. package/payload/platform/services/telegram-channel/dist/notification.d.ts.map +1 -1
  81. package/payload/platform/services/telegram-channel/dist/notification.js +13 -19
  82. package/payload/platform/services/telegram-channel/dist/notification.js.map +1 -1
  83. package/payload/platform/services/telegram-channel/dist/server.d.ts.map +1 -1
  84. package/payload/platform/services/telegram-channel/dist/server.js +3 -10
  85. package/payload/platform/services/telegram-channel/dist/server.js.map +1 -1
  86. package/payload/platform/services/webchat-channel/dist/instructions.d.ts +1 -0
  87. package/payload/platform/services/webchat-channel/dist/instructions.d.ts.map +1 -1
  88. package/payload/platform/services/webchat-channel/dist/instructions.js +60 -0
  89. package/payload/platform/services/webchat-channel/dist/instructions.js.map +1 -1
  90. package/payload/platform/services/webchat-channel/dist/notification.d.ts +9 -14
  91. package/payload/platform/services/webchat-channel/dist/notification.d.ts.map +1 -1
  92. package/payload/platform/services/webchat-channel/dist/notification.js +13 -19
  93. package/payload/platform/services/webchat-channel/dist/notification.js.map +1 -1
  94. package/payload/platform/services/webchat-channel/dist/server.d.ts.map +1 -1
  95. package/payload/platform/services/webchat-channel/dist/server.js +5 -10
  96. package/payload/platform/services/webchat-channel/dist/server.js.map +1 -1
  97. package/payload/platform/services/whatsapp-channel/dist/notification.d.ts +91 -9
  98. package/payload/platform/services/whatsapp-channel/dist/notification.d.ts.map +1 -1
  99. package/payload/platform/services/whatsapp-channel/dist/notification.js +155 -17
  100. package/payload/platform/services/whatsapp-channel/dist/notification.js.map +1 -1
  101. package/payload/platform/services/whatsapp-channel/dist/server.d.ts.map +1 -1
  102. package/payload/platform/services/whatsapp-channel/dist/server.js +83 -19
  103. package/payload/platform/services/whatsapp-channel/dist/server.js.map +1 -1
  104. package/payload/platform/services/whatsapp-channel/dist/targets.d.ts +23 -3
  105. package/payload/platform/services/whatsapp-channel/dist/targets.d.ts.map +1 -1
  106. package/payload/platform/services/whatsapp-channel/dist/targets.js +19 -2
  107. package/payload/platform/services/whatsapp-channel/dist/targets.js.map +1 -1
  108. package/payload/server/server.js +42 -43
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rubytech/create-maxy-code",
3
- "version": "0.1.544",
3
+ "version": "0.1.546",
4
4
  "description": "Install Maxy — AI for Productive People",
5
5
  "bin": {
6
6
  "create-maxy-code": "./dist/index.js"
@@ -180,6 +180,17 @@ process.on("exit", (code) => {
180
180
  `code=${codeField} signal=${exitSignal ?? "—"} lifetimeMs=${lifetimeMs} stderr-tail=${JSON.stringify(tail)}`, level);
181
181
  });
182
182
  emitLifecycle(`op=spawn session=${SESSION_ID8} server=${SERVER_NAME} pid=${process.pid} entry=${ENTRY}`, "info");
183
+ // Task 2356 — present argv as a direct `node <entry>` invocation before the
184
+ // entry loads. The shim is meant to be transparent: an entry that asks the
185
+ // standard "am I the entry module?" question
186
+ // (`import.meta.url === pathToFileURL(realpathSync(process.argv[1])).href`)
187
+ // must get the same answer it gets when Claude Code runs it without the shim.
188
+ // With the shim's own path left in argv[1] the answer is no, so such an entry
189
+ // loads, starts nothing, and exits 0 — measured on the three channel servers,
190
+ // which all gate main() that way. Plugin MCPs connect at module top level and
191
+ // never read argv, so this is inert for them. ENTRY was captured above, before
192
+ // the rewrite. Extra CLI args keep their positions after the entry slot.
193
+ process.argv = [process.argv[0], ENTRY, ...process.argv.slice(3)];
183
194
  // Run the real MCP server in THIS process. Dynamic import() (not top-level
184
195
  // await — this file compiles to CommonJS) loads the ESM entry; a load-time
185
196
  // failure mirrors the old child spawn-error path (op=error, exit 127). The
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -143,6 +143,45 @@ describe('mcp-spawn-tee in-process shim', () => {
143
143
  expect(readdirSync(logDir).some((f) => /stderr-\d{4}-\d{2}-\d{2}\.log$/.test(f))).toBe(false)
144
144
  })
145
145
 
146
+ // Task 2356 — the shim must be transparent to the entry's own "am I the
147
+ // entry module?" check. Every channel server (whatsapp, webchat, telegram)
148
+ // gates main() on `import.meta.url === pathToFileURL(realpath(argv[1]))`. With
149
+ // the shim's own path left in argv[1] that guard is false, so the server
150
+ // loads, connects nothing, and exits 0 — a channel that is silently dead.
151
+ it('presents argv as a direct `node <entry>` invocation', () => {
152
+ const { stub, logDir } = makeStub(
153
+ 'process.stdout.write(JSON.stringify(process.argv.slice(1))); process.exit(0)',
154
+ )
155
+ const r = runSync(stub, { LOG_DIR: logDir, SESSION_ID: 'argv1', MCP_SPAWN_TEE_NAME: 'stub' })
156
+ expect(JSON.parse(r.stdout)).toEqual([stub])
157
+ })
158
+
159
+ it('the entry-module guard the channel servers use passes under the shim', () => {
160
+ const { stub, logDir } = makeStub(
161
+ [
162
+ 'const { realpathSync } = require("node:fs")',
163
+ 'const { pathToFileURL } = require("node:url")',
164
+ 'const isEntry = process.argv[1] &&',
165
+ ' pathToFileURL(__filename).href === pathToFileURL(realpathSync(process.argv[1])).href',
166
+ 'process.stdout.write(isEntry ? "MAIN-RAN" : "MAIN-SKIPPED")',
167
+ 'process.exit(0)',
168
+ ].join('\n'),
169
+ )
170
+ const r = runSync(stub, { LOG_DIR: logDir, SESSION_ID: 'argv2', MCP_SPAWN_TEE_NAME: 'stub' })
171
+ expect(r.stdout).toBe('MAIN-RAN')
172
+ })
173
+
174
+ it('forwards extra CLI args to the entry, after the entry slot', () => {
175
+ const { stub, logDir } = makeStub(
176
+ 'process.stdout.write(JSON.stringify(process.argv.slice(1))); process.exit(0)',
177
+ )
178
+ const r = spawnSync(process.execPath, [shim, stub, '--flag', 'value'], {
179
+ env: { ...process.env, SESSION_ID: 'argv3', PLATFORM_PORT: '', LOG_DIR: logDir, MCP_SPAWN_TEE_NAME: 'stub' },
180
+ encoding: 'utf8',
181
+ })
182
+ expect(JSON.parse(r.stdout)).toEqual([stub, '--flag', 'value'])
183
+ })
184
+
146
185
  it('does not crash when PLATFORM_PORT is unreachable', () => {
147
186
  const { stub, logDir } = makeStub('process.stderr.write("x\\n"); process.exit(0)')
148
187
  const r = runSync(stub, { LOG_DIR: logDir, SESSION_ID: 'p1', MCP_SPAWN_TEE_NAME: 'stub', PLATFORM_PORT: '59999' })
@@ -189,6 +189,18 @@ process.on("exit", (code) => {
189
189
 
190
190
  emitLifecycle(`op=spawn session=${SESSION_ID8} server=${SERVER_NAME} pid=${process.pid} entry=${ENTRY}`, "info");
191
191
 
192
+ // Task 2356 — present argv as a direct `node <entry>` invocation before the
193
+ // entry loads. The shim is meant to be transparent: an entry that asks the
194
+ // standard "am I the entry module?" question
195
+ // (`import.meta.url === pathToFileURL(realpathSync(process.argv[1])).href`)
196
+ // must get the same answer it gets when Claude Code runs it without the shim.
197
+ // With the shim's own path left in argv[1] the answer is no, so such an entry
198
+ // loads, starts nothing, and exits 0 — measured on the three channel servers,
199
+ // which all gate main() that way. Plugin MCPs connect at module top level and
200
+ // never read argv, so this is inert for them. ENTRY was captured above, before
201
+ // the rewrite. Extra CLI args keep their positions after the entry slot.
202
+ process.argv = [process.argv[0], ENTRY, ...process.argv.slice(3)];
203
+
192
204
  // Run the real MCP server in THIS process. Dynamic import() (not top-level
193
205
  // await — this file compiles to CommonJS) loads the ESM entry; a load-time
194
206
  // failure mirrors the old child spawn-error path (op=error, exit 127). The
@@ -180,6 +180,17 @@ process.on("exit", (code) => {
180
180
  `code=${codeField} signal=${exitSignal ?? "—"} lifetimeMs=${lifetimeMs} stderr-tail=${JSON.stringify(tail)}`, level);
181
181
  });
182
182
  emitLifecycle(`op=spawn session=${SESSION_ID8} server=${SERVER_NAME} pid=${process.pid} entry=${ENTRY}`, "info");
183
+ // Task 2356 — present argv as a direct `node <entry>` invocation before the
184
+ // entry loads. The shim is meant to be transparent: an entry that asks the
185
+ // standard "am I the entry module?" question
186
+ // (`import.meta.url === pathToFileURL(realpathSync(process.argv[1])).href`)
187
+ // must get the same answer it gets when Claude Code runs it without the shim.
188
+ // With the shim's own path left in argv[1] the answer is no, so such an entry
189
+ // loads, starts nothing, and exits 0 — measured on the three channel servers,
190
+ // which all gate main() that way. Plugin MCPs connect at module top level and
191
+ // never read argv, so this is inert for them. ENTRY was captured above, before
192
+ // the rewrite. Extra CLI args keep their positions after the entry slot.
193
+ process.argv = [process.argv[0], ENTRY, ...process.argv.slice(3)];
183
194
  // Run the real MCP server in THIS process. Dynamic import() (not top-level
184
195
  // await — this file compiles to CommonJS) loads the ESM entry; a load-time
185
196
  // failure mirrors the old child spawn-error path (op=error, exit 127). The
@@ -1,46 +1,60 @@
1
1
  ---
2
2
  name: channel-instruction
3
- description: "Set up or change what this account's agent does with an incoming channel message. Triggers when the owner says 'set up what my agent does with incoming messages', 'change how you handle WhatsApp messages', 'write my channel instruction', 'my agent replied to a customer and it shouldn't have', or asks why an incoming message got no answer."
3
+ description: "Set up or change what this account's agent does when a message arrives from someone who is not an account manager. Triggers when the owner says 'set up what my agent does with incoming messages', 'change how you handle WhatsApp messages from customers', 'write my channel instruction', 'my agent replied to a customer and it shouldn't have', or asks why an incoming message got no answer."
4
4
  ---
5
5
 
6
6
  # Channel instruction
7
7
 
8
8
  Invoked by the admin agent directly.
9
9
 
10
- A message arriving on a channel wakes a session and hands it a turn in two parts: a `## Context` block holding the message, and an `## Instruction` block holding this account's standing rules for what to do with it. This skill is about the file that supplies the second block.
10
+ When someone who is not an account manager sends a WhatsApp message to this account's line, the platform wakes each manager's own session with a turn about it. That turn is handed a `## Context` block holding the message and an `## Instruction` block saying what to do with it. This skill is about the file that supplies the second block.
11
11
 
12
12
  ## The file
13
13
 
14
14
  `$ACCOUNT_DIR/agents/admin/INSTRUCTION.md`
15
15
 
16
- Its whole body becomes the `## Instruction` block, verbatim. The platform wraps it in nothing and adds no wording of its own, so that file is the entire instruction the agent gets on every channel turn. All three channels read it: WhatsApp, the admin webchat, and Telegram.
16
+ Its whole body becomes the `## Instruction` block on that turn, verbatim. The platform wraps it in nothing and adds no wording of its own.
17
17
 
18
18
  The install creates the file empty. What goes in it is the account owner's, not the platform's.
19
19
 
20
- ## An empty file refuses every message
20
+ ## Which turns it governs, and which it does not
21
21
 
22
- A file that is missing, unreadable, or holds only whitespace resolves to absent, and an absent instruction refuses the turn: no session wakes and no reply goes out. The sender gets silence.
22
+ It governs one kind of turn: a WhatsApp message from someone who is not an account manager. Nothing else.
23
23
 
24
- Nothing warns the owner in advance. The only trace is two lines in `server.log`, written at the moment a message was dropped:
24
+ **A message the owner sends is unaffected by this file.** Whether they type it in the admin webchat, send it from their own WhatsApp handset, or it arrives from the scheduler, the platform supplies its own directive for that turn and never reads this file. An instruction written here for handling a customer will not change how the agent answers the owner.
25
25
 
26
- - `op=instruction-source accountId=<id> source=absent bytes=0`
26
+ **A public agent does not read this file either.** An account's public agent reads `$ACCOUNT_DIR/agents/<its-slug>/INSTRUCTION.md`, its own. Writing a rule here in the hope of steering the public agent does nothing.
27
+
28
+ If the owner wants to change how the agent behaves toward them personally, this is the wrong file. Identity and manner live in `IDENTITY.md` and `SOUL.md` in the same directory, and they apply to every session.
29
+
30
+ ## An empty file drops the message
31
+
32
+ A file that is missing, unreadable, or holds only whitespace resolves to absent, and an absent instruction refuses the turn: no session wakes, and nothing happens about the message. The sender gets silence and the owner is never told.
33
+
34
+ Nothing warns the owner in advance. The only trace is two lines in `server.log`, written at the moment the message was dropped:
35
+
36
+ - `op=instruction-source accountId=<id> turn=relay source=absent bytes=0`
27
37
  - `op=inbound … delivery=refused` for the same message
28
38
 
29
- So on an account whose channel is connected and whose file is empty, writing this file is the fix for "nobody is getting an answer". If the owner reports that, check this file before anything else.
39
+ So on an account whose WhatsApp line is connected and whose file is empty, writing this file is the fix for "a customer wrote in and nothing happened". If the owner reports that, check this file before anything else. Their own chat will be working normally throughout, which is what makes this hard to spot.
30
40
 
31
41
  ## Writing it
32
42
 
33
43
  Read the file first and tell the owner what is in it. Then find out what they want and write it.
34
44
 
35
- What belongs in it is the account's standing rules for handling an incoming message: who the agent may answer on its own and who it must not, what has to go to a person, what it must never say, how it should sound, and what it must always do before the turn ends.
45
+ What belongs in it is what should happen when a stranger writes in: who the agent may answer on its own and who it must not, what has to be put to a person, what it must never say, and what it must always do before the turn ends.
36
46
 
37
47
  Two rules on how you write it:
38
48
 
39
49
  - Write only what the owner has told you. A rule they did not ask for is someone else's wording in their file, which is exactly what this file exists to keep out.
40
50
  - Never carry rules over from another account or from another owner's file.
41
51
 
42
- An edit takes effect on the next incoming message. No restart, no upgrade, no publish. So a change can be checked by sending one message to the account's channel and reading the reply.
52
+ An edit takes effect on the next incoming message. No restart, no upgrade, no publish. So a change can be checked by having someone message the account's WhatsApp line and reading what the agent does.
53
+
54
+ ## Checking which body a turn actually took
55
+
56
+ Every WhatsApp turn writes one line to `server.log` naming the decision:
43
57
 
44
- ## What this file is not
58
+ `op=instruction-source accountId=<id> agentDir=<dir> turn=relay|direct|public source=account|platform|absent bytes=<n>`
45
59
 
46
- It is not the agent's identity or its personality. Those are `IDENTITY.md` and `SOUL.md` in the same directory, and they apply to every session, not just a channel turn. Editing this file changes what happens to incoming messages and nothing else.
60
+ `turn=relay` with `source=account` means the turn took this file. `turn=direct` with `source=platform` means the owner sent it themselves and this file was not read. If the owner believes a rule they wrote is being applied to their own messages, that line settles it.
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: platform-architecture
3
3
  description: Use when grounding any documented-surface claim about what Maxy ships — plugins, skills, specialists, install/deploy flows, internals. This is the install catalogue, not evidence of what is enabled on the current account. For install state on this account, call `capabilities-here`; for documented surface, cite the `Source:` URL inline.
4
- content-hash: sha256:f14bd26752dbb6d11c821c26753ca43a5bd526f23e370067111fac8a4a6d4579
4
+ content-hash: sha256:4566431caa948d080401758fc742c6b3f6340a0a1d17129ff51c9887836aaf87
5
5
  brand: maxy-code
6
6
  product-name: Maxy
7
7
  ---
@@ -1710,6 +1710,8 @@ Every turn carries a `source`. Three values exist:
1710
1710
  | `schedule` | The scheduler wrote this turn. The named destination did not send it. |
1711
1711
  | `public-relay` | The platform wrote this turn because a public visitor messaged the account. The named sender is a manager being told, not someone who wrote to you. |
1712
1712
 
1713
+ **A `public-relay` turn is the one turn whose `## Instruction` block is the account's own.** Its body is `agents/admin/INSTRUCTION.md`, written by the account owner, and it is the only instruction that turn carries: no standing-rules block rides with it. Every other turn on this line, including one the owner sent from their own handset and one the scheduler wrote, takes the platform's own directive and never reads that file. If the file is empty the relay turn is refused outright, so the message is dropped and nobody is told. A public visitor's own turn reads its agent's file at `agents/<slug>/INSTRUCTION.md`, never the admin's.
1714
+
1713
1715
  A `public-relay` turn also says so in its own first two lines, and states which handset its reply tool reaches. That handset is the manager's, never the visitor's: the turn runs in the manager's own admin session, so a reply written to the visitor is delivered to the manager. If a turn claims a manager wrote something they plainly would not have written, check the source before answering them.
1714
1716
 
1715
1717
  **On a `public-relay` turn the reply tool is the only way to reach anyone.** On an ordinary turn the platform forwards your closing text to the sender when you called no reply tool. On a relay-woken turn it does not, because the relay carries its own reply path. So whatever you decide the manager should receive has to go through the reply tool; text you merely write at the end of the turn reaches nobody, and a decision not to reply is silence rather than a message.
@@ -9,6 +9,15 @@ Invoked by the admin agent directly.
9
9
 
10
10
  This is the platform's release timeline, newest first. Each entry shows the date it shipped and the version it shipped in, so you can tell the operator how current their install is. To compare, read the installed version from `capabilities-here` and match it against the versions below. Keep answers high level and in plain English; this is a summary, not a full commit log.
11
11
 
12
+ ## 2026-08-02 (0.1.546)
13
+
14
+ - When the assistant notifies you on a channel, the outcome of that notification is now recorded, so a message that never arrived can be traced rather than guessed at.
15
+
16
+ ## 2026-08-02 (0.1.545)
17
+
18
+ - The assistant is now instructed differently depending on whether you messaged it directly or the turn was relayed from someone outside your team, so your own standing rules no longer ride along on a relayed turn.
19
+ - Publishing your booking availability now goes through Cloudflare's own interface rather than a command-line tool, and a retry that can no longer finish in time is refused instead of hanging.
20
+
12
21
  ## 2026-08-02 (0.1.544)
13
22
 
14
23
  - Every channel, including Telegram, now takes its turn instruction from the account's own file rather than text fixed in the code, and a build check stops any of the three services hardcoding it again.
@@ -180,6 +180,17 @@ process.on("exit", (code) => {
180
180
  `code=${codeField} signal=${exitSignal ?? "—"} lifetimeMs=${lifetimeMs} stderr-tail=${JSON.stringify(tail)}`, level);
181
181
  });
182
182
  emitLifecycle(`op=spawn session=${SESSION_ID8} server=${SERVER_NAME} pid=${process.pid} entry=${ENTRY}`, "info");
183
+ // Task 2356 — present argv as a direct `node <entry>` invocation before the
184
+ // entry loads. The shim is meant to be transparent: an entry that asks the
185
+ // standard "am I the entry module?" question
186
+ // (`import.meta.url === pathToFileURL(realpathSync(process.argv[1])).href`)
187
+ // must get the same answer it gets when Claude Code runs it without the shim.
188
+ // With the shim's own path left in argv[1] the answer is no, so such an entry
189
+ // loads, starts nothing, and exits 0 — measured on the three channel servers,
190
+ // which all gate main() that way. Plugin MCPs connect at module top level and
191
+ // never read argv, so this is inert for them. ENTRY was captured above, before
192
+ // the rewrite. Extra CLI args keep their positions after the entry slot.
193
+ process.argv = [process.argv[0], ENTRY, ...process.argv.slice(3)];
183
194
  // Run the real MCP server in THIS process. Dynamic import() (not top-level
184
195
  // await — this file compiles to CommonJS) loads the ESM entry; a load-time
185
196
  // failure mirrors the old child spawn-error path (op=error, exit 127). The
@@ -180,6 +180,17 @@ process.on("exit", (code) => {
180
180
  `code=${codeField} signal=${exitSignal ?? "—"} lifetimeMs=${lifetimeMs} stderr-tail=${JSON.stringify(tail)}`, level);
181
181
  });
182
182
  emitLifecycle(`op=spawn session=${SESSION_ID8} server=${SERVER_NAME} pid=${process.pid} entry=${ENTRY}`, "info");
183
+ // Task 2356 — present argv as a direct `node <entry>` invocation before the
184
+ // entry loads. The shim is meant to be transparent: an entry that asks the
185
+ // standard "am I the entry module?" question
186
+ // (`import.meta.url === pathToFileURL(realpathSync(process.argv[1])).href`)
187
+ // must get the same answer it gets when Claude Code runs it without the shim.
188
+ // With the shim's own path left in argv[1] the answer is no, so such an entry
189
+ // loads, starts nothing, and exits 0 — measured on the three channel servers,
190
+ // which all gate main() that way. Plugin MCPs connect at module top level and
191
+ // never read argv, so this is inert for them. ENTRY was captured above, before
192
+ // the rewrite. Extra CLI args keep their positions after the entry slot.
193
+ process.argv = [process.argv[0], ENTRY, ...process.argv.slice(3)];
183
194
  // Run the real MCP server in THIS process. Dynamic import() (not top-level
184
195
  // await — this file compiles to CommonJS) loads the ESM entry; a load-time
185
196
  // failure mirrors the old child spawn-error path (op=error, exit 127). The
@@ -180,6 +180,17 @@ process.on("exit", (code) => {
180
180
  `code=${codeField} signal=${exitSignal ?? "—"} lifetimeMs=${lifetimeMs} stderr-tail=${JSON.stringify(tail)}`, level);
181
181
  });
182
182
  emitLifecycle(`op=spawn session=${SESSION_ID8} server=${SERVER_NAME} pid=${process.pid} entry=${ENTRY}`, "info");
183
+ // Task 2356 — present argv as a direct `node <entry>` invocation before the
184
+ // entry loads. The shim is meant to be transparent: an entry that asks the
185
+ // standard "am I the entry module?" question
186
+ // (`import.meta.url === pathToFileURL(realpathSync(process.argv[1])).href`)
187
+ // must get the same answer it gets when Claude Code runs it without the shim.
188
+ // With the shim's own path left in argv[1] the answer is no, so such an entry
189
+ // loads, starts nothing, and exits 0 — measured on the three channel servers,
190
+ // which all gate main() that way. Plugin MCPs connect at module top level and
191
+ // never read argv, so this is inert for them. ENTRY was captured above, before
192
+ // the rewrite. Extra CLI args keep their positions after the entry slot.
193
+ process.argv = [process.argv[0], ENTRY, ...process.argv.slice(3)];
183
194
  // Run the real MCP server in THIS process. Dynamic import() (not top-level
184
195
  // await — this file compiles to CommonJS) loads the ESM entry; a load-time
185
196
  // failure mirrors the old child spawn-error path (op=error, exit 127). The
@@ -180,6 +180,17 @@ process.on("exit", (code) => {
180
180
  `code=${codeField} signal=${exitSignal ?? "—"} lifetimeMs=${lifetimeMs} stderr-tail=${JSON.stringify(tail)}`, level);
181
181
  });
182
182
  emitLifecycle(`op=spawn session=${SESSION_ID8} server=${SERVER_NAME} pid=${process.pid} entry=${ENTRY}`, "info");
183
+ // Task 2356 — present argv as a direct `node <entry>` invocation before the
184
+ // entry loads. The shim is meant to be transparent: an entry that asks the
185
+ // standard "am I the entry module?" question
186
+ // (`import.meta.url === pathToFileURL(realpathSync(process.argv[1])).href`)
187
+ // must get the same answer it gets when Claude Code runs it without the shim.
188
+ // With the shim's own path left in argv[1] the answer is no, so such an entry
189
+ // loads, starts nothing, and exits 0 — measured on the three channel servers,
190
+ // which all gate main() that way. Plugin MCPs connect at module top level and
191
+ // never read argv, so this is inert for them. ENTRY was captured above, before
192
+ // the rewrite. Extra CLI args keep their positions after the entry slot.
193
+ process.argv = [process.argv[0], ENTRY, ...process.argv.slice(3)];
183
194
  // Run the real MCP server in THIS process. Dynamic import() (not top-level
184
195
  // await — this file compiles to CommonJS) loads the ESM entry; a load-time
185
196
  // failure mirrors the old child spawn-error path (op=error, exit 127). The
@@ -28,6 +28,8 @@ Every turn carries a `source`. Three values exist:
28
28
  | `schedule` | The scheduler wrote this turn. The named destination did not send it. |
29
29
  | `public-relay` | The platform wrote this turn because a public visitor messaged the account. The named sender is a manager being told, not someone who wrote to you. |
30
30
 
31
+ **A `public-relay` turn is the one turn whose `## Instruction` block is the account's own.** Its body is `agents/admin/INSTRUCTION.md`, written by the account owner, and it is the only instruction that turn carries: no standing-rules block rides with it. Every other turn on this line, including one the owner sent from their own handset and one the scheduler wrote, takes the platform's own directive and never reads that file. If the file is empty the relay turn is refused outright, so the message is dropped and nobody is told. A public visitor's own turn reads its agent's file at `agents/<slug>/INSTRUCTION.md`, never the admin's.
32
+
31
33
  A `public-relay` turn also says so in its own first two lines, and states which handset its reply tool reaches. That handset is the manager's, never the visitor's: the turn runs in the manager's own admin session, so a reply written to the visitor is delivered to the manager. If a turn claims a manager wrote something they plainly would not have written, check the source before answering them.
32
34
 
33
35
  **On a `public-relay` turn the reply tool is the only way to reach anyone.** On an ordinary turn the platform forwards your closing text to the sender when you called no reply tool. On a relay-woken turn it does not, because the relay carries its own reply path. So whatever you decide the manager should receive has to go through the reply tool; text you merely write at the end of the turn reaches nobody, and a decision not to reply is silence rather than a message.
@@ -180,6 +180,17 @@ process.on("exit", (code) => {
180
180
  `code=${codeField} signal=${exitSignal ?? "—"} lifetimeMs=${lifetimeMs} stderr-tail=${JSON.stringify(tail)}`, level);
181
181
  });
182
182
  emitLifecycle(`op=spawn session=${SESSION_ID8} server=${SERVER_NAME} pid=${process.pid} entry=${ENTRY}`, "info");
183
+ // Task 2356 — present argv as a direct `node <entry>` invocation before the
184
+ // entry loads. The shim is meant to be transparent: an entry that asks the
185
+ // standard "am I the entry module?" question
186
+ // (`import.meta.url === pathToFileURL(realpathSync(process.argv[1])).href`)
187
+ // must get the same answer it gets when Claude Code runs it without the shim.
188
+ // With the shim's own path left in argv[1] the answer is no, so such an entry
189
+ // loads, starts nothing, and exits 0 — measured on the three channel servers,
190
+ // which all gate main() that way. Plugin MCPs connect at module top level and
191
+ // never read argv, so this is inert for them. ENTRY was captured above, before
192
+ // the rewrite. Extra CLI args keep their positions after the entry slot.
193
+ process.argv = [process.argv[0], ENTRY, ...process.argv.slice(3)];
183
194
  // Run the real MCP server in THIS process. Dynamic import() (not top-level
184
195
  // await — this file compiles to CommonJS) loads the ESM entry; a load-time
185
196
  // failure mirrors the old child spawn-error path (op=error, exit 127). The
@@ -180,6 +180,17 @@ process.on("exit", (code) => {
180
180
  `code=${codeField} signal=${exitSignal ?? "—"} lifetimeMs=${lifetimeMs} stderr-tail=${JSON.stringify(tail)}`, level);
181
181
  });
182
182
  emitLifecycle(`op=spawn session=${SESSION_ID8} server=${SERVER_NAME} pid=${process.pid} entry=${ENTRY}`, "info");
183
+ // Task 2356 — present argv as a direct `node <entry>` invocation before the
184
+ // entry loads. The shim is meant to be transparent: an entry that asks the
185
+ // standard "am I the entry module?" question
186
+ // (`import.meta.url === pathToFileURL(realpathSync(process.argv[1])).href`)
187
+ // must get the same answer it gets when Claude Code runs it without the shim.
188
+ // With the shim's own path left in argv[1] the answer is no, so such an entry
189
+ // loads, starts nothing, and exits 0 — measured on the three channel servers,
190
+ // which all gate main() that way. Plugin MCPs connect at module top level and
191
+ // never read argv, so this is inert for them. ENTRY was captured above, before
192
+ // the rewrite. Extra CLI args keep their positions after the entry slot.
193
+ process.argv = [process.argv[0], ENTRY, ...process.argv.slice(3)];
183
194
  // Run the real MCP server in THIS process. Dynamic import() (not top-level
184
195
  // await — this file compiles to CommonJS) loads the ESM entry; a load-time
185
196
  // failure mirrors the old child spawn-error path (op=error, exit 127). The
@@ -180,6 +180,17 @@ process.on("exit", (code) => {
180
180
  `code=${codeField} signal=${exitSignal ?? "—"} lifetimeMs=${lifetimeMs} stderr-tail=${JSON.stringify(tail)}`, level);
181
181
  });
182
182
  emitLifecycle(`op=spawn session=${SESSION_ID8} server=${SERVER_NAME} pid=${process.pid} entry=${ENTRY}`, "info");
183
+ // Task 2356 — present argv as a direct `node <entry>` invocation before the
184
+ // entry loads. The shim is meant to be transparent: an entry that asks the
185
+ // standard "am I the entry module?" question
186
+ // (`import.meta.url === pathToFileURL(realpathSync(process.argv[1])).href`)
187
+ // must get the same answer it gets when Claude Code runs it without the shim.
188
+ // With the shim's own path left in argv[1] the answer is no, so such an entry
189
+ // loads, starts nothing, and exits 0 — measured on the three channel servers,
190
+ // which all gate main() that way. Plugin MCPs connect at module top level and
191
+ // never read argv, so this is inert for them. ENTRY was captured above, before
192
+ // the rewrite. Extra CLI args keep their positions after the entry slot.
193
+ process.argv = [process.argv[0], ENTRY, ...process.argv.slice(3)];
183
194
  // Run the real MCP server in THIS process. Dynamic import() (not top-level
184
195
  // await — this file compiles to CommonJS) loads the ESM entry; a load-time
185
196
  // failure mirrors the old child spawn-error path (op=error, exit 127). The
@@ -180,6 +180,17 @@ process.on("exit", (code) => {
180
180
  `code=${codeField} signal=${exitSignal ?? "—"} lifetimeMs=${lifetimeMs} stderr-tail=${JSON.stringify(tail)}`, level);
181
181
  });
182
182
  emitLifecycle(`op=spawn session=${SESSION_ID8} server=${SERVER_NAME} pid=${process.pid} entry=${ENTRY}`, "info");
183
+ // Task 2356 — present argv as a direct `node <entry>` invocation before the
184
+ // entry loads. The shim is meant to be transparent: an entry that asks the
185
+ // standard "am I the entry module?" question
186
+ // (`import.meta.url === pathToFileURL(realpathSync(process.argv[1])).href`)
187
+ // must get the same answer it gets when Claude Code runs it without the shim.
188
+ // With the shim's own path left in argv[1] the answer is no, so such an entry
189
+ // loads, starts nothing, and exits 0 — measured on the three channel servers,
190
+ // which all gate main() that way. Plugin MCPs connect at module top level and
191
+ // never read argv, so this is inert for them. ENTRY was captured above, before
192
+ // the rewrite. Extra CLI args keep their positions after the entry slot.
193
+ process.argv = [process.argv[0], ENTRY, ...process.argv.slice(3)];
183
194
  // Run the real MCP server in THIS process. Dynamic import() (not top-level
184
195
  // await — this file compiles to CommonJS) loads the ESM entry; a load-time
185
196
  // failure mirrors the old child spawn-error path (op=error, exit 127). The
@@ -180,6 +180,17 @@ process.on("exit", (code) => {
180
180
  `code=${codeField} signal=${exitSignal ?? "—"} lifetimeMs=${lifetimeMs} stderr-tail=${JSON.stringify(tail)}`, level);
181
181
  });
182
182
  emitLifecycle(`op=spawn session=${SESSION_ID8} server=${SERVER_NAME} pid=${process.pid} entry=${ENTRY}`, "info");
183
+ // Task 2356 — present argv as a direct `node <entry>` invocation before the
184
+ // entry loads. The shim is meant to be transparent: an entry that asks the
185
+ // standard "am I the entry module?" question
186
+ // (`import.meta.url === pathToFileURL(realpathSync(process.argv[1])).href`)
187
+ // must get the same answer it gets when Claude Code runs it without the shim.
188
+ // With the shim's own path left in argv[1] the answer is no, so such an entry
189
+ // loads, starts nothing, and exits 0 — measured on the three channel servers,
190
+ // which all gate main() that way. Plugin MCPs connect at module top level and
191
+ // never read argv, so this is inert for them. ENTRY was captured above, before
192
+ // the rewrite. Extra CLI args keep their positions after the entry slot.
193
+ process.argv = [process.argv[0], ENTRY, ...process.argv.slice(3)];
183
194
  // Run the real MCP server in THIS process. Dynamic import() (not top-level
184
195
  // await — this file compiles to CommonJS) loads the ESM entry; a load-time
185
196
  // failure mirrors the old child spawn-error path (op=error, exit 127). The
@@ -180,6 +180,17 @@ process.on("exit", (code) => {
180
180
  `code=${codeField} signal=${exitSignal ?? "—"} lifetimeMs=${lifetimeMs} stderr-tail=${JSON.stringify(tail)}`, level);
181
181
  });
182
182
  emitLifecycle(`op=spawn session=${SESSION_ID8} server=${SERVER_NAME} pid=${process.pid} entry=${ENTRY}`, "info");
183
+ // Task 2356 — present argv as a direct `node <entry>` invocation before the
184
+ // entry loads. The shim is meant to be transparent: an entry that asks the
185
+ // standard "am I the entry module?" question
186
+ // (`import.meta.url === pathToFileURL(realpathSync(process.argv[1])).href`)
187
+ // must get the same answer it gets when Claude Code runs it without the shim.
188
+ // With the shim's own path left in argv[1] the answer is no, so such an entry
189
+ // loads, starts nothing, and exits 0 — measured on the three channel servers,
190
+ // which all gate main() that way. Plugin MCPs connect at module top level and
191
+ // never read argv, so this is inert for them. ENTRY was captured above, before
192
+ // the rewrite. Extra CLI args keep their positions after the entry slot.
193
+ process.argv = [process.argv[0], ENTRY, ...process.argv.slice(3)];
183
194
  // Run the real MCP server in THIS process. Dynamic import() (not top-level
184
195
  // await — this file compiles to CommonJS) loads the ESM entry; a load-time
185
196
  // failure mirrors the old child spawn-error path (op=error, exit 127). The
@@ -180,6 +180,17 @@ process.on("exit", (code) => {
180
180
  `code=${codeField} signal=${exitSignal ?? "—"} lifetimeMs=${lifetimeMs} stderr-tail=${JSON.stringify(tail)}`, level);
181
181
  });
182
182
  emitLifecycle(`op=spawn session=${SESSION_ID8} server=${SERVER_NAME} pid=${process.pid} entry=${ENTRY}`, "info");
183
+ // Task 2356 — present argv as a direct `node <entry>` invocation before the
184
+ // entry loads. The shim is meant to be transparent: an entry that asks the
185
+ // standard "am I the entry module?" question
186
+ // (`import.meta.url === pathToFileURL(realpathSync(process.argv[1])).href`)
187
+ // must get the same answer it gets when Claude Code runs it without the shim.
188
+ // With the shim's own path left in argv[1] the answer is no, so such an entry
189
+ // loads, starts nothing, and exits 0 — measured on the three channel servers,
190
+ // which all gate main() that way. Plugin MCPs connect at module top level and
191
+ // never read argv, so this is inert for them. ENTRY was captured above, before
192
+ // the rewrite. Extra CLI args keep their positions after the entry slot.
193
+ process.argv = [process.argv[0], ENTRY, ...process.argv.slice(3)];
183
194
  // Run the real MCP server in THIS process. Dynamic import() (not top-level
184
195
  // await — this file compiles to CommonJS) loads the ESM entry; a load-time
185
196
  // failure mirrors the old child spawn-error path (op=error, exit 127). The
@@ -180,6 +180,17 @@ process.on("exit", (code) => {
180
180
  `code=${codeField} signal=${exitSignal ?? "—"} lifetimeMs=${lifetimeMs} stderr-tail=${JSON.stringify(tail)}`, level);
181
181
  });
182
182
  emitLifecycle(`op=spawn session=${SESSION_ID8} server=${SERVER_NAME} pid=${process.pid} entry=${ENTRY}`, "info");
183
+ // Task 2356 — present argv as a direct `node <entry>` invocation before the
184
+ // entry loads. The shim is meant to be transparent: an entry that asks the
185
+ // standard "am I the entry module?" question
186
+ // (`import.meta.url === pathToFileURL(realpathSync(process.argv[1])).href`)
187
+ // must get the same answer it gets when Claude Code runs it without the shim.
188
+ // With the shim's own path left in argv[1] the answer is no, so such an entry
189
+ // loads, starts nothing, and exits 0 — measured on the three channel servers,
190
+ // which all gate main() that way. Plugin MCPs connect at module top level and
191
+ // never read argv, so this is inert for them. ENTRY was captured above, before
192
+ // the rewrite. Extra CLI args keep their positions after the entry slot.
193
+ process.argv = [process.argv[0], ENTRY, ...process.argv.slice(3)];
183
194
  // Run the real MCP server in THIS process. Dynamic import() (not top-level
184
195
  // await — this file compiles to CommonJS) loads the ESM entry; a load-time
185
196
  // failure mirrors the old child spawn-error path (op=error, exit 127). The
@@ -180,6 +180,17 @@ process.on("exit", (code) => {
180
180
  `code=${codeField} signal=${exitSignal ?? "—"} lifetimeMs=${lifetimeMs} stderr-tail=${JSON.stringify(tail)}`, level);
181
181
  });
182
182
  emitLifecycle(`op=spawn session=${SESSION_ID8} server=${SERVER_NAME} pid=${process.pid} entry=${ENTRY}`, "info");
183
+ // Task 2356 — present argv as a direct `node <entry>` invocation before the
184
+ // entry loads. The shim is meant to be transparent: an entry that asks the
185
+ // standard "am I the entry module?" question
186
+ // (`import.meta.url === pathToFileURL(realpathSync(process.argv[1])).href`)
187
+ // must get the same answer it gets when Claude Code runs it without the shim.
188
+ // With the shim's own path left in argv[1] the answer is no, so such an entry
189
+ // loads, starts nothing, and exits 0 — measured on the three channel servers,
190
+ // which all gate main() that way. Plugin MCPs connect at module top level and
191
+ // never read argv, so this is inert for them. ENTRY was captured above, before
192
+ // the rewrite. Extra CLI args keep their positions after the entry slot.
193
+ process.argv = [process.argv[0], ENTRY, ...process.argv.slice(3)];
183
194
  // Run the real MCP server in THIS process. Dynamic import() (not top-level
184
195
  // await — this file compiles to CommonJS) loads the ESM entry; a load-time
185
196
  // failure mirrors the old child spawn-error path (op=error, exit 127). The
@@ -274,7 +274,7 @@ Dispatch results are recorded on the Event node (`lastDispatchResult`, `lastDisp
274
274
 
275
275
  It is **armed by the always-running `platform/ui` server on an interval**, because the Pi has no other periodic surface (the same gap `check-due-events` has). A PID lock at `<configDir>/reconcile-bookings.lock` prevents overlapping runs. It registers **no agent tool** — there is nothing for the agent to call — so the tool-surface registries are untouched.
276
276
 
277
- Env: `PLATFORM_ROOT`, `NEO4J_URI` (and `NEO4J_PASSWORD` or `config/.neo4j-password`). Per account it reads `data/accounts/<id>/calendar-availability.json` (for `bookingDbName`). The Cloudflare credential is read house-only from `config/cloudflare-house.env`; that credential is a minter, so the calendar mints a `calendar-d1` (D1-only) token from it via `cf-token.sh` and shells `wrangler` for the D1 reads/writes on that scoped token, never the minter directly.
277
+ Env: `PLATFORM_ROOT`, `NEO4J_URI` (and `NEO4J_PASSWORD` or `config/.neo4j-password`). Per account it reads `data/accounts/<id>/calendar-availability.json` (for `bookingDbName`). The Cloudflare credential is read house-only from `config/cloudflare-house.env`; that credential is a minter, so the calendar mints a `calendar-d1` (D1-only) token from it via `cf-token.sh` and authenticates the D1 reads/writes on that scoped token, never the minter directly. Those reads and writes go over Cloudflare's D1 HTTP query API (`lib/d1-command.ts`), not a `wrangler` shell-out — no scheduling path shells a CLI, on this loop or on the availability publish. The bounds that the retired spawn carried survive as a 60 s per-request timeout and a 16 MB streamed response cap, and the endpoint is addressed per Cloudflare account, so `CLOUDFLARE_ACCOUNT_ID` is required rather than inferred.
278
278
 
279
279
  Observability: one `[calendar-booking]` line per step keyed by `bookingId` (`reconcile-claim`, `meeting-write`, `notify`, `booked`), and one `[calendar-reconcile] account=… eligibleExamined=… pendingUnreconciled=… pendingOldestAgeMin=… orphanMeetings=…` audit line per account per run. `eligibleExamined` separates "nothing to do" from "verified clean". A `pendingUnreconciled` that persists across runs means those rows are failing to become meetings rather than merely lagging — the `attempt=` count on the claim and failure lines says how many times each has been tried. `orphanMeetings` means a booking meeting has no backing eligible D1 row. `pendingOldestAgeMin` ages that backlog: a count cannot separate a short lag from a nine-hour one. The companion `[calendar-reconcile] op=liveness lastRunAgeMin=… lastAuditAgeMin=… lastSkipReason=… stale=<bool>` line is emitted every ten minutes by the platform/ui server rather than by this script, because the failure it detects is this script not reporting at all. `op=liveness-write-failed` is the one case where that record could not be written; it carries the error body and never stops the run, because a run that worked must not be reported as failed for losing its own bookkeeping.
280
280
 
@@ -180,6 +180,17 @@ process.on("exit", (code) => {
180
180
  `code=${codeField} signal=${exitSignal ?? "—"} lifetimeMs=${lifetimeMs} stderr-tail=${JSON.stringify(tail)}`, level);
181
181
  });
182
182
  emitLifecycle(`op=spawn session=${SESSION_ID8} server=${SERVER_NAME} pid=${process.pid} entry=${ENTRY}`, "info");
183
+ // Task 2356 — present argv as a direct `node <entry>` invocation before the
184
+ // entry loads. The shim is meant to be transparent: an entry that asks the
185
+ // standard "am I the entry module?" question
186
+ // (`import.meta.url === pathToFileURL(realpathSync(process.argv[1])).href`)
187
+ // must get the same answer it gets when Claude Code runs it without the shim.
188
+ // With the shim's own path left in argv[1] the answer is no, so such an entry
189
+ // loads, starts nothing, and exits 0 — measured on the three channel servers,
190
+ // which all gate main() that way. Plugin MCPs connect at module top level and
191
+ // never read argv, so this is inert for them. ENTRY was captured above, before
192
+ // the rewrite. Extra CLI args keep their positions after the entry slot.
193
+ process.argv = [process.argv[0], ENTRY, ...process.argv.slice(3)];
183
194
  // Run the real MCP server in THIS process. Dynamic import() (not top-level
184
195
  // await — this file compiles to CommonJS) loads the ESM entry; a load-time
185
196
  // failure mirrors the old child spawn-error path (op=error, exit 127). The
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=availability-snapshot.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"availability-snapshot.test.d.ts","sourceRoot":"","sources":["../../../src/lib/__tests__/availability-snapshot.test.ts"],"names":[],"mappings":""}