indusagi 0.12.33 → 0.13.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 (70) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/dist/agent.js +1247 -184
  3. package/dist/ai.js +72 -4
  4. package/dist/capabilities.js +69 -2
  5. package/dist/cli.js +1353 -29
  6. package/dist/connectors-saas.js +66 -0
  7. package/dist/index.js +1353 -29
  8. package/dist/interop.js +66 -0
  9. package/dist/mcp.js +270 -363
  10. package/dist/react-ink.js +1391 -41
  11. package/dist/shell-app.js +1353 -29
  12. package/dist/smithy.js +69 -2
  13. package/dist/swarm.js +69 -2
  14. package/dist/types/capabilities/backends/node-backends.d.ts +3 -1
  15. package/dist/types/capabilities/files/read-state-gate.d.ts +69 -0
  16. package/dist/types/capabilities/files/read-state-gate.test.d.ts +14 -0
  17. package/dist/types/capabilities/kernel/context.d.ts +4 -0
  18. package/dist/types/capabilities/kernel/index.d.ts +2 -2
  19. package/dist/types/capabilities/kernel/spec.d.ts +55 -0
  20. package/dist/types/facade/bot/actions/bash.d.ts +15 -0
  21. package/dist/types/facade/bot/actions/bash.test.d.ts +1 -0
  22. package/dist/types/facade/bot/actions/checkpoint.d.ts +49 -0
  23. package/dist/types/facade/bot/actions/checkpoint.test.d.ts +1 -0
  24. package/dist/types/facade/bot/actions/edit-utils.d.ts +86 -0
  25. package/dist/types/facade/bot/actions/edit.d.ts +18 -0
  26. package/dist/types/facade/bot/actions/edit.test.d.ts +1 -0
  27. package/dist/types/facade/bot/actions/find.d.ts +2 -0
  28. package/dist/types/facade/bot/actions/find.test.d.ts +1 -0
  29. package/dist/types/facade/bot/actions/grep.d.ts +10 -0
  30. package/dist/types/facade/bot/actions/grep.test.d.ts +1 -0
  31. package/dist/types/facade/bot/actions/index.d.ts +16 -0
  32. package/dist/types/facade/bot/actions/read-state.d.ts +83 -0
  33. package/dist/types/facade/bot/actions/read-state.test.d.ts +1 -0
  34. package/dist/types/facade/bot/actions/read.d.ts +7 -0
  35. package/dist/types/facade/bot/actions/read.test.d.ts +1 -0
  36. package/dist/types/facade/bot/actions/sandbox-backend.d.ts +99 -0
  37. package/dist/types/facade/bot/actions/sandbox-backend.test.d.ts +1 -0
  38. package/dist/types/facade/bot/actions/websearch.d.ts +5 -2
  39. package/dist/types/facade/bot/actions/websearch.test.d.ts +1 -0
  40. package/dist/types/facade/bot/actions/write.d.ts +15 -0
  41. package/dist/types/facade/bot/agent-loop.d.ts +10 -0
  42. package/dist/types/facade/bot/agent-loop.test.d.ts +1 -0
  43. package/dist/types/facade/bot/agent.d.ts +9 -1
  44. package/dist/types/facade/bot/permission-gate.test.d.ts +1 -0
  45. package/dist/types/facade/bot/types.d.ts +60 -0
  46. package/dist/types/facade/mcp-core/client.d.ts +71 -15
  47. package/dist/types/facade/mcp-core/client.test.d.ts +18 -0
  48. package/dist/types/facade/mcp-core/types.d.ts +10 -0
  49. package/dist/types/facade/ml/adapters/anthropic-retry.test.d.ts +1 -0
  50. package/dist/types/facade/ml/adapters/anthropic.d.ts +17 -0
  51. package/dist/types/facade/ml/adapters/simple-options.d.ts +13 -0
  52. package/dist/types/facade/ml/adapters/simple-options.test.d.ts +1 -0
  53. package/dist/types/react-ink/components/StatusLine.d.ts +10 -1
  54. package/dist/types/react-ink/components/ToolEventBlock.d.ts +9 -1
  55. package/dist/types/react-ink/components/ToolEventBlock.test.d.ts +1 -0
  56. package/dist/types/react-ink/components/dialogs/SelectableDialog.d.ts +7 -1
  57. package/dist/types/react-ink/components/dialogs/ThemeDialog.d.ts +21 -2
  58. package/dist/types/react-ink/diff/Diff.d.ts +22 -0
  59. package/dist/types/react-ink/diff/diff.test.d.ts +1 -0
  60. package/dist/types/react-ink/diff/structured.d.ts +41 -0
  61. package/dist/types/react-ink/diff/word-diff.d.ts +27 -0
  62. package/dist/types/react-ink/index.d.ts +8 -0
  63. package/dist/types/react-ink/markdown/Markdown.d.ts +23 -0
  64. package/dist/types/react-ink/markdown/MarkdownTable.d.ts +19 -0
  65. package/dist/types/react-ink/markdown/StreamingMarkdown.d.ts +34 -0
  66. package/dist/types/react-ink/markdown/format-token.d.ts +39 -0
  67. package/dist/types/react-ink/markdown/highlight.d.ts +31 -0
  68. package/dist/types/react-ink/theme-adapter.d.ts +58 -1
  69. package/dist/types/react-ink/utils/tool-display.d.ts +17 -1
  70. package/package.json +5 -1
