compact-agent 1.8.4 → 1.10.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.
@@ -0,0 +1,332 @@
1
+ /**
2
+ * MemPalace storage layer.
3
+ *
4
+ * Each store is a single JSON file written atomically (tmp file + rename)
5
+ * so a crashed write can't corrupt the existing state. The choice of JSON-
6
+ * over-SQLite is deliberate for v1: zero native dependencies, trivial to
7
+ * inspect by hand, no migration story needed for npm-published CLI.
8
+ *
9
+ * The Store interface is the swappable seam — when we outgrow JSON (>~10k
10
+ * drawers, slow search) we replace the implementation with SQLite/FTS5 or
11
+ * a vector store without touching callers.
12
+ */
13
+ import { readFileSync, writeFileSync, existsSync, mkdirSync, renameSync, unlinkSync } from 'node:fs';
14
+ import { join, dirname } from 'node:path';
15
+ import { homedir } from 'node:os';
16
+ import { randomBytes } from 'node:crypto';
17
+ import { SCHEMA_VERSION } from './types.js';
18
+ import { searchDrawers } from './search.js';
19
+ // ── Paths ─────────────────────────────────────────────────
20
+ /** ~/.crowcoder/memory/store.json — cross-project knowledge */
21
+ export function globalStorePath() {
22
+ return join(homedir(), '.crowcoder', 'memory', 'store.json');
23
+ }
24
+ /** <cwd>/.crowcoder/memory/store.json — per-repo knowledge */
25
+ export function projectStorePath(cwd) {
26
+ return join(cwd, '.crowcoder', 'memory', 'store.json');
27
+ }
28
+ // ── ID generation ─────────────────────────────────────────
29
+ /**
30
+ * Time-ordered ID: hex timestamp + 8 random hex chars. Sorts naturally by
31
+ * creation time, no UUID library needed. ~96 bits of entropy total which
32
+ * is overkill for a single-user store but keeps collision chances at
33
+ * effective zero even across machines.
34
+ */
35
+ export function newId(prefix) {
36
+ const ts = Date.now().toString(16).padStart(11, '0');
37
+ const rand = randomBytes(4).toString('hex');
38
+ return `${prefix}_${ts}${rand}`;
39
+ }
40
+ // ── Atomic JSON read/write ────────────────────────────────
41
+ function readState(path) {
42
+ if (!existsSync(path)) {
43
+ return { schemaVersion: SCHEMA_VERSION, drawers: [], tunnels: [], triples: [] };
44
+ }
45
+ try {
46
+ const raw = readFileSync(path, 'utf-8');
47
+ const parsed = JSON.parse(raw);
48
+ // Defensive: future schema migrations would dispatch on parsed.schemaVersion here
49
+ return {
50
+ schemaVersion: parsed.schemaVersion ?? SCHEMA_VERSION,
51
+ drawers: parsed.drawers ?? [],
52
+ tunnels: parsed.tunnels ?? [],
53
+ triples: parsed.triples ?? [],
54
+ };
55
+ }
56
+ catch {
57
+ // Corrupted store — log and return empty so we don't crash the agent.
58
+ // The bad file is preserved alongside as .corrupt for forensic recovery.
59
+ try {
60
+ renameSync(path, path + '.corrupt-' + Date.now());
61
+ }
62
+ catch { /* noop */ }
63
+ return { schemaVersion: SCHEMA_VERSION, drawers: [], tunnels: [], triples: [] };
64
+ }
65
+ }
66
+ function writeState(path, state) {
67
+ mkdirSync(dirname(path), { recursive: true });
68
+ const tmp = path + '.tmp-' + process.pid;
69
+ writeFileSync(tmp, JSON.stringify(state, null, 2), 'utf-8');
70
+ try {
71
+ renameSync(tmp, path);
72
+ }
73
+ catch (e) {
74
+ // On some Windows filesystems renaming over an existing file can fail.
75
+ // Fall back to unlink + rename.
76
+ try {
77
+ unlinkSync(path);
78
+ }
79
+ catch { /* noop */ }
80
+ renameSync(tmp, path);
81
+ }
82
+ }
83
+ // ── Store class ───────────────────────────────────────────
84
+ /**
85
+ * A single-file MemPalace store backed by JSON. Mutations are immediately
86
+ * persisted to disk — there's no in-memory cache to flush. Trades a small
87
+ * amount of throughput for crash safety, which matters more for a memory
88
+ * system.
89
+ */
90
+ export class JsonStore {
91
+ path;
92
+ scope;
93
+ constructor(path, scope) {
94
+ this.path = path;
95
+ this.scope = scope;
96
+ }
97
+ read() { return readState(this.path); }
98
+ write(s) { writeState(this.path, s); }
99
+ // ── Drawers ───────────────────────────────────────────
100
+ addDrawer(input) {
101
+ const now = new Date().toISOString();
102
+ const drawer = {
103
+ id: newId('drw'),
104
+ createdAt: now,
105
+ updatedAt: now,
106
+ scope: this.scope,
107
+ ...input,
108
+ // Defensive normalization
109
+ tags: (input.tags || []).map((t) => t.toLowerCase().trim()).filter(Boolean),
110
+ importance: clamp01(input.importance ?? 0.5),
111
+ wing: input.wing.toLowerCase().trim(),
112
+ room: input.room.toLowerCase().trim(),
113
+ };
114
+ const state = this.read();
115
+ state.drawers.push(drawer);
116
+ this.write(state);
117
+ return drawer;
118
+ }
119
+ getDrawer(id) {
120
+ return this.read().drawers.find((d) => d.id === id) || null;
121
+ }
122
+ updateDrawer(id, patch) {
123
+ const state = this.read();
124
+ const idx = state.drawers.findIndex((d) => d.id === id);
125
+ if (idx < 0)
126
+ return null;
127
+ const updated = {
128
+ ...state.drawers[idx],
129
+ ...patch,
130
+ updatedAt: new Date().toISOString(),
131
+ tags: patch.tags
132
+ ? patch.tags.map((t) => t.toLowerCase().trim()).filter(Boolean)
133
+ : state.drawers[idx].tags,
134
+ importance: patch.importance !== undefined ? clamp01(patch.importance) : state.drawers[idx].importance,
135
+ };
136
+ state.drawers[idx] = updated;
137
+ this.write(state);
138
+ return updated;
139
+ }
140
+ deleteDrawer(id) {
141
+ const state = this.read();
142
+ const before = state.drawers.length;
143
+ state.drawers = state.drawers.filter((d) => d.id !== id);
144
+ // Cascade: drop tunnels touching this drawer + triples sourced from it
145
+ state.tunnels = state.tunnels.filter((t) => t.fromDrawerId !== id && t.toDrawerId !== id);
146
+ state.triples = state.triples.filter((t) => t.sourceDrawerId !== id);
147
+ const removed = state.drawers.length < before;
148
+ if (removed)
149
+ this.write(state);
150
+ return removed;
151
+ }
152
+ listDrawers(filter = {}) {
153
+ let drawers = this.read().drawers;
154
+ if (filter.wing)
155
+ drawers = drawers.filter((d) => d.wing === filter.wing.toLowerCase());
156
+ if (filter.room)
157
+ drawers = drawers.filter((d) => d.room === filter.room.toLowerCase());
158
+ if (filter.tag)
159
+ drawers = drawers.filter((d) => d.tags.includes(filter.tag.toLowerCase()));
160
+ return drawers;
161
+ }
162
+ // ── Wings + Rooms (derived views) ─────────────────────
163
+ listWings() {
164
+ const drawers = this.read().drawers;
165
+ const byWing = new Map();
166
+ for (const d of drawers) {
167
+ const entry = byWing.get(d.wing) ?? { rooms: new Set(), count: 0 };
168
+ entry.rooms.add(d.room);
169
+ entry.count++;
170
+ byWing.set(d.wing, entry);
171
+ }
172
+ return [...byWing.entries()]
173
+ .map(([name, v]) => ({ name, rooms: [...v.rooms].sort(), drawerCount: v.count }))
174
+ .sort((a, b) => a.name.localeCompare(b.name));
175
+ }
176
+ listRooms(wing) {
177
+ const drawers = this.read().drawers;
178
+ const filtered = wing ? drawers.filter((d) => d.wing === wing.toLowerCase()) : drawers;
179
+ const byRoom = new Map();
180
+ for (const d of filtered) {
181
+ const key = `${d.wing}/${d.room}`;
182
+ const entry = byRoom.get(key) ?? { wing: d.wing, count: 0, lastTouched: d.updatedAt };
183
+ entry.count++;
184
+ if (d.updatedAt > entry.lastTouched)
185
+ entry.lastTouched = d.updatedAt;
186
+ byRoom.set(key, entry);
187
+ }
188
+ return [...byRoom.entries()]
189
+ .map(([key, v]) => ({
190
+ wing: v.wing,
191
+ name: key.split('/')[1],
192
+ drawerCount: v.count,
193
+ lastTouched: v.lastTouched,
194
+ }))
195
+ .sort((a, b) => b.lastTouched.localeCompare(a.lastTouched));
196
+ }
197
+ // ── Tunnels (drawer relationships) ────────────────────
198
+ addTunnel(fromDrawerId, toDrawerId, relation) {
199
+ const tunnel = {
200
+ id: newId('tun'),
201
+ fromDrawerId,
202
+ toDrawerId,
203
+ relation: relation.toLowerCase().trim(),
204
+ createdAt: new Date().toISOString(),
205
+ scope: this.scope,
206
+ };
207
+ const state = this.read();
208
+ state.tunnels.push(tunnel);
209
+ this.write(state);
210
+ return tunnel;
211
+ }
212
+ /**
213
+ * Find all tunnels touching a drawer, in either direction. Useful for
214
+ * "what is this drawer related to?" queries.
215
+ */
216
+ findTunnels(drawerId) {
217
+ const tunnels = this.read().tunnels;
218
+ return {
219
+ outgoing: tunnels.filter((t) => t.fromDrawerId === drawerId),
220
+ incoming: tunnels.filter((t) => t.toDrawerId === drawerId),
221
+ };
222
+ }
223
+ /**
224
+ * Walk outgoing tunnels from a start drawer for up to maxDepth hops,
225
+ * returning every drawer reached and the path that got us there.
226
+ * Bounded by visited-set so cycles don't loop forever.
227
+ */
228
+ traverse(startId, maxDepth = 3) {
229
+ const state = this.read();
230
+ const drawerById = new Map(state.drawers.map((d) => [d.id, d]));
231
+ const tunnelsByFrom = new Map();
232
+ for (const t of state.tunnels) {
233
+ const arr = tunnelsByFrom.get(t.fromDrawerId) ?? [];
234
+ arr.push(t);
235
+ tunnelsByFrom.set(t.fromDrawerId, arr);
236
+ }
237
+ const visited = new Set();
238
+ const results = [];
239
+ const queue = [{ id: startId, depth: 0, via: [] }];
240
+ while (queue.length > 0) {
241
+ const cur = queue.shift();
242
+ if (visited.has(cur.id))
243
+ continue;
244
+ visited.add(cur.id);
245
+ const drawer = drawerById.get(cur.id);
246
+ if (drawer && cur.depth > 0) {
247
+ results.push({ drawer, depth: cur.depth, via: cur.via });
248
+ }
249
+ if (cur.depth >= maxDepth)
250
+ continue;
251
+ const outgoing = tunnelsByFrom.get(cur.id) ?? [];
252
+ for (const t of outgoing) {
253
+ if (!visited.has(t.toDrawerId)) {
254
+ queue.push({ id: t.toDrawerId, depth: cur.depth + 1, via: [...cur.via, t.relation] });
255
+ }
256
+ }
257
+ }
258
+ return results;
259
+ }
260
+ deleteTunnel(id) {
261
+ const state = this.read();
262
+ const before = state.tunnels.length;
263
+ state.tunnels = state.tunnels.filter((t) => t.id !== id);
264
+ const removed = state.tunnels.length < before;
265
+ if (removed)
266
+ this.write(state);
267
+ return removed;
268
+ }
269
+ // ── Knowledge graph triples ───────────────────────────
270
+ addTriple(t) {
271
+ const triple = {
272
+ ...t,
273
+ id: newId('kg'),
274
+ createdAt: new Date().toISOString(),
275
+ scope: this.scope,
276
+ confidence: clamp01(t.confidence ?? 1.0),
277
+ };
278
+ const state = this.read();
279
+ state.triples.push(triple);
280
+ this.write(state);
281
+ return triple;
282
+ }
283
+ /**
284
+ * Query triples with optional s/p/o filters. Any field left undefined
285
+ * acts as a wildcard. Matching is case-insensitive equality on each
286
+ * specified field.
287
+ */
288
+ queryTriples(q) {
289
+ const triples = this.read().triples;
290
+ const lc = (s) => s?.toLowerCase();
291
+ const s = lc(q.subject);
292
+ const p = lc(q.predicate);
293
+ const o = lc(q.object);
294
+ return triples.filter((t) => (s === undefined || t.subject.toLowerCase() === s) &&
295
+ (p === undefined || t.predicate.toLowerCase() === p) &&
296
+ (o === undefined || t.object.toLowerCase() === o));
297
+ }
298
+ /** Most-recent triples first. For "what have I been learning?" views. */
299
+ recentTriples(limit = 20) {
300
+ return [...this.read().triples]
301
+ .sort((a, b) => b.createdAt.localeCompare(a.createdAt))
302
+ .slice(0, limit);
303
+ }
304
+ // ── Search ────────────────────────────────────────────
305
+ search(query, opts = {}) {
306
+ const drawers = this.read().drawers;
307
+ return searchDrawers(drawers, query, opts);
308
+ }
309
+ // ── Stats ─────────────────────────────────────────────
310
+ stats() {
311
+ const state = this.read();
312
+ const wings = new Set(state.drawers.map((d) => d.wing));
313
+ const rooms = new Set(state.drawers.map((d) => `${d.wing}/${d.room}`));
314
+ return {
315
+ drawers: state.drawers.length,
316
+ tunnels: state.tunnels.length,
317
+ triples: state.triples.length,
318
+ wings: wings.size,
319
+ rooms: rooms.size,
320
+ };
321
+ }
322
+ }
323
+ function clamp01(n) {
324
+ if (Number.isNaN(n))
325
+ return 0.5;
326
+ if (n < 0)
327
+ return 0;
328
+ if (n > 1)
329
+ return 1;
330
+ return n;
331
+ }
332
+ //# sourceMappingURL=store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/mempalace/store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,140 @@
1
+ /**
2
+ * MemPalace data model — TypeScript port of the core MemPalace concepts.
3
+ *
4
+ * Mental model:
5
+ *
6
+ * Wing ─ a top-level domain ("projects", "people", "code", "wisdom")
7
+ * Room ─ a category inside a wing ("crowcoder", "abc-reborn", ...)
8
+ * Drawer ─ an individual memory item: a chunk of text + tags + metadata
9
+ * Tunnel ─ a directed link between two drawers, typed by relation
10
+ * Triple ─ a knowledge-graph fact: (subject, predicate, object)
11
+ *
12
+ * Drawers are the atomic unit of memory. Wings + rooms give them addressable
13
+ * structure. Tunnels and KG triples give them relational structure.
14
+ *
15
+ * Two stores live side-by-side:
16
+ * GLOBAL ~/.crowcoder/memory/ cross-project knowledge (user prefs,
17
+ * recurring patterns, skills)
18
+ * PROJECT <cwd>/.crowcoder/memory/ this-codebase-specific (e.g. "build
19
+ * is broken because X", "the queue lives
20
+ * in services/queue/...")
21
+ *
22
+ * The agent picks which store to query based on the request. User-modeling
23
+ * queries hit global; codebase queries hit project. Search APIs accept a
24
+ * Scope hint or 'both' to search across.
25
+ */
26
+ /**
27
+ * Which memory store an operation targets.
28
+ *
29
+ * 'global' — ~/.crowcoder/memory/ (cross-project)
30
+ * 'project' — <cwd>/.crowcoder/memory/ (per-repo)
31
+ * 'both' — search both; writes pick based on content (global for user
32
+ * preferences, project for codebase facts)
33
+ */
34
+ export type Scope = 'global' | 'project' | 'both';
35
+ /**
36
+ * A drawer is one memory item — a chunk of text living at wing/room. Tags
37
+ * are free-form lowercase strings used for filtering. Importance is a 0–1
38
+ * heuristic the agent or user can set to bias search ordering.
39
+ */
40
+ export interface Drawer {
41
+ id: string;
42
+ wing: string;
43
+ room: string;
44
+ content: string;
45
+ tags: string[];
46
+ importance: number;
47
+ createdAt: string;
48
+ updatedAt: string;
49
+ scope: Scope;
50
+ sourceSessionId?: string;
51
+ }
52
+ /**
53
+ * Convenience metadata about a wing — populated by `listWings` for UI/
54
+ * status output. Not stored separately; derived from drawer counts.
55
+ */
56
+ export interface WingMeta {
57
+ name: string;
58
+ rooms: string[];
59
+ drawerCount: number;
60
+ }
61
+ /**
62
+ * Same for rooms.
63
+ */
64
+ export interface RoomMeta {
65
+ wing: string;
66
+ name: string;
67
+ drawerCount: number;
68
+ lastTouched: string;
69
+ }
70
+ /**
71
+ * A tunnel is a directed link from one drawer to another with a labelled
72
+ * relation. Use cases:
73
+ * - "drawer A inspired drawer B" (relation: "inspired")
74
+ * - "drawer A supersedes drawer B" (relation: "supersedes")
75
+ * - "drawer A is part of drawer B's project" (relation: "in-project")
76
+ *
77
+ * Bidirectional traversal is supported by following tunnels in reverse.
78
+ */
79
+ export interface Tunnel {
80
+ id: string;
81
+ fromDrawerId: string;
82
+ toDrawerId: string;
83
+ relation: string;
84
+ createdAt: string;
85
+ scope: Scope;
86
+ }
87
+ /**
88
+ * A knowledge-graph triple — semantic-web style. Stored separately from
89
+ * drawers because triples express *facts* (small, atomic, queryable in
90
+ * aggregate) whereas drawers express *content* (larger, narrative).
91
+ *
92
+ * Example: ("rsfit", "owns", "Crowcoder") or ("compact-agent", "uses",
93
+ * "OpenRouter").
94
+ */
95
+ export interface KGTriple {
96
+ id: string;
97
+ subject: string;
98
+ predicate: string;
99
+ object: string;
100
+ confidence: number;
101
+ sourceDrawerId?: string;
102
+ sourceSessionId?: string;
103
+ createdAt: string;
104
+ scope: Scope;
105
+ }
106
+ /**
107
+ * Internal disk schema. One JSON file per store containing all drawers,
108
+ * tunnels, and triples. Simple, atomic-write-via-rename safe.
109
+ *
110
+ * For very large stores (>10k drawers) this approach will get slow; the
111
+ * intent is to swap to SQLite/FTS5 later behind the same Store interface.
112
+ */
113
+ export interface StoreState {
114
+ schemaVersion: number;
115
+ drawers: Drawer[];
116
+ tunnels: Tunnel[];
117
+ triples: KGTriple[];
118
+ }
119
+ export declare const SCHEMA_VERSION = 1;
120
+ /**
121
+ * Search options shared across query APIs.
122
+ */
123
+ export interface SearchOptions {
124
+ scope?: Scope;
125
+ wing?: string;
126
+ room?: string;
127
+ tags?: string[];
128
+ limit?: number;
129
+ minImportance?: number;
130
+ }
131
+ /**
132
+ * A scored search hit — the drawer plus the relevance score the search
133
+ * function computed. Higher = more relevant. Score is unitless; only
134
+ * meaningful for ordering, not absolute comparison.
135
+ */
136
+ export interface SearchHit {
137
+ drawer: Drawer;
138
+ score: number;
139
+ matchedFields: ('content' | 'tags' | 'wing' | 'room')[];
140
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * MemPalace data model — TypeScript port of the core MemPalace concepts.
3
+ *
4
+ * Mental model:
5
+ *
6
+ * Wing ─ a top-level domain ("projects", "people", "code", "wisdom")
7
+ * Room ─ a category inside a wing ("crowcoder", "abc-reborn", ...)
8
+ * Drawer ─ an individual memory item: a chunk of text + tags + metadata
9
+ * Tunnel ─ a directed link between two drawers, typed by relation
10
+ * Triple ─ a knowledge-graph fact: (subject, predicate, object)
11
+ *
12
+ * Drawers are the atomic unit of memory. Wings + rooms give them addressable
13
+ * structure. Tunnels and KG triples give them relational structure.
14
+ *
15
+ * Two stores live side-by-side:
16
+ * GLOBAL ~/.crowcoder/memory/ cross-project knowledge (user prefs,
17
+ * recurring patterns, skills)
18
+ * PROJECT <cwd>/.crowcoder/memory/ this-codebase-specific (e.g. "build
19
+ * is broken because X", "the queue lives
20
+ * in services/queue/...")
21
+ *
22
+ * The agent picks which store to query based on the request. User-modeling
23
+ * queries hit global; codebase queries hit project. Search APIs accept a
24
+ * Scope hint or 'both' to search across.
25
+ */
26
+ export const SCHEMA_VERSION = 1;
27
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/mempalace/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AA6GH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,CAAC"}
@@ -17,6 +17,14 @@ export async function checkPermission(tool, input, config, rl) {
17
17
  // Read-only tools always allowed
18
18
  if (tool.isReadOnly)
19
19
  return true;
20
+ // Per-tool persistent allowlist. Populated by the "always" answer
21
+ // below. Checked BEFORE the destructive branch so a previous "always"
22
+ // on bash or write_file actually stops the next prompt. The earlier
23
+ // implementation only flipped the global mode to 'auto', which still
24
+ // prompts for tools marked isDestructive — that was the "I keep typing
25
+ // always but it keeps asking" bug.
26
+ if (config.alwaysAllowedTools?.includes(tool.name))
27
+ return true;
20
28
  // auto mode = allow non-destructive, ask for destructive
21
29
  if (config.permissionMode === 'auto' && !tool.isDestructive)
22
30
  return true;
@@ -27,10 +35,15 @@ export async function checkPermission(tool, input, config, rl) {
27
35
  const answer = await rl.question(chalk.yellow('Allow? [Y/n/always] '));
28
36
  const a = answer.trim().toLowerCase();
29
37
  if (a === 'always') {
30
- // Upgrade to auto for rest of session and save to disk
31
- config.permissionMode = 'auto';
38
+ // Persist as a per-tool allow. Old behavior also flipped permissionMode
39
+ // to 'auto', but that only helps non-destructive tools; per-tool list
40
+ // is the actual fix for destructive tools like bash + write_file.
41
+ config.alwaysAllowedTools = config.alwaysAllowedTools || [];
42
+ if (!config.alwaysAllowedTools.includes(tool.name)) {
43
+ config.alwaysAllowedTools.push(tool.name);
44
+ }
32
45
  saveConfig(config);
33
- console.log(chalk.dim(' (auto-approving safe operations for this session and future sessions)'));
46
+ console.log(chalk.dim(` (always-allowing ${tool.name} for this session and future sessions — clear with /perm-reset)`));
34
47
  return true;
35
48
  }
36
49
  return a === '' || a === 'y' || a === 'yes';
@@ -1 +1 @@
1
- {"version":3,"file":"permissions.js","sourceRoot":"","sources":["../src/permissions.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,IAAU,EACV,KAA8B,EAC9B,MAAuB,EACvB,EAAsB;IAEtB,iCAAiC;IACjC,IAAI,MAAM,CAAC,cAAc,KAAK,MAAM;QAAE,OAAO,IAAI,CAAC;IAElD,iCAAiC;IACjC,IAAI,IAAI,CAAC,UAAU;QAAE,OAAO,IAAI,CAAC;IAEjC,yDAAyD;IACzD,IAAI,MAAM,CAAC,cAAc,KAAK,MAAM,IAAI,CAAC,IAAI,CAAC,aAAa;QAAE,OAAO,IAAI,CAAC;IAEzE,qDAAqD;IACrD,MAAM,IAAI,GAAG,cAAc,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IACzC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,aAAa,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IACpD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;IAE7B,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,sBAAsB,CAAC,CAAC,CAAC;IACvE,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAEtC,IAAI,CAAC,KAAK,QAAQ,EAAE,CAAC;QACnB,uDAAuD;QACvD,MAAM,CAAC,cAAc,GAAG,MAAM,CAAC;QAC/B,UAAU,CAAC,MAAM,CAAC,CAAC;QACnB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,yEAAyE,CAAC,CAAC,CAAC;QAClG,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,KAAK,CAAC;AAC9C,CAAC;AAED,SAAS,cAAc,CAAC,IAAU,EAAE,KAA8B;IAChE,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC;QAClB,KAAK,MAAM;YACT,OAAO,OAAO,KAAK,CAAC,OAAO,EAAE,CAAC;QAChC,KAAK,YAAY;YACf,OAAO,eAAe,KAAK,CAAC,SAAS,KAAK,CAAE,KAAK,CAAC,OAAkB,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,SAAS,CAAC;QAC1G,KAAK,WAAW;YACd,OAAO,WAAW,KAAK,CAAC,SAAS,EAAE,CAAC;QACtC;YACE,OAAO,KAAK,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC;IACtD,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"permissions.js","sourceRoot":"","sources":["../src/permissions.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,IAAU,EACV,KAA8B,EAC9B,MAAuB,EACvB,EAAsB;IAEtB,iCAAiC;IACjC,IAAI,MAAM,CAAC,cAAc,KAAK,MAAM;QAAE,OAAO,IAAI,CAAC;IAElD,iCAAiC;IACjC,IAAI,IAAI,CAAC,UAAU;QAAE,OAAO,IAAI,CAAC;IAEjC,kEAAkE;IAClE,sEAAsE;IACtE,oEAAoE;IACpE,qEAAqE;IACrE,uEAAuE;IACvE,mCAAmC;IACnC,IAAI,MAAM,CAAC,kBAAkB,EAAE,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IAEhE,yDAAyD;IACzD,IAAI,MAAM,CAAC,cAAc,KAAK,MAAM,IAAI,CAAC,IAAI,CAAC,aAAa;QAAE,OAAO,IAAI,CAAC;IAEzE,qDAAqD;IACrD,MAAM,IAAI,GAAG,cAAc,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IACzC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,aAAa,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IACpD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;IAE7B,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,sBAAsB,CAAC,CAAC,CAAC;IACvE,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAEtC,IAAI,CAAC,KAAK,QAAQ,EAAE,CAAC;QACnB,wEAAwE;QACxE,sEAAsE;QACtE,kEAAkE;QAClE,MAAM,CAAC,kBAAkB,GAAG,MAAM,CAAC,kBAAkB,IAAI,EAAE,CAAC;QAC5D,IAAI,CAAC,MAAM,CAAC,kBAAkB,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YACnD,MAAM,CAAC,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC5C,CAAC;QACD,UAAU,CAAC,MAAM,CAAC,CAAC;QACnB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,sBAAsB,IAAI,CAAC,IAAI,iEAAiE,CAAC,CAAC,CAAC;QACzH,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,KAAK,CAAC;AAC9C,CAAC;AAED,SAAS,cAAc,CAAC,IAAU,EAAE,KAA8B;IAChE,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC;QAClB,KAAK,MAAM;YACT,OAAO,OAAO,KAAK,CAAC,OAAO,EAAE,CAAC;QAChC,KAAK,YAAY;YACf,OAAO,eAAe,KAAK,CAAC,SAAS,KAAK,CAAE,KAAK,CAAC,OAAkB,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,SAAS,CAAC;QAC1G,KAAK,WAAW;YACd,OAAO,WAAW,KAAK,CAAC,SAAS,EAAE,CAAC;QACtC;YACE,OAAO,KAAK,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC;IACtD,CAAC;AACH,CAAC"}
package/dist/query.js CHANGED
@@ -11,6 +11,7 @@ import { isVoiceEnabled, getTtsConfig, getAccessibilityConfig, speakAssistantRes
11
11
  import { isLikelyDestructive, describeDestructive, countWords, summarize } from './accessibility.js';
12
12
  import { audioCue } from './audio.js';
13
13
  import { setStatus } from './status.js';
14
+ import { collapseCompletedTurns } from './turn-context.js';
14
15
  function startInputSuppression() {
15
16
  const stdin = process.stdin;
16
17
  if (!stdin.isTTY) {
@@ -141,6 +142,13 @@ export async function runQuery(ctx) {
141
142
  // the guard is always cleaned up even if something throws unexpectedly.
142
143
  const inputGuard = startInputSuppression();
143
144
  try {
145
+ // Turn-boundary collapse runs BEFORE compaction. Every completed prior
146
+ // turn becomes [user, "<final text>\n[Completed: used X, Y]"] — the
147
+ // model no longer sees stale tool_calls that it might mistake for
148
+ // pending work (the "I'll handle BOTH requests" / "all THREE requests"
149
+ // bug). The current turn (latest user message forward) is left intact
150
+ // because its tool_calls and tool messages are still in flight.
151
+ ctx.messages = collapseCompletedTurns(ctx.messages);
144
152
  // Auto-compact if context is getting large
145
153
  if (shouldCompact(ctx.messages, DEFAULT_COMPACTION)) {
146
154
  console.log(theme.dim(` ${sym.running} auto-compacting conversation context...`));