@rubytech/create-maxy-code 0.1.568 → 0.1.570

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 (56) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/plugins/admin/PLUGIN.md +7 -5
  3. package/payload/platform/plugins/admin/hooks/__tests__/pdf-text-layer-inject.test.sh +177 -0
  4. package/payload/platform/plugins/admin/hooks/__tests__/quote-path-write-gate.test.sh +152 -0
  5. package/payload/platform/plugins/admin/hooks/__tests__/quote-pdf-bash-gate.test.sh +95 -0
  6. package/payload/platform/plugins/admin/hooks/__tests__/quote-render-gate.test.sh +141 -45
  7. package/payload/platform/plugins/admin/hooks/__tests__/quote-render-pdf-conformance.test.sh +114 -0
  8. package/payload/platform/plugins/admin/hooks/pdf-text-layer-inject.sh +137 -0
  9. package/payload/platform/plugins/admin/hooks/quote-path-write-gate.sh +137 -0
  10. package/payload/platform/plugins/admin/hooks/quote-pdf-bash-gate.sh +70 -0
  11. package/payload/platform/plugins/admin/hooks/quote-render-gate.sh +105 -37
  12. package/payload/platform/plugins/admin/hooks/quote-render-pdf-conformance.sh +178 -0
  13. package/payload/platform/plugins/admin/mcp/dist/lib/roster-census.d.ts.map +1 -1
  14. package/payload/platform/plugins/admin/mcp/dist/lib/roster-census.js +50 -4
  15. package/payload/platform/plugins/admin/mcp/dist/lib/roster-census.js.map +1 -1
  16. package/payload/platform/plugins/admin/skills/a4-print-documents/SKILL.md +12 -1
  17. package/payload/platform/plugins/admin/skills/a4-print-documents/pdf-inspect.mjs +113 -0
  18. package/payload/platform/plugins/admin/skills/whats-new/SKILL.md +14 -0
  19. package/payload/platform/plugins/docs/references/admin-session.md +15 -0
  20. package/payload/platform/scripts/__tests__/mailbox-inject-registered.test.sh +19 -43
  21. package/payload/platform/scripts/__tests__/preference-hooks-registered.test.sh +3 -1
  22. package/payload/platform/scripts/__tests__/public-prompt-gate-registered.test.sh +22 -37
  23. package/payload/platform/scripts/__tests__/public-surface-registered.test.sh +12 -35
  24. package/payload/platform/scripts/__tests__/quote-ownership-contract.test.sh +86 -0
  25. package/payload/platform/scripts/lib/__tests__/account-settings-reconcile.test.sh +73 -0
  26. package/payload/platform/scripts/lib/account-settings-reconcile.sh +88 -0
  27. package/payload/platform/scripts/lib/provision-account-dir.sh +11 -144
  28. package/payload/platform/scripts/setup-account.sh +11 -53
  29. package/payload/platform/services/claude-session-manager/dist/account-agent-registry.d.ts +20 -0
  30. package/payload/platform/services/claude-session-manager/dist/account-agent-registry.d.ts.map +1 -1
  31. package/payload/platform/services/claude-session-manager/dist/account-agent-registry.js +78 -1
  32. package/payload/platform/services/claude-session-manager/dist/account-agent-registry.js.map +1 -1
  33. package/payload/platform/services/claude-session-manager/dist/blocked-tool-census.d.ts +129 -0
  34. package/payload/platform/services/claude-session-manager/dist/blocked-tool-census.d.ts.map +1 -0
  35. package/payload/platform/services/claude-session-manager/dist/blocked-tool-census.js +344 -0
  36. package/payload/platform/services/claude-session-manager/dist/blocked-tool-census.js.map +1 -0
  37. package/payload/platform/services/claude-session-manager/dist/index.js +107 -0
  38. package/payload/platform/services/claude-session-manager/dist/index.js.map +1 -1
  39. package/payload/platform/services/claude-session-manager/dist/quote-delivery-reconcile.d.ts +42 -0
  40. package/payload/platform/services/claude-session-manager/dist/quote-delivery-reconcile.d.ts.map +1 -0
  41. package/payload/platform/services/claude-session-manager/dist/quote-delivery-reconcile.js +215 -0
  42. package/payload/platform/services/claude-session-manager/dist/quote-delivery-reconcile.js.map +1 -0
  43. package/payload/platform/services/claude-session-manager/dist/specialist-preference-audit.d.ts +108 -0
  44. package/payload/platform/services/claude-session-manager/dist/specialist-preference-audit.d.ts.map +1 -0
  45. package/payload/platform/services/claude-session-manager/dist/specialist-preference-audit.js +299 -0
  46. package/payload/platform/services/claude-session-manager/dist/specialist-preference-audit.js.map +1 -0
  47. package/payload/platform/templates/account-settings.json +159 -0
  48. package/payload/server/server.js +750 -656
  49. package/payload/platform/scripts/lib/__tests__/account-settings-askgate.test.sh +0 -144
  50. package/payload/platform/scripts/lib/__tests__/account-settings-mailbox-inject.test.sh +0 -166
  51. package/payload/platform/scripts/lib/__tests__/account-settings-pdf-conformance.test.sh +0 -151
  52. package/payload/platform/scripts/lib/__tests__/account-settings-public-surface.test.sh +0 -109
  53. package/payload/platform/scripts/lib/account-settings-askgate.sh +0 -98
  54. package/payload/platform/scripts/lib/account-settings-mailbox-inject.sh +0 -91
  55. package/payload/platform/scripts/lib/account-settings-pdf-conformance.sh +0 -98
  56. package/payload/platform/scripts/lib/account-settings-public-surface.sh +0 -109
