@krapht/ork 1.0.0-alpha.4 → 1.0.0-alpha.5

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.
Files changed (36) hide show
  1. package/dist/{__build_info__-1i1IwLg7.mjs → __build_info__-Dl8yTEu-.mjs} +2 -2
  2. package/dist/bin.mjs +54 -224
  3. package/dist/{clean-B1zPxq6A.mjs → clean-DYVmLkd4.mjs} +1 -1
  4. package/dist/{completion-BY-frez8.mjs → completion-BVx917Yr.mjs} +5 -5
  5. package/dist/{context-CD5TCD1D.mjs → context-CepHRoj9.mjs} +3 -3
  6. package/dist/{decide-CBhYcjwI.mjs → decide-CK_-lI5V.mjs} +3 -3
  7. package/dist/{find-BgZjVKhv.mjs → find-DWiDJ5Lc.mjs} +1 -1
  8. package/dist/{format-Creyq4U3.mjs → format-yBEAXZhk.mjs} +223 -17
  9. package/dist/{in-a-run-CsSBQQ2q.mjs → in-a-run-CKFIeMpU.mjs} +1 -1
  10. package/dist/{limits-D_HxH0CK.mjs → limits-CwzPe9wZ.mjs} +1 -1
  11. package/dist/{list-CzzpVEpM.mjs → list-BE02pCZ9.mjs} +1 -1
  12. package/dist/{mcp-DYmQt372.mjs → mcp-DMuBbUHU.mjs} +6 -6
  13. package/dist/{output-CuivUcDa.mjs → output-BhYkshYq.mjs} +9 -6
  14. package/dist/{pause-W6MzQqg8.mjs → pause-C_Q0W6iX.mjs} +4 -4
  15. package/dist/{relay-CsdoZUXJ.mjs → relay-COCvG-0I.mjs} +2 -2
  16. package/dist/{remove-DLfDjBdF.mjs → remove-RVcX1KqP.mjs} +3 -3
  17. package/dist/{send-Bw2FI_Sv.mjs → send-OVpRei_5.mjs} +3 -3
  18. package/dist/{src-CrYCkU9J.mjs → src-CxZr4Bp2.mjs} +1 -1
  19. package/dist/{src-BRB5EQyC.mjs → src-Do_MhwiV.mjs} +54 -20
  20. package/dist/{src-Bc9yaAiZ.mjs → src-HxtJOMRr.mjs} +14 -7
  21. package/dist/{src-CsjumY1t.mjs → src-QEycKJWk.mjs} +26 -12
  22. package/dist/ui/THIRD_PARTY_LICENSES.txt +594 -0
  23. package/dist/ui/assets/app-DzI77WV9.js +75 -0
  24. package/dist/ui/assets/app-pTYGrAbI.css +1 -0
  25. package/dist/ui/assets/{index-UEC5ZLYw.js → index-2G_6XIIv.js} +3 -3
  26. package/dist/ui/assets/theme-CdXvqTQ6.js +1 -0
  27. package/dist/ui/index.html +2 -2
  28. package/dist/{ui-wX050aH1.mjs → ui-Dz_qfUOz.mjs} +4 -4
  29. package/dist/{usage-C96RDH-G.mjs → usage-BcLUZ1xw.mjs} +5 -5
  30. package/dist/{watch-vdRobJil.mjs → watch-Buyn0Mhz.mjs} +6 -6
  31. package/dist/{workflows-B28q8zMY.mjs → workflows-1sntXqSd.mjs} +3 -3
  32. package/dist/{worktrees-CKDRkl6b.mjs → worktrees-B_Go_A9w.mjs} +4 -4
  33. package/package.json +1 -1
  34. package/dist/ui/assets/app-BNT0SuIz.css +0 -1
  35. package/dist/ui/assets/app-m9Gfrg_S.js +0 -67
  36. package/dist/ui/assets/theme-3xH5jxKs.js +0 -1
@@ -1,6 +1,211 @@
1
- import { ht as liveStatus } from "./src-BRB5EQyC.mjs";
2
- import { i as style, n as cost } from "./output-CuivUcDa.mjs";
1
+ import { cn as maskSecrets, ht as liveStatus } from "./src-Do_MhwiV.mjs";
2
+ import { i as style, n as cost } from "./output-BhYkshYq.mjs";
3
3
  import { createInterface } from "node:readline/promises";
