@bli-cockpit/mcp 0.1.57 → 0.1.58

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/README.md CHANGED
@@ -34,7 +34,7 @@ about what an agent can reach.
34
34
 
35
35
  <!-- BEGIN GENERATED verb census — `npm run mcp:readme` -->
36
36
 
37
- **106 of 111 Tower verbs have an MCP twin.**
37
+ **107 of 112 Tower verbs have an MCP twin.**
38
38
  Each tool goes through the SAME door its CLI verb calls, with the same
39
39
  collector device token — never a second route and never a service-role
40
40
  reader. `src/verb-census.test.ts` fails when a verb is in none of the
@@ -95,6 +95,7 @@ three tables below.
95
95
  | `cockpit memory forget` | `forget_memory` (`bli-memory`) | `POST /api/memory/forget` |
96
96
  | `cockpit memory log` | `memory_experience` | `POST /api/memory/experience` |
97
97
  | `cockpit memory profile` | `memory_profile` | `GET /api/memory/profile` |
98
+ | `cockpit memory requeue` | `memory_requeue` | `POST /api/memory/requeue` |
98
99
  | `cockpit memory save` | `save_memory` (`bli-memory`) | `POST /api/memory/save` |
99
100
  | `cockpit memory search` | `search_memory` (`bli-memory`) | `POST /api/memory/search` |
100
101
  | `cockpit memory update` | `update_memory` (`bli-memory`) | `POST /api/memory/update` |
@@ -0,0 +1,23 @@
1
+ /**
2
+ * `memory_requeue` (BLI-4539) — put a window of finished extractions back in
3
+ * the queue, over the same `POST /api/memory/requeue` door
4
+ * `cockpit memory requeue` calls.
5
+ *
6
+ * It exists because the alternative was a one-off script. Between 04:16Z and
7
+ * 11:36Z on 2026-09-21 every insert into the memory store that carried aliases
8
+ * was refused, 122 extraction receipts closed as if nothing were wrong, and
9
+ * recovering them meant somebody hand-writing SQL against production.
10
+ *
11
+ * It lives on `bli-tower` rather than on `bli-memory` for the same reason
12
+ * `memory_profile` does: that server's four tools are the vendor-compatible
13
+ * set the Claude Code plugin expects, and this is ours.
14
+ *
15
+ * **`dryRun` defaults to TRUE, here as at the door.** An apply re-runs one
16
+ * model call per receipt, so an agent that means to count must never be able
17
+ * to spend by omission. The door's super_admin gate is the real boundary; this
18
+ * default is the one that stops an honest mistake.
19
+ */
20
+ import { type ToolDeps } from "./tool-result.js";
21
+ export declare function registerMemoryRequeueTools(server: {
22
+ registerTool: (...args: never[]) => unknown;
23
+ }, deps: ToolDeps): void;
@@ -0,0 +1,107 @@
1
+ /**
2
+ * `memory_requeue` (BLI-4539) — put a window of finished extractions back in
3
+ * the queue, over the same `POST /api/memory/requeue` door
4
+ * `cockpit memory requeue` calls.
5
+ *
6
+ * It exists because the alternative was a one-off script. Between 04:16Z and
7
+ * 11:36Z on 2026-09-21 every insert into the memory store that carried aliases
8
+ * was refused, 122 extraction receipts closed as if nothing were wrong, and
9
+ * recovering them meant somebody hand-writing SQL against production.
10
+ *
11
+ * It lives on `bli-tower` rather than on `bli-memory` for the same reason
12
+ * `memory_profile` does: that server's four tools are the vendor-compatible
13
+ * set the Claude Code plugin expects, and this is ours.
14
+ *
15
+ * **`dryRun` defaults to TRUE, here as at the door.** An apply re-runs one
16
+ * model call per receipt, so an agent that means to count must never be able
17
+ * to spend by omission. The door's super_admin gate is the real boundary; this
18
+ * default is the one that stops an honest mistake.
19
+ */
20
+ import { z } from "zod";
21
+ import { callAgentDoor } from "./agent-door.js";
22
+ import { doorFailureText, errorResult, registrarFor, textResult, withSession, } from "./tool-result.js";
23
+ /** `hook:104, harvest:18` — counts and labels only, never a receipt's content. */
24
+ function split(counts) {
25
+ if (!counts)
26
+ return "none";
27
+ const entries = Object.entries(counts).sort((left, right) => right[1] - left[1] || (left[0] < right[0] ? -1 : 1));
28
+ return entries.length === 0 ? "none" : entries.map(([label, count]) => `${label}:${count}`).join(", ");
29
+ }
30
+ export function registerMemoryRequeueTools(server, deps) {
31
+ registrarFor(server)("memory_requeue", {
32
+ title: "Requeue a window of memory extractions",
33
+ description: "Put finished `memory_extract_jobs` receipts back to `queued` after the store refused their writes — "
34
+ + "the recovery for a `store_failed` window once its cause is fixed. Nothing is ever deleted: the previous "
35
+ + "outcome is kept on the row under `result.requeued_from`. Super admins only. `dryRun` defaults to TRUE "
36
+ + "and an apply re-runs one model call per receipt, so count first. A window is required; there is no "
37
+ + "requeue-everything.",
38
+ inputSchema: {
39
+ since: z
40
+ .string()
41
+ .min(1)
42
+ .describe("ISO instant; receipts that FINISHED at or after it, e.g. 2026-09-21T04:10:00Z."),
43
+ until: z
44
+ .string()
45
+ .min(1)
46
+ .optional()
47
+ .describe("ISO instant; receipts that finished at or before it."),
48
+ source: z
49
+ .enum(["hook", "harvest"])
50
+ .optional()
51
+ .describe("One arm only. Absent means both."),
52
+ reason: z
53
+ .string()
54
+ .min(1)
55
+ .max(80)
56
+ .optional()
57
+ .describe("A short label, written onto the row as `requeued:<label>`. Never a sentence."),
58
+ dryRun: z
59
+ .boolean()
60
+ .optional()
61
+ .describe("Count and write nothing. Defaults to true; pass false to apply."),
62
+ },
63
+ }, async (args) => withSession(deps, async (session) => {
64
+ const dryRun = args.dryRun === undefined ? true : Boolean(args.dryRun);
65
+ const response = await callAgentDoor(session, deps.fetchImpl, "POST", "/api/memory/requeue", {
66
+ since: String(args.since),
67
+ ...(args.until === undefined ? {} : { until: String(args.until) }),
68
+ ...(args.source === undefined ? {} : { source: String(args.source) }),
69
+ ...(args.reason === undefined ? {} : { reason: String(args.reason) }),
70
+ dryRun,
71
+ });
72
+ if (!response.ok)
73
+ return errorResult(doorFailureText("memory_requeue", response));
74
+ const body = response.body;
75
+ const matched = body.matched ?? 0;
76
+ if (matched === 0) {
77
+ // Named, not blank: "nothing matched" and "the store is down" are
78
+ // different facts, and the second one took the branch above.
79
+ return textResult(`No finished extraction receipt is in that window. Nothing to requeue; the receipts table answered.`, { ok: true, dryRun: body.dryRun ?? dryRun, matched: 0, requeued: 0 });
80
+ }
81
+ const head = body.dryRun
82
+ ? `${matched} receipt(s) would be requeued`
83
+ : `${body.requeued ?? 0} of ${matched} receipt(s) requeued`;
84
+ return textResult([
85
+ `${head} (${split(body.bySource)}).`,
86
+ `By reason: ${split(body.byReason)}.`,
87
+ body.coverage === "capped"
88
+ ? "The page ceiling stopped this run before the window ended; run it again for the rest."
89
+ : "",
90
+ body.dryRun
91
+ ? "Nothing was written. Call again with dryRun: false to apply."
92
+ : "The previous result is kept on each row under result.requeued_from.",
93
+ ]
94
+ .filter((line) => line.length > 0)
95
+ .join("\n"), {
96
+ ok: true,
97
+ dryRun: body.dryRun ?? dryRun,
98
+ matched,
99
+ scanned: body.scanned ?? 0,
100
+ requeued: body.requeued ?? 0,
101
+ refused: body.refused ?? 0,
102
+ coverage: body.coverage ?? "unknown",
103
+ bySource: body.bySource ?? {},
104
+ byReason: body.byReason ?? {},
105
+ });
106
+ }));
107
+ }
package/dist/server.js CHANGED
@@ -18,6 +18,7 @@ import { registerNotesWriteTools } from "./notes-write-tools.js";
18
18
  import { registerMemoryConsolidationTools } from "./memory-consolidation-tools.js";
