@theokit/sdk-tools 0.27.6 → 0.29.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.
Files changed (52) hide show
  1. package/CHANGELOG.md +74 -0
  2. package/dist/extract-document.cjs +175 -0
  3. package/dist/extract-document.cjs.map +1 -0
  4. package/dist/extract-document.d.cts +59 -0
  5. package/dist/extract-document.d.ts +59 -0
  6. package/dist/extract-document.js +173 -0
  7. package/dist/extract-document.js.map +1 -0
  8. package/dist/github-link-preview.cjs +227 -0
  9. package/dist/github-link-preview.cjs.map +1 -0
  10. package/dist/github-link-preview.d.cts +69 -0
  11. package/dist/github-link-preview.d.ts +69 -0
  12. package/dist/github-link-preview.js +225 -0
  13. package/dist/github-link-preview.js.map +1 -0
  14. package/dist/goal.cjs +84 -0
  15. package/dist/goal.cjs.map +1 -0
  16. package/dist/goal.d.cts +58 -0
  17. package/dist/goal.d.ts +58 -0
  18. package/dist/goal.js +82 -0
  19. package/dist/goal.js.map +1 -0
  20. package/dist/index.cjs +127 -7
  21. package/dist/index.cjs.map +1 -1
  22. package/dist/index.d.cts +110 -59
  23. package/dist/index.d.ts +110 -59
  24. package/dist/index.js +128 -11
  25. package/dist/index.js.map +1 -1
  26. package/dist/read-pdf.cjs +154 -0
  27. package/dist/read-pdf.cjs.map +1 -0
  28. package/dist/read-pdf.d.cts +58 -0
  29. package/dist/read-pdf.d.ts +58 -0
  30. package/dist/read-pdf.js +152 -0
  31. package/dist/read-pdf.js.map +1 -0
  32. package/dist/screenshot-logbook.cjs +245 -0
  33. package/dist/screenshot-logbook.cjs.map +1 -0
  34. package/dist/screenshot-logbook.d.cts +124 -0
  35. package/dist/screenshot-logbook.d.ts +124 -0
  36. package/dist/screenshot-logbook.js +241 -0
  37. package/dist/screenshot-logbook.js.map +1 -0
  38. package/dist/structured-output.cjs +52 -0
  39. package/dist/structured-output.cjs.map +1 -0
  40. package/dist/structured-output.d.cts +61 -0
  41. package/dist/structured-output.d.ts +61 -0
  42. package/dist/structured-output.js +50 -0
  43. package/dist/structured-output.js.map +1 -0
  44. package/dist/web-search-CE82zZVl.d.cts +61 -0
  45. package/dist/web-search-CE82zZVl.d.ts +61 -0
  46. package/dist/web-search-adapters.cjs +71 -0
  47. package/dist/web-search-adapters.cjs.map +1 -0
  48. package/dist/web-search-adapters.d.cts +57 -0
  49. package/dist/web-search-adapters.d.ts +57 -0
  50. package/dist/web-search-adapters.js +68 -0
  51. package/dist/web-search-adapters.js.map +1 -0
  52. package/package.json +84 -4
package/dist/index.d.cts CHANGED
@@ -2,6 +2,8 @@ import { CustomTool, ConfigurationError } from '@theokit/sdk';
2
2
  import { FilesystemProvider } from '@theokit/sdk/filesystem';
3
3
  import { SandboxProvider } from '@theokit/sdk/sandbox';
4
4
  import { InteractiveProvider } from '@theokit/sdk/interactive';
5
+ import { W as WebSearchCallback } from './web-search-CE82zZVl.cjs';
6
+ export { C as CreateWebSearchToolOptions, a as WebSearchResult, c as createWebSearchTool } from './web-search-CE82zZVl.cjs';
5
7
 
6
8
  /**
7
9
  * `apply_patch` — built-in tool for coding agents.
@@ -1279,6 +1281,113 @@ interface CreateShellToolOptions {
1279
1281
  */
1280
1282
  declare function createShellTool(opts: CreateShellToolOptions): CustomTool;
1281
1283
 
