agentfootprint 9.24.0 → 9.26.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 (289) hide show
  1. package/AGENTS.md +1 -1
  2. package/CLAUDE.md +7 -1
  3. package/README.md +3 -1
  4. package/ai-instructions/claude-code/SKILL.md +1 -1
  5. package/dist/adapters/code/local.js +102 -2
  6. package/dist/adapters/code/local.js.map +1 -1
  7. package/dist/adapters/hosting/agentcore.js +11 -0
  8. package/dist/adapters/hosting/agentcore.js.map +1 -1
  9. package/dist/adapters/identity/jwks.js +277 -0
  10. package/dist/adapters/identity/jwks.js.map +1 -0
  11. package/dist/adapters/types.js +18 -1
  12. package/dist/adapters/types.js.map +1 -1
  13. package/dist/artifacts/fileArtifacts.js +144 -17
  14. package/dist/artifacts/fileArtifacts.js.map +1 -1
  15. package/dist/artifacts/gcsArtifacts.js +398 -0
  16. package/dist/artifacts/gcsArtifacts.js.map +1 -0
  17. package/dist/artifacts/index.js +17 -1
  18. package/dist/artifacts/index.js.map +1 -1
  19. package/dist/artifacts/objectStore.js +264 -0
  20. package/dist/artifacts/objectStore.js.map +1 -0
  21. package/dist/artifacts/payload.js +41 -2
  22. package/dist/artifacts/payload.js.map +1 -1
  23. package/dist/artifacts/recordingArtifact.js +88 -0
  24. package/dist/artifacts/recordingArtifact.js.map +1 -0
  25. package/dist/artifacts/s3Artifacts.js +542 -0
  26. package/dist/artifacts/s3Artifacts.js.map +1 -0
  27. package/dist/artifacts/scopePath.js +87 -0
  28. package/dist/artifacts/scopePath.js.map +1 -0
  29. package/dist/artifacts/streaming.js +132 -0
  30. package/dist/artifacts/streaming.js.map +1 -0
  31. package/dist/artifacts/types.js +10 -2
  32. package/dist/artifacts/types.js.map +1 -1
  33. package/dist/core/Agent.js +176 -0
  34. package/dist/core/Agent.js.map +1 -1
  35. package/dist/core/agent/AgentBuilder.js +20 -0
  36. package/dist/core/agent/AgentBuilder.js.map +1 -1
  37. package/dist/core/agent/repeatedCall.js +190 -0
  38. package/dist/core/agent/repeatedCall.js.map +1 -0
  39. package/dist/core/agent/stages/toolCalls.js +50 -0
  40. package/dist/core/agent/stages/toolCalls.js.map +1 -1
  41. package/dist/core/codeRunnerTool.js +149 -12
  42. package/dist/core/codeRunnerTool.js.map +1 -1
  43. package/dist/esm/adapters/code/local.d.ts +16 -0
  44. package/dist/esm/adapters/code/local.js +102 -2
  45. package/dist/esm/adapters/code/local.js.map +1 -1
  46. package/dist/esm/adapters/hosting/agentcore.js +11 -0
  47. package/dist/esm/adapters/hosting/agentcore.js.map +1 -1
  48. package/dist/esm/adapters/identity/jwks.d.ts +124 -0
  49. package/dist/esm/adapters/identity/jwks.js +249 -0
  50. package/dist/esm/adapters/identity/jwks.js.map +1 -0
  51. package/dist/esm/adapters/types.d.ts +86 -0
  52. package/dist/esm/adapters/types.js +16 -0
  53. package/dist/esm/adapters/types.js.map +1 -1
  54. package/dist/esm/artifacts/fileArtifacts.d.ts +13 -1
  55. package/dist/esm/artifacts/fileArtifacts.js +145 -18
  56. package/dist/esm/artifacts/fileArtifacts.js.map +1 -1
  57. package/dist/esm/artifacts/gcsArtifacts.d.ts +142 -0
  58. package/dist/esm/artifacts/gcsArtifacts.js +394 -0
  59. package/dist/esm/artifacts/gcsArtifacts.js.map +1 -0
  60. package/dist/esm/artifacts/index.d.ts +5 -1
  61. package/dist/esm/artifacts/index.js +4 -0
  62. package/dist/esm/artifacts/index.js.map +1 -1
  63. package/dist/esm/artifacts/objectStore.d.ts +166 -0
  64. package/dist/esm/artifacts/objectStore.js +252 -0
  65. package/dist/esm/artifacts/objectStore.js.map +1 -0
  66. package/dist/esm/artifacts/payload.d.ts +36 -4
  67. package/dist/esm/artifacts/payload.js +38 -1
  68. package/dist/esm/artifacts/payload.js.map +1 -1
  69. package/dist/esm/artifacts/recordingArtifact.d.ts +76 -0
  70. package/dist/esm/artifacts/recordingArtifact.js +83 -0
  71. package/dist/esm/artifacts/recordingArtifact.js.map +1 -0
  72. package/dist/esm/artifacts/s3Artifacts.d.ts +149 -0
  73. package/dist/esm/artifacts/s3Artifacts.js +538 -0
  74. package/dist/esm/artifacts/s3Artifacts.js.map +1 -0
  75. package/dist/esm/artifacts/scopePath.d.ts +50 -0
  76. package/dist/esm/artifacts/scopePath.js +80 -0
  77. package/dist/esm/artifacts/scopePath.js.map +1 -0
  78. package/dist/esm/artifacts/streaming.d.ts +82 -0
  79. package/dist/esm/artifacts/streaming.js +123 -0
  80. package/dist/esm/artifacts/streaming.js.map +1 -0
  81. package/dist/esm/artifacts/types.d.ts +71 -5
  82. package/dist/esm/artifacts/types.js +10 -2
  83. package/dist/esm/artifacts/types.js.map +1 -1
  84. package/dist/esm/core/Agent.d.ts +66 -2
  85. package/dist/esm/core/Agent.js +176 -0
  86. package/dist/esm/core/Agent.js.map +1 -1
  87. package/dist/esm/core/agent/AgentBuilder.js +20 -0
  88. package/dist/esm/core/agent/AgentBuilder.js.map +1 -1
  89. package/dist/esm/core/agent/repeatedCall.d.ts +150 -0
  90. package/dist/esm/core/agent/repeatedCall.js +184 -0
  91. package/dist/esm/core/agent/repeatedCall.js.map +1 -0
  92. package/dist/esm/core/agent/stages/toolCalls.d.ts +12 -0
  93. package/dist/esm/core/agent/stages/toolCalls.js +50 -0
  94. package/dist/esm/core/agent/stages/toolCalls.js.map +1 -1
  95. package/dist/esm/core/agent/types.d.ts +82 -0
  96. package/dist/esm/core/codeRunnerTool.d.ts +31 -0
  97. package/dist/esm/core/codeRunnerTool.js +149 -12
  98. package/dist/esm/core/codeRunnerTool.js.map +1 -1
  99. package/dist/esm/events/payloads.d.ts +22 -0
  100. package/dist/esm/events/registry.d.ts +3 -1
  101. package/dist/esm/events/registry.js +2 -0
  102. package/dist/esm/events/registry.js.map +1 -1
  103. package/dist/esm/hosting/admission.d.ts +180 -0
  104. package/dist/esm/hosting/admission.js +188 -0
  105. package/dist/esm/hosting/admission.js.map +1 -0
  106. package/dist/esm/hosting/artifactWire.d.ts +8 -5
  107. package/dist/esm/hosting/artifactWire.js +14 -9
  108. package/dist/esm/hosting/artifactWire.js.map +1 -1
  109. package/dist/esm/hosting/envelope.d.ts +34 -0
  110. package/dist/esm/hosting/envelope.js +81 -0
  111. package/dist/esm/hosting/envelope.js.map +1 -1
  112. package/dist/esm/hosting/errors.d.ts +131 -0
  113. package/dist/esm/hosting/errors.js +195 -0
  114. package/dist/esm/hosting/errors.js.map +1 -1
  115. package/dist/esm/hosting/httpHost.d.ts +21 -0
  116. package/dist/esm/hosting/httpHost.js +44 -2
  117. package/dist/esm/hosting/httpHost.js.map +1 -1
  118. package/dist/esm/hosting/identityVerification.d.ts +154 -0
  119. package/dist/esm/hosting/identityVerification.js +135 -0
  120. package/dist/esm/hosting/identityVerification.js.map +1 -0
  121. package/dist/esm/hosting/index.d.ts +12 -3
  122. package/dist/esm/hosting/index.js +16 -2
  123. package/dist/esm/hosting/index.js.map +1 -1
  124. package/dist/esm/hosting/memorySessions.d.ts +9 -0
  125. package/dist/esm/hosting/memorySessions.js +86 -0
  126. package/dist/esm/hosting/memorySessions.js.map +1 -1
  127. package/dist/esm/hosting/nodeHost.js +8 -0
  128. package/dist/esm/hosting/nodeHost.js.map +1 -1
  129. package/dist/esm/hosting/sessionWire.d.ts +126 -0
  130. package/dist/esm/hosting/sessionWire.js +95 -0
  131. package/dist/esm/hosting/sessionWire.js.map +1 -0
  132. package/dist/esm/hosting/sqliteSessions.js +122 -5
  133. package/dist/esm/hosting/sqliteSessions.js.map +1 -1
  134. package/dist/esm/hosting/standingAgent.js +299 -8
  135. package/dist/esm/hosting/standingAgent.js.map +1 -1
  136. package/dist/esm/hosting/types.d.ts +166 -6
  137. package/dist/esm/hosting/types.js.map +1 -1
  138. package/dist/esm/hosting/wireOps.d.ts +52 -0
  139. package/dist/esm/hosting/wireOps.js +60 -0
  140. package/dist/esm/hosting/wireOps.js.map +1 -0
  141. package/dist/esm/identity.d.ts +1 -0
  142. package/dist/esm/identity.js +6 -0
  143. package/dist/esm/identity.js.map +1 -1
  144. package/dist/esm/index.d.ts +3 -2
  145. package/dist/esm/index.js +10 -1
  146. package/dist/esm/index.js.map +1 -1
  147. package/dist/esm/lib/injection-engine/factories/defineSkill.d.ts +27 -0
  148. package/dist/esm/lib/injection-engine/factories/defineSkill.js +13 -0
  149. package/dist/esm/lib/injection-engine/factories/defineSkill.js.map +1 -1
  150. package/dist/esm/lib/injection-engine/index.d.ts +1 -0
  151. package/dist/esm/lib/injection-engine/index.js +7 -0
  152. package/dist/esm/lib/injection-engine/index.js.map +1 -1
  153. package/dist/esm/lib/injection-engine/skillGraph.js +9 -1
  154. package/dist/esm/lib/injection-engine/skillGraph.js.map +1 -1
  155. package/dist/esm/lib/injection-engine/skillGraphCheckup.d.ts +1 -1
  156. package/dist/esm/lib/injection-engine/skillGraphCheckup.js.map +1 -1
  157. package/dist/esm/lib/injection-engine/skillSteps.d.ts +16 -0
  158. package/dist/esm/lib/injection-engine/skillSteps.js +5 -0
  159. package/dist/esm/lib/injection-engine/skillSteps.js.map +1 -1
  160. package/dist/esm/lib/injection-engine/skillVocabulary.d.ts +83 -0
  161. package/dist/esm/lib/injection-engine/skillVocabulary.js +218 -0
  162. package/dist/esm/lib/injection-engine/skillVocabulary.js.map +1 -0
  163. package/dist/events/registry.js +2 -0
  164. package/dist/events/registry.js.map +1 -1
  165. package/dist/hosting/admission.js +194 -0
  166. package/dist/hosting/admission.js.map +1 -0
  167. package/dist/hosting/artifactWire.js +14 -9
  168. package/dist/hosting/artifactWire.js.map +1 -1
  169. package/dist/hosting/envelope.js +84 -1
  170. package/dist/hosting/envelope.js.map +1 -1
  171. package/dist/hosting/errors.js +203 -1
  172. package/dist/hosting/errors.js.map +1 -1
  173. package/dist/hosting/httpHost.js +43 -1
  174. package/dist/hosting/httpHost.js.map +1 -1
  175. package/dist/hosting/identityVerification.js +140 -0
  176. package/dist/hosting/identityVerification.js.map +1 -0
  177. package/dist/hosting/index.js +38 -1
  178. package/dist/hosting/index.js.map +1 -1
  179. package/dist/hosting/memorySessions.js +86 -0
  180. package/dist/hosting/memorySessions.js.map +1 -1
  181. package/dist/hosting/nodeHost.js +8 -0
  182. package/dist/hosting/nodeHost.js.map +1 -1
  183. package/dist/hosting/sessionWire.js +100 -0
  184. package/dist/hosting/sessionWire.js.map +1 -0
  185. package/dist/hosting/sqliteSessions.js +121 -4
  186. package/dist/hosting/sqliteSessions.js.map +1 -1
  187. package/dist/hosting/standingAgent.js +297 -6
  188. package/dist/hosting/standingAgent.js.map +1 -1
  189. package/dist/hosting/types.js.map +1 -1
  190. package/dist/hosting/wireOps.js +65 -0
  191. package/dist/hosting/wireOps.js.map +1 -0
  192. package/dist/identity.js +9 -1
  193. package/dist/identity.js.map +1 -1
  194. package/dist/index.js +30 -13
  195. package/dist/index.js.map +1 -1
  196. package/dist/lib/injection-engine/factories/defineSkill.js +13 -0
  197. package/dist/lib/injection-engine/factories/defineSkill.js.map +1 -1
  198. package/dist/lib/injection-engine/index.js +10 -1
  199. package/dist/lib/injection-engine/index.js.map +1 -1
  200. package/dist/lib/injection-engine/skillGraph.js +9 -1
  201. package/dist/lib/injection-engine/skillGraph.js.map +1 -1
  202. package/dist/lib/injection-engine/skillGraphCheckup.js.map +1 -1
  203. package/dist/lib/injection-engine/skillSteps.js +5 -0
  204. package/dist/lib/injection-engine/skillSteps.js.map +1 -1
  205. package/dist/lib/injection-engine/skillVocabulary.js +224 -0
  206. package/dist/lib/injection-engine/skillVocabulary.js.map +1 -0
  207. package/dist/types/adapters/code/local.d.ts +16 -0
  208. package/dist/types/adapters/code/local.d.ts.map +1 -1
  209. package/dist/types/adapters/hosting/agentcore.d.ts.map +1 -1
  210. package/dist/types/adapters/identity/jwks.d.ts +125 -0
  211. package/dist/types/adapters/identity/jwks.d.ts.map +1 -0
  212. package/dist/types/adapters/types.d.ts +86 -0
  213. package/dist/types/adapters/types.d.ts.map +1 -1
  214. package/dist/types/artifacts/fileArtifacts.d.ts +13 -1
  215. package/dist/types/artifacts/fileArtifacts.d.ts.map +1 -1
  216. package/dist/types/artifacts/gcsArtifacts.d.ts +143 -0
  217. package/dist/types/artifacts/gcsArtifacts.d.ts.map +1 -0
  218. package/dist/types/artifacts/index.d.ts +5 -1
  219. package/dist/types/artifacts/index.d.ts.map +1 -1
  220. package/dist/types/artifacts/objectStore.d.ts +167 -0
  221. package/dist/types/artifacts/objectStore.d.ts.map +1 -0
  222. package/dist/types/artifacts/payload.d.ts +36 -4
  223. package/dist/types/artifacts/payload.d.ts.map +1 -1
  224. package/dist/types/artifacts/recordingArtifact.d.ts +77 -0
  225. package/dist/types/artifacts/recordingArtifact.d.ts.map +1 -0
  226. package/dist/types/artifacts/s3Artifacts.d.ts +150 -0
  227. package/dist/types/artifacts/s3Artifacts.d.ts.map +1 -0
  228. package/dist/types/artifacts/scopePath.d.ts +51 -0
  229. package/dist/types/artifacts/scopePath.d.ts.map +1 -0
  230. package/dist/types/artifacts/streaming.d.ts +83 -0
  231. package/dist/types/artifacts/streaming.d.ts.map +1 -0
  232. package/dist/types/artifacts/types.d.ts +71 -5
  233. package/dist/types/artifacts/types.d.ts.map +1 -1
  234. package/dist/types/core/Agent.d.ts +66 -2
  235. package/dist/types/core/Agent.d.ts.map +1 -1
  236. package/dist/types/core/agent/AgentBuilder.d.ts.map +1 -1
  237. package/dist/types/core/agent/repeatedCall.d.ts +151 -0
  238. package/dist/types/core/agent/repeatedCall.d.ts.map +1 -0
  239. package/dist/types/core/agent/stages/toolCalls.d.ts +12 -0
  240. package/dist/types/core/agent/stages/toolCalls.d.ts.map +1 -1
  241. package/dist/types/core/agent/types.d.ts +82 -0
  242. package/dist/types/core/agent/types.d.ts.map +1 -1
  243. package/dist/types/core/codeRunnerTool.d.ts +31 -0
  244. package/dist/types/core/codeRunnerTool.d.ts.map +1 -1
  245. package/dist/types/events/payloads.d.ts +22 -0
  246. package/dist/types/events/payloads.d.ts.map +1 -1
  247. package/dist/types/events/registry.d.ts +3 -1
  248. package/dist/types/events/registry.d.ts.map +1 -1
  249. package/dist/types/hosting/admission.d.ts +181 -0
  250. package/dist/types/hosting/admission.d.ts.map +1 -0
  251. package/dist/types/hosting/artifactWire.d.ts +8 -5
  252. package/dist/types/hosting/artifactWire.d.ts.map +1 -1
  253. package/dist/types/hosting/envelope.d.ts +34 -0
  254. package/dist/types/hosting/envelope.d.ts.map +1 -1
  255. package/dist/types/hosting/errors.d.ts +131 -0
  256. package/dist/types/hosting/errors.d.ts.map +1 -1
  257. package/dist/types/hosting/httpHost.d.ts +21 -0
  258. package/dist/types/hosting/httpHost.d.ts.map +1 -1
  259. package/dist/types/hosting/identityVerification.d.ts +155 -0
  260. package/dist/types/hosting/identityVerification.d.ts.map +1 -0
  261. package/dist/types/hosting/index.d.ts +12 -3
  262. package/dist/types/hosting/index.d.ts.map +1 -1
  263. package/dist/types/hosting/memorySessions.d.ts +9 -0
  264. package/dist/types/hosting/memorySessions.d.ts.map +1 -1
  265. package/dist/types/hosting/nodeHost.d.ts.map +1 -1
  266. package/dist/types/hosting/sessionWire.d.ts +127 -0
  267. package/dist/types/hosting/sessionWire.d.ts.map +1 -0
  268. package/dist/types/hosting/sqliteSessions.d.ts.map +1 -1
  269. package/dist/types/hosting/standingAgent.d.ts.map +1 -1
  270. package/dist/types/hosting/types.d.ts +166 -6
  271. package/dist/types/hosting/types.d.ts.map +1 -1
  272. package/dist/types/hosting/wireOps.d.ts +53 -0
  273. package/dist/types/hosting/wireOps.d.ts.map +1 -0
  274. package/dist/types/identity.d.ts +1 -0
  275. package/dist/types/identity.d.ts.map +1 -1
  276. package/dist/types/index.d.ts +3 -2
  277. package/dist/types/index.d.ts.map +1 -1
  278. package/dist/types/lib/injection-engine/factories/defineSkill.d.ts +27 -0
  279. package/dist/types/lib/injection-engine/factories/defineSkill.d.ts.map +1 -1
  280. package/dist/types/lib/injection-engine/index.d.ts +1 -0
  281. package/dist/types/lib/injection-engine/index.d.ts.map +1 -1
  282. package/dist/types/lib/injection-engine/skillGraph.d.ts.map +1 -1
  283. package/dist/types/lib/injection-engine/skillGraphCheckup.d.ts +1 -1
  284. package/dist/types/lib/injection-engine/skillGraphCheckup.d.ts.map +1 -1
  285. package/dist/types/lib/injection-engine/skillSteps.d.ts +16 -0
  286. package/dist/types/lib/injection-engine/skillSteps.d.ts.map +1 -1
  287. package/dist/types/lib/injection-engine/skillVocabulary.d.ts +84 -0
  288. package/dist/types/lib/injection-engine/skillVocabulary.d.ts.map +1 -0
  289. package/package.json +15 -1
