@namzu/sdk 7.0.0 → 8.0.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 (128) hide show
  1. package/CHANGELOG.md +437 -0
  2. package/dist/agents/SupervisorAgent.d.ts.map +1 -1
  3. package/dist/agents/SupervisorAgent.js +172 -162
  4. package/dist/agents/SupervisorAgent.js.map +1 -1
  5. package/dist/agents/__tests__/supervisor-inbox-scope.test.d.ts +2 -0
  6. package/dist/agents/__tests__/supervisor-inbox-scope.test.d.ts.map +1 -0
  7. package/dist/agents/__tests__/supervisor-inbox-scope.test.js +125 -0
  8. package/dist/agents/__tests__/supervisor-inbox-scope.test.js.map +1 -0
  9. package/dist/bridge/a2a/mapper.d.ts.map +1 -1
  10. package/dist/bridge/a2a/mapper.js +4 -0
  11. package/dist/bridge/a2a/mapper.js.map +1 -1
  12. package/dist/bridge/sse/mapper.d.ts.map +1 -1
  13. package/dist/bridge/sse/mapper.js +24 -0
  14. package/dist/bridge/sse/mapper.js.map +1 -1
  15. package/dist/contracts/api.d.ts +1 -1
  16. package/dist/contracts/api.d.ts.map +1 -1
  17. package/dist/gateway/__tests__/completion-inbox.test.js +292 -2
  18. package/dist/gateway/__tests__/completion-inbox.test.js.map +1 -1
  19. package/dist/gateway/completion-inbox.d.ts +94 -6
  20. package/dist/gateway/completion-inbox.d.ts.map +1 -1
  21. package/dist/gateway/completion-inbox.js +235 -15
  22. package/dist/gateway/completion-inbox.js.map +1 -1
  23. package/dist/manager/run/persistence.d.ts +8 -0
  24. package/dist/manager/run/persistence.d.ts.map +1 -1
  25. package/dist/manager/run/persistence.js +12 -0
  26. package/dist/manager/run/persistence.js.map +1 -1
  27. package/dist/run/reporter.d.ts.map +1 -1
  28. package/dist/run/reporter.js +11 -0
  29. package/dist/run/reporter.js.map +1 -1
  30. package/dist/runtime/query/__tests__/completion-does-not-erase-the-answer.test.d.ts +2 -0
  31. package/dist/runtime/query/__tests__/completion-does-not-erase-the-answer.test.d.ts.map +1 -0
  32. package/dist/runtime/query/__tests__/completion-does-not-erase-the-answer.test.js +142 -0
  33. package/dist/runtime/query/__tests__/completion-does-not-erase-the-answer.test.js.map +1 -0
  34. package/dist/runtime/query/__tests__/completion-notification.test.js +414 -32
  35. package/dist/runtime/query/__tests__/completion-notification.test.js.map +1 -1
  36. package/dist/runtime/query/__tests__/context-size-on-the-wire.test.d.ts +2 -0
  37. package/dist/runtime/query/__tests__/context-size-on-the-wire.test.d.ts.map +1 -0
  38. package/dist/runtime/query/__tests__/context-size-on-the-wire.test.js +100 -0
  39. package/dist/runtime/query/__tests__/context-size-on-the-wire.test.js.map +1 -0
  40. package/dist/runtime/query/guard.d.ts +28 -0
  41. package/dist/runtime/query/guard.d.ts.map +1 -1
  42. package/dist/runtime/query/guard.js +31 -0
  43. package/dist/runtime/query/guard.js.map +1 -1
  44. package/dist/runtime/query/iteration/__tests__/settle-grace.test.d.ts +2 -0
  45. package/dist/runtime/query/iteration/__tests__/settle-grace.test.d.ts.map +1 -0
  46. package/dist/runtime/query/iteration/__tests__/settle-grace.test.js +226 -0
  47. package/dist/runtime/query/iteration/__tests__/settle-grace.test.js.map +1 -0
  48. package/dist/runtime/query/iteration/index.d.ts +92 -0
  49. package/dist/runtime/query/iteration/index.d.ts.map +1 -1
  50. package/dist/runtime/query/iteration/index.js +814 -566
  51. package/dist/runtime/query/iteration/index.js.map +1 -1
  52. package/dist/runtime/query/iteration/phases/__tests__/compaction-declined.test.d.ts +2 -0
  53. package/dist/runtime/query/iteration/phases/__tests__/compaction-declined.test.d.ts.map +1 -0
  54. package/dist/runtime/query/iteration/phases/__tests__/compaction-declined.test.js +95 -0
  55. package/dist/runtime/query/iteration/phases/__tests__/compaction-declined.test.js.map +1 -0
  56. package/dist/runtime/query/iteration/phases/compaction.d.ts +34 -0
  57. package/dist/runtime/query/iteration/phases/compaction.d.ts.map +1 -1
  58. package/dist/runtime/query/iteration/phases/compaction.js +61 -4
  59. package/dist/runtime/query/iteration/phases/compaction.js.map +1 -1
  60. package/dist/tools/__tests__/untrusted-envelope.test.js +16 -0
  61. package/dist/tools/__tests__/untrusted-envelope.test.js.map +1 -1
  62. package/dist/tools/coordinator/__tests__/completion-delivery.test.js +117 -0
  63. package/dist/tools/coordinator/__tests__/completion-delivery.test.js.map +1 -1
  64. package/dist/tools/coordinator/__tests__/task-list.test.js +57 -0
  65. package/dist/tools/coordinator/__tests__/task-list.test.js.map +1 -1
  66. package/dist/tools/coordinator/index.d.ts.map +1 -1
  67. package/dist/tools/coordinator/index.js +109 -11
  68. package/dist/tools/coordinator/index.js.map +1 -1
  69. package/dist/tools/untrusted-envelope.d.ts.map +1 -1
  70. package/dist/tools/untrusted-envelope.js +9 -1
  71. package/dist/tools/untrusted-envelope.js.map +1 -1
  72. package/dist/types/agent/gateway.d.ts +18 -0
  73. package/dist/types/agent/gateway.d.ts.map +1 -1
  74. package/dist/types/provider/interface.d.ts +35 -0
  75. package/dist/types/provider/interface.d.ts.map +1 -1
  76. package/dist/types/run/entity.d.ts +16 -0
  77. package/dist/types/run/entity.d.ts.map +1 -1
  78. package/dist/types/run/events.d.ts +75 -0
  79. package/dist/types/run/events.d.ts.map +1 -1
  80. package/dist/types/run/events.js.map +1 -1
  81. package/dist/types/run/prepare-step.d.ts +17 -2
  82. package/dist/types/run/prepare-step.d.ts.map +1 -1
  83. package/dist/types/verification/index.d.ts +98 -0
  84. package/dist/types/verification/index.d.ts.map +1 -1
  85. package/dist/types/verification/index.js +10 -0
  86. package/dist/types/verification/index.js.map +1 -1
  87. package/dist/verification/__tests__/argument-pattern.test.d.ts +2 -0
  88. package/dist/verification/__tests__/argument-pattern.test.d.ts.map +1 -0
  89. package/dist/verification/__tests__/argument-pattern.test.js +122 -0
  90. package/dist/verification/__tests__/argument-pattern.test.js.map +1 -0
  91. package/dist/verification/gate.d.ts.map +1 -1
  92. package/dist/verification/gate.js +44 -0
  93. package/dist/verification/gate.js.map +1 -1
  94. package/dist/verification/rules.d.ts.map +1 -1
  95. package/dist/verification/rules.js +27 -0
  96. package/dist/verification/rules.js.map +1 -1
  97. package/package.json +1 -1
  98. package/src/agents/SupervisorAgent.ts +175 -166
  99. package/src/agents/__tests__/supervisor-inbox-scope.test.ts +149 -0
  100. package/src/bridge/a2a/mapper.ts +4 -0
  101. package/src/bridge/sse/mapper.ts +25 -0
  102. package/src/contracts/api.ts +1 -0
  103. package/src/gateway/__tests__/completion-inbox.test.ts +348 -2
  104. package/src/gateway/completion-inbox.ts +248 -16
  105. package/src/manager/run/persistence.ts +12 -0
  106. package/src/run/reporter.ts +12 -0
  107. package/src/runtime/query/__tests__/completion-does-not-erase-the-answer.test.ts +163 -0
  108. package/src/runtime/query/__tests__/completion-notification.test.ts +486 -34
  109. package/src/runtime/query/__tests__/context-size-on-the-wire.test.ts +122 -0
  110. package/src/runtime/query/guard.ts +32 -0
  111. package/src/runtime/query/iteration/__tests__/settle-grace.test.ts +265 -0
  112. package/src/runtime/query/iteration/index.ts +902 -636
  113. package/src/runtime/query/iteration/phases/__tests__/compaction-declined.test.ts +124 -0
  114. package/src/runtime/query/iteration/phases/compaction.ts +83 -10
  115. package/src/tools/__tests__/untrusted-envelope.test.ts +23 -0
  116. package/src/tools/coordinator/__tests__/completion-delivery.test.ts +147 -0
  117. package/src/tools/coordinator/__tests__/task-list.test.ts +72 -0
  118. package/src/tools/coordinator/index.ts +122 -15
  119. package/src/tools/untrusted-envelope.ts +9 -1
  120. package/src/types/agent/gateway.ts +18 -0
  121. package/src/types/provider/interface.ts +39 -0
  122. package/src/types/run/entity.ts +17 -0
  123. package/src/types/run/events.ts +75 -0
  124. package/src/types/run/prepare-step.ts +17 -2
  125. package/src/types/verification/index.ts +61 -0
  126. package/src/verification/__tests__/argument-pattern.test.ts +158 -0
  127. package/src/verification/gate.ts +45 -0
  128. package/src/verification/rules.ts +28 -0