1284
+ /**
1285
+ * `text_to_speech` — turns text into an audio file inside the project root.
1286
+ *
1287
+ * The audio comes from a `synthesize` callback the consumer injects (the `web_search` pattern): the
1288
+ * tool holds no key and opens no socket. The clip is written to `outputDir` below `projectRoot`
1289
+ * under the same confinement `view_image` and `read_file` carry, and the model receives the
1290
+ * relative path, the mime type and the byte count, never the audio itself: a model cannot listen
1291
+ * to it, and a base64 clip at the default ceiling would be about 13 MB of context text.
1292
+ *
1293
+ * Return shape (always a JSON string):
1294
+ * - `{ ok: true, path, mime_type, bytes }`
1295
+ * - `{ ok: false, error }` with `error` one of `path_traversal`, `text_too_large`,
1296
+ * `synthesis_failed`, `unsupported_audio_type`, `empty_audio`, `audio_too_large`, `write_failed`
1297
+ *
1298
+ * Nothing is written on any failure, and a refused path or an oversize text never reaches the
1299
+ * provider.
1300
+ *
1301
+ * Limits, accepted and stated: the confinement is checked before the write and again after the
1302
+ * output directory is created, but a symlink swapped in by another process between that check and
1303
+ * the rename is not caught (the same-user race the sibling tools leave open). A clip is written with
1304
+ * mode 0o600. A chosen `filename`, or a default stem two texts happen to share (32 bits of the
1305
+ * text's sha256), replaces the existing file of that name. `synthesis_failed` carries the provider's
1306
+ * own message on purpose: the provider is the consumer's code, and its words are what the model
1307
+ * needs to explain the failure; `write_failed` carries only the errno code, since a file-system
1308
+ * message names the host's absolute paths.
1309
+ */
1310
+
1311
+ /** Default ceiling on the text sent to the provider, in UTF-16 code units. A default, not a measured limit. */
1312
+ declare const DEFAULT_MAX_TEXT_CHARS = 4096;
1313
+ /** Default ceiling on the clip written to disk: 10 MiB, by analogy with `DEFAULT_MAX_IMAGE_BYTES`. */
1314
+ declare const DEFAULT_MAX_AUDIO_BYTES: number;
1315
+ /** What the provider returns: the encoded clip and its mime type. */
1316
+ interface SynthesizedAudio {
1317
+ audio: Uint8Array;
1318
+ mimeType: string;
1319
+ }
1320
+ /**
1321
+ * The speech provider {@link createTextToSpeechTool} calls.
1322
+ *
1323
+ * Reject the promise to signal a failed synthesis: the tool turns a rejection into
1324
+ * `{ ok: false, error: "synthesis_failed", message }` and never lets it escape into the agent turn.
1325
+ */
1326
+ type TextToSpeechCallback = (text: string) => Promise<SynthesizedAudio>;
1327
+ /** Options for {@link createTextToSpeechTool}. `synthesize` and `projectRoot` are required. */
1328
+ interface CreateTextToSpeechToolOptions {
1329
+ /** Speech provider callback — the consumer injects the implementation. */
1330
+ synthesize: TextToSpeechCallback;
1331
+ /** Root the tool writes under. Every path is resolved inside it. */
1332
+ projectRoot: string;
1333
+ /** Directory for the clips, relative to `projectRoot`. Omitted ⇒ `audio`. Created when absent. */
1334
+ outputDir?: string;
1335
+ /** Name exposed to the model. Omitted ⇒ `text_to_speech`. The name is a contract: the approval
1336
+ * key, what the model sees and what telemetry records. */
1337
+ name?: string;
1338
+ /** Description exposed to the model. Omitted ⇒ the literal below. */
1339
+ description?: string;
1340
+ /** Longest text sent to the provider. Omitted ⇒ {@link DEFAULT_MAX_TEXT_CHARS}. */
1341
+ maxTextChars?: number;
1342
+ /** Largest clip written. Omitted ⇒ {@link DEFAULT_MAX_AUDIO_BYTES}. A clip of exactly this size fits. */
1343
+ maxAudioBytes?: number;
1344
+ }
1345
+ type TextToSpeechFailure = {
1346
+ ok: false;
1347
+ error: "path_traversal";
1348
+ path: string;
1349
+ } | {
1350
+ ok: false;
1351
+ error: "text_too_large";
1352
+ chars: number;
1353
+ max_chars: number;
1354
+ } | {
1355
+ ok: false;
1356
+ error: "synthesis_failed";
1357
+ message: string;
1358
+ } | {
1359
+ ok: false;
1360
+ error: "unsupported_audio_type";
1361
+ mime_type: string;
1362
+ } | {
1363
+ ok: false;
1364
+ error: "empty_audio";
1365
+ } | {
1366
+ ok: false;
1367
+ error: "audio_too_large";
1368
+ bytes: number;
1369
+ max_bytes: number;
1370
+ } | {
1371
+ ok: false;
1372
+ error: "write_failed";
1373
+ message: string;
1374
+ };
1375
+ /** The tool's result, before it is serialised to the JSON string the handler returns. */
1376
+ type TextToSpeechResult = {
1377
+ ok: true;
1378
+ path: string;
1379
+ mime_type: string;
1380
+ bytes: number;
1381
+ } | TextToSpeechFailure;
1382
+ /**
1383
+ * Build the `text_to_speech` tool over a caller-supplied provider.
1384
+ *
1385
+ * Order of checks: the text length, then the path (stem and output directory), then the provider,
1386
+ * then the mime table, the empty and size checks, then the write. A refused path or an oversize
1387
+ * text never reaches the provider, and no failure leaves a file behind.
1388
+ */
1389
+ declare function createTextToSpeechTool(opts: CreateTextToSpeechToolOptions): CustomTool;
1390
+
1282
1391
  /**
1283
1392
  * `todolist` — in-session task tracking for multi-step work.
1284
1393
  *
@@ -1607,64 +1716,6 @@ interface CreateWebFetchToolOptions {
1607
1716
  */
