@bli-cockpit/mcp 0.1.4 → 0.1.5

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
@@ -127,6 +127,14 @@ JARVIS itself — the same assistant Tower web chat, the Slack DM and
127
127
  | `jarvis_dispatch` | `POST /api/jarvis/cli` | The coding arm (BLI-2981). Two calls: once for the plan, once with the person's approval code. |
128
128
  | `jarvis_check` | `POST /api/jarvis/cli` | Where a dispatched coding task got to. Spends no approval. |
129
129
 
130
+ **One conversation per call (BLI-3786).** A thread is single-writer: two turns
131
+ in the same conversation at the same time read each other's messages, and one
132
+ can answer the other's question. So every `jarvis_*` tool sends NO thread
133
+ unless the caller named one, the door mints a conversation for that call alone,
134
+ and the answer's `thread_id` is the name it used — pass that back as `thread`
135
+ to continue deliberately. `"main"` is the shared terminal default nobody chose
136
+ and is never sent from here.
137
+
130
138
  `jarvis_ask` answers with the SAME object `cockpit jarvis --json` prints —
131
139
  `answer`, `sources`, `turn_id`, `thread_id`, `trace_thread_id`, `degraded`,
132
140
  `degraded_reasons` — so a script and an agent read one contract. A worked
@@ -136,7 +144,7 @@ example:
136
144
  jarvis_ask { "question": "what did Brandon ship yesterday?" }
137
145
  -> { "answer": "Two pull requests went in…\n\nSource: BLI-3654 — …",
138
146
  "sources": ["Source: BLI-3654 — …"],
139
- "turn_id": "0f3c…", "thread_id": "main",
147
+ "turn_id": "0f3c…", "thread_id": "agent-6b1e…",
140
148
  "degraded": false, "degraded_reasons": [] }
141
149
 
142
150
  jarvis_trace { "turn_id": "0f3c…" } # or "last"
@@ -24,6 +24,15 @@
24
24
  * `jarvis_ask` answers with `jarvis-answer-envelope.ts` — the same object
25
25
  * `cockpit jarvis --json` prints. A script and an agent read one shape.
26
26
  *
27
+ * ## One conversation per call (BLI-3786)
28
+ *
29
+ * A thread is single-writer: two turns in one conversation at the same time
30
+ * read each other's messages, and one can answer the other's question. Every
31
+ * tool here therefore sends NO thread unless the caller named one, and the
32
+ * door mints a conversation for that call alone and reports it as `thread_id`.
33
+ * Passing that id back is how an agent continues deliberately. `"main"` is the
34
+ * shared terminal default nobody chose and is never sent from here.
35
+ *
27
36
  * ## The approval code, and what this surface can and cannot prove
28
37
  *
29
38
  * The coding arm's gate (BLI-2981) has two locks: the code is an HMAC over the
@@ -24,6 +24,15 @@
24
24
  * `jarvis_ask` answers with `jarvis-answer-envelope.ts` — the same object
25
25
  * `cockpit jarvis --json` prints. A script and an agent read one shape.
26
26
  *
27
+ * ## One conversation per call (BLI-3786)
28
+ *
29
+ * A thread is single-writer: two turns in one conversation at the same time
30
+ * read each other's messages, and one can answer the other's question. Every
31
+ * tool here therefore sends NO thread unless the caller named one, and the
32
+ * door mints a conversation for that call alone and reports it as `thread_id`.
33
+ * Passing that id back is how an agent continues deliberately. `"main"` is the
34
+ * shared terminal default nobody chose and is never sent from here.
35
+ *
27
36
  * ## The approval code, and what this surface can and cannot prove
28
37
  *
29
38
  * The coding arm's gate (BLI-2981) has two locks: the code is an HMAC over the
