@corenel/term 0.3.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -4,7 +4,7 @@ Corenel's terminal face: a headless chat controller and the [Ink](https://github
4
4
  views that render it. Published so [`@corenel/cli`](https://www.npmjs.com/package/@corenel/cli)
5
5
  can depend on it; usable directly if you are building a terminal agent of your own.
6
6
 
7
- Requires Node 18+.
7
+ Requires Node 22+.
8
8
 
9
9
  ## The split that matters
10
10
 
@@ -0,0 +1,6 @@
1
+ import type { ApprovalSuggestion, SuggestionChoice } from '@corenel/protocol';
2
+ export declare function suggestionLine(s: ApprovalSuggestion): string;
3
+ export declare function suggestionKeys(s: ApprovalSuggestion): string;
4
+ export declare function parseSuggestionChoice(s: ApprovalSuggestion, input: string | null): SuggestionChoice | null;
5
+ export declare function suggestionOutcome(tool: string, choice: SuggestionChoice): string;
6
+ //# sourceMappingURL=approvalText.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"approvalText.d.ts","sourceRoot":"","sources":["../src/approvalText.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAiB9E,wBAAgB,cAAc,CAAC,CAAC,EAAE,kBAAkB,GAAG,MAAM,CAE5D;AAED,wBAAgB,cAAc,CAAC,CAAC,EAAE,kBAAkB,GAAG,MAAM,CAI5D;AAED,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,kBAAkB,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,gBAAgB,GAAG,IAAI,CAO1G;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,gBAAgB,GAAG,MAAM,CAQhF"}
@@ -1,13 +1,13 @@
1
1
  import {
2
2
  emptyState,
3
3
  parseTodos
4
- } from "./chunk-2MGDL3I4.js";
4
+ } from "./chunk-MAZLORMD.js";
5
5
  import {
6
6
  formatHelpText,
7
7
  isKnownCommandWord,
8
8
  parseCommand,
9
9
  unknownCommandMessage
10
- } from "./chunk-5RPHYXND.js";
10
+ } from "./chunk-TWFWLEIP.js";
11
11
  import {
12
12
  WRITE_TOOL_NAMES,
13
13
  buildWritePreview
@@ -155,9 +155,44 @@ function stripInline(s) {
155
155
  return s.replace(/\[([^\]]+)\]\(([^)]+)\)/g, "$1 ($2)").replace(/\*\*(\S(?:[^*]*\S)?)\*\*/g, "$1").replace(/(?<!\*)\*(\S(?:[^*]*\S)?)\*(?!\*)/g, "$1").replace(/`([^`]+)`/g, "$1");
156
156
  }
157
157
 
158
+ // src/approvalText.ts
159
+ function sanitizeToolName(name) {
160
+ return name.replace(/[\u0000-\u001f\u007f-\u009f]/g, "");
161
+ }
162
+ function suggestionLine(s) {
163
+ return `You have allowed ${sanitizeToolName(s.tool)} ${s.count} times${s.scope === "workspace" ? " in this folder" : ""}. Always allow it?`;
164
+ }
165
+ function suggestionKeys(s) {
166
+ return s.scopeChoice ? "[w] this folder [e] everywhere [n] not now [x] never" : "[e] always [n] not now [x] never";
167
+ }
168
+ function parseSuggestionChoice(s, input) {
169
+ const k = (input ?? "").trim().toLowerCase();
170
+ if (k === "w") return s.scopeChoice ? "workspace" : null;
171
+ if (k === "e") return "global";
172
+ if (k === "n") return "not-now";
173
+ if (k === "x") return "never";
174
+ return null;
175
+ }
176
+ function suggestionOutcome(tool, choice) {
177
+ const t = sanitizeToolName(tool);
178
+ switch (choice) {
179
+ case "workspace":
180
+ return `always allowing ${t} in this folder (undo: corenel approvals revoke ${t})`;
181
+ case "global":
182
+ return `always allowing ${t} everywhere (undo: corenel approvals revoke ${t} --global)`;
183
+ case "not-now":
184
+ return `not now: ${t} will be suggested again after more approvals`;
185
+ case "never":
186
+ return `${t} will not be suggested again`;
187
+ }
188
+ }
189
+
158
190
  // src/session.ts
159
191
  var DELTA_FLUSH_MS = 50;
160
192
  var TOOL_DELTA_CAP = 4e3;
193
+ function sameTool(heldKey, askedName) {
194
+ return heldKey === askedName || heldKey.endsWith(`:${askedName}`);
195
+ }
161
196
  var TermSession = class {
162
197
  constructor(deps) {
163
198
  this.deps = deps;
@@ -203,6 +238,12 @@ var TermSession = class {
203
238
  turnId = 0;
204
239
  activeTurn = 0;
205
240
  pending = null;
241
+ /** The tool name a `pending` permission ask is FOR -- set only by
242
+ * `askPermission`, never by `askUser` (a question is not tied to one tool).
243
+ * Consumed once by `resolvePending` to decide whether a denied ask should
244
+ * drop a held suggestion for the same tool (final review M3). */
245
+ pendingAskTool = null;
246
+ heldSuggestion = null;
206
247
  draining = false;
207
248
  /* Bumped whenever the conversation is REPLACED out from under a turn --
208
249
  * `/clear` and `resume()`. A turn captures it when it starts and only adopts
@@ -1016,9 +1057,12 @@ var TermSession = class {
1016
1057
  }
1017
1058
  /** esc, or any other reason the overlay should close -- a no-op unless the
1018
1059
  * manager is actually the modal open right now, so it can never steal a
1019
- * permission or question modal's close. */
1060
+ * permission or question modal's close. Restores a held suggestion (final
1061
+ * review M3) rather than just clearing the modal -- otherwise an offer held
1062
+ * behind this overlay came back only when some LATER, unrelated permission
1063
+ * ask happened to resolve. */
1020
1064
  closeMcpManager() {
1021
- if (this.state.modal?.kind === "mcp") this.set({ modal: null });
1065
+ if (this.state.modal?.kind === "mcp") this.restoreHeldSuggestion(null);
1022
1066
  }
1023
1067
  /* ---- the Ink `/help` overlay -------------------------------------------
1024
1068
  *
@@ -1038,9 +1082,10 @@ var TermSession = class {
1038
1082
  }
1039
1083
  /** esc, or any other reason the overlay should close -- a no-op unless the
1040
1084
  * help overlay is actually the modal open right now, so it can never steal
1041
- * a permission, question, or MCP modal's close. */
1085
+ * a permission, question, or MCP modal's close. Restores a held suggestion
1086
+ * (final review M3) -- see closeMcpManager's doc. */
1042
1087
  closeHelp() {
1043
- if (this.state.modal?.kind === "help") this.set({ modal: null });
1088
+ if (this.state.modal?.kind === "help") this.restoreHeldSuggestion(null);
1044
1089
  }
1045
1090
  /* ---- the Ink `/context` overlay ----------------------------------------
1046
1091
  *
@@ -1079,9 +1124,11 @@ var TermSession = class {
1079
1124
  }
1080
1125
  /** esc, or any other reason the context overlay should close -- a no-op
1081
1126
  * unless the context overlay is actually the modal open right now, so it
1082
- * can never steal a permission, question, help, or MCP modal's close. */
1127
+ * can never steal a permission, question, help, or MCP modal's close.
1128
+ * Restores a held suggestion (final review M3) -- see closeMcpManager's
1129
+ * doc. */
1083
1130
  closeContext() {
1084
- if (this.state.modal?.kind === "context") this.set({ modal: null });
1131
+ if (this.state.modal?.kind === "context") this.restoreHeldSuggestion(null);
1085
1132
  }
1086
1133
  /** Up/down inside the overlay -- wraps, and does nothing with zero rows. */
1087
1134
  moveMcpSelection(delta) {
@@ -1260,6 +1307,7 @@ ${line}` : line;
1260
1307
  this.activeTurn = mine;