4
+ // ../core/src/control/queue.ts
5
+ const PERMISSION_OPTIONS = [{
6
+ id: "allow",
7
+ label: "Allow"
8
+ }, {
9
+ id: "deny",
10
+ label: "Deny"
11
+ }];
12
+ const DENIED = "The user did not allow this tool.";
13
+ /** The deny message a session gets, with the person's words when given. */
14
+ const deniedMessage = (note) => note === void 0 ? DENIED : `${DENIED} They said: ${note}`;
15
+ /** The input fields that say what a tool will do, the first one present. */
16
+ const TOOL_INPUT_KEYS = [
17
+ "command",
18
+ "file_path",
19
+ "path",
20
+ "url",
21
+ "pattern"
22
+ ];
23
+ /** A tool's input for the decision's context: as written, cut at `max`. */
24
+ const inputContext = (input, max = 4e3) => {
25
+ const key = TOOL_INPUT_KEYS.find((k) => typeof input[k] === "string");
26
+ const text = key === void 0 ? JSON.stringify(input, void 0, 2) : String(input[key]);
27
+ return text.length > max ? `${text.slice(0, max - 1)}…` : text;
28
+ };
29
+ const createDecisionQueue = (options) => {
30
+ const { store, runId, emit } = options;
31
+ let count = 0;
32
+ /** Opens a decision and settles it with the first answer. */
33
+ const decide = (open, local, withdrawn) => {
34
+ count += 1;
35
+ const id = `${runId}-${String(count)}`;
36
+ const record = store.openDecision({
37
+ ...open,
38
+ id,
39
+ runId
40
+ });
41
+ const event = {
42
+ type: "decision.opened",
43
+ node: record.node,
44
+ decision: id,
45
+ kind: record.kind,
46
+ question: record.question,
47
+ options: record.options
48
+ };
49
+ if (record.recommended !== void 0) event.recommended = record.recommended;
50
+ if (record.owner !== void 0) event.owner = record.owner;
51
+ emit(event);
52
+ const { promise, resolve, reject } = Promise.withResolvers();
53
+ const settled = new AbortController();
54
+ /** `note` is the stored one: masked, and absent when blank. */
55
+ const finish = (answer, by, note) => {
56
+ if (settled.signal.aborted) return;
57
+ settled.abort();
58
+ const answered = {
59
+ type: "decision.answered",
60
+ node: open.node,
61
+ decision: id,
62
+ answer,
63
+ by
64
+ };
65
+ if (note !== void 0) answered.note = note;
66
+ emit(answered);
67
+ resolve(note === void 0 ? {
68
+ answer,
69
+ by
70
+ } : {
71
+ answer,
72
+ by,
73
+ note
74
+ });
75
+ };
76
+ const withdraw = () => {
77
+ if (settled.signal.aborted) return;
78
+ settled.abort();
79
+ store.withdrawDecision(id);
80
+ emit({
81
+ type: "decision.withdrawn",
82
+ node: open.node,
83
+ decision: id
84
+ });
85
+ reject(/* @__PURE__ */ new Error("the question was withdrawn"));
86
+ };
87
+ if (withdrawn?.aborted === true) withdraw();
88
+ withdrawn?.addEventListener("abort", withdraw, { once: true });
89
+ const fromStore = async () => {
90
+ try {
91
+ const closed = await options.waitFor(id, settled.signal);
92
+ if (closed.status === "answered" && closed.answer !== void 0) finish(closed.answer, closed.answeredBy ?? "decide", closed.note);
93
+ } catch {}
94
+ };
95
+ const fromTerminal = async (ask) => {
96
+ try {
97
+ const { answer, note } = await ask(settled.signal);
98
+ if (store.answerDecision(id, answer, "terminal", note)) finish(answer, "terminal", store.decision(id)?.note);
99
+ } catch {}
100
+ };
101
+ fromStore();
102
+ if (local !== void 0) fromTerminal(local);
103
+ return promise;
104
+ };
105
+ return {
106
+ ask: (question) => {
107
+ const open = {
108
+ node: question.nodeId,
109
+ kind: "question",
110
+ question: question.question,
111
+ options: question.options
112
+ };
113
+ if (question.recommended !== void 0) open.recommended = question.recommended;
114
+ if (question.context !== void 0) open.context = question.context;
115
+ if (question.document !== void 0) open.document = question.document;
116
+ if (question.owner !== void 0) open.owner = question.owner;
117
+ const { local } = options;
118
+ return decide(open, local === void 0 ? void 0 : (signal) => {
119
+ let note;
120
+ return local.ask({
121
+ ...question,
122
+ question: maskSecrets(question.question),
123
+ ...question.context === void 0 ? {} : { context: maskSecrets(question.context) },
124
+ ...question.document === void 0 ? {} : { document: maskSecrets(question.document) },
125
+ signal,
126
+ onNote: (given) => {
127
+ note = given;
128
+ }
129
+ }).then((answer) => note === void 0 ? { answer } : {
130
+ answer,
131
+ note
132
+ });
133
+ }, question.signal).then(({ answer, note }) => {
134
+ if (note !== void 0) question.onNote?.(note);
135
+ return answer;
136
+ });
137
+ },
138
+ settled: (question) => {
139
+ count += 1;
140
+ const id = `${runId}-${String(count)}`;
141
+ const { owner } = question;
142
+ const record = store.openDecision({
143
+ id,
144
+ runId,
145
+ node: question.nodeId,
146
+ kind: "question",
147
+ question: question.question,
148
+ options: question.options,
149
+ owner,
150
+ ...question.recommended === void 0 ? {} : { recommended: question.recommended },
151
+ ...question.context === void 0 ? {} : { context: question.context }
152
+ });
153
+ emit({
154
+ type: "decision.opened",
155
+ node: record.node,
156
+ decision: id,
157
+ kind: "question",
158
+ question: record.question,
159
+ options: record.options,
160
+ owner,
161
+ ...record.recommended === void 0 ? {} : { recommended: record.recommended }
162
+ });
163
+ store.answerDecision(id, question.answer, "owner", question.note);
164
+ const note = store.decision(id)?.note;
165
+ emit({
166
+ type: "decision.answered",
167
+ node: record.node,
168
+ decision: id,
169
+ answer: question.answer,
170
+ by: "owner",
171
+ owner,
172
+ ...note === void 0 ? {} : { note }
173
+ });
174
+ },
175
+ permit: async (request) => {
176
+ const { local } = options;
177
+ let localMessage = DENIED;
178
+ const { answer, note } = await decide({
179
+ node: request.nodeId,
180
+ kind: "permission",
181
+ question: `Allow ${request.toolName}?`,
182
+ options: PERMISSION_OPTIONS,
183
+ context: inputContext(request.input)
184
+ }, local === void 0 ? void 0 : (signal) => {
185
+ let said;
186
+ return local.permit({
187
+ ...request,
188
+ signal,
189
+ onNote: (given) => {
190
+ said = given;
191
+ }
192
+ }).then((result) => {
193
+ if (!result.allow) localMessage = result.message;
194
+ const choice = result.allow ? "allow" : "deny";
195
+ return said === void 0 ? { answer: choice } : {
196
+ answer: choice,
197
+ note: said
198
+ };
199
+ });
200
+ });
201
+ if (note !== void 0) request.onNote?.(note);
202
+ return answer === "allow" ? { allow: true } : {
203
+ allow: false,
204
+ message: note === void 0 ? localMessage : deniedMessage(note)
205
+ };
206
+ }
207
+ };
208
+ };
4
209
  // src/human.ts
