@vincemakes/kiso-code 0.40.6 → 0.41.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/mode.d.ts CHANGED
@@ -38,7 +38,7 @@ export declare function getMode(): Mode;
38
38
  export declare function setMode(m: Mode): void;
39
39
  /** The startup mode from env: KISO_MODE, or undefined when unset (the
40
40
  * config layer's mode then applies — merge round B; the --mode flag is
41
- * applied by main before the first makeAgent and wins over everything). */
41
+ * applied by main before the first createCodingAgent and wins over everything). */
42
42
  export declare function modeFromEnv(): Mode | undefined;
43
43
  /** The six built-in mode tiers as chain extensions — named "mode:<tier>"
44
44
  * so the runtime's decidedBy records exactly that (the runtime derives
package/dist/mode.js CHANGED
@@ -56,7 +56,7 @@ export function setMode(m) {
56
56
  }
57
57
  /** The startup mode from env: KISO_MODE, or undefined when unset (the
58
58
  * config layer's mode then applies — merge round B; the --mode flag is
59
- * applied by main before the first makeAgent and wins over everything). */
59
+ * applied by main before the first createCodingAgent and wins over everything). */
60
60
  export function modeFromEnv() {
61
61
  const raw = process.env.KISO_MODE;
62
62
  return MODES.find((x) => x === raw);
@@ -33,7 +33,7 @@
33
33
  import { statSync } from "node:fs";
34
34
  import { homedir } from "node:os";
35
35
  import { basename, isAbsolute, join, relative } from "node:path";
36
- import { isCredentialName } from "@vincemakes/kiso-tools-node";
36
+ import { isCredentialName, isCredentialPath } from "@vincemakes/kiso-tools-node";
37
37
  import { getMode } from "./mode.js";
38
38
  import { HOME_SUBTREES, parseShell, realCase, resolveShellPath } from "./shell-words.js";
39
39
  // ── the predicates ──────────────────────────────────────────────────────
@@ -55,15 +55,11 @@ function scope(ctx, word) {
55
55
  }
56
56
  return null;
57
57
  }
58
- /** B4: credentials by the shell's own list — names the search corpus's
59
- * rule does not cover, on the canonical path. (The corpus rule itself,
60
- * shared with read_file and search_text, is not moved before the freeze:
61
- * finding RO-F4, 0.41.0.) */
62
- const SHELL_CREDENTIAL_NAMES = new Set([".npmrc", ".pypirc", ".git-credentials", ".htpasswd"]);
63
- const SHELL_CREDENTIAL_PATHS = ["/.aws/credentials", "/.config/gh/hosts.yml", "/.docker/config.json", "/.kube/config"];
58
+ /** B4, and since RO-F4 (0.40.7) the search corpus's own set: the shell and
59
+ * the file tools ask ONE rule — names (`isCredentialName`) and the
60
+ * directory-scoped paths (`isCredentialPath`) — on the canonical path. */
64
61
  function shellCredential(canonical) {
65
- const folded = canonical.toLowerCase();
66
- return SHELL_CREDENTIAL_NAMES.has(basename(folded)) || SHELL_CREDENTIAL_PATHS.some((s) => folded.endsWith(s));
62
+ return isCredentialName(basename(canonical).toLowerCase()) || isCredentialPath(canonical);
67
63
  }
