@sjawhar/pi-legion-envoy 1.27.1 → 1.27.2
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 +23 -203
- package/dist/legion.js +23 -203
- package/package.json +1 -1
package/dist/envoy.js
CHANGED
|
@@ -32402,12 +32402,23 @@ async function executeDispatchTool(input) {
|
|
|
32402
32402
|
if (!input.config.enabled || !configUrl || !configToken) {
|
|
32403
32403
|
throw new Error("Dispatch is disabled; resolve both DISPATCH_URL and DISPATCH_TOKEN");
|
|
32404
32404
|
}
|
|
32405
|
+
const baseFetch = input.fetchImpl ?? fetch;
|
|
32406
|
+
const fetchImpl = Object.assign(async (...args) => {
|
|
32407
|
+
try {
|
|
32408
|
+
return await baseFetch(...args);
|
|
32409
|
+
} catch (error) {
|
|
32410
|
+
if (error instanceof TypeError) {
|
|
32411
|
+
throw new Error(`Dispatch at ${configUrl} is unreachable: ${error.message}. If the Dispatch URL changed, restart this agent process so it picks up the new configuration.`);
|
|
32412
|
+
}
|
|
32413
|
+
throw error;
|
|
32414
|
+
}
|
|
32415
|
+
}, { preconnect: baseFetch.preconnect });
|
|
32405
32416
|
if (input.tool === "dispatch_open_asks") {
|
|
32406
32417
|
const sessionId = input.sessionId?.trim();
|
|
32407
32418
|
if (!sessionId)
|
|
32408
32419
|
throw new Error("host session id is required for dispatch_open_asks");
|
|
32409
32420
|
toolSchema(input.tool).parse(input.args);
|
|
32410
|
-
const client = new DispatchClient(configUrl, configToken,
|
|
32421
|
+
const client = new DispatchClient(configUrl, configToken, fetchImpl, input.signal);
|
|
32411
32422
|
const response = await client.openAsks(sessionId);
|
|
32412
32423
|
return { text: formatOpenAsksSummary(response, configUrl), details: { ...response } };
|
|
32413
32424
|
}
|
|
@@ -32416,7 +32427,7 @@ async function executeDispatchTool(input) {
|
|
|
32416
32427
|
const ownerArguments = await resolveOwnerArguments(input.tool, input.args, input.cwd, env, exec);
|
|
32417
32428
|
const args = toolSchema(input.tool).parse(ownerArguments.args);
|
|
32418
32429
|
const actor = toolActor(await resolveOrigin(env, exec, input.cwd), input);
|
|
32419
|
-
const client = new DispatchClient(configUrl, configToken,
|
|
32430
|
+
const client = new DispatchClient(configUrl, configToken, fetchImpl, input.signal);
|
|
32420
32431
|
const owner = ownerArguments.owner?.kind === "issue" ? {
|
|
32421
32432
|
kind: "issue",
|
|
32422
32433
|
issue: await ensureIssue(client, ownerArguments.owner.issue, actor)
|
|
@@ -33255,59 +33266,7 @@ async function copySessionID(copyToClipboard, sessionID) {
|
|
|
33255
33266
|
var codec2 = import_nats.StringCodec();
|
|
33256
33267
|
var NATS_RETRY_INTERVAL_MS = 15000;
|
|
33257
33268
|
var ROLE_CLAIM_ENTRY = "envoy-role-claim";
|
|
33258
|
-
var ASK_AWARENESS_ENTRY = "dispatch-ask-awareness";
|
|
33259
|
-
var LEGION_MANAGED_ENTRY = "legion-managed-session";
|
|
33260
33269
|
var OPEN_ASKS_TIMEOUT_MS = 3000;
|
|
33261
|
-
var OPEN_ASKS_REMINDER = "You have no unanswered asks in Dispatch. If you are waiting for human input, open an ask. Otherwise ignore this reminder and continue with any remaining work. Do not reply just to acknowledge this reminder.";
|
|
33262
|
-
function isAskAwarenessEntry(entry) {
|
|
33263
|
-
if (typeof entry !== "object" || entry === null)
|
|
33264
|
-
return false;
|
|
33265
|
-
if (!("type" in entry) || entry.type !== "custom")
|
|
33266
|
-
return false;
|
|
33267
|
-
if (!("customType" in entry) || entry.customType !== ASK_AWARENESS_ENTRY)
|
|
33268
|
-
return false;
|
|
33269
|
-
if (!("data" in entry) || typeof entry.data !== "object" || entry.data === null)
|
|
33270
|
-
return false;
|
|
33271
|
-
const data = entry.data;
|
|
33272
|
-
return "session_id" in data && typeof data.session_id === "string" && "period" in data && typeof data.period === "number" && "baseline_as_of" in data && (typeof data.baseline_as_of === "string" || data.baseline_as_of === null) && "checked" in data && typeof data.checked === "boolean" && "fired" in data && typeof data.fired === "boolean" && "saw_ask" in data && typeof data.saw_ask === "boolean";
|
|
33273
|
-
}
|
|
33274
|
-
function isLegionManagedEntry(entry) {
|
|
33275
|
-
if (typeof entry !== "object" || entry === null)
|
|
33276
|
-
return false;
|
|
33277
|
-
if (!("type" in entry) || entry.type !== "custom")
|
|
33278
|
-
return false;
|
|
33279
|
-
if (!("customType" in entry) || entry.customType !== LEGION_MANAGED_ENTRY)
|
|
33280
|
-
return false;
|
|
33281
|
-
if (!("data" in entry) || typeof entry.data !== "object" || entry.data === null)
|
|
33282
|
-
return false;
|
|
33283
|
-
return "session_id" in entry.data && typeof entry.data.session_id === "string";
|
|
33284
|
-
}
|
|
33285
|
-
function emptyAskAwareness(sessionID) {
|
|
33286
|
-
return {
|
|
33287
|
-
session_id: sessionID,
|
|
33288
|
-
period: 0,
|
|
33289
|
-
baseline_as_of: null,
|
|
33290
|
-
checked: false,
|
|
33291
|
-
fired: false,
|
|
33292
|
-
saw_ask: false
|
|
33293
|
-
};
|
|
33294
|
-
}
|
|
33295
|
-
function transcriptAskAwareness(branch, sessionID) {
|
|
33296
|
-
let state;
|
|
33297
|
-
for (const entry of branch) {
|
|
33298
|
-
if (isAskAwarenessEntry(entry) && entry.data.session_id === sessionID)
|
|
33299
|
-
state = entry.data;
|
|
33300
|
-
}
|
|
33301
|
-
return state;
|
|
33302
|
-
}
|
|
33303
|
-
function transcriptMarksLegionSession(branch, sessionID) {
|
|
33304
|
-
return branch.some((entry) => isLegionManagedEntry(entry) && entry.data.session_id === sessionID);
|
|
33305
|
-
}
|
|
33306
|
-
function isHumanTurnInitiator(message) {
|
|
33307
|
-
if (message.attribution !== "user")
|
|
33308
|
-
return false;
|
|
33309
|
-
return message.role === "user" || message.role === "custom" && (message.customType === "skill-prompt" || message.customType === "collab-prompt");
|
|
33310
|
-
}
|
|
33311
33270
|
function isRoleClaimEntry(entry) {
|
|
33312
33271
|
if (typeof entry !== "object" || entry === null)
|
|
33313
33272
|
return false;
|
|
@@ -33327,15 +33286,13 @@ function legionRoleClaimBridge() {
|
|
|
33327
33286
|
let bridge = store[LEGION_ROLE_CLAIM_BRIDGE];
|
|
33328
33287
|
if (bridge === undefined) {
|
|
33329
33288
|
const ready = Promise.withResolvers();
|
|
33330
|
-
bridge = { ready
|
|
33289
|
+
bridge = { ready };
|
|
33331
33290
|
store[LEGION_ROLE_CLAIM_BRIDGE] = bridge;
|
|
33332
33291
|
}
|
|
33333
33292
|
return bridge;
|
|
33334
33293
|
}
|
|
33335
33294
|
async function claimEnvoyRole(sessionID, role, context) {
|
|
33336
33295
|
const bridge = legionRoleClaimBridge();
|
|
33337
|
-
bridge.managedSessions.add(sessionID);
|
|
33338
|
-
bridge.markManagedSession?.(sessionID, context);
|
|
33339
33296
|
const claim = bridge.claim ?? await bridge.ready.promise;
|
|
33340
33297
|
await claim(sessionID, role, context);
|
|
33341
33298
|
}
|
|
@@ -33384,87 +33341,26 @@ function envoyExtension(pi) {
|
|
|
33384
33341
|
let claimedRoleTopic;
|
|
33385
33342
|
let activeSessionContext;
|
|
33386
33343
|
const inbox = [];
|
|
33387
|
-
|
|
33388
|
-
let candidateBaseline;
|
|
33389
|
-
let awarenessGeneration = 0;
|
|
33390
|
-
let availabilityWarningPeriod;
|
|
33391
|
-
let agentRunning = false;
|
|
33392
|
-
let agentWillContinue = false;
|
|
33393
|
-
let legionManagedSessionID = "";
|
|
33394
|
-
let terminalInputUnsubscribe;
|
|
33395
|
-
const persistAskAwareness = (state) => {
|
|
33396
|
-
askAwareness = state;
|
|
33397
|
-
pi.appendEntry(ASK_AWARENESS_ENTRY, state);
|
|
33398
|
-
};
|
|
33344
|
+
const availabilityWarningSessionIDs = new Set;
|
|
33399
33345
|
const warnAskAvailability = (context, error) => {
|
|
33400
|
-
const
|
|
33401
|
-
if (
|
|
33346
|
+
const sessionID = context.sessionManager.getSessionId();
|
|
33347
|
+
if (availabilityWarningSessionIDs.has(sessionID))
|
|
33402
33348
|
return;
|
|
33403
|
-
|
|
33404
|
-
context.ui.notify(`envoy: Dispatch open-ask check unavailable (${messageFor(error)});
|
|
33349
|
+
availabilityWarningSessionIDs.add(sessionID);
|
|
33350
|
+
context.ui.notify(`envoy: Dispatch open-ask check unavailable (${messageFor(error)}); summary unavailable`, "warning");
|
|
33405
33351
|
};
|
|
33406
|
-
const queryOpenAsks = async (requestedSessionID
|
|
33352
|
+
const queryOpenAsks = async (requestedSessionID) => {
|
|
33407
33353
|
const config = activeDispatchConfig();
|
|
33408
33354
|
if (config === null)
|
|
33409
33355
|
return null;
|
|
33410
|
-
const
|
|
33411
|
-
|
|
33412
|
-
return
|
|
33413
|
-
};
|
|
33414
|
-
const setReminderBaseline = (requestedSessionID, period, asOf) => {
|
|
33415
|
-
candidateBaseline = { sessionID: requestedSessionID, asOf };
|
|
33416
|
-
if (askAwareness.session_id !== requestedSessionID || askAwareness.period !== period || askAwareness.checked || askAwareness.fired || askAwareness.baseline_as_of !== null) {
|
|
33417
|
-
return;
|
|
33418
|
-
}
|
|
33419
|
-
persistAskAwareness({ ...askAwareness, baseline_as_of: asOf });
|
|
33420
|
-
};
|
|
33421
|
-
const refreshReminderBaseline = async (requestedSessionID, period, context) => {
|
|
33422
|
-
try {
|
|
33423
|
-
const snapshot = await queryOpenAsks(requestedSessionID);
|
|
33424
|
-
if (snapshot === null)
|
|
33425
|
-
return;
|
|
33426
|
-
setReminderBaseline(requestedSessionID, period, snapshot.as_of);
|
|
33427
|
-
} catch (error) {
|
|
33428
|
-
warnAskAvailability(context, error);
|
|
33429
|
-
}
|
|
33430
|
-
};
|
|
33431
|
-
const armAskReminder = async (context, refreshBaseline = false) => {
|
|
33432
|
-
const id = context.sessionManager.getSessionId();
|
|
33433
|
-
if (id === "" || activeDispatchConfig() === null)
|
|
33434
|
-
return;
|
|
33435
|
-
awarenessGeneration++;
|
|
33436
|
-
availabilityWarningPeriod = undefined;
|
|
33437
|
-
const baseline = !agentRunning && !refreshBaseline && candidateBaseline !== undefined && candidateBaseline.sessionID === id ? candidateBaseline.asOf : null;
|
|
33438
|
-
const previousPeriod = askAwareness.session_id === id ? askAwareness.period : 0;
|
|
33439
|
-
persistAskAwareness({
|
|
33440
|
-
session_id: id,
|
|
33441
|
-
period: previousPeriod + 1,
|
|
33442
|
-
baseline_as_of: baseline,
|
|
33443
|
-
checked: false,
|
|
33444
|
-
fired: false,
|
|
33445
|
-
saw_ask: false
|
|
33446
|
-
});
|
|
33447
|
-
if (agentRunning || refreshBaseline) {
|
|
33448
|
-
await refreshReminderBaseline(id, askAwareness.period, context);
|
|
33449
|
-
}
|
|
33450
|
-
};
|
|
33451
|
-
const cancelPendingAskReminder = () => {
|
|
33452
|
-
awarenessGeneration++;
|
|
33453
|
-
};
|
|
33454
|
-
const installTerminalInputCancellation = (context) => {
|
|
33455
|
-
terminalInputUnsubscribe?.();
|
|
33456
|
-
terminalInputUnsubscribe = context.ui.onTerminalInput(cancelPendingAskReminder);
|
|
33356
|
+
const snapshot = await new DispatchClient(config.url, config.token, fetch, AbortSignal.timeout(OPEN_ASKS_TIMEOUT_MS)).openAsks(requestedSessionID);
|
|
33357
|
+
availabilityWarningSessionIDs.delete(requestedSessionID);
|
|
33358
|
+
return snapshot;
|
|
33457
33359
|
};
|
|
33458
33360
|
const restoreLocalSessionState = (context) => {
|
|
33459
33361
|
sessionDirectory = context.cwd;
|
|
33460
33362
|
sessionID = context.sessionManager.getSessionId();
|
|
33461
33363
|
activeSessionContext = context;
|
|
33462
|
-
candidateBaseline = undefined;
|
|
33463
|
-
const branch = context.sessionManager.getBranch?.() ?? [];
|
|
33464
|
-
askAwareness = transcriptAskAwareness(branch, sessionID) ?? emptyAskAwareness(sessionID);
|
|
33465
|
-
const persistedLegionSession = transcriptMarksLegionSession(branch, sessionID);
|
|
33466
|
-
const managed = persistedLegionSession || legionRoleClaimBridge().managedSessions.has(sessionID);
|
|
33467
|
-
legionManagedSessionID = managed ? sessionID : "";
|
|
33468
33364
|
};
|
|
33469
33365
|
pi.on("resources_discover", async () => ({ skillPaths: [SKILLS_DIRECTORY] }));
|
|
33470
33366
|
registerEnvoyMessageRenderer(pi);
|
|
@@ -33505,13 +33401,6 @@ function envoyExtension(pi) {
|
|
|
33505
33401
|
const dedupeKey = rendered.envelope?.dedupe_key;
|
|
33506
33402
|
const duplicate = dedupeKey !== undefined && dedupeKeys.has(dedupeKey);
|
|
33507
33403
|
if (!duplicate && !rendered.skip) {
|
|
33508
|
-
if (rendered.dispatchActor?.kind === "user" && activeSessionContext !== undefined) {
|
|
33509
|
-
try {
|
|
33510
|
-
await armAskReminder(activeSessionContext, true);
|
|
33511
|
-
} catch (error) {
|
|
33512
|
-
warnAskAvailability(activeSessionContext, error);
|
|
33513
|
-
}
|
|
33514
|
-
}
|
|
33515
33404
|
const envelope = rendered.envelope;
|
|
33516
33405
|
if (envelope !== undefined) {
|
|
33517
33406
|
inbox.unshift({
|
|
@@ -33831,17 +33720,6 @@ function envoyExtension(pi) {
|
|
|
33831
33720
|
return run;
|
|
33832
33721
|
};
|
|
33833
33722
|
const bridge = legionRoleClaimBridge();
|
|
33834
|
-
bridge.markManagedSession = (targetSessionID, context) => {
|
|
33835
|
-
bridge.managedSessions.add(targetSessionID);
|
|
33836
|
-
if (targetSessionID !== sessionID)
|
|
33837
|
-
return;
|
|
33838
|
-
if (legionManagedSessionID === targetSessionID)
|
|
33839
|
-
return;
|
|
33840
|
-
legionManagedSessionID = targetSessionID;
|
|
33841
|
-
if (context !== undefined || activeSessionContext !== undefined) {
|
|
33842
|
-
pi.appendEntry(LEGION_MANAGED_ENTRY, { session_id: targetSessionID });
|
|
33843
|
-
}
|
|
33844
|
-
};
|
|
33845
33723
|
const claim = async (targetSessionID, role, callerContext) => {
|
|
33846
33724
|
const context = callerContext ?? activeSessionContext;
|
|
33847
33725
|
if (context === undefined || context.sessionManager.getSessionId() !== targetSessionID) {
|
|
@@ -33855,7 +33733,6 @@ function envoyExtension(pi) {
|
|
|
33855
33733
|
bridge.claim = claim;
|
|
33856
33734
|
bridge.ready.resolve(claim);
|
|
33857
33735
|
pi.on("session_start", async (_event, context) => {
|
|
33858
|
-
installTerminalInputCancellation(context);
|
|
33859
33736
|
const previousSessionID = sessionID;
|
|
33860
33737
|
restoreLocalSessionState(context);
|
|
33861
33738
|
if (dispatchConfig.error !== null) {
|
|
@@ -33891,7 +33768,6 @@ function envoyExtension(pi) {
|
|
|
33891
33768
|
}
|
|
33892
33769
|
});
|
|
33893
33770
|
const rebind = async (reason, context) => {
|
|
33894
|
-
installTerminalInputCancellation(context);
|
|
33895
33771
|
const previousID = sessionID;
|
|
33896
33772
|
restoreLocalSessionState(context);
|
|
33897
33773
|
if (defaults.natsUrls.length === 0)
|
|
@@ -33941,8 +33817,6 @@ function envoyExtension(pi) {
|
|
|
33941
33817
|
]);
|
|
33942
33818
|
} finally {
|
|
33943
33819
|
clearTimeout(timer);
|
|
33944
|
-
terminalInputUnsubscribe?.();
|
|
33945
|
-
terminalInputUnsubscribe = undefined;
|
|
33946
33820
|
connection = undefined;
|
|
33947
33821
|
activeSessionContext = undefined;
|
|
33948
33822
|
subscriptions.clear();
|
|
@@ -33996,7 +33870,6 @@ function envoyExtension(pi) {
|
|
|
33996
33870
|
const snapshot = await queryOpenAsks(id);
|
|
33997
33871
|
if (snapshot === null)
|
|
33998
33872
|
return;
|
|
33999
|
-
setReminderBaseline(id, askAwareness.session_id === id ? askAwareness.period : 0, snapshot.as_of);
|
|
34000
33873
|
return {
|
|
34001
33874
|
message: {
|
|
34002
33875
|
customType: "dispatch-open-asks",
|
|
@@ -34018,60 +33891,7 @@ ${formatOpenAsksSummary(snapshot, currentDispatchConfig().url)}`,
|
|
|
34018
33891
|
};
|
|
34019
33892
|
}
|
|
34020
33893
|
});
|
|
34021
|
-
pi.on("agent_start", async () => {
|
|
34022
|
-
agentRunning = true;
|
|
34023
|
-
agentWillContinue = false;
|
|
34024
|
-
});
|
|
34025
|
-
pi.on("agent_end", async (event) => {
|
|
34026
|
-
agentRunning = false;
|
|
34027
|
-
agentWillContinue = event.willContinue === true;
|
|
34028
|
-
});
|
|
34029
|
-
pi.on("input", async () => {
|
|
34030
|
-
cancelPendingAskReminder();
|
|
34031
|
-
});
|
|
34032
|
-
pi.on("message_start", async (event, context) => {
|
|
34033
|
-
if (!isHumanTurnInitiator(event.message))
|
|
34034
|
-
return;
|
|
34035
|
-
try {
|
|
34036
|
-
await armAskReminder(context);
|
|
34037
|
-
} catch (error) {
|
|
34038
|
-
warnAskAvailability(context, error);
|
|
34039
|
-
}
|
|
34040
|
-
});
|
|
34041
|
-
pi.on("session_stop", async (event, context) => {
|
|
34042
|
-
if (event.stop_hook_active || event.signal.aborted || shuttingDown || agentWillContinue || event.last_assistant_message?.role !== "assistant" || event.session_id !== sessionID || legionManagedSessionID === event.session_id || askAwareness.session_id !== event.session_id || askAwareness.period === 0 || askAwareness.checked || askAwareness.fired) {
|
|
34043
|
-
return;
|
|
34044
|
-
}
|
|
34045
|
-
const baseline = askAwareness.baseline_as_of;
|
|
34046
|
-
if (baseline === null)
|
|
34047
|
-
return;
|
|
34048
|
-
if (context.ui.getEditorText().trim() !== "")
|
|
34049
|
-
return;
|
|
34050
|
-
const claimed = { ...askAwareness, checked: true };
|
|
34051
|
-
const generation = awarenessGeneration;
|
|
34052
|
-
persistAskAwareness(claimed);
|
|
34053
|
-
let snapshot;
|
|
34054
|
-
try {
|
|
34055
|
-
snapshot = await queryOpenAsks(event.session_id, baseline, event.signal);
|
|
34056
|
-
} catch (error) {
|
|
34057
|
-
if (!event.signal.aborted && generation === awarenessGeneration) {
|
|
34058
|
-
warnAskAvailability(context, error);
|
|
34059
|
-
}
|
|
34060
|
-
return;
|
|
34061
|
-
}
|
|
34062
|
-
if (snapshot === null || event.signal.aborted || generation !== awarenessGeneration || shuttingDown || agentWillContinue || context.sessionManager.getSessionId() !== event.session_id || sessionID !== event.session_id || legionManagedSessionID === event.session_id || context.ui.getEditorText().trim() !== "" || askAwareness.session_id !== claimed.session_id || askAwareness.period !== claimed.period || !askAwareness.checked) {
|
|
34063
|
-
return;
|
|
34064
|
-
}
|
|
34065
|
-
if (snapshot.count > 0 || snapshot.opened_since || askAwareness.saw_ask) {
|
|
34066
|
-
return;
|
|
34067
|
-
}
|
|
34068
|
-
persistAskAwareness({ ...askAwareness, fired: true });
|
|
34069
|
-
return { continue: true, additionalContext: OPEN_ASKS_REMINDER };
|
|
34070
|
-
});
|
|
34071
33894
|
pi.on("tool_result", async (event) => {
|
|
34072
|
-
if (!event.isError && event.toolName === "dispatch_ask" && typeof event.details === "object" && event.details !== null && "ask" in event.details && typeof event.details.ask === "string" && askAwareness.session_id === sessionID && askAwareness.period > 0 && !askAwareness.saw_ask) {
|
|
34073
|
-
persistAskAwareness({ ...askAwareness, saw_ask: true });
|
|
34074
|
-
}
|
|
34075
33895
|
if (event.isError)
|
|
34076
33896
|
return;
|
|
34077
33897
|
const topic = dispatchSubscriptionTopic(event.details);
|
package/dist/legion.js
CHANGED
|
@@ -32833,12 +32833,23 @@ async function executeDispatchTool(input) {
|
|
|
32833
32833
|
if (!input.config.enabled || !configUrl || !configToken) {
|
|
32834
32834
|
throw new Error("Dispatch is disabled; resolve both DISPATCH_URL and DISPATCH_TOKEN");
|
|
32835
32835
|
}
|
|
32836
|
+
const baseFetch = input.fetchImpl ?? fetch;
|
|
32837
|
+
const fetchImpl = Object.assign(async (...args) => {
|
|
32838
|
+
try {
|
|
32839
|
+
return await baseFetch(...args);
|
|
32840
|
+
} catch (error) {
|
|
32841
|
+
if (error instanceof TypeError) {
|
|
32842
|
+
throw new Error(`Dispatch at ${configUrl} is unreachable: ${error.message}. If the Dispatch URL changed, restart this agent process so it picks up the new configuration.`);
|
|
32843
|
+
}
|
|
32844
|
+
throw error;
|
|
32845
|
+
}
|
|
32846
|
+
}, { preconnect: baseFetch.preconnect });
|
|
32836
32847
|
if (input.tool === "dispatch_open_asks") {
|
|
32837
32848
|
const sessionId = input.sessionId?.trim();
|
|
32838
32849
|
if (!sessionId)
|
|
32839
32850
|
throw new Error("host session id is required for dispatch_open_asks");
|
|
32840
32851
|
toolSchema(input.tool).parse(input.args);
|
|
32841
|
-
const client = new DispatchClient(configUrl, configToken,
|
|
32852
|
+
const client = new DispatchClient(configUrl, configToken, fetchImpl, input.signal);
|
|
32842
32853
|
const response = await client.openAsks(sessionId);
|
|
32843
32854
|
return { text: formatOpenAsksSummary(response, configUrl), details: { ...response } };
|
|
32844
32855
|
}
|
|
@@ -32847,7 +32858,7 @@ async function executeDispatchTool(input) {
|
|
|
32847
32858
|
const ownerArguments = await resolveOwnerArguments(input.tool, input.args, input.cwd, env, exec);
|
|
32848
32859
|
const args = toolSchema(input.tool).parse(ownerArguments.args);
|
|
32849
32860
|
const actor = toolActor(await resolveOrigin(env, exec, input.cwd), input);
|
|
32850
|
-
const client = new DispatchClient(configUrl, configToken,
|
|
32861
|
+
const client = new DispatchClient(configUrl, configToken, fetchImpl, input.signal);
|
|
32851
32862
|
const owner = ownerArguments.owner?.kind === "issue" ? {
|
|
32852
32863
|
kind: "issue",
|
|
32853
32864
|
issue: await ensureIssue(client, ownerArguments.owner.issue, actor)
|
|
@@ -33678,59 +33689,7 @@ async function copySessionID(copyToClipboard, sessionID) {
|
|
|
33678
33689
|
var codec2 = import_nats.StringCodec();
|
|
33679
33690
|
var NATS_RETRY_INTERVAL_MS = 15000;
|
|
33680
33691
|
var ROLE_CLAIM_ENTRY = "envoy-role-claim";
|
|
33681
|
-
var ASK_AWARENESS_ENTRY = "dispatch-ask-awareness";
|
|
33682
|
-
var LEGION_MANAGED_ENTRY = "legion-managed-session";
|
|
33683
33692
|
var OPEN_ASKS_TIMEOUT_MS = 3000;
|
|
33684
|
-
var OPEN_ASKS_REMINDER = "You have no unanswered asks in Dispatch. If you are waiting for human input, open an ask. Otherwise ignore this reminder and continue with any remaining work. Do not reply just to acknowledge this reminder.";
|
|
33685
|
-
function isAskAwarenessEntry(entry) {
|
|
33686
|
-
if (typeof entry !== "object" || entry === null)
|
|
33687
|
-
return false;
|
|
33688
|
-
if (!("type" in entry) || entry.type !== "custom")
|
|
33689
|
-
return false;
|
|
33690
|
-
if (!("customType" in entry) || entry.customType !== ASK_AWARENESS_ENTRY)
|
|
33691
|
-
return false;
|
|
33692
|
-
if (!("data" in entry) || typeof entry.data !== "object" || entry.data === null)
|
|
33693
|
-
return false;
|
|
33694
|
-
const data = entry.data;
|
|
33695
|
-
return "session_id" in data && typeof data.session_id === "string" && "period" in data && typeof data.period === "number" && "baseline_as_of" in data && (typeof data.baseline_as_of === "string" || data.baseline_as_of === null) && "checked" in data && typeof data.checked === "boolean" && "fired" in data && typeof data.fired === "boolean" && "saw_ask" in data && typeof data.saw_ask === "boolean";
|
|
33696
|
-
}
|
|
33697
|
-
function isLegionManagedEntry(entry) {
|
|
33698
|
-
if (typeof entry !== "object" || entry === null)
|
|
33699
|
-
return false;
|
|
33700
|
-
if (!("type" in entry) || entry.type !== "custom")
|
|
33701
|
-
return false;
|
|
33702
|
-
if (!("customType" in entry) || entry.customType !== LEGION_MANAGED_ENTRY)
|
|
33703
|
-
return false;
|
|
33704
|
-
if (!("data" in entry) || typeof entry.data !== "object" || entry.data === null)
|
|
33705
|
-
return false;
|
|
33706
|
-
return "session_id" in entry.data && typeof entry.data.session_id === "string";
|
|
33707
|
-
}
|
|
33708
|
-
function emptyAskAwareness(sessionID) {
|
|
33709
|
-
return {
|
|
33710
|
-
session_id: sessionID,
|
|
33711
|
-
period: 0,
|
|
33712
|
-
baseline_as_of: null,
|
|
33713
|
-
checked: false,
|
|
33714
|
-
fired: false,
|
|
33715
|
-
saw_ask: false
|
|
33716
|
-
};
|
|
33717
|
-
}
|
|
33718
|
-
function transcriptAskAwareness(branch, sessionID) {
|
|
33719
|
-
let state;
|
|
33720
|
-
for (const entry of branch) {
|
|
33721
|
-
if (isAskAwarenessEntry(entry) && entry.data.session_id === sessionID)
|
|
33722
|
-
state = entry.data;
|
|
33723
|
-
}
|
|
33724
|
-
return state;
|
|
33725
|
-
}
|
|
33726
|
-
function transcriptMarksLegionSession(branch, sessionID) {
|
|
33727
|
-
return branch.some((entry) => isLegionManagedEntry(entry) && entry.data.session_id === sessionID);
|
|
33728
|
-
}
|
|
33729
|
-
function isHumanTurnInitiator(message) {
|
|
33730
|
-
if (message.attribution !== "user")
|
|
33731
|
-
return false;
|
|
33732
|
-
return message.role === "user" || message.role === "custom" && (message.customType === "skill-prompt" || message.customType === "collab-prompt");
|
|
33733
|
-
}
|
|
33734
33693
|
function isRoleClaimEntry(entry) {
|
|
33735
33694
|
if (typeof entry !== "object" || entry === null)
|
|
33736
33695
|
return false;
|
|
@@ -33750,15 +33709,13 @@ function legionRoleClaimBridge() {
|
|
|
33750
33709
|
let bridge = store[LEGION_ROLE_CLAIM_BRIDGE];
|
|
33751
33710
|
if (bridge === undefined) {
|
|
33752
33711
|
const ready = Promise.withResolvers();
|
|
33753
|
-
bridge = { ready
|
|
33712
|
+
bridge = { ready };
|
|
33754
33713
|
store[LEGION_ROLE_CLAIM_BRIDGE] = bridge;
|
|
33755
33714
|
}
|
|
33756
33715
|
return bridge;
|
|
33757
33716
|
}
|
|
33758
33717
|
async function claimEnvoyRole(sessionID, role, context) {
|
|
33759
33718
|
const bridge = legionRoleClaimBridge();
|
|
33760
|
-
bridge.managedSessions.add(sessionID);
|
|
33761
|
-
bridge.markManagedSession?.(sessionID, context);
|
|
33762
33719
|
const claim = bridge.claim ?? await bridge.ready.promise;
|
|
33763
33720
|
await claim(sessionID, role, context);
|
|
33764
33721
|
}
|
|
@@ -33807,87 +33764,26 @@ function envoyExtension(pi) {
|
|
|
33807
33764
|
let claimedRoleTopic;
|
|
33808
33765
|
let activeSessionContext;
|
|
33809
33766
|
const inbox = [];
|
|
33810
|
-
|
|
33811
|
-
let candidateBaseline;
|
|
33812
|
-
let awarenessGeneration = 0;
|
|
33813
|
-
let availabilityWarningPeriod;
|
|
33814
|
-
let agentRunning = false;
|
|
33815
|
-
let agentWillContinue = false;
|
|
33816
|
-
let legionManagedSessionID = "";
|
|
33817
|
-
let terminalInputUnsubscribe;
|
|
33818
|
-
const persistAskAwareness = (state) => {
|
|
33819
|
-
askAwareness = state;
|
|
33820
|
-
pi.appendEntry(ASK_AWARENESS_ENTRY, state);
|
|
33821
|
-
};
|
|
33767
|
+
const availabilityWarningSessionIDs = new Set;
|
|
33822
33768
|
const warnAskAvailability = (context, error) => {
|
|
33823
|
-
const
|
|
33824
|
-
if (
|
|
33769
|
+
const sessionID = context.sessionManager.getSessionId();
|
|
33770
|
+
if (availabilityWarningSessionIDs.has(sessionID))
|
|
33825
33771
|
return;
|
|
33826
|
-
|
|
33827
|
-
context.ui.notify(`envoy: Dispatch open-ask check unavailable (${messageFor(error)});
|
|
33772
|
+
availabilityWarningSessionIDs.add(sessionID);
|
|
33773
|
+
context.ui.notify(`envoy: Dispatch open-ask check unavailable (${messageFor(error)}); summary unavailable`, "warning");
|
|
33828
33774
|
};
|
|
33829
|
-
const queryOpenAsks = async (requestedSessionID
|
|
33775
|
+
const queryOpenAsks = async (requestedSessionID) => {
|
|
33830
33776
|
const config = activeDispatchConfig();
|
|
33831
33777
|
if (config === null)
|
|
33832
33778
|
return null;
|
|
33833
|
-
const
|
|
33834
|
-
|
|
33835
|
-
return
|
|
33836
|
-
};
|
|
33837
|
-
const setReminderBaseline = (requestedSessionID, period, asOf) => {
|
|
33838
|
-
candidateBaseline = { sessionID: requestedSessionID, asOf };
|
|
33839
|
-
if (askAwareness.session_id !== requestedSessionID || askAwareness.period !== period || askAwareness.checked || askAwareness.fired || askAwareness.baseline_as_of !== null) {
|
|
33840
|
-
return;
|
|
33841
|
-
}
|
|
33842
|
-
persistAskAwareness({ ...askAwareness, baseline_as_of: asOf });
|
|
33843
|
-
};
|
|
33844
|
-
const refreshReminderBaseline = async (requestedSessionID, period, context) => {
|
|
33845
|
-
try {
|
|
33846
|
-
const snapshot = await queryOpenAsks(requestedSessionID);
|
|
33847
|
-
if (snapshot === null)
|
|
33848
|
-
return;
|
|
33849
|
-
setReminderBaseline(requestedSessionID, period, snapshot.as_of);
|
|
33850
|
-
} catch (error) {
|
|
33851
|
-
warnAskAvailability(context, error);
|
|
33852
|
-
}
|
|
33853
|
-
};
|
|
33854
|
-
const armAskReminder = async (context, refreshBaseline = false) => {
|
|
33855
|
-
const id = context.sessionManager.getSessionId();
|
|
33856
|
-
if (id === "" || activeDispatchConfig() === null)
|
|
33857
|
-
return;
|
|
33858
|
-
awarenessGeneration++;
|
|
33859
|
-
availabilityWarningPeriod = undefined;
|
|
33860
|
-
const baseline = !agentRunning && !refreshBaseline && candidateBaseline !== undefined && candidateBaseline.sessionID === id ? candidateBaseline.asOf : null;
|
|
33861
|
-
const previousPeriod = askAwareness.session_id === id ? askAwareness.period : 0;
|
|
33862
|
-
persistAskAwareness({
|
|
33863
|
-
session_id: id,
|
|
33864
|
-
period: previousPeriod + 1,
|
|
33865
|
-
baseline_as_of: baseline,
|
|
33866
|
-
checked: false,
|
|
33867
|
-
fired: false,
|
|
33868
|
-
saw_ask: false
|
|
33869
|
-
});
|
|
33870
|
-
if (agentRunning || refreshBaseline) {
|
|
33871
|
-
await refreshReminderBaseline(id, askAwareness.period, context);
|
|
33872
|
-
}
|
|
33873
|
-
};
|
|
33874
|
-
const cancelPendingAskReminder = () => {
|
|
33875
|
-
awarenessGeneration++;
|
|
33876
|
-
};
|
|
33877
|
-
const installTerminalInputCancellation = (context) => {
|
|
33878
|
-
terminalInputUnsubscribe?.();
|
|
33879
|
-
terminalInputUnsubscribe = context.ui.onTerminalInput(cancelPendingAskReminder);
|
|
33779
|
+
const snapshot = await new DispatchClient(config.url, config.token, fetch, AbortSignal.timeout(OPEN_ASKS_TIMEOUT_MS)).openAsks(requestedSessionID);
|
|
33780
|
+
availabilityWarningSessionIDs.delete(requestedSessionID);
|
|
33781
|
+
return snapshot;
|
|
33880
33782
|
};
|
|
33881
33783
|
const restoreLocalSessionState = (context) => {
|
|
33882
33784
|
sessionDirectory = context.cwd;
|
|
33883
33785
|
sessionID = context.sessionManager.getSessionId();
|
|
33884
33786
|
activeSessionContext = context;
|
|
33885
|
-
candidateBaseline = undefined;
|
|
33886
|
-
const branch = context.sessionManager.getBranch?.() ?? [];
|
|
33887
|
-
askAwareness = transcriptAskAwareness(branch, sessionID) ?? emptyAskAwareness(sessionID);
|
|
33888
|
-
const persistedLegionSession = transcriptMarksLegionSession(branch, sessionID);
|
|
33889
|
-
const managed = persistedLegionSession || legionRoleClaimBridge().managedSessions.has(sessionID);
|
|
33890
|
-
legionManagedSessionID = managed ? sessionID : "";
|
|
33891
33787
|
};
|
|
33892
33788
|
pi.on("resources_discover", async () => ({ skillPaths: [SKILLS_DIRECTORY] }));
|
|
33893
33789
|
registerEnvoyMessageRenderer(pi);
|
|
@@ -33928,13 +33824,6 @@ function envoyExtension(pi) {
|
|
|
33928
33824
|
const dedupeKey = rendered.envelope?.dedupe_key;
|
|
33929
33825
|
const duplicate = dedupeKey !== undefined && dedupeKeys.has(dedupeKey);
|
|
33930
33826
|
if (!duplicate && !rendered.skip) {
|
|
33931
|
-
if (rendered.dispatchActor?.kind === "user" && activeSessionContext !== undefined) {
|
|
33932
|
-
try {
|
|
33933
|
-
await armAskReminder(activeSessionContext, true);
|
|
33934
|
-
} catch (error) {
|
|
33935
|
-
warnAskAvailability(activeSessionContext, error);
|
|
33936
|
-
}
|
|
33937
|
-
}
|
|
33938
33827
|
const envelope = rendered.envelope;
|
|
33939
33828
|
if (envelope !== undefined) {
|
|
33940
33829
|
inbox.unshift({
|
|
@@ -34254,17 +34143,6 @@ function envoyExtension(pi) {
|
|
|
34254
34143
|
return run;
|
|
34255
34144
|
};
|
|
34256
34145
|
const bridge = legionRoleClaimBridge();
|
|
34257
|
-
bridge.markManagedSession = (targetSessionID, context) => {
|
|
34258
|
-
bridge.managedSessions.add(targetSessionID);
|
|
34259
|
-
if (targetSessionID !== sessionID)
|
|
34260
|
-
return;
|
|
34261
|
-
if (legionManagedSessionID === targetSessionID)
|
|
34262
|
-
return;
|
|
34263
|
-
legionManagedSessionID = targetSessionID;
|
|
34264
|
-
if (context !== undefined || activeSessionContext !== undefined) {
|
|
34265
|
-
pi.appendEntry(LEGION_MANAGED_ENTRY, { session_id: targetSessionID });
|
|
34266
|
-
}
|
|
34267
|
-
};
|
|
34268
34146
|
const claim = async (targetSessionID, role, callerContext) => {
|
|
34269
34147
|
const context = callerContext ?? activeSessionContext;
|
|
34270
34148
|
if (context === undefined || context.sessionManager.getSessionId() !== targetSessionID) {
|
|
@@ -34278,7 +34156,6 @@ function envoyExtension(pi) {
|
|
|
34278
34156
|
bridge.claim = claim;
|
|
34279
34157
|
bridge.ready.resolve(claim);
|
|
34280
34158
|
pi.on("session_start", async (_event, context) => {
|
|
34281
|
-
installTerminalInputCancellation(context);
|
|
34282
34159
|
const previousSessionID = sessionID;
|
|
34283
34160
|
restoreLocalSessionState(context);
|
|
34284
34161
|
if (dispatchConfig.error !== null) {
|
|
@@ -34314,7 +34191,6 @@ function envoyExtension(pi) {
|
|
|
34314
34191
|
}
|
|
34315
34192
|
});
|
|
34316
34193
|
const rebind = async (reason, context) => {
|
|
34317
|
-
installTerminalInputCancellation(context);
|
|
34318
34194
|
const previousID = sessionID;
|
|
34319
34195
|
restoreLocalSessionState(context);
|
|
34320
34196
|
if (defaults.natsUrls.length === 0)
|
|
@@ -34364,8 +34240,6 @@ function envoyExtension(pi) {
|
|
|
34364
34240
|
]);
|
|
34365
34241
|
} finally {
|
|
34366
34242
|
clearTimeout(timer);
|
|
34367
|
-
terminalInputUnsubscribe?.();
|
|
34368
|
-
terminalInputUnsubscribe = undefined;
|
|
34369
34243
|
connection = undefined;
|
|
34370
34244
|
activeSessionContext = undefined;
|
|
34371
34245
|
subscriptions.clear();
|
|
@@ -34419,7 +34293,6 @@ function envoyExtension(pi) {
|
|
|
34419
34293
|
const snapshot = await queryOpenAsks(id);
|
|
34420
34294
|
if (snapshot === null)
|
|
34421
34295
|
return;
|
|
34422
|
-
setReminderBaseline(id, askAwareness.session_id === id ? askAwareness.period : 0, snapshot.as_of);
|
|
34423
34296
|
return {
|
|
34424
34297
|
message: {
|
|
34425
34298
|
customType: "dispatch-open-asks",
|
|
@@ -34441,60 +34314,7 @@ ${formatOpenAsksSummary(snapshot, currentDispatchConfig().url)}`,
|
|
|
34441
34314
|
};
|
|
34442
34315
|
}
|
|
34443
34316
|
});
|
|
34444
|
-
pi.on("agent_start", async () => {
|
|
34445
|
-
agentRunning = true;
|
|
34446
|
-
agentWillContinue = false;
|
|
34447
|
-
});
|
|
34448
|
-
pi.on("agent_end", async (event) => {
|
|
34449
|
-
agentRunning = false;
|
|
34450
|
-
agentWillContinue = event.willContinue === true;
|
|
34451
|
-
});
|
|
34452
|
-
pi.on("input", async () => {
|
|
34453
|
-
cancelPendingAskReminder();
|
|
34454
|
-
});
|
|
34455
|
-
pi.on("message_start", async (event, context) => {
|
|
34456
|
-
if (!isHumanTurnInitiator(event.message))
|
|
34457
|
-
return;
|
|
34458
|
-
try {
|
|
34459
|
-
await armAskReminder(context);
|
|
34460
|
-
} catch (error) {
|
|
34461
|
-
warnAskAvailability(context, error);
|
|
34462
|
-
}
|
|
34463
|
-
});
|
|
34464
|
-
pi.on("session_stop", async (event, context) => {
|
|
34465
|
-
if (event.stop_hook_active || event.signal.aborted || shuttingDown || agentWillContinue || event.last_assistant_message?.role !== "assistant" || event.session_id !== sessionID || legionManagedSessionID === event.session_id || askAwareness.session_id !== event.session_id || askAwareness.period === 0 || askAwareness.checked || askAwareness.fired) {
|
|
34466
|
-
return;
|
|
34467
|
-
}
|
|
34468
|
-
const baseline = askAwareness.baseline_as_of;
|
|
34469
|
-
if (baseline === null)
|
|
34470
|
-
return;
|
|
34471
|
-
if (context.ui.getEditorText().trim() !== "")
|
|
34472
|
-
return;
|
|
34473
|
-
const claimed = { ...askAwareness, checked: true };
|
|
34474
|
-
const generation = awarenessGeneration;
|
|
34475
|
-
persistAskAwareness(claimed);
|
|
34476
|
-
let snapshot;
|
|
34477
|
-
try {
|
|
34478
|
-
snapshot = await queryOpenAsks(event.session_id, baseline, event.signal);
|
|
34479
|
-
} catch (error) {
|
|
34480
|
-
if (!event.signal.aborted && generation === awarenessGeneration) {
|
|
34481
|
-
warnAskAvailability(context, error);
|
|
34482
|
-
}
|
|
34483
|
-
return;
|
|
34484
|
-
}
|
|
34485
|
-
if (snapshot === null || event.signal.aborted || generation !== awarenessGeneration || shuttingDown || agentWillContinue || context.sessionManager.getSessionId() !== event.session_id || sessionID !== event.session_id || legionManagedSessionID === event.session_id || context.ui.getEditorText().trim() !== "" || askAwareness.session_id !== claimed.session_id || askAwareness.period !== claimed.period || !askAwareness.checked) {
|
|
34486
|
-
return;
|
|
34487
|
-
}
|
|
34488
|
-
if (snapshot.count > 0 || snapshot.opened_since || askAwareness.saw_ask) {
|
|
34489
|
-
return;
|
|
34490
|
-
}
|
|
34491
|
-
persistAskAwareness({ ...askAwareness, fired: true });
|
|
34492
|
-
return { continue: true, additionalContext: OPEN_ASKS_REMINDER };
|
|
34493
|
-
});
|
|
34494
34317
|
pi.on("tool_result", async (event) => {
|
|
34495
|
-
if (!event.isError && event.toolName === "dispatch_ask" && typeof event.details === "object" && event.details !== null && "ask" in event.details && typeof event.details.ask === "string" && askAwareness.session_id === sessionID && askAwareness.period > 0 && !askAwareness.saw_ask) {
|
|
34496
|
-
persistAskAwareness({ ...askAwareness, saw_ask: true });
|
|
34497
|
-
}
|
|
34498
34318
|
if (event.isError)
|
|
34499
34319
|
return;
|
|
34500
34320
|
const topic = dispatchSubscriptionTopic(event.details);
|