@@ -70,10 +79,12 @@ export function registerJarvisTools(server, deps) {
70
79
  title: "Ask JARVIS",
71
80
  description: "Asks JARVIS one question and returns its answer, the Source: lines behind it, this turn's "
72
81
  + "turn_id and the conversation's thread_id. Same JARVIS, same tool belt and same evidence "
73
- + "rules as Tower web chat, the Slack DM and `cockpit jarvis`. Pass thread to continue an "
74
- + "earlier conversation; pass the returned turn_id to jarvis_trace to see how the answer was "
75
- + "reached. JARVIS speaks as the person this machine is paired to and cannot be made to "
76
- + "speak as anyone else.",
82
+ + "rules as Tower web chat, the Slack DM and `cockpit jarvis`. A thread is SINGLE-WRITER: "
83
+ + "omit thread and this turn gets a FRESH conversation of its own, so two questions asked at "
84
+ + "the same time can never read each other's; pass the thread_id the answer returned to "
85
+ + "continue that conversation deliberately. Pass the returned turn_id to jarvis_trace to see "
86
+ + "how the answer was reached. JARVIS speaks as the person this machine is paired to and "
87
+ + "cannot be made to speak as anyone else.",
77
88
  inputSchema: {
78
89
  question: z.string().min(1).max(50_000).describe("What to ask, in plain words."),
79
90
  thread: z
@@ -81,7 +92,9 @@ export function registerJarvisTools(server, deps) {
81
92
  .min(1)
82
93
  .max(200)
83
94
  .optional()
84
- .describe('The conversation to continue. Omit for "main", the default terminal thread.'),
95
+ .describe("The thread_id of a conversation to continue, exactly as an earlier answer returned "
96
+ + 'it. Omit for a fresh conversation for this call alone. "main" is the shared '
97
+ + "default nobody chose, so it is treated as naming nothing here."),
85
98
  subject: z
86
99
  .string()
87
100
  .min(1)
@@ -105,7 +118,10 @@ export function registerJarvisTools(server, deps) {
105
118
  }, async (args) => withSession(deps, async (session) => {
106
119
  const { result } = await takeTurn(deps, session, "jarvis_ask", {
107
120
  question: String(args.question ?? ""),
108
- thread: typeof args.thread === "string" ? args.thread : "main",
121
+ // BLI-3786: no thread means no thread. The door mints one for this
122
+ // call and reports it as thread_id; sending "main" here would put
123
+ // every agent on one conversation, which is the bug this replaced.
124
+ ...(typeof args.thread === "string" ? { thread: args.thread } : {}),
109
125
  ...(typeof args.subject === "string" ? { subject: args.subject } : {}),
110
126
  ...(typeof args.date === "string" ? { date: args.date } : {}),
111
127
  ...(typeof args.model === "string" ? { model: args.model } : {}),
@@ -201,7 +217,13 @@ export function registerJarvisTools(server, deps) {
201
217
  .optional()
202
218
  .describe("The 8-character hex confirmation code THE PERSON fetched for this proposal and handed "
203
219
  + "to you. Omit it on the first call — that call is never given one."),
204
- thread: z.string().min(1).max(200).optional().describe('Defaults to "main".'),
220
+ thread: z
221
+ .string()
222
+ .min(1)
223
+ .max(200)
224
+ .optional()
225
+ .describe("The thread_id of a conversation to continue. Omit for a fresh one — the approval "
226
+ + "code is bound to the PLAN, not to a conversation, so a dispatch needs no thread."),
205
227
  },
206
228
  }, async (args) => withSession(deps, async (session) => {
207
229
  const log = deps.log ?? defaultLog;
@@ -231,7 +253,7 @@ export function registerJarvisTools(server, deps) {
231
253
  ? ["", `The person approved this plan. Confirmation code: ${code.toLowerCase()}`]
232
254
  : []),
233
255
  ].join("\n");
234
- const { result } = await takeTurn(deps, session, "jarvis_dispatch", { question, thread: typeof args.thread === "string" ? args.thread : "main" }, remember);
256
+ const { result } = await takeTurn(deps, session, "jarvis_dispatch", { question, ...(typeof args.thread === "string" ? { thread: args.thread } : {}) }, remember);
235
257
  return result;
236
258
  }));
237
259
  register("jarvis_check", {
@@ -246,14 +268,19 @@ export function registerJarvisTools(server, deps) {
246
268
  .max(200)
247
269
  .optional()
248
270
  .describe("The task id JARVIS named when it dispatched. Omit to ask about the recent ones."),
249
- thread: z.string().min(1).max(200).optional().describe('Defaults to "main".'),
271
+ thread: z
272
+ .string()
273
+ .min(1)
274
+ .max(200)
275
+ .optional()
276
+ .describe("The thread_id of a conversation to continue. Omit for a fresh one."),
250
277
  },
251
278
  }, async (args) => withSession(deps, async (session) => {
252
279
  const task = typeof args.task === "string" ? args.task.trim() : "";
253
280
  const question = task.length > 0
254
281
  ? `Check the coding task ${task} and tell me its status, its branch and its pull request if it has one.`
255
282
  : "Check the coding tasks dispatched for me recently and tell me the status of each.";
256
- const { result } = await takeTurn(deps, session, "jarvis_check", { question, thread: typeof args.thread === "string" ? args.thread : "main" }, remember);
283
+ const { result } = await takeTurn(deps, session, "jarvis_check", { question, ...(typeof args.thread === "string" ? { thread: args.thread } : {}) }, remember);
257
284
  return result;
258
285
  }));
259
286
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bli-cockpit/mcp",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
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",