@theokit/sdk-tools 0.26.2 → 0.27.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 +38 -0
- package/dist/index.cjs +95 -13
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +69 -15
- package/dist/index.d.ts +69 -15
- package/dist/index.js +95 -15
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.d.cts
CHANGED
|
@@ -130,7 +130,7 @@ declare function createCurrentTimeTool(opts?: CreateCurrentTimeToolOptions): Cus
|
|
|
130
130
|
|
|
131
131
|
interface CreateEditFileToolOptions {
|
|
132
132
|
/**
|
|
133
|
-
* M76 —
|
|
133
|
+
* M76 — the name exposed to the model. Omitted ⇒ today's literal (additive).
|
|
134
134
|
*
|
|
135
135
|
* It exists because, in Codex, the name is BORN in the tool definition and is the approval decision
|
|
136
136
|
* key — three consumers (model, approval, telemetry) of a string decided in one place. Renaming
|
|
@@ -226,15 +226,15 @@ interface CreateGitStatusToolOptions {
|
|
|
226
226
|
maxStdoutBytes?: number;
|
|
227
227
|
/**
|
|
228
228
|
* Injected execution backend (`@theokit/sdk/sandbox`) — when present, `git status` runs via
|
|
229
|
-
* `SandboxBackend.execute`;
|
|
229
|
+
* `SandboxBackend.execute`; omitted ⇒ the local `git` (unchanged).
|
|
230
230
|
*
|
|
231
231
|
* Symmetry with `createGitDiffTool`, flagged by the M76 review: without it `git_diff` would run
|
|
232
232
|
* confined and `git_status` not, in the same session — and the asymmetry would be invisible until someone
|
|
233
|
-
*
|
|
233
|
+
* notice that one of the two escapes the sandbox.
|
|
234
234
|
*/
|
|
235
235
|
sandbox?: SandboxProvider;
|
|
236
236
|
/**
|
|
237
|
-
* M76 —
|
|
237
|
+
* M76 — the name exposed to the model. Omitted ⇒ `"git_status"` (additive).
|
|
238
238
|
*
|
|
239
239
|
* The name is a contract: the approval key, what the model sees and what telemetry records.
|
|
240
240
|
*/
|
|
@@ -490,7 +490,7 @@ declare function buildRepoMap(cwd: string, opts?: RepoMapOptions): string;
|
|
|
490
490
|
* This is a heuristic GUARDRAIL, NOT a sandbox: it is bypassable by deep obfuscation
|
|
491
491
|
* (base64/env-indirection) and is best-effort. POSIX `/bin/sh` only; Windows
|
|
492
492
|
* PowerShell is out of scope. True isolation needs a container.
|
|
493
|
-
*
|
|
493
|
+
* reference: .claude/knowledge-base/references/theocode-shell-guard/server-lib/shell-guard.ts
|
|
494
494
|
*/
|
|
495
495
|
|
|
496
496
|
/** Thrown / reported when a command matches the catastrophic deny-list. */
|
|
@@ -616,7 +616,7 @@ declare function withShellExitGuidance(tool: CustomTool): CustomTool;
|
|
|
616
616
|
|
|
617
617
|
interface CreateListDirToolOptions {
|
|
618
618
|
/**
|
|
619
|
-
* M76 —
|
|
619
|
+
* M76 — the name exposed to the model. Omitted ⇒ today's literal (additive).
|
|
620
620
|
*
|
|
621
621
|
* It exists because, in Codex, the name is BORN in the tool definition and is the approval decision key —
|
|
622
622
|
* three consumers (model, approval, telemetry) of a string decided in one place. Renaming
|
|
@@ -627,8 +627,8 @@ interface CreateListDirToolOptions {
|
|
|
627
627
|
/** M76 — description exposed to the model. Omitted => today's literal (additive). */
|
|
628
628
|
description?: string;
|
|
629
629
|
/**
|
|
630
|
-
* M76 — opt-in "
|
|
631
|
-
* (
|
|
630
|
+
* M76 — opt-in "list anywhere": honours an ABSOLUTE `path` outside `projectRoot`
|
|
631
|
+
* (parity with `createReadFileTool`/`createSearchTextTool`, Codex's read-only sandbox).
|
|
632
632
|
*
|
|
633
633
|
* The ANY-segment secret guard still applies, and is not separable: `isForbiddenPath`
|
|
634
634
|
* only blocks the sensitive item when it is the FIRST segment, so a `/home/u/proj/.env/sub`
|
|
@@ -720,9 +720,9 @@ interface QuestionToolOptions {
|
|
|
720
720
|
/**
|
|
721
721
|
* Callback that presents the question to the user and resolves with the answer.
|
|
722
722
|
*
|
|
723
|
-
* M76 —
|
|
723
|
+
* M76 — became OPTIONAL: the preferred asker comes from the run context
|
|
724
724
|
* (`ctx.context.askUser`), because a value pinned here is the "baked into each factory" that the
|
|
725
|
-
* `CustomTool.handler`
|
|
725
|
+
* `CustomTool.handler` names as the problem `ctx.context` exists to solve. This field
|
|
726
726
|
* remains as a fallback, for callers building the tool with a fixed asker (backward-compatible).
|
|
727
727
|
*/
|
|
728
728
|
askUser?: (question: string, threadId?: string) => Promise<string>;
|
|
@@ -735,11 +735,11 @@ interface QuestionToolOptions {
|
|
|
735
735
|
/** Maximum time to wait for user response in ms. Default: 300_000 (5 min). */
|
|
736
736
|
timeoutMs?: number;
|
|
737
737
|
/**
|
|
738
|
-
* M76 —
|
|
738
|
+
* M76 — the name exposed to the model. Omitted ⇒ `"question"` (additive).
|
|
739
739
|
*
|
|
740
740
|
* The consumer needed this: Codex calls the tool `request_user_input`, and without the option it
|
|
741
741
|
* had to rebuild the whole object by hand — the two-cast adapter T3.3
|
|
742
|
-
*
|
|
742
|
+
* removed.
|
|
743
743
|
*/
|
|
744
744
|
name?: string;
|
|
745
745
|
/** M76 — description exposed to the model. Omitted => today's literal (additive). */
|
|
@@ -945,7 +945,7 @@ declare function createRunVitestTool(opts: CreateRunVitestToolOptions): CustomTo
|
|
|
945
945
|
|
|
946
946
|
interface CreateSearchTextToolOptions {
|
|
947
947
|
/**
|
|
948
|
-
* M76 —
|
|
948
|
+
* M76 — the name exposed to the model. Omitted ⇒ today's literal (additive).
|
|
949
949
|
*
|
|
950
950
|
* It exists because, in Codex, the name is BORN in the tool definition and is the approval decision
|
|
951
951
|
* key — three consumers (model, approval, telemetry) of a string decided in one place. Renaming
|
|
@@ -985,7 +985,7 @@ declare function createSearchTextTool(opts: CreateSearchTextToolOptions): Custom
|
|
|
985
985
|
|
|
986
986
|
interface CreateShellToolOptions {
|
|
987
987
|
/**
|
|
988
|
-
* M76 —
|
|
988
|
+
* M76 — the name exposed to the model. Omitted ⇒ today's literal (additive).
|
|
989
989
|
*
|
|
990
990
|
* It exists because, in Codex, the name is BORN in the tool definition and is the approval decision
|
|
991
991
|
* key — three consumers (model, approval, telemetry) of a string decided in one place. Renaming
|
|
@@ -1173,6 +1173,60 @@ declare function truncateOutput(output: string, opts?: TruncationOptions): Trunc
|
|
|
1173
1173
|
|
|
1174
1174
|
declare function createUpdatePlanTool(): CustomTool;
|
|
1175
1175
|
|
|
1176
|
+
/**
|
|
1177
|
+
* `view_image` — let the agent LOOK at an image in the project.
|
|
1178
|
+
*
|
|
1179
|
+
* ## Why this is a built-in
|
|
1180
|
+
*
|
|
1181
|
+
* It was the one tool a consumer had to write from scratch (89 LOC), and its shape — a `handler`
|
|
1182
|
+
* returning a structured result plus `toModelOutput` shaping it into an `ImageBlock` — is the
|
|
1183
|
+
* canonical multimodal shape the SDK already defines (SE17). Every product that wants an agent to look at a
|
|
1184
|
+
* screenshot rewrites the same base64 + media-type + confinement logic.
|
|
1185
|
+
*
|
|
1186
|
+
* The confinement is the part that is easy to get wrong, and the reason this belongs in a reviewed
|
|
1187
|
+
* built-in rather than in each product: **an image reader that honours any path is a file
|
|
1188
|
+
* exfiltration primitive with a friendly name.** `/etc/passwd` renamed to `.png` is not a
|
|
1189
|
+
* hypothetical — it is one prompt away.
|
|
1190
|
+
*
|
|
1191
|
+
* ## The two channels
|
|
1192
|
+
*
|
|
1193
|
+
* This built-in uses the SE17 split. The handler returns the envelope as a JSON **string** — which
|
|
1194
|
+
* is what `Tool.create` types it to return — and `toModelOutput` turns that string into an
|
|
1195
|
+
* `ImageBlock` for the model, while `defineTool` routes the full value to `onToolEnd` through a
|
|
1196
|
+
* resolver under `TOOL_SPLIT_RESOLVER`.
|
|
1197
|
+
*
|
|
1198
|
+
* So `tool.handler(...)` yields image blocks on success and the JSON string on failure: the factory
|
|
1199
|
+
* has already applied the shaping. There is no `tool.toModelOutput` left to call, and returning the
|
|
1200
|
+
* envelope unshaped would send the model a base64 blob as TEXT — something it cannot look at, which
|
|
1201
|
+
* is the failure this tool exists to avoid.
|
|
1202
|
+
*
|
|
1203
|
+
* ## Result shape (the APP channel)
|
|
1204
|
+
*
|
|
1205
|
+
* - `{ ok: true, path, media_type, bytes, data }`
|
|
1206
|
+
* - `{ ok: false, error: "path_traversal" | "not_found" | "unsupported_image_type" | "image_too_large", … }`
|
|
1207
|
+
*/
|
|
1208
|
+
|
|
1209
|
+
/**
|
|
1210
|
+
* Default ceiling: 5 MB on disk.
|
|
1211
|
+
*
|
|
1212
|
+
* Base64 inflates by 4/3 and the result lands directly in the model's context. A 20 MB screenshot is
|
|
1213
|
+
* not a slow request — it is a failed turn, and an expensive one.
|
|
1214
|
+
*/
|
|
1215
|
+
declare const DEFAULT_MAX_IMAGE_BYTES: number;
|
|
1216
|
+
interface CreateViewImageToolOptions {
|
|
1217
|
+
/** Root the tool reads from. Every path is resolved inside it. */
|
|
1218
|
+
projectRoot: string;
|
|
1219
|
+
/** Name exposed to the model. Omitted ⇒ `view_image`. The name is a contract: it is the approval
|
|
1220
|
+
* key, what the model sees, and what telemetry records. */
|
|
1221
|
+
name?: string;
|
|
1222
|
+
/** Description exposed to the model. Omitted ⇒ the literal below. */
|
|
1223
|
+
description?: string;
|
|
1224
|
+
/** Ceiling in bytes, measured on disk. Omitted ⇒ {@link DEFAULT_MAX_IMAGE_BYTES}. */
|
|
1225
|
+
maxBytes?: number;
|
|
1226
|
+
}
|
|
1227
|
+
/** Read an image from the project so the model can look at it. */
|
|
1228
|
+
declare function createViewImageTool(options: CreateViewImageToolOptions): CustomTool;
|
|
1229
|
+
|
|
1176
1230
|
/**
|
|
1177
1231
|
* `web_fetch` — built-in tool for coding agents.
|
|
1178
1232
|
*
|
|
@@ -1351,4 +1405,4 @@ interface CreateWriteFileToolOptions {
|
|
|
1351
1405
|
}
|
|
1352
1406
|
declare function createWriteFileTool(opts: CreateWriteFileToolOptions): CustomTool;
|
|
1353
1407
|
|
|
1354
|
-
export { CatastrophicCommandError, type CommandPolicy, ContextMatchError, type ContextMatchReason, type CreateApplyPatchToolOptions, type CreateBraveWebSearchAdapterOptions, type CreateCurrentTimeToolOptions, type CreateEditFileToolOptions, type CreateGenericHttpSearchAdapterOptions, type CreateGitDiffToolOptions, type CreateGitStatusToolOptions, type CreateGlobToolOptions, type CreateInteractiveShellToolOptions, type CreateListDirToolOptions, type CreateReadFileToolOptions, type CreateRunVitestToolOptions, type CreateSearchTextToolOptions, type CreateShellToolOptions, type CreateWebFetchToolOptions, type CreateWebSearchToolOptions, type CreateWriteFileToolOptions, DEFAULT_TOOL_GUIDANCE, type EnvContextOptions, type PlanModeTool, type PlanModeToolOptions, type PlanModeToolWithStore, type PlanNode, type QuestionTool, type QuestionToolOptions, ReadTracker, ReasoningTools, RedirectBlockedError, type RepoMapOptions, type ResolveAndScreenOptions, type ScreenedFetchOptions, type SessionArtifactStore, type SessionArtifactStoreOptions, SsrfBlockedError, type TodoItem, type TodolistTool, type ToolGuidanceMap, type TruncationMode, type TruncationOptions, type TruncationResult, type VitestSummary, type WebSearchCallback, type WebSearchResult, buildEnvContext, buildRepoMap, catastrophicShellReason, commandDenialReason, createApplyPatchTool, createBraveWebSearchAdapter, createCurrentTimeTool, createEditFileTool, createGenericHttpSearchAdapter, createGitDiffTool, createGitStatusTool, createGlobTool, createInteractiveShellTool, createListDirTool, createPlanModeTool, createQuestionTool, createReadFileTool, createRunVitestTool, createSearchTextTool, createSessionArtifactStore, createShellTool, createTodolistTool, createUpdatePlanTool, createWebFetchTool, createWebSearchTool, createWriteFileTool, createWriteStdinTool, denyCatastrophicCommands, formatCode, formatDiff, formatError, formatFileList, injectGuidance, isBlockedIp, isCommandAllowed, renderToolList, replaceUnique, resolveAndScreen, screenedFetch, todoItemsToPlanNodes, truncateOutput, withDefaultGuidance, withDescription, withName, withShellExitGuidance, withToolResultGuidance };
|
|
1408
|
+
export { CatastrophicCommandError, type CommandPolicy, ContextMatchError, type ContextMatchReason, type CreateApplyPatchToolOptions, type CreateBraveWebSearchAdapterOptions, type CreateCurrentTimeToolOptions, type CreateEditFileToolOptions, type CreateGenericHttpSearchAdapterOptions, type CreateGitDiffToolOptions, type CreateGitStatusToolOptions, type CreateGlobToolOptions, type CreateInteractiveShellToolOptions, type CreateListDirToolOptions, type CreateReadFileToolOptions, type CreateRunVitestToolOptions, type CreateSearchTextToolOptions, type CreateShellToolOptions, type CreateViewImageToolOptions, type CreateWebFetchToolOptions, type CreateWebSearchToolOptions, type CreateWriteFileToolOptions, DEFAULT_MAX_IMAGE_BYTES, DEFAULT_TOOL_GUIDANCE, type EnvContextOptions, type PlanModeTool, type PlanModeToolOptions, type PlanModeToolWithStore, type PlanNode, type QuestionTool, type QuestionToolOptions, ReadTracker, ReasoningTools, RedirectBlockedError, type RepoMapOptions, type ResolveAndScreenOptions, type ScreenedFetchOptions, type SessionArtifactStore, type SessionArtifactStoreOptions, SsrfBlockedError, type TodoItem, type TodolistTool, type ToolGuidanceMap, type TruncationMode, type TruncationOptions, type TruncationResult, type VitestSummary, type WebSearchCallback, type WebSearchResult, buildEnvContext, buildRepoMap, catastrophicShellReason, commandDenialReason, createApplyPatchTool, createBraveWebSearchAdapter, createCurrentTimeTool, createEditFileTool, createGenericHttpSearchAdapter, createGitDiffTool, createGitStatusTool, createGlobTool, createInteractiveShellTool, createListDirTool, createPlanModeTool, createQuestionTool, createReadFileTool, createRunVitestTool, createSearchTextTool, createSessionArtifactStore, createShellTool, createTodolistTool, createUpdatePlanTool, createViewImageTool, createWebFetchTool, createWebSearchTool, createWriteFileTool, createWriteStdinTool, denyCatastrophicCommands, formatCode, formatDiff, formatError, formatFileList, injectGuidance, isBlockedIp, isCommandAllowed, renderToolList, replaceUnique, resolveAndScreen, screenedFetch, todoItemsToPlanNodes, truncateOutput, withDefaultGuidance, withDescription, withName, withShellExitGuidance, withToolResultGuidance };
|
package/dist/index.d.ts
CHANGED
|
@@ -130,7 +130,7 @@ declare function createCurrentTimeTool(opts?: CreateCurrentTimeToolOptions): Cus
|
|
|
130
130
|
|
|
131
131
|
interface CreateEditFileToolOptions {
|
|
132
132
|
/**
|
|
133
|
-
* M76 —
|
|
133
|
+
* M76 — the name exposed to the model. Omitted ⇒ today's literal (additive).
|
|
134
134
|
*
|
|
135
135
|
* It exists because, in Codex, the name is BORN in the tool definition and is the approval decision
|
|
136
136
|
* key — three consumers (model, approval, telemetry) of a string decided in one place. Renaming
|
|
@@ -226,15 +226,15 @@ interface CreateGitStatusToolOptions {
|
|
|
226
226
|
maxStdoutBytes?: number;
|
|
227
227
|
/**
|
|
228
228
|
* Injected execution backend (`@theokit/sdk/sandbox`) — when present, `git status` runs via
|
|
229
|
-
* `SandboxBackend.execute`;
|
|
229
|
+
* `SandboxBackend.execute`; omitted ⇒ the local `git` (unchanged).
|
|
230
230
|
*
|
|
231
231
|
* Symmetry with `createGitDiffTool`, flagged by the M76 review: without it `git_diff` would run
|
|
232
232
|
* confined and `git_status` not, in the same session — and the asymmetry would be invisible until someone
|
|
233
|
-
*
|
|
233
|
+
* notice that one of the two escapes the sandbox.
|
|
234
234
|
*/
|
|
235
235
|
sandbox?: SandboxProvider;
|
|
236
236
|
/**
|
|
237
|
-
* M76 —
|
|
237
|
+
* M76 — the name exposed to the model. Omitted ⇒ `"git_status"` (additive).
|
|
238
238
|
*
|
|
239
239
|
* The name is a contract: the approval key, what the model sees and what telemetry records.
|
|
240
240
|
*/
|
|
@@ -490,7 +490,7 @@ declare function buildRepoMap(cwd: string, opts?: RepoMapOptions): string;
|
|
|
490
490
|
* This is a heuristic GUARDRAIL, NOT a sandbox: it is bypassable by deep obfuscation
|
|
491
491
|
* (base64/env-indirection) and is best-effort. POSIX `/bin/sh` only; Windows
|
|
492
492
|
* PowerShell is out of scope. True isolation needs a container.
|
|
493
|
-
*
|
|
493
|
+
* reference: .claude/knowledge-base/references/theocode-shell-guard/server-lib/shell-guard.ts
|
|
494
494
|
*/
|
|
495
495
|
|
|
496
496
|
/** Thrown / reported when a command matches the catastrophic deny-list. */
|
|
@@ -616,7 +616,7 @@ declare function withShellExitGuidance(tool: CustomTool): CustomTool;
|
|
|
616
616
|
|
|
617
617
|
interface CreateListDirToolOptions {
|
|
618
618
|
/**
|
|
619
|
-
* M76 —
|
|
619
|
+
* M76 — the name exposed to the model. Omitted ⇒ today's literal (additive).
|
|
620
620
|
*
|
|
621
621
|
* It exists because, in Codex, the name is BORN in the tool definition and is the approval decision key —
|
|
622
622
|
* three consumers (model, approval, telemetry) of a string decided in one place. Renaming
|
|
@@ -627,8 +627,8 @@ interface CreateListDirToolOptions {
|
|
|
627
627
|
/** M76 — description exposed to the model. Omitted => today's literal (additive). */
|
|
628
628
|
description?: string;
|
|
629
629
|
/**
|
|
630
|
-
* M76 — opt-in "
|
|
631
|
-
* (
|
|
630
|
+
* M76 — opt-in "list anywhere": honours an ABSOLUTE `path` outside `projectRoot`
|
|
631
|
+
* (parity with `createReadFileTool`/`createSearchTextTool`, Codex's read-only sandbox).
|
|
632
632
|
*
|
|
633
633
|
* The ANY-segment secret guard still applies, and is not separable: `isForbiddenPath`
|
|
634
634
|
* only blocks the sensitive item when it is the FIRST segment, so a `/home/u/proj/.env/sub`
|
|
@@ -720,9 +720,9 @@ interface QuestionToolOptions {
|
|
|
720
720
|
/**
|
|
721
721
|
* Callback that presents the question to the user and resolves with the answer.
|
|
722
722
|
*
|
|
723
|
-
* M76 —
|
|
723
|
+
* M76 — became OPTIONAL: the preferred asker comes from the run context
|
|
724
724
|
* (`ctx.context.askUser`), because a value pinned here is the "baked into each factory" that the
|
|
725
|
-
* `CustomTool.handler`
|
|
725
|
+
* `CustomTool.handler` names as the problem `ctx.context` exists to solve. This field
|
|
726
726
|
* remains as a fallback, for callers building the tool with a fixed asker (backward-compatible).
|
|
727
727
|
*/
|
|
728
728
|
askUser?: (question: string, threadId?: string) => Promise<string>;
|
|
@@ -735,11 +735,11 @@ interface QuestionToolOptions {
|
|
|
735
735
|
/** Maximum time to wait for user response in ms. Default: 300_000 (5 min). */
|
|
736
736
|
timeoutMs?: number;
|
|
737
737
|
/**
|
|
738
|
-
* M76 —
|
|
738
|
+
* M76 — the name exposed to the model. Omitted ⇒ `"question"` (additive).
|
|
739
739
|
*
|
|
740
740
|
* The consumer needed this: Codex calls the tool `request_user_input`, and without the option it
|
|
741
741
|
* had to rebuild the whole object by hand — the two-cast adapter T3.3
|
|
742
|
-
*
|
|
742
|
+
* removed.
|
|
743
743
|
*/
|
|
744
744
|
name?: string;
|
|
745
745
|
/** M76 — description exposed to the model. Omitted => today's literal (additive). */
|
|
@@ -945,7 +945,7 @@ declare function createRunVitestTool(opts: CreateRunVitestToolOptions): CustomTo
|
|
|
945
945
|
|
|
946
946
|
interface CreateSearchTextToolOptions {
|
|
947
947
|
/**
|
|
948
|
-
* M76 —
|
|
948
|
+
* M76 — the name exposed to the model. Omitted ⇒ today's literal (additive).
|
|
949
949
|
*
|
|
950
950
|
* It exists because, in Codex, the name is BORN in the tool definition and is the approval decision
|
|
951
951
|
* key — three consumers (model, approval, telemetry) of a string decided in one place. Renaming
|
|
@@ -985,7 +985,7 @@ declare function createSearchTextTool(opts: CreateSearchTextToolOptions): Custom
|
|
|
985
985
|
|
|
986
986
|
interface CreateShellToolOptions {
|
|
987
987
|
/**
|
|
988
|
-
* M76 —
|
|
988
|
+
* M76 — the name exposed to the model. Omitted ⇒ today's literal (additive).
|
|
989
989
|
*
|
|
990
990
|
* It exists because, in Codex, the name is BORN in the tool definition and is the approval decision
|
|
991
991
|
* key — three consumers (model, approval, telemetry) of a string decided in one place. Renaming
|
|
@@ -1173,6 +1173,60 @@ declare function truncateOutput(output: string, opts?: TruncationOptions): Trunc
|
|
|
1173
1173
|
|
|
1174
1174
|
declare function createUpdatePlanTool(): CustomTool;
|
|
1175
1175
|
|
|
1176
|
+
/**
|
|
1177
|
+
* `view_image` — let the agent LOOK at an image in the project.
|
|
1178
|
+
*
|
|
1179
|
+
* ## Why this is a built-in
|
|
1180
|
+
*
|
|
1181
|
+
* It was the one tool a consumer had to write from scratch (89 LOC), and its shape — a `handler`
|
|
1182
|
+
* returning a structured result plus `toModelOutput` shaping it into an `ImageBlock` — is the
|
|
1183
|
+
* canonical multimodal shape the SDK already defines (SE17). Every product that wants an agent to look at a
|
|
1184
|
+
* screenshot rewrites the same base64 + media-type + confinement logic.
|
|
1185
|
+
*
|
|
1186
|
+
* The confinement is the part that is easy to get wrong, and the reason this belongs in a reviewed
|
|
1187
|
+
* built-in rather than in each product: **an image reader that honours any path is a file
|
|
1188
|
+
* exfiltration primitive with a friendly name.** `/etc/passwd` renamed to `.png` is not a
|
|
1189
|
+
* hypothetical — it is one prompt away.
|
|
1190
|
+
*
|
|
1191
|
+
* ## The two channels
|
|
1192
|
+
*
|
|
1193
|
+
* This built-in uses the SE17 split. The handler returns the envelope as a JSON **string** — which
|
|
1194
|
+
* is what `Tool.create` types it to return — and `toModelOutput` turns that string into an
|
|
1195
|
+
* `ImageBlock` for the model, while `defineTool` routes the full value to `onToolEnd` through a
|
|
1196
|
+
* resolver under `TOOL_SPLIT_RESOLVER`.
|
|
1197
|
+
*
|
|
1198
|
+
* So `tool.handler(...)` yields image blocks on success and the JSON string on failure: the factory
|
|
1199
|
+
* has already applied the shaping. There is no `tool.toModelOutput` left to call, and returning the
|
|
1200
|
+
* envelope unshaped would send the model a base64 blob as TEXT — something it cannot look at, which
|
|
1201
|
+
* is the failure this tool exists to avoid.
|
|
1202
|
+
*
|
|
1203
|
+
* ## Result shape (the APP channel)
|
|
1204
|
+
*
|
|
1205
|
+
* - `{ ok: true, path, media_type, bytes, data }`
|
|
1206
|
+
* - `{ ok: false, error: "path_traversal" | "not_found" | "unsupported_image_type" | "image_too_large", … }`
|
|
1207
|
+
*/
|
|
1208
|
+
|
|
1209
|
+
/**
|
|
1210
|
+
* Default ceiling: 5 MB on disk.
|
|
1211
|
+
*
|
|
1212
|
+
* Base64 inflates by 4/3 and the result lands directly in the model's context. A 20 MB screenshot is
|
|
1213
|
+
* not a slow request — it is a failed turn, and an expensive one.
|
|
1214
|
+
*/
|
|
1215
|
+
declare const DEFAULT_MAX_IMAGE_BYTES: number;
|
|
1216
|
+
interface CreateViewImageToolOptions {
|
|
1217
|
+
/** Root the tool reads from. Every path is resolved inside it. */
|
|
1218
|
+
projectRoot: string;
|
|
1219
|
+
/** Name exposed to the model. Omitted ⇒ `view_image`. The name is a contract: it is the approval
|
|
1220
|
+
* key, what the model sees, and what telemetry records. */
|
|
1221
|
+
name?: string;
|
|
1222
|
+
/** Description exposed to the model. Omitted ⇒ the literal below. */
|
|
1223
|
+
description?: string;
|
|
1224
|
+
/** Ceiling in bytes, measured on disk. Omitted ⇒ {@link DEFAULT_MAX_IMAGE_BYTES}. */
|
|
1225
|
+
maxBytes?: number;
|
|
1226
|
+
}
|
|
1227
|
+
/** Read an image from the project so the model can look at it. */
|
|
1228
|
+
declare function createViewImageTool(options: CreateViewImageToolOptions): CustomTool;
|
|
1229
|
+
|
|
1176
1230
|
/**
|
|
1177
1231
|
* `web_fetch` — built-in tool for coding agents.
|
|
1178
1232
|
*
|
|
@@ -1351,4 +1405,4 @@ interface CreateWriteFileToolOptions {
|
|
|
1351
1405
|
}
|
|
1352
1406
|
declare function createWriteFileTool(opts: CreateWriteFileToolOptions): CustomTool;
|
|
1353
1407
|
|
|
1354
|
-
export { CatastrophicCommandError, type CommandPolicy, ContextMatchError, type ContextMatchReason, type CreateApplyPatchToolOptions, type CreateBraveWebSearchAdapterOptions, type CreateCurrentTimeToolOptions, type CreateEditFileToolOptions, type CreateGenericHttpSearchAdapterOptions, type CreateGitDiffToolOptions, type CreateGitStatusToolOptions, type CreateGlobToolOptions, type CreateInteractiveShellToolOptions, type CreateListDirToolOptions, type CreateReadFileToolOptions, type CreateRunVitestToolOptions, type CreateSearchTextToolOptions, type CreateShellToolOptions, type CreateWebFetchToolOptions, type CreateWebSearchToolOptions, type CreateWriteFileToolOptions, DEFAULT_TOOL_GUIDANCE, type EnvContextOptions, type PlanModeTool, type PlanModeToolOptions, type PlanModeToolWithStore, type PlanNode, type QuestionTool, type QuestionToolOptions, ReadTracker, ReasoningTools, RedirectBlockedError, type RepoMapOptions, type ResolveAndScreenOptions, type ScreenedFetchOptions, type SessionArtifactStore, type SessionArtifactStoreOptions, SsrfBlockedError, type TodoItem, type TodolistTool, type ToolGuidanceMap, type TruncationMode, type TruncationOptions, type TruncationResult, type VitestSummary, type WebSearchCallback, type WebSearchResult, buildEnvContext, buildRepoMap, catastrophicShellReason, commandDenialReason, createApplyPatchTool, createBraveWebSearchAdapter, createCurrentTimeTool, createEditFileTool, createGenericHttpSearchAdapter, createGitDiffTool, createGitStatusTool, createGlobTool, createInteractiveShellTool, createListDirTool, createPlanModeTool, createQuestionTool, createReadFileTool, createRunVitestTool, createSearchTextTool, createSessionArtifactStore, createShellTool, createTodolistTool, createUpdatePlanTool, createWebFetchTool, createWebSearchTool, createWriteFileTool, createWriteStdinTool, denyCatastrophicCommands, formatCode, formatDiff, formatError, formatFileList, injectGuidance, isBlockedIp, isCommandAllowed, renderToolList, replaceUnique, resolveAndScreen, screenedFetch, todoItemsToPlanNodes, truncateOutput, withDefaultGuidance, withDescription, withName, withShellExitGuidance, withToolResultGuidance };
|
|
1408
|
+
export { CatastrophicCommandError, type CommandPolicy, ContextMatchError, type ContextMatchReason, type CreateApplyPatchToolOptions, type CreateBraveWebSearchAdapterOptions, type CreateCurrentTimeToolOptions, type CreateEditFileToolOptions, type CreateGenericHttpSearchAdapterOptions, type CreateGitDiffToolOptions, type CreateGitStatusToolOptions, type CreateGlobToolOptions, type CreateInteractiveShellToolOptions, type CreateListDirToolOptions, type CreateReadFileToolOptions, type CreateRunVitestToolOptions, type CreateSearchTextToolOptions, type CreateShellToolOptions, type CreateViewImageToolOptions, type CreateWebFetchToolOptions, type CreateWebSearchToolOptions, type CreateWriteFileToolOptions, DEFAULT_MAX_IMAGE_BYTES, DEFAULT_TOOL_GUIDANCE, type EnvContextOptions, type PlanModeTool, type PlanModeToolOptions, type PlanModeToolWithStore, type PlanNode, type QuestionTool, type QuestionToolOptions, ReadTracker, ReasoningTools, RedirectBlockedError, type RepoMapOptions, type ResolveAndScreenOptions, type ScreenedFetchOptions, type SessionArtifactStore, type SessionArtifactStoreOptions, SsrfBlockedError, type TodoItem, type TodolistTool, type ToolGuidanceMap, type TruncationMode, type TruncationOptions, type TruncationResult, type VitestSummary, type WebSearchCallback, type WebSearchResult, buildEnvContext, buildRepoMap, catastrophicShellReason, commandDenialReason, createApplyPatchTool, createBraveWebSearchAdapter, createCurrentTimeTool, createEditFileTool, createGenericHttpSearchAdapter, createGitDiffTool, createGitStatusTool, createGlobTool, createInteractiveShellTool, createListDirTool, createPlanModeTool, createQuestionTool, createReadFileTool, createRunVitestTool, createSearchTextTool, createSessionArtifactStore, createShellTool, createTodolistTool, createUpdatePlanTool, createViewImageTool, createWebFetchTool, createWebSearchTool, createWriteFileTool, createWriteStdinTool, denyCatastrophicCommands, formatCode, formatDiff, formatError, formatFileList, injectGuidance, isBlockedIp, isCommandAllowed, renderToolList, replaceUnique, resolveAndScreen, screenedFetch, todoItemsToPlanNodes, truncateOutput, withDefaultGuidance, withDescription, withName, withShellExitGuidance, withToolResultGuidance };
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { rm, mkdir, writeFile, readFile, readdir, open, stat, copyFile } from 'fs/promises';
|
|
2
|
-
import { dirname, relative, join, isAbsolute } from 'path';
|
|
2
|
+
import { dirname, relative, join, isAbsolute, extname } from 'path';
|
|
3
3
|
import { Tool, ConfigurationError } from '@theokit/sdk';
|
|
4
4
|
import { z } from 'zod';
|
|
5
5
|
import { safePathJoin, assertNoSymlinkEscape, PathTraversalError, ForbiddenPathError, isForbiddenPath, safeFilenameForId } from '@theokit/sdk/path-safety';
|
|
@@ -791,12 +791,12 @@ function checkPathScope(path, projectRoot) {
|
|
|
791
791
|
throw err;
|
|
792
792
|
}
|
|
793
793
|
}
|
|
794
|
-
var
|
|
795
|
-
function
|
|
794
|
+
var SENSITIVE_SEGMENTS = /* @__PURE__ */ new Set([".env", ".git", "node_modules", ".theo"]);
|
|
795
|
+
function isForbiddenAtAnyDepth(path) {
|
|
796
796
|
const segs = path.replace(/\\/g, "/").split("/").filter(Boolean);
|
|
797
797
|
return segs.some((s) => {
|
|
798
798
|
if (s === ".env.example") return false;
|
|
799
|
-
return
|
|
799
|
+
return SENSITIVE_SEGMENTS.has(s) || /^\.env\./.test(s);
|
|
800
800
|
});
|
|
801
801
|
}
|
|
802
802
|
|
|
@@ -1584,7 +1584,7 @@ function createListDirTool(opts) {
|
|
|
1584
1584
|
}),
|
|
1585
1585
|
handler: async ({ path }, ctx) => {
|
|
1586
1586
|
const relative3 = path === "" || path === "." ? "." : path;
|
|
1587
|
-
const verdict =
|
|
1587
|
+
const verdict = decideScope(relative3, path, opts.allowAbsolute === true);
|
|
1588
1588
|
if (verdict.error !== void 0) return verdict.error;
|
|
1589
1589
|
if (verdict.absoluteRoot !== void 0) {
|
|
1590
1590
|
return listViaLocalFs(verdict.absoluteRoot, ".", path, max);
|
|
@@ -1597,14 +1597,14 @@ function createListDirTool(opts) {
|
|
|
1597
1597
|
}
|
|
1598
1598
|
});
|
|
1599
1599
|
}
|
|
1600
|
-
function
|
|
1600
|
+
function decideScope(relative3, original, allowAbsolute) {
|
|
1601
1601
|
const refuse = (error) => ({
|
|
1602
1602
|
error: JSON.stringify({ ok: false, error, path: original })
|
|
1603
1603
|
});
|
|
1604
1604
|
if (relative3 !== "." && isForbiddenPath(relative3)) return refuse("forbidden_path");
|
|
1605
1605
|
if (!isAbsolute(relative3)) return {};
|
|
1606
1606
|
if (!allowAbsolute) return refuse("path_traversal");
|
|
1607
|
-
if (
|
|
1607
|
+
if (isForbiddenAtAnyDepth(relative3)) return refuse("forbidden_path");
|
|
1608
1608
|
return { absoluteRoot: relative3 };
|
|
1609
1609
|
}
|
|
1610
1610
|
async function listViaLocalFs(projectRoot, relative3, originalPath, max) {
|
|
@@ -1767,10 +1767,10 @@ function createPlanModeTool(options) {
|
|
|
1767
1767
|
}
|
|
1768
1768
|
|
|
1769
1769
|
// src/question.ts
|
|
1770
|
-
function
|
|
1770
|
+
function askerFromContext(context) {
|
|
1771
1771
|
if (typeof context !== "object" || context === null) return void 0;
|
|
1772
|
-
const
|
|
1773
|
-
return typeof
|
|
1772
|
+
const candidate = context.askUser;
|
|
1773
|
+
return typeof candidate === "function" ? candidate : void 0;
|
|
1774
1774
|
}
|
|
1775
1775
|
function createQuestionTool(opts) {
|
|
1776
1776
|
const timeoutMs = opts.timeoutMs ?? 3e5;
|
|
@@ -1785,7 +1785,7 @@ function createQuestionTool(opts) {
|
|
|
1785
1785
|
required: ["question"]
|
|
1786
1786
|
},
|
|
1787
1787
|
handler: async (input, ctx) => {
|
|
1788
|
-
const askUser =
|
|
1788
|
+
const askUser = askerFromContext(ctx?.context) ?? opts.askUser;
|
|
1789
1789
|
if (askUser === void 0) {
|
|
1790
1790
|
return JSON.stringify({
|
|
1791
1791
|
ok: false,
|
|
@@ -1822,7 +1822,7 @@ function forbiddenReadError(path, allowAbsolute) {
|
|
|
1822
1822
|
if (isForbiddenPath(path)) {
|
|
1823
1823
|
return JSON.stringify({ ok: false, error: "forbidden_path", path });
|
|
1824
1824
|
}
|
|
1825
|
-
if (allowAbsolute && isAbsolute(path) &&
|
|
1825
|
+
if (allowAbsolute && isAbsolute(path) && isForbiddenAtAnyDepth(path)) {
|
|
1826
1826
|
return JSON.stringify({ ok: false, error: "forbidden_path", path });
|
|
1827
1827
|
}
|
|
1828
1828
|
return null;
|
|
@@ -2625,6 +2625,86 @@ function createUpdatePlanTool() {
|
|
|
2625
2625
|
}
|
|
2626
2626
|
});
|
|
2627
2627
|
}
|
|
2628
|
+
var MEDIA_TYPES = /* @__PURE__ */ new Map([
|
|
2629
|
+
[".png", "image/png"],
|
|
2630
|
+
[".jpg", "image/jpeg"],
|
|
2631
|
+
[".jpeg", "image/jpeg"],
|
|
2632
|
+
[".gif", "image/gif"],
|
|
2633
|
+
[".webp", "image/webp"]
|
|
2634
|
+
]);
|
|
2635
|
+
var DEFAULT_MAX_IMAGE_BYTES = 5 * 1024 * 1024;
|
|
2636
|
+
var json = (result) => JSON.stringify(result);
|
|
2637
|
+
function createViewImageTool(options) {
|
|
2638
|
+
const { projectRoot } = options;
|
|
2639
|
+
const maxBytes = options.maxBytes ?? DEFAULT_MAX_IMAGE_BYTES;
|
|
2640
|
+
return Tool.create({
|
|
2641
|
+
name: options.name ?? "view_image",
|
|
2642
|
+
description: options.description ?? "Read an image file from the project and show it to the model. Supports png, jpeg, gif and webp.",
|
|
2643
|
+
inputSchema: z.object({
|
|
2644
|
+
path: z.string().describe("Path to the image, relative to the project root.")
|
|
2645
|
+
}),
|
|
2646
|
+
handler: (input) => {
|
|
2647
|
+
const refused = checkPathScope(input.path, projectRoot);
|
|
2648
|
+
if (refused !== null) return refused;
|
|
2649
|
+
if (isForbiddenAtAnyDepth(input.path)) {
|
|
2650
|
+
return json({ ok: false, error: "path_traversal", path: input.path });
|
|
2651
|
+
}
|
|
2652
|
+
const mediaType = MEDIA_TYPES.get(extname(input.path).toLowerCase());
|
|
2653
|
+
if (mediaType === void 0) {
|
|
2654
|
+
return json({
|
|
2655
|
+
ok: false,
|
|
2656
|
+
error: "unsupported_image_type",
|
|
2657
|
+
path: input.path,
|
|
2658
|
+
supported: [...MEDIA_TYPES.keys()]
|
|
2659
|
+
});
|
|
2660
|
+
}
|
|
2661
|
+
const absolute = safePathJoin(projectRoot, input.path);
|
|
2662
|
+
let bytes;
|
|
2663
|
+
try {
|
|
2664
|
+
bytes = statSync(absolute).size;
|
|
2665
|
+
} catch {
|
|
2666
|
+
return json({ ok: false, error: "not_found", path: input.path });
|
|
2667
|
+
}
|
|
2668
|
+
if (bytes > maxBytes) {
|
|
2669
|
+
return json({
|
|
2670
|
+
ok: false,
|
|
2671
|
+
error: "image_too_large",
|
|
2672
|
+
path: input.path,
|
|
2673
|
+
bytes,
|
|
2674
|
+
limit_bytes: maxBytes
|
|
2675
|
+
});
|
|
2676
|
+
}
|
|
2677
|
+
return json({
|
|
2678
|
+
ok: true,
|
|
2679
|
+
path: input.path,
|
|
2680
|
+
media_type: mediaType,
|
|
2681
|
+
bytes,
|
|
2682
|
+
data: readFileSync(absolute).toString("base64")
|
|
2683
|
+
});
|
|
2684
|
+
},
|
|
2685
|
+
/**
|
|
2686
|
+
* Turn a successful read into an image block.
|
|
2687
|
+
*
|
|
2688
|
+
* A failed read stays TEXT on purpose: the model needs to read "not_found" and try another path,
|
|
2689
|
+
* and an error is not something to look at.
|
|
2690
|
+
*/
|
|
2691
|
+
toModelOutput: (output) => {
|
|
2692
|
+
let result;
|
|
2693
|
+
try {
|
|
2694
|
+
result = JSON.parse(output);
|
|
2695
|
+
} catch {
|
|
2696
|
+
return output;
|
|
2697
|
+
}
|
|
2698
|
+
if (result.ok !== true) return output;
|
|
2699
|
+
return [
|
|
2700
|
+
{
|
|
2701
|
+
type: "image",
|
|
2702
|
+
source: { type: "base64", media_type: result.media_type, data: result.data }
|
|
2703
|
+
}
|
|
2704
|
+
];
|
|
2705
|
+
}
|
|
2706
|
+
});
|
|
2707
|
+
}
|
|
2628
2708
|
var DEFAULT_TIMEOUT_MS4 = 3e4;
|
|
2629
2709
|
var MAX_BODY_BYTES = 1 * 1024 * 1024;
|
|
2630
2710
|
function createWebFetchTool(opts) {
|
|
@@ -2766,8 +2846,8 @@ function createBraveWebSearchAdapter(opts = {}) {
|
|
|
2766
2846
|
headers: { "X-Subscription-Token": apiKey, Accept: "application/json" }
|
|
2767
2847
|
});
|
|
2768
2848
|
if (!res.ok) throw new Error(`brave_search_failed: HTTP ${res.status}`);
|
|
2769
|
-
const
|
|
2770
|
-
const results =
|
|
2849
|
+
const json2 = await res.json();
|
|
2850
|
+
const results = json2?.web?.results ?? [];
|
|
2771
2851
|
return results.map((r) => ({
|
|
2772
2852
|
title: String(r?.title ?? ""),
|
|
2773
2853
|
url: String(r?.url ?? ""),
|
|
@@ -2927,6 +3007,6 @@ async function isBinaryFile(absolutePath) {
|
|
|
2927
3007
|
}
|
|
2928
3008
|
}
|
|
2929
3009
|
|
|
2930
|
-
export { CatastrophicCommandError, ContextMatchError, DEFAULT_TOOL_GUIDANCE, ReadTracker, ReasoningTools, RedirectBlockedError, SsrfBlockedError, buildEnvContext, buildRepoMap, catastrophicShellReason, commandDenialReason, createApplyPatchTool, createBraveWebSearchAdapter, createCurrentTimeTool, createEditFileTool, createGenericHttpSearchAdapter, createGitDiffTool, createGitStatusTool, createGlobTool, createInteractiveShellTool, createListDirTool, createPlanModeTool, createQuestionTool, createReadFileTool, createRunVitestTool, createSearchTextTool, createSessionArtifactStore, createShellTool, createTodolistTool, createUpdatePlanTool, createWebFetchTool, createWebSearchTool, createWriteFileTool, createWriteStdinTool, denyCatastrophicCommands, formatCode, formatDiff, formatError, formatFileList, injectGuidance, isBlockedIp, isCommandAllowed, renderToolList, replaceUnique, resolveAndScreen, screenedFetch, todoItemsToPlanNodes, truncateOutput, withDefaultGuidance, withDescription, withName, withShellExitGuidance, withToolResultGuidance };
|
|
3010
|
+
export { CatastrophicCommandError, ContextMatchError, DEFAULT_MAX_IMAGE_BYTES, DEFAULT_TOOL_GUIDANCE, ReadTracker, ReasoningTools, RedirectBlockedError, SsrfBlockedError, buildEnvContext, buildRepoMap, catastrophicShellReason, commandDenialReason, createApplyPatchTool, createBraveWebSearchAdapter, createCurrentTimeTool, createEditFileTool, createGenericHttpSearchAdapter, createGitDiffTool, createGitStatusTool, createGlobTool, createInteractiveShellTool, createListDirTool, createPlanModeTool, createQuestionTool, createReadFileTool, createRunVitestTool, createSearchTextTool, createSessionArtifactStore, createShellTool, createTodolistTool, createUpdatePlanTool, createViewImageTool, createWebFetchTool, createWebSearchTool, createWriteFileTool, createWriteStdinTool, denyCatastrophicCommands, formatCode, formatDiff, formatError, formatFileList, injectGuidance, isBlockedIp, isCommandAllowed, renderToolList, replaceUnique, resolveAndScreen, screenedFetch, todoItemsToPlanNodes, truncateOutput, withDefaultGuidance, withDescription, withName, withShellExitGuidance, withToolResultGuidance };
|
|
2931
3011
|
//# sourceMappingURL=index.js.map
|
|
2932
3012
|
//# sourceMappingURL=index.js.map
|