@nexrall/code-core 1.4.66 → 1.4.67

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 (75) hide show
  1. package/dist/agent/agentTypes.d.ts +6 -2
  2. package/dist/agent/agentTypes.d.ts.map +1 -1
  3. package/dist/agent/agentTypes.js +13 -4
  4. package/dist/agent/askOnce.d.ts +12 -0
  5. package/dist/agent/askOnce.d.ts.map +1 -0
  6. package/dist/agent/askOnce.js +41 -0
  7. package/dist/agent/compaction.d.ts +244 -0
  8. package/dist/agent/compaction.d.ts.map +1 -0
  9. package/dist/agent/compaction.js +976 -0
  10. package/dist/agent/fileLocks.d.ts +34 -0
  11. package/dist/agent/fileLocks.d.ts.map +1 -0
  12. package/dist/agent/fileLocks.js +114 -0
  13. package/dist/agent/hooks.d.ts +324 -0
  14. package/dist/agent/hooks.d.ts.map +1 -0
  15. package/dist/agent/hooks.js +1228 -0
  16. package/dist/agent/iterationPolicy.d.ts +121 -0
  17. package/dist/agent/iterationPolicy.d.ts.map +1 -0
  18. package/dist/agent/iterationPolicy.js +297 -0
  19. package/dist/agent/lifecycleHost.d.ts +55 -0
  20. package/dist/agent/lifecycleHost.d.ts.map +1 -0
  21. package/dist/agent/lifecycleHost.js +294 -0
  22. package/dist/agent/loop.d.ts +11 -491
  23. package/dist/agent/loop.d.ts.map +1 -1
  24. package/dist/agent/loop.js +417 -3031
  25. package/dist/agent/planMode.d.ts.map +1 -1
  26. package/dist/agent/planMode.js +1 -0
  27. package/dist/agent/sharedTasks.d.ts +7 -0
  28. package/dist/agent/sharedTasks.d.ts.map +1 -1
  29. package/dist/agent/sharedTasks.js +16 -0
  30. package/dist/agent/subAgentBudget.d.ts +65 -0
  31. package/dist/agent/subAgentBudget.d.ts.map +1 -0
  32. package/dist/agent/subAgentBudget.js +269 -0
  33. package/dist/agent/subTask.d.ts +6 -0
  34. package/dist/agent/subTask.d.ts.map +1 -0
  35. package/dist/agent/subTask.js +713 -0
  36. package/dist/agent/subTaskSupport.d.ts +156 -0
  37. package/dist/agent/subTaskSupport.d.ts.map +1 -0
  38. package/dist/agent/subTaskSupport.js +409 -0
  39. package/dist/agent/toolDescriptions.d.ts +3 -0
  40. package/dist/agent/toolDescriptions.d.ts.map +1 -0
  41. package/dist/agent/toolDescriptions.js +116 -0
  42. package/dist/api/client.d.ts.map +1 -1
  43. package/dist/api/client.js +17 -0
  44. package/dist/index.d.ts +3 -0
  45. package/dist/index.d.ts.map +1 -1
  46. package/dist/index.js +3 -0
  47. package/dist/mcp/client.d.ts +104 -0
  48. package/dist/mcp/client.d.ts.map +1 -1
  49. package/dist/mcp/client.js +136 -2
  50. package/dist/mcp/httpClient.d.ts +17 -1
  51. package/dist/mcp/httpClient.d.ts.map +1 -1
  52. package/dist/mcp/httpClient.js +120 -19
  53. package/dist/mcp/manager.d.ts +77 -2
  54. package/dist/mcp/manager.d.ts.map +1 -1
  55. package/dist/mcp/manager.js +275 -9
  56. package/dist/mcp/sseClient.d.ts +7 -1
  57. package/dist/mcp/sseClient.d.ts.map +1 -1
  58. package/dist/mcp/sseClient.js +45 -1
  59. package/dist/mcp/stats.d.ts +41 -0
  60. package/dist/mcp/stats.d.ts.map +1 -0
  61. package/dist/mcp/stats.js +108 -0
  62. package/dist/permissions/destructive.d.ts +2 -0
  63. package/dist/permissions/destructive.d.ts.map +1 -1
  64. package/dist/permissions/destructive.js +6 -2
  65. package/dist/permissions/destructiveTokens.d.ts +5 -0
  66. package/dist/permissions/destructiveTokens.d.ts.map +1 -1
  67. package/dist/permissions/destructiveTokens.js +9 -3
  68. package/dist/permissions/modePolicy.d.ts.map +1 -1
  69. package/dist/permissions/modePolicy.js +5 -2
  70. package/dist/permissions/rules.d.ts +4 -1
  71. package/dist/permissions/rules.d.ts.map +1 -1
  72. package/dist/permissions/rules.js +29 -0
  73. package/dist/types.d.ts +45 -1
  74. package/dist/types.d.ts.map +1 -1
  75. package/package.json +1 -1
