@snowyroad/braid 0.92.3 → 0.93.1
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/dist/chunk-HQNAAXUM.js +173 -0
- package/dist/{chunk-WJ2X7ILC.js → chunk-KBWVFEO5.js} +143 -107
- package/dist/cli.js +81 -17
- package/dist/{http-WLOQB44Q.js → http-65DOKSCL.js} +2 -2
- package/dist/mcp/server.js +6 -3
- package/package.json +2 -2
- package/service-shrinkwrap.json +150 -129
- package/dist/chunk-BDOF72CL.js +0 -78
package/dist/cli.js
CHANGED
|
@@ -29,7 +29,9 @@ import {
|
|
|
29
29
|
classifyFatal,
|
|
30
30
|
configDir,
|
|
31
31
|
createAdapter,
|
|
32
|
+
createWorkspaceNoticeGate,
|
|
32
33
|
decodeInvite,
|
|
34
|
+
distinctWorkspaceNames,
|
|
33
35
|
effectiveAnthropicCredential,
|
|
34
36
|
handoffCwdFor,
|
|
35
37
|
hasSubscriptionLogin,
|
|
@@ -69,10 +71,12 @@ import {
|
|
|
69
71
|
startingLines,
|
|
70
72
|
toolModeLabel,
|
|
71
73
|
toolStatusLine,
|
|
74
|
+
updateAgentWorkspace,
|
|
72
75
|
validateRelayWsUrl,
|
|
73
76
|
wsToHttp
|
|
74
|
-
} from "./chunk-
|
|
77
|
+
} from "./chunk-KBWVFEO5.js";
|
|
75
78
|
import {
|
|
79
|
+
clampUntrusted,
|
|
76
80
|
externalSourceLabel,
|
|
77
81
|
externalUntrustedPreamble,
|
|
78
82
|
fence,
|
|
@@ -85,7 +89,7 @@ import {
|
|
|
85
89
|
sameText,
|
|
86
90
|
untrustedPreamble,
|
|
87
91
|
utext
|
|
88
|
-
} from "./chunk-
|
|
92
|
+
} from "./chunk-HQNAAXUM.js";
|
|
89
93
|
|
|
90
94
|
// src/health.ts
|
|
91
95
|
var CEILING_STRIKES_TO_DEAD = 3;
|
|
@@ -263,7 +267,7 @@ ${fence("flow discussion history", joinUntrusted(lines, "\n"))}
|
|
|
263
267
|
|
|
264
268
|
`;
|
|
265
269
|
}
|
|
266
|
-
function buildFlowPrompt(signal, agentName, channelId, toolMode = "readonly", cause = "operator") {
|
|
270
|
+
function buildFlowPrompt(signal, agentName, channelId, toolMode = "readonly", cause = "operator", channelName) {
|
|
267
271
|
if (signal.kind === "direction") {
|
|
268
272
|
const candidates = joinUntrusted(signal.candidates ?? [], ", ");
|
|
269
273
|
const history2 = renderFlowHistory(signal.history);
|
|
@@ -298,8 +302,9 @@ function buildFlowPrompt(signal, agentName, channelId, toolMode = "readonly", ca
|
|
|
298
302
|
${toolStatusLine(toolMode, cause)}
|
|
299
303
|
|
|
300
304
|
${header}
|
|
301
|
-
CHANNEL:
|
|
302
|
-
|
|
305
|
+
` + (channelName ? `CHANNEL:
|
|
306
|
+
${fence("channel name", clampUntrusted(channelName, 60))}
|
|
307
|
+
` : ``) + `FLOW: ${signal.flowId}
|
|
303
308
|
TOPIC:
|
|
304
309
|
${fence("flow topic", signal.topic)}
|
|
305
310
|
` + role + ctx + synth + "\n" + history + closer;
|
|
@@ -357,7 +362,7 @@ function externalTaintOf(messages) {
|
|
|
357
362
|
return { preambleLabel: "an external source", fenceLabel: "external message" };
|
|
358
363
|
}
|
|
359
364
|
var ChannelSession = class {
|
|
360
|
-
constructor(adapter, onReply, agentName, channelId, flow, fetchContext, beacon, toolMode = "readonly", holdCatchUpMaxMentions = 3, fetchPrivateContext) {
|
|
365
|
+
constructor(adapter, onReply, agentName, channelId, flow, fetchContext, beacon, toolMode = "readonly", holdCatchUpMaxMentions = 3, fetchPrivateContext, channelName) {
|
|
361
366
|
this.adapter = adapter;
|
|
362
367
|
this.onReply = onReply;
|
|
363
368
|
this.agentName = agentName;
|
|
@@ -368,6 +373,7 @@ var ChannelSession = class {
|
|
|
368
373
|
this.toolMode = toolMode;
|
|
369
374
|
this.holdCatchUpMaxMentions = holdCatchUpMaxMentions;
|
|
370
375
|
this.fetchPrivateContext = fetchPrivateContext;
|
|
376
|
+
this.channelName = channelName;
|
|
371
377
|
}
|
|
372
378
|
adapter;
|
|
373
379
|
onReply;
|
|
@@ -379,6 +385,7 @@ var ChannelSession = class {
|
|
|
379
385
|
toolMode;
|
|
380
386
|
holdCatchUpMaxMentions;
|
|
381
387
|
fetchPrivateContext;
|
|
388
|
+
channelName;
|
|
382
389
|
session = null;
|
|
383
390
|
roster = [];
|
|
384
391
|
/** Replace the known peer roster (situational facts surfaced per message). */
|
|
@@ -500,8 +507,11 @@ ${toolStatusLine(mode, cause)}
|
|
|
500
507
|
${fence("reply parent sender", msg.replyTo.senderName)}
|
|
501
508
|
${fence("reply parent content", msg.replyTo.content)}
|
|
502
509
|
`;
|
|
503
|
-
const
|
|
504
|
-
|
|
510
|
+
const channelLabel = this.channelName?.(this.channelId);
|
|
511
|
+
const head = this.promptHead(effectiveMode, externalLabel) + privateContext + channelContext + (channelLabel ? `You are ${this.agentName} observing a message in a Braid channel named:
|
|
512
|
+
${fence("channel name", clampUntrusted(channelLabel, 60))}
|
|
513
|
+
` : `You are ${this.agentName} observing a message in a Braid channel.
|
|
514
|
+
`) + // BL-057 D-b1: fixed wording only — senderName is UntrustedText and must never
|
|
505
515
|
// appear outside a fence (the fenced identity block above already carries it).
|
|
506
516
|
(msg.channelKind === "dm" ? "This is a direct message between you and the sender identified above. Reply directly; no mention is needed.\n" : "") + `This message's id is ${msg.id} (use it as message_id if you decide to react to this message).
|
|
507
517
|
FROM:
|
|
@@ -539,7 +549,14 @@ ${fence("reactions", formatReactions(msg.reactions))}
|
|
|
539
549
|
if (history.length === 0) history = await this.flow.fetchHistory(signal.flowId);
|
|
540
550
|
const effectiveMode = narrowestMode(this.lastBundleMode, this.toolMode);
|
|
541
551
|
const cause = effectiveMode === "readonly" && this.toolMode !== "readonly" ? "channel" : "operator";
|
|
542
|
-
const prompt = buildFlowPrompt(
|
|
552
|
+
const prompt = buildFlowPrompt(
|
|
553
|
+
{ ...signal, history },
|
|
554
|
+
this.agentName,
|
|
555
|
+
this.channelId,
|
|
556
|
+
effectiveMode,
|
|
557
|
+
cause,
|
|
558
|
+
this.channelName?.(this.channelId)
|
|
559
|
+
);
|
|
543
560
|
const reply = await this.session.converseLocal(capPrompt(prompt), narrowestOverride(effectiveMode, this.toolMode));
|
|
544
561
|
await this.flow.postReply(signal.flowId, reply.trim() || "(no response)", void 0, signal.dispatchId);
|
|
545
562
|
} finally {
|
|
@@ -586,6 +603,10 @@ ${fence("reactions", formatReactions(msg.reactions))}
|
|
|
586
603
|
const effectiveMode = narrowestMode(bundleMode, this.toolMode, externalMode);
|
|
587
604
|
const overrideMode = narrowestOverride(effectiveMode, this.toolMode);
|
|
588
605
|
const transcriptLabel = taint ? taint.fenceLabel : "missed channel messages";
|
|
606
|
+
const channelLabel = this.channelName?.(this.channelId);
|
|
607
|
+
const reconnectedClause = channelLabel ? `You just reconnected to a Braid channel named:
|
|
608
|
+
${fence("channel name", clampUntrusted(channelLabel, 60))}
|
|
609
|
+
after being away. ` : `You just reconnected to this Braid channel after being away. `;
|
|
589
610
|
const transcript = joinUntrusted(
|
|
590
611
|
result.context.map((m) => utext`[${m.createdAt}] ${firstNonEmpty([m.senderName, m.senderId], "someone")}: ${m.content}${m.reactions && m.reactions.length ? utext` [reactions: ${formatReactions(m.reactions)}]` : utext``}`),
|
|
591
612
|
"\n"
|
|
@@ -595,7 +616,7 @@ ${fence("reactions", formatReactions(msg.reactions))}
|
|
|
595
616
|
this.beacon?.begin();
|
|
596
617
|
try {
|
|
597
618
|
await this.session.converseLocal(capPrompt(
|
|
598
|
-
this.promptHead(effectiveMode, taint?.preambleLabel) +
|
|
619
|
+
this.promptHead(effectiveMode, taint?.preambleLabel) + reconnectedClause + `Here is what you missed (context only, do NOT reply to it):
|
|
599
620
|
` + fence(transcriptLabel, transcript)
|
|
600
621
|
), overrideMode);
|
|
601
622
|
} finally {
|
|
@@ -610,7 +631,9 @@ ${fence("reactions", formatReactions(msg.reactions))}
|
|
|
610
631
|
result.mentions.map((m) => utext`[${m.createdAt}] ${firstNonEmpty([m.senderName, m.senderId], "someone")}: ${m.content}`),
|
|
611
632
|
"\n"
|
|
612
633
|
);
|
|
613
|
-
const head = this.promptHead(effectiveMode, taint?.preambleLabel) + privateContext + channelContext + `You are ${this.agentName}. You just reconnected to Braid channel
|
|
634
|
+
const head = this.promptHead(effectiveMode, taint?.preambleLabel) + privateContext + channelContext + `You are ${this.agentName}. ` + (channelLabel ? `You just reconnected to a Braid channel named:
|
|
635
|
+
${fence("channel name", clampUntrusted(channelLabel, 60))}
|
|
636
|
+
after ` : `You just reconnected to this Braid channel after `) + `being away. While you were gone, the channel said (context):
|
|
614
637
|
${fence(transcriptLabel, transcript)}
|
|
615
638
|
|
|
616
639
|
You were directly addressed (@mentioned) in:
|
|
@@ -641,6 +664,10 @@ ${fence(taint ? taint.fenceLabel : "messages mentioning you", addressed)}
|
|
|
641
664
|
const effectiveMode = narrowestMode(bundleMode, this.toolMode, externalMode);
|
|
642
665
|
const overrideMode = narrowestOverride(effectiveMode, this.toolMode);
|
|
643
666
|
const transcriptLabel = taint ? taint.fenceLabel : "recent channel messages";
|
|
667
|
+
const channelLabel = this.channelName?.(this.channelId);
|
|
668
|
+
const joinedClause = channelLabel ? `You just joined a Braid channel named:
|
|
669
|
+
${fence("channel name", clampUntrusted(channelLabel, 60))}
|
|
670
|
+
Here is the recent discussion so far ` : `You just joined this Braid channel. Here is the recent discussion so far `;
|
|
644
671
|
const transcript = joinUntrusted(
|
|
645
672
|
history.map((m) => utext`[${m.createdAt}] ${firstNonEmpty([m.senderName, m.senderId], "someone")}: ${m.content}`),
|
|
646
673
|
"\n"
|
|
@@ -648,7 +675,7 @@ ${fence(taint ? taint.fenceLabel : "messages mentioning you", addressed)}
|
|
|
648
675
|
this.beacon?.begin();
|
|
649
676
|
try {
|
|
650
677
|
await this.session.converseLocal(capPrompt(
|
|
651
|
-
this.promptHead(effectiveMode, taint?.preambleLabel) +
|
|
678
|
+
this.promptHead(effectiveMode, taint?.preambleLabel) + joinedClause + `(context only, do NOT reply to it):
|
|
652
679
|
` + fence(transcriptLabel, transcript)
|
|
653
680
|
), overrideMode);
|
|
654
681
|
} finally {
|
|
@@ -2508,7 +2535,16 @@ async function startBridge(cfg, relay, deps) {
|
|
|
2508
2535
|
cfg.catchUpMaxMentions,
|
|
2509
2536
|
// BL-056 task 7: PHYSICALLY SEPARATE from fetchChannelContext above — this
|
|
2510
2537
|
// agent's own private-memory lane, never the shared channel path.
|
|
2511
|
-
|
|
2538
|
+
// D12 task 7.2: thread this session's own channelId (closure-bound above)
|
|
2539
|
+
// so the relay org-scopes recall to it, and the channelNames lookup (same
|
|
2540
|
+
// map/pattern as the handoff controller's channelName resolver, line ~647)
|
|
2541
|
+
// so a channel-scoped hit's attribution renders the channel NAME, never
|
|
2542
|
+
// its raw UUID (G7).
|
|
2543
|
+
(queryHint) => relay.fetchPrivateMemoryContext(channelId, queryHint, (id) => channelNames.get(id)),
|
|
2544
|
+
// D12 task 7.2 (review follow-up): the SAME channelNames resolver, threaded so
|
|
2545
|
+
// ChannelSession's own situational framing lines ("You are X observing a message
|
|
2546
|
+
// in Braid channel <NAME>") never fall back to the raw UUID either (G7).
|
|
2547
|
+
(id) => channelNames.get(id)
|
|
2512
2548
|
);
|
|
2513
2549
|
const broker = new SourceBroker(channelId, relay);
|
|
2514
2550
|
const brokerInfo = await broker.start();
|
|
@@ -2654,7 +2690,12 @@ async function startBridge(cfg, relay, deps) {
|
|
|
2654
2690
|
});
|
|
2655
2691
|
const handoffCtl = cs.handoffGate ? makeSocketHandoffController({
|
|
2656
2692
|
sessions,
|
|
2657
|
-
|
|
2693
|
+
// Display path (handoff picker/banner): unwrap the branded name for the
|
|
2694
|
+
// TTY. String() is the sanctioned display unwrap (not a prompt sink).
|
|
2695
|
+
channelName: (id) => {
|
|
2696
|
+
const u = channelNames.get(id);
|
|
2697
|
+
return u === void 0 ? void 0 : String(u);
|
|
2698
|
+
},
|
|
2658
2699
|
provider: cfg.agent,
|
|
2659
2700
|
cwdFor: (id) => handoffCwdFor(cfg, id),
|
|
2660
2701
|
scope: cfg.scope,
|
|
@@ -4547,6 +4588,10 @@ function renderStatusHuman(input) {
|
|
|
4547
4588
|
);
|
|
4548
4589
|
const ipcProfile = resolveIpcProfile(e.agent.scope, void 0);
|
|
4549
4590
|
lines.push(` ipcprofile: ${ipcProfile} (${e.agent.scope?.ipcProfile !== void 0 ? "file" : "default"})`);
|
|
4591
|
+
if (e.agent.workspace) lines.push(` workspace: ${sanitizeForTty(e.agent.workspace)}`);
|
|
4592
|
+
if (e.agent.channelWorkspaces && e.agent.channelWorkspaces.length > 1) {
|
|
4593
|
+
lines.push(` channels in: ${e.agent.channelWorkspaces.map((w) => sanitizeForTty(w)).join(", ")}`);
|
|
4594
|
+
}
|
|
4550
4595
|
const eps = endpointRows(e, k, input);
|
|
4551
4596
|
for (const ep of eps) {
|
|
4552
4597
|
const state = ep.kind === "env" ? "auto-allowed (env)" : ep.pinned ? `pinned ${ep.pinnedAt}` : "NOT PINNED \u2014 pending approval";
|
|
@@ -4579,7 +4624,11 @@ function renderStatusJson(input) {
|
|
|
4579
4624
|
}
|
|
4580
4625
|
},
|
|
4581
4626
|
ipcProfile: { value: ipcProfile, origin: e.agent.scope?.ipcProfile !== void 0 ? "file" : "default" },
|
|
4582
|
-
endpoints: endpointRows(e, k, input)
|
|
4627
|
+
endpoints: endpointRows(e, k, input),
|
|
4628
|
+
// Task 7.1 (D4): null (not "personal"/omitted key) when never cached, so machine
|
|
4629
|
+
// consumers can distinguish "no data yet" from an actual value.
|
|
4630
|
+
workspace: e.agent.workspace ?? null,
|
|
4631
|
+
channelWorkspaces: e.agent.channelWorkspaces ?? []
|
|
4583
4632
|
};
|
|
4584
4633
|
});
|
|
4585
4634
|
return JSON.stringify({ agents }, null, 2);
|
|
@@ -6374,6 +6423,18 @@ async function main() {
|
|
|
6374
6423
|
onRelayCreated: (relay) => {
|
|
6375
6424
|
relayRef.current = relay;
|
|
6376
6425
|
relay.onConnEvent((e) => connStatus.handle(e));
|
|
6426
|
+
const workspaceNoticeGate = createWorkspaceNoticeGate();
|
|
6427
|
+
relay.onHello(({ channelOrgs, agentWorkspace }) => {
|
|
6428
|
+
const notice = workspaceNoticeGate(channelOrgs);
|
|
6429
|
+
if (notice.length > 0) console.log(banner("braid", "WORKSPACES", notice));
|
|
6430
|
+
if (cfg.agentName) {
|
|
6431
|
+
const channelWorkspaces = distinctWorkspaceNames(channelOrgs);
|
|
6432
|
+
updateAgentWorkspace(configDir(process.env), cfg.agentName, {
|
|
6433
|
+
...agentWorkspace ? { workspace: agentWorkspace } : {},
|
|
6434
|
+
...channelWorkspaces.length > 0 ? { channelWorkspaces } : {}
|
|
6435
|
+
});
|
|
6436
|
+
}
|
|
6437
|
+
});
|
|
6377
6438
|
}
|
|
6378
6439
|
});
|
|
6379
6440
|
if (cfg.runtimeVersion) {
|
|
@@ -6394,12 +6455,15 @@ async function main() {
|
|
|
6394
6455
|
const channelLabel = (channelId) => {
|
|
6395
6456
|
const name = bridge.channelNames.get(channelId);
|
|
6396
6457
|
const tag = sanitizeForTty(channelId).slice(0, 8);
|
|
6397
|
-
return name ? `#${sanitizeForTty(name).slice(0, 40)} (${tag})` : `#${tag}`;
|
|
6458
|
+
return name ? `#${sanitizeForTty(String(name)).slice(0, 40)} (${tag})` : `#${tag}`;
|
|
6398
6459
|
};
|
|
6399
6460
|
attach = startAttach({
|
|
6400
6461
|
hub: bridge.hub,
|
|
6401
6462
|
listChannels: () => [...bridge.sessions.keys()],
|
|
6402
|
-
channelName: (channelId) =>
|
|
6463
|
+
channelName: (channelId) => {
|
|
6464
|
+
const u = bridge.channelNames.get(channelId);
|
|
6465
|
+
return u === void 0 ? void 0 : String(u);
|
|
6466
|
+
},
|
|
6403
6467
|
stdin: process.stdin,
|
|
6404
6468
|
stdout: stickyWriteThrough(sticky, process.stdout),
|
|
6405
6469
|
onInterrupt: () => {
|
package/dist/mcp/server.js
CHANGED
|
@@ -3,8 +3,9 @@ import {
|
|
|
3
3
|
applyEnvAliases
|
|
4
4
|
} from "../chunk-SHUJGYPW.js";
|
|
5
5
|
import {
|
|
6
|
-
neutralizeMarkers
|
|
7
|
-
|
|
6
|
+
neutralizeMarkers,
|
|
7
|
+
resolveChannelLabel
|
|
8
|
+
} from "../chunk-HQNAAXUM.js";
|
|
8
9
|
|
|
9
10
|
// src/mcp/server.ts
|
|
10
11
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
@@ -296,7 +297,9 @@ ${lines.join("\n")}
|
|
|
296
297
|
const results = out?.results ?? [];
|
|
297
298
|
if (results.length === 0) return "No matching entries found in your private memory.";
|
|
298
299
|
const lines = results.map((r) => {
|
|
299
|
-
const
|
|
300
|
+
const channelId = typeof r?.channelId === "string" && r.channelId ? r.channelId : void 0;
|
|
301
|
+
const label = resolveChannelLabel(channelId);
|
|
302
|
+
const channel = label ? ` (channel ${neutralizeMarkers(String(label))})` : "";
|
|
300
303
|
const snippet = neutralizeMarkers(String(r?.content ?? ""));
|
|
301
304
|
return `- ${snippet}${channel}`;
|
|
302
305
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@snowyroad/braid",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.93.1",
|
|
4
4
|
"description": "Braid: connect your own coding agent (Claude Code, Codex, Gemini, Grok) to a Braid channel and collaborate with other agents and humans.",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE.md",
|
|
6
6
|
"author": "SnowyRoad",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"@anthropic-ai/sandbox-runtime": "0.0.63",
|
|
52
52
|
"@anthropic-ai/sdk": "0.104.1",
|
|
53
53
|
"@modelcontextprotocol/sdk": "1.29.0",
|
|
54
|
-
"@snowyroad/braid-tui": "0.0.
|
|
54
|
+
"@snowyroad/braid-tui": "0.0.30",
|
|
55
55
|
"canonicalize": "^3.0.0",
|
|
56
56
|
"diff": "^9.0.0",
|
|
57
57
|
"jose": "^6.2.3",
|