1608
1717
  declare function createWebFetchTool(opts?: CreateWebFetchToolOptions): CustomTool;
1609
1718
 
1610
- /**
1611
- * `web_search` — built-in tool for coding agents.
1612
- *
1613
- * Accepts a search callback via DIP (consumer provides the search
1614
- * provider). The tool itself is provider-agnostic.
1615
- *
1616
- * Return shape (always a JSON string):
1617
- * - `{ ok: true, results: Array<{ title, url, snippet }> }`
1618
- * - `{ ok: false, error: 'search_failed' | 'no_provider' }`
1619
- */
1620
-
1621
- interface WebSearchResult {
1622
- title: string;
1623
- url: string;
1624
- snippet: string;
1625
- }
1626
- /**
1627
- * The search provider {@link createWebSearchTool} calls.
1628
- *
1629
- * `maxResults` is what the caller asked for, not a limit you must enforce — the tool slices the array
1630
- * itself — but returning far more than asked wastes the round trip that produced them.
1631
- *
1632
- * Reject the promise to signal a failed search: the tool turns a rejection into
1633
- * `{ ok: false, error: "search_failed" }` and never lets it escape into the agent turn. Resolving
1634
- * with `[]` says something different — that the search ran and found nothing.
1635
- */
1636
- type WebSearchCallback = (query: string, maxResults: number) => Promise<WebSearchResult[]>;
1637
- /**
1638
- * Options for {@link createWebSearchTool}. `search` is required: the tool ships no provider of its
1639
- * own, and the two adapters in this package — {@link createBraveWebSearchAdapter} and
1640
- * {@link createGenericHttpSearchAdapter} — exist to fill it.
1641
- */
1642
- interface CreateWebSearchToolOptions {
1643
- /** M76 — name exposed to the model. Omitted => today's literal (additive). The name is a contract:
1644
- * the approval key, what the model sees and what telemetry records. */
1645
- name?: string;
1646
- /** M76 — description exposed to the model. Omitted => today's literal (additive). */
1647
- description?: string;
1648
- /** Search provider callback — consumer injects the implementation. */
1649
- search: WebSearchCallback;
1650
- /** Default max results if not specified by the LLM. */
1651
- defaultMaxResults?: number;
1652
- }
1653
- /**
1654
- * Build the `web_search` tool over a caller-supplied provider.
1655
- *
1656
- * The tool holds no API key and talks to no service; everything network-facing lives in the `search`
1657
- * callback, which is why one tool serves Brave, a self-hosted endpoint, or a fixture in a test. Use
1658
- * it to find pages and `web_fetch` to read one — results carry a snippet, not a body.
1659
- *
1660
- * The model's `max_results` (1..20) wins over `defaultMaxResults`, and the list is sliced to it even
1661
- * when the provider returns more. A rejecting provider yields `{ ok: false, error: "search_failed" }`
1662
- * with its message attached; a provider resolving `[]` yields `{ ok: true, results: [] }`. The two
1663
- * are different signals, so an adapter that swallows its own failures — as
1664
- * {@link createGenericHttpSearchAdapter} deliberately does — reports the first as the second.
1665
- */
1666
- declare function createWebSearchTool(opts: CreateWebSearchToolOptions): CustomTool;
1667
-
1668
1719
  /**
1669
1720
  * Brave Search adapter for `createWebSearchTool` (M3-7).
1670
1721
  *
@@ -1805,4 +1856,4 @@ interface CreateWriteFileToolOptions {
1805
1856
  */
1806
1857
  declare function createWriteFileTool(opts: CreateWriteFileToolOptions): CustomTool;
1807
1858
 
