agentfootprint 9.76.1 → 9.78.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.
Files changed (105) hide show
  1. package/CHANGELOG.md +188 -0
  2. package/CLAUDE.md +2 -0
  3. package/dist/core/Agent.js +86 -0
  4. package/dist/core/Agent.js.map +1 -1
  5. package/dist/core/agent/integrityFindings.js +47 -0
  6. package/dist/core/agent/integrityFindings.js.map +1 -0
  7. package/dist/core/agent/stages/callLLM.js +28 -29
  8. package/dist/core/agent/stages/callLLM.js.map +1 -1
  9. package/dist/core/agent/stages/toolCalls.js +184 -0
  10. package/dist/core/agent/stages/toolCalls.js.map +1 -1
  11. package/dist/core/tools.js +9 -1
  12. package/dist/core/tools.js.map +1 -1
  13. package/dist/esm/core/Agent.d.ts +10 -0
  14. package/dist/esm/core/Agent.js +86 -0
  15. package/dist/esm/core/Agent.js.map +1 -1
  16. package/dist/esm/core/agent/integrityFindings.d.ts +27 -0
  17. package/dist/esm/core/agent/integrityFindings.js +43 -0
  18. package/dist/esm/core/agent/integrityFindings.js.map +1 -0
  19. package/dist/esm/core/agent/stages/callLLM.d.ts +25 -0
  20. package/dist/esm/core/agent/stages/callLLM.js +25 -26
  21. package/dist/esm/core/agent/stages/callLLM.js.map +1 -1
  22. package/dist/esm/core/agent/stages/toolCalls.d.ts +43 -0
  23. package/dist/esm/core/agent/stages/toolCalls.js +185 -1
  24. package/dist/esm/core/agent/stages/toolCalls.js.map +1 -1
  25. package/dist/esm/core/agent/types.d.ts +112 -0
  26. package/dist/esm/core/tools.d.ts +63 -0
  27. package/dist/esm/core/tools.js +17 -0
  28. package/dist/esm/core/tools.js.map +1 -1
  29. package/dist/esm/index.d.ts +3 -0
  30. package/dist/esm/index.js +18 -0
  31. package/dist/esm/index.js.map +1 -1
  32. package/dist/esm/integrity/argumentLeaves.d.ts +33 -0
  33. package/dist/esm/integrity/argumentLeaves.js +49 -0
  34. package/dist/esm/integrity/argumentLeaves.js.map +1 -0
  35. package/dist/esm/integrity/column-types/check.d.ts +156 -0
  36. package/dist/esm/integrity/column-types/check.js +363 -0
  37. package/dist/esm/integrity/column-types/check.js.map +1 -0
  38. package/dist/esm/integrity/column-types/types.d.ts +121 -0
  39. package/dist/esm/integrity/column-types/types.js +107 -0
  40. package/dist/esm/integrity/column-types/types.js.map +1 -0
  41. package/dist/esm/integrity/disposition/lifecycle.d.ts +31 -1
  42. package/dist/esm/integrity/disposition/lifecycle.js +54 -1
  43. package/dist/esm/integrity/disposition/lifecycle.js.map +1 -1
  44. package/dist/esm/integrity/empty-lookup/check.d.ts +140 -0
  45. package/dist/esm/integrity/empty-lookup/check.js +212 -0
  46. package/dist/esm/integrity/empty-lookup/check.js.map +1 -0
  47. package/dist/esm/integrity/finding/types.d.ts +23 -2
  48. package/dist/esm/integrity/finding/types.js.map +1 -1
  49. package/dist/esm/integrity/unsupported-argument/check.js +11 -27
  50. package/dist/esm/integrity/unsupported-argument/check.js.map +1 -1
  51. package/dist/esm/lib/mcp/toolExtras.d.ts +17 -2
  52. package/dist/esm/lib/mcp/toolExtras.js +5 -2
  53. package/dist/esm/lib/mcp/toolExtras.js.map +1 -1
  54. package/dist/esm/lib/trace-toolpack/traceToolpack.js +8 -4
  55. package/dist/esm/lib/trace-toolpack/traceToolpack.js.map +1 -1
  56. package/dist/index.js +26 -3
  57. package/dist/index.js.map +1 -1
  58. package/dist/integrity/argumentLeaves.js +54 -0
  59. package/dist/integrity/argumentLeaves.js.map +1 -0
  60. package/dist/integrity/column-types/check.js +368 -0
  61. package/dist/integrity/column-types/check.js.map +1 -0
  62. package/dist/integrity/column-types/types.js +112 -0
  63. package/dist/integrity/column-types/types.js.map +1 -0
  64. package/dist/integrity/disposition/lifecycle.js +54 -1
  65. package/dist/integrity/disposition/lifecycle.js.map +1 -1
  66. package/dist/integrity/empty-lookup/check.js +217 -0
  67. package/dist/integrity/empty-lookup/check.js.map +1 -0
  68. package/dist/integrity/finding/types.js.map +1 -1
  69. package/dist/integrity/unsupported-argument/check.js +13 -29
  70. package/dist/integrity/unsupported-argument/check.js.map +1 -1
  71. package/dist/lib/mcp/toolExtras.js +4 -1
  72. package/dist/lib/mcp/toolExtras.js.map +1 -1
  73. package/dist/lib/trace-toolpack/traceToolpack.js +8 -4
  74. package/dist/lib/trace-toolpack/traceToolpack.js.map +1 -1
  75. package/dist/types/core/Agent.d.ts +10 -0
  76. package/dist/types/core/Agent.d.ts.map +1 -1
  77. package/dist/types/core/agent/integrityFindings.d.ts +28 -0
  78. package/dist/types/core/agent/integrityFindings.d.ts.map +1 -0
  79. package/dist/types/core/agent/stages/callLLM.d.ts +25 -0
  80. package/dist/types/core/agent/stages/callLLM.d.ts.map +1 -1
  81. package/dist/types/core/agent/stages/toolCalls.d.ts +43 -0
  82. package/dist/types/core/agent/stages/toolCalls.d.ts.map +1 -1
  83. package/dist/types/core/agent/types.d.ts +112 -0
  84. package/dist/types/core/agent/types.d.ts.map +1 -1
  85. package/dist/types/core/tools.d.ts +63 -0
  86. package/dist/types/core/tools.d.ts.map +1 -1
  87. package/dist/types/index.d.ts +3 -0
  88. package/dist/types/index.d.ts.map +1 -1
  89. package/dist/types/integrity/argumentLeaves.d.ts +34 -0
  90. package/dist/types/integrity/argumentLeaves.d.ts.map +1 -0
  91. package/dist/types/integrity/column-types/check.d.ts +157 -0
  92. package/dist/types/integrity/column-types/check.d.ts.map +1 -0
  93. package/dist/types/integrity/column-types/types.d.ts +122 -0
  94. package/dist/types/integrity/column-types/types.d.ts.map +1 -0
  95. package/dist/types/integrity/disposition/lifecycle.d.ts +31 -1
  96. package/dist/types/integrity/disposition/lifecycle.d.ts.map +1 -1
  97. package/dist/types/integrity/empty-lookup/check.d.ts +141 -0
  98. package/dist/types/integrity/empty-lookup/check.d.ts.map +1 -0
  99. package/dist/types/integrity/finding/types.d.ts +23 -2
  100. package/dist/types/integrity/finding/types.d.ts.map +1 -1
  101. package/dist/types/integrity/unsupported-argument/check.d.ts.map +1 -1
  102. package/dist/types/lib/mcp/toolExtras.d.ts +17 -2
  103. package/dist/types/lib/mcp/toolExtras.d.ts.map +1 -1
  104. package/dist/types/lib/trace-toolpack/traceToolpack.d.ts.map +1 -1
  105. package/package.json +1 -1