@@ -0,0 +1,344 @@
1
+ // Task 2476 — standing blocked-tool census.
2
+ //
3
+ // Claude Code raises a permission card for `dangerousRemoval` even under
4
+ // `permissionMode=bypassPermissions`: that entry in its safety table is
5
+ // `bypassImmune: true`, so no allow rule and no mode pre-approves it. When the
6
+ // card is raised inside a BACKGROUND SUBAGENT, its only prompt surface is the
7
+ // parent session's UI. A WhatsApp or Telegram operator cannot see it, so the
8
+ // session simply stops with the PTY alive.
9
+ //
10
+ // Nothing anywhere records that state. Measured 2026-08-06 on 192.168.88.10:
11
+ // `server.log` returns zero matches for `permission-request`,
12
+ // `permission-prompt`, `awaiting-permission`, `op=permission` and `waitingFor`,
13
+ // and the session-manager tree contains no `waitingFor` at all. No event exists
14
+ // and the failure does not reproduce on demand, so it cannot be detected by a
15
+ // log hung on an action — only by an independent periodic check that
16
+ // reconciles against live on-disk state. Same rationale, and the same module
17
+ // shape, as pty-census.ts (Task 627).
18
+ //
19
+ // The detection primitive is transcript-intrinsic and needs no cooperation from
20
+ // the binary: an assistant record carries `tool_use` blocks with ids, and the
21
+ // following user record carries `tool_result` blocks whose `tool_use_id` names
22
+ // the call it answers. A `tool_use` with no matching `tool_result` is a call
23
+ // that has not returned. That alone does not mean blocked — a call in flight
24
+ // looks identical — so the second condition is that the transcript has not
25
+ // advanced for `staleMs`. Verified against a real subagent transcript:
26
+ // 14 `tool_use`, 14 `tool_result`, zero unmatched.
27
+ //
28
+ // `blockedForMs` is the age of the transcript's last record, not a timer on the
29
+ // call. That is the quantity actually measurable from disk, and it is the one
30
+ // reported, so the line never claims precision it does not have.
31
+ import { existsSync, readdirSync, openSync, closeSync, readSync, statSync } from 'node:fs';
32
+ import { basename, dirname, join } from 'node:path';
33
+ import { forEachLivePty } from './pty-spawner.js';
34
+ import { findExistingJsonlForSessionId } from './jsonl-path.js';
35
+ export const TAG = '[blocked-tool]';
36
+ function parseRecord(line) {
37
+ const trimmed = line.trim();
38
+ if (trimmed.length === 0)
39
+ return null;
40
+ try {
41
+ const obj = JSON.parse(trimmed);
42
+ return obj && typeof obj === 'object' && !Array.isArray(obj) ? obj : null;
43
+ }
44
+ catch {
45
+ return null;
46
+ }
47
+ }
48
+ function contentBlocks(rec) {
49
+ const msg = rec.message;
50
+ if (!msg || typeof msg !== 'object')
51
+ return [];
52
+ const content = msg.content;
53
+ if (!Array.isArray(content))
54
+ return [];
55
+ return content.filter((b) => !!b && typeof b === 'object' && !Array.isArray(b));
56
+ }
57
+ /** PURE. Every `tool_use` id in these lines with no matching `tool_result`.
58
+ * Order is first-sighting order. Malformed lines are skipped, not thrown on:
59
+ * a JSONL read can land mid-append. A `tool_use` carrying no `name` is
60
+ * reported as `unknown` rather than dropped — an unnamed blocked call is
61
+ * still a blocked call. */
62
+ export function unmatchedToolUses(lines) {
63
+ const uses = [];
64
+ const seenUseIds = new Set();
65
+ const results = new Set();
66
+ for (const line of lines) {
67
+ const rec = parseRecord(line);
68
+ if (!rec)
69
+ continue;
70
+ for (const block of contentBlocks(rec)) {
71
+ if (block.type === 'tool_use' && typeof block.id === 'string') {
72
+ if (seenUseIds.has(block.id))
73
+ continue;
74
+ seenUseIds.add(block.id);
75
+ uses.push({ id: block.id, name: typeof block.name === 'string' ? block.name : 'unknown' });
76
+ }
77
+ else if (block.type === 'tool_result' && typeof block.tool_use_id === 'string') {
78
+ results.add(block.tool_use_id);
79
+ }
80
+ }
81
+ }
82
+ return uses.filter((u) => !results.has(u.id));
83
+ }
84
+ /** PURE. Which unmatched calls are blocked. Blocked means unmatched AND the
85
+ * transcript has not advanced for `staleMs`. Without the staleness term every
86
+ * in-flight call would be reported, which would make the signal useless. */
87
+ export function blockedFromScans(sessionId, scans, nowMs, staleMs) {
88
+ const out = [];
89
+ for (const scan of scans) {
90
+ const age = nowMs - scan.mtimeMs;
91
+ if (age < staleMs)
92
+ continue;
93
+ for (const u of scan.unmatched) {
94
+ out.push({ sessionId, agentId: scan.agentId, toolUseId: u.id, tool: u.name, blockedForMs: age });
95
+ }
96
+ }
97
+ return out;
98
+ }
99
+ function short(sessionId) {
100
+ return sessionId.slice(0, 8);
101
+ }
102
+ /** PURE. Emitted every tick INCLUDING zero, so a census that stopped ticking is
103
+ * distinguishable from one that found nothing. `sessions` is separate from
104
+ * `scanned` on purpose: `sessions=3 scanned=0` is a census that could not
105
+ * resolve a transcript for any live session, which without the first field
106
+ * reads exactly like a quiet box. */
107
+ export function formatBlockedCensus(s) {
108
+ return `${TAG} op=census sessions=${s.liveSessionIds.length} scanned=${s.scanned} blocked=${s.blocked.length}`;
109
+ }
110
+ /** PURE. */
111
+ export function formatBlocked(c) {
112
+ return `${TAG} op=blocked sessionId=${short(c.sessionId)} agentId=${c.agentId} toolUseId=${c.toolUseId} tool=${c.tool} blockedForMs=${c.blockedForMs}`;
113
+ }
114
+ /** PURE. Same ids as its `op=blocked` twin, so the pair is greppable. Emitted
115
+ * ONLY when the session is still live and the result landed. */
116
+ export function formatCleared(c) {
117
+ return `${TAG} op=cleared sessionId=${short(c.sessionId)} agentId=${c.agentId} toolUseId=${c.toolUseId} tool=${c.tool} blockedForMs=${c.blockedForMs}`;
118
+ }
119
+ /** PURE. The other way a blocked call leaves the reported set: its session
120
+ * stopped being live. The call did NOT resolve, and saying `op=cleared` here
121
+ * would tell the operator the block lifted when the session actually went
122
+ * away with the card still unanswered. */
123
+ export function formatSessionGone(c) {
124
+ return `${TAG} op=session-gone sessionId=${short(c.sessionId)} agentId=${c.agentId} toolUseId=${c.toolUseId} tool=${c.tool} blockedForMs=${c.blockedForMs}`;
125
+ }
126
+ export const DEFAULT_STALE_MS = 120_000;
127
+ export const DEFAULT_NOTIFY_AFTER_MS = 180_000;
128
+ /** A `tool_result` always follows its `tool_use` in the same file, so a
129
+ * contiguous tail containing the use contains its result too — unless one
130
+ * result exceeds this window, in which case that one call reads as unmatched.
131
+ * 1 MB is a chosen bound, not a measured one; the failure mode it admits is a
132
+ * false `op=blocked` on a call whose result was larger than the window. */
133
+ export const TAIL_BYTES = 1024 * 1024;
134
+ /** IMPURE. `scanned` counts transcripts actually READ, so a session whose
135
+ * files vanished does not inflate the count into looking like coverage. */
136
+ export function collectBlockedCensus(io, staleMs, tailBytes) {
137
+ let scanned = 0;
138
+ const blocked = [];
139
+ const liveSessionIds = io.listSessionIds();
140
+ for (const sessionId of liveSessionIds) {
141
+ const scans = [];
142
+ for (const t of io.listTranscripts(sessionId)) {
143
+ const tail = io.readTail(t.path, tailBytes);
144
+ if (!tail)
145
+ continue;
146
+ scanned += 1;
147
+ scans.push({
148
+ agentId: t.agentId,
149
+ path: t.path,
150
+ mtimeMs: tail.mtimeMs,
151
+ unmatched: unmatchedToolUses(tail.lines),
152
+ });
153
+ }
154
+ blocked.push(...blockedFromScans(sessionId, scans, io.now(), staleMs));
155
+ }
156
+ return { scanned, blocked, liveSessionIds };
157
+ }
158
+ /** `claudeConfigDir` is the state root (`CLAUDE_CONFIG_DIR`), NOT a spawn cwd.
159
+ * Task 1490 re-points the manager's `spawnCwd` per sub-account spawn, so a
160
+ * client session's transcript is filed under a slug derived from the
161
+ * sub-account directory rather than the house cwd (http-server.ts:815 states
162
+ * this: reading `deps.spawnCwd` "would always miss the file"). Keying this
163
+ * census on one cwd would make it blind to exactly the sub-account sessions it
164
+ * was written for, so it resolves each session through the all-slug scan
165
+ * instead. */
166
+ export function defaultBlockedCensusIo(claudeConfigDir) {
167
+ return {
168
+ listSessionIds() {
169
+ const out = [];
170
+ forEachLivePty((t) => {
171
+ if (!t.exited)
172
+ out.push(t.sessionId);
173
+ });
174
+ return out;
175
+ },
176
+ listTranscripts(sessionId) {
177
+ const rows = [];
178
+ const own = findExistingJsonlForSessionId(claudeConfigDir, sessionId);
179
+ if (!own)
180
+ return rows;
181
+ rows.push({ agentId: '—', path: own });
182
+ // Verified on disk: subagent transcripts live in a directory NAMED for
183
+ // the session, sibling to `<sessionId>.jsonl`, not inside it.
184
+ const subDir = join(dirname(own), sessionId, 'subagents');
185
+ if (!existsSync(subDir))
186
+ return rows;
187
+ let names;
188
+ try {
189
+ names = readdirSync(subDir);
190
+ }
191
+ catch {
192
+ return rows;
193
+ }
194
+ for (const n of names) {
195
+ if (!n.startsWith('agent-') || !n.endsWith('.jsonl'))
196
+ continue;
197
+ rows.push({ agentId: basename(n, '.jsonl'), path: join(subDir, n) });
198
+ }
199
+ return rows;
200
+ },
201
+ readTail(path, maxBytes) {
202
+ let fd = null;
203
+ try {
204
+ const st = statSync(path);
205
+ const start = st.size > maxBytes ? st.size - maxBytes : 0;
206
+ const length = st.size - start;
207
+ if (length === 0)
208
+ return { lines: [], mtimeMs: st.mtimeMs };
209
+ fd = openSync(path, 'r');
210
+ const buf = Buffer.allocUnsafe(length);
211
+ // The file can be truncated (rotation) between statSync and readSync,
212
+ // so honour bytesRead: allocUnsafe leaves the unwritten tail as
213
+ // uninitialised memory, and decoding that would inject garbage lines.
214
+ const bytesRead = readSync(fd, buf, 0, length, start);
215
+ const text = buf.subarray(0, bytesRead).toString('utf8');
216
+ const lines = text.split('\n');
217
+ // A non-zero start almost certainly lands mid-record; that fragment is
218
+ // not parseable and must not be mistaken for a malformed transcript.
219
+ if (start > 0)
220
+ lines.shift();
221
+ return { lines, mtimeMs: st.mtimeMs };
222
+ }
223
+ catch {
224
+ return null;
225
+ }
226
+ finally {
227
+ if (fd !== null)
228
+ closeSync(fd);
229
+ }
230
+ },
231
+ now: () => Date.now(),
232
+ };
233
+ }
234
+ /** A blocked call's identity across ticks. */
235
+ function callKey(c) {
236
+ return `${c.sessionId} ${c.toolUseId}`;
237
+ }
238
+ export function createBlockedToolCensus(deps) {
239
+ const staleMs = deps.staleMs ?? DEFAULT_STALE_MS;
240
+ const notifyAfterMs = deps.notifyAfterMs ?? DEFAULT_NOTIFY_AFTER_MS;
241
+ const io = deps.io ?? defaultBlockedCensusIo(deps.claudeConfigDir);
242
+ let timer = null;
243
+ let lastSnapshot = null;
244
+ /** Reported blocked and not yet cleared. Held here, not at module scope, so
245
+ * two censuses in one process (tests) cannot cross-contaminate. */
246
+ const reported = new Map();
247
+ /** Notified once. In-memory by design: after a manager restart nothing else
248
+ * records that the call is blocked, so re-notifying once is correct. */
249
+ const notified = new Set();
250
+ function tickOnce() {
251
+ const snapshot = collectBlockedCensus(io, staleMs, TAIL_BYTES);
252
+ lastSnapshot = snapshot;
253
+ deps.logger(formatBlockedCensus(snapshot));
254
+ const live = new Set(snapshot.blocked.map(callKey));
255
+ for (const call of snapshot.blocked) {
256
+ const key = callKey(call);
257
+ if (!reported.has(key))
258
+ deps.logger(formatBlocked(call));
259
+ reported.set(key, call);
260
+ if (call.blockedForMs >= notifyAfterMs && !notified.has(key)) {
261
+ notified.add(key);
262
+ const notify = deps.notify;
263
+ if (notify) {
264
+ void notify(call)
265
+ .then((r) => deps.logger(`${TAG} op=blocked-notify channel=${r.channel} delivered=${r.delivered}`))
266
+ .catch((err) => deps.logger(`${TAG} op=blocked-notify channel=none delivered=false error=${err instanceof Error ? err.message : String(err)}`));
267
+ }
268
+ }
269
+ }
270
+ const liveSessions = new Set(snapshot.liveSessionIds);
271
+ for (const [key, call] of reported) {
272
+ if (live.has(key))
273
+ continue;
274
+ // Two distinct exits from the reported set, never conflated: the result
275
+ // landed on a still-live session (cleared), or the session stopped being
276
+ // live with the card still unanswered (gone).
277
+ deps.logger(liveSessions.has(call.sessionId) ? formatCleared(call) : formatSessionGone(call));
278
+ reported.delete(key);
279
+ notified.delete(key);
280
+ }
281
+ return snapshot;
282
+ }
283
+ return {
284
+ tickOnce,
285
+ latest: () => lastSnapshot,
286
+ start() {
287
+ if (timer)
288
+ return;
289
+ timer = setInterval(() => {
290
+ try {
291
+ tickOnce();
292
+ }
293
+ catch (err) {
294
+ deps.logger(`${TAG} op=census-tick-failed message=${err instanceof Error ? err.message : String(err)}`);
295
+ }
296
+ }, deps.intervalMs);
297
+ if (typeof timer.unref === 'function')
298
+ timer.unref();
299
+ deps.logger(`${TAG} op=census-started intervalMs=${deps.intervalMs} staleMs=${staleMs} notifyAfterMs=${notifyAfterMs}`);
300
+ },
301
+ stop() {
302
+ if (timer) {
303
+ clearInterval(timer);
304
+ timer = null;
305
+ }
306
+ },
307
+ };
308
+ }
309
+ /** Production notifier. POSTs to the brand UI's loopback route, the same
310
+ * transport `postLogLineViaFetch` uses. A throw or a non-200 resolves to
311
+ * `delivered:false` rather than rejecting: the census logs the outcome either
312
+ * way, and a send that failed silently is the blind spot this task closes. */
313
+ export function postBlockedNotifyViaFetch(opts) {
314
+ const url = `http://127.0.0.1:${opts.uiPort}/api/admin/session-blocked`;
315
+ return async (c) => {
316
+ try {
317
+ const res = await fetch(url, {
318
+ method: 'POST',
319
+ headers: { 'content-type': 'application/json' },
320
+ body: JSON.stringify({
321
+ sessionId: c.sessionId,
322
+ agentId: c.agentId,
323
+ toolUseId: c.toolUseId,
324
+ tool: c.tool,
325
+ blockedForMs: c.blockedForMs,
326
+ }),
327
+ });
328
+ if (!res.ok) {
329
+ opts.logger(`${TAG} op=notify-post-rejected status=${res.status}`);
330
+ return { delivered: false, channel: 'none' };
331
+ }
332
+ const json = (await res.json());
333
+ return {
334
+ delivered: json.delivered === true,
335
+ channel: typeof json.channel === 'string' ? json.channel : 'none',
336
+ };
337
+ }
338
+ catch (err) {
339
+ opts.logger(`${TAG} op=notify-post-failed error=${JSON.stringify(err instanceof Error ? err.message : String(err))}`);
340
+ return { delivered: false, channel: 'none' };
341
+ }
342
+ };
343
+ }
344
+ //# sourceMappingURL=blocked-tool-census.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blocked-tool-census.js","sourceRoot":"","sources":["../src/blocked-tool-census.ts"],"names":[],"mappings":"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"}
@@ -39,6 +39,7 @@ import { createRoleCensus } from './role-census.js';
39
39
  import { createPublicPromptSurfaceCensus } from './public-prompt-surface.js';