1808
- 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 };
1859
+ 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 CreateTextToSpeechToolOptions, type CreateViewImageToolOptions, type CreateWebFetchToolOptions, type CreateWriteFileToolOptions, DEFAULT_MAX_AUDIO_BYTES, DEFAULT_MAX_IMAGE_BYTES, DEFAULT_MAX_TEXT_CHARS, 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 SynthesizedAudio, type TextToSpeechCallback, type TextToSpeechResult, type TodoItem, type TodolistTool, type ToolGuidanceMap, type TruncationMode, type TruncationOptions, type TruncationResult, type VitestSummary, WebSearchCallback, buildEnvContext, buildRepoMap, catastrophicShellReason, commandDenialReason, createApplyPatchTool, createBraveWebSearchAdapter, createCurrentTimeTool, createEditFileTool, createGenericHttpSearchAdapter, createGitDiffTool, createGitStatusTool, createGlobTool, createInteractiveShellTool, createListDirTool, createPlanModeTool, createQuestionTool, createReadFileTool, createRunVitestTool, createSearchTextTool, createSessionArtifactStore, createShellTool, createTextToSpeechTool, createTodolistTool, createUpdatePlanTool, createViewImageTool, createWebFetchTool, 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
@@ -2,6 +2,8 @@ import { CustomTool, ConfigurationError } from '@theokit/sdk';
2
2
  import { FilesystemProvider } from '@theokit/sdk/filesystem';
3
3
  import { SandboxProvider } from '@theokit/sdk/sandbox';
4
4
  import { InteractiveProvider } from '@theokit/sdk/interactive';
5
+ import { W as WebSearchCallback } from './web-search-CE82zZVl.js';
6
+ export { C as CreateWebSearchToolOptions, a as WebSearchResult, c as createWebSearchTool } from './web-search-CE82zZVl.js';
5
7
 
6
8
  /**
7
9
  * `apply_patch` — built-in tool for coding agents.
@@ -1279,6 +1281,113 @@ interface CreateShellToolOptions {
1279
1281
  */
1280
1282
  declare function createShellTool(opts: CreateShellToolOptions): CustomTool;
1281
1283
 
