@mutagent/cli 0.2.215 → 0.2.217
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/README.md +4 -2
- package/dist/bin/cli.js +365 -16
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -85,8 +85,8 @@ To run Helix on your own machine instead, install the Helix binary with `mutagen
|
|
|
85
85
|
belong here.
|
|
86
86
|
- **Helix session**: a cloud run of Helix, addressed by a reference (`hs1_...`) that is printed
|
|
87
87
|
when the run starts. `mutagent helix session` lists sessions, attaches to them, sends input,
|
|
88
|
-
stops, checkpoints and restores them. A sandbox idle for 15 minutes is
|
|
89
|
-
interactive session in it is checkpointed first.
|
|
88
|
+
answers their questions, stops, checkpoints and restores them. A sandbox idle for 15 minutes is
|
|
89
|
+
stopped, and an interactive session in it is checkpointed first.
|
|
90
90
|
- **Managed agent**: a folder whose entry file is `agent.md` (YAML frontmatter, then the standing
|
|
91
91
|
prompt). `mutagent agent deploy` stores each change as a new revision. A slot is the agent in one
|
|
92
92
|
Environment (`--env`).
|
|
@@ -201,6 +201,8 @@ mutagent helix session list
|
|
|
201
201
|
mutagent helix session attach <reference> # Watch it (--since <n> replays)
|
|
202
202
|
mutagent helix session send <reference> "and now the tests"
|
|
203
203
|
mutagent helix session send <reference> --type steer "stop and re-read the spec"
|
|
204
|
+
mutagent helix session approvals <reference> # What it is waiting on (a question it asked)
|
|
205
|
+
mutagent helix session answer <reference> <id> --value 'an option' # Answer it (--cancel denies)
|
|
204
206
|
mutagent helix session signal <reference> --force # Stop it; the sandbox stays up
|
|
205
207
|
mutagent helix session close-input <reference>
|
|
206
208
|
mutagent helix session checkpoint <reference>
|
package/dist/bin/cli.js
CHANGED
|
@@ -28750,6 +28750,16 @@ async function restoreSession(routes, reference, body) {
|
|
|
28750
28750
|
async function endSession(routes, reference) {
|
|
28751
28751
|
return onRoute(routes, (base) => requestJson(referencePath(base, reference) + "/end", { method: "POST", body: "{}" }));
|
|
28752
28752
|
}
|
|
28753
|
+
async function listSessionApprovals(routes, reference, signal) {
|
|
28754
|
+
return onRoute(routes, (base) => requestJson(referencePath(base, reference) + "/approvals", { signal }));
|
|
28755
|
+
}
|
|
28756
|
+
async function answerSessionApproval(routes, reference, id, answer, signal) {
|
|
28757
|
+
await onRoute(routes, (base) => requestJson(`${referencePath(base, reference)}/approvals/${encodeURIComponent(id)}`, {
|
|
28758
|
+
method: "POST",
|
|
28759
|
+
body: JSON.stringify({ decision: answer.decision, ...answer.value === undefined ? {} : { value: answer.value } }),
|
|
28760
|
+
signal
|
|
28761
|
+
}));
|
|
28762
|
+
}
|
|
28753
28763
|
async function onSessionRoute(routes, call) {
|
|
28754
28764
|
return onRoute(routes, (base) => call(base));
|
|
28755
28765
|
}
|
|
@@ -28762,6 +28772,215 @@ init_errors();
|
|
|
28762
28772
|
import { createInterface as createInterface2 } from "node:readline";
|
|
28763
28773
|
init_known_secrets();
|
|
28764
28774
|
|
|
28775
|
+
// src/lib/session-questions.ts
|
|
28776
|
+
var QUESTION_KINDS = ["select", "confirm", "input", "editor"];
|
|
28777
|
+
var isQuestionKind = (method) => typeof method === "string" && QUESTION_KINDS.includes(method);
|
|
28778
|
+
var str = (value) => typeof value === "string" ? value : null;
|
|
28779
|
+
function questionOfFrame(frameText) {
|
|
28780
|
+
let frame;
|
|
28781
|
+
try {
|
|
28782
|
+
frame = JSON.parse(frameText);
|
|
28783
|
+
} catch {
|
|
28784
|
+
return null;
|
|
28785
|
+
}
|
|
28786
|
+
if (frame === null || typeof frame !== "object" || Array.isArray(frame))
|
|
28787
|
+
return null;
|
|
28788
|
+
const record = frame;
|
|
28789
|
+
if (record.type !== "extension_ui_request")
|
|
28790
|
+
return null;
|
|
28791
|
+
const method = record.method;
|
|
28792
|
+
if (!isQuestionKind(method))
|
|
28793
|
+
return null;
|
|
28794
|
+
const id = str(record.id);
|
|
28795
|
+
if (id === null || id === "")
|
|
28796
|
+
return null;
|
|
28797
|
+
return {
|
|
28798
|
+
id,
|
|
28799
|
+
kind: method,
|
|
28800
|
+
title: str(record.title) ?? "",
|
|
28801
|
+
message: method === "confirm" ? str(record.message) : null,
|
|
28802
|
+
options: method === "select" && Array.isArray(record.options) ? record.options.filter((option) => typeof option === "string") : null,
|
|
28803
|
+
placeholder: method === "input" ? str(record.placeholder) : null
|
|
28804
|
+
};
|
|
28805
|
+
}
|
|
28806
|
+
function shellQuote2(value) {
|
|
28807
|
+
if (value !== "" && /^[A-Za-z0-9_@%+=:,./-]+$/.test(value))
|
|
28808
|
+
return value;
|
|
28809
|
+
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
28810
|
+
}
|
|
28811
|
+
function answerCommand(cli, reference, question) {
|
|
28812
|
+
if (question.kind === "confirm")
|
|
28813
|
+
return `${cli} session answer ${reference} ${question.id}`;
|
|
28814
|
+
const example = question.options !== null && question.options.length > 0 ? question.options[0] ?? "<text>" : "<text>";
|
|
28815
|
+
return `${cli} session answer ${reference} ${question.id} --value ${shellQuote2(example)}`;
|
|
28816
|
+
}
|
|
28817
|
+
function cancelCommand(cli, reference, id) {
|
|
28818
|
+
return `${cli} session answer ${reference} ${id} --cancel`;
|
|
28819
|
+
}
|
|
28820
|
+
function questionDetail(question) {
|
|
28821
|
+
if (question.kind === "select") {
|
|
28822
|
+
return question.options === null || question.options.length === 0 ? "" : `options: ${question.options.join(" | ")}`;
|
|
28823
|
+
}
|
|
28824
|
+
if (question.kind === "confirm")
|
|
28825
|
+
return question.message === null ? "" : `message: ${question.message}`;
|
|
28826
|
+
if (question.kind === "input")
|
|
28827
|
+
return question.placeholder === null ? "" : `placeholder: ${question.placeholder}`;
|
|
28828
|
+
return "";
|
|
28829
|
+
}
|
|
28830
|
+
function questionLines(question, cli, reference) {
|
|
28831
|
+
const lines = [`The session asks (${question.kind}, id ${question.id}): ${question.title === "" ? "(no title)" : question.title}`];
|
|
28832
|
+
if (question.kind === "select" && question.options !== null && question.options.length > 0) {
|
|
28833
|
+
lines.push(` Options: ${question.options.join(" | ")}`);
|
|
28834
|
+
}
|
|
28835
|
+
if (question.kind === "confirm" && question.message !== null && question.message !== "") {
|
|
28836
|
+
lines.push(` Message: ${question.message}`);
|
|
28837
|
+
}
|
|
28838
|
+
if (question.kind === "input" && question.placeholder !== null && question.placeholder !== "") {
|
|
28839
|
+
lines.push(` Placeholder: ${question.placeholder}`);
|
|
28840
|
+
}
|
|
28841
|
+
lines.push(` Answer: ${answerCommand(cli, reference, question)}`);
|
|
28842
|
+
lines.push(` Deny: ${cancelCommand(cli, reference, question.id)}`);
|
|
28843
|
+
return lines;
|
|
28844
|
+
}
|
|
28845
|
+
function pendingQuestionLines(approvals, cli, reference) {
|
|
28846
|
+
if (approvals.length === 0)
|
|
28847
|
+
return [];
|
|
28848
|
+
const count = `${String(approvals.length)} question${approvals.length === 1 ? "" : "s"}`;
|
|
28849
|
+
return [
|
|
28850
|
+
`The session is waiting on ${count} asked before this attach — answer from any terminal:`,
|
|
28851
|
+
...approvals.flatMap((approval) => questionLines(approval, cli, reference))
|
|
28852
|
+
];
|
|
28853
|
+
}
|
|
28854
|
+
var TURN_ENDS = new Set(["turn_end", "agent_end"]);
|
|
28855
|
+
function questionSignalOfFrame(frameText) {
|
|
28856
|
+
const question = questionOfFrame(frameText);
|
|
28857
|
+
if (question !== null)
|
|
28858
|
+
return { type: "ask", question };
|
|
28859
|
+
if (!/(?:extension_ui_response|turn_end|agent_end)/.test(frameText))
|
|
28860
|
+
return null;
|
|
28861
|
+
let frame;
|
|
28862
|
+
try {
|
|
28863
|
+
frame = JSON.parse(frameText);
|
|
28864
|
+
} catch {
|
|
28865
|
+
return null;
|
|
28866
|
+
}
|
|
28867
|
+
if (frame === null || typeof frame !== "object" || Array.isArray(frame))
|
|
28868
|
+
return null;
|
|
28869
|
+
const record = frame;
|
|
28870
|
+
if (record.type === "extension_ui_response") {
|
|
28871
|
+
const id = str(record.id);
|
|
28872
|
+
return id === null ? null : { type: "answer", id };
|
|
28873
|
+
}
|
|
28874
|
+
return typeof record.type === "string" && TURN_ENDS.has(record.type) ? { type: "turn-end" } : null;
|
|
28875
|
+
}
|
|
28876
|
+
|
|
28877
|
+
class SessionQuestions {
|
|
28878
|
+
#notice;
|
|
28879
|
+
#cli;
|
|
28880
|
+
#reference;
|
|
28881
|
+
#fetchOpen;
|
|
28882
|
+
#known = false;
|
|
28883
|
+
#started = new Set;
|
|
28884
|
+
#open = new Set;
|
|
28885
|
+
#closed = new Set;
|
|
28886
|
+
#printed = new Set;
|
|
28887
|
+
#waiting = [];
|
|
28888
|
+
#inFlight = null;
|
|
28889
|
+
constructor(options) {
|
|
28890
|
+
this.#notice = options.notice;
|
|
28891
|
+
this.#cli = options.cli;
|
|
28892
|
+
this.#reference = options.reference;
|
|
28893
|
+
this.#fetchOpen = options.fetchOpen;
|
|
28894
|
+
}
|
|
28895
|
+
async start() {
|
|
28896
|
+
if (this.#fetchOpen === undefined)
|
|
28897
|
+
return;
|
|
28898
|
+
let open;
|
|
28899
|
+
try {
|
|
28900
|
+
open = await this.#fetchOpen();
|
|
28901
|
+
} catch {
|
|
28902
|
+
return;
|
|
28903
|
+
}
|
|
28904
|
+
this.#known = true;
|
|
28905
|
+
for (const approval of open) {
|
|
28906
|
+
this.#started.add(approval.id);
|
|
28907
|
+
this.#open.add(approval.id);
|
|
28908
|
+
}
|
|
28909
|
+
for (const line of pendingQuestionLines(open, this.#cli, this.#reference))
|
|
28910
|
+
this.#notice(line);
|
|
28911
|
+
}
|
|
28912
|
+
frame(frameText) {
|
|
28913
|
+
const signal = questionSignalOfFrame(frameText);
|
|
28914
|
+
if (signal === null)
|
|
28915
|
+
return;
|
|
28916
|
+
if (signal.type === "answer") {
|
|
28917
|
+
this.#closed.add(signal.id);
|
|
28918
|
+
this.#open.delete(signal.id);
|
|
28919
|
+
return;
|
|
28920
|
+
}
|
|
28921
|
+
if (signal.type === "turn-end") {
|
|
28922
|
+
for (const id of this.#open)
|
|
28923
|
+
this.#closed.add(id);
|
|
28924
|
+
for (const question of this.#waiting)
|
|
28925
|
+
this.#closed.add(question.id);
|
|
28926
|
+
this.#open.clear();
|
|
28927
|
+
this.#waiting = [];
|
|
28928
|
+
return;
|
|
28929
|
+
}
|
|
28930
|
+
const question = signal.question;
|
|
28931
|
+
if (this.#printed.has(question.id) || this.#closed.has(question.id))
|
|
28932
|
+
return;
|
|
28933
|
+
if (this.#started.has(question.id)) {
|
|
28934
|
+
this.#printed.add(question.id);
|
|
28935
|
+
return;
|
|
28936
|
+
}
|
|
28937
|
+
if (this.#fetchOpen === undefined || !this.#known) {
|
|
28938
|
+
this.#render(question);
|
|
28939
|
+
return;
|
|
28940
|
+
}
|
|
28941
|
+
this.#waiting.push(question);
|
|
28942
|
+
this.#inFlight ??= this.#verify();
|
|
28943
|
+
}
|
|
28944
|
+
async settle() {
|
|
28945
|
+
while (this.#inFlight !== null)
|
|
28946
|
+
await this.#inFlight;
|
|
28947
|
+
}
|
|
28948
|
+
async#verify() {
|
|
28949
|
+
try {
|
|
28950
|
+
const open = await this.#fetchOpen?.() ?? [];
|
|
28951
|
+
const openIds = new Set(open.map((approval) => approval.id));
|
|
28952
|
+
for (const question of this.#drain()) {
|
|
28953
|
+
if (openIds.has(question.id))
|
|
28954
|
+
this.#render(question);
|
|
28955
|
+
else {
|
|
28956
|
+
this.#closed.add(question.id);
|
|
28957
|
+
this.#open.delete(question.id);
|
|
28958
|
+
}
|
|
28959
|
+
}
|
|
28960
|
+
} catch {
|
|
28961
|
+
for (const question of this.#drain())
|
|
28962
|
+
this.#render(question);
|
|
28963
|
+
} finally {
|
|
28964
|
+
this.#inFlight = null;
|
|
28965
|
+
if (this.#waiting.length > 0)
|
|
28966
|
+
this.#inFlight = this.#verify();
|
|
28967
|
+
}
|
|
28968
|
+
}
|
|
28969
|
+
#drain() {
|
|
28970
|
+
const waiting = this.#waiting;
|
|
28971
|
+
this.#waiting = [];
|
|
28972
|
+
return waiting;
|
|
28973
|
+
}
|
|
28974
|
+
#render(question) {
|
|
28975
|
+
if (this.#closed.has(question.id))
|
|
28976
|
+
return;
|
|
28977
|
+
this.#printed.add(question.id);
|
|
28978
|
+
this.#open.add(question.id);
|
|
28979
|
+
for (const line of questionLines(question, this.#cli, this.#reference))
|
|
28980
|
+
this.#notice(line);
|
|
28981
|
+
}
|
|
28982
|
+
}
|
|
28983
|
+
|
|
28765
28984
|
// src/lib/sse.ts
|
|
28766
28985
|
var DEFAULT_EVENT_NAME = "message";
|
|
28767
28986
|
|
|
@@ -29303,9 +29522,22 @@ async function runNativeSession(options) {
|
|
|
29303
29522
|
notice("Session " + options.reference + (options.interrupt === undefined ? ". Ctrl-C detaches." : ". Ctrl-C stops the session (SIGINT)."));
|
|
29304
29523
|
if (options.interruptRequested)
|
|
29305
29524
|
onSigint();
|
|
29525
|
+
const questions = new SessionQuestions({
|
|
29526
|
+
notice,
|
|
29527
|
+
cli: activeProduct().cli,
|
|
29528
|
+
reference: options.reference,
|
|
29529
|
+
...options.openQuestions === undefined ? {} : { fetchOpen: options.openQuestions }
|
|
29530
|
+
});
|
|
29531
|
+
let attached = false;
|
|
29306
29532
|
const result = await runStreamSession({
|
|
29307
29533
|
open: async (request) => {
|
|
29308
29534
|
const stream = await options.open(request);
|
|
29535
|
+
if (!attached) {
|
|
29536
|
+
attached = true;
|
|
29537
|
+
try {
|
|
29538
|
+
await questions.start();
|
|
29539
|
+
} catch {}
|
|
29540
|
+
}
|
|
29309
29541
|
if (options.rpc && !startedInput && !inputController.signal.aborted) {
|
|
29310
29542
|
startedInput = true;
|
|
29311
29543
|
inputTask = pump().catch((error) => {
|
|
@@ -29328,6 +29560,7 @@ async function runNativeSession(options) {
|
|
|
29328
29560
|
stdout(text.endsWith(`
|
|
29329
29561
|
`) ? text : text + `
|
|
29330
29562
|
`);
|
|
29563
|
+
questions.frame(text);
|
|
29331
29564
|
},
|
|
29332
29565
|
banner: (text) => {
|
|
29333
29566
|
stdout(text.endsWith(`
|
|
@@ -29347,6 +29580,9 @@ async function runNativeSession(options) {
|
|
|
29347
29580
|
signal: controller.signal
|
|
29348
29581
|
});
|
|
29349
29582
|
finished = true;
|
|
29583
|
+
try {
|
|
29584
|
+
await questions.settle();
|
|
29585
|
+
} catch {}
|
|
29350
29586
|
const woke = result.session;
|
|
29351
29587
|
if (woke?.reference !== undefined && (woke.status === "woken" || woke.status === "exited")) {
|
|
29352
29588
|
stderr("[mutagent] " + JSON.stringify({ event: "woken", reference: woke.reference }) + `
|
|
@@ -31042,6 +31278,12 @@ Send does not wait for the reply. The reply arrives on the session's stream as a
|
|
|
31042
31278
|
'response' event carrying the command id (in --json output). Watch it with:
|
|
31043
31279
|
${chalk29.dim("$")} mutagent helix session attach <reference>
|
|
31044
31280
|
|
|
31281
|
+
Questions are not sent to, they are answered: when the session asks (its ask tool —
|
|
31282
|
+
a select, confirm, input or editor dialog), 'mutagent helix session approvals
|
|
31283
|
+
<reference>' lists what is open and 'mutagent helix session answer <reference> <id>
|
|
31284
|
+
--value <text>' (or --cancel) answers it. Attach prints the same command when the
|
|
31285
|
+
question appears.
|
|
31286
|
+
|
|
31045
31287
|
AI Agent Directive:
|
|
31046
31288
|
--json returns ONE object: { success, reference, type, id, line, accepted,
|
|
31047
31289
|
_links }. 'accepted' means delivered, not answered: the answer is a 'response'
|
|
@@ -31049,7 +31291,10 @@ AI Agent Directive:
|
|
|
31049
31291
|
command does not wait for it. wake.state 'waking' means queued: the session is
|
|
31050
31292
|
being restored and the message is not delivered yet; the stream reports the
|
|
31051
31293
|
delivery, or the failed restore and the message it lost. Use --type and a message; use --line only for a
|
|
31052
|
-
command the four types cannot express.
|
|
31294
|
+
command the four types cannot express. A question the session asks is never
|
|
31295
|
+
answered by send: poll 'mutagent helix session approvals <reference> --json'
|
|
31296
|
+
and answer with 'mutagent helix session answer <reference> <id> --value <text>'
|
|
31297
|
+
(or --cancel).
|
|
31053
31298
|
`)).action(async (id, messageParts, options) => {
|
|
31054
31299
|
const isJson = getJsonFlag(jsonSource);
|
|
31055
31300
|
const output = new OutputFormatter(isJson ? "json" : "table");
|
|
@@ -31285,8 +31530,10 @@ Examples:
|
|
|
31285
31530
|
${chalk31.dim("$")} mutagent helix session checkpoint <reference>
|
|
31286
31531
|
|
|
31287
31532
|
A session reference (hs1_...) is printed when a run starts and listed by 'session list'.
|
|
31288
|
-
attach, send, signal, close-input, checkpoint, checkpoints
|
|
31289
|
-
checkpoint saves a session's conversation; restore continues it in
|
|
31533
|
+
attach, send, signal, close-input, checkpoint, checkpoints, approvals, answer and
|
|
31534
|
+
restore take it. checkpoint saves a session's conversation; restore continues it in
|
|
31535
|
+
a rebuilt sandbox. approvals lists the questions the session is waiting on a human
|
|
31536
|
+
for, and answer answers one — a session that asked blocks until it is answered.
|
|
31290
31537
|
A sandbox idle for 15 minutes is stopped; an interactive session in it is checkpointed first.
|
|
31291
31538
|
A send to that session wakes it from its last checkpoint; restore is the manual option.
|
|
31292
31539
|
|
|
@@ -31294,7 +31541,7 @@ AI Agent Directive:
|
|
|
31294
31541
|
Start a steerable session with mutagent helix --mode rpc (or -p for one-shot)
|
|
31295
31542
|
and keep the session reference it prints; 'mutagent helix session list --json'
|
|
31296
31543
|
lists references too. Use the reference for attach, send, signal, close-input,
|
|
31297
|
-
checkpoint, checkpoints and restore; never guess one. Only an interactive
|
|
31544
|
+
checkpoint, checkpoints, approvals, answer and restore; never guess one. Only an interactive
|
|
31298
31545
|
(--mode rpc) session accepts send. restore starts a NEW session and returns its
|
|
31299
31546
|
reference. A managed agent's session cannot be restored yet (409).
|
|
31300
31547
|
`));
|
|
@@ -31303,6 +31550,8 @@ AI Agent Directive:
|
|
|
31303
31550
|
registerSessionList(session, parent, p);
|
|
31304
31551
|
registerSessionAttach(session, p);
|
|
31305
31552
|
registerSendCommand(session, { jsonSource: parent, product: p });
|
|
31553
|
+
registerApprovals(session, parent, p);
|
|
31554
|
+
registerAnswer(session, parent, p);
|
|
31306
31555
|
registerSignalCommand(session, { jsonSource: parent, product: p });
|
|
31307
31556
|
registerCloseInput(session, parent, p);
|
|
31308
31557
|
registerCheckpoint(session, parent, p);
|
|
@@ -31470,7 +31719,10 @@ function registerSessionAttach(session, p) {
|
|
|
31470
31719
|
" A dropped stream reconnects at its cursor for up to 5 minutes; if it gives up, it prints the seq to resume from.",
|
|
31471
31720
|
" A reference names one session; after a restore, take the new reference from session list.",
|
|
31472
31721
|
' When a send wakes the session, stderr carries [mutagent] {"event":"woken","reference":"hs1_..."}:',
|
|
31473
|
-
" this stream ends there; attach to that reference with --since 0."
|
|
31722
|
+
" this stream ends there; attach to that reference with --since 0.",
|
|
31723
|
+
" A question the session asks (its ask tool: a select, confirm, input or editor dialog)",
|
|
31724
|
+
" prints on stderr with the command that answers it; a question replayed from",
|
|
31725
|
+
" history that has since closed prints nothing. session approvals lists what is open."
|
|
31474
31726
|
].join(`
|
|
31475
31727
|
`))).action(async (reference, options) => {
|
|
31476
31728
|
try {
|
|
@@ -31482,13 +31734,110 @@ function registerSessionAttach(session, p) {
|
|
|
31482
31734
|
check: () => checkSession(reference, findRow),
|
|
31483
31735
|
...since === undefined ? {} : { since },
|
|
31484
31736
|
open: (request) => openSessionStream(p, reference, request),
|
|
31485
|
-
send: (line, signal) => sendSessionInput(p, reference, line, signal)
|
|
31737
|
+
send: (line, signal) => sendSessionInput(p, reference, line, signal),
|
|
31738
|
+
openQuestions: async () => {
|
|
31739
|
+
const { approvals } = await listSessionApprovals(p, reference, AbortSignal.timeout(1e4));
|
|
31740
|
+
return Array.isArray(approvals) ? approvals : [];
|
|
31741
|
+
}
|
|
31486
31742
|
});
|
|
31487
31743
|
} catch (error) {
|
|
31488
31744
|
handleError(sessionGoneGuidance(error, "stream", reference), false);
|
|
31489
31745
|
}
|
|
31490
31746
|
});
|
|
31491
31747
|
}
|
|
31748
|
+
function registerApprovals(session, root, p) {
|
|
31749
|
+
const t = (text) => productText(text, p);
|
|
31750
|
+
session.command("approvals").description(t("List the questions a Helix session is waiting on")).argument("<reference>", t("Session reference (hs1_...) from 'mutagent helix' or 'session list'")).addHelpText("after", t(`
|
|
31751
|
+
Examples:
|
|
31752
|
+
${chalk31.dim("$")} mutagent helix session approvals <reference>
|
|
31753
|
+
${chalk31.dim("$")} mutagent helix session approvals <reference> --json
|
|
31754
|
+
|
|
31755
|
+
The questions the session asked and is waiting on a human to answer: a select
|
|
31756
|
+
(its options), a confirm (yes or no), an input or an editor (text). While any
|
|
31757
|
+
is open the session is needs-input and its turn waits; a question whose timeout
|
|
31758
|
+
passes is resolved by the sandbox itself. Each row's id is what an answer
|
|
31759
|
+
names — answer it with 'mutagent helix session answer'; attach prints the same
|
|
31760
|
+
commands the moment a question appears.
|
|
31761
|
+
|
|
31762
|
+
AI Agent Directive:
|
|
31763
|
+
--json returns { success, reference, approvals: [{ id, kind, title, message,
|
|
31764
|
+
options, placeholder, requestedAt, expiresAt }], count, _links }. Poll it to
|
|
31765
|
+
find a session blocked on a person; answer with 'mutagent helix session answer
|
|
31766
|
+
<reference> <id> --value <text>' (or --cancel), never with session send.
|
|
31767
|
+
`)).action(async (reference) => {
|
|
31768
|
+
const isJson = getJsonFlag(root);
|
|
31769
|
+
const output = new OutputFormatter(isJson ? "json" : "table");
|
|
31770
|
+
try {
|
|
31771
|
+
const { approvals } = await listSessionApprovals(p, reference);
|
|
31772
|
+
const open = Array.isArray(approvals) ? approvals : [];
|
|
31773
|
+
if (isJson) {
|
|
31774
|
+
output.output({
|
|
31775
|
+
reference,
|
|
31776
|
+
approvals: open,
|
|
31777
|
+
count: open.length,
|
|
31778
|
+
_links: { api: sessionPath(p, reference) + "/approvals" }
|
|
31779
|
+
});
|
|
31780
|
+
return;
|
|
31781
|
+
}
|
|
31782
|
+
if (open.length === 0) {
|
|
31783
|
+
output.info(t("No open questions: the session is not waiting on anyone."));
|
|
31784
|
+
return;
|
|
31785
|
+
}
|
|
31786
|
+
output.output(open.map((approval) => ({
|
|
31787
|
+
id: approval.id,
|
|
31788
|
+
kind: approval.kind,
|
|
31789
|
+
title: approval.title,
|
|
31790
|
+
detail: questionDetail(approval),
|
|
31791
|
+
asked: new Date(approval.requestedAt).toLocaleString()
|
|
31792
|
+
})));
|
|
31793
|
+
} catch (error) {
|
|
31794
|
+
handleError(withoutSandboxIds(error), isJson);
|
|
31795
|
+
}
|
|
31796
|
+
});
|
|
31797
|
+
}
|
|
31798
|
+
function registerAnswer(session, root, p) {
|
|
31799
|
+
const t = (text) => productText(text, p);
|
|
31800
|
+
session.command("answer").description(t("Answer a question a Helix session is waiting on")).argument("<reference>", t("Session reference (hs1_...) from 'mutagent helix' or 'session list'")).argument("<id>", "The question id, from session approvals or an attach notice").option("--value <text>", "Approve with this answer (a select option, or the text)").option("--cancel", "Deny the question instead of approving it").addHelpText("after", t(`
|
|
31801
|
+
Examples:
|
|
31802
|
+
${chalk31.dim("$")} mutagent helix session answer <reference> q1 --value 'Deploy to production'
|
|
31803
|
+
${chalk31.dim("$")} mutagent helix session answer <reference> q2 # approve a confirm
|
|
31804
|
+
${chalk31.dim("$")} mutagent helix session answer <reference> q2 --cancel # deny it
|
|
31805
|
+
|
|
31806
|
+
Approve answers the question; --cancel denies it. A select takes one of its
|
|
31807
|
+
options as --value, an input or editor takes the text, and a confirm needs no
|
|
31808
|
+
value at all. The id comes from 'session approvals' or from the block attach
|
|
31809
|
+
prints when the question appears; answering from any terminal reaches the
|
|
31810
|
+
session, and the turn continues on its stream, not in this output.
|
|
31811
|
+
|
|
31812
|
+
AI Agent Directive:
|
|
31813
|
+
--json returns { success, reference, id, decision, value?, _links }. Delivery
|
|
31814
|
+
is not the continuation: watch the session's stream for it. APPROVAL_NOT_FOUND
|
|
31815
|
+
means another terminal answered it or its timeout passed — list what is still
|
|
31816
|
+
open with 'mutagent helix session approvals <reference> --json'.
|
|
31817
|
+
`)).action(async (reference, id, options) => {
|
|
31818
|
+
const isJson = getJsonFlag(root);
|
|
31819
|
+
const output = new OutputFormatter(isJson ? "json" : "table");
|
|
31820
|
+
try {
|
|
31821
|
+
if (options.cancel === true && options.value !== undefined) {
|
|
31822
|
+
throw new MutagentError("INVALID_ARGUMENTS", "--cancel denies the question, so --value would be discarded.", "Send --value to approve the question, or --cancel to deny it — one of the two.");
|
|
31823
|
+
}
|
|
31824
|
+
const answer = options.cancel === true ? { decision: "deny" } : options.value === undefined ? { decision: "approve" } : { decision: "approve", value: options.value };
|
|
31825
|
+
await answerSessionApproval(p, reference, id, answer);
|
|
31826
|
+
if (isJson) {
|
|
31827
|
+
output.output({
|
|
31828
|
+
reference,
|
|
31829
|
+
id,
|
|
31830
|
+
...answer,
|
|
31831
|
+
_links: { api: sessionPath(p, reference) + "/approvals/" + encodeURIComponent(id) }
|
|
31832
|
+
});
|
|
31833
|
+
} else {
|
|
31834
|
+
output.success(`Answered ${id} (${answer.decision}${answer.value === undefined ? "" : `: ${answer.value}`}) on ${reference}; the session continues on its stream.`);
|
|
31835
|
+
}
|
|
31836
|
+
} catch (error) {
|
|
31837
|
+
handleError(withoutSandboxIds(error), isJson);
|
|
31838
|
+
}
|
|
31839
|
+
});
|
|
31840
|
+
}
|
|
31492
31841
|
function registerCheckpoint(session, root, p) {
|
|
31493
31842
|
const t = (text) => productText(text, p);
|
|
31494
31843
|
session.command("checkpoint").description("Save a session's conversation as a checkpoint").argument("<reference>", "Session reference (hs1_...)").addOption(new Option4("--session <id>", "The session id, when the argument is a sandbox id").hideHelp()).addHelpText("after", t(`
|
|
@@ -37807,7 +38156,7 @@ function readBody(value) {
|
|
|
37807
38156
|
function isInteractive(isJson) {
|
|
37808
38157
|
return !isJson && process.env.MUTAGENT_NON_INTERACTIVE !== "true" && process.env.CI !== "true" && process.stdin.isTTY && process.stdout.isTTY;
|
|
37809
38158
|
}
|
|
37810
|
-
function
|
|
38159
|
+
function shellQuote3(value) {
|
|
37811
38160
|
return /^[\w@%+=:,./-]+$/.test(value) ? value : `'${value.replace(/'/g, `'\\''`)}'`;
|
|
37812
38161
|
}
|
|
37813
38162
|
function rerunWithYes(options) {
|
|
@@ -37823,7 +38172,7 @@ function rerunWithYes(options) {
|
|
|
37823
38172
|
];
|
|
37824
38173
|
for (const [flag, value] of flags) {
|
|
37825
38174
|
if (value !== undefined)
|
|
37826
|
-
parts.push(`--${flag}`,
|
|
38175
|
+
parts.push(`--${flag}`, shellQuote3(value));
|
|
37827
38176
|
}
|
|
37828
38177
|
parts.push("--yes", "--json");
|
|
37829
38178
|
return parts.join(" ");
|
|
@@ -39206,21 +39555,21 @@ init_sandbox_scope_error();
|
|
|
39206
39555
|
// src/commands/integrations/secret-required.ts
|
|
39207
39556
|
init_output();
|
|
39208
39557
|
import chalk66 from "chalk";
|
|
39209
|
-
function
|
|
39558
|
+
function shellQuote4(value) {
|
|
39210
39559
|
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
39211
39560
|
}
|
|
39212
39561
|
function rerunCommand(body) {
|
|
39213
|
-
const parts = [`${SOURCES3} add ${
|
|
39562
|
+
const parts = [`${SOURCES3} add ${shellQuote4(String(body.kind))}`, `--name ${shellQuote4(String(body.name))}`];
|
|
39214
39563
|
if (typeof body.host === "string")
|
|
39215
|
-
parts.push(`--host ${
|
|
39564
|
+
parts.push(`--host ${shellQuote4(body.host)}`);
|
|
39216
39565
|
if (typeof body.org === "string")
|
|
39217
|
-
parts.push(`--openobserve-org ${
|
|
39566
|
+
parts.push(`--openobserve-org ${shellQuote4(body.org)}`);
|
|
39218
39567
|
if (typeof body.stream === "string")
|
|
39219
|
-
parts.push(`--stream ${
|
|
39568
|
+
parts.push(`--stream ${shellQuote4(body.stream)}`);
|
|
39220
39569
|
if (typeof body.signal === "string")
|
|
39221
|
-
parts.push(`--signal ${
|
|
39570
|
+
parts.push(`--signal ${shellQuote4(body.signal)}`);
|
|
39222
39571
|
if (typeof body.dataset === "string")
|
|
39223
|
-
parts.push(`--dataset ${
|
|
39572
|
+
parts.push(`--dataset ${shellQuote4(body.dataset)}`);
|
|
39224
39573
|
return parts.join(" ");
|
|
39225
39574
|
}
|
|
39226
39575
|
function printSecretRequired(isJson, detail, rerun) {
|
|
@@ -45170,4 +45519,4 @@ if (process.env.CLI_VERSION) {
|
|
|
45170
45519
|
}
|
|
45171
45520
|
runCli(process.argv.slice(2), cliVersion);
|
|
45172
45521
|
|
|
45173
|
-
//# debugId=
|
|
45522
|
+
//# debugId=3B06A5F83D41F6E764756E2164756E21
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mutagent/cli",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.217",
|
|
4
4
|
"description": "Bun-native CLI for the MutagenT AI platform - auth, provider config, and lifecycle-tool installer for AI-native workflows",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
47
|
"@inquirer/prompts": "^8.7.2",
|
|
48
|
-
"@mutagent/sdk": "0.12.
|
|
48
|
+
"@mutagent/sdk": "0.12.28",
|
|
49
49
|
"@sinclair/typebox": "^0.34.0",
|
|
50
50
|
"chalk": "^5.3.0",
|
|
51
51
|
"commander": "^12.1.0",
|
|
@@ -94,5 +94,5 @@
|
|
|
94
94
|
"bugs": {
|
|
95
95
|
"url": "https://docs.mutagent.io/support"
|
|
96
96
|
},
|
|
97
|
-
"gitHead": "
|
|
97
|
+
"gitHead": "ef764b4ebf7cc639f707d2f8d7d413224139e4a0"
|
|
98
98
|
}
|