@@ -0,0 +1,190 @@
1
+ "use strict";
2
+ /**
3
+ * core/agent/repeatedCall — tell a model when it has already asked this.
4
+ *
5
+ * ── The measured failure ────────────────────────────────────────────────────
6
+ * A traced production run: the model called one tool three times in a row with
7
+ * byte-identical arguments and got a byte-identical result each time. The tool
8
+ * was doing its job — the arguments named a filter the backend did not honour,
9
+ * so the same rows came back — and the model read the same rows as a fresh
10
+ * answer each iteration, concluded nothing had changed, and tried again. Three
11
+ * calls, three identical results, one wasted turn, and nothing anywhere in the
12
+ * loop that could say "you have done this".
13
+ *
14
+ * That class of loop is invisible from inside the conversation, because the
15
+ * history genuinely does show three separate calls that each returned data. The
16
+ * only party with the whole picture is the framework, which watched all three
17
+ * land.
18
+ *
19
+ * ── The intervention: a NOTE, never a refusal ───────────────────────────────
20
+ * On the Nth identical (tool, args) → identical result, the framework appends
21
+ * one sentence to that result. It does not block the call, does not change the
22
+ * result, does not error, and does not stop a later identical call from
23
+ * running. That restraint is the design:
24
+ *
25
+ * • **A repeat is sometimes correct.** Polling a job until its status
26
+ * changes is a loop of identical calls returning identical results, on
27
+ * purpose. Refusing it would break a legitimate pattern to fix an
28
+ * illegitimate one, and the model is the only party that knows which it is
29
+ * doing.
30
+ * • **A note is evidence; a refusal is a wall.** The model reads what
31
+ * happened and decides. That is the same posture every teaching refusal in
32
+ * this library takes, minus the refusal.
33
+ *
34
+ * ── Why the result must match too ───────────────────────────────────────────
35
+ * Identical ARGUMENTS alone are not evidence of anything: a "check status" call
36
+ * with the same arguments returning a DIFFERENT status is progress. It is the
37
+ * identical RESULT that makes the repeat pointless, so both halves are
38
+ * required. Which also means the note can say something true and specific —
39
+ * *calling again will not change it* — rather than a vague "you seem to be
40
+ * repeating yourself".
41
+ *
42
+ * ── What is stored, and what is deliberately not ────────────────────────────
43
+ * A short non-cryptographic FINGERPRINT of the arguments and the result, never
44
+ * the values. Tool arguments routinely carry the things redaction exists for,
45
+ * and a fingerprint answers the only question this feature asks ("is this the
46
+ * same?") and answers nothing else.
47
+ *
48
+ * ── And WHERE it is stored: beside the run, never inside its state ──────────
49
+ * The counters live in {@link repeatedCallLedgers}, a run-keyed map the
50
+ * dispatch loop holds — NOT on tracked scope. That is not a tidiness
51
+ * preference, it is the zero-cost law:
52
+ *
53
+ * • Tracked scope IS the commit log, the snapshot, the narrative, every
54
+ * recording and every causal slice. A key written there on every tool
55
+ * landing would appear in all of them for every existing agent the moment
56
+ * it upgraded — a turn that never repeated anything would no longer be
57
+ * byte-identical to the release before, and a consumer asserting a state-key
58
+ * set would break for a feature it never asked for.
59
+ * • A within-turn counter is not conversation state. Nothing resumes from it,
60
+ * nothing branches on it later, and no reader of a trace needs it: the
61
+ * repeat itself lands on the record as `agentfootprint.tools.repeated_call`,
62
+ * which is the telemetry channel this library puts per-attempt facts on
63
+ * (the same reasoning as retries and streaming tokens, and the same
64
+ * reasoning that keeps a consent record off tracked state).
65
+ *
66
+ * So an agent that never repeats a call is byte-identical to 9.25 in behaviour,
67
+ * in state AND in events — the ledger exists only in memory, only for the
68
+ * duration of a run, and only where a tool actually landed.
69
+ */
70
+ Object.defineProperty(exports, "__esModule", { value: true });
71
+ exports.noteRepeatedCall = exports.repeatedCallKey = exports.repeatedCallLedgers = exports.REPEATED_CALL_THRESHOLD = void 0;
72
+ /** How many identical (tool, args, result) landings trigger the note. The
73
+ * SECOND one: the first repeat is the first moment the fact exists. */
74
+ exports.REPEATED_CALL_THRESHOLD = 2;
75
+ /** How many runs' counters one dispatch loop keeps at once. Small on purpose:
76
+ * concurrent runs on ONE agent instance are the only reason for more than
77
+ * one, and a deployment serving many callers at once gives each its own
78
+ * agent (that is what `agentFactory` is for). */
79
+ const LEDGER_RUNS = 8;
80
+ /** Build the run-keyed holder. One per agent — the chart is built once, so the
81
+ * loop that owns this is built once too. */
82
+ function repeatedCallLedgers(maxRuns = LEDGER_RUNS) {
83
+ const byRun = new Map();
84
+ return {
85
+ read: (runId) => byRun.get(runId),
86
+ write: (runId, ledger) => {
87
+ // Delete-then-set makes insertion order recency order, which is what the
88
+ // eviction below reads.
89
+ byRun.delete(runId);
90
+ byRun.set(runId, ledger);
91
+ while (byRun.size > maxRuns) {
92
+ const oldest = byRun.keys().next();
93
+ if (oldest.done === true)
94
+ break;
95
+ byRun.delete(oldest.value);
96
+ }
97
+ },
98
+ };
99
+ }
100
+ exports.repeatedCallLedgers = repeatedCallLedgers;
101
+ /**
102
+ * A stable, order-insensitive fingerprint of one call.
103
+ *
104
+ * Object keys are SORTED before hashing, at every depth: `{ a: 1, b: 2 }` and
105
+ * `{ b: 2, a: 1 }` are the same call, and a model that happens to emit its JSON
106
+ * in a different key order twice is repeating itself just as surely. Arrays
107
+ * keep their order, because `[1, 2]` and `[2, 1]` are not the same argument.
108
+ */
109
+ function stableString(value) {
110
+ if (value === null || typeof value !== 'object')
111
+ return JSON.stringify(value) ?? 'undefined';
112
+ if (Array.isArray(value))
113
+ return `[${value.map(stableString).join(',')}]`;
114
+ const entries = Object.entries(value)
115
+ .filter(([, v]) => v !== undefined)
116
+ .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0));
117
+ return `{${entries.map(([k, v]) => `${JSON.stringify(k)}:${stableString(v)}`).join(',')}}`;
118
+ }
119
+ /**
120
+ * FNV-1a, 32-bit, as 8 hex characters.
121
+ *
122
+ * Non-cryptographic ON PURPOSE and stated as such: this compares a string to
123
+ * ITSELF a few iterations later inside one run, where an adversary has nothing
124
+ * to gain from a collision and the cost of `crypto.subtle` (async, and one
125
+ * digest per tool result) buys nothing. A collision produces one unnecessary
126
+ * teaching note, which is the cheapest possible failure this could have.
127
+ */
128
+ function fingerprint(text) {
129
+ let hash = 0x811c9dc5;
130
+ for (let i = 0; i < text.length; i += 1) {
131
+ hash ^= text.charCodeAt(i);
132
+ hash = Math.imul(hash, 0x01000193) >>> 0;
133
+ }
134
+ return hash.toString(16).padStart(8, '0');
135
+ }
136
+ /**
137
+ * The two fingerprints for one landing, and the ledger key that joins them.
138
+ *
139
+ * Kept apart on the way out so a debugger reading the event can tell "same
140
+ * call, different answer" from "same call, same answer" — without either value
141
+ * being present anywhere.
142
+ */
143
+ function repeatedCallKey(toolName, args, result) {
144
+ const argsFingerprint = fingerprint(stableString(args));
145
+ const resultFingerprint = fingerprint(result);
146
+ return {
147
+ key: `${toolName}:${argsFingerprint}:${resultFingerprint}`,
148
+ argsFingerprint,
149
+ resultFingerprint,
150
+ };
151
+ }
152
+ exports.repeatedCallKey = repeatedCallKey;
153
+ /**
154
+ * The teaching sentence.
155
+ *
156
+ * It says three things, in the order the model needs them: what happened (the
157
+ * fact it cannot see), what follows from it (calling again is not going to
158
+ * help), and what to do instead (two named options, neither of which is "stop"
159
+ * — a model told only to stop tends to stop answering).
160
+ */
161
+ function noteFor(toolName, occurrences) {
162
+ return (`\n\n[identical call: '${toolName}' has now returned exactly this result ${occurrences} ` +
163
+ `times this turn, for exactly these arguments. Calling it again will not change it — ` +
164
+ `act on what you have, or change the arguments (a different filter, a wider range, a ` +
165
+ `different tool). If you are waiting for something to change, say so in your answer ` +
166
+ `rather than calling again.]`);
167
+ }
168
+ /**
169
+ * Record one landing and decide whether it earns the note.
170
+ *
171
+ * PURE — takes the ledger, returns the next one. The dispatch loop owns reading
172
+ * and writing scope, so this file never imports the agent and can be tested as
173
+ * a table.
174
+ *
175
+ * The note fires ONCE per distinct call, on the Nth landing exactly. A third
176
+ * and fourth identical call add nothing further: the model has been told, and
177
+ * repeating the lesson every iteration would be the framework doing the very
178
+ * thing it is complaining about.
179
+ */
180
+ function noteRepeatedCall(ledger, toolName, args, result) {
181
+ const { key, argsFingerprint, resultFingerprint } = repeatedCallKey(toolName, args, result);
182
+ const occurrences = (ledger?.[key] ?? 0) + 1;
183
+ const next = { ...(ledger ?? {}), [key]: occurrences };
184
+ const outcome = { ledger: next, occurrences, argsFingerprint, resultFingerprint };
185
+ if (occurrences !== exports.REPEATED_CALL_THRESHOLD)
186
+ return outcome;
187
+ return { ...outcome, note: noteFor(toolName, occurrences) };
188
+ }
189
+ exports.noteRepeatedCall = noteRepeatedCall;
190
+ //# sourceMappingURL=repeatedCall.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repeatedCall.js","sourceRoot":"","sources":["../../../src/core/agent/repeatedCall.ts"],"names":[],"mappings":"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"}
@@ -62,6 +62,7 @@ const askComponent_js_1 = require("../../askComponent.js");
62
62
  const checkin_js_1 = require("../../checkin.js");