40
40
  import { createPublicKnowledgeAudit, readPublicAgentKnowledge } from './public-knowledge-audit.js';
41
41
  import { createPtyCensus } from './pty-census.js';
42
+ import { createBlockedToolCensus, postBlockedNotifyViaFetch } from './blocked-tool-census.js';
42
43
  import { createSliceMemoryPolicy } from './slice-memory-policy.js';
43
44
  import { emitRootlessCensus, createRootlessAudit } from './rootless-client-audit.js';
44
45
  import { createPreferenceAudit } from './preference-reconciliation-audit.js';
@@ -48,9 +49,11 @@ import { createPassiveIntakeAudit } from './passive-intake-audit.js';
48
49
  import { createPassiveDispatchPrune } from './passive-dispatch-prune.js';
49
50
  import { createXacctCensus } from './cross-account-census.js';
50
51
  import { createAccountDirSchemaReconcile } from './account-dir-schema-reconcile.js';
52
+ import { createQuoteDeliveryReconcile } from './quote-delivery-reconcile.js';
51
53
  import { createSessionCapAudit } from './session-cap-audit.js';
52
54
  import { createSkillStalenessAudit, collectPlatformSkillMtimes, collectPremiumSkillMtimes, resolveEnabledPlugins, buildLiveSessionRefs, parseProcStartMs, } from './skill-staleness-audit.js';