1284
+ /**
1285
+ * `text_to_speech` — turns text into an audio file inside the project root.
1286
+ *
1287
+ * The audio comes from a `synthesize` callback the consumer injects (the `web_search` pattern): the
1288
+ * tool holds no key and opens no socket. The clip is written to `outputDir` below `projectRoot`
1289
+ * under the same confinement `view_image` and `read_file` carry, and the model receives the
1290
+ * relative path, the mime type and the byte count, never the audio itself: a model cannot listen
1291
+ * to it, and a base64 clip at the default ceiling would be about 13 MB of context text.
1292
+ *
1293
+ * Return shape (always a JSON string):
1294
+ * - `{ ok: true, path, mime_type, bytes }`
1295
+ * - `{ ok: false, error }` with `error` one of `path_traversal`, `text_too_large`,
1296
+ * `synthesis_failed`, `unsupported_audio_type`, `empty_audio`, `audio_too_large`, `write_failed`
1297
+ *
1298
+ * Nothing is written on any failure, and a refused path or an oversize text never reaches the
1299
+ * provider.
1300
+ *
1301
+ * Limits, accepted and stated: the confinement is checked before the write and again after the
1302
+ * output directory is created, but a symlink swapped in by another process between that check and
1303
+ * the rename is not caught (the same-user race the sibling tools leave open). A clip is written with
1304
+ * mode 0o600. A chosen `filename`, or a default stem two texts happen to share (32 bits of the
1305
+ * text's sha256), replaces the existing file of that name. `synthesis_failed` carries the provider's
1306
+ * own message on purpose: the provider is the consumer's code, and its words are what the model
1307
+ * needs to explain the failure; `write_failed` carries only the errno code, since a file-system
1308
+ * message names the host's absolute paths.
1309
+ */
1310
+
1311
+ /** Default ceiling on the text sent to the provider, in UTF-16 code units. A default, not a measured limit. */
1312
+ declare const DEFAULT_MAX_TEXT_CHARS = 4096;
1313
+ /** Default ceiling on the clip written to disk: 10 MiB, by analogy with `DEFAULT_MAX_IMAGE_BYTES`. */
1314
+ declare const DEFAULT_MAX_AUDIO_BYTES: number;
1315
+ /** What the provider returns: the encoded clip and its mime type. */
1316
+ interface SynthesizedAudio {
1317
+ audio: Uint8Array;
1318
+ mimeType: string;
1319
+ }
1320
+ /**
1321
+ * The speech provider {@link createTextToSpeechTool} calls.
1322
+ *
1323
+ * Reject the promise to signal a failed synthesis: the tool turns a rejection into
1324
+ * `{ ok: false, error: "synthesis_failed", message }` and never lets it escape into the agent turn.
1325
+ */
1326
+ type TextToSpeechCallback = (text: string) => Promise<SynthesizedAudio>;
1327
+ /** Options for {@link createTextToSpeechTool}. `synthesize` and `projectRoot` are required. */
1328
+ interface CreateTextToSpeechToolOptions {
1329
+ /** Speech provider callback — the consumer injects the implementation. */
1330
+ synthesize: TextToSpeechCallback;
1331
+ /** Root the tool writes under. Every path is resolved inside it. */
1332
+ projectRoot: string;
1333
+ /** Directory for the clips, relative to `projectRoot`. Omitted ⇒ `audio`. Created when absent. */
1334
+ outputDir?: string;
1335
+ /** Name exposed to the model. Omitted ⇒ `text_to_speech`. The name is a contract: the approval
1336
+ * key, what the model sees and what telemetry records. */
1337
+ name?: string;
1338
+ /** Description exposed to the model. Omitted ⇒ the literal below. */
1339
+ description?: string;
1340
+ /** Longest text sent to the provider. Omitted ⇒ {@link DEFAULT_MAX_TEXT_CHARS}. */
1341
+ maxTextChars?: number;
1342
+ /** Largest clip written. Omitted ⇒ {@link DEFAULT_MAX_AUDIO_BYTES}. A clip of exactly this size fits. */
1343
+ maxAudioBytes?: number;
1344
+ }
1345
+ type TextToSpeechFailure = {
1346
+ ok: false;
1347
+ error: "path_traversal";
1348
+ path: string;
1349
+ } | {
1350
+ ok: false;
1351
+ error: "text_too_large";
1352
+ chars: number;
1353
+ max_chars: number;
1354
+ } | {
1355
+ ok: false;
1356
+ error: "synthesis_failed";
1357
+ message: string;
1358
+ } | {
1359
+ ok: false;
1360
+ error: "unsupported_audio_type";
1361
+ mime_type: string;
1362
+ } | {
1363
+ ok: false;
1364
+ error: "empty_audio";
1365
+ } | {
1366
+ ok: false;
1367
+ error: "audio_too_large";
1368
+ bytes: number;
1369
+ max_bytes: number;
1370
+ } | {
1371
+ ok: false;
1372
+ error: "write_failed";
1373
+ message: string;
1374
+ };
1375
+ /** The tool's result, before it is serialised to the JSON string the handler returns. */
1376
+ type TextToSpeechResult = {
1377
+ ok: true;
1378
+ path: string;
1379
+ mime_type: string;
1380
+ bytes: number;
1381
+ } | TextToSpeechFailure;
1382
+ /**
1383
+ * Build the `text_to_speech` tool over a caller-supplied provider.
1384
+ *
1385
+ * Order of checks: the text length, then the path (stem and output directory), then the provider,
1386
+ * then the mime table, the empty and size checks, then the write. A refused path or an oversize
1387
+ * text never reaches the provider, and no failure leaves a file behind.
1388
+ */
1389
+ declare function createTextToSpeechTool(opts: CreateTextToSpeechToolOptions): CustomTool;
1390
+
1282
1391
  /**
1283
1392
  * `todolist` — in-session task tracking for multi-step work.
1284
1393
  *
@@ -1607,64 +1716,6 @@ interface CreateWebFetchToolOptions {
1607
1716
  */
1608
1717
  declare function createWebFetchTool(opts?: CreateWebFetchToolOptions): CustomTool;
1609
1718
 
