@sagentlab/navarch-runtime 0.1.132 → 0.1.133
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/adapters/muse.cjs +61 -17
- package/dist/session.cjs +2 -2
- package/package.json +1 -1
package/dist/adapters/muse.cjs
CHANGED
|
@@ -12,7 +12,11 @@ const node_path_1 = __importDefault(require("node:path"));
|
|
|
12
12
|
const node_crypto_1 = require("node:crypto");
|
|
13
13
|
const kill_agent_cjs_1 = require("../kill-agent.cjs");
|
|
14
14
|
const version_cjs_1 = require("../version.cjs");
|
|
15
|
-
|
|
15
|
+
// `interrupt: native` is MSP `turn/cancel` on the live connection, then a new
|
|
16
|
+
// `turn/start` in the same session. It is a protocol request and is not the
|
|
17
|
+
// lease-loss path, which waits CANCEL_GRACE_MS and then kills the host. There
|
|
18
|
+
// is no mid-turn input channel: `steer` is `none` and `queue` waits for the turn.
|
|
19
|
+
const MUSE_STEERING = Object.freeze({ steer: "none", queue: "boundary", interrupt: "native" });
|
|
16
20
|
const CANCEL_GRACE_MS = 2_000;
|
|
17
21
|
const SHUTDOWN_GRACE_MS = 30_000;
|
|
18
22
|
const TERMINATE_GRACE_MS = 2_000;
|
|
@@ -82,7 +86,9 @@ async function runMuseProcess(options, mcpServers, sdk) {
|
|
|
82
86
|
let protocolFailed = false;
|
|
83
87
|
const closed = deferred();
|
|
84
88
|
const stopped = deferred();
|
|
85
|
-
|
|
89
|
+
let completed = deferred();
|
|
90
|
+
let pendingInterrupt;
|
|
91
|
+
let sawTurn = false; // An earlier turn ended, so the session is resumable even if the next start fails.
|
|
86
92
|
const items = new Map();
|
|
87
93
|
const usage = new Map();
|
|
88
94
|
const pending = [];
|
|
@@ -252,19 +258,56 @@ async function runMuseProcess(options, mcpServers, sdk) {
|
|
|
252
258
|
}
|
|
253
259
|
if (timedOut || killedByLeaseLoss || protocolFailed)
|
|
254
260
|
return;
|
|
255
|
-
const
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
261
|
+
const startTurn = async (text) => {
|
|
262
|
+
const ack = await connection.command("turn/start", {
|
|
263
|
+
sessionId,
|
|
264
|
+
input: [{ type: "text", text }],
|
|
265
|
+
...(options.reasoningEffort ? { reasoningEffort: options.reasoningEffort } : {}),
|
|
266
|
+
}, { maxAttempts: 1 });
|
|
267
|
+
if (typeof ack.turnId !== "string" || ack.disposition !== "started")
|
|
268
|
+
throw new Error("Muse did not start the requested turn.");
|
|
269
|
+
turnId = ack.turnId;
|
|
270
|
+
for (const notification of pending.splice(0))
|
|
271
|
+
consume(notification);
|
|
272
|
+
};
|
|
273
|
+
const channel = {
|
|
274
|
+
send: () => false,
|
|
275
|
+
// Native interrupt: cancel the running turn on this connection. The
|
|
276
|
+
// host stays up; the run loop starts the next turn once this one ends.
|
|
277
|
+
interrupt: (input) => {
|
|
278
|
+
if (!turnId || terminal || pendingInterrupt || cancelling || timedOut || killedByLeaseLoss || protocolFailed)
|
|
279
|
+
return false;
|
|
280
|
+
pendingInterrupt = input;
|
|
281
|
+
void connection.command("turn/cancel", { sessionId, turnId }, { maxAttempts: 1 }).catch(() => undefined);
|
|
282
|
+
return true;
|
|
283
|
+
},
|
|
284
|
+
};
|
|
285
|
+
await startTurn(options.prompt);
|
|
286
|
+
options.liveInput?.attach(channel);
|
|
287
|
+
for (;;) {
|
|
288
|
+
await Promise.race([completed.promise, connection.closed]);
|
|
289
|
+
if (!terminal && !timedOut && !killedByLeaseLoss)
|
|
290
|
+
throw new Error("Muse closed before the turn completed.");
|
|
291
|
+
const next = pendingInterrupt;
|
|
292
|
+
const endedCleanly = terminal?.terminal === "cancelled" || terminal?.terminal === "completed";
|
|
293
|
+
if (!next || !endedCleanly || timedOut || killedByLeaseLoss || protocolFailed)
|
|
294
|
+
break;
|
|
295
|
+
// The cancelled turn is the interrupt, not a failure. A turn that
|
|
296
|
+
// finished before the cancel landed makes this a boundary delivery.
|
|
297
|
+
const stoppedTurn = terminal?.terminal === "cancelled";
|
|
298
|
+
pendingInterrupt = undefined;
|
|
299
|
+
sawTurn = true;
|
|
300
|
+
terminal = undefined;
|
|
301
|
+
turnId = undefined;
|
|
302
|
+
completed = deferred();
|
|
303
|
+
items.clear();
|
|
304
|
+
await startTurn(next.text);
|
|
305
|
+
// Teardown may have run while the ack was in flight; do not re-attach.
|
|
306
|
+
if (timedOut || killedByLeaseLoss || protocolFailed || cancelling)
|
|
307
|
+
return;
|
|
308
|
+
options.liveInput?.consumed(next.id, !stoppedTurn, stoppedTurn);
|
|
309
|
+
options.liveInput?.attach(channel);
|
|
310
|
+
}
|
|
268
311
|
};
|
|
269
312
|
await Promise.race([run(), stopped.promise]);
|
|
270
313
|
}
|
|
@@ -276,6 +319,7 @@ async function runMuseProcess(options, mcpServers, sdk) {
|
|
|
276
319
|
diagnostic(error instanceof Error ? error.message : "Muse protocol request failed.");
|
|
277
320
|
}
|
|
278
321
|
finally {
|
|
322
|
+
options.liveInput?.attach(null);
|
|
279
323
|
await cancelling;
|
|
280
324
|
// EOF releases Muse's writer lease. A bounded shutdown always reaps the
|
|
281
325
|
// child, including a host that ignores EOF or died during a request.
|
|
@@ -312,7 +356,7 @@ async function runMuseProcess(options, mcpServers, sdk) {
|
|
|
312
356
|
timedOut, killedByLeaseLoss, stdout, stderr,
|
|
313
357
|
...(reportText ? { reportText } : {}),
|
|
314
358
|
...(authError ? { authError } : {}),
|
|
315
|
-
...(durable && sessionId && terminal ? { agentSessionId: sessionId } : {}),
|
|
359
|
+
...(durable && sessionId && (terminal || sawTurn) ? { agentSessionId: sessionId } : {}),
|
|
316
360
|
...normalizedUsage([...usage.values()], !options.resumeSessionId),
|
|
317
361
|
};
|
|
318
362
|
}
|
|
@@ -431,4 +475,4 @@ function spawnMuse(options) {
|
|
|
431
475
|
}
|
|
432
476
|
// Read-only Navi conversations are deliberately absent: an approval mode is
|
|
433
477
|
// not a filesystem read boundary, and the SDK cannot yet prove that contract.
|
|
434
|
-
exports.museAdapter = { agentType: "muse", run: runMuseAdapter, steering:
|
|
478
|
+
exports.museAdapter = { agentType: "muse", run: runMuseAdapter, steering: MUSE_STEERING };
|
package/dist/session.cjs
CHANGED
|
@@ -914,8 +914,8 @@ async function runClaimedSession(deps, claimed, sessionId, lifecycle) {
|
|
|
914
914
|
signal: turnAbort.signal,
|
|
915
915
|
// Native steering: the adapter keeps the agent's input open and
|
|
916
916
|
// reports each message the agent reads. Absent for every adapter
|
|
917
|
-
// whose record says `steer:
|
|
918
|
-
liveInput: steering && steeringCapabilities?.steer === "native" ? steering.liveInput : undefined,
|
|
917
|
+
// whose record says neither `steer: native` nor `interrupt: native`.
|
|
918
|
+
liveInput: steering && (steeringCapabilities?.steer === "native" || steeringCapabilities?.interrupt === "native") ? steering.liveInput : undefined,
|
|
919
919
|
onProcessStarted: () => {
|
|
920
920
|
if (steeringTaken.length > 0 && !steeringTurnStarted) {
|
|
921
921
|
clearTimeout(steeringStartTimer);
|
package/package.json
CHANGED