63
63
  const runChain_js_1 = require("../middleware/runChain.js");
64
64
  const ledger_js_1 = require("../middleware/ledger.js");
65
+ const repeatedCall_js_1 = require("../repeatedCall.js");
65
66
  const toolArgsValidation_js_1 = require("../toolArgsValidation.js");
66
67
  const validators_js_1 = require("../validators.js");
67
68
  const toolResultCap_js_1 = require("../toolResultCap.js");
@@ -222,6 +223,17 @@ function argsForPausedCall(scope, toolCallId) {
222
223
  function appendBatchResult(scope, entry) {
223
224
  scope.toolResults = [...(scope.toolResults ?? []), entry];
224
225
  }
226
+ /**
227
+ * The ledger key for a handler built with no `currentRun` accessor — a
228
+ * hand-composed dispatch loop in a test, never an Agent (which always wires
229
+ * it).
230
+ *
231
+ * One shared bucket, stated rather than hidden: without a run id there is
232
+ * nothing to tell two runs apart by, and the honest consequence is that such a
233
+ * loop counts across them. It cannot leak anything — the ledger holds
234
+ * fingerprints — and the only visible effect is a note arriving one call early.
235
+ */
236
+ const UNSCOPED_RUN = '#no-run-id';
225
237
  function buildToolCallsHandler(deps) {
226
238
  const { registryByName, externalToolProvider, providerToolCache, permissionChecker } = deps;
227
239
  const toolArgValidation = deps.toolArgValidation ?? 'enforce';
@@ -232,6 +244,10 @@ function buildToolCallsHandler(deps) {
232
244
  // that THROWS on use (never undefined) — so a tool can't silently no-op.
233
245
  const credentials = deps.credentialProvider ?? (0, types_js_2.unconfiguredCredentialProvider)();
234
246
  const hasCredentials = deps.credentialProvider !== undefined;
247
+ // The repeated-call counters (9.26.0), run-keyed and OFF tracked scope — an
248
+ // empty Map here, and not one byte written anywhere a reader can see until a
249
+ // tool actually lands. See `../repeatedCall.ts` for why they are not state.
250
+ const repeatLedgers = (0, repeatedCall_js_1.repeatedCallLedgers)();
235
251
  /**
236
252
  * The tool-result ceiling, applied at the ONE place a result leaves dispatch.
237
253
  *
@@ -2035,6 +2051,11 @@ function buildToolCallsHandler(deps) {
2035
2051
  ...(toolStatus !== undefined && { status: toolStatus }),
2036
2052
  });
2037
2053
  let resultStr = typeof modelResult === 'string' ? modelResult : (0, validators_js_1.safeStringify)(modelResult);
2054
+ // The tool's OWN answer, before any framework suffix joins it — what
2055
+ // the repeated-call ledger fingerprints. Fingerprinting the decorated
2056
+ // string instead would compare our own additions (a step that advanced
2057
+ // once, an effect note) and miss the repeat they decorate.
2058
+ const deliveredResult = resultStr;
2038
2059
  // ── Step boundary (9.18.0): advance decided, then decorate, THEN
2039
2060
  // push — the suffix must be part of the one past every reader sees
2040
2061
  // (history, `lastToolResult`, the batch), never spliced in later.
@@ -2071,6 +2092,35 @@ function buildToolCallsHandler(deps) {
2071
2092
  if (toolEnvelope !== undefined && executed && !error && !denied) {
2072
2093
  resultStr += applyToolEffects(scope, { toolName: tc.name, toolCallId: tc.id, iteration }, toolEnvelope, transitionState);
2073
2094
  }
2095
+ // ── Repeated-call nudge (9.26.0) ───────────────────────────────
2096
+ // The framework is the only party that watched all three landings, so
2097
+ // it is the only one that can say so. A NOTE on the result — the call
2098
+ // ran, nothing was refused, and a later identical call is not blocked.
2099
+ // Only for a call that RAN: a permission denial, a gate rejection and
2100
+ // a ceiling refusal each already teach their own lesson, and stacking
2101
+ // a second one would bury it.
2102
+ if (deps.repeatedCallNudge !== false && executed && !denied && !skillRejected) {
2103
+ // Counted beside the run, never inside its state: a tracked write is
2104
+ // a commit-log entry, a snapshot key, a narrative line and a row in
2105
+ // every recording, and a turn that repeats NOTHING must stay
2106
+ // byte-identical to the release before this one. `currentRun()` is
2107
+ // the same accessor `ctx.runId` is composed from — one answer to
2108
+ // "which run is this", not a second spelling of it.
2109
+ const runKey = deps.currentRun?.().runId ?? UNSCOPED_RUN;
2110
+ const repeat = (0, repeatedCall_js_1.noteRepeatedCall)(repeatLedgers.read(runKey), tc.name, callArgs, deliveredResult);
2111
+ repeatLedgers.write(runKey, repeat.ledger);
2112
+ if (repeat.note !== undefined) {
2113
+ resultStr += repeat.note;
2114
+ (0, typedEmit_js_1.typedEmit)(scope, 'agentfootprint.tools.repeated_call', {
2115
+ toolName: tc.name,
2116
+ toolCallId: tc.id,
2117
+ iteration,
2118
+ occurrences: repeat.occurrences,
2119
+ argsFingerprint: repeat.argsFingerprint,
2120
+ resultFingerprint: repeat.resultFingerprint,
2121
+ });
2122
+ }
2123
+ }
2074
2124
  newHistory.push({
2075
2125
  role: 'tool',
2076
2126
  content: resultStr,