1610
- /**
1611
- * `web_search` — built-in tool for coding agents.
1612
- *
1613
- * Accepts a search callback via DIP (consumer provides the search
1614
- * provider). The tool itself is provider-agnostic.
1615
- *
1616
- * Return shape (always a JSON string):
1617
- * - `{ ok: true, results: Array<{ title, url, snippet }> }`
1618
- * - `{ ok: false, error: 'search_failed' | 'no_provider' }`
1619
- */
1620
-
1621
- interface WebSearchResult {
1622
- title: string;
1623
- url: string;
1624
- snippet: string;
1625
- }
1626
- /**
1627
- * The search provider {@link createWebSearchTool} calls.
1628
- *
1629
- * `maxResults` is what the caller asked for, not a limit you must enforce — the tool slices the array
1630
- * itself — but returning far more than asked wastes the round trip that produced them.
1631
- *
1632
- * Reject the promise to signal a failed search: the tool turns a rejection into
1633
- * `{ ok: false, error: "search_failed" }` and never lets it escape into the agent turn. Resolving
1634
- * with `[]` says something different — that the search ran and found nothing.
1635
- */
1636
- type WebSearchCallback = (query: string, maxResults: number) => Promise<WebSearchResult[]>;
1637
- /**
1638
- * Options for {@link createWebSearchTool}. `search` is required: the tool ships no provider of its
1639
- * own, and the two adapters in this package — {@link createBraveWebSearchAdapter} and
1640
- * {@link createGenericHttpSearchAdapter} — exist to fill it.
1641
- */
1642
- interface CreateWebSearchToolOptions {
1643
- /** M76 — name exposed to the model. Omitted => today's literal (additive). The name is a contract:
1644
- * the approval key, what the model sees and what telemetry records. */
1645
- name?: string;
1646
- /** M76 — description exposed to the model. Omitted => today's literal (additive). */
1647
- description?: string;
1648
- /** Search provider callback — consumer injects the implementation. */
1649
- search: WebSearchCallback;
1650
- /** Default max results if not specified by the LLM. */
1651
- defaultMaxResults?: number;
1652
- }
1653
- /**
1654
- * Build the `web_search` tool over a caller-supplied provider.
1655
- *
1656
- * The tool holds no API key and talks to no service; everything network-facing lives in the `search`
1657
- * callback, which is why one tool serves Brave, a self-hosted endpoint, or a fixture in a test. Use
1658
- * it to find pages and `web_fetch` to read one — results carry a snippet, not a body.
1659
- *
1660
- * The model's `max_results` (1..20) wins over `defaultMaxResults`, and the list is sliced to it even
1661
- * when the provider returns more. A rejecting provider yields `{ ok: false, error: "search_failed" }`
1662
- * with its message attached; a provider resolving `[]` yields `{ ok: true, results: [] }`. The two
1663
- * are different signals, so an adapter that swallows its own failures — as
1664
- * {@link createGenericHttpSearchAdapter} deliberately does — reports the first as the second.
1665
- */
1666
- declare function createWebSearchTool(opts: CreateWebSearchToolOptions): CustomTool;
1667
-
1668
1719
  /**
1669
1720
  * Brave Search adapter for `createWebSearchTool` (M3-7).
1670
1721
  *
@@ -1805,4 +1856,4 @@ interface CreateWriteFileToolOptions {
1805
1856
  */
1806
1857
  declare function createWriteFileTool(opts: CreateWriteFileToolOptions): CustomTool;
1807
1858
 
1808
- 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 };
1859
+ 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 CreateTextToSpeechToolOptions, type CreateViewImageToolOptions, type CreateWebFetchToolOptions, type CreateWriteFileToolOptions, DEFAULT_MAX_AUDIO_BYTES, DEFAULT_MAX_IMAGE_BYTES, DEFAULT_MAX_TEXT_CHARS, 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 SynthesizedAudio, type TextToSpeechCallback, type TextToSpeechResult, type TodoItem, type TodolistTool, type ToolGuidanceMap, type TruncationMode, type TruncationOptions, type TruncationResult, type VitestSummary, WebSearchCallback, buildEnvContext, buildRepoMap, catastrophicShellReason, commandDenialReason, createApplyPatchTool, createBraveWebSearchAdapter, createCurrentTimeTool, createEditFileTool, createGenericHttpSearchAdapter, createGitDiffTool, createGitStatusTool, createGlobTool, createInteractiveShellTool, createListDirTool, createPlanModeTool, createQuestionTool, createReadFileTool, createRunVitestTool, createSearchTextTool, createSessionArtifactStore, createShellTool, createTextToSpeechTool, createTodolistTool, createUpdatePlanTool, createViewImageTool, createWebFetchTool, 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
- import { rm, mkdir, writeFile, readFile, readdir, open, stat } from 'fs/promises';
2
- import { dirname, relative, join, isAbsolute, extname } from 'path';
1
+ import { rm, mkdir, writeFile, readFile, readdir, open, stat, rename } from 'fs/promises';
2
+ import { dirname, relative, join, isAbsolute, posix, extname, basename } 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';
@@ -11,7 +11,7 @@ import { spawn } from 'child_process';
11
11
  import { resolveInteractive, InteractiveUnavailableError, NoSuchSessionError } from '@theokit/sdk/interactive';
12
12
  import { lookup } from 'dns/promises';
13
13
  import { isIP } from 'net';
14
- import { randomUUID } from 'crypto';
14
+ import { randomUUID, createHash, randomBytes } from 'crypto';
15
15
 
16
16
  // src/apply-patch.ts
17
17
 
@@ -2459,6 +2459,123 @@ function formatResult(result, timeoutMs) {
2459
2459
  exit_code: result.exitCode
2460
2460
  });
2461
2461
  }
