@freesyntax/notch-cli 0.5.19 → 0.5.21

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 (42) hide show
  1. package/dist/{apply-patch-EBZ5VLO7.js → apply-patch-D5PDUXUC.js} +0 -1
  2. package/dist/{auth-S3FIB42I.js → auth-JQX6MHJG.js} +0 -1
  3. package/dist/builtins/archimedes.toml +18 -0
  4. package/dist/builtins/awaiter.toml +18 -0
  5. package/dist/builtins/euclid.toml +18 -0
  6. package/dist/builtins/hypatia.toml +18 -0
  7. package/dist/builtins/kepler.toml +18 -0
  8. package/dist/builtins/plato.toml +18 -0
  9. package/dist/builtins/ptolemy.toml +18 -0
  10. package/dist/builtins/pythagoras.toml +18 -0
  11. package/dist/chunk-443G6HCC.js +543 -0
  12. package/dist/chunk-GFVLHUSS.js +155 -0
  13. package/dist/chunk-MMBFNIKE.js +509 -0
  14. package/dist/chunk-OSWUX6TC.js +167 -0
  15. package/dist/{chunk-6M6CXXWR.js → chunk-PKZKVOAN.js} +209 -1
  16. package/dist/chunk-QKM27RHS.js +198 -0
  17. package/dist/chunk-TU465P2P.js +3106 -0
  18. package/dist/compression-SQAIQ2UU.js +32 -0
  19. package/dist/{edit-FXWXOFAF.js → edit-JEFEK43H.js} +0 -1
  20. package/dist/{git-XVWI2BT7.js → git-5T5TSQTX.js} +0 -1
  21. package/dist/{github-DOZ2MVQE.js → github-DWRGWX6U.js} +0 -1
  22. package/dist/{glob-XSBN4MDB.js → glob-BI3P4C7Q.js} +0 -1
  23. package/dist/{grep-2A42QPWM.js → grep-VZ3I5GNW.js} +0 -1
  24. package/dist/index.js +5049 -1113
  25. package/dist/{lsp-WUEGBQ3F.js → lsp-UPY6I3L7.js} +0 -1
  26. package/dist/{notebook-5U6PAF6M.js → notebook-FXJBTSPA.js} +0 -1
  27. package/dist/ollama-bench-QQHBIG2D.js +190 -0
  28. package/dist/ollama-launch-2ASVER3S.js +18 -0
  29. package/dist/ollama-usage-2WPCZJJI.js +69 -0
  30. package/dist/{plugins-GJIUZCJ5.js → plugins-OG2P75K5.js} +0 -1
  31. package/dist/{read-LY2VGCZY.js → read-OVJG2XKW.js} +0 -1
  32. package/dist/{server-4JRQH3DT.js → server-7UQKCB2Z.js} +15 -17
  33. package/dist/session-index-SSGOOZXK.js +21 -0
  34. package/dist/{shell-RGXMLRLH.js → shell-4X545EVN.js} +0 -1
  35. package/dist/{task-VIJ3N5EB.js → task-OS3E5F3X.js} +0 -1
  36. package/dist/{tools-XKVTMNR5.js → tools-7WAWS6V4.js} +3 -2
  37. package/dist/{web-fetch-XOH5PUCP.js → web-fetch-KNIV3Z3W.js} +0 -1
  38. package/dist/{write-DOLDW7HM.js → write-NNHLOTYK.js} +0 -1
  39. package/package.json +6 -4
  40. package/dist/chunk-3RG5ZIWI.js +0 -10
  41. package/dist/chunk-75K7DQVI.js +0 -630
  42. package/dist/compression-LPFNGAV6.js +0 -17
@@ -1,5 +1,6 @@
1
1
  // src/agent/compression.ts
2
2
  import { generateText } from "ai";