5
210
  /**
6
211
  * @file The terminal `HumanChannel`: answers `ask_user` questions, gates and
@@ -10,18 +215,14 @@ import { createInterface } from "node:readline/promises";
10
215
  * answerer, racing `ork decide`.
11
216
  */
12
217
  const PREVIEW = 160;
218
+ /** A document longer than this is offered (`d`) rather than printed. */
219
+ const SHORT_DOCUMENT_LINES = 40;
13
220
  const NO_ONE_TO_PERMIT = "No one is at the terminal to approve this tool, so it is not allowed in this run.";
14
221
  const NOTE_PROMPT = " Note for the run (optional, Enter to skip): ";
15
222
  const NO_ONE_TO_ASK = "no one (nobody is at the terminal; use your best judgment and say so in your report)";
16
223
  /** One line that shows what a tool is about to do. */
17
224
  const previewInput = (toolName, input) => {
18
- const key = [
19
- "command",
20
- "file_path",
21
- "path",
22
- "url",
23
- "pattern"
24
- ].find((k) => typeof input[k] === "string");
225
+ const key = TOOL_INPUT_KEYS.find((k) => typeof input[k] === "string");
25
226
  const line = (key === void 0 ? JSON.stringify(input) : `${key}: ${String(input[key])}`).replaceAll(/\s+/gu, " ").trim();
26
227
  return line.length > PREVIEW ? `${toolName} ${line.slice(0, 159)}…` : `${toolName} ${line}`;
27
228
  };
@@ -63,11 +264,13 @@ const createTerminalHuman = (io) => {
63
264
  const note = (await prompt(NOTE_PROMPT, signal)).trim();
64
265
  if (note !== "") onNote(note);
65
266
  };
66
- const choose = async (hint, options, recommended, signal) => {
67
- const picked = pickOption(await prompt(` Choose${hint}: `, signal), options, recommended);
267
+ const choose = async (hint, options, recommended, signal, document) => {
268
+ const answer = await prompt(` Choose${hint}: `, signal);
269
+ const picked = pickOption(answer, options, recommended);
68
270
  if (picked !== void 0) return picked;
69
- say(style("dim", " Type an option's number or id."));
70
- return choose(hint, options, recommended, signal);
271
+ if (document !== void 0 && answer.trim() === "d") say(document);
272
+ else say(style("dim", " Type an option's number or id."));
273
+ return choose(hint, options, recommended, signal, document);
71
274
  };
72
275
  const oneAtATime = (work) => {
73
276
  const result = turn.then(work);
@@ -93,10 +296,12 @@ const createTerminalHuman = (io) => {
93
296
  message: "The user did not allow this tool."
94
297
  };
95
298
  }),
