@corenel/term 0.1.0 → 0.2.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.
Files changed (55) hide show
  1. package/dist/chunk-2D7G4YW2.js +97 -0
  2. package/dist/chunk-CB5RFNB4.js +33 -0
  3. package/dist/chunk-F4ENQXQQ.js +909 -0
  4. package/dist/chunk-I44FC7PE.js +142 -0
  5. package/dist/chunk-IK4ZANNW.js +95 -0
  6. package/dist/chunk-M44CET5L.js +40 -0
  7. package/dist/chunk-OSJFMQCW.js +44 -0
  8. package/dist/{chunk-R6AXTD55.js → chunk-VTJEWHM3.js} +19 -1
  9. package/dist/commands.d.ts +14 -0
  10. package/dist/commands.d.ts.map +1 -1
  11. package/dist/commands.js +1 -1
  12. package/dist/describe.d.ts +23 -0
  13. package/dist/describe.d.ts.map +1 -1
  14. package/dist/describe.js +6 -1
  15. package/dist/diff.d.ts +100 -0
  16. package/dist/diff.d.ts.map +1 -0
  17. package/dist/diff.js +18 -0
  18. package/dist/ink/App.d.ts +12 -0
  19. package/dist/ink/App.d.ts.map +1 -1
  20. package/dist/ink/Modal.d.ts.map +1 -1
  21. package/dist/ink/TodoBlock.d.ts +10 -0
  22. package/dist/ink/TodoBlock.d.ts.map +1 -0
  23. package/dist/ink/banner.d.ts +36 -0
  24. package/dist/ink/banner.d.ts.map +1 -0
  25. package/dist/ink/index.d.ts.map +1 -1
  26. package/dist/ink/index.js +300 -140
  27. package/dist/ink/items.d.ts.map +1 -1
  28. package/dist/ink/scroll.d.ts +14 -2
  29. package/dist/ink/scroll.d.ts.map +1 -1
  30. package/dist/ink/status.d.ts.map +1 -1
  31. package/dist/ink/theme.d.ts +9 -0
  32. package/dist/ink/theme.d.ts.map +1 -1
  33. package/dist/ink/todos.d.ts +20 -0
  34. package/dist/ink/todos.d.ts.map +1 -0
  35. package/dist/markdown.d.ts.map +1 -1
  36. package/dist/persist.d.ts +27 -4
  37. package/dist/persist.d.ts.map +1 -1
  38. package/dist/persist.js +3 -1
  39. package/dist/policySource.d.ts +26 -0
  40. package/dist/policySource.d.ts.map +1 -0
  41. package/dist/policySource.js +8 -0
  42. package/dist/session.d.ts +199 -13
  43. package/dist/session.d.ts.map +1 -1
  44. package/dist/session.js +10 -5
  45. package/dist/state.d.ts +84 -0
  46. package/dist/state.d.ts.map +1 -1
  47. package/dist/state.js +5 -3
  48. package/dist/writePreview.d.ts +58 -0
  49. package/dist/writePreview.d.ts.map +1 -0
  50. package/dist/writePreview.js +9 -0
  51. package/package.json +19 -6
  52. package/dist/chunk-47IH6X5H.js +0 -20
  53. package/dist/chunk-HAQGHLW6.js +0 -235
  54. package/dist/chunk-HYWLODZT.js +0 -1230
  55. package/dist/chunk-TCVDVYMF.js +0 -40
