@nexrall/code-core 1.4.6 → 1.4.8

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.
@@ -1 +1 @@
1
- {"version":3,"file":"loop.d.ts","sourceRoot":"","sources":["../../src/agent/loop.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,OAAO,EAMP,gBAAgB,EAChB,UAAU,EACX,MAAM,UAAU,CAAC;AAsKlB,wBAAgB,oBAAoB,CAClC,WAAW,EAAE,MAAM,GAAG,SAAS,EAC/B,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACnC,MAAM,CAWR;AA8RD,+EAA+E;AAC/E,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,MAAM,CAM7D;AAsBD,iFAAiF;AACjF,eAAO,MAAM,gBAAgB,aAA+G,CAAC;AAC7I,gGAAgG;AAChG,eAAO,MAAM,aAAa,QAA2J,CAAC;AAEtL;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAK5E;AAUD;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,MAAM,CAwBxD;AAoBD,MAAM,WAAW,cAAc;IAC7B,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC3D,aAAa,EAAE,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,OAAO,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAClE,qFAAqF;IACrF,aAAa,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACvD;;;;OAIG;IACH,KAAK,EAAE,MAAM,CAAC;CACf;AAED,wBAAgB,YAAY,IAAI,cAAc,CAE7C;AAED,kFAAkF;AAClF,wBAAgB,YAAY,CAC1B,MAAM,EAAE,cAAc,EACtB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,EAC1C,EAAE,EAAE,OAAO,EACX,MAAM,CAAC,EAAE,MAAM,EACf,QAAQ,CAAC,EAAE,MAAM,GAChB,IAAI,CAoDN;AAED,kFAAkF;AAClF,wBAAgB,aAAa,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,CA6B5D;AAmBD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,MAAM,CAmB/D;AA+GD,gFAAgF;AAChF,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,MAAM,CAE/D;AAED;;;;;;;;;GASG;AACH,wBAAsB,wBAAwB,CAC5C,QAAQ,EAAE,OAAO,EAAE,EACnB,IAAI,EAAE;IACJ,KAAK,CAAC,EAAE,OAAO,GAAG,KAAK,GAAG,OAAO,CAAC;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,GAAG,CAAC,EAAE,UAAU,CAAC;IACjB,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CACnC,GACA,OAAO,CAAC,OAAO,CAAC,CA2DlB;AAID,wBAAsB,YAAY,CAChC,eAAe,EAAE,OAAO,EAAE,EAC1B,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC,OAAO,EAAE,CAAC,CAulBpB"}
1
+ {"version":3,"file":"loop.d.ts","sourceRoot":"","sources":["../../src/agent/loop.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,OAAO,EAMP,gBAAgB,EAChB,UAAU,EACX,MAAM,UAAU,CAAC;AAsKlB,wBAAgB,oBAAoB,CAClC,WAAW,EAAE,MAAM,GAAG,SAAS,EAC/B,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACnC,MAAM,CAWR;AA8RD,+EAA+E;AAC/E,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,MAAM,CAM7D;AAsBD,iFAAiF;AACjF,eAAO,MAAM,gBAAgB,aAA+G,CAAC;AAC7I,gGAAgG;AAChG,eAAO,MAAM,aAAa,QAA2J,CAAC;AAEtL;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAK5E;AAUD;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,MAAM,CAwBxD;AAoBD,MAAM,WAAW,cAAc;IAC7B,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC3D,aAAa,EAAE,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,OAAO,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAClE,qFAAqF;IACrF,aAAa,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACvD;;;;OAIG;IACH,KAAK,EAAE,MAAM,CAAC;CACf;AAED,wBAAgB,YAAY,IAAI,cAAc,CAE7C;AAED,kFAAkF;AAClF,wBAAgB,YAAY,CAC1B,MAAM,EAAE,cAAc,EACtB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,EAC1C,EAAE,EAAE,OAAO,EACX,MAAM,CAAC,EAAE,MAAM,EACf,QAAQ,CAAC,EAAE,MAAM,GAChB,IAAI,CAoDN;AAED,kFAAkF;AAClF,wBAAgB,aAAa,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,CA6B5D;AAmBD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,MAAM,CAmB/D;AAsHD,gFAAgF;AAChF,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,MAAM,CAE/D;AAED;;;;;;;;;GASG;AACH,wBAAsB,wBAAwB,CAC5C,QAAQ,EAAE,OAAO,EAAE,EACnB,IAAI,EAAE;IACJ,KAAK,CAAC,EAAE,OAAO,GAAG,KAAK,GAAG,OAAO,CAAC;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,GAAG,CAAC,EAAE,UAAU,CAAC;IACjB,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CACnC,GACA,OAAO,CAAC,OAAO,CAAC,CA2DlB;AAID,wBAAsB,YAAY,CAChC,eAAe,EAAE,OAAO,EAAE,EAC1B,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC,OAAO,EAAE,CAAC,CAulBpB"}
@@ -736,7 +736,14 @@ async function autoCompactMessages(messages, options, ledger) {
736
736
  let summary = '';
737
737
  try {
738
738
  const reply = await (0, client_1.streamChat)([{ role: 'user', content: [{ type: 'text', text: summaryPrompt }] }], {
739
- model: options.model ?? 'turbo',
739
+ // COST: the summariser is a mechanical "bullet-point this transcript" task —
740
+ // its quality is indistinguishable across model tiers, so there is no reason
741
+ // to run it on the user's (possibly expensive) tier. Force 'turbo' (Sonnet 5):
742
+ // it is the cheapest 1M-context tier (in $2.0/1M vs Opus $2.5, Fable $3.0;
743
+ // out $15/1M vs $25 / $50), so this is always ≤ the user's cost, and its 1M
744
+ // window comfortably holds the transcript (capped at ~170K tokens by
745
+ // transcriptOf) even on the largest sessions.
746
+ model: 'turbo',
740
747
  mode: 'ask', // summariser must not call tools; ask-mode discourages action
741
748
  env: options.env,
742
749
  clientType: options.clientType,
@@ -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;AAw9DtE,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,GAClB,OAAO,CAAC,UAAU,CAAC,CAiBrB"}
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;AAk/DtE,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,GAClB,OAAO,CAAC,UAAU,CAAC,CAiBrB"}
@@ -1390,6 +1390,19 @@ async function fetchUrl(input, _workDir, _redirectCount = 0) {
1390
1390
  return callback(null, address, family);
1391
1391
  });
1392
1392
  });
1393
+ // Guard against double-resolve and — critically — against our OWN intentional
1394
+ // req.destroy() (used to stop downloading once we hit MAX_FETCH_BYTES or to
1395
+ // hand off a redirect) surfacing as an ECONNRESET on res/req 'error'. Without
1396
+ // this flag, aborting the socket ourselves would reject with "socket hang up"
1397
+ // and the truncated-but-valid body we already collected would be thrown away.
1398
+ let settled = false;
1399
+ let selfAborted = false;
1400
+ const finish = (result) => {
1401
+ if (settled)
1402
+ return;
1403
+ settled = true;
1404
+ resolve(result);
1405
+ };
1393
1406
  const req = transport.get(url, {
1394
1407
  timeout: DEFAULT_TIMEOUT_MS,
1395
1408
  lookup: guardedLookup,
@@ -1402,6 +1415,7 @@ async function fetchUrl(input, _workDir, _redirectCount = 0) {
1402
1415
  // Follow redirects (301/302/307/308) with depth counter to prevent infinite loops
1403
1416
  const status = res.statusCode ?? 0;
1404
1417
  if ((status === 301 || status === 302 || status === 307 || status === 308) && res.headers.location) {
1418
+ selfAborted = true; // our destroy() below is intentional — ignore the reset it triggers
1405
1419
  req.destroy();
1406
1420
  // Resolve relative Location headers against the current URL
1407
1421
  let nextUrl;
@@ -1409,10 +1423,10 @@ async function fetchUrl(input, _workDir, _redirectCount = 0) {
1409
1423
  nextUrl = new URL(res.headers.location, url).href;
1410
1424
  }
1411
1425
  catch {
1412
- resolve({ error: `Invalid redirect location: ${res.headers.location}` });
1426
+ finish({ error: `Invalid redirect location: ${res.headers.location}` });
1413
1427
  return;
1414
1428
  }
1415
- fetchUrl({ url: nextUrl }, undefined, _redirectCount + 1).then(resolve);
1429
+ fetchUrl({ url: nextUrl }, undefined, _redirectCount + 1).then(finish);
1416
1430
  return;
1417
1431
  }
1418
1432
  const contentType = res.headers['content-type'] ?? '';
@@ -1420,6 +1434,19 @@ async function fetchUrl(input, _workDir, _redirectCount = 0) {
1420
1434
  const chunks = [];
1421
1435
  let totalBytes = 0;
1422
1436
  let truncated = false;
1437
+ // Emit whatever we've collected so far (used by both the clean 'end' and the
1438
+ // size-limit truncation path — the two must produce identical output shape).
1439
+ const emitBody = () => {
1440
+ let body = Buffer.concat(chunks).toString('utf-8');
1441
+ if (isHtml)
1442
+ body = stripHtml(body);
1443
+ const truncNote = truncated ? `\n\n[Truncated at ${MAX_FETCH_BYTES / 1024}KB]` : '';
1444
+ // Prompt-injection mitigation: fetched web content is fully attacker-controlled
1445
+ // (anyone can put "ignore previous instructions..." on a page). Wrap it in an
1446
+ // explicit untrusted-data delimiter so the model treats it as DATA to read, not
1447
+ // as instructions to follow — defense-in-depth alongside the system-prompt rule.
1448
+ finish({ output: `HTTP ${status}\n\n<untrusted_web_content url="${url}">\n${body}${truncNote}\n</untrusted_web_content>` });
1449
+ };
1423
1450
  res.on('data', (chunk) => {
1424
1451
  if (totalBytes + chunk.length > MAX_FETCH_BYTES) {
1425
1452
  const remaining = MAX_FETCH_BYTES - totalBytes;
@@ -1427,30 +1454,30 @@ async function fetchUrl(input, _workDir, _redirectCount = 0) {
1427
1454
  chunks.push(chunk.subarray(0, remaining));
1428
1455
  totalBytes = MAX_FETCH_BYTES;
1429
1456
  truncated = true;
1457
+ // We deliberately stop here. Mark the abort as self-inflicted so the
1458
+ // error handlers don't turn the resulting ECONNRESET into a failure,
1459
+ // then emit the (valid, truncated) body we already have.
1460
+ selfAborted = true;
1430
1461
  req.destroy();
1462
+ emitBody();
1431
1463
  }
1432
1464
  else {
1433
1465
  chunks.push(chunk);
1434
1466
  totalBytes += chunk.length;
1435
1467
  }
1436
1468
  });
1437
- res.on('end', () => {
1438
- let body = Buffer.concat(chunks).toString('utf-8');
1439
- if (isHtml)
1440
- body = stripHtml(body);
1441
- const truncNote = truncated ? `\n\n[Truncated at ${MAX_FETCH_BYTES / 1024}KB]` : '';
1442
- // Prompt-injection mitigation: fetched web content is fully attacker-controlled
1443
- // (anyone can put "ignore previous instructions..." on a page). Wrap it in an
1444
- // explicit untrusted-data delimiter so the model treats it as DATA to read, not
1445
- // as instructions to follow — defense-in-depth alongside the system-prompt rule.
1446
- resolve({ output: `HTTP ${status}\n\n<untrusted_web_content url="${url}">\n${body}${truncNote}\n</untrusted_web_content>` });
1447
- });
1448
- res.on('error', (err) => resolve({ error: err.message }));
1469
+ res.on('end', emitBody);
1470
+ // Ignore errors caused by our own intentional destroy (truncation / redirect);
1471
+ // only real transport failures should surface.
1472
+ res.on('error', (err) => { if (!selfAborted)
1473
+ finish({ error: err.message }); });
1449
1474
  });
1450
- req.on('error', (err) => resolve({ error: err.message }));
1475
+ req.on('error', (err) => { if (!selfAborted)
1476
+ finish({ error: err.message }); });
1451
1477
  req.on('timeout', () => {
1478
+ selfAborted = true;
1452
1479
  req.destroy();
1453
- resolve({ error: `Request timed out after ${DEFAULT_TIMEOUT_MS}ms` });
1480
+ finish({ error: `Request timed out after ${DEFAULT_TIMEOUT_MS}ms` });
1454
1481
  });
1455
1482
  });
1456
1483
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nexrall/code-core",
3
- "version": "1.4.6",
3
+ "version": "1.4.8",
4
4
  "description": "Core agent loop, tools, and extension primitives for Nexrall Code — embed an AI coding agent in any Node.js application.",
5
5
  "license": "MIT",
6
6
  "author": "Nexrall <support@nexrall.com> (https://nexrall.com)",