@namzu/sdk 0.6.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +362 -0
- package/dist/advisory/executor.d.ts.map +1 -1
- package/dist/advisory/executor.js +9 -2
- package/dist/advisory/executor.js.map +1 -1
- package/dist/advisory/executor.test.d.ts +2 -1
- package/dist/advisory/executor.test.d.ts.map +1 -1
- package/dist/advisory/executor.test.js +7 -4
- package/dist/advisory/executor.test.js.map +1 -1
- package/dist/agents/ReactiveAgent.d.ts.map +1 -1
- package/dist/agents/ReactiveAgent.js +2 -0
- package/dist/agents/ReactiveAgent.js.map +1 -1
- package/dist/agents/SupervisorAgent.d.ts.map +1 -1
- package/dist/agents/SupervisorAgent.js +7 -0
- package/dist/agents/SupervisorAgent.js.map +1 -1
- package/dist/bridge/sse/mapper.test.js +2 -2
- package/dist/constants/compaction/index.d.ts.map +1 -1
- package/dist/constants/compaction/index.js +8 -3
- package/dist/constants/compaction/index.js.map +1 -1
- package/dist/constants/sandbox/index.d.ts +21 -0
- package/dist/constants/sandbox/index.d.ts.map +1 -1
- package/dist/constants/sandbox/index.js +30 -0
- package/dist/constants/sandbox/index.js.map +1 -1
- package/dist/constants/tools/index.d.ts.map +1 -1
- package/dist/constants/tools/index.js +33 -2
- package/dist/constants/tools/index.js.map +1 -1
- package/dist/manager/run/persistence.d.ts.map +1 -1
- package/dist/manager/run/persistence.js +35 -5
- package/dist/manager/run/persistence.js.map +1 -1
- package/dist/persona/assembler.d.ts +1 -0
- package/dist/persona/assembler.d.ts.map +1 -1
- package/dist/persona/assembler.js +28 -6
- package/dist/persona/assembler.js.map +1 -1
- package/dist/provider/collect.test.js +2 -2
- package/dist/public-runtime.d.ts +5 -4
- package/dist/public-runtime.d.ts.map +1 -1
- package/dist/public-runtime.js +5 -4
- package/dist/public-runtime.js.map +1 -1
- package/dist/public-tools.d.ts +2 -0
- package/dist/public-tools.d.ts.map +1 -1
- package/dist/public-tools.js +2 -0
- package/dist/public-tools.js.map +1 -1
- package/dist/public-types.d.ts +3 -0
- package/dist/public-types.d.ts.map +1 -1
- package/dist/registry/index.d.ts +2 -0
- package/dist/registry/index.d.ts.map +1 -1
- package/dist/registry/index.js +1 -0
- package/dist/registry/index.js.map +1 -1
- package/dist/registry/tool/execute.d.ts.map +1 -1
- package/dist/registry/tool/execute.js +87 -5
- package/dist/registry/tool/execute.js.map +1 -1
- package/dist/registry/tool/execute.test.d.ts +4 -2
- package/dist/registry/tool/execute.test.d.ts.map +1 -1
- package/dist/registry/tool/execute.test.js +112 -3
- package/dist/registry/tool/execute.test.js.map +1 -1
- package/dist/registry/toolset/catalog.d.ts +42 -0
- package/dist/registry/toolset/catalog.d.ts.map +1 -0
- package/dist/registry/toolset/catalog.js +217 -0
- package/dist/registry/toolset/catalog.js.map +1 -0
- package/dist/registry/toolset/catalog.test.d.ts +2 -0
- package/dist/registry/toolset/catalog.test.d.ts.map +1 -0
- package/dist/registry/toolset/catalog.test.js +85 -0
- package/dist/registry/toolset/catalog.test.js.map +1 -0
- package/dist/runtime/query/__tests__/deferred-tools.test.d.ts +2 -0
- package/dist/runtime/query/__tests__/deferred-tools.test.d.ts.map +1 -0
- package/dist/runtime/query/__tests__/deferred-tools.test.js +147 -0
- package/dist/runtime/query/__tests__/deferred-tools.test.js.map +1 -0
- package/dist/runtime/query/__tests__/executor-concurrency.test.d.ts +2 -0
- package/dist/runtime/query/__tests__/executor-concurrency.test.d.ts.map +1 -0
- package/dist/runtime/query/__tests__/executor-concurrency.test.js +98 -0
- package/dist/runtime/query/__tests__/executor-concurrency.test.js.map +1 -0
- package/dist/runtime/query/__tests__/executor-plugin-hooks.test.js +38 -3
- package/dist/runtime/query/__tests__/executor-plugin-hooks.test.js.map +1 -1
- package/dist/runtime/query/__tests__/prompt.test.js +47 -2
- package/dist/runtime/query/__tests__/prompt.test.js.map +1 -1
- package/dist/runtime/query/__tests__/stream-recovery.test.d.ts +2 -0
- package/dist/runtime/query/__tests__/stream-recovery.test.d.ts.map +1 -0
- package/dist/runtime/query/__tests__/stream-recovery.test.js +126 -0
- package/dist/runtime/query/__tests__/stream-recovery.test.js.map +1 -0
- package/dist/runtime/query/continuation.d.ts +16 -0
- package/dist/runtime/query/continuation.d.ts.map +1 -0
- package/dist/runtime/query/continuation.js +16 -0
- package/dist/runtime/query/continuation.js.map +1 -0
- package/dist/runtime/query/executor.d.ts +3 -0
- package/dist/runtime/query/executor.d.ts.map +1 -1
- package/dist/runtime/query/executor.js +71 -3
- 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 +19 -3
- package/dist/runtime/query/index.js.map +1 -1
- package/dist/runtime/query/iteration/index.d.ts +22 -0
- package/dist/runtime/query/iteration/index.d.ts.map +1 -1
- package/dist/runtime/query/iteration/index.js +227 -60
- package/dist/runtime/query/iteration/index.js.map +1 -1
- package/dist/runtime/query/iteration/phases/context.d.ts +10 -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/prompt.d.ts.map +1 -1
- package/dist/runtime/query/prompt.js +21 -1
- package/dist/runtime/query/prompt.js.map +1 -1
- package/dist/runtime/query/tooling.d.ts +1 -0
- package/dist/runtime/query/tooling.d.ts.map +1 -1
- package/dist/runtime/query/tooling.js +1 -0
- package/dist/runtime/query/tooling.js.map +1 -1
- package/dist/sandbox/provider/local.d.ts.map +1 -1
- package/dist/sandbox/provider/local.js +32 -1
- package/dist/sandbox/provider/local.js.map +1 -1
- package/dist/session/workspace/__tests__/shared-run.test.d.ts +2 -0
- package/dist/session/workspace/__tests__/shared-run.test.d.ts.map +1 -0
- package/dist/session/workspace/__tests__/shared-run.test.js +147 -0
- package/dist/session/workspace/__tests__/shared-run.test.js.map +1 -0
- package/dist/session/workspace/index.d.ts +2 -0
- package/dist/session/workspace/index.d.ts.map +1 -1
- package/dist/session/workspace/index.js +1 -0
- package/dist/session/workspace/index.js.map +1 -1
- package/dist/session/workspace/shared-run.d.ts +81 -0
- package/dist/session/workspace/shared-run.d.ts.map +1 -0
- package/dist/session/workspace/shared-run.js +251 -0
- package/dist/session/workspace/shared-run.js.map +1 -0
- package/dist/skills/loader.d.ts.map +1 -1
- package/dist/skills/loader.js +36 -6
- package/dist/skills/loader.js.map +1 -1
- package/dist/skills/loader.test.d.ts +2 -0
- package/dist/skills/loader.test.d.ts.map +1 -0
- package/dist/skills/loader.test.js +65 -0
- package/dist/skills/loader.test.js.map +1 -0
- package/dist/streaming/coalesce.test.js +1 -1
- package/dist/tools/builtins/__tests__/edit.test.d.ts +2 -0
- package/dist/tools/builtins/__tests__/edit.test.d.ts.map +1 -0
- package/dist/tools/builtins/__tests__/edit.test.js +38 -0
- package/dist/tools/builtins/__tests__/edit.test.js.map +1 -0
- package/dist/tools/builtins/__tests__/payload-budget.test.d.ts +2 -0
- package/dist/tools/builtins/__tests__/payload-budget.test.d.ts.map +1 -0
- package/dist/tools/builtins/__tests__/payload-budget.test.js +22 -0
- package/dist/tools/builtins/__tests__/payload-budget.test.js.map +1 -0
- package/dist/tools/builtins/__tests__/read-file.test.d.ts +2 -0
- package/dist/tools/builtins/__tests__/read-file.test.d.ts.map +1 -0
- package/dist/tools/builtins/__tests__/read-file.test.js +24 -0
- package/dist/tools/builtins/__tests__/read-file.test.js.map +1 -0
- package/dist/tools/builtins/__tests__/verify-outputs.test.d.ts +2 -0
- package/dist/tools/builtins/__tests__/verify-outputs.test.d.ts.map +1 -0
- package/dist/tools/builtins/__tests__/verify-outputs.test.js +52 -0
- package/dist/tools/builtins/__tests__/verify-outputs.test.js.map +1 -0
- package/dist/tools/builtins/__tests__/write-file.test.d.ts +2 -0
- package/dist/tools/builtins/__tests__/write-file.test.d.ts.map +1 -0
- package/dist/tools/builtins/__tests__/write-file.test.js +74 -0
- package/dist/tools/builtins/__tests__/write-file.test.js.map +1 -0
- package/dist/tools/builtins/bash.d.ts.map +1 -1
- package/dist/tools/builtins/bash.js +40 -7
- package/dist/tools/builtins/bash.js.map +1 -1
- package/dist/tools/builtins/edit.d.ts +5 -2
- package/dist/tools/builtins/edit.d.ts.map +1 -1
- package/dist/tools/builtins/edit.js +114 -18
- package/dist/tools/builtins/edit.js.map +1 -1
- package/dist/tools/builtins/index.d.ts +1 -0
- package/dist/tools/builtins/index.d.ts.map +1 -1
- package/dist/tools/builtins/index.js +13 -13
- package/dist/tools/builtins/index.js.map +1 -1
- package/dist/tools/builtins/read-file.d.ts +1 -0
- package/dist/tools/builtins/read-file.d.ts.map +1 -1
- package/dist/tools/builtins/read-file.js +23 -8
- package/dist/tools/builtins/read-file.js.map +1 -1
- package/dist/tools/builtins/search-tools.d.ts.map +1 -1
- package/dist/tools/builtins/search-tools.js +4 -1
- package/dist/tools/builtins/search-tools.js.map +1 -1
- package/dist/tools/builtins/verify-outputs.d.ts +5 -0
- package/dist/tools/builtins/verify-outputs.d.ts.map +1 -0
- package/dist/tools/builtins/verify-outputs.js +103 -0
- package/dist/tools/builtins/verify-outputs.js.map +1 -0
- package/dist/tools/builtins/write-file.d.ts +3 -2
- package/dist/tools/builtins/write-file.d.ts.map +1 -1
- package/dist/tools/builtins/write-file.js +72 -12
- package/dist/tools/builtins/write-file.js.map +1 -1
- package/dist/tools/coordinator/__tests__/agent.test.d.ts +15 -0
- package/dist/tools/coordinator/__tests__/agent.test.d.ts.map +1 -0
- package/dist/tools/coordinator/__tests__/agent.test.js +142 -0
- package/dist/tools/coordinator/__tests__/agent.test.js.map +1 -0
- package/dist/tools/coordinator/__tests__/task-list.test.d.ts +13 -0
- package/dist/tools/coordinator/__tests__/task-list.test.d.ts.map +1 -0
- package/dist/tools/coordinator/__tests__/task-list.test.js +162 -0
- package/dist/tools/coordinator/__tests__/task-list.test.js.map +1 -0
- package/dist/tools/coordinator/agent.d.ts +34 -0
- package/dist/tools/coordinator/agent.d.ts.map +1 -0
- package/dist/tools/coordinator/agent.js +107 -0
- package/dist/tools/coordinator/agent.js.map +1 -0
- package/dist/tools/coordinator/index.d.ts +7 -0
- package/dist/tools/coordinator/index.d.ts.map +1 -1
- package/dist/tools/coordinator/index.js +111 -21
- package/dist/tools/coordinator/index.js.map +1 -1
- package/dist/types/agent/base.d.ts +8 -0
- package/dist/types/agent/base.d.ts.map +1 -1
- package/dist/types/agent/reactive.d.ts +23 -0
- package/dist/types/agent/reactive.d.ts.map +1 -1
- package/dist/types/agent/supervisor.d.ts +14 -0
- package/dist/types/agent/supervisor.d.ts.map +1 -1
- package/dist/types/message/index.d.ts +22 -1
- package/dist/types/message/index.d.ts.map +1 -1
- package/dist/types/message/index.js +7 -2
- package/dist/types/message/index.js.map +1 -1
- package/dist/types/provider/chat.d.ts +2 -9
- package/dist/types/provider/chat.d.ts.map +1 -1
- package/dist/types/run/events.d.ts +6 -0
- package/dist/types/run/events.d.ts.map +1 -1
- package/dist/types/run/events.js.map +1 -1
- package/dist/types/sandbox/index.d.ts +193 -0
- package/dist/types/sandbox/index.d.ts.map +1 -1
- package/dist/types/sandbox/index.js.map +1 -1
- package/dist/types/skills/index.d.ts +2 -0
- package/dist/types/skills/index.d.ts.map +1 -1
- package/dist/types/tool/index.d.ts +22 -0
- package/dist/types/tool/index.d.ts.map +1 -1
- package/dist/types/toolset/index.d.ts +71 -0
- package/dist/types/toolset/index.d.ts.map +1 -0
- package/dist/types/toolset/index.js +2 -0
- package/dist/types/toolset/index.js.map +1 -0
- package/dist/types/workspace/index.d.ts +1 -0
- package/dist/types/workspace/index.d.ts.map +1 -1
- package/dist/types/workspace/shared-run.d.ts +61 -0
- package/dist/types/workspace/shared-run.d.ts.map +1 -0
- package/dist/types/workspace/shared-run.js +2 -0
- package/dist/types/workspace/shared-run.js.map +1 -0
- package/dist/verification/index.d.ts +1 -0
- package/dist/verification/index.d.ts.map +1 -1
- package/dist/verification/index.js +1 -0
- package/dist/verification/index.js.map +1 -1
- package/dist/verification/presets.d.ts +53 -0
- package/dist/verification/presets.d.ts.map +1 -0
- package/dist/verification/presets.js +70 -0
- package/dist/verification/presets.js.map +1 -0
- package/dist/verification/presets.test.d.ts +16 -0
- package/dist/verification/presets.test.d.ts.map +1 -0
- package/dist/verification/presets.test.js +79 -0
- package/dist/verification/presets.test.js.map +1 -0
- package/package.json +3 -2
- package/src/advisory/executor.test.ts +7 -4
- package/src/advisory/executor.ts +11 -2
- package/src/agents/ReactiveAgent.ts +2 -0
- package/src/agents/SupervisorAgent.ts +7 -0
- package/src/bridge/sse/mapper.test.ts +2 -2
- package/src/constants/compaction/index.ts +8 -3
- package/src/constants/sandbox/index.ts +37 -0
- package/src/constants/tools/index.ts +33 -2
- package/src/manager/run/persistence.ts +34 -6
- package/src/persona/assembler.ts +31 -8
- package/src/provider/collect.test.ts +2 -2
- package/src/public-runtime.ts +14 -1
- package/src/public-tools.ts +2 -0
- package/src/public-types.ts +7 -0
- package/src/registry/index.ts +7 -0
- package/src/registry/tool/execute.test.ts +132 -3
- package/src/registry/tool/execute.ts +94 -9
- package/src/registry/toolset/catalog.test.ts +97 -0
- package/src/registry/toolset/catalog.ts +283 -0
- package/src/runtime/query/__tests__/deferred-tools.test.ts +183 -0
- package/src/runtime/query/__tests__/executor-concurrency.test.ts +122 -0
- package/src/runtime/query/__tests__/executor-plugin-hooks.test.ts +48 -3
- package/src/runtime/query/__tests__/prompt.test.ts +51 -2
- package/src/runtime/query/__tests__/stream-recovery.test.ts +156 -0
- package/src/runtime/query/continuation.ts +16 -0
- package/src/runtime/query/executor.ts +82 -13
- package/src/runtime/query/index.ts +24 -3
- package/src/runtime/query/iteration/index.ts +263 -68
- package/src/runtime/query/iteration/phases/context.ts +10 -0
- package/src/runtime/query/prompt.ts +17 -1
- package/src/runtime/query/tooling.ts +2 -0
- package/src/sandbox/provider/local.ts +33 -0
- package/src/session/workspace/__tests__/shared-run.test.ts +181 -0
- package/src/session/workspace/index.ts +6 -0
- package/src/session/workspace/shared-run.ts +316 -0
- package/src/skills/loader.test.ts +89 -0
- package/src/skills/loader.ts +37 -6
- package/src/streaming/coalesce.test.ts +1 -1
- package/src/tools/builtins/__tests__/edit.test.ts +57 -0
- package/src/tools/builtins/__tests__/payload-budget.test.ts +29 -0
- package/src/tools/builtins/__tests__/read-file.test.ts +31 -0
- package/src/tools/builtins/__tests__/verify-outputs.test.ts +71 -0
- package/src/tools/builtins/__tests__/write-file.test.ts +97 -0
- package/src/tools/builtins/bash.ts +48 -7
- package/src/tools/builtins/edit.ts +162 -27
- package/src/tools/builtins/index.ts +13 -13
- package/src/tools/builtins/read-file.ts +31 -8
- package/src/tools/builtins/search-tools.ts +5 -1
- package/src/tools/builtins/verify-outputs.ts +126 -0
- package/src/tools/builtins/write-file.ts +83 -14
- package/src/tools/coordinator/__tests__/agent.test.ts +172 -0
- package/src/tools/coordinator/__tests__/task-list.test.ts +182 -0
- package/src/tools/coordinator/agent.ts +157 -0
- package/src/tools/coordinator/index.ts +128 -22
- package/src/types/agent/base.ts +8 -0
- package/src/types/agent/reactive.ts +25 -0
- package/src/types/agent/supervisor.ts +16 -0
- package/src/types/message/index.ts +32 -2
- package/src/types/provider/chat.ts +2 -9
- package/src/types/run/events.ts +6 -0
- package/src/types/sandbox/index.ts +219 -0
- package/src/types/skills/index.ts +4 -0
- package/src/types/tool/index.ts +24 -0
- package/src/types/toolset/index.ts +86 -0
- package/src/types/workspace/index.ts +9 -0
- package/src/types/workspace/shared-run.ts +65 -0
- package/src/verification/index.ts +1 -0
- package/src/verification/presets.test.ts +112 -0
- package/src/verification/presets.ts +72 -0
|
@@ -6,6 +6,7 @@ export { GlobTool } from './glob.js'
|
|
|
6
6
|
export { GrepTool } from './grep.js'
|
|
7
7
|
export { LsTool } from './ls.js'
|
|
8
8
|
export { SearchToolsTool } from './search-tools.js'
|
|
9
|
+
export { VerifyOutputsTool } from './verify-outputs.js'
|
|
9
10
|
export { createStructuredOutputTool, STRUCTURED_OUTPUT_TOOL_NAME } from './structuredOutput.js'
|
|
10
11
|
export { createComputerUseTool, COMPUTER_USE_TOOL_NAME } from './computer-use.js'
|
|
11
12
|
|
|
@@ -14,22 +15,21 @@ import { BashTool } from './bash.js'
|
|
|
14
15
|
import { EditTool } from './edit.js'
|
|
15
16
|
import { GlobTool } from './glob.js'
|
|
16
17
|
import { GrepTool } from './grep.js'
|
|
17
|
-
import { LsTool } from './ls.js'
|
|
18
18
|
import { ReadFileTool } from './read-file.js'
|
|
19
|
-
import {
|
|
19
|
+
import { VerifyOutputsTool } from './verify-outputs.js'
|
|
20
20
|
import { WriteFileTool } from './write-file.js'
|
|
21
21
|
// Note: createStructuredOutputTool is not included in getBuiltinTools()
|
|
22
|
-
// because it requires a schema parameter and is created per-use case
|
|
22
|
+
// because it requires a schema parameter and is created per-use case.
|
|
23
|
+
//
|
|
24
|
+
// `LsTool` and `SearchToolsTool` are still exported for direct use but are
|
|
25
|
+
// NOT in the default builtin set. Claude Code's training distribution (per
|
|
26
|
+
// `code.claude.com/docs/en/tools-reference`) does NOT include `LS` —
|
|
27
|
+
// directory listing is canonical `Bash` + `Glob`. `search_tools` has no
|
|
28
|
+
// Claude analogue at all. Including these in the defaults gives the model
|
|
29
|
+
// tools that look right but degrade alignment. File extension is canonical
|
|
30
|
+
// `edit` with `insertLine: "end"` — the legacy `Append` tool is gone.
|
|
31
|
+
// Hosts that genuinely want LS/search can still register them explicitly.
|
|
23
32
|
|
|
24
33
|
export function getBuiltinTools(): ToolDefinition[] {
|
|
25
|
-
return [
|
|
26
|
-
ReadFileTool,
|
|
27
|
-
WriteFileTool,
|
|
28
|
-
EditTool,
|
|
29
|
-
BashTool,
|
|
30
|
-
GlobTool,
|
|
31
|
-
GrepTool,
|
|
32
|
-
LsTool,
|
|
33
|
-
SearchToolsTool,
|
|
34
|
-
]
|
|
34
|
+
return [BashTool, EditTool, GlobTool, GrepTool, ReadFileTool, VerifyOutputsTool, WriteFileTool]
|
|
35
35
|
}
|
|
@@ -5,6 +5,12 @@ import { defineTool } from '../defineTool.js'
|
|
|
5
5
|
|
|
6
6
|
const inputSchema = z.object({
|
|
7
7
|
path: z.string().describe('Path to the file to read (absolute or relative)'),
|
|
8
|
+
readRange: z
|
|
9
|
+
.tuple([z.coerce.number().int().min(1), z.coerce.number().int().min(1)])
|
|
10
|
+
.optional()
|
|
11
|
+
.describe(
|
|
12
|
+
'Optional 1-indexed inclusive line range, e.g. [10, 40]. When provided it takes precedence over offset/limit.',
|
|
13
|
+
),
|
|
8
14
|
offset: z.coerce
|
|
9
15
|
.number()
|
|
10
16
|
.int()
|
|
@@ -15,9 +21,9 @@ const inputSchema = z.object({
|
|
|
15
21
|
})
|
|
16
22
|
|
|
17
23
|
export const ReadFileTool = defineTool({
|
|
18
|
-
name: '
|
|
24
|
+
name: 'read',
|
|
19
25
|
description:
|
|
20
|
-
'Reads a file and returns its contents with line numbers. Supports
|
|
26
|
+
'Reads a file and returns its contents with line numbers. Supports readRange ([start,end], 1-indexed inclusive) or offset/limit for large files.',
|
|
21
27
|
inputSchema,
|
|
22
28
|
category: 'filesystem',
|
|
23
29
|
permissions: ['file_read'],
|
|
@@ -32,11 +38,12 @@ export const ReadFileTool = defineTool({
|
|
|
32
38
|
const content = buffer.toString('utf-8')
|
|
33
39
|
const lines = content.split('\n')
|
|
34
40
|
|
|
35
|
-
const start =
|
|
36
|
-
const end = input.limit ? start + input.limit : lines.length
|
|
41
|
+
const { start, end } = resolveReadWindow(input, lines.length)
|
|
37
42
|
const selectedLines = lines.slice(start, end)
|
|
38
43
|
|
|
39
|
-
const numberedLines = selectedLines.map((line, i) => `${start + i}\t${line}`).join('\n')
|
|
44
|
+
const numberedLines = selectedLines.map((line, i) => `${start + i + 1}\t${line}`).join('\n')
|
|
45
|
+
|
|
46
|
+
context.fileReadTracker?.recordRead(input.path)
|
|
40
47
|
|
|
41
48
|
return {
|
|
42
49
|
success: true,
|
|
@@ -54,11 +61,12 @@ export const ReadFileTool = defineTool({
|
|
|
54
61
|
const content = await readFile(filePath, 'utf-8')
|
|
55
62
|
const lines = content.split('\n')
|
|
56
63
|
|
|
57
|
-
const start =
|
|
58
|
-
const end = input.limit ? start + input.limit : lines.length
|
|
64
|
+
const { start, end } = resolveReadWindow(input, lines.length)
|
|
59
65
|
const selectedLines = lines.slice(start, end)
|
|
60
66
|
|
|
61
|
-
const numberedLines = selectedLines.map((line, i) => `${start + i}\t${line}`).join('\n')
|
|
67
|
+
const numberedLines = selectedLines.map((line, i) => `${start + i + 1}\t${line}`).join('\n')
|
|
68
|
+
|
|
69
|
+
context.fileReadTracker?.recordRead(filePath)
|
|
62
70
|
|
|
63
71
|
return {
|
|
64
72
|
success: true,
|
|
@@ -71,3 +79,18 @@ export const ReadFileTool = defineTool({
|
|
|
71
79
|
}
|
|
72
80
|
},
|
|
73
81
|
})
|
|
82
|
+
|
|
83
|
+
function resolveReadWindow(
|
|
84
|
+
input: z.infer<typeof inputSchema>,
|
|
85
|
+
totalLines: number,
|
|
86
|
+
): { start: number; end: number } {
|
|
87
|
+
if (input.readRange) {
|
|
88
|
+
const [first, last] = input.readRange
|
|
89
|
+
const start = Math.max(0, first - 1)
|
|
90
|
+
const end = Math.min(totalLines, Math.max(start, last))
|
|
91
|
+
return { start, end }
|
|
92
|
+
}
|
|
93
|
+
const start = Math.max(0, input.offset ?? 0)
|
|
94
|
+
const end = input.limit ? start + input.limit : totalLines
|
|
95
|
+
return { start, end }
|
|
96
|
+
}
|
|
@@ -26,7 +26,11 @@ export const SearchToolsTool = defineTool({
|
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
const
|
|
29
|
+
const allowed =
|
|
30
|
+
context.allowedTools && context.allowedTools.length > 0 ? new Set(context.allowedTools) : null
|
|
31
|
+
const matches = context.toolRegistry
|
|
32
|
+
.searchDeferred(input.query)
|
|
33
|
+
.filter((tool) => !allowed || allowed.has(tool.name))
|
|
30
34
|
|
|
31
35
|
if (matches.length === 0) {
|
|
32
36
|
return {
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { stat } from 'node:fs/promises'
|
|
2
|
+
import { resolve } from 'node:path'
|
|
3
|
+
import { z } from 'zod'
|
|
4
|
+
import type { ToolContext } from '../../types/tool/index.js'
|
|
5
|
+
import { defineTool } from '../defineTool.js'
|
|
6
|
+
|
|
7
|
+
const inputSchema = z.object({
|
|
8
|
+
paths: z
|
|
9
|
+
.array(z.string().min(1))
|
|
10
|
+
.min(1)
|
|
11
|
+
.describe('Expected output file paths to verify. Each path is checked for existence and size.'),
|
|
12
|
+
min_bytes: z
|
|
13
|
+
.number()
|
|
14
|
+
.int()
|
|
15
|
+
.min(0)
|
|
16
|
+
.optional()
|
|
17
|
+
.describe(
|
|
18
|
+
'Minimum acceptable file size in bytes. A file under this size counts as missing. Default: 1 (any non-empty file passes).',
|
|
19
|
+
),
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
type Result = {
|
|
23
|
+
path: string
|
|
24
|
+
exists: boolean
|
|
25
|
+
size_bytes?: number
|
|
26
|
+
ok: boolean
|
|
27
|
+
error?: string
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export const VerifyOutputsTool = defineTool({
|
|
31
|
+
name: 'verify_outputs',
|
|
32
|
+
description:
|
|
33
|
+
"Verify that a set of expected output files actually exist on disk and are non-empty. Use this BEFORE declaring multi-worker work done — pass every expected output path the workers were supposed to produce. Returns a per-path report (exists, size_bytes, ok) plus an overall pass/fail summary. If any path fails, spawn a fresh `create_task` on the responsible specialist with a brief that names the missing path and the prior worker's output location — do NOT paper over a missing file in prose.",
|
|
34
|
+
inputSchema,
|
|
35
|
+
category: 'filesystem',
|
|
36
|
+
permissions: ['file_read'],
|
|
37
|
+
readOnly: true,
|
|
38
|
+
destructive: false,
|
|
39
|
+
concurrencySafe: true,
|
|
40
|
+
|
|
41
|
+
async execute(input, context) {
|
|
42
|
+
const minBytes = input.min_bytes ?? 1
|
|
43
|
+
const results: Result[] = await Promise.all(
|
|
44
|
+
input.paths.map((path) => verifyOne({ path, minBytes, context })),
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
const passed = results.filter((r) => r.ok).length
|
|
48
|
+
const failed = results.filter((r) => !r.ok)
|
|
49
|
+
const summary = {
|
|
50
|
+
total: results.length,
|
|
51
|
+
passed,
|
|
52
|
+
failed: failed.length,
|
|
53
|
+
min_bytes: minBytes,
|
|
54
|
+
}
|
|
55
|
+
const lines = results.map((r) =>
|
|
56
|
+
r.ok
|
|
57
|
+
? `- OK ${r.path}${typeof r.size_bytes === 'number' ? ` (${r.size_bytes}B)` : ''}`
|
|
58
|
+
: `- FAIL ${r.path} — ${r.error ?? (r.exists ? `size ${r.size_bytes ?? 0}B < min ${minBytes}B` : 'missing')}`,
|
|
59
|
+
)
|
|
60
|
+
const header = `Verify outputs: ${passed}/${results.length} passed (min ${minBytes}B)`
|
|
61
|
+
return {
|
|
62
|
+
success: failed.length === 0,
|
|
63
|
+
output: [header, '', ...lines].join('\n'),
|
|
64
|
+
error:
|
|
65
|
+
failed.length > 0
|
|
66
|
+
? `${failed.length} of ${results.length} expected outputs failed verification`
|
|
67
|
+
: undefined,
|
|
68
|
+
data: { results, summary },
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
async function verifyOne(input: {
|
|
74
|
+
path: string
|
|
75
|
+
minBytes: number
|
|
76
|
+
context: ToolContext
|
|
77
|
+
}): Promise<Result> {
|
|
78
|
+
const { path, minBytes, context } = input
|
|
79
|
+
if (context.sandbox) {
|
|
80
|
+
try {
|
|
81
|
+
const buffer = await context.sandbox.readFile(path)
|
|
82
|
+
const size = buffer.byteLength
|
|
83
|
+
return {
|
|
84
|
+
path,
|
|
85
|
+
exists: true,
|
|
86
|
+
size_bytes: size,
|
|
87
|
+
ok: size >= minBytes,
|
|
88
|
+
}
|
|
89
|
+
} catch (err) {
|
|
90
|
+
return {
|
|
91
|
+
path,
|
|
92
|
+
exists: false,
|
|
93
|
+
ok: false,
|
|
94
|
+
error: err instanceof Error ? err.message : String(err),
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const filePath = resolve(context.workingDirectory, path)
|
|
100
|
+
try {
|
|
101
|
+
const info = await stat(filePath)
|
|
102
|
+
if (!info.isFile()) {
|
|
103
|
+
return {
|
|
104
|
+
path,
|
|
105
|
+
exists: true,
|
|
106
|
+
ok: false,
|
|
107
|
+
error: 'not a regular file',
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return {
|
|
111
|
+
path,
|
|
112
|
+
exists: true,
|
|
113
|
+
size_bytes: info.size,
|
|
114
|
+
ok: info.size >= minBytes,
|
|
115
|
+
}
|
|
116
|
+
} catch (err) {
|
|
117
|
+
const code = (err as NodeJS.ErrnoException)?.code
|
|
118
|
+
if (code === 'ENOENT') return { path, exists: false, ok: false }
|
|
119
|
+
return {
|
|
120
|
+
path,
|
|
121
|
+
exists: false,
|
|
122
|
+
ok: false,
|
|
123
|
+
error: err instanceof Error ? err.message : String(err),
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
@@ -1,17 +1,40 @@
|
|
|
1
|
-
import { mkdir, writeFile } from 'node:fs/promises'
|
|
1
|
+
import { access, mkdir, writeFile } from 'node:fs/promises'
|
|
2
2
|
import { dirname, resolve } from 'node:path'
|
|
3
3
|
import { z } from 'zod'
|
|
4
|
+
import type { ToolContext } from '../../types/tool/index.js'
|
|
4
5
|
import { defineTool } from '../defineTool.js'
|
|
5
6
|
|
|
6
|
-
const inputSchema = z
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
const inputSchema = z
|
|
8
|
+
.object({
|
|
9
|
+
path: z
|
|
10
|
+
.string()
|
|
11
|
+
.min(1)
|
|
12
|
+
.describe(
|
|
13
|
+
'Relative path to the file to write (e.g. "outputs/report.md"). Required. Must be a non-empty string.',
|
|
14
|
+
),
|
|
15
|
+
content: z
|
|
16
|
+
.string()
|
|
17
|
+
.optional()
|
|
18
|
+
.describe(
|
|
19
|
+
'Full file body to write. Required (use "" only for an intentionally empty file). The file is fully overwritten — pass the COMPLETE intended content for this bounded chunk, not a diff. Self-budget content under 12000 characters before calling; if the intended body is longer, write a smaller opening section here, then use `edit` with insertLine: "end" to extend the file section by section. Do NOT try to chain multiple `write` calls, since each one overwrites the previous.',
|
|
20
|
+
),
|
|
21
|
+
newStr: z
|
|
22
|
+
.string()
|
|
23
|
+
.optional()
|
|
24
|
+
.describe(
|
|
25
|
+
'Alias for content. Useful for hosts that expose create/write operations as newStr. Self-budget this payload under 12000 characters before calling.',
|
|
26
|
+
),
|
|
27
|
+
})
|
|
28
|
+
.refine((value) => typeof value.content === 'string' || typeof value.newStr === 'string', {
|
|
29
|
+
message: 'Either content or newStr is required.',
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
type WriteInput = z.infer<typeof inputSchema>
|
|
10
33
|
|
|
11
34
|
export const WriteFileTool = defineTool({
|
|
12
|
-
name: '
|
|
35
|
+
name: 'write',
|
|
13
36
|
description:
|
|
14
|
-
'Writes
|
|
37
|
+
'Writes a file to the local filesystem. Overwrites the existing file at the path if there is one.\n\n- If the file already exists, you must use the `read` tool on it first in this conversation, or this call will fail.\n- Prefer the `edit` tool for modifying existing files — it only sends the diff and preserves the rest of the file byte-for-byte.\n- Use `write` to create a new file or to perform a deliberate full rewrite of a file you have already read.\n- Self-budget content/newStr under 12000 characters before emitting the tool call. For long content, write a smaller opening section, then use `edit` with insertLine: "end" to extend the file section by section. Do not chain multiple `write` calls — each one overwrites the previous.',
|
|
15
38
|
inputSchema,
|
|
16
39
|
category: 'filesystem',
|
|
17
40
|
permissions: ['file_write'],
|
|
@@ -19,26 +42,72 @@ export const WriteFileTool = defineTool({
|
|
|
19
42
|
destructive: true,
|
|
20
43
|
concurrencySafe: false,
|
|
21
44
|
|
|
22
|
-
async execute(input, context) {
|
|
45
|
+
async execute(input: WriteInput, context) {
|
|
46
|
+
const content = input.content ?? input.newStr ?? ''
|
|
23
47
|
// Sandbox-aware: route through sandbox.writeFile() when available
|
|
24
48
|
if (context.sandbox) {
|
|
25
|
-
await context
|
|
49
|
+
const sandboxExists = await sandboxFileExists(context, input.path)
|
|
50
|
+
if (sandboxExists) {
|
|
51
|
+
const guard = enforceReadBeforeOverwrite(context, input.path)
|
|
52
|
+
if (guard) return guard
|
|
53
|
+
}
|
|
54
|
+
await context.sandbox.writeFile(input.path, content)
|
|
55
|
+
context.fileReadTracker?.recordRead(input.path)
|
|
26
56
|
return {
|
|
27
57
|
success: true,
|
|
28
|
-
output: `File written successfully: ${input.path} (${
|
|
29
|
-
data: { path: input.path, size:
|
|
58
|
+
output: `File written successfully: ${input.path} (${content.length} chars) [sandboxed]`,
|
|
59
|
+
data: { path: input.path, size: content.length, sandboxed: true },
|
|
30
60
|
}
|
|
31
61
|
}
|
|
32
62
|
|
|
33
63
|
const filePath = resolve(context.workingDirectory, input.path)
|
|
34
64
|
|
|
65
|
+
const localExists = await pathExists(filePath)
|
|
66
|
+
if (localExists) {
|
|
67
|
+
const guard = enforceReadBeforeOverwrite(context, filePath)
|
|
68
|
+
if (guard) return guard
|
|
69
|
+
}
|
|
70
|
+
|
|
35
71
|
await mkdir(dirname(filePath), { recursive: true })
|
|
36
|
-
await writeFile(filePath,
|
|
72
|
+
await writeFile(filePath, content, 'utf-8')
|
|
73
|
+
context.fileReadTracker?.recordRead(filePath)
|
|
37
74
|
|
|
38
75
|
return {
|
|
39
76
|
success: true,
|
|
40
|
-
output: `File written successfully: ${filePath} (${
|
|
41
|
-
data: { path: filePath, size:
|
|
77
|
+
output: `File written successfully: ${filePath} (${content.length} chars)`,
|
|
78
|
+
data: { path: filePath, size: content.length },
|
|
42
79
|
}
|
|
43
80
|
},
|
|
44
81
|
})
|
|
82
|
+
|
|
83
|
+
function enforceReadBeforeOverwrite(
|
|
84
|
+
context: ToolContext,
|
|
85
|
+
key: string,
|
|
86
|
+
): { success: false; output: ''; error: string } | null {
|
|
87
|
+
if (!context.fileReadTracker) return null
|
|
88
|
+
if (context.fileReadTracker.hasRead(key)) return null
|
|
89
|
+
return {
|
|
90
|
+
success: false,
|
|
91
|
+
output: '',
|
|
92
|
+
error: `${key} already exists. Use the \`read\` tool on it first in this conversation, then call \`write\` again — or prefer \`edit\` for a targeted change.`,
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async function pathExists(filePath: string): Promise<boolean> {
|
|
97
|
+
try {
|
|
98
|
+
await access(filePath)
|
|
99
|
+
return true
|
|
100
|
+
} catch {
|
|
101
|
+
return false
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async function sandboxFileExists(context: ToolContext, path: string): Promise<boolean> {
|
|
106
|
+
if (!context.sandbox) return false
|
|
107
|
+
try {
|
|
108
|
+
await context.sandbox.readFile(path)
|
|
109
|
+
return true
|
|
110
|
+
} catch {
|
|
111
|
+
return false
|
|
112
|
+
}
|
|
113
|
+
}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Behavioural contract for `buildAgentTool`:
|
|
3
|
+
*
|
|
4
|
+
* - Reports `success: true` only when BOTH the gateway task state and
|
|
5
|
+
* the underlying `BaseAgentResult.status` say completed.
|
|
6
|
+
* - Reports `success: false` and surfaces `lastError` when either
|
|
7
|
+
* layer disagrees — the canonical bug Codex caught was that a
|
|
8
|
+
* failed subagent could be reported as successful when the gateway
|
|
9
|
+
* forwarded `state: 'completed'` from a manager that did not
|
|
10
|
+
* propagate the run's `status: 'failed'`.
|
|
11
|
+
* - Returns the subagent's `result` string as the tool output on
|
|
12
|
+
* success.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { describe, expect, it } from 'vitest'
|
|
16
|
+
|
|
17
|
+
import type { TaskGateway, TaskHandle } from '../../../types/agent/gateway.js'
|
|
18
|
+
import type { TaskId } from '../../../types/ids/index.js'
|
|
19
|
+
import type { ToolContext } from '../../../types/tool/index.js'
|
|
20
|
+
import { buildAgentTool } from '../agent.js'
|
|
21
|
+
|
|
22
|
+
function makeContext(): ToolContext {
|
|
23
|
+
return {
|
|
24
|
+
runId: 'run_test' as never,
|
|
25
|
+
workingDirectory: '/tmp/test',
|
|
26
|
+
abortSignal: new AbortController().signal,
|
|
27
|
+
env: {},
|
|
28
|
+
log: () => {},
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function fakeGateway(handle: TaskHandle, completed: TaskHandle): TaskGateway {
|
|
33
|
+
return {
|
|
34
|
+
async createTask() {
|
|
35
|
+
return handle
|
|
36
|
+
},
|
|
37
|
+
async waitForTask() {
|
|
38
|
+
return completed
|
|
39
|
+
},
|
|
40
|
+
async continueTask() {},
|
|
41
|
+
cancelTask() {},
|
|
42
|
+
getTask() {
|
|
43
|
+
return completed
|
|
44
|
+
},
|
|
45
|
+
listTasks() {
|
|
46
|
+
return [completed]
|
|
47
|
+
},
|
|
48
|
+
onTaskCompleted() {
|
|
49
|
+
return () => {}
|
|
50
|
+
},
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const taskId = 'task_subagent' as TaskId
|
|
55
|
+
|
|
56
|
+
const launched: TaskHandle = {
|
|
57
|
+
taskId,
|
|
58
|
+
agentId: 'sales-strategy',
|
|
59
|
+
state: 'running',
|
|
60
|
+
createdAt: 0,
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
describe('buildAgentTool', () => {
|
|
64
|
+
it('reports success when both task state and run status say completed', async () => {
|
|
65
|
+
const gateway = fakeGateway(launched, {
|
|
66
|
+
...launched,
|
|
67
|
+
state: 'completed',
|
|
68
|
+
result: {
|
|
69
|
+
runId: 'run_inner' as never,
|
|
70
|
+
status: 'completed',
|
|
71
|
+
usage: { inputTokens: 0, outputTokens: 0, totalTokens: 0 } as never,
|
|
72
|
+
cost: { inputCostUsd: 0, outputCostUsd: 0, totalCostUsd: 0 } as never,
|
|
73
|
+
iterations: 1,
|
|
74
|
+
durationMs: 10,
|
|
75
|
+
messages: [],
|
|
76
|
+
result: 'final report text',
|
|
77
|
+
},
|
|
78
|
+
completedAt: 10,
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
const tool = buildAgentTool({
|
|
82
|
+
gateway,
|
|
83
|
+
workingDirectory: '/tmp/test',
|
|
84
|
+
allowedAgentIds: ['sales-strategy'],
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
const result = await tool.execute(
|
|
88
|
+
{ description: 'plan', prompt: 'go', subagent_type: 'sales-strategy' },
|
|
89
|
+
makeContext(),
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
expect(result.success).toBe(true)
|
|
93
|
+
expect(result.output).toBe('final report text')
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
it('reports failure when run status is failed even though task state is completed', async () => {
|
|
97
|
+
const gateway = fakeGateway(launched, {
|
|
98
|
+
...launched,
|
|
99
|
+
state: 'completed',
|
|
100
|
+
result: {
|
|
101
|
+
runId: 'run_inner' as never,
|
|
102
|
+
status: 'failed',
|
|
103
|
+
usage: { inputTokens: 0, outputTokens: 0, totalTokens: 0 } as never,
|
|
104
|
+
cost: { inputCostUsd: 0, outputCostUsd: 0, totalCostUsd: 0 } as never,
|
|
105
|
+
iterations: 1,
|
|
106
|
+
durationMs: 10,
|
|
107
|
+
messages: [],
|
|
108
|
+
lastError: 'tool budget exceeded',
|
|
109
|
+
},
|
|
110
|
+
completedAt: 10,
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
const tool = buildAgentTool({
|
|
114
|
+
gateway,
|
|
115
|
+
workingDirectory: '/tmp/test',
|
|
116
|
+
allowedAgentIds: ['sales-strategy'],
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
const result = await tool.execute(
|
|
120
|
+
{ description: 'plan', prompt: 'go', subagent_type: 'sales-strategy' },
|
|
121
|
+
makeContext(),
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
expect(result.success).toBe(false)
|
|
125
|
+
expect(result.error).toContain('tool budget exceeded')
|
|
126
|
+
expect(result.error).toContain('failed')
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
it('reports failure when task state itself is failed', async () => {
|
|
130
|
+
const gateway = fakeGateway(launched, {
|
|
131
|
+
...launched,
|
|
132
|
+
state: 'failed',
|
|
133
|
+
result: undefined,
|
|
134
|
+
completedAt: 10,
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
const tool = buildAgentTool({
|
|
138
|
+
gateway,
|
|
139
|
+
workingDirectory: '/tmp/test',
|
|
140
|
+
allowedAgentIds: ['sales-strategy'],
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
const result = await tool.execute(
|
|
144
|
+
{ description: 'plan', prompt: 'go', subagent_type: 'sales-strategy' },
|
|
145
|
+
makeContext(),
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
expect(result.success).toBe(false)
|
|
149
|
+
expect(result.error).toContain('failed')
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
it("does not accept a taskStore or runId — plan-task lifecycle is the parent's job", () => {
|
|
153
|
+
// Compile-time pin: AgentToolOptions must NOT include `taskStore`
|
|
154
|
+
// or `runId`. The Agent tool used to manage a per-call plan task
|
|
155
|
+
// internally and Codex caught a leak: when the subagent failed,
|
|
156
|
+
// the plan task stayed `'in_progress'` forever because
|
|
157
|
+
// `TaskStatus` has no `'failed'` value to flip to. Drop the
|
|
158
|
+
// integration entirely; if a host wants to track delegations as
|
|
159
|
+
// plan tasks, it does so via `TaskCreate` / `TaskUpdate` on its
|
|
160
|
+
// own side, where it owns the status semantics. This test
|
|
161
|
+
// freezes that decision.
|
|
162
|
+
const allowedOpts: Parameters<typeof buildAgentTool>[0] = {
|
|
163
|
+
gateway: fakeGateway(launched, launched),
|
|
164
|
+
workingDirectory: '/tmp/test',
|
|
165
|
+
allowedAgentIds: ['sales-strategy'],
|
|
166
|
+
runtimeContext: undefined,
|
|
167
|
+
onTaskLaunched: undefined,
|
|
168
|
+
}
|
|
169
|
+
expect('taskStore' in allowedOpts).toBe(false)
|
|
170
|
+
expect('runId' in allowedOpts).toBe(false)
|
|
171
|
+
})
|
|
172
|
+
})
|