@@ -0,0 +1,142 @@
1
+ // src/diff.ts
2
+ function splitLines(s) {
3
+ if (s === "") return [];
4
+ const parts = s.split("\n").map((l) => l.endsWith("\r") ? l.slice(0, -1) : l);
5
+ if (parts[parts.length - 1] === "") parts.pop();
6
+ return parts;
7
+ }
8
+ function lineDiff(a, b) {
9
+ const oldLines = splitLines(a);
10
+ const newLines = splitLines(b);
11
+ const n = oldLines.length;
12
+ const m = newLines.length;
13
+ const lcs = Array.from({ length: n + 1 }, () => new Array(m + 1).fill(0));
14
+ for (let i2 = n - 1; i2 >= 0; i2--) {
15
+ for (let j2 = m - 1; j2 >= 0; j2--) {
16
+ lcs[i2][j2] = oldLines[i2] === newLines[j2] ? lcs[i2 + 1][j2 + 1] + 1 : Math.max(lcs[i2 + 1][j2], lcs[i2][j2 + 1]);
17
+ }
18
+ }
19
+ const out = [];
20
+ let i = 0;
21
+ let j = 0;
22
+ while (i < n && j < m) {
23
+ if (oldLines[i] === newLines[j]) {
24
+ out.push({ kind: "ctx", text: oldLines[i] });
25
+ i++;
26
+ j++;
27
+ } else if (lcs[i + 1][j] >= lcs[i][j + 1]) {
28
+ out.push({ kind: "del", text: oldLines[i] });
29
+ i++;
30
+ } else {
31
+ out.push({ kind: "add", text: newLines[j] });
32
+ j++;
33
+ }
34
+ }
35
+ while (i < n) {
36
+ out.push({ kind: "del", text: oldLines[i++] });
37
+ }
38
+ while (j < m) {
39
+ out.push({ kind: "add", text: newLines[j++] });
40
+ }
41
+ return out;
42
+ }
43
+ function lineEndingInfo(text) {
44
+ let crlf = 0;
45
+ let lf = 0;
46
+ for (let i = 0; i < text.length; i++) {
47
+ if (text[i] === "\n") {
48
+ if (i > 0 && text[i - 1] === "\r") crlf++;
49
+ else lf++;
50
+ }
51
+ }
52
+ const kind = crlf === 0 && lf === 0 ? "none" : lf === 0 ? "CRLF" : crlf === 0 ? "LF" : "mixed";
53
+ return { kind, crlf, lf };
54
+ }
55
+ function detectLineEnding(text) {
56
+ return lineEndingInfo(text).kind;
57
+ }
58
+ function lineEndingChange(before, after) {
59
+ const from = lineEndingInfo(before);
60
+ const to = lineEndingInfo(after);
61
+ if (from.kind === "none" || to.kind === "none") return null;
62
+ if (from.kind === to.kind && from.kind !== "mixed") return null;
63
+ if (from.kind === "mixed" && to.kind === "mixed" && from.crlf === to.crlf && from.lf === to.lf) return null;
64
+ return { from, to };
65
+ }
66
+ function formatEndingChange(change, contentUnchanged) {
67
+ const label = (info) => info.kind === "mixed" ? `mixed (${info.crlf} CRLF, ${info.lf} LF)` : info.kind;
68
+ const verb = change.from.kind === "mixed" && change.to.kind !== "mixed" ? "normalized" : "change";
69
+ const base = `line endings ${verb}: ${label(change.from)} -> ${label(change.to)} (whole file)`;
70
+ return contentUnchanged ? `${base} -- content otherwise unchanged` : base;
71
+ }
72
+ function diffStat(lines) {
73
+ let added = 0;
74
+ let removed = 0;
75
+ for (const l of lines) {
76
+ if (l.kind === "add") added++;
77
+ else if (l.kind === "del") removed++;
78
+ }
79
+ return { added, removed };
80
+ }
81
+ var DIFF_CONTEXT_LINES = 3;
82
+ function contextWindows(changeIdx, total, radius) {
83
+ const windows = [];
84
+ for (const i of changeIdx) {
85
+ const start = Math.max(0, i - radius);
86
+ const end = Math.min(total - 1, i + radius);
87
+ const last = windows[windows.length - 1];
88
+ if (last && start <= last[1] + 1) last[1] = Math.max(last[1], end);
89
+ else windows.push([start, end]);
90
+ }
91
+ return windows;
92
+ }
93
+ function renderAtRadius(lines, changeIdx, radius) {
94
+ const windows = contextWindows(changeIdx, lines.length, radius);
95
+ const rows = [];
96
+ let cursor = 0;
97
+ for (const [start, end] of windows) {
98
+ if (start > cursor) rows.push({ kind: "skip", count: start - cursor, reason: "context" });
99
+ for (let i = start; i <= end; i++) rows.push(lines[i]);
100
+ cursor = end + 1;
101
+ }
102
+ if (cursor < lines.length) rows.push({ kind: "skip", count: lines.length - cursor, reason: "context" });
103
+ return rows;
104
+ }
105
+ function capDiffLines(lines, max, contextLines = DIFF_CONTEXT_LINES) {
106
+ if (max <= 0) return [];
107
+ const changeIdx = [];
108
+ lines.forEach((l, i) => {
109
+ if (l.kind !== "ctx") changeIdx.push(i);
110
+ });
111
+ if (changeIdx.length > max) {
112
+ const keptIdx2 = changeIdx.slice(0, max - 1);
113
+ const rows2 = keptIdx2.map((i) => lines[i]);
114
+ const hidden2 = changeIdx.length - keptIdx2.length;
115
+ if (hidden2 > 0) rows2.push({ kind: "skip", count: hidden2, reason: "changes" });
116
+ return rows2;
117
+ }
118
+ if (changeIdx.length === 0) {
119
+ if (lines.length <= max) return lines;
120
+ const kept = lines.slice(0, max - 1);
121
+ return [...kept, { kind: "skip", count: lines.length - kept.length, reason: "context" }];
122
+ }
123
+ for (let radius = contextLines; radius >= 0; radius--) {
124
+ const rows2 = renderAtRadius(lines, changeIdx, radius);
125
+ if (rows2.length <= max) return rows2;
126
+ }
127
+ const keptIdx = changeIdx.slice(0, max - 1);
128
+ const rows = keptIdx.map((i) => lines[i]);
129
+ const hidden = lines.length - keptIdx.length;
130
+ if (hidden > 0) rows.push({ kind: "skip", count: hidden, reason: keptIdx.length < changeIdx.length ? "changes" : "context" });
131
+ return rows;
132
+ }
133
+
134
+ export {
135
+ lineDiff,
136
+ detectLineEnding,
137
+ lineEndingChange,
138
+ formatEndingChange,
139
+ diffStat,
140
+ DIFF_CONTEXT_LINES,
141
+ capDiffLines
142
+ };
@@ -0,0 +1,95 @@
1
+ import {
2
+ capPreview,
3
+ describeToolCall
4
+ } from "./chunk-2D7G4YW2.js";
5
+
6
+ // src/persist.ts
7
+ import { sessionsRoot } from "@corenel/protocol";
8
+ import { JsonlSessionStore } from "@corenel/harness/sessions/fileSessionStore";
9
+ import { parseOffloadPointer } from "@corenel/harness/compression";
10
+ function createSessionPersistence(files) {
11
+ const store = new JsonlSessionStore(() => files, () => sessionsRoot());
12
+ return {
13
+ async save(input) {
14
+ const session = input.id ? await store.get(input.id) : await store.create(input.title, "agent");
15
+ if (!session) {
16
+ const fresh = await store.create(input.title, "agent");
17
+ fresh.messages = input.messages;
18
+ await store.save(fresh);
19
+ return { id: fresh.id };
20
+ }
21
+ session.messages = input.messages;
22
+ await store.save(session);
23
+ return { id: session.id };
24
+ },
25
+ async load(id) {
26
+ if (id) {
27
+ const s = await store.get(id);
28
+ return s ? { id: s.id, title: s.title, messages: s.messages } : null;
29
+ }
30
+ const all = await store.list("agent");
31
+ const newest = [...all].sort((a, b) => b.updatedAt - a.updatedAt)[0];
32
+ return newest ? { id: newest.id, title: newest.title, messages: newest.messages } : null;
33
+ },
34
+ list: () => store.list("agent")
35
+ };
36
+ }
37
+ function parseToolArgs(raw) {
38
+ try {
39
+ return JSON.parse(raw);
40
+ } catch {
41
+ return void 0;
42
+ }
43
+ }
44
+ function committedFromMessages(messages) {
45
+ const items = [];
46
+ let n = 0;
47
+ const pending = /* @__PURE__ */ new Map();
48
+ for (const m of messages) {
49
+ if (m.role === "user" && typeof m.content === "string") {
50
+ items.push({ kind: "user", id: `h${n++}`, text: m.content });
51
+ continue;
52
+ }
53
+ if (m.role === "assistant") {
54
+ if (typeof m.content === "string" && m.content.trim()) {
55
+ items.push({ kind: "assistant", id: `h${n++}`, text: m.content, leadingBlank: true });
56
+ }
57
+ for (const call of m.tool_calls ?? []) {
58
+ if (call.type !== "function") continue;
59
+ pending.set(call.id, { name: call.function.name, args: parseToolArgs(call.function.arguments) });
60
+ }
61
+ continue;
62
+ }
63
+ if (m.role === "tool") {
64
+ const call = pending.get(m.tool_call_id);
65
+ pending.delete(m.tool_call_id);
66
+ if (!call) continue;
67
+ const content = typeof m.content === "string" ? m.content : "";
68
+ const pointer = parseOffloadPointer(content);
69
+ items.push({
70
+ kind: "tool",
71
+ id: `h${n++}`,
72
+ callId: m.tool_call_id,
73
+ name: call.name,
74
+ subject: describeToolCall(call.name, call.args),
75
+ preview: capPreview(content, false),
76
+ isError: false,
77
+ restored: true,
78
+ ...pointer ? { offloadedBytes: pointer.bytes, offloadRefId: pointer.id } : {}
79
+ });
80
+ }
81
+ }
82
+ return items;
83
+ }
84
+ function titleFrom(messages) {
85
+ const first = messages.find((m) => m.role === "user");
86
+ const text = typeof first?.content === "string" ? first.content : "";
87
+ const line = text.split("\n")[0].trim();
88
+ return line.length > 60 ? `${line.slice(0, 59)}\u2026` : line || "conversation";
89
+ }
90
+
91
+ export {
92
+ createSessionPersistence,
93
+ committedFromMessages,
94
+ titleFrom
95
+ };
@@ -0,0 +1,40 @@
1
+ import {
2
+ capDiffLines,
3
+ diffStat,
4
+ lineDiff,
5
+ lineEndingChange
6
+ } from "./chunk-I44FC7PE.js";
7
+
8
+ // src/writePreview.ts
9
+ var WRITE_TOOL_NAMES = /* @__PURE__ */ new Set(["write_file", "create_file"]);
10
+ var DEFAULT_MAX_LINES = 40;
11
+ async function buildWritePreview(tool, input, readExisting, maxLines = DEFAULT_MAX_LINES) {
12
+ if (!WRITE_TOOL_NAMES.has(tool)) return null;
13
+ const rec = input && typeof input === "object" ? input : null;
14
+ const path = typeof rec?.path === "string" ? rec.path : "";
15
+ if (!path) return null;
16
+ const content = typeof rec?.content === "string" ? rec.content : "";
17
+ let existing;
18
+ try {
19
+ existing = await readExisting(path);
20
+ } catch {
21
+ return null;
22
+ }
23
+ const isNewFile = existing === null;
24
+ const before = existing ?? "";
25
+ const noop = !isNewFile && before === content;
26
+ const endingChange = lineEndingChange(before, content);
27
+ if (noop) {
28
+ return { path, isNewFile, noop, endingChange, unexplainedChange: false, rows: [], totalLines: 0, added: 0, removed: 0 };
29
+ }
30
+ const full = lineDiff(before, content);
31
+ const { added, removed } = diffStat(full);
32
+ const rows = capDiffLines(full, maxLines);
33
+ const unexplainedChange = !isNewFile && before !== content && added === 0 && removed === 0 && endingChange === null;
34
+ return { path, isNewFile, noop, endingChange, unexplainedChange, rows, totalLines: full.length, added, removed };
35
+ }
36
+
37
+ export {
38
+ WRITE_TOOL_NAMES,
39
+ buildWritePreview
40
+ };
@@ -0,0 +1,44 @@
1
+ // src/state.ts
2
+ function emptyState() {
3
+ return {
4
+ seq: 0,
5
+ transcriptGen: 0,
6
+ committed: [],
7
+ live: null,
8
+ queued: [],
9
+ modal: null,
10
+ usage: { promptTokens: 0, completionTokens: 0 },
11
+ turnUsage: { promptTokens: 0, completionTokens: 0 },
12
+ session: null,
13
+ status: "idle",
14
+ error: null,
15
+ todos: [],
16
+ pendingSkill: null
17
+ };
18
+ }
19
+ var TODO_STATUSES = ["pending", "in_progress", "completed", "blocked"];
20
+ function isTodoStatus(v) {
21
+ return typeof v === "string" && TODO_STATUSES.includes(v);
22
+ }
23
+ function parseTodos(args) {
24
+ if (typeof args !== "object" || args === null) return [];
25
+ const raw = args.todos;
26
+ if (!Array.isArray(raw)) return [];
27
+ const out = [];
28
+ for (const entry of raw) {
29
+ if (typeof entry !== "object" || entry === null) continue;
30
+ const e = entry;
31
+ const id = typeof e.id === "string" ? e.id.trim() : "";
32
+ const title = typeof e.title === "string" ? e.title.trim() : "";
33
+ if (!id || !title) continue;
34
+ const status = isTodoStatus(e.status) ? e.status : "pending";
35
+ const note = typeof e.note === "string" && e.note.trim() ? e.note.trim() : void 0;
36
+ out.push({ id, title, status, ...note ? { note } : {} });
37
+ }
38
+ return out;
39
+ }
40
+
41
+ export {
42
+ emptyState,
43
+ parseTodos
44
+ };
@@ -2,10 +2,18 @@
2
2
  var TERM_COMMANDS = [
3
3
  { name: "help", summary: "what you can type here" },
4
4
  { name: "tools", summary: "what the agent can do, and what it can change" },
5
+ { name: "policy", summary: "how this session is governed -- levels, caps, and what actually confines it" },
5
6
  { name: "expand", args: "<#n>", summary: "re-print a tool result in full" },
6
7
  { name: "resume", args: "[id]", summary: "reopen an earlier conversation" },
7
8
  { name: "clear", summary: "forget the conversation" },
8
- { name: "exit", summary: "leave" }
9
+ { name: "exit", summary: "leave" },
10
+ // -- config-workspace commands: what these do to the NEXT turn is real, see
11
+ // TermSessionDeps.system in ./session (the re-derivable system prompt) and
12
+ // the `mode`/`persona`/`skill`/`strategy` cases in runCommand.
13
+ { name: "mode", args: "<id>", summary: "switch the agent mode (takes effect next turn); no id lists what is configured" },
14
+ { name: "persona", args: "<id>", summary: "switch persona (takes effect next turn); no id lists what is configured" },
15
+ { name: "skill", args: "<name>", summary: "apply a skill to your NEXT message only, then it clears; no name lists what is installed" },
16
+ { name: "strategy", args: "<name>", summary: "run a saved strategy from .prompd/strategies; no name lists what is saved" }
9
17
  ];