@@ -0,0 +1,294 @@
1
+ "use strict";
2
+ // Host-driven lifecycle hooks — the two events the AGENT LOOP cannot fire itself:
3
+ //
4
+ // • FileChanged — a watched file in the working directory changed on disk.
5
+ // The loop has no watcher; the host (CLI / VS Code) starts one.
6
+ // • InstructionsLoaded — instruction files (nexrall.md, memory) were read. Only the
7
+ // host knows which files those were and why they were read.
8
+ //
9
+ // Both use the same config, payload and exit-code protocol as every other hook; see
10
+ // loop.ts. Both are OBSERVERS: a hook can inform the user, it cannot veto anything —
11
+ // the file already changed, the file is already loaded.
12
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
13
+ if (k2 === undefined) k2 = k;
14
+ var desc = Object.getOwnPropertyDescriptor(m, k);
15
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
16
+ desc = { enumerable: true, get: function() { return m[k]; } };
17
+ }
18
+ Object.defineProperty(o, k2, desc);
19
+ }) : (function(o, m, k, k2) {
20
+ if (k2 === undefined) k2 = k;
21
+ o[k2] = m[k];
22
+ }));
23
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
24
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
25
+ }) : function(o, v) {
26
+ o["default"] = v;
27
+ });
28
+ var __importStar = (this && this.__importStar) || (function () {
29
+ var ownKeys = function(o) {
30
+ ownKeys = Object.getOwnPropertyNames || function (o) {
31
+ var ar = [];
32
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
33
+ return ar;
34
+ };
35
+ return ownKeys(o);
36
+ };
37
+ return function (mod) {
38
+ if (mod && mod.__esModule) return mod;
39
+ var result = {};
40
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
41
+ __setModuleDefault(result, mod);
42
+ return result;
43
+ };
44
+ })();
45
+ Object.defineProperty(exports, "__esModule", { value: true });
46
+ exports.fireInstructionsLoadedHooks = fireInstructionsLoadedHooks;
47
+ exports.startFileChangedWatcher = startFileChangedWatcher;
48
+ exports.filesChangedSinceSnapshot = filesChangedSinceSnapshot;
49
+ const fs = __importStar(require("fs"));
50
+ const path = __importStar(require("path"));
51
+ const loop_1 = require("./loop");
52
+ /**
53
+ * Tell the InstructionsLoaded hooks which instruction files this session loaded.
54
+ * Observer: output and exit code are ignored (Claude Code says the same for this event).
55
+ *
56
+ * Called by the host at the moment the files are actually read — a `--bare` run reads
57
+ * none and therefore fires nothing, which is the honest behaviour.
58
+ */
59
+ async function fireInstructionsLoadedHooks(workDir, sessionId, files) {
60
+ if (!files.length)
61
+ return;
62
+ try {
63
+ const items = (0, loop_1.loadHooks)(workDir).InstructionsLoaded;
64
+ if (!items?.length)
65
+ return;
66
+ for (const f of files) {
67
+ await (0, loop_1.runLifecycleHooks)(items, 'InstructionsLoaded', workDir, {
68
+ session_id: sessionId,
69
+ file_path: f.file_path,
70
+ memory_type: f.memory_type,
71
+ load_reason: f.load_reason,
72
+ ...(f.globs ? { globs: f.globs } : {}),
73
+ }, f.load_reason).catch(() => undefined);
74
+ }
75
+ }
76
+ catch { /* a hook must never break startup */ }
77
+ }
78
+ /** Only basenames in the working directory can be watched — a matcher is a filename, not a path. */
79
+ function isWatchableName(name) {
80
+ // Claude Code is narrower still (letters, digits, _, |); we allow the characters a real
81
+ // filename needs and drop anything that tries to be a path or a glob.
82
+ return !!name && !name.includes('/') && !name.includes('\\') && !name.includes('*') && !name.includes('?') && name !== '.' && name !== '..';
83
+ }
84
+ /**
85
+ * Start watching the files named by the FileChanged hooks' matchers ("a.txt|b.txt").
86
+ *
87
+ * - Returns null when no FileChanged hook is configured, so callers can wire it
88
+ * unconditionally.
89
+ * - `fs.watch` on the WORKING DIRECTORY (not the files themselves: editors replace
90
+ * files, which kills a per-file watch on rename) filtered to the watched basenames,
91
+ * with a short per-file debounce — one save is several fs events.
92
+ * - A hook's `{"watchPaths": […]}` answer replaces the dynamic list; `{"systemMessage": "…"}`
93
+ * is handed to `onSystemMessage` for the host to display.
94
+ * - Never throws into the host: watching is best-effort (an OS without inotify, a
95
+ * directory that vanishes) and a broken watch must not break the session.
96
+ */
97
+ function startFileChangedWatcher(workDir, sessionId, opts) {
98
+ let items;
99
+ try {
100
+ items = (0, loop_1.loadHooks)(workDir).FileChanged;
101
+ }
102
+ catch {
103
+ items = undefined;
104
+ }
105
+ if (!items?.length)
106
+ return null;
107
+ // Static names: every literal in every matcher. Flat handlers contribute none — a flat
108
+ // handler with no matcher watches nothing until some hook returns a watchPaths list.
109
+ const staticNames = new Set();
110
+ for (const it of items) {
111
+ if (it && Array.isArray(it.hooks)) {
112
+ const m = it.matcher;
113
+ if (!m || m === '*')
114
+ continue;
115
+ for (const raw of m.split('|')) {
116
+ const name = raw.trim();
117
+ if (isWatchableName(name))
118
+ staticNames.add(name);
119
+ }
120
+ }
121
+ }
122
+ if (!staticNames.size)
123
+ return null;
124
+ const dynamicNames = new Set();
125
+ const watched = () => new Set([...staticNames, ...dynamicNames]);
126
+ // Who existed when we last looked. fs.watch's eventType is NOT an add/change oracle:
127
+ // on macOS a plain overwrite arrives as 'rename', so distinguishing by eventType
128
+ // labelled every save an "add". Existence across events is the honest signal, and it
129
+ // needs a seed — a file already present when the watcher starts is not "added" later.
130
+ const known = new Set();
131
+ const seedKnown = (names) => {
132
+ for (const n of names) {
133
+ try {
134
+ if (fs.existsSync(path.join(workDir, n)))
135
+ known.add(n);
136
+ }
137
+ catch { /* best-effort */ }
138
+ }
139
+ };
140
+ seedKnown(staticNames);
141
+ // A cheap identity for "this file changed": mtime + size. Used by the ARMING RECONCILE
142
+ // below, which exists because fs.watch arms ASYNCHRONOUSLY — the FSEvents/FSEventStream
143
+ // stream starts on the next tick, and an event that lands before it is armed is not
144
+ // queued, it is GONE. An editor that saves the instant the session starts is exactly
145
+ // that case (measured: under load the write was lost outright, the hook never ran).
146
+ const stamp = (name) => {
147
+ try {
148
+ const st = fs.statSync(path.join(workDir, name));
149
+ return `${st.mtimeMs}:${st.size}`;
150
+ }
151
+ catch {
152
+ return '';
153
+ }
154
+ };
155
+ const seen = new Map();
156
+ const snapshot = (names) => { for (const n of names)
157
+ seen.set(n, stamp(n)); };
158
+ snapshot(staticNames);
159
+ const pending = new Map();
160
+ let closed = false;
161
+ /** Arm the per-file debounce — shared by real fs events and by the arming reconcile. */
162
+ const scheduleFire = (name) => {
163
+ const prev = pending.get(name);
164
+ if (prev)
165
+ clearTimeout(prev);
166
+ pending.set(name, setTimeout(() => {
167
+ pending.delete(name);
168
+ let exists = true;
169
+ try {
170
+ exists = fs.existsSync(path.join(workDir, name));
171
+ }
172
+ catch {
173
+ exists = false;
174
+ }
175
+ const event = !exists ? 'unlink' : known.has(name) ? 'change' : 'add';
176
+ if (exists)
177
+ known.add(name);
178
+ else
179
+ known.delete(name);
180
+ seen.set(name, stamp(name));
181
+ void fireChanged(workDir, sessionId, name, event, items, dynamicNames.has(name)).then((o) => {
182
+ if (!o)
183
+ return;
184
+ if (o.systemMessage && opts?.onSystemMessage) {
185
+ try {
186
+ opts.onSystemMessage(o.systemMessage);
187
+ }
188
+ catch { /* host callback */ }
189
+ }
190
+ if (o.watchPaths) {
191
+ // watchPaths REPLACES the dynamic list (Claude Code semantics); the static
192
+ // matcher names always stay watched — they are what activated this watcher.
193
+ dynamicNames.clear();
194
+ for (const p of o.watchPaths) {
195
+ const n = isWatchableName(path.basename(p)) ? path.basename(p) : '';
196
+ if (n)
197
+ dynamicNames.add(n);
198
+ }
199
+ seedKnown(dynamicNames);
200
+ snapshot(dynamicNames);
201
+ }
202
+ }).catch(() => { });
203
+ }, 250));
204
+ };
205
+ let watcher = null;
206
+ try {
207
+ watcher = fs.watch(workDir, { persistent: false }, (_eventType, filename) => {
208
+ if (closed)
209
+ return;
210
+ const name = filename ? path.basename(String(filename)) : '';
211
+ if (!name || !watched().has(name))
212
+ return;
213
+ // Debounce per file: a single save reaches us as create+change+change on some
214
+ // platforms, and running the hook per raw event would fire it 3×.
215
+ scheduleFire(name);
216
+ });
217
+ // A watcher that failed to attach must not hold a dead object.
218
+ watcher.on('error', () => { });
219
+ }
220
+ catch {
221
+ return null;
222
+ }
223
+ // Arming reconcile (see `stamp` above): one look after the stream has had time to arm,
224
+ // to catch a write the stream was not yet listening for. It reuses scheduleFire, so a
225
+ // file caught by BOTH paths still fires exactly once (the pending timer is re-armed,
226
+ // not duplicated).
227
+ const armGraceMs = opts?.armGraceMs ?? 400;
228
+ const reconcileTimer = armGraceMs > 0
229
+ ? setTimeout(() => {
230
+ if (closed)
231
+ return;
232
+ for (const n of filesChangedSinceSnapshot(watched(), seen, stamp))
233
+ scheduleFire(n);
234
+ }, armGraceMs)
235
+ : null;
236
+ return {
237
+ stop() {
238
+ closed = true;
239
+ if (reconcileTimer)
240
+ clearTimeout(reconcileTimer);
241
+ for (const t of pending.values())
242
+ clearTimeout(t);
243
+ pending.clear();
244
+ try {
245
+ watcher?.close();
246
+ }
247
+ catch { /* already closed */ }
248
+ watcher = null;
249
+ },
250
+ updateWatchPaths(paths) {
251
+ dynamicNames.clear();
252
+ for (const p of paths) {
253
+ const n = isWatchableName(path.basename(p)) ? path.basename(p) : '';
254
+ if (n)
255
+ dynamicNames.add(n);
256
+ }
257
+ seedKnown(dynamicNames);
258
+ snapshot(dynamicNames);
259
+ },
260
+ watchedFiles() { return [...watched()].sort(); },
261
+ };
262
+ }
263
+ /**
264
+ * Which watched files differ from the snapshot taken when the watcher armed. This is the
265
+ * whole substance of the arming reconcile: fs.watch arms asynchronously, so a write that
266
+ * lands before the stream is listening is not queued — it is gone. Exported so the
267
+ * comparison can be tested without racing an OS event.
268
+ */
269
+ function filesChangedSinceSnapshot(names, seen, stamp) {
270
+ const out = [];
271
+ for (const n of names)
272
+ if (stamp(n) !== (seen.get(n) ?? ''))
273
+ out.push(n);
274
+ return out;
275
+ }
276
+ /** Fire the FileChanged hooks for one file, returning the merged outcome (or null on any failure). */
277
+ async function fireChanged(workDir, sessionId, name, event, items, isDynamic) {
278
+ const abs = path.join(workDir, name);
279
+ try {
280
+ // A file admitted through watchPaths had no matcher naming it, so no matcher may
281
+ // filter it either — every FileChanged group hears about it. A static matcher file
282
+ // runs only the groups that named it.
283
+ const o = await (0, loop_1.runLifecycleHooks)(items, 'FileChanged', workDir, {
284
+ session_id: sessionId,
285
+ file_path: abs,
286
+ event,
287
+ }, isDynamic ? undefined : name);
288
+ return { watchPaths: o.watchPaths, systemMessage: o.systemMessage };
289
+ }
290
+ catch {
291
+ return null;
292
+ }
293
+ }
294
+ //# sourceMappingURL=lifecycleHost.js.map