@celestea/tools 2.7.1

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 (140) hide show
  1. package/LICENSE +21 -0
  2. package/dist/args.d.ts +15 -0
  3. package/dist/args.js +55 -0
  4. package/dist/attachments/image-header.d.ts +28 -0
  5. package/dist/attachments/image-header.js +148 -0
  6. package/dist/attachments/store.d.ts +56 -0
  7. package/dist/attachments/store.js +148 -0
  8. package/dist/browser/cdp.d.ts +162 -0
  9. package/dist/browser/cdp.js +269 -0
  10. package/dist/browser/launch.d.ts +92 -0
  11. package/dist/browser/launch.js +232 -0
  12. package/dist/browser/memory-guard.d.ts +55 -0
  13. package/dist/browser/memory-guard.js +210 -0
  14. package/dist/browser/session.d.ts +146 -0
  15. package/dist/browser/session.js +371 -0
  16. package/dist/browser/snapshot.d.ts +69 -0
  17. package/dist/browser/snapshot.js +163 -0
  18. package/dist/browser/types.d.ts +51 -0
  19. package/dist/browser/types.js +8 -0
  20. package/dist/builtin.d.ts +54 -0
  21. package/dist/builtin.js +77 -0
  22. package/dist/desc.d.ts +20 -0
  23. package/dist/desc.js +22 -0
  24. package/dist/disclosure.d.ts +113 -0
  25. package/dist/disclosure.js +141 -0
  26. package/dist/env.d.ts +10 -0
  27. package/dist/env.js +33 -0
  28. package/dist/errors.d.ts +25 -0
  29. package/dist/errors.js +60 -0
  30. package/dist/exposure.d.ts +145 -0
  31. package/dist/exposure.js +244 -0
  32. package/dist/fn-tool.d.ts +14 -0
  33. package/dist/fn-tool.js +14 -0
  34. package/dist/fs/file-io.d.ts +78 -0
  35. package/dist/fs/file-io.js +239 -0
  36. package/dist/guard/path-guard.d.ts +144 -0
  37. package/dist/guard/path-guard.js +289 -0
  38. package/dist/guard/paths.d.ts +35 -0
  39. package/dist/guard/paths.js +100 -0
  40. package/dist/http/errors.d.ts +24 -0
  41. package/dist/http/errors.js +64 -0
  42. package/dist/http/headers.d.ts +19 -0
  43. package/dist/http/headers.js +62 -0
  44. package/dist/http/redirects.d.ts +31 -0
  45. package/dist/http/redirects.js +76 -0
  46. package/dist/http/ssrf.d.ts +105 -0
  47. package/dist/http/ssrf.js +272 -0
  48. package/dist/http/transport.d.ts +50 -0
  49. package/dist/http/transport.js +130 -0
  50. package/dist/index.d.ts +114 -0
  51. package/dist/index.js +129 -0
  52. package/dist/memory/log.d.ts +75 -0
  53. package/dist/memory/log.js +157 -0
  54. package/dist/memory/store.d.ts +47 -0
  55. package/dist/memory/store.js +61 -0
  56. package/dist/platform/exec.d.ts +79 -0
  57. package/dist/platform/exec.js +218 -0
  58. package/dist/platform/index.d.ts +12 -0
  59. package/dist/platform/index.js +12 -0
  60. package/dist/platform/paths.d.ts +51 -0
  61. package/dist/platform/paths.js +60 -0
  62. package/dist/platform/quote.d.ts +72 -0
  63. package/dist/platform/quote.js +102 -0
  64. package/dist/plugin.d.ts +96 -0
  65. package/dist/plugin.js +101 -0
  66. package/dist/process/buffers.d.ts +33 -0
  67. package/dist/process/buffers.js +86 -0
  68. package/dist/process/registry.d.ts +98 -0
  69. package/dist/process/registry.js +282 -0
  70. package/dist/registry.d.ts +52 -0
  71. package/dist/registry.js +161 -0
  72. package/dist/run-code/broker.d.ts +68 -0
  73. package/dist/run-code/broker.js +465 -0
  74. package/dist/run-code/limits.d.ts +69 -0
  75. package/dist/run-code/limits.js +88 -0
  76. package/dist/run-code/lines.d.ts +69 -0
  77. package/dist/run-code/lines.js +199 -0
  78. package/dist/run-code/sdk-ts.d.ts +34 -0
  79. package/dist/run-code/sdk-ts.js +276 -0
  80. package/dist/run-code/sdk.d.ts +39 -0
  81. package/dist/run-code/sdk.js +294 -0
  82. package/dist/sandbox/async.d.ts +10 -0
  83. package/dist/sandbox/async.js +26 -0
  84. package/dist/sandbox/bwrap-argv.d.ts +62 -0
  85. package/dist/sandbox/bwrap-argv.js +113 -0
  86. package/dist/sandbox/bwrap.d.ts +94 -0
  87. package/dist/sandbox/bwrap.js +159 -0
  88. package/dist/sandbox/child.d.ts +38 -0
  89. package/dist/sandbox/child.js +98 -0
  90. package/dist/sandbox/config.d.ts +89 -0
  91. package/dist/sandbox/config.js +149 -0
  92. package/dist/sandbox/fake-sandbox.d.ts +57 -0
  93. package/dist/sandbox/fake-sandbox.js +110 -0
  94. package/dist/sandbox/launch.d.ts +51 -0
  95. package/dist/sandbox/launch.js +134 -0
  96. package/dist/sandbox/limits.d.ts +63 -0
  97. package/dist/sandbox/limits.js +113 -0
  98. package/dist/sandbox/probe.d.ts +46 -0
  99. package/dist/sandbox/probe.js +102 -0
  100. package/dist/sandbox/provider.d.ts +83 -0
  101. package/dist/sandbox/provider.js +126 -0
  102. package/dist/sandbox/rlimit.d.ts +60 -0
  103. package/dist/sandbox/rlimit.js +76 -0
  104. package/dist/sandbox/seccomp.d.ts +48 -0
  105. package/dist/sandbox/seccomp.js +115 -0
  106. package/dist/sandbox/userspace.d.ts +65 -0
  107. package/dist/sandbox/userspace.js +107 -0
  108. package/dist/sandbox/workdir.d.ts +13 -0
  109. package/dist/sandbox/workdir.js +44 -0
  110. package/dist/schema.d.ts +20 -0
  111. package/dist/schema.js +135 -0
  112. package/dist/testing/platform-gates.d.ts +54 -0
  113. package/dist/testing/platform-gates.js +62 -0
  114. package/dist/tool-failure.d.ts +13 -0
  115. package/dist/tool-failure.js +19 -0
  116. package/dist/tools/ask-user.d.ts +32 -0
  117. package/dist/tools/ask-user.js +145 -0
  118. package/dist/tools/browser.d.ts +24 -0
  119. package/dist/tools/browser.js +132 -0
  120. package/dist/tools/http-request.d.ts +31 -0
  121. package/dist/tools/http-request.js +117 -0
  122. package/dist/tools/list-dir.d.ts +9 -0
  123. package/dist/tools/list-dir.js +45 -0
  124. package/dist/tools/load-skill.d.ts +37 -0
  125. package/dist/tools/load-skill.js +76 -0
  126. package/dist/tools/memory.d.ts +46 -0
  127. package/dist/tools/memory.js +131 -0
  128. package/dist/tools/process-control.d.ts +14 -0
  129. package/dist/tools/process-control.js +49 -0
  130. package/dist/tools/read-file.d.ts +11 -0
  131. package/dist/tools/read-file.js +81 -0
  132. package/dist/tools/read-image.d.ts +33 -0
  133. package/dist/tools/read-image.js +144 -0
  134. package/dist/tools/run-code.d.ts +48 -0
  135. package/dist/tools/run-code.js +115 -0
  136. package/dist/tools/run-shell.d.ts +22 -0
  137. package/dist/tools/run-shell.js +81 -0
  138. package/dist/tools/write-file.d.ts +8 -0
  139. package/dist/tools/write-file.js +31 -0
  140. package/package.json +28 -0
@@ -0,0 +1,161 @@
1
+ /**
2
+ * `ToolRegistryImpl` — the dispatch pipeline of the tool seam.
3
+ *
4
+ * One dispatch runs four stages, in this order (a hard pipeline, not a
5
+ * convention):
6
+ * 1. **schema** — `args` are validated against `tool.spec().parameters`
7
+ * (`toolargs: code=schema …`, the tool never runs);
8
+ * 2. **guard** — the guard chain runs in registration order and the FIRST
9
+ * non-`Allow` decision short-circuits (`denied: toolguard: …`) — a later
10
+ * `Allow` never un-denies an earlier `Deny`;
11
+ * 3. **execute** — through `executeWith` when the tool overrides it, else
12
+ * `execute(args)`;
13
+ * 4. **structure** — the result is a `ToolOutput`: canonical `value`, an
14
+ * optional authored `render`, captured `error`, and the guard `decision`
15
+ * as a first-class field.
16
+ *
17
+ * Errors are captured, never thrown across the seam (parity:
18
+ * `crates/tools/src/registry.rs`).
19
+ *
20
+ * **The verdict never lies (W738 P1)**: `decision` describes what the seam did
21
+ * with the call, so a call the seam REFUSED to run (unknown tool, schema
22
+ * rejection) is a `deny`, never an `allow` — an `allow` there would tell the
23
+ * caller (and the audit log) that a rejected call passed every check. A tool that
24
+ * did run and then failed keeps `allow`: the guards really did allow it and the
25
+ * verdict is not a success flag.
26
+ */
27
+ import { GUARD_ERROR_PREFIX, TOOLARG_ERROR_PREFIX, contractError, errorText, quoteMessage } from "./errors.js";
28
+ import { validateArgs } from "./schema.js";
29
+ const ALLOW = { kind: "allow" };
30
+ export class ToolRegistryImpl {
31
+ tools = new Map();
32
+ guards = [];
33
+ register(tool) {
34
+ this.tools.set(tool.spec().name, tool);
35
+ }
36
+ addGuard(guard) {
37
+ this.guards.push(guard);
38
+ }
39
+ get(name) {
40
+ return this.tools.get(name);
41
+ }
42
+ /** Registered tool names, in registration order (`schemas()` sorts instead). */
43
+ names() {
44
+ return [...this.tools.keys()];
45
+ }
46
+ /** The guard chain, in evaluation order (diagnostics / compose assertions). */
47
+ guardChain() {
48
+ return this.guards;
49
+ }
50
+ schemas() {
51
+ return [...this.tools.values()].map((t) => t.spec()).sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
52
+ }
53
+ async dispatch(input) {
54
+ const tool = this.tools.get(input.name);
55
+ if (tool === undefined)
56
+ return refused(input.call_id, `unknown tool: ${input.name}`);
57
+ const invalid = validateArgs(tool.spec().parameters, input.args);
58
+ if (invalid !== null) {
59
+ return refused(input.call_id, contractError(TOOLARG_ERROR_PREFIX, "schema", invalid.message));
60
+ }
61
+ const decision = await this.runGuards(input);
62
+ if (decision.kind === "deny")
63
+ return decisionFailure(input.call_id, "deny", decision.reason);
64
+ if (decision.kind === "ask")
65
+ return decisionFailure(input.call_id, "ask", decision.reason);
66
+ return this.runTool(tool, input);
67
+ }
68
+ async runTool(tool, input) {
69
+ try {
70
+ const outcome = tool.executeWith === undefined
71
+ ? { value: await tool.execute(input.args), render: null }
72
+ : await tool.executeWith(input);
73
+ return {
74
+ call_id: input.call_id,
75
+ value: outcome.value,
76
+ render: outcome.render ?? humanRender(outcome.value),
77
+ error: null,
78
+ decision: ALLOW,
79
+ // W855 (B6): a tool-authored model face (e.g. read_file truncation).
80
+ ...(outcome.surface === undefined ? {} : { surface: outcome.surface }),
81
+ };
82
+ }
83
+ catch (e) {
84
+ return failure(input.call_id, errorText(e));
85
+ }
86
+ }
87
+ async runGuards(input) {
88
+ for (const guard of this.guards) {
89
+ let decision;
90
+ try {
91
+ decision = await guard.check(input);
92
+ }
93
+ catch (e) {
94
+ decision = { kind: "deny", reason: contractError(GUARD_ERROR_PREFIX, "guard_error", errorText(e)) };
95
+ }
96
+ if (decision.kind !== "allow")
97
+ return decision;
98
+ }
99
+ return ALLOW;
100
+ }
101
+ }
102
+ /**
103
+ * A call that ran and then failed: the guards allowed it, so the verdict is
104
+ * `allow` — `error` carries the failure (`decision` is not a success flag).
105
+ */
106
+ function failure(callId, error) {
107
+ return { call_id: callId, value: null, render: null, error, decision: ALLOW };
108
+ }
109
+ /**
110
+ * A call the seam REFUSED before execution (unknown tool / invalid args): the
111
+ * verdict is a `deny` whose reason is the very error the caller sees, so
112
+ * "refused" can never be reported as "allowed" (W738 P1).
113
+ */
114
+ function refused(callId, error) {
115
+ return { call_id: callId, value: null, render: null, error, decision: { kind: "deny", reason: error } };
116
+ }
117
+ function decisionFailure(callId, kind, reason) {
118
+ // parity: a Deny surfaces as `denied: <reason>`, an Ask as `ask: <reason>`.
119
+ return {
120
+ call_id: callId,
121
+ value: null,
122
+ render: null,
123
+ error: `${kind === "deny" ? "denied" : "ask"}: ${reason}`,
124
+ decision: kind === "deny" ? { kind: "deny", reason } : { kind: "ask", reason },
125
+ };
126
+ }
127
+ /** Registry seeded with tools + guards (guards run in the given order). */
128
+ export function createToolRegistry(tools = [], guards = []) {
129
+ const registry = new ToolRegistryImpl();
130
+ for (const tool of tools)
131
+ registry.register(tool);
132
+ for (const guard of guards)
133
+ registry.addGuard(guard);
134
+ return registry;
135
+ }
136
+ /**
137
+ * Best-effort human-readable rendering of a successful result (legacy
138
+ * `human_render`): a `{stdout, stderr, exit_code}` object condenses to a stream
139
+ * summary; plain text and everything else keep the generic value view (`null`).
140
+ */
141
+ export function humanRender(value) {
142
+ if (typeof value !== "object" || value === null || Array.isArray(value))
143
+ return null;
144
+ const obj = value;
145
+ const isStreamShape = "stdout" in obj || "stderr" in obj || "exit_code" in obj;
146
+ if (!isStreamShape)
147
+ return null;
148
+ const lines = [];
149
+ if (obj["exit_code"] !== undefined && obj["exit_code"] !== null)
150
+ lines.push(`exit_code: ${String(obj["exit_code"])}`);
151
+ if (typeof obj["stdout"] === "string" && obj["stdout"] !== "")
152
+ lines.push(`stdout: ${obj["stdout"]}`);
153
+ if (typeof obj["stderr"] === "string" && obj["stderr"] !== "")
154
+ lines.push(`stderr: ${obj["stderr"]}`);
155
+ const rendered = lines.join("\n").trimEnd();
156
+ return rendered === "" ? null : rendered;
157
+ }
158
+ /** Stamp a guard denial reason with the contract prefix (compose-time helper). */
159
+ export function guardDenyReason(code, message) {
160
+ return contractError(GUARD_ERROR_PREFIX, code, quoteMessage(message));
161
+ }
@@ -0,0 +1,68 @@
1
+ /**
2
+ * The `run_code` parent broker (`crates/tools/src/run_code.rs:562-978`).
3
+ *
4
+ * One `run_code` call = one round trip. The assembled program — TypeScript by
5
+ * default since W774, Python on request — runs in the sandbox; its sub-calls
6
+ * arrive as one-line JSON on stdout and the parent answers on stdin after
7
+ * dispatching each one through the **same** registry pipeline (schema → guards →
8
+ * execute) the model itself would use. Only `main()`'s return value travels back
9
+ * as the tool result.
10
+ *
11
+ * The protocol is language-neutral (it is byte-identical for both SDKs), so the
12
+ * language only decides two things: the script file's extension and the
13
+ * interpreter that runs it.
14
+ *
15
+ * Invariants:
16
+ * - every limit is enforced here, never in the child: the 21st sub-call is
17
+ * refused before dispatch, the wall clock is enforced while waiting for a
18
+ * line, the sub-call output ledger is charged per reply;
19
+ * - every infrastructure failure is a structured `run_code: code=… msg="…"`
20
+ * (invalid_arg | registry | config | spawn | protocol | timeout | aborted);
21
+ * a program exception is that exception's text plus a bounded log tail;
22
+ * - the child is killed on timeout, on cancel and on protocol failure — never
23
+ * left behind — and its script file is removed on every exit path.
24
+ */
25
+ import type { Sandbox, SessionEvent, ToolExecOutcome, ToolRegistry } from "@celestea/core";
26
+ import { type RunCodeConfig } from "./limits.js";
27
+ import { type RunCodeLanguage } from "./sdk.js";
28
+ /** Session-log sink for sub-call rows (legacy `Fn(SessionEvent)` sink). */
29
+ export type RunCodeEventSink = (event: SessionEvent) => void;
30
+ /** Everything one broker run needs (the tool binds the registry + call id). */
31
+ export interface BrokerContext {
32
+ sandbox: Sandbox;
33
+ registry: ToolRegistry;
34
+ events?: RunCodeEventSink;
35
+ config: RunCodeConfig;
36
+ /** The `run_code` call id: sub-call ids are `<parentId>:c<n>`. */
37
+ parentId: string;
38
+ }
39
+ /**
40
+ * Absolute interpreter path of a TypeScript program (W774): the Node that runs
41
+ * THIS process, which is the same binary the sandbox can see (`--ro-bind / /`
42
+ * mounts the host root read-only) and never depends on the child's PATH.
43
+ * `/usr/bin/node` is preferred because it is the host's system-wide install;
44
+ * `process.execPath` is the honest fallback (nvm/volta hosts).
45
+ *
46
+ * W885: the constant is kept for compatibility, but the interpreter actually
47
+ * used is resolved per run (`resolveInterpreter`), which checks the platform
48
+ * PATH first — `/usr/bin/node` is a POSIX convention that simply does not
49
+ * exist on Windows (W883 B9/B15).
50
+ */
51
+ export declare const TS_PROGRAM_RUNTIME: string;
52
+ /** One full run_code round trip: the program's final value + its render. */
53
+ export declare function brokerRun(ctx: BrokerContext, args: unknown): Promise<ToolExecOutcome>;
54
+ /**
55
+ * The interpreter of one `run_code` program (W885).
56
+ *
57
+ * TypeScript runs under the Node that runs THIS process — the one binary the
58
+ * sandbox is guaranteed to see through `--ro-bind / /` — with the host's PATH
59
+ * consulted first ONLY when that yields a Node (`node` is `node.exe` on
60
+ * Windows, and `process.execPath` there is routinely `C:\\Program Files\\…`
61
+ * with a space in it). Python keeps the historical `python3` on POSIX and
62
+ * falls back to the names Windows actually ships (`python`, `py`).
63
+ *
64
+ * Anything `whichSync` cannot find becomes the bare name, so a missing
65
+ * interpreter still surfaces as the interpreter's own "not found" instead of a
66
+ * silent wrong one.
67
+ */
68
+ export declare function resolveInterpreter(language: RunCodeLanguage, platform?: string, env?: NodeJS.ProcessEnv): string;