@sjawhar/pi-legion-envoy 1.33.0 → 1.35.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 +90 -41
- package/dist/legion.js +236 -4410
- package/dist/skills/dispatch/SKILL.md +27 -4
- package/dist/skills/envoy/SKILL.md +10 -0
- package/dist/skills/legion-architect/SKILL.md +16 -9
- package/dist/skills/legion-controller/SKILL.md +235 -17
- package/dist/skills/legion-worker/SKILL.md +9 -5
- package/package.json +2 -2
package/dist/envoy.js
CHANGED
|
@@ -29649,6 +29649,7 @@ function date4(params) {
|
|
|
29649
29649
|
// ../../node_modules/.bun/zod@4.3.6/node_modules/zod/v4/classic/external.js
|
|
29650
29650
|
config(en_default());
|
|
29651
29651
|
// ../contracts/src/dispatch-api.ts
|
|
29652
|
+
var DELIVERY_CAPABILITIES = ["aside", "btw", "steer"];
|
|
29652
29653
|
function searchOwnerOf(result) {
|
|
29653
29654
|
return result.owner ?? { kind: "issue", key: result.issue.key };
|
|
29654
29655
|
}
|
|
@@ -29731,6 +29732,8 @@ var CommentEventPayloadSchema = object({
|
|
|
29731
29732
|
ask_id: string2().nullish(),
|
|
29732
29733
|
ask_question: string2().optional(),
|
|
29733
29734
|
ask_state: _enum2(["open", "answered", "resolved"]).optional(),
|
|
29735
|
+
ask_waiting_on: _enum2(["human", "agent"]).optional(),
|
|
29736
|
+
turn: _enum2(["human", "agent"]).nullish(),
|
|
29734
29737
|
anchor: object({ block_id: string2().nullable().optional(), quote: string2().optional() }).nullish(),
|
|
29735
29738
|
suggestion: object({ replace_with: string2().optional() }).nullish(),
|
|
29736
29739
|
author: object({ kind: string2(), id: string2() }).optional(),
|
|
@@ -29757,7 +29760,7 @@ var DispatchTargetedMessagePayloadSchema = MessageEventPayloadSchema.extend({
|
|
|
29757
29760
|
var MessageDeliveryEventPayloadSchema = object({
|
|
29758
29761
|
message_id: string2().optional(),
|
|
29759
29762
|
attempt: number2().int().positive().optional(),
|
|
29760
|
-
delivery: _enum2(
|
|
29763
|
+
delivery: _enum2(DELIVERY_CAPABILITIES).optional(),
|
|
29761
29764
|
session_id: string2().optional(),
|
|
29762
29765
|
target: string2().optional(),
|
|
29763
29766
|
title: string2().optional(),
|
|
@@ -29848,6 +29851,16 @@ function documentOwnerValidation(requireArtifact, alwaysRequireArtifact = false)
|
|
|
29848
29851
|
message: alwaysRequireArtifact ? "Exactly one of issue and project is required; artifact or ref must name the document." : "Exactly one of issue and project is required; with project, artifact names the document."
|
|
29849
29852
|
};
|
|
29850
29853
|
}
|
|
29854
|
+
var commentValidation = (() => {
|
|
29855
|
+
const owner = documentOwnerValidation(true);
|
|
29856
|
+
return {
|
|
29857
|
+
check: (value) => {
|
|
29858
|
+
const input = value;
|
|
29859
|
+
return owner.check(value) && (input.turn === undefined || typeof input.reply_to_ask === "string");
|
|
29860
|
+
},
|
|
29861
|
+
message: `${owner.message} turn requires reply_to_ask.`
|
|
29862
|
+
};
|
|
29863
|
+
})();
|
|
29851
29864
|
var SPEC_SECTIONS = [
|
|
29852
29865
|
"Summary",
|
|
29853
29866
|
"Decisions needed",
|
|
@@ -29943,9 +29956,10 @@ var dispatchToolSpecs = [
|
|
|
29943
29956
|
occurrence: z.number({ int: true, min: 0 }).describe("Optional zero-based occurrence of quote.").optional(),
|
|
29944
29957
|
body: z.string({ max: 2000 }).describe("Review comment, at most 2,000 characters."),
|
|
29945
29958
|
reply_to: z.string().describe("Full id of a comment to reply to; replying to any comment in a thread continues that " + "thread (an ask's clarification thread included).").optional(),
|
|
29946
|
-
reply_to_ask: z.string().describe("Optional ask id to reply to, threading this comment under that question. Mutually " + "exclusive with reply_to.").optional()
|
|
29959
|
+
reply_to_ask: z.string().describe("Optional ask id to reply to, threading this comment under that question. Mutually " + "exclusive with reply_to.").optional(),
|
|
29960
|
+
turn: z.enum(["agent", "human"]).describe("Only with reply_to_ask: who holds the turn after this reply. agent: a progress note - " + "you keep the turn and the ask stays 'Waiting on agents' for the human; human (default): " + "you need the human to act - the ask returns to 'Waiting on you'.").optional()
|
|
29947
29961
|
}),
|
|
29948
|
-
validation:
|
|
29962
|
+
validation: commentValidation
|
|
29949
29963
|
},
|
|
29950
29964
|
{
|
|
29951
29965
|
name: "dispatch_suggest",
|
|
@@ -30334,7 +30348,7 @@ var LegionDaemonApi = {
|
|
|
30334
30348
|
queue: array(nonEmptyString)
|
|
30335
30349
|
}),
|
|
30336
30350
|
gates: record(string2(), stateGate),
|
|
30337
|
-
controllerLocator:
|
|
30351
|
+
controllerLocator: stateTreeLocator.optional(),
|
|
30338
30352
|
roles: record(string2(), stateRole),
|
|
30339
30353
|
controllerPendingNotices: number2().int().nonnegative(),
|
|
30340
30354
|
pendingStatusWrites: array(nonEmptyString),
|
|
@@ -30342,7 +30356,11 @@ var LegionDaemonApi = {
|
|
|
30342
30356
|
})
|
|
30343
30357
|
},
|
|
30344
30358
|
ControllerReady: {
|
|
30345
|
-
request: strictObject({
|
|
30359
|
+
request: strictObject({
|
|
30360
|
+
secret: nonEmptyString,
|
|
30361
|
+
sessionId: nonEmptyString,
|
|
30362
|
+
ompSessionFile: nonEmptyString.optional()
|
|
30363
|
+
}),
|
|
30346
30364
|
response: object({})
|
|
30347
30365
|
},
|
|
30348
30366
|
ProcessStarted: {
|
|
@@ -30462,17 +30480,23 @@ var LegionDaemonApi = {
|
|
|
30462
30480
|
response: object({})
|
|
30463
30481
|
},
|
|
30464
30482
|
Grant: {
|
|
30465
|
-
request:
|
|
30466
|
-
|
|
30467
|
-
|
|
30468
|
-
|
|
30469
|
-
|
|
30470
|
-
|
|
30483
|
+
request: union([
|
|
30484
|
+
strictObject({
|
|
30485
|
+
sessionId: nonEmptyString,
|
|
30486
|
+
secret: nonEmptyString,
|
|
30487
|
+
tree: nonEmptyString,
|
|
30488
|
+
issue: nonEmptyString
|
|
30489
|
+
}),
|
|
30490
|
+
strictObject({ sessionId: nonEmptyString, secret: nonEmptyString })
|
|
30491
|
+
]),
|
|
30471
30492
|
response: object({ grantId: nonEmptyString, expiresAt: nonEmptyString })
|
|
30472
30493
|
},
|
|
30473
30494
|
GitHubToken: {
|
|
30474
|
-
request: strictObject({ grantId: nonEmptyString }),
|
|
30495
|
+
request: strictObject({ grantId: nonEmptyString, merge: literal(true).optional() }),
|
|
30475
30496
|
response: object({ token: nonEmptyString, appLogin: string2().endsWith("[bot]") })
|
|
30497
|
+
},
|
|
30498
|
+
GitCredential: {
|
|
30499
|
+
request: strictObject({ grantId: nonEmptyString })
|
|
30476
30500
|
}
|
|
30477
30501
|
};
|
|
30478
30502
|
// ../contracts/src/repo.ts
|
|
@@ -31103,7 +31127,7 @@ var TolerantInboundEnvelopeSchema = object({
|
|
|
31103
31127
|
}).passthrough();
|
|
31104
31128
|
var DispatchDeliveryRequestSchema = object({
|
|
31105
31129
|
attempt: number2().int().positive(),
|
|
31106
|
-
mode: _enum2(
|
|
31130
|
+
mode: _enum2(DELIVERY_CAPABILITIES)
|
|
31107
31131
|
});
|
|
31108
31132
|
var DispatchTargetedFrameSchema = object({
|
|
31109
31133
|
event: DispatchEventSchema,
|
|
@@ -32595,21 +32619,34 @@ async function executeDispatchTool(input) {
|
|
|
32595
32619
|
if (replyTo !== undefined && replyToAsk !== undefined) {
|
|
32596
32620
|
throw new Error("reply_to and reply_to_ask cannot both be set");
|
|
32597
32621
|
}
|
|
32622
|
+
const requestedTurn = optionalString(args, "turn");
|
|
32623
|
+
if (requestedTurn !== undefined && replyToAsk === undefined) {
|
|
32624
|
+
throw new Error("turn requires reply_to_ask");
|
|
32625
|
+
}
|
|
32626
|
+
if (requestedTurn !== undefined && requestedTurn !== "agent" && requestedTurn !== "human") {
|
|
32627
|
+
throw new Error("turn must be agent or human");
|
|
32628
|
+
}
|
|
32598
32629
|
const commentInput = {
|
|
32599
32630
|
body: stringArg(args, "body"),
|
|
32600
32631
|
...anchored === undefined ? {} : { anchor: anchored },
|
|
32601
32632
|
...replyTo === undefined ? {} : { reply_to: replyTo },
|
|
32602
32633
|
...replyToAsk === undefined ? {} : { ask_id: replyToAsk },
|
|
32634
|
+
...requestedTurn === undefined ? {} : { turn: requestedTurn },
|
|
32603
32635
|
actor
|
|
32604
32636
|
};
|
|
32605
32637
|
const comment = resolved?.owner.kind === "project" ? await client.artifactComment(resolved.artifact.id, commentInput) : await client.comment(issue(), commentInput);
|
|
32638
|
+
const askState = comment.turn === null ? "" : ` (ask now waiting on ${comment.turn})`;
|
|
32606
32639
|
return {
|
|
32607
|
-
text: `Posted comment ${comment.id}`,
|
|
32640
|
+
text: `Posted comment ${comment.id}${askState}`,
|
|
32608
32641
|
details: resolved === undefined ? {
|
|
32609
32642
|
issue: comment.issue_key,
|
|
32610
32643
|
topic: dispatchIssueSubject(issue(), ">"),
|
|
32611
|
-
comment: comment.id
|
|
32612
|
-
|
|
32644
|
+
comment: comment.id,
|
|
32645
|
+
...comment.turn === null ? {} : { ask_waiting_on: comment.turn }
|
|
32646
|
+
} : writeResultDetails(resolved, {
|
|
32647
|
+
comment: comment.id,
|
|
32648
|
+
...comment.turn === null ? {} : { ask_waiting_on: comment.turn }
|
|
32649
|
+
})
|
|
32613
32650
|
};
|
|
32614
32651
|
}
|
|
32615
32652
|
case "dispatch_suggest": {
|
|
@@ -33226,6 +33263,31 @@ function messageMetadata(input) {
|
|
|
33226
33263
|
import { logger } from "@oh-my-pi/pi-utils";
|
|
33227
33264
|
var import_nats = __toESM(require_mod4(), 1);
|
|
33228
33265
|
|
|
33266
|
+
// src/legion/role-claim-bridge.ts
|
|
33267
|
+
var LEGION_ROLE_CLAIM_BRIDGE = Symbol.for("legion.pi-envoy.role-claim-bridge");
|
|
33268
|
+
function legionRoleClaimBridge() {
|
|
33269
|
+
const store = globalThis;
|
|
33270
|
+
const bridge = store[LEGION_ROLE_CLAIM_BRIDGE];
|
|
33271
|
+
if (bridge)
|
|
33272
|
+
return bridge;
|
|
33273
|
+
const createdBridge = {
|
|
33274
|
+
instances: [],
|
|
33275
|
+
regained: undefined
|
|
33276
|
+
};
|
|
33277
|
+
store[LEGION_ROLE_CLAIM_BRIDGE] = createdBridge;
|
|
33278
|
+
return createdBridge;
|
|
33279
|
+
}
|
|
33280
|
+
async function claimEnvoyRole(sessionID, role, context) {
|
|
33281
|
+
const bridge = legionRoleClaimBridge();
|
|
33282
|
+
const instance = bridge.instances.findLast((candidate) => candidate.sessionID() === sessionID) ?? bridge.instances.at(-1);
|
|
33283
|
+
if (instance === undefined)
|
|
33284
|
+
throw new Error("Envoy has no bound instance for a role claim");
|
|
33285
|
+
await instance.claim(sessionID, role, context);
|
|
33286
|
+
}
|
|
33287
|
+
function onEnvoyRoleRegained(listener) {
|
|
33288
|
+
legionRoleClaimBridge().regained = listener;
|
|
33289
|
+
}
|
|
33290
|
+
|
|
33229
33291
|
// src/tool-result.ts
|
|
33230
33292
|
function toolSuccess(text, details = {}) {
|
|
33231
33293
|
return { content: [{ type: "text", text }], details };
|
|
@@ -33290,6 +33352,8 @@ async function copySessionID(copyToClipboard, sessionID) {
|
|
|
33290
33352
|
// extensions/envoy.ts
|
|
33291
33353
|
var codec2 = import_nats.StringCodec();
|
|
33292
33354
|
var NATS_RETRY_INTERVAL_MS = 15000;
|
|
33355
|
+
var ALL_CAPABILITIES = DELIVERY_CAPABILITIES;
|
|
33356
|
+
var CAPABILITIES_WITHOUT_BTW = DELIVERY_CAPABILITIES.filter((capability) => capability !== "btw");
|
|
33293
33357
|
var ROLE_CLAIM_ENTRY = "envoy-role-claim";
|
|
33294
33358
|
var OPEN_ASKS_TIMEOUT_MS = 3000;
|
|
33295
33359
|
function isRoleClaimEntry(entry) {
|
|
@@ -33305,25 +33369,6 @@ function isRoleClaimEntry(entry) {
|
|
|
33305
33369
|
return false;
|
|
33306
33370
|
return entry.data.role === null || typeof entry.data.role === "string";
|
|
33307
33371
|
}
|
|
33308
|
-
var LEGION_ROLE_CLAIM_BRIDGE = Symbol.for("legion.pi-envoy.role-claim-bridge");
|
|
33309
|
-
function legionRoleClaimBridge() {
|
|
33310
|
-
const store = globalThis;
|
|
33311
|
-
let bridge = store[LEGION_ROLE_CLAIM_BRIDGE];
|
|
33312
|
-
if (bridge === undefined) {
|
|
33313
|
-
const ready = Promise.withResolvers();
|
|
33314
|
-
bridge = { ready };
|
|
33315
|
-
store[LEGION_ROLE_CLAIM_BRIDGE] = bridge;
|
|
33316
|
-
}
|
|
33317
|
-
return bridge;
|
|
33318
|
-
}
|
|
33319
|
-
async function claimEnvoyRole(sessionID, role, context) {
|
|
33320
|
-
const bridge = legionRoleClaimBridge();
|
|
33321
|
-
const claim = bridge.claim ?? await bridge.ready.promise;
|
|
33322
|
-
await claim(sessionID, role, context);
|
|
33323
|
-
}
|
|
33324
|
-
function onEnvoyRoleRegained(listener) {
|
|
33325
|
-
legionRoleClaimBridge().regained = listener;
|
|
33326
|
-
}
|
|
33327
33372
|
function resolveSkillsDirectory() {
|
|
33328
33373
|
const moduleDirectory = dirname(fileURLToPath(import.meta.url));
|
|
33329
33374
|
const candidates = [
|
|
@@ -33548,7 +33593,7 @@ function envoyExtension(pi) {
|
|
|
33548
33593
|
topics: [...new Set([agentSubject(sessionID), ...subscriptions.keys()])],
|
|
33549
33594
|
port: 0,
|
|
33550
33595
|
title: activeSessionContext?.sessionManager.getSessionName?.() ?? "",
|
|
33551
|
-
capabilities: typeof pi.askEphemeral === "function" ?
|
|
33596
|
+
capabilities: typeof pi.askEphemeral === "function" ? ALL_CAPABILITIES : CAPABILITIES_WITHOUT_BTW,
|
|
33552
33597
|
driving: false,
|
|
33553
33598
|
selfSubscribed: true
|
|
33554
33599
|
});
|
|
@@ -33759,8 +33804,11 @@ function envoyExtension(pi) {
|
|
|
33759
33804
|
await registerSession();
|
|
33760
33805
|
await setEnvoyRole(role);
|
|
33761
33806
|
};
|
|
33762
|
-
|
|
33763
|
-
|
|
33807
|
+
const claimInstance = {
|
|
33808
|
+
claim,
|
|
33809
|
+
sessionID: () => activeSessionContext?.sessionManager.getSessionId() ?? sessionID
|
|
33810
|
+
};
|
|
33811
|
+
bridge.instances.push(claimInstance);
|
|
33764
33812
|
pi.on("session_start", async (_event, context) => {
|
|
33765
33813
|
const previousSessionID = sessionID;
|
|
33766
33814
|
restoreLocalSessionState(context);
|
|
@@ -33829,6 +33877,9 @@ function envoyExtension(pi) {
|
|
|
33829
33877
|
pi.on("session_tree", (_event, context) => rebind(undefined, context));
|
|
33830
33878
|
pi.on("session_shutdown", async () => {
|
|
33831
33879
|
shuttingDown = true;
|
|
33880
|
+
const bound = bridge.instances.indexOf(claimInstance);
|
|
33881
|
+
if (bound !== -1)
|
|
33882
|
+
bridge.instances.splice(bound, 1);
|
|
33832
33883
|
const deadline = Promise.withResolvers();
|
|
33833
33884
|
const timer = setTimeout(deadline.resolve, 1000);
|
|
33834
33885
|
try {
|
|
@@ -34097,7 +34148,5 @@ function topicsFor(parameters, fallback = []) {
|
|
|
34097
34148
|
return expandSubscriptionTopics(value);
|
|
34098
34149
|
}
|
|
34099
34150
|
export {
|
|
34100
|
-
|
|
34101
|
-
envoyExtension as default,
|
|
34102
|
-
onEnvoyRoleRegained
|
|
34151
|
+
envoyExtension as default
|
|
34103
34152
|
};
|