@astrosheep/keiyaku 2.9.7 → 2.9.9
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/.tsbuildinfo +1 -1
- package/build/agents/harness/event-persistence.js +7 -5
- package/build/agents/harness/events.js +3 -2
- package/build/agents/harness/projection.js +8 -6
- package/build/agents/providers/codex-app-server/adapter.js +6 -1
- package/build/agents/providers/codex-app-server/session.js +8 -7
- package/build/agents/selector.js +12 -1
- package/build/cli/commands/akuma/view/handler.js +3 -11
- package/build/cli/commands/contract/amend/handler.js +1 -1
- package/build/cli/commands/contract/amend/meta.js +4 -4
- package/build/cli/commands/projection/status/handler.js +5 -4
- package/build/cli/commands/projection/status/meta.js +2 -2
- package/build/cli/commands/task/add/meta.js +9 -1
- package/build/cli/commands/task/shared.js +2 -1
- package/build/cli/completion.js +8 -0
- package/build/cli/render/kanshi.js +238 -80
- package/build/cli/render/path-prefix-compaction.js +119 -76
- package/build/cli/render/projection-activity.js +65 -21
- package/build/cli/render/shared.js +8 -7
- package/build/cli/render/status.js +11 -23
- package/build/cli/render/wait.js +42 -64
- package/build/config/env-keys.js +1 -1
- package/build/config/env.js +1 -1
- package/build/config/settings/disease.js +4 -4
- package/build/config/settings/loader.js +44 -21
- package/build/core/addressing.js +40 -9
- package/build/core/amend.js +21 -5
- package/build/core/call/context.js +19 -3
- package/build/core/call/execution.js +43 -20
- package/build/core/ledger-batch.js +194 -0
- package/build/core/projection/generation/database.js +22 -0
- package/build/core/projection/generation/projection-generation-execution.js +52 -38
- package/build/core/projection/generation/projection-generation-launcher.js +148 -20
- package/build/core/projection/generation/projection-generation-process.js +3 -1
- package/build/core/projection/generation/projection-generation-runner.js +76 -40
- package/build/core/projection/generation/projection-generation-runtime.js +82 -19
- package/build/core/projection/generation/store.js +17 -1
- package/build/core/projection/generation/transitions.js +89 -12
- package/build/core/projection/index.js +3 -3
- package/build/core/projection/projection-activity.js +2 -0
- package/build/core/projection/projection-kill.js +22 -10
- package/build/core/projection/projection-runner-lock.js +177 -37
- package/build/core/projection/projection-status.js +183 -60
- package/build/core/projection/projection-wake.js +171 -72
- package/build/core/status/board.js +55 -14
- package/build/core/status/drift.js +21 -5
- package/build/core/status/ledger-batch.js +1 -158
- package/build/core/task/settlement-git.js +2 -2
- package/build/core/task/task-git-runtime.js +8 -10
- package/build/core/task/task-git-store.js +5 -3
- package/build/core/worktree-path.js +39 -25
- package/build/flow-error.js +1 -1
- package/build/generated/version.js +2 -2
- package/build/git/refs.js +47 -1
- package/package.json +1 -1
- package/skills/keiyaku-akuma/SKILL.md +18 -0
- package/skills/keiyaku-workflow/SKILL.md +68 -13
|
@@ -1,88 +1,131 @@
|
|
|
1
|
-
const
|
|
2
|
-
const
|
|
3
|
-
const
|
|
4
|
-
/**
|
|
5
|
-
export function
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
if (!leftParts || !rightParts)
|
|
10
|
-
return literal;
|
|
11
|
-
let sharedCount = 0;
|
|
12
|
-
const maximumShared = Math.min(leftParts.length, rightParts.length) - 1;
|
|
13
|
-
while (sharedCount < maximumShared && leftParts[sharedCount] === rightParts[sharedCount]) {
|
|
14
|
-
sharedCount += 1;
|
|
15
|
-
}
|
|
16
|
-
if (sharedCount === 0)
|
|
17
|
-
return literal;
|
|
18
|
-
const prefix = leftParts.slice(0, sharedCount).join("/");
|
|
19
|
-
const compacted = `${prefix}/{${leftParts.slice(sharedCount).join("/")} × ${rightParts.slice(sharedCount).join("/")}}`;
|
|
20
|
-
return savesEnough(literal, compacted) ? compacted : literal;
|
|
1
|
+
const PREFIX_MARKER = "⋯";
|
|
2
|
+
const MIN_FOLDED_PREFIX_LENGTH = 8;
|
|
3
|
+
export const PATH_PREVIEW_BODY_ROWS = 7;
|
|
4
|
+
/** Render one exact pattern/path without giving display punctuation matcher meaning. */
|
|
5
|
+
export function renderExactPath(value) {
|
|
6
|
+
if (!requiresQuoting(value))
|
|
7
|
+
return value;
|
|
8
|
+
return `"${[...value].map(escapeQuotedCharacter).join("")}"`;
|
|
21
9
|
}
|
|
22
|
-
/**
|
|
23
|
-
export function
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
return literal;
|
|
37
|
-
const compacted = renderTrie(trie);
|
|
38
|
-
return savesEnough(literal, compacted) ? compacted : literal;
|
|
10
|
+
/** Fold one qualifying directory prefix while retaining row order and exact suffixes. */
|
|
11
|
+
export function foldPathRows(values) {
|
|
12
|
+
const rendered = values.map(renderExactPath);
|
|
13
|
+
const plainIndexes = values
|
|
14
|
+
.map((value, index) => renderExactPath(value) === value ? index : -1)
|
|
15
|
+
.filter((index) => index >= 0);
|
|
16
|
+
const prefix = bestSharedDirectoryPrefix(values, plainIndexes);
|
|
17
|
+
if (!prefix)
|
|
18
|
+
return { rows: rendered };
|
|
19
|
+
const foldedIndexes = new Set(plainIndexes.filter((index) => values[index].startsWith(prefix)));
|
|
20
|
+
return {
|
|
21
|
+
prefix,
|
|
22
|
+
rows: values.map((value, index) => foldedIndexes.has(index) ? `${PREFIX_MARKER}${value.slice(prefix.length)}` : rendered[index]),
|
|
23
|
+
};
|
|
39
24
|
}
|
|
40
|
-
|
|
41
|
-
|
|
25
|
+
/** Render one overlap fact as two labeled exact pattern rows. */
|
|
26
|
+
export function renderPatternPair(leftLabel, leftPattern, rightLabel, rightPattern) {
|
|
27
|
+
const folded = foldPathRows([leftPattern, rightPattern]);
|
|
28
|
+
const labelWidth = Math.max(leftLabel.length, rightLabel.length) + 2;
|
|
29
|
+
return [
|
|
30
|
+
...(folded.prefix ? [`${PREFIX_MARKER} = ${folded.prefix}`] : []),
|
|
31
|
+
`${leftLabel.padEnd(labelWidth)}${folded.rows[0]}`,
|
|
32
|
+
`${rightLabel.padEnd(labelWidth)}${folded.rows[1]}`,
|
|
33
|
+
];
|
|
42
34
|
}
|
|
43
|
-
|
|
44
|
-
|
|
35
|
+
/** Render a bounded path snapshot; declarations consume the same row budget as paths. */
|
|
36
|
+
export function renderPathPreview(snapshot, bodyRows = PATH_PREVIEW_BODY_ROWS) {
|
|
37
|
+
const plan = planPathPreview(snapshot.preview, Math.max(0, bodyRows));
|
|
38
|
+
const omitted = Math.max(0, snapshot.count - plan.visibleCount);
|
|
39
|
+
return [
|
|
40
|
+
`scope · ${snapshot.count} pattern${snapshot.count === 1 ? "" : "s"}`,
|
|
41
|
+
...(plan.prefix ? [`${PREFIX_MARKER} = ${plan.prefix}`] : []),
|
|
42
|
+
...plan.rows,
|
|
43
|
+
...(omitted > 0 ? [`+${omitted} more patterns`] : []),
|
|
44
|
+
];
|
|
45
45
|
}
|
|
46
|
-
function
|
|
47
|
-
const
|
|
48
|
-
if (
|
|
49
|
-
return
|
|
46
|
+
function planPathPreview(preview, bodyRows) {
|
|
47
|
+
const fullValues = preview.slice(0, bodyRows);
|
|
48
|
+
if (fullValues.length < bodyRows) {
|
|
49
|
+
return { ...foldPathRows(fullValues), visibleCount: fullValues.length };
|
|
50
50
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
}
|
|
56
|
-
function insertPath(node, parts) {
|
|
57
|
-
let current = node;
|
|
58
|
-
for (const part of parts) {
|
|
59
|
-
let child = current.children.get(part);
|
|
60
|
-
if (!child) {
|
|
61
|
-
child = createTrie();
|
|
62
|
-
current.children.set(part, child);
|
|
63
|
-
}
|
|
64
|
-
current = child;
|
|
51
|
+
const chargedValues = preview.slice(0, Math.max(0, bodyRows - 1));
|
|
52
|
+
const chargedFold = foldPathRows(chargedValues);
|
|
53
|
+
if (chargedFold.prefix) {
|
|
54
|
+
return { ...chargedFold, visibleCount: chargedValues.length };
|
|
65
55
|
}
|
|
66
|
-
|
|
56
|
+
return { rows: fullValues.map(renderExactPath), visibleCount: fullValues.length };
|
|
67
57
|
}
|
|
68
|
-
function
|
|
69
|
-
if (
|
|
58
|
+
function requiresQuoting(value) {
|
|
59
|
+
if (value.length === 0 || /^\s|\s$/u.test(value))
|
|
70
60
|
return true;
|
|
71
|
-
return
|
|
61
|
+
return value.includes("\\")
|
|
62
|
+
|| value.includes('"')
|
|
63
|
+
|| value.includes(PREFIX_MARKER)
|
|
64
|
+
|| [...value].some(isControlCharacter);
|
|
72
65
|
}
|
|
73
|
-
function
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
66
|
+
function isControlCharacter(character) {
|
|
67
|
+
const codePoint = character.codePointAt(0);
|
|
68
|
+
return codePoint <= 0x1f || (codePoint >= 0x7f && codePoint <= 0x9f);
|
|
69
|
+
}
|
|
70
|
+
function escapeQuotedCharacter(character) {
|
|
71
|
+
if (character === "\\")
|
|
72
|
+
return "\\\\";
|
|
73
|
+
if (character === '"')
|
|
74
|
+
return '\\"';
|
|
75
|
+
const codePoint = character.codePointAt(0);
|
|
76
|
+
const named = new Map([
|
|
77
|
+
[0x07, "\\a"],
|
|
78
|
+
[0x08, "\\b"],
|
|
79
|
+
[0x09, "\\t"],
|
|
80
|
+
[0x0a, "\\n"],
|
|
81
|
+
[0x0b, "\\v"],
|
|
82
|
+
[0x0c, "\\f"],
|
|
83
|
+
[0x0d, "\\r"],
|
|
84
|
+
]).get(codePoint);
|
|
85
|
+
if (named)
|
|
86
|
+
return named;
|
|
87
|
+
if (!isControlCharacter(character))
|
|
88
|
+
return character;
|
|
89
|
+
return [...Buffer.from(character, "utf8")]
|
|
90
|
+
.map((byte) => `\\${byte.toString(8).padStart(3, "0")}`)
|
|
91
|
+
.join("");
|
|
77
92
|
}
|
|
78
|
-
function
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
93
|
+
function bestSharedDirectoryPrefix(values, plainIndexes) {
|
|
94
|
+
let best;
|
|
95
|
+
for (let left = 0; left < plainIndexes.length; left += 1) {
|
|
96
|
+
for (let right = left + 1; right < plainIndexes.length; right += 1) {
|
|
97
|
+
const prefix = sharedDirectoryPrefix(values[plainIndexes[left]], values[plainIndexes[right]]);
|
|
98
|
+
if (!prefix || prefix.length < MIN_FOLDED_PREFIX_LENGTH)
|
|
99
|
+
continue;
|
|
100
|
+
const members = plainIndexes.filter((index) => values[index].startsWith(prefix));
|
|
101
|
+
const common = members
|
|
102
|
+
.slice(1)
|
|
103
|
+
.reduce((current, index) => sharedDirectoryPrefix(current, values[index]) ?? "", values[members[0]]);
|
|
104
|
+
if (common.length < MIN_FOLDED_PREFIX_LENGTH)
|
|
105
|
+
continue;
|
|
106
|
+
const candidate = {
|
|
107
|
+
prefix: common,
|
|
108
|
+
count: members.length,
|
|
109
|
+
first: members[0],
|
|
110
|
+
score: common.length * (members.length - 1),
|
|
111
|
+
};
|
|
112
|
+
if (!best
|
|
113
|
+
|| candidate.score > best.score
|
|
114
|
+
|| (candidate.score === best.score && candidate.count > best.count)
|
|
115
|
+
|| (candidate.score === best.score && candidate.count === best.count && candidate.first < best.first)
|
|
116
|
+
|| (candidate.score === best.score && candidate.count === best.count && candidate.first === best.first
|
|
117
|
+
&& candidate.prefix < best.prefix)) {
|
|
118
|
+
best = candidate;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return best?.prefix;
|
|
85
123
|
}
|
|
86
|
-
function
|
|
87
|
-
|
|
124
|
+
function sharedDirectoryPrefix(left, right) {
|
|
125
|
+
let sharedLength = 0;
|
|
126
|
+
const maximum = Math.min(left.length, right.length);
|
|
127
|
+
while (sharedLength < maximum && left[sharedLength] === right[sharedLength])
|
|
128
|
+
sharedLength += 1;
|
|
129
|
+
const slash = left.lastIndexOf("/", sharedLength - 1);
|
|
130
|
+
return slash >= 0 ? left.slice(0, slash + 1) : undefined;
|
|
88
131
|
}
|
|
@@ -1,10 +1,31 @@
|
|
|
1
1
|
import { compactText } from "./compact-text.js";
|
|
2
|
-
import { displayColumns, fitVariableLine, resolveLineColumns, truncateMiddleColumns } from "./line-width.js";
|
|
2
|
+
import { displayColumns, fitVariableLine, resolveLineColumns, truncateMiddleColumns, truncateColumns } from "./line-width.js";
|
|
3
3
|
import { presentFoldedToolActivity, } from "./tool-presentation.js";
|
|
4
4
|
import { presentLedgerPath, summarizeToolLedger } from "./tool-ledger-rollup.js";
|
|
5
|
-
const TIME_GUTTER_WIDTH =
|
|
5
|
+
const TIME_GUTTER_WIDTH = 5;
|
|
6
6
|
const VERB_WIDTH = 7;
|
|
7
7
|
const SILENCE_THRESHOLD_MS = 60_000;
|
|
8
|
+
const TIMELINE_GAP_MARKER = "⋮";
|
|
9
|
+
/** Keep the renderer and its gap grammar while giving Kanshi its smaller live window. */
|
|
10
|
+
export function selectProjectionActivityWindow(activity, ordinaryRows) {
|
|
11
|
+
// A Kanshi flow narrows only the settled history window. Unfinished activity
|
|
12
|
+
// remains pinned in its timeline position; it is not another history row.
|
|
13
|
+
const selected = activity.window.slice(-ordinaryRows);
|
|
14
|
+
const selectedOrders = new Set([
|
|
15
|
+
...selected.map((row) => row.order),
|
|
16
|
+
...activity.pinned.map((row) => row.order),
|
|
17
|
+
]);
|
|
18
|
+
return {
|
|
19
|
+
activity: {
|
|
20
|
+
...activity,
|
|
21
|
+
window: selected,
|
|
22
|
+
// Turn seals are a wait concern. Kanshi's narrow operational flow contains
|
|
23
|
+
// only ordinary activity plus durable tells.
|
|
24
|
+
timeline: activity.timeline.filter((row) => selectedOrders.has(row.order)),
|
|
25
|
+
},
|
|
26
|
+
leadingOmittedCount: activity.omittedSettledCount + activity.window.length - selected.length,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
8
29
|
export function formatTimelineDuration(ms) {
|
|
9
30
|
const totalSeconds = Math.max(0, Math.floor(ms / 1_000));
|
|
10
31
|
if (totalSeconds < 60)
|
|
@@ -39,6 +60,14 @@ export function formatWallClock(at) {
|
|
|
39
60
|
return " : │";
|
|
40
61
|
return `${String(value.getHours()).padStart(2, "0")}:${String(value.getMinutes()).padStart(2, "0")}│`;
|
|
41
62
|
}
|
|
63
|
+
/** One durable-tell line dialect shared by wait and the embedded Kanshi flow. */
|
|
64
|
+
export function renderPendingProjectionTell(tell, input) {
|
|
65
|
+
const createdAtMs = Date.parse(tell.createdAt);
|
|
66
|
+
const age = Math.max(0, input.anchorMs - (Number.isFinite(createdAtMs) ? createdAtMs : input.anchorMs));
|
|
67
|
+
const fixed = `${formatWallClock(tell.createdAt)} ${"told".padEnd(VERB_WIDTH)} `;
|
|
68
|
+
const tail = ` — ${formatTimelineDuration(age)}…`;
|
|
69
|
+
return `${fixed}${truncateColumns(`“${tell.text}”`, Math.max(0, input.maxColumns - displayColumns(fixed) - displayColumns(tail)))}${input.renderUnconsumedTail?.(tail) ?? tail}`;
|
|
70
|
+
}
|
|
42
71
|
function commonPrefix(left, right) {
|
|
43
72
|
const a = [...left];
|
|
44
73
|
const b = [...right];
|
|
@@ -72,6 +101,9 @@ function compressedPrefix(prefix) {
|
|
|
72
101
|
function timelineGutter(label, spine) {
|
|
73
102
|
return label || `${"".padStart(TIME_GUTTER_WIDTH)}${spine}`;
|
|
74
103
|
}
|
|
104
|
+
function renderTimelineGap(omittedRowCount, maxColumns) {
|
|
105
|
+
return fitVariableLine(`${timelineGutter("", TIMELINE_GAP_MARKER)} `, `${omittedRowCount} more`, maxColumns);
|
|
106
|
+
}
|
|
75
107
|
function compactTokens(value) {
|
|
76
108
|
if (value < 1_000)
|
|
77
109
|
return String(value);
|
|
@@ -144,13 +176,15 @@ function renderTerminalRollup(activity, input) {
|
|
|
144
176
|
? fitVariableLine(`${endPrefix} `, segments.join(" · "), input.maxColumns)
|
|
145
177
|
: endPrefix,
|
|
146
178
|
});
|
|
147
|
-
|
|
148
|
-
const
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
179
|
+
if (input.includeFiles !== false) {
|
|
180
|
+
for (const file of summary.files) {
|
|
181
|
+
const repeat = file.count > 1 ? ` ×${file.count}` : "";
|
|
182
|
+
const diffstat = file.diffstat ? ` +${file.diffstat.additions} −${file.diffstat.deletions}` : "";
|
|
183
|
+
rows.push({ kind: "file", line: fitVariableLine("", `${file.path}${repeat}${diffstat}`, input.maxColumns) });
|
|
184
|
+
}
|
|
185
|
+
if (summary.hiddenFileCount > 0) {
|
|
186
|
+
rows.push({ kind: "file", line: fitVariableLine("", `… ${summary.hiddenFileCount} more files`, input.maxColumns) });
|
|
187
|
+
}
|
|
154
188
|
}
|
|
155
189
|
return { rows, hasMutationFiles: summary.files.length > 0 };
|
|
156
190
|
}
|
|
@@ -170,6 +204,7 @@ function timelineEntries(activity) {
|
|
|
170
204
|
export function renderProjectionActivity(activity, input) {
|
|
171
205
|
const lines = [];
|
|
172
206
|
const rowKinds = [];
|
|
207
|
+
const rowAts = [];
|
|
173
208
|
let priorAtMs;
|
|
174
209
|
let priorMinute;
|
|
175
210
|
let priorCompletedRan;
|
|
@@ -177,9 +212,12 @@ export function renderProjectionActivity(activity, input) {
|
|
|
177
212
|
let lastRunningTool;
|
|
178
213
|
const maxColumns = input.maxColumns ?? resolveLineColumns();
|
|
179
214
|
const entries = timelineEntries(activity);
|
|
180
|
-
const
|
|
215
|
+
const gapBeforeOrder = input.leadingOmittedCount ? activity.window[0]?.order : undefined;
|
|
216
|
+
let gapEmitted = false;
|
|
217
|
+
const pushRow = (line, kind, at) => {
|
|
181
218
|
lines.push(line);
|
|
182
219
|
rowKinds.push(kind);
|
|
220
|
+
rowAts.push(at);
|
|
183
221
|
return true;
|
|
184
222
|
};
|
|
185
223
|
for (let index = 0; index < entries.length; index += 1) {
|
|
@@ -190,8 +228,12 @@ export function renderProjectionActivity(activity, input) {
|
|
|
190
228
|
priorAtMs = atMs;
|
|
191
229
|
continue;
|
|
192
230
|
}
|
|
193
|
-
if (
|
|
194
|
-
pushRow(
|
|
231
|
+
if (!gapEmitted && input.leadingOmittedCount && gapBeforeOrder !== undefined && entry.row.order >= gapBeforeOrder) {
|
|
232
|
+
pushRow(renderTimelineGap(input.leadingOmittedCount, maxColumns), "activity", entry.at);
|
|
233
|
+
gapEmitted = true;
|
|
234
|
+
}
|
|
235
|
+
if (input.showSilence !== false && priorAtMs !== undefined && Number.isFinite(atMs) && atMs - priorAtMs >= SILENCE_THRESHOLD_MS) {
|
|
236
|
+
pushRow(fitVariableLine(`${"".padStart(TIME_GUTTER_WIDTH)}┆ `, `${formatTimelineDuration(atMs - priorAtMs)} pass in silence`, maxColumns), "silence", entry.at);
|
|
195
237
|
}
|
|
196
238
|
if (Number.isFinite(atMs))
|
|
197
239
|
priorAtMs = atMs;
|
|
@@ -226,22 +268,22 @@ export function renderProjectionActivity(activity, input) {
|
|
|
226
268
|
}
|
|
227
269
|
priorCompletedRan = row.state === "completed" ? presentation.variableText : undefined;
|
|
228
270
|
const budget = Math.max(0, maxColumns - displayColumns(fixed) - displayColumns(presentation.fixedSuffix));
|
|
229
|
-
pushRow(`${fixed}${truncateMiddleColumns(variable, budget)}${presentation.fixedSuffix}`, "activity");
|
|
271
|
+
pushRow(`${fixed}${truncateMiddleColumns(variable, budget)}${presentation.fixedSuffix}`, "activity", entry.at);
|
|
230
272
|
continue;
|
|
231
273
|
}
|
|
232
274
|
if (row.kind === "said") {
|
|
233
275
|
priorCompletedRan = undefined;
|
|
234
|
-
pushRow(fitVariableLine(`${timelineGutter(label, spine)} ${"said".padEnd(VERB_WIDTH)} `, `“${compactText(row.text)}”`, maxColumns), "activity");
|
|
276
|
+
pushRow(fitVariableLine(`${timelineGutter(label, spine)} ${"said".padEnd(VERB_WIDTH)} `, `“${compactText(row.text)}”`, maxColumns), "activity", entry.at);
|
|
235
277
|
}
|
|
236
278
|
else if (row.kind === "thought") {
|
|
237
279
|
priorCompletedRan = undefined;
|
|
238
280
|
const text = `${compactText(row.text)}${row.truncated && !row.text.endsWith("…") ? "…" : ""}`;
|
|
239
281
|
const duration = formatActionDuration(row.durationMs);
|
|
240
|
-
pushRow(fitVariableLine(`${timelineGutter(label, spine)} ${"thought".padEnd(VERB_WIDTH)} `, `${duration ? `${duration} — ` : ""}“${text}”`, maxColumns), "activity");
|
|
282
|
+
pushRow(fitVariableLine(`${timelineGutter(label, spine)} ${"thought".padEnd(VERB_WIDTH)} `, `${duration ? `${duration} — ` : ""}“${text}”`, maxColumns), "activity", entry.at);
|
|
241
283
|
}
|
|
242
284
|
else if (row.kind === "warning") {
|
|
243
285
|
priorCompletedRan = undefined;
|
|
244
|
-
pushRow(fitVariableLine(`${timelineGutter(label, spine)} ${"warning".padEnd(VERB_WIDTH)} `, warningText(row), maxColumns), "activity");
|
|
286
|
+
pushRow(fitVariableLine(`${timelineGutter(label, spine)} ${"warning".padEnd(VERB_WIDTH)} `, warningText(row), maxColumns), "activity", entry.at);
|
|
245
287
|
}
|
|
246
288
|
else {
|
|
247
289
|
priorCompletedRan = undefined;
|
|
@@ -249,7 +291,7 @@ export function renderProjectionActivity(activity, input) {
|
|
|
249
291
|
? ` · ${compactTokens(row.inputTokens)}▸${compactTokens(row.outputTokens)} tok`
|
|
250
292
|
: "";
|
|
251
293
|
const cost = row.costUsd !== undefined ? ` · $${row.costUsd.toFixed(2)}` : "";
|
|
252
|
-
pushRow(fitVariableLine(`${"".padStart(TIME_GUTTER_WIDTH)}├ `, `turn ${row.turn}${usage}${cost}`, maxColumns), "activity");
|
|
294
|
+
pushRow(fitVariableLine(`${"".padStart(TIME_GUTTER_WIDTH)}├ `, `turn ${row.turn}${usage}${cost}`, maxColumns), "activity", entry.at);
|
|
253
295
|
}
|
|
254
296
|
}
|
|
255
297
|
for (const row of activity.pinned) {
|
|
@@ -258,10 +300,10 @@ export function renderProjectionActivity(activity, input) {
|
|
|
258
300
|
}
|
|
259
301
|
if (input.showPlan && activity.plan) {
|
|
260
302
|
const planAtMs = Date.parse(activity.plan.at);
|
|
261
|
-
if (priorAtMs !== undefined && Number.isFinite(planAtMs) && planAtMs - priorAtMs >= SILENCE_THRESHOLD_MS) {
|
|
262
|
-
pushRow(fitVariableLine(`${"".padStart(TIME_GUTTER_WIDTH)}┆ `, `${formatTimelineDuration(planAtMs - priorAtMs)} pass in silence`, maxColumns), "silence");
|
|
303
|
+
if (input.showSilence !== false && priorAtMs !== undefined && Number.isFinite(planAtMs) && planAtMs - priorAtMs >= SILENCE_THRESHOLD_MS) {
|
|
304
|
+
pushRow(fitVariableLine(`${"".padStart(TIME_GUTTER_WIDTH)}┆ `, `${formatTimelineDuration(planAtMs - priorAtMs)} pass in silence`, maxColumns), "silence", activity.plan.at);
|
|
263
305
|
}
|
|
264
|
-
pushRow(renderPlan(activity.plan, maxColumns), "plan");
|
|
306
|
+
pushRow(renderPlan(activity.plan, maxColumns), "plan", activity.plan.at);
|
|
265
307
|
}
|
|
266
308
|
let hasMutationFiles = false;
|
|
267
309
|
if (["returned", "dead", "lost", "dismissed"].includes(input.state)) {
|
|
@@ -269,10 +311,11 @@ export function renderProjectionActivity(activity, input) {
|
|
|
269
311
|
anchorMs: input.anchorMs,
|
|
270
312
|
...(input.workspaceRoot ? { workspaceRoot: input.workspaceRoot } : {}),
|
|
271
313
|
...(input.terminalDiagnostic ? { diagnostic: input.terminalDiagnostic } : {}),
|
|
314
|
+
includeFiles: input.showTerminalFiles !== false,
|
|
272
315
|
maxColumns,
|
|
273
316
|
});
|
|
274
317
|
for (const row of rollup.rows) {
|
|
275
|
-
pushRow(row.line, row.kind);
|
|
318
|
+
pushRow(row.line, row.kind, new Date(input.anchorMs).toISOString());
|
|
276
319
|
}
|
|
277
320
|
hasEndAnchor = true;
|
|
278
321
|
hasMutationFiles = rollup.hasMutationFiles;
|
|
@@ -280,6 +323,7 @@ export function renderProjectionActivity(activity, input) {
|
|
|
280
323
|
return {
|
|
281
324
|
lines,
|
|
282
325
|
rowKinds,
|
|
326
|
+
rowAts,
|
|
283
327
|
...(lastRunningTool ? { lastRunningTool } : {}),
|
|
284
328
|
hasEndAnchor,
|
|
285
329
|
hasMutationFiles,
|
|
@@ -3,7 +3,7 @@ import { commissionSlug, formatCommissionCoordinate } from "../../core/ids.js";
|
|
|
3
3
|
import { assembleResponse, buildSection, DISPLAY_TEXT_MAX_CHARS, FORMAT_LIST_MAX_ITEM_CHARS, FORMAT_LIST_MAX_ITEMS, formatMaybe, formatWarnings, shellQuote, truncateForDisplay, } from "./format.js";
|
|
4
4
|
import { WARNING_SECTION_TITLE } from "./response-style.js";
|
|
5
5
|
import { prependAddressReceipt } from "./address.js";
|
|
6
|
-
import {
|
|
6
|
+
import { renderPathPreview, renderPatternPair } from "./path-prefix-compaction.js";
|
|
7
7
|
export function textResponse(text) {
|
|
8
8
|
const payload = {};
|
|
9
9
|
return {
|
|
@@ -76,16 +76,17 @@ function renderScopeOverlapWarnings(overlaps) {
|
|
|
76
76
|
return [...overlapsByCounterparty.entries()].map(([contractId, overlaps]) => renderScopeOverlapGroup(commissionSlug(contractId), overlaps));
|
|
77
77
|
}
|
|
78
78
|
function renderScopeOverlapGroup(contractAddr, overlaps) {
|
|
79
|
-
const
|
|
79
|
+
const nextAddr = commissionSlug(overlaps[0].contracts[0]);
|
|
80
|
+
const pairs = overlaps.flatMap((overlap) => {
|
|
80
81
|
const [nextPattern, existingPattern] = overlap.patterns;
|
|
81
|
-
const patternPair =
|
|
82
|
+
const patternPair = renderPatternPair(nextAddr, nextPattern, contractAddr, existingPattern);
|
|
82
83
|
const evidence = overlap.kind === "potential"
|
|
83
|
-
? "potential
|
|
84
|
-
:
|
|
85
|
-
return
|
|
84
|
+
? ["potential · no current tracked path proves it"]
|
|
85
|
+
: renderPathPreview(overlap.paths);
|
|
86
|
+
return [...patternPair, ...evidence].map((line) => ` ${line}`);
|
|
86
87
|
});
|
|
87
88
|
return [
|
|
88
|
-
`▲
|
|
89
|
+
`▲ overlap ${nextAddr} ↔ ${contractAddr}`,
|
|
89
90
|
...pairs,
|
|
90
91
|
` consider --after ${contractAddr}`,
|
|
91
92
|
].join("\n");
|
|
@@ -69,12 +69,12 @@ function terminalFailureSuffix(row) {
|
|
|
69
69
|
function renderProjectionOmission(board) {
|
|
70
70
|
if (!board.omitted)
|
|
71
71
|
return undefined;
|
|
72
|
-
const counts = ["failed", "dead", "done", "killed", "dismissed"
|
|
72
|
+
const counts = ["failed", "dead", "done", "killed", "dismissed"]
|
|
73
73
|
.flatMap((state) => {
|
|
74
74
|
const count = board.omitted?.byState[state] ?? 0;
|
|
75
75
|
return count > 0 ? [`${count} ${state}`] : [];
|
|
76
76
|
});
|
|
77
|
-
return
|
|
77
|
+
return counts.length > 0 ? `+ ${counts.join(" · ")}` : undefined;
|
|
78
78
|
}
|
|
79
79
|
function renderProjectionAge(row) {
|
|
80
80
|
const duration = formatAgeMs(row.durationMs) ?? "?";
|
|
@@ -170,15 +170,18 @@ export function renderProjectionFilteredStatus(board, projectionId) {
|
|
|
170
170
|
if (!row)
|
|
171
171
|
return `${heading}\nno projections on projection ${projectionId}\n`;
|
|
172
172
|
if (row.state === "failed" || row.terminalFailure) {
|
|
173
|
-
|
|
174
|
-
return
|
|
173
|
+
const lines = [...renderTerminalFailureFace(projectionId, row.terminalFailure), "", `» keiyaku wait ${projectionId}`];
|
|
174
|
+
return lines.join("\n");
|
|
175
175
|
}
|
|
176
176
|
const lines = [heading, "", ...renderProjectionRow(row), ""];
|
|
177
177
|
if (row.observation.compatibility === "incompatible") {
|
|
178
178
|
return `${lines.join("\n")}\n`;
|
|
179
179
|
}
|
|
180
|
-
if (
|
|
181
|
-
lines.push(`» ${
|
|
180
|
+
if (isTerminalProjectionStatus(row)) {
|
|
181
|
+
lines.push(`» keiyaku wait ${projectionId}`);
|
|
182
|
+
return lines.join("\n");
|
|
183
|
+
}
|
|
184
|
+
lines.push(`» ${formatTellCommand(projectionId, '"..."')}`);
|
|
182
185
|
lines.push(`» keiyaku wait ${projectionId}`);
|
|
183
186
|
return `${lines.join("\n")}\n`;
|
|
184
187
|
}
|
|
@@ -206,27 +209,12 @@ function renderClaimReconciliationSection(recon) {
|
|
|
206
209
|
*/
|
|
207
210
|
export function renderStatusBoard(board) {
|
|
208
211
|
const boardText = renderKanshiBoard(board);
|
|
209
|
-
const projectionText = renderProjectionStatusSection(board.projections);
|
|
210
|
-
const receiptsText = renderClaimReconciliationSection(board.claimReconciliation);
|
|
211
|
-
const taskText = renderTaskStatusSection(board.tasks);
|
|
212
212
|
const taskDiseaseText = [
|
|
213
213
|
...(board.tasks?.diseases ?? []).map((disease) => disease.message),
|
|
214
214
|
...(board.tasks?.diagnostic ? [board.tasks.diagnostic] : []),
|
|
215
215
|
];
|
|
216
216
|
const diseasesText = renderSettingsDiseasesSection(board.diseases, taskDiseaseText);
|
|
217
|
-
if (
|
|
217
|
+
if (diseasesText === "")
|
|
218
218
|
return boardText;
|
|
219
|
-
|
|
220
|
-
if (board.targetFilter !== null && (projectionText || receiptsText || diseasesText)) {
|
|
221
|
-
sections.push("repository-wide — not filtered by target");
|
|
222
|
-
}
|
|
223
|
-
if (projectionText)
|
|
224
|
-
sections.push(projectionText.trimEnd());
|
|
225
|
-
if (receiptsText)
|
|
226
|
-
sections.push(receiptsText.trimEnd());
|
|
227
|
-
if (taskText)
|
|
228
|
-
sections.push(taskText.trimEnd());
|
|
229
|
-
if (diseasesText)
|
|
230
|
-
sections.push(diseasesText.trimEnd());
|
|
231
|
-
return `${sections.join("\n\n")}\n`;
|
|
219
|
+
return `${[boardText.trimEnd(), diseasesText.trimEnd()].join("\n\n")}\n`;
|
|
232
220
|
}
|
package/build/cli/render/wait.js
CHANGED
|
@@ -1,29 +1,8 @@
|
|
|
1
|
-
import { formatTimelineDuration,
|
|
1
|
+
import { formatTimelineDuration, renderPendingProjectionTell, renderProjectionActivity, } from "./projection-activity.js";
|
|
2
2
|
import { displayColumns, resolveLineColumns, truncateColumns } from "./line-width.js";
|
|
3
3
|
import { formatTellCommand, projectionRejoinHintLines, textResponse } from "./shared.js";
|
|
4
4
|
import { renderTerminalFailureFace } from "./terminal-failure.js";
|
|
5
5
|
const WAIT_HEADER_MAX_COLUMNS = 60;
|
|
6
|
-
const WAIT_BODY_MAX_ROWS = 10;
|
|
7
|
-
const WAIT_ROW_PRIORITY = {
|
|
8
|
-
"end-anchor": 100,
|
|
9
|
-
"storage-diagnostic": 90,
|
|
10
|
-
diagnostic: 90,
|
|
11
|
-
tell: 80,
|
|
12
|
-
summary: 70,
|
|
13
|
-
activity: 60,
|
|
14
|
-
plan: 55,
|
|
15
|
-
liveness: 50,
|
|
16
|
-
silence: 65,
|
|
17
|
-
file: 20,
|
|
18
|
-
};
|
|
19
|
-
/** One owner admits every wait-body row, then restores presentation order. */
|
|
20
|
-
export function selectWaitBodyRows(rows, limit = WAIT_BODY_MAX_ROWS) {
|
|
21
|
-
const selected = rows
|
|
22
|
-
.slice()
|
|
23
|
-
.sort((left, right) => WAIT_ROW_PRIORITY[right.kind] - WAIT_ROW_PRIORITY[left.kind] || left.order - right.order)
|
|
24
|
-
.slice(0, limit);
|
|
25
|
-
return selected.sort((left, right) => left.order - right.order);
|
|
26
|
-
}
|
|
27
6
|
function shortAddress(projectionId) {
|
|
28
7
|
return /\/([0-9a-f]{8})$/.exec(projectionId)?.[1] ?? projectionId;
|
|
29
8
|
}
|
|
@@ -70,12 +49,23 @@ function waitHeader(projectionId, akuma, snapshot, outputColumns) {
|
|
|
70
49
|
const dashCount = Math.max(1, maxColumns - displayColumns(`${prefix} ${state}`));
|
|
71
50
|
return `${prefix} ${"─".repeat(dashCount)} ${state}`;
|
|
72
51
|
}
|
|
73
|
-
function
|
|
74
|
-
return snapshot.tells.window.map((tell) => {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
52
|
+
function pendingTellRows(snapshot, maxColumns, orderOffset) {
|
|
53
|
+
return snapshot.tells.window.map((tell, index) => ({
|
|
54
|
+
line: renderPendingProjectionTell(tell, {
|
|
55
|
+
anchorMs: snapshot.observedAtMs,
|
|
56
|
+
maxColumns,
|
|
57
|
+
}),
|
|
58
|
+
at: tell.createdAt,
|
|
59
|
+
order: orderOffset + index,
|
|
60
|
+
}));
|
|
61
|
+
}
|
|
62
|
+
function orderFlowRows(rows) {
|
|
63
|
+
return rows.slice().sort((left, right) => {
|
|
64
|
+
const leftAt = Date.parse(left.at ?? "");
|
|
65
|
+
const rightAt = Date.parse(right.at ?? "");
|
|
66
|
+
const safeLeft = Number.isFinite(leftAt) ? leftAt : Number.POSITIVE_INFINITY;
|
|
67
|
+
const safeRight = Number.isFinite(rightAt) ? rightAt : Number.POSITIVE_INFINITY;
|
|
68
|
+
return safeLeft - safeRight || left.order - right.order;
|
|
79
69
|
});
|
|
80
70
|
}
|
|
81
71
|
function ledgerHint(projectionId, snapshot) {
|
|
@@ -142,14 +132,13 @@ function unknownResponse(projectionId, akuma, snapshot, maxColumns) {
|
|
|
142
132
|
anchorMs: snapshot.observedAtMs,
|
|
143
133
|
state: "unknown",
|
|
144
134
|
showPlan: false,
|
|
135
|
+
showSilence: false,
|
|
136
|
+
showTerminalFiles: false,
|
|
145
137
|
...(snapshot.workspaceRoot ? { workspaceRoot: snapshot.workspaceRoot } : {}),
|
|
146
138
|
maxColumns,
|
|
139
|
+
leadingOmittedCount: snapshot.activity.omittedSettledCount,
|
|
147
140
|
});
|
|
148
|
-
const historicalLines =
|
|
149
|
-
kind: rendered.rowKinds[index] ?? "activity",
|
|
150
|
-
line,
|
|
151
|
-
order: index,
|
|
152
|
-
}))).map((row) => row.line.replace(/[┆├]/, "│"));
|
|
141
|
+
const historicalLines = rendered.lines.map((line) => line.replace(/[┆├]/, "│"));
|
|
153
142
|
return {
|
|
154
143
|
...textResponse([
|
|
155
144
|
`? ${akuma} · projection: ${projectionId} · unknown`,
|
|
@@ -216,43 +205,32 @@ export function buildWaitResponse(projectionId, result, options = {}) {
|
|
|
216
205
|
if (state === "returned") {
|
|
217
206
|
return returnedResponse(projectionId, result);
|
|
218
207
|
}
|
|
219
|
-
const tells = pendingTellLines(snapshot, maxColumns);
|
|
220
|
-
const summary = ["running", "stalled", "unknown"].includes(state)
|
|
221
|
-
? renderProjectionActivitySummary(snapshot.activity, {
|
|
222
|
-
state,
|
|
223
|
-
...(snapshot.workspaceRoot ? { workspaceRoot: snapshot.workspaceRoot } : {}),
|
|
224
|
-
maxColumns,
|
|
225
|
-
})
|
|
226
|
-
: undefined;
|
|
227
208
|
const rendered = renderProjectionActivity(snapshot.activity, {
|
|
228
209
|
anchorMs: snapshot.observedAtMs,
|
|
229
210
|
state,
|
|
230
|
-
showPlan:
|
|
211
|
+
showPlan: false,
|
|
212
|
+
showSilence: false,
|
|
213
|
+
showTerminalFiles: false,
|
|
231
214
|
...(snapshot.workspaceRoot ? { workspaceRoot: snapshot.workspaceRoot } : {}),
|
|
232
215
|
maxColumns,
|
|
216
|
+
leadingOmittedCount: snapshot.activity.omittedSettledCount,
|
|
233
217
|
});
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
const bodyLines = selectWaitBodyRows(bodyPlan).map((row) => truncateColumns(row.line, maxColumns));
|
|
251
|
-
const lines = [waitHeader(projectionId, result.pact.akuma, snapshot, maxColumns), ...bodyLines];
|
|
252
|
-
const pendingTellCount = snapshot.tells.totals.undelivered;
|
|
253
|
-
if (pendingTellCount > 0) {
|
|
254
|
-
lines.push(`${pendingTellCount} tell${pendingTellCount === 1 ? "" : "s"} undelivered`);
|
|
255
|
-
}
|
|
218
|
+
const diagnosticLines = (snapshot.eventDiagnostics ?? [])
|
|
219
|
+
.map((diagnostic) => `‼ ${diagnostic.kind} · ${diagnostic.detail}`);
|
|
220
|
+
const activityRows = rendered.lines.map((line, index) => ({
|
|
221
|
+
line,
|
|
222
|
+
at: rendered.rowAts[index],
|
|
223
|
+
order: index,
|
|
224
|
+
}));
|
|
225
|
+
const flowLines = orderFlowRows([
|
|
226
|
+
...activityRows,
|
|
227
|
+
...pendingTellRows(snapshot, maxColumns, activityRows.length),
|
|
228
|
+
]).map((row) => truncateColumns(row.line, maxColumns));
|
|
229
|
+
const lines = [
|
|
230
|
+
waitHeader(projectionId, result.pact.akuma, snapshot, maxColumns),
|
|
231
|
+
...diagnosticLines,
|
|
232
|
+
...flowLines,
|
|
233
|
+
];
|
|
256
234
|
const nextActions = state === "running" && options.runningActions === false
|
|
257
235
|
? []
|
|
258
236
|
: nextActionLines(options.alias ?? projectionId, result, state);
|