@@ -34,6 +34,34 @@ export declare class GuardCoordinator {
34
34
  * one field, let the resume branch hand it over.
35
35
  */
36
36
  restoreElapsed(elapsedMs: number): void;
37
+ /**
38
+ * Wall-clock left before this run is asked to start finishing.
39
+ *
40
+ * NOT the time left before the deadline, and the difference is the whole
41
+ * point. The checks above run BETWEEN iterations, so anything that waits
42
+ * inside one cannot be stopped by them, and a caller sizing such a wait
43
+ * needs a number the run actually owns: a fixed two-minute hold measured
44
+ * against a run configured for twenty seconds kept it open for 120,267 ms.
45
+ *
46
+ * Measuring to the DEADLINE was the first attempt and it was wrong. The
47
+ * binding constraint is `budgetWarningThreshold`, the point at which this
48
+ * guard stops asking for more work and asks for a closing summary — that
49
+ * last slice exists so the run can produce an answer, and a wait sized
50
+ * against the deadline eats into it. Half of the time-to-deadline, started
51
+ * just under the threshold, ends at 95% of the budget: half the closing
52
+ * reserve spent waiting for a result the closing answer was supposed to
53
+ * use.
54
+ *
55
+ * Zero once the threshold has passed, which is also how a caller gets the
56
+ * re-evaluation it needs: `forceFinalize` is sampled at the top of an
57
+ * iteration and this is read when the wait is about to start, so a long
58
+ * iteration that crossed the line in between is told to wait for nothing.
59
+ *
60
+ * Reads through the same `startTime` the limit checks use, so a run
61
+ * resumed from a checkpoint (see `restoreElapsed`) reports the time left
62
+ * on the RUN rather than on the process now hosting it.
63
+ */
64
+ remainingBeforeFinalizeMs(): number;
37
65
  beforeIteration(runMgr: RunPersistence, abortSignal: AbortSignal): GuardCheckResult;
38
66
  }
