@vincemakes/kiso-code 0.53.0 → 0.54.1
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.js +3 -1
- package/dist/create-coding-agent.js +2 -2
- package/dist/dispatch.d.ts +4 -2
- package/dist/dispatch.js +9 -5
- package/dist/empty-sessions.js +2 -0
- package/dist/floor.d.ts +2 -2
- package/dist/floor.js +13 -6
- package/dist/index.js +5 -0
- package/dist/policy.d.ts +2 -0
- package/dist/policy.js +2 -0
- package/dist/projects.js +1 -1
- package/dist/replay.js +2 -1
- package/dist/shell-facts.d.ts +9 -0
- package/dist/shell-facts.js +46 -0
- package/dist/shell-words.d.ts +2 -0
- package/dist/shell-words.js +4 -0
- package/dist/state.d.ts +3 -1
- package/dist/state.js +11 -1
- package/package.json +14 -14
package/dist/chat.js
CHANGED
|
@@ -1444,7 +1444,7 @@ export async function chat(session, faux, input, autoCompact, nav, seed) {
|
|
|
1444
1444
|
// empty box only while no other turn has started or waits; off with the faux model and KISO_FOLLOWUP=off
|
|
1445
1445
|
if (last?.type === "terminal" && last.outcome.kind === "completed" && !faux && input.setGhost !== undefined && process.env["KISO_FOLLOWUP"] !== "off" && queued === 0) {
|
|
1446
1446
|
void session.followUp({ maxTokens: 200, signal: AbortSignal.timeout(15_000) }).then((line) => {
|
|
1447
|
-
if (turnNo === myTurn && currentRun === null && queued === 0)
|
|
1447
|
+
if (turnNo === myTurn && currentRun === null && queued === 0 && !leaving)
|
|
1448
1448
|
input.setGhost?.(line === "" ? null : line);
|
|
1449
1449
|
}, () => undefined);
|
|
1450
1450
|
}
|
|
@@ -1902,11 +1902,13 @@ export async function chat(session, faux, input, autoCompact, nav, seed) {
|
|
|
1902
1902
|
requestSwitch: (id) => {
|
|
1903
1903
|
switchTo = id;
|
|
1904
1904
|
leaving = true;
|
|
1905
|
+
input.setGhost?.(null); // REL-0530-F1: the follow-up belongs to the session being left
|
|
1905
1906
|
resolveEnd();
|
|
1906
1907
|
},
|
|
1907
1908
|
requestReload: () => {
|
|
1908
1909
|
reloadReq = true;
|
|
1909
1910
|
leaving = true;
|
|
1911
|
+
input.setGhost?.(null);
|
|
1910
1912
|
resolveEnd();
|
|
1911
1913
|
},
|
|
1912
1914
|
sessions: () => nav?.sessions() ?? [],
|
|
@@ -34,7 +34,7 @@ import { adapterOptionsFor } from "./auth/adapter-options.js";
|
|
|
34
34
|
import { createFauxProvider } from "@vincemakes/kiso-evals";
|
|
35
35
|
import { sweepStaleMergeDirs } from "./temp-sweep.js";
|
|
36
36
|
import { CODING_TOOL_RULES, composeSystemPrompt } from "./coding-prompt.js";
|
|
37
|
-
import { createAgent, loadExtensions, loadProjectExtensions, SessionStore } from "@vincemakes/kiso-runtime";
|
|
37
|
+
import { createAgent, loadExtensions, loadProjectExtensions, readEnvironment, SessionStore } from "@vincemakes/kiso-runtime";
|
|
38
38
|
import { preferences, startingEffort } from "./preferences.js";
|
|
39
39
|
/** LT-1: the stream watchdog's bound from the environment — a non-negative
|
|
40
40
|
* number of milliseconds (0 disables it); anything else is ignored. The
|
|
@@ -269,7 +269,7 @@ export async function createCodingAgent(sessionId, input, modelFlag) {
|
|
|
269
269
|
setUserProtectedPaths(userConfig?.protectedPaths);
|
|
270
270
|
const extensions = [
|
|
271
271
|
protectedShellExtension({ files: protectedFiles, workspaceRoot, env: () => ({ home: homedir(), kisoHome: kisoHome() }) }),
|
|
272
|
-
floorExtension(() => floorOn, workspaceRoot),
|
|
272
|
+
floorExtension(() => floorOn, workspaceRoot, undefined, () => (sessionId === undefined ? [] : readEnvironment(activeStoreDir, sessionId).added)),
|
|
273
273
|
breakerExtension(),
|
|
274
274
|
...modeExtensions(workspaceRoot),
|
|
275
275
|
readOnlyShellExtension(codingToolOptions),
|
package/dist/dispatch.d.ts
CHANGED
|
@@ -74,6 +74,8 @@ export declare function parseBang(trimmed: string): {
|
|
|
74
74
|
export declare function markCompactWhy(why: string): void;
|
|
75
75
|
export declare function markAutoCompact(): void;
|
|
76
76
|
export declare function dispatch(line: string, ctx: DispatchCtx): void;
|
|
77
|
-
/** /fork's rows: each finished run
|
|
78
|
-
*
|
|
77
|
+
/** /fork's rows: each finished run, newest first, by the person's message it answered and when it ended; the opened
|
|
78
|
+
* row shows the reply's first line under it (owner 2026-11-04: the person knows a turn by what they asked, and an
|
|
79
|
+
* "answered:" label over their own line read backwards). The card's id is the seq of the run's terminal, where
|
|
80
|
+
* SessionStore.fork cuts. */
|
|
79
81
|
export declare function replyCards(records: readonly StoreRecord[]): SessionCardView[];
|
package/dist/dispatch.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
import { STATUS_GLYPHS, contextRows, contextSheetRows, contextUnavailableRows, contextUnavailableSheetRows, displayVerb, escapeTerminal, helpRows, toolTarget, infoSheetRows, kUnit, modePickView, modelPickView, namedPickView, settingsPickView, compactingStatus, liveRow, palette, renderEvent, settledLabel, slashCommandNames } from "@vincemakes/kiso-tui";
|
|
7
7
|
import { newSessionId } from "./session-id.js";
|
|
8
8
|
import { buildAdapter, isForkNote, lookupModelMetadata, readSessionName, resolveContinuationScope, resolveReasoning, sessionTitle, tiersFor, writeSessionName } from "@vincemakes/kiso-runtime/internal";
|
|
9
|
+
import { isEnvironmentNote } from "@vincemakes/kiso-runtime";
|
|
9
10
|
import { ForkRefusedError, SessionStore } from "@vincemakes/kiso-runtime";
|
|
10
11
|
import { MODE_LABEL, MODE_NOTE, OFFERED_MODES, applyModeSetting, getDontAsk, getMode, modeDisplay, parseMode, setDontAsk } from "./mode.js";
|
|
11
12
|
import { clipboardWrite, lastAnswer } from "./clipboard.js";
|
|
@@ -1628,15 +1629,17 @@ async function tasksCommand(arg, ctx) {
|
|
|
1628
1629
|
else if (action === 1)
|
|
1629
1630
|
stop(task);
|
|
1630
1631
|
}
|
|
1631
|
-
/** /fork's rows: each finished run
|
|
1632
|
-
*
|
|
1632
|
+
/** /fork's rows: each finished run, newest first, by the person's message it answered and when it ended; the opened
|
|
1633
|
+
* row shows the reply's first line under it (owner 2026-11-04: the person knows a turn by what they asked, and an
|
|
1634
|
+
* "answered:" label over their own line read backwards). The card's id is the seq of the run's terminal, where
|
|
1635
|
+
* SessionStore.fork cuts. */
|
|
1633
1636
|
export function replyCards(records) {
|
|
1634
1637
|
const cards = [];
|
|
1635
1638
|
let asked = "";
|
|
1636
1639
|
let reply = "";
|
|
1637
1640
|
for (const { event: e, ts } of records) {
|
|
1638
1641
|
if (e.type === "user_input" && e.source !== "system") {
|
|
1639
|
-
const content = Array.isArray(e.content) ? e.content.filter((b) => !(b.type === "text" && isForkNote(b.text ?? ""))) : e.content;
|
|
1642
|
+
const content = Array.isArray(e.content) ? e.content.filter((b) => !(b.type === "text" && (isForkNote(b.text ?? "") || isEnvironmentNote(b.text ?? "")))) : e.content;
|
|
1640
1643
|
asked = (typeof content === "string" ? content : content.map((b) => (b.type === "text" ? (b.text ?? "") : "")).join(" ")).trim();
|
|
1641
1644
|
reply = "";
|
|
1642
1645
|
}
|
|
@@ -1645,9 +1648,10 @@ export function replyCards(records) {
|
|
|
1645
1648
|
else if (e.type === "text_delta")
|
|
1646
1649
|
reply += e.text;
|
|
1647
1650
|
else if (e.type === "terminal" && e.outcome.kind === "completed") {
|
|
1648
|
-
// the row is
|
|
1651
|
+
// the row is the person's own line; the reply's first line, inline code read as plain text, opens under it
|
|
1649
1652
|
const first = (reply.split("\n").map((l) => l.trim()).find((l) => l !== "") ?? "(no reply text)").replace(/`([^`]*)`/g, "$1");
|
|
1650
|
-
|
|
1653
|
+
const line = asked.split("\n").map((l) => l.trim()).find((l) => l !== "") ?? "(no message text)";
|
|
1654
|
+
cards.unshift({ id: String(e.seq), title: line, badge: "completed", turns: null, updatedAt: ts, uncertain: 0, asks: 0, outcome: null, detail: `└ ${first}` });
|
|
1651
1655
|
}
|
|
1652
1656
|
}
|
|
1653
1657
|
return cards;
|
package/dist/empty-sessions.js
CHANGED
|
@@ -65,6 +65,8 @@ export function findEmptySessions(dirs, alive = pidAlive) {
|
|
|
65
65
|
const files = [name];
|
|
66
66
|
if (names.includes(lock))
|
|
67
67
|
files.push(lock);
|
|
68
|
+
if (names.includes(`${id}.environment.json`))
|
|
69
|
+
files.push(`${id}.environment.json`);
|
|
68
70
|
if (existsSync(join(dir, "traces", `${id}.jsonl`)))
|
|
69
71
|
files.push(join("traces", `${id}.jsonl`));
|
|
70
72
|
empty.push({ dir, id, files });
|
package/dist/floor.d.ts
CHANGED
|
@@ -88,12 +88,12 @@ export declare function gitInvocation(args: readonly LooseWord[]): GitInvocation
|
|
|
88
88
|
* Would the floor refuse this command line? `workspaceRoot` is where the
|
|
89
89
|
* shell runs it.
|
|
90
90
|
*/
|
|
91
|
-
export declare function floorCheck(commandLine: string, workspaceRoot: string, home?: string): FloorVerdict;
|
|
91
|
+
export declare function floorCheck(commandLine: string, workspaceRoot: string, home?: string, added?: readonly string[]): FloorVerdict;
|
|
92
92
|
/** Is there a destructive command anywhere in the line, whatever its
|
|
93
93
|
* target? A saved allow is never inherited by one (plan §4). */
|
|
94
94
|
export declare function isDestructive(commandLine: string): boolean;
|
|
95
95
|
/** The chain member, at the chain's head: DENY or ABSTAIN. */
|
|
96
|
-
export declare function floorExtension(on: () => boolean, workspaceRoot: () => string, check?: typeof floorCheck): KisoExtension;
|
|
96
|
+
export declare function floorExtension(on: () => boolean, workspaceRoot: () => string, check?: typeof floorCheck, added?: () => readonly string[]): KisoExtension;
|
|
97
97
|
/** Plan §4: "destructive commands never inherit a saved allow in the
|
|
98
98
|
* asking modes" — the predicate the saved-allow wrap
|
|
99
99
|
* (protected-writes.ts) is given, keyed on destructiveness, not on the
|
package/dist/floor.js
CHANGED
|
@@ -77,9 +77,9 @@ function windowsRoots(home) {
|
|
|
77
77
|
/** Memoized per (workspace, home): the roots do not move within a session,
|
|
78
78
|
* and resolving forty of them cost every shell call ~4 ms. */
|
|
79
79
|
const WHERE = new Map();
|
|
80
|
-
function where(root, home) {
|
|
80
|
+
function where(root, home, added = []) {
|
|
81
81
|
const dialect = hostDialect();
|
|
82
|
-
const key = `${dialect.msys ? "msys" : "posix"}\0${root}\0${home}`;
|
|
82
|
+
const key = `${dialect.msys ? "msys" : "posix"}\0${root}\0${home}\0${added.join("\0")}`;
|
|
83
83
|
const hit = WHERE.get(key);
|
|
84
84
|
if (hit !== undefined)
|
|
85
85
|
return hit;
|
|
@@ -94,6 +94,7 @@ function where(root, home) {
|
|
|
94
94
|
rootReal,
|
|
95
95
|
homeReal: real(home),
|
|
96
96
|
gitReal: join(rootReal, ".git"),
|
|
97
|
+
added: added.map(real),
|
|
97
98
|
sys: named(roots.sys),
|
|
98
99
|
mounts: named(roots.mounts),
|
|
99
100
|
temps: [...new Set(roots.temps.map(real))],
|
|
@@ -137,6 +138,12 @@ function unrecoverable(w, p, over) {
|
|
|
137
138
|
return what("the workspace root");
|
|
138
139
|
if (within(p, w.rootReal))
|
|
139
140
|
return what(`a directory above the workspace (${p})`);
|
|
141
|
+
for (const a of w.added) {
|
|
142
|
+
if (same(p, a) || within(p, a))
|
|
143
|
+
return what(`an added folder, or a directory above one (${p})`);
|
|
144
|
+
if (within(hostDialect().path.join(a, ".git"), p))
|
|
145
|
+
return what("an added folder's .git");
|
|
146
|
+
}
|
|
140
147
|
// R7: the workspace's history — without a remote, gone
|
|
141
148
|
if (within(w.gitReal, p))
|
|
142
149
|
return what("the workspace's .git");
|
|
@@ -519,8 +526,8 @@ const child = (s) => ({ cwds: [...s.cwds], lastCd: null, prev: s.prev });
|
|
|
519
526
|
* Would the floor refuse this command line? `workspaceRoot` is where the
|
|
520
527
|
* shell runs it.
|
|
521
528
|
*/
|
|
522
|
-
export function floorCheck(commandLine, workspaceRoot, home = homedir()) {
|
|
523
|
-
const w = where(workspaceRoot, home);
|
|
529
|
+
export function floorCheck(commandLine, workspaceRoot, home = homedir(), added = []) {
|
|
530
|
+
const w = where(workspaceRoot, home, added);
|
|
524
531
|
const seen = new Set();
|
|
525
532
|
const tilde = (word) => (word.tilde && (word.text === "~" || word.text.startsWith("~/")) ? home + word.text.slice(1) : word.text);
|
|
526
533
|
const walk = (nodes, state, depth) => {
|
|
@@ -652,7 +659,7 @@ export function isDestructive(commandLine) {
|
|
|
652
659
|
}
|
|
653
660
|
const ABSTAIN = { action: "abstain" };
|
|
654
661
|
/** The chain member, at the chain's head: DENY or ABSTAIN. */
|
|
655
|
-
export function floorExtension(on, workspaceRoot, check = floorCheck) {
|
|
662
|
+
export function floorExtension(on, workspaceRoot, check = floorCheck, added = () => []) {
|
|
656
663
|
return {
|
|
657
664
|
name: "floor",
|
|
658
665
|
approvals: [
|
|
@@ -669,7 +676,7 @@ export function floorExtension(on, workspaceRoot, check = floorCheck) {
|
|
|
669
676
|
// allow. The floor denies what it could not read.
|
|
670
677
|
let v;
|
|
671
678
|
try {
|
|
672
|
-
v = check(command, workspaceRoot());
|
|
679
|
+
v = check(command, workspaceRoot(), undefined, added());
|
|
673
680
|
}
|
|
674
681
|
catch (err) {
|
|
675
682
|
return { action: "deny", reason: `the floor could not read this line (${err instanceof Error ? err.name : "error"}) — kiso does not run it.` };
|
package/dist/index.js
CHANGED
|
@@ -33,6 +33,7 @@ import { createRequire } from "node:module";
|
|
|
33
33
|
import { basename, dirname, join } from "node:path";
|
|
34
34
|
import { Body, Editor, PROMPT, bannerLines, currentGround, currentGroundRgb, parseOscColor, resolveGround, setGround, escapeTerminal, extensionsBannerText, extensionsFact, idColumn, interactivePrompt, palette, renderSessionLine, sessionListFooter, sessionListHeader, sessionListRow, sessionListUnknownLine, slashCommandNames } from "@vincemakes/kiso-tui";
|
|
35
35
|
import { disposeExtensions, SessionStore } from "@vincemakes/kiso-runtime";
|
|
36
|
+
import { readEnvironment } from "@vincemakes/kiso-runtime";
|
|
36
37
|
import { listSessionSidecars, migrateSummaries, readProfile, readSessionName, summaryMigrationPending, writeSessionName } from "@vincemakes/kiso-runtime/internal";
|
|
37
38
|
import { skillMenuItems } from "./skill-invoke.js";
|
|
38
39
|
import { canonicalPath, hasSession, locateSession, projectLayoutActive, sessionFolders } from "./projects.js";
|
|
@@ -1073,6 +1074,10 @@ async function chatLoop(first, firstId, input, autoCompact) {
|
|
|
1073
1074
|
refused = true;
|
|
1074
1075
|
continue;
|
|
1075
1076
|
}
|
|
1077
|
+
// ADR-0058 Amendment 19: the folders a web Work added; the tools here honour them too
|
|
1078
|
+
const addedFolders = readEnvironment(activeStoreDir, id).added;
|
|
1079
|
+
if (addedFolders.length > 0)
|
|
1080
|
+
bodyLog(`also working in: ${addedFolders.map(tildeOf).join(", ")}`);
|
|
1076
1081
|
opened = id;
|
|
1077
1082
|
if (rebuilt) {
|
|
1078
1083
|
rebuilt = false;
|
package/dist/policy.d.ts
CHANGED
|
@@ -10,7 +10,9 @@
|
|
|
10
10
|
export { CODING_TOOL_RULES, SYSTEM_PROMPT, composeSystemPrompt } from "./coding-prompt.js";
|
|
11
11
|
export { breakerExtension } from "./breaker.js";
|
|
12
12
|
export { floorExtension } from "./floor.js";
|
|
13
|
+
export { fileStopRoots } from "./shell-words.js";
|
|
13
14
|
export { MODE_LABEL, OFFERED_MODES, getMode, modeExtensions, modeSystemPrompt, setMode } from "./mode.js";
|
|
14
15
|
export { protectedShellExtension } from "./protected-shell.js";
|
|
15
16
|
export { guardSavedAllow, neverInheritedCall } from "./protected-writes.js";
|
|
16
17
|
export { readOnlyShellExtension } from "./readonly-shell.js";
|
|
18
|
+
export { cdTargets, isGhPrCreate } from "./shell-facts.js";
|
package/dist/policy.js
CHANGED
|
@@ -10,7 +10,9 @@
|
|
|
10
10
|
export { CODING_TOOL_RULES, SYSTEM_PROMPT, composeSystemPrompt } from "./coding-prompt.js";
|
|
11
11
|
export { breakerExtension } from "./breaker.js";
|
|
12
12
|
export { floorExtension } from "./floor.js";
|
|
13
|
+
export { fileStopRoots } from "./shell-words.js";
|
|
13
14
|
export { MODE_LABEL, OFFERED_MODES, getMode, modeExtensions, modeSystemPrompt, setMode } from "./mode.js";
|
|
14
15
|
export { protectedShellExtension } from "./protected-shell.js";
|
|
15
16
|
export { guardSavedAllow, neverInheritedCall } from "./protected-writes.js";
|
|
16
17
|
export { readOnlyShellExtension } from "./readonly-shell.js";
|
|
18
|
+
export { cdTargets, isGhPrCreate } from "./shell-facts.js";
|
package/dist/projects.js
CHANGED
|
@@ -153,7 +153,7 @@ export function locateSession(home, id, except) {
|
|
|
153
153
|
* repeated move is a no-op.
|
|
154
154
|
*/
|
|
155
155
|
export function sessionFiles(id) {
|
|
156
|
-
return [join("traces", `${id}.jsonl`), `${id}.meta.json`, `${id}.lock`, `${id}.jsonl`];
|
|
156
|
+
return [join("traces", `${id}.jsonl`), `${id}.meta.json`, `${id}.environment.json`, `${id}.environment.json.lock`, `${id}.lock`, `${id}.jsonl`];
|
|
157
157
|
}
|
|
158
158
|
export function moveSessionFiles(fromDir, toDir, id, files = sessionFiles(id)) {
|
|
159
159
|
let moved = 0;
|
package/dist/replay.js
CHANGED
|
@@ -23,6 +23,7 @@ import { echoText } from "@vincemakes/kiso-tui-cells/render";
|
|
|
23
23
|
import { bandHeader } from "@vincemakes/kiso-tui-cells/strings";
|
|
24
24
|
import { cappedWaitLine, taskNoticeLines, taskNoticeRows, taskWhat } from "./task-notice.js";
|
|
25
25
|
import { isForkNote } from "@vincemakes/kiso-runtime/internal";
|
|
26
|
+
import { isEnvironmentNote } from "@vincemakes/kiso-runtime";
|
|
26
27
|
/** How many turns stay on screen in full. */
|
|
27
28
|
const FULL_TURNS = 2;
|
|
28
29
|
/** A user turn's opening line, or null when this input does not open one
|
|
@@ -34,7 +35,7 @@ function askOf(e) {
|
|
|
34
35
|
return null;
|
|
35
36
|
const via = e.via;
|
|
36
37
|
// a fork's first input carries the fork note before the person's words: the model's, not the line they typed
|
|
37
|
-
const content = Array.isArray(e.content) ? e.content.filter((b) => !(b.type === "text" && isForkNote(b.text ?? ""))) : e.content;
|
|
38
|
+
const content = Array.isArray(e.content) ? e.content.filter((b) => !(b.type === "text" && (isForkNote(b.text ?? "") || isEnvironmentNote(b.text ?? "")))) : e.content;
|
|
38
39
|
const text = typeof via?.line === "string" ? via.line : echoText(content);
|
|
39
40
|
return text.trim() === "" ? null : text;
|
|
40
41
|
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The directories a line moves the shell to, in order, when the call that ran it did not fail: the literal `cd <path>` commands of a line
|
|
3
|
+
* that is one `&&` chain, so every one of them ran (`cd /nonexistent; echo hello` exits 0 without having moved, and so does
|
|
4
|
+
* `false && cd /x && ls || true`). Relative paths resolve against `base`, then against the previous `cd`; `~` is `home`. An option, a
|
|
5
|
+
* variable or a word the host's shell cannot spell stops the reading there.
|
|
6
|
+
*/
|
|
7
|
+
export declare function cdTargets(line: string, base: string, home: string): readonly string[];
|
|
8
|
+
/** Whether the line is one `&&` chain that ENDS in `gh pr create`: what its output ends with is the pull request it made (a mention in an echo, a `||` branch, or a command after it that prints another URL, is not one). */
|
|
9
|
+
export declare function isGhPrCreate(line: string): boolean;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Two facts a UI may read from a shell line, conservatively: literal words only, and what the reader cannot read yields nothing.
|
|
3
|
+
* They are DISPLAY facts (the web page's Recent folders and Repository rows): nothing here grants or refuses anything.
|
|
4
|
+
*/
|
|
5
|
+
import { hostDialect, msysPath, parseShellLooseChecked } from "./shell-words.js";
|
|
6
|
+
const literal = (w) => (w === undefined || w.unknownAt !== -1 || w.variableOnly ? null : w.text);
|
|
7
|
+
/** The commands of a line that is ONE `&&` chain (no `||`, `;`, pipe or group anywhere), or null: only then did every command run when the call did not fail. */
|
|
8
|
+
function chainOf(line) {
|
|
9
|
+
const { nodes, truncated } = parseShellLooseChecked(line);
|
|
10
|
+
if (truncated || nodes.some((n, i) => n.kind !== "cmd" || (i > 0 && n.joinedBy !== "&&")))
|
|
11
|
+
return null;
|
|
12
|
+
return nodes;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* The directories a line moves the shell to, in order, when the call that ran it did not fail: the literal `cd <path>` commands of a line
|
|
16
|
+
* that is one `&&` chain, so every one of them ran (`cd /nonexistent; echo hello` exits 0 without having moved, and so does
|
|
17
|
+
* `false && cd /x && ls || true`). Relative paths resolve against `base`, then against the previous `cd`; `~` is `home`. An option, a
|
|
18
|
+
* variable or a word the host's shell cannot spell stops the reading there.
|
|
19
|
+
*/
|
|
20
|
+
export function cdTargets(line, base, home) {
|
|
21
|
+
const chain = chainOf(line);
|
|
22
|
+
if (chain === null)
|
|
23
|
+
return [];
|
|
24
|
+
const out = [];
|
|
25
|
+
const d = hostDialect(); // the shell's own spelling: Git Bash on Windows (`/c/x`, `C:/x`), POSIX elsewhere
|
|
26
|
+
let at = base;
|
|
27
|
+
for (const cmd of chain) {
|
|
28
|
+
if (literal(cmd.argv[0]) !== "cd")
|
|
29
|
+
continue;
|
|
30
|
+
const raw = cmd.argv.length === 2 ? literal(cmd.argv[1]) : null;
|
|
31
|
+
if (raw === null || raw === "-" || raw.startsWith("-"))
|
|
32
|
+
break;
|
|
33
|
+
const word = raw === "~" || raw.startsWith("~/") ? d.path.join(home, raw.slice(2)) : raw;
|
|
34
|
+
const read = d.msys ? msysPath(word) : { native: word };
|
|
35
|
+
if ("opaque" in read)
|
|
36
|
+
break;
|
|
37
|
+
at = d.path.resolve(at, read.native);
|
|
38
|
+
out.push(at);
|
|
39
|
+
}
|
|
40
|
+
return out;
|
|
41
|
+
}
|
|
42
|
+
/** Whether the line is one `&&` chain that ENDS in `gh pr create`: what its output ends with is the pull request it made (a mention in an echo, a `||` branch, or a command after it that prints another URL, is not one). */
|
|
43
|
+
export function isGhPrCreate(line) {
|
|
44
|
+
const c = chainOf(line)?.at(-1);
|
|
45
|
+
return c !== undefined && literal(c.argv[0]) === "gh" && literal(c.argv[1]) === "pr" && literal(c.argv[2]) === "create";
|
|
46
|
+
}
|
package/dist/shell-words.d.ts
CHANGED
|
@@ -93,6 +93,8 @@ export declare const HOME_SUBTREES: readonly string[];
|
|
|
93
93
|
/** The home subtrees for this host: on Windows, AppData too — credentials
|
|
94
94
|
* and configuration that runs live there (Windows P2). */
|
|
95
95
|
export declare function homeSubtrees(): readonly string[];
|
|
96
|
+
/** Folders the file tools' new reach never enters (ADR-0058 Amendment 19): kiso's own home and the credential homes. */
|
|
97
|
+
export declare function fileStopRoots(home: string, kisoHome: string): readonly string[];
|
|
96
98
|
/** A real path in the case the DISK holds it. The JS realpath keeps the
|
|
97
99
|
* case it was given, so on a case-insensitive disk `.ENV` stayed `.ENV`
|
|
98
100
|
* and walked past every name-based predicate (the lead's review, B2). */
|
package/dist/shell-words.js
CHANGED
|
@@ -334,6 +334,10 @@ export const HOME_SUBTREES = [".ssh", ".config", ".kiso", ".gnupg", ".aws"];
|
|
|
334
334
|
export function homeSubtrees() {
|
|
335
335
|
return hostDialect().msys ? [...HOME_SUBTREES, "AppData"] : HOME_SUBTREES;
|
|
336
336
|
}
|
|
337
|
+
/** Folders the file tools' new reach never enters (ADR-0058 Amendment 19): kiso's own home and the credential homes. */
|
|
338
|
+
export function fileStopRoots(home, kisoHome) {
|
|
339
|
+
return [kisoHome, ...homeSubtrees().map((s) => hostDialect().path.join(home, s))];
|
|
340
|
+
}
|
|
337
341
|
/** A real path in the case the DISK holds it. The JS realpath keeps the
|
|
338
342
|
* case it was given, so on a case-insensitive disk `.ENV` stayed `.ENV`
|
|
339
343
|
* and walked past every name-based predicate (the lead's review, B2). */
|
package/dist/state.d.ts
CHANGED
|
@@ -10,7 +10,7 @@ import type { TaskDeliveryItem } from "@vincemakes/kiso-core";
|
|
|
10
10
|
import { kisoHome, type KisoExtension, type StoreRecord } from "@vincemakes/kiso-runtime";
|
|
11
11
|
import type { ReasoningSetting } from "@vincemakes/kiso-runtime/internal";
|
|
12
12
|
import { TaskManager } from "@vincemakes/kiso-runtime/internal";
|
|
13
|
-
import { type ShellTasks } from "@vincemakes/kiso-tools-node";
|
|
13
|
+
import { type FileAccess, type ShellTasks } from "@vincemakes/kiso-tools-node";
|
|
14
14
|
/** finding #11: KISO_HOME is the ONE root — every default path derives from
|
|
15
15
|
* it (sessions, trust, extensions, mcp config, skills). The dedicated
|
|
16
16
|
* env vars (KISO_EXTENSIONS_DIR / KISO_MCP_CONFIG / KISO_SKILLS_DIR)
|
|
@@ -35,6 +35,8 @@ export declare function codingToolOptions(): {
|
|
|
35
35
|
readonly excludeRoots: readonly string[];
|
|
36
36
|
readonly secretEnvNames: readonly string[];
|
|
37
37
|
readonly protectedFiles: readonly string[];
|
|
38
|
+
readonly access: (sessionId: string | undefined) => FileAccess;
|
|
39
|
+
readonly stopRoots: readonly string[];
|
|
38
40
|
readonly tasks: (sessionId: string | undefined) => ShellTasks | undefined;
|
|
39
41
|
readonly observedRevision: (sessionId: string | undefined, full: string, kind?: "edit" | "write") => string | undefined;
|
|
40
42
|
readonly limits?: {
|
package/dist/state.js
CHANGED
|
@@ -12,12 +12,14 @@ import { dirname, join, resolve } from "node:path";
|
|
|
12
12
|
import { fileURLToPath } from "node:url";
|
|
13
13
|
import { AT_CAP, AT_SKIP, Dock } from "@vincemakes/kiso-tui";
|
|
14
14
|
import { projectMessages } from "@vincemakes/kiso-core";
|
|
15
|
-
import { kisoHome } from "@vincemakes/kiso-runtime";
|
|
15
|
+
import { kisoHome, readEnvironment } from "@vincemakes/kiso-runtime";
|
|
16
16
|
import { TaskManager } from "@vincemakes/kiso-runtime/internal";
|
|
17
17
|
import { processTaskBackend, readShowsWholeFile } from "@vincemakes/kiso-tools-node";
|
|
18
18
|
import { collectWriter } from "@vincemakes/kiso-subagent-ext";
|
|
19
19
|
import { canonicalPath, LEGACY_SESSIONS_DIR, projectDirFor, projectLayoutActive } from "./projects.js";
|
|
20
20
|
import { taskNoticeLines, taskWhat } from "./task-notice.js";
|
|
21
|
+
import { getMode } from "./mode.js";
|
|
22
|
+
import { fileStopRoots } from "./shell-words.js";
|
|
21
23
|
/** finding #11: KISO_HOME is the ONE root — every default path derives from
|
|
22
24
|
* it (sessions, trust, extensions, mcp config, skills). The dedicated
|
|
23
25
|
* env vars (KISO_EXTENSIONS_DIR / KISO_MCP_CONFIG / KISO_SKILLS_DIR)
|
|
@@ -49,6 +51,12 @@ let printRun = false;
|
|
|
49
51
|
export function setPrintRun() {
|
|
50
52
|
printRun = true;
|
|
51
53
|
}
|
|
54
|
+
/** ADR-0058 Amendment 19: the same list the web reads, per call; a delegate child gets none of it. */
|
|
55
|
+
function fileAccess(sessionId) {
|
|
56
|
+
if ((Number.parseInt(process.env.KISO_SUBAGENT_DEPTH ?? "0", 10) || 0) >= 1)
|
|
57
|
+
return { added: [], outside: "deny" };
|
|
58
|
+
return { added: sessionId === undefined || activeStoreDir === "" ? [] : readEnvironment(activeStoreDir, sessionId).added, outside: getMode() === "full-access" ? "allow" : "deny" };
|
|
59
|
+
}
|
|
52
60
|
export function codingToolOptions() {
|
|
53
61
|
// KISO_MIN_FOREGROUND_MS — the test rigs' knob (like KISO_AUTO_DETACH_MS):
|
|
54
62
|
// the least foreground wait (ADR-0058 Amendment 10, default 60 s). The
|
|
@@ -66,6 +74,8 @@ export function codingToolOptions() {
|
|
|
66
74
|
excludeRoots: [kisoHome()],
|
|
67
75
|
secretEnvNames: secretEnvNamesOf(configModels),
|
|
68
76
|
protectedFiles: protectedFiles(),
|
|
77
|
+
access: fileAccess,
|
|
78
|
+
stopRoots: fileStopRoots(homedir(), kisoHome()),
|
|
69
79
|
tasks: tasksFor,
|
|
70
80
|
observedRevision: observedRevisionFromLog,
|
|
71
81
|
...(Object.keys(limits).length > 0 ? { limits } : {}),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vincemakes/kiso-code",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.54.1",
|
|
4
4
|
"description": "kiso-code \u2014 the coding agent on kiso, the durable runtime for AI agents: kiso chat / kiso resume / kiso sessions.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -29,19 +29,19 @@
|
|
|
29
29
|
"test": "vitest run"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@vincemakes/kiso-ask-ext": "0.
|
|
33
|
-
"@vincemakes/kiso-core": "0.
|
|
34
|
-
"@vincemakes/kiso-evals": "0.
|
|
35
|
-
"@vincemakes/kiso-mcp-ext": "0.
|
|
36
|
-
"@vincemakes/kiso-provider-anthropic": "0.
|
|
37
|
-
"@vincemakes/kiso-provider-openai": "0.
|
|
38
|
-
"@vincemakes/kiso-provider-openai-responses": "0.
|
|
39
|
-
"@vincemakes/kiso-runtime": "0.
|
|
40
|
-
"@vincemakes/kiso-skills-ext": "0.
|
|
41
|
-
"@vincemakes/kiso-subagent-ext": "0.
|
|
42
|
-
"@vincemakes/kiso-tools-node": "0.
|
|
43
|
-
"@vincemakes/kiso-tui": "0.
|
|
44
|
-
"@vincemakes/kiso-tui-cells": "0.
|
|
32
|
+
"@vincemakes/kiso-ask-ext": "0.54.1",
|
|
33
|
+
"@vincemakes/kiso-core": "0.54.1",
|
|
34
|
+
"@vincemakes/kiso-evals": "0.54.1",
|
|
35
|
+
"@vincemakes/kiso-mcp-ext": "0.54.1",
|
|
36
|
+
"@vincemakes/kiso-provider-anthropic": "0.54.1",
|
|
37
|
+
"@vincemakes/kiso-provider-openai": "0.54.1",
|
|
38
|
+
"@vincemakes/kiso-provider-openai-responses": "0.54.1",
|
|
39
|
+
"@vincemakes/kiso-runtime": "0.54.1",
|
|
40
|
+
"@vincemakes/kiso-skills-ext": "0.54.1",
|
|
41
|
+
"@vincemakes/kiso-subagent-ext": "0.54.1",
|
|
42
|
+
"@vincemakes/kiso-tools-node": "0.54.1",
|
|
43
|
+
"@vincemakes/kiso-tui": "0.54.1",
|
|
44
|
+
"@vincemakes/kiso-tui-cells": "0.54.1"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
47
|
"@types/node": "^26.1.2",
|