96
- ask: ({ nodeId, question, options, recommended, context, signal, onNote }) => oneAtATime(async () => {
299
+ ask: ({ nodeId, question, options, recommended, context, document, signal, onNote }) => oneAtATime(async () => {
97
300
  if (signal?.aborted === true) throw new Error("the question was withdrawn");
301
+ const documentLines = document?.trimEnd().split("\n").length ?? 0;
302
+ if (document !== void 0 && documentLines <= SHORT_DOCUMENT_LINES) say(document);
98
303
  say(`${style(["yellow", "bold"], "?")} ${style("bold", nodeId)} asks: ${question}`);
99
- if (context !== void 0) say(style("dim", ` ${context}`));
304
+ if (context !== void 0) say(style("dim", ` ${context.replaceAll("\n", "\n ")}`));
100
305
  for (const [index, option] of options.entries()) say(optionLine(option, index, recommended));
101
306
  if (!io.interactive) {
102
307
  const answer = recommended ?? NO_ONE_TO_ASK;
@@ -105,7 +310,8 @@ const createTerminalHuman = (io) => {
105
310
  }
106
311
  const fallback = options.findIndex((o) => o.id === recommended);
107
312
  const hint = fallback === -1 ? "" : ` [${String(fallback + 1)}]`;
108
- const picked = await choose(hint, options, recommended, signal);
313
+ if (documentLines > SHORT_DOCUMENT_LINES) say(` Document: ${String(documentLines)} lines. Type d to read it.`);
314
+ const picked = await choose(hint, options, recommended, signal, document);
109
315
  await takeNote(onNote, signal);
110
316
  return picked;
111
317
  }),
@@ -424,4 +630,4 @@ const formatTree = (roots) => {
424
630
  return `${text.padEnd(label)} ${style("dim", node.type.padEnd(type))} ${state}${usage}`;
425
631
  });
426
632
  };
427
- export { formatRunEvent as a, formatEvent as i, formatRuns as n, createTerminalHuman as o, formatTree as r, ago as t };
633
+ export { formatRunEvent as a, formatEvent as i, formatRuns as n, createTerminalHuman as o, formatTree as r, createDecisionQueue as s, ago as t };
@@ -1,4 +1,4 @@
1
- import { p as runMarkerIn } from "./src-Bc9yaAiZ.mjs";
1
+ import { p as runMarkerIn } from "./src-HxtJOMRr.mjs";
2
2
  // src/commands/in-a-run.ts
3
3
  /**
4
4
  * @file The commands that act as the user refuse a program or an agent in a run
@@ -1,4 +1,4 @@
1
- import { a as limitsNow, i as NOT_STARTED, o as limitsReport, r as capsHere } from "./src-BRB5EQyC.mjs";
1
+ import { a as limitsNow, i as NOT_STARTED, o as limitsReport, r as capsHere } from "./src-Do_MhwiV.mjs";
2
2
  // src/run/limits.ts
3
3
  /**
4
4
  * @file The limits across runs that apply here (RFC-007): the user's settings
@@ -1,4 +1,4 @@
1
- import { A as gitOk, j as lines, k as git } from "./src-BRB5EQyC.mjs";
1
+ import { A as gitOk, j as lines, k as git } from "./src-Do_MhwiV.mjs";
2
2
  import { existsSync } from "node:fs";
3
3
  // ../core/src/worktree/list.ts
4
4
  /**
@@ -1,8 +1,8 @@
1
- import { h as isNewer } from "./src-BRB5EQyC.mjs";
2
- import { n as ORK_VERSION } from "./__build_info__-1i1IwLg7.mjs";
3
- import { a as openMcp, c as refused, o as tool, s as answer } from "./src-CsjumY1t.mjs";
4
- import { p as runMarkerIn } from "./src-Bc9yaAiZ.mjs";
5
- import { n as explainNode } from "./context-CD5TCD1D.mjs";
1
+ import { h as isNewer } from "./src-Do_MhwiV.mjs";
2
+ import { n as ORK_VERSION } from "./__build_info__-Dl8yTEu-.mjs";
3
+ import { a as openMcp, c as refused, o as tool, s as answer } from "./src-QEycKJWk.mjs";
4
+ import { p as runMarkerIn } from "./src-HxtJOMRr.mjs";
5
+ import { n as explainNode } from "./context-CepHRoj9.mjs";
6
6
  import { z } from "zod";
7
7
  import { defineCommand } from "citty";
8
8
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
@@ -43,7 +43,7 @@ const orkMcpServer = (options = {}) => {
43
43
  ...options.script === void 0 ? {} : { script: options.script }
44
44
  });
45
45
  const all = [...opened.tools, explainTool(opened, options.home)];
46
- const tools = options.expects !== void 0 && isNewer(options.expects, "1.0.0-alpha.4") ? tooOld(all, options.expects) : all;
46
+ const tools = options.expects !== void 0 && isNewer(options.expects, "1.0.0-alpha.5") ? tooOld(all, options.expects) : all;
47
47
  const server = new McpServer({
48
48
  name: "ork",
49
49
  version: ORK_VERSION
@@ -1,4 +1,4 @@
1
- import { $ as text, $t as maskReplacer, Ht as FEEDBACK_SEVERITIES, Q as inTransaction, Ut as FEEDBACK_TARGETS, Vt as FEEDBACK_KINDS, X as usageQueries, Y as backfillUsage, Z as eventOf, en as maskSecrets, yn as PROJECT_MODES, yt as ownProcessStart } from "./src-BRB5EQyC.mjs";
1
+ import { $ as text, Ht as FEEDBACK_SEVERITIES, Q as inTransaction, Ut as FEEDBACK_TARGETS, Vt as FEEDBACK_KINDS, X as usageQueries, Y as backfillUsage, Z as eventOf, cn as maskSecrets, sn as maskReplacer, yn as PROJECT_MODES, yt as ownProcessStart } from "./src-Do_MhwiV.mjs";
2
2
  import path from "node:path";
3
3
  import { mkdirSync } from "node:fs";
4
4
  import { homedir } from "node:os";
@@ -70,6 +70,7 @@ const decisionOf = (row) => {
70
70
  const optional = {
71
71
  recommended: text(row, "recommended"),
72
72
  context: text(row, "context"),
73
+ document: text(row, "document"),
73
74
  answer: text(row, "answer"),
74
75
  answeredAt: text(row, "answered_at"),
75
76
  note: text(row, "note"),
@@ -81,8 +82,8 @@ const decisionOf = (row) => {
81
82
  return record;
82
83
  };
83
84
  const decisionQueries = (db) => {
84
- const insert = db.prepare(`INSERT INTO decisions (id, run_id, node, kind, question, options, recommended, context, owner, opened_at, status)
85
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'open')`);
85
+ const insert = db.prepare(`INSERT INTO decisions (id, run_id, node, kind, question, options, recommended, context, document, owner, opened_at, status)
86
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'open')`);
86
87
  const answer = db.prepare(`UPDATE decisions SET status = 'answered', answer = ?, answered_by = ?, answered_at = ?, note = ?
87
88
  WHERE id = ? AND status = 'open'`);
88
89
  const withdraw = db.prepare("UPDATE decisions SET status = 'withdrawn', answered_at = ? WHERE id = ? AND status = 'open'");
@@ -102,9 +103,10 @@ const decisionQueries = (db) => {
102
103
  const masked = {
103
104
  ...decision,
104
105
  question: maskSecrets(decision.question),
105
- ...decision.context === void 0 ? {} : { context: maskSecrets(decision.context) }
106
+ ...decision.context === void 0 ? {} : { context: maskSecrets(decision.context) },
107
+ ...decision.document === void 0 ? {} : { document: maskSecrets(decision.document) }
106
108
  };
107
- insert.run(masked.id, masked.runId, masked.node, masked.kind, masked.question, JSON.stringify(masked.options), masked.recommended ?? null, masked.context ?? null, masked.owner ?? null, openedAt);
109
+ insert.run(masked.id, masked.runId, masked.node, masked.kind, masked.question, JSON.stringify(masked.options), masked.recommended ?? null, masked.context ?? null, masked.document ?? null, masked.owner ?? null, openedAt);
108
110
  return {
109
111
  ...masked,
110
112
  openedAt,
@@ -731,7 +733,8 @@ const MIGRATIONS = [
731
733
  `ALTER TABLE runs ADD COLUMN skipped INTEGER;
732
734
  ALTER TABLE runs ADD COLUMN failures_handled INTEGER;`,
733
735
  `ALTER TABLE decisions ADD COLUMN note TEXT;`,
734
- `ALTER TABLE decisions ADD COLUMN owner TEXT;`
736
+ `ALTER TABLE decisions ADD COLUMN owner TEXT;`,
737
+ `ALTER TABLE decisions ADD COLUMN document TEXT;`
735
738
  ];
736
739
  const versionOf = (db) => Number(db.prepare("PRAGMA user_version").get()?.["user_version"] ?? 0);
737
740
  /**
@@ -1,7 +1,7 @@
1
- import { G as requestContinue, K as requestPause } from "./src-BRB5EQyC.mjs";
2
- import { t as findRun } from "./find-BgZjVKhv.mjs";
3
- import { a as write, c as openStore, i as style, o as writeErr } from "./output-CuivUcDa.mjs";
4
- import { r as continueRefusal } from "./in-a-run-CsSBQQ2q.mjs";
1
+ import { G as requestContinue, K as requestPause } from "./src-Do_MhwiV.mjs";
2
+ import { t as findRun } from "./find-DWiDJ5Lc.mjs";
3
+ import { a as write, c as openStore, i as style, o as writeErr } from "./output-BhYkshYq.mjs";
4
+ import { r as continueRefusal } from "./in-a-run-CKFIeMpU.mjs";
5
5
  import { defineCommand } from "citty";
6
6
  // src/commands/pause.ts
7
7
  /**
@@ -1,5 +1,5 @@
1
- import { B as linkNodes, L as addressOf, U as unlinkNodes } from "./src-BRB5EQyC.mjs";
2
- import { a as write, c as openStore, i as style, o as writeErr } from "./output-CuivUcDa.mjs";
1
+ import { B as linkNodes, L as addressOf, U as unlinkNodes } from "./src-Do_MhwiV.mjs";
2
+ import { a as write, c as openStore, i as style, o as writeErr } from "./output-BhYkshYq.mjs";
3
3
  import { defineCommand } from "citty";
4
4
  // src/commands/relay.ts
5
5
  /**
@@ -1,6 +1,6 @@
1
- import { ht as liveStatus, j as lines, k as git } from "./src-BRB5EQyC.mjs";
2
- import { n as listOrkWorktrees, t as createdWorktrees } from "./list-CzzpVEpM.mjs";
3
- import { i as keyOf, n as applyStep, r as cleanStep, s as stepPrunes } from "./clean-B1zPxq6A.mjs";
1
+ import { ht as liveStatus, j as lines, k as git } from "./src-Do_MhwiV.mjs";
2
+ import { n as listOrkWorktrees, t as createdWorktrees } from "./list-BE02pCZ9.mjs";
3
+ import { i as keyOf, n as applyStep, r as cleanStep, s as stepPrunes } from "./clean-DYVmLkd4.mjs";
4
4
  // ../core/src/worktree/words.ts
5
5
  /**
6
6
  * @file The plain words for what `ork worktrees` shows, in one place, so the
@@ -1,6 +1,6 @@
1
- import { cn as wakeRun, ht as liveStatus, kt as scriptContactsOf, on as socketPath } from "./src-BRB5EQyC.mjs";
2
- import { c as openStore, o as writeErr } from "./output-CuivUcDa.mjs";
3
- import { u as inheritedScrubber } from "./src-Bc9yaAiZ.mjs";
1
+ import { ht as liveStatus, in as socketPath, kt as scriptContactsOf, on as wakeRun } from "./src-Do_MhwiV.mjs";
2
+ import { c as openStore, o as writeErr } from "./output-BhYkshYq.mjs";
3
+ import { u as inheritedScrubber } from "./src-HxtJOMRr.mjs";
4
4
  import { defineCommand } from "citty";
5
5
  // ../core/src/remote/send.ts
6
6
  /**
@@ -1,4 +1,4 @@
1
- import { Ht as FEEDBACK_SEVERITIES, Ut as FEEDBACK_TARGETS, Vt as FEEDBACK_KINDS, pt as parseReport } from "./src-BRB5EQyC.mjs";
1
+ import { Ht as FEEDBACK_SEVERITIES, Ut as FEEDBACK_TARGETS, Vt as FEEDBACK_KINDS, pt as parseReport } from "./src-Do_MhwiV.mjs";
2
2
  import { z } from "zod";
3
3
  import { randomBytes, timingSafeEqual } from "node:crypto";
4
4
  import { createInterface } from "node:readline";
@@ -661,6 +661,7 @@ const HumanGateNodeSchema = z.strictObject({
661
661
  ...base,
662
662
  ...notifyFields,
663
663
  question: z.string(),
664
+ document: z.string().optional(),
664
665
  options: z.array(DecisionOptionSchema).min(2),
665
666
  recommended: z.string().optional(),
666
667
  timeout: DurationSchema.optional(),
@@ -3601,6 +3602,26 @@ const writePlugin = (stack, dir, description) => {
3601
3602
  }
3602
3603
  return dir;
3603
3604
  };
3605
+ // ../core/src/store/mask.ts
3606
+ /**
3607
+ * @file Masks secrets before anything is stored: tokens and keys in common
3608
+ * formats, anywhere in an event's strings. The store and feedback records use
3609
+ * the same patterns; the terminal is not masked (it is the user's own).
3610
+ */
3611
+ const PATTERNS = [
3612
+ /sk-ant-[\w-]{20,}/gu,
3613
+ /\bgh[opsur]_[A-Za-z0-9]{20,}/gu,
3614
+ /\bgithub_pat_\w{20,}/gu,
3615
+ /\b(?:AKIA|ASIA)[0-9A-Z]{16}\b/gu,
3616
+ /\bxox[abprs]-[\w-]{10,}/gu,
3617
+ /-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/gu,
3618
+ /(?<=\bbearer\s+)[\w.~+/-]{20,}=*/giu,
3619
+ /(?<=aws_secret_access_key\s*[=:]\s*)\S+/giu
3620
+ ];
3621
+ const MASK = "[masked]";
3622
+ const maskSecrets = (text) => PATTERNS.reduce((out, pattern) => out.replaceAll(pattern, MASK), text);
3623
+ /** A `JSON.stringify` replacer that masks every string. */
3624
+ const maskReplacer = (_key, value) => typeof value === "string" ? maskSecrets(value) : value;
3604
3625
  // ../core/src/control/socket.ts
3605
3626
  /**
3606
3627
  * @file The per-run local socket (`~/.ork/sockets/<run>.sock`): the fast path
@@ -4223,27 +4244,39 @@ const stringify$1 = (value) => {
4223
4244
  if (typeof value === "number" || typeof value === "boolean") return String(value);
4224
4245
  return JSON.stringify(value);
4225
4246
  };
4226
- const renderTemplate = (parts, scope) => parts.map((part) => typeof part === "string" ? part : stringify$1(evaluate(part.expr, scope))).join("");
4227
- // ../core/src/store/mask.ts
4247
+ const isContainer = (value) => value !== null && typeof value === "object";
4248
+ const depthOf = (value) => isContainer(value) ? 1 + Math.max(0, ...Object.values(value).map((item) => depthOf(item))) : 0;
4249
+ /** Keeps a multi-line value inside its list item or `key:` line. */
4250
+ const hang = (text) => text.replaceAll("\n", "\n ");
4251
+ /** A value at most two levels deep as Markdown list items and `key: value`. */
4252
+ const listed$2 = (value) => {
4253
+ if (!isContainer(value)) return stringify$1(value);
4254
+ if (Array.isArray(value)) return value.map((item) => `- ${hang(listed$2(item))}`).join("\n");
4255
+ return Object.entries(value).map(([key, item]) => isContainer(item) ? `${key}:\n ${hang(listed$2(item))}` : `${key}: ${hang(stringify$1(item))}`).join("\n");
4256
+ };
4228
4257
  /**
4229
- * @file Masks secrets before anything is stored: tokens and keys in common
4230
- * formats, anywhere in an event's strings. The store and feedback records use
4231
- * the same patterns; the terminal is not masked (it is the user's own).
4258
+ * A value as a person reads it: Markdown, never a line of raw JSON. `before` is
4259
+ * the text so far: a list or object starts on its own line, after a blank one
4260
+ * when text shares its line.
4232
4261
  */
4233
- const PATTERNS = [
4234
- /sk-ant-[\w-]{20,}/gu,
4235
- /\bgh[opsur]_[A-Za-z0-9]{20,}/gu,
4236
- /\bgithub_pat_\w{20,}/gu,
4237
- /\b(?:AKIA|ASIA)[0-9A-Z]{16}\b/gu,
4238
- /\bxox[abprs]-[\w-]{10,}/gu,
4239
- /-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/gu,
4240
- /(?<=\bbearer\s+)[\w.~+/-]{20,}=*/giu,
4241
- /(?<=aws_secret_access_key\s*[=:]\s*)\S+/giu
4242
- ];
4243
- const MASK = "[masked]";
4244
- const maskSecrets = (text) => PATTERNS.reduce((out, pattern) => out.replaceAll(pattern, MASK), text);
4245
- /** A `JSON.stringify` replacer that masks every string. */
4246
- const maskReplacer = (_key, value) => typeof value === "string" ? maskSecrets(value) : value;
4262
+ const readableText = (value, before) => {
4263
+ if (!isContainer(value)) return stringify$1(value);
4264
+ if (depthOf(value) <= 2) {
4265
+ const list = listed$2(value);
4266
+ return list === "" || before === "" || before.endsWith("\n") ? list : `\n\n${list}`;
4267
+ }
4268
+ const code = JSON.stringify(value, void 0, 2).replaceAll(/^/gmu, " ");
4269
+ return before === "" || before.endsWith("\n\n") ? code : `\n\n${code}`;
4270
+ };
4271
+ /**
4272
+ * Fills a parsed template. `readable` is for text a person reads (a gate's
4273
+ * `question` and `document`); scripts and prompts keep JSON (ADR-047).
4274
+ */
4275
+ const renderTemplate = (parts, scope, { readable = false } = {}) => parts.reduce((text, part) => {
4276
+ if (typeof part === "string") return text + part;
4277
+ const value = evaluate(part.expr, scope);
4278
+ return text + (readable ? readableText(value, text) : stringify$1(value));
4279
+ }, "");
4247
4280
  // ../core/src/feedback/fingerprint.ts
4248
4281
  /**
4249
4282
  * @file Feedback fingerprints: records about the same thing (same target, kind,
@@ -5893,6 +5926,7 @@ const TEMPLATE_FIELDS = [
5893
5926
  "command",
5894
5927
  "cwd",
5895
5928
  "question",
5929
+ "document",
5896
5930
  "workflow",
5897
5931
  "project",
5898
5932
  "ref"
@@ -13140,4 +13174,4 @@ const createWorkspace = (options) => {
13140
13174
  close
13141
13175
  };
13142
13176
  };
13143
- export { text as $, isToolNode as $n, maskReplacer as $t, gitOk as A, projectRootOf as An, runScope as At, linkNodes as B, readDocument as Bn, serversIn as Bt, workflowTextHash as C, placeFiles as Cn, walkChecks as Ct, inspectProject as D, shownPath as Dn, userMessageText as Dt, BRANCH_PREFIX as E, runDirOf as En, MESSAGE_LIMIT as Et, declaredProblem as F, writePlainFile as Fn, PROJECT_FILE as Ft, requestContinue as G, EdgeInputSchema as Gn, planPost as Gt, tellNode as H, parseWorkflow as Hn, FEEDBACK_SEVERITIES as Ht, createCalls as I, skillsIn as In, gitRulesOf as It, tellLiveRun as J, expandEdge as Jn, publishPost as Jt, requestPause as K, WorkflowSchema as Kn, runGh as Kt, addressOf as L, WorkflowEditSchema as Ln, loadProject as Lt, clearGitLocalEnv as M, estimateTokens as Mn, envReadsOf as Mt, parseInputPairs as N, guardedBase as Nn, nodeSays as Nt, removeWorktree as O, workflowDirOf as On, heldTimeout as Ot, resolveInputs as P, plainPathInside as Pn, declaredOutputOf as Pt, inTransaction as Q, isClaudeNode as Qn, targetOf as Qt, endLink as R, applyEdits as Rn, MCP_FILE as Rt, readProjectTriggers as S, newProjectId as Sn, checkWorkflowText as St, watchingPid as T, projectsHome as Tn, startRun as Tt, unlinkNodes as U, serializeWorkflow as Un, FEEDBACK_TARGETS as Ut, peerOf as V, renderJsonSchema as Vn, FEEDBACK_KINDS as Vt, flattenTree as W, markValue as Wn, DISCUSSIONS as Wt, usageQueries as X, NodeSchema as Xn, fingerprint as Xt, backfillUsage as Y, settingsOf as Yn, createFeedback as Yt, eventOf as Z, beforeOf as Zn, normalizeTitle as Zt, lateTriggers as _, catalogAgent as _n, runStatus as _t, limitsNow as a, serveRun as an, integrationFor as ar, DETACHED_ENV as at, triggerStanding as b, hasInRepoFiles as bn, processAlive as bt, usd as c, wakeRun as cn, CLAUDE_DRIVERS as cr, explainWorkflowText as ct, runningRunIds as d, planMoveInto as dn, experimentsOn as dr, namingNoDriver as dt, maskSecrets as en, programsRunBy as er, adoptedCalls as et, runningScript as f, linkTools as fn, knownExperiments as fr, sessionDriver as ft, parseVersion as g, deleteAgent as gn, pausedOf as gt, isNewer as h, createAgent as hn, liveStatus as ht, NOT_STARTED as i, readCondition as in, TOOL_PREFIX as ir, replayLoopAnswers as it, lines as j, LESSON_CAP_TOKENS as jn, calledWorkflows as jt, git as k, inside as kn, scriptContactsOf as kt, planUpgrade as l, planAgentMigration as ln, firstLine as lr, explainSession as lt, compareVersions as m, toolsInUse as mn, reportSchemaFor as mt, resolveBase as n, parseTemplate as nn, timeoutMs as nr, resumable as nt, limitsReport as o, socketPath as on, AGENT_TOOL_LAYOUTS as or, detachRun as ot, detectInstaller as p, toolLinks as pn, parseReport as pt, requestStop as q, CheckReportSchema as qn, sendPost as qt, capsHere as r, renderTemplate as rn, INTEGRATIONS as rr, createReplay as rt, planReason as s, socketRoom as sn, ORK_LAYOUT as sr, followEvents as st, createWorkspace as t, evaluate as tn, NodeIdSchema as tr, detachResume as tt, releaseNotes as u, applySteps as un, EXPERIMENTS as ur, planNodeSession as ut, triggersReport as v, loadAgents as vn, runTree as vt, takeWatchLock as w, placeFor as wn, walkSummary as wt, declaredTriggers as x, inRepoPlace as xn, stillTheSameProcess as xt, createWatcher as y, PROJECT_MODES as yn, ownProcessStart as yt, endLinksOf as z, newWorkflowText as zn, mcpNotText as zt };
13177
+ export { text as $, isToolNode as $n, evaluate as $t, gitOk as A, projectRootOf as An, runScope as At, linkNodes as B, readDocument as Bn, serversIn as Bt, workflowTextHash as C, placeFiles as Cn, walkChecks as Ct, inspectProject as D, shownPath as Dn, userMessageText as Dt, BRANCH_PREFIX as E, runDirOf as En, MESSAGE_LIMIT as Et, declaredProblem as F, writePlainFile as Fn, PROJECT_FILE as Ft, requestContinue as G, EdgeInputSchema as Gn, planPost as Gt, tellNode as H, parseWorkflow as Hn, FEEDBACK_SEVERITIES as Ht, createCalls as I, skillsIn as In, gitRulesOf as It, tellLiveRun as J, expandEdge as Jn, publishPost as Jt, requestPause as K, WorkflowSchema as Kn, runGh as Kt, addressOf as L, WorkflowEditSchema as Ln, loadProject as Lt, clearGitLocalEnv as M, estimateTokens as Mn, envReadsOf as Mt, parseInputPairs as N, guardedBase as Nn, nodeSays as Nt, removeWorktree as O, workflowDirOf as On, heldTimeout as Ot, resolveInputs as P, plainPathInside as Pn, declaredOutputOf as Pt, inTransaction as Q, isClaudeNode as Qn, targetOf as Qt, endLink as R, applyEdits as Rn, MCP_FILE as Rt, readProjectTriggers as S, newProjectId as Sn, checkWorkflowText as St, watchingPid as T, projectsHome as Tn, startRun as Tt, unlinkNodes as U, serializeWorkflow as Un, FEEDBACK_TARGETS as Ut, peerOf as V, renderJsonSchema as Vn, FEEDBACK_KINDS as Vt, flattenTree as W, markValue as Wn, DISCUSSIONS as Wt, usageQueries as X, NodeSchema as Xn, fingerprint as Xt, backfillUsage as Y, settingsOf as Yn, createFeedback as Yt, eventOf as Z, beforeOf as Zn, normalizeTitle as Zt, lateTriggers as _, catalogAgent as _n, runStatus as _t, limitsNow as a, socketRoom as an, integrationFor as ar, DETACHED_ENV as at, triggerStanding as b, hasInRepoFiles as bn, processAlive as bt, usd as c, maskSecrets as cn, CLAUDE_DRIVERS as cr, explainWorkflowText as ct, runningRunIds as d, planMoveInto as dn, experimentsOn as dr, namingNoDriver as dt, parseTemplate as en, programsRunBy as er, adoptedCalls as et, runningScript as f, linkTools as fn, knownExperiments as fr, sessionDriver as ft, parseVersion as g, deleteAgent as gn, pausedOf as gt, isNewer as h, createAgent as hn, liveStatus as ht, NOT_STARTED as i, socketPath as in, TOOL_PREFIX as ir, replayLoopAnswers as it, lines as j, LESSON_CAP_TOKENS as jn, calledWorkflows as jt, git as k, inside as kn, scriptContactsOf as kt, planUpgrade as l, planAgentMigration as ln, firstLine as lr, explainSession as lt, compareVersions as m, toolsInUse as mn, reportSchemaFor as mt, resolveBase as n, readCondition as nn, timeoutMs as nr, resumable as nt, limitsReport as o, wakeRun as on, AGENT_TOOL_LAYOUTS as or, detachRun as ot, detectInstaller as p, toolLinks as pn, parseReport as pt, requestStop as q, CheckReportSchema as qn, sendPost as qt, capsHere as r, serveRun as rn, INTEGRATIONS as rr, createReplay as rt, planReason as s, maskReplacer as sn, ORK_LAYOUT as sr, followEvents as st, createWorkspace as t, renderTemplate as tn, NodeIdSchema as tr, detachResume as tt, releaseNotes as u, applySteps as un, EXPERIMENTS as ur, planNodeSession as ut, triggersReport as v, loadAgents as vn, runTree as vt, takeWatchLock as w, placeFor as wn, walkSummary as wt, declaredTriggers as x, inRepoPlace as xn, stillTheSameProcess as xt, createWatcher as y, PROJECT_MODES as yn, ownProcessStart as yt, endLinksOf as z, newWorkflowText as zn, mcpNotText as zt };
@@ -1,4 +1,4 @@
1
- import { $n as isToolNode, At as runScope, Bt as serversIn, F as declaredProblem, Mt as envReadsOf, Ot as heldTimeout, Pt as declaredOutputOf, Rt as MCP_FILE, Wn as markValue, Zn as beforeOf, ar as integrationFor, er as programsRunBy, ft as sessionDriver, ir as TOOL_PREFIX, jt as calledWorkflows, mt as reportSchemaFor, nn as parseTemplate, nr as timeoutMs, rn as renderTemplate, rr as INTEGRATIONS, s as planReason, tn as evaluate, ut as planNodeSession, zt as mcpNotText } from "./src-BRB5EQyC.mjs";
1
+ import { $n as isToolNode, $t as evaluate, At as runScope, Bt as serversIn, F as declaredProblem, Mt as envReadsOf, Ot as heldTimeout, Pt as declaredOutputOf, Rt as MCP_FILE, Wn as markValue, Zn as beforeOf, ar as integrationFor, en as parseTemplate, er as programsRunBy, ft as sessionDriver, ir as TOOL_PREFIX, jt as calledWorkflows, mt as reportSchemaFor, nr as timeoutMs, rr as INTEGRATIONS, s as planReason, tn as renderTemplate, ut as planNodeSession, zt as mcpNotText } from "./src-Do_MhwiV.mjs";
2
2
  import path from "node:path";
3
3
  import { accessSync, constants, lstatSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
4
4
  import { spawn } from "node:child_process";
@@ -1689,10 +1689,11 @@ const claudeExecutor = (options) => async (context) => {
1689
1689
  };
1690
1690
  // ../nodes/src/gate.ts
1691
1691
  /**
1692
- * @file The `human.gate` node type: asks the human the rendered question,
1693
- * outputs their choice with the note they gave, and answers with the
1694
- * `default` when the `timeout` passes. The timeout counts only while the run
1695
- * and the gate are not paused (ADR-041).
1692
+ * @file The `human.gate` node type: asks the human the rendered question, with
1693
+ * its document if it has one, both in readable text (no raw JSON), outputs
1694
+ * their choice with the note they gave, and answers with the `default` when
1695
+ * the `timeout` passes. The timeout counts only while the run and the gate
1696
+ * are not paused (ADR-041).
1696
1697
  */
1697
1698
  /** A promise that never settles: the losing side of a race. */
1698
1699
  const NEVER = new Promise(() => {});
@@ -1714,8 +1715,13 @@ const gateExecutor = async (context) => {
1714
1715
  output: {},
1715
1716
  error: `${context.id} is not a gate`
1716
1717
  };
1717
- const template = parseTemplate(node.question);
1718
- const question = template.ok ? renderTemplate(template.parts, runScope(context.inputs, context.outputs, context.runId)) : node.question;
1718
+ const scope = runScope(context.inputs, context.outputs, context.runId);
1719
+ const render = (text) => {
1720
+ const template = parseTemplate(text);
1721
+ return template.ok ? renderTemplate(template.parts, scope, { readable: true }) : text;
1722
+ };
1723
+ const question = render(node.question);
1724
+ const document = render(node.document ?? "");
1719
1725
  const options = node.options.map((o) => choiceOf(o));
1720
1726
  const withdraw = new AbortController();
1721
1727
  context.signal.addEventListener("abort", () => {
@@ -1732,6 +1738,7 @@ const gateExecutor = async (context) => {
1732
1738
  }
1733
1739
  };
1734
1740
  if (node.recommended !== void 0) asked.recommended = node.recommended;
1741
+ if (document.trim() !== "") asked.document = document;
1735
1742
  const answer = context.human.ask(asked).then((choice) => ({
1736
1743
  choice,
1737
1744
  answeredBy: "user",