@namzu/sdk 52.2.0 → 52.4.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 (143) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/bridge/a2a/mapper.d.ts.map +1 -1
  3. package/dist/bridge/a2a/mapper.js +3 -0
  4. package/dist/bridge/a2a/mapper.js.map +1 -1
  5. package/dist/bridge/sse/mapper.d.ts.map +1 -1
  6. package/dist/bridge/sse/mapper.js +18 -0
  7. package/dist/bridge/sse/mapper.js.map +1 -1
  8. package/dist/connector/web/guarded-fetch.d.ts.map +1 -1
  9. package/dist/connector/web/guarded-fetch.js +44 -0
  10. package/dist/connector/web/guarded-fetch.js.map +1 -1
  11. package/dist/contracts/session/api.d.ts +1 -1
  12. package/dist/contracts/session/api.d.ts.map +1 -1
  13. package/dist/index.d.ts +4 -2
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +2 -1
  16. package/dist/index.js.map +1 -1
  17. package/dist/pals/channels/routing.d.ts +1 -1
  18. package/dist/pals/channels/schema.d.ts +7 -7
  19. package/dist/pals/communication/ingress-schema.d.ts +32 -32
  20. package/dist/process/kill-tree.d.ts +3 -2
  21. package/dist/process/kill-tree.d.ts.map +1 -1
  22. package/dist/process/kill-tree.js.map +1 -1
  23. package/dist/public-runtime.d.ts +2 -2
  24. package/dist/public-runtime.d.ts.map +1 -1
  25. package/dist/public-runtime.js +1 -1
  26. package/dist/public-runtime.js.map +1 -1
  27. package/dist/public-tools.d.ts +3 -1
  28. package/dist/public-tools.d.ts.map +1 -1
  29. package/dist/public-tools.js +6 -1
  30. package/dist/public-tools.js.map +1 -1
  31. package/dist/runtime/jobs/awaited-jobs.d.ts +59 -2
  32. package/dist/runtime/jobs/awaited-jobs.d.ts.map +1 -1
  33. package/dist/runtime/jobs/awaited-jobs.js +108 -5
  34. package/dist/runtime/jobs/awaited-jobs.js.map +1 -1
  35. package/dist/runtime/jobs/monitor-stream.d.ts +96 -0
  36. package/dist/runtime/jobs/monitor-stream.d.ts.map +1 -0
  37. package/dist/runtime/jobs/monitor-stream.js +221 -0
  38. package/dist/runtime/jobs/monitor-stream.js.map +1 -0
  39. package/dist/runtime/jobs/registry.d.ts +175 -1
  40. package/dist/runtime/jobs/registry.d.ts.map +1 -1
  41. package/dist/runtime/jobs/registry.js +267 -19
  42. package/dist/runtime/jobs/registry.js.map +1 -1
  43. package/dist/runtime/jobs/websocket-source.d.ts +120 -0
  44. package/dist/runtime/jobs/websocket-source.d.ts.map +1 -0
  45. package/dist/runtime/jobs/websocket-source.js +379 -0
  46. package/dist/runtime/jobs/websocket-source.js.map +1 -0
  47. package/dist/runtime/query/executor.d.ts +9 -0
  48. package/dist/runtime/query/executor.d.ts.map +1 -1
  49. package/dist/runtime/query/executor.js +6 -0
  50. package/dist/runtime/query/executor.js.map +1 -1
  51. package/dist/runtime/query/index.d.ts +16 -0
  52. package/dist/runtime/query/index.d.ts.map +1 -1
  53. package/dist/runtime/query/index.js +93 -3
  54. package/dist/runtime/query/index.js.map +1 -1
  55. package/dist/runtime/query/iteration/outstanding-work.d.ts +14 -0
  56. package/dist/runtime/query/iteration/outstanding-work.d.ts.map +1 -1
  57. package/dist/runtime/query/iteration/outstanding-work.js +64 -3
  58. package/dist/runtime/query/iteration/outstanding-work.js.map +1 -1
  59. package/dist/runtime/query/iteration/phases/context.d.ts +9 -0
  60. package/dist/runtime/query/iteration/phases/context.d.ts.map +1 -1
  61. package/dist/runtime/query/iteration/phases/context.js.map +1 -1
  62. package/dist/runtime/query/iteration/phases/tool-review.d.ts.map +1 -1
  63. package/dist/runtime/query/iteration/phases/tool-review.js +11 -2
  64. package/dist/runtime/query/iteration/phases/tool-review.js.map +1 -1
  65. package/dist/runtime/query/release-turn.d.ts.map +1 -1
  66. package/dist/runtime/query/release-turn.js +35 -0
  67. package/dist/runtime/query/release-turn.js.map +1 -1
  68. package/dist/runtime/query/steering.d.ts +35 -0
  69. package/dist/runtime/query/steering.d.ts.map +1 -1
  70. package/dist/runtime/query/steering.js +134 -0
  71. package/dist/runtime/query/steering.js.map +1 -1
  72. package/dist/runtime/query/tooling.d.ts +4 -0
  73. package/dist/runtime/query/tooling.d.ts.map +1 -1
  74. package/dist/runtime/query/tooling.js +4 -0
  75. package/dist/runtime/query/tooling.js.map +1 -1
  76. package/dist/runtime/system-events.d.ts +9 -0
  77. package/dist/runtime/system-events.d.ts.map +1 -1
  78. package/dist/runtime/system-events.js +12 -0
  79. package/dist/runtime/system-events.js.map +1 -1
  80. package/dist/tools/builtins/bash.d.ts +3 -0
  81. package/dist/tools/builtins/bash.d.ts.map +1 -1
  82. package/dist/tools/builtins/bash.js +3 -2
  83. package/dist/tools/builtins/bash.js.map +1 -1
  84. package/dist/tools/builtins/index.d.ts +1 -0
  85. package/dist/tools/builtins/index.d.ts.map +1 -1
  86. package/dist/tools/builtins/index.js +1 -0
  87. package/dist/tools/builtins/index.js.map +1 -1
  88. package/dist/tools/builtins/job.d.ts.map +1 -1
  89. package/dist/tools/builtins/job.js +6 -2
  90. package/dist/tools/builtins/job.js.map +1 -1
  91. package/dist/tools/builtins/monitor.d.ts +58 -0
  92. package/dist/tools/builtins/monitor.d.ts.map +1 -0
  93. package/dist/tools/builtins/monitor.js +306 -0
  94. package/dist/tools/builtins/monitor.js.map +1 -0
  95. package/dist/tools/builtins/wait-for-job.d.ts +1 -1
  96. package/dist/turn/reporter.d.ts.map +1 -1
  97. package/dist/turn/reporter.js +8 -0
  98. package/dist/turn/reporter.js.map +1 -1
  99. package/dist/types/sandbox/index.d.ts +10 -0
  100. package/dist/types/sandbox/index.d.ts.map +1 -1
  101. package/dist/types/sandbox/index.js.map +1 -1
  102. package/dist/types/session/events.d.ts +30 -4
  103. package/dist/types/session/events.d.ts.map +1 -1
  104. package/dist/types/session/events.js +6 -1
  105. package/dist/types/session/events.js.map +1 -1
  106. package/dist/types/session/records.d.ts +2 -2
  107. package/dist/types/session/records.d.ts.map +1 -1
  108. package/dist/types/session/records.js +3 -2
  109. package/dist/types/session/records.js.map +1 -1
  110. package/dist/types/tool/index.d.ts +60 -0
  111. package/dist/types/tool/index.d.ts.map +1 -1
  112. package/dist/types/tool/index.js.map +1 -1
  113. package/package.json +4 -2
  114. package/src/bridge/a2a/mapper.ts +3 -0
  115. package/src/bridge/sse/mapper.ts +18 -0
  116. package/src/connector/web/guarded-fetch.ts +41 -0
  117. package/src/contracts/session/api.ts +1 -0
  118. package/src/index.ts +23 -0
  119. package/src/process/kill-tree.ts +4 -2
  120. package/src/public-runtime.ts +4 -1
  121. package/src/public-tools.ts +14 -0
  122. package/src/runtime/jobs/awaited-jobs.ts +127 -5
  123. package/src/runtime/jobs/monitor-stream.ts +269 -0
  124. package/src/runtime/jobs/registry.ts +437 -23
  125. package/src/runtime/jobs/websocket-source.ts +438 -0
  126. package/src/runtime/query/executor.ts +15 -0
  127. package/src/runtime/query/index.ts +116 -3
  128. package/src/runtime/query/iteration/outstanding-work.ts +64 -3
  129. package/src/runtime/query/iteration/phases/context.ts +9 -0
  130. package/src/runtime/query/iteration/phases/tool-review.ts +21 -9
  131. package/src/runtime/query/release-turn.ts +33 -0
  132. package/src/runtime/query/steering.ts +170 -0
  133. package/src/runtime/query/tooling.ts +8 -0
  134. package/src/runtime/system-events.ts +17 -0
  135. package/src/tools/builtins/bash.ts +3 -2
  136. package/src/tools/builtins/index.ts +6 -0
  137. package/src/tools/builtins/job.ts +11 -2
  138. package/src/tools/builtins/monitor.ts +376 -0
  139. package/src/turn/reporter.ts +8 -0
  140. package/src/types/sandbox/index.ts +10 -0
  141. package/src/types/session/events.ts +31 -4
  142. package/src/types/session/records.ts +3 -2
  143. package/src/types/tool/index.ts +51 -1
