@namzu/sdk 45.0.0 → 45.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 +55 -0
- package/dist/authorization/gate.d.ts.map +1 -1
- package/dist/authorization/gate.js +12 -2
- package/dist/authorization/gate.js.map +1 -1
- package/dist/authorization/rules.d.ts.map +1 -1
- package/dist/authorization/rules.js +37 -6
- package/dist/authorization/rules.js.map +1 -1
- package/dist/authorization/shell-lexer.d.ts +14 -0
- package/dist/authorization/shell-lexer.d.ts.map +1 -1
- package/dist/authorization/shell-lexer.js +19 -6
- package/dist/authorization/shell-lexer.js.map +1 -1
- package/dist/bridge/a2a/mapper.d.ts.map +1 -1
- package/dist/bridge/a2a/mapper.js +2 -0
- package/dist/bridge/a2a/mapper.js.map +1 -1
- package/dist/bridge/sse/mapper.d.ts.map +1 -1
- package/dist/bridge/sse/mapper.js +1 -0
- package/dist/bridge/sse/mapper.js.map +1 -1
- package/dist/directory/types.d.ts +2 -0
- package/dist/directory/types.d.ts.map +1 -1
- package/dist/directory/types.js.map +1 -1
- package/dist/manager/resident/outbox.d.ts +4 -4
- package/dist/prompt/coding-agent-doctrine.d.ts +1 -1
- package/dist/prompt/coding-agent-doctrine.d.ts.map +1 -1
- package/dist/prompt/coding-agent-doctrine.js +1 -0
- package/dist/prompt/coding-agent-doctrine.js.map +1 -1
- package/dist/public-runtime.d.ts +4 -1
- package/dist/public-runtime.d.ts.map +1 -1
- package/dist/public-runtime.js +11 -1
- package/dist/public-runtime.js.map +1 -1
- package/dist/public-tools.d.ts +4 -0
- package/dist/public-tools.d.ts.map +1 -1
- package/dist/public-tools.js +10 -0
- package/dist/public-tools.js.map +1 -1
- package/dist/public-types.d.ts +7 -1
- package/dist/public-types.d.ts.map +1 -1
- package/dist/runtime/query/declined.d.ts +12 -0
- package/dist/runtime/query/declined.d.ts.map +1 -0
- package/dist/runtime/query/declined.js +12 -0
- package/dist/runtime/query/declined.js.map +1 -0
- package/dist/runtime/query/executor.d.ts +3 -1
- package/dist/runtime/query/executor.d.ts.map +1 -1
- package/dist/runtime/query/executor.js +14 -2
- package/dist/runtime/query/executor.js.map +1 -1
- package/dist/runtime/query/index.d.ts.map +1 -1
- package/dist/runtime/query/index.js +1 -0
- package/dist/runtime/query/index.js.map +1 -1
- package/dist/runtime/query/iteration/index.d.ts.map +1 -1
- package/dist/runtime/query/iteration/index.js +7 -0
- package/dist/runtime/query/iteration/index.js.map +1 -1
- package/dist/runtime/query/iteration/phases/context.d.ts +6 -0
- package/dist/runtime/query/iteration/phases/context.d.ts.map +1 -1
- package/dist/runtime/query/iteration/phases/context.js.map +1 -1
- package/dist/runtime/query/iteration/phases/handoff.d.ts +22 -0
- package/dist/runtime/query/iteration/phases/handoff.d.ts.map +1 -0
- package/dist/runtime/query/iteration/phases/handoff.js +65 -0
- package/dist/runtime/query/iteration/phases/handoff.js.map +1 -0
- package/dist/runtime/query/iteration/phases/index.d.ts +1 -0
- package/dist/runtime/query/iteration/phases/index.d.ts.map +1 -1
- package/dist/runtime/query/iteration/phases/index.js +1 -0
- package/dist/runtime/query/iteration/phases/index.js.map +1 -1
- package/dist/runtime/query/iteration/phases/tool-review.d.ts.map +1 -1
- package/dist/runtime/query/iteration/phases/tool-review.js +8 -3
- package/dist/runtime/query/iteration/phases/tool-review.js.map +1 -1
- package/dist/runtime/query/resume-pending.d.ts.map +1 -1
- package/dist/runtime/query/resume-pending.js +3 -2
- package/dist/runtime/query/resume-pending.js.map +1 -1
- package/dist/runtime/query/review-policy.d.ts.map +1 -1
- package/dist/runtime/query/review-policy.js +4 -3
- package/dist/runtime/query/review-policy.js.map +1 -1
- package/dist/schedules/cron.d.ts +21 -0
- package/dist/schedules/cron.d.ts.map +1 -0
- package/dist/schedules/cron.js +167 -0
- package/dist/schedules/cron.js.map +1 -0
- package/dist/schedules/describe.d.ts +14 -0
- package/dist/schedules/describe.d.ts.map +1 -0
- package/dist/schedules/describe.js +133 -0
- package/dist/schedules/describe.js.map +1 -0
- package/dist/schedules/errors.d.ts +11 -0
- package/dist/schedules/errors.d.ts.map +1 -0
- package/dist/schedules/errors.js +15 -0
- package/dist/schedules/errors.js.map +1 -0
- package/dist/schedules/evaluate.d.ts +35 -0
- package/dist/schedules/evaluate.d.ts.map +1 -0
- package/dist/schedules/evaluate.js +158 -0
- package/dist/schedules/evaluate.js.map +1 -0
- package/dist/schedules/index.d.ts +11 -0
- package/dist/schedules/index.d.ts.map +1 -0
- package/dist/schedules/index.js +8 -0
- package/dist/schedules/index.js.map +1 -0
- package/dist/schedules/next-fire.d.ts +50 -0
- package/dist/schedules/next-fire.d.ts.map +1 -0
- package/dist/schedules/next-fire.js +250 -0
- package/dist/schedules/next-fire.js.map +1 -0
- package/dist/schedules/spec.d.ts +30 -0
- package/dist/schedules/spec.d.ts.map +1 -0
- package/dist/schedules/spec.js +169 -0
- package/dist/schedules/spec.js.map +1 -0
- package/dist/schedules/types.d.ts +144 -0
- package/dist/schedules/types.d.ts.map +1 -0
- package/dist/schedules/types.js +11 -0
- package/dist/schedules/types.js.map +1 -0
- package/dist/schedules/tz.d.ts +44 -0
- package/dist/schedules/tz.d.ts.map +1 -0
- package/dist/schedules/tz.js +141 -0
- package/dist/schedules/tz.js.map +1 -0
- package/dist/skills/index.d.ts +1 -1
- package/dist/skills/index.d.ts.map +1 -1
- package/dist/skills/index.js +1 -1
- package/dist/skills/index.js.map +1 -1
- package/dist/skills/loader.d.ts +16 -3
- package/dist/skills/loader.d.ts.map +1 -1
- package/dist/skills/loader.js +48 -4
- package/dist/skills/loader.js.map +1 -1
- package/dist/tools/builtins/browser-url.d.ts +83 -0
- package/dist/tools/builtins/browser-url.d.ts.map +1 -0
- package/dist/tools/builtins/browser-url.js +240 -0
- package/dist/tools/builtins/browser-url.js.map +1 -0
- package/dist/tools/builtins/browser.d.ts +367 -0
- package/dist/tools/builtins/browser.d.ts.map +1 -0
- package/dist/tools/builtins/browser.js +704 -0
- package/dist/tools/builtins/browser.js.map +1 -0
- package/dist/tools/builtins/skill.d.ts.map +1 -1
- package/dist/tools/builtins/skill.js +15 -0
- package/dist/tools/builtins/skill.js.map +1 -1
- package/dist/tools/defineTool.d.ts +2 -0
- package/dist/tools/defineTool.d.ts.map +1 -1
- package/dist/tools/defineTool.js +1 -0
- package/dist/tools/defineTool.js.map +1 -1
- package/dist/tools/schedules/index.d.ts +5 -0
- package/dist/tools/schedules/index.d.ts.map +1 -0
- package/dist/tools/schedules/index.js +4 -0
- package/dist/tools/schedules/index.js.map +1 -0
- package/dist/tools/schedules/loop-tool.d.ts +14 -0
- package/dist/tools/schedules/loop-tool.d.ts.map +1 -0
- package/dist/tools/schedules/loop-tool.js +81 -0
- package/dist/tools/schedules/loop-tool.js.map +1 -0
- package/dist/tools/schedules/present.d.ts +16 -0
- package/dist/tools/schedules/present.d.ts.map +1 -0
- package/dist/tools/schedules/present.js +69 -0
- package/dist/tools/schedules/present.js.map +1 -0
- package/dist/tools/schedules/prompt-scan.d.ts +17 -0
- package/dist/tools/schedules/prompt-scan.d.ts.map +1 -0
- package/dist/tools/schedules/prompt-scan.js +92 -0
- package/dist/tools/schedules/prompt-scan.js.map +1 -0
- package/dist/tools/schedules/schedule-tool.d.ts +16 -0
- package/dist/tools/schedules/schedule-tool.d.ts.map +1 -0
- package/dist/tools/schedules/schedule-tool.js +327 -0
- package/dist/tools/schedules/schedule-tool.js.map +1 -0
- package/dist/tools/schedules/types.d.ts +184 -0
- package/dist/tools/schedules/types.d.ts.map +1 -0
- package/dist/tools/schedules/types.js +11 -0
- package/dist/tools/schedules/types.js.map +1 -0
- package/dist/types/authorization/index.d.ts +86 -9
- package/dist/types/authorization/index.d.ts.map +1 -1
- package/dist/types/authorization/index.js +11 -1
- package/dist/types/authorization/index.js.map +1 -1
- package/dist/types/browser/index.d.ts +280 -0
- package/dist/types/browser/index.d.ts.map +1 -0
- package/dist/types/browser/index.js +12 -0
- package/dist/types/browser/index.js.map +1 -0
- package/dist/types/session/events.d.ts +6 -0
- package/dist/types/session/events.d.ts.map +1 -1
- package/dist/types/session/events.js.map +1 -1
- package/dist/types/session/records.d.ts +23 -0
- package/dist/types/session/records.d.ts.map +1 -1
- package/dist/types/session/records.js +9 -0
- package/dist/types/session/records.js.map +1 -1
- package/dist/types/tool/index.d.ts +41 -0
- package/dist/types/tool/index.d.ts.map +1 -1
- package/dist/types/tool/index.js.map +1 -1
- package/dist/types/tool/presentation.d.ts +7 -0
- package/dist/types/tool/presentation.d.ts.map +1 -1
- package/dist/utils/frontmatter.d.ts +18 -2
- package/dist/utils/frontmatter.d.ts.map +1 -1
- package/dist/utils/frontmatter.js +13 -3
- package/dist/utils/frontmatter.js.map +1 -1
- package/dist/utils/id.d.ts +8 -0
- package/dist/utils/id.d.ts.map +1 -1
- package/dist/utils/id.js +12 -0
- package/dist/utils/id.js.map +1 -1
- package/package.json +1 -1
- package/src/authorization/gate.ts +14 -2
- package/src/authorization/rules.ts +37 -7
- package/src/authorization/shell-lexer.ts +36 -6
- package/src/bridge/a2a/mapper.ts +2 -0
- package/src/bridge/sse/mapper.ts +1 -0
- package/src/directory/types.ts +2 -0
- package/src/prompt/coding-agent-doctrine.ts +1 -0
- package/src/public-runtime.ts +28 -0
- package/src/public-tools.ts +35 -0
- package/src/public-types.ts +50 -0
- package/src/runtime/query/declined.ts +12 -0
- package/src/runtime/query/executor.ts +17 -1
- package/src/runtime/query/index.ts +1 -0
- package/src/runtime/query/iteration/index.ts +8 -0
- package/src/runtime/query/iteration/phases/context.ts +6 -0
- package/src/runtime/query/iteration/phases/handoff.ts +74 -0
- package/src/runtime/query/iteration/phases/index.ts +1 -0
- package/src/runtime/query/iteration/phases/tool-review.ts +11 -4
- package/src/runtime/query/resume-pending.ts +3 -2
- package/src/runtime/query/review-policy.ts +4 -3
- package/src/schedules/cron.ts +202 -0
- package/src/schedules/describe.ts +138 -0
- package/src/schedules/errors.ts +15 -0
- package/src/schedules/evaluate.ts +178 -0
- package/src/schedules/index.ts +18 -0
- package/src/schedules/next-fire.ts +257 -0
- package/src/schedules/spec.ts +210 -0
- package/src/schedules/types.ts +163 -0
- package/src/schedules/tz.ts +155 -0
- package/src/skills/index.ts +1 -1
- package/src/skills/loader.ts +55 -4
- package/src/tools/builtins/browser-url.ts +251 -0
- package/src/tools/builtins/browser.ts +817 -0
- package/src/tools/builtins/skill.ts +18 -0
- package/src/tools/defineTool.ts +3 -0
- package/src/tools/schedules/index.ts +4 -0
- package/src/tools/schedules/loop-tool.ts +85 -0
- package/src/tools/schedules/present.ts +86 -0
- package/src/tools/schedules/prompt-scan.ts +96 -0
- package/src/tools/schedules/schedule-tool.ts +376 -0
- package/src/tools/schedules/types.ts +197 -0
- package/src/types/authorization/index.ts +60 -2
- package/src/types/browser/index.ts +341 -0
- package/src/types/session/events.ts +6 -0
- package/src/types/session/records.ts +10 -0
- package/src/types/tool/index.ts +42 -0
- package/src/types/tool/presentation.ts +7 -0
- package/src/utils/frontmatter.ts +29 -3
- package/src/utils/id.ts +14 -0
|
@@ -57,15 +57,27 @@ export function describeRule(rule: AuthorizationRule): string {
|
|
|
57
57
|
// Names the argument, not just the pattern. That is what tells a
|
|
58
58
|
// model whether a different value could get through — which is the
|
|
59
59
|
// difference between rewording once and rewording forever.
|
|
60
|
-
const verb =
|
|
60
|
+
const verb =
|
|
61
|
+
rule.decision === 'deny'
|
|
62
|
+
? 'denied'
|
|
63
|
+
: rule.decision === 'review'
|
|
64
|
+
? 'sent for review'
|
|
65
|
+
: 'allowed'
|
|
61
66
|
return `${verb} because the \`${rule.argument}\` argument matched ${rule.pattern} (this rule applies to ${rule.toolNames.join(', ')})`
|
|
62
67
|
}
|
|
63
68
|
|
|
64
69
|
case 'custom_pattern': {
|
|
65
70
|
const where = rule.target === 'both' ? 'name or arguments' : rule.target
|
|
66
|
-
const verb =
|
|
71
|
+
const verb =
|
|
72
|
+
rule.decision === 'deny'
|
|
73
|
+
? 'denied'
|
|
74
|
+
: rule.decision === 'review'
|
|
75
|
+
? 'sent for review'
|
|
76
|
+
: 'allowed'
|
|
67
77
|
return `${verb} by a pattern rule matching the ${where}: ${rule.pattern}`
|
|
68
78
|
}
|
|
79
|
+
case 'predicate':
|
|
80
|
+
return rule.description
|
|
69
81
|
default: {
|
|
70
82
|
const exhaustive: never = rule
|
|
71
83
|
return `matched an unrecognised rule: ${JSON.stringify(exhaustive)}`
|
|
@@ -134,19 +134,33 @@ export function evaluateRule(
|
|
|
134
134
|
// A command line is not one string, and testing it as one is how a
|
|
135
135
|
// prohibition gets bypassed: `^git push` sees `git push origin main`
|
|
136
136
|
// and does not see `true; git push origin main`. See
|
|
137
|
-
// `decomposeCommandLine` for the measurement and for why the
|
|
137
|
+
// `decomposeCommandLine` for the measurement and for why the
|
|
138
138
|
// decisions must read the result differently.
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
139
|
+
//
|
|
140
|
+
// An argument the tool declares as a canonical URL is not a command
|
|
141
|
+
// line, and cutting it at `&` would make an `allow` for a site
|
|
142
|
+
// decline every address with a query string. See
|
|
143
|
+
// `ToolDefinition.urlArgument`.
|
|
144
|
+
const isUrl = toolDef?.urlArgument === rule.argument
|
|
145
|
+
const { segments, opaque } = isUrl
|
|
146
|
+
? { segments: [subject], opaque: false }
|
|
147
|
+
: decomposeCommandLine(subject, dialect)
|
|
148
|
+
|
|
149
|
+
if (rule.decision === 'deny' || rule.decision === 'review') {
|
|
142
150
|
// ANY segment. The whole subject is tested first so an
|
|
143
151
|
// unanchored deny keeps matching across a boundary, which
|
|
144
152
|
// splitting alone would have taken away. Then each command's
|
|
145
153
|
// words as bash passes them, so that `'git' push` is `git push`.
|
|
146
|
-
|
|
147
|
-
|
|
154
|
+
//
|
|
155
|
+
// `review` reads like `deny`, because it is a restriction too: a
|
|
156
|
+
// rule that asks before `git push` must ask before
|
|
157
|
+
// `true; git push` as well. Matching too much costs a prompt;
|
|
158
|
+
// matching too little skips the question the operator asked for.
|
|
159
|
+
if (compiledPattern.test(subject)) return rule.decision
|
|
160
|
+
if (segments.some((segment) => compiledPattern.test(segment))) return rule.decision
|
|
161
|
+
if (isUrl) return null
|
|
148
162
|
return decodedCommands(subject, dialect).some((text) => compiledPattern.test(text))
|
|
149
|
-
?
|
|
163
|
+
? rule.decision
|
|
150
164
|
: null
|
|
151
165
|
}
|
|
152
166
|
|
|
@@ -165,6 +179,22 @@ export function evaluateRule(
|
|
|
165
179
|
return null
|
|
166
180
|
}
|
|
167
181
|
|
|
182
|
+
case 'predicate': {
|
|
183
|
+
// A rule that throws has not decided the call is safe. Reading the
|
|
184
|
+
// exception as "no opinion" would let the next rule, or the mode,
|
|
185
|
+
// approve what this one was written to refuse.
|
|
186
|
+
try {
|
|
187
|
+
return rule.decide({
|
|
188
|
+
toolName,
|
|
189
|
+
toolInput,
|
|
190
|
+
toolDef,
|
|
191
|
+
commandDialect: dialect,
|
|
192
|
+
})
|
|
193
|
+
} catch {
|
|
194
|
+
return 'deny'
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
168
198
|
default: {
|
|
169
199
|
const _exhaustive: never = rule
|
|
170
200
|
throw new Error(`Unhandled verification rule type: ${(_exhaustive as { type: string }).type}`)
|
|
@@ -83,6 +83,13 @@ export interface ShellRedirection {
|
|
|
83
83
|
readonly fd?: string
|
|
84
84
|
/** The target word. For a here-document, its delimiter. */
|
|
85
85
|
readonly target: ShellWord
|
|
86
|
+
/**
|
|
87
|
+
* A here-document's body as written, up to its delimiter line, once the
|
|
88
|
+
* lexer has read it: the text the command reads on its input, which is
|
|
89
|
+
* a command line of its own when the command is a shell (`bash <<EOF`).
|
|
90
|
+
* Absent for every other operator and for a body the line never reached.
|
|
91
|
+
*/
|
|
92
|
+
readonly body?: string
|
|
86
93
|
}
|
|
87
94
|
|
|
88
95
|
/** One simple command. */
|
|
@@ -118,6 +125,13 @@ export interface ShellLexResult {
|
|
|
118
125
|
* (`{ a; } > f`) that belong to no single simple command.
|
|
119
126
|
*/
|
|
120
127
|
readonly redirections: readonly ShellRedirection[]
|
|
128
|
+
/**
|
|
129
|
+
* Words that belong to no simple command: a `for` or `select` loop's
|
|
130
|
+
* variable and the words of its list, and a `case` statement's subject
|
|
131
|
+
* and patterns. `for d in ~/x; do rm -r "$d"; done` passes `~/x` to `rm`
|
|
132
|
+
* although no command lists it.
|
|
133
|
+
*/
|
|
134
|
+
readonly compoundWords: readonly ShellWord[]
|
|
121
135
|
/** True when {@link commands} may not be everything the line runs. */
|
|
122
136
|
readonly opaque: boolean
|
|
123
137
|
/** False when parsing stopped early: a syntax error or an unsupported construct. */
|
|
@@ -169,6 +183,7 @@ export function lexShellCommandLine(line: string, options: ShellLexOptions = {})
|
|
|
169
183
|
return {
|
|
170
184
|
commands: context.commands,
|
|
171
185
|
redirections: context.redirections,
|
|
186
|
+
compoundWords: context.compoundWords,
|
|
172
187
|
opaque: context.reasons.size > 0,
|
|
173
188
|
complete: context.complete,
|
|
174
189
|
reasons: [...context.reasons],
|
|
@@ -178,6 +193,7 @@ export function lexShellCommandLine(line: string, options: ShellLexOptions = {})
|
|
|
178
193
|
class Context {
|
|
179
194
|
readonly commands: ShellCommand[] = []
|
|
180
195
|
readonly redirections: ShellRedirection[] = []
|
|
196
|
+
readonly compoundWords: ShellWord[] = []
|
|
181
197
|
readonly reasons = new Set<string>()
|
|
182
198
|
complete = true
|
|
183
199
|
/**
|
|
@@ -358,6 +374,8 @@ interface PendingHeredoc {
|
|
|
358
374
|
readonly delimiter: string
|
|
359
375
|
readonly stripTabs: boolean
|
|
360
376
|
readonly quoted: boolean
|
|
377
|
+
/** The redirection the body belongs to, filled in when it is read. */
|
|
378
|
+
readonly redirection: { body?: string }
|
|
361
379
|
}
|
|
362
380
|
|
|
363
381
|
type Last =
|
|
@@ -790,6 +808,7 @@ class Parser {
|
|
|
790
808
|
}
|
|
791
809
|
const name = this.takePlainWord()
|
|
792
810
|
if (name.kind !== 'word') throw this.unexpected(name)
|
|
811
|
+
this.context.compoundWords.push(name.word)
|
|
793
812
|
this.newlines()
|
|
794
813
|
token = this.peek()
|
|
795
814
|
if (token.kind === 'word' && !token.word.quoted && token.word.value === 'in') {
|
|
@@ -807,7 +826,10 @@ class Parser {
|
|
|
807
826
|
}
|
|
808
827
|
for (;;) {
|
|
809
828
|
token = this.takePlainWord()
|
|
810
|
-
if (token.kind === 'word')
|
|
829
|
+
if (token.kind === 'word') {
|
|
830
|
+
this.context.compoundWords.push(token.word)
|
|
831
|
+
continue
|
|
832
|
+
}
|
|
811
833
|
if (token.kind === 'newline' || (token.kind === 'op' && token.op === ';')) break
|
|
812
834
|
throw this.unexpected(token)
|
|
813
835
|
}
|
|
@@ -854,6 +876,7 @@ class Parser {
|
|
|
854
876
|
this.take()
|
|
855
877
|
const subject = this.takePlainWord()
|
|
856
878
|
if (subject.kind !== 'word') throw this.unexpected(subject)
|
|
879
|
+
this.context.compoundWords.push(subject.word)
|
|
857
880
|
this.newlines()
|
|
858
881
|
const keyword = this.take()
|
|
859
882
|
if (keyword.kind !== 'word' || keyword.word.quoted || keyword.word.value !== 'in') {
|
|
@@ -887,6 +910,7 @@ class Parser {
|
|
|
887
910
|
patterns()
|
|
888
911
|
const pattern = this.take()
|
|
889
912
|
if (pattern.kind !== 'word') throw this.unexpected(pattern)
|
|
913
|
+
this.context.compoundWords.push(pattern.word)
|
|
890
914
|
const next = this.take()
|
|
891
915
|
if (next.kind === 'op' && next.op === '|') continue
|
|
892
916
|
if (next.kind === 'op' && next.op === ')') break
|
|
@@ -1114,6 +1138,11 @@ class Parser {
|
|
|
1114
1138
|
// Ubuntu ship).
|
|
1115
1139
|
this.context.opaque('quoted or expanding target of >& or <&')
|
|
1116
1140
|
}
|
|
1141
|
+
const redirection: { -readonly [K in keyof ShellRedirection]: ShellRedirection[K] } = {
|
|
1142
|
+
operator: operator.op,
|
|
1143
|
+
...(operator.fd !== undefined ? { fd: operator.fd } : {}),
|
|
1144
|
+
target: target.word,
|
|
1145
|
+
}
|
|
1117
1146
|
if (operator.op === '<<' || operator.op === '<<-') {
|
|
1118
1147
|
if (target.word.value.includes('\n')) {
|
|
1119
1148
|
// Bash starts the body at the newline inside the delimiter, so
|
|
@@ -1125,13 +1154,10 @@ class Parser {
|
|
|
1125
1154
|
delimiter: target.word.value,
|
|
1126
1155
|
stripTabs: operator.op === '<<-',
|
|
1127
1156
|
quoted: target.word.quoted,
|
|
1157
|
+
redirection,
|
|
1128
1158
|
})
|
|
1129
1159
|
}
|
|
1130
|
-
return
|
|
1131
|
-
operator: operator.op,
|
|
1132
|
-
...(operator.fd !== undefined ? { fd: operator.fd } : {}),
|
|
1133
|
-
target: target.word,
|
|
1134
|
-
}
|
|
1160
|
+
return redirection
|
|
1135
1161
|
}
|
|
1136
1162
|
|
|
1137
1163
|
/**
|
|
@@ -1641,7 +1667,9 @@ class Parser {
|
|
|
1641
1667
|
const heredoc = this.heredocs.shift() as PendingHeredoc
|
|
1642
1668
|
let i = this.pos
|
|
1643
1669
|
const bodyStart = i
|
|
1670
|
+
let bodyEnd = i
|
|
1644
1671
|
for (;;) {
|
|
1672
|
+
bodyEnd = i
|
|
1645
1673
|
if (i >= src.length) break
|
|
1646
1674
|
let line = ''
|
|
1647
1675
|
let j = i
|
|
@@ -1664,7 +1692,9 @@ class Parser {
|
|
|
1664
1692
|
const test = heredoc.stripTabs ? line.replace(/^\t+/, '') : line
|
|
1665
1693
|
i = j
|
|
1666
1694
|
if (test === heredoc.delimiter) break
|
|
1695
|
+
bodyEnd = i
|
|
1667
1696
|
}
|
|
1697
|
+
heredoc.redirection.body = src.slice(bodyStart, bodyEnd)
|
|
1668
1698
|
if (!heredoc.quoted) {
|
|
1669
1699
|
const body = src.slice(bodyStart, i)
|
|
1670
1700
|
if (/\$[({[]|`/.test(body)) this.heredocBody(body)
|
package/src/bridge/a2a/mapper.ts
CHANGED
|
@@ -227,6 +227,8 @@ const MAPPING: {
|
|
|
227
227
|
// it whether and when a retry is justified.
|
|
228
228
|
{
|
|
229
229
|
checkpointId: e.checkpointId,
|
|
230
|
+
// A person, not a retry, is what this pause waits for.
|
|
231
|
+
...(e.handoff ? { handoff: e.handoff } : {}),
|
|
230
232
|
...(e.failure
|
|
231
233
|
? {
|
|
232
234
|
code: e.failure.code,
|
package/src/bridge/sse/mapper.ts
CHANGED
|
@@ -297,6 +297,7 @@ const MAPPING: {
|
|
|
297
297
|
...(e.failure ? { failure: e.failure } : {}),
|
|
298
298
|
...(e.providerError ? { provider_error: e.providerError } : {}),
|
|
299
299
|
...(e.explanation ? { explanation: e.explanation } : {}),
|
|
300
|
+
...(e.handoff ? { handoff: e.handoff } : {}),
|
|
300
301
|
}),
|
|
301
302
|
},
|
|
302
303
|
|
package/src/directory/types.ts
CHANGED
|
@@ -16,6 +16,8 @@ import type { ToolDefinition } from '../types/tool/index.js'
|
|
|
16
16
|
* those is a `major` waiting to happen on a published type. The layering
|
|
17
17
|
* question — does a convention belong outside the kernel — was answered yes
|
|
18
18
|
* and stands; the shape question had simply never been asked.
|
|
19
|
+
* Schedules were later answered outside this loader, with a zone per spec and
|
|
20
|
+
* a claimed occurrence key: see `docs/sdk/schedules.md` (`../schedules/`).
|
|
19
21
|
*/
|
|
20
22
|
export type DirectorySlot = 'agent' | 'instructions' | 'tools' | 'skills' | 'agents'
|
|
21
23
|
|
|
@@ -69,6 +69,7 @@ export const CODING_AGENT_WORKING_DOCTRINE = `## How you work
|
|
|
69
69
|
- Before any command that could discard uncommitted work, run \`git status\`. If there are changes there that you did not make, stop and ask rather than stashing, committing or discarding somebody else's work.
|
|
70
70
|
- Commit only when the user asks, on the branch that is checked out. Do not create or switch branches unless the user asks or the project's instructions require it.
|
|
71
71
|
- After a broad \`git add\`, review what was staged before committing; a file whose name looks harmless can still carry a secret.
|
|
72
|
+
- Never change git configuration or other persistent settings without asking: not \`user.name\` or \`user.email\`, hooks, remotes, credential helpers, or any \`git config\` (local or global), nor a shell profile or a tool's own config file. If a commit fails because no identity is set, stop and tell the user the command to set one; do not invent one.
|
|
72
73
|
|
|
73
74
|
### Keeping the user informed
|
|
74
75
|
- Before a batch of tool calls, say in one short line what you are about to do and why. When you have been working for a while without saying anything, say in a few words where you are, then continue. One line, not a paragraph; the tool rows on screen already show the details.
|
package/src/public-runtime.ts
CHANGED
|
@@ -423,6 +423,7 @@ export {
|
|
|
423
423
|
discoverSkills,
|
|
424
424
|
loadSkill,
|
|
425
425
|
resolveSkillChain,
|
|
426
|
+
SKILL_FRONTMATTER_KEYS,
|
|
426
427
|
SkillRegistry,
|
|
427
428
|
} from './skills/index.js'
|
|
428
429
|
// The one frontmatter reader. `loadSkill` is built on it, and a host reading
|
|
@@ -631,6 +632,26 @@ export {
|
|
|
631
632
|
formatCompletionNotification,
|
|
632
633
|
} from './scheduler/completion-inbox.js'
|
|
633
634
|
|
|
635
|
+
// Scheduled jobs: WHEN something is due, and nothing about how a host stores
|
|
636
|
+
// or runs a job. The time engine and the evaluator are pure (`Intl` for time
|
|
637
|
+
// zones, no clock, no I/O); the CLI keeps its job files and history to itself.
|
|
638
|
+
export {
|
|
639
|
+
countOccurrences,
|
|
640
|
+
describeSchedule,
|
|
641
|
+
evaluateJob,
|
|
642
|
+
hostTimeZone,
|
|
643
|
+
nextFireTime,
|
|
644
|
+
parseCronExpression,
|
|
645
|
+
parseDuration,
|
|
646
|
+
parseScheduleSpec,
|
|
647
|
+
previousFireTime,
|
|
648
|
+
SCHEDULE_CATCH_UP_WINDOW_MS,
|
|
649
|
+
SCHEDULE_LATE_GRACE_MS,
|
|
650
|
+
ScheduleValidationError,
|
|
651
|
+
upcomingFireTimes,
|
|
652
|
+
validateTimeZone,
|
|
653
|
+
} from './schedules/index.js'
|
|
654
|
+
|
|
634
655
|
// ─── providers, sandbox, vault ───────────────────────────────────────────
|
|
635
656
|
|
|
636
657
|
export {
|
|
@@ -899,6 +920,11 @@ export {
|
|
|
899
920
|
AuthorizationGate,
|
|
900
921
|
} from './authorization/index.js'
|
|
901
922
|
|
|
923
|
+
// The one reader of a bash command line the gate itself uses. A host that
|
|
924
|
+
// writes a `predicate` rule about what a line runs decides on this reading,
|
|
925
|
+
// so its rule and the SDK's own never disagree about where a quote ends.
|
|
926
|
+
export { lexShellCommandLine } from './authorization/shell-lexer.js'
|
|
927
|
+
|
|
902
928
|
// NZ-BOOT-03: the module-attributed invariant registry. `compaction.ts` and
|
|
903
929
|
// `claim-disk.ts` register themselves against the shared `invariants`
|
|
904
930
|
// instance at import time (see each file); `namzu doctor` and any host can
|
|
@@ -1283,6 +1309,8 @@ export {
|
|
|
1283
1309
|
isReviewExempt,
|
|
1284
1310
|
isReviewMode,
|
|
1285
1311
|
} from './runtime/query/review-policy.js'
|
|
1312
|
+
// What the model is told when a person declines a call without words of their own.
|
|
1313
|
+
export { DECLINED_TOOL_CALL_FEEDBACK } from './runtime/query/declined.js'
|
|
1286
1314
|
|
|
1287
1315
|
// The system prompt is open: a contribution registry the assembler
|
|
1288
1316
|
// consumes, with skills as its first contributor. See `prompt/contributions.ts`.
|
package/src/public-tools.ts
CHANGED
|
@@ -107,6 +107,29 @@ export {
|
|
|
107
107
|
COMPUTER_USE_TOOL_NAME,
|
|
108
108
|
createComputerUseTool,
|
|
109
109
|
} from './tools/builtins/computer-use.js'
|
|
110
|
+
// The browser contract: the two tools over a host a separate package
|
|
111
|
+
// provides, and the canonicalisers a host and a site-rule compiler must share
|
|
112
|
+
// with the tools so every party reads one spelling of an address.
|
|
113
|
+
export {
|
|
114
|
+
BROWSER_ACT_TOOL_NAME,
|
|
115
|
+
BROWSER_TOOL_NAME,
|
|
116
|
+
browserHostErrorOf,
|
|
117
|
+
createBrowserTools,
|
|
118
|
+
formatBrowserPageHeader,
|
|
119
|
+
isBrowserCallReadOnly,
|
|
120
|
+
} from './tools/builtins/browser.js'
|
|
121
|
+
export {
|
|
122
|
+
BROWSER_URL_MAX_LENGTH,
|
|
123
|
+
canonicalizeBrowserOrigin,
|
|
124
|
+
canonicalizeBrowserSitePattern,
|
|
125
|
+
canonicalizeBrowserUrl,
|
|
126
|
+
isCloudMetadataHost,
|
|
127
|
+
} from './tools/builtins/browser-url.js'
|
|
128
|
+
export {
|
|
129
|
+
BROWSER_FILL_FORM_MAX_FIELDS,
|
|
130
|
+
BROWSER_SNAPSHOT_MAX_CHARS,
|
|
131
|
+
BROWSER_WAIT_MAX_MS,
|
|
132
|
+
} from './types/browser/index.js'
|
|
110
133
|
|
|
111
134
|
// ─── Domain tool builders ────────────────────────────────────────────────
|
|
112
135
|
|
|
@@ -129,6 +152,18 @@ export {
|
|
|
129
152
|
} from './tools/coordinator/ask-user-question.js'
|
|
130
153
|
export { buildAgentTool, type AgentToolOptions } from './tools/coordinator/agent.js'
|
|
131
154
|
|
|
155
|
+
// Scheduled jobs and in-session loops, over host callbacks: the host stores
|
|
156
|
+
// jobs, draws the confirmation and computes every field it shows. Register
|
|
157
|
+
// only where a person can confirm (never headless, scheduled or delegated).
|
|
158
|
+
export {
|
|
159
|
+
buildScheduleTools,
|
|
160
|
+
buildSessionLoopTools,
|
|
161
|
+
revealHiddenCharacters,
|
|
162
|
+
scanSchedulePrompt,
|
|
163
|
+
SCHEDULE_TOOL_NAME,
|
|
164
|
+
SESSION_LOOP_TOOL_NAME,
|
|
165
|
+
} from './tools/schedules/index.js'
|
|
166
|
+
|
|
132
167
|
// ─── RAG tool builder ────────────────────────────────────────────────────
|
|
133
168
|
|
|
134
169
|
export { createRAGTool } from './rag/index.js'
|
package/src/public-types.ts
CHANGED
|
@@ -62,6 +62,13 @@ export type * from './types/sandbox/index.js'
|
|
|
62
62
|
export type * from './types/structured-output/index.js'
|
|
63
63
|
export type * from './types/invocation/index.js'
|
|
64
64
|
export type * from './types/computer-use/index.js'
|
|
65
|
+
export type * from './types/browser/index.js'
|
|
66
|
+
export type { BrowserActToolInput, BrowserToolInput } from './tools/builtins/browser.js'
|
|
67
|
+
export type {
|
|
68
|
+
BrowserOriginVerdict,
|
|
69
|
+
BrowserSitePatternVerdict,
|
|
70
|
+
BrowserUrlVerdict,
|
|
71
|
+
} from './tools/builtins/browser-url.js'
|
|
65
72
|
export type * from './types/authorization/index.js'
|
|
66
73
|
export type * from './types/bus/index.js'
|
|
67
74
|
export type * from './types/probe/index.js'
|
|
@@ -115,6 +122,7 @@ export type { PricingSubject } from './manager/session/turn-recorder.js'
|
|
|
115
122
|
export type {
|
|
116
123
|
FrontmatterOptions,
|
|
117
124
|
FrontmatterValue,
|
|
125
|
+
ParseFrontmatterOptions,
|
|
118
126
|
ParsedFrontmatter,
|
|
119
127
|
} from './utils/frontmatter.js'
|
|
120
128
|
export type {
|
|
@@ -262,6 +270,13 @@ export type { AgentBusConfig } from './bus/index.js'
|
|
|
262
270
|
export type { ToolCallContext } from './authorization/index.js'
|
|
263
271
|
export type { PermissionPreset } from './authorization/index.js'
|
|
264
272
|
export type { EvaluateRuleOptions } from './authorization/rules.js'
|
|
273
|
+
export type {
|
|
274
|
+
ShellCommand,
|
|
275
|
+
ShellLexOptions,
|
|
276
|
+
ShellLexResult,
|
|
277
|
+
ShellRedirection,
|
|
278
|
+
ShellWord,
|
|
279
|
+
} from './authorization/shell-lexer.js'
|
|
265
280
|
|
|
266
281
|
export type {
|
|
267
282
|
DiskSessionStoreConfig,
|
|
@@ -629,3 +644,38 @@ export type {
|
|
|
629
644
|
ParsedSessionLogLine,
|
|
630
645
|
SessionLogLineFault,
|
|
631
646
|
} from './session/log-hash.js'
|
|
647
|
+
|
|
648
|
+
// Scheduled jobs: the time engine's and evaluator's types. The unions here
|
|
649
|
+
// may grow in a minor release; switch over them with a `default:` branch.
|
|
650
|
+
export type {
|
|
651
|
+
CronExpression,
|
|
652
|
+
DescribeScheduleOptions,
|
|
653
|
+
OccurrenceCount,
|
|
654
|
+
ParseScheduleOptions,
|
|
655
|
+
ScheduleAtSpec,
|
|
656
|
+
ScheduleCronSpec,
|
|
657
|
+
ScheduleDecision,
|
|
658
|
+
ScheduleEvaluationInput,
|
|
659
|
+
ScheduleEvaluationJob,
|
|
660
|
+
ScheduleEvaluationState,
|
|
661
|
+
ScheduleEverySpec,
|
|
662
|
+
ScheduleFireTrigger,
|
|
663
|
+
ScheduleJobLifecycle,
|
|
664
|
+
ScheduleMissedReason,
|
|
665
|
+
ScheduleObservedGap,
|
|
666
|
+
ScheduleSkipReason,
|
|
667
|
+
ScheduleSpec,
|
|
668
|
+
} from './schedules/index.js'
|
|
669
|
+
export type {
|
|
670
|
+
ScheduleBrowserGrant,
|
|
671
|
+
ScheduleBrowserSiteLevel,
|
|
672
|
+
ScheduleConfirmAnswer,
|
|
673
|
+
ScheduleConfirmRequest,
|
|
674
|
+
ScheduleJobDraft,
|
|
675
|
+
ScheduleJobPreview,
|
|
676
|
+
ScheduleJobSummary,
|
|
677
|
+
ScheduleRuleEffect,
|
|
678
|
+
ScheduleToolHost,
|
|
679
|
+
SessionLoop,
|
|
680
|
+
SessionLoopHost,
|
|
681
|
+
} from './tools/schedules/index.js'
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the model is told when a person declines its tool calls and gave no
|
|
3
|
+
* words of their own.
|
|
4
|
+
*
|
|
5
|
+
* "Declined" alone reads to a model as an obstacle to route around: in a live
|
|
6
|
+
* session an operator refused a browser navigation and the model fetched the
|
|
7
|
+
* same page through web search instead. The refusal is about the outcome, not
|
|
8
|
+
* the tool, so the text says so — for every tool, since any of them can be
|
|
9
|
+
* swapped for another that reaches the same place.
|
|
10
|
+
*/
|
|
11
|
+
export const DECLINED_TOOL_CALL_FEEDBACK =
|
|
12
|
+
'The user declined this. Do not get the same content or result another way (another tool, another site or address, or a web search) unless you ask the user first and they agree. Say what you wanted to do, or carry on without it.'
|
|
@@ -39,6 +39,7 @@ import type {
|
|
|
39
39
|
SkillRegistryRef,
|
|
40
40
|
ToolContext,
|
|
41
41
|
ToolDispatchOptions,
|
|
42
|
+
ToolHandoff,
|
|
42
43
|
ToolRegistryContract,
|
|
43
44
|
ToolResult,
|
|
44
45
|
} from '../../types/tool/index.js'
|
|
@@ -489,6 +490,8 @@ export interface ToolCallOutcome {
|
|
|
489
490
|
/** Rich form for the model, when the tool supplied one. */
|
|
490
491
|
content?: ToolResultContent
|
|
491
492
|
isError?: boolean
|
|
493
|
+
/** The tool asked for a person; see `ToolResult.handoff`. */
|
|
494
|
+
handoff?: ToolHandoff
|
|
492
495
|
}
|
|
493
496
|
|
|
494
497
|
export interface ToolExecutionBatch {
|
|
@@ -1446,9 +1449,16 @@ export class ToolExecutor {
|
|
|
1446
1449
|
}
|
|
1447
1450
|
|
|
1448
1451
|
private resultPresentation(name: string, input: unknown, result: ToolResult) {
|
|
1449
|
-
if (!result.success) return {}
|
|
1450
1452
|
try {
|
|
1451
1453
|
const view = this.config.tools.get(name)?.presentResult?.(input, result)
|
|
1454
|
+
// A failed call carries one view only: the person's No on the tool's
|
|
1455
|
+
// own screen, which a host draws as cancelled rather than failed and
|
|
1456
|
+
// cannot tell apart from the result text alone.
|
|
1457
|
+
if (!result.success) {
|
|
1458
|
+
return view?.kind === 'generic' && view.outcome === 'cancelled'
|
|
1459
|
+
? { presentation: { kind: 'generic', label: view.label, outcome: 'cancelled' } as const }
|
|
1460
|
+
: {}
|
|
1461
|
+
}
|
|
1452
1462
|
if (view?.kind !== 'diff') return {}
|
|
1453
1463
|
const serialized = JSON.stringify(view)
|
|
1454
1464
|
if (serialized.length > (this.config.maxToolOutputChars ?? DEFAULT_MAX_TOOL_OUTPUT_CHARS))
|
|
@@ -2004,6 +2014,12 @@ export class ToolExecutor {
|
|
|
2004
2014
|
// a result whose image is unaffected — and a hook that needs it gone
|
|
2005
2015
|
// says so with `content`, which wins over both.
|
|
2006
2016
|
...(modelContent !== undefined ? { content: modelContent } : {}),
|
|
2017
|
+
// Carried whatever a post-tool hook did to the text: the request is
|
|
2018
|
+
// the tool's statement about the world, not about its output, and a
|
|
2019
|
+
// hook that redacts a sign-in page has not signed anyone in.
|
|
2020
|
+
...(result.handoff !== undefined && !this.config.abortSignal.aborted
|
|
2021
|
+
? { handoff: result.handoff }
|
|
2022
|
+
: {}),
|
|
2007
2023
|
}
|
|
2008
2024
|
}
|
|
2009
2025
|
|
|
@@ -1462,6 +1462,7 @@ export async function* query(params: QueryParams): AsyncGenerator<SessionEvent,
|
|
|
1462
1462
|
provider: resilientProvider,
|
|
1463
1463
|
providerCapabilities: capabilities,
|
|
1464
1464
|
strictCapabilities: params.strictCapabilities === true,
|
|
1465
|
+
...(params.parentSessionId !== undefined ? { delegated: true } : {}),
|
|
1465
1466
|
servingMember: () => serving.current,
|
|
1466
1467
|
turnConfig,
|
|
1467
1468
|
...(params.stopWhen ? { stopWhen: params.stopWhen } : {}),
|
|
@@ -71,6 +71,7 @@ import {
|
|
|
71
71
|
relieveOverflow,
|
|
72
72
|
runCompactionCheck,
|
|
73
73
|
} from './phases/compaction.js'
|
|
74
|
+
import { runHandoffPause } from './phases/handoff.js'
|
|
74
75
|
import type { IterationContext } from './phases/index.js'
|
|
75
76
|
import { runPlanGate } from './phases/plan.js'
|
|
76
77
|
import { runToolReview } from './phases/tool-review.js'
|
|
@@ -1421,6 +1422,13 @@ export class IterationOrchestrator {
|
|
|
1421
1422
|
return
|
|
1422
1423
|
}
|
|
1423
1424
|
|
|
1425
|
+
// A tool asked for a person. The whole batch is committed; the
|
|
1426
|
+
// turn parks here rather than asking the model what to do about
|
|
1427
|
+
// something only the operator can do.
|
|
1428
|
+
if ((yield* runHandoffPause(this.ctx, iterationNum, reviewOutcome.results)) === 'stop') {
|
|
1429
|
+
return
|
|
1430
|
+
}
|
|
1431
|
+
|
|
1424
1432
|
if (reviewOutcome.decision === 'rejected') {
|
|
1425
1433
|
continue
|
|
1426
1434
|
}
|
|
@@ -44,6 +44,12 @@ import type { ToolGrantSet } from '../../tool-grants.js'
|
|
|
44
44
|
|
|
45
45
|
export interface IterationContext {
|
|
46
46
|
readonly provider: LLMProvider
|
|
47
|
+
/**
|
|
48
|
+
* The turn runs in a delegated child session. A tool's request for a
|
|
49
|
+
* person then fails the turn instead of pausing it: nobody is watching
|
|
50
|
+
* a child to resume it, and its parent is waiting on a result.
|
|
51
|
+
*/
|
|
52
|
+
readonly delegated?: boolean
|
|
47
53
|
/** Driver-level request shapes negotiated for this turn. */
|
|
48
54
|
readonly providerCapabilities?: ResolvedProviderCapabilities
|
|
49
55
|
/** Refuse a capability mismatch instead of emitting a warning and degrading. */
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { GENAI, NAMZU } from '../../../../telemetry/attributes.js'
|
|
2
|
+
import { NamzuError } from '../../../../types/errors/index.js'
|
|
3
|
+
import type { SessionEvent } from '../../../../types/session/index.js'
|
|
4
|
+
import type { ToolCallOutcome } from '../../executor.js'
|
|
5
|
+
import type { IterationContext, PhaseSignal } from './context.js'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* A tool asked for a person: stop before the next model call.
|
|
9
|
+
*
|
|
10
|
+
* Runs after the batch settled, so every result — the one that asked and
|
|
11
|
+
* its siblings — is already in the transcript and queued for the session
|
|
12
|
+
* log. The checkpoint written here is taken from that state, which is what
|
|
13
|
+
* lets a resume continue exactly as it does after a provider pause: the
|
|
14
|
+
* next step is a model call that sees the results.
|
|
15
|
+
*
|
|
16
|
+
* The first request in the batch speaks for it. Two tools that both need a
|
|
17
|
+
* person need the same thing from the operator — to come and look — and
|
|
18
|
+
* one pause answers both.
|
|
19
|
+
*
|
|
20
|
+
* In a delegated child the turn fails with the reason instead. Nobody
|
|
21
|
+
* resumes a child's turn: its parent is waiting on a result, and a failed
|
|
22
|
+
* child with the reason in it is a result the parent's model can act on.
|
|
23
|
+
*/
|
|
24
|
+
export async function* runHandoffPause(
|
|
25
|
+
ctx: IterationContext,
|
|
26
|
+
iterationNum: number,
|
|
27
|
+
results: readonly ToolCallOutcome[],
|
|
28
|
+
): AsyncGenerator<SessionEvent, PhaseSignal> {
|
|
29
|
+
const requested = results.find((result) => result.handoff !== undefined)
|
|
30
|
+
const handoff = requested?.handoff
|
|
31
|
+
if (!requested || !handoff) return 'continue'
|
|
32
|
+
|
|
33
|
+
if (ctx.delegated) {
|
|
34
|
+
ctx.log.info('A tool in a delegated turn needs a person; the turn fails', {
|
|
35
|
+
[NAMZU.TURN_ID]: ctx.recorder.turnId,
|
|
36
|
+
[NAMZU.ITERATION]: iterationNum,
|
|
37
|
+
[GENAI.TOOL_NAME]: requested.toolName,
|
|
38
|
+
})
|
|
39
|
+
throw new NamzuError({
|
|
40
|
+
code: 'tool_error',
|
|
41
|
+
message: `${requested.toolName} needs a person: ${handoff.reason}`,
|
|
42
|
+
details: { toolName: requested.toolName, handoff },
|
|
43
|
+
retryable: false,
|
|
44
|
+
})
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const checkpoint = await ctx.checkpointMgr.create(ctx.recorder, iterationNum)
|
|
48
|
+
await ctx.emitEvent({
|
|
49
|
+
type: 'checkpoint_created',
|
|
50
|
+
turnId: ctx.recorder.turnId,
|
|
51
|
+
checkpointId: checkpoint.id,
|
|
52
|
+
iteration: iterationNum,
|
|
53
|
+
})
|
|
54
|
+
yield* ctx.drainPending()
|
|
55
|
+
|
|
56
|
+
await ctx.emitEvent({
|
|
57
|
+
type: 'turn_paused',
|
|
58
|
+
budget: ctx.recorder.budget?.summary(),
|
|
59
|
+
turnId: ctx.recorder.turnId,
|
|
60
|
+
checkpointId: checkpoint.id,
|
|
61
|
+
reason: handoff.reason,
|
|
62
|
+
handoff,
|
|
63
|
+
})
|
|
64
|
+
yield* ctx.drainPending()
|
|
65
|
+
ctx.recorder.setStopReason('paused')
|
|
66
|
+
ctx.log.info('Turn paused for a person', {
|
|
67
|
+
[NAMZU.TURN_ID]: ctx.recorder.turnId,
|
|
68
|
+
[NAMZU.ITERATION]: iterationNum,
|
|
69
|
+
[GENAI.TOOL_NAME]: requested.toolName,
|
|
70
|
+
'namzu.checkpoint.id': checkpoint.id,
|
|
71
|
+
'namzu.runtime.reason': handoff.reason,
|
|
72
|
+
})
|
|
73
|
+
return 'stop'
|
|
74
|
+
}
|