@nexrall/code-core 1.4.63 → 1.4.65

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 (45) hide show
  1. package/dist/agent/agentRegistry.d.ts +14 -3
  2. package/dist/agent/agentRegistry.d.ts.map +1 -1
  3. package/dist/agent/agentRegistry.js +136 -6
  4. package/dist/agent/agentTypes.d.ts +51 -1
  5. package/dist/agent/agentTypes.d.ts.map +1 -1
  6. package/dist/agent/agentTypes.js +194 -11
  7. package/dist/agent/backgroundAgents.d.ts +66 -0
  8. package/dist/agent/backgroundAgents.d.ts.map +1 -0
  9. package/dist/agent/backgroundAgents.js +145 -0
  10. package/dist/agent/loop.d.ts +43 -6
  11. package/dist/agent/loop.d.ts.map +1 -1
  12. package/dist/agent/loop.js +942 -259
  13. package/dist/agent/modelCatalogue.d.ts +15 -0
  14. package/dist/agent/modelCatalogue.d.ts.map +1 -1
  15. package/dist/agent/modelCatalogue.js +46 -0
  16. package/dist/agent/readDedupe.d.ts +15 -0
  17. package/dist/agent/readDedupe.d.ts.map +1 -0
  18. package/dist/agent/readDedupe.js +146 -0
  19. package/dist/agent/toolPrefetch.d.ts +44 -0
  20. package/dist/agent/toolPrefetch.d.ts.map +1 -0
  21. package/dist/agent/toolPrefetch.js +101 -0
  22. package/dist/agent/trust.d.ts +0 -5
  23. package/dist/agent/trust.d.ts.map +1 -1
  24. package/dist/agent/trust.js +41 -0
  25. package/dist/api/client.d.ts +14 -0
  26. package/dist/api/client.d.ts.map +1 -1
  27. package/dist/api/client.js +88 -8
  28. package/dist/index.d.ts +1 -0
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.js +1 -0
  31. package/dist/permissions/modePolicy.d.ts +10 -0
  32. package/dist/permissions/modePolicy.d.ts.map +1 -1
  33. package/dist/permissions/modePolicy.js +11 -0
  34. package/dist/tools/executor.d.ts +36 -0
  35. package/dist/tools/executor.d.ts.map +1 -1
  36. package/dist/tools/executor.js +408 -85
  37. package/dist/tools/tsLangService.d.ts.map +1 -1
  38. package/dist/tools/tsLangService.js +107 -21
  39. package/dist/types.d.ts +99 -5
  40. package/dist/types.d.ts.map +1 -1
  41. package/dist/types.js +12 -1
  42. package/dist/util/miniYaml.d.ts +10 -0
  43. package/dist/util/miniYaml.d.ts.map +1 -0
  44. package/dist/util/miniYaml.js +149 -0
  45. package/package.json +8 -17
@@ -5,6 +5,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.API_BASE = void 0;
7
7
  exports.chooseFinalContent = chooseFinalContent;
8
+ exports.earlyToolResolveEnabled = earlyToolResolveEnabled;
9
+ exports.gzipRequestsEnabled = gzipRequestsEnabled;
8
10
  exports.streamChat = streamChat;
9
11
  exports.cancelTurn = cancelTurn;
10
12
  exports.revokeRefreshToken = revokeRefreshToken;
@@ -21,6 +23,7 @@ exports.getUserProfile = getUserProfile;
21
23
  const eventsource_parser_1 = require("eventsource-parser");
22
24
  const node_fetch_1 = __importDefault(require("node-fetch"));
23
25
  const crypto_1 = require("crypto");
26
+ const zlib_1 = require("zlib");
24
27
  const index_1 = require("../auth/index");
25
28
  // ─── Constants ────────────────────────────────────────────────────────────────
26
29
  const DEFAULT_API_BASE = 'https://api.nexrall.com';