39
67
  //# sourceMappingURL=guard.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"guard.d.ts","sourceRoot":"","sources":["../../../src/runtime/query/guard.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAA;AAEtE,OAAO,KAAK,EAAsB,UAAU,EAAE,MAAM,0BAA0B,CAAA;AAE9E,MAAM,WAAW,WAAW;IAC3B,WAAW,EAAE,MAAM,CAAA;IACnB,SAAS,EAAE,MAAM,CAAA;IACjB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB;;;;;;;OAOG;IACH,eAAe,CAAC,EAAE,MAAM,CAAA;CACxB;AAED,MAAM,WAAW,gBAAgB;IAChC,UAAU,EAAE,OAAO,CAAA;IACnB,aAAa,EAAE,OAAO,CAAA;IACtB,UAAU,CAAC,EAAE,UAAU,CAAA;IACvB,WAAW,EAAE,OAAO,CAAA;CACpB;AAED,qBAAa,gBAAgB;IAC5B,OAAO,CAAC,WAAW,CAAoB;IACvC,OAAO,CAAC,SAAS,CAAQ;gBAEb,MAAM,EAAE,WAAW;IAc/B;;;;;;;OAOG;IACH,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAIvC,eAAe,CAAC,MAAM,EAAE,cAAc,EAAE,WAAW,EAAE,WAAW,GAAG,gBAAgB;CAmCnF"}