53
55
  import { createPublicSurfaceAudit, parseMcpLogServerName } from './public-surface-audit.js';
56
+ import { createSpecialistPreferenceAudit, readLinesSync } from './specialist-preference-audit.js';
54
57
  import { projectsDirForCwd, sessionsDir, jsonlPathForSessionId, slugForExistingJsonl, jsonlPathForSlug, sidecarPathForSlug } from './jsonl-path.js';
55
58
  import { readSidecar } from './session-sidecar.js';
56
59
  import { runSystemPromptSelfTest, runIdentityDriftAssertion, runIdentityForbiddenTokenCheck } from './system-prompt.js';
@@ -885,6 +888,76 @@ async function main() {
885
888
  },
886
889
  });
887
890
  publicSurfaceAudit.start();
891
+ // Task 2469 — the second question about a specialist. The registry reconcile
892
+ // beside the spawn answers whether one CAN be reached; this answers whether
893
+ // it WAS. Both were needed: four authored clerks owned the works orders,
894
+ // invoices and client emails on a live account while content-producer
895
+ // produced all of them, and every reachability signal read green throughout.
896
+ //
897
+ // `<projectDir>/<sessionId>/subagents/` is the only surface that ties a write
898
+ // to a specialist — the parent transcript carries none of a subagent's tool
899
+ // calls — so the walk is over that directory across every account on the
900
+ // install. 300s rather than the 60s beside it: this reads transcripts, and
901
+ // the mtime guard means a steady state costs one stat per file.
902
+ const preferenceProjectsRoot = dirname(projectsDirForCwd(config.spawnCwd));
903
+ /** sessionId -> its `subagents/` directory, rebuilt by `listSessions` at the
904
+ * top of every tick. Held here rather than in the audit because resolving a
905
+ * session id to a path is install layout, which is this file's business. */
906
+ const subagentDirs = new Map();
907
+ const specialistPreferenceAudit = createSpecialistPreferenceAudit({
908
+ intervalMs: 300_000,
909
+ logger: log,
910
+ listSessions: () => {
911
+ subagentDirs.clear();
912
+ let accountProjectDirs;
913
+ try {
914
+ accountProjectDirs = readdirSync(preferenceProjectsRoot);
915
+ }
916
+ catch {
917
+ return [];
918
+ }
919
+ for (const projectDir of accountProjectDirs) {
920
+ const abs = join(preferenceProjectsRoot, projectDir);
921
+ let sessions;
922
+ try {
923
+ sessions = readdirSync(abs);
924
+ }
925
+ catch {
926
+ continue;
927
+ }
928
+ for (const sessionId of sessions) {
929
+ const dir = join(abs, sessionId, 'subagents');
930
+ if (existsSync(dir))
931
+ subagentDirs.set(sessionId, dir);
932
+ }
933
+ }
934
+ return Array.from(subagentDirs.keys());
935
+ },
936
+ listTranscripts: (sessionId) => {
937
+ const dir = subagentDirs.get(sessionId);
938
+ if (!dir)
939
+ return [];
940
+ // A throw here is caught by the audit, which names the session rather
941
+ // than losing the whole run to one unreadable directory.
942
+ return readdirSync(dir)
943
+ .filter((f) => f.startsWith('agent-') && f.endsWith('.jsonl'))
944
+ .map((f) => ({
945
+ jsonl: join(dir, f),
946
+ meta: join(dir, `${f.slice(0, -'.jsonl'.length)}.meta.json`),
947
+ mtimeMs: statSync(join(dir, f)).mtimeMs,
948
+ }));
949
+ },
950
+ readMeta: (path) => {
951
+ try {
952
+ return readFileSync(path, 'utf8');
953
+ }
954
+ catch {
955
+ return null;
956
+ }
957
+ },
958
+ readLines: (path) => readLinesSync(path),
959
+ });
960
+ specialistPreferenceAudit.start();
888
961
  // Task 2285 — standing audit for public-agent knowledge curation. A