3
+ import { createHash } from "crypto";
3
4
  function estimateTokens(messages) {
4
5
  let chars = 0;
5
6
  for (const msg of messages) {
@@ -15,6 +16,112 @@ function estimateTokens(messages) {
15
16
  }
16
17
  return Math.ceil(chars / 4);
17
18
  }
19
+ var InMemoryArtifactStore = class {
20
+ map = /* @__PURE__ */ new Map();
21
+ put(key, value) {
22
+ this.map.set(key, value);
23
+ }
24
+ get(key) {
25
+ return this.map.get(key);
26
+ }
27
+ has(key) {
28
+ return this.map.has(key);
29
+ }
30
+ };
31
+ var globalArtifactStore = new InMemoryArtifactStore();
32
+ function setArtifactStore(store) {
33
+ const prev = globalArtifactStore;
34
+ globalArtifactStore = store;
35
+ return () => {
36
+ globalArtifactStore = prev;
37
+ };
38
+ }
39
+ function getArtifactStore() {
40
+ return globalArtifactStore;
41
+ }
42
+ var ARTIFACT_URI_PREFIX = "notch-artifact://";
43
+ function artifactKey(content) {
44
+ return createHash("sha1").update(content).digest("hex").slice(0, 16);
45
+ }
46
+ function makeArtifactUri(key) {
47
+ return `${ARTIFACT_URI_PREFIX}${key}`;
48
+ }
49
+ function parseArtifactUri(text) {
50
+ const match = text.match(/notch-artifact:\/\/([a-f0-9]{8,64})/);
51
+ return match ? match[1] : null;
52
+ }
53
+ var BUDGET_TOOL_RESULT_MAX = 3e3;
54
+ var BUDGET_PREVIEW_CHARS = 400;
55
+ function budgetReduce(messages, opts = {}) {
56
+ const maxChars = opts.maxToolResultChars ?? BUDGET_TOOL_RESULT_MAX;
57
+ const previewChars = opts.previewChars ?? BUDGET_PREVIEW_CHARS;
58
+ const keepLastN = opts.keepLastN ?? 4;
59
+ const store = opts.store ?? globalArtifactStore;
60
+ const toolIdx = [];
61
+ for (let i = 0; i < messages.length; i++) {
62
+ if (messages[i].role === "tool") toolIdx.push(i);
63
+ }
64
+ const eligibleUpTo = toolIdx.length - keepLastN;
65
+ if (eligibleUpTo <= 0) return { messages, stashed: 0, savedChars: 0 };
66
+ const eligible = new Set(toolIdx.slice(0, eligibleUpTo));
67
+ let stashed = 0;
68
+ let savedChars = 0;
69
+ const out = messages.map((msg, idx) => {
70
+ if (msg.role !== "tool" || !eligible.has(idx)) return msg;
71
+ if (!Array.isArray(msg.content)) return msg;
72
+ let touched = false;
73
+ const newContent = msg.content.map((block) => {
74
+ const b = block;
75
+ if (b.type !== "tool-result") return block;
76
+ const resultStr = typeof b.result === "string" ? b.result : JSON.stringify(b.result ?? "");
77
+ if (resultStr.length <= maxChars) return block;
78
+ const key = artifactKey(resultStr);
79
+ try {
80
+ void store.put(key, resultStr);
81
+ } catch {
82
+ return block;
83
+ }
84
+ const preview = resultStr.slice(0, previewChars);
85
+ const replacement = `${preview}
86
+ ... [+${resultStr.length - previewChars} chars stashed at ${makeArtifactUri(key)}]`;
87
+ savedChars += resultStr.length - replacement.length;
88
+ stashed++;
89
+ touched = true;
90
+ return {
91
+ ...b,
92
+ result: replacement
93
+ };
94
+ });
95
+ if (!touched) return msg;
96
+ return { ...msg, content: newContent };
97
+ });
98
+ return { messages: out, stashed, savedChars };
99
+ }
100
+ function snip(messages, opts = {}) {
101
+ const keepStart = opts.keepStart ?? 1;
102
+ const keepRecent = opts.keepRecent ?? 6;
103
+ const trigger = opts.triggerMiddleCount ?? 40;
104
+ const snipCount = opts.snipCount ?? 10;
105
+ if (messages.length < keepStart + keepRecent + trigger) {
106
+ return { messages, snipped: 0 };
107
+ }
108
+ const head = messages.slice(0, keepStart);
109
+ const tail = messages.slice(-keepRecent);
110
+ const middle = messages.slice(keepStart, messages.length - keepRecent);
111
+ let cut = Math.min(snipCount, middle.length);
112
+ while (cut < middle.length && middle[cut].role === "tool") cut++;
113
+ const dropped = middle.slice(0, cut);
114
+ const kept = middle.slice(cut);
115
+ if (dropped.length === 0) return { messages, snipped: 0 };
116
+ const marker = {
117
+ role: "user",
118
+ content: `[snip: ${dropped.length} older messages removed by age]`
119
+ };
120
+ return {
121
+ messages: [...head, marker, ...kept, ...tail],
122
+ snipped: dropped.length
123
+ };
124
+ }
18
125
  var TEXT_BLOCK_MAX = 8e3;
19
126
  function microCompact(messages) {
20
127
  const result = [];
@@ -35,6 +142,48 @@ function microCompact(messages) {
35
142
  }
36
143
  return result;
37
144
  }
145
+ function contextCollapse(messages, opts = {}) {
146
+ const keepStart = opts.keepStart ?? 1;
147
+ const keepRecent = opts.keepRecent ?? 4;
148
+ const trigger = opts.triggerMiddleCount ?? 12;
149
+ const store = opts.store ?? globalArtifactStore;
150
+ if (messages.length < keepStart + keepRecent + trigger) {
151
+ return { messages, collapsed: false };
152
+ }
153
+ const head = messages.slice(0, keepStart);
154
+ const middle = messages.slice(keepStart, messages.length - keepRecent);
155
+ const tail = messages.slice(-keepRecent);
156
+ if (middle.length === 0) return { messages, collapsed: false };
157
+ let key;
158
+ try {
159
+ const serialized = JSON.stringify(middle);
160
+ key = artifactKey(serialized);
161
+ void store.put(key, serialized);
162
+ } catch {
163
+ }
164
+ const summaryText = buildDeterministicSummary(middle);
165
+ const ref = key ? ` (originals at ${makeArtifactUri(key)})` : "";
166
+ const summaryMarker = {
167
+ role: "user",
168
+ content: `[Context collapsed \u2014 ${middle.length} messages summarized${ref}]
169
+ ${summaryText}`
170
+ };
171
+ return {
172
+ messages: [...head, summaryMarker, ...tail],
173
+ collapsed: true,
174
+ artifactKey: key
175
+ };
176
+ }
177
+ async function restoreCollapsedSpan(artifactKeyOrUri, store = globalArtifactStore) {
178
+ const key = artifactKeyOrUri.startsWith(ARTIFACT_URI_PREFIX) ? artifactKeyOrUri.slice(ARTIFACT_URI_PREFIX.length) : artifactKeyOrUri;
179
+ const raw = await store.get(key);
180
+ if (!raw) return null;
181
+ try {
182
+ return JSON.parse(raw);
183
+ } catch {
184
+ return null;
185
+ }
186
+ }
38
187
  var RESERVE_BUFFER_TOKENS = 13e3;
39
188
  var MAX_SUMMARY_TOKENS = 2e4;
40
189
  var MAX_COMPRESSION_FAILURES = 3;
@@ -180,10 +329,19 @@ ${firstContent}`
180
329
  return { messages: compressed, compressed: true };
181
330
  }
182
331
  async function autoCompress(messages, model, contextWindow, onCompress) {
183
- let result = microCompact(messages);
184
332
  const threshold = (contextWindow - RESERVE_BUFFER_TOKENS) * 0.75;
333
+ let result = budgetReduce(messages).messages;
334
+ result = snip(result).messages;
335
+ result = microCompact(result);
185
336
  let tokens = estimateTokens(result);
186
337
  if (tokens < threshold) return result;
338
+ const collapsed = contextCollapse(result);
339
+ if (collapsed.collapsed) {
340
+ onCompress?.();
341
+ result = collapsed.messages;
342
+ tokens = estimateTokens(result);
343
+ }
344
+ if (tokens < threshold) return result;
187
345
  const auto = await autoCompactSummarize(result, { model, contextWindow });
188
346
  if (auto.compressed) {
189
347
  onCompress?.();
@@ -198,6 +356,48 @@ async function autoCompress(messages, model, contextWindow, onCompress) {
198
356
  }
199
357
  return result;
200
358
  }
359
+ async function autoCompressWithStats(messages, model, contextWindow) {
360
+ const stats = {
361
+ budgetStashed: 0,
362
+ snipped: 0,
363
+ collapsed: false,
364
+ autoCompacted: false,
365
+ fullCompacted: false
366
+ };
367
+ const threshold = (contextWindow - RESERVE_BUFFER_TOKENS) * 0.75;
368
+ const br = budgetReduce(messages);
369
+ let result = br.messages;
370
+ stats.budgetStashed = br.stashed;
371
+ const sn = snip(result);
372
+ result = sn.messages;
373
+ stats.snipped = sn.snipped;
374
+ result = microCompact(result);
375
+ let tokens = estimateTokens(result);
376
+ if (tokens >= threshold) {
377
+ const collapsed = contextCollapse(result);
378
+ if (collapsed.collapsed) {
379
+ result = collapsed.messages;
380
+ stats.collapsed = true;
381
+ tokens = estimateTokens(result);
382
+ }
383
+ }
384
+ if (tokens >= threshold) {
385
+ const auto = await autoCompactSummarize(result, { model, contextWindow });
386
+ if (auto.compressed) {
387
+ result = auto.messages;
388
+ stats.autoCompacted = true;
389
+ tokens = estimateTokens(result);
390
+ }
391
+ }
392
+ if (tokens >= threshold) {
393
+ const full = await fullCompact(result, model);
394
+ if (full.compressed) {
395
+ result = full.messages;
396
+ stats.fullCompacted = true;
397
+ }
398
+ }
399
+ return { messages: result, stats };
400
+ }
201
401
  async function compressHistory(messages, opts) {
202
402
  const result = await autoCompactSummarize(messages, opts);
203
403
  return result;
@@ -205,9 +405,17 @@ async function compressHistory(messages, opts) {
205
405
 
206
406
  export {
207
407
  estimateTokens,
408
+ setArtifactStore,
409
+ getArtifactStore,
410
+ parseArtifactUri,
411
+ budgetReduce,
412
+ snip,
208
413
  microCompact,
414
+ contextCollapse,
415
+ restoreCollapsedSpan,
209
416
  autoCompactSummarize,
210
417
  fullCompact,
211
418
  autoCompress,
419
+ autoCompressWithStats,
212
420
  compressHistory
213
421
  };
@@ -0,0 +1,198 @@
1
+ // src/session/rollout.ts
2
+ import fsp from "fs/promises";
3
+ import path from "path";
4
+ import os from "os";
5
+ import crypto from "crypto";
6
+ var ROLLOUT_DIR = path.join(os.homedir(), ".notch", "rollouts");
7
+ async function ensureDir() {
8
+ await fsp.mkdir(ROLLOUT_DIR, { recursive: true });
9
+ }
10
+ function rolloutPath(id) {
11
+ return path.join(ROLLOUT_DIR, `${id}.jsonl`);
12
+ }
13
+ function indexPath(id) {
14
+ return path.join(ROLLOUT_DIR, `${id}.idx.json`);
15
+ }
16
+ function generateSessionId() {
17
+ const d = /* @__PURE__ */ new Date();
18
+ const stamp = d.toISOString().slice(0, 10).replace(/-/g, "") + "-" + d.toISOString().slice(11, 16).replace(":", "");
19
+ const rand = crypto.randomBytes(3).toString("hex");
20
+ return `${stamp}-${rand}`;
21
+ }
22
+ var Rollout = class {
23
+ constructor(id) {
24
+ this.id = id;
25
+ }
26
+ id;
27
+ fd = null;
28
+ seq = 0;
29
+ idx = {
30
+ lastSeq: -1,
31
+ messageCount: 0,
32
+ branches: {},
33
+ activeStream: null
34
+ };
35
+ async openNew(header) {
36
+ await ensureDir();
37
+ this.fd = await fsp.open(rolloutPath(this.id), "a");
38
+ await this.writeRecord({
39
+ type: "header",
40
+ payload: { id: this.id, project: header.project, model: header.model, createdAt: (/* @__PURE__ */ new Date()).toISOString(), schema: 1 }
41
+ });
42
+ }
43
+ async openExisting() {
44
+ await ensureDir();
45
+ this.idx = await readIndex(this.id);
46
+ this.seq = this.idx.lastSeq + 1;
47
+ this.fd = await fsp.open(rolloutPath(this.id), "a");
48
+ }
49
+ async close() {
50
+ await this.saveIndex();
51
+ if (this.fd) {
52
+ await this.fd.close();
53
+ this.fd = null;
54
+ }
55
+ try {
56
+ const { upsertSessionIndexEntry } = await import("./session-index-SSGOOZXK.js");
57
+ await upsertSessionIndexEntry(this.id);
58
+ } catch {
59
+ }
60
+ }
61
+ async writeRecord(partial) {
62
+ if (!this.fd) throw new Error("rollout not open");
63
+ const rec = { seq: this.seq++, ts: Date.now(), ...partial };
64
+ const line = JSON.stringify(rec) + "\n";
65
+ await this.fd.write(line);
66
+ this.idx.lastSeq = rec.seq;
67
+ if (rec.type === "user-message" || rec.type === "assistant-message") {
68
+ this.idx.messageCount++;
69
+ if (rec.parent && rec.msgId) {
70
+ (this.idx.branches[rec.parent] ??= []).push(rec.msgId);
71
+ }
72
+ }
73
+ if (rec.type === "active-stream") {
74
+ this.idx.activeStream = rec.streamId ?? null;
75
+ } else if (rec.type === "turn-end" || rec.type === "error") {
76
+ this.idx.activeStream = null;
77
+ }
78
+ return rec;
79
+ }
80
+ append(record) {
81
+ return this.writeRecord(record);
82
+ }
83
+ async flush() {
84
+ if (this.fd) await this.fd.sync();
85
+ await this.saveIndex();
86
+ }
87
+ async saveIndex() {
88
+ await fsp.writeFile(indexPath(this.id), JSON.stringify(this.idx, null, 2));
89
+ }
90
+ get activeStream() {
91
+ return this.idx.activeStream;
92
+ }
93
+ get index() {
94
+ return this.idx;
95
+ }
96
+ };
97
+ async function readRollout(id) {
98
+ const raw = await fsp.readFile(rolloutPath(id), "utf-8");
99
+ const out = [];
100
+ for (const line of raw.split("\n")) {
101
+ if (!line.trim()) continue;
102
+ try {
103
+ out.push(JSON.parse(line));
104
+ } catch {
105
+ }
106
+ }
107
+ return out;
108
+ }
109
+ async function readIndex(id) {
110
+ try {
111
+ const raw = await fsp.readFile(indexPath(id), "utf-8");
112
+ return JSON.parse(raw);
113
+ } catch {
114
+ const records = await readRollout(id);
115
+ const idx = { lastSeq: -1, messageCount: 0, branches: {}, activeStream: null };
116
+ for (const r of records) {
117
+ idx.lastSeq = r.seq;
118
+ if (r.type === "user-message" || r.type === "assistant-message") {
119
+ idx.messageCount++;
120
+ if (r.parent && r.msgId) (idx.branches[r.parent] ??= []).push(r.msgId);
121
+ }
122
+ if (r.type === "active-stream") idx.activeStream = r.streamId ?? null;
123
+ else if (r.type === "turn-end" || r.type === "error") idx.activeStream = null;
124
+ }
125
+ await fsp.writeFile(indexPath(id), JSON.stringify(idx, null, 2)).catch(() => {
126
+ });
127
+ return idx;
128
+ }
129
+ }
130
+ async function listRollouts() {
131
+ try {
132
+ const entries = await fsp.readdir(ROLLOUT_DIR);
133
+ const files = entries.filter((e) => e.endsWith(".jsonl"));
134
+ const out = [];
135
+ for (const f of files) {
136
+ const id = f.replace(/\.jsonl$/, "");
137
+ const stat = await fsp.stat(path.join(ROLLOUT_DIR, f));
138
+ const idx = await readIndex(id).catch(() => ({ messageCount: 0 }));
139
+ let project;
140
+ let model;
141
+ try {
142
+ const first = (await fsp.readFile(path.join(ROLLOUT_DIR, f), "utf-8")).split("\n", 1)[0];
143
+ if (first) {
144
+ const header = JSON.parse(first);
145
+ project = header.payload?.project;
146
+ model = header.payload?.model;
147
+ }
148
+ } catch {
149
+ }
150
+ out.push({ id, updated: stat.mtimeMs, messageCount: idx.messageCount, project, model });
151
+ }
152
+ return out.sort((a, b) => b.updated - a.updated);
153
+ } catch {
154
+ return [];
155
+ }
156
+ }
157
+ function rebuildMessagesFromRollout(records) {
158
+ const msgs = [];
159
+ let current = null;
160
+ for (const r of records) {
161
+ if (r.type === "user-message") {
162
+ if (current) {
163
+ msgs.push(current);
164
+ current = null;
165
+ }
166
+ msgs.push({ role: "user", content: r.payload?.content ?? "" });
167
+ } else if (r.type === "text-delta") {
168
+ const delta = r.payload?.content ?? "";
169
+ if (!current) current = { role: "assistant", content: "" };
170
+ current.content += delta;
171
+ } else if (r.type === "assistant-message") {
172
+ if (current) {
173
+ msgs.push(current);
174
+ current = null;
175
+ }
176
+ msgs.push({ role: "assistant", content: r.payload?.content ?? "" });
177
+ } else if (r.type === "turn-end" && current) {
178
+ msgs.push(current);
179
+ current = null;
180
+ }
181
+ }
182
+ if (current) msgs.push(current);
183
+ return msgs;
184
+ }
185
+ function hasActiveStream(idx) {
186
+ return !!idx.activeStream;
187
+ }
188
+
189
+ export {
190
+ rolloutPath,
191
+ generateSessionId,
192
+ Rollout,
193
+ readRollout,
194
+ readIndex,
195
+ listRollouts,
196
+ rebuildMessagesFromRollout,
197
+ hasActiveStream
198
+ };