@@ -1,5 +1,7 @@
1
1
  import type { AgentTool } from "../types.js";
2
2
  import { fuzzyFindText } from "./edit-diff.js";
3
+ import { type CheckpointHandle } from "./checkpoint.js";
4
+ import { type ReadStateHandle } from "./read-state.js";
3
5
  export interface MatchingStrategy {
4
6
  find(content: string, oldText: string): ReturnType<typeof fuzzyFindText>;
5
7
  }
@@ -7,6 +9,7 @@ declare const editSchema: import("@sinclair/typebox").TObject<{
7
9
  path: import("@sinclair/typebox").TString;
8
10
  oldText: import("@sinclair/typebox").TString;
9
11
  newText: import("@sinclair/typebox").TString;
12
+ replaceAll: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
10
13
  }>;
11
14
  export interface EditToolDetails {
12
15
  /** Formatted diff showing what changed. */
@@ -30,6 +33,20 @@ export interface EditToolOptions {
30
33
  /** Swap out the filesystem callbacks; the local disk is used by default. */
31
34
  operations?: EditOperations;
32
35
  matchingStrategy?: MatchingStrategy;
36
+ /**
37
+ * Optional read-before-edit gate store. When present, the edit is refused
38
+ * unless the file was read this session and has not drifted on disk since.
39
+ * Absent → no gate (no-op), preserving the prior behaviour exactly.
40
+ */
41
+ readState?: ReadStateHandle;
42
+ /**
43
+ * Optional file-checkpoint sink. When present, the file's ORIGINAL
44
+ * pre-edit on-disk content is captured exactly once before the edit is
45
+ * applied, so a later rewind can roll the working tree back. The edit tool
46
+ * only ever targets an existing file, so the snapshot is its old bytes (never
47
+ * `null`). Absent → no snapshot (no-op).
48
+ */
49
+ checkpoint?: CheckpointHandle;
33
50
  }
34
51
  export declare function createEditTool(cwd: string, options?: EditToolOptions): AgentTool<typeof editSchema>;
35
52
  /** Ready-to-use edit tool bound to the current working directory. */
@@ -37,5 +54,6 @@ export declare const editTool: AgentTool<import("@sinclair/typebox").TObject<{
37
54
  path: import("@sinclair/typebox").TString;
38
55
  oldText: import("@sinclair/typebox").TString;
39
56
  newText: import("@sinclair/typebox").TString;
57
+ replaceAll: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
40
58
  }>, any>;
41
59
  export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -3,6 +3,7 @@ import { type TruncationResult } from "./truncate.js";
3
3
  declare const findSchema: import("@sinclair/typebox").TObject<{
4
4
  pattern: import("@sinclair/typebox").TString;
5
5
  path: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
6
+ type: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
6
7
  limit: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
7
8
  }>;
8
9
  export interface FindToolDetails {
@@ -20,6 +21,7 @@ export declare function createFindTool(cwd: string, options?: FindToolOptions):
20
21
  export declare const findTool: AgentTool<import("@sinclair/typebox").TObject<{
21
22
  pattern: import("@sinclair/typebox").TString;
22
23
  path: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
24
+ type: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
23
25
  limit: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
24
26
  }>, any>;
25
27
  export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -6,6 +6,11 @@ declare const grepSchema: import("@sinclair/typebox").TObject<{
6
6
  ignoreCase: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
7
7
  literal: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
8
8
  context: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
9
+ before: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
10
+ after: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
11
+ output_mode: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"content">, import("@sinclair/typebox").TLiteral<"files_with_matches">, import("@sinclair/typebox").TLiteral<"count">]>>;
12
+ glob: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
13
+ type: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
9
14
  limit: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
10
15
  }>;
11
16
  export interface GrepToolDetails {
@@ -38,6 +43,11 @@ export declare const grepTool: AgentTool<import("@sinclair/typebox").TObject<{
38
43
  ignoreCase: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
39
44
  literal: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
40
45
  context: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
46
+ before: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
47
+ after: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
48
+ output_mode: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"content">, import("@sinclair/typebox").TLiteral<"files_with_matches">, import("@sinclair/typebox").TLiteral<"count">]>>;
49
+ glob: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
50
+ type: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
41
51
  limit: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
42
52
  }>, any>;
43
53
  export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -3,6 +3,7 @@ export { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, formatSize, truncateHead, truncat
3
3
  export { expandPath, resolveReadPath, resolveToCwd } from "./path-utils.js";
4
4
  export { createReadTool, readTool, type ReadOperations, type ReadToolOptions, type ReadToolDetails, } from "./read.js";
5
5
  export { createBashTool, bashTool, type BashOperations, type BashToolOptions, type BashToolDetails, } from "./bash.js";
6
+ export { buildSeatbeltProfile, buildSeatbeltArgv, buildBwrapArgv, buildSandboxArgv, argvToCommandString, sandboxAvailability, createSandboxedBashOperations, type SandboxConfig, type SandboxAvailability, type SandboxedOperationsOptions, } from "./sandbox-backend.js";
6
7
  export { createEditTool, editTool, type EditOperations, type EditToolOptions, type EditToolDetails, } from "./edit.js";
7
8
  export { createWriteTool, writeTool, type WriteOperations, type WriteToolOptions, } from "./write.js";
8
9
  export { createGrepTool, grepTool, type GrepOperations, type GrepToolOptions, type GrepToolDetails, } from "./grep.js";
@@ -15,6 +16,7 @@ export { createWebSearchTool, webSearchTool, type WebSearchToolOptions, type Web
15
16
  export { createWebFetchTool, webFetchTool, type WebFetchToolOptions, type WebFetchToolDetails, } from "./webfetch.js";
16
17
  export * from "./composio/index.js";
17
18
  export { computeEditDiff, generateDiffString } from "./edit-diff.js";
19
+ export { DESANITIZATIONS, desanitizeMatchString, FILE_NOT_FOUND_CWD_NOTE, findSimilarFile, preserveQuoteStyle, replaceAllLiteral, suggestPathUnderCwd, } from "./edit-utils.js";
18
20
  export { ToolFactory, ToolRegistry, type ToolMetadata, type ToolCategory } from "./registry.js";
19
21
  export * from "./crew/index.js";
20
22
  export declare const TOOL_METADATA: Record<string, ToolMetadata>;
@@ -26,6 +28,7 @@ export declare const codingTools: (import("../types.js").AgentTool<import("@sinc
26
28
  path: import("@sinclair/typebox").TString;
27
29
  oldText: import("@sinclair/typebox").TString;
28
30
  newText: import("@sinclair/typebox").TString;
31
+ replaceAll: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
29
32
  }>, any> | import("../types.js").AgentTool<import("@sinclair/typebox").TObject<{
30
33
  search: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
31
34
  toolkits: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>>;
@@ -101,6 +104,7 @@ export declare const codingTools: (import("../types.js").AgentTool<import("@sinc
101
104
  export declare const readOnlyTools: (import("../types.js").AgentTool<import("@sinclair/typebox").TObject<{
102
105
  pattern: import("@sinclair/typebox").TString;
103
106
  path: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
107
+ type: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
104
108
  limit: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
105
109
  }>, any> | import("../types.js").AgentTool<import("@sinclair/typebox").TObject<{
106
110
  pattern: import("@sinclair/typebox").TString;
@@ -108,6 +112,11 @@ export declare const readOnlyTools: (import("../types.js").AgentTool<import("@si
108
112
  ignoreCase: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
109
113
  literal: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
110
114
  context: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
115
+ before: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
116
+ after: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
117
+ output_mode: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"content">, import("@sinclair/typebox").TLiteral<"files_with_matches">, import("@sinclair/typebox").TLiteral<"count">]>>;
118
+ glob: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
119
+ type: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
111
120
  limit: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
112
121
  }>, any> | import("../types.js").AgentTool<import("@sinclair/typebox").TObject<{
113
122
  path: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
@@ -162,6 +171,7 @@ export declare const allTools: {
162
171
  path: import("@sinclair/typebox").TString;
163
172
  oldText: import("@sinclair/typebox").TString;
164
173
  newText: import("@sinclair/typebox").TString;
174
+ replaceAll: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
165
175
  }>, any>;
166
176
  readonly write: import("../types.js").AgentTool<import("@sinclair/typebox").TObject<{
167
177
  path: import("@sinclair/typebox").TString;
@@ -173,11 +183,17 @@ export declare const allTools: {
173
183
  ignoreCase: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
174
184
  literal: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
175
185
  context: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
186
+ before: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
187
+ after: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
188
+ output_mode: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"content">, import("@sinclair/typebox").TLiteral<"files_with_matches">, import("@sinclair/typebox").TLiteral<"count">]>>;
189
+ glob: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
190
+ type: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
176
191
  limit: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
177
192
  }>, any>;
178
193
  readonly find: import("../types.js").AgentTool<import("@sinclair/typebox").TObject<{
179
194
  pattern: import("@sinclair/typebox").TString;
180
195
  path: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
196
+ type: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
181
197
  limit: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
182
198
  }>, any>;
183
199
  readonly ls: import("../types.js").AgentTool<import("@sinclair/typebox").TObject<{
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Read-before-edit gate for the WIRED facade tool layer.
3
+ *
4
+ * This is the product-side half of the read-edit-gate feature. The framework's
5
+ * `capabilities/files/read-state-gate.ts` carries the same discipline, but the
6
+ * product wires the `read` / `edit` / `write` factories exported from
7
+ * `indusagi/agent` (i.e. these `facade/bot/actions/` factories), so the gate had
8
+ * to live here too to actually take effect.
9
+ *
10
+ * When — and ONLY when — a host passes a {@link ReadStateHandle} via the tool's
11
+ * options bag, the file tools enforce a small discipline borrowed from
12
+ * interactive coding agents:
13
+ *
14
+ * 1. A file may not be edited or overwritten until it has been *read* in this
15
+ * session (so the model is never blindly clobbering content it has not
16
+ * seen). Brand-new files (those that do not yet exist on disk) are exempt
17
+ * from the write tool's gate.
18
+ * 2. A file may not be mutated if it has drifted on disk since that read — its
19
+ * modification time or byte size advanced past what was recorded — because
20
+ * that usually means a human or a linter changed it underneath us.
21
+ *
22
+ * After every successful read (and every successful mutation) the recorded state
23
+ * is refreshed from the fresh on-disk stat, so the next gate check compares
24
+ * against the latest known-good snapshot.
25
+ *
26
+ * The whole mechanism is *additive*: with no handle present, every helper here
27
+ * is a no-op and the tools behave exactly as they did before. The handle is
28
+ * duck-typed so the host and these factories agree on shape without a
29
+ * cross-package type import.
30
+ */
31
+ /** A single recorded read: the on-disk shape captured at read time. */
32
+ export interface ReadStateRecord {
33
+ /** File modification time in ms, floored to a whole millisecond. */
34
+ mtimeMs: number;
35
+ /** File byte size at read time. */
36
+ size: number;
37
+ /** Optional content hash; unused by the default gate but reserved for hosts. */
38
+ contentHash?: string;
39
+ /** When the read was recorded; the gate mirrors this to `mtimeMs`. */
40
+ readAt: number;
41
+ }
42
+ /**
43
+ * Duck-typed store the host injects to drive the gate. Any object exposing this
44
+ * shape (e.g. a `Map`-backed wrapper) works; nothing is imported across packages.
45
+ */
46
+ export interface ReadStateHandle {
47
+ get(path: string): ReadStateRecord | undefined;
48
+ set(path: string, rec: ReadStateRecord): void;
49
+ has(path: string): boolean;
50
+ }
51
+ /** The two byte-stable refusal messages the gate emits. */
52
+ export declare const READ_BEFORE_EDIT_MESSAGE = "File has not been read yet. Read it first before writing to it.";
53
+ export declare const MODIFIED_SINCE_READ_MESSAGE = "File has been modified since read, either by the user or by a linter. Read it again before attempting to write it.";
54
+ /**
55
+ * Record (or refresh) the read state for `absPath` from a fresh stat.
56
+ *
57
+ * No-op when no handle is present. The mtime is floored to a whole millisecond
58
+ * so a sub-ms clock skew between read and a later compare can never spuriously
59
+ * trip the staleness check. `readAt` mirrors the floored mtime — no wall-clock
60
+ * call is made. A failed stat is swallowed: state-keeping is best-effort and
61
+ * must never mask the real operation's outcome.
62
+ */
63
+ export declare function recordReadState(handle: ReadStateHandle | undefined, absPath: string): void;
64
+ /** Outcome of a gate check: either cleared, or refused with a message. */
65
+ export type GateOutcome = {
66
+ ok: true;
67
+ } | {
68
+ ok: false;
69
+ message: string;
70
+ };
71
+ /**
72
+ * Enforce the read-before-edit + staleness gate ahead of a mutation.
73
+ *
74
+ * With no handle present this always clears (the gate is opt-in). With a handle:
75
+ * - refuse when `absPath` has no recorded read this session;
76
+ * - refuse when the on-disk modification time or byte size has advanced past
77
+ * the recorded read.
78
+ *
79
+ * The on-disk mtime is floored the same way the recorded mtime is, so the
80
+ * comparison is apples-to-apples. A stat failure (e.g. the file vanished) clears
81
+ * the gate so the underlying tool can surface its own, more specific error.
82
+ */
83
+ export declare function enforceReadGate(handle: ReadStateHandle | undefined, absPath: string): GateOutcome;
@@ -0,0 +1 @@
1
+ export {};
@@ -1,4 +1,5 @@
1
1
  import type { AgentTool } from "../types.js";
2
+ import { type ReadStateHandle } from "./read-state.js";
2
3
  import { type TruncationResult } from "./truncate.js";
3
4
  declare const readSchema: import("@sinclair/typebox").TObject<{
4
5
  path: import("@sinclair/typebox").TString;
@@ -25,6 +26,12 @@ export interface ReadToolOptions {
25
26
  autoResizeImages?: boolean;
26
27
  /** Swap out the filesystem callbacks; the local disk is used by default. */
27
28
  operations?: ReadOperations;
29
+ /**
30
+ * Optional read-before-edit gate store. When present, a successful read
31
+ * records the file's on-disk stat so a later edit/write can verify it was
32
+ * read first and has not drifted. Absent → no gate bookkeeping (no-op).
33
+ */
34
+ readState?: ReadStateHandle;
28
35
  }
29
36
  export declare function createReadTool(cwd: string, options?: ReadToolOptions): AgentTool<typeof readSchema>;
30
37
  /** Ready-to-use read tool bound to the current working directory. */
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,99 @@
1
+ import type { BashOperations } from "./bash.js";
2
+ /** Configuration for the OPT-IN OS sandbox applied to bash command execution. */
3
+ export interface SandboxConfig {
4
+ /** Master switch. When false/undefined the sandbox seam is a pure no-op. */
5
+ enabled?: boolean;
6
+ /**
7
+ * When true, the sandbox permits outbound network access. When false (the
8
+ * default) all network traffic is denied at the OS level.
9
+ */
10
+ allowNetwork?: boolean;
11
+ /**
12
+ * Extra absolute paths the command is allowed to write to, in addition to the
13
+ * working directory and the OS temp directory which are always writable.
14
+ */
15
+ writableRoots?: string[];
16
+ /**
17
+ * Override for the temp directory treated as writable. Defaults to the OS
18
+ * temp directory. Exposed mainly so tests can pin a deterministic value.
19
+ */
20
+ tmpDir?: string;
21
+ }
22
+ /** A platform/availability probe result for the OS sandbox tooling. */
23
+ export interface SandboxAvailability {
24
+ platform: "macos" | "linux" | "unsupported";
25
+ /** The sandbox launcher binary for this platform, if one exists. */
26
+ binary: "sandbox-exec" | "bwrap" | null;
27
+ /** True only when the launcher binary is actually present on PATH. */
28
+ binaryPresent: boolean;
29
+ /** Human-readable explanation when the sandbox cannot be applied. */
30
+ reason?: string;
31
+ }
32
+ /**
33
+ * Build a Seatbelt profile string for `sandbox-exec -p`.
34
+ *
35
+ * Policy: deny by default, allow process exec + read everywhere, allow write
36
+ * only under the resolved writable roots (cwd + tmp + extras), and deny network
37
+ * unless `allowNetwork` is set. This is a PURE function — no I/O, no spawning —
38
+ * so the generated text is unit-testable on any platform.
39
+ */
40
+ export declare function buildSeatbeltProfile(config: SandboxConfig, cwd: string): string;
41
+ /**
42
+ * Build the `sandbox-exec` argv that runs `command` under the generated
43
+ * profile. PURE: returns the argv vector without spawning anything.
44
+ *
45
+ * Shape: `sandbox-exec -p <profile> /bin/sh -c <command>`.
46
+ */
47
+ export declare function buildSeatbeltArgv(config: SandboxConfig, cwd: string, command: string): string[];
48
+ /**
49
+ * Build the `bwrap` argv that runs `command` with the writable roots bind
50
+ * mounted read-write over an otherwise read-only root. PURE: no spawning.
51
+ *
52
+ * Shape: `bwrap --ro-bind / / [--bind <root> <root> ...] [--unshare-net]
53
+ * --dev /dev --proc /proc /bin/sh -c <command>`.
54
+ *
55
+ * Network is denied by unsharing the network namespace unless `allowNetwork`.
56
+ */
57
+ export declare function buildBwrapArgv(config: SandboxConfig, cwd: string, command: string): string[];
58
+ /**
59
+ * Probe the current platform for OS-sandbox support. Never throws and never
60
+ * spawns the sandbox itself — it only checks for the launcher binary so callers
61
+ * can decide between sandboxed execution and an honest passthrough fallback.
62
+ *
63
+ * The optional `platform`/`probe` parameters are seams for unit tests so the
64
+ * pure decision logic can be exercised without depending on the host OS.
65
+ */
66
+ export declare function sandboxAvailability(platform?: NodeJS.Platform, probe?: (binary: string) => boolean): SandboxAvailability;
67
+ /**
68
+ * Build the platform-appropriate sandbox argv for `command`, or `null` when no
69
+ * sandbox can be applied. PURE with respect to the OS: callers pass an
70
+ * availability result (or rely on the live probe) and get back either the
71
+ * wrapped argv or `null`, never a spawn.
72
+ */
73
+ export declare function buildSandboxArgv(config: SandboxConfig, cwd: string, command: string, availability?: SandboxAvailability): string[] | null;
74
+ /** Renders an argv vector into a single `sh -c`-safe command string. */
75
+ export declare function argvToCommandString(argv: string[]): string;
76
+ /** Optional reporting hook so callers can surface sandbox status honestly. */
77
+ export interface SandboxedOperationsOptions {
78
+ /** Underlying backend that actually spawns. Defaults are supplied by bash.ts. */
79
+ base: BashOperations;
80
+ /** Availability probe override (mainly for tests). */
81
+ availability?: SandboxAvailability;
82
+ /**
83
+ * Called once per exec with a human-readable note describing whether the
84
+ * sandbox was applied or why it was skipped. Lets the caller record the
85
+ * truth instead of the wrapper silently claiming sandboxing.
86
+ */
87
+ onNote?: (note: string) => void;
88
+ }
89
+ /**
90
+ * Wrap a `BashOperations` backend so each command is executed inside the OS
91
+ * sandbox when one is available. When the sandbox tool is missing or the
92
+ * platform is unsupported, the command is passed through UNCHANGED and an
93
+ * honest note is emitted via `onNote` — we never pretend a command was
94
+ * sandboxed when it was not.
95
+ *
96
+ * When `config.enabled` is falsy this returns the base operations unchanged so
97
+ * the seam is a guaranteed no-op for the default (off) path.
98
+ */
99
+ export declare function createSandboxedBashOperations(config: SandboxConfig, options: SandboxedOperationsOptions): BashOperations;
@@ -1,8 +1,11 @@
1
1
  /**
2
2
  * Web Search Tool
3
3
  *
4
- * Performs real-time web searches using DuckDuckGo API.
5
- * Provides up-to-date information for current events and recent data.
4
+ * Performs real-time web searches by scraping DuckDuckGo's lightweight HTML
5
+ * results endpoint. Each organic hit is distilled to the three fields a model
6
+ * reasons over — title, destination URL, and a short snippet — and rendered as a
7
+ * compact numbered list. Provides up-to-date information for current events and
8
+ * recent data.
6
9
  */
7
10
  import type { AgentTool } from "../types.js";
8
11
  declare const webSearchSchema: import("@sinclair/typebox").TObject<{
@@ -0,0 +1 @@
1
+ export {};
@@ -1,4 +1,6 @@
1
1
  import type { AgentTool } from "../types.js";
2
+ import { type CheckpointHandle } from "./checkpoint.js";
3
+ import { type ReadStateHandle } from "./read-state.js";
2
4
  declare const writeSchema: import("@sinclair/typebox").TObject<{
3
5
  path: import("@sinclair/typebox").TString;
4
6
  content: import("@sinclair/typebox").TString;
@@ -20,6 +22,19 @@ export interface WriteToolOptions {
20
22
  operations?: WriteOperations;
21
23
  /** If set, duplicate any existing file to a sibling .bak before it is overwritten. */
22
24
  createBackup?: boolean;
25
+ /**
26
+ * Optional read-before-edit gate store. When present, overwriting an EXISTING
27
+ * file is refused unless it was read this session and has not drifted on disk;
28
+ * brand-new files (not yet on disk) are exempt. Absent → no gate (no-op).
29
+ */
30
+ readState?: ReadStateHandle;
31
+ /**
32
+ * Optional file-checkpoint sink. When present, the file's pre-mutation
33
+ * on-disk content (or `null` when it did not exist) is captured exactly once
34
+ * before the overwrite, so a later rewind can roll the working tree back.
35
+ * Absent → no snapshot (no-op).
36
+ */
37
+ checkpoint?: CheckpointHandle;
23
38
  }
24
39
  export declare function createWriteTool(cwd: string, options?: WriteToolOptions): AgentTool<typeof writeSchema>;
25
40
  /** Ready-to-use write tool bound to the current working directory. */
@@ -8,6 +8,16 @@
8
8
  */
9
9
  import { EventStream } from "../ml/index.js";
10
10
  import type { AgentContext, AgentEvent, AgentLoopConfig, AgentMessage, StreamFn } from "./types.js";
11
+ /**
12
+ * Conservative static allow-list of tools that only read state, never mutate
13
+ * it. Consecutive runs of these within a single assistant turn are safe to
14
+ * dispatch concurrently. Anything not on this list — mutating tools (edit,
15
+ * write, bash), and every dynamic / MCP / composio tool whose behavior the
16
+ * loop can't statically vouch for — runs serially in request order.
17
+ *
18
+ * Callers can supply their own set through `AgentLoopConfig.readOnlyToolNames`.
19
+ */
20
+ export declare const READ_ONLY_TOOL_NAMES: ReadonlySet<string>;
11
21
  /**
12
22
  * Begin a brand-new run. The supplied prompt messages are appended to the
13
23
  * conversation up front, then the turn cycle starts.
@@ -0,0 +1 @@
1
+ export {};
@@ -5,7 +5,7 @@
5
5
  * default the model is reached through streamSimple.
6
6
  */
7
7
  import { type ImageContent, type Message, type Model, type ThinkingBudgets } from "../ml/index.js";
8
- import type { AgentEvent, AgentMessage, AgentState, AgentTool, StreamFn, ThinkingLevel } from "./types.js";
8
+ import type { AgentEvent, AgentMessage, AgentState, AgentTool, CanUseToolFn, StreamFn, ThinkingLevel } from "./types.js";
9
9
  type QueueMode = "all" | "one-at-a-time";
10
10
  export interface AgentOptions {
11
11
  initialState?: Partial<AgentState>;
@@ -46,6 +46,13 @@ export interface AgentOptions {
46
46
  * expire, such as short-lived OAuth access tokens.
47
47
  */
48
48
  getApiKey?: (provider: string) => Promise<string | undefined> | string | undefined;
49
+ /**
50
+ * Hard permission gate consulted before each tool runs. A host supplies it to
51
+ * enforce an allow/ask/deny policy; the loop awaits it on the validated args
52
+ * and either proceeds (optionally with substituted input) or short-circuits
53
+ * to an isError result. Omit it to allow every tool — today's behavior.
54
+ */
55
+ canUseTool?: CanUseToolFn;
49
56
  /**
50
57
  * Per-level token allowances for thinking; only meaningful for providers
51
58
  * whose reasoning effort is expressed as a token budget.
@@ -63,6 +70,7 @@ export declare class Agent {
63
70
  streamFn: StreamFn;
64
71
  private _sessionId?;
65
72
  getApiKey?: (provider: string) => Promise<string | undefined> | string | undefined;
73
+ canUseTool?: CanUseToolFn;
66
74
  private runningPrompt?;
67
75
  private resolveRunningPrompt?;
68
76
  private _thinkingBudgets?;
@@ -0,0 +1 @@
1
+ export {};
@@ -38,8 +38,41 @@ export interface AgentToolResult<T> {
38
38
  }
39
39
  export interface AgentTool<TParameters extends TSchema = TSchema, TDetails = any> extends Tool<TParameters> {
40
40
  label: string;
41
+ /**
42
+ * Marks a tool that only inspects state and never mutates it (read/ls/grep/
43
+ * find/websearch/webfetch/todoread). Hosts can lean on this to auto-allow
44
+ * such tools in a permission policy without re-deriving the read-only set by
45
+ * name. Optional and defaults to undefined → treated as "not known read-only".
46
+ */
47
+ readOnly?: boolean;
41
48
  execute: (toolCallId: string, params: Static<TParameters>, signal?: AbortSignal, onUpdate?: AgentToolUpdateCallback<TDetails>) => Promise<AgentToolResult<TDetails>>;
42
49
  }
50
+ /**
51
+ * The verdict a {@link CanUseToolFn} returns for a single tool call.
52
+ *
53
+ * - `allow` proceeds with execution; an optional `updatedInput` replaces the
54
+ * validated arguments before the tool runs (e.g. a sanitized command).
55
+ * - `deny` short-circuits: the tool never executes and `message` is surfaced as
56
+ * an `isError` tool result so the model sees why it was blocked.
57
+ */
58
+ export type PermissionDecision = {
59
+ behavior: "allow";
60
+ updatedInput?: unknown;
61
+ } | {
62
+ behavior: "deny";
63
+ message: string;
64
+ };
65
+ /**
66
+ * Hard permission gate consulted immediately before a tool executes. Hosts
67
+ * supply it to enforce an allow/ask/deny policy; the loop awaits it (passing the
68
+ * abort signal so a pending prompt can be cancelled) and acts on the verdict.
69
+ *
70
+ * Entirely optional — when omitted the loop allows every tool, preserving
71
+ * today's behavior.
72
+ */
73
+ export type CanUseToolFn = (toolName: string, input: unknown, opts: {
74
+ signal?: AbortSignal;
75
+ }) => Promise<PermissionDecision>;
43
76
  export interface AgentContext {
44
77
  systemPrompt: string;
45
78
  messages: AgentMessage[];
@@ -116,6 +149,15 @@ export interface AgentLoopConfig extends SimpleStreamOptions {
116
149
  * while a slow stretch of tool calls is still running.
117
150
  */
118
151
  getApiKey?: (provider: string) => Promise<string | undefined> | string | undefined;
152
+ /**
153
+ * Hard permission gate consulted right before each tool executes. When set,
154
+ * the loop awaits it with the tool name and its VALIDATED arguments; a `deny`
155
+ * verdict short-circuits to an `isError` tool result (the tool never runs),
156
+ * while an `allow` verdict proceeds — substituting `updatedInput` when given.
157
+ *
158
+ * Omit it to allow every tool, i.e. exactly today's behavior.
159
+ */
160
+ canUseTool?: CanUseToolFn;
119
161
  /**
120
162
  * Yields steering messages to splice into the run while it is in flight.
121
163
  *
@@ -135,6 +177,24 @@ export interface AgentLoopConfig extends SimpleStreamOptions {
135
177
  * Use it for input that should wait until the current work wraps up.
136
178
  */
137
179
  getFollowUpMessages?: () => Promise<AgentMessage[]>;
180
+ /**
181
+ * Names of tools that are safe to run concurrently because they only read
182
+ * (never mutate) state. Consecutive runs of such calls within one assistant
183
+ * turn are dispatched in parallel (bounded by {@link maxToolConcurrency});
184
+ * everything else still runs one-at-a-time in request order.
185
+ *
186
+ * When omitted the loop falls back to a conservative built-in set
187
+ * (`read`/`ls`/`grep`/`find`/`websearch`/`webfetch`/`todoread`). Tools that
188
+ * aren't on the list — including dynamic/MCP/composio tools — always run
189
+ * serially, preserving today's behavior.
190
+ */
191
+ readOnlyToolNames?: ReadonlySet<string>;
192
+ /**
193
+ * Upper bound on how many read-only tool calls run at once. Defaults to the
194
+ * `CLAUDE_CODE_MAX_TOOL_USE_CONCURRENCY` / `INDUS_MAX_TOOL_CONCURRENCY`
195
+ * environment override, or 10. Set to 1 to force fully-sequential dispatch.
196
+ */
197
+ maxToolConcurrency?: number;
138
198
  }
139
199
  type AgentLifecycleEvent = {
140
200
  type: "agent_start";