889
962
  // `knowledgeKeywords` subscription nobody asked for is dormant until matching
890
963
  // content lands, and a KNOWLEDGE.md that grows after deployment emits nothing
@@ -1053,6 +1126,21 @@ async function main() {
1053
1126
  intervalMs: 300_000,
1054
1127
  });
1055
1128
  schemaReconcile.start();
1129
+ // Task 2467 — standing reconcile of delivered client quotes against their
1130
+ // render receipts. The three PreToolUse quote gates refuse the print, the
1131
+ // Bash conversion and the write, but all three fail open on an uninspectable
1132
+ // envelope and none can see a delivery route nobody enumerated. This pass is
1133
+ // the backstop, and it is the only instrument that can see the failure at
1134
+ // all: on 2026-08-05 a hand-authored client quote was printed and emailed
1135
+ // while the bypass counter for that day sat at zero, so no transition log
1136
+ // could have shown it. Emits one line per unreconciled document plus one
1137
+ // summary per run, zero count included. Purely filesystem — no Neo4j.
1138
+ const quoteReconcile = createQuoteDeliveryReconcile({
1139
+ accountsDir: dirname(config.accountDir),
1140
+ logger: log,
1141
+ intervalMs: 900_000,
1142
+ });
1143
+ quoteReconcile.start();
1056
1144
  // Task 183 — recorder JSONL tail. Posts [recorder-session-init] and