19
19
  import { registerMemoryExperienceTools } from "./memory-experience-tools.js";
20
20
  import { registerMemoryProfileTools } from "./memory-profile-tools.js";
21
+ import { registerMemoryRequeueTools } from "./memory-requeue-tools.js";
21
22
  import { registerModelsTools } from "./models-tools.js";
22
23
  import { registerOpsTools } from "./ops-tools.js";
23
24
  import { registerPagesTools } from "./pages-tools.js";
@@ -430,6 +431,9 @@ export function createServer(deps) {
430
431
  // BLI-4534: the nightly consolidation receipts, twin of `cockpit memory
431
432
  // consolidation`. Read-only, and the pass it reports on never deletes.
432
433
  registerMemoryConsolidationTools(server, { fetchImpl: deps.fetchImpl });
434
+ // BLI-4539: putting a window of refused extractions back, the twin of
435
+ // `cockpit memory requeue`. Dry run by default, super_admin at the door.
436
+ registerMemoryRequeueTools(server, { fetchImpl: deps.fetchImpl });
433
437
  registerSettingsTools(server, { fetchImpl: deps.fetchImpl });
434
438
  registerPagesTools(server, { fetchImpl: deps.fetchImpl });
435
439
  // BLI-3756 batch 2: the WRITES. Same doors, same device token, same refusal
@@ -308,6 +308,10 @@ export const MCP_TWINS = {
308
308
  // `bli-tower` rather than `bli-memory`, because it is a read of a JOB s
309
309
  // counts and not a read or write of the store itself.
310
310
  "memory consolidation": { tool: "memory_consolidation", door: "GET /api/memory/consolidation" },
311
+ // BLI-4539: the recovery verb, after a window of extractions whose writes
312
+ // the store refused. Also on `bli-tower`, for the same reason
313
+ // `memory profile` is: it is ours, not the vendor's four.
314
+ "memory requeue": { tool: "memory_requeue", door: "POST /api/memory/requeue" },
311
315
  "ops status": { tool: "ops_status", door: "GET /api/ops/status" },
312
316
  "ops recompile": { tool: "ops_recompile", door: "POST /api/ops/recompile" },
313
317
  "slack coverage": { tool: "slack_coverage", door: "GET /api/ops/slack/coverage" },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bli-cockpit/mcp",
3
- "version": "0.1.57",
3
+ "version": "0.1.58",
4
4
  "private": false,
5
5
  "description": "bli-tower: an MCP server over BLI Cockpit's agent doors: JARVIS (jarvis_*), documents (docs_*), channels (msg_*), issues (work_*), the daily page (brief_*), meeting notes (notes_*), the ops board (ops_status/slack_*), settings/team/model, Scout and the workbook, plus the legacy event-stream tools (emit_event, get_ticket_timeline, get_active_tickets).",
6
6
  "type": "module",