pi-wasm-kit 0.0.0-stage → 0.2.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 (38) hide show
  1. package/LICENSE +21 -0
  2. package/NOTICE +11 -0
  3. package/README.md +206 -2
  4. package/dist/engine.d.ts +30 -0
  5. package/dist/engine.js +246 -0
  6. package/dist/guest/bridge.d.ts +1 -0
  7. package/dist/guest/bridge.js +14 -0
  8. package/dist/guest/entry.d.ts +1 -0
  9. package/dist/guest/entry.js +134 -0
  10. package/dist/guest/journal.d.ts +6 -0
  11. package/dist/guest/journal.js +21 -0
  12. package/dist/guest/platform.d.ts +1 -0
  13. package/dist/guest/platform.js +64 -0
  14. package/dist/guest-meta.json +1 -0
  15. package/dist/guest-source.d.ts +2 -0
  16. package/dist/guest-source.js +3 -0
  17. package/dist/guest.js +21929 -0
  18. package/dist/index.d.ts +104 -0
  19. package/dist/index.js +84 -0
  20. package/dist/licenses/@earendil-works__chord__LICENSE +21 -0
  21. package/dist/licenses/@earendil-works__pi-ai__LICENSE +21 -0
  22. package/dist/licenses/@earendil-works__pi-durable__LICENSE +21 -0
  23. package/dist/licenses/@jitl__quickjs-wasmfile-release-sync__LICENSE +47 -0
  24. package/dist/licenses/abort-controller__LICENSE +21 -0
  25. package/dist/licenses/event-target-shim__LICENSE +22 -0
  26. package/dist/licenses/manifest.json +47 -0
  27. package/dist/licenses/partial-json__LICENSE +21 -0
  28. package/dist/licenses/quickjs-emscripten-core__LICENSE +21 -0
  29. package/dist/licenses/typebox__license +23 -0
  30. package/dist/providers/openai.d.ts +8 -0
  31. package/dist/providers/openai.js +41 -0
  32. package/dist/storage/browser.d.ts +2 -0
  33. package/dist/storage/browser.js +75 -0
  34. package/dist/storage/node.d.ts +2 -0
  35. package/dist/storage/node.js +105 -0
  36. package/dist/types.d.ts +66 -0
  37. package/dist/types.js +1 -0
  38. package/package.json +75 -4
@@ -0,0 +1,104 @@
1
+ import type { AgentOptions, AgentConfiguration } from './types.js';
2
+ export type * from './types.js';
3
+ import type { SubmissionDraft, UserInput, ContextView, HarnessInspection, TaskGraph } from '@earendil-works/pi-durable';
4
+ export declare class PiWasmAgent {
5
+ private engine;
6
+ private options;
7
+ readonly conversationId: number;
8
+ private constructor();
9
+ static open(options: AgentOptions): Promise<PiWasmAgent>;
10
+ /** Start scheduling after opening with autoResume: false. */
11
+ resume(): Promise<void>;
12
+ submit(content: UserInput, requestId?: `${string}-${string}-${string}-${string}-${string}`, options?: {
13
+ conversationId?: number;
14
+ whenBusy?: 'steer' | 'followUp' | 'reject';
15
+ }): Promise<{
16
+ id: number;
17
+ }>;
18
+ submitDraft(submission: SubmissionDraft, conversationId?: number): Promise<{
19
+ id: number;
20
+ }>;
21
+ wait(id: number): Promise<({
22
+ readonly id: import("@earendil-works/pi-durable").SubmissionId;
23
+ readonly conversationId: import("@earendil-works/pi-durable").ConversationId;
24
+ readonly requestId?: string;
25
+ } & {
26
+ readonly type: "input";
27
+ } & {
28
+ readonly status: "done";
29
+ readonly entry: import("@earendil-works/pi-durable").EntryId;
30
+ readonly answer: import("@earendil-works/pi-durable").EntryId;
31
+ readonly reason?: never;
32
+ readonly detail?: never;
33
+ } & {
34
+ readonly status: "done" | "unanswered";
35
+ }) | ({
36
+ readonly id: import("@earendil-works/pi-durable").SubmissionId;
37
+ readonly conversationId: import("@earendil-works/pi-durable").ConversationId;
38
+ readonly requestId?: string;
39
+ } & {
40
+ readonly type: "input";
41
+ } & {
42
+ readonly status: "unanswered";
43
+ readonly entry?: import("@earendil-works/pi-durable").EntryId;
44
+ readonly answer?: never;
45
+ readonly reason: string;
46
+ readonly detail?: import("@earendil-works/chord").JsonValue;
47
+ } & {
48
+ readonly status: "done" | "unanswered";
49
+ }) | ({
50
+ readonly id: import("@earendil-works/pi-durable").SubmissionId;
51
+ readonly conversationId: import("@earendil-works/pi-durable").ConversationId;
52
+ readonly requestId?: string;
53
+ } & {
54
+ readonly type: "write";
55
+ } & {
56
+ readonly status: "done";
57
+ readonly entry: import("@earendil-works/pi-durable").EntryId;
58
+ readonly answer?: never;
59
+ readonly reason?: never;
60
+ readonly detail?: never;
61
+ } & {
62
+ readonly status: "done" | "unanswered";
63
+ }) | ({
64
+ readonly id: import("@earendil-works/pi-durable").SubmissionId;
65
+ readonly conversationId: import("@earendil-works/pi-durable").ConversationId;
66
+ readonly requestId?: string;
67
+ } & {
68
+ readonly type: "write";
69
+ } & {
70
+ readonly status: "unanswered";
71
+ readonly entry?: never;
72
+ readonly answer?: never;
73
+ readonly reason: string;
74
+ readonly detail?: import("@earendil-works/chord").JsonValue;
75
+ } & {
76
+ readonly status: "done" | "unanswered";
77
+ })>;
78
+ view(conversationId?: number): Promise<import("@earendil-works/pi-durable").ConversationView>;
79
+ context(conversationId?: number, at?: number): Promise<ContextView>;
80
+ configure(change: AgentConfiguration, conversationId?: number): Promise<void>;
81
+ createConversation(agent?: Omit<AgentConfiguration, 'extensions' | 'tools'>): Promise<{
82
+ conversationId: number;
83
+ }>;
84
+ fork(at: number, conversationId?: number): Promise<{
85
+ conversationId: number;
86
+ }>;
87
+ reset(handoff?: string, conversationId?: number): Promise<void>;
88
+ compact(instructions?: string, conversationId?: number): Promise<{
89
+ taskId: number;
90
+ }>;
91
+ waitForTask(id: number): Promise<any>;
92
+ inspect(): Promise<HarnessInspection>;
93
+ taskGraph(): Promise<TaskGraph>;
94
+ abort(conversationId?: number, background?: boolean): Promise<void>;
95
+ abortSubmission(id: number): Promise<string>;
96
+ watch(conversationId?: number): Promise<void>;
97
+ install(source: string): Promise<string>;
98
+ uninstall(name: string): Promise<void>;
99
+ /** Trusted application code inside WASM. Arguments/results must be JSON serializable. */
100
+ evaluate<T = unknown>(source: string, args?: unknown, conversationId?: number): Promise<T>;
101
+ close(): Promise<void>;
102
+ /** Abrupt shutdown: unfinished work stays persisted and resumes on next open. */
103
+ terminate(): Promise<void>;
104
+ }
package/dist/index.js ADDED
@@ -0,0 +1,84 @@
1
+ import { Engine } from './engine.js';
2
+ export class PiWasmAgent {
3
+ engine;
4
+ options;
5
+ conversationId;
6
+ constructor(engine, options, conversationId) {
7
+ this.engine = engine;
8
+ this.options = options;
9
+ this.conversationId = conversationId;
10
+ }
11
+ static async open(options) {
12
+ let engine;
13
+ try {
14
+ const names = new Set();
15
+ for (const tool of options.tools ?? []) {
16
+ if (names.has(tool.name))
17
+ throw new Error(`Duplicate tool: ${tool.name}`);
18
+ names.add(tool.name);
19
+ }
20
+ const emit = (event) => { try {
21
+ options.onEvent?.(event);
22
+ }
23
+ catch { /* Observers cannot affect commits. */ } };
24
+ engine = await Engine.create(async (method, data, signal, update) => {
25
+ if (signal.aborted)
26
+ throw new Error('Runtime terminated');
27
+ switch (method) {
28
+ case 'load': return options.journal.load();
29
+ case 'append':
30
+ await options.journal.append(data);
31
+ emit({ type: 'commit', seq: data.seq });
32
+ return null;
33
+ case 'model': return options.model(data, signal, update);
34
+ case 'tool': {
35
+ const tool = options.tools?.find(tool => tool.name === data.name);
36
+ if (!tool)
37
+ throw new Error(`Tool not granted: ${data.name}`);
38
+ return tool.execute(data.args, { taskId: data.taskId, conversationId: data.conversationId, callId: data.callId, signal, output: text => update({ type: 'output', text }) });
39
+ }
40
+ default: throw new Error(`Capability not granted: ${method}`);
41
+ }
42
+ }, emit, options.cpuSliceMs, options.memoryLimitBytes);
43
+ const opened = await engine.call('open', { tools: (options.tools ?? []).map(({ execute, ...definition }) => definition), instructions: options.instructions, modelId: options.modelId, contextWindow: options.contextWindow, extensions: options.extensions, settings: options.settings, events: !!options.onEvent, autoResume: options.autoResume });
44
+ return new PiWasmAgent(engine, options, opened.conversationId);
45
+ }
46
+ catch (error) {
47
+ await engine?.dispose();
48
+ await options.journal.close();
49
+ throw error;
50
+ }
51
+ }
52
+ /** Start scheduling after opening with autoResume: false. */
53
+ resume() { return this.engine.call('resume', {}); }
54
+ submit(content, requestId = crypto.randomUUID(), options = {}) {
55
+ return this.submitDraft({ type: 'input', content, requestId, whenBusy: options.whenBusy }, options.conversationId);
56
+ }
57
+ submitDraft(submission, conversationId) { return this.engine.call('submit', { submission, conversationId }); }
58
+ wait(id) { return this.engine.call('wait', { id }); }
59
+ view(conversationId) { return this.engine.call('view', { conversationId }); }
60
+ context(conversationId, at) { return this.engine.call('context', { conversationId, at }); }
61
+ configure(change, conversationId) { return this.engine.call('configure', { change, conversationId }); }
62
+ createConversation(agent) { return this.engine.call('createConversation', { agent }); }
63
+ fork(at, conversationId) { return this.engine.call('fork', { at, conversationId }); }
64
+ reset(handoff, conversationId) { return this.engine.call('reset', { handoff, conversationId }); }
65
+ compact(instructions, conversationId) { return this.engine.call('compact', { instructions, conversationId }); }
66
+ waitForTask(id) { return this.engine.call('waitForTask', { id }); }
67
+ inspect() { return this.engine.call('inspect'); }
68
+ taskGraph() { return this.engine.call('taskGraph'); }
69
+ abort(conversationId, background = false) { return this.engine.call('abort', { conversationId, background }); }
70
+ abortSubmission(id) { return this.engine.call('abortSubmission', { id }); }
71
+ watch(conversationId) { return this.engine.call('watch', { conversationId }); }
72
+ install(source) { return this.engine.call('install', { source }); }
73
+ uninstall(name) { return this.engine.call('uninstall', { name }); }
74
+ /** Trusted application code inside WASM. Arguments/results must be JSON serializable. */
75
+ evaluate(source, args = null, conversationId) { return this.engine.call('evaluate', { source, args, conversationId }); }
76
+ async close() { try {
77
+ await this.engine.call('close');
78
+ }
79
+ finally {
80
+ await this.terminate();
81
+ } }
82
+ /** Abrupt shutdown: unfinished work stays persisted and resumes on next open. */
83
+ async terminate() { await this.engine.dispose(); await this.options.journal.close(); }
84
+ }
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Mario Zechner
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Mario Zechner
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Mario Zechner
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,47 @@
1
+ quickjs-emscripten:
2
+ The MIT License
3
+
4
+ quickjs-emscripten copyright (c) 2019-2024 Jake Teton-Landis
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ SOFTWARE.
23
+
24
+
25
+ quickjs:
26
+ QuickJS Javascript Engine
27
+
28
+ Copyright (c) 2017-2021 Fabrice Bellard
29
+ Copyright (c) 2017-2021 Charlie Gordon
30
+
31
+ Permission is hereby granted, free of charge, to any person obtaining a copy
32
+ of this software and associated documentation files (the "Software"), to deal
33
+ in the Software without restriction, including without limitation the rights
34
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
35
+ copies of the Software, and to permit persons to whom the Software is
36
+ furnished to do so, subject to the following conditions:
37
+
38
+ The above copyright notice and this permission notice shall be included in
39
+ all copies or substantial portions of the Software.
40
+
41
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
42
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
43
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
44
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
45
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
46
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
47
+ THE SOFTWARE.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2017 Toru Nagashima
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,22 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Toru Nagashima
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
@@ -0,0 +1,47 @@
1
+ [
2
+ {
3
+ "name": "@earendil-works/chord",
4
+ "version": "1.1.0",
5
+ "license": "MIT"
6
+ },
7
+ {
8
+ "name": "@earendil-works/pi-ai",
9
+ "version": "1.1.0",
10
+ "license": "MIT"
11
+ },
12
+ {
13
+ "name": "@earendil-works/pi-durable",
14
+ "version": "1.1.0",
15
+ "license": "MIT"
16
+ },
17
+ {
18
+ "name": "@jitl/quickjs-wasmfile-release-sync",
19
+ "version": "0.32.0",
20
+ "license": "MIT"
21
+ },
22
+ {
23
+ "name": "abort-controller",
24
+ "version": "3.0.0",
25
+ "license": "MIT"
26
+ },
27
+ {
28
+ "name": "event-target-shim",
29
+ "version": "5.0.1",
30
+ "license": "MIT"
31
+ },
32
+ {
33
+ "name": "partial-json",
34
+ "version": "0.1.7",
35
+ "license": "MIT"
36
+ },
37
+ {
38
+ "name": "quickjs-emscripten-core",
39
+ "version": "0.32.0",
40
+ "license": "MIT"
41
+ },
42
+ {
43
+ "name": "typebox",
44
+ "version": "1.3.27",
45
+ "license": "MIT"
46
+ }
47
+ ]
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 Promplate Dev Team
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,21 @@
1
+ The MIT License
2
+
3
+ quickjs-emscripten copyright (c) 2019-2024 Jake Teton-Landis
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,23 @@
1
+ TypeBox
2
+
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2017-2026 Haydn Paterson
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
@@ -0,0 +1,8 @@
1
+ import type { ModelHandler } from '../types.js';
2
+ /** OpenAI Chat Completions-compatible transport. Credentials never enter WASM. */
3
+ export declare function openAICompatible(options: {
4
+ baseURL: string;
5
+ model: string;
6
+ apiKey?: string;
7
+ timeoutMs?: number;
8
+ }): ModelHandler;
@@ -0,0 +1,41 @@
1
+ import { getSystemMessageText } from '@earendil-works/pi-ai/utils/text';
2
+ import { getCurrentTools } from '@earendil-works/pi-ai/utils/transcript';
3
+ /** OpenAI Chat Completions-compatible transport. Credentials never enter WASM. */
4
+ export function openAICompatible(options) {
5
+ return async ({ context }, signal) => {
6
+ const messages = context.messages.map((message) => {
7
+ if (message.role === 'system')
8
+ return { role: 'system', content: getSystemMessageText(message) };
9
+ if (message.role === 'user')
10
+ return { role: 'user', content: typeof message.content === 'string' ? message.content : message.content.map((x) => { if (x.type === 'text')
11
+ return { type: 'text', text: x.text }; if (x.type === 'image')
12
+ return { type: 'image_url', image_url: { url: `data:${x.mimeType};base64,${x.data}` } }; throw new Error(`Unsupported user content: ${x.type}`); }) };
13
+ if (message.role === 'toolResult') {
14
+ if (message.content.some((x) => x.type !== 'text'))
15
+ throw new Error('HTTP adapter supports text tool results only');
16
+ return { role: 'tool', tool_call_id: message.toolCallId, content: message.content.filter((x) => x.type === 'text').map((x) => x.text).join('\n') };
17
+ }
18
+ const text = message.content.filter((x) => x.type === 'text').map((x) => x.text).join('');
19
+ const calls = message.content.filter((x) => x.type === 'toolCall').map((x) => ({ id: x.id, type: 'function', function: { name: x.name, arguments: JSON.stringify(x.arguments) } }));
20
+ return { role: 'assistant', content: text || null, ...(calls.length ? { tool_calls: calls } : {}) };
21
+ });
22
+ const tools = getCurrentTools(context.messages).map(tool => ({ type: 'function', function: { name: tool.name, description: tool.description, parameters: tool.parameters } }));
23
+ const response = await fetch(`${options.baseURL.replace(/\/$/, '')}/chat/completions`, {
24
+ method: 'POST', headers: { 'Content-Type': 'application/json', ...(options.apiKey ? { Authorization: `Bearer ${options.apiKey}` } : {}) },
25
+ body: JSON.stringify({ model: options.model, messages, ...(tools.length ? { tools } : {}), stream: false }),
26
+ signal: AbortSignal.any([signal, AbortSignal.timeout(options.timeoutMs ?? 60000)]),
27
+ });
28
+ if (!response.ok)
29
+ throw new Error(`Model request failed: HTTP ${response.status}`);
30
+ const data = await response.json();
31
+ const choice = data.choices?.[0];
32
+ if (!choice?.message)
33
+ throw new Error('Model returned no assistant message');
34
+ const content = [];
35
+ if (choice.message.content)
36
+ content.push({ type: 'text', text: choice.message.content });
37
+ for (const call of choice.message.tool_calls ?? [])
38
+ content.push({ type: 'toolCall', id: call.id, name: call.function.name, arguments: JSON.parse(call.function.arguments) });
39
+ return { role: 'assistant', content, api: 'host', provider: 'host', model: options.model, stopReason: content.some(x => x.type === 'toolCall') ? 'toolUse' : choice.finish_reason === 'length' ? 'length' : 'stop', usage: { input: data.usage?.prompt_tokens ?? 0, output: data.usage?.completion_tokens ?? 0, totalTokens: data.usage?.total_tokens ?? 0, cacheRead: 0, cacheWrite: 0, cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 } }, timestamp: Date.now() };
40
+ };
41
+ }
@@ -0,0 +1,2 @@
1
+ import type { Journal } from '../types.js';
2
+ export declare function openBrowserJournal(name: string): Promise<Journal>;
@@ -0,0 +1,75 @@
1
+ export async function openBrowserJournal(name) {
2
+ if (!globalThis.navigator?.locks)
3
+ throw new Error('Web Locks requires a secure browser context (HTTPS or localhost)');
4
+ let unlock;
5
+ let acquired;
6
+ let denied;
7
+ const ready = new Promise((resolve, reject) => { acquired = resolve; denied = reject; });
8
+ const lock = navigator.locks.request(`pi-wasm-kit:${name}`, { ifAvailable: true }, async (token) => {
9
+ if (!token) {
10
+ denied(new Error('This journal is already open in another tab'));
11
+ return;
12
+ }
13
+ await new Promise(resolve => { unlock = resolve; acquired(); });
14
+ });
15
+ void lock.catch(denied);
16
+ await ready;
17
+ let db;
18
+ try {
19
+ db = await new Promise((resolve, reject) => {
20
+ const request = indexedDB.open(`pi-wasm-kit:${name}`, 1);
21
+ request.onupgradeneeded = () => request.result.createObjectStore('commits', { keyPath: 'seq' });
22
+ request.onsuccess = () => resolve(request.result);
23
+ request.onerror = () => reject(request.error);
24
+ });
25
+ }
26
+ catch (error) {
27
+ unlock();
28
+ await lock;
29
+ throw error;
30
+ }
31
+ let closed = false;
32
+ let tail = Promise.resolve();
33
+ return {
34
+ async load() {
35
+ if (closed)
36
+ throw new Error('Journal is closed');
37
+ return new Promise((resolve, reject) => {
38
+ const tx = db.transaction('commits', 'readonly');
39
+ const request = tx.objectStore('commits').getAll();
40
+ tx.oncomplete = () => resolve(request.result);
41
+ tx.onabort = () => reject(tx.error ?? new Error('Journal read aborted'));
42
+ });
43
+ },
44
+ append(commit) {
45
+ if (closed)
46
+ return Promise.reject(new Error('Journal is closed'));
47
+ const snapshot = structuredClone(commit);
48
+ const write = tail.then(() => new Promise((resolve, reject) => {
49
+ const tx = db.transaction('commits', 'readwrite', { durability: 'strict' });
50
+ const store = tx.objectStore('commits');
51
+ const count = store.count();
52
+ count.onsuccess = () => {
53
+ if (snapshot.seq !== count.result + 1) {
54
+ tx.abort();
55
+ return;
56
+ }
57
+ store.add(snapshot);
58
+ };
59
+ tx.oncomplete = () => resolve();
60
+ tx.onabort = () => reject(tx.error ?? new Error('Journal sequence conflict'));
61
+ }));
62
+ tail = write;
63
+ return write;
64
+ },
65
+ async close() { if (closed)
66
+ return; closed = true; try {
67
+ await tail;
68
+ }
69
+ finally {
70
+ db.close();
71
+ unlock();
72
+ await lock;
73
+ } },
74
+ };
75
+ }
@@ -0,0 +1,2 @@
1
+ import type { Journal } from '../types.js';
2
+ export declare function openNodeJournal(path: string): Promise<Journal>;