1
+ {"version":3,"file":"guard.d.ts","sourceRoot":"","sources":["../../../src/runtime/query/guard.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAA;AAEtE,OAAO,KAAK,EAAsB,UAAU,EAAE,MAAM,0BAA0B,CAAA;AAE9E,MAAM,WAAW,WAAW;IAC3B,WAAW,EAAE,MAAM,CAAA;IACnB,SAAS,EAAE,MAAM,CAAA;IACjB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB;;;;;;;OAOG;IACH,eAAe,CAAC,EAAE,MAAM,CAAA;CACxB;AAED,MAAM,WAAW,gBAAgB;IAChC,UAAU,EAAE,OAAO,CAAA;IACnB,aAAa,EAAE,OAAO,CAAA;IACtB,UAAU,CAAC,EAAE,UAAU,CAAA;IACvB,WAAW,EAAE,OAAO,CAAA;CACpB;AAED,qBAAa,gBAAgB;IAC5B,OAAO,CAAC,WAAW,CAAoB;IACvC,OAAO,CAAC,SAAS,CAAQ;gBAEb,MAAM,EAAE,WAAW;IAc/B;;;;;;;OAOG;IACH,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAIvC;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BG;IACH,yBAAyB,IAAI,MAAM;IAKnC,eAAe,CAAC,MAAM,EAAE,cAAc,EAAE,WAAW,EAAE,WAAW,GAAG,gBAAgB;CAmCnF"}
@@ -21,6 +21,37 @@ export class GuardCoordinator {
21
21
  restoreElapsed(elapsedMs) {
22
22
  this.startTime = Date.now() - Math.max(0, elapsedMs);
23
23
  }
24
+ /**
25
+ * Wall-clock left before this run is asked to start finishing.
26
+ *
27
+ * NOT the time left before the deadline, and the difference is the whole
28
+ * point. The checks above run BETWEEN iterations, so anything that waits
29
+ * inside one cannot be stopped by them, and a caller sizing such a wait
30
+ * needs a number the run actually owns: a fixed two-minute hold measured
31
+ * against a run configured for twenty seconds kept it open for 120,267 ms.
32
+ *
33
+ * Measuring to the DEADLINE was the first attempt and it was wrong. The
34
+ * binding constraint is `budgetWarningThreshold`, the point at which this
35
+ * guard stops asking for more work and asks for a closing summary — that
36
+ * last slice exists so the run can produce an answer, and a wait sized
37
+ * against the deadline eats into it. Half of the time-to-deadline, started
38
+ * just under the threshold, ends at 95% of the budget: half the closing
39
+ * reserve spent waiting for a result the closing answer was supposed to
40
+ * use.
41
+ *
42
+ * Zero once the threshold has passed, which is also how a caller gets the
43
+ * re-evaluation it needs: `forceFinalize` is sampled at the top of an
44
+ * iteration and this is read when the wait is about to start, so a long
45
+ * iteration that crossed the line in between is told to wait for nothing.
46
+ *
47
+ * Reads through the same `startTime` the limit checks use, so a run
48
+ * resumed from a checkpoint (see `restoreElapsed`) reports the time left
49
+ * on the RUN rather than on the process now hosting it.
50
+ */
51
+ remainingBeforeFinalizeMs() {
52
+ const finalizeAt = this.limitConfig.timeoutMs * this.limitConfig.budgetWarningThreshold;
53
+ return Math.max(0, finalizeAt - (Date.now() - this.startTime));
54
+ }
24
55
  beforeIteration(runMgr, abortSignal) {
25
56
  const limitState = {
26
57
  aborted: abortSignal.aborted,
@@ -1 +1 @@
1
- {"version":3,"file":"guard.js","sourceRoot":"","sources":["../../../src/runtime/query/guard.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAA;AA0BjF,MAAM,OAAO,gBAAgB;IACpB,WAAW,CAAoB;IAC/B,SAAS,CAAQ;IAEzB,YAAY,MAAmB;QAC9B,IAAI,CAAC,WAAW,GAAG,gBAAgB,CAClC,MAAM,CAAC,WAAW,EAClB,MAAM,CAAC,SAAS,EAChB,MAAM,CAAC,YAAY,EACnB,MAAM,CAAC,aAAa,CACpB,CAAA;QACD,oEAAoE;QACpE,gEAAgE;QAChE,kEAAkE;QAClE,SAAS;QACT,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,eAAe,IAAI,CAAC,CAAC,CAAA;IACvE,CAAC;IAED;;;;;;;OAOG;IACH,cAAc,CAAC,SAAiB;QAC/B,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,SAAS,CAAC,CAAA;IACrD,CAAC;IAED,eAAe,CAAC,MAAsB,EAAE,WAAwB;QAC/D,MAAM,UAAU,GAAG;YAClB,OAAO,EAAE,WAAW,CAAC,OAAO;YAC5B,WAAW,EAAE,MAAM,CAAC,UAAU,CAAC,WAAW;YAC1C,SAAS,EAAE,MAAM,CAAC,QAAQ,CAAC,SAAS;YACpC,gBAAgB,EAAE,MAAM,CAAC,gBAAgB;YACzC,SAAS,EAAE,IAAI,CAAC,SAAS;SACzB,CAAA;QAED,MAAM,WAAW,GAAG,mBAAmB,CAAC,IAAI,CAAC,WAAW,EAAE,UAAU,CAAC,CAAA;QAErE,IAAI,WAAW,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;YACtC,OAAO;gBACN,UAAU,EAAE,IAAI;gBAChB,aAAa,EAAE,KAAK;gBACpB,UAAU,EAAE,WAAW,CAAC,MAAM;gBAC9B,WAAW,EAAE,WAAW,CAAC,MAAM,KAAK,WAAW;aAC/C,CAAA;QACF,CAAC;QAED,IAAI,WAAW,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YACpC,OAAO;gBACN,UAAU,EAAE,KAAK;gBACjB,aAAa,EAAE,IAAI;gBACnB,UAAU,EAAE,WAAW,CAAC,MAAM;gBAC9B,WAAW,EAAE,KAAK;aAClB,CAAA;QACF,CAAC;QAED,OAAO;YACN,UAAU,EAAE,KAAK;YACjB,aAAa,EAAE,KAAK;YACpB,WAAW,EAAE,KAAK;SAClB,CAAA;IACF,CAAC;CACD"}
1
+ {"version":3,"file":"guard.js","sourceRoot":"","sources":["../../../src/runtime/query/guard.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAA;AA0BjF,MAAM,OAAO,gBAAgB;IACpB,WAAW,CAAoB;IAC/B,SAAS,CAAQ;IAEzB,YAAY,MAAmB;QAC9B,IAAI,CAAC,WAAW,GAAG,gBAAgB,CAClC,MAAM,CAAC,WAAW,EAClB,MAAM,CAAC,SAAS,EAChB,MAAM,CAAC,YAAY,EACnB,MAAM,CAAC,aAAa,CACpB,CAAA;QACD,oEAAoE;QACpE,gEAAgE;QAChE,kEAAkE;QAClE,SAAS;QACT,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,eAAe,IAAI,CAAC,CAAC,CAAA;IACvE,CAAC;IAED;;;;;;;OAOG;IACH,cAAc,CAAC,SAAiB;QAC/B,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,SAAS,CAAC,CAAA;IACrD,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BG;IACH,yBAAyB;QACxB,MAAM,UAAU,GAAG,IAAI,CAAC,WAAW,CAAC,SAAS,GAAG,IAAI,CAAC,WAAW,CAAC,sBAAsB,CAAA;QACvF,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,UAAU,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAA;IAC/D,CAAC;IAED,eAAe,CAAC,MAAsB,EAAE,WAAwB;QAC/D,MAAM,UAAU,GAAG;YAClB,OAAO,EAAE,WAAW,CAAC,OAAO;YAC5B,WAAW,EAAE,MAAM,CAAC,UAAU,CAAC,WAAW;YAC1C,SAAS,EAAE,MAAM,CAAC,QAAQ,CAAC,SAAS;YACpC,gBAAgB,EAAE,MAAM,CAAC,gBAAgB;YACzC,SAAS,EAAE,IAAI,CAAC,SAAS;SACzB,CAAA;QAED,MAAM,WAAW,GAAG,mBAAmB,CAAC,IAAI,CAAC,WAAW,EAAE,UAAU,CAAC,CAAA;QAErE,IAAI,WAAW,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;YACtC,OAAO;gBACN,UAAU,EAAE,IAAI;gBAChB,aAAa,EAAE,KAAK;gBACpB,UAAU,EAAE,WAAW,CAAC,MAAM;gBAC9B,WAAW,EAAE,WAAW,CAAC,MAAM,KAAK,WAAW;aAC/C,CAAA;QACF,CAAC;QAED,IAAI,WAAW,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YACpC,OAAO;gBACN,UAAU,EAAE,KAAK;gBACjB,aAAa,EAAE,IAAI;gBACnB,UAAU,EAAE,WAAW,CAAC,MAAM;gBAC9B,WAAW,EAAE,KAAK;aAClB,CAAA;QACF,CAAC;QAED,OAAO;YACN,UAAU,EAAE,KAAK;YACjB,aAAa,EAAE,KAAK;YACpB,WAAW,EAAE,KAAK;SAClB,CAAA;IACF,CAAC;CACD"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=settle-grace.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settle-grace.test.d.ts","sourceRoot":"","sources":["../../../../../src/runtime/query/iteration/__tests__/settle-grace.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,226 @@
1
+ import { mkdtemp, rm } from 'node:fs/promises';
2
+ import { tmpdir } from 'node:os';
3
+ import { join } from 'node:path';
4
+ import { afterEach, describe, expect, it } from 'vitest';
5
+ import { CompletionInbox } from '../../../../gateway/completion-inbox.js';
6
+ import { ToolRegistry } from '../../../../registry/tool/execute.js';
7
+ import { DELEGATION_TIMEOUT_MS } from '../../../../tools/coordinator/index.js';
8
+ import { createUserMessage } from '../../../../types/message/index.js';
9
+ import { GuardCoordinator } from '../../guard.js';
10
+ import { drainQuery } from '../../index.js';
11
+ import { settleGraceMs } from '../index.js';
12
+ /**
13
+ * How long a finishing run waits for a worker it launched.
14
+ *
15
+ * This used to be a constant — 120 seconds, unrelated to the run holding it.
16
+ * Measured before the change: a run configured `timeoutMs: 20_000` was held
17
+ * open for 120,267 ms, because the hold sits INSIDE an iteration and the guard
18
+ * only checks between them, so nothing could interrupt it. The same constant
19
+ * abandoned workers observed at 4m21s, 5m58s and 8m04s on runs that had hours
20
+ * left.
21
+ */
22
+ describe('the grace is a share of what the run has left', () => {
23
+ it('takes half, so the turn that reads the result still has time to happen', () => {
24
+ // The fraction is the whole argument. Spending everything remaining
25
+ // would deliver a notification into a run with no turn left to act on
26
+ // it — the failure this mechanism exists to prevent, in a new costume.
27
+ expect(settleGraceMs(60_000)).toBe(30_000);
28
+ });
29
+ it('holds for nothing when the run has nothing left', () => {
30
+ // A decision, not a rounding artefact: with no time left there is no
31
+ // turn in which to read a notification, so waiting can only delay a
32
+ // stop that is already due. Nothing is lost — `waitForArrival` returns
33
+ // before it looks at its timer when a completion is already in hand.
34
+ expect(settleGraceMs(0)).toBe(0);
35
+ });
36
+ it('cannot outlive the deadline it was derived from', () => {
37
+ // Bounded by construction, which is why the guard's inability to
38
+ // interrupt a hold needs no new interrupt seam.
39
+ for (const remaining of [1, 250, 30_000, 600_000, 3_600_000]) {
40
+ expect(settleGraceMs(remaining)).toBeLessThan(remaining);
41
+ }
42
+ });
43
+ it('stops at the longest this subsystem ever waits for a worker', () => {
44
+ // Only reachable for a host whose run timeout exceeds two hours.
45
+ expect(settleGraceMs(10 * 60 * 60 * 1000)).toBe(DELEGATION_TIMEOUT_MS);
46
+ });
47
+ });
48
+ describe('the guard reports time to the finalize point, not to the deadline', () => {
49
+ it('stops short of the closing reserve', () => {
50
+ // 90% of the budget, not 100%. The last tenth is what the guard keeps
51
+ // so a run can produce a closing answer; a wait sized against the
52
+ // deadline spends it on waiting instead.
53
+ const guard = new GuardCoordinator({ tokenBudget: 1_000, timeoutMs: 60_000 });
54
+ const remaining = guard.remainingBeforeFinalizeMs();
55
+ expect(remaining).toBeLessThanOrEqual(54_000);
56
+ expect(remaining).toBeGreaterThan(50_000);
57
+ });
58
+ it('subtracts the time a previous process already spent', () => {
59
+ // A run resumed from a checkpoint gets the remainder of ITS budget, not
60
+ // a fresh clock — otherwise N resumes buy N x timeoutMs, and a hold
61
+ // sized from the fresh clock would outlive the real deadline.
62
+ const guard = new GuardCoordinator({ tokenBudget: 1_000, timeoutMs: 60_000 });
63
+ guard.restoreElapsed(50_000);
64
+ expect(guard.remainingBeforeFinalizeMs()).toBeLessThanOrEqual(4_000);
65
+ expect(guard.remainingBeforeFinalizeMs()).toBeGreaterThan(1_000);
66
+ });
67
+ it('reports nothing once the run is already past the finalize point', () => {
68
+ // 95% elapsed: the guard is about to ask for a closing summary, and a
69
+ // hold opened here would be taken out of the answer's time.
70
+ const guard = new GuardCoordinator({ tokenBudget: 1_000, timeoutMs: 60_000 });
71
+ guard.restoreElapsed(57_000);
72
+ expect(guard.remainingBeforeFinalizeMs()).toBe(0);
73
+ });
74
+ it('never reports a negative remainder', () => {
75
+ const guard = new GuardCoordinator({ tokenBudget: 1_000, timeoutMs: 1_000 });
76
+ guard.restoreElapsed(500_000);
77
+ expect(guard.remainingBeforeFinalizeMs()).toBe(0);
78
+ });
79
+ });
80
+ describe('the hold cannot reach into the run closing reserve', () => {
81
+ it('ends before the finalize point however late it starts', () => {
82
+ // The failure the finalize-relative input exists for. Against the
83
+ // DEADLINE, a hold beginning at elapsed fraction e ends at
84
+ // 0.5 + 0.5e — so one starting just under the 0.9 threshold ends at
85
+ // 95% of the budget, with half the closing reserve gone. Against the
86
+ // finalize point it cannot cross 90% at all.
87
+ const timeoutMs = 60_000;
88
+ const finalizeAt = timeoutMs * 0.9;
89
+ for (const elapsed of [0, 30_000, 50_000, 53_000, 53_900]) {
90
+ const remainingBeforeFinalize = Math.max(0, finalizeAt - elapsed);
91
+ const endsAt = elapsed + settleGraceMs(remainingBeforeFinalize);
92
+ expect(endsAt, `a hold starting at ${elapsed}ms crossed the finalize point`).toBeLessThanOrEqual(finalizeAt);
93
+ }
94
+ });
95
+ });
96
+ const ZERO_USAGE = {
97
+ promptTokens: 0,
98
+ completionTokens: 0,
99
+ totalTokens: 0,
100
+ cachedTokens: 0,
101
+ cacheWriteTokens: 0,
102
+ };
103
+ /** Answers straight away. The hold is the only thing that can delay this run. */
104
+ class AnswersImmediately {
105
+ id = 'answers';
106
+ name = 'Answers Immediately';
107
+ async *chatStream() {
108
+ yield { id: 'm1', delta: { content: 'Done.' } };
109
+ yield { id: 'm1', delta: {}, finishReason: 'stop', usage: ZERO_USAGE };
110
+ }
111
+ }
112
+ const workdirs = [];
113
+ afterEach(async () => {
114
+ await Promise.all(workdirs.map((dir) => rm(dir, { recursive: true, force: true })));
115
+ workdirs.length = 0;
116
+ });
117
+ /**
118
+ * The loop, not the helper.
119
+ *
120
+ * `settleGraceMs` has its own tests above and every one of them passes with
121
+ * the loop still calling a constant. Only a real run can say which number the
122
+ * hold actually used.
123
+ */
124
+ describe('the hold a run pays is the one its own budget allows', () => {
125
+ async function runHoldingFor(timeoutMs) {
126
+ const workingDirectory = await mkdtemp(join(tmpdir(), 'namzu-grace-'));
127
+ workdirs.push(workingDirectory);
128
+ const inbox = new CompletionInbox();
129
+ // `getTask` because the inbox asks the gateway about a task whose
130
+ // completion may have been announced before the launch was recorded.
131
+ // A double that omits a method the interface requires is not a smaller
132
+ // gateway, it is a broken one.
133
+ inbox.attach({ onTaskCompleted: () => () => { }, getTask: () => undefined });
134
+ // Outstanding and never settling: the worst case the bound exists for.
135
+ // `expect` records the launch as well, so the inbox owns this task.
136
+ inbox.expect('tsk_never');
137
+ const startedAt = Date.now();
138
+ await drainQuery({
139
+ provider: new AnswersImmediately(),
140
+ tools: new ToolRegistry(),
141
+ completionInbox: inbox,
142
+ agentId: 'agent_test',
143
+ agentName: 'Test Agent',
144
+ messages: [createUserMessage('go')],
145
+ workingDirectory,
146
+ runConfig: {
147
+ model: 'mock-model',
148
+ timeoutMs,
149
+ tokenBudget: 100_000,
150
+ maxIterations: 2,
151
+ maxResponseTokens: 256,
152
+ },
153
+ sessionId: 'ses_grace',
154
+ threadId: 'thd_grace',
155
+ projectId: 'prj_grace',
156
+ tenantId: 'tnt_grace',
157
+ });
158
+ return Date.now() - startedAt;
159
+ }
160
+ it('a two-second run does not wait two minutes for a worker', async () => {
161
+ const elapsed = await runHoldingFor(2_000);
162
+ // Half of two seconds, plus whatever the turn itself costs. The number
163
+ // this replaced would have parked here for 120 s regardless.
164
+ expect(elapsed, 'the hold outlived the run budget that bounds it').toBeLessThan(5_000);
165
+ }, 200_000);
166
+ it('a run with no time left does not hold at all', async () => {
167
+ // The guard stops this run at the top of its first iteration, so no
168
+ // hold is even reached — which is the point: the last tenth of a run
169
+ // already never holds, so no artificial minimum has to defend it.
170
+ const elapsed = await runHoldingFor(1);
171
+ expect(elapsed).toBeLessThan(2_000);
172
+ }, 200_000);
173
+ it('still delivers a completion that arrives inside the grace', async () => {
174
+ // The bound must not become an excuse to drop what was nearly ready.
175
+ const workingDirectory = await mkdtemp(join(tmpdir(), 'namzu-grace-hit-'));
176
+ workdirs.push(workingDirectory);
177
+ const inbox = new CompletionInbox();
178
+ let announce;
179
+ inbox.attach({
180
+ onTaskCompleted: (cb) => {
181
+ announce = cb;
182
+ return () => {
183
+ announce = undefined;
184
+ };
185
+ },
186
+ getTask: () => undefined,
187
+ });
188
+ inbox.expect('tsk_soon');
189
+ const timer = setTimeout(() => {
190
+ announce?.({
191
+ taskId: 'tsk_soon',
192
+ agentId: 'reviewer',
193
+ state: 'completed',
194
+ createdAt: 0,
195
+ completedAt: 1,
196
+ result: { status: 'completed', result: 'ARRIVED INSIDE THE GRACE' },
197
+ });
198
+ }, 150);
199
+ timer.unref?.();
200
+ const run = await drainQuery({
201
+ provider: new AnswersImmediately(),
202
+ tools: new ToolRegistry(),
203
+ completionInbox: inbox,
204
+ agentId: 'agent_test',
205
+ agentName: 'Test Agent',
206
+ messages: [createUserMessage('go')],
207
+ workingDirectory,
208
+ runConfig: {
209
+ model: 'mock-model',
210
+ timeoutMs: 30_000,
211
+ tokenBudget: 100_000,
212
+ maxIterations: 3,
213
+ maxResponseTokens: 256,
214
+ },
215
+ sessionId: 'ses_grace',
216
+ threadId: 'thd_grace',
217
+ projectId: 'prj_grace',
218
+ tenantId: 'tnt_grace',
219
+ });
220
+ const userText = run.messages
221
+ .filter((m) => m.role === 'user')
222
+ .map((m) => (typeof m.content === 'string' ? m.content : ''));
223
+ expect(userText.some((m) => m.includes('ARRIVED INSIDE THE GRACE'))).toBe(true);
224
+ }, 200_000);
225
+ });
226
+ //# sourceMappingURL=settle-grace.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settle-grace.test.js","sourceRoot":"","sources":["../../../../../src/runtime/query/iteration/__tests__/settle-grace.test.ts"],"names":[],"mappings":"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"}
@@ -4,10 +4,62 @@ import type { IterationContext } from './phases/index.js';
4
4
  export type { IterationContext } from './phases/index.js';
5
5
  export type { PhaseSignal } from './phases/index.js';
6
6
  export type { ToolReviewOutcome } from './phases/index.js';
7
+ /**
8
+ * How long a finishing run waits for a background worker it launched.
9
+ *
10
+ * Derived from the run rather than fixed, because a constant is wrong in both
11
+ * directions at once. The 120 seconds this replaces held a run configured for
12
+ * a twenty-second timeout open for 120,267 ms — six times its own budget, and
13
+ * unreachable by the guard, which only checks between iterations — while on an
14
+ * hour-long run it abandoned workers measured at 4m21s, 5m58s and 8m04s, all
15
+ * of them well inside the hour the delegation tools themselves declare.
16
+ *
17
+ * **Bounded by construction, and against the right boundary.** The input is
18
+ * time-to-FINALIZE, not time-to-deadline (see
19
+ * `GuardCoordinator.remainingBeforeFinalizeMs`). Measuring to the deadline was
20
+ * the first attempt and it was wrong in a way that looked safe: a hold cannot
21
+ * outlive the deadline either way, but half of the time-to-deadline started
22
+ * just under the warning threshold ends at 95% of the budget — so the slice
23
+ * that exists for the run to produce a closing answer is half spent waiting
24
+ * for the result that answer was supposed to use. Against the finalize point
25
+ * the hold cannot reach the reserve at all, which is what makes the guard's
26
+ * inability to interrupt a hold a non-issue rather than a smaller issue.
27
+ *
28
+ * **The floor of zero is a decision, not a clamp artefact.** A run with no
29
+ * time left before it must start finishing has no turn in which to read a
30
+ * notification, so waiting could only delay a stop that is already due.
31
+ * Nothing is lost by it: `CompletionInbox.waitForArrival` returns before it
32
+ * looks at its timer when a completion is already in hand, so a zero grace
33
+ * still delivers everything that has arrived. No minimum is invented on top,
34
+ * because zero is exactly what a run past the threshold should wait — and
35
+ * reading the remainder at hold time rather than trusting `forceFinalize`,
36
+ * which is sampled at the top of the iteration, is what makes a long iteration
37
+ * that crossed the line in between compute it.
38
+ *
39
+ * **The ceiling is the longest anything in this subsystem waits for a
40
+ * delegated worker.** It binds only for a host whose run timeout exceeds
41
+ * roughly two and a quarter hours; below that the fraction is smaller.
42
+ */
43
+ export declare function settleGraceMs(remainingBeforeFinalizeMs: number): number;
7
44
  export declare class IterationOrchestrator {
8
45
  private ctx;
9
46
  /** Rejections so far. See {@link DEFAULT_ANSWER_REVIEW_LIMIT}. */
10
47
  private answerReviewAttempts;
48
+ /**
49
+ * The previous iteration held a `stopWhen` decision open for a worker.
50
+ *
51
+ * Set when the stop predicate fired and the run took one extra turn to
52
+ * read a delegated result, so the turn that then ends the run can report
53
+ * WHY it is over. Without it the outcome was right and the record was
54
+ * wrong: the run stopped because the host said so and reported `end_turn`,
55
+ * and this repo carries thirteen `StopReason` values precisely so that a
56
+ * run which ends for a nameable reason names it.
57
+ *
58
+ * Lives for exactly one iteration — see the read-and-clear at the top of
59
+ * the loop, which is the only site that touches it besides the one that
60
+ * sets it.
61
+ */
62
+ private stopDeferredForOutstandingWork;
11
63
  constructor(ctx: IterationContext);
12
64
  /**
13
65
  * Adopt the run's span after construction.
@@ -18,6 +70,46 @@ export declare class IterationOrchestrator {
18
70
  */
19
71
  setRootSpan(span: Span): void;
20
72
  runLoop(): AsyncGenerator<RunEvent>;
73
+ /**
74
+ * Hold the run open for a worker that has not finished, and deliver it.
75
+ *
76
+ * Returns whether a completion arrived and was put in the transcript — the
77
+ * caller continues the loop on `true`, so the model gets a turn in which to
78
+ * USE the result. That turn is the entire justification for waiting, which
79
+ * is why only the exits that can still take one call this.
80
+ *
81
+ * Bounded by `settleGraceMs` and by `maxIterations`, so a worker that never
82
+ * finishes cannot keep the run open.
83
+ */
84
+ private holdForOutstandingWork;
85
+ /**
86
+ * Account for delegated work on the way out: deliver what arrived, and say
87
+ * what did not.
88
+ *
89
+ * A run that ends with a worker outstanding must not leave the impression
90
+ * that the worker's result was delivered. There are exactly two honest
91
+ * outcomes and this does both:
92
+ *
93
+ * - **What has already arrived is delivered.** It makes no false claim,
94
+ * and dropping it is pure loss — the message rides out on
95
+ * `Run.messages`, so a host reads it and the next turn of a continued
96
+ * thread starts with it. This does NOT wait: a hold buys the model a
97
+ * turn in which to USE a result, and on an exit whose answer is already
98
+ * decided there is no such turn, so waiting would delay a settled answer
99
+ * to append text this run will not read. The bounded hold stays where it
100
+ * was, on the exits that do have a turn left.
101
+ * - **What is still running is NAMED, not cancelled.** Giving up on a wait
102
+ * is a statement about the waiter, not about the work — the rule
103
+ * `wait-with-idle-bound.ts` already states for the same subsystem — and
104
+ * "the parent answered early" is a weaker warrant for killing a child
105
+ * than "the clock ran out", not a stronger one. Killing a worker that
106
+ * may be mid-write is a policy only the host can judge, and it has
107
+ * `cancel_task` and the run controller to judge it with.
108
+ */
109
+ private settleOutstandingWork;
110
+ /** Delegated work this run walked away from. See {@link settleOutstandingWork}. */
111
+ private recordAbandonedWork;
112
+ private deliverArrivedCompletions;
21
113
  /**
22
114
  * Ask the host how to shape this step.
23
115
  *
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/runtime/query/iteration/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,IAAI,EAAkB,MAAM,oBAAoB,CAAA;AA6B9D,OAAO,KAAK,EAEX,QAAQ,EACR,UAAU,EAEV,MAAM,6BAA6B,CAAA;AAUpC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AAMzD,YAAY,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AACzD,YAAY,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AACpD,YAAY,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AAsB1D,qBAAa,qBAAqB;IACjC,OAAO,CAAC,GAAG,CAAkB;IAC7B,kEAAkE;IAClE,OAAO,CAAC,oBAAoB,CAAI;gBAEpB,GAAG,EAAE,gBAAgB;IAIjC;;;;;;OAMG;IACH,WAAW,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI;IAItB,OAAO,IAAI,cAAc,CAAC,QAAQ,CAAC;IAsuB1C;;;;;;;OAOG;YACW,WAAW;IA2EzB,6DAA6D;IAC7D,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAmB;IAEzC,QAAQ,IAAI,SAAS,UAAU,EAAE;IAIjC;;;;;;;OAOG;IACH,OAAO,CAAC,UAAU;IAgDlB,wEAAwE;IACxE,OAAO,CAAC,wBAAwB,CAAI;IACpC,OAAO,CAAC,oBAAoB,CAAQ;IAEpC,OAAO,CAAC,0BAA0B;IAIlC,uEAAuE;IACvE,OAAO,CAAC,qBAAqB;IAI7B;;;;;;;OAOG;IACH;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,kBAAkB;IA4B1B,OAAO,CAAC,uBAAuB;IAe/B;;;;;;;;;;;OAWG;YACW,YAAY;IAiB1B,6DAA6D;YAC/C,UAAU;IAsBxB,OAAO,CAAC,oBAAoB;YAOd,oBAAoB;CA6ElC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/runtime/query/iteration/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,IAAI,EAAkB,MAAM,oBAAoB,CAAA;AA+B9D,OAAO,KAAK,EAEX,QAAQ,EACR,UAAU,EAEV,MAAM,6BAA6B,CAAA;AAUpC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AAMzD,YAAY,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AACzD,YAAY,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AACpD,YAAY,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AA+B1D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,wBAAgB,aAAa,CAAC,yBAAyB,EAAE,MAAM,GAAG,MAAM,CAKvE;AAED,qBAAa,qBAAqB;IACjC,OAAO,CAAC,GAAG,CAAkB;IAC7B,kEAAkE;IAClE,OAAO,CAAC,oBAAoB,CAAI;IAChC;;;;;;;;;;;;;OAaG;IACH,OAAO,CAAC,8BAA8B,CAAQ;gBAElC,GAAG,EAAE,gBAAgB;IAIjC;;;;;;OAMG;IACH,WAAW,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI;IAItB,OAAO,IAAI,cAAc,CAAC,QAAQ,CAAC;IA4yB1C;;;;;;;;;;OAUG;YACY,sBAAsB;IA+BrC;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACH,OAAO,CAAC,qBAAqB;IAK7B,mFAAmF;IACnF,OAAO,CAAC,mBAAmB;IAW3B,OAAO,CAAC,yBAAyB;IA8BjC;;;;;;;OAOG;YACW,WAAW;IAyFzB,6DAA6D;IAC7D,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAmB;IAEzC,QAAQ,IAAI,SAAS,UAAU,EAAE;IAIjC;;;;;;;OAOG;IACH,OAAO,CAAC,UAAU;IAgDlB,wEAAwE;IACxE,OAAO,CAAC,wBAAwB,CAAI;IACpC,OAAO,CAAC,oBAAoB,CAAQ;IAEpC,OAAO,CAAC,0BAA0B;IAIlC,uEAAuE;IACvE,OAAO,CAAC,qBAAqB;IAI7B;;;;;;;OAOG;IACH;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,kBAAkB;IA4B1B,OAAO,CAAC,uBAAuB;IAe/B;;;;;;;;;;;OAWG;YACW,YAAY;IAiB1B,6DAA6D;YAC/C,UAAU;IAsBxB,OAAO,CAAC,oBAAoB;YAOd,oBAAoB;CA6ElC"}