@@ -73,6 +73,9 @@ const resultCeiling_js_1 = require("../resultCeiling.js");
73
73
  const skillSteps_js_1 = require("../../../lib/injection-engine/skillSteps.js");
74
74
  const envelope_js_1 = require("../../../lib/semantics/envelope.js");
75
75
  const ledger_js_2 = require("../../../integrity/unsupported-claim/ledger.js");
76
+ const check_js_1 = require("../../../integrity/empty-lookup/check.js");
77
+ const check_js_2 = require("../../../integrity/column-types/check.js");
78
+ const integrityFindings_js_1 = require("../integrityFindings.js");
76
79
  const index_js_1 = require("../coverage/index.js");
77
80
  const toolEffects_js_1 = require("../toolEffects.js");
78
81
  /** Declaration order preserved, ids de-duplicated — the shape the gate's re-prompt
@@ -315,6 +318,73 @@ function buildToolCallsHandler(deps) {
315
318
  });
316
319
  return verdict.refusal;
317
320
  };
321
+ /**
322
+ * THE COLUMN-TYPE CONTRACT (9.78.0) — the tool declared what its rows
323
+ * contain, and this is where the rows are.
324
+ *
325
+ * Placed at the SAME execute boundaries as `refuseOverCeiling` and for the
326
+ * same reason: `resultColumns` is the TOOL AUTHOR's contract on the tool's
327
+ * OWN return, so it is judged the moment the handler's value lands, before
328
+ * governance, before placement, before the cap — and a resumed call is
329
+ * judged exactly as an inline one.
330
+ *
331
+ * AFTER the ceiling, deliberately. A payload the model will never read has
332
+ * no columns worth arguing about, and judging one that was already refused
333
+ * would file findings about rows nobody was ever served.
334
+ *
335
+ * `undefined` = nothing declared, nothing to refuse, or `'warn'` — the
336
+ * caller keeps today's path byte for byte. A string is the teaching
337
+ * refusal, which under `'enforce'` replaces the payload on every channel,
338
+ * exactly as the ceiling's does.
339
+ *
340
+ * The three field failures this exists for: LUN 0 stored as an empty string
341
+ * on 2,094 mappings; an 8 MiB disk rendered `0.0 GB`; a whole tool family
342
+ * returning its numbers as quoted strings. This catches the first and the
343
+ * third. It cannot catch the second — that is MEANING, and the ceiling
344
+ * sentence in every finding says so.
345
+ */
346
+ const judgeColumns = (scope, call, value) => {
347
+ const declarations = deps.columnDeclarations;
348
+ if (declarations === undefined)
349
+ return undefined;
350
+ const columns = declarations.get(call.toolName);
351
+ if (columns === undefined)
352
+ return undefined;
353
+ const { findings, disposition, refusal } = (0, check_js_2.columnTypesOf)({
354
+ toolName: call.toolName,
355
+ toolCallId: call.toolCallId,
356
+ columns,
357
+ reading: (0, check_js_2.readRowset)(value),
358
+ mode: deps.columnCheckMode ?? 'warn',
359
+ }, call.iteration);
360
+ // ONE encounter, TWO rows. The declaration arms both checks and a call
361
+ // meets both at once, so both must answer for it — a shape that is not a
362
+ // rowset is `not-applicable` to each, and a clean rowset is a
363
+ // `checked-pass` for each. Filing one row would leave the other check
364
+ // looking untouched, which is the rot signal, not the truth.
365
+ const ledger = deps.integrityLedger?.current;
366
+ for (const kind of ['column-type-mismatch', 'missing-column']) {
367
+ const kindFired = findings.some((f) => f.kind === kind);
368
+ // A rowset this check could read, where THIS kind found nothing, is a
369
+ // pass for THIS kind even when its sibling failed: the columns really
370
+ // were all present, or really did all hold their declared type.
371
+ const row = disposition === 'not-applicable'
372
+ ? 'not-applicable'
373
+ : kindFired
374
+ ? 'checked-fail'
375
+ : 'checked-pass';
376
+ ledger?.note(kind, 'write', row, kindFired ? Date.now() : undefined);
377
+ }
378
+ // The findings ARE the record of a refusal: each one names what was
379
+ // refused, which column, how many rows, and — through `outcomeClause` —
380
+ // that the model read a sentence instead of the rows. Deliberately NOT
381
+ // `tools.result_refused`: that event's whole vocabulary is a SIZE
382
+ // (`sizeChars`, `maxChars`), and reusing it here would put a fabricated
383
+ // ceiling on the record to describe a refusal that had nothing to do
384
+ // with size.
385
+ (0, integrityFindings_js_1.fileIntegrityFindings)(scope, findings, call.iteration);
386
+ return refusal;
387
+ };
318
388
  /**
319
389
  * Coverage declarations — `absent(…)` and `coverage(…)`, recognized at the
320
390
  * SAME execute boundaries the ceiling is measured at (one implementation,
@@ -385,6 +455,75 @@ function buildToolCallsHandler(deps) {
385
455
  scope.coverageDeclared = [...(scope.coverageDeclared ?? []), ...rows];
386
456
  return reading.status;
387
457
  };
458
+ /**
459
+ * THE WRITE SEAM (9.77.0) — `empty-lookup`, at the one moment a lookup's
460
+ * answer becomes a fact in the conversation.
461
+ *
462
+ * The recorded failure this exists for: a reverse-lookup tool filtered a
463
+ * column before a pivot, so EVERY lookup returned an empty result, for
464
+ * every identifier, always — and the agent reported, with confidence and in
465
+ * a table, that the device was logged in nowhere. It was logged in the
466
+ * whole time. An empty result from a broken filter is byte-identical to an
467
+ * empty result from a genuine absence, so nothing anywhere could notice.
468
+ *
469
+ * What CAN be noticed is the pair: the run itself produced this identifier
470
+ * (it came out of a result from a tool this one's author named in
471
+ * `argumentsFrom`), and the lookup keyed on it came back empty. The finding
472
+ * is an ADVISORY and says so — an empty answer can be perfectly true, and
473
+ * this cannot tell which one it is looking at.
474
+ *
475
+ * Judged on the TOOL'S OWN answer, before the after-tool chain rewrites it
476
+ * for the model: whether the lookup found anything is a fact about the
477
+ * tool, not about what a governance rule then did with it. A refused,
478
+ * placed or capped result reads as bespoke anyway — a claim ticket has no
479
+ * rows to count.
480
+ */
481
+ const noticeEmptyLookup = (scope, call, args, result, history,
482
+ /** The call produced a real result to read (ran, no error, nothing refused it). */
483
+ answered) => {
484
+ const grounding = deps.emptyLookupGrounding;
485
+ if (grounding === undefined)
486
+ return;
487
+ const argumentsFrom = grounding.get(call.toolName);
488
+ if (argumentsFrom === undefined)
489
+ return;
490
+ const ledger = deps.integrityLedger?.current;
491
+ // A call that never ran, was denied, errored or had its payload refused
492
+ // produced no lookup result to read. Not a pass, not a fail — the check
493
+ // met its subject and the subject is out of scope BY RULE, which is what
494
+ // `not-applicable` is for. Stated, never silence.
495
+ if (!answered) {
496
+ ledger?.note('empty-lookup', 'write', 'not-applicable');
497
+ return;
498
+ }
499
+ // The producer corpus: every earlier `role: 'tool'` message from a tool
500
+ // THIS tool declares as one of its grounds. `history` here excludes the
501
+ // call being judged (it is pushed after), and a producer called earlier
502
+ // in the same batch is already in it — which is right: the model can only
503
+ // have taken a value from a result it had already been served.
504
+ const produced = [];
505
+ for (const message of history) {
506
+ if (message.role !== 'tool')
507
+ continue;
508
+ const name = message.toolName;
509
+ if (typeof name !== 'string' || !argumentsFrom.includes(name))
510
+ continue;
511
+ produced.push({ toolName: name, text: message.content });
512
+ }
513
+ const { findings, disposition } = (0, check_js_1.emptyLookupOf)({
514
+ toolName: call.toolName,
515
+ toolCallId: call.toolCallId,
516
+ args,
517
+ argumentsFrom,
518
+ // `readAbsence` is the ONE owner of the `af_absent` marker; the pure
519
+ // check is handed its verdict rather than re-deriving it, so
520
+ // `src/integrity/` stays a leaf and the reserved word keeps one
521
+ // spelling.
522
+ reading: (0, check_js_1.readLookupResult)(result, (0, index_js_1.readAbsence)(result) !== undefined),
523
+ }, produced, call.iteration);
524
+ ledger?.note('empty-lookup', 'write', disposition, disposition === 'checked-fail' ? Date.now() : undefined);
525
+ (0, integrityFindings_js_1.fileIntegrityFindings)(scope, findings, call.iteration);
526
+ };
388
527
  /**
389
528
  * The semantic envelope (9.53.0) — recognized at the SAME execute
390
529
  * boundaries as coverage and the ceiling (one implementation, every door),
@@ -1465,6 +1604,22 @@ function buildToolCallsHandler(deps) {
1465
1604
  envelope: { ...envelope, content: refusal, status: 'invalid' },
1466
1605
  };
1467
1606
  }
1607
+ // The column-type contract (9.78.0), at the same door and one step
1608
+ // after the ceiling. `'warn'` files and returns undefined — this
1609
+ // path keeps every byte it had.
1610
+ const columnRefusal = judgeColumns(scope, { toolName, toolCallId, iteration }, content);
1611
+ if (columnRefusal !== undefined) {
1612
+ return {
1613
+ result: columnRefusal,
1614
+ executed: true,
1615
+ // The ceiling's flag, reused for the ceiling's reason: a refused
1616
+ // payload must not advance the step pointer on any path, and
1617
+ // there is exactly one fact here — the model did not get the
1618
+ // result — not two spellings of it.
1619
+ ceilingRefused: true,
1620
+ envelope: { ...envelope, content: columnRefusal, status: 'invalid' },
1621
+ };
1622
+ }
1468
1623
  return {
1469
1624
  result: content,
1470
1625
  executed: true,
@@ -1496,6 +1651,17 @@ function buildToolCallsHandler(deps) {
1496
1651
  envelope: { content: refusal, effects: [], status: 'invalid', malformed: [] },
1497
1652
  };
1498
1653
  }
1654
+ // The column-type contract on this path's own execute boundary — a
1655
+ // resumed call's rows are judged exactly as an inline one's.
1656
+ const columnRefusal = judgeColumns(scope, { toolName, toolCallId, iteration }, result);
1657
+ if (columnRefusal !== undefined) {
1658
+ return {
1659
+ result: columnRefusal,
1660
+ executed: true,
1661
+ ceilingRefused: true,
1662
+ envelope: { content: columnRefusal, effects: [], status: 'invalid', malformed: [] },
1663
+ };
1664
+ }
1499
1665
  // A status-only shape missing its `effects: []` marker is DATA (bytes
1500
1666
  // unchanged) — but never silently: name the dropped marker in dev mode.
1501
1667
  const nearMiss = (0, toolEffects_js_1.explainStatusOnlyNearMiss)(result);
@@ -2234,6 +2400,18 @@ function buildToolCallsHandler(deps) {
2234
2400
  result = overflowRefusal;
2235
2401
  toolStatus = 'invalid';
2236
2402
  }
2403
+ else {
2404
+ // The column-type contract (9.78.0) — the main batch-loop
2405
+ // door, one step after the ceiling. Only when the payload
2406
+ // survived it: rows nobody will read have no columns worth
2407
+ // arguing about.
2408
+ const columnRefusal = judgeColumns(scope, { toolName: tc.name, toolCallId: tc.id, iteration }, result);
2409
+ if (columnRefusal !== undefined) {
2410
+ ceilingRefused = true;
2411
+ result = columnRefusal;
2412
+ toolStatus = 'invalid';
2413
+ }
2414
+ }
2237
2415
  await endCall(tc.id);
2238
2416
  }
2239
2417
  catch (err) {
@@ -2426,6 +2604,12 @@ function buildToolCallsHandler(deps) {
2426
2604
  // placed: a refusal is already short, and 'tool-result/<name>' must
2427
2605
  // never claim an error is the tool's result.
2428
2606
  const placedValues = await placeResults(scope, { toolName: tc.name, toolCallId: tc.id, iteration }, { result, modelResult: rawModelResult }, executed && !error && !denied && !ceilingRefused);
2607
+ // The write seam (9.77.0) — judged on `result`, the tool's own answer,
2608
+ // and on `newHistory`, which at this point holds every earlier tool
2609
+ // result and not yet this one. Same guard the placement decision uses:
2610
+ // a call that did not run, errored, was denied or had its payload
2611
+ // refused has no lookup answer to read.
2612
+ noticeEmptyLookup(scope, { toolName: tc.name, toolCallId: tc.id, iteration }, callArgs, result, newHistory, executed && !error && !denied && !ceilingRefused);
2429
2613
  // The ceiling, last — after the tool, after the chain. A rule that
2430
2614
  // already summarized a huge result is measured on what it produced, so
2431
2615
  // the cap composes with governance rather than pre-empting it.