@astrosheep/keiyaku 4.6.15 → 4.6.16

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 (56) hide show
  1. package/build/plugins/claude/skills/keiyaku/SKILL.md +1 -1
  2. package/build/plugins/claude/skills/keiyaku-akuma/SKILL.md +13 -8
  3. package/build/plugins/codex/skills/keiyaku/SKILL.md +1 -1
  4. package/build/plugins/codex/skills/keiyaku-akuma/SKILL.md +13 -8
  5. package/build/plugins/opencode/skills/keiyaku/SKILL.md +1 -1
  6. package/build/plugins/opencode/skills/keiyaku-akuma/SKILL.md +13 -8
  7. package/build/skills/keiyaku/SKILL.md +1 -1
  8. package/build/skills/keiyaku-akuma/SKILL.md +13 -8
  9. package/build/src/akuma/archetype.js +2 -2
  10. package/build/src/cli/commands/akuma-grammar.js +18 -14
  11. package/build/src/cli/commands/akuma.d.ts +2 -1
  12. package/build/src/cli/commands/akuma.js +24 -25
  13. package/build/src/cli/commands/contract-help.d.ts +11 -11
  14. package/build/src/cli/commands/contract-help.js +16 -16
  15. package/build/src/cli/commands/contract.d.ts +3 -2
  16. package/build/src/cli/commands/contract.js +45 -65
  17. package/build/src/cli/commands/debt-grammar.js +8 -15
  18. package/build/src/cli/commands/debt.d.ts +2 -1
  19. package/build/src/cli/commands/debt.js +22 -22
  20. package/build/src/cli/commands/install.d.ts +2 -1
  21. package/build/src/cli/commands/install.js +4 -3
  22. package/build/src/cli/commands/whoami.d.ts +46 -0
  23. package/build/src/cli/commands/whoami.js +127 -0
  24. package/build/src/cli/main.js +5 -4
  25. package/build/src/cli/parse.d.ts +7 -1
  26. package/build/src/cli/parse.js +23 -3
  27. package/build/src/cli/render/akuma-activity.d.ts +10 -23
  28. package/build/src/cli/render/akuma-activity.js +178 -212
  29. package/build/src/cli/render/akuma.d.ts +7 -6
  30. package/build/src/cli/render/akuma.js +22 -18
  31. package/build/src/cli/render/catalog.js +4 -3
  32. package/build/src/cli/render/contract-history.js +5 -16
  33. package/build/src/cli/render/debt.d.ts +2 -1
  34. package/build/src/cli/render/debt.js +6 -6
  35. package/build/src/cli/render/nuke.d.ts +2 -1
  36. package/build/src/cli/render/nuke.js +4 -2
  37. package/build/src/cli/render/receipt.js +8 -3
  38. package/build/src/cli/render/reconcile.js +8 -7
  39. package/build/src/cli/render/refusal.js +7 -3
  40. package/build/src/cli/render/terminal.d.ts +5 -2
  41. package/build/src/cli/render/terminal.js +74 -12
  42. package/build/src/cli/render/whoami.d.ts +2 -0
  43. package/build/src/cli/render/whoami.js +56 -0
  44. package/build/src/cli/runtime.d.ts +2 -1
  45. package/build/src/cli/runtime.js +59 -58
  46. package/build/src/cli/streams.d.ts +11 -1
  47. package/build/src/cli/streams.js +6 -2
  48. package/build/src/cli/usage.d.ts +1 -0
  49. package/build/src/cli/usage.js +7 -6
  50. package/build/src/contract-worktree.js +1 -1
  51. package/build/src/library/selection.d.ts +1 -0
  52. package/build/src/library/selection.js +1 -1
  53. package/build/src/runtime/proc/windows-launch.exe +0 -0
  54. package/package.json +1 -1
  55. package/skills/keiyaku/SKILL.md +1 -1
  56. package/skills/keiyaku-akuma/SKILL.md +13 -8
@@ -1,10 +1,21 @@
1
1
  import { defaultWaitComplete } from "../../akuma/akuma-observe.js";
2
2
  import { renderDiffstat, toolContent, toolRepr } from "./akuma-tool.js";
3
+ import { dispositionText } from "./debt.js";
3
4
  import { debtDispositionMark } from "./marks.js";
4
5
  import { DEFAULT_CLI_COLUMNS, displayColumns, padToDisplay, renderBoundedPayload, renderBoundedTextBlock, safeText, takeDisplayColumns, takeDisplayColumnsFromEnd, truncateDisplayText, truncateMiddleDisplayText, } from "./terminal.js";
5
6
  export const DEFAULT_CONTEXT = { columns: DEFAULT_CLI_COLUMNS, color: false };
