nanocodex 0.1.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.
- package/README.md +116 -0
- package/actions/events.d.mts +10 -0
- package/actions/events.mjs +91 -0
- package/actions/index.d.mts +12 -0
- package/actions/index.mjs +20 -0
- package/actions/session.d.mts +14 -0
- package/actions/session.mjs +9 -0
- package/actions/turn.d.mts +30 -0
- package/actions/turn.mjs +22 -0
- package/browser/Agent.d.mts +25 -0
- package/browser/Agent.mjs +54 -0
- package/browser/host.d.mts +20 -0
- package/browser/host.mjs +161 -0
- package/browser/index.d.mts +13 -0
- package/browser/index.mjs +2 -0
- package/index.d.mts +19 -0
- package/index.mjs +1 -0
- package/internal.mjs +303 -0
- package/node/Agent.d.mts +24 -0
- package/node/Agent.mjs +51 -0
- package/node/host.mjs +204 -0
- package/node/index.d.mts +13 -0
- package/node/index.mjs +2 -0
- package/package.json +61 -0
- package/pkg-node/nanocodex.d.ts +109 -0
- package/pkg-node/nanocodex.js +652 -0
- package/pkg-node/nanocodex_bg.wasm +0 -0
- package/pkg-node/nanocodex_bg.wasm.d.ts +27 -0
- package/pkg-node/package.json +1 -0
- package/pkg-web/nanocodex.d.ts +161 -0
- package/pkg-web/nanocodex.js +745 -0
- package/pkg-web/nanocodex_bg.wasm +0 -0
- package/pkg-web/nanocodex_bg.wasm.d.ts +27 -0
- package/pkg-web/package.json +1 -0
- package/runtime/code-runtime.mjs +191 -0
- package/scripts/check-package.mjs +38 -0
- package/types.d.mts +82 -0
|
Binary file
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/* tslint:disable */
|
|
2
|
+
/* eslint-disable */
|
|
3
|
+
export const memory: WebAssembly.Memory;
|
|
4
|
+
export const __wbg_nanocodex_free: (a: number, b: number) => void;
|
|
5
|
+
export const __wbg_turn_free: (a: number, b: number) => void;
|
|
6
|
+
export const nanocodex_fork: (a: number) => any;
|
|
7
|
+
export const nanocodex_forkFrom: (a: number, b: number) => any;
|
|
8
|
+
export const nanocodex_new: (a: number, b: number) => [number, number, number];
|
|
9
|
+
export const nanocodex_prompt: (a: number, b: number, c: number) => [number, number, number];
|
|
10
|
+
export const nanocodex_promptContent: (a: number, b: number, c: number) => [number, number, number];
|
|
11
|
+
export const nanocodex_sessionId: (a: number) => [number, number];
|
|
12
|
+
export const nanocodex_spawn: (a: number) => any;
|
|
13
|
+
export const turn_cancel: (a: number) => any;
|
|
14
|
+
export const turn_result: (a: number) => any;
|
|
15
|
+
export const turn_steer: (a: number, b: number, c: number) => any;
|
|
16
|
+
export const turn_steerContent: (a: number, b: number, c: number) => any;
|
|
17
|
+
export const wasm_bindgen__convert__closures_____invoke__h02eb5eea4c10a535: (a: number, b: number, c: any) => [number, number];
|
|
18
|
+
export const wasm_bindgen__convert__closures_____invoke__h179cb8eb2e8755ae: (a: number, b: number, c: any, d: any) => void;
|
|
19
|
+
export const __wbindgen_malloc: (a: number, b: number) => number;
|
|
20
|
+
export const __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
|
|
21
|
+
export const __wbindgen_exn_store: (a: number) => void;
|
|
22
|
+
export const __externref_table_alloc: () => number;
|
|
23
|
+
export const __wbindgen_externrefs: WebAssembly.Table;
|
|
24
|
+
export const __wbindgen_destroy_closure: (a: number, b: number) => void;
|
|
25
|
+
export const __externref_table_dealloc: (a: number) => void;
|
|
26
|
+
export const __wbindgen_free: (a: number, b: number, c: number) => void;
|
|
27
|
+
export const __wbindgen_start: () => void;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"type":"module"}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
export function createCodeRuntime(toolConfiguration = {}, extras = {}) {
|
|
2
|
+
const stores = new Map();
|
|
3
|
+
let nextCallId = 1;
|
|
4
|
+
const definitions = [];
|
|
5
|
+
|
|
6
|
+
for (const [name, tool] of Object.entries(toolConfiguration)) {
|
|
7
|
+
if (!tool || typeof tool.handler !== "function") {
|
|
8
|
+
throw new TypeError(`tool ${name} requires a handler function`);
|
|
9
|
+
}
|
|
10
|
+
definitions.push({
|
|
11
|
+
type: "function",
|
|
12
|
+
name,
|
|
13
|
+
description: tool.description || "Application-defined tool.",
|
|
14
|
+
strict: false,
|
|
15
|
+
parameters: tool.parameters || {
|
|
16
|
+
type: "object",
|
|
17
|
+
additionalProperties: true,
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
Object.freeze(definitions);
|
|
22
|
+
|
|
23
|
+
async function executeCode(source, sessionId = "default", parentCallId = "exec") {
|
|
24
|
+
const startedAt = performance.now();
|
|
25
|
+
const content = [];
|
|
26
|
+
const stored = stores.get(sessionId) || new Map();
|
|
27
|
+
stores.set(sessionId, stored);
|
|
28
|
+
const nestedCalls = [];
|
|
29
|
+
const tools = Object.create(null);
|
|
30
|
+
for (const [name, tool] of Object.entries(toolConfiguration)) {
|
|
31
|
+
tools[name] = async (input) => {
|
|
32
|
+
const callId = `${parentCallId}/code-${nextCallId++}`;
|
|
33
|
+
const toolStartedAt = performance.now();
|
|
34
|
+
try {
|
|
35
|
+
const result = await tool.handler(input, { sessionId, parentCallId, callId });
|
|
36
|
+
nestedCalls.push({
|
|
37
|
+
call_id: callId,
|
|
38
|
+
name,
|
|
39
|
+
// JSON.stringify omits object fields whose value is undefined.
|
|
40
|
+
// Keep zero-argument calls wire-complete for Rust's typed event.
|
|
41
|
+
input: clone(input) ?? null,
|
|
42
|
+
output: outputBody(result),
|
|
43
|
+
success: true,
|
|
44
|
+
duration_ns: elapsedNs(toolStartedAt),
|
|
45
|
+
});
|
|
46
|
+
return result;
|
|
47
|
+
} catch (error) {
|
|
48
|
+
nestedCalls.push({
|
|
49
|
+
call_id: callId,
|
|
50
|
+
name,
|
|
51
|
+
input: clone(input) ?? null,
|
|
52
|
+
output: errorMessage(error),
|
|
53
|
+
success: false,
|
|
54
|
+
duration_ns: elapsedNs(toolStartedAt),
|
|
55
|
+
});
|
|
56
|
+
throw error;
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
Object.freeze(tools);
|
|
61
|
+
const AsyncFunction = Object.getPrototypeOf(async function () {}).constructor;
|
|
62
|
+
const EXIT = Symbol("exit");
|
|
63
|
+
|
|
64
|
+
function text(value) {
|
|
65
|
+
content.push({ type: "input_text", text: stringify(value) });
|
|
66
|
+
}
|
|
67
|
+
function image(value, detail = "auto") {
|
|
68
|
+
if (typeof value === "string") {
|
|
69
|
+
content.push({ type: "input_image", image_url: value, detail });
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
if (!value || typeof value !== "object" || typeof value.image_url !== "string") {
|
|
73
|
+
throw new TypeError("image() requires an image URL or image item");
|
|
74
|
+
}
|
|
75
|
+
content.push({
|
|
76
|
+
type: "input_image",
|
|
77
|
+
image_url: value.image_url,
|
|
78
|
+
detail: value.detail == null ? detail : value.detail,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
function generatedImage(result) {
|
|
82
|
+
if (!result || typeof result !== "object" || typeof result.image_url !== "string") {
|
|
83
|
+
throw new TypeError("generatedImage() requires an image generation result");
|
|
84
|
+
}
|
|
85
|
+
image(result.image_url, "high");
|
|
86
|
+
if (typeof result.output_hint === "string" && result.output_hint) text(result.output_hint);
|
|
87
|
+
}
|
|
88
|
+
function store(key, value) {
|
|
89
|
+
if (typeof key !== "string") throw new TypeError("store key must be a string");
|
|
90
|
+
stored.set(key, clone(value));
|
|
91
|
+
}
|
|
92
|
+
function load(key) {
|
|
93
|
+
return stored.has(key) ? clone(stored.get(key)) : undefined;
|
|
94
|
+
}
|
|
95
|
+
function exit() {
|
|
96
|
+
throw EXIT;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
try {
|
|
100
|
+
const script = new AsyncFunction(
|
|
101
|
+
"tools",
|
|
102
|
+
"ALL_TOOLS",
|
|
103
|
+
"text",
|
|
104
|
+
"image",
|
|
105
|
+
"generatedImage",
|
|
106
|
+
"store",
|
|
107
|
+
"load",
|
|
108
|
+
"exit",
|
|
109
|
+
"require",
|
|
110
|
+
"console",
|
|
111
|
+
source,
|
|
112
|
+
);
|
|
113
|
+
try {
|
|
114
|
+
await script(
|
|
115
|
+
tools,
|
|
116
|
+
definitions,
|
|
117
|
+
text,
|
|
118
|
+
image,
|
|
119
|
+
generatedImage,
|
|
120
|
+
store,
|
|
121
|
+
load,
|
|
122
|
+
exit,
|
|
123
|
+
extras.require,
|
|
124
|
+
extras.console || console,
|
|
125
|
+
);
|
|
126
|
+
} catch (error) {
|
|
127
|
+
if (error !== EXIT) throw error;
|
|
128
|
+
}
|
|
129
|
+
return JSON.stringify({
|
|
130
|
+
output: withStatus("Script completed", startedAt, content),
|
|
131
|
+
success: true,
|
|
132
|
+
nested_calls: nestedCalls,
|
|
133
|
+
});
|
|
134
|
+
} catch (error) {
|
|
135
|
+
return JSON.stringify({
|
|
136
|
+
output: `Script failed\nWall time ${wallTime(startedAt)} seconds\nOutput:\n${errorMessage(error)}`,
|
|
137
|
+
success: false,
|
|
138
|
+
nested_calls: nestedCalls,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return Object.freeze({
|
|
144
|
+
executeCode,
|
|
145
|
+
toolDefinitions: () => JSON.stringify(definitions),
|
|
146
|
+
reset() {
|
|
147
|
+
stores.clear();
|
|
148
|
+
},
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function outputBody(value) {
|
|
153
|
+
if (Array.isArray(value) && value.every((item) => item?.type === "input_text" || item?.type === "input_image")) {
|
|
154
|
+
return value;
|
|
155
|
+
}
|
|
156
|
+
return stringify(value);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function stringify(value) {
|
|
160
|
+
if (typeof value === "string") return value;
|
|
161
|
+
if (value === undefined) return "undefined";
|
|
162
|
+
try {
|
|
163
|
+
return JSON.stringify(value);
|
|
164
|
+
} catch {
|
|
165
|
+
return String(value);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function clone(value) {
|
|
170
|
+
if (typeof globalThis.structuredClone === "function") return structuredClone(value);
|
|
171
|
+
return value === undefined ? undefined : JSON.parse(JSON.stringify(value));
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function errorMessage(error) {
|
|
175
|
+
if (error && (error.stack || error.message)) return error.stack || error.message;
|
|
176
|
+
return String(error);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function elapsedNs(startedAt) {
|
|
180
|
+
return Math.max(0, Math.round((performance.now() - startedAt) * 1_000_000));
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function wallTime(startedAt) {
|
|
184
|
+
return ((performance.now() - startedAt) / 1_000).toFixed(1);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function withStatus(status, startedAt, content) {
|
|
188
|
+
const heading = `${status}\nWall time ${wallTime(startedAt)} seconds\nOutput:\n`;
|
|
189
|
+
if (!content.length) return heading;
|
|
190
|
+
return [{ type: "input_text", text: heading }, ...content];
|
|
191
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { readFile, stat } from "node:fs/promises";
|
|
3
|
+
|
|
4
|
+
const root = new URL("../", import.meta.url);
|
|
5
|
+
const packageJson = JSON.parse(await readFile(new URL("package.json", root), "utf8"));
|
|
6
|
+
|
|
7
|
+
assert.equal(packageJson.name, "nanocodex");
|
|
8
|
+
assert.equal(packageJson.type, "module");
|
|
9
|
+
assert.equal(packageJson.publishConfig?.access, "public");
|
|
10
|
+
assert.equal(packageJson.exports?.["./browser"]?.import, "./browser/index.mjs");
|
|
11
|
+
assert.equal(packageJson.exports?.["./node"]?.import, "./node/index.mjs");
|
|
12
|
+
|
|
13
|
+
const requiredFiles = [
|
|
14
|
+
"browser/index.mjs",
|
|
15
|
+
"browser/index.d.mts",
|
|
16
|
+
"node/index.mjs",
|
|
17
|
+
"node/index.d.mts",
|
|
18
|
+
"pkg-web/nanocodex.js",
|
|
19
|
+
"pkg-web/nanocodex.d.ts",
|
|
20
|
+
"pkg-web/nanocodex_bg.wasm",
|
|
21
|
+
"pkg-node/nanocodex.js",
|
|
22
|
+
"pkg-node/nanocodex.d.ts",
|
|
23
|
+
"pkg-node/nanocodex_bg.wasm",
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
for (const file of requiredFiles) {
|
|
27
|
+
const metadata = await stat(new URL(file, root));
|
|
28
|
+
assert(metadata.isFile(), `${file} must be a file`);
|
|
29
|
+
assert(metadata.size > 0, `${file} must not be empty`);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
for (const target of ["web", "node"]) {
|
|
33
|
+
const wasm = await readFile(new URL(`pkg-${target}/nanocodex_bg.wasm`, root));
|
|
34
|
+
assert(wasm.byteLength > 100_000, `pkg-${target} WASM is unexpectedly small`);
|
|
35
|
+
assert.deepEqual([...wasm.subarray(0, 4)], [0x00, 0x61, 0x73, 0x6d]);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
console.log(`nanocodex@${packageJson.version} package artifacts are complete`);
|
package/types.d.mts
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
export type Thinking = "none" | "low" | "medium" | "high" | "xhigh" | "max";
|
|
2
|
+
export type ReasoningMode = "standard" | "pro";
|
|
3
|
+
|
|
4
|
+
export type PromptItem =
|
|
5
|
+
| { type: "text"; text: string }
|
|
6
|
+
| { type: "image"; image_url: string; detail?: "auto" | "low" | "high" | "original" | undefined }
|
|
7
|
+
| { type: "audio"; audio_url: string };
|
|
8
|
+
|
|
9
|
+
export type PromptInput = string | readonly PromptItem[];
|
|
10
|
+
|
|
11
|
+
export type AgentEvent = {
|
|
12
|
+
protocol_version: number;
|
|
13
|
+
request_id: string;
|
|
14
|
+
seq: number;
|
|
15
|
+
type: string;
|
|
16
|
+
payload: Record<string, unknown>;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export type AgentOptions = {
|
|
20
|
+
instructions?: string | undefined;
|
|
21
|
+
reasoningMode?: ReasoningMode | undefined;
|
|
22
|
+
sessionId?: string | undefined;
|
|
23
|
+
thinking?: Thinking | undefined;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export type ForkOptions = { at?: Turn | undefined };
|
|
27
|
+
export type WatchEventsOptions = { includeAllSessions?: boolean | undefined };
|
|
28
|
+
|
|
29
|
+
export type EventWatcher = Readonly<{
|
|
30
|
+
onEvent(listener: (event: AgentEvent) => void): () => void;
|
|
31
|
+
off(): void;
|
|
32
|
+
[Symbol.asyncIterator](): AsyncIterableIterator<AgentEvent>;
|
|
33
|
+
}>;
|
|
34
|
+
|
|
35
|
+
export type AgentActions = {
|
|
36
|
+
events: {
|
|
37
|
+
watch(options?: WatchEventsOptions): EventWatcher;
|
|
38
|
+
};
|
|
39
|
+
session: {
|
|
40
|
+
fork(options?: ForkOptions): Promise<DefaultAgent>;
|
|
41
|
+
spawn(): Promise<DefaultAgent>;
|
|
42
|
+
};
|
|
43
|
+
turn: {
|
|
44
|
+
prompt(options: { input: PromptInput }): Turn;
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export type Agent<extended extends object = {}> = {
|
|
49
|
+
readonly key: string;
|
|
50
|
+
readonly name: string;
|
|
51
|
+
readonly sessionId: string;
|
|
52
|
+
readonly type: string;
|
|
53
|
+
readonly uid: string;
|
|
54
|
+
extend<const extension extends object>(
|
|
55
|
+
decorator: (agent: Agent<extended>) => extension,
|
|
56
|
+
): Agent<extended & extension>;
|
|
57
|
+
dispose(): void;
|
|
58
|
+
} & extended;
|
|
59
|
+
|
|
60
|
+
export type DefaultAgent = Agent<AgentActions>;
|
|
61
|
+
|
|
62
|
+
export type Turn<agent extends Agent<object> = Agent<object>> = Readonly<{
|
|
63
|
+
readonly agent: agent;
|
|
64
|
+
result(): Promise<string>;
|
|
65
|
+
steer(options: { input: PromptInput }): Promise<void>;
|
|
66
|
+
cancel(): Promise<void>;
|
|
67
|
+
dispose(): void;
|
|
68
|
+
}>;
|
|
69
|
+
|
|
70
|
+
export type ToolContext = {
|
|
71
|
+
callId: string;
|
|
72
|
+
parentCallId: string;
|
|
73
|
+
sessionId: string;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
export type Tool = {
|
|
77
|
+
description: string;
|
|
78
|
+
parameters: Record<string, unknown>;
|
|
79
|
+
handler(input: unknown, context: ToolContext): unknown | Promise<unknown>;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
export type ToolMap = Record<string, Tool>;
|