@@ -0,0 +1,269 @@
1
+ /**
2
+ * Turns a command's stdout into a few model-sized events.
3
+ *
4
+ * A watcher (`tail -f`, a poll loop, `gh run watch`) can print a line every
5
+ * millisecond, and every line the model is told about costs a context resend.
6
+ * So a monitor does not forward lines, it forwards BATCHES: lines are split,
7
+ * trimmed and truncated here, held for a short fixed window, and delivered
8
+ * together, and the batches themselves are rationed by a token bucket. A
9
+ * source that outruns the ration is first told so, then stopped, because a
10
+ * model that is flooded stops reading and one that is silenced does not know
11
+ * to look.
12
+ *
13
+ * Pure: no process, no registry, no model. The clock and the timer are
14
+ * injectable (and default to the globals read at call time, so fake timers
15
+ * work without any injection), which is what lets the tests advance a window
16
+ * by calling a function rather than by waiting for one.
17
+ *
18
+ * The constants and the wording come from the reference implementation of this
19
+ * tool; deviations are named where they are made.
20
+ */
21
+
22
+ /** A longer line keeps its first this-many characters. */
23
+ export const MONITOR_LINE_MAX_CHARS = 500
24
+ /** A joined batch longer than this keeps its first this-many characters. */
25
+ export const MONITOR_BATCH_MAX_CHARS = 3000
26
+ /**
27
+ * The first buffered line arms ONE timer for this long; everything that
28
+ * arrives before it fires rides in the same batch. A fixed window, not a
29
+ * debounce: a source that never pauses still gets a batch every window
30
+ * instead of never.
31
+ */
32
+ export const MONITOR_BATCH_WINDOW_MS = 200
33
+ /** A partial line (no newline yet) keeps its tail past this many bytes. */
34
+ export const MONITOR_PARTIAL_LINE_MAX_BYTES = 1024 * 1024
35
+ /** One token per BATCH, not per line. */
36
+ export const MONITOR_BUCKET_CAPACITY = 10
37
+ export const MONITOR_BUCKET_REFILL_MS = 2000
38
+ /** Suppression older than this, with another batch dropped, stops the monitor. */
39
+ export const MONITOR_SUPPRESSION_LIMIT_MS = 30_000
40
+ /** The effective ceiling on a monitor's life; a host may lower it, never raise it past the schema. */
41
+ export const MONITOR_TIMEOUT_CAP_MS = 1_800_000
42
+ export const MONITOR_TIMEOUT_DEFAULT_MS = 300_000
43
+ export const MONITOR_TIMEOUT_MIN_MS = 1000
44
+ /** The schema ceiling; anything between the cap and this is clamped, not refused. */
45
+ export const MONITOR_TIMEOUT_MAX_MS = 3_600_000
46
+ /** One notification handed to the model is cut to this many characters, in the middle. */
47
+ export const MONITOR_NOTIFICATION_MAX_CHARS = 100_000
48
+ /**
49
+ * One event's content is shrunk to this many characters BEFORE it is framed,
50
+ * so the notification cap never has to cut inside an envelope. A batch is at
51
+ * most 3000, so this only matters to a notice or a caller that outgrew that.
52
+ */
53
+ export const MONITOR_EVENT_CONTENT_MAX_CHARS = 20_000
54
+
55
+ const LINE_TRUNCATED = '...(truncated)'
56
+ const BATCH_TRUNCATED = '\n...(truncated)'
57
+
58
+ /** `30m`, `5m`, `90s`: whole minutes as minutes, anything else in seconds. */
59
+ export function formatMonitorDuration(ms: number): string {
60
+ const seconds = Math.max(1, Math.round(ms / 1000))
61
+ return seconds % 60 === 0 ? `${seconds / 60}m` : `${seconds}s`
62
+ }
63
+
64
+ export function suppressionNotice(count: number): string {
65
+ return `[${count} events suppressed — output rate too high. Consider stopping this monitor and starting it with a more selective command.]`
66
+ }
67
+
68
+ export function tooMuchOutputNotice(count: number, overMs: number): string {
69
+ return `[Monitor stopped — too much output (${count} events suppressed over ${formatMonitorDuration(overMs)}). Restart it with a more selective command.]`
70
+ }
71
+
72
+ export function expiryNotice(ms: number, delivered: number): string {
73
+ return delivered > 0
74
+ ? `[Monitor expired after ${formatMonitorDuration(ms)} with ${delivered} events delivered. Start it again if you still need the watch.]`
75
+ : `[Monitor expired after ${formatMonitorDuration(ms)} with no events delivered. Start it again if you still need the watch — and widen the filter if silence was unexpected.]`
76
+ }
77
+
78
+ export interface MonitorStreamStats {
79
+ /** Batches handed to `onBatch`. */
80
+ readonly eventsDelivered: number
81
+ /** Batches the bucket dropped, over the monitor's whole life. */
82
+ readonly suppressed: number
83
+ /** Characters pushed in, before any trimming: zero means the command never wrote to stdout. */
84
+ readonly stdoutChars: number
85
+ }
86
+
87
+ export interface MonitorStreamOptions {
88
+ /** One batch of lines, joined with `\n`. */
89
+ onBatch(text: string): void
90
+ /** Housekeeping text from the stream itself (the suppression notice). Not rationed. */
91
+ onNotice(text: string): void
92
+ /**
93
+ * The source was suppressed for too long and another batch was dropped. The
94
+ * stream is closed before this is called; stopping the process is the
95
+ * caller's.
96
+ */
97
+ onOverflow(info: { readonly suppressed: number; readonly overMs: number }): void
98
+ now?: () => number
99
+ setTimer?: (callback: () => void, ms: number) => unknown
100
+ clearTimer?: (handle: unknown) => void
101
+ }
102
+
103
+ export interface MonitorStream {
104
+ /** Feed raw stdout text; chunks may split a line anywhere. */
105
+ push(text: string): void
106
+ /** Emit the pending batch now, without the partial line. For expiry. */
107
+ flush(): void
108
+ /** The end of the stream: the partial line counts as a line, then everything pending is emitted. */
109
+ drain(): void
110
+ /** Stop for good. Whatever is pending is discarded. */
111
+ close(): void
112
+ stats(): MonitorStreamStats
113
+ }
114
+
115
+ /** The tail of `text` that fits `maxBytes`, starting on a whole code point. */
116
+ function keepTail(text: string, maxBytes: number): string {
117
+ const bytes = Buffer.from(text)
118
+ if (bytes.length <= maxBytes) return text
119
+ let start = bytes.length - maxBytes
120
+ while (start < bytes.length && ((bytes[start] ?? 0) & 0xc0) === 0x80) start++
121
+ return bytes.subarray(start).toString('utf8')
122
+ }
123
+
124
+ export function createMonitorStream(options: MonitorStreamOptions): MonitorStream {
125
+ const now = options.now ?? (() => Date.now())
126
+ const setTimer =
127
+ options.setTimer ??
128
+ ((callback: () => void, ms: number) => {
129
+ const handle = setTimeout(callback, ms)
130
+ // A pending batch must not keep a process alive; the job's own exit
131
+ // drains it.
132
+ handle.unref?.()
133
+ return handle
134
+ })
135
+ const clearTimer =
136
+ options.clearTimer ?? ((handle: unknown) => clearTimeout(handle as NodeJS.Timeout))
137
+
138
+ let closed = false
139
+ let partial = ''
140
+ let lines: string[] = []
141
+ let timer: unknown
142
+ let timerArmed = false
143
+
144
+ let tokens = MONITOR_BUCKET_CAPACITY
145
+ let refilledAt = now()
146
+
147
+ let eventsDelivered = 0
148
+ let suppressedTotal = 0
149
+ let stdoutChars = 0
150
+
151
+ // The suppression tracker. `count` is what the auto-stop reports; `unannounced`
152
+ // is what the next notice reports. They differ after a notice, because the
153
+ // tracker outlives it on purpose: a source that is told and keeps going must
154
+ // still reach the 30 s rule.
155
+ let suppressionStartedAt: number | undefined
156
+ let lastDropAt = 0
157
+ let count = 0
158
+ let unannounced = 0
159
+ let noticeEmitted = false
160
+
161
+ const refill = (at: number): void => {
162
+ const elapsed = at - refilledAt
163
+ if (elapsed <= 0) return
164
+ tokens = Math.min(MONITOR_BUCKET_CAPACITY, tokens + elapsed / MONITOR_BUCKET_REFILL_MS)
165
+ refilledAt = at
166
+ }
167
+
168
+ const deliver = (batch: string): void => {
169
+ const at = now()
170
+ // Reset only after a notice went out AND the source has been quiet for
171
+ // three refills. Resetting on quiet alone would let a source that is
172
+ // dropped every other window never reach the stop rule.
173
+ if (noticeEmitted && at - lastDropAt > 3 * MONITOR_BUCKET_REFILL_MS) {
174
+ suppressionStartedAt = undefined
175
+ count = 0
176
+ unannounced = 0
177
+ noticeEmitted = false
178
+ }
179
+ refill(at)
180
+ if (tokens >= 1) {
181
+ tokens -= 1
182
+ if (unannounced > 0) {
183
+ options.onNotice(suppressionNotice(unannounced))
184
+ unannounced = 0
185
+ noticeEmitted = true
186
+ }
187
+ eventsDelivered++
188
+ options.onBatch(batch)
189
+ return
190
+ }
191
+ // Whether the rule fires is decided by when suppression BEGAN, before
192
+ // this drop is counted, and the count reported includes it.
193
+ const began = suppressionStartedAt
194
+ suppressionStartedAt ??= at
195
+ lastDropAt = at
196
+ suppressedTotal++
197
+ count++
198
+ unannounced++
199
+ if (began !== undefined && at - began > MONITOR_SUPPRESSION_LIMIT_MS) {
200
+ stop()
201
+ options.onOverflow({ suppressed: count, overMs: at - began })
202
+ }
203
+ }
204
+
205
+ const emitPending = (): void => {
206
+ if (timerArmed) {
207
+ clearTimer(timer)
208
+ timerArmed = false
209
+ }
210
+ if (lines.length === 0) return
211
+ let batch = lines.join('\n')
212
+ lines = []
213
+ if (batch.length > MONITOR_BATCH_MAX_CHARS) {
214
+ batch = batch.slice(0, MONITOR_BATCH_MAX_CHARS) + BATCH_TRUNCATED
215
+ }
216
+ deliver(batch)
217
+ }
218
+
219
+ const addLine = (raw: string): void => {
220
+ const line = raw.trim()
221
+ if (line.length === 0) return
222
+ lines.push(
223
+ line.length > MONITOR_LINE_MAX_CHARS
224
+ ? line.slice(0, MONITOR_LINE_MAX_CHARS) + LINE_TRUNCATED
225
+ : line,
226
+ )
227
+ if (!timerArmed) {
228
+ timerArmed = true
229
+ timer = setTimer(() => {
230
+ timerArmed = false
231
+ if (!closed) emitPending()
232
+ }, MONITOR_BATCH_WINDOW_MS)
233
+ }
234
+ }
235
+
236
+ const stop = (): void => {
237
+ closed = true
238
+ if (timerArmed) {
239
+ clearTimer(timer)
240
+ timerArmed = false
241
+ }
242
+ lines = []
243
+ partial = ''
244
+ }
245
+
246
+ return {
247
+ push(text) {
248
+ if (closed || text.length === 0) return
249
+ stdoutChars += text.length
250
+ const buffered = partial + text
251
+ const parts = buffered.split('\n')
252
+ partial = keepTail(parts.pop() ?? '', MONITOR_PARTIAL_LINE_MAX_BYTES)
253
+ for (const part of parts) addLine(part)
254
+ },
255
+ flush() {
256
+ if (closed) return
257
+ emitPending()
258
+ },
259
+ drain() {
260
+ if (closed) return
261
+ const rest = partial
262
+ partial = ''
263
+ addLine(rest)
264
+ emitPending()
265
+ },
266
+ close: stop,
267
+ stats: () => ({ eventsDelivered, suppressed: suppressedTotal, stdoutChars }),
268
+ }
269
+ }