2462
+ var AUDIO_EXTENSIONS = /* @__PURE__ */ new Map([
2463
+ ["audio/mpeg", "mp3"],
2464
+ ["audio/wav", "wav"],
2465
+ ["audio/x-wav", "wav"],
2466
+ ["audio/ogg", "ogg"],
2467
+ ["audio/opus", "opus"],
2468
+ ["audio/flac", "flac"],
2469
+ ["audio/aac", "aac"],
2470
+ ["audio/webm", "webm"]
2471
+ ]);
2472
+ var DEFAULT_MAX_TEXT_CHARS = 4096;
2473
+ var DEFAULT_MAX_AUDIO_BYTES = 10 * 1024 * 1024;
2474
+ var STEM_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
2475
+ var json = (result) => JSON.stringify(result);
2476
+ var errorMessage = (err) => err instanceof Error ? err.message : String(err);
2477
+ var errorCode = (err) => err.code ?? "unknown";
2478
+ function requirePositiveInteger(name, value) {
2479
+ if (!Number.isInteger(value) || value <= 0) {
2480
+ throw new RangeError(
2481
+ `createTextToSpeechTool: ${name} must be a positive integer, got ${value}`
2482
+ );
2483
+ }
2484
+ return value;
2485
+ }
2486
+ function defaultStem(text) {
2487
+ return `speech-${createHash("sha256").update(text).digest("hex").slice(0, 8)}`;
2488
+ }
2489
+ function refusePath(relativeStem, stem, projectRoot) {
2490
+ if (!STEM_PATTERN.test(stem) || isForbiddenAtAnyDepth(relativeStem)) {
2491
+ return json({ ok: false, error: "path_traversal", path: relativeStem });
2492
+ }
2493
+ return checkPathScope(relativeStem, projectRoot);
2494
+ }
2495
+ async function synthesizeSafely(synthesize, text) {
2496
+ try {
2497
+ const value = await synthesize(text);
2498
+ if (!(value?.audio instanceof Uint8Array) || typeof value.mimeType !== "string") {
2499
+ return { ok: false, error: "synthesis_failed", message: "provider returned no audio" };
2500
+ }
2501
+ return { audio: value.audio, mimeType: value.mimeType };
2502
+ } catch (err) {
2503
+ return { ok: false, error: "synthesis_failed", message: errorMessage(err) };
2504
+ }
2505
+ }
2506
+ function checkAudio({ audio, mimeType }, maxAudioBytes) {
2507
+ const extension = AUDIO_EXTENSIONS.get(mimeType);
2508
+ if (extension === void 0)
2509
+ return { ok: false, error: "unsupported_audio_type", mime_type: mimeType };
2510
+ if (audio.length === 0) return { ok: false, error: "empty_audio" };
2511
+ if (audio.length > maxAudioBytes) {
2512
+ return { ok: false, error: "audio_too_large", bytes: audio.length, max_bytes: maxAudioBytes };
2513
+ }
2514
+ return { extension };
2515
+ }
2516
+ async function writeAtomic(target, audio) {
2517
+ const temp = `${dirname(target)}/.${basename(target)}.${randomBytes(6).toString("hex")}.tmp`;
2518
+ try {
2519
+ await writeFile(temp, audio, { flag: "wx", mode: 384 });
2520
+ await rename(temp, target);
2521
+ } catch (err) {
2522
+ await rm(temp, { force: true });
2523
+ throw err;
2524
+ }
2525
+ }
2526
+ async function saveClip(projectRoot, outputDir, relativePath2, { audio, mimeType }) {
2527
+ const target = safePathJoin(projectRoot, relativePath2);
2528
+ try {
2529
+ await mkdir(dirname(target), { recursive: true });
2530
+ const refused = checkPathScope(outputDir, projectRoot);
2531
+ if (refused !== null) return refused;
2532
+ await writeAtomic(target, audio);
2533
+ return json({ ok: true, path: relativePath2, mime_type: mimeType, bytes: audio.length });
2534
+ } catch (err) {
2535
+ return json({ ok: false, error: "write_failed", message: errorCode(err) });
2536
+ }
2537
+ }
2538
+ function createTextToSpeechTool(opts) {
2539
+ const { synthesize, projectRoot } = opts;
2540
+ const outputDir = posix.normalize((opts.outputDir ?? "audio").replace(/\\/g, "/"));
2541
+ const maxTextChars = requirePositiveInteger(
2542
+ "maxTextChars",
2543
+ opts.maxTextChars ?? DEFAULT_MAX_TEXT_CHARS
2544
+ );
2545
+ const maxAudioBytes = requirePositiveInteger(
2546
+ "maxAudioBytes",
2547
+ opts.maxAudioBytes ?? DEFAULT_MAX_AUDIO_BYTES
2548
+ );
2549
+ return Tool.create({
2550
+ name: opts.name ?? "text_to_speech",
2551
+ description: opts.description ?? "Turn text into spoken audio and save it as a file in the project. Use when the user asks for speech, a voice reply or an audio clip. Returns { ok, path, mime_type, bytes } with the path relative to the project root, or { ok: false, error }. The speech provider is injected by the consumer.",
2552
+ inputSchema: z.object({
2553
+ text: z.string().min(1).describe("The text to speak."),
2554
+ filename: z.string().optional().describe(
2555
+ "File name without extension (letters, digits, '.', '_', '-'). Default: derived from the text."
2556
+ )
2557
+ }),
2558
+ handler: async ({ text, filename }) => {
2559
+ if (text.length > maxTextChars) {
2560
+ return json({
2561
+ ok: false,
2562
+ error: "text_too_large",
2563
+ chars: text.length,
2564
+ max_chars: maxTextChars
2565
+ });
2566
+ }
2567
+ const stem = filename ?? defaultStem(text);
2568
+ const refused = refusePath(posix.join(outputDir, stem), stem, projectRoot);
2569
+ if (refused !== null) return refused;
2570
+ const synthesized = await synthesizeSafely(synthesize, text);
2571
+ if ("ok" in synthesized) return json(synthesized);
2572
+ const checked = checkAudio(synthesized, maxAudioBytes);
2573
+ if ("ok" in checked) return json(checked);
2574
+ const relativePath2 = posix.join(outputDir, `${stem}.${checked.extension}`);
2575
+ return saveClip(projectRoot, outputDir, relativePath2, synthesized);
2576
+ }
2577
+ });
2578
+ }
2462
2579
 
