@theokit/sdk-tools 0.12.0 → 0.14.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/dist/index.cjs +167 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +57 -1
- package/dist/index.d.ts +57 -1
- package/dist/index.js +164 -15
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { CustomTool, ConfigurationError } from '@theokit/sdk';
|
|
2
|
+
import { InteractiveProvider } from '@theokit/sdk/interactive';
|
|
2
3
|
import { FilesystemProvider } from '@theokit/sdk/filesystem';
|
|
3
4
|
|
|
4
5
|
/**
|
|
@@ -153,6 +154,33 @@ interface CreateGlobToolOptions {
|
|
|
153
154
|
}
|
|
154
155
|
declare function createGlobTool(opts: CreateGlobToolOptions): CustomTool;
|
|
155
156
|
|
|
157
|
+
/**
|
|
158
|
+
* `interactive_shell` + `write_stdin` — built-in tools for driving an interactive
|
|
159
|
+
* session (a REPL, `git rebase -i`, any command that PROMPTS for stdin).
|
|
160
|
+
*
|
|
161
|
+
* Surface-agnostic by construction: both tools depend on an INJECTED
|
|
162
|
+
* `InteractiveProvider` (`@theokit/sdk/interactive`) exactly as `read_file`
|
|
163
|
+
* depends on `FilesystemProvider`. The HOST supplies the backend — a local
|
|
164
|
+
* `@theokit/sdk-pty` (terminal), a container/E2B backend (cluster/web), or a
|
|
165
|
+
* Tauri backend (desktop) — so the SAME tool runs on every theokit surface with
|
|
166
|
+
* NO native dependency here. When no backend is injected (or it cannot allocate
|
|
167
|
+
* a session) the tool returns `{ ok: false, error: 'interactive_unavailable' }`
|
|
168
|
+
* and the agent falls back to non-interactive exec.
|
|
169
|
+
*
|
|
170
|
+
* Return shapes (always a JSON string):
|
|
171
|
+
* interactive_shell → `{ ok: true, session_id, output }` | `{ ok: false, error }`
|
|
172
|
+
* write_stdin → `{ ok: true, output, alive }` | `{ ok: false, error }`
|
|
173
|
+
*/
|
|
174
|
+
|
|
175
|
+
interface CreateInteractiveShellToolOptions {
|
|
176
|
+
/** The interactive backend, or a per-request resolver of one (injected — never a direct native dep). */
|
|
177
|
+
interactive: InteractiveProvider<unknown>;
|
|
178
|
+
}
|
|
179
|
+
/** Start an interactive session; returns a `session_id` to drive with `write_stdin`. */
|
|
180
|
+
declare function createInteractiveShellTool(opts: CreateInteractiveShellToolOptions): CustomTool;
|
|
181
|
+
/** Write to a live interactive session's stdin and read the output it produces. */
|
|
182
|
+
declare function createWriteStdinTool(opts: CreateInteractiveShellToolOptions): CustomTool;
|
|
183
|
+
|
|
156
184
|
/**
|
|
157
185
|
* Composable command-permission policy layer (M3-6).
|
|
158
186
|
*
|
|
@@ -182,6 +210,34 @@ declare function commandDenialReason(command: string, policies: CommandPolicy[])
|
|
|
182
210
|
/** `true` when no policy denies `command` (an empty policy array allows everything). */
|
|
183
211
|
declare function isCommandAllowed(command: string, policies: CommandPolicy[]): boolean;
|
|
184
212
|
|
|
213
|
+
/**
|
|
214
|
+
* Context-tolerant single-occurrence text matching (`seek_sequence` parity, ported
|
|
215
|
+
* from the AgentBuilder Codex clone, M10/M13). Where a naive `edit_file` matches
|
|
216
|
+
* `old_string` byte-for-byte (or with a single whitespace-normalized fallback),
|
|
217
|
+
* this matcher is safe AND forgiving:
|
|
218
|
+
*
|
|
219
|
+
* Stage 1 — exact byte substring, with an **ambiguity guard**: if the target
|
|
220
|
+
* appears more than once it throws instead of silently editing the first hit
|
|
221
|
+
* (a too-short `old_string` editing the wrong location is the classic footgun).
|
|
222
|
+
* Stage 2 — line-based match down a **strictness ladder** (exact → rstrip → trim
|
|
223
|
+
* → unicode-normalize), stopping at the first rung that yields exactly ONE
|
|
224
|
+
* match; a rung matching 2+ positions throws (ambiguous), never picks one.
|
|
225
|
+
*
|
|
226
|
+
* Pure. Preserves the file's CRLF/LF style on the replaced region. Throws typed
|
|
227
|
+
* {@link ContextMatchError} (`empty | ambiguous | not_found`) so the caller maps to
|
|
228
|
+
* its own `{ ok: false, error }` shape.
|
|
229
|
+
*/
|
|
230
|
+
type ContextMatchReason = "empty" | "ambiguous" | "not_found";
|
|
231
|
+
declare class ContextMatchError extends Error {
|
|
232
|
+
readonly reason: ContextMatchReason;
|
|
233
|
+
constructor(reason: ContextMatchReason, message: string);
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Return `content` with the single occurrence of `find` replaced by `replace`, using the two-stage
|
|
237
|
+
* context-tolerant matcher. Throws {@link ContextMatchError} on empty/ambiguous/not-found.
|
|
238
|
+
*/
|
|
239
|
+
declare function replaceUnique(content: string, find: string, replace: string): string;
|
|
240
|
+
|
|
185
241
|
/**
|
|
186
242
|
* SSRF guard for network tools (M3-1).
|
|
187
243
|
*
|
|
@@ -975,4 +1031,4 @@ interface CreateWriteFileToolOptions {
|
|
|
975
1031
|
}
|
|
976
1032
|
declare function createWriteFileTool(opts: CreateWriteFileToolOptions): CustomTool;
|
|
977
1033
|
|
|
978
|
-
export { CatastrophicCommandError, type CommandPolicy, type CreateApplyPatchToolOptions, type CreateBraveWebSearchAdapterOptions, type CreateEditFileToolOptions, type CreateGenericHttpSearchAdapterOptions, type CreateGitDiffToolOptions, type CreateGlobToolOptions, 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 TruncationOptions, type TruncationResult, type VitestSummary, type WebSearchCallback, type WebSearchResult, buildEnvContext, buildRepoMap, catastrophicShellReason, commandDenialReason, createApplyPatchTool, createBraveWebSearchAdapter, createEditFileTool, createGenericHttpSearchAdapter, createGitDiffTool, createGlobTool, createListDirTool, createPlanModeTool, createQuestionTool, createReadFileTool, createRunVitestTool, createSearchTextTool, createSessionArtifactStore, createShellTool, createTodolistTool, createWebFetchTool, createWebSearchTool, createWriteFileTool, denyCatastrophicCommands, formatCode, formatDiff, formatError, formatFileList, injectGuidance, isBlockedIp, isCommandAllowed, renderToolList, resolveAndScreen, screenedFetch, todoItemsToPlanNodes, truncateOutput, withDefaultGuidance, withDescription, withName, withShellExitGuidance, withToolResultGuidance };
|
|
1034
|
+
export { CatastrophicCommandError, type CommandPolicy, ContextMatchError, type ContextMatchReason, type CreateApplyPatchToolOptions, type CreateBraveWebSearchAdapterOptions, type CreateEditFileToolOptions, type CreateGenericHttpSearchAdapterOptions, type CreateGitDiffToolOptions, 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 TruncationOptions, type TruncationResult, type VitestSummary, type WebSearchCallback, type WebSearchResult, buildEnvContext, buildRepoMap, catastrophicShellReason, commandDenialReason, createApplyPatchTool, createBraveWebSearchAdapter, createEditFileTool, createGenericHttpSearchAdapter, createGitDiffTool, createGlobTool, createInteractiveShellTool, createListDirTool, createPlanModeTool, createQuestionTool, createReadFileTool, createRunVitestTool, createSearchTextTool, createSessionArtifactStore, createShellTool, createTodolistTool, 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
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { CustomTool, ConfigurationError } from '@theokit/sdk';
|
|
2
|
+
import { InteractiveProvider } from '@theokit/sdk/interactive';
|
|
2
3
|
import { FilesystemProvider } from '@theokit/sdk/filesystem';
|
|
3
4
|
|
|
4
5
|
/**
|
|
@@ -153,6 +154,33 @@ interface CreateGlobToolOptions {
|
|
|
153
154
|
}
|
|
154
155
|
declare function createGlobTool(opts: CreateGlobToolOptions): CustomTool;
|
|
155
156
|
|
|
157
|
+
/**
|
|
158
|
+
* `interactive_shell` + `write_stdin` — built-in tools for driving an interactive
|
|
159
|
+
* session (a REPL, `git rebase -i`, any command that PROMPTS for stdin).
|
|
160
|
+
*
|
|
161
|
+
* Surface-agnostic by construction: both tools depend on an INJECTED
|
|
162
|
+
* `InteractiveProvider` (`@theokit/sdk/interactive`) exactly as `read_file`
|
|
163
|
+
* depends on `FilesystemProvider`. The HOST supplies the backend — a local
|
|
164
|
+
* `@theokit/sdk-pty` (terminal), a container/E2B backend (cluster/web), or a
|
|
165
|
+
* Tauri backend (desktop) — so the SAME tool runs on every theokit surface with
|
|
166
|
+
* NO native dependency here. When no backend is injected (or it cannot allocate
|
|
167
|
+
* a session) the tool returns `{ ok: false, error: 'interactive_unavailable' }`
|
|
168
|
+
* and the agent falls back to non-interactive exec.
|
|
169
|
+
*
|
|
170
|
+
* Return shapes (always a JSON string):
|
|
171
|
+
* interactive_shell → `{ ok: true, session_id, output }` | `{ ok: false, error }`
|
|
172
|
+
* write_stdin → `{ ok: true, output, alive }` | `{ ok: false, error }`
|
|
173
|
+
*/
|
|
174
|
+
|
|
175
|
+
interface CreateInteractiveShellToolOptions {
|
|
176
|
+
/** The interactive backend, or a per-request resolver of one (injected — never a direct native dep). */
|
|
177
|
+
interactive: InteractiveProvider<unknown>;
|
|
178
|
+
}
|
|
179
|
+
/** Start an interactive session; returns a `session_id` to drive with `write_stdin`. */
|
|
180
|
+
declare function createInteractiveShellTool(opts: CreateInteractiveShellToolOptions): CustomTool;
|
|
181
|
+
/** Write to a live interactive session's stdin and read the output it produces. */
|
|
182
|
+
declare function createWriteStdinTool(opts: CreateInteractiveShellToolOptions): CustomTool;
|
|
183
|
+
|
|
156
184
|
/**
|
|
157
185
|
* Composable command-permission policy layer (M3-6).
|
|
158
186
|
*
|
|
@@ -182,6 +210,34 @@ declare function commandDenialReason(command: string, policies: CommandPolicy[])
|
|
|
182
210
|
/** `true` when no policy denies `command` (an empty policy array allows everything). */
|
|
183
211
|
declare function isCommandAllowed(command: string, policies: CommandPolicy[]): boolean;
|
|
184
212
|
|
|
213
|
+
/**
|
|
214
|
+
* Context-tolerant single-occurrence text matching (`seek_sequence` parity, ported
|
|
215
|
+
* from the AgentBuilder Codex clone, M10/M13). Where a naive `edit_file` matches
|
|
216
|
+
* `old_string` byte-for-byte (or with a single whitespace-normalized fallback),
|
|
217
|
+
* this matcher is safe AND forgiving:
|
|
218
|
+
*
|
|
219
|
+
* Stage 1 — exact byte substring, with an **ambiguity guard**: if the target
|
|
220
|
+
* appears more than once it throws instead of silently editing the first hit
|
|
221
|
+
* (a too-short `old_string` editing the wrong location is the classic footgun).
|
|
222
|
+
* Stage 2 — line-based match down a **strictness ladder** (exact → rstrip → trim
|
|
223
|
+
* → unicode-normalize), stopping at the first rung that yields exactly ONE
|
|
224
|
+
* match; a rung matching 2+ positions throws (ambiguous), never picks one.
|
|
225
|
+
*
|
|
226
|
+
* Pure. Preserves the file's CRLF/LF style on the replaced region. Throws typed
|
|
227
|
+
* {@link ContextMatchError} (`empty | ambiguous | not_found`) so the caller maps to
|
|
228
|
+
* its own `{ ok: false, error }` shape.
|
|
229
|
+
*/
|
|
230
|
+
type ContextMatchReason = "empty" | "ambiguous" | "not_found";
|
|
231
|
+
declare class ContextMatchError extends Error {
|
|
232
|
+
readonly reason: ContextMatchReason;
|
|
233
|
+
constructor(reason: ContextMatchReason, message: string);
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Return `content` with the single occurrence of `find` replaced by `replace`, using the two-stage
|
|
237
|
+
* context-tolerant matcher. Throws {@link ContextMatchError} on empty/ambiguous/not-found.
|
|
238
|
+
*/
|
|
239
|
+
declare function replaceUnique(content: string, find: string, replace: string): string;
|
|
240
|
+
|
|
185
241
|
/**
|
|
186
242
|
* SSRF guard for network tools (M3-1).
|
|
187
243
|
*
|
|
@@ -975,4 +1031,4 @@ interface CreateWriteFileToolOptions {
|
|
|
975
1031
|
}
|
|
976
1032
|
declare function createWriteFileTool(opts: CreateWriteFileToolOptions): CustomTool;
|
|
977
1033
|
|
|
978
|
-
export { CatastrophicCommandError, type CommandPolicy, type CreateApplyPatchToolOptions, type CreateBraveWebSearchAdapterOptions, type CreateEditFileToolOptions, type CreateGenericHttpSearchAdapterOptions, type CreateGitDiffToolOptions, type CreateGlobToolOptions, 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 TruncationOptions, type TruncationResult, type VitestSummary, type WebSearchCallback, type WebSearchResult, buildEnvContext, buildRepoMap, catastrophicShellReason, commandDenialReason, createApplyPatchTool, createBraveWebSearchAdapter, createEditFileTool, createGenericHttpSearchAdapter, createGitDiffTool, createGlobTool, createListDirTool, createPlanModeTool, createQuestionTool, createReadFileTool, createRunVitestTool, createSearchTextTool, createSessionArtifactStore, createShellTool, createTodolistTool, createWebFetchTool, createWebSearchTool, createWriteFileTool, denyCatastrophicCommands, formatCode, formatDiff, formatError, formatFileList, injectGuidance, isBlockedIp, isCommandAllowed, renderToolList, resolveAndScreen, screenedFetch, todoItemsToPlanNodes, truncateOutput, withDefaultGuidance, withDescription, withName, withShellExitGuidance, withToolResultGuidance };
|
|
1034
|
+
export { CatastrophicCommandError, type CommandPolicy, ContextMatchError, type ContextMatchReason, type CreateApplyPatchToolOptions, type CreateBraveWebSearchAdapterOptions, type CreateEditFileToolOptions, type CreateGenericHttpSearchAdapterOptions, type CreateGitDiffToolOptions, 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 TruncationOptions, type TruncationResult, type VitestSummary, type WebSearchCallback, type WebSearchResult, buildEnvContext, buildRepoMap, catastrophicShellReason, commandDenialReason, createApplyPatchTool, createBraveWebSearchAdapter, createEditFileTool, createGenericHttpSearchAdapter, createGitDiffTool, createGlobTool, createInteractiveShellTool, createListDirTool, createPlanModeTool, createQuestionTool, createReadFileTool, createRunVitestTool, createSearchTextTool, createSessionArtifactStore, createShellTool, createTodolistTool, 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
|
@@ -6,6 +6,7 @@ import { existsSync, statSync, mkdirSync, writeFileSync, realpathSync, readFileS
|
|
|
6
6
|
import { safeFilenameForId, safePathJoin as safePathJoin$1 } from '@theokit/sdk/path-safety';
|
|
7
7
|
import { replaceFileAtomic } from '@theokit/sdk/persistence';
|
|
8
8
|
import { spawn } from 'child_process';
|
|
9
|
+
import { resolveInteractive, InteractiveUnavailableError, NoSuchSessionError } from '@theokit/sdk/interactive';
|
|
9
10
|
import { lookup } from 'dns/promises';
|
|
10
11
|
import { isIP } from 'net';
|
|
11
12
|
import { resolveFilesystem, FileNotFoundError, FilesystemSecurityError, FilesystemReadOnlyError, StaleFileError, FilesystemError } from '@theokit/sdk/filesystem';
|
|
@@ -263,6 +264,90 @@ function createSessionArtifactStore(options) {
|
|
|
263
264
|
}
|
|
264
265
|
return { write, read, has, list, path };
|
|
265
266
|
}
|
|
267
|
+
|
|
268
|
+
// src/internal/context-match.ts
|
|
269
|
+
var ContextMatchError = class extends Error {
|
|
270
|
+
reason;
|
|
271
|
+
constructor(reason, message) {
|
|
272
|
+
super(message);
|
|
273
|
+
this.name = "ContextMatchError";
|
|
274
|
+
this.reason = reason;
|
|
275
|
+
}
|
|
276
|
+
};
|
|
277
|
+
function normalizeUnicode(s) {
|
|
278
|
+
return s.replace(/[‐-―−]/g, "-").replace(/[‘-‛]/g, "'").replace(/[“-‟]/g, '"').replace(/[ - ]/g, " ");
|
|
279
|
+
}
|
|
280
|
+
var LINE_MATCH_LADDER = [
|
|
281
|
+
(s) => s,
|
|
282
|
+
// exact
|
|
283
|
+
(s) => s.replace(/\s+$/, ""),
|
|
284
|
+
// rstrip (ignore trailing whitespace)
|
|
285
|
+
(s) => s.trim(),
|
|
286
|
+
// trim (ignore leading + trailing)
|
|
287
|
+
(s) => normalizeUnicode(s).trim()
|
|
288
|
+
// unicode + trim (loosest)
|
|
289
|
+
];
|
|
290
|
+
function matchesAt(lines, pattern, i, norm) {
|
|
291
|
+
for (let p = 0; p < pattern.length; p++) {
|
|
292
|
+
const lineAt = lines[i + p];
|
|
293
|
+
const patAt = pattern[p];
|
|
294
|
+
if (lineAt === void 0 || patAt === void 0 || norm(lineAt) !== norm(patAt)) return false;
|
|
295
|
+
}
|
|
296
|
+
return true;
|
|
297
|
+
}
|
|
298
|
+
function findHits(lines, pattern, norm) {
|
|
299
|
+
const hits = [];
|
|
300
|
+
for (let i = 0; i + pattern.length <= lines.length; i++) {
|
|
301
|
+
if (matchesAt(lines, pattern, i, norm)) hits.push(i);
|
|
302
|
+
}
|
|
303
|
+
return hits;
|
|
304
|
+
}
|
|
305
|
+
function seekUniqueLineMatch(lines, pattern, find) {
|
|
306
|
+
if (pattern.length === 0 || pattern.length > lines.length) return null;
|
|
307
|
+
for (const norm of LINE_MATCH_LADDER) {
|
|
308
|
+
const hits = findHits(lines, pattern, norm);
|
|
309
|
+
if (hits.length === 1) return hits[0] ?? null;
|
|
310
|
+
if (hits.length > 1) {
|
|
311
|
+
throw new ContextMatchError(
|
|
312
|
+
"ambiguous",
|
|
313
|
+
`target text is ambiguous (multiple matches); include more context:
|
|
314
|
+
${find}`
|
|
315
|
+
);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
return null;
|
|
319
|
+
}
|
|
320
|
+
function replaceUnique(content, find, replace) {
|
|
321
|
+
if (find === "") {
|
|
322
|
+
throw new ContextMatchError(
|
|
323
|
+
"empty",
|
|
324
|
+
"empty find is not a valid target; provide the text to replace"
|
|
325
|
+
);
|
|
326
|
+
}
|
|
327
|
+
const first = content.indexOf(find);
|
|
328
|
+
if (first !== -1) {
|
|
329
|
+
if (content.indexOf(find, first + 1) !== -1) {
|
|
330
|
+
throw new ContextMatchError(
|
|
331
|
+
"ambiguous",
|
|
332
|
+
`target text is ambiguous (multiple matches); include more context:
|
|
333
|
+
${find}`
|
|
334
|
+
);
|
|
335
|
+
}
|
|
336
|
+
return content.slice(0, first) + replace + content.slice(first + find.length);
|
|
337
|
+
}
|
|
338
|
+
const lines = content.split("\n");
|
|
339
|
+
const at = seekUniqueLineMatch(lines, find.split("\n"), find);
|
|
340
|
+
if (at === null) {
|
|
341
|
+
throw new ContextMatchError("not_found", `target text not found:
|
|
342
|
+
${find}`);
|
|
343
|
+
}
|
|
344
|
+
const patternLen = find.split("\n").length;
|
|
345
|
+
const matchedCrlf = patternLen > 0 && lines.slice(at, at + patternLen).every((l) => l.endsWith("\r"));
|
|
346
|
+
const replaceLines = matchedCrlf ? replace.split("\n").map((l) => l.endsWith("\r") ? l : `${l}\r`) : replace.split("\n");
|
|
347
|
+
return [...lines.slice(0, at), ...replaceLines, ...lines.slice(at + patternLen)].join("\n");
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// src/edit-file.ts
|
|
266
351
|
function createEditFileTool(opts) {
|
|
267
352
|
const { projectRoot } = opts;
|
|
268
353
|
return Tool.create({
|
|
@@ -304,26 +389,36 @@ function createEditFileTool(opts) {
|
|
|
304
389
|
const exactIdx = content.indexOf(old_string);
|
|
305
390
|
if (exactIdx !== -1) {
|
|
306
391
|
await copyFile(absolutePath, `${absolutePath}.bak`);
|
|
307
|
-
const
|
|
308
|
-
await writeFile(absolutePath,
|
|
392
|
+
const result = content.slice(0, exactIdx) + new_string + content.slice(exactIdx + old_string.length);
|
|
393
|
+
await writeFile(absolutePath, result, "utf-8");
|
|
309
394
|
return JSON.stringify({ ok: true, replacements: 1 });
|
|
310
395
|
}
|
|
311
396
|
const normalizedContent = normalizeWhitespace(content);
|
|
312
397
|
const normalizedOld = normalizeWhitespace(old_string);
|
|
313
398
|
const normalizedIdx = normalizedContent.indexOf(normalizedOld);
|
|
314
|
-
if (normalizedIdx
|
|
315
|
-
|
|
399
|
+
if (normalizedIdx !== -1) {
|
|
400
|
+
const span = findOriginalSpan(
|
|
401
|
+
content,
|
|
402
|
+
normalizedContent,
|
|
403
|
+
normalizedIdx,
|
|
404
|
+
normalizedOld.length
|
|
405
|
+
);
|
|
406
|
+
await copyFile(absolutePath, `${absolutePath}.bak`);
|
|
407
|
+
const result = content.slice(0, span.start) + new_string + content.slice(span.end);
|
|
408
|
+
await writeFile(absolutePath, result, "utf-8");
|
|
409
|
+
return JSON.stringify({ ok: true, replacements: 1 });
|
|
410
|
+
}
|
|
411
|
+
try {
|
|
412
|
+
const result = replaceUnique(content, old_string, new_string);
|
|
413
|
+
await copyFile(absolutePath, `${absolutePath}.bak`);
|
|
414
|
+
await writeFile(absolutePath, result, "utf-8");
|
|
415
|
+
return JSON.stringify({ ok: true, replacements: 1 });
|
|
416
|
+
} catch (err) {
|
|
417
|
+
if (err instanceof ContextMatchError) {
|
|
418
|
+
return JSON.stringify({ ok: false, error: "no_match", path });
|
|
419
|
+
}
|
|
420
|
+
throw err;
|
|
316
421
|
}
|
|
317
|
-
const span = findOriginalSpan(
|
|
318
|
-
content,
|
|
319
|
-
normalizedContent,
|
|
320
|
-
normalizedIdx,
|
|
321
|
-
normalizedOld.length
|
|
322
|
-
);
|
|
323
|
-
await copyFile(absolutePath, `${absolutePath}.bak`);
|
|
324
|
-
const result = content.slice(0, span.start) + new_string + content.slice(span.end);
|
|
325
|
-
await writeFile(absolutePath, result, "utf-8");
|
|
326
|
-
return JSON.stringify({ ok: true, replacements: 1 });
|
|
327
422
|
}
|
|
328
423
|
});
|
|
329
424
|
}
|
|
@@ -595,6 +690,60 @@ function globToRegex(pattern) {
|
|
|
595
690
|
}
|
|
596
691
|
return new RegExp(`^${regexStr}$`);
|
|
597
692
|
}
|
|
693
|
+
function toErrorJson(err) {
|
|
694
|
+
if (err instanceof InteractiveUnavailableError) {
|
|
695
|
+
return JSON.stringify({ ok: false, error: "interactive_unavailable" });
|
|
696
|
+
}
|
|
697
|
+
if (err instanceof NoSuchSessionError) {
|
|
698
|
+
return JSON.stringify({ ok: false, error: "no_such_session" });
|
|
699
|
+
}
|
|
700
|
+
throw err;
|
|
701
|
+
}
|
|
702
|
+
function createInteractiveShellTool(opts) {
|
|
703
|
+
const { interactive } = opts;
|
|
704
|
+
return Tool.create({
|
|
705
|
+
name: "interactive_shell",
|
|
706
|
+
description: "Start an interactive shell session for a command that PROMPTS for input or is a REPL (python, node, `git rebase -i`, a `read` prompt) \u2014 NOT for one-shot commands (use shell_exec). Returns a session_id; drive it with write_stdin, reading the incremental output each step. Returns { ok, session_id, output } or { ok: false, error }.",
|
|
707
|
+
inputSchema: z.object({
|
|
708
|
+
command: z.string().min(1).describe("Command to run interactively, e.g. 'python3' or 'bash -i'."),
|
|
709
|
+
yield_time_ms: z.number().int().positive().optional().describe("How long to wait for startup output before returning (clamped by the backend).")
|
|
710
|
+
}),
|
|
711
|
+
handler: async ({ command, yield_time_ms }, ctx) => {
|
|
712
|
+
try {
|
|
713
|
+
const backend = await resolveInteractive(interactive, ctx ?? {});
|
|
714
|
+
const { sessionId, output } = await backend.startInteractive(command, {
|
|
715
|
+
yieldMs: yield_time_ms
|
|
716
|
+
});
|
|
717
|
+
return JSON.stringify({ ok: true, session_id: sessionId, output });
|
|
718
|
+
} catch (err) {
|
|
719
|
+
return toErrorJson(err);
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
});
|
|
723
|
+
}
|
|
724
|
+
function createWriteStdinTool(opts) {
|
|
725
|
+
const { interactive } = opts;
|
|
726
|
+
return Tool.create({
|
|
727
|
+
name: "write_stdin",
|
|
728
|
+
description: "Write input to a live interactive session (from interactive_shell) and read the output it produces during the wait window. Include a trailing newline to submit a line. Returns { ok, output, alive } (alive:false means the session exited) or { ok: false, error }.",
|
|
729
|
+
inputSchema: z.object({
|
|
730
|
+
session_id: z.string().min(1).describe("The session_id returned by interactive_shell."),
|
|
731
|
+
input: z.string().describe("Text to write to stdin (add a trailing '\\n' to submit a line)."),
|
|
732
|
+
yield_time_ms: z.number().int().positive().optional().describe("How long to wait for output before returning (clamped by the backend).")
|
|
733
|
+
}),
|
|
734
|
+
handler: async ({ session_id, input, yield_time_ms }, ctx) => {
|
|
735
|
+
try {
|
|
736
|
+
const backend = await resolveInteractive(interactive, ctx ?? {});
|
|
737
|
+
const { output, alive } = await backend.writeStdin(session_id, input, {
|
|
738
|
+
yieldMs: yield_time_ms
|
|
739
|
+
});
|
|
740
|
+
return JSON.stringify({ ok: true, output, alive });
|
|
741
|
+
} catch (err) {
|
|
742
|
+
return toErrorJson(err);
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
});
|
|
746
|
+
}
|
|
598
747
|
var CatastrophicCommandError = class extends ConfigurationError {
|
|
599
748
|
name = "CatastrophicCommandError";
|
|
600
749
|
constructor(reason) {
|
|
@@ -2247,6 +2396,6 @@ async function isBinaryFile(absolutePath) {
|
|
|
2247
2396
|
}
|
|
2248
2397
|
}
|
|
2249
2398
|
|
|
2250
|
-
export { CatastrophicCommandError, DEFAULT_TOOL_GUIDANCE, ReadTracker, ReasoningTools, RedirectBlockedError, SsrfBlockedError, buildEnvContext, buildRepoMap, catastrophicShellReason, commandDenialReason, createApplyPatchTool, createBraveWebSearchAdapter, createEditFileTool, createGenericHttpSearchAdapter, createGitDiffTool, createGlobTool, createListDirTool, createPlanModeTool, createQuestionTool, createReadFileTool, createRunVitestTool, createSearchTextTool, createSessionArtifactStore, createShellTool, createTodolistTool, createWebFetchTool, createWebSearchTool, createWriteFileTool, denyCatastrophicCommands, formatCode, formatDiff, formatError, formatFileList, injectGuidance, isBlockedIp, isCommandAllowed, renderToolList, resolveAndScreen, screenedFetch, todoItemsToPlanNodes, truncateOutput, withDefaultGuidance, withDescription, withName, withShellExitGuidance, withToolResultGuidance };
|
|
2399
|
+
export { CatastrophicCommandError, ContextMatchError, DEFAULT_TOOL_GUIDANCE, ReadTracker, ReasoningTools, RedirectBlockedError, SsrfBlockedError, buildEnvContext, buildRepoMap, catastrophicShellReason, commandDenialReason, createApplyPatchTool, createBraveWebSearchAdapter, createEditFileTool, createGenericHttpSearchAdapter, createGitDiffTool, createGlobTool, createInteractiveShellTool, createListDirTool, createPlanModeTool, createQuestionTool, createReadFileTool, createRunVitestTool, createSearchTextTool, createSessionArtifactStore, createShellTool, createTodolistTool, createWebFetchTool, createWebSearchTool, createWriteFileTool, createWriteStdinTool, denyCatastrophicCommands, formatCode, formatDiff, formatError, formatFileList, injectGuidance, isBlockedIp, isCommandAllowed, renderToolList, replaceUnique, resolveAndScreen, screenedFetch, todoItemsToPlanNodes, truncateOutput, withDefaultGuidance, withDescription, withName, withShellExitGuidance, withToolResultGuidance };
|
|
2251
2400
|
//# sourceMappingURL=index.js.map
|
|
2252
2401
|
//# sourceMappingURL=index.js.map
|