68
64
  function content(ctx, word) {
69
65
  const s = scope(ctx, word);
package/dist/state.d.ts CHANGED
@@ -204,7 +204,7 @@ export declare function setBody(value: Body): void;
204
204
  /** v2d: body output routes through the cell renderer — the single writer.
205
205
  * bodyLog adds the trailing newline; internal newlines are preserved. */
206
206
  export declare function bodyLog(text: string, wrap?: "words"): void;
207
- /** TUI2-R2 ②/③: the session store makeAgent built — the ONE store per
207
+ /** TUI2-R2 ②/③: the session store createCodingAgent built — the ONE store per
208
208
  * process. The navigation surfaces need its read side (load) to project
209
209
  * the badges, and a second store on the same root would be a second
210
210
  * lock manager for a job that never writes. */
@@ -214,18 +214,22 @@ export declare let sessionStoreRef: {
214
214
  export declare function setSessionStore(value: {
215
215
  load(id: string): readonly StoreRecord[];
216
216
  }): void;
217
- /** The model name for the status bar — set by makeAgent. */
217
+ /** The folder createCodingAgent built the store on — written there and by
218
+ * the resume picker's folder switch, read by the listings. */
219
+ export declare let activeStoreDir: string;
220
+ export declare function setActiveStoreDir(value: string): void;
221
+ /** The model name for the status bar — set by createCodingAgent. */
218
222
  export declare let agentModel: string;
219
223
  /** OR-1: the live model's ENDPOINT, set in the same call as the model so
220
224
  * the two can never drift: the window lookup keys on (model, endpoint). */
221
225
  export declare let agentBaseUrl: string | undefined;
222
226
  export declare function setAgentModel(value: string, baseUrl?: string): void;
223
227
  /** merge round B: whether the agent runs on the faux provider (no real key) —
224
- * set inside makeAgent, read by main for chat/resume's exhaustion check. */
228
+ * set inside createCodingAgent, read by main for chat/resume's exhaustion check. */
225
229
  export declare let currentFaux: boolean;
226
230
  export declare function setCurrentFaux(value: boolean): void;
227
231
  /** merge round B: the merged config (user + trusted project) as resolved by the
228
- * LAST makeAgent — /model and autoCompact resolve against it. */
232
+ * LAST createCodingAgent — /model and autoCompact resolve against it. */
229
233
  export declare let mergedConfig: import("./config.js").KisoConfig;
230
234
  export declare function setMergedConfig(value: import("./config.js").KisoConfig): void;
231
235
  /** ADR-0005 Amendment 2: the retry the kernel announced and is waiting
@@ -270,7 +274,7 @@ export declare function upstreamOf(baseUrl: string | undefined): string | undefi
270
274
  * first reload — the effort-axis round produced that bug once already
271
275
  * from having two sources for the model.
272
276
  *
273
- * One variable, seeded by `makeAgent` from the startup flag and
277
+ * One variable, seeded by `createCodingAgent` from the startup flag and
274
278
  * overwritten by `/model`. Undefined means "resolve exactly as this
275
279
  * process did at startup", which is the right answer for a session that
276
280
  * never named a profile.
@@ -292,7 +296,7 @@ export declare function setCurrentProfileName(value: string | null): void;
292
296
  * `chat()` drains them into its own replay, so no line is answered by the
293
297
  * session being left and none is dropped. */
294
298
  export declare const queuedSwitchLines: string[];
295
- /** E1: the extensions loaded by makeAgent — their names feed the banner. */
299
+ /** E1: the extensions loaded by createCodingAgent — their names feed the banner. */
296
300
  /** 0.40.0: whether the catastrophe floor is on (floor.ts) — the user
297
301
  * config's `floor`, read where the chain is assembled. */
298
302
  /** 0.40.6: what /settings needs to name each value's layer — the two config
@@ -329,7 +333,7 @@ export declare function setExtensionLists(builtIn: readonly KisoExtension[], use
329
333
  * different set. Null when no skills extension is loaded (or a user
330
334
  * extension named "skills" shadows it and reports none). */
331
335
  export declare function loadedSkillsCatalog(): import("@vincemakes/kiso-skills-ext").SkillsCatalog | null;
332
- /** W21: the CURRENT agent's extensions array — set by makeAgent, the
336
+ /** W21: the CURRENT agent's extensions array — set by createCodingAgent, the
333
337
  * don't-ask-again writer pushes the generated extension into it so a
334
338
  * first-time rule joins the chain at the NEXT run (the run's policies
335
339
  * are fixed at its start; the array is shared by reference with the
package/dist/state.js CHANGED
@@ -290,7 +290,7 @@ export function setBody(value) {
290
290
  export function bodyLog(text, wrap) {
291
291
  body.raw(text.split("\n"), wrap);
292
292
  }
293
- /** TUI2-R2 ②/③: the session store makeAgent built — the ONE store per
293
+ /** TUI2-R2 ②/③: the session store createCodingAgent built — the ONE store per
294
294
  * process. The navigation surfaces need its read side (load) to project
295
295
  * the badges, and a second store on the same root would be a second
296
296
  * lock manager for a job that never writes. */
@@ -298,7 +298,13 @@ export let sessionStoreRef = null;
298
298
  export function setSessionStore(value) {
299
299
  sessionStoreRef = value;
300
300
  }
301
- /** The model name for the status bar — set by makeAgent. */
301
+ /** The folder createCodingAgent built the store on — written there and by
302
+ * the resume picker's folder switch, read by the listings. */
303
+ export let activeStoreDir = "";
304
+ export function setActiveStoreDir(value) {
305
+ activeStoreDir = value;
306
+ }
307
+ /** The model name for the status bar — set by createCodingAgent. */
302
308
  export let agentModel = "faux";
303
309
  /** OR-1: the live model's ENDPOINT, set in the same call as the model so
304
310
  * the two can never drift: the window lookup keys on (model, endpoint). */
@@ -308,13 +314,13 @@ export function setAgentModel(value, baseUrl) {
308
314
  agentBaseUrl = baseUrl;
309
315
  }
310
316
  /** merge round B: whether the agent runs on the faux provider (no real key) —
311
- * set inside makeAgent, read by main for chat/resume's exhaustion check. */
317
+ * set inside createCodingAgent, read by main for chat/resume's exhaustion check. */
312
318
  export let currentFaux = true;
313
319
  export function setCurrentFaux(value) {
314
320
  currentFaux = value;
315
321
  }
316
322
  /** merge round B: the merged config (user + trusted project) as resolved by the
317
- * LAST makeAgent — /model and autoCompact resolve against it. */
323
+ * LAST createCodingAgent — /model and autoCompact resolve against it. */
318
324
  export let mergedConfig = {};
319
325
  export function setMergedConfig(value) {
320
326
  mergedConfig = value;
@@ -366,7 +372,7 @@ export function upstreamOf(baseUrl) {
366
372
  * first reload — the effort-axis round produced that bug once already
367
373
  * from having two sources for the model.
368
374
  *
369
- * One variable, seeded by `makeAgent` from the startup flag and
375
+ * One variable, seeded by `createCodingAgent` from the startup flag and
370
376
  * overwritten by `/model`. Undefined means "resolve exactly as this
371
377
  * process did at startup", which is the right answer for a session that
372
378
  * never named a profile.
@@ -394,7 +400,7 @@ export function setCurrentProfileName(value) {
394
400
  * `chat()` drains them into its own replay, so no line is answered by the
395
401
  * session being left and none is dropped. */
396
402
  export const queuedSwitchLines = [];
397
- /** E1: the extensions loaded by makeAgent — their names feed the banner. */
403
+ /** E1: the extensions loaded by createCodingAgent — their names feed the banner. */
398
404
  /** 0.40.0: whether the catastrophe floor is on (floor.ts) — the user
399
405
  * config's `floor`, read where the chain is assembled. */
400
406
  /** 0.40.6: what /settings needs to name each value's layer — the two config
@@ -437,7 +443,7 @@ export function loadedSkillsCatalog() {
437
443
  const ext = loadedExtensions.find((e) => e.name === "skills");
438
444
  return ext?.catalog ?? null;
439
445
  }
440
- /** W21: the CURRENT agent's extensions array — set by makeAgent, the
446
+ /** W21: the CURRENT agent's extensions array — set by createCodingAgent, the
441
447
  * don't-ask-again writer pushes the generated extension into it so a
442
448
  * first-time rule joins the chain at the NEXT run (the run's policies
443
449
  * are fixed at its start; the array is shared by reference with the
@@ -0,0 +1,12 @@
1
+ export declare const LEGACY_MAX_AGE_MS: number;
2
+ /** One startup never spends long here, whatever the temp directory holds. */
3
+ export declare const SWEEP_LIMIT = 200;
4
+ /** The mkdtemp prefix for a merge directory: this process's pid in the name. */
5
+ export declare function mergeDirPrefix(kind: "mcp" | "skills"): string;
6
+ /** Is a process with this pid running? EPERM means it is, and belongs to
7
+ * someone else; only ESRCH says it is gone. */
8
+ export declare function pidAlive(pid: number): boolean;
9
+ /** Remove the merge directories whose owner is gone (and legacy ones a week
10
+ * old). Returns the names removed. Every failure is skipped — a sweep is
11
+ * housekeeping, never a reason for a session not to start. */
12
+ export declare function sweepStaleMergeDirs(dir?: string, now?: number, alive?: (pid: number) => boolean): string[];
@@ -0,0 +1,76 @@
1
+ /**
2
+ * 0.40.7 — the merge directories a killed kiso left behind.
3
+ *
4
+ * A project with its own `.kiso/mcp.json` or skills gets a merged copy in
5
+ * the temp directory (`kiso-mcp-*`, `kiso-skills-*`, trust-ui.ts). A clean
6
+ * exit removes it; `kill -9` does not, and macOS's temp cleaner removes old
7
+ * FILES but leaves directories — so every killed session left one behind
8
+ * for good (the owner's machine once held 449,956 of the test suite's).
9
+ *
10
+ * The directory's name now carries its owner's pid
11
+ * (`kiso-mcp-<pid>-XXXXXX`). At startup kiso removes the ones whose owner
12
+ * is gone; a live session's directory is never touched. Names from before
13
+ * this version carry no pid: those are removed only when untouched for a
14
+ * week, which no live session's merge can be (it is read at every reload).
15
+ * Only directories this user owns are considered; nothing else in the
16
+ * temp directory is read.
17
+ */
18
+ import { lstatSync, readdirSync, rmSync } from "node:fs";
19
+ import { tmpdir } from "node:os";
20
+ import { join } from "node:path";
21
+ export const LEGACY_MAX_AGE_MS = 7 * 24 * 3600_000;
22
+ /** One startup never spends long here, whatever the temp directory holds. */
23
+ export const SWEEP_LIMIT = 200;
24
+ const OWNED = /^kiso-(?:mcp|skills)-(\d+)-[A-Za-z0-9]{6}$/;
25
+ const LEGACY = /^kiso-(?:mcp|skills)-[A-Za-z0-9]{6}$/;
26
+ /** The mkdtemp prefix for a merge directory: this process's pid in the name. */
27
+ export function mergeDirPrefix(kind) {
28
+ return join(tmpdir(), `kiso-${kind}-${process.pid}-`);
29
+ }
30
+ /** Is a process with this pid running? EPERM means it is, and belongs to
31
+ * someone else; only ESRCH says it is gone. */
32
+ export function pidAlive(pid) {
33
+ try {
34
+ process.kill(pid, 0);
35
+ return true;
36
+ }
37
+ catch (err) {
38
+ return err.code === "EPERM";
39
+ }
40
+ }
41
+ /** Remove the merge directories whose owner is gone (and legacy ones a week
42
+ * old). Returns the names removed. Every failure is skipped — a sweep is
43
+ * housekeeping, never a reason for a session not to start. */
44
+ export function sweepStaleMergeDirs(dir = tmpdir(), now = Date.now(), alive = pidAlive) {
45
+ let names;
46
+ try {
47
+ names = readdirSync(dir);
48
+ }
49
+ catch {
50
+ return [];
51
+ }
52
+ const uid = typeof process.getuid === "function" ? process.getuid() : undefined;
53
+ const removed = [];
54
+ for (const name of names) {
55
+ if (removed.length >= SWEEP_LIMIT)
56
+ break;
57
+ const owned = OWNED.exec(name);
58
+ if (owned === null && !LEGACY.test(name))
59
+ continue;
60
+ const full = join(dir, name);
61
+ try {
62
+ const st = lstatSync(full);
63
+ if (!st.isDirectory() || (uid !== undefined && st.uid !== uid))
64
+ continue;
65
+ const stale = owned !== null ? Number(owned[1]) !== process.pid && !alive(Number(owned[1])) : now - st.mtimeMs > LEGACY_MAX_AGE_MS;
66
+ if (!stale)
67
+ continue;
68
+ rmSync(full, { recursive: true, force: true });
69
+ removed.push(name);
70
+ }
71
+ catch {
72
+ // gone already, or not ours to remove
73
+ }
74
+ }
75
+ return removed;
76
+ }
package/dist/trust-ui.js CHANGED
@@ -6,13 +6,14 @@
6
6
  * decisions. All bodies moved verbatim from index.ts.
7
7
  */
8
8
  import { existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, symlinkSync, writeFileSync } from "node:fs";
9
- import { homedir, tmpdir } from "node:os";
9
+ import { homedir } from "node:os";
10
10
  import { join } from "node:path";
11
11
  import { pathToFileURL } from "node:url";
12
12
  import { askDeclineAll, askView, projectTrustRows, projectTrustView, projectUntrustedNote, uncertainView, unansweredAskView } from "@vincemakes/kiso-tui";
13
13
  import { projectArtifacts, recordTrust, trustFor } from "@vincemakes/kiso-runtime";
14
14
  import { bodyLog, currentAgentExtensions, dock, extensionsDir, kisoHome, mergedTempPaths, neverInherited } from "./state.js";
15
15
  import { guardSavedAllow } from "./protected-writes.js";
16
+ import { mergeDirPrefix } from "./temp-sweep.js";
16
17
  import { loadUserConfig, resolveProjectTrustPolicy } from "./config.js";
17
18
  import { getMode } from "./mode.js";
18
19
  /**
@@ -415,7 +416,7 @@ function applyMcpMerge(root) {
415
416
  // the paths the previous set owned deleted the file the new set had just
416
417
  // been merged into. One directory per merge, and the directory is what is
417
418
  // recorded, so the cleanup removes exactly what that merge created.
418
- const dir = mkdtempSync(join(tmpdir(), "kiso-mcp-"));
419
+ const dir = mkdtempSync(mergeDirPrefix("mcp")); // 0.40.7: the owner's pid in the name (temp-sweep.ts)
419
420
  const temp = join(dir, "mcp.json");
420
421
  writeFileSync(temp, `${JSON.stringify(merged, null, 2)}\n`, "utf8");
421
422
  process.env.KISO_MCP_CONFIG = temp;
@@ -442,7 +443,7 @@ function applySkillsMerge(root) {
442
443
  originalUserSkillsDir ??= process.env.KISO_SKILLS_DIR ?? join(kisoHome(), "skills");
443
444
  const userDir = originalUserSkillsDir;
444
445
  const projectDir = join(root, "skills");
445
- const merged = mkdtempSync(join(tmpdir(), "kiso-skills-"));
446
+ const merged = mkdtempSync(mergeDirPrefix("skills"));
446
447
  mergedTempPaths.push(merged);
447
448
  for (const dir of readdirSyncSafe(projectDir)) {
448
449
  symlinkSync(join(projectDir, dir), join(merged, dir)); // project wins on collision
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vincemakes/kiso-code",
3
- "version": "0.40.6",
3
+ "version": "0.41.0",
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",
@@ -18,20 +18,20 @@
18
18
  "test": "vitest run"
19
19
  },
20
20
  "dependencies": {
21
- "@vincemakes/kiso-ask-ext": "0.40.6",
22
- "@vincemakes/kiso-core": "0.40.6",
23
- "@vincemakes/kiso-evals": "0.40.6",
24
- "@vincemakes/kiso-mcp-ext": "0.40.6",
25
- "@vincemakes/kiso-provider-anthropic": "0.40.6",
26
- "@vincemakes/kiso-provider-openai": "0.40.6",
27
- "@vincemakes/kiso-provider-openai-responses": "0.40.6",
28
- "@vincemakes/kiso-runtime": "0.40.6",
29
- "@vincemakes/kiso-skills-ext": "0.40.6",
30
- "@vincemakes/kiso-subagent-ext": "0.40.6",
31
- "@vincemakes/kiso-task-ext": "0.40.6",
32
- "@vincemakes/kiso-tools-node": "0.40.6",
33
- "@vincemakes/kiso-tui": "0.40.6",
34
- "@vincemakes/kiso-tui-cells": "0.40.6"
21
+ "@vincemakes/kiso-ask-ext": "0.41.0",
22
+ "@vincemakes/kiso-core": "0.41.0",
23
+ "@vincemakes/kiso-evals": "0.41.0",
24
+ "@vincemakes/kiso-mcp-ext": "0.41.0",
25
+ "@vincemakes/kiso-provider-anthropic": "0.41.0",
26
+ "@vincemakes/kiso-provider-openai": "0.41.0",
27
+ "@vincemakes/kiso-provider-openai-responses": "0.41.0",
28
+ "@vincemakes/kiso-runtime": "0.41.0",
29
+ "@vincemakes/kiso-skills-ext": "0.41.0",
30
+ "@vincemakes/kiso-subagent-ext": "0.41.0",
31
+ "@vincemakes/kiso-task-ext": "0.41.0",
32
+ "@vincemakes/kiso-tools-node": "0.41.0",
33
+ "@vincemakes/kiso-tui": "0.41.0",
34
+ "@vincemakes/kiso-tui-cells": "0.41.0"
35
35
  },
36
36
  "devDependencies": {
37
37
  "@types/node": "^26.1.2",