@namzu/sdk 26.0.0 → 26.1.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.
- package/CHANGELOG.md +68 -0
- package/dist/connector/mcp/index.d.ts +1 -0
- package/dist/connector/mcp/index.d.ts.map +1 -1
- package/dist/connector/mcp/index.js +3 -0
- package/dist/connector/mcp/index.js.map +1 -1
- package/dist/connector/mcp/server-stdio.d.ts +62 -0
- package/dist/connector/mcp/server-stdio.d.ts.map +1 -0
- package/dist/connector/mcp/server-stdio.js +121 -0
- package/dist/connector/mcp/server-stdio.js.map +1 -0
- package/dist/session/workspace/git-worktree.d.ts +15 -0
- package/dist/session/workspace/git-worktree.d.ts.map +1 -1
- package/dist/session/workspace/git-worktree.js +55 -1
- package/dist/session/workspace/git-worktree.js.map +1 -1
- package/dist/tools/builtins/bash.d.ts.map +1 -1
- package/dist/tools/builtins/bash.js +35 -0
- package/dist/tools/builtins/bash.js.map +1 -1
- package/dist/tools/coordinator/agent.d.ts.map +1 -1
- package/dist/tools/coordinator/agent.js +12 -0
- package/dist/tools/coordinator/agent.js.map +1 -1
- package/dist/tools/coordinator/index.d.ts.map +1 -1
- package/dist/tools/coordinator/index.js +134 -6
- package/dist/tools/coordinator/index.js.map +1 -1
- package/dist/types/sandbox/index.d.ts +23 -0
- package/dist/types/sandbox/index.d.ts.map +1 -1
- package/dist/types/sandbox/index.js.map +1 -1
- package/package.json +1 -1
- package/src/connector/mcp/index.ts +3 -0
- package/src/connector/mcp/server-stdio.ts +137 -0
- package/src/session/workspace/git-worktree.ts +55 -1
- package/src/tools/builtins/bash.ts +34 -0
- package/src/tools/coordinator/agent.ts +12 -0
- package/src/tools/coordinator/index.ts +144 -8
- package/src/types/sandbox/index.ts +23 -0
|
@@ -7,6 +7,7 @@ import { defineTool } from '../defineTool.js'
|
|
|
7
7
|
import { wrapUntrusted } from '../untrusted-envelope.js'
|
|
8
8
|
import { failureLabel, taskSucceeded } from './outcome.js'
|
|
9
9
|
|
|
10
|
+
import { DELEGATION_TIMEOUT_MS } from './index.js'
|
|
10
11
|
import type { TaskLaunchedCallback } from './index.js'
|
|
11
12
|
|
|
12
13
|
/**
|
|
@@ -107,6 +108,17 @@ export function buildAgentTool(opts: AgentToolOptions): ToolDefinition {
|
|
|
107
108
|
readOnly: false,
|
|
108
109
|
destructive: false,
|
|
109
110
|
concurrencySafe: true,
|
|
111
|
+
// Declaring nothing here does not mean "no deadline"; it means the
|
|
112
|
+
// executor's 120-second default, which is a bound for a tool call and
|
|
113
|
+
// absurd for a whole agent run. `create_task` in the sibling module
|
|
114
|
+
// carries the same reasoning and the same hour, and the measurement
|
|
115
|
+
// behind that number is in its docblock: three delegated children took
|
|
116
|
+
// 4m21s, 5m58s and 8m04s, and all three parents gave up at 120s.
|
|
117
|
+
//
|
|
118
|
+
// This surface did not get that fix when its twin did, and the file's
|
|
119
|
+
// own note above records the pair doing exactly this before. The two
|
|
120
|
+
// tools are twins; a bound applied to one of them is not applied.
|
|
121
|
+
timeoutMs: DELEGATION_TIMEOUT_MS,
|
|
110
122
|
...(opts.terminal !== undefined ? { terminal: opts.terminal } : {}),
|
|
111
123
|
async execute({ description, prompt, subagent_type }, context) {
|
|
112
124
|
// With a single registered subagent the type is optional — default to
|
|
@@ -157,6 +157,73 @@ const askUserQuestionModelInputSchema: Record<string, unknown> = {
|
|
|
157
157
|
additionalProperties: false,
|
|
158
158
|
}
|
|
159
159
|
|
|
160
|
+
/** One well-formed tag token: `<step>`, `</step>`, `<a href="…">`, `<br/>`. */
|
|
161
|
+
const TAG_TOKEN = /<\/?[A-Za-z][\w-]*(?:\s[^<>]*)?\/?>/g
|
|
162
|
+
const DESCRIPTION_BLOCK = /<description>([\s\S]*?)<\/description>/gi
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Remove every tag token, including the ones removing a tag creates.
|
|
166
|
+
*
|
|
167
|
+
* One pass is not enough and the reason is not obvious: deleting an inner
|
|
168
|
+
* tag can splice its neighbours into a new one. `<<step>step>` loses the
|
|
169
|
+
* inner `<step>` and the halves close up into `<step>` again, so a line
|
|
170
|
+
* that is nothing but markup comes back non-empty and is offered to a
|
|
171
|
+
* human as a step to approve — which is the exact outcome this whole path
|
|
172
|
+
* exists to prevent.
|
|
173
|
+
*
|
|
174
|
+
* Repeating to a fixed point terminates: every pass that changes the
|
|
175
|
+
* string removes at least one token and so strictly shortens it.
|
|
176
|
+
*
|
|
177
|
+
* Only ever used to ANSWER "is there anything here besides markup". The
|
|
178
|
+
* result is never shown to anyone, so this is a test rather than a
|
|
179
|
+
* sanitiser, and it does not have to defend against every way a tag can
|
|
180
|
+
* be spelled.
|
|
181
|
+
*/
|
|
182
|
+
function withoutTags(text: string): string {
|
|
183
|
+
let current = text
|
|
184
|
+
for (;;) {
|
|
185
|
+
const next = current.replace(TAG_TOKEN, '')
|
|
186
|
+
if (next === current) return current
|
|
187
|
+
current = next
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Peel tag wrappers off the ENDS of one line, and nowhere else — a step
|
|
193
|
+
* that legitimately says "wrap it in a <div>" keeps its sentence.
|
|
194
|
+
*/
|
|
195
|
+
function unwrapStepLine(line: string): string {
|
|
196
|
+
let text = line.trim()
|
|
197
|
+
for (;;) {
|
|
198
|
+
const next = text
|
|
199
|
+
.replace(/^<[A-Za-z][\w-]*(?:\s[^<>]*)?>\s*/, '')
|
|
200
|
+
.replace(/\s*<\/[A-Za-z][\w-]*>$/, '')
|
|
201
|
+
.trim()
|
|
202
|
+
if (next === text) break
|
|
203
|
+
text = next
|
|
204
|
+
}
|
|
205
|
+
return text
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* A step list the model serialized instead of building.
|
|
210
|
+
*
|
|
211
|
+
* The line-splitting fallback below is the general case, and it had one
|
|
212
|
+
* shape badly wrong. A model that serializes this array tends to reach for
|
|
213
|
+
* MARKUP, not for prose:
|
|
214
|
+
*
|
|
215
|
+
* <steps>
|
|
216
|
+
* <step>
|
|
217
|
+
* <description>Convert the document to Word</description>
|
|
218
|
+
* </step>
|
|
219
|
+
* </steps>
|
|
220
|
+
*
|
|
221
|
+
* Split on newlines, that is seven "steps", five of which are tags. A host
|
|
222
|
+
* then numbered them in an approval card and asked a person to approve
|
|
223
|
+
* `</steps>` — reported from a real run. The descriptions the model named
|
|
224
|
+
* are right there, so read them; fall back to lines only when there are
|
|
225
|
+
* none, and drop the lines that carry no words at all.
|
|
226
|
+
*/
|
|
160
227
|
function normalizeApprovePlanSteps(value: unknown): unknown {
|
|
161
228
|
if (typeof value !== 'string') return value
|
|
162
229
|
|
|
@@ -171,19 +238,84 @@ function normalizeApprovePlanSteps(value: unknown): unknown {
|
|
|
171
238
|
}
|
|
172
239
|
}
|
|
173
240
|
|
|
241
|
+
const described = [...trimmed.matchAll(DESCRIPTION_BLOCK)]
|
|
242
|
+
.map((match) => (match[1] ?? '').trim())
|
|
243
|
+
.filter(Boolean)
|
|
244
|
+
if (described.length > 0) {
|
|
245
|
+
return described.map((description) => ({ description }))
|
|
246
|
+
}
|
|
247
|
+
|
|
174
248
|
const lines = trimmed
|
|
175
249
|
.split(/\r?\n+/)
|
|
176
250
|
.map((line) =>
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
251
|
+
unwrapStepLine(
|
|
252
|
+
line
|
|
253
|
+
.trim()
|
|
254
|
+
.replace(/^(?:[-*•]|\d+[.)])\s*/, '')
|
|
255
|
+
.trim(),
|
|
256
|
+
),
|
|
181
257
|
)
|
|
182
|
-
.filter(
|
|
258
|
+
.filter((line) => line.length > 0 && withoutTags(line).trim().length > 0)
|
|
259
|
+
|
|
260
|
+
// Every line was markup: there is no plan in this string, and inventing
|
|
261
|
+
// one step reading `<steps>` is worse than saying so.
|
|
262
|
+
if (lines.length === 0) {
|
|
263
|
+
return withoutTags(unwrapStepLine(trimmed)).trim()
|
|
264
|
+
? [{ description: unwrapStepLine(trimmed) }]
|
|
265
|
+
: []
|
|
266
|
+
}
|
|
183
267
|
|
|
184
|
-
return
|
|
185
|
-
|
|
186
|
-
|
|
268
|
+
return lines.map((description) => ({ description }))
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* The single closed shape a capable provider constrains this call to —
|
|
273
|
+
* the same instrument `ask_user_question` carries, for the same failure.
|
|
274
|
+
*
|
|
275
|
+
* `steps` arriving as a STRING is what everything above exists to survive,
|
|
276
|
+
* and surviving it is not the same as preventing it: the normalizer can
|
|
277
|
+
* only guess at a structure the model already threw away. Advertising the
|
|
278
|
+
* closed shape turns the guess into a refusal at generation time.
|
|
279
|
+
*/
|
|
280
|
+
const approvePlanModelInputSchema: Record<string, unknown> = {
|
|
281
|
+
type: 'object',
|
|
282
|
+
properties: {
|
|
283
|
+
title: {
|
|
284
|
+
type: 'string',
|
|
285
|
+
description: 'Short title for the plan (e.g. "TypeScript Security & Performance Review").',
|
|
286
|
+
},
|
|
287
|
+
summary: {
|
|
288
|
+
type: 'string',
|
|
289
|
+
description: '1-3 sentence summary of what you plan to do.',
|
|
290
|
+
},
|
|
291
|
+
steps: {
|
|
292
|
+
type: 'array',
|
|
293
|
+
description:
|
|
294
|
+
'A JSON array of ordered step objects. Never a string, and never markup — no <step> or <description> tags.',
|
|
295
|
+
items: {
|
|
296
|
+
type: 'object',
|
|
297
|
+
properties: {
|
|
298
|
+
description: {
|
|
299
|
+
type: 'string',
|
|
300
|
+
description: 'What this step does, as one plain sentence a person can read.',
|
|
301
|
+
},
|
|
302
|
+
agent_id: {
|
|
303
|
+
type: 'string',
|
|
304
|
+
description: 'Which agent handles this; omit for steps you carry out yourself.',
|
|
305
|
+
},
|
|
306
|
+
depends_on: {
|
|
307
|
+
type: 'array',
|
|
308
|
+
items: { type: 'string' },
|
|
309
|
+
description: 'Descriptions of the steps that must finish before this one.',
|
|
310
|
+
},
|
|
311
|
+
},
|
|
312
|
+
required: ['description'],
|
|
313
|
+
additionalProperties: false,
|
|
314
|
+
},
|
|
315
|
+
},
|
|
316
|
+
},
|
|
317
|
+
required: ['title', 'summary', 'steps'],
|
|
318
|
+
additionalProperties: false,
|
|
187
319
|
}
|
|
188
320
|
|
|
189
321
|
/**
|
|
@@ -1013,6 +1145,10 @@ export function buildCoordinatorTools(opts: CoordinatorToolsOptions): ToolDefini
|
|
|
1013
1145
|
.preprocess(normalizeApprovePlanSteps, z.array(approvePlanStepSchema))
|
|
1014
1146
|
.describe('Ordered list of planned steps'),
|
|
1015
1147
|
}),
|
|
1148
|
+
modelInputSchema: structuredClone(approvePlanModelInputSchema),
|
|
1149
|
+
enforceModelInput: true,
|
|
1150
|
+
validationErrorHint:
|
|
1151
|
+
'Required shape: {"title":"…","summary":"…","steps":[{"description":"One plain sentence"}]}. "steps" must be a JSON array of objects — never a string, and never markup such as <step> or <description>.',
|
|
1016
1152
|
category: 'custom',
|
|
1017
1153
|
permissions: [],
|
|
1018
1154
|
readOnly: true,
|
|
@@ -103,6 +103,29 @@ export interface SandboxExecOptions {
|
|
|
103
103
|
readonly timeout?: number
|
|
104
104
|
readonly env?: Record<string, string>
|
|
105
105
|
readonly cwd?: string
|
|
106
|
+
/**
|
|
107
|
+
* Called as output arrives, before the command has finished.
|
|
108
|
+
*
|
|
109
|
+
* Every container-tier worker already streams its output a chunk at a
|
|
110
|
+
* time — the wire carries `stdout_delta` and `stderr_delta` events —
|
|
111
|
+
* and every backend concatenated them into a string and returned that
|
|
112
|
+
* when the process exited. So a command that takes eight minutes said
|
|
113
|
+
* nothing for eight minutes, on a transport that had been reporting
|
|
114
|
+
* the whole time.
|
|
115
|
+
*
|
|
116
|
+
* Additive and optional: a backend that cannot stream simply never
|
|
117
|
+
* calls it, and `SandboxExecResult.stdout` still carries the complete
|
|
118
|
+
* output either way. A caller that wants only the result ignores this
|
|
119
|
+
* and behaves exactly as before.
|
|
120
|
+
*
|
|
121
|
+
* The callback must not throw and must not be awaited — it is on the
|
|
122
|
+
* read path of a running process, so a slow or failing consumer would
|
|
123
|
+
* otherwise become a slow or failing command.
|
|
124
|
+
*/
|
|
125
|
+
readonly onOutput?: (chunk: {
|
|
126
|
+
readonly stream: 'stdout' | 'stderr'
|
|
127
|
+
readonly data: string
|
|
128
|
+
}) => void
|
|
106
129
|
/**
|
|
107
130
|
* Cancellation for the command. A backend that honours it kills the
|
|
108
131
|
* process; one that does not simply ignores it, so this is additive.
|