10
18
  var ALIASES = { quit: "exit" };
11
19
  function parseCommand(line) {
@@ -19,6 +27,8 @@ function parseCommand(line) {
19
27
  return { cmd: "help" };
20
28
  case "tools":
21
29
  return { cmd: "tools" };
30
+ case "policy":
31
+ return { cmd: "policy" };
22
32
  case "clear":
23
33
  return { cmd: "clear" };
24
34
  case "exit":
@@ -29,6 +39,14 @@ function parseCommand(line) {
29
39
  }
30
40
  case "resume":
31
41
  return arg ? { cmd: "resume", id: arg } : { cmd: "resume" };
42
+ case "mode":
43
+ return arg ? { cmd: "mode", id: arg } : { cmd: "mode" };
44
+ case "persona":
45
+ return arg ? { cmd: "persona", id: arg } : { cmd: "persona" };
46
+ case "skill":
47
+ return arg ? { cmd: "skill", id: arg } : { cmd: "skill" };
48
+ case "strategy":
49
+ return arg ? { cmd: "strategy", id: arg } : { cmd: "strategy" };
32
50
  default:
33
51
  return null;
34
52
  }
@@ -8,6 +8,8 @@ export type ParsedCommand = {
8
8
  cmd: 'help';
9
9
  } | {
10
10
  cmd: 'tools';
11
+ } | {
12
+ cmd: 'policy';
11
13
  } | {
12
14
  cmd: 'expand';
13
15
  ord?: number;
@@ -18,6 +20,18 @@ export type ParsedCommand = {
18
20
  cmd: 'clear';
19
21
  } | {
20
22
  cmd: 'exit';
23
+ } | {
24
+ cmd: 'mode';
25
+ id?: string;
26
+ } | {
27
+ cmd: 'persona';
28
+ id?: string;
29
+ } | {
30
+ cmd: 'skill';
31
+ id?: string;
32
+ } | {
33
+ cmd: 'strategy';
34
+ id?: string;
21
35
  };
22
36
  export declare function parseCommand(line: string): ParsedCommand | null;
23
37
  /** Commands whose name starts with what has been typed after the slash. */
@@ -1 +1 @@
1
- {"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../src/commands.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,eAAO,MAAM,aAAa,EAAE,WAAW,EAOtC,CAAC;AAEF,MAAM,MAAM,aAAa,GACrB;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,GACf;IAAE,GAAG,EAAE,OAAO,CAAA;CAAE,GAChB;IAAE,GAAG,EAAE,QAAQ,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GAC/B;IAAE,GAAG,EAAE,QAAQ,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,GAC9B;IAAE,GAAG,EAAE,OAAO,CAAA;CAAE,GAChB;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC;AAQpB,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CAkB/D;AAED,2EAA2E;AAC3E,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,WAAW,EAAE,CAK3D"}
1
+ {"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../src/commands.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,eAAO,MAAM,aAAa,EAAE,WAAW,EAetC,CAAC;AAEF,MAAM,MAAM,aAAa,GACrB;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,GACf;IAAE,GAAG,EAAE,OAAO,CAAA;CAAE,GAChB;IAAE,GAAG,EAAE,QAAQ,CAAA;CAAE,GACjB;IAAE,GAAG,EAAE,QAAQ,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GAC/B;IAAE,GAAG,EAAE,QAAQ,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,GAC9B;IAAE,GAAG,EAAE,OAAO,CAAA;CAAE,GAChB;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,GACf;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,GAC5B;IAAE,GAAG,EAAE,SAAS,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,GAC/B;IAAE,GAAG,EAAE,OAAO,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,GAC7B;IAAE,GAAG,EAAE,UAAU,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAQrC,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CAuB/D;AAED,2EAA2E;AAC3E,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,WAAW,EAAE,CAK3D"}
package/dist/commands.js CHANGED
@@ -2,7 +2,7 @@ import {
2
2
  TERM_COMMANDS,
3
3
  filterCommands,
4
4
  parseCommand
5
- } from "./chunk-R6AXTD55.js";
5
+ } from "./chunk-VTJEWHM3.js";
6
6
  export {
7
7
  TERM_COMMANDS,
8
8
  filterCommands,
@@ -1,7 +1,14 @@
1
+ import { type NamespacedTool } from '@corenel/harness/tools/namespaces';
2
+ import type { Policy, PermissionLevel } from '@corenel/protocol';
3
+ import { type PolicySource } from './policySource';
1
4
  /** Preview lines kept from a tool result. Failure gets more, because the output
2
5
  * you actually need is the output of the thing that broke. */
3
6
  export declare const PREVIEW_LINES = 6;
4
7
  export declare const PREVIEW_LINES_ERROR = 12;
8
+ /** The `#n` an id prints as, padded so `#1` and `#12` line up in the same
9
+ * column across every row -- this is the one place that padding is decided,
10
+ * so the two faces (Ink, readline) can never disagree on it. */
11
+ export declare function formatOrd(ord: number): string;
5
12
  /** A tool call in one line: the argument that says WHAT it is acting on. A path
6
13
  * or a command is the difference between "it did something" and "it read my ssh
7
14
  * config". */
@@ -22,4 +29,20 @@ export interface ToolFact {
22
29
  * their project directory can run arbitrary commands.
23
30
  */
24
31
  export declare function formatTools(tools: ToolFact[]): string;
32
+ /** A tool as `/policy` needs it -- enough to resolve its real permission
33
+ * level and group it by namespace. `AgentTool` already satisfies this. */
34
+ export interface PolicyFormatTool extends NamespacedTool {
35
+ mutates?: boolean;
36
+ permission?: PermissionLevel;
37
+ }
38
+ /**
39
+ * `/policy` — how this session is actually governed, in plain text (no
40
+ * emoji — project rule).
41
+ *
42
+ * Every level shown here comes from the SAME `PermissionService.resolveLevel`
43
+ * call the loop makes before every real tool check — never a re-implemented
44
+ * precedence — so this can't tell the user something the gate would then
45
+ * contradict.
46
+ */
47
+ export declare function formatPolicy(policy: Policy, source: PolicySource, tools: readonly PolicyFormatTool[]): string;
25
48
  //# sourceMappingURL=describe.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"describe.d.ts","sourceRoot":"","sources":["../src/describe.ts"],"names":[],"mappings":"AAGA;8DAC8D;AAC9D,eAAO,MAAM,aAAa,IAAI,CAAC;AAC/B,eAAO,MAAM,mBAAmB,KAAK,CAAC;AAItC;;cAEc;AACd,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,MAAM,CAQrE;AAED,iFAAiF;AACjF,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,MAAM,EAAE,CASrE;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,QAAQ,EAAE,GAAG,MAAM,CASrD"}
1
+ {"version":3,"file":"describe.d.ts","sourceRoot":"","sources":["../src/describe.ts"],"names":[],"mappings":"AAGA,OAAO,EAAmD,KAAK,cAAc,EAAE,MAAM,mCAAmC,CAAC;AACzH,OAAO,KAAK,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACjE,OAAO,EAAqB,KAAK,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAEtE;8DAC8D;AAC9D,eAAO,MAAM,aAAa,IAAI,CAAC;AAC/B,eAAO,MAAM,mBAAmB,KAAK,CAAC;AAStC;;gEAEgE;AAChE,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE7C;AAED;;cAEc;AACd,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,MAAM,CAQrE;AAED,iFAAiF;AACjF,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,MAAM,EAAE,CASrE;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,QAAQ,EAAE,GAAG,MAAM,CASrD;AAED;0EAC0E;AAC1E,MAAM,WAAW,gBAAiB,SAAQ,cAAc;IACtD,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,UAAU,CAAC,EAAE,eAAe,CAAC;CAC9B;AAID;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,SAAS,gBAAgB,EAAE,GAAG,MAAM,CA2C7G"}
package/dist/describe.js CHANGED
@@ -3,12 +3,17 @@ import {
3
3
  PREVIEW_LINES_ERROR,
4
4
  capPreview,
5
5
  describeToolCall,
6
+ formatOrd,
7
+ formatPolicy,
6
8
  formatTools
7
- } from "./chunk-TCVDVYMF.js";
9
+ } from "./chunk-2D7G4YW2.js";
10
+ import "./chunk-CB5RFNB4.js";
8
11
  export {
9
12
  PREVIEW_LINES,
10
13
  PREVIEW_LINES_ERROR,
11
14
  capPreview,
12
15
  describeToolCall,
16
+ formatOrd,
17
+ formatPolicy,
13
18
  formatTools
14
19
  };
package/dist/diff.d.ts ADDED
@@ -0,0 +1,100 @@
1
+ export interface DiffLine {
2
+ kind: 'ctx' | 'add' | 'del';
3
+ text: string;
4
+ }
5
+ export declare function lineDiff(a: string, b: string): DiffLine[];
6
+ /** A file's dominant line ending, or a special value when there's nothing to
7
+ * call dominant. 'none' means no line break was found at all (a single line
8
+ * with no terminator -- there's nothing to compare). 'mixed' means the text
9
+ * contains BOTH bare `\n` and `\r\n` breaks. Unlike 'none', 'mixed' is NOT
10
+ * ambiguous -- it has a factual, countable answer (see `LineEndingInfo`) --
11
+ * so it is still reportable, just not as a single before/after label. */
12
+ export type LineEnding = 'CRLF' | 'LF' | 'mixed' | 'none';
13
+ /** A line ending classification plus the raw counts behind it. The counts are
14
+ * what make 'mixed' reportable rather than a dead end: "mixed (12 CRLF, 1
15
+ * LF)" tells the reader something a bare "mixed" label doesn't, and lets them
16
+ * judge the situation themselves rather than trusting a guessed majority. */
17
+ export interface LineEndingInfo {
18
+ kind: LineEnding;
19
+ crlf: number;
20
+ lf: number;
21
+ }
22
+ export declare function detectLineEnding(text: string): LineEnding;
23
+ export interface LineEndingChange {
24
+ from: LineEndingInfo;
25
+ to: LineEndingInfo;
26
+ }
27
+ /**
28
+ * Whether the WRITE ITSELF changes line endings, as a fact distinct from the
29
+ * content diff -- `lineDiff` is deliberately ending-insensitive (see
30
+ * `splitLines`), so a CRLF file rewritten with byte-identical text but LF
31
+ * endings renders as an empty, all-context diff even though every line
32
+ * ending in the file just flipped and `git status` will show the whole file
33
+ * modified. Silent here would be exactly the failure this feature exists to
34
+ * prevent: what the user approved would not match what happened.
35
+ *
36
+ * Returns `null` -- no reportable change -- when:
37
+ * - either side is 'none' (nothing to compare, e.g. a brand-new file, or a
38
+ * single line with no terminator at all) -- there is no ending on that
39
+ * side at all, so there is nothing to report a change to/from, and
40
+ * - both sides are the SAME single consistent kind ('CRLF'->'CRLF' or
41
+ * 'LF'->'LF') -- the ordinary case, which must not fire on every routine
42
+ * write or the row becomes noise people learn to ignore, and
43
+ * - both sides are 'mixed' with IDENTICAL crlf/lf counts -- the aggregate
44
+ * ending mix didn't change (this is also what keeps a genuine byte-for-
45
+ * byte no-op silent: identical text trivially has identical counts).
46
+ *
47
+ * Every other combination -- including 'mixed' on either side, as long as
48
+ * the two sides don't have the same mixed counts -- IS reported: 'mixed' is
49
+ * not ambiguous in the same way 'none' is. A mostly-CRLF file with one stray
50
+ * LF line getting normalized to all-LF (or the reverse) is a real, common
51
+ * state (partial manual edits, a tool that normalized half a file, a paste
52
+ * from another OS), and discarding that information because it isn't a
53
+ * clean CRLF<->LF swap would reintroduce exactly the silent-empty-diff bug
54
+ * this function exists to prevent.
55
+ */
56
+ export declare function lineEndingChange(before: string, after: string): LineEndingChange | null;
57
+ /** Render a `LineEndingChange` as the one line the approval prompt shows --
58
+ * shared by both faces (Ink and readline) so the wording can't drift between
59
+ * them. `contentUnchanged` adds the "-- content otherwise unchanged" note
60
+ * (the write is real, but the line-level diff has nothing else to show). */
61
+ export declare function formatEndingChange(change: LineEndingChange, contentUnchanged: boolean): string;
62
+ export declare function diffStat(lines: DiffLine[]): {
63
+ added: number;
64
+ removed: number;
65
+ };
66
+ /** A row a renderer prints in place of one or more elided diff lines. `reason`
67
+ * is what makes this different from an ordinary truncation marker: 'context'
68
+ * means only unchanged surrounding lines were skipped (the change itself is
69
+ * fully visible elsewhere in the output); 'changes' means actual `+`/`-`
70
+ * lines did not fit and were dropped. Those mean very different things when
71
+ * deciding whether to approve a write, so they render as distinct text. */
72
+ export interface SkippedRows {
73
+ kind: 'skip';
74
+ count: number;
75
+ reason: 'context' | 'changes';
76
+ }
77
+ export type DiffRow = DiffLine | SkippedRows;
78
+ /** Unchanged lines kept on each side of a run of changes -- unified diff's
79
+ * conventional default. Named rather than inlined so the choice is visible
80
+ * and a caller can override it (tests do, to keep fixtures small). */
81
+ export declare const DIFF_CONTEXT_LINES = 3;
82
+ /**
83
+ * Cap a diff to at most `max` RENDERED rows, so a huge diff can never grow the
84
+ * approval modal past the terminal window (the bug this guards against: the
85
+ * live region growing taller than the window pushed the input box off the top
86
+ * of the screen).
87
+ *
88
+ * Unlike a naive head-truncation, this keeps every CHANGED (`+`/`-`) line
89
+ * visible whenever the changes themselves fit the budget -- a single-line
90
+ * edit at the end of a 500-line file must still be visible, not buried behind
91
+ * "N more lines hidden" while the user stares at 39 lines of unchanged
92
+ * context from the top of the file. Unchanged runs between (and around)
93
+ * clusters of changes collapse into a `skip` row naming how many lines were
94
+ * elided; if the CHANGED lines alone exceed the budget, those are what get
95
+ * truncated instead, with a row that says so distinctly from a context
96
+ * elision (see `SkippedRows.reason`) -- those two cases mean very different
97
+ * things when deciding whether to approve.
98
+ */
99
+ export declare function capDiffLines(lines: DiffLine[], max: number, contextLines?: number): DiffRow[];
100
+ //# sourceMappingURL=diff.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff.d.ts","sourceRoot":"","sources":["../src/diff.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,QAAQ;IAAG,IAAI,EAAE,KAAK,GAAG,KAAK,GAAG,KAAK,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE;AA2BvE,wBAAgB,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,QAAQ,EAAE,CAkCzD;AAED;;;;;yEAKyE;AACzE,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,IAAI,GAAG,OAAO,GAAG,MAAM,CAAC;AAE1D;;;6EAG6E;AAC7E,MAAM,WAAW,cAAc;IAAG,IAAI,EAAE,UAAU,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE;AAe9E,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,CAEzD;AAED,MAAM,WAAW,gBAAgB;IAAG,IAAI,EAAE,cAAc,CAAC;IAAC,EAAE,EAAE,cAAc,CAAA;CAAE;AAE9E;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI,CAOvF;AAED;;;4EAG4E;AAC5E,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,OAAO,GAAG,MAAM,CAU9F;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,QAAQ,EAAE,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAQ9E;AAED;;;;;2EAK2E;AAC3E,MAAM,WAAW,WAAW;IAAG,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,SAAS,GAAG,SAAS,CAAA;CAAE;AAE3F,MAAM,MAAM,OAAO,GAAG,QAAQ,GAAG,WAAW,CAAC;AAE7C;;sEAEsE;AACtE,eAAO,MAAM,kBAAkB,IAAI,CAAC;AAgCpC;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,QAAQ,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,YAAY,GAAE,MAA2B,GAAG,OAAO,EAAE,CAoDjH"}
package/dist/diff.js ADDED
@@ -0,0 +1,18 @@
1
+ import {
2
+ DIFF_CONTEXT_LINES,
3
+ capDiffLines,
4
+ detectLineEnding,
5
+ diffStat,
6
+ formatEndingChange,
7
+ lineDiff,
8
+ lineEndingChange
9
+ } from "./chunk-I44FC7PE.js";
10
+ export {
11
+ DIFF_CONTEXT_LINES,
12
+ capDiffLines,
13
+ detectLineEnding,
14
+ diffStat,
15
+ formatEndingChange,
16
+ lineDiff,
17
+ lineEndingChange
18
+ };
package/dist/ink/App.d.ts CHANGED
@@ -24,6 +24,18 @@ export interface AppProps {
24
24
  /** The palette's highlighted row, owned by Root so it survives across
25
25
  * renders and can be moved with the arrow keys. */
26
26
  paletteSelected: number;
27
+ /** Bumped by Root whenever it replaces `value` PROGRAMMATICALLY (a palette
28
+ * Tab/Enter completion) rather than from a keystroke `ink-text-input`
29
+ * itself produced. Used as the field's React `key`: ink-text-input only
30
+ * clamps its internal cursor offset to the new value's length when the OLD
31
+ * offset already ran past it (see its `useEffect`), so completing "/mo" (a
32
+ * cursor at 3) to "/mode " (length 6) left the cursor sitting after "/mo"
33
+ * instead of at the end of what was just inserted. Changing `key` remounts
34
+ * the field, whose `useState` initializer sets the cursor to
35
+ * `value.length` fresh -- the one thing that reliably moves it. Optional so
36
+ * a caller that never does a programmatic completion (a test rendering
37
+ * <App> directly with hand-built props) doesn't have to invent one. */
38
+ inputKey?: number;
27
39
  }
28
40
  /** The whole face: a <Static> region the terminal owns, and a live tail. */
29
41
  export declare function App(props: AppProps): React.ReactElement;
@@ -1 +1 @@
1
- {"version":3,"file":"App.d.ts","sourceRoot":"","sources":["../../src/ink/App.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA8B,MAAM,OAAO,CAAC;AAInD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAyBzC,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,QAAQ,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ;;iBAEa;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,OAAO,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9B,QAAQ,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9B,QAAQ,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9B;;;;;oDAKgD;IAChD,KAAK,EAAE,OAAO,CAAC;IACf;uDACmD;IACnD,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,4EAA4E;AAC5E,wBAAgB,GAAG,CAAC,KAAK,EAAE,QAAQ,GAAG,KAAK,CAAC,YAAY,CAmFvD"}
1
+ {"version":3,"file":"App.d.ts","sourceRoot":"","sources":["../../src/ink/App.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA8B,MAAM,OAAO,CAAC;AAInD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AA0BzC,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,QAAQ,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ;;iBAEa;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,OAAO,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9B,QAAQ,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9B,QAAQ,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9B;;;;;oDAKgD;IAChD,KAAK,EAAE,OAAO,CAAC;IACf;uDACmD;IACnD,eAAe,EAAE,MAAM,CAAC;IACxB;;;;;;;;;;2EAUuE;IACvE,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,4EAA4E;AAC5E,wBAAgB,GAAG,CAAC,KAAK,EAAE,QAAQ,GAAG,KAAK,CAAC,YAAY,CA6FvD"}
@@ -1 +1 @@
1
- {"version":3,"file":"Modal.d.ts","sourceRoot":"","sources":["../../src/ink/Modal.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AAGtC,wBAAgB,SAAS,CAAC,EAAE,KAAK,EAAE,EAAE;IAAE,KAAK,EAAE,KAAK,CAAA;CAAE,GAAG,KAAK,CAAC,YAAY,CAgCzE"}
1
+ {"version":3,"file":"Modal.d.ts","sourceRoot":"","sources":["../../src/ink/Modal.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AAuDtC,wBAAgB,SAAS,CAAC,EAAE,KAAK,EAAE,EAAE;IAAE,KAAK,EAAE,KAAK,CAAA;CAAE,GAAG,KAAK,CAAC,YAAY,CAiCzE"}
@@ -0,0 +1,10 @@
1
+ import React from 'react';
2
+ import type { TodoItem } from '../state';
3
+ /** The agent's task list, bounded to a handful of rows regardless of how long
4
+ * the list gets -- see todos.ts's module comment for why. Renders nothing
5
+ * once every item is `completed`, so a finished list disappears rather than
6
+ * sitting there as an empty box. */
7
+ export declare function TodoBlock({ todos }: {
8
+ todos: TodoItem[];
9
+ }): React.ReactElement | null;
10
+ //# sourceMappingURL=TodoBlock.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TodoBlock.d.ts","sourceRoot":"","sources":["../../src/ink/TodoBlock.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,KAAK,EAAE,QAAQ,EAAc,MAAM,UAAU,CAAC;AA4BrD;;;oCAGoC;AACpC,wBAAgB,SAAS,CAAC,EAAE,KAAK,EAAE,EAAE;IAAE,KAAK,EAAE,QAAQ,EAAE,CAAA;CAAE,GAAG,KAAK,CAAC,YAAY,GAAG,IAAI,CAUrF"}