@@ -216,6 +219,20 @@ function isExpiredTokenResponse(status, body) {
216
219
  const MAX_RETRIES = 5;
217
220
  const RETRY_BASE_MS = 1000;
218
221
  const RETRY_MAX_MS = 30000;
222
+ /**
223
+ * Whether a tool_use round may return as soon as message_complete + usage arrive,
224
+ * instead of waiting for the trailing `done` (which the backend only sends after its
225
+ * billing DB write). NEXRALL_EARLY_TOOL_RESOLVE=0 restores the old wait.
226
+ */
227
+ function earlyToolResolveEnabled() {
228
+ return process.env.NEXRALL_EARLY_TOOL_RESOLVE !== '0';
229
+ }
230
+ /** Chat bodies smaller than this are sent uncompressed. */
231
+ const GZIP_MIN_BYTES = 32 * 1024;
232
+ /** NEXRALL_GZIP_REQUESTS=0 sends chat bodies uncompressed. */
233
+ function gzipRequestsEnabled() {
234
+ return process.env.NEXRALL_GZIP_REQUESTS !== '0';
235
+ }
219
236
  /**
220
237
  * SSE frames that are backend HOUSEKEEPING, not model output.
221
238
  *
@@ -249,6 +266,7 @@ const RETRY_MAX_MS = 30000;
249
266
  */
250
267
  const HOUSEKEEPING_FRAMES = new Set([
251
268
  'resumable',
269
+ 'runtime_context',
252
270
  'context_warning',
253
271
  'tool_progress',
254
272
  ]);
@@ -373,7 +391,7 @@ function sleep(ms) {
373
391
  * the nested product (36) exists to bound pathological cases, not to be reached. */
374
392
  const MAX_TOTAL_ATTEMPTS = (MAX_RETRIES + 1) * (MAX_RETRIES + 1);
375
393
  async function streamChat(messages, options, onEvent) {
376
- const { model, env, editorContext, nexrallMd, mode, effort, clientType, abortSignal, extraTools, agents, skills, allowRestartAfterRender, canWriteSharedMemory, hasOwnAgentStore, canSpawnSubAgents, canBrowseWeb } = options;
394
+ const { model, env, editorContext, nexrallMd, mode, effort, clientType, abortSignal, extraTools, agents, skills, allowRestartAfterRender, canWriteSharedMemory, hasOwnAgentStore, canSpawnSubAgents, canBrowseWeb, toolAllowlist } = options;
377
395
  // IDEMPOTENCY KEY for this logical turn, generated ONCE and reused by every retry
378
396
  // below (that reuse is the entire point — a fresh id per attempt would be useless).
379
397
  //
@@ -449,6 +467,26 @@ async function streamChat(messages, options, onEvent) {
449
467
  // chained one so the header wait can be timed out independently. It stays because
450
468
  // this shape is also what an unwrapped fetch would need, and a request with no
451
469
  // signal at all would silently become uncancellable if that wrapper were removed.
470
+ // The chat body is serialised (and, when large, gzipped) ONCE per streamChat call and
471
+ // reused by every retry: the payload is identical across attempts, and re-stringifying
472
+ // a multi-MB history per retry is pure waste. Gzip is only applied above a threshold —
473
+ // below it, the CPU cost outweighs the uplink saving. Every Nexrall API host parses
474
+ // bodies with express.json(), which inflates `Content-Encoding: gzip` natively
475
+ // (verified against production). NEXRALL_GZIP_REQUESTS=0 disables it.
476
+ // Keyed by turnId: a restart mints a NEW turnId (prepareRestart), and the cached body
477
+ // must not carry the old one. The billing override is read per call for the same reason
478
+ // the body used to be rebuilt per attempt — it cannot change mid-turn in practice, and a
479
+ // restart (the only path long enough for it to change) rebuilds anyway.
480
+ let chatBody = null;
481
+ const getChatBody = () => {
482
+ if (chatBody && chatBody.turnId === turnId)
483
+ return chatBody;
484
+ const json = JSON.stringify({ messages, model, env, editorContext, nexrallMd, mode, effort, clientType, extraTools, agents, skills, turnId, canWriteSharedMemory, hasOwnAgentStore, canSpawnSubAgents, canBrowseWeb, toolAllowlist, ...billingOverrideFields() });
485
+ chatBody = gzipRequestsEnabled() && json.length >= GZIP_MIN_BYTES
486
+ ? { data: (0, zlib_1.gzipSync)(json, { level: 5 }), gzip: true, turnId }
487
+ : { data: json, gzip: false, turnId };
488
+ return chatBody;
489
+ };
452
490
  const buildFetchArgs = () => {
453
491
  const signal = controller.signal;
454
492
  if (resuming) {
@@ -463,12 +501,15 @@ async function streamChat(messages, options, onEvent) {
463
501
  }
464
502
  return [
465
503
  `${exports.API_BASE}/api/code/chat`,
466
- {
467
- method: 'POST',
468
- headers: authHeaders(),
469
- body: JSON.stringify({ messages, model, env, editorContext, nexrallMd, mode, effort, clientType, extraTools, agents, skills, turnId, canWriteSharedMemory, hasOwnAgentStore, canSpawnSubAgents, canBrowseWeb, ...billingOverrideFields() }),
470
- signal,
471
- },
504
+ (() => {
505
+ const body = getChatBody();
506
+ return {
507
+ method: 'POST',
508
+ headers: body.gzip ? { ...authHeaders(), 'Content-Encoding': 'gzip' } : authHeaders(),
509
+ body: body.data,
510
+ signal,
511
+ };
512
+ })(),
472
513
  ];
473
514
  };
474
515
  // A transient mid-stream failure (Anthropic "overloaded_error") arrives as an SSE error
@@ -1274,6 +1315,28 @@ async function streamChat(messages, options, onEvent) {
1274
1315
  cache_read_input_tokens: typeof evt.cache_read_input_tokens === 'number' ? evt.cache_read_input_tokens : undefined,
1275
1316
  },
1276
1317
  });
1318
+ // EARLY RESOLVE ON TOOL ROUNDS. The backend emits message_complete +
1319
+ // usage synchronously, then awaits its billing write (Postgres + Redis)
1320
+ // before sending `done`. For a tool_use turn nothing after this point is
1321
+ // needed to run the tools, so waiting for `done` put a DB round-trip on
1322
+ // the critical path of EVERY tool round. Return now; the socket keeps
1323
+ // draining in the background, so a late balance_status / done / error
1324
+ // still reaches onEvent. Only tool_use: a final (end_turn) answer keeps
1325
+ // waiting so the low-balance nudge lands before the turn is reported done.
1326
+ if (evt.partial !== true &&
1327
+ completedMessage?.stopReason === 'tool_use' &&
1328
+ earlyToolResolveEnabled()) {
1329
+ clearInterval(heartbeatWatchdog);
1330
+ // The watchdog is gone, so bound the background drain ourselves: a
1331
+ // backend that never sends `done` must not pin the socket forever.
1332
+ const drainGuard = setTimeout(() => {
1333
+ stream.destroy?.();
1334
+ }, HEARTBEAT_TIMEOUT_MS);
1335
+ drainGuard.unref?.();
1336
+ stream.once('end', () => clearTimeout(drainGuard));
1337
+ stream.once('close', () => clearTimeout(drainGuard));
1338
+ resolve();
1339
+ }
1277
1340
  }
1278
1341
  break;
1279
1342
  }
@@ -1326,6 +1389,23 @@ async function streamChat(messages, options, onEvent) {
1326
1389
  serverResumable = true;
1327
1390
  break;
1328
1391
  }
1392
+ case 'tool_use_ready': {
1393
+ // A complete tool_use block, sent before the message finishes (see
1394
+ // SSEToolUseReadyEvent). Forwarded as-is; the loop decides what to prefetch.
1395
+ if (typeof evt.id === 'string' && typeof evt.name === 'string') {
1396
+ const input = evt.input && typeof evt.input === 'object' ? evt.input : {};
1397
+ onEvent({ type: 'tool_use_ready', id: evt.id, name: evt.name, input });
1398
+ }
1399
+ break;
1400
+ }
1401
+ case 'runtime_context': {
1402
+ // The exact context block the backend appended to our final message. The
1403
+ // agent loop persists it so the next round carries it in the cached history.
1404
+ if (typeof evt.text === 'string' && evt.text) {
1405
+ onEvent({ type: 'runtime_context', text: evt.text });
1406
+ }
1407
+ break;
1408
+ }
1329
1409
  case 'balance_status': {
1330
1410
  const balance = typeof evt.balance === 'number' ? evt.balance : 0;
1331
1411
  const zero = !!evt.zero;
@@ -1780,7 +1860,7 @@ async function getCodeModels() {
1780
1860
  const data = (await response.json());
1781
1861
  return {
1782
1862
  models: Array.isArray(data.models) ? data.models : [],
1783
- defaultModel: typeof data.defaultModel === 'string' ? data.defaultModel : 'claude-sonnet-5',
1863
+ defaultModel: typeof data.defaultModel === 'string' ? data.defaultModel : 'deepseek-flash',
1784
1864
  };
1785
1865
  }
1786
1866
  /**
package/dist/index.d.ts CHANGED
@@ -22,6 +22,7 @@ export * from './commands/loader';
22
22
  export * from './agent/agentTypes';
23
23
  export * from './agent/planMode';
24
24
  export * from './agent/agentRegistry';
25
+ export * from './agent/backgroundAgents';
25
26
  export * from './permissions/rules';
26
27
  export * from './permissions/modePolicy';
27
28
  export * from './permissions/bashClassify';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,kBAAkB,CAAC;AACjC,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,kBAAkB,CAAC;AACjC,cAAc,cAAc,CAAC;AAC7B,cAAc,wBAAwB,CAAC;AACvC,cAAc,uBAAuB,CAAC;AACtC,cAAc,0BAA0B,CAAC;AACzC,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAClC,cAAc,eAAe,CAAC;AAC9B,cAAc,uBAAuB,CAAC;AACtC,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,cAAc,CAAC;AAC7B,cAAc,kBAAkB,CAAC;AACjC,cAAc,eAAe,CAAC;AAC9B,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC;AACjC,cAAc,uBAAuB,CAAC;AACtC,cAAc,qBAAqB,CAAC;AACpC,cAAc,0BAA0B,CAAC;AACzC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,iBAAiB,CAAC;AAChC,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC;AAClC,cAAc,eAAe,CAAC;AAC9B,cAAc,0BAA0B,CAAC;AACzC,cAAc,kBAAkB,CAAC;AACjC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,qBAAqB,CAAC;AACpC,cAAc,sBAAsB,CAAC;AACrC,cAAc,uBAAuB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,kBAAkB,CAAC;AACjC,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,kBAAkB,CAAC;AACjC,cAAc,cAAc,CAAC;AAC7B,cAAc,wBAAwB,CAAC;AACvC,cAAc,uBAAuB,CAAC;AACtC,cAAc,0BAA0B,CAAC;AACzC,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAClC,cAAc,eAAe,CAAC;AAC9B,cAAc,uBAAuB,CAAC;AACtC,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,cAAc,CAAC;AAC7B,cAAc,kBAAkB,CAAC;AACjC,cAAc,eAAe,CAAC;AAC9B,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC;AACjC,cAAc,uBAAuB,CAAC;AACtC,cAAc,0BAA0B,CAAC;AACzC,cAAc,qBAAqB,CAAC;AACpC,cAAc,0BAA0B,CAAC;AACzC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,iBAAiB,CAAC;AAChC,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC;AAClC,cAAc,eAAe,CAAC;AAC9B,cAAc,0BAA0B,CAAC;AACzC,cAAc,kBAAkB,CAAC;AACjC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,qBAAqB,CAAC;AACpC,cAAc,sBAAsB,CAAC;AACrC,cAAc,uBAAuB,CAAC"}
package/dist/index.js CHANGED
@@ -38,6 +38,7 @@ __exportStar(require("./commands/loader"), exports);
38
38
  __exportStar(require("./agent/agentTypes"), exports);
39
39
  __exportStar(require("./agent/planMode"), exports);
40
40
  __exportStar(require("./agent/agentRegistry"), exports);
41
+ __exportStar(require("./agent/backgroundAgents"), exports);
41
42
  __exportStar(require("./permissions/rules"), exports);
42
43
  __exportStar(require("./permissions/modePolicy"), exports);
43
44
  __exportStar(require("./permissions/bashClassify"), exports);
@@ -18,6 +18,16 @@ export interface PolicyInput {
18
18
  * in two windows. Note `memory_write` is NOT here even though the CLI's old list had it
19
19
  * — it mutates durable cross-session state, and listing it under "read-only / safe"
20
20
  * meant it silently bypassed plan mode's read-only guarantee.
21
+ *
22
+ * `task` IS here on purpose, to stay in lockstep with `planMode.ts`'s own
23
+ * READ_ONLY_TOOLS: that module already lets `task` through its plan-mode lock
24
+ * because delegating research to a sub-agent is safe — `runAgentLoop` passes
25
+ * `planMode` down, so the sub-agent is under the identical read-only lock and
26
+ * its own write tools are refused one level deeper. Without `task` here too,
27
+ * step 3 of `decide()` (`mode === 'plan' → deny`) would silently re-block a
28
+ * call that `checkPlanMode()` had already deliberately allowed through — the
29
+ * exact "two copies that drift" failure this module exists to prevent, just
30
+ * hitting `task` specifically instead of the read-only tools it was written for.
21
31
  */
22
32
  export declare const READ_ONLY_TOOLS: Set<string>;
23
33
  /** Tools that write to the filesystem. */
@@ -1 +1 @@
1
- {"version":3,"file":"modePolicy.d.ts","sourceRoot":"","sources":["../../src/permissions/modePolicy.ts"],"names":[],"mappings":"AAsBA,yDAAyD;AACzD,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,CAAC;AAE9D,kDAAkD;AAClD,MAAM,MAAM,cAAc,GACtB,OAAO,GACP,KAAK,GACL,MAAM,GACN,SAAS,CAAC;AAEd,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,IAAI,EAAE,cAAc,CAAC;IACrB,oFAAoF;IACpF,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,2EAA2E;IAC3E,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,eAAe,aAU1B,CAAC;AAEH,0CAA0C;AAC1C,eAAO,MAAM,gBAAgB,aAG3B,CAAC;AAEH,oEAAoE;AACpE,eAAO,MAAM,iBAAiB,aAE5B,CAAC;AAEH,sCAAsC;AACtC,eAAO,MAAM,UAAU,aAAsD,CAAC;AAE9E;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAEvD;AAED;;;;;;;GAOG;AACH,wBAAgB,MAAM,CAAC,GAAG,EAAE,WAAW,GAAG,cAAc,CAwDvD;AAED,4DAA4D;AAC5D,wBAAgB,YAAY,CAAC,IAAI,EAAE,cAAc,GAAG,MAAM,CAOzD;AAED,gFAAgF;AAChF,wBAAgB,SAAS,CAAC,GAAG,EAAE,OAAO,GAAG,cAAc,CAGtD"}
1
+ {"version":3,"file":"modePolicy.d.ts","sourceRoot":"","sources":["../../src/permissions/modePolicy.ts"],"names":[],"mappings":"AAsBA,yDAAyD;AACzD,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,CAAC;AAE9D,kDAAkD;AAClD,MAAM,MAAM,cAAc,GACtB,OAAO,GACP,KAAK,GACL,MAAM,GACN,SAAS,CAAC;AAEd,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,IAAI,EAAE,cAAc,CAAC;IACrB,oFAAoF;IACpF,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,2EAA2E;IAC3E,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,eAAe,aAW1B,CAAC;AAEH,0CAA0C;AAC1C,eAAO,MAAM,gBAAgB,aAG3B,CAAC;AAEH,oEAAoE;AACpE,eAAO,MAAM,iBAAiB,aAE5B,CAAC;AAEH,sCAAsC;AACtC,eAAO,MAAM,UAAU,aAAsD,CAAC;AAE9E;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAEvD;AAED;;;;;;;GAOG;AACH,wBAAgB,MAAM,CAAC,GAAG,EAAE,WAAW,GAAG,cAAc,CAwDvD;AAED,4DAA4D;AAC5D,wBAAgB,YAAY,CAAC,IAAI,EAAE,cAAc,GAAG,MAAM,CAOzD;AAED,gFAAgF;AAChF,wBAAgB,SAAS,CAAC,GAAG,EAAE,OAAO,GAAG,cAAc,CAGtD"}
@@ -13,6 +13,16 @@ const bashClassify_1 = require("./bashClassify");
13
13
  * in two windows. Note `memory_write` is NOT here even though the CLI's old list had it
14
14
  * — it mutates durable cross-session state, and listing it under "read-only / safe"
15
15
  * meant it silently bypassed plan mode's read-only guarantee.
16
+ *
17
+ * `task` IS here on purpose, to stay in lockstep with `planMode.ts`'s own
18
+ * READ_ONLY_TOOLS: that module already lets `task` through its plan-mode lock
19
+ * because delegating research to a sub-agent is safe — `runAgentLoop` passes
20
+ * `planMode` down, so the sub-agent is under the identical read-only lock and
21
+ * its own write tools are refused one level deeper. Without `task` here too,
22
+ * step 3 of `decide()` (`mode === 'plan' → deny`) would silently re-block a
23
+ * call that `checkPlanMode()` had already deliberately allowed through — the
24
+ * exact "two copies that drift" failure this module exists to prevent, just
25
+ * hitting `task` specifically instead of the read-only tools it was written for.
16
26
  */
17
27
  exports.READ_ONLY_TOOLS = new Set([
18
28
  'read_file', 'list_directory', 'search_files', 'glob', 'grep',
@@ -24,6 +34,7 @@ exports.READ_ONLY_TOOLS = new Set([
24
34
  'notebook_read',
25
35
  'get_diagnostics', 'go_to_definition', 'find_references',
26
36
  'get_symbols', 'get_workspace_symbols', 'get_hover',
37
+ 'task',
27
38
  ]);
28
39
  /** Tools that write to the filesystem. */
29
40
  exports.FILE_WRITE_TOOLS = new Set([
@@ -1,5 +1,41 @@
1
1
  import type { ToolResult } from '../types';
2
2
  import { type SandboxConfig } from './sandbox';
3
+ /**
4
+ * Cap output from a tool we do not control (MCP servers). Built-in tools all cap their
5
+ * own output, but an MCP result went into the conversation verbatim: one call to a
6
+ * DB/browser/log server could inline megabytes, which is then re-sent (and re-billed as
7
+ * cache writes) on every later round until compaction. Keep HEAD+TAIL inline and spill
8
+ * the full text to a temp file the model can read_file with offset/limit — the same
9
+ * contract as bash's GAP B spill.
10
+ */
11
+ export declare function capExternalOutput(output: string, label: string, maxChars?: number): string;
12
+ /**
13
+ * Async drop-in for the subset of `spawnSync` the search tools use. spawnSync blocked
14
+ * the Node event loop for the whole search: several search_files calls issued in one
15
+ * parallel tool batch ran strictly one after another, and a search started by the
16
+ * mid-stream prefetcher froze SSE parsing and UI rendering until it finished. Result
17
+ * shape mirrors spawnSync's (`status`, `signal`, `error` with code ETIMEDOUT / ENOBUFS)
18
+ * so the callers' existing failure handling is unchanged.
19
+ */
20
+ export interface CaptureResult {
21
+ stdout: string;
22
+ stderr: string;
23
+ status: number | null;
24
+ signal: NodeJS.Signals | null;
25
+ error?: NodeJS.ErrnoException;
26
+ }
27
+ export declare function runCapture(cmd: string, args: string[], opts: {
28
+ cwd?: string;
29
+ timeout: number;
30
+ maxBuffer: number;
31
+ }): Promise<CaptureResult>;
32
+ /**
33
+ * Glob → anchored RegExp. Scans the pattern once, so the `*` / `?` rules can never
34
+ * rewrite the regex emitted for `**` (the old chained .replace() calls turned
35
+ * `**\/` into `([^/]:[^/]+/)` — every `**` pattern silently matched nothing).
36
+ * Supports `**`, `*`, `?`, `{a,b}` and `[...]` classes.
37
+ */
38
+ export declare function globToRegex(pattern: string): RegExp;
3
39
  export declare function executeTool(name: string, input: Record<string, unknown>, abortSignal?: {
4
40
  aborted: boolean;
5
41
  }, sandbox?: SandboxConfig, workDir?: string, agentScope?: string, onStream?: (chunk: string) => void, selfPeer?: {
@@ -1 +1 @@
1
- {"version":3,"file":"executor.d.ts","sourceRoot":"","sources":["../../src/tools/executor.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAC3C,OAAO,EAAyB,KAAK,aAAa,EAAE,MAAM,WAAW,CAAC;AAqxFtE,wBAAsB,WAAW,CAC/B,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,WAAW,CAAC,EAAE;IAAE,OAAO,EAAE,OAAO,CAAA;CAAE,EAClC,OAAO,CAAC,EAAE,aAAa,EACvB,OAAO,CAAC,EAAE,MAAM,EAChB,UAAU,CAAC,EAAE,MAAM,EACnB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,EAMlC,QAAQ,CAAC,EAAE;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACtC,OAAO,CAAC,UAAU,CAAC,CAkCrB"}
1
+ {"version":3,"file":"executor.d.ts","sourceRoot":"","sources":["../../src/tools/executor.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAC3C,OAAO,EAAyB,KAAK,aAAa,EAAE,MAAM,WAAW,CAAC;AAokCtE;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,SAAmB,GAAG,MAAM,CAcpG;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC,OAAO,GAAG,IAAI,CAAC;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC,cAAc,CAAC;CAC/B;AACD,wBAAgB,UAAU,CACxB,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,EAAE,EACd,IAAI,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GACzD,OAAO,CAAC,aAAa,CAAC,CA0CxB;AAsoCD;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAqCnD;AAwwBD,wBAAsB,WAAW,CAC/B,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,WAAW,CAAC,EAAE;IAAE,OAAO,EAAE,OAAO,CAAA;CAAE,EAClC,OAAO,CAAC,EAAE,aAAa,EACvB,OAAO,CAAC,EAAE,MAAM,EAChB,UAAU,CAAC,EAAE,MAAM,EACnB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,EAMlC,QAAQ,CAAC,EAAE;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACtC,OAAO,CAAC,UAAU,CAAC,CAkCrB"}