1057
1145
  // [recorder-turn] lines for every database-operator specialist spawn,
1058
1146
  // routed through the platform UI's /api/admin/log-ingest loopback
@@ -1075,6 +1163,25 @@ async function main() {
1075
1163
  postLogLine: postLogLineViaFetch({ uiPort: uiPortForTail || '0', logger: log }),
1076
1164
  logger: log,
1077
1165
  });
1166
+ // Task 2476 — standing blocked-tool census. A `dangerousRemoval` card is
1167
+ // raised even under bypassPermissions (that entry is `bypassImmune: true`),
1168
+ // and when it is raised inside a background subagent its only prompt surface
1169
+ // is the parent session UI, which a channel operator cannot reach. Nothing
1170
+ // emits an event for that state and it does not reproduce on demand, so it is
1171
+ // detected by reconciling unmatched `tool_use` against `tool_result` across
1172
+ // each live session's transcripts every 60s — never by a log hung on an
1173
+ // action. Registered here rather than beside the other 60s censuses above
1174
+ // because it shares the recorder's `uiPortForTail`, declared just above.
1175
+ const blockedToolCensus = createBlockedToolCensus({
1176
+ // The STATE ROOT, not a spawn cwd: Task 1490 re-points spawnCwd per
1177
+ // sub-account spawn, so a cwd-keyed census would be blind to every client
1178
+ // session (http-server.ts:815).
1179
+ claudeConfigDir: claudeConfigDirResolved,
1180
+ intervalMs: 60_000,
1181
+ logger: log,
1182
+ notify: postBlockedNotifyViaFetch({ uiPort: uiPortForTail || '0', logger: log }),
1183
+ });
1184
+ blockedToolCensus.start();
1078
1185
  // Task 278 — derives effectivePermissionMode from the per-session
1079
1186
  // auto-mode debug capture. http-server's jsonl-event subscriber drives it.
1080
1187
  const permissionModeTail = createPermissionModeTail({ logger: log });