1261
1308
  this.beginTurn();
1262
1309
  const system = await this.resolveSystem();
1310
+ const skills = this.deps.skills ? await this.deps.skills().catch(() => []) : [];
1263
1311
  const run = this.deps.runner ?? runAgent;
1264
1312
  try {
1265
1313
  const result = await run({
@@ -1268,6 +1316,7 @@ ${line}` : line;
1268
1316
  ...system ? { system } : {},
1269
1317
  messages: this.history,
1270
1318
  tools: this.deps.tools,
1319
+ ...skills.length ? { skills } : {},
1271
1320
  toolCtx: this.deps.toolCtx(ac.signal, (q) => this.askUser(q), this.permission),
1272
1321
  signal: ac.signal,
1273
1322
  ...this.permission ? { permission: this.permission } : {},
@@ -1344,15 +1393,17 @@ ${line}` : line;
1344
1393
  * fully synchronous, exactly as before this method could show a diff at
1345
1394
  * all. */
1346
1395
  askPermission(req) {
1347
- if (this.deps.approveAll) return Promise.resolve("allow");
1396
+ if (this.deps.approveAll) return Promise.resolve({ decision: "allow", by: "unattended" });
1348
1397
  if (this.pending) throw new Error("a question is already open");
1349
1398
  const detail = req.input;
1350
1399
  const shown = typeof detail?.command === "string" ? detail.command : typeof detail?.path === "string" ? detail.path : "";
1351
1400
  const openModal = (diff) => {
1352
- if (this.state.status === "aborting") return "deny";
1401
+ if (this.state.status === "aborting") return { decision: "deny", by: "timeout" };
1402
+ if (this.state.modal?.kind === "suggestion") this.heldSuggestion = this.state.modal.suggestion;
1353
1403
  this.set({ modal: { kind: "permission", tool: req.tool, detail: shown, ...diff ? { diff } : {} } });
1354
1404
  return new Promise((resolve) => {
1355
1405
  this.pending = { resolve: (v) => resolve(v ?? "deny") };
1406
+ this.pendingAskTool = req.tool;
1356
1407
  });
1357
1408
  };
1358
1409
  if (!this.deps.previewWrite || !WRITE_TOOL_NAMES.has(req.tool)) return Promise.resolve(openModal(null));
@@ -1360,6 +1411,7 @@ ${line}` : line;
1360
1411
  }
1361
1412
  askUser(questions) {
1362
1413
  if (this.pending) throw new Error("a question is already open");
1414
+ if (this.state.modal?.kind === "suggestion") this.heldSuggestion = this.state.modal.suggestion;
1363
1415
  this.set({ modal: { kind: "user", questions } });
1364
1416
  return new Promise((resolve) => {
1365
1417
  this.pending = { resolve: (v) => resolve(v ?? null) };
@@ -1377,15 +1429,65 @@ ${line}` : line;
1377
1429
  * outcome that `askUserTool` (packages/harness/tools/builtins.ts) checks for. */
1378
1430
  forceClosePending() {
1379
1431
  if (!this.pending) return;
1380
- this.resolvePending(this.state.modal?.kind === "user" ? null : "deny");
1432
+ this.resolvePending(this.state.modal?.kind === "user" ? null : { decision: "deny", by: "timeout" });
1381
1433
  }
1382
1434
  resolvePending(value) {
1383
1435
  const p = this.pending;
1384
1436
  if (!p) return;
1385
1437
  this.pending = null;
1386
- this.set({ modal: null });
1438
+ const askTool = this.pendingAskTool;
1439
+ this.pendingAskTool = null;
1440
+ const decision = typeof value === "object" && value !== null && "decision" in value ? value.decision : value;
1441
+ this.restoreHeldSuggestion(decision === "deny" && askTool !== null ? askTool : null);
1387
1442
  p.resolve(value);
1388
1443
  }
1444
+ /** Restore whatever suggestion is held (if any) as the visible modal,
1445
+ * dropping it instead if `dropForTool` names the tool it is for. Shared by
1446
+ * every path that can uncover a held offer: answering a permission/question
1447
+ * ask (`resolvePending`), and closing the help/MCP/context overlays -- all
1448
+ * of which used to just clear the modal, silently losing a held offer until
1449
+ * some LATER, unrelated permission answer happened to surface it (final
1450
+ * review M3). */
1451
+ restoreHeldSuggestion(dropForTool) {
1452
+ let held = this.heldSuggestion;
1453
+ this.heldSuggestion = null;
1454
+ if (held && dropForTool !== null && sameTool(held.tool, dropForTool)) held = null;
1455
+ this.set({ modal: held ? { kind: "suggestion", suggestion: held, openedAt: Date.now(), armedChoice: null } : null });
1456
+ }
1457
+ /** Offer a standing approval. Shown now if nothing is open, otherwise held
1458
+ * until the open prompt is answered; a newer offer replaces a held one. */
1459
+ offerApprovalSuggestion(s) {
1460
+ if (!this.deps.saveApprovalChoice) return;
1461
+ if (this.state.modal === null) {
1462
+ this.set({ modal: { kind: "suggestion", suggestion: s, openedAt: Date.now(), armedChoice: null } });
1463
+ return;
1464
+ }
1465
+ this.heldSuggestion = s;
1466
+ }
1467
+ /** Arm a widening choice (`workspace` / `global`) without granting it yet --
1468
+ * only a following Enter (`respondApprovalSuggestion`) does that. See
1469
+ * ApprovalSuggestionView's doc (final review I1). A no-op if the suggestion
1470
+ * modal is not open. */
1471
+ armApprovalSuggestion(choice) {
1472
+ if (this.state.modal?.kind !== "suggestion") return;
1473
+ this.set({ modal: { ...this.state.modal, armedChoice: choice } });
1474
+ }
1475
+ async respondApprovalSuggestion(choice) {
1476
+ const modal = this.state.modal;
1477
+ const save = this.deps.saveApprovalChoice;
1478
+ if (modal?.kind !== "suggestion" || !save) return;
1479
+ this.set({ modal: null });
1480
+ try {
1481
+ await save(modal.suggestion, choice);
1482
+ this.note("info", suggestionOutcome(modal.suggestion.tool, choice));
1483
+ } catch (e) {
1484
+ this.note("warn", `could not save that choice: ${e instanceof Error ? e.message : String(e)}`);
1485
+ }
1486
+ }
1487
+ /** Escape: ignore the offer. Writes nothing (plan Task 5: ignoring is not a choice). */
1488
+ dismissApprovalSuggestion() {
1489
+ if (this.state.modal?.kind === "suggestion") this.set({ modal: null });
1490
+ }
1389
1491
  };
1390
1492
 
1391
1493
  export {
@@ -1393,6 +1495,10 @@ export {
1393
1495
  ageWord,
1394
1496
  formatContext,
1395
1497
  parseMarkdown,
1498
+ suggestionLine,
1499
+ suggestionKeys,
1500
+ parseSuggestionChoice,
1501
+ suggestionOutcome,
1396
1502
  DELTA_FLUSH_MS,
1397
1503
  TOOL_DELTA_CAP,
1398
1504
  TermSession
@@ -1,4 +1,5 @@
1
1
  // src/state.ts
2
+ var SUGGESTION_ARM_DELAY_MS = 400;
2
3
  function emptyState(model = "") {
3
4
  return {
4
5
  seq: 0,
@@ -45,6 +46,7 @@ function parseTodos(args) {
45
46
  }
46
47
 
47
48
  export {
49
+ SUGGESTION_ARM_DELAY_MS,
48
50
  emptyState,
49
51
  parseTodos
50
52
  };
@@ -1,4 +1,5 @@
1
1
  // src/commands.ts
2
+ import { strategiesRoot } from "@corenel/protocol";
2
3
  var TERM_COMMANDS = [
3
4
  { name: "help", summary: "what you can type here" },
4
5
  { name: "tools", summary: "what the agent can do, and what it can change" },
@@ -15,7 +16,9 @@ var TERM_COMMANDS = [
15
16
  { name: "mode", args: "<id>", summary: "switch the agent mode (takes effect next turn); no id lists what is configured" },
16
17
  { name: "persona", args: "<id>", summary: "switch persona (takes effect next turn); no id lists what is configured" },
17
18
  { name: "skill", args: "<name>", summary: "apply a skill to your NEXT message only, then it clears; no name lists what is installed" },
18
- { name: "strategy", args: "<name>", summary: "run a saved strategy from .prompd/strategies; no name lists what is saved" },
19
+ { name: "strategy", args: "<name>", get summary() {
20
+ return `run a saved strategy from the current project's strategies folder (${strategiesRoot()} in a folder with no project); no name lists what is saved`;
21
+ } },
19
22
  { name: "mcp", args: "[list|add|remove|login|refresh|forget]", summary: "MCP servers -- what is connected, and sign in or add one; no argument opens the manager" }
20
23
  ];
21
24
  function formatHelpText() {
@@ -1 +1 @@
1
- {"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../src/commands.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,eAAO,MAAM,aAAa,EAAE,WAAW,EAkBtC,CAAC;AAEF;;;;;;;+DAO+D;AAC/D,wBAAgB,cAAc,IAAI,MAAM,CAiBvC;AAED,MAAM,MAAM,aAAa,GACrB;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,GACf;IAAE,GAAG,EAAE,OAAO,CAAA;CAAE,GAChB;IAAE,GAAG,EAAE,QAAQ,CAAA;CAAE,GACjB;IAAE,GAAG,EAAE,SAAS,CAAA;CAAE,GAClB;IAAE,GAAG,EAAE,QAAQ,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GAC/B;IAAE,GAAG,EAAE,QAAQ,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,GAC9B;IAAE,GAAG,EAAE,OAAO,CAAA;CAAE,GAChB;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,GACf;IAAE,GAAG,EAAE,OAAO,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,GAC7B;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,GAC5B;IAAE,GAAG,EAAE,SAAS,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,GAC/B;IAAE,GAAG,EAAE,OAAO,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,GAC7B;IAAE,GAAG,EAAE,UAAU,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,GAChC;IAAE,GAAG,EAAE,KAAK,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,QAAQ,GAAG,OAAO,GAAG,SAAS,GAAG,QAAQ,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAQxH;;;;;yDAKyD;AACzD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAGxD;AAkBD;;;;;;;WAOW;AACX,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAU1D;AAED;;yBAEyB;AACzB,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAG1D;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CAwC/D;AAED,2EAA2E;AAC3E,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,WAAW,EAAE,CAK3D"}
1
+ {"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../src/commands.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,eAAO,MAAM,aAAa,EAAE,WAAW,EAkBtC,CAAC;AAEF;;;;;;;+DAO+D;AAC/D,wBAAgB,cAAc,IAAI,MAAM,CAiBvC;AAED,MAAM,MAAM,aAAa,GACrB;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,GACf;IAAE,GAAG,EAAE,OAAO,CAAA;CAAE,GAChB;IAAE,GAAG,EAAE,QAAQ,CAAA;CAAE,GACjB;IAAE,GAAG,EAAE,SAAS,CAAA;CAAE,GAClB;IAAE,GAAG,EAAE,QAAQ,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GAC/B;IAAE,GAAG,EAAE,QAAQ,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,GAC9B;IAAE,GAAG,EAAE,OAAO,CAAA;CAAE,GAChB;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,GACf;IAAE,GAAG,EAAE,OAAO,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,GAC7B;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,GAC5B;IAAE,GAAG,EAAE,SAAS,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,GAC/B;IAAE,GAAG,EAAE,OAAO,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,GAC7B;IAAE,GAAG,EAAE,UAAU,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,GAChC;IAAE,GAAG,EAAE,KAAK,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,QAAQ,GAAG,OAAO,GAAG,SAAS,GAAG,QAAQ,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAQxH;;;;;yDAKyD;AACzD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAGxD;AAkBD;;;;;;;WAOW;AACX,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAU1D;AAED;;yBAEyB;AACzB,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAG1D;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CAwC/D;AAED,2EAA2E;AAC3E,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,WAAW,EAAE,CAK3D"}
package/dist/commands.js CHANGED
@@ -6,7 +6,7 @@ import {
6
6
  parseCommand,
7
7
  suggestCommand,
8
8
  unknownCommandMessage
9
- } from "./chunk-5RPHYXND.js";
9
+ } from "./chunk-TWFWLEIP.js";
10
10
  export {
11
11
  TERM_COMMANDS,
12
12
  filterCommands,
@@ -1 +1 @@
1
- {"version":3,"file":"App.d.ts","sourceRoot":"","sources":["../../src/ink/App.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA8B,MAAM,OAAO,CAAC;AAInD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAuCzC,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,QAAQ,CAAC;IAChB;;;;iDAI6C;IAC7C,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ;;iBAEa;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB;;;;;;;oDAOgD;IAChD,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,UAAU,EAAE,OAAO,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9B,QAAQ,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9B,QAAQ,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9B;;;;;oDAKgD;IAChD,KAAK,EAAE,OAAO,CAAC;IACf;uDACmD;IACnD,eAAe,EAAE,MAAM,CAAC;IACxB;;;;;;;;;;2EAUuE;IACvE,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,4EAA4E;AAC5E,wBAAgB,GAAG,CAAC,KAAK,EAAE,QAAQ,GAAG,KAAK,CAAC,YAAY,CAsHvD"}
1
+ {"version":3,"file":"App.d.ts","sourceRoot":"","sources":["../../src/ink/App.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA8B,MAAM,OAAO,CAAC;AAInD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAoEzC,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,QAAQ,CAAC;IAChB;;;;iDAI6C;IAC7C,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ;;iBAEa;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB;;;;;;;oDAOgD;IAChD,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,UAAU,EAAE,OAAO,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9B,QAAQ,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9B,QAAQ,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9B;;;;;oDAKgD;IAChD,KAAK,EAAE,OAAO,CAAC;IACf;uDACmD;IACnD,eAAe,EAAE,MAAM,CAAC;IACxB;;;;;;;;;;2EAUuE;IACvE,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,4EAA4E;AAC5E,wBAAgB,GAAG,CAAC,KAAK,EAAE,QAAQ,GAAG,KAAK,CAAC,YAAY,CAuIvD"}
@@ -8,8 +8,9 @@ import { type ContextSnapshot } from '../contextText';
8
8
  * so a turn that lands while this is open updates the numbers on the very
9
9
  * next frame with no re-fetch and no re-open -- unlike the `/mcp` manager,
10
10
  * there is nothing here that needs fetching in the first place. */
11
- export declare function ContextOverlay({ snapshot }: {
11
+ export declare function ContextOverlay({ snapshot, rows }: {
12
12
  snapshot: ContextSnapshot;
13
+ rows?: number;
13
14
  }): React.ReactElement;
14
15
  /** Build the overlay's data straight off live `TuiState` and the two
15
16
  * host-supplied static facts -- exported so App.tsx can assemble it without
@@ -1 +1 @@
1
- {"version":3,"file":"ContextOverlay.d.ts","sourceRoot":"","sources":["../../src/ink/ContextOverlay.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAiB,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAGrE;;;;;;mEAMmE;AACnE,wBAAgB,cAAc,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,EAAE,eAAe,CAAA;CAAE,GAAG,KAAK,CAAC,YAAY,CAa9F;AAED;;yEAEyE;AACzE,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,GAAG,IAAI,EAAE,SAAS,EAAE,MAAM,GAAG,eAAe,CAS1H"}
1
+ {"version":3,"file":"ContextOverlay.d.ts","sourceRoot":"","sources":["../../src/ink/ContextOverlay.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAiB,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;AASrE;;;;;;mEAMmE;AACnE,wBAAgB,cAAc,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE;IAAE,QAAQ,EAAE,eAAe,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,KAAK,CAAC,YAAY,CAsBnH;AAED;;yEAEyE;AACzE,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,GAAG,IAAI,EAAE,SAAS,EAAE,MAAM,GAAG,eAAe,CAS1H"}
@@ -7,6 +7,13 @@ import React from 'react';
7
7
  * Rendered FROM `TERM_COMMANDS` -- the same table `parseCommand` dispatches
8
8
  * against, the palette filters, and `formatHelpText` renders as text --
9
9
  * rather than from a hand-written constant, so this list cannot fall behind
10
- * the commands it documents. */
11
- export declare function HelpOverlay(): React.ReactElement;
10
+ * the commands it documents.
11
+ *
12
+ * `rows` bounds the command list to what the terminal can actually show (see
13
+ * overlayBudget.ts's module doc) -- a full command table is ~18 rows on its
14
+ * own, comfortably taller than a lot of real terminals once the input box and
15
+ * status row are accounted for. */
16
+ export declare function HelpOverlay({ rows }: {
17
+ rows?: number;
18
+ }): React.ReactElement;
12
19
  //# sourceMappingURL=HelpOverlay.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"HelpOverlay.d.ts","sourceRoot":"","sources":["../../src/ink/HelpOverlay.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAK1B;;;;;;;;gCAQgC;AAChC,wBAAgB,WAAW,IAAI,KAAK,CAAC,YAAY,CAqBhD"}
1
+ {"version":3,"file":"HelpOverlay.d.ts","sourceRoot":"","sources":["../../src/ink/HelpOverlay.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAc1B;;;;;;;;;;;;;mCAamC;AACnC,wBAAgB,WAAW,CAAC,EAAE,IAAI,EAAE,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,KAAK,CAAC,YAAY,CAsB3E"}
@@ -1,12 +1,7 @@
1
1
  import React from 'react';
2
2
  import type { McpManager } from '../state';
3
- /** The `/mcp` manager, as an Ink overlay -- pure presentation over
4
- * `TermSession`'s own `/mcp` dispatch (see mcpText.ts's module comment). It
5
- * holds no server list, connection state, or login state of its own; every
6
- * field it renders comes straight off `modal`, which the session owns and
7
- * refreshes after a mutation, the same way the text `/mcp` commands already
8
- * do. See ink/index.tsx's `useInput` for what a/x/l/esc/arrows actually run. */
9
- export declare function McpOverlay({ modal }: {
3
+ export declare function McpOverlay({ modal, rows }: {
10
4
  modal: McpManager;
5
+ rows?: number;
11
6
  }): React.ReactElement;
12
7
  //# sourceMappingURL=McpOverlay.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"McpOverlay.d.ts","sourceRoot":"","sources":["../../src/ink/McpOverlay.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAa3C;;;;;gFAKgF;AAChF,wBAAgB,UAAU,CAAC,EAAE,KAAK,EAAE,EAAE;IAAE,KAAK,EAAE,UAAU,CAAA;CAAE,GAAG,KAAK,CAAC,YAAY,CA+C/E"}
1
+ {"version":3,"file":"McpOverlay.d.ts","sourceRoot":"","sources":["../../src/ink/McpOverlay.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAwB3C,wBAAgB,UAAU,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;IAAE,KAAK,EAAE,UAAU,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,KAAK,CAAC,YAAY,CAqFpG"}
@@ -1,11 +1,20 @@
1
1
  import React from 'react';
2
+ import type { ApprovalSuggestion } from '@corenel/protocol';
2
3
  import type { PermissionAsk, UserAsk } from '../state';
4
+ /** The standing-approval offer (plan 2): one line of question, one of keys.
5
+ * Not part of ModalView -- it pauses nothing -- so App.tsx routes it here. */
6
+ export declare function SuggestionView({ suggestion }: {
7
+ suggestion: ApprovalSuggestion;
8
+ }): React.ReactElement;
3
9
  /** The permission ask and the free-text/choice ask -- the two modals that
4
10
  * pause a turn waiting on the user. `McpManager` and `HelpView` are NOT this
5
11
  * component's job: App.tsx routes those to their own views (McpOverlay.tsx;
6
12
  * a help view, Task 10) before this is ever called, so this stays narrowed
7
- * to the two `Modal` variants it actually knows how to draw. */
8
- export declare function ModalView({ modal }: {
13
+ * to the two `Modal` variants it actually knows how to draw. `rows` bounds
14
+ * both the diff preview and the options list to the terminal's actual
15
+ * height (see overlayBudget.ts's module doc). */
16
+ export declare function ModalView({ modal, rows }: {
9
17
  modal: PermissionAsk | UserAsk;
18
+ rows?: number;
10
19
  }): React.ReactElement;
11
20
  //# sourceMappingURL=Modal.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Modal.d.ts","sourceRoot":"","sources":["../../src/ink/Modal.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,OAAO,KAAK,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAuDvD;;;;gEAIgE;AAChE,wBAAgB,SAAS,CAAC,EAAE,KAAK,EAAE,EAAE;IAAE,KAAK,EAAE,aAAa,GAAG,OAAO,CAAA;CAAE,GAAG,KAAK,CAAC,YAAY,CAiC3F"}
1
+ {"version":3,"file":"Modal.d.ts","sourceRoot":"","sources":["../../src/ink/Modal.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAE5D,OAAO,KAAK,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAKvD;8EAC8E;AAC9E,wBAAgB,cAAc,CAAC,EAAE,UAAU,EAAE,EAAE;IAAE,UAAU,EAAE,kBAAkB,CAAA;CAAE,GAAG,KAAK,CAAC,YAAY,CAOrG;AAyGD;;;;;;iDAMiD;AACjD,wBAAgB,SAAS,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;IAAE,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,KAAK,CAAC,YAAY,CA2DhH"}
@@ -1,4 +1,6 @@
1
1
  import React from 'react';
2
+ import { PermissionService } from '@corenel/harness/guardrail/permission-service';
3
+ import { type ApprovalsService } from '@corenel/harness/approvals';
2
4
  import { TermSession, type TermSessionDeps } from '../session';
3
5
  export interface TuiDeps extends TermSessionDeps {
4
6
  version: string;
@@ -6,6 +8,14 @@ export interface TuiDeps extends TermSessionDeps {
6
8
  resume?: {
7
9
  id?: string;
8
10
  };
11
+ /** The host's approvals service (plan 2). Absent: no standing approvals, no
12
+ * decision log, no suggestions -- the kernel runs exactly as before. */
13
+ approvals?: ApprovalsService;
14
+ /** Stamped on each decision record. The session id is added at decision time. */
15
+ approvalsContext?: {
16
+ runId?: string;
17
+ workspace?: string;
18
+ };
9
19
  }
10
20
  /** Exported for `interaction.test.tsx` only: driving real keystrokes through
11
21
  * ink-testing-library's `stdin` requires rendering this directly, since
@@ -14,5 +24,13 @@ export declare function Root({ session, deps }: {
14
24
  session: TermSession;
15
25
  deps: TuiDeps;
16
26
  }): React.ReactElement;
27
+ /** The session's PermissionService: prompts through the session's modal,
28
+ * and -- with an approvals service -- honors standing approvals, records
29
+ * every decision, and offers a suggestion after the answer. Exported for
30
+ * approvalSuggestion.test.tsx. */
31
+ export declare function tuiPermission(session: TermSession, deps: TuiDeps): {
32
+ permission: PermissionService;
33
+ settled: () => Promise<void>;
34
+ };
17
35
  export declare function runTui(deps: TuiDeps): Promise<number>;
18
36
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ink/index.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAsD,MAAM,OAAO,CAAC;AAK3E,OAAO,EAAE,WAAW,EAAE,KAAK,eAAe,EAAE,MAAM,YAAY,CAAC;AAM/D,MAAM,WAAW,OAAQ,SAAQ,eAAe;IAC9C,OAAO,EAAE,MAAM,CAAC;IAChB,qDAAqD;IACrD,MAAM,CAAC,EAAE;QAAE,EAAE,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CAS1B;AAED;;wEAEwE;AACxE,wBAAgB,IAAI,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE;IAAE,OAAO,EAAE,WAAW,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GAAG,KAAK,CAAC,YAAY,CAwPnG;AAED,wBAAsB,MAAM,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAyD3D"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ink/index.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAsD,MAAM,OAAO,CAAC;AAE3E,OAAO,EAAE,iBAAiB,EAAE,MAAM,+CAA+C,CAAC;AAElF,OAAO,EAAkB,KAAK,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAEnF,OAAO,EAAE,WAAW,EAAE,KAAK,eAAe,EAAE,MAAM,YAAY,CAAC;AAQ/D,MAAM,WAAW,OAAQ,SAAQ,eAAe;IAC9C,OAAO,EAAE,MAAM,CAAC;IAChB,qDAAqD;IACrD,MAAM,CAAC,EAAE;QAAE,EAAE,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IASzB;4EACwE;IACxE,SAAS,CAAC,EAAE,gBAAgB,CAAC;IAC7B,iFAAiF;IACjF,gBAAgB,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CAC3D;AAED;;wEAEwE;AACxE,wBAAgB,IAAI,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE;IAAE,OAAO,EAAE,WAAW,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GAAG,KAAK,CAAC,YAAY,CAoSnG;AAED;;;kCAGkC;AAClC,wBAAgB,aAAa,CAAC,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,OAAO,GAAG;IAAE,UAAU,EAAE,iBAAiB,CAAC;IAAC,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CAsBlI;AAED,wBAAsB,MAAM,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAwD3D"}