@snowyroad/arp 0.3.3 → 0.3.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/dist/cli.js +96 -51
- package/package.json +10 -6
package/dist/cli.js
CHANGED
|
@@ -680,6 +680,33 @@ function redactConfig(cfg) {
|
|
|
680
680
|
import { randomUUID } from "crypto";
|
|
681
681
|
|
|
682
682
|
// src/untrusted.ts
|
|
683
|
+
function untrusted(s) {
|
|
684
|
+
return s;
|
|
685
|
+
}
|
|
686
|
+
function rawUntrusted(u) {
|
|
687
|
+
return u;
|
|
688
|
+
}
|
|
689
|
+
function utext(strings, ...values) {
|
|
690
|
+
let out = strings[0];
|
|
691
|
+
for (let i = 0; i < values.length; i++) out += String(values[i]) + strings[i + 1];
|
|
692
|
+
return out;
|
|
693
|
+
}
|
|
694
|
+
function joinUntrusted(parts, sep2) {
|
|
695
|
+
return parts.join(sep2);
|
|
696
|
+
}
|
|
697
|
+
function firstNonEmpty(parts, fallback) {
|
|
698
|
+
for (const p of parts) if (p !== "") return p;
|
|
699
|
+
return fallback;
|
|
700
|
+
}
|
|
701
|
+
function hasText(u) {
|
|
702
|
+
return u !== "";
|
|
703
|
+
}
|
|
704
|
+
function isBlankText(u) {
|
|
705
|
+
return u.trim() === "";
|
|
706
|
+
}
|
|
707
|
+
function sameText(u, s) {
|
|
708
|
+
return u === s;
|
|
709
|
+
}
|
|
683
710
|
var MARKER_RE = /<<<(?=[\s\u200B-\u200D\u2060\uFEFF]*(?:END[\s\u200B-\u200D\u2060\uFEFF]+)?UNTRUSTED)/gi;
|
|
684
711
|
function neutralizeMarkers(content) {
|
|
685
712
|
return content.replace(MARKER_RE, "<<\\<");
|
|
@@ -690,7 +717,7 @@ function sanitizeLabel(label) {
|
|
|
690
717
|
function fence(label, content) {
|
|
691
718
|
const l = sanitizeLabel(label);
|
|
692
719
|
return `<<<UNTRUSTED ${l}>>>
|
|
693
|
-
${neutralizeMarkers(content)}
|
|
720
|
+
${neutralizeMarkers(rawUntrusted(content))}
|
|
694
721
|
<<<END UNTRUSTED ${l}>>>`;
|
|
695
722
|
}
|
|
696
723
|
function untrustedPreamble(mode) {
|
|
@@ -774,24 +801,24 @@ function normalizeRosterEntry(name, memberDescription, card) {
|
|
|
774
801
|
if (typeof card.description === "string" && card.description) description = card.description;
|
|
775
802
|
if (Array.isArray(card.skills)) skills = card.skills.map((s) => s && typeof s.name === "string" ? s.name : "").filter(Boolean);
|
|
776
803
|
}
|
|
777
|
-
return { name, description, skills };
|
|
804
|
+
return { name: untrusted(name), description: untrusted(description), skills: skills.map(untrusted) };
|
|
778
805
|
}
|
|
779
806
|
function assembleRosterFacts(entries, selfName) {
|
|
780
|
-
const peers = entries.filter((e) => e.name
|
|
807
|
+
const peers = entries.filter((e) => !sameText(e.name, selfName));
|
|
781
808
|
if (peers.length === 0) return "";
|
|
782
809
|
const lines = peers.map((p) => {
|
|
783
|
-
const desc = p.description ? `: ${p.description}` : "";
|
|
784
|
-
const skills = p.skills.length ? ` [skills: ${p.skills
|
|
785
|
-
return `- ${p.name}${desc}${skills}`;
|
|
810
|
+
const desc = hasText(p.description) ? utext`: ${p.description}` : "";
|
|
811
|
+
const skills = p.skills.length ? utext` [skills: ${joinUntrusted(p.skills, ", ")}]` : "";
|
|
812
|
+
return utext`- ${p.name}${desc}${skills}`;
|
|
786
813
|
});
|
|
787
814
|
return `Also in this channel:
|
|
788
|
-
${fence("peer roster", lines
|
|
815
|
+
${fence("peer roster", joinUntrusted(lines, "\n"))}`;
|
|
789
816
|
}
|
|
790
817
|
|
|
791
818
|
// src/channelContext.ts
|
|
792
819
|
function buildChannelContext(input) {
|
|
793
820
|
let out = "";
|
|
794
|
-
if (input.memory
|
|
821
|
+
if (!isBlankText(input.memory)) {
|
|
795
822
|
out += `## Channel Memory (shared context for this channel)
|
|
796
823
|
${fence("channel memory", input.memory)}
|
|
797
824
|
---
|
|
@@ -799,8 +826,7 @@ ${fence("channel memory", input.memory)}
|
|
|
799
826
|
`;
|
|
800
827
|
}
|
|
801
828
|
if (input.pins.length > 0) {
|
|
802
|
-
const sections = input.pins.map((p) => fence("pinned file",
|
|
803
|
-
${p.content}`));
|
|
829
|
+
const sections = input.pins.map((p) => fence("pinned file", utext`📌 ${p.label}\n${p.content}`));
|
|
804
830
|
out += `## Pinned Files (from GitHub)
|
|
805
831
|
${sections.join("\n\n")}
|
|
806
832
|
---
|
|
@@ -810,10 +836,10 @@ ${sections.join("\n\n")}
|
|
|
810
836
|
if (input.topics.length > 0) {
|
|
811
837
|
const lines = input.topics.map((t) => {
|
|
812
838
|
const count = t.count != null ? ` (${t.count} messages)` : "";
|
|
813
|
-
return `- ${t.title}${count}`;
|
|
839
|
+
return utext`- ${t.title}${count}`;
|
|
814
840
|
});
|
|
815
841
|
out += `## Channel Topics
|
|
816
|
-
${fence("channel topic titles", lines
|
|
842
|
+
${fence("channel topic titles", joinUntrusted(lines, "\n"))}
|
|
817
843
|
---
|
|
818
844
|
|
|
819
845
|
`;
|
|
@@ -825,18 +851,18 @@ ${fence("channel topic titles", lines.join("\n"))}
|
|
|
825
851
|
function isAddressed(content, agentName) {
|
|
826
852
|
const name = agentName.trim();
|
|
827
853
|
if (!name) return false;
|
|
828
|
-
const c = content.toLowerCase();
|
|
854
|
+
const c = rawUntrusted(content).toLowerCase();
|
|
829
855
|
const forms = /* @__PURE__ */ new Set([name.toLowerCase(), name.toLowerCase().replace(/\s+/g, "_")]);
|
|
830
856
|
for (const f of forms) if (c.includes("@" + f)) return true;
|
|
831
857
|
return false;
|
|
832
858
|
}
|
|
833
859
|
function classifyCatchUp(messages, agentName, nowMs, opts) {
|
|
834
860
|
const inWindow = messages.filter((m) => {
|
|
835
|
-
const t = Date.parse(m.createdAt);
|
|
861
|
+
const t = Date.parse(rawUntrusted(m.createdAt));
|
|
836
862
|
return Number.isFinite(t) ? t >= nowMs - opts.ttlMs : true;
|
|
837
863
|
});
|
|
838
864
|
const mentions = inWindow.filter(
|
|
839
|
-
(m) => m.senderName
|
|
865
|
+
(m) => !sameText(m.senderName, agentName) && isAddressed(m.content, agentName)
|
|
840
866
|
);
|
|
841
867
|
const capped = mentions.slice(-opts.maxMentions);
|
|
842
868
|
return { context: inWindow, mentions: capped };
|
|
@@ -904,6 +930,8 @@ var RelayClient = class {
|
|
|
904
930
|
graceTimer = null;
|
|
905
931
|
confirmed = false;
|
|
906
932
|
// single-shot: onReady has fired (relay accepted the agent)
|
|
933
|
+
reconnectAnnounced = false;
|
|
934
|
+
// per-connection: "reconnected" printed for this connection
|
|
907
935
|
catchUpCbs = [];
|
|
908
936
|
caughtUp = /* @__PURE__ */ new Set();
|
|
909
937
|
// channels caught up this connection
|
|
@@ -995,6 +1023,7 @@ var RelayClient = class {
|
|
|
995
1023
|
this.caughtUp.clear();
|
|
996
1024
|
this.backfillCharsThisConn = 0;
|
|
997
1025
|
this.connectedAt = Date.now();
|
|
1026
|
+
this.reconnectAnnounced = false;
|
|
998
1027
|
this.heartbeatTimer = setInterval(() => this.send({ type: "ping" }), HEARTBEAT_MS);
|
|
999
1028
|
this.armWatchdog();
|
|
1000
1029
|
this.stableTimer = setTimeout(() => {
|
|
@@ -1005,13 +1034,20 @@ var RelayClient = class {
|
|
|
1005
1034
|
this.graceTimer = setTimeout(() => this.confirmReady(), AUTH_GRACE_MS);
|
|
1006
1035
|
this.onConnected();
|
|
1007
1036
|
}
|
|
1008
|
-
/**
|
|
1037
|
+
/** Fires onReady at most once, on the FIRST successful connect; on later
|
|
1038
|
+
* connections it announces the reconnect instead (once per connection). */
|
|
1009
1039
|
confirmReady() {
|
|
1010
1040
|
if (this.graceTimer) {
|
|
1011
1041
|
clearTimeout(this.graceTimer);
|
|
1012
1042
|
this.graceTimer = null;
|
|
1013
1043
|
}
|
|
1014
|
-
if (this.confirmed)
|
|
1044
|
+
if (this.confirmed) {
|
|
1045
|
+
if (!this.reconnectAnnounced) {
|
|
1046
|
+
this.reconnectAnnounced = true;
|
|
1047
|
+
console.log("[arp-bridge] reconnected");
|
|
1048
|
+
}
|
|
1049
|
+
return;
|
|
1050
|
+
}
|
|
1015
1051
|
this.confirmed = true;
|
|
1016
1052
|
this.readyCb?.();
|
|
1017
1053
|
}
|
|
@@ -1064,12 +1100,12 @@ var RelayClient = class {
|
|
|
1064
1100
|
id: String(m.id ?? ""),
|
|
1065
1101
|
seq,
|
|
1066
1102
|
channelId,
|
|
1067
|
-
content,
|
|
1068
|
-
senderId: String(m.agentId ?? ""),
|
|
1069
|
-
senderName: String(m.agentName ?? ""),
|
|
1103
|
+
content: untrusted(content),
|
|
1104
|
+
senderId: untrusted(String(m.agentId ?? "")),
|
|
1105
|
+
senderName: untrusted(String(m.agentName ?? "")),
|
|
1070
1106
|
senderType: String(m.messageType ?? m.type ?? ""),
|
|
1071
1107
|
// relay live/resume key is messageType; history path uses type
|
|
1072
|
-
createdAt: String(m.createdAt ?? ""),
|
|
1108
|
+
createdAt: untrusted(String(m.createdAt ?? "")),
|
|
1073
1109
|
isHistory: false
|
|
1074
1110
|
// shape parity with live messages; the caller decides how to handle them
|
|
1075
1111
|
});
|
|
@@ -1189,7 +1225,7 @@ var RelayClient = class {
|
|
|
1189
1225
|
}
|
|
1190
1226
|
onMessage(raw) {
|
|
1191
1227
|
this.armWatchdog();
|
|
1192
|
-
|
|
1228
|
+
this.confirmReady();
|
|
1193
1229
|
let msg;
|
|
1194
1230
|
try {
|
|
1195
1231
|
msg = JSON.parse(raw);
|
|
@@ -1279,13 +1315,13 @@ var RelayClient = class {
|
|
|
1279
1315
|
id: String(m.id ?? ""),
|
|
1280
1316
|
seq: Number(m.seq ?? 0),
|
|
1281
1317
|
channelId,
|
|
1282
|
-
content: clampContent(String(m.content ?? "")),
|
|
1318
|
+
content: untrusted(clampContent(String(m.content ?? ""))),
|
|
1283
1319
|
// bound per-message memory/prompt size (BRIDGE-09)
|
|
1284
|
-
senderId: String(m.agentId ?? ""),
|
|
1285
|
-
senderName: String(m.agentName ?? ""),
|
|
1320
|
+
senderId: untrusted(String(m.agentId ?? "")),
|
|
1321
|
+
senderName: untrusted(String(m.agentName ?? "")),
|
|
1286
1322
|
senderType: String(m.messageType ?? m.type ?? ""),
|
|
1287
1323
|
// relay live/resume key is messageType; history path uses type
|
|
1288
|
-
createdAt: String(m.createdAt ?? ""),
|
|
1324
|
+
createdAt: untrusted(String(m.createdAt ?? "")),
|
|
1289
1325
|
isHistory: Boolean(msg.isHistory)
|
|
1290
1326
|
};
|
|
1291
1327
|
const gapDetected = !inbound.isHistory && this.cursorOf(channelId) > 0 && inbound.seq > this.cursorOf(channelId) + 1;
|
|
@@ -1305,8 +1341,8 @@ var RelayClient = class {
|
|
|
1305
1341
|
if (!flowId) return;
|
|
1306
1342
|
const rawHistory = kind === "synthesis" || kind === "direction" ? msg.flowHistory ?? msg.messages ?? [] : msg.recentMessages ?? [];
|
|
1307
1343
|
const history = (Array.isArray(rawHistory) ? rawHistory : []).map((e) => ({
|
|
1308
|
-
agentName: String(e.agentName ?? ""),
|
|
1309
|
-
content: clampContent(String(e.content ?? "")),
|
|
1344
|
+
agentName: untrusted(String(e.agentName ?? "")),
|
|
1345
|
+
content: untrusted(clampContent(String(e.content ?? ""))),
|
|
1310
1346
|
// bound per-entry size (BRIDGE-09)
|
|
1311
1347
|
messageType: e.messageType ?? e.type,
|
|
1312
1348
|
turnNumber: e.turnNumber,
|
|
@@ -1316,11 +1352,11 @@ var RelayClient = class {
|
|
|
1316
1352
|
kind,
|
|
1317
1353
|
flowId,
|
|
1318
1354
|
channelId: String(msg.channelId ?? ""),
|
|
1319
|
-
topic: String(msg.topic ?? ""),
|
|
1320
|
-
rolePrompt: typeof msg.rolePrompt === "string" ? msg.rolePrompt : void 0,
|
|
1321
|
-
contextPrompt: typeof msg.contextPrompt === "string" ? msg.contextPrompt : void 0,
|
|
1322
|
-
synthesisPrompt: typeof msg.synthesisPrompt === "string" ? msg.synthesisPrompt : void 0,
|
|
1323
|
-
candidates: Array.isArray(msg.candidates) ? msg.candidates : void 0,
|
|
1355
|
+
topic: untrusted(String(msg.topic ?? "")),
|
|
1356
|
+
rolePrompt: typeof msg.rolePrompt === "string" ? untrusted(msg.rolePrompt) : void 0,
|
|
1357
|
+
contextPrompt: typeof msg.contextPrompt === "string" ? untrusted(msg.contextPrompt) : void 0,
|
|
1358
|
+
synthesisPrompt: typeof msg.synthesisPrompt === "string" ? untrusted(msg.synthesisPrompt) : void 0,
|
|
1359
|
+
candidates: Array.isArray(msg.candidates) ? msg.candidates.map((c) => untrusted(String(c ?? ""))) : void 0,
|
|
1324
1360
|
history
|
|
1325
1361
|
};
|
|
1326
1362
|
this.flowCbs.forEach((cb) => cb(signal));
|
|
@@ -1455,22 +1491,22 @@ var RelayClient = class {
|
|
|
1455
1491
|
console.warn("[arp-bridge] flow post failed:", sanitizeForTty(String(err)));
|
|
1456
1492
|
}
|
|
1457
1493
|
}
|
|
1458
|
-
/** Channel memory text (
|
|
1494
|
+
/** Channel memory text (empty if none or on error — never throws). Branded (H2-4). */
|
|
1459
1495
|
async fetchChannelMemory(channelId) {
|
|
1460
1496
|
const ch = this.pathId(channelId, "channelId");
|
|
1461
|
-
if (!ch) return "";
|
|
1497
|
+
if (!ch) return untrusted("");
|
|
1462
1498
|
const url = `${this.cfg.relayHttpUrl}/channels/${ch}/memory`;
|
|
1463
1499
|
try {
|
|
1464
1500
|
const res = await this.deps.fetchFn(url, { headers: { Authorization: `Bearer ${this.cfg.token}` } });
|
|
1465
1501
|
if (!res.ok) {
|
|
1466
1502
|
console.warn("[arp-bridge] memory HTTP", res.status);
|
|
1467
|
-
return "";
|
|
1503
|
+
return untrusted("");
|
|
1468
1504
|
}
|
|
1469
1505
|
const data = await res.json();
|
|
1470
|
-
return typeof data?.content === "string" ? data.content : "";
|
|
1506
|
+
return untrusted(typeof data?.content === "string" ? data.content : "");
|
|
1471
1507
|
} catch (err) {
|
|
1472
1508
|
console.warn("[arp-bridge] memory fetch failed:", sanitizeForTty(String(err)));
|
|
1473
|
-
return "";
|
|
1509
|
+
return untrusted("");
|
|
1474
1510
|
}
|
|
1475
1511
|
}
|
|
1476
1512
|
/** Channel topics with message counts ([] if none or on error). The relay returns
|
|
@@ -1488,7 +1524,7 @@ var RelayClient = class {
|
|
|
1488
1524
|
const data = await res.json();
|
|
1489
1525
|
const topics = data?.topics ?? [];
|
|
1490
1526
|
const counts = data?.messageCounts ?? {};
|
|
1491
|
-
return topics.filter((t) => typeof t?.title === "string").map((t) => ({ title: t.title, count: typeof counts[t.id] === "number" ? counts[t.id] : null }));
|
|
1527
|
+
return topics.filter((t) => typeof t?.title === "string").map((t) => ({ title: untrusted(t.title), count: typeof counts[t.id] === "number" ? counts[t.id] : null }));
|
|
1492
1528
|
} catch (err) {
|
|
1493
1529
|
console.warn("[arp-bridge] topics fetch failed:", sanitizeForTty(String(err)));
|
|
1494
1530
|
return [];
|
|
@@ -1507,7 +1543,10 @@ var RelayClient = class {
|
|
|
1507
1543
|
}
|
|
1508
1544
|
const data = await res.json();
|
|
1509
1545
|
const pins = data?.pins ?? [];
|
|
1510
|
-
return pins.filter((p) => p?.injectContext && typeof p.cachedContent === "string" && p.cachedContent.trim()).map((p) => ({
|
|
1546
|
+
return pins.filter((p) => p?.injectContext && typeof p.cachedContent === "string" && p.cachedContent.trim()).map((p) => ({
|
|
1547
|
+
label: untrusted(p.displayName || `${p.repoUrl ?? ""}/${p.filePath ?? ""}`),
|
|
1548
|
+
content: untrusted(p.cachedContent)
|
|
1549
|
+
}));
|
|
1511
1550
|
} catch (err) {
|
|
1512
1551
|
console.warn("[arp-bridge] pins fetch failed:", sanitizeForTty(String(err)));
|
|
1513
1552
|
return [];
|
|
@@ -1541,8 +1580,8 @@ var RelayClient = class {
|
|
|
1541
1580
|
const data = await res.json();
|
|
1542
1581
|
const list = Array.isArray(data) ? data : data.messages ?? [];
|
|
1543
1582
|
return list.map((e) => ({
|
|
1544
|
-
agentName: String(e.agentName ?? ""),
|
|
1545
|
-
content: String(e.content ?? ""),
|
|
1583
|
+
agentName: untrusted(String(e.agentName ?? "")),
|
|
1584
|
+
content: untrusted(String(e.content ?? "")),
|
|
1546
1585
|
messageType: e.messageType ?? e.type,
|
|
1547
1586
|
turnNumber: e.turnNumber,
|
|
1548
1587
|
createdAt: e.createdAt
|
|
@@ -1557,15 +1596,15 @@ var RelayClient = class {
|
|
|
1557
1596
|
// src/flow.ts
|
|
1558
1597
|
function renderFlowHistory(entries) {
|
|
1559
1598
|
if (entries.length === 0) return "";
|
|
1560
|
-
const lines = entries.map((e) => `${e.agentName
|
|
1599
|
+
const lines = entries.map((e) => utext`${firstNonEmpty([e.agentName], "someone")}: ${e.content}`);
|
|
1561
1600
|
return `DISCUSSION HISTORY:
|
|
1562
|
-
${fence("flow discussion history", lines
|
|
1601
|
+
${fence("flow discussion history", joinUntrusted(lines, "\n"))}
|
|
1563
1602
|
|
|
1564
1603
|
`;
|
|
1565
1604
|
}
|
|
1566
1605
|
function buildFlowPrompt(signal, agentName, channelId, toolMode = "readonly") {
|
|
1567
1606
|
if (signal.kind === "direction") {
|
|
1568
|
-
const candidates = (signal.candidates ?? []
|
|
1607
|
+
const candidates = joinUntrusted(signal.candidates ?? [], ", ");
|
|
1569
1608
|
const history2 = renderFlowHistory(signal.history);
|
|
1570
1609
|
const hasHistory = history2 !== "";
|
|
1571
1610
|
const preamble = hasHistory ? [``, history2.trimEnd(), ``, `Read the conversation above and decide who should speak next.`] : [``, `No turns have been taken yet \u2014 choose who should speak FIRST.`];
|
|
@@ -1577,7 +1616,7 @@ function buildFlowPrompt(signal, agentName, channelId, toolMode = "readonly") {
|
|
|
1577
1616
|
fence("flow topic", signal.topic),
|
|
1578
1617
|
...preamble,
|
|
1579
1618
|
`Available participants:`,
|
|
1580
|
-
fence("flow participant names", candidates
|
|
1619
|
+
fence("flow participant names", firstNonEmpty([candidates], "(none online)")),
|
|
1581
1620
|
``,
|
|
1582
1621
|
`Reply with ONLY the name of the single participant who should speak next,`,
|
|
1583
1622
|
`or reply with ONLY the word END to conclude the discussion and move to synthesis.`,
|
|
@@ -1679,7 +1718,7 @@ ${toolStatusLine(this.toolMode)}
|
|
|
1679
1718
|
*/
|
|
1680
1719
|
async submit(msg) {
|
|
1681
1720
|
if (!this.session) throw new Error("ChannelSession not started");
|
|
1682
|
-
const who = msg.senderName
|
|
1721
|
+
const who = firstNonEmpty([msg.senderName, msg.senderId], "someone");
|
|
1683
1722
|
const facts = assembleRosterFacts(this.roster, this.agentName);
|
|
1684
1723
|
const rosterBlock = facts ? `${facts}
|
|
1685
1724
|
|
|
@@ -1750,7 +1789,10 @@ ${fence("channel message", msg.content)}
|
|
|
1750
1789
|
async submitCatchUp(result) {
|
|
1751
1790
|
if (!this.session) throw new Error("ChannelSession not started");
|
|
1752
1791
|
if (result.context.length === 0) return;
|
|
1753
|
-
const transcript =
|
|
1792
|
+
const transcript = joinUntrusted(
|
|
1793
|
+
result.context.map((m) => utext`[${m.createdAt}] ${firstNonEmpty([m.senderName, m.senderId], "someone")}: ${m.content}`),
|
|
1794
|
+
"\n"
|
|
1795
|
+
);
|
|
1754
1796
|
if (result.mentions.length === 0) {
|
|
1755
1797
|
if (!this.session.converseLocal) return;
|
|
1756
1798
|
this.beacon?.begin();
|
|
@@ -1765,7 +1807,10 @@ ${fence("channel message", msg.content)}
|
|
|
1765
1807
|
return;
|
|
1766
1808
|
}
|
|
1767
1809
|
const channelContext = this.fetchContext ? await this.fetchContext() : "";
|
|
1768
|
-
const addressed =
|
|
1810
|
+
const addressed = joinUntrusted(
|
|
1811
|
+
result.mentions.map((m) => utext`[${m.createdAt}] ${firstNonEmpty([m.senderName, m.senderId], "someone")}: ${m.content}`),
|
|
1812
|
+
"\n"
|
|
1813
|
+
);
|
|
1769
1814
|
const head = this.promptHead() + channelContext + `You are ${this.agentName}. You just reconnected to ARP channel ${this.channelId} after being away. While you were gone, the channel said (context):
|
|
1770
1815
|
${fence("missed channel messages", transcript)}
|
|
1771
1816
|
|
|
@@ -2613,8 +2658,8 @@ async function startBridge(cfg, relay, deps) {
|
|
|
2613
2658
|
}
|
|
2614
2659
|
relay.onInbound((m) => {
|
|
2615
2660
|
if (m.isHistory) return;
|
|
2616
|
-
if (m.senderId && m.senderId
|
|
2617
|
-
if (
|
|
2661
|
+
if (hasText(m.senderId) && sameText(m.senderId, cfg.agentUuid) || hasText(m.senderName) && sameText(m.senderName, cfg.agentName)) return;
|
|
2662
|
+
if (isBlankText(m.content)) return;
|
|
2618
2663
|
ensureSession(m.channelId).then((s) => s.submit(m)).catch((e) => console.warn(`[arp-bridge] inbound routing failed for channel ${sanitizeForTty(m.channelId)}:`, sanitizeForTty(String(e))));
|
|
2619
2664
|
});
|
|
2620
2665
|
relay.onFlowSignal((signal) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@snowyroad/arp",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.5",
|
|
4
4
|
"description": "Connect your own coding agent (Claude Code, Codex, Gemini, Grok) to an Agent Relay Protocol channel and collaborate with other agents and humans.",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE.md",
|
|
6
6
|
"author": "SnowyRoad",
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
"engines": {
|
|
21
21
|
"node": ">=20"
|
|
22
22
|
},
|
|
23
|
+
"packageManager": "pnpm@9.15.4",
|
|
23
24
|
"bin": {
|
|
24
25
|
"arp": "dist/cli.js"
|
|
25
26
|
},
|
|
@@ -39,16 +40,19 @@
|
|
|
39
40
|
"typecheck": "tsc --noEmit"
|
|
40
41
|
},
|
|
41
42
|
"dependencies": {
|
|
42
|
-
"@agentclientprotocol/sdk": "0.
|
|
43
|
-
"@anthropic-ai/claude-agent-sdk": "0.
|
|
44
|
-
"
|
|
43
|
+
"@agentclientprotocol/sdk": "0.25.1",
|
|
44
|
+
"@anthropic-ai/claude-agent-sdk": "0.3.177",
|
|
45
|
+
"@anthropic-ai/sdk": "0.104.1",
|
|
46
|
+
"@modelcontextprotocol/sdk": "1.29.0",
|
|
47
|
+
"ws": "8.21.0",
|
|
48
|
+
"zod": "4.4.3"
|
|
45
49
|
},
|
|
46
50
|
"devDependencies": {
|
|
47
|
-
"@types/node": "^
|
|
51
|
+
"@types/node": "^25.9.3",
|
|
48
52
|
"@types/ws": "^8.5.12",
|
|
49
53
|
"tsup": "^8.5.1",
|
|
50
54
|
"tsx": "^4.19.0",
|
|
51
|
-
"typescript": "^
|
|
55
|
+
"typescript": "^6.0.3",
|
|
52
56
|
"vitest": "^2.1.0"
|
|
53
57
|
}
|
|
54
58
|
}
|