2463
2580
  // src/todo-plan-nodes.ts
2464
2581
  function todoItemsToPlanNodes(items) {
@@ -2661,7 +2778,7 @@ var MEDIA_TYPES = /* @__PURE__ */ new Map([
2661
2778
  [".webp", "image/webp"]
2662
2779
  ]);
2663
2780
  var DEFAULT_MAX_IMAGE_BYTES = 5 * 1024 * 1024;
2664
- var json = (result) => JSON.stringify(result);
2781
+ var json2 = (result) => JSON.stringify(result);
2665
2782
  function createViewImageTool(options) {
2666
2783
  const { projectRoot } = options;
2667
2784
  const maxBytes = options.maxBytes ?? DEFAULT_MAX_IMAGE_BYTES;
@@ -2675,11 +2792,11 @@ function createViewImageTool(options) {
2675
2792
  const refused = checkPathScope(input.path, projectRoot);
2676
2793
  if (refused !== null) return refused;
2677
2794
  if (isForbiddenAtAnyDepth(input.path)) {
2678
- return json({ ok: false, error: "path_traversal", path: input.path });
2795
+ return json2({ ok: false, error: "path_traversal", path: input.path });
2679
2796
  }
2680
2797
  const mediaType = MEDIA_TYPES.get(extname(input.path).toLowerCase());
2681
2798
  if (mediaType === void 0) {
2682
- return json({
2799
+ return json2({
2683
2800
  ok: false,
2684
2801
  error: "unsupported_image_type",
2685
2802
  path: input.path,
@@ -2688,8 +2805,8 @@ function createViewImageTool(options) {
2688
2805
  }
2689
2806
  const absolute = safePathJoin(projectRoot, input.path);
2690
2807
  const outcome = readWithinBudget(absolute, maxBytes);
2691
- if (!outcome.ok) return json(failureFor(outcome, input.path, maxBytes));
2692
- return json({
2808
+ if (!outcome.ok) return json2(failureFor(outcome, input.path, maxBytes));
2809
+ return json2({
2693
2810
  ok: true,
2694
2811
  path: input.path,
2695
2812
  media_type: mediaType,
@@ -2886,8 +3003,8 @@ function createBraveWebSearchAdapter(opts = {}) {
2886
3003
  headers: { "X-Subscription-Token": apiKey, Accept: "application/json" }
2887
3004
  });
2888
3005
  if (!res.ok) throw new Error(`brave_search_failed: HTTP ${res.status}`);
2889
- const json2 = await res.json();
2890
- const results = json2?.web?.results ?? [];
3006
+ const json3 = await res.json();
3007
+ const results = json3?.web?.results ?? [];
2891
3008
  return results.map((r) => ({
2892
3009
  title: String(r?.title ?? ""),
2893
3010
  url: String(r?.url ?? ""),
@@ -3047,6 +3164,6 @@ async function isBinaryFile(absolutePath) {
3047
3164
  }
3048
3165
  }
3049
3166
 
3050
- 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 };
3167
+ export { CatastrophicCommandError, ContextMatchError, DEFAULT_MAX_AUDIO_BYTES, DEFAULT_MAX_IMAGE_BYTES, DEFAULT_MAX_TEXT_CHARS, 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, createTextToSpeechTool, 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 };
3051
3168
  //# sourceMappingURL=index.js.map
3052
3169
  //# sourceMappingURL=index.js.map