@sjawhar/pi-legion-envoy 5.7.0 → 5.8.0
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/envoy.js +296 -1
- package/dist/legion.js +25 -1
- package/package.json +1 -1
package/dist/envoy.js
CHANGED
|
@@ -29880,6 +29880,30 @@ function actorLabel(actor, titles) {
|
|
|
29880
29880
|
}
|
|
29881
29881
|
return `${name} (as ${serviceSubjectLabel(service)})`;
|
|
29882
29882
|
}
|
|
29883
|
+
// ../contracts/src/agent-stream.ts
|
|
29884
|
+
var AGENT_STREAM_SUBJECT_PREFIX = "agentstream.";
|
|
29885
|
+
var AGENT_STREAM_PROTOCOL = 1;
|
|
29886
|
+
function agentStreamFramesSubject(sessionID) {
|
|
29887
|
+
return `${AGENT_STREAM_SUBJECT_PREFIX}${sessionID}.frames`;
|
|
29888
|
+
}
|
|
29889
|
+
function agentStreamControlSubject(sessionID) {
|
|
29890
|
+
return `${AGENT_STREAM_SUBJECT_PREFIX}${sessionID}.control`;
|
|
29891
|
+
}
|
|
29892
|
+
var AGENT_STREAM_WATCH_TTL_MS = 30000;
|
|
29893
|
+
var AGENT_STREAM_LIMITS = {
|
|
29894
|
+
partChars: 16000,
|
|
29895
|
+
toolChars: 8000,
|
|
29896
|
+
historyMessages: 200,
|
|
29897
|
+
historyBytes: 512 * 1024,
|
|
29898
|
+
snapshotIntervalMs: 100
|
|
29899
|
+
};
|
|
29900
|
+
var AGENT_STREAM_TRUNCATION_SUFFIX = `
|
|
29901
|
+
\u2026 truncated`;
|
|
29902
|
+
function capAgentStreamText(text, limit) {
|
|
29903
|
+
if (text.length <= limit)
|
|
29904
|
+
return text;
|
|
29905
|
+
return text.slice(0, limit) + AGENT_STREAM_TRUNCATION_SUFFIX;
|
|
29906
|
+
}
|
|
29883
29907
|
// ../contracts/src/dispatch-href.ts
|
|
29884
29908
|
function itemFromSearch(search) {
|
|
29885
29909
|
const params = new URLSearchParams(search);
|
|
@@ -34799,6 +34823,202 @@ function sourceSession(sessionID) {
|
|
|
34799
34823
|
import { logger } from "@oh-my-pi/pi-utils";
|
|
34800
34824
|
var import_nats2 = __toESM(require_mod4(), 1);
|
|
34801
34825
|
|
|
34826
|
+
// src/agent-stream.ts
|
|
34827
|
+
var encoder = new TextEncoder;
|
|
34828
|
+
function readMessage(raw) {
|
|
34829
|
+
if (typeof raw !== "object" || raw === null)
|
|
34830
|
+
return null;
|
|
34831
|
+
const message = raw;
|
|
34832
|
+
if (typeof message.role !== "string" || typeof message.timestamp !== "number")
|
|
34833
|
+
return null;
|
|
34834
|
+
return {
|
|
34835
|
+
host: message,
|
|
34836
|
+
isError: message.isError === true,
|
|
34837
|
+
role: message.role,
|
|
34838
|
+
timestamp: message.timestamp,
|
|
34839
|
+
toolCallId: typeof message.toolCallId === "string" ? message.toolCallId : undefined,
|
|
34840
|
+
toolName: typeof message.toolName === "string" ? message.toolName : undefined
|
|
34841
|
+
};
|
|
34842
|
+
}
|
|
34843
|
+
function keyFor(message) {
|
|
34844
|
+
if (message.role === "toolResult") {
|
|
34845
|
+
return message.toolCallId === undefined ? null : `t:${message.toolCallId}`;
|
|
34846
|
+
}
|
|
34847
|
+
if (message.role !== "assistant" && message.role !== "user")
|
|
34848
|
+
return null;
|
|
34849
|
+
return `m:${message.role === "assistant" ? "a" : "u"}${message.timestamp}`;
|
|
34850
|
+
}
|
|
34851
|
+
function contentText(content) {
|
|
34852
|
+
if (typeof content === "string")
|
|
34853
|
+
return content;
|
|
34854
|
+
if (!Array.isArray(content))
|
|
34855
|
+
return "";
|
|
34856
|
+
const pieces = [];
|
|
34857
|
+
for (const raw of content) {
|
|
34858
|
+
if (typeof raw !== "object" || raw === null)
|
|
34859
|
+
continue;
|
|
34860
|
+
const part = raw;
|
|
34861
|
+
if (typeof part.text === "string")
|
|
34862
|
+
pieces.push(part.text);
|
|
34863
|
+
else if (typeof part.type === "string")
|
|
34864
|
+
pieces.push(`[${part.type}]`);
|
|
34865
|
+
}
|
|
34866
|
+
return pieces.join(`
|
|
34867
|
+
`);
|
|
34868
|
+
}
|
|
34869
|
+
function boundedStrings(_key, value) {
|
|
34870
|
+
return typeof value === "string" ? capAgentStreamText(value, AGENT_STREAM_LIMITS.toolChars) : value;
|
|
34871
|
+
}
|
|
34872
|
+
function renderArguments(value) {
|
|
34873
|
+
if (value === undefined || value === null)
|
|
34874
|
+
return "";
|
|
34875
|
+
try {
|
|
34876
|
+
return JSON.stringify(value, boundedStrings, 2) ?? "";
|
|
34877
|
+
} catch {
|
|
34878
|
+
return "[unserialisable arguments]";
|
|
34879
|
+
}
|
|
34880
|
+
}
|
|
34881
|
+
function conversationParts(content) {
|
|
34882
|
+
const { partChars, toolChars } = AGENT_STREAM_LIMITS;
|
|
34883
|
+
if (typeof content === "string") {
|
|
34884
|
+
return content === "" ? [] : [{ text: capAgentStreamText(content, partChars), type: "text" }];
|
|
34885
|
+
}
|
|
34886
|
+
if (!Array.isArray(content))
|
|
34887
|
+
return [];
|
|
34888
|
+
const parts = [];
|
|
34889
|
+
for (const raw of content) {
|
|
34890
|
+
if (typeof raw !== "object" || raw === null)
|
|
34891
|
+
continue;
|
|
34892
|
+
const part = raw;
|
|
34893
|
+
if (part.type === "text" && typeof part.text === "string") {
|
|
34894
|
+
parts.push({ text: capAgentStreamText(part.text, partChars), type: "text" });
|
|
34895
|
+
} else if (part.type === "thinking" && typeof part.thinking === "string") {
|
|
34896
|
+
parts.push({ text: capAgentStreamText(part.thinking, partChars), type: "reasoning" });
|
|
34897
|
+
} else if (part.type === "toolCall" && typeof part.id === "string") {
|
|
34898
|
+
parts.push({
|
|
34899
|
+
argsText: capAgentStreamText(renderArguments(part.arguments), toolChars),
|
|
34900
|
+
toolCallId: part.id,
|
|
34901
|
+
toolName: typeof part.name === "string" ? part.name : "tool",
|
|
34902
|
+
type: "tool-call"
|
|
34903
|
+
});
|
|
34904
|
+
} else if (typeof part.type === "string") {
|
|
34905
|
+
parts.push({ text: `[${part.type}]`, type: "text" });
|
|
34906
|
+
}
|
|
34907
|
+
}
|
|
34908
|
+
return parts;
|
|
34909
|
+
}
|
|
34910
|
+
function frameFor(entry) {
|
|
34911
|
+
const { message } = entry;
|
|
34912
|
+
if (message.role === "toolResult" && message.toolCallId !== undefined) {
|
|
34913
|
+
return {
|
|
34914
|
+
kind: "tool-result",
|
|
34915
|
+
result: {
|
|
34916
|
+
at: message.timestamp,
|
|
34917
|
+
isError: message.isError,
|
|
34918
|
+
output: capAgentStreamText(contentText(message.host.content), AGENT_STREAM_LIMITS.toolChars),
|
|
34919
|
+
toolCallId: message.toolCallId,
|
|
34920
|
+
toolName: message.toolName ?? "tool"
|
|
34921
|
+
},
|
|
34922
|
+
seq: entry.seq,
|
|
34923
|
+
v: AGENT_STREAM_PROTOCOL
|
|
34924
|
+
};
|
|
34925
|
+
}
|
|
34926
|
+
return {
|
|
34927
|
+
kind: "message",
|
|
34928
|
+
message: {
|
|
34929
|
+
at: message.timestamp,
|
|
34930
|
+
id: entry.key.slice(2),
|
|
34931
|
+
parts: conversationParts(message.host.content),
|
|
34932
|
+
role: message.role === "assistant" ? "assistant" : "user",
|
|
34933
|
+
streaming: entry.streaming
|
|
34934
|
+
},
|
|
34935
|
+
seq: entry.seq,
|
|
34936
|
+
v: AGENT_STREAM_PROTOCOL
|
|
34937
|
+
};
|
|
34938
|
+
}
|
|
34939
|
+
|
|
34940
|
+
class AgentStreamPublisher {
|
|
34941
|
+
#deps;
|
|
34942
|
+
#history = new Map;
|
|
34943
|
+
#settled = new Set;
|
|
34944
|
+
#publishedAt = new Map;
|
|
34945
|
+
#seq = 0;
|
|
34946
|
+
#watchedUntil = 0;
|
|
34947
|
+
constructor(deps) {
|
|
34948
|
+
this.#deps = deps;
|
|
34949
|
+
}
|
|
34950
|
+
noteViewer() {
|
|
34951
|
+
this.#watchedUntil = this.#deps.now() + AGENT_STREAM_WATCH_TTL_MS;
|
|
34952
|
+
}
|
|
34953
|
+
get watched() {
|
|
34954
|
+
return this.#deps.now() < this.#watchedUntil;
|
|
34955
|
+
}
|
|
34956
|
+
reset() {
|
|
34957
|
+
this.#history.clear();
|
|
34958
|
+
this.#settled.clear();
|
|
34959
|
+
this.#publishedAt.clear();
|
|
34960
|
+
this.#watchedUntil = 0;
|
|
34961
|
+
}
|
|
34962
|
+
record(subject2, raw, streaming) {
|
|
34963
|
+
const message = readMessage(raw);
|
|
34964
|
+
if (message === null)
|
|
34965
|
+
return false;
|
|
34966
|
+
const key = keyFor(message);
|
|
34967
|
+
if (key === null)
|
|
34968
|
+
return false;
|
|
34969
|
+
const stillStreaming = message.role === "assistant" && streaming;
|
|
34970
|
+
if (stillStreaming && this.#settled.has(key))
|
|
34971
|
+
return false;
|
|
34972
|
+
this.#seq += 1;
|
|
34973
|
+
const entry = {
|
|
34974
|
+
at: message.timestamp,
|
|
34975
|
+
key,
|
|
34976
|
+
message,
|
|
34977
|
+
seq: this.#seq,
|
|
34978
|
+
streaming: stillStreaming
|
|
34979
|
+
};
|
|
34980
|
+
this.#history.set(key, entry);
|
|
34981
|
+
if (!stillStreaming)
|
|
34982
|
+
this.#settled.add(key);
|
|
34983
|
+
while (this.#history.size > AGENT_STREAM_LIMITS.historyMessages) {
|
|
34984
|
+
const oldest = this.#history.keys().next();
|
|
34985
|
+
if (oldest.done === true)
|
|
34986
|
+
break;
|
|
34987
|
+
this.#history.delete(oldest.value);
|
|
34988
|
+
this.#settled.delete(oldest.value);
|
|
34989
|
+
this.#publishedAt.delete(oldest.value);
|
|
34990
|
+
}
|
|
34991
|
+
if (!this.watched)
|
|
34992
|
+
return true;
|
|
34993
|
+
const now = this.#deps.now();
|
|
34994
|
+
const published = this.#publishedAt.get(key);
|
|
34995
|
+
const tooSoon = published !== undefined && now - published < AGENT_STREAM_LIMITS.snapshotIntervalMs;
|
|
34996
|
+
if (stillStreaming && tooSoon)
|
|
34997
|
+
return true;
|
|
34998
|
+
this.#publishedAt.set(key, now);
|
|
34999
|
+
this.#deps.publish(subject2, JSON.stringify(frameFor(entry)));
|
|
35000
|
+
return true;
|
|
35001
|
+
}
|
|
35002
|
+
replay(sessionID) {
|
|
35003
|
+
if (!this.watched)
|
|
35004
|
+
return { frames: [], session_id: sessionID, v: AGENT_STREAM_PROTOCOL };
|
|
35005
|
+
const ordered = [...this.#history.values()].sort((left, right) => left.at === right.at ? left.seq - right.seq : left.at - right.at);
|
|
35006
|
+
let budget = AGENT_STREAM_LIMITS.historyBytes;
|
|
35007
|
+
const kept = [];
|
|
35008
|
+
for (let index = ordered.length - 1;index >= 0; index -= 1) {
|
|
35009
|
+
const entry = ordered[index];
|
|
35010
|
+
if (entry === undefined)
|
|
35011
|
+
continue;
|
|
35012
|
+
const frame = frameFor(entry);
|
|
35013
|
+
budget -= encoder.encode(JSON.stringify(frame)).length;
|
|
35014
|
+
if (budget < 0)
|
|
35015
|
+
break;
|
|
35016
|
+
kept.unshift(frame);
|
|
35017
|
+
}
|
|
35018
|
+
return { frames: kept, session_id: sessionID, v: AGENT_STREAM_PROTOCOL };
|
|
35019
|
+
}
|
|
35020
|
+
}
|
|
35021
|
+
|
|
34802
35022
|
// src/envoy-session.ts
|
|
34803
35023
|
import path2 from "path";
|
|
34804
35024
|
var ENVOY_SESSIONS = Symbol.for("legion.pi-envoy.envoy-session");
|
|
@@ -35371,6 +35591,75 @@ function envoyExtension(pi) {
|
|
|
35371
35591
|
}
|
|
35372
35592
|
return true;
|
|
35373
35593
|
};
|
|
35594
|
+
const agentStream = new AgentStreamPublisher({
|
|
35595
|
+
now: () => Date.now(),
|
|
35596
|
+
publish: (subject2, payload) => connection?.publish(subject2, codec2.encode(payload))
|
|
35597
|
+
});
|
|
35598
|
+
let agentStreamControl;
|
|
35599
|
+
let agentStreamControlSession = "";
|
|
35600
|
+
const pumpAgentStreamControl = async (subscription, session) => {
|
|
35601
|
+
try {
|
|
35602
|
+
for await (const message of subscription) {
|
|
35603
|
+
try {
|
|
35604
|
+
const parsed = JSON.parse(codec2.decode(message.data));
|
|
35605
|
+
if (typeof parsed !== "object" || parsed === null)
|
|
35606
|
+
continue;
|
|
35607
|
+
const request = parsed;
|
|
35608
|
+
if (request.type !== "replay" && request.type !== "watch")
|
|
35609
|
+
continue;
|
|
35610
|
+
const reply = message.reply;
|
|
35611
|
+
const answer = request.type === "replay" && reply !== undefined ? JSON.stringify(agentStream.replay(session)) : undefined;
|
|
35612
|
+
agentStream.noteViewer();
|
|
35613
|
+
if (answer === undefined || reply === undefined)
|
|
35614
|
+
continue;
|
|
35615
|
+
connection?.publish(reply, codec2.encode(answer));
|
|
35616
|
+
} catch (error48) {
|
|
35617
|
+
logger.warn("envoy: agent stream control message failed", {
|
|
35618
|
+
error: messageFor(error48),
|
|
35619
|
+
sessionID: session
|
|
35620
|
+
});
|
|
35621
|
+
}
|
|
35622
|
+
}
|
|
35623
|
+
} catch {}
|
|
35624
|
+
if (agentStreamControl === subscription)
|
|
35625
|
+
agentStreamControl = undefined;
|
|
35626
|
+
if (shuttingDown || agentStreamControlSession !== session)
|
|
35627
|
+
return;
|
|
35628
|
+
setTimeout(() => {
|
|
35629
|
+
if (shuttingDown || agentStreamControlSession !== session)
|
|
35630
|
+
return;
|
|
35631
|
+
watchAgentStream(session).catch(() => {
|
|
35632
|
+
return;
|
|
35633
|
+
});
|
|
35634
|
+
}, RESUBSCRIBE_DELAY_MS);
|
|
35635
|
+
};
|
|
35636
|
+
const watchAgentStream = async (session) => {
|
|
35637
|
+
if (agentStreamControlSession === session && agentStreamControl !== undefined)
|
|
35638
|
+
return;
|
|
35639
|
+
agentStreamControl?.unsubscribe();
|
|
35640
|
+
agentStreamControl = undefined;
|
|
35641
|
+
if (agentStreamControlSession !== session)
|
|
35642
|
+
agentStream.reset();
|
|
35643
|
+
agentStreamControlSession = session;
|
|
35644
|
+
const active = await ensureConnection();
|
|
35645
|
+
const subscription = active.subscribe(agentStreamControlSubject(session));
|
|
35646
|
+
agentStreamControl = subscription;
|
|
35647
|
+
pumpAgentStreamControl(subscription, session);
|
|
35648
|
+
};
|
|
35649
|
+
const recordAgentStreamMessage = (message, streaming) => {
|
|
35650
|
+
if (sessionID === "")
|
|
35651
|
+
return;
|
|
35652
|
+
agentStream.record(agentStreamFramesSubject(sessionID), message, streaming);
|
|
35653
|
+
};
|
|
35654
|
+
pi.on("message_start", async (event) => {
|
|
35655
|
+
recordAgentStreamMessage(event.message, true);
|
|
35656
|
+
});
|
|
35657
|
+
pi.on("message_update", async (event) => {
|
|
35658
|
+
recordAgentStreamMessage(event.message, true);
|
|
35659
|
+
});
|
|
35660
|
+
pi.on("message_end", async (event) => {
|
|
35661
|
+
recordAgentStreamMessage(event.message, false);
|
|
35662
|
+
});
|
|
35374
35663
|
const endRole = () => {
|
|
35375
35664
|
claimedRoleTopic = undefined;
|
|
35376
35665
|
claimedRoleSessionID = undefined;
|
|
@@ -35592,6 +35881,7 @@ function envoyExtension(pi) {
|
|
|
35592
35881
|
}
|
|
35593
35882
|
await ensureConnection();
|
|
35594
35883
|
await subscribe(currentTopic);
|
|
35884
|
+
await watchAgentStream(sessionID);
|
|
35595
35885
|
if (resumed) {
|
|
35596
35886
|
await recoverRegisteredInterests();
|
|
35597
35887
|
}
|
|
@@ -35756,6 +36046,8 @@ function envoyExtension(pi) {
|
|
|
35756
36046
|
subscriptions.clear();
|
|
35757
36047
|
intentionallyClosed.clear();
|
|
35758
36048
|
awaitingRetry.clear();
|
|
36049
|
+
agentStreamControl = undefined;
|
|
36050
|
+
agentStreamControlSession = "";
|
|
35759
36051
|
}
|
|
35760
36052
|
});
|
|
35761
36053
|
for (const spec of envoyToolSpecs) {
|
|
@@ -35880,7 +36172,10 @@ function envoyExtension(pi) {
|
|
|
35880
36172
|
askCheckAbort = abort;
|
|
35881
36173
|
let answered;
|
|
35882
36174
|
try {
|
|
35883
|
-
answered = askEphemeral({
|
|
36175
|
+
answered = askEphemeral({
|
|
36176
|
+
prompt: ASK_SELF_CHECK_PROMPT(open.snapshot.asks),
|
|
36177
|
+
signal: abort.signal
|
|
36178
|
+
}).then((reply) => reply.replyText, (error48) => {
|
|
35884
36179
|
if (!abort.signal.aborted)
|
|
35885
36180
|
logSelfCheckFailure(id, error48);
|
|
35886
36181
|
return;
|
package/dist/legion.js
CHANGED
|
@@ -16192,7 +16192,7 @@ import { logger } from "@oh-my-pi/pi-utils";
|
|
|
16192
16192
|
// package.json
|
|
16193
16193
|
var package_default = {
|
|
16194
16194
|
name: "@sjawhar/pi-legion-envoy",
|
|
16195
|
-
version: "5.
|
|
16195
|
+
version: "5.8.0",
|
|
16196
16196
|
type: "module",
|
|
16197
16197
|
omp: {
|
|
16198
16198
|
extensions: [
|
|
@@ -30003,6 +30003,30 @@ function actorLabel(actor, titles) {
|
|
|
30003
30003
|
}
|
|
30004
30004
|
return `${name} (as ${serviceSubjectLabel(service)})`;
|
|
30005
30005
|
}
|
|
30006
|
+
// ../contracts/src/agent-stream.ts
|
|
30007
|
+
var AGENT_STREAM_SUBJECT_PREFIX = "agentstream.";
|
|
30008
|
+
var AGENT_STREAM_PROTOCOL = 1;
|
|
30009
|
+
function agentStreamFramesSubject(sessionID) {
|
|
30010
|
+
return `${AGENT_STREAM_SUBJECT_PREFIX}${sessionID}.frames`;
|
|
30011
|
+
}
|
|
30012
|
+
function agentStreamControlSubject(sessionID) {
|
|
30013
|
+
return `${AGENT_STREAM_SUBJECT_PREFIX}${sessionID}.control`;
|
|
30014
|
+
}
|
|
30015
|
+
var AGENT_STREAM_WATCH_TTL_MS = 30000;
|
|
30016
|
+
var AGENT_STREAM_LIMITS = {
|
|
30017
|
+
partChars: 16000,
|
|
30018
|
+
toolChars: 8000,
|
|
30019
|
+
historyMessages: 200,
|
|
30020
|
+
historyBytes: 512 * 1024,
|
|
30021
|
+
snapshotIntervalMs: 100
|
|
30022
|
+
};
|
|
30023
|
+
var AGENT_STREAM_TRUNCATION_SUFFIX = `
|
|
30024
|
+
\u2026 truncated`;
|
|
30025
|
+
function capAgentStreamText(text, limit) {
|
|
30026
|
+
if (text.length <= limit)
|
|
30027
|
+
return text;
|
|
30028
|
+
return text.slice(0, limit) + AGENT_STREAM_TRUNCATION_SUFFIX;
|
|
30029
|
+
}
|
|
30006
30030
|
// ../contracts/src/dispatch-href.ts
|
|
30007
30031
|
function itemFromSearch(search) {
|
|
30008
30032
|
const params = new URLSearchParams(search);
|