6
- const TIME_WIDTH = 5;
7
- const VERB_WIDTH = 6;
7
+ /** The action family's fixed 3-cell state margin and minimum verb cell. */
8
+ const ACTION_MARGIN = " ";
9
+ const ACTION_VERB_WIDTH = 6;
10
+ /** Top-level rows: one mark plus its separator, and two spaces beneath it. */
11
+ const TOP_SPEAK = "▎ ";
12
+ const TOP_INPUT = "▸ ";
13
+ const TOP_PENDING = "⧗ ";
14
+ const TOP_ANSWER = "✓ ";
15
+ const TOP_FAILURE = "! ";
16
+ const TOP_CONTINUATION = " ";
17
+ /** The one row-omission glyph, in verb position. */
18
+ const OMISSION = "···";
8
19
  /**
9
20
  * The one blessed ruler: a run of U+2500 as wide as the frame head's widest line,
10
21
  * never wider than the terminal, marking the boundary between an observation
@@ -56,47 +67,13 @@ export function actionReceipt(input) {
56
67
  function contractFacts(contract) {
57
68
  return contract.kind === "failed" ? [`! contract failed ${safeText(contract.diagnostic)}`] : [];
58
69
  }
59
- function unobservedText(id, diagnostic) {
60
- return `× Akuma observation failed ${safeText(id)} — ${safeText(diagnostic)}`;
61
- }
62
- function clock(at) {
63
- const date = new Date(at);
64
- return Number.isFinite(date.getTime())
65
- ? `${String(date.getHours()).padStart(2, "0")}:${String(date.getMinutes()).padStart(2, "0")}`
66
- : "unknown";
67
- }
68
- function label(row, tool) {
69
- if (row.kind === "said")
70
- return "";
71
- if (row.kind === "thought")
72
- return "think";
73
- if (row.kind === "note")
74
- return "note";
75
- if (row.kind === "call")
76
- return "call";
77
- if (row.kind === "tell")
78
- return row.state === "told" ? "told" : "tell";
79
- if (row.kind === "outcome")
80
- return row.outcome.kind === "answered" ? "answer" : "error";
81
- if (row.kind === "turn")
82
- return "call";
83
- if (row.kind !== "tool")
84
- return row.kind;
85
- return tool.label;
86
- }
87
- function mark(row, live = false) {
88
- if (row.kind === "outcome")
89
- return row.outcome.kind === "answered" ? "✓" : "!";
90
- if (row.kind === "tell" && row.state === "pending")
91
- return "⧗";
92
- if (row.kind === "tool") {
93
- if (row.state === "active")
94
- return live ? "●" : "?";
95
- if (row.state === "unsettled")
96
- return "?";
97
- return row.state.status === "ok" ? "✓" : "!";
98
- }
99
- return "│";
70
+ /** The action family's one mark cell: a settled success stays blank, and live is only a frame mark. */
71
+ function toolMargin(row, live) {
72
+ if (row.state === "active")
73
+ return live ? "● " : "? ";
74
+ if (row.state === "unsettled")
75
+ return "? ";
76
+ return row.state.status === "ok" ? ACTION_MARGIN : "! ";
100
77
  }
101
78
  function rowText(row, tool) {
102
79
  if (row.kind === "said" ||
@@ -126,48 +103,30 @@ function rowText(row, tool) {
126
103
  ...(repr.suffix === undefined ? {} : { suffix: repr.suffix }),
127
104
  };
128
105
  }
129
- function eventPrefix(glyph, verb, time, columns) {
130
- const gutter = time === undefined ? " ".repeat(TIME_WIDTH) : time.padEnd(TIME_WIDTH);
131
- return actionCell(`${gutter} ${glyph}`, verb, columns);
132
- }
133
- /** The action column one row spends: six cells by default, or the name's own width when longer. */
134
- function verbColumn(verb) {
135
- return Math.max(VERB_WIDTH, displayColumns(verb));
136
- }
137
106
  /**
138
- * Attach the action cell after one row prefix. A name wider than the row's
139
- * remaining columns truncates grapheme-safely, spending every column the row
140
- * leaves after the separator so an exact-fit name stays whole and args trim first.
107
+ * The action family's one prefix: a state margin, a verb cell at least six cells wide and as wide as a
108
+ * longer name, then one space. Continuation lines align with the actual content start.
141
109
  */
142
- function actionCell(head, verb, columns) {
143
- const prefix = `${head} `;
144
- if (verb === "")
145
- return prefix;
146
- const available = Math.max(0, columns - displayColumns(prefix) - 1);
147
- const width = Math.min(verbColumn(verb), available);
148
- return `${prefix}${padToDisplay(truncateDisplayText(verb, width), width)} `;
149
- }
150
- function continuationPrefix(verb) {
151
- return " ".repeat(TIME_WIDTH) + " │ " + (verb === "" ? "" : " ".repeat(VERB_WIDTH + 1));
152
- }
153
- function timelineMarker(text) {
154
- return `${" ".repeat(TIME_WIDTH)} ⋮ ${text}`;
155
- }
156
- function plainLayout() {
157
- return {
158
- head: (time, glyph, verb, columns) => eventPrefix(glyph, verb, time, columns),
159
- continuation: continuationPrefix,
160
- marker: (count) => timelineMarker(`${count} omitted`),
161
- };
162
- }
163
- function compactLayout() {
164
- return { ...plainLayout(), compactRun: true };
165
- }
166
- function historyLayout() {
167
- return { ...compactLayout(), history: true };
168
- }
169
- function quotedBody(row) {
170
- return row.kind === "thought" || row.kind === "tell" || (row.kind === "outcome" && row.outcome.kind === "answered");
110
+ function actionPrefix(margin, verb, columns) {
111
+ const available = Math.max(0, columns - displayColumns(margin) - 1);
112
+ const width = Math.min(Math.max(ACTION_VERB_WIDTH, displayColumns(verb)), available);
113
+ const label = truncateDisplayText(verb, width);
114
+ return { first: `${margin}${padToDisplay(label, width)} `, continuation: `${margin}${" ".repeat(width + 1)}` };
115
+ }
116
+ function rowPrefix(row, tool, columns, view) {
117
+ if (row.kind === "tool")
118
+ return actionPrefix(toolMargin(row, view.live === true), tool.label, columns);
119
+ if (row.kind === "thought")
120
+ return actionPrefix(ACTION_MARGIN, "think", columns);
121
+ if (row.kind === "note")
122
+ return actionPrefix(ACTION_MARGIN, "note", columns);
123
+ if (row.kind === "outcome")
124
+ return { first: row.outcome.kind === "answered" ? TOP_ANSWER : TOP_FAILURE, continuation: TOP_CONTINUATION };
125
+ if (row.kind === "said")
126
+ return { first: TOP_SPEAK, continuation: TOP_SPEAK };
127
+ if (row.kind === "tell" && row.state === "pending")
128
+ return { first: TOP_PENDING, continuation: TOP_CONTINUATION };
129
+ return { first: TOP_INPUT, continuation: TOP_CONTINUATION };
171
130
  }
172
131
  function pathTail(path, maximum) {
173
132
  if (displayColumns(path) <= maximum)
@@ -230,6 +189,9 @@ function renderRunCommand(first, continuation, text, suffix, columns) {
230
189
  const tail = takeDisplayColumnsFromEnd(command, tailBudget).text;
231
190
  return [`${first}$ ${head}…`, `${continuation}…${tail}${keptSuffix}`];
232
191
  }
192
+ function omissionMarker(text, columns) {
193
+ return `${actionPrefix(ACTION_MARGIN, OMISSION, columns).first}${text}`;
194
+ }
233
195
  function rowBody(row, text, columns) {
234
196
  if (row.kind !== "tool" || row.call.kind !== "other")
235
197
  return text;
@@ -237,84 +199,60 @@ function rowBody(row, text, columns) {
237
199
  }
238
200
  function tailAnswerPreview(first, text, columns) {
239
201
  const readable = safeText(text).replace(/\s+/gu, " ").trim();
240
- const budget = Math.max(0, columns - displayColumns(first + "“”"));
202
+ const budget = Math.max(0, columns - displayColumns(first));
241
203
  const tail = takeDisplayColumnsFromEnd(readable, budget).text;
242
204
  const preview = tail.length === readable.length ? tail : `…${takeDisplayColumnsFromEnd(readable, Math.max(0, budget - 1)).text}`;
243
- return [`${first}“${preview}”`];
205
+ return [`${first}${preview}`];
244
206
  }
245
- function toolOverflowPreview(options, value, columns) {
246
- const { tool, layout, first, continuation } = options;
247
- if (tool?.overflow !== "command")
248
- return undefined;
249
- if (layout.compactRun !== true)
250
- return renderRunCommand(first, continuation, value.text, value.suffix ?? "", columns);
251
- const remaining = columns - displayColumns(first);
207
+ function renderCommandOverflow(first, continuation, value, columns, view) {
252
208
  const suffix = value.suffix ?? "";
209
+ if (view.compact !== true && view.history !== true)
210
+ return renderRunCommand(first, continuation, value.text, suffix, columns);
211
+ const remaining = columns - displayColumns(first);
253
212
  const withSuffix = remaining - displayColumns(suffix);
254
213
  const showSuffix = suffix.length > 0 && withSuffix >= 6;
255
214
  return [
256
215
  `${first}${truncateMiddleDisplayText(value.text, Math.max(0, showSuffix ? withSuffix : remaining))}${showSuffix ? suffix : ""}`,
257
216
  ];
258
217
  }
259
- function renderRow(row, context, options) {
260
- const { layout, first, continuation, tool } = options;
218
+ /** Every selected timeline row renders through here: one prefix family per row class. */
219
+ function renderRow(row, context, view = {}) {
220
+ const tool = row.kind === "tool" ? toolRepr(row) : undefined;
261
221
  const value = rowText(row, tool);
262
- const overflow = toolOverflowPreview(options, value, context.columns);
263
- if (overflow !== undefined)
264
- return overflow;
222
+ const { first, continuation } = rowPrefix(row, tool, context.columns, view);
223
+ const compact = view.compact === true;
224
+ if (tool?.overflow === "command")
225
+ return renderCommandOverflow(first, continuation, value, context.columns, view);
265
226
  if (tool?.pathPreview !== undefined)
266
227
  return [renderPathPreview(first, value.text, tool.pathPreview, context.columns)];
267
- if (layout.tailAnswer === true && row.kind === "outcome" && row.outcome.kind === "answered")
228
+ if (view.tailAnswer === true && row.kind === "outcome" && row.outcome.kind === "answered")
268
229
  return tailAnswerPreview(first, value.text, context.columns);
269
230
  const available = context.columns - displayColumns(first) - displayColumns(value.suffix ?? "");
231
+ const singleLine = compact || row.kind === "call" || row.kind === "tell" || row.kind === "turn";
270
232
  return renderBoundedPayload({
271
233
  text: rowBody(row, value.text, available),
272
234
  first,
273
235
  continuation,
274
236
  columns: context.columns,
275
- maxLines: layout.singleLine === true && row.kind !== "said"
276
- ? 1
277
- : layout.history === true
278
- ? Number.MAX_SAFE_INTEGER
279
- : value.lines,
280
- quote: quotedBody(row) ? (layout.singleLine === true ? '"' : "“") : "",
237
+ maxLines: singleLine ? 1 : view.history === true ? Number.MAX_SAFE_INTEGER : value.lines,
238
+ quote: "",
281
239
  truncated: "truncated" in row && row.truncated === true,
240
+ ...(singleLine || row.kind === "tool" ? {} : { preserveLines: true }),
282
241
  });
283
242
  }
284
- function renderTimelineRow(row, context, layout, time, options = {}) {
285
- const { inFlightSay = false, live = false, unsettled = false } = options;
286
- const tool = row.kind === "tool" ? toolRepr(row) : undefined;
287
- const glyph = inFlightSay ? (live ? "●" : "?") : unsettled ? "?" : mark(row, live);
288
- const verb = label(row, tool);
289
- return renderRow(row, context, {
290
- layout,
291
- first: layout.head(time, glyph, verb, context.columns),
292
- continuation: layout.continuation(verb),
293
- tool,
294
- });
295
- }
296
- function groupedEntries(entries, context, layout = plainLayout(), options = {}) {
243
+ function groupedEntries(entries, context, view = {}) {
297
244
  const lines = [];
298
- let previousClock;
299
245
  for (const entry of entries) {
300
246
  if (entry.kind === "gap") {
301
- lines.push(layout.marker(entry.count));
247
+ lines.push(omissionMarker(`${entry.count} omitted`, context.columns));
302
248
  continue;
303
249
  }
304
- const row = entry.row;
305
- const at = clock(row.at);
306
- const changed = previousClock === undefined || at !== previousClock;
307
- lines.push(...renderTimelineRow(row, context, layout, changed ? at : undefined, {
308
- inFlightSay: options.inFlightSay?.(row) ?? false,
309
- live: options.live === true,
310
- unsettled: options.unsettled?.(row) ?? false,
311
- }));
312
- previousClock = at;
250
+ lines.push(...renderRow(entry.row, context, view));
313
251
  }
314
252
  return lines;
315
253
  }
316
- function groupedRows(rows, context, layout = plainLayout()) {
317
- return groupedEntries(rows.filter((row) => row.kind !== "turn").map((row) => ({ kind: "row", row })), context, layout);
254
+ function groupedRows(rows, context, view = {}) {
255
+ return groupedEntries(rows.filter((row) => row.kind !== "turn").map((row) => ({ kind: "row", row })), context, view);
318
256
  }
319
257
  /** Ordered entry stream of one retained snapshot; an idle outcome keeps its sequence position. */
320
258
  function orderedSnapshotEntries(snapshot) {
@@ -377,7 +315,7 @@ export function snapshotActivityLines(snapshot, context, selection = {}) {
377
315
  return latest === undefined
378
316
  ? []
379
317
  : groupedEntries([latest], context, {
380
- ...compactLayout(),
318
+ compact: true,
381
319
  ...(selection.tailAnswer === true ? { tailAnswer: true } : {}),
382
320
  });
383
321
  }
@@ -412,16 +350,6 @@ function rememberPendingTells(state, activity) {
412
350
  function baselineOmissionCount(activity, after, before) {
413
351
  return activity.rows.filter((row) => row.sequence > after && (before === undefined || row.sequence < before) && isSettledStreamRow(row)).length;
414
352
  }
415
- function renderStreamRow(state, row, lines, options) {
416
- const { context, layout, inFlightSay = false, unsettled = false } = options;
417
- const at = clock(row.at);
418
- const changed = state.previousClock === undefined || at !== state.previousClock;
419
- lines.push(...renderTimelineRow(row, context, layout, changed ? at : undefined, {
420
- inFlightSay,
421
- unsettled,
422
- }));
423
- state.previousClock = at;
424
- }
425
353
  /**
426
354
  * A say is in-flight only while it is the newest row of the open turn: it may still be streaming, so it
427
355
  * stays in the redrawable frame. A say with any successor row is settled evidence and renders on the rail.
@@ -511,37 +439,21 @@ function observeActivitySnapshot(state, activity) {
511
439
  }
512
440
  else if (row.kind === "said" || row.kind === "tell")
513
441
  state.checkpointTools = 0;
514
- emitRow(state, emissions, { kind: "row", row, inFlightSay: inFlightSay(activity, row) });
442
+ emitRow(state, emissions, { kind: "row", row });
515
443
  }
516
444
  return emissions;
517
445
  }
518
- function renderEmissions(state, emissions, context, layout) {
519
- const lines = [];
520
- for (const emission of emissions) {
521
- if (emission.kind === "gap")
522
- lines.push(layout.marker(emission.count));
523
- else
524
- renderStreamRow(state, emission.row, lines, {
525
- context,
526
- layout,
527
- ...(emission.inFlightSay === undefined ? {} : { inFlightSay: emission.inFlightSay }),
528
- ...(emission.unsettled === undefined ? {} : { unsettled: emission.unsettled }),
529
- });
530
- }
531
- return lines;
532
- }
533
446
  /**
534
447
  * Append-only live view over one command's successive settled snapshots.
535
448
  * After each say or Tell, the first three tools print as they settle; later
536
449
  * tools until the next checkpoint fold into one omission marker. Failed tools
537
450
  * always print.
538
451
  */
539
- export function activityStream(context, layout = plainLayout()) {
452
+ export function activityStream(context) {
540
453
  const state = {
541
454
  newestSettledSequence: undefined,
542
455
  admissionSequence: undefined,
543
456
  pendingTellSequences: new Set(),
544
- previousClock: undefined,
545
457
  renderedBoundaries: new Set(),
546
458
  admittedTellSequences: new Set(),
547
459
  checkpointTools: 0,
@@ -587,15 +499,15 @@ export function activityStream(context, layout = plainLayout()) {
587
499
  state.omitted = 0;
588
500
  for (const row of [...state.liveRows.values()].sort((left, right) => left.sequence - right.sequence)) {
589
501
  const unresolved = row.kind === "tool" && row.state === "active" ? { ...row, state: "unsettled" } : row;
590
- emissions.push({ kind: "row", row: unresolved, unsettled: true });
502
+ emissions.push({ kind: "row", row: unresolved });
591
503
  }
592
504
  state.liveRows.clear();
593
505
  return emissions;
594
506
  };
595
- const render = (emissions) => renderEmissions(state, emissions, context, layout);
507
+ const render = (emissions) => groupedEntries(emissions, context);
596
508
  const frame = () => groupedEntries([...state.liveRows.values()]
597
509
  .sort((left, right) => left.sequence - right.sequence)
598
- .map((row) => ({ kind: "row", row })), context, layout, { live: true, inFlightSay: (row) => row.kind === "said" });
510
+ .map((row) => ({ kind: "row", row })), context, { live: true });
599
511
  const observe = (activity) => render(observeActivitySnapshot(state, activity));
600
512
  return Object.assign(observe, {
601
513
  seed: (activity, alreadyRenderedSequence) => render(seed(activity, alreadyRenderedSequence)),
@@ -627,9 +539,6 @@ function conclusionMarkVerb(status, outcome) {
627
539
  return { mark: "!", verb: "untidy" };
628
540
  return { mark: "!", verb: "stranded" };
629
541
  }
630
- function clockFromMs(at) {
631
- return clock(new Date(at).toISOString());
632
- }
633
542
  function durationText(durationMs) {
634
543
  const seconds = Math.max(0, Math.round(durationMs / 1000));
635
544
  if (seconds < 60)
@@ -637,20 +546,17 @@ function durationText(durationMs) {
637
546
  return `${Math.floor(seconds / 60)}m${String(seconds % 60)}s`;
638
547
  }
639
548
  /**
640
- * A conclusion clause asserts real waiting: a target that settled at or after
641
- * this wait began keeps its duration, a target already settled names no
642
- * duration, and an unfinished target carries the elapsed wait.
549
+ * One wait-style conclusion for every observer of an input-bound Tell. A target
550
+ * that settled at or after this wait began keeps its duration, a target already
551
+ * settled names no duration, and an unfinished target carries the elapsed wait.
643
552
  */
644
- function conclusionClause(at, startedAt, complete, end) {
645
- if (!complete)
646
- return ` — waited ${durationText(Math.max(0, end - startedAt))}`;
647
- return at >= startedAt ? ` — ${durationText(Math.max(0, at - startedAt))}` : "";
648
- }
649
- /** One wait-style conclusion for every observer of an input-bound Tell. */
650
553
  function waitConclusionRow(input) {
651
- const time = input.at === undefined ? "unknown" : clockFromMs(input.at);
652
- const duration = input.at === undefined ? "" : conclusionClause(input.at, input.startedAt, input.complete, input.end);
653
- return `${time}${input.target ?? ""} ${input.mark} ${input.verb}${duration}`;
554
+ const duration = input.complete
555
+ ? input.at !== undefined && input.at >= input.startedAt
556
+ ? ` — ${durationText(Math.max(0, input.at - input.startedAt))}`
557
+ : ""
558
+ : ` — waited ${durationText(Math.max(0, input.end - input.startedAt))}`;
559
+ return `${input.mark} ${input.verb}${duration}`;
654
560
  }
655
561
  export function inputWaitConclusion(observation, input) {
656
562
  const end = input.now ?? Date.now();
@@ -673,7 +579,7 @@ export function inputWaitConclusion(observation, input) {
673
579
  ...conclusion,
674
580
  }),
675
581
  ...(observation.reason === "failed" || observation.reason === "invalid-output"
676
- ? [`! error ${safeText(observation.diagnostic)}`]
582
+ ? [`! ${safeText(observation.diagnostic)}`]
677
583
  : []),
678
584
  ];
679
585
  }
@@ -741,7 +647,7 @@ export function inputWaitStream(context, head, options) {
741
647
  lines.push(...result.evidence);
742
648
  lines.push(...activity.flush());
743
649
  if (result.kind === "failed")
744
- lines.push(`! error ${safeText(result.diagnostic)}`);
650
+ lines.push(`! ${safeText(result.diagnostic)}`);
745
651
  else
746
652
  lines.push(...inputWaitConclusion(result.observation, {
747
653
  startedAt,
@@ -798,12 +704,12 @@ function renderReportedChangeLines(snapshot) {
798
704
  return [
799
705
  `changes ${snapshot.reportedChanges.length + snapshot.reportedChangesOmitted}`,
800
706
  ...snapshot.reportedChanges.map((change) => ` ${renderDiffstat(change.diffstat).padEnd(width)} ${safeText(change.path)}`),
801
- ...(snapshot.reportedChangesOmitted > 0 ? [` ⋮ ${snapshot.reportedChangesOmitted} more files`] : []),
707
+ ...(snapshot.reportedChangesOmitted > 0 ? [` ${OMISSION} ${snapshot.reportedChangesOmitted} more files`] : []),
802
708
  ];
803
709
  }
804
710
  function renderDebtRow(row, columns) {
805
711
  const prefix = ` ${debtDispositionMark(row.disposition)} ${row.id} · `;
806
- const body = `${safeText(row.title)} · ${row.disposition} · P${row.priority}`;
712
+ const body = `${safeText(row.title)} · ${dispositionText(row.disposition)} · P${row.priority}`;
807
713
  const inline = `${prefix}${body}`;
808
714
  if (displayColumns(inline) <= columns)
809
715
  return [inline];
@@ -839,13 +745,12 @@ function snapshotCore(view, context, options) {
839
745
  }
840
746
  function snapshotConclusion(status) {
841
747
  const timeline = status.timeline;
842
- // The outcome row (`✓ answer` / `! error`) is already the ending; a closer would be a second mark
843
- // carrying no new information, so it is absent exactly then.
748
+ // The outcome row (`✓` / `!`) is already the ending; a closer would be a second mark carrying no
749
+ // new information, so it is absent exactly then.
844
750
  if (timeline.kind === "idle" && timeline.outcome !== undefined)
845
751
  return "";
846
- const last = [...timeline.entries].reverse().find((entry) => entry.kind === "row")?.row;
847
752
  const { mark, verb } = conclusionMarkVerb(status, "life");
848
- return `${last === undefined ? "unknown" : clock(last.at)} ${mark} ${verb}`;
753
+ return `${mark} ${verb}`;
849
754
  }
850
755
  export function snapshotText(view, context, options = {}) {
851
756
  const core = snapshotCore(view, context, options);
@@ -900,17 +805,69 @@ export function historyRawAnswer(result, historyId) {
900
805
  const outcome = result.outcome.outcome;
901
806
  return outcome.kind === "answered" ? outcome.answer : outcome.diagnostic;
902
807
  }
808
+ /** One compact wait result line: an observed life mark, the complete identity, a bounded summary. */
809
+ const WAIT_SUMMARY_SEPARATOR = " — ";
810
+ const WAIT_LIFE = {
811
+ running: { mark: "●", state: "running" },
812
+ killed: { mark: "×", state: "killed" },
813
+ hung: { mark: "?", state: "hung" },
814
+ untidy: { mark: "!", state: "untidy" },
815
+ stranded: { mark: "!", state: "stranded" },
816
+ };
817
+ function foldedSummary(value) {
818
+ return safeText(value).replace(/\s+/gu, " ").trim();
819
+ }
820
+ function preview(value, fallback) {
821
+ return foldedSummary(value) || fallback;
822
+ }
823
+ function waitLine(mark, subject, summary, columns) {
824
+ const head = `${mark} ${subject}`;
825
+ const room = columns - displayColumns(head) - displayColumns(WAIT_SUMMARY_SEPARATOR);
826
+ const folded = foldedSummary(summary);
827
+ return room < 1 || folded.length === 0
828
+ ? head
829
+ : `${head}${WAIT_SUMMARY_SEPARATOR}${truncateDisplayText(folded, room)}`;
830
+ }
903
831
  /**
904
- * One wait's final text: each observed Akuma's own status snapshot, then one
905
- * failure fact per member the wait could not read. A wait observes to its
906
- * completion judgment and streams nothing, so an answer is visible only as the
907
- * snapshot's own outcome row.
832
+ * The final public snapshot as one wait summary. A stopped life stays explicit over any older
833
+ * outcome; pending input stays visible beside the life it belongs to; a sleeping member without
834
+ * pending input shows its current answered preview or failed diagnostic, else completion. The
835
+ * mark reports the observed life, never continuing liveness.
836
+ */
837
+ function waitSummary(status) {
838
+ const pending = status.timeline.entries.some((entry) => entry.kind === "row" && entry.row.kind === "tell" && entry.row.state === "pending");
839
+ const outcome = status.timeline.kind === "idle" ? status.timeline.outcome?.outcome : undefined;
840
+ const observed = WAIT_LIFE[status.life] ??
841
+ (pending
842
+ ? { mark: "⧗", state: "pending tell" }
843
+ : outcome === undefined
844
+ ? { mark: "✓", state: "completed" }
845
+ : outcome.kind === "answered"
846
+ ? { mark: "✓", state: preview(outcome.answer, "answered") }
847
+ : { mark: "!", state: preview(outcome.diagnostic, "failed") });
848
+ return {
849
+ ...observed,
850
+ summary: [
851
+ observed.state,
852
+ ...(pending && status.life !== "asleep" ? ["pending tell"] : []),
853
+ ...(status.strandedReason === undefined ? [] : [status.strandedReason]),
854
+ ].join(FACT_SEPARATOR),
855
+ };
856
+ }
857
+ /**
858
+ * One wait's final text: one compact line per observed member, then one failure line per member
859
+ * the wait could not read. A wait opens no frame and extracts no separate answer, so an answer is
860
+ * visible only as the snapshot's own bounded summary.
908
861
  */
909
862
  export function waitText(result, presentation, context) {
910
863
  const alias = result.observations.length === 1 && result.unobserved.length === 0 ? presentation.alias : undefined;
911
- const blocks = result.observations.map((observation) => snapshotText(observation, context, { ...(alias === undefined ? {} : { alias }) }));
912
- const unobserved = result.unobserved.map((member) => unobservedText(member.id, member.diagnostic));
913
- return [...blocks, ...(unobserved.length === 0 ? [] : [unobserved.join("\n")])].join("\n\n");
864
+ return [
865
+ ...result.observations.map((observation) => {
866
+ const { mark, summary } = waitSummary(observation.status);
867
+ return waitLine(mark, identity(observation.status.id, alias), summary, context.columns);
868
+ }),
869
+ ...result.unobserved.map((member) => waitLine("!", safeText(member.id), `observation failed${FACT_SEPARATOR}${member.diagnostic}`, context.columns)),
870
+ ].join("\n");
914
871
  }
915
872
  export function historyText(result, presentation, context) {
916
873
  if (presentation.id !== undefined) {
@@ -924,12 +881,12 @@ export function historyText(result, presentation, context) {
924
881
  return result.kind === "last" ? result.answer : "no answer retained";
925
882
  if (result.kind !== "history")
926
883
  throw new Error("history result lacks page");
927
- const rows = groupedRows(result.history.rows, context, historyLayout());
884
+ const rows = groupedRows(result.history.rows, context, { history: true });
928
885
  const paging = result.history.omitted > 0
929
886
  ? [
930
- timelineMarker(result.history.hasLater
887
+ omissionMarker(result.history.hasLater
931
888
  ? `${result.history.omitted} later events · showing first ${result.history.rows.length}`
932
- : `${result.history.omitted} earlier events · showing last ${result.history.rows.length}`),
889
+ : `${result.history.omitted} earlier events · showing last ${result.history.rows.length}`, context.columns),
933
890
  ]
934
891
  : [];
935
892
  // History is an immutable timeline, so it takes the shared heading and content boundary and
@@ -938,7 +895,7 @@ export function historyText(result, presentation, context) {
938
895
  const head = panelHead(result.id, presentation.alias, result.contract);
939
896
  return [...head, ...(content.length === 0 ? [] : [frameRule(head, context.columns), ...content])].join("\n");
940
897
  }
941
- /** One bounded preview of a submitted Tell, indented once beneath its verdict or evidence. */
898
+ /** One bounded, quoted preview of a Tell an outer receipt already admitted, indented beneath it. */
942
899
  export function tellPayloadLines(text, context) {
943
900
  return renderBoundedPayload({
944
901
  text,
@@ -950,38 +907,47 @@ export function tellPayloadLines(text, context) {
950
907
  truncated: false,
951
908
  });
952
909
  }
953
- /** One admitted Tell as a timeline row inside a shared input panel; the panel owns the identity. */
910
+ /** One admitted Tell as a timeline input row inside a shared panel; the panel owns the identity. */
954
911
  export function tellAdmissionRow(tell, context) {
955
- const wake = tell.wake;
956
- const glyph = wake.kind === "failed" ? "!" : wake.kind === "held" ? "⧗" : "✓";
957
- const verb = wake.kind === "failed" || wake.kind === "held" ? "tell" : "told";
958
- return groupedRows([tell.row], context, {
959
- ...plainLayout(),
960
- head: (time, _glyph, _verb, columns) => eventPrefix(glyph, verb, time, columns),
961
- singleLine: true,
962
- }).join("\n");
912
+ const lead = tell.wake.kind === "failed" ? TOP_FAILURE : tell.wake.kind === "held" ? TOP_PENDING : TOP_INPUT;
913
+ return renderBoundedPayload({
914
+ text: tell.row.text,
915
+ first: lead,
916
+ continuation: TOP_CONTINUATION,
917
+ columns: context.columns,
918
+ maxLines: 1,
919
+ quote: "",
920
+ truncated: false,
921
+ })[0];
963
922
  }
964
923
  /**
965
- * One Tell action receipt: the delivery verdict and complete subject, then the submitted payload
966
- * once. A held Tell keeps neutral vocabulary; a failed delivery keeps its own concrete diagnosis.
924
+ * One Tell action receipt: the delivery verdict and complete subject stand whole on the first line
925
+ * with the submitted payload folded onto that same line, bounded so only its preview truncates. A
926
+ * held Tell keeps neutral vocabulary; a failed delivery keeps its concrete diagnosis and native log
927
+ * coordinates as separate supporting facts.
967
928
  */
968
929
  export function tellText(result, alias, context) {
969
930
  const wake = result.tell.wake;
970
931
  const mark = wake.kind === "failed" ? "!" : wake.kind === "held" ? "⧗" : "✓";
971
932
  const verb = wake.kind === "failed" || wake.kind === "held" ? "tell" : "told";
972
- const verdict = actionReceipt({
933
+ const head = actionReceipt({
973
934
  mark,
974
935
  verb,
975
936
  id: result.akuma,
976
937
  ...(alias === undefined ? {} : { alias }),
977
- });
938
+ })[0];
939
+ const preview = safeText(result.tell.row.text).replace(/\s+/gu, " ").trim();
940
+ const prefix = `${head} — `;
941
+ // The verdict and identity never truncate, so an over-wide identity still keeps one visible
942
+ // ellipsis for the input it could not show.
943
+ const room = Math.max(1, context.columns - displayColumns(prefix));
944
+ const verdict = preview.length === 0 ? head : `${prefix}${truncateDisplayText(preview, room)}`;
978
945
  if (wake.kind !== "failed")
979
- return [...verdict, ...tellPayloadLines(result.tell.row.text, context)].join("\n");
946
+ return verdict;
980
947
  const child = "child" in wake ? wake.child : undefined;
981
948
  const failure = `! tell delivery failed · ${safeText(wake.diagnostic)}${child === undefined ? "" : ` · log ${child.log.path} ${child.log.from}..${child.log.to}`}`;
982
949
  return [
983
- ...verdict,
984
- ...tellPayloadLines(result.tell.row.text, context),
950
+ verdict,
985
951
  ...subordinateLines(renderBoundedTextBlock(failure, {
986
952
  first: "",
987
953
  continuation: "",
@@ -1,3 +1,4 @@
1
+ import type { CliOutcome } from "../streams.js";
1
2
  import type { LiveStatusObservation } from "../../akuma/akuma-observe.js";
2
3
  import type { TellResult } from "../../akuma/body.js";
3
4
  import type { AkuId } from "../../akuma/identity.js";
@@ -29,10 +30,10 @@ export declare function renderCallText(result: CallResult, streamed: boolean, co
29
30
  export declare function renderFailedCallObservation(result: CallResult, context: TextRenderContext): string;
30
31
  export declare function renderForkText(receipt: ForkResult): string;
31
32
  export declare function renderKillText(result: AkumaKillResult, alias: string | undefined, context: TextRenderContext): string;
32
- export declare function callExitCode(result: CallResult): number;
33
- export declare function killExitCode(result: AkumaKillResult): number;
34
- export declare function tellExitCode(result: AkumaTellResult): number;
35
- export declare function askExitCode(result: AkumaAskResult): number;
36
- export declare function forkExitCode(receipt: ForkResult): number;
37
- export declare function historyExitCode(result: AkumaHistoryResult, id: string | undefined): number;
33
+ export declare function callOutcome(result: CallResult): CliOutcome;
34
+ export declare function killOutcome(result: AkumaKillResult): CliOutcome;
35
+ export declare function tellOutcome(result: AkumaTellResult): CliOutcome;
36
+ export declare function askOutcome(result: AkumaAskResult): CliOutcome;
37
+ export declare function forkOutcome(receipt: ForkResult): CliOutcome;
38
+ export declare function historyOutcome(result: AkumaHistoryResult, id: string | undefined): CliOutcome;
38
39
  export {};