@vincemakes/kiso-code 0.46.2 → 0.47.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auth/oauth/chatgpt.js +25 -8
- package/dist/auth/oauth/page.d.ts +25 -0
- package/dist/auth/oauth/page.js +44 -0
- package/dist/chat.d.ts +12 -1
- package/dist/chat.js +120 -43
- package/dist/coding-prompt.d.ts +7 -0
- package/dist/coding-prompt.js +12 -4
- package/dist/create-coding-agent.js +11 -2
- package/dist/dispatch.d.ts +2 -0
- package/dist/dispatch.js +330 -77
- package/dist/git-branch.d.ts +17 -0
- package/dist/git-branch.js +69 -0
- package/dist/in-run-compaction.d.ts +54 -0
- package/dist/in-run-compaction.js +44 -0
- package/dist/index.js +101 -41
- package/dist/opening.d.ts +48 -0
- package/dist/opening.js +43 -0
- package/dist/replay.d.ts +8 -1
- package/dist/replay.js +22 -11
- package/dist/resume.js +21 -16
- package/dist/session-cards.d.ts +2 -0
- package/dist/session-cards.js +3 -3
- package/dist/settings.d.ts +19 -0
- package/dist/settings.js +34 -5
- package/dist/skill-invoke.d.ts +11 -0
- package/dist/skill-invoke.js +59 -0
- package/dist/state.d.ts +7 -0
- package/dist/task-notice.d.ts +33 -0
- package/dist/task-notice.js +73 -0
- package/dist/trust-ui.d.ts +7 -0
- package/dist/trust-ui.js +54 -9
- package/dist/window-title.d.ts +43 -23
- package/dist/window-title.js +78 -50
- package/package.json +14 -14
package/dist/replay.js
CHANGED
|
@@ -20,7 +20,8 @@
|
|
|
20
20
|
* the viewer — then any uncovered earlier turns folded as above.
|
|
21
21
|
*/
|
|
22
22
|
import { echoText } from "@vincemakes/kiso-tui-cells/render";
|
|
23
|
-
import {
|
|
23
|
+
import { bandHeader } from "@vincemakes/kiso-tui-cells/strings";
|
|
24
|
+
import { taskNoticeLines, taskNoticeRows, taskWhat } from "./task-notice.js";
|
|
24
25
|
/** How many turns stay on screen in full. */
|
|
25
26
|
const FULL_TURNS = 2;
|
|
26
27
|
/** A user turn's opening line, or null when this input does not open one
|
|
@@ -48,7 +49,7 @@ export function turnsOf(events) {
|
|
|
48
49
|
}
|
|
49
50
|
/** One turn through the Body — consumeRun's mapping, minus everything that
|
|
50
51
|
* needs the live process (clocks, tailers, usage, the recap). */
|
|
51
|
-
function replayTurn(body, turn,
|
|
52
|
+
function replayTurn(body, turn, tasks) {
|
|
52
53
|
let thinking = false;
|
|
53
54
|
let said = false;
|
|
54
55
|
const results = new Set();
|
|
@@ -62,9 +63,17 @@ function replayTurn(body, turn, what) {
|
|
|
62
63
|
const ask = askOf(e);
|
|
63
64
|
if (ask !== null)
|
|
64
65
|
body.userLine(ask);
|
|
65
|
-
else if (e.via?.kind === "tasks")
|
|
66
|
-
|
|
67
|
-
|
|
66
|
+
else if (e.via?.kind === "tasks") {
|
|
67
|
+
// the tasks round: the same rows a live session drew, from the
|
|
68
|
+
// journal; the pipe's lines are taskNoticeLines' (Amendment 8: a
|
|
69
|
+
// lost task's line names what it ran)
|
|
70
|
+
const items = e.via.items;
|
|
71
|
+
const what = (taskId) => {
|
|
72
|
+
const t = tasks.find((x) => x.id === taskId);
|
|
73
|
+
return t === undefined ? undefined : taskWhat(t);
|
|
74
|
+
};
|
|
75
|
+
body.metaNotice(taskNoticeLines(items, what).join("\n"), taskNoticeRows(items, tasks));
|
|
76
|
+
}
|
|
68
77
|
else if (e.source === "system") {
|
|
69
78
|
body.notice("verification pass");
|
|
70
79
|
body.notice(` ${typeof e.content === "string" ? e.content : ""}`);
|
|
@@ -125,7 +134,7 @@ const plural = (n, one) => `${n} ${one}${n === 1 ? "" : "s"}`;
|
|
|
125
134
|
* Replay a session's durable events into the body. Returns the number of
|
|
126
135
|
* turns it found (0: a fresh session — nothing is drawn).
|
|
127
136
|
*/
|
|
128
|
-
export function replayInto(body, events, W = 80,
|
|
137
|
+
export function replayInto(body, events, W = 80, tasks = []) {
|
|
129
138
|
// The latest checkpoint: the turns it covers are what the model will
|
|
130
139
|
// read as its summary, not as turns.
|
|
131
140
|
let checkpoint = null;
|
|
@@ -141,22 +150,24 @@ export function replayInto(body, events, W = 80, what) {
|
|
|
141
150
|
const shown = rest.slice(-FULL_TURNS);
|
|
142
151
|
const earlier = rest.slice(0, rest.length - shown.length);
|
|
143
152
|
const label = total > shown.length ? `resuming · ${plural(total, "turn")}, showing the last ${shown.length}` : `resuming · ${plural(total, "turn")}`;
|
|
144
|
-
|
|
145
|
-
|
|
153
|
+
// Graphite §8.1 (R3b): the resumed history opens like every band — the
|
|
154
|
+
// hairline with its name, `resuming` bold gold, the counts dim (the same
|
|
155
|
+
// words the dim rule carried)
|
|
156
|
+
body.raw([bandHeader(label, Math.max(1, W))]);
|
|
146
157
|
if (checkpoint !== null) {
|
|
147
158
|
const summary = checkpoint.summary;
|
|
148
159
|
body.fold(`checkpoint · summarizes ${plural(covered.length, "earlier turn")} · ctrl+r to read`, () => {
|
|
149
160
|
for (const t of covered)
|
|
150
|
-
replayTurn(body, t,
|
|
161
|
+
replayTurn(body, t, tasks);
|
|
151
162
|
}, summary);
|
|
152
163
|
}
|
|
153
164
|
if (earlier.length > 0) {
|
|
154
165
|
body.fold(`${plural(earlier.length, "earlier turn")} · ctrl+r to read`, () => {
|
|
155
166
|
for (const t of earlier)
|
|
156
|
-
replayTurn(body, t,
|
|
167
|
+
replayTurn(body, t, tasks);
|
|
157
168
|
});
|
|
158
169
|
}
|
|
159
170
|
for (const t of shown)
|
|
160
|
-
replayTurn(body, t,
|
|
171
|
+
replayTurn(body, t, tasks);
|
|
161
172
|
return total;
|
|
162
173
|
}
|
package/dist/resume.js
CHANGED
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
* The ergonomics batch B4 (pure move) — resume: the RECOVERY flow (Area 2/7). The body
|
|
3
3
|
* moved verbatim from index.ts.
|
|
4
4
|
*/
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { agentModel, dock, retryOnRow, floorOn } from "./state.js";
|
|
5
|
+
import { STATUS_GLYPHS, workingRow } from "@vincemakes/kiso-tui";
|
|
6
|
+
import { dock, retryOnRow } from "./state.js";
|
|
8
7
|
import { pendingAsk, resolveUncertains } from "./trust-ui.js";
|
|
9
8
|
import { failOnFauxExhaustion } from "./faux-glue.js";
|
|
10
|
-
import {
|
|
9
|
+
import { setTitleState } from "./window-title.js";
|
|
10
|
+
import { barFor, consumeRun, estimateCtxRatio, startStatusSpinner } from "./chat.js";
|
|
11
11
|
/**
|
|
12
12
|
* Resume = the RECOVERY flow (Area 2/7): uncertain executions are decided,
|
|
13
13
|
* the interrupted run is continued via session.resume() — never faked with
|
|
@@ -21,23 +21,26 @@ export async function resume(session, prompt, faux, input) {
|
|
|
21
21
|
let turnNo = 0;
|
|
22
22
|
// v3 §03: the two-state status bar (see chat — same shapes).
|
|
23
23
|
let runUsage = { in: null, out: null, cache: null, known: false };
|
|
24
|
-
let runGlyph =
|
|
24
|
+
let runGlyph = STATUS_GLYPHS[0];
|
|
25
25
|
let runStart = Date.now();
|
|
26
26
|
// KC2 §5: the rows the REPL and this flow used to build separately are
|
|
27
|
-
// ONE formatter now
|
|
28
|
-
|
|
27
|
+
// ONE formatter now. Graphite §8.7 / §8.9: the running turn's facts ride
|
|
28
|
+
// the live row, the session's the status bar — the chat's own two
|
|
29
|
+
// (barFor), so the recovery flow now spells plan's posture the same way.
|
|
30
|
+
const width = () => (process.stdout.columns > 0 ? process.stdout.columns : 80);
|
|
31
|
+
const statusCb = (u, _ctx) => {
|
|
29
32
|
runUsage = u;
|
|
30
|
-
if (dock.active)
|
|
31
|
-
|
|
33
|
+
if (!dock.active)
|
|
34
|
+
return;
|
|
35
|
+
dock.setLive(workingRow(runGlyph, runStart, u.out, null, width(), retryOnRow()));
|
|
36
|
+
dock.setBar(barFor(session, { tokPerSec: null }));
|
|
32
37
|
};
|
|
33
|
-
// the recovery flow prints the BARE mode (chat spells plan's posture) —
|
|
34
|
-
// the extraction keeps that difference, it was not asked to settle it.
|
|
35
38
|
const paintIdle = () => {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
setTitleState("ready");
|
|
40
|
+
if (!dock.active)
|
|
41
|
+
return;
|
|
42
|
+
dock.setLive(null);
|
|
43
|
+
dock.setBar(barFor(session, { tokPerSec: null }));
|
|
41
44
|
};
|
|
42
45
|
const withRun = async (run) => {
|
|
43
46
|
currentRun = run;
|
|
@@ -47,6 +50,8 @@ export async function resume(session, prompt, faux, input) {
|
|
|
47
50
|
runGlyph = g;
|
|
48
51
|
statusCb(runUsage, estimateCtxRatio(session));
|
|
49
52
|
});
|
|
53
|
+
statusCb(runUsage, estimateCtxRatio(session)); // the live row from the run's first frame
|
|
54
|
+
setTitleState("working");
|
|
50
55
|
try {
|
|
51
56
|
turnNo += 1;
|
|
52
57
|
const last = await consumeRun(session, run, input, turnNo, faux, statusCb);
|
package/dist/session-cards.d.ts
CHANGED
|
@@ -88,6 +88,8 @@ export declare function projectSessionCard(input: {
|
|
|
88
88
|
readonly asks: number;
|
|
89
89
|
readonly workspace?: string | null;
|
|
90
90
|
readonly profileName?: string | null;
|
|
91
|
+
/** Graphite R3d: the session's `/name`, which wins over the derived title */
|
|
92
|
+
readonly name?: string | null;
|
|
91
93
|
}): SessionCard;
|
|
92
94
|
/** The shapes this module needs from the agent — structural, so the
|
|
93
95
|
* projection is testable without standing up a provider. */
|
package/dist/session-cards.js
CHANGED
|
@@ -76,7 +76,7 @@ export function projectSessionCard(input) {
|
|
|
76
76
|
: "failed";
|
|
77
77
|
return {
|
|
78
78
|
id: input.id,
|
|
79
|
-
title: sessionTitle(input.records),
|
|
79
|
+
title: input.name ?? sessionTitle(input.records),
|
|
80
80
|
badge,
|
|
81
81
|
turns,
|
|
82
82
|
updatedAt: input.updatedAt,
|
|
@@ -128,11 +128,11 @@ export function cardFromListing(l) {
|
|
|
128
128
|
const s = l.summary;
|
|
129
129
|
const base = { id: l.id, workspace: l.workspace, profileName: l.profileName, ...(l.inferred === true ? { inferred: true } : {}) };
|
|
130
130
|
if (s === null)
|
|
131
|
-
return { ...base, title: l.id, badge: "unknown", turns: null, updatedAt: l.mtime, uncertain: 0, asks: 0, outcome: "no summary" };
|
|
131
|
+
return { ...base, title: l.name ?? l.id, badge: "unknown", turns: null, updatedAt: l.mtime, uncertain: 0, asks: 0, outcome: "no summary" };
|
|
132
132
|
const badge = s.uncertain > 0 ? "uncertain" : s.asks > 0 ? "ask" : s.state === "open" ? "interrupted" : s.state === "completed" ? "completed" : s.state === null ? "unknown" : "failed";
|
|
133
133
|
return {
|
|
134
134
|
...base,
|
|
135
|
-
title: s.title ?? l.id,
|
|
135
|
+
title: l.name ?? s.title ?? l.id,
|
|
136
136
|
badge,
|
|
137
137
|
turns: s.turns,
|
|
138
138
|
updatedAt: s.updatedAt,
|
package/dist/settings.d.ts
CHANGED
|
@@ -37,4 +37,23 @@ export interface SettingsInput {
|
|
|
37
37
|
readonly thinkingRemembered: boolean;
|
|
38
38
|
readonly version: string;
|
|
39
39
|
}
|
|
40
|
+
export interface Row {
|
|
41
|
+
readonly name: string;
|
|
42
|
+
readonly value: string;
|
|
43
|
+
readonly from: string;
|
|
44
|
+
readonly change: string;
|
|
45
|
+
/** Graphite P3: the value as a table cell (`on`, `1M`) where `value` is
|
|
46
|
+
* a sentence; absent, the value is already short */
|
|
47
|
+
readonly brief?: string;
|
|
48
|
+
/** Graphite P3: what the sentence said that the cell does not — the
|
|
49
|
+
* panel's opened row carries it */
|
|
50
|
+
readonly means?: string;
|
|
51
|
+
}
|
|
52
|
+
/** Graphite R3e: the settings as data — the /settings panel's rows and
|
|
53
|
+
* the printed form below read the same list. */
|
|
54
|
+
export declare function settingsFacts(i: SettingsInput): Row[];
|
|
55
|
+
/** The printed form: one block per setting — what it is, where it came
|
|
56
|
+
* from and how to change it (a pipe, a dock-less TTY, and a panel row's
|
|
57
|
+
* enter). */
|
|
58
|
+
export declare function settingRow(r: Row, width?: number): string;
|
|
40
59
|
export declare function settingsRows(i: SettingsInput): string[];
|
package/dist/settings.js
CHANGED
|
@@ -40,7 +40,7 @@ function dontAskRow(i) {
|
|
|
40
40
|
const on = i.dontAsk === true;
|
|
41
41
|
const resolved = resolveModeLayers(modeLayers(i));
|
|
42
42
|
const from = (resolved.dontAsk !== "off") === on ? dontAskFrom(i, resolved.from.dontAsk) : "set in this session";
|
|
43
|
-
return { name: "don't ask", value: on ? "on — what would ask is refused" : "off", from, change };
|
|
43
|
+
return { name: "don't ask", value: on ? "on — what would ask is refused" : "off", from, change, brief: on ? "on" : "off", ...(on ? { means: "what would ask is refused" } : {}) };
|
|
44
44
|
}
|
|
45
45
|
function modelRow(i) {
|
|
46
46
|
const change = "/model; \"model\" in ~/.kiso/config.json";
|
|
@@ -73,7 +73,15 @@ function layered(i, key) {
|
|
|
73
73
|
return { value: i.user[key], from: "user config" };
|
|
74
74
|
return { value: undefined, from: "default" };
|
|
75
75
|
}
|
|
76
|
-
|
|
76
|
+
/** The window's sentence as a cell and the rest (`1M` / `the registry's
|
|
77
|
+
* for this endpoint`) — the window chain words it `<size>, <source>`. */
|
|
78
|
+
function windowCells(window) {
|
|
79
|
+
const cut = window.indexOf(", ");
|
|
80
|
+
return cut < 0 ? {} : { brief: window.slice(0, cut), means: window.slice(cut + 2) };
|
|
81
|
+
}
|
|
82
|
+
/** Graphite R3e: the settings as data — the /settings panel's rows and
|
|
83
|
+
* the printed form below read the same list. */
|
|
84
|
+
export function settingsFacts(i) {
|
|
77
85
|
const trust = layered(i, "projectTrust");
|
|
78
86
|
const auto = i.env.KISO_AUTO_COMPACT !== undefined ? { value: i.env.KISO_AUTO_COMPACT, from: "env KISO_AUTO_COMPACT" } : layered(i, "autoCompact");
|
|
79
87
|
const rows = [
|
|
@@ -86,9 +94,18 @@ export function settingsRows(i) {
|
|
|
86
94
|
value: i.floorOn ? "on — irrecoverable deletes are refused in every mode" : "off",
|
|
87
95
|
from: i.user?.floor !== undefined ? "user config" : "default",
|
|
88
96
|
change: "\"floor\": \"off\" in ~/.kiso/config.json (user config only)",
|
|
97
|
+
brief: i.floorOn ? "on" : "off",
|
|
98
|
+
...(i.floorOn ? { means: "irrecoverable deletes are refused in every mode" } : {}),
|
|
99
|
+
},
|
|
100
|
+
{ name: "window", value: i.window, from: "the window chain (see /status)", change: "\"contextWindow\" on the profile; KISO_CONTEXT_WINDOW for one run", ...windowCells(i.window) },
|
|
101
|
+
{
|
|
102
|
+
name: "compaction",
|
|
103
|
+
value: "in-run: past half the window at a phase end, past 80% at once",
|
|
104
|
+
from: "built in",
|
|
105
|
+
change: "/compact on demand",
|
|
106
|
+
brief: "50% · 80%",
|
|
107
|
+
means: "past half the window at a phase end, past 80% at once",
|
|
89
108
|
},
|
|
90
|
-
{ name: "window", value: i.window, from: "the window chain (see /status)", change: "\"contextWindow\" on the profile; KISO_CONTEXT_WINDOW for one run" },
|
|
91
|
-
{ name: "compaction", value: "in-run: past half the window at a phase end, past 80% at once", from: "built in", change: "/compact on demand" },
|
|
92
109
|
{
|
|
93
110
|
name: "auto-compact",
|
|
94
111
|
value: auto.value === undefined ? "off" : typeof auto.value === "object" ? `at ${auto.value.thresholdRatio} of the window, at a run's start` : String(auto.value),
|
|
@@ -101,9 +118,21 @@ export function settingsRows(i) {
|
|
|
101
118
|
value: i.thinkingHidden ? "hidden — one line per block" : "shown",
|
|
102
119
|
from: i.thinkingRemembered ? "ctrl+t (remembered)" : "default",
|
|
103
120
|
change: "ctrl+t",
|
|
121
|
+
brief: i.thinkingHidden ? "hidden" : "shown",
|
|
122
|
+
...(i.thinkingHidden ? { means: "one line per block" } : {}),
|
|
104
123
|
},
|
|
105
124
|
{ name: "version", value: i.version, from: "running", change: "kiso update" },
|
|
106
125
|
];
|
|
126
|
+
return rows;
|
|
127
|
+
}
|
|
128
|
+
/** The printed form: one block per setting — what it is, where it came
|
|
129
|
+
* from and how to change it (a pipe, a dock-less TTY, and a panel row's
|
|
130
|
+
* enter). */
|
|
131
|
+
export function settingRow(r, width = r.name.length + 2) {
|
|
132
|
+
return `${r.name.padEnd(width)}${r.value}\n${" ".repeat(width)}from ${r.from} · change: ${r.change}`;
|
|
133
|
+
}
|
|
134
|
+
export function settingsRows(i) {
|
|
135
|
+
const rows = settingsFacts(i);
|
|
107
136
|
const width = Math.max(...rows.map((r) => r.name.length)) + 2;
|
|
108
|
-
return rows.map((r) =>
|
|
137
|
+
return rows.map((r) => settingRow(r, width));
|
|
109
138
|
}
|
package/dist/skill-invoke.d.ts
CHANGED
|
@@ -44,6 +44,17 @@ export declare function nearest(name: string, names: readonly string[]): string[
|
|
|
44
44
|
* `model only` tag for `user-invocable: false`, then the broken entries
|
|
45
45
|
* with the loader's own reason. */
|
|
46
46
|
export declare function skillsRows(catalog: SkillsCatalog | null, sourceOf: (dir: string) => string, userDir: string): string[];
|
|
47
|
+
/**
|
|
48
|
+
* The sheets round (owner, 2026-10-06) — `/skills` on a dock is a sheet
|
|
49
|
+
* over the input, the shape `/status` has (§8.16). The band says how many,
|
|
50
|
+
* how many cannot load, and where they live when that is one place
|
|
51
|
+
* (`skills · 3 · 1 cannot load · ~/.kiso/skills`); each skill is one row,
|
|
52
|
+
* `/name` in ink in a measured column and its description dim, cut by
|
|
53
|
+
* cells; with more than one place, the place ends the row. A skill that
|
|
54
|
+
* cannot load says why, in the failure colour. How to run one is the
|
|
55
|
+
* closing row. On a pipe `/skills` prints `skillsRows`, unchanged.
|
|
56
|
+
*/
|
|
57
|
+
export declare function skillsSheetRows(catalog: SkillsCatalog | null, sourceOf: (dir: string) => string, userDir: string, W: number): string[];
|
|
47
58
|
/**
|
|
48
59
|
* 0.40.1 (owner's ruling, revoking the 0.41.0 deferral) — the installed
|
|
49
60
|
* skills as `/` menu entries. The same rule the dispatcher follows, so the
|
package/dist/skill-invoke.js
CHANGED
|
@@ -14,6 +14,9 @@
|
|
|
14
14
|
* one answer — the model's index and this door are one list), and every
|
|
15
15
|
* outcome is data. The dispatcher does the printing and the submitting.
|
|
16
16
|
*/
|
|
17
|
+
import { bandHeader } from "@vincemakes/kiso-tui-cells/strings";
|
|
18
|
+
import { cutLine, escapeTerminal, palette } from "@vincemakes/kiso-tui-cells/render";
|
|
19
|
+
import { visibleWidth, widthCut } from "@vincemakes/kiso-tui-cells/width";
|
|
17
20
|
/**
|
|
18
21
|
* What a submitted line asks of the skills, if anything. `null` means the
|
|
19
22
|
* line is not a skill request, and the dispatcher goes on as before — which
|
|
@@ -120,6 +123,62 @@ export function skillsRows(catalog, sourceOf, userDir) {
|
|
|
120
123
|
rows.push("/<name> [args] or /skill <name> [args] runs one · a built-in command wins a shared name");
|
|
121
124
|
return rows;
|
|
122
125
|
}
|
|
126
|
+
/** The last `cells` cells of `text`, by display width. */
|
|
127
|
+
function leftCut(text, cells) {
|
|
128
|
+
const chars = Array.from(text);
|
|
129
|
+
let out = "";
|
|
130
|
+
for (let i = chars.length - 1; i >= 0; i -= 1) {
|
|
131
|
+
if (visibleWidth(chars[i] + out) > cells)
|
|
132
|
+
break;
|
|
133
|
+
out = chars[i] + out;
|
|
134
|
+
}
|
|
135
|
+
return out;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* The sheets round (owner, 2026-10-06) — `/skills` on a dock is a sheet
|
|
139
|
+
* over the input, the shape `/status` has (§8.16). The band says how many,
|
|
140
|
+
* how many cannot load, and where they live when that is one place
|
|
141
|
+
* (`skills · 3 · 1 cannot load · ~/.kiso/skills`); each skill is one row,
|
|
142
|
+
* `/name` in ink in a measured column and its description dim, cut by
|
|
143
|
+
* cells; with more than one place, the place ends the row. A skill that
|
|
144
|
+
* cannot load says why, in the failure colour. How to run one is the
|
|
145
|
+
* closing row. On a pipe `/skills` prints `skillsRows`, unchanged.
|
|
146
|
+
*/
|
|
147
|
+
export function skillsSheetRows(catalog, sourceOf, userDir, W) {
|
|
148
|
+
const p = palette();
|
|
149
|
+
const entries = catalog?.entries ?? [];
|
|
150
|
+
const broken = catalog?.broken ?? [];
|
|
151
|
+
const close = ` ${p.dim}/<name> runs one \u00b7 a built-in wins its name \u00b7 esc closes${p.reset}`;
|
|
152
|
+
if (entries.length === 0 && broken.length === 0) {
|
|
153
|
+
return [bandHeader("skills \u00b7 none", W), ` ${p.dim}add one as ${escapeTerminal(userDir)}/<name>/SKILL.md${p.reset}`, close].map((r) => cutLine(r, W));
|
|
154
|
+
}
|
|
155
|
+
const places = [...new Set([...entries.map((e) => sourceOf(e.dir)), ...broken.map((b) => sourceOf(b.dir))])];
|
|
156
|
+
const facts = [String(entries.length), ...(broken.length > 0 ? [`${broken.length} cannot load`] : [])];
|
|
157
|
+
// the place is cut from the LEFT when the band cannot hold it (P4's
|
|
158
|
+
// rule for a path in a band name): its end — the folder's own name —
|
|
159
|
+
// and the rule after it stay on screen
|
|
160
|
+
const head = `skills \u00b7 ${facts.join(" \u00b7 ")}`;
|
|
161
|
+
const where = places.length === 1 ? escapeTerminal(places[0]) : "";
|
|
162
|
+
const roomFor = Math.max(0, W - 8 - visibleWidth(head) - 3);
|
|
163
|
+
const shown = where === "" || roomFor < 2 ? "" : visibleWidth(where) <= roomFor ? where : `\u2026${leftCut(where, roomFor - 1)}`;
|
|
164
|
+
const rows = [bandHeader(shown === "" ? head : `${head} \u00b7 ${shown}`, W)];
|
|
165
|
+
const names = [...entries.map((e) => `/${escapeTerminal(e.name)}`), ...broken.map((b) => escapeTerminal(b.dir))];
|
|
166
|
+
const nw = Math.max(...names.map((n) => visibleWidth(n))) + 2;
|
|
167
|
+
const fit = (text, room) => (visibleWidth(text) <= room ? text : `${widthCut(text, Math.max(1, room - 1))}…`);
|
|
168
|
+
const place = (dir) => (places.length > 1 ? ` \u00b7 ${escapeTerminal(sourceOf(dir))}` : "");
|
|
169
|
+
const room = Math.max(1, W - 2 - nw - 1);
|
|
170
|
+
for (const e of entries) {
|
|
171
|
+
const name = `/${escapeTerminal(e.name)}`;
|
|
172
|
+
const what = `${escapeTerminal(e.description)}${e.userInvocable ? "" : " (model only)"}${place(e.dir)}`;
|
|
173
|
+
rows.push(` ${name}${" ".repeat(nw - visibleWidth(name))}${p.dim}${fit(what, room)}${p.reset}`);
|
|
174
|
+
}
|
|
175
|
+
for (const b of broken) {
|
|
176
|
+
const name = escapeTerminal(b.dir);
|
|
177
|
+
rows.push(` ${p.dim}${name}${" ".repeat(nw - visibleWidth(name))}${p.reset}${p.fail}${fit(`cannot load: ${escapeTerminal(b.reason)}${place(b.dir)}`, room)}${p.fgEnd}`);
|
|
178
|
+
}
|
|
179
|
+
rows.push(close);
|
|
180
|
+
return rows.map((r) => cutLine(r, W));
|
|
181
|
+
}
|
|
123
182
|
/**
|
|
124
183
|
* 0.40.1 (owner's ruling, revoking the 0.41.0 deferral) — the installed
|
|
125
184
|
* skills as `/` menu entries. The same rule the dispatcher follows, so the
|
package/dist/state.d.ts
CHANGED
|
@@ -196,6 +196,13 @@ export interface LineInput {
|
|
|
196
196
|
panelAsk(view: PanelView, onCommit: (v: PanelVerdict) => void, opts?: {
|
|
197
197
|
safer?: () => Promise<SaferAnswer>;
|
|
198
198
|
}): void;
|
|
199
|
+
/** Graphite R3e: a read-only sheet over the input with the caller's rows
|
|
200
|
+
* (`/status`, `/context`, `/skills`); any key closes it. Absent off a
|
|
201
|
+
* dock. */
|
|
202
|
+
openSheet?(rows: (W: number) => string[]): void;
|
|
203
|
+
/** The sheets round: open the command list, as a typed `/` does
|
|
204
|
+
* (`/help`). Absent off a dock. */
|
|
205
|
+
openCommands?(): void;
|
|
199
206
|
/** W21: cancel the panel — the SIGINT pair to panelAsk. */
|
|
200
207
|
panelCancel(): void;
|
|
201
208
|
/** TUI2-R2 ②: open the session picker — the editor takes the keys
|
package/dist/task-notice.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { TaskDeliveryItem } from "@vincemakes/kiso-core";
|
|
2
2
|
import { type TaskInfo } from "@vincemakes/kiso-runtime/internal";
|
|
3
|
+
import type { NoticeMark } from "@vincemakes/kiso-tui";
|
|
3
4
|
/** The lines a delivery shows: today's `✦ task t1 exited · t2 failed` for
|
|
4
5
|
* the ordinary items, and one `✦ lost track of …` row per lost task. The
|
|
5
6
|
* shape is fixed (a design keys on it): the opening `✦ lost track of <id>
|
|
@@ -28,3 +29,35 @@ export declare function taskOutput(task: TaskInfo, lines?: number): string[];
|
|
|
28
29
|
export declare function taskCounts(tasks: readonly TaskInfo[]): {
|
|
29
30
|
readonly running: number;
|
|
30
31
|
};
|
|
32
|
+
/** The tasks round (owner, 2026-10-06) — how a task ended, in one word and
|
|
33
|
+
* its colour: a shell `exited 0` (or `failed 1`), a child `answered` (or
|
|
34
|
+
* `failed`), `stopped` dim, `lost track — may still be running` gold (the
|
|
35
|
+
* main sync, Amendment 8's words; it read `◌ outcome unknown`), `ready`
|
|
36
|
+
* when a long-running command said it was. Read from the task's own journal, so a
|
|
37
|
+
* resumed session says what a live one did; with no journal entry, the
|
|
38
|
+
* delivery's transition word, uncoloured. */
|
|
39
|
+
export declare function taskOutcome(task: TaskInfo | undefined, transition: TaskDeliveryItem["transition"]): {
|
|
40
|
+
readonly word: string;
|
|
41
|
+
readonly tone: NoticeMark["tone"] | null;
|
|
42
|
+
};
|
|
43
|
+
/** The tasks round — a task delivery on the terminal: one meta row per
|
|
44
|
+
* task, `TASK` once and the rows under it unlabelled, each `<id> <how it
|
|
45
|
+
* ended> · <what ran>` with the outcome word marked; a lost task adds
|
|
46
|
+
* where to look (`· /tasks shows it`). The pipe keeps `taskNoticeLines`'
|
|
47
|
+
* lines, byte for byte. */
|
|
48
|
+
export declare function taskNoticeRows(items: readonly TaskDeliveryItem[], tasks: readonly TaskInfo[]): {
|
|
49
|
+
readonly label: string;
|
|
50
|
+
readonly sentence: string;
|
|
51
|
+
readonly mark?: NoticeMark;
|
|
52
|
+
}[];
|
|
53
|
+
/** A session's tasks for display, or none: a journal that cannot be read
|
|
54
|
+
* is reported where it is read on purpose (`/tasks`), never here. */
|
|
55
|
+
export declare function tasksForDisplay(manager: {
|
|
56
|
+
list(): TaskInfo[];
|
|
57
|
+
} | undefined): readonly TaskInfo[];
|
|
58
|
+
/** The tasks round (owner, 2026-10-06) — `/tasks` → show its output, on a
|
|
59
|
+
* dock: a sheet over the input, `/context`'s shape (§8.16). The band
|
|
60
|
+
* names the task, which output it is, and how it ended; the lines at the
|
|
61
|
+
* content edge, each cut to one row; the closing row. On a pipe the
|
|
62
|
+
* printed lines stay. */
|
|
63
|
+
export declare function taskOutputSheetRows(task: TaskInfo, lines: readonly string[], W: number): string[];
|
package/dist/task-notice.js
CHANGED
|
@@ -16,6 +16,8 @@ import { closeSync, fstatSync, openSync, readFileSync, readSync } from "node:fs"
|
|
|
16
16
|
import { dirname, join } from "node:path";
|
|
17
17
|
import { readRecords } from "@vincemakes/kiso-runtime/internal";
|
|
18
18
|
import { elapsedLabel } from "@vincemakes/kiso-tui-cells";
|
|
19
|
+
import { SHEET_CLOSE, bandHeader } from "@vincemakes/kiso-tui-cells/strings";
|
|
20
|
+
import { cutLine, escapeTerminal, palette } from "@vincemakes/kiso-tui-cells/render";
|
|
19
21
|
/** The lines a delivery shows: today's `✦ task t1 exited · t2 failed` for
|
|
20
22
|
* the ordinary items, and one `✦ lost track of …` row per lost task. The
|
|
21
23
|
* shape is fixed (a design keys on it): the opening `✦ lost track of <id>
|
|
@@ -121,3 +123,74 @@ export function taskCounts(tasks) {
|
|
|
121
123
|
running += 1;
|
|
122
124
|
return { running };
|
|
123
125
|
}
|
|
126
|
+
/** The tasks round (owner, 2026-10-06) — how a task ended, in one word and
|
|
127
|
+
* its colour: a shell `exited 0` (or `failed 1`), a child `answered` (or
|
|
128
|
+
* `failed`), `stopped` dim, `lost track — may still be running` gold (the
|
|
129
|
+
* main sync, Amendment 8's words; it read `◌ outcome unknown`), `ready`
|
|
130
|
+
* when a long-running command said it was. Read from the task's own journal, so a
|
|
131
|
+
* resumed session says what a live one did; with no journal entry, the
|
|
132
|
+
* delivery's transition word, uncoloured. */
|
|
133
|
+
export function taskOutcome(task, transition) {
|
|
134
|
+
if (task === undefined)
|
|
135
|
+
return { word: transition, tone: null };
|
|
136
|
+
if (transition === "ready")
|
|
137
|
+
return { word: "ready", tone: "ok" };
|
|
138
|
+
const label = taskStateLabel(task);
|
|
139
|
+
if (label === "exited 0")
|
|
140
|
+
return { word: task.agent !== undefined ? "answered" : "exited 0", tone: "ok" };
|
|
141
|
+
if (label.startsWith("failed"))
|
|
142
|
+
return { word: task.agent !== undefined && label !== "failed to start" ? "failed" : label, tone: "fail" };
|
|
143
|
+
if (task.state.kind === "unknown")
|
|
144
|
+
return { word: label, tone: "gold" };
|
|
145
|
+
return { word: label, tone: null };
|
|
146
|
+
}
|
|
147
|
+
/** The tasks round — a task delivery on the terminal: one meta row per
|
|
148
|
+
* task, `TASK` once and the rows under it unlabelled, each `<id> <how it
|
|
149
|
+
* ended> · <what ran>` with the outcome word marked; a lost task adds
|
|
150
|
+
* where to look (`· /tasks shows it`). The pipe keeps `taskNoticeLines`'
|
|
151
|
+
* lines, byte for byte. */
|
|
152
|
+
export function taskNoticeRows(items, tasks) {
|
|
153
|
+
// (a lost task adds `· /tasks shows it`, before what ran)
|
|
154
|
+
return items.map((item, i) => {
|
|
155
|
+
const task = tasks.find((t) => t.id === item.taskId);
|
|
156
|
+
const { word, tone } = item.transition === "unknown" && task === undefined ? { word: "lost track — may still be running", tone: "gold" } : taskOutcome(task, item.transition);
|
|
157
|
+
const what = task === undefined ? "" : ` · ${taskWhat(task)}`;
|
|
158
|
+
// where to look before what ran: the row is cut at the end
|
|
159
|
+
const where = item.transition === "unknown" ? " · /tasks shows it" : "";
|
|
160
|
+
return { label: i === 0 ? "TASK" : "", sentence: `${item.taskId} ${word}${where}${what}`, ...(tone !== null ? { mark: { text: word, tone } } : {}) };
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
/** A session's tasks for display, or none: a journal that cannot be read
|
|
164
|
+
* is reported where it is read on purpose (`/tasks`), never here. */
|
|
165
|
+
export function tasksForDisplay(manager) {
|
|
166
|
+
if (manager === undefined)
|
|
167
|
+
return [];
|
|
168
|
+
try {
|
|
169
|
+
return manager.list();
|
|
170
|
+
}
|
|
171
|
+
catch {
|
|
172
|
+
return [];
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
/** The tasks round (owner, 2026-10-06) — `/tasks` → show its output, on a
|
|
176
|
+
* dock: a sheet over the input, `/context`'s shape (§8.16). The band
|
|
177
|
+
* names the task, which output it is, and how it ended; the lines at the
|
|
178
|
+
* content edge, each cut to one row; the closing row. On a pipe the
|
|
179
|
+
* printed lines stay. */
|
|
180
|
+
export function taskOutputSheetRows(task, lines, W) {
|
|
181
|
+
const p = palette();
|
|
182
|
+
const which = task.agent !== undefined ? "its answer" : "its last output";
|
|
183
|
+
const { word } = taskOutcome(task, "exited");
|
|
184
|
+
const rows = [bandHeader(`${task.id} · ${which} · ${task.state.kind === "running" || task.state.kind === "starting" ? taskStateLabel(task) : word}`, W)];
|
|
185
|
+
// the main sync (0.46.2, Amendment 8): a lost task says why, first, in
|
|
186
|
+
// gold — the printed form's `t1 — lost track: …` line
|
|
187
|
+
const why = lostReason(task);
|
|
188
|
+
if (why !== undefined)
|
|
189
|
+
rows.push(` ${p.gold}lost track: ${escapeTerminal(why)}; it may still be running${p.gold === "" ? "" : p.fgEnd}`);
|
|
190
|
+
if (lines.length === 0)
|
|
191
|
+
rows.push(` ${p.dim}nothing yet${p.reset}`);
|
|
192
|
+
for (const line of lines)
|
|
193
|
+
rows.push(` ${p.ink2}${escapeTerminal(line)}${p.ink2 === "" ? "" : p.fgEnd}`);
|
|
194
|
+
rows.push(` ${p.dim}${SHEET_CLOSE}${p.reset}`);
|
|
195
|
+
return rows.map((r) => cutLine(r, W));
|
|
196
|
+
}
|
package/dist/trust-ui.d.ts
CHANGED
|
@@ -111,4 +111,11 @@ export declare function applyProjectMerges(artifacts: ProjectArtifacts): void;
|
|
|
111
111
|
* execution stays uncertain and durable — no rerun/abandoned is
|
|
112
112
|
* fabricated, round 10). The resolution feedback lands in the body —
|
|
113
113
|
* the body is the single stdout writer, never a stray console.log. */
|
|
114
|
+
/** Graphite P1b — what an uncertain execution's panel quotes: a shell
|
|
115
|
+
* command verbatim (every line of it), any other call's target as its
|
|
116
|
+
* tool card names it. From the execution record's own input. */
|
|
117
|
+
export declare function uncertainTarget(name: string, input: Readonly<Record<string, unknown>>): string[];
|
|
118
|
+
/** Graphite P1b — an interrupted ask_user's questions, as the model wrote
|
|
119
|
+
* them; empty when the input carries none (the panel then quotes the id). */
|
|
120
|
+
export declare function askedQuestions(input: Readonly<Record<string, unknown>>): string[];
|
|
114
121
|
export declare function resolveUncertains(session: AgentSession, input: LineInput, isCancelled: () => boolean): Promise<void>;
|
package/dist/trust-ui.js
CHANGED
|
@@ -9,10 +9,11 @@ import { existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, symlinkS
|
|
|
9
9
|
import { homedir } from "node:os";
|
|
10
10
|
import { join } from "node:path";
|
|
11
11
|
import { pathToFileURL } from "node:url";
|
|
12
|
-
import { askDeclineAll, askView, projectTrustRows, projectTrustView, projectUntrustedNote, uncertainView, unansweredAskView } from "@vincemakes/kiso-tui";
|
|
12
|
+
import { askDeclineAll, askView, projectTrustRows, projectTrustView, projectUntrustedNote, toolTarget, uncertainView, unansweredAskView } from "@vincemakes/kiso-tui";
|
|
13
13
|
import { projectArtifacts, recordTrust, trustFor } from "@vincemakes/kiso-runtime";
|
|
14
|
-
import { bodyLog, currentAgentExtensions, dock, extensionsDir, kisoHome, mergedTempPaths, neverInherited } from "./state.js";
|
|
14
|
+
import { body, bodyLog, currentAgentExtensions, dock, extensionsDir, kisoHome, mergedTempPaths, neverInherited } from "./state.js";
|
|
15
15
|
import { guardSavedAllow } from "./protected-writes.js";
|
|
16
|
+
import { setTitleState, titleState } from "./window-title.js";
|
|
16
17
|
import { mergeDirPrefix } from "./temp-sweep.js";
|
|
17
18
|
import { loadUserConfig, resolveProjectTrustPolicy } from "./config.js";
|
|
18
19
|
import { getDontAsk } from "./mode.js";
|
|
@@ -50,7 +51,15 @@ opts) {
|
|
|
50
51
|
console.log(`[non-interactive — no human to ask: ${view.fallbackQuestion}]`);
|
|
51
52
|
return Promise.resolve({ action: "deny", reason: "no human to ask" });
|
|
52
53
|
}
|
|
53
|
-
|
|
54
|
+
// Graphite §8.10: while the person is asked, the title says so; the
|
|
55
|
+
// answer (or the cancel) puts back what it said before
|
|
56
|
+
const before = titleState();
|
|
57
|
+
setTitleState("needs-you");
|
|
58
|
+
return new Promise((settle) => {
|
|
59
|
+
const resolve = (v) => {
|
|
60
|
+
setTitleState(before);
|
|
61
|
+
settle(v);
|
|
62
|
+
};
|
|
54
63
|
let settled = false;
|
|
55
64
|
pendingAsk = () => {
|
|
56
65
|
if (settled)
|
|
@@ -151,7 +160,7 @@ export function askUi(input) {
|
|
|
151
160
|
// question the model puts is declined, recorded as unanswered,
|
|
152
161
|
// exactly as a non-interactive session declines it.
|
|
153
162
|
if (getDontAsk()) {
|
|
154
|
-
|
|
163
|
+
body.notice("[dontAsk] the model's question was declined — nothing asks in dontAsk");
|
|
155
164
|
return askDeclineAll(spec);
|
|
156
165
|
}
|
|
157
166
|
const verdict = await askPanel(input, askView(spec));
|
|
@@ -331,15 +340,33 @@ export async function resolveProjectTrust(input) {
|
|
|
331
340
|
// carries the question, the args the artifact listing (the same rows
|
|
332
341
|
// the bodyLog below records, verbatim — the listing still lands in
|
|
333
342
|
// the scrollback; the panel is a bounded block, the record is not).
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
343
|
+
// The tasks round (owner, 2026-10-06): on a dock the question lists the
|
|
344
|
+
// files itself, so nothing is printed above it; the record lands after
|
|
345
|
+
// the answer (below). Off a dock the listing is the record, as before.
|
|
346
|
+
if (!dock.active) {
|
|
347
|
+
bodyLog(`[project .kiso] ${artifacts.root}`);
|
|
348
|
+
bodyLog(projectTrustRows(artifacts.files, " ").join("\n")); // DC-51: one call, one cell
|
|
349
|
+
}
|
|
350
|
+
const home = homedir();
|
|
351
|
+
const shownRoot = artifacts.root === home ? "~" : artifacts.root.startsWith(`${home}/`) ? `~${artifacts.root.slice(home.length)}` : artifacts.root;
|
|
352
|
+
const verdict = await askPanel(input, projectTrustView(artifacts.root, artifacts.files, shownRoot));
|
|
337
353
|
// A cancel is a "no" HERE — refused is sticky, the project does not
|
|
338
354
|
// load (re-evaluate by deleting the trust line or changing a file).
|
|
339
355
|
// The non-TTY branch above returned WITHOUT a record so an interactive
|
|
340
356
|
// run can still decide later.
|
|
341
357
|
const granted = verdict.action === "allow";
|
|
342
358
|
recordTrust({ root: artifacts.root, digest: artifacts.digest, decision: granted ? "granted" : "refused" });
|
|
359
|
+
// the record of what was trusted stays in the scrollback: ONE meta row,
|
|
360
|
+
// once the person has answered — `PROJECT trusted · ~/w/.kiso · <files>`
|
|
361
|
+
if (dock.active) {
|
|
362
|
+
const word = granted ? "trusted" : "not trusted";
|
|
363
|
+
const files = projectTrustRows(artifacts.files, "").map((r) => r.trim().replace(/\s{2,}/g, " ")).join(", ");
|
|
364
|
+
// a long root outside home keeps its last two parts — the folder's
|
|
365
|
+
// own name — so the row still has room for what was trusted
|
|
366
|
+
const parts = shownRoot.split("/");
|
|
367
|
+
const where = shownRoot.length > 40 && !shownRoot.startsWith("~") && parts.length > 3 ? `\u2026/${parts.slice(-2).join("/")}` : shownRoot;
|
|
368
|
+
body.metaNotice(`[project .kiso] ${word} \u2014 ${artifacts.root}`, [{ label: "PROJECT", sentence: `${word} \u00b7 ${where} \u00b7 ${files}`, ...(granted ? { mark: { text: word, tone: "ok" } } : {}) }]);
|
|
369
|
+
}
|
|
343
370
|
if (!granted)
|
|
344
371
|
return null;
|
|
345
372
|
applyProjectMerges(artifacts);
|
|
@@ -471,6 +498,22 @@ function readdirSyncSafe(dir) {
|
|
|
471
498
|
* execution stays uncertain and durable — no rerun/abandoned is
|
|
472
499
|
* fabricated, round 10). The resolution feedback lands in the body —
|
|
473
500
|
* the body is the single stdout writer, never a stray console.log. */
|
|
501
|
+
/** Graphite P1b — what an uncertain execution's panel quotes: a shell
|
|
502
|
+
* command verbatim (every line of it), any other call's target as its
|
|
503
|
+
* tool card names it. From the execution record's own input. */
|
|
504
|
+
export function uncertainTarget(name, input) {
|
|
505
|
+
if (name === "shell")
|
|
506
|
+
return String(input.command ?? "").split(/\r\n|\n|\r/);
|
|
507
|
+
return [toolTarget(name, { ...input })];
|
|
508
|
+
}
|
|
509
|
+
/** Graphite P1b — an interrupted ask_user's questions, as the model wrote
|
|
510
|
+
* them; empty when the input carries none (the panel then quotes the id). */
|
|
511
|
+
export function askedQuestions(input) {
|
|
512
|
+
const qs = input.questions;
|
|
513
|
+
if (!Array.isArray(qs))
|
|
514
|
+
return [];
|
|
515
|
+
return qs.flatMap((q) => (typeof q === "object" && q !== null && typeof q.question === "string" ? [q.question] : []));
|
|
516
|
+
}
|
|
474
517
|
export async function resolveUncertains(session, input, isCancelled) {
|
|
475
518
|
// The lead's review of #63, B5: under dontAsk nothing asks, so the
|
|
476
519
|
// uncertain executions are left exactly as a cancel leaves them —
|
|
@@ -478,8 +521,10 @@ export async function resolveUncertains(session, input, isCancelled) {
|
|
|
478
521
|
// line says what to do. An unattended session must not sit on a panel.
|
|
479
522
|
if (getDontAsk()) {
|
|
480
523
|
const n = session.uncertainExecutions().length;
|
|
524
|
+
// Graphite P1b: a notice, so a dock draws it as the UNCERTAIN meta row
|
|
525
|
+
// (notice-meta.ts); a pipe prints the same line it always did
|
|
481
526
|
if (n > 0)
|
|
482
|
-
|
|
527
|
+
body.notice(`[dontAsk] ${n} uncertain execution${n === 1 ? "" : "s"} left unresolved — resolve them in an asking mode`);
|
|
483
528
|
return;
|
|
484
529
|
}
|
|
485
530
|
for (const uncertain of session.uncertainExecutions()) {
|
|
@@ -487,7 +532,7 @@ export async function resolveUncertains(session, input, isCancelled) {
|
|
|
487
532
|
// have applied — it is a question nobody answered. The COPY says
|
|
488
533
|
// so; the mechanism is untouched (allow → the runtime's own rerun
|
|
489
534
|
// resolution, whose error-fill text this round never edits).
|
|
490
|
-
const view = uncertain.name === "ask_user" ? unansweredAskView(uncertain.executionId) : uncertainView(uncertain.name, uncertain.executionId);
|
|
535
|
+
const view = uncertain.name === "ask_user" ? unansweredAskView(uncertain.executionId, askedQuestions(uncertain.input)) : uncertainView(uncertain.name, uncertain.executionId, uncertainTarget(uncertain.name, uncertain.input));
|
|
491
536
|
const verdict = await askPanel(input, view);
|
|
492
537
|
if (isCancelled() || verdict.action === "cancel") {
|
|
493
538
|
// round 10: a cancellation NEVER records a verdict — the execution
|