@astrosheep/keiyaku 4.5.26 → 4.5.28
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/build/integrations/marketplace/plugins/keiyaku/.claude-plugin/plugin.json +1 -1
- package/build/integrations/marketplace/plugins/keiyaku/.codex-plugin/plugin.json +1 -1
- package/build/integrations/marketplace/plugins/keiyaku/package.json +1 -1
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku/SKILL.md +3 -3
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-akuma/SKILL.md +82 -162
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-bind/SKILL.md +153 -76
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-task/SKILL.md +68 -64
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-workflow/SKILL.md +115 -146
- package/build/src/akuma/akuma-errors.d.ts +6 -0
- package/build/src/akuma/akuma-errors.js +11 -0
- package/build/src/akuma/akuma-handle.d.ts +54 -1
- package/build/src/akuma/akuma-handle.js +79 -8
- package/build/src/akuma/akuma-instance.js +6 -22
- package/build/src/akuma/akuma-observe.d.ts +6 -0
- package/build/src/akuma/akuma-observe.js +3 -1
- package/build/src/akuma/akuma-probe.d.ts +20 -1
- package/build/src/akuma/akuma-probe.js +20 -3
- package/build/src/akuma/akuma.d.ts +13 -0
- package/build/src/akuma/akuma.js +1 -0
- package/build/src/akuma/allowed.d.ts +2 -1
- package/build/src/akuma/allowed.js +9 -4
- package/build/src/akuma/archetype.js +3 -3
- package/build/src/akuma/body.js +8 -3
- package/build/src/akuma/fleet-execution.d.ts +13 -2
- package/build/src/akuma/fleet-execution.js +45 -0
- package/build/src/akuma/fleet-observation.d.ts +241 -0
- package/build/src/akuma/fleet-observation.js +13 -0
- package/build/src/akuma/fleet-owner-port.d.ts +12 -0
- package/build/src/akuma/fleet-owner-port.js +46 -0
- package/build/src/akuma/fleet-request.d.ts +55 -4
- package/build/src/akuma/fleet-request.js +101 -6
- package/build/src/akuma/heart/index.d.ts +1 -0
- package/build/src/akuma/heart/index.js +4 -1
- package/build/src/akuma/heart/rows.d.ts +1 -0
- package/build/src/akuma/heart/rows.js +8 -0
- package/build/src/akuma/heart/soul.js +2 -2
- package/build/src/akuma/identity.js +11 -9
- package/build/src/akuma/projection.d.ts +36 -0
- package/build/src/akuma/projection.js +2 -1
- package/build/src/akuma/provider.d.ts +14 -0
- package/build/src/akuma/provider.js +66 -4
- package/build/src/akuma/providers/acp/events.js +16 -8
- package/build/src/akuma/providers/claude/events.js +5 -7
- package/build/src/akuma/providers/opencode-sdk/events.js +9 -5
- package/build/src/akuma/providers/pi/events.js +4 -3
- package/build/src/akuma/request-lifecycle.js +10 -1
- package/build/src/akuma-body.js +1 -33
- package/build/src/alias/index.js +3 -3
- package/build/src/cli/commands/akuma-invoke.d.ts +8 -0
- package/build/src/cli/commands/akuma-invoke.js +67 -6
- package/build/src/cli/commands/akuma.d.ts +1 -0
- package/build/src/cli/commands/akuma.js +19 -30
- package/build/src/cli/commands/contract-help.d.ts +1 -0
- package/build/src/cli/commands/contract-help.js +9 -5
- package/build/src/cli/invoke.js +1 -1
- package/build/src/cli/render/akuma-activity.d.ts +7 -1
- package/build/src/cli/render/akuma-activity.js +164 -50
- package/build/src/cli/render/akuma-tool.d.ts +5 -0
- package/build/src/cli/render/akuma-tool.js +118 -14
- package/build/src/cli/render/akuma.d.ts +12 -1
- package/build/src/cli/render/akuma.js +74 -2
- package/build/src/cli/render/catalog.js +2 -3
- package/build/src/cli/render/contract-observation.d.ts +1 -0
- package/build/src/cli/render/contract-observation.js +20 -1
- package/build/src/cli/render/contract.js +4 -4
- package/build/src/cli/render/execution-progress.d.ts +1 -0
- package/build/src/cli/render/execution-progress.js +77 -3
- package/build/src/cli/render/kanshi.js +5 -19
- package/build/src/cli/render/terminal.js +3 -1
- package/build/src/cli/runtime.d.ts +6 -0
- package/build/src/cli/runtime.js +35 -17
- package/build/src/core/facts/offer.d.ts +1 -2
- package/build/src/core/verbs/abandon.js +1 -1
- package/build/src/core/verbs/amend.js +1 -1
- package/build/src/core/verbs/arc.js +1 -1
- package/build/src/core/verbs/attestation.js +1 -1
- package/build/src/core/verbs/bind.js +1 -1
- package/build/src/core/verbs/deliver.js +1 -1
- package/build/src/core/verbs/placement.js +1 -1
- package/build/src/core/verbs/reintegrate.js +1 -1
- package/build/src/git/admission.js +18 -7
- package/build/src/identity/coordinates.js +1 -1
- package/build/src/identity/normalize.d.ts +3 -0
- package/build/src/identity/normalize.js +10 -0
- package/build/src/identity/selector.d.ts +1 -0
- package/build/src/identity/selector.js +15 -4
- package/build/src/library/address.d.ts +33 -3
- package/build/src/library/address.js +90 -29
- package/build/src/library/akuma-creation.d.ts +1 -1
- package/build/src/library/akuma-creation.js +10 -52
- package/build/src/library/composition.d.ts +14 -1
- package/build/src/library/fleet.d.ts +13 -2
- package/build/src/library/fleet.js +142 -48
- package/build/src/library/keiyaku.d.ts +14 -1
- package/build/src/plugin/public.d.ts +2 -0
- package/build/src/protocol/completion.js +9 -2
- package/build/src/runtime/proc/windows-launch.exe +0 -0
- package/package.json +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { parseAkumaStatus } from "../../akuma/akuma.js";
|
|
2
2
|
import { defaultWaitComplete } from "../../akuma/akuma-observe.js";
|
|
3
|
-
import { toolRepr } from "./akuma-tool.js";
|
|
4
|
-
import { displayColumns, renderBoundedTextBlock, safeText, takeDisplayColumns, truncateMiddleDisplayText, } from "./terminal.js";
|
|
3
|
+
import { toolContent, toolRepr } from "./akuma-tool.js";
|
|
4
|
+
import { displayColumns, renderBoundedTextBlock, safeText, takeDisplayColumns, truncateDisplayText, truncateMiddleDisplayText, } from "./terminal.js";
|
|
5
5
|
export const DEFAULT_CONTEXT = { columns: 80, color: false };
|
|
6
6
|
const TIME_WIDTH = 5;
|
|
7
7
|
const VERB_WIDTH = 6;
|
|
@@ -38,7 +38,7 @@ function contractFacts(contract) {
|
|
|
38
38
|
return contract.kind === "failed" ? [`! contract failed ${safeText(contract.diagnostic)}`] : [];
|
|
39
39
|
}
|
|
40
40
|
function unobservedText(id, diagnostic) {
|
|
41
|
-
return
|
|
41
|
+
return `× Akuma observation failed ${safeText(id)} — ${safeText(diagnostic)}`;
|
|
42
42
|
}
|
|
43
43
|
function lifeLabel(life) {
|
|
44
44
|
if (life === "running")
|
|
@@ -57,7 +57,7 @@ function clock(at) {
|
|
|
57
57
|
? `${String(date.getHours()).padStart(2, "0")}:${String(date.getMinutes()).padStart(2, "0")}`
|
|
58
58
|
: "unknown";
|
|
59
59
|
}
|
|
60
|
-
function label(row) {
|
|
60
|
+
function label(row, tool) {
|
|
61
61
|
if (row.kind === "said")
|
|
62
62
|
return "say";
|
|
63
63
|
if (row.kind === "thought")
|
|
@@ -74,7 +74,7 @@ function label(row) {
|
|
|
74
74
|
return "call";
|
|
75
75
|
if (row.kind !== "tool")
|
|
76
76
|
return row.kind;
|
|
77
|
-
return
|
|
77
|
+
return tool.label;
|
|
78
78
|
}
|
|
79
79
|
function mark(row) {
|
|
80
80
|
if (row.kind === "outcome")
|
|
@@ -92,7 +92,7 @@ function mark(row) {
|
|
|
92
92
|
}
|
|
93
93
|
return "│";
|
|
94
94
|
}
|
|
95
|
-
function rowText(row) {
|
|
95
|
+
function rowText(row, tool) {
|
|
96
96
|
if (row.kind === "said" ||
|
|
97
97
|
row.kind === "thought" ||
|
|
98
98
|
row.kind === "note" ||
|
|
@@ -111,40 +111,62 @@ function rowText(row) {
|
|
|
111
111
|
return { text: "", lines: 1 };
|
|
112
112
|
if (row.kind !== "tool")
|
|
113
113
|
return { text: "", lines: 1 };
|
|
114
|
-
|
|
114
|
+
if (row.call.kind === "other")
|
|
115
|
+
return { text: "", lines: 1 };
|
|
116
|
+
const repr = tool;
|
|
115
117
|
return {
|
|
116
118
|
text: repr.text,
|
|
117
|
-
lines:
|
|
119
|
+
lines: 1,
|
|
118
120
|
...(repr.overflow === "middle-ellipsis" ? { middle: true } : {}),
|
|
119
121
|
...(repr.suffix === undefined ? {} : { suffix: repr.suffix }),
|
|
120
122
|
};
|
|
121
123
|
}
|
|
122
|
-
function eventPrefix(glyph, verb, time) {
|
|
124
|
+
function eventPrefix(glyph, verb, time, columns) {
|
|
123
125
|
const gutter = time === undefined ? " ".repeat(TIME_WIDTH) : time.padEnd(TIME_WIDTH);
|
|
124
|
-
return `${gutter} ${glyph}
|
|
126
|
+
return actionCell(`${gutter} ${glyph}`, verb, columns);
|
|
127
|
+
}
|
|
128
|
+
/** The action column one row spends: six cells by default, or the name's own width when longer. */
|
|
129
|
+
function verbColumn(verb) {
|
|
130
|
+
return Math.max(VERB_WIDTH, displayColumns(verb));
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Attach the action cell after one row prefix. A name wider than the row's
|
|
134
|
+
* remaining columns truncates grapheme-safely, spending every column the row
|
|
135
|
+
* leaves after the separator so an exact-fit name stays whole and args trim first.
|
|
136
|
+
*/
|
|
137
|
+
function actionCell(head, verb, columns) {
|
|
138
|
+
const prefix = `${head} `;
|
|
139
|
+
const available = Math.max(0, columns - displayColumns(prefix) - 1);
|
|
140
|
+
const width = Math.min(verbColumn(verb), available);
|
|
141
|
+
return `${prefix}${padToDisplay(truncateDisplayText(verb, width), width)} `;
|
|
125
142
|
}
|
|
126
143
|
function continuationPrefix() {
|
|
127
|
-
return
|
|
144
|
+
return " ".repeat(TIME_WIDTH) + " │ " + " ".repeat(VERB_WIDTH) + " ";
|
|
128
145
|
}
|
|
129
146
|
/** Pad to a terminal-column width; raw string length is never the measuring stick. */
|
|
130
147
|
function padToDisplay(text, width) {
|
|
131
148
|
const remaining = width - displayColumns(text);
|
|
132
149
|
return remaining > 0 ? `${text}${" ".repeat(remaining)}` : text;
|
|
133
150
|
}
|
|
151
|
+
function timelineMarker(text) {
|
|
152
|
+
return `${" ".repeat(TIME_WIDTH)} ⋮ ${text}`;
|
|
153
|
+
}
|
|
134
154
|
function plainLayout() {
|
|
135
155
|
return {
|
|
136
|
-
head: (time, glyph, verb) => eventPrefix(glyph, verb, time),
|
|
156
|
+
head: (time, glyph, verb, columns) => eventPrefix(glyph, verb, time, columns),
|
|
137
157
|
continuation: continuationPrefix,
|
|
138
|
-
marker: (count) => `${
|
|
158
|
+
marker: (count) => timelineMarker(`${count} omitted`),
|
|
139
159
|
};
|
|
140
160
|
}
|
|
141
|
-
function sourceLayout(source, width) {
|
|
161
|
+
function sourceLayout(source, width, clock) {
|
|
142
162
|
const gutter = () => `${" ".repeat(TIME_WIDTH)} ${padToDisplay(source, width())} `;
|
|
143
163
|
return {
|
|
144
|
-
head: (time, glyph, verb) => `${time === undefined ? " ".repeat(TIME_WIDTH) : time.padEnd(TIME_WIDTH)} ${padToDisplay(source, width())} ${glyph}
|
|
164
|
+
head: (time, glyph, verb, columns) => actionCell(`${time === undefined ? " ".repeat(TIME_WIDTH) : time.padEnd(TIME_WIDTH)} ${padToDisplay(source, width())} ${glyph}`, verb, columns),
|
|
145
165
|
// Continuations blank the time and source columns and align under the mark.
|
|
146
166
|
continuation: () => `${" ".repeat(TIME_WIDTH)} ${" ".repeat(width())} │ ${" ".repeat(VERB_WIDTH)} `,
|
|
147
167
|
marker: (count) => `${gutter()}⋮ ${count} omitted`,
|
|
168
|
+
clock,
|
|
169
|
+
singleLine: true,
|
|
148
170
|
};
|
|
149
171
|
}
|
|
150
172
|
function quotedBody(row) {
|
|
@@ -172,14 +194,35 @@ function renderMiddleEllipsis(first, text, suffix, columns) {
|
|
|
172
194
|
const showSuffix = suffix.length > 0 && withSuffix >= 6;
|
|
173
195
|
return `${first}${truncateMiddleDisplayText(text, Math.max(0, showSuffix ? withSuffix : remaining))}${showSuffix ? suffix : ""}`;
|
|
174
196
|
}
|
|
175
|
-
function
|
|
176
|
-
|
|
197
|
+
function rowBody(row, text, columns) {
|
|
198
|
+
if (row.kind !== "tool" || row.call.kind !== "other")
|
|
199
|
+
return text;
|
|
200
|
+
return toolContent(row, columns);
|
|
201
|
+
}
|
|
202
|
+
function renderRow(row, context, options) {
|
|
203
|
+
const { history, first, continuation, tool, singleLine = false, inFlightSay = false } = options;
|
|
204
|
+
const value = rowText(row, tool);
|
|
177
205
|
const quoted = quotedBody(row);
|
|
206
|
+
if (singleLine) {
|
|
207
|
+
const openQuote = row.kind === "said" && inFlightSay;
|
|
208
|
+
const quoteWidth = quoted ? (openQuote ? 1 : 2) : 0;
|
|
209
|
+
const remaining = context.columns - displayColumns(first) - quoteWidth;
|
|
210
|
+
const bodyText = rowBody(row, value.text, remaining);
|
|
211
|
+
const text = truncateDisplayText(bodyText, Math.max(0, remaining));
|
|
212
|
+
if (!quoted)
|
|
213
|
+
return [text.length === 0 ? first.trimEnd() : `${first}${text}`];
|
|
214
|
+
return [`${first}"${text}${openQuote ? "" : '"'}`];
|
|
215
|
+
}
|
|
178
216
|
const quoteWidth = quoted ? 2 : 0;
|
|
179
217
|
if (value.middle === true) {
|
|
180
218
|
return [renderMiddleEllipsis(first, value.text, value.suffix ?? "", context.columns - quoteWidth)];
|
|
181
219
|
}
|
|
182
|
-
const
|
|
220
|
+
const remaining = context.columns - quoteWidth - displayColumns(first);
|
|
221
|
+
// A name that already fills its row spends the width whole; its arguments trim away entirely.
|
|
222
|
+
if (remaining <= 0)
|
|
223
|
+
return [first.trimEnd()];
|
|
224
|
+
const bodyText = rowBody(row, value.text, remaining - displayColumns(value.suffix ?? ""));
|
|
225
|
+
const lines = renderBoundedTextBlock(bodyText, {
|
|
183
226
|
first,
|
|
184
227
|
continuation,
|
|
185
228
|
columns: context.columns - quoteWidth,
|
|
@@ -199,7 +242,14 @@ function groupedEntries(entries, context, history = false, layout = plainLayout(
|
|
|
199
242
|
const row = entry.row;
|
|
200
243
|
const at = clock(row.at);
|
|
201
244
|
const changed = previousClock === undefined || at !== previousClock;
|
|
202
|
-
|
|
245
|
+
const tool = row.kind === "tool" ? toolRepr(row) : undefined;
|
|
246
|
+
lines.push(...renderRow(row, context, {
|
|
247
|
+
history,
|
|
248
|
+
first: layout.head(changed ? at : undefined, mark(row), label(row, tool), context.columns),
|
|
249
|
+
continuation: layout.continuation(),
|
|
250
|
+
tool,
|
|
251
|
+
singleLine: layout.singleLine === true,
|
|
252
|
+
}));
|
|
203
253
|
previousClock = at;
|
|
204
254
|
}
|
|
205
255
|
return lines;
|
|
@@ -307,11 +357,27 @@ function baselineOmissionCount(activity, boundary) {
|
|
|
307
357
|
}
|
|
308
358
|
return count;
|
|
309
359
|
}
|
|
310
|
-
function renderStreamRow(state, row, lines,
|
|
360
|
+
function renderStreamRow(state, row, lines, options) {
|
|
361
|
+
const { context, layout, inFlightSay = false } = options;
|
|
311
362
|
const at = clock(row.at);
|
|
312
|
-
const
|
|
313
|
-
|
|
314
|
-
|
|
363
|
+
const previousClock = layout.clock?.previous ?? state.previousClock;
|
|
364
|
+
const changed = previousClock === undefined || at !== previousClock;
|
|
365
|
+
const tool = row.kind === "tool" ? toolRepr(row) : undefined;
|
|
366
|
+
lines.push(...renderRow(row, context, {
|
|
367
|
+
history: false,
|
|
368
|
+
first: layout.head(changed ? at : undefined, inFlightSay ? "⧖" : mark(row), label(row, tool), context.columns),
|
|
369
|
+
continuation: layout.continuation(),
|
|
370
|
+
tool,
|
|
371
|
+
singleLine: layout.singleLine === true,
|
|
372
|
+
inFlightSay,
|
|
373
|
+
}));
|
|
374
|
+
if (layout.clock !== undefined)
|
|
375
|
+
layout.clock.previous = at;
|
|
376
|
+
else
|
|
377
|
+
state.previousClock = at;
|
|
378
|
+
}
|
|
379
|
+
function inFlightSay(activity, row) {
|
|
380
|
+
return activity.snapshot.kind === "open" && row.kind === "said";
|
|
315
381
|
}
|
|
316
382
|
function coalesceDeferredGaps(state) {
|
|
317
383
|
state.deferred = state.deferred.reduce((entries, entry) => {
|
|
@@ -340,7 +406,7 @@ function flushSafeActivityPrefix(state, lines, context, layout) {
|
|
|
340
406
|
return;
|
|
341
407
|
if (first.kind === "row") {
|
|
342
408
|
state.deferred.shift();
|
|
343
|
-
renderStreamRow(state, first.row, lines, context, layout);
|
|
409
|
+
renderStreamRow(state, first.row, lines, { context, layout, inFlightSay: first.inFlightSay });
|
|
344
410
|
continue;
|
|
345
411
|
}
|
|
346
412
|
const next = state.deferred[1];
|
|
@@ -355,7 +421,7 @@ function renderCurrentTurnBoundary(state, activity, lines, context, layout) {
|
|
|
355
421
|
if (boundary !== undefined && !state.renderedBoundaries.has(boundary.turnSequence)) {
|
|
356
422
|
state.renderedBoundaries.add(boundary.turnSequence);
|
|
357
423
|
if (boundary.row.kind !== "thought")
|
|
358
|
-
renderStreamRow(state, boundary.row, lines, context, layout);
|
|
424
|
+
renderStreamRow(state, boundary.row, lines, { context, layout });
|
|
359
425
|
}
|
|
360
426
|
return boundary;
|
|
361
427
|
}
|
|
@@ -383,14 +449,14 @@ function observeActivitySnapshot(state, activity, context, layout) {
|
|
|
383
449
|
for (const row of rows) {
|
|
384
450
|
if (isBoundedStreamTool(row) && state.openingTools < OPENING_TOOL_BUDGET) {
|
|
385
451
|
state.openingTools += 1;
|
|
386
|
-
renderStreamRow(state, row, lines, context, layout);
|
|
452
|
+
renderStreamRow(state, row, lines, { context, layout, inFlightSay: inFlightSay(activity, row) });
|
|
387
453
|
continue;
|
|
388
454
|
}
|
|
389
455
|
if (!isBoundedStreamTool(row) && state.deferred.length === 0) {
|
|
390
|
-
renderStreamRow(state, row, lines, context, layout);
|
|
456
|
+
renderStreamRow(state, row, lines, { context, layout, inFlightSay: inFlightSay(activity, row) });
|
|
391
457
|
continue;
|
|
392
458
|
}
|
|
393
|
-
state.deferred.push({ kind: "row", row });
|
|
459
|
+
state.deferred.push({ kind: "row", row, inFlightSay: inFlightSay(activity, row) });
|
|
394
460
|
if (isBoundedStreamTool(row)) {
|
|
395
461
|
const pendingTools = state.deferred.filter((entry) => entry.kind === "row" && isBoundedStreamTool(entry.row)).length;
|
|
396
462
|
if (pendingTools > RECENT_TOOL_BUDGET)
|
|
@@ -406,7 +472,7 @@ function flushActivityTail(state, context, layout) {
|
|
|
406
472
|
if (entry.kind === "gap")
|
|
407
473
|
lines.push(layout.marker(entry.count));
|
|
408
474
|
else
|
|
409
|
-
renderStreamRow(state, entry.row, lines, context, layout);
|
|
475
|
+
renderStreamRow(state, entry.row, lines, { context, layout, inFlightSay: entry.inFlightSay });
|
|
410
476
|
}
|
|
411
477
|
state.deferred = [];
|
|
412
478
|
return lines;
|
|
@@ -492,14 +558,50 @@ function createWaitObservationState() {
|
|
|
492
558
|
return {
|
|
493
559
|
streams: new Map(),
|
|
494
560
|
sources: new Map(),
|
|
561
|
+
tags: new Map(),
|
|
495
562
|
contracts: new Map(),
|
|
496
563
|
settledAt: new Map(),
|
|
497
564
|
sourceWidth: 0,
|
|
565
|
+
clock: {},
|
|
498
566
|
plural: undefined,
|
|
499
567
|
headerEmitted: false,
|
|
500
568
|
observed: false,
|
|
501
569
|
};
|
|
502
570
|
}
|
|
571
|
+
function shortestUniquePrefix(value, values) {
|
|
572
|
+
for (let length = 4; length <= value.length; length += 1) {
|
|
573
|
+
const tag = value.slice(0, length);
|
|
574
|
+
if (values.filter((candidate) => candidate.slice(0, length) === tag).length === 1)
|
|
575
|
+
return tag;
|
|
576
|
+
}
|
|
577
|
+
return undefined;
|
|
578
|
+
}
|
|
579
|
+
function shortestUniqueSegmentSuffix(id, ids) {
|
|
580
|
+
const segments = id.split("/");
|
|
581
|
+
for (let start = segments.length - 2; start >= 0; start -= 1) {
|
|
582
|
+
const tag = segments.slice(start).join("/");
|
|
583
|
+
if (ids.filter((candidate) => candidate.endsWith(`/${tag}`) || candidate === tag).length === 1)
|
|
584
|
+
return tag;
|
|
585
|
+
}
|
|
586
|
+
return undefined;
|
|
587
|
+
}
|
|
588
|
+
function waitIdentityTag(id, ids) {
|
|
589
|
+
const finals = ids.map((candidate) => candidate.slice(candidate.lastIndexOf("/") + 1));
|
|
590
|
+
const index = ids.indexOf(id);
|
|
591
|
+
const own = finals[index];
|
|
592
|
+
if (own === undefined)
|
|
593
|
+
return id;
|
|
594
|
+
return (shortestUniquePrefix(own, finals) ?? shortestUniqueSegmentSuffix(id, ids) ?? shortestUniquePrefix(id, ids) ?? id);
|
|
595
|
+
}
|
|
596
|
+
/** The selected set fixes identity tags before its first attributed row. */
|
|
597
|
+
function freezeWaitTags(state) {
|
|
598
|
+
const ids = [...state.sources.keys()];
|
|
599
|
+
state.tags = new Map(ids.map((id) => [id, waitIdentityTag(id, ids)]));
|
|
600
|
+
state.sourceWidth = Math.max(0, ...Array.from(state.tags.values(), displayColumns));
|
|
601
|
+
}
|
|
602
|
+
function sourceTag(state, id) {
|
|
603
|
+
return state.tags.get(id) ?? waitIdentityTag(id, [...state.sources.keys()]);
|
|
604
|
+
}
|
|
503
605
|
/** Register one selected or newly observed source; the set freezes the column before the first row. */
|
|
504
606
|
function registerWaitSource(state, id, alias, contract) {
|
|
505
607
|
if (state.sources.has(id))
|
|
@@ -508,8 +610,6 @@ function registerWaitSource(state, id, alias, contract) {
|
|
|
508
610
|
state.sources.set(id, label);
|
|
509
611
|
if (contract !== undefined)
|
|
510
612
|
state.contracts.set(id, contract);
|
|
511
|
-
if (!state.headerEmitted)
|
|
512
|
-
state.sourceWidth = Math.max(state.sourceWidth, displayColumns(label));
|
|
513
613
|
}
|
|
514
614
|
/**
|
|
515
615
|
* The one aggregate head a plural wait prints before any activity row: the
|
|
@@ -520,7 +620,8 @@ function registerWaitSource(state, id, alias, contract) {
|
|
|
520
620
|
function aggregateHeading(state) {
|
|
521
621
|
const head = [...state.sources].map(([id, label]) => {
|
|
522
622
|
const contractId = associatedContractId(state.contracts.get(id) ?? { kind: "none" });
|
|
523
|
-
|
|
623
|
+
const named = `${sourceTag(state, id)} ${label}`;
|
|
624
|
+
return contractId === undefined ? named : `${named} · ${contractId}`;
|
|
524
625
|
});
|
|
525
626
|
return [...head, frameRule(head)];
|
|
526
627
|
}
|
|
@@ -529,6 +630,8 @@ function observeWaitRound(state, round, context, now) {
|
|
|
529
630
|
// Establish the whole round's sources before any row so widths stay aligned within it.
|
|
530
631
|
for (const member of round)
|
|
531
632
|
registerWaitSource(state, member.status.id, member.alias, member.contract);
|
|
633
|
+
if (!state.headerEmitted)
|
|
634
|
+
freezeWaitTags(state);
|
|
532
635
|
const lines = [];
|
|
533
636
|
// The observation subject opens once: an aggregate head for a plural set, the observed
|
|
534
637
|
// identity frame for a single target. Every later round only appends attributed rows.
|
|
@@ -551,7 +654,7 @@ function observeWaitRound(state, round, context, now) {
|
|
|
551
654
|
else {
|
|
552
655
|
// Only a plural wait attributes its rows; a single-target stream keeps the plain row grammar.
|
|
553
656
|
const stream = activityStream(context, state.plural === true
|
|
554
|
-
? sourceLayout(state
|
|
657
|
+
? sourceLayout(sourceTag(state, status.id), () => state.sourceWidth, state.clock)
|
|
555
658
|
: plainLayout());
|
|
556
659
|
state.streams.set(status.id, stream);
|
|
557
660
|
// Seed marks skipped retained evidence without spending this command's live tool budget.
|
|
@@ -595,15 +698,20 @@ function concludeWaitStream(state, result, startedAt, now) {
|
|
|
595
698
|
for (const stream of state.streams.values())
|
|
596
699
|
tail.push(...stream.flush());
|
|
597
700
|
const end = now();
|
|
701
|
+
for (const observation of result.observations)
|
|
702
|
+
registerWaitSource(state, observation.status.id, undefined, observation.contract);
|
|
703
|
+
for (const member of result.unobserved)
|
|
704
|
+
registerWaitSource(state, member.id, undefined);
|
|
705
|
+
if (!state.headerEmitted)
|
|
706
|
+
freezeWaitTags(state);
|
|
598
707
|
const order = orderedWaitIds(state, result);
|
|
599
708
|
const multi = order.length > 1;
|
|
600
709
|
const observationById = new Map(result.observations.map((observation) => [observation.status.id, observation]));
|
|
601
710
|
const unobservedById = new Map(result.unobserved.map((member) => [member.id, member]));
|
|
602
|
-
// A
|
|
603
|
-
const label = (id) => (multi ? (state.sources.get(id) ?? id) : id);
|
|
711
|
+
// A failure fact keeps the complete identity even where the scoreboard attributes a target by its identity tag.
|
|
604
712
|
const unobservedLines = order
|
|
605
713
|
.filter((id) => unobservedById.has(id))
|
|
606
|
-
.map((id) => unobservedText(
|
|
714
|
+
.map((id) => unobservedText(id, unobservedById.get(id).diagnostic));
|
|
607
715
|
const conclusions = order.flatMap((id) => {
|
|
608
716
|
const observation = observationById.get(id);
|
|
609
717
|
if (observation === undefined)
|
|
@@ -612,7 +720,7 @@ function concludeWaitStream(state, result, startedAt, now) {
|
|
|
612
720
|
const complete = defaultWaitComplete(status);
|
|
613
721
|
const at = complete ? (state.settledAt.get(id) ?? end) : end;
|
|
614
722
|
const { mark, verb } = conclusionMarkVerb(status, statusAnswer(observation) !== undefined);
|
|
615
|
-
const target = multi ? ` ${padToDisplay(state
|
|
723
|
+
const target = multi ? ` ${padToDisplay(sourceTag(state, id), state.sourceWidth)}` : "";
|
|
616
724
|
return [`${clockFromMs(at)}${target} ${mark} ${verb}${conclusionClause(at, startedAt, complete, end)}`];
|
|
617
725
|
});
|
|
618
726
|
const answeredSingle = !multi && result.observations.length === 1 && statusAnswer(result.observations[0]) !== undefined;
|
|
@@ -643,6 +751,7 @@ export function waitObservationStream(context, options = {}) {
|
|
|
643
751
|
for (const member of selected)
|
|
644
752
|
registerWaitSource(state, member.id, member.alias, member.contract);
|
|
645
753
|
state.plural ??= selected.length > 1;
|
|
754
|
+
freezeWaitTags(state);
|
|
646
755
|
};
|
|
647
756
|
const observe = (round) => observeWaitRound(state, round, context, now);
|
|
648
757
|
const conclude = (result) => concludeWaitStream(state, result, startedAt, now);
|
|
@@ -763,6 +872,7 @@ function answeredBlock(observation, answer, alias, columns) {
|
|
|
763
872
|
function snapshotCore(view, context, options) {
|
|
764
873
|
const activity = snapshotActivityLines(view.status.timeline, context);
|
|
765
874
|
const facts = [
|
|
875
|
+
...(view.status.cwd === undefined ? [] : [`cwd ${safeText(view.status.cwd)}`]),
|
|
766
876
|
...(options.showAllowed === true ? [`allowed ${view.status.allowed.join(", ") || "none"}`] : []),
|
|
767
877
|
...(view.status.readonly?.enforcement === "none" ? [`! ${safeText(view.status.readonly.diagnostic)}`] : []),
|
|
768
878
|
...contractFacts(view.contract),
|
|
@@ -871,23 +981,19 @@ export function waitText(result, context) {
|
|
|
871
981
|
const end = Date.now();
|
|
872
982
|
const startedAt = result.startedAt ?? end;
|
|
873
983
|
const order = [];
|
|
874
|
-
const
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
return;
|
|
878
|
-
labels.set(id, frozenAlias ?? id);
|
|
879
|
-
order.push(id);
|
|
984
|
+
const remember = (id) => {
|
|
985
|
+
if (!order.includes(id))
|
|
986
|
+
order.push(id);
|
|
880
987
|
};
|
|
881
988
|
for (const member of result.selection ?? [])
|
|
882
|
-
remember(member.id
|
|
989
|
+
remember(member.id);
|
|
883
990
|
for (const observation of observations)
|
|
884
991
|
remember(observation.status.id);
|
|
885
992
|
for (const member of unobserved)
|
|
886
993
|
remember(member.id);
|
|
887
|
-
const label = (id) => labels.get(id) ?? id;
|
|
888
994
|
const observationById = new Map(observations.map((observation) => [observation.status.id, observation]));
|
|
889
995
|
const unobservedById = new Map(unobserved.map((member) => [member.id, member]));
|
|
890
|
-
const sourceWidth = order.
|
|
996
|
+
const sourceWidth = Math.max(0, ...order.map((id) => displayColumns(waitIdentityTag(id, order))));
|
|
891
997
|
const blocks = [
|
|
892
998
|
...order.flatMap((id) => {
|
|
893
999
|
const observation = observationById.get(id);
|
|
@@ -902,7 +1008,7 @@ export function waitText(result, context) {
|
|
|
902
1008
|
? [
|
|
903
1009
|
order
|
|
904
1010
|
.filter((id) => unobservedById.has(id))
|
|
905
|
-
.map((id) => unobservedText(
|
|
1011
|
+
.map((id) => unobservedText(id, unobservedById.get(id).diagnostic))
|
|
906
1012
|
.join("\n"),
|
|
907
1013
|
]
|
|
908
1014
|
: []),
|
|
@@ -916,7 +1022,7 @@ export function waitText(result, context) {
|
|
|
916
1022
|
const at = complete ? (settleMoment(status) ?? end) : end;
|
|
917
1023
|
const { mark, verb } = conclusionMarkVerb(status, statusAnswer(observation) !== undefined);
|
|
918
1024
|
return [
|
|
919
|
-
`${clockFromMs(at)} ${padToDisplay(
|
|
1025
|
+
`${clockFromMs(at)} ${padToDisplay(waitIdentityTag(id, order), sourceWidth)} ${mark} ${verb}${conclusionClause(at, startedAt, complete, end)}`,
|
|
920
1026
|
];
|
|
921
1027
|
});
|
|
922
1028
|
return rows.length > 0 ? [...blocks, rows.join("\n")].join("\n\n") : blocks.join("\n\n");
|
|
@@ -934,14 +1040,22 @@ export function historyText(command, result, context) {
|
|
|
934
1040
|
throw new Error("history result lacks page");
|
|
935
1041
|
const rows = groupedRows(result.history.rows, context, true);
|
|
936
1042
|
const paging = result.history.omitted > 0
|
|
937
|
-
? [
|
|
1043
|
+
? [
|
|
1044
|
+
timelineMarker(result.history.hasLater
|
|
1045
|
+
? `${result.history.omitted} later events · showing first ${result.history.rows.length}`
|
|
1046
|
+
: `${result.history.omitted} earlier events · showing last ${result.history.rows.length}`),
|
|
1047
|
+
]
|
|
938
1048
|
: [];
|
|
939
1049
|
return [...snapshotHeading(result.akuma, result.alias, result.historyResult.contract), ...paging, ...rows].join("\n");
|
|
940
1050
|
}
|
|
941
1051
|
export function tellText(result, context) {
|
|
942
1052
|
const wake = result.result.tell.wake;
|
|
943
1053
|
const target = identity(result.result.akuma, result.alias);
|
|
944
|
-
const row = groupedRows([result.result.tell.row], context
|
|
1054
|
+
const row = groupedRows([result.result.tell.row], context, false, {
|
|
1055
|
+
...plainLayout(),
|
|
1056
|
+
head: (time, _glyph, _verb, columns) => eventPrefix(wake.kind === "held" ? "⧗" : "⧖", "tell", time, columns),
|
|
1057
|
+
singleLine: true,
|
|
1058
|
+
}).join("\n");
|
|
945
1059
|
if (wake.kind === "failed") {
|
|
946
1060
|
const child = "child" in wake ? wake.child : undefined;
|
|
947
1061
|
const failure = `! tell delivery failed · ${safeText(wake.diagnostic)}${child === undefined ? "" : ` · log ${child.log.path} ${child.log.from}..${child.log.to}`}`;
|
|
@@ -6,6 +6,11 @@ type FleetTimelineRow = Extract<AkumaObservation["status"]["timeline"]["entries"
|
|
|
6
6
|
type ToolRow = Extract<ActivityRow | SnapshotRow | FleetTimelineRow, {
|
|
7
7
|
kind: "tool";
|
|
8
8
|
}>;
|
|
9
|
+
/** The bounded body of a generic tool row, including its terminal diagnostic. */
|
|
10
|
+
export declare function toolContent(row: ToolRow, columns: number): string;
|
|
11
|
+
export declare function toolLabel(row: ToolRow): string;
|
|
12
|
+
/** The trailing failure, duration, or message clause one settled tool row carries, otherwise the empty string. */
|
|
13
|
+
export declare function toolDiagnostic(row: ToolRow): string;
|
|
9
14
|
export type ToolRepr = Readonly<{
|
|
10
15
|
label: string;
|
|
11
16
|
text: string;
|
|
@@ -1,4 +1,71 @@
|
|
|
1
|
+
import { displayColumns, truncateDisplayText } from "./terminal.js";
|
|
1
2
|
import { normalizeToolCommand } from "./akuma-tool-command.js";
|
|
3
|
+
/** The bounded body of a generic tool row, including its terminal diagnostic. */
|
|
4
|
+
export function toolContent(row, columns) {
|
|
5
|
+
const input = row.call.kind === "other" ? row.call.input : undefined;
|
|
6
|
+
const diagnostic = toolDiagnostic(row);
|
|
7
|
+
const width = Math.max(0, columns - displayColumns(diagnostic));
|
|
8
|
+
let text;
|
|
9
|
+
if (input === undefined) {
|
|
10
|
+
text = "";
|
|
11
|
+
}
|
|
12
|
+
else {
|
|
13
|
+
const common = commonOtherText(row);
|
|
14
|
+
if (common !== undefined)
|
|
15
|
+
text = common;
|
|
16
|
+
else if (input.json === "{}")
|
|
17
|
+
text = "";
|
|
18
|
+
else if (input.truncated)
|
|
19
|
+
text = `${input.json}…`;
|
|
20
|
+
else {
|
|
21
|
+
const value = jsonObject(input.json);
|
|
22
|
+
text = value === undefined ? input.json : objectFieldsText(value, width);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return `${truncateDisplayText(text, width)}${diagnostic}`;
|
|
26
|
+
}
|
|
27
|
+
export function toolLabel(row) {
|
|
28
|
+
return toolCore(row).label;
|
|
29
|
+
}
|
|
30
|
+
/** The trailing failure, duration, or message clause one settled tool row carries, otherwise the empty string. */
|
|
31
|
+
export function toolDiagnostic(row) {
|
|
32
|
+
const suffix = result(row);
|
|
33
|
+
return suffix === undefined ? "" : ` — ${suffix}`;
|
|
34
|
+
}
|
|
35
|
+
function jsonObject(json) {
|
|
36
|
+
let value;
|
|
37
|
+
try {
|
|
38
|
+
value = JSON.parse(json);
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
return value !== null && typeof value === "object" && !Array.isArray(value)
|
|
44
|
+
? value
|
|
45
|
+
: undefined;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Retain leading whole fields that fit and name the trailing omit count. When
|
|
49
|
+
* even that marker cannot fit, keep the first field or the object's own prefix
|
|
50
|
+
* with a visible ellipsis, never a silently complete-looking value.
|
|
51
|
+
*/
|
|
52
|
+
function objectFieldsText(value, columns) {
|
|
53
|
+
const fields = Object.entries(value).map(([key, field]) => `${JSON.stringify(key)}:${JSON.stringify(field)}`);
|
|
54
|
+
if (fields.length === 0)
|
|
55
|
+
return "";
|
|
56
|
+
const full = `{${fields.join(",")}}`;
|
|
57
|
+
if (displayColumns(full) <= columns)
|
|
58
|
+
return full;
|
|
59
|
+
for (let count = fields.length - 1; count >= 1; count -= 1) {
|
|
60
|
+
const candidate = `{${fields.slice(0, count).join(",")}} +${fields.length - count} fields`;
|
|
61
|
+
if (displayColumns(candidate) <= columns)
|
|
62
|
+
return candidate;
|
|
63
|
+
}
|
|
64
|
+
const first = fields[0];
|
|
65
|
+
if (fields.length > 1 && displayColumns(first) <= columns)
|
|
66
|
+
return `${first}…`;
|
|
67
|
+
return truncateDisplayText(full, columns);
|
|
68
|
+
}
|
|
2
69
|
function oneLine(value) {
|
|
3
70
|
return value.replace(/\s+/gu, " ").trim();
|
|
4
71
|
}
|
|
@@ -22,7 +89,7 @@ function result(row) {
|
|
|
22
89
|
: row.state.status;
|
|
23
90
|
const parts = [
|
|
24
91
|
...(row.call.kind === "run" && row.durationMs !== undefined ? [duration(row.durationMs)] : []),
|
|
25
|
-
...(
|
|
92
|
+
...(disposition === "ok" ? [] : [disposition]),
|
|
26
93
|
...(row.state.message === undefined ? [] : [oneLine(row.state.message)]),
|
|
27
94
|
];
|
|
28
95
|
return parts.length === 0 ? undefined : parts.join(" · ");
|
|
@@ -52,6 +119,43 @@ function searchText(call) {
|
|
|
52
119
|
...(call.glob === undefined ? [] : [oneLine(call.glob)]),
|
|
53
120
|
].join(" · ");
|
|
54
121
|
}
|
|
122
|
+
function inputObject(row) {
|
|
123
|
+
if (row.call.kind !== "other" || row.call.input?.truncated === true)
|
|
124
|
+
return undefined;
|
|
125
|
+
return jsonObject(row.call.input?.json ?? "");
|
|
126
|
+
}
|
|
127
|
+
function suppliedSlice(value) {
|
|
128
|
+
const offset = typeof value.offset_chars === "number" ? `from ${value.offset_chars}` : "";
|
|
129
|
+
const limit = typeof value.limit_chars === "number" ? `${value.limit_chars} chars` : "";
|
|
130
|
+
return [offset, limit].filter(Boolean).join(" · ");
|
|
131
|
+
}
|
|
132
|
+
function commonOtherText(row) {
|
|
133
|
+
const input = inputObject(row);
|
|
134
|
+
if (input === undefined)
|
|
135
|
+
return undefined;
|
|
136
|
+
if (row.name === "get_context_remaining")
|
|
137
|
+
return undefined;
|
|
138
|
+
if (row.name === "notes_read") {
|
|
139
|
+
const address = typeof input.address === "string" ? input.address : typeof input.path === "string" ? input.path : undefined;
|
|
140
|
+
return [address, suppliedSlice(input)]
|
|
141
|
+
.filter((value) => value !== undefined && value !== "")
|
|
142
|
+
.join(" · ");
|
|
143
|
+
}
|
|
144
|
+
if (row.name === "history_read") {
|
|
145
|
+
const item = typeof input.item_id === "string" ? input.item_id : undefined;
|
|
146
|
+
const window = typeof input.window_id === "string" ? input.window_id : undefined;
|
|
147
|
+
return [item, window, suppliedSlice(input)]
|
|
148
|
+
.filter((value) => value !== undefined && value !== "")
|
|
149
|
+
.join(" · ");
|
|
150
|
+
}
|
|
151
|
+
if (row.name === "history_list") {
|
|
152
|
+
const role = typeof input.role === "string" ? `role ${input.role}` : undefined;
|
|
153
|
+
const order = input.recent_first === true ? "newest first" : input.recent_first === false ? "oldest first" : undefined;
|
|
154
|
+
const limit = typeof input.limit === "number" ? `${input.limit} rows` : undefined;
|
|
155
|
+
return [role, order, limit].filter((value) => value !== undefined).join(" · ");
|
|
156
|
+
}
|
|
157
|
+
return undefined;
|
|
158
|
+
}
|
|
55
159
|
function fileChange(call, state) {
|
|
56
160
|
const first = call.changes[0];
|
|
57
161
|
if (first === undefined)
|
|
@@ -69,30 +173,30 @@ function fileChange(call, state) {
|
|
|
69
173
|
}), { added: 0, removed: 0 });
|
|
70
174
|
return { label, text: `${subject} — +${totals.added} -${totals.removed}` };
|
|
71
175
|
}
|
|
72
|
-
/**
|
|
73
|
-
|
|
74
|
-
let core;
|
|
176
|
+
/** One provider-neutral core owns every tool-kind label and non-generic body. */
|
|
177
|
+
function toolCore(row) {
|
|
75
178
|
switch (row.call.kind) {
|
|
76
179
|
case "run":
|
|
77
|
-
|
|
180
|
+
return {
|
|
78
181
|
label: "run",
|
|
79
182
|
text: `$ ${oneLine(normalizeToolCommand(row.call.command))}`,
|
|
80
183
|
overflow: "middle-ellipsis",
|
|
81
184
|
};
|
|
82
|
-
break;
|
|
83
185
|
case "read":
|
|
84
|
-
|
|
85
|
-
break;
|
|
186
|
+
return { label: "read", text: readText(row.call) };
|
|
86
187
|
case "search":
|
|
87
|
-
|
|
88
|
-
break;
|
|
188
|
+
return { label: searchLabel(row.call.scope), text: searchText(row.call) };
|
|
89
189
|
case "fileChange":
|
|
90
|
-
|
|
91
|
-
break;
|
|
190
|
+
return fileChange(row.call, row.state);
|
|
92
191
|
case "other":
|
|
93
|
-
|
|
94
|
-
break;
|
|
192
|
+
return { label: row.name, text: "" };
|
|
95
193
|
}
|
|
194
|
+
}
|
|
195
|
+
/** Pure provider-neutral tool presentation; it performs no activity selection. */
|
|
196
|
+
export function toolRepr(row) {
|
|
197
|
+
const core = toolCore(row);
|
|
198
|
+
if (row.call.kind === "other")
|
|
199
|
+
return core;
|
|
96
200
|
const suffix = result(row);
|
|
97
201
|
if (suffix === undefined)
|
|
98
202
|
return core;
|
|
@@ -1,8 +1,19 @@
|
|
|
1
1
|
import type { AkumaInvocationResult } from "../commands/akuma-invoke.js";
|
|
2
2
|
import type { ParsedCommand } from "../parse.js";
|
|
3
3
|
import { type ObservedCallHead } from "./akuma-activity.js";
|
|
4
|
+
import type { AkumaTellWaitResult } from "../../akuma/fleet-observation.js";
|
|
5
|
+
import type { AkuId } from "../../akuma/identity.js";
|
|
6
|
+
import type { LiveStatusObservation } from "../../akuma/akuma-observe.js";
|
|
7
|
+
import type { TellResult } from "../../akuma/akuma.js";
|
|
4
8
|
import { type TextRenderContext } from "./terminal.js";
|
|
5
|
-
export
|
|
9
|
+
export type TellWaitProgress = Readonly<{
|
|
10
|
+
admitted: (tell: TellResult, id: AkuId) => readonly string[];
|
|
11
|
+
observe: (observation: LiveStatusObservation) => readonly string[];
|
|
12
|
+
conclude: (result: AkumaTellWaitResult) => readonly string[];
|
|
13
|
+
}>;
|
|
14
|
+
export declare function tellWaitProgressStream(akuma: AkuId | undefined, alias: string | undefined, context: TextRenderContext): TellWaitProgress;
|
|
15
|
+
export declare function waitedTellProgress(result: AkumaTellWaitResult, alias: string | undefined, context: TextRenderContext): string;
|
|
16
|
+
export declare function akumaRawAnswer(result: AkumaInvocationResult): string | undefined;
|
|
6
17
|
/**
|
|
7
18
|
* The head a streamed observing call opens with: the identity its birth already
|
|
8
19
|
* established, its Contract association, and the birth's diagnostics. The
|