@vincemakes/kiso-code 0.11.0 → 0.13.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/chat.d.ts +63 -0
- package/dist/chat.js +198 -3
- package/dist/index.js +6 -2
- package/dist/state.d.ts +9 -2
- package/dist/trust-ui.d.ts +4 -2
- package/dist/trust-ui.js +7 -2
- package/package.json +14 -14
package/dist/chat.d.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* estimates. All bodies moved verbatim from index.ts.
|
|
6
6
|
*/
|
|
7
7
|
import { type RunUsage } from "@vincemakes/kiso-tui";
|
|
8
|
+
import { type SaferFailure, type SaferOption } from "@vincemakes/kiso-tui";
|
|
8
9
|
import type { AgentSession, Run } from "@vincemakes/kiso-runtime";
|
|
9
10
|
import { type LineInput } from "./state.js";
|
|
10
11
|
/**
|
|
@@ -81,6 +82,68 @@ export declare function usageFromEvent(route: string | undefined, ev: import("@v
|
|
|
81
82
|
* first event. */
|
|
82
83
|
export declare function startStatusSpinner(onTick: (glyph: string) => void): () => void;
|
|
83
84
|
export declare function startShellTail(sessionId: string, callId: string, command: string, startedAt: number): () => void;
|
|
85
|
+
/**
|
|
86
|
+
* R3v2-F1: the format contract, stated FIRMLY. The first cut asked for
|
|
87
|
+
* "JSON ONLY" and left it there, which a verbose model reads as a
|
|
88
|
+
* preference — it wrote three sentences of preamble, opened a fence, and
|
|
89
|
+
* the cap ended the reply mid-string. Forbidding prose, naming the exact
|
|
90
|
+
* schema, and giving the nothing-is-safer case its own literal answer
|
|
91
|
+
* are all the same instruction: there is one thing to emit and no room
|
|
92
|
+
* to be helpful in the margins.
|
|
93
|
+
*
|
|
94
|
+
* The schema is an ENVELOPE rather than a bare array because a single
|
|
95
|
+
* top-level object leaves the model nowhere to put a preamble.
|
|
96
|
+
*/
|
|
97
|
+
export declare const SAFER_SYSTEM_PROMPT: string;
|
|
98
|
+
/**
|
|
99
|
+
* R3v2-F1: the side query's output ceiling — raised from 500, which was
|
|
100
|
+
* the cap the live failures hit EXACTLY.
|
|
101
|
+
*
|
|
102
|
+
* The JSON-only reply the prompt now asks for is about 200 tokens for
|
|
103
|
+
* three alternatives, so this ceiling is a runaway guard and not a
|
|
104
|
+
* budget the answer is expected to approach: it exists so a model that
|
|
105
|
+
* ignores the contract and writes an essay still stops, not so the
|
|
106
|
+
* answer has room. Output is billed only when generated, and the query
|
|
107
|
+
* fires only on a press, so the raise costs nothing on the path that
|
|
108
|
+
* works and removes the one that could not.
|
|
109
|
+
*/
|
|
110
|
+
export declare const SAFER_MAX_TOKENS = 1500;
|
|
111
|
+
/**
|
|
112
|
+
* R3v2-F1: WHY the ask failed, when the reply's own text can prove it.
|
|
113
|
+
*
|
|
114
|
+
* This side reports the cause and never the copy — the sentences live in
|
|
115
|
+
* the panel package, next to each other, so there is one place where the
|
|
116
|
+
* words are chosen and one place they can drift from.
|
|
117
|
+
*
|
|
118
|
+
* "Cut short" is a DIAGNOSIS, so it is only claimed when the text shows
|
|
119
|
+
* it: a reply that closed its JSON and then failed our SHAPE returns
|
|
120
|
+
* null and gets the unqualified line, because telling that human their
|
|
121
|
+
* reply was truncated would be a confident wrong answer.
|
|
122
|
+
*/
|
|
123
|
+
export declare function saferFailure(text: string): SaferFailure | null;
|
|
124
|
+
/**
|
|
125
|
+
* Parse the model's answer DEFENSIVELY — anything unexpected is a
|
|
126
|
+
* failure, and a failure degrades honestly.
|
|
127
|
+
*
|
|
128
|
+
* The temptation here is to be clever: salvage a half-parse, coerce a
|
|
129
|
+
* string into a command, accept an object where an array was asked for.
|
|
130
|
+
* All of that produces a list of alternatives the model did not propose,
|
|
131
|
+
* shown to a human deciding whether to run a destructive command. The
|
|
132
|
+
* only honest failure mode is the dim line, so anything that is not
|
|
133
|
+
* exactly the requested shape returns null.
|
|
134
|
+
*
|
|
135
|
+
* A fenced code block is the one accommodation, because models emit it
|
|
136
|
+
* constantly and it changes no content.
|
|
137
|
+
*
|
|
138
|
+
* R3v2-F1 widens that accommodation and NOTHING else. Unwrapping the
|
|
139
|
+
* named `alternatives` envelope, and reading `reason` as the spelling of
|
|
140
|
+
* `why` the prompt now asks for, are transport details: the entries that
|
|
141
|
+
* come out are verbatim the entries the model put in. That is the line
|
|
142
|
+
* between an accommodation and the salvage this parser refuses — a
|
|
143
|
+
* salvage changes WHICH alternatives are shown, and every rule that does
|
|
144
|
+
* that is still here. One bad entry still poisons the batch.
|
|
145
|
+
*/
|
|
146
|
+
export declare function parseSaferOptions(text: string): SaferOption[] | null;
|
|
84
147
|
/**
|
|
85
148
|
* Consume a run, answering approval pauses as they arrive. `resumeMode`
|
|
86
149
|
* marks a session.resume() continuation. `faux` picks the status line's
|
package/dist/chat.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { readFileSync, statSync } from "node:fs";
|
|
8
8
|
import { escapeTerminal, cacheHitPct, idleStatus, palette, renderEvent, renderRecap, runningStatus, toolTarget, STATUS_GLYPHS, } from "@vincemakes/kiso-tui";
|
|
9
|
-
import { editFileDiff, writeFileDiff } from "@vincemakes/kiso-tui";
|
|
9
|
+
import { deletionRiskHint, editFileDiff, writeFileDiff } from "@vincemakes/kiso-tui";
|
|
10
10
|
import { canonicalTargetPath, shellProgressPath } from "@vincemakes/kiso-tools-node";
|
|
11
11
|
import { canonicalizeUsage } from "@vincemakes/kiso-runtime";
|
|
12
12
|
import { dispatch } from "./dispatch.js";
|
|
@@ -178,24 +178,198 @@ function approvalDiff(name, input) {
|
|
|
178
178
|
return null; // never let the diff break the approval
|
|
179
179
|
}
|
|
180
180
|
}
|
|
181
|
+
/**
|
|
182
|
+
* TUI2-R3v2 ③ — the safer-options request: its prompt, and its parser.
|
|
183
|
+
*
|
|
184
|
+
* The prompt is deliberately small. It carries the pending call and
|
|
185
|
+
* nothing else — no conversation, no tools, no project context — because
|
|
186
|
+
* everything it does not send is rent the human pays for pressing a
|
|
187
|
+
* button, and because "propose a safer version of THIS command" is a
|
|
188
|
+
* question that needs no history to answer.
|
|
189
|
+
*/
|
|
190
|
+
/** TUI2-R3v2 ③: tools whose NEXT approval is the model's answer to a
|
|
191
|
+
* refusal — the "(amended)" marker's source. Per process, cleared as
|
|
192
|
+
* soon as it is shown: the marker describes ONE call, not a mode. */
|
|
193
|
+
const amendedCalls = new Set();
|
|
194
|
+
/**
|
|
195
|
+
* R3v2-F1: the format contract, stated FIRMLY. The first cut asked for
|
|
196
|
+
* "JSON ONLY" and left it there, which a verbose model reads as a
|
|
197
|
+
* preference — it wrote three sentences of preamble, opened a fence, and
|
|
198
|
+
* the cap ended the reply mid-string. Forbidding prose, naming the exact
|
|
199
|
+
* schema, and giving the nothing-is-safer case its own literal answer
|
|
200
|
+
* are all the same instruction: there is one thing to emit and no room
|
|
201
|
+
* to be helpful in the margins.
|
|
202
|
+
*
|
|
203
|
+
* The schema is an ENVELOPE rather than a bare array because a single
|
|
204
|
+
* top-level object leaves the model nowhere to put a preamble.
|
|
205
|
+
*/
|
|
206
|
+
export const SAFER_SYSTEM_PROMPT = [
|
|
207
|
+
"You propose safer alternatives to a single shell/tool call a human is being asked to approve.",
|
|
208
|
+
"Reply with JSON ONLY — no prose, no preamble, no code fence, nothing before or after the JSON.",
|
|
209
|
+
'The exact schema is {"alternatives":[{"command":"...","reason":"..."}]}, with 2-3 entries.',
|
|
210
|
+
'"command" is the full replacement call. "reason" is ONE line of plain language saying what it does differently.',
|
|
211
|
+
'Prefer alternatives that avoid irreversible deletion. If you cannot improve on it, reply {"alternatives":[]}.',
|
|
212
|
+
].join(" ");
|
|
213
|
+
/**
|
|
214
|
+
* R3v2-F1: the side query's output ceiling — raised from 500, which was
|
|
215
|
+
* the cap the live failures hit EXACTLY.
|
|
216
|
+
*
|
|
217
|
+
* The JSON-only reply the prompt now asks for is about 200 tokens for
|
|
218
|
+
* three alternatives, so this ceiling is a runaway guard and not a
|
|
219
|
+
* budget the answer is expected to approach: it exists so a model that
|
|
220
|
+
* ignores the contract and writes an essay still stops, not so the
|
|
221
|
+
* answer has room. Output is billed only when generated, and the query
|
|
222
|
+
* fires only on a press, so the raise costs nothing on the path that
|
|
223
|
+
* works and removes the one that could not.
|
|
224
|
+
*/
|
|
225
|
+
export const SAFER_MAX_TOKENS = 1500;
|
|
226
|
+
/**
|
|
227
|
+
* R3v2-F1: WHY the ask failed, when the reply's own text can prove it.
|
|
228
|
+
*
|
|
229
|
+
* This side reports the cause and never the copy — the sentences live in
|
|
230
|
+
* the panel package, next to each other, so there is one place where the
|
|
231
|
+
* words are chosen and one place they can drift from.
|
|
232
|
+
*
|
|
233
|
+
* "Cut short" is a DIAGNOSIS, so it is only claimed when the text shows
|
|
234
|
+
* it: a reply that closed its JSON and then failed our SHAPE returns
|
|
235
|
+
* null and gets the unqualified line, because telling that human their
|
|
236
|
+
* reply was truncated would be a confident wrong answer.
|
|
237
|
+
*/
|
|
238
|
+
export function saferFailure(text) {
|
|
239
|
+
return jsonBody(text) === "truncated" ? { reason: "truncated" } : null;
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* R3v2-F1: find the reply's JSON body by BALANCING brackets rather than
|
|
243
|
+
* by first-and-last.
|
|
244
|
+
*
|
|
245
|
+
* `indexOf("[")` / `lastIndexOf("]")` had two failure modes a verbose
|
|
246
|
+
* model hits constantly: a bracket in the trailing prose moved the end
|
|
247
|
+
* past the array, and a reply the cap cut in half had no end at all.
|
|
248
|
+
* Both returned null, and null could not say which — which is why the
|
|
249
|
+
* degradation line could not either.
|
|
250
|
+
*
|
|
251
|
+
* Returns the balanced slice, `"truncated"` when a value opens and the
|
|
252
|
+
* text ends before it closes, or null when there is no JSON value at
|
|
253
|
+
* all.
|
|
254
|
+
*/
|
|
255
|
+
function jsonBody(text) {
|
|
256
|
+
// a CLOSED fence is content-preserving to strip. An OPEN one means
|
|
257
|
+
// the reply ended inside the block — drop the opener and let the scan
|
|
258
|
+
// below reach the same verdict from the content.
|
|
259
|
+
const closed = text.match(/```(?:json)?\s*([\s\S]*?)```/);
|
|
260
|
+
const body = closed?.[1] ?? text.replace(/^[\s\S]*?```(?:json)?[ \t]*\r?\n/, "");
|
|
261
|
+
const start = body.search(/[[{]/);
|
|
262
|
+
if (start < 0)
|
|
263
|
+
return null;
|
|
264
|
+
let depth = 0;
|
|
265
|
+
let inString = false;
|
|
266
|
+
let escaped = false;
|
|
267
|
+
for (let i = start; i < body.length; i += 1) {
|
|
268
|
+
const c = body[i];
|
|
269
|
+
if (escaped) {
|
|
270
|
+
escaped = false;
|
|
271
|
+
}
|
|
272
|
+
else if (inString) {
|
|
273
|
+
if (c === "\\")
|
|
274
|
+
escaped = true;
|
|
275
|
+
else if (c === '"')
|
|
276
|
+
inString = false;
|
|
277
|
+
}
|
|
278
|
+
else if (c === '"') {
|
|
279
|
+
inString = true;
|
|
280
|
+
}
|
|
281
|
+
else if (c === "[" || c === "{") {
|
|
282
|
+
depth += 1;
|
|
283
|
+
}
|
|
284
|
+
else if (c === "]" || c === "}") {
|
|
285
|
+
depth -= 1;
|
|
286
|
+
if (depth === 0)
|
|
287
|
+
return body.slice(start, i + 1);
|
|
288
|
+
if (depth < 0)
|
|
289
|
+
return null;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
return "truncated";
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* Parse the model's answer DEFENSIVELY — anything unexpected is a
|
|
296
|
+
* failure, and a failure degrades honestly.
|
|
297
|
+
*
|
|
298
|
+
* The temptation here is to be clever: salvage a half-parse, coerce a
|
|
299
|
+
* string into a command, accept an object where an array was asked for.
|
|
300
|
+
* All of that produces a list of alternatives the model did not propose,
|
|
301
|
+
* shown to a human deciding whether to run a destructive command. The
|
|
302
|
+
* only honest failure mode is the dim line, so anything that is not
|
|
303
|
+
* exactly the requested shape returns null.
|
|
304
|
+
*
|
|
305
|
+
* A fenced code block is the one accommodation, because models emit it
|
|
306
|
+
* constantly and it changes no content.
|
|
307
|
+
*
|
|
308
|
+
* R3v2-F1 widens that accommodation and NOTHING else. Unwrapping the
|
|
309
|
+
* named `alternatives` envelope, and reading `reason` as the spelling of
|
|
310
|
+
* `why` the prompt now asks for, are transport details: the entries that
|
|
311
|
+
* come out are verbatim the entries the model put in. That is the line
|
|
312
|
+
* between an accommodation and the salvage this parser refuses — a
|
|
313
|
+
* salvage changes WHICH alternatives are shown, and every rule that does
|
|
314
|
+
* that is still here. One bad entry still poisons the batch.
|
|
315
|
+
*/
|
|
316
|
+
export function parseSaferOptions(text) {
|
|
317
|
+
const body = jsonBody(text);
|
|
318
|
+
// truncation and absence part ways in saferFailureNote(), which reads
|
|
319
|
+
// the same scan; for the list itself both are the same nothing.
|
|
320
|
+
if (body === null || body === "truncated")
|
|
321
|
+
return null;
|
|
322
|
+
let parsed;
|
|
323
|
+
try {
|
|
324
|
+
parsed = JSON.parse(body);
|
|
325
|
+
}
|
|
326
|
+
catch {
|
|
327
|
+
return null;
|
|
328
|
+
}
|
|
329
|
+
const envelope = typeof parsed === "object" && parsed !== null ? parsed.alternatives : undefined;
|
|
330
|
+
const list = Array.isArray(parsed) ? parsed : Array.isArray(envelope) ? envelope : null;
|
|
331
|
+
if (list === null || list.length === 0)
|
|
332
|
+
return null;
|
|
333
|
+
const out = [];
|
|
334
|
+
for (const item of list.slice(0, 3)) {
|
|
335
|
+
if (typeof item !== "object" || item === null)
|
|
336
|
+
return null;
|
|
337
|
+
const { command, reason, why } = item;
|
|
338
|
+
if (typeof command !== "string" || command.trim() === "")
|
|
339
|
+
return null;
|
|
340
|
+
const line = typeof reason === "string" ? reason : typeof why === "string" ? why : "";
|
|
341
|
+
out.push({ command: command.trim(), why: line.trim() });
|
|
342
|
+
}
|
|
343
|
+
return out.length === 0 ? null : out;
|
|
344
|
+
}
|
|
181
345
|
/** W21 — the panel view for a permission_requested: the rule line (the
|
|
182
346
|
* why-asked speaker + the §3.5 fix hint), the toolTarget title, the
|
|
183
347
|
* "▸ run paused" status, and the ALWAYS-verbose args. */
|
|
184
|
-
function approvalView(name, ev) {
|
|
348
|
+
function approvalView(name, ev, amended = false) {
|
|
185
349
|
const speaker = ev.speaker ?? "kiso";
|
|
186
350
|
const input = ev.input ?? {};
|
|
187
351
|
// exactOptionalPropertyTypes: the hint is OMITTED when the speaker has
|
|
188
352
|
// no fix (mode:accept-edits, shell in default) — never `hint: undefined`.
|
|
189
353
|
const hint = fixHintFor(speaker, name);
|
|
354
|
+
// TUI2-R3v2 ④: the deletion-risk line, for shell calls whose command
|
|
355
|
+
// matches one of the four irreversible patterns. Local rules, no
|
|
356
|
+
// request, and absent for every other command — which is most of them.
|
|
357
|
+
const risk = name === "shell" ? deletionRiskHint(String(input.command ?? "")) : null;
|
|
190
358
|
return {
|
|
191
359
|
flavor: "approval",
|
|
192
360
|
name,
|
|
193
361
|
title: toolTarget(name, input),
|
|
194
362
|
speaker,
|
|
195
363
|
...(hint !== undefined ? { hint } : {}),
|
|
364
|
+
...(risk !== null ? { riskHint: risk } : {}),
|
|
196
365
|
statusText: "▸ run paused",
|
|
197
366
|
args: approvalArgs(name, input),
|
|
198
367
|
fallbackQuestion: `approve ${escapeTerminal(name)}? (y/n) `,
|
|
368
|
+
// TUI2-R3v2 ③: the v4 frame's "(amended)" marker. It says WHY this
|
|
369
|
+
// call looks different from the one just refused — without it, a
|
|
370
|
+
// second approval for the same tool reads as the product asking
|
|
371
|
+
// twice rather than as the model answering.
|
|
372
|
+
...(amended ? { amended: true } : {}),
|
|
199
373
|
};
|
|
200
374
|
}
|
|
201
375
|
/** The panel's ALWAYS-verbose args: edit_file/write_file → the full ±
|
|
@@ -433,7 +607,25 @@ submitTurn) {
|
|
|
433
607
|
const name = ev.name;
|
|
434
608
|
body.toolApproval(ev.callId, approvalDiff(name, ev.input ?? {}));
|
|
435
609
|
const decisionId = ev.decisionId;
|
|
436
|
-
|
|
610
|
+
// TUI2-R3v2 ③: the on-demand alternatives provider. It is built
|
|
611
|
+
// per approval and captured by the panel; it fires ONLY if the
|
|
612
|
+
// human presses option 3, which is the whole zero-ambient-rent
|
|
613
|
+
// mechanism — no press, no request, nothing in the trace.
|
|
614
|
+
const safer = async () => {
|
|
615
|
+
const answer = await session.sideQuery({
|
|
616
|
+
purpose: "safer-options",
|
|
617
|
+
systemPrompt: SAFER_SYSTEM_PROMPT,
|
|
618
|
+
prompt: `the pending call is: ${name} ${JSON.stringify(ev.input ?? {})}`,
|
|
619
|
+
maxTokens: SAFER_MAX_TOKENS,
|
|
620
|
+
});
|
|
621
|
+
// R3v2-F1: a failure reports its CAUSE when the reply can
|
|
622
|
+
// prove one, so the panel can say which failure this was.
|
|
623
|
+
// saferFailure() returns null for every cause we cannot
|
|
624
|
+
// demonstrate, which is the unqualified line — unchanged.
|
|
625
|
+
return parseSaferOptions(answer) ?? saferFailure(answer);
|
|
626
|
+
};
|
|
627
|
+
const verdict = await askPanel(input, approvalView(name, ev, amendedCalls.has(name)), { safer });
|
|
628
|
+
amendedCalls.delete(name);
|
|
437
629
|
switch (verdict.action) {
|
|
438
630
|
case "cancel": {
|
|
439
631
|
// round 10: a cancellation is a CONSERVATIVE denial,
|
|
@@ -465,6 +657,9 @@ submitTurn) {
|
|
|
465
657
|
if (verdict.reason.trim() !== "") {
|
|
466
658
|
// No+words — the words become the tool_result; the
|
|
467
659
|
// run continues with the model seeing the denial.
|
|
660
|
+
// TUI2-R3v2 ③: whatever the model proposes next for
|
|
661
|
+
// this tool IS the amended call, and the panel says so.
|
|
662
|
+
amendedCalls.add(name);
|
|
468
663
|
await session.approve(decisionId, false, verdict.reason);
|
|
469
664
|
}
|
|
470
665
|
else {
|
package/dist/index.js
CHANGED
|
@@ -154,8 +154,8 @@ function editorInput(editor) {
|
|
|
154
154
|
},
|
|
155
155
|
// W21: the panel — the editor's own state machine takes the
|
|
156
156
|
// keys; the compositor renders it via the bound state.
|
|
157
|
-
panelAsk(view, onCommit) {
|
|
158
|
-
editor.beginPanel(view, onCommit);
|
|
157
|
+
panelAsk(view, onCommit, opts) {
|
|
158
|
+
editor.beginPanel(view, onCommit, opts);
|
|
159
159
|
},
|
|
160
160
|
panelCancel() {
|
|
161
161
|
editor.cancelPanel();
|
|
@@ -202,6 +202,10 @@ function makeLineInput() {
|
|
|
202
202
|
// "input lives here"; the line-mode path keeps the brick ▌, so
|
|
203
203
|
// pipe bytes do not change)
|
|
204
204
|
dock.bindInput(() => editor.dockState(), "› ");
|
|
205
|
+
// TUI2-R3v2 ②: the click hit-test's wiring — the compositor places
|
|
206
|
+
// the panel's option rows, so the compositor is what the editor asks
|
|
207
|
+
// where they are. Neither side computes the other's geometry.
|
|
208
|
+
editor.bindPanelRows(() => dock.panelOptionRows());
|
|
205
209
|
dock.bindMenu(() => editor.menuState()); // v3 §04: the slash-command menu
|
|
206
210
|
editor.bindAtItems(atFiles); // KC3 §5: the file source — listed per OPEN
|
|
207
211
|
dock.bindAt(() => editor.atState()); // KC3 §4: the picker's band
|
package/dist/state.d.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* creates the mutable ones (setBody / setAgentModel / setExtensionLists);
|
|
6
6
|
* the moved modules read and mutate at call time.
|
|
7
7
|
*/
|
|
8
|
-
import { Dock, type AtItem, type Body, type PanelVerdict, type PanelView, type SessionCardView } from "@vincemakes/kiso-tui";
|
|
8
|
+
import { Dock, type AtItem, type Body, type PanelVerdict, type PanelView, type SaferAnswer, type SessionCardView } from "@vincemakes/kiso-tui";
|
|
9
9
|
import type { KisoExtension, StoreRecord } from "@vincemakes/kiso-runtime";
|
|
10
10
|
/** finding #11: KISO_HOME is the ONE root — every default path derives from
|
|
11
11
|
* it (sessions, trust, extensions, mcp config, skills). The dedicated
|
|
@@ -80,7 +80,14 @@ export interface LineInput {
|
|
|
80
80
|
/** W21: open the approval panel — the editor's state machine takes
|
|
81
81
|
* the keys (the digits/y/n/tab/esc/enter routing, the rule input,
|
|
82
82
|
* the tab-amend), the compositor renders the block + the leads. */
|
|
83
|
-
|
|
83
|
+
/** TUI2-R3v2 ③: `opts.safer` is the on-demand alternatives provider —
|
|
84
|
+
* absent for every panel that has no such option (the ask, the pick,
|
|
85
|
+
* the trust gate), so those buttons cannot exist to be pressed.
|
|
86
|
+
* R3v2-F1: it resolves a SaferAnswer, so a failure that can name its
|
|
87
|
+
* cause does — `null` still means a failure with nothing to add. */
|
|
88
|
+
panelAsk(view: PanelView, onCommit: (v: PanelVerdict) => void, opts?: {
|
|
89
|
+
safer?: () => Promise<SaferAnswer>;
|
|
90
|
+
}): void;
|
|
84
91
|
/** W21: cancel the panel — the SIGINT pair to panelAsk. */
|
|
85
92
|
panelCancel(): void;
|
|
86
93
|
/** TUI2-R2 ②: open the session picker — the editor takes the keys
|
package/dist/trust-ui.d.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* chat.ts), the don't-ask-again rule writer, and the uncertain-execution
|
|
6
6
|
* decisions. All bodies moved verbatim from index.ts.
|
|
7
7
|
*/
|
|
8
|
-
import { type PanelVerdict, type PanelView } from "@vincemakes/kiso-tui";
|
|
8
|
+
import { type PanelVerdict, type PanelView, type SaferAnswer } from "@vincemakes/kiso-tui";
|
|
9
9
|
import type { AskUI } from "@vincemakes/kiso-ask-ext";
|
|
10
10
|
import { type ProjectArtifacts } from "@vincemakes/kiso-runtime";
|
|
11
11
|
import type { AgentSession } from "@vincemakes/kiso-runtime";
|
|
@@ -34,7 +34,9 @@ import { type LineInput } from "./state.js";
|
|
|
34
34
|
* the dead question.
|
|
35
35
|
*/
|
|
36
36
|
export declare let pendingAsk: (() => void) | null;
|
|
37
|
-
export declare function askPanel(input: LineInput, view: PanelView
|
|
37
|
+
export declare function askPanel(input: LineInput, view: PanelView, opts?: {
|
|
38
|
+
safer?: () => Promise<SaferAnswer>;
|
|
39
|
+
}): Promise<PanelVerdict>;
|
|
38
40
|
/**
|
|
39
41
|
* KC3.5 — the AskUI bridge: the panel the ask extension asks through.
|
|
40
42
|
*
|
package/dist/trust-ui.js
CHANGED
|
@@ -38,7 +38,12 @@ import { getMode } from "./mode.js";
|
|
|
38
38
|
* the dead question.
|
|
39
39
|
*/
|
|
40
40
|
export let pendingAsk = null;
|
|
41
|
-
export function askPanel(input, view
|
|
41
|
+
export function askPanel(input, view,
|
|
42
|
+
// TUI2-R3v2 ③: the safer-options provider, when the caller has one.
|
|
43
|
+
// Only the approval site passes it; every other panel (the ask, the
|
|
44
|
+
// pick, the trust gate, the uncertain resolutions) omits it, so their
|
|
45
|
+
// lists cannot grow a button they have no answer for.
|
|
46
|
+
opts) {
|
|
42
47
|
if (!process.stdin.isTTY) {
|
|
43
48
|
console.log(`[non-interactive — no human to ask: ${view.fallbackQuestion}]`);
|
|
44
49
|
return Promise.resolve({ action: "deny", reason: "no human to ask" });
|
|
@@ -63,7 +68,7 @@ export function askPanel(input, view) {
|
|
|
63
68
|
settled = true;
|
|
64
69
|
pendingAsk = null;
|
|
65
70
|
resolve(verdict);
|
|
66
|
-
});
|
|
71
|
+
}, opts);
|
|
67
72
|
}
|
|
68
73
|
else {
|
|
69
74
|
// v2c: a TTY without a dock (rows < 4) — the fallback question
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vincemakes/kiso-code",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"description": "kiso CLI — the durable coding agent that survives kill -9: kiso chat / kiso resume / kiso sessions.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -18,19 +18,19 @@
|
|
|
18
18
|
"test": "vitest run"
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@vincemakes/kiso-ask-ext": "0.
|
|
22
|
-
"@vincemakes/kiso-core": "0.
|
|
23
|
-
"@vincemakes/kiso-evals": "0.
|
|
24
|
-
"@vincemakes/kiso-mcp-ext": "0.
|
|
25
|
-
"@vincemakes/kiso-provider-anthropic": "0.
|
|
26
|
-
"@vincemakes/kiso-provider-openai": "0.
|
|
27
|
-
"@vincemakes/kiso-runtime": "0.
|
|
28
|
-
"@vincemakes/kiso-skills-ext": "0.
|
|
29
|
-
"@vincemakes/kiso-subagent-ext": "0.
|
|
30
|
-
"@vincemakes/kiso-task-ext": "0.
|
|
31
|
-
"@vincemakes/kiso-tools-node": "0.
|
|
32
|
-
"@vincemakes/kiso-tui": "0.
|
|
33
|
-
"@vincemakes/kiso-tui-cells": "0.
|
|
21
|
+
"@vincemakes/kiso-ask-ext": "0.13.0",
|
|
22
|
+
"@vincemakes/kiso-core": "0.13.0",
|
|
23
|
+
"@vincemakes/kiso-evals": "0.13.0",
|
|
24
|
+
"@vincemakes/kiso-mcp-ext": "0.13.0",
|
|
25
|
+
"@vincemakes/kiso-provider-anthropic": "0.13.0",
|
|
26
|
+
"@vincemakes/kiso-provider-openai": "0.13.0",
|
|
27
|
+
"@vincemakes/kiso-runtime": "0.13.0",
|
|
28
|
+
"@vincemakes/kiso-skills-ext": "0.13.0",
|
|
29
|
+
"@vincemakes/kiso-subagent-ext": "0.13.0",
|
|
30
|
+
"@vincemakes/kiso-task-ext": "0.13.0",
|
|
31
|
+
"@vincemakes/kiso-tools-node": "0.13.0",
|
|
32
|
+
"@vincemakes/kiso-tui": "0.13.0",
|
|
33
|
+
"@vincemakes/kiso-tui-cells": "0.13.0"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"@types/node": "^26.1.2",
|