@yolk-sdk/codemode 0.1.0-canary.106 → 0.1.0-canary.108
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/README.md +20 -2
- package/dist/catalog.d.mts +69 -9
- package/dist/catalog.d.mts.map +1 -1
- package/dist/catalog.mjs +168 -19
- package/dist/catalog.mjs.map +1 -1
- package/dist/classifier-tool.mjs +1 -1
- package/dist/executor.d.mts +10 -1
- package/dist/executor.d.mts.map +1 -1
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +2 -2
- package/dist/node.d.mts.map +1 -1
- package/dist/node.mjs +12 -5
- package/dist/node.mjs.map +1 -1
- package/dist/search.d.mts +11 -6
- package/dist/search.d.mts.map +1 -1
- package/dist/search.mjs +23 -45
- package/dist/search.mjs.map +1 -1
- package/dist/tool.d.mts +9 -0
- package/dist/tool.d.mts.map +1 -1
- package/dist/tool.mjs +72 -21
- package/dist/tool.mjs.map +1 -1
- package/package.json +2 -2
- package/src/catalog.ts +241 -22
- package/src/executor.ts +6 -0
- package/src/index.ts +8 -1
- package/src/node.ts +22 -9
- package/src/search.ts +31 -85
- package/src/tool.ts +112 -16
package/src/node.ts
CHANGED
|
@@ -121,7 +121,7 @@ const maxArgumentDepth = 64
|
|
|
121
121
|
* proxy (close-match suggestions). Not a security boundary (`globalThis.tools` is unguarded):
|
|
122
122
|
* arguments are still decoded by the host.
|
|
123
123
|
*/
|
|
124
|
-
const toolsGuardSource = `(function (tools, labels) {
|
|
124
|
+
const toolsGuardSource = `(function (tools, labels, unbound) {
|
|
125
125
|
// Captured before the script runs, so a script that changes built-ins cannot change the checks.
|
|
126
126
|
const { apply, getPrototypeOf, setPrototypeOf } = Reflect
|
|
127
127
|
const isFinite = Number.isFinite
|
|
@@ -259,8 +259,13 @@ const toolsGuardSource = `(function (tools, labels) {
|
|
|
259
259
|
if (!wrappers.has(call)) wrappers.set(call, wrap('tools[' + JSON.stringify(key) + ']', call))
|
|
260
260
|
guarded[key] = wrappers.get(call)
|
|
261
261
|
}
|
|
262
|
+
// Unbound keys (stage-only tools) reject with their hint instead of being undefined.
|
|
263
|
+
const rejections = create(null)
|
|
264
|
+
for (const [key, message] of unbound) {
|
|
265
|
+
if (!(key in guarded)) rejections[key] = async () => { throw new TypeErrorCtor(message) }
|
|
266
|
+
}
|
|
262
267
|
return new Proxy(Object.freeze(guarded), {
|
|
263
|
-
get: (target, key) => (key in target ? target[key] : tools[key])
|
|
268
|
+
get: (target, key) => (key in target ? target[key] : key in rejections ? rejections[key] : tools[key])
|
|
264
269
|
})
|
|
265
270
|
})`
|
|
266
271
|
|
|
@@ -275,7 +280,10 @@ const jsonSource = (value: unknown) =>
|
|
|
275
280
|
char === '\u2028' ? '\\u2028' : '\\u2029'
|
|
276
281
|
)
|
|
277
282
|
|
|
278
|
-
const guardSuffix = (
|
|
283
|
+
const guardSuffix = (
|
|
284
|
+
tools: ReadonlyArray<CodeModeExecutorTool>,
|
|
285
|
+
unbound: CodeModeExecuteOptions['unbound']
|
|
286
|
+
) => {
|
|
279
287
|
const names = tools.map(tool => tool.name)
|
|
280
288
|
const labels = codeModeCallLabels(names)
|
|
281
289
|
|
|
@@ -285,7 +293,9 @@ const guardSuffix = (tools: ReadonlyArray<CodeModeExecutorTool>) => {
|
|
|
285
293
|
tool.callLabel ?? labels[index] ?? `tools[${JSON.stringify(tool.name)}]`
|
|
286
294
|
])
|
|
287
295
|
|
|
288
|
-
|
|
296
|
+
const rejected = (unbound ?? []).map(entry => [entry.key, entry.message])
|
|
297
|
+
|
|
298
|
+
return `\n})(${toolsGuardSource}(tools, ${jsonSource(pairs)}, ${jsonSource(rejected)}), console)`
|
|
289
299
|
}
|
|
290
300
|
|
|
291
301
|
const lineTerminators = /\r\n?|[\n\u2028\u2029]/g
|
|
@@ -510,11 +520,14 @@ export const makePiCodeModeExecutor = (
|
|
|
510
520
|
|
|
511
521
|
try {
|
|
512
522
|
return fromPiResult(
|
|
513
|
-
await sandbox.execute(
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
523
|
+
await sandbox.execute(
|
|
524
|
+
`${guardPrefix}${stripped.code}${guardSuffix(execution.tools, execution.unbound)}`,
|
|
525
|
+
{
|
|
526
|
+
signal: execution.signal,
|
|
527
|
+
store: execution.store,
|
|
528
|
+
timeoutMs
|
|
529
|
+
}
|
|
530
|
+
),
|
|
518
531
|
scriptLineCount(stripped.code)
|
|
519
532
|
)
|
|
520
533
|
} finally {
|
package/src/search.ts
CHANGED
|
@@ -1,113 +1,59 @@
|
|
|
1
|
+
import { rankToolSearchDocuments, toolSearchTokens } from '@yolk-sdk/agent/tools'
|
|
1
2
|
import type { CodeModeCatalogTool } from './catalog.ts'
|
|
2
3
|
|
|
3
4
|
/** Default number of `searchTools` results. */
|
|
4
5
|
export const defaultCodeModeSearchLimit = 8
|
|
5
6
|
|
|
6
|
-
const k1 = 1.2
|
|
7
|
-
|
|
8
|
-
const b = 0.75
|
|
9
|
-
|
|
10
|
-
/** Lowercase word tokens; splits on non-alphanumerics and camelCase boundaries. */
|
|
11
|
-
export const codeModeSearchTokens = (text: string): ReadonlyArray<string> =>
|
|
12
|
-
text
|
|
13
|
-
.replace(/([a-z0-9])([A-Z])/g, '$1 $2')
|
|
14
|
-
.toLowerCase()
|
|
15
|
-
.split(/[^a-z0-9]+/)
|
|
16
|
-
.filter(token => token.length > 0)
|
|
17
|
-
|
|
18
|
-
type IndexedTool = {
|
|
19
|
-
readonly tool: CodeModeCatalogTool
|
|
20
|
-
readonly frequencies: ReadonlyMap<string, number>
|
|
21
|
-
readonly length: number
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const indexTool = (tool: CodeModeCatalogTool): IndexedTool => {
|
|
25
|
-
const tokens = codeModeSearchTokens(
|
|
26
|
-
[
|
|
27
|
-
tool.identifier,
|
|
28
|
-
tool.name,
|
|
29
|
-
tool.description,
|
|
30
|
-
tool.namespace,
|
|
31
|
-
tool.namespaceDescription ?? ''
|
|
32
|
-
].join(' ')
|
|
33
|
-
)
|
|
34
|
-
|
|
35
|
-
const frequencies = new Map<string, number>()
|
|
36
|
-
|
|
37
|
-
for (const token of tokens) {
|
|
38
|
-
frequencies.set(token, (frequencies.get(token) ?? 0) + 1)
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
return { tool, frequencies, length: tokens.length }
|
|
42
|
-
}
|
|
43
|
-
|
|
44
7
|
export type CodeModeSearchOptions = {
|
|
45
8
|
readonly limit?: number
|
|
46
9
|
readonly namespace?: string
|
|
47
10
|
}
|
|
48
11
|
|
|
49
12
|
export type CodeModeSearchHit = {
|
|
50
|
-
/** The identifier scripts call as `tools.<name>(args)
|
|
13
|
+
/** The identifier scripts call as `tools.<name>(args)`, or for a stage-only tool the name
|
|
14
|
+
* scripts pass to `stage(name, args)`.
|
|
15
|
+
*/
|
|
51
16
|
readonly name: string
|
|
52
17
|
readonly description: string
|
|
18
|
+
/** A stage-only tool (ADR 0007): no `tools` binding, only `stage()` it. */
|
|
19
|
+
readonly stageOnly?: true
|
|
53
20
|
}
|
|
54
21
|
|
|
55
22
|
/**
|
|
56
|
-
* BM25 over each tool's identifier, raw name, description,
|
|
57
|
-
* A query without words lists the
|
|
23
|
+
* BM25 (the shared `rankToolSearchDocuments`) over each tool's identifier, raw name, description,
|
|
24
|
+
* namespace, and namespace description. Ties keep catalog order. A query without words lists the
|
|
25
|
+
* (namespace-filtered) tools in catalog order.
|
|
58
26
|
*/
|
|
59
27
|
export const searchCodeModeTools = (
|
|
60
28
|
catalog: ReadonlyArray<CodeModeCatalogTool>,
|
|
61
29
|
query: string,
|
|
62
30
|
options: CodeModeSearchOptions = {}
|
|
63
31
|
): ReadonlyArray<CodeModeSearchHit> => {
|
|
64
|
-
const limit = Math.max(0, Math.floor(options.limit ?? defaultCodeModeSearchLimit))
|
|
65
|
-
|
|
66
32
|
const scoped =
|
|
67
33
|
options.namespace === undefined
|
|
68
34
|
? catalog
|
|
69
35
|
: catalog.filter(tool => tool.namespace === options.namespace)
|
|
70
36
|
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
return [term, Math.log(1 + (documents.length - containing + 0.5) / (containing + 0.5))]
|
|
92
|
-
})
|
|
93
|
-
)
|
|
94
|
-
|
|
95
|
-
return documents
|
|
96
|
-
.map((document, index) => {
|
|
97
|
-
const score = terms.reduce((total, term) => {
|
|
98
|
-
const frequency = document.frequencies.get(term) ?? 0
|
|
99
|
-
|
|
100
|
-
if (frequency === 0) return total
|
|
101
|
-
|
|
102
|
-
const norm = k1 * (1 - b + (b * document.length) / Math.max(1, averageLength))
|
|
103
|
-
|
|
104
|
-
return total + ((inverse.get(term) ?? 0) * frequency * (k1 + 1)) / (frequency + norm)
|
|
105
|
-
}, 0)
|
|
106
|
-
|
|
107
|
-
return { document, index, score }
|
|
108
|
-
})
|
|
109
|
-
.filter(entry => entry.score > 0)
|
|
110
|
-
.sort((left, right) => right.score - left.score || left.index - right.index)
|
|
111
|
-
.slice(0, limit)
|
|
112
|
-
.map(entry => hit(entry.document.tool))
|
|
37
|
+
const hit = (tool: CodeModeCatalogTool): CodeModeSearchHit =>
|
|
38
|
+
tool.exposure === 'stage'
|
|
39
|
+
? { name: tool.name, description: tool.description, stageOnly: true }
|
|
40
|
+
: { name: tool.identifier, description: tool.description }
|
|
41
|
+
|
|
42
|
+
return rankToolSearchDocuments(
|
|
43
|
+
scoped.map(tool => ({
|
|
44
|
+
value: tool,
|
|
45
|
+
text: [
|
|
46
|
+
tool.identifier,
|
|
47
|
+
tool.name,
|
|
48
|
+
tool.description,
|
|
49
|
+
tool.namespace,
|
|
50
|
+
tool.namespaceDescription ?? ''
|
|
51
|
+
].join(' ')
|
|
52
|
+
})),
|
|
53
|
+
query,
|
|
54
|
+
options.limit ?? defaultCodeModeSearchLimit
|
|
55
|
+
).map(hit)
|
|
113
56
|
}
|
|
57
|
+
|
|
58
|
+
/** Lowercase word tokens of code mode search (the shared `toolSearchTokens`). */
|
|
59
|
+
export const codeModeSearchTokens = toolSearchTokens
|
package/src/tool.ts
CHANGED
|
@@ -30,9 +30,12 @@ import {
|
|
|
30
30
|
type NestedToolCallInput,
|
|
31
31
|
type NestedToolCallStatus
|
|
32
32
|
} from '@yolk-sdk/agent/protocol'
|
|
33
|
+
import { toCodemodeIdentifier } from '@earendil-works/pi-codemode/declarations'
|
|
33
34
|
import {
|
|
34
35
|
makeTool,
|
|
36
|
+
type NestedTool,
|
|
35
37
|
type NestedToolExecutor,
|
|
38
|
+
type NestedToolInstructor,
|
|
36
39
|
type StagedCallReceipt,
|
|
37
40
|
type ToolExecutionInput,
|
|
38
41
|
type ToolLedgerAbandonedInput,
|
|
@@ -43,11 +46,15 @@ import {
|
|
|
43
46
|
} from '@yolk-sdk/agent/tools'
|
|
44
47
|
import {
|
|
45
48
|
codeModeCatalog,
|
|
49
|
+
codeModeCatalogInstruction,
|
|
50
|
+
codeModeStageOnlyCatalog,
|
|
46
51
|
defaultCodeModeInlineBudget,
|
|
47
52
|
describeCodeModeTool,
|
|
48
53
|
findCodeModeTool,
|
|
49
54
|
renderCodeModeDescription,
|
|
50
|
-
|
|
55
|
+
renderCodeModeInstructedDescription,
|
|
56
|
+
type CodeModeCatalogTool,
|
|
57
|
+
type CodeModeStagingDescription
|
|
51
58
|
} from './catalog.ts'
|
|
52
59
|
import type {
|
|
53
60
|
CodeModeExecutionResult,
|
|
@@ -156,6 +163,15 @@ export type MakeCodeModeToolOptions<Context> = {
|
|
|
156
163
|
* resolution's cap.
|
|
157
164
|
*/
|
|
158
165
|
readonly staging?: CodeModeStagingOptions | false
|
|
166
|
+
/**
|
|
167
|
+
* Where the nested tool catalog goes (ADR 0007). `description` (default): in this tool's
|
|
168
|
+
* description, which changes with the catalog. `instructions`: the description stays invariant
|
|
169
|
+
* and the catalog (every namespace with its tool count, listed tools within `inlineBudget`) is
|
|
170
|
+
* the resolution's `instructions` entry; hosts pass `ResolvedToolSet.instructions` to
|
|
171
|
+
* `RunConfig.instructions`, so a change is appended to the conversation instead of rewriting the
|
|
172
|
+
* cached request prefix.
|
|
173
|
+
*/
|
|
174
|
+
readonly catalog?: 'description' | 'instructions'
|
|
159
175
|
}
|
|
160
176
|
|
|
161
177
|
export type CodeModeStagingOptions = {
|
|
@@ -267,7 +283,11 @@ const hookOutcome = (exit: Exit.Exit<ToolResult>): CodeModeNestedCallOutcome =>
|
|
|
267
283
|
type CodeModeNamespaceDescription = {
|
|
268
284
|
readonly name: string
|
|
269
285
|
description?: string
|
|
270
|
-
readonly tools: ReadonlyArray<{
|
|
286
|
+
readonly tools: ReadonlyArray<{
|
|
287
|
+
readonly name: string
|
|
288
|
+
readonly description: string
|
|
289
|
+
readonly stageOnly?: true
|
|
290
|
+
}>
|
|
271
291
|
}
|
|
272
292
|
|
|
273
293
|
const SearchOptions = Schema.Struct({
|
|
@@ -277,7 +297,8 @@ const SearchOptions = Schema.Struct({
|
|
|
277
297
|
|
|
278
298
|
const decodeSearchOptions = Schema.decodeUnknownOption(SearchOptions)
|
|
279
299
|
|
|
280
|
-
/** `searchTools`, `describeTool`, and `describeNamespace` over every nested tool
|
|
300
|
+
/** `searchTools`, `describeTool`, and `describeNamespace` over every nested tool and, while the
|
|
301
|
+
* script may stage, every stage-only tool. */
|
|
281
302
|
const discoveryGlobals = (
|
|
282
303
|
catalog: ReadonlyArray<CodeModeCatalogTool>
|
|
283
304
|
): ReadonlyArray<CodeModeExecutorGlobal> => [
|
|
@@ -317,7 +338,11 @@ const discoveryGlobals = (
|
|
|
317
338
|
|
|
318
339
|
const namespace: CodeModeNamespaceDescription = {
|
|
319
340
|
name,
|
|
320
|
-
tools: tools.map(tool =>
|
|
341
|
+
tools: tools.map(tool =>
|
|
342
|
+
tool.exposure === 'stage'
|
|
343
|
+
? { name: tool.name, description: tool.description, stageOnly: true }
|
|
344
|
+
: { name: tool.identifier, description: tool.description }
|
|
345
|
+
)
|
|
321
346
|
}
|
|
322
347
|
|
|
323
348
|
if (description !== undefined) namespace.description = description
|
|
@@ -332,8 +357,15 @@ type StageFork = (
|
|
|
332
357
|
options: { readonly signal: AbortSignal }
|
|
333
358
|
) => Fiber.Fiber<StagedCallReceipt, ToolPlanStageError>
|
|
334
359
|
|
|
335
|
-
/**
|
|
336
|
-
|
|
360
|
+
/**
|
|
361
|
+
* `stage(name, args)`: records one call into the script's plan (never runs it). `name` is the raw
|
|
362
|
+
* tool name or its catalog identifier (what `searchTools` returns for callable tools).
|
|
363
|
+
*/
|
|
364
|
+
const stageGlobal = (
|
|
365
|
+
plan: ToolPlanBuilder,
|
|
366
|
+
fork: StageFork,
|
|
367
|
+
stageable: ReadonlyArray<NestedTool>
|
|
368
|
+
): CodeModeExecutorGlobal => ({
|
|
337
369
|
name: 'stage',
|
|
338
370
|
spread: true,
|
|
339
371
|
signature: '(name: string, args: object): Promise<{ staged: true; key: string; index: number }>',
|
|
@@ -347,8 +379,13 @@ const stageGlobal = (plan: ToolPlanBuilder, fork: StageFork): CodeModeExecutorGl
|
|
|
347
379
|
if (!Predicate.isString(name))
|
|
348
380
|
return Promise.reject(new Error('stage: the first argument must be a tool name.'))
|
|
349
381
|
|
|
382
|
+
const toolName =
|
|
383
|
+
stageable.find(({ def }) => def.name === name)?.def.name ??
|
|
384
|
+
stageable.find(({ def }) => toCodemodeIdentifier(def.name) === name)?.def.name ??
|
|
385
|
+
name
|
|
386
|
+
|
|
350
387
|
return new Promise((resolve, reject) => {
|
|
351
|
-
fork(plan.stage({ name, params: params === undefined ? {} : params }), {
|
|
388
|
+
fork(plan.stage({ name: toolName, params: params === undefined ? {} : params }), {
|
|
352
389
|
signal
|
|
353
390
|
}).addObserver(exit => {
|
|
354
391
|
if (Exit.isSuccess(exit)) {
|
|
@@ -373,6 +410,19 @@ const stageGlobal = (plan: ToolPlanBuilder, fork: StageFork): CodeModeExecutorGl
|
|
|
373
410
|
}
|
|
374
411
|
})
|
|
375
412
|
|
|
413
|
+
/** A direct call of a stage-only tool rejects with a hint to stage it (ADR 0007). */
|
|
414
|
+
const stageOnlyRejections = (stageOnly: ReadonlyArray<CodeModeCatalogTool>) =>
|
|
415
|
+
stageOnly.flatMap(tool => {
|
|
416
|
+
const message = `${tool.name} is approval-gated: record it with stage(${JSON.stringify(tool.name)}, args) instead of calling it.`
|
|
417
|
+
|
|
418
|
+
return tool.identifier === tool.name
|
|
419
|
+
? [{ key: tool.name, message }]
|
|
420
|
+
: [
|
|
421
|
+
{ key: tool.identifier, message },
|
|
422
|
+
{ key: tool.name, message }
|
|
423
|
+
]
|
|
424
|
+
})
|
|
425
|
+
|
|
376
426
|
const planNotice = (plan: CodeModeStagedPlan) =>
|
|
377
427
|
`\n\nStaged ${plan.count} call${plan.count === 1 ? '' : 's'} as plan ${plan.id}; nothing was applied yet. To apply them, call ${plan.reviewToolName}(${JSON.stringify({ planId: plan.id, planDigest: plan.digest })}): a person reviews the plan and selects the calls to apply.`
|
|
378
428
|
|
|
@@ -600,14 +650,23 @@ const runScript = <Context>(input: RunInput<Context>): Effect.Effect<ToolResult,
|
|
|
600
650
|
execute: callTool(tool)
|
|
601
651
|
}))
|
|
602
652
|
|
|
653
|
+
const stageOnly =
|
|
654
|
+
plan === undefined || staging === undefined
|
|
655
|
+
? []
|
|
656
|
+
: codeModeStageOnlyCatalog(staging.tools)
|
|
657
|
+
|
|
603
658
|
const result = yield* Effect.tryPromise({
|
|
604
659
|
try: signal =>
|
|
605
660
|
options.executor.execute(input.code, {
|
|
606
661
|
tools,
|
|
607
662
|
globals:
|
|
608
|
-
plan === undefined
|
|
663
|
+
plan === undefined || staging === undefined
|
|
609
664
|
? discoveryGlobals(catalog)
|
|
610
|
-
: [
|
|
665
|
+
: [
|
|
666
|
+
...discoveryGlobals([...catalog, ...stageOnly]),
|
|
667
|
+
stageGlobal(plan, stageFork, staging.tools)
|
|
668
|
+
],
|
|
669
|
+
unbound: stageOnlyRejections(stageOnly),
|
|
611
670
|
timeoutMs,
|
|
612
671
|
memoryLimitBytes: limits.memoryLimitBytes,
|
|
613
672
|
store,
|
|
@@ -893,18 +952,41 @@ export const makeCodeModeTool = <Context>(
|
|
|
893
952
|
const inlineBudget = options.inlineBudget ?? defaultCodeModeInlineBudget
|
|
894
953
|
const store = options.loadStore !== undefined
|
|
895
954
|
|
|
896
|
-
|
|
955
|
+
const instructed = options.catalog === 'instructions'
|
|
956
|
+
|
|
957
|
+
// In description mode `stage()` is documented once a stageable tool exists (the description
|
|
958
|
+
// lists them anyway). In instructions mode the description must not depend on tools: staging
|
|
959
|
+
// availability alone decides.
|
|
960
|
+
const offered = (staging: CodeModeStagingDescription | undefined) =>
|
|
961
|
+
options.staging === false ||
|
|
962
|
+
staging === undefined ||
|
|
963
|
+
(!instructed && staging.tools.length === 0)
|
|
964
|
+
? undefined
|
|
965
|
+
: staging
|
|
966
|
+
|
|
967
|
+
const registration = makeTool<Context, typeof CodeModeParams>({
|
|
897
968
|
name,
|
|
898
|
-
description:
|
|
969
|
+
description: instructed
|
|
970
|
+
? renderCodeModeInstructedDescription({ store })
|
|
971
|
+
: renderCodeModeDescription({ tools: [], inlineBudget, store }),
|
|
899
972
|
parameters: CodeModeParams,
|
|
900
973
|
access: 'write',
|
|
901
974
|
nestedToolAccess: true,
|
|
902
|
-
describe: ({ tools, staging }) =>
|
|
903
|
-
|
|
904
|
-
|
|
975
|
+
describe: ({ tools, staging }) => {
|
|
976
|
+
const offeredStaging = offered(staging)
|
|
977
|
+
|
|
978
|
+
if (instructed)
|
|
979
|
+
return renderCodeModeInstructedDescription(
|
|
980
|
+
offeredStaging === undefined ? { store } : { store, staging: offeredStaging }
|
|
981
|
+
)
|
|
982
|
+
|
|
983
|
+
return renderCodeModeDescription(
|
|
984
|
+
offeredStaging === undefined
|
|
905
985
|
? { tools, inlineBudget, store }
|
|
906
|
-
: { tools, inlineBudget, store, staging }
|
|
907
|
-
)
|
|
986
|
+
: { tools, inlineBudget, store, staging: offeredStaging }
|
|
987
|
+
)
|
|
988
|
+
},
|
|
989
|
+
|
|
908
990
|
abandonedResult: abandonedScriptResult(limits),
|
|
909
991
|
execute: ({
|
|
910
992
|
call,
|
|
@@ -922,4 +1004,18 @@ export const makeCodeModeTool = <Context>(
|
|
|
922
1004
|
)
|
|
923
1005
|
: runScript({ options, limits, call, context, code: params.code, nested })
|
|
924
1006
|
})
|
|
1007
|
+
|
|
1008
|
+
if (!instructed) return registration
|
|
1009
|
+
|
|
1010
|
+
const instruct: NestedToolInstructor = ({ tools, staging }) => {
|
|
1011
|
+
const offeredStaging = offered(staging)
|
|
1012
|
+
|
|
1013
|
+
return codeModeCatalogInstruction(
|
|
1014
|
+
offeredStaging === undefined
|
|
1015
|
+
? { tools, inlineBudget }
|
|
1016
|
+
: { tools, inlineBudget, staging: offeredStaging }
|
|
1017
|
+
)
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
return { ...registration, instruct }
|
|
925
1021
|
}
|