@sjawhar/pi-legion-envoy 0.6.0 → 0.7.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 +92 -48
- package/package.json +1 -1
package/dist/envoy.js
CHANGED
|
@@ -20829,11 +20829,25 @@ function superRefine(fn) {
|
|
|
20829
20829
|
}
|
|
20830
20830
|
// ../../node_modules/.bun/zod@4.3.6/node_modules/zod/v4/classic/external.js
|
|
20831
20831
|
config(en_default());
|
|
20832
|
+
// ../contracts/src/dispatch-question.ts
|
|
20833
|
+
var DispatchQuestionOptionSchema = strictObject({
|
|
20834
|
+
label: string2().min(1),
|
|
20835
|
+
description: string2().optional()
|
|
20836
|
+
});
|
|
20837
|
+
var DispatchQuestionSchema = strictObject({
|
|
20838
|
+
askId: string2().optional(),
|
|
20839
|
+
question: string2().min(1),
|
|
20840
|
+
header: string2().optional(),
|
|
20841
|
+
options: array(DispatchQuestionOptionSchema).optional(),
|
|
20842
|
+
multiple: boolean2().optional(),
|
|
20843
|
+
custom: boolean2().optional()
|
|
20844
|
+
});
|
|
20845
|
+
var DispatchQuestionInputSchema = DispatchQuestionSchema.omit({ askId: true });
|
|
20832
20846
|
// ../contracts/src/envelope.ts
|
|
20833
20847
|
var isSubject = (value) => typeof value === "string" && value.length > 0;
|
|
20834
20848
|
var EnvelopeSchema = object({
|
|
20835
20849
|
event_id: string2().min(1),
|
|
20836
|
-
source: _enum(["agent", "envoy", "github", "slack", "whatsapp", "ghostwispr"]),
|
|
20850
|
+
source: _enum(["agent", "human", "envoy", "github", "slack", "whatsapp", "ghostwispr"]),
|
|
20837
20851
|
source_event_id: string2().min(1),
|
|
20838
20852
|
source_session: string2().optional(),
|
|
20839
20853
|
topic: custom(isSubject, { message: "topic must be a non-empty subject" }),
|
|
@@ -21143,25 +21157,13 @@ var DISPATCH_ARGUMENTS = {
|
|
|
21143
21157
|
repo: "owner/name. Opening a thread only; defaults to the working directory's GitHub repo.",
|
|
21144
21158
|
parent: "<n> | owner/name#<n>[#<commentId>]. Opening a thread only: link the thread as a sub-issue of an existing issue and append a breadcrumb to the comment."
|
|
21145
21159
|
};
|
|
21146
|
-
|
|
21147
21160
|
class DispatchArgumentError extends Error {
|
|
21148
21161
|
name = "DispatchArgumentError";
|
|
21149
21162
|
}
|
|
21150
|
-
var QuestionOptionSchema = strictObject({
|
|
21151
|
-
label: string2().min(1),
|
|
21152
|
-
description: string2().optional()
|
|
21153
|
-
});
|
|
21154
|
-
var DispatchQuestionSchema = strictObject({
|
|
21155
|
-
question: string2().min(1),
|
|
21156
|
-
header: string2().optional(),
|
|
21157
|
-
options: array(QuestionOptionSchema).optional(),
|
|
21158
|
-
multiple: boolean2().optional(),
|
|
21159
|
-
custom: boolean2().optional()
|
|
21160
|
-
});
|
|
21161
21163
|
var prose = {
|
|
21162
21164
|
context: string2(),
|
|
21163
21165
|
question: string2(),
|
|
21164
|
-
ask: array(
|
|
21166
|
+
ask: array(DispatchQuestionInputSchema).optional()
|
|
21165
21167
|
};
|
|
21166
21168
|
var OpenThreadCallSchema = strictObject({
|
|
21167
21169
|
subject: string2(),
|
|
@@ -21176,7 +21178,7 @@ var dispatchToolShape = {
|
|
|
21176
21178
|
thread: string2().describe(DISPATCH_ARGUMENTS.thread).optional(),
|
|
21177
21179
|
context: string2().describe(DISPATCH_ARGUMENTS.context),
|
|
21178
21180
|
question: string2().describe(DISPATCH_ARGUMENTS.question),
|
|
21179
|
-
ask: array(
|
|
21181
|
+
ask: array(DispatchQuestionInputSchema).describe(DISPATCH_ARGUMENTS.ask).optional(),
|
|
21180
21182
|
urgency: _enum(DISPATCH_URGENCIES).describe(DISPATCH_ARGUMENTS.urgency).optional(),
|
|
21181
21183
|
repo: string2().describe(DISPATCH_ARGUMENTS.repo).optional(),
|
|
21182
21184
|
parent: string2().describe(DISPATCH_ARGUMENTS.parent).optional()
|
|
@@ -21284,7 +21286,7 @@ async function resolveOrigin(env, exec, cwd) {
|
|
|
21284
21286
|
cwd,
|
|
21285
21287
|
machine: machineID()
|
|
21286
21288
|
};
|
|
21287
|
-
const pane = env
|
|
21289
|
+
const pane = env.TMUX_PANE;
|
|
21288
21290
|
if (pane) {
|
|
21289
21291
|
const output = await tryExec(exec, "tmux", ["display-message", "-p", "-t", pane, "#S:#I.#P #{pane_id}"], cwd);
|
|
21290
21292
|
const [target, paneId] = output?.trim().split(" ") ?? [];
|
|
@@ -21478,10 +21480,10 @@ function readEnvoyFile(filePath) {
|
|
|
21478
21480
|
return { kind: "valid", settings: parsed.data.dispatch ?? null };
|
|
21479
21481
|
}
|
|
21480
21482
|
function resolveDispatchConfig(env, options = {}) {
|
|
21481
|
-
const explicit = env
|
|
21483
|
+
const explicit = env.DISPATCH_MCP_URL;
|
|
21482
21484
|
if (explicit)
|
|
21483
21485
|
return { url: explicit, error: null };
|
|
21484
|
-
const home = options.home ?? env
|
|
21486
|
+
const home = options.home ?? env.HOME ?? homedir();
|
|
21485
21487
|
const cwd = options.cwd ?? process.cwd();
|
|
21486
21488
|
const userFile = readEnvoyFile(path.join(home, ".config", "opencode", "envoy.json"));
|
|
21487
21489
|
const repoFile = readEnvoyFile(path.join(cwd, ".opencode", "envoy.json"));
|
|
@@ -21521,6 +21523,26 @@ function dispatchSubscriptionTopic(tool, output) {
|
|
|
21521
21523
|
return dispatchThreadTopic(owner, repo, thread);
|
|
21522
21524
|
}
|
|
21523
21525
|
|
|
21526
|
+
// ../envoy-client/src/delivery.ts
|
|
21527
|
+
function senderLabel(envelope) {
|
|
21528
|
+
return envelope.source_session ?? envelope.source;
|
|
21529
|
+
}
|
|
21530
|
+
function replyWith(envelope) {
|
|
21531
|
+
if (envelope.source !== "agent" || envelope.source_session === undefined)
|
|
21532
|
+
return;
|
|
21533
|
+
return `envoy_send(session_id="${envelope.source_session}", message="...")`;
|
|
21534
|
+
}
|
|
21535
|
+
function isOwnDispatchEcho(envelope, sessionID) {
|
|
21536
|
+
if (envelope.source !== "github" || envelope.payload === undefined)
|
|
21537
|
+
return false;
|
|
21538
|
+
try {
|
|
21539
|
+
const payload = JSON.parse(envelope.payload);
|
|
21540
|
+
return typeof payload === "object" && payload !== null && "dispatch_session" in payload && payload.dispatch_session === sessionID;
|
|
21541
|
+
} catch {
|
|
21542
|
+
return false;
|
|
21543
|
+
}
|
|
21544
|
+
}
|
|
21545
|
+
|
|
21524
21546
|
// ../envoy-client/src/tool-contract.ts
|
|
21525
21547
|
var EnvoyToolOperation = {
|
|
21526
21548
|
subscribe: "subscribe",
|
|
@@ -21663,18 +21685,23 @@ function createEnvoyClient(config) {
|
|
|
21663
21685
|
},
|
|
21664
21686
|
getInterest: async (sessionID) => InterestWireSchema.parse(JSON.parse(await request(`/v1/interests/${sessionID}`, {}))),
|
|
21665
21687
|
send: async (input) => toEnvelope(await post("/v1/messages/send", {
|
|
21666
|
-
|
|
21688
|
+
source: input.source ?? "agent",
|
|
21689
|
+
...input.sourceSessionID === undefined ? {} : { source_session: input.sourceSessionID },
|
|
21667
21690
|
target_session: input.targetSessionID,
|
|
21668
21691
|
message: input.message,
|
|
21669
21692
|
...input.idempotencyKey === undefined ? {} : { idempotency_key: input.idempotencyKey }
|
|
21670
21693
|
})),
|
|
21671
21694
|
publish: async (input) => toEnvelope(await post("/v1/messages/publish", {
|
|
21672
|
-
|
|
21695
|
+
source: input.source ?? "agent",
|
|
21696
|
+
...input.sourceSessionID === undefined ? {} : { source_session: input.sourceSessionID },
|
|
21673
21697
|
topic: input.topic,
|
|
21674
21698
|
message: input.message,
|
|
21675
21699
|
...input.payload === undefined ? {} : { payload: input.payload },
|
|
21676
21700
|
...input.idempotencyKey === undefined ? {} : { idempotency_key: input.idempotencyKey }
|
|
21677
21701
|
})),
|
|
21702
|
+
unregisterSession: async (sessionID) => {
|
|
21703
|
+
await request(`/v1/sessions/${encodeURIComponent(sessionID)}`, { method: "DELETE" });
|
|
21704
|
+
},
|
|
21678
21705
|
setRole: async (input) => InterestWireSchema.parse(JSON.parse(await post("/v1/roles/set", { session_id: input.sessionID, role: input.role }))),
|
|
21679
21706
|
listSessions: async () => SessionWireSchema.array().parse(JSON.parse(await request("/v1/sessions", {})))
|
|
21680
21707
|
};
|
|
@@ -22312,6 +22339,7 @@ function envoyExtension(pi) {
|
|
|
22312
22339
|
const deliver = async (subject, raw, reply) => {
|
|
22313
22340
|
let content = "";
|
|
22314
22341
|
let duplicate = false;
|
|
22342
|
+
let dispatchEcho = false;
|
|
22315
22343
|
try {
|
|
22316
22344
|
const envelope = EnvelopeSchema.parse(JSON.parse(raw));
|
|
22317
22345
|
duplicate = dedupeKeys.has(envelope.dedupe_key);
|
|
@@ -22322,6 +22350,7 @@ function envoyExtension(pi) {
|
|
|
22322
22350
|
if (!oldest.done)
|
|
22323
22351
|
dedupeKeys.delete(oldest.value);
|
|
22324
22352
|
}
|
|
22353
|
+
dispatchEcho = isOwnDispatchEcho(envelope, sessionID);
|
|
22325
22354
|
let message;
|
|
22326
22355
|
if (envelope.payload !== undefined) {
|
|
22327
22356
|
try {
|
|
@@ -22330,26 +22359,26 @@ function envoyExtension(pi) {
|
|
|
22330
22359
|
message = envelope.payload;
|
|
22331
22360
|
}
|
|
22332
22361
|
}
|
|
22333
|
-
|
|
22334
|
-
|
|
22335
|
-
|
|
22336
|
-
|
|
22337
|
-
|
|
22338
|
-
|
|
22339
|
-
|
|
22340
|
-
|
|
22341
|
-
|
|
22342
|
-
|
|
22343
|
-
|
|
22344
|
-
|
|
22345
|
-
|
|
22346
|
-
}
|
|
22347
|
-
}
|
|
22362
|
+
if (!dispatchEcho) {
|
|
22363
|
+
const reply = replyWith(envelope);
|
|
22364
|
+
const echo = envelope.source_session !== undefined && envelope.source_session === sessionID;
|
|
22365
|
+
content = encode2({
|
|
22366
|
+
envoy: {
|
|
22367
|
+
topic: envelope.topic,
|
|
22368
|
+
from: senderLabel(envelope),
|
|
22369
|
+
...echo ? {
|
|
22370
|
+
echo: `your own message, sent by this session (${sessionID}) \u2014 not an incoming reply`
|
|
22371
|
+
} : reply === undefined ? {} : { reply_with: reply },
|
|
22372
|
+
summary: envelope.payload_summary,
|
|
22373
|
+
...message === undefined ? {} : { message }
|
|
22374
|
+
}
|
|
22375
|
+
});
|
|
22376
|
+
}
|
|
22348
22377
|
}
|
|
22349
22378
|
} catch {
|
|
22350
22379
|
content = encode2({ envoy: { topic: subject, message: raw } });
|
|
22351
22380
|
}
|
|
22352
|
-
if (!duplicate) {
|
|
22381
|
+
if (!duplicate && !dispatchEcho) {
|
|
22353
22382
|
pi.sendMessage({ customType: "envoy-message", content, display: true }, { deliverAs: "steer", triggerTurn: true });
|
|
22354
22383
|
}
|
|
22355
22384
|
if (reply !== "" && subject === agentSubject(sessionID)) {
|
|
@@ -22420,17 +22449,16 @@ function envoyExtension(pi) {
|
|
|
22420
22449
|
selfSubscribed: true
|
|
22421
22450
|
});
|
|
22422
22451
|
};
|
|
22423
|
-
const registrationRequired = () => process.env.ENVOY_REGISTER_SESSION === "1" || claimedRoleTopic !== undefined;
|
|
22424
22452
|
const ensureHeartbeat = (context) => {
|
|
22425
22453
|
if (heartbeatRegistered)
|
|
22426
22454
|
return;
|
|
22427
22455
|
let heartbeatOutageNotified = false;
|
|
22428
22456
|
let healing = false;
|
|
22429
22457
|
context.setInterval(() => {
|
|
22430
|
-
if (!registrationRequired())
|
|
22431
|
-
return;
|
|
22432
22458
|
const liveSessionID = context.sessionManager.getSessionId();
|
|
22433
|
-
|
|
22459
|
+
if (liveSessionID === "")
|
|
22460
|
+
return;
|
|
22461
|
+
const drifted = liveSessionID !== sessionID;
|
|
22434
22462
|
if (healing)
|
|
22435
22463
|
return;
|
|
22436
22464
|
healing = true;
|
|
@@ -22464,6 +22492,12 @@ function envoyExtension(pi) {
|
|
|
22464
22492
|
sessionDirectory = context.cwd;
|
|
22465
22493
|
sessionID = context.sessionManager.getSessionId();
|
|
22466
22494
|
activeSessionContext = context;
|
|
22495
|
+
if (sessionID === "") {
|
|
22496
|
+
if (previousTopic !== undefined)
|
|
22497
|
+
closeIntentionally(previousTopic);
|
|
22498
|
+
ensureHeartbeat(context);
|
|
22499
|
+
return;
|
|
22500
|
+
}
|
|
22467
22501
|
const currentTopic = agentSubject(sessionID);
|
|
22468
22502
|
if (previousTopic !== undefined && previousTopic !== currentTopic) {
|
|
22469
22503
|
closeIntentionally(previousTopic);
|
|
@@ -22473,10 +22507,8 @@ function envoyExtension(pi) {
|
|
|
22473
22507
|
if ((context.sessionManager.getBranch?.() ?? []).length > 0) {
|
|
22474
22508
|
await recoverRegisteredInterests();
|
|
22475
22509
|
}
|
|
22476
|
-
|
|
22477
|
-
|
|
22478
|
-
ensureHeartbeat(context);
|
|
22479
|
-
}
|
|
22510
|
+
await registerSession();
|
|
22511
|
+
ensureHeartbeat(context);
|
|
22480
22512
|
};
|
|
22481
22513
|
const setEnvoyRole = async (role) => {
|
|
22482
22514
|
const topic = ROLE_TOPIC_PREFIX + role;
|
|
@@ -22566,11 +22598,23 @@ function envoyExtension(pi) {
|
|
|
22566
22598
|
pi.on("session_tree", (_event, context) => rebind(undefined, context));
|
|
22567
22599
|
pi.on("session_shutdown", async () => {
|
|
22568
22600
|
shuttingDown = true;
|
|
22601
|
+
const deadline = Promise.withResolvers();
|
|
22602
|
+
const timer = setTimeout(deadline.resolve, 1000);
|
|
22569
22603
|
try {
|
|
22570
|
-
const
|
|
22571
|
-
|
|
22572
|
-
|
|
22573
|
-
|
|
22604
|
+
const deregistration = sessionID === "" ? Promise.resolve() : client.unregisterSession(sessionID).catch(() => {
|
|
22605
|
+
return;
|
|
22606
|
+
});
|
|
22607
|
+
const draining = connection?.drain().catch(() => {
|
|
22608
|
+
return;
|
|
22609
|
+
}) ?? Promise.resolve();
|
|
22610
|
+
await Promise.race([
|
|
22611
|
+
Promise.allSettled([deregistration, draining]).then(() => {
|
|
22612
|
+
return;
|
|
22613
|
+
}),
|
|
22614
|
+
deadline.promise
|
|
22615
|
+
]);
|
|
22616
|
+
} finally {
|
|
22617
|
+
clearTimeout(timer);
|
|
22574
22618
|
connection = undefined;
|
|
22575
22619
|
activeSessionContext = undefined;
|
|
22576
22620
|
subscriptions.clear();
|