@neurocode-ai/codemode 1.18.8

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.
@@ -0,0 +1,159 @@
1
+ import { Effect, Schema } from "effect"
2
+ import { executeWithLimits } from "./interpreter/runtime.js"
3
+ import { type HostTools, type Services, type ToolDescription, ToolRuntime } from "./tool-runtime.js"
4
+ import type { Definition } from "./tool.js"
5
+
6
+ /** A tool call admitted during an execution. */
7
+ export type { ToolCall, ToolCallEnded, ToolCallHooks, ToolCallStarted, ToolDescription } from "./tool-runtime.js"
8
+
9
+ /** Resource budgets enforced independently during each CodeMode program execution. */
10
+ export type ExecutionLimits = {
11
+ /** Maximum wall-clock execution time in milliseconds. No default: absent means no timeout. */
12
+ readonly timeoutMs?: number
13
+ /** Maximum number of tool calls admitted by the runtime. No default: absent means unlimited. */
14
+ readonly maxToolCalls?: number
15
+ /** Maximum UTF-8 bytes of model-facing output. No default: absent means no truncation. */
16
+ readonly maxOutputBytes?: number
17
+ }
18
+
19
+ /** Controls how much of the tool catalog is inlined in agent instructions. */
20
+ export type DiscoveryOptions = {
21
+ /** Approximate token budget (chars/4, default 2000) for full catalog entries. */
22
+ readonly catalogBudget?: number
23
+ }
24
+
25
+ type ToolTree<R = never> = {
26
+ readonly [name: string]: Definition<R> | ToolTree<R>
27
+ }
28
+
29
+ export type ResolvedExecutionLimits = {
30
+ readonly timeoutMs: number | undefined
31
+ readonly maxToolCalls: number | undefined
32
+ readonly maxOutputBytes: number | undefined
33
+ }
34
+
35
+ /** Options for one CodeMode execution. */
36
+ export type ExecuteOptions<Tools extends Record<string, unknown> = {}> = {
37
+ /** Source for one program in the supported JavaScript subset. */
38
+ code: string
39
+ /** Explicit tool tree exposed to the program as `tools`. */
40
+ tools?: Tools & ToolTree<Services<Tools>>
41
+ /** Per-execution overrides for the default resource limits. */
42
+ limits?: ExecutionLimits
43
+ /** Observes decoded tool input immediately before tool execution. */
44
+ onToolCallStart?: (call: ToolRuntime.ToolCallStarted) => Effect.Effect<void, never, Services<Tools>>
45
+ /** Observes each admitted tool call as it settles, with outcome and duration. */
46
+ onToolCallEnd?: (call: ToolRuntime.ToolCallEnded) => Effect.Effect<void, never, Services<Tools>>
47
+ }
48
+
49
+ /** A JSON value that can cross the confined interpreter boundary. */
50
+ export type DataValue = Schema.Json
51
+
52
+ /** Configuration shared by `CodeMode.make` and `CodeMode.execute`. */
53
+ export type Options<Tools extends Record<string, unknown> = {}> = Omit<ExecuteOptions<Tools>, "code"> & {
54
+ /** Progressive-disclosure configuration for the agent-facing tool catalog. */
55
+ readonly discovery?: DiscoveryOptions
56
+ }
57
+
58
+ /** Schema for a host tool input containing CodeMode source. */
59
+ export const Input = Schema.Struct({ code: Schema.String })
60
+ export type Input = typeof Input.Type
61
+
62
+ export const DiagnosticKind = Schema.Literals([
63
+ "ParseError",
64
+ "UnsupportedSyntax",
65
+ "UnknownTool",
66
+ "InvalidToolInput",
67
+ "InvalidToolOutput",
68
+ "InvalidDataValue",
69
+ "ToolCallLimitExceeded",
70
+ "TimeoutExceeded",
71
+ "ToolFailure",
72
+ "ExecutionFailure",
73
+ ])
74
+ /** Stable categories produced by program, schema, tool, and limit failures. */
75
+ export type DiagnosticKind = typeof DiagnosticKind.Type
76
+
77
+ export const Diagnostic = Schema.Struct({
78
+ kind: DiagnosticKind,
79
+ message: Schema.String,
80
+ location: Schema.optionalKey(Schema.Struct({ line: Schema.Number, column: Schema.Number })),
81
+ suggestions: Schema.optionalKey(Schema.Array(Schema.String)),
82
+ })
83
+ /** A normalized program diagnostic safe to return across an agent tool boundary. */
84
+ export type Diagnostic = typeof Diagnostic.Type
85
+
86
+ const ToolCallSchema = Schema.Struct({ name: Schema.String })
87
+ export const Success = Schema.Struct({
88
+ ok: Schema.Literal(true),
89
+ value: Schema.Json,
90
+ logs: Schema.optionalKey(Schema.Array(Schema.String)),
91
+ truncated: Schema.optionalKey(Schema.Boolean),
92
+ toolCalls: Schema.Array(ToolCallSchema),
93
+ })
94
+ /** Successful execution after the result has crossed the plain-data boundary. */
95
+ export type Success = typeof Success.Type
96
+
97
+ export const Failure = Schema.Struct({
98
+ ok: Schema.Literal(false),
99
+ error: Diagnostic,
100
+ logs: Schema.optionalKey(Schema.Array(Schema.String)),
101
+ truncated: Schema.optionalKey(Schema.Boolean),
102
+ toolCalls: Schema.Array(ToolCallSchema),
103
+ })
104
+ /** Failed execution with calls admitted before the diagnostic was produced. */
105
+ export type Failure = typeof Failure.Type
106
+
107
+ /** Schema for the structured success or diagnostic returned by CodeMode execution. */
108
+ export const Result = Schema.Union([Success, Failure])
109
+ /** Result of executing a CodeMode program. Program failures are data, not Effect failures. */
110
+ export type Result = typeof Result.Type
111
+
112
+ /** Reusable confined runtime over one explicit tool tree. */
113
+ export type Runtime<R = never> = {
114
+ readonly catalog: () => ReadonlyArray<ToolDescription>
115
+ readonly instructions: () => string
116
+ readonly execute: (code: string) => Effect.Effect<Result, never, R>
117
+ }
118
+
119
+ const validateLimit = <Value extends number | undefined>(
120
+ name: keyof ExecutionLimits,
121
+ value: Value,
122
+ minimum: number,
123
+ ): Value => {
124
+ if (value !== undefined && (!Number.isSafeInteger(value) || value < minimum)) {
125
+ throw new RangeError(`${name} must be a safe integer greater than or equal to ${minimum}.`)
126
+ }
127
+ return value
128
+ }
129
+
130
+ const resolveExecutionLimits = (limits?: ExecutionLimits): ResolvedExecutionLimits => ({
131
+ timeoutMs: validateLimit("timeoutMs", limits?.timeoutMs, 1),
132
+ maxToolCalls: validateLimit("maxToolCalls", limits?.maxToolCalls, 0),
133
+ maxOutputBytes: validateLimit("maxOutputBytes", limits?.maxOutputBytes, 0),
134
+ })
135
+
136
+ /** Executes one Effect-native CodeMode program without constructing a reusable runtime. */
137
+ export const execute = <const Tools extends Record<string, unknown>>(
138
+ options: ExecuteOptions<Tools>,
139
+ ): Effect.Effect<Result, never, Services<Tools>> => {
140
+ const tools = (options.tools ?? {}) as HostTools<Services<Tools>>
141
+ ToolRuntime.assertValidTools(tools)
142
+ return executeWithLimits(options, resolveExecutionLimits(options.limits), ToolRuntime.searchIndex(tools))
143
+ }
144
+
145
+ /** Creates an Effect-native runtime over explicit, schema-described tools. */
146
+ export const make = <const Tools extends Record<string, unknown> = {}>(
147
+ options: Options<Tools> = {} as Options<Tools>,
148
+ ): Runtime<Services<Tools>> => {
149
+ const tools = (options.tools ?? {}) as HostTools<Services<Tools>>
150
+ ToolRuntime.assertValidTools(tools)
151
+ const limits = resolveExecutionLimits(options.limits)
152
+ const prepared = ToolRuntime.prepare(tools, options.discovery?.catalogBudget)
153
+
154
+ return {
155
+ catalog: () => prepared.catalog,
156
+ instructions: () => prepared.instructions,
157
+ execute: (code) => executeWithLimits<Tools>({ ...options, code }, limits, prepared.searchIndex),
158
+ }
159
+ }
package/src/index.ts ADDED
@@ -0,0 +1,4 @@
1
+ export * as CodeMode from "./codemode.js"
2
+ export * as Tool from "./tool.js"
3
+ export * as OpenAPI from "./openapi/index.js"
4
+ export { ToolError, toolError } from "./tool-error.js"
@@ -0,0 +1,201 @@
1
+ import type { SafeObject } from "../tool-runtime.js"
2
+ import type { SandboxURL } from "../values.js"
3
+
4
+ export type SourcePosition = {
5
+ line: number
6
+ column: number
7
+ }
8
+
9
+ export type SourceLocation = {
10
+ start: SourcePosition
11
+ end: SourcePosition
12
+ }
13
+
14
+ export type AstNode = {
15
+ type: string
16
+ loc?: SourceLocation
17
+ [key: string]: unknown
18
+ }
19
+
20
+ export type ProgramNode = AstNode & {
21
+ type: "Program"
22
+ body: Array<AstNode>
23
+ }
24
+
25
+ export type Binding = {
26
+ mutable: boolean
27
+ value: unknown
28
+ initialized?: boolean
29
+ }
30
+
31
+ export type StatementResult =
32
+ | { kind: "none" }
33
+ | { kind: "value"; value: unknown }
34
+ | { kind: "return"; value: unknown }
35
+ | { kind: "break" }
36
+ | { kind: "continue" }
37
+
38
+ export type MemberReference = {
39
+ target: SafeObject | Array<unknown> | SandboxURL
40
+ key: string | number
41
+ }
42
+
43
+ export class CodeModeFunction {
44
+ constructor(
45
+ readonly parameters: ReadonlyArray<AstNode>,
46
+ readonly body: AstNode,
47
+ readonly capturedScopes: ReadonlyArray<Map<string, Binding>>,
48
+ ) {}
49
+ }
50
+
51
+ export class IntrinsicReference {
52
+ constructor(
53
+ readonly receiver: unknown,
54
+ readonly name: string,
55
+ ) {}
56
+ }
57
+
58
+ export class ComputedValue {
59
+ constructor(readonly value: unknown) {}
60
+ }
61
+
62
+ export class PromiseNamespace {}
63
+
64
+ export type PromiseMethodName = "all" | "allSettled" | "race" | "resolve" | "reject"
65
+
66
+ export class PromiseMethodReference {
67
+ constructor(readonly name: PromiseMethodName) {}
68
+ }
69
+
70
+ export type GlobalNamespaceName =
71
+ | "Object"
72
+ | "Math"
73
+ | "JSON"
74
+ | "Array"
75
+ | "console"
76
+ | "Date"
77
+ | "RegExp"
78
+ | "Map"
79
+ | "Set"
80
+ | "URL"
81
+ | "URLSearchParams"
82
+
83
+ export class GlobalNamespace {
84
+ constructor(readonly name: GlobalNamespaceName) {}
85
+ }
86
+
87
+ export class GlobalMethodReference {
88
+ constructor(
89
+ readonly namespace: GlobalNamespaceName | "Number" | "String",
90
+ readonly name: string,
91
+ ) {}
92
+ }
93
+
94
+ export class CoercionFunction {
95
+ constructor(readonly name: "Number" | "String" | "Boolean" | "parseInt" | "parseFloat") {}
96
+ }
97
+
98
+ export class UriFunction {
99
+ constructor(readonly name: "encodeURI" | "encodeURIComponent" | "decodeURI" | "decodeURIComponent") {}
100
+ }
101
+
102
+ export class ProgramThrow {
103
+ constructor(readonly value: unknown) {}
104
+ }
105
+
106
+ export class ErrorConstructorReference {
107
+ constructor(readonly name: string) {}
108
+ }
109
+
110
+ export type DiagnosticKind =
111
+ | "ParseError"
112
+ | "UnsupportedSyntax"
113
+ | "UnknownTool"
114
+ | "InvalidToolInput"
115
+ | "InvalidToolOutput"
116
+ | "InvalidDataValue"
117
+ | "ToolCallLimitExceeded"
118
+ | "TimeoutExceeded"
119
+ | "ToolFailure"
120
+ | "ExecutionFailure"
121
+
122
+ export const OptionalShortCircuit: unique symbol = Symbol("codemode.optional-short-circuit")
123
+
124
+ export const supportedSyntaxMessage =
125
+ "Supported orchestration syntax: tools.* calls (they return promises - resolve them with await), data literals, destructuring, optional chaining, template literals, conditionals, switch, loops (incl. for...of and for...in over object/array/tools keys), arrow functions, spread, try/catch, array methods (map/filter/find/findIndex/some/every/reduce/flatMap/forEach/sort/slice/concat/indexOf/lastIndexOf/at/flat/reverse/includes/join), string methods (incl. match/matchAll/replace/split with regular expressions), Date/RegExp/Map/Set/URL/URLSearchParams, URI encoding helpers, Object/Math/JSON helpers, captured console.log/warn/error/dir/table, and Promise.all/allSettled/race/resolve/reject over arrays mixing promises and plain values for parallel tool calls (promise chaining with .then/.catch is not supported - use await with try/catch)."
126
+
127
+ export class InterpreterRuntimeError extends Error {
128
+ readonly node?: AstNode
129
+ errorName: string = "Error"
130
+
131
+ constructor(
132
+ message: string,
133
+ node?: AstNode,
134
+ readonly kind: DiagnosticKind = "ExecutionFailure",
135
+ readonly suggestions?: ReadonlyArray<string>,
136
+ ) {
137
+ super(message)
138
+ this.name = "InterpreterRuntimeError"
139
+ if (node) this.node = node
140
+ }
141
+
142
+ as(errorName: string): this {
143
+ this.errorName = errorName
144
+ return this
145
+ }
146
+ }
147
+
148
+ export const unsupportedSyntax = (kind: string, node: AstNode): InterpreterRuntimeError =>
149
+ new InterpreterRuntimeError(
150
+ `Syntax '${kind}' is not supported in CodeMode. ${supportedSyntaxMessage}`,
151
+ node,
152
+ "UnsupportedSyntax",
153
+ [supportedSyntaxMessage],
154
+ )
155
+
156
+ export const isRecord = (value: unknown): value is Record<string, unknown> =>
157
+ typeof value === "object" && value !== null
158
+
159
+ export const asNode = (value: unknown, context: string): AstNode => {
160
+ if (!isRecord(value) || typeof value.type !== "string") {
161
+ throw new InterpreterRuntimeError(`Invalid AST node while reading ${context}.`)
162
+ }
163
+ return value as AstNode
164
+ }
165
+
166
+ export const getArray = (node: AstNode, key: string): Array<unknown> => {
167
+ const value = node[key]
168
+ if (!Array.isArray(value)) throw new InterpreterRuntimeError(`Expected '${key}' to be an array.`, node)
169
+ return value
170
+ }
171
+
172
+ export const getString = (node: AstNode, key: string): string => {
173
+ const value = node[key]
174
+ if (typeof value !== "string") throw new InterpreterRuntimeError(`Expected '${key}' to be a string.`, node)
175
+ return value
176
+ }
177
+
178
+ export const getBoolean = (node: AstNode, key: string): boolean => {
179
+ const value = node[key]
180
+ if (typeof value !== "boolean") throw new InterpreterRuntimeError(`Expected '${key}' to be a boolean.`, node)
181
+ return value
182
+ }
183
+
184
+ export const getOptionalNode = (node: AstNode, key: string): AstNode | undefined => {
185
+ const value = node[key]
186
+ if (value === undefined || value === null) return undefined
187
+ return asNode(value, key)
188
+ }
189
+
190
+ export const getNode = (node: AstNode, key: string): AstNode => asNode(node[key], key)
191
+
192
+ export const sourceLocation = (node: AstNode): { readonly line: number; readonly column: number } => ({
193
+ line: Math.max(1, (node.loc?.start.line ?? 2) - 1),
194
+ column: Math.max(1, (node.loc?.start.column ?? 4) - 3),
195
+ })
196
+
197
+ export const formatLocation = (node?: AstNode): string => {
198
+ if (!node?.loc) return ""
199
+ const location = sourceLocation(node)
200
+ return ` (line ${location.line}, col ${location.column})`
201
+ }