@ai-ecoverse/slicc-agent 1.1.0 → 2.0.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.
- package/README.md +20 -5
- package/dist/client.js +64 -0
- package/dist/client.js.map +7 -0
- package/dist/control.js +61 -0
- package/dist/control.js.map +7 -0
- package/dist/host.js +82 -0
- package/dist/host.js.map +7 -0
- package/dist/index.js +36 -4
- package/dist/index.js.map +2 -2
- package/dist/opfs-pool.js +55 -0
- package/dist/opfs-pool.js.map +7 -0
- package/dist/owner.js +34 -0
- package/dist/owner.js.map +7 -0
- package/dist/services.js +12 -0
- package/dist/services.js.map +7 -0
- package/dist/sqlite.js +158 -0
- package/dist/sqlite.js.map +7 -0
- package/dist/types/client.d.ts +14 -0
- package/dist/types/control.d.ts +4 -0
- package/dist/types/host.d.ts +13 -0
- package/dist/types/index.d.ts +8 -1
- package/dist/types/opfs-pool.d.ts +9 -0
- package/dist/types/owner.d.ts +21 -0
- package/dist/types/services.d.ts +50 -0
- package/dist/types/sqlite.d.ts +39 -0
- package/dist/types/wire.d.ts +27 -0
- package/dist/types/worker.d.ts +5 -0
- package/dist/wire.js +164 -0
- package/dist/wire.js.map +7 -0
- package/dist/worker.js +14 -0
- package/dist/worker.js.map +7 -0
- package/package.json +6 -2
- package/dist/port.js +0 -57
- package/dist/port.js.map +0 -7
- package/dist/types/port.d.ts +0 -23
package/README.md
CHANGED
|
@@ -6,17 +6,32 @@ The package ships unbundled ESM: `dist/` holds one file per source file and impo
|
|
|
6
6
|
|
|
7
7
|
## Use
|
|
8
8
|
|
|
9
|
+
In the agent's dedicated worker:
|
|
10
|
+
|
|
9
11
|
```js
|
|
10
|
-
import {
|
|
12
|
+
import { hostAgent, openAgent, openOpfsSqliteStorage, serveConnections } from '@ai-ecoverse/slicc-agent';
|
|
13
|
+
|
|
14
|
+
const storage = await openOpfsSqliteStorage();
|
|
15
|
+
serveConnections(self, openAgent({ models, model, storage }).then((agent) => hostAgent(agent)));
|
|
16
|
+
```
|
|
11
17
|
|
|
12
|
-
|
|
18
|
+
In the page:
|
|
13
19
|
|
|
14
|
-
|
|
20
|
+
```js
|
|
21
|
+
import { startAgent } from '@ai-ecoverse/slicc-agent';
|
|
22
|
+
|
|
23
|
+
const owner = await startAgent({ worker: () => new Worker(workerUrl, { type: 'module' }) });
|
|
24
|
+
const agent = await owner.connect();
|
|
25
|
+
agent.transcript.subscribe((view) => render(view));
|
|
15
26
|
await agent.prompt('What is in /home?');
|
|
16
27
|
```
|
|
17
28
|
|
|
18
|
-
- `openAgent({ models, model, storage?, registry?, settings? })` opens a durable Harness (in memory unless `storage` is given) and its root conversation
|
|
19
|
-
- `
|
|
29
|
+
- `openAgent({ models, model, storage?, registry?, settings? })` opens a durable Harness (in memory unless `storage` is given) and its root conversation, and resumes work a previous worker left unfinished.
|
|
30
|
+
- `openOpfsSqliteStorage({ directory?, file? })` keeps the session in wasm SQLite ([`@sqlite.org/sqlite-wasm`](https://www.npmjs.com/package/@sqlite.org/sqlite-wasm)) in an `opfs-sahpool` pool, by default `/.slicc/agent/` in OPFS. It needs a dedicated worker. Only one worker holds a pool at a time: the opener takes a Web Lock per directory and waits until every file of the pool can be opened before sqlite-wasm installs it, because a failed install deletes the pool's directory. `openMemorySqliteStorage()` keeps the same database in memory.
|
|
31
|
+
- `hostAgent(agent)` serves the agent with pi's protocol: a pi-server with one session, `agent`, whose Chord services are `slicc.agent.control` (send with `whenBusy` `steer`, `followUp` or `reject`, wait, withdraw, abort, compact) and `slicc.agent.transcript` (the conversation's durable view as replicated state). `serveConnections(self, host)` hands it every `MessagePort` the page sends.
|
|
32
|
+
- `startAgent({ worker })` takes the Web Lock `slicc-agent`, so one tab at a time owns the agent worker, and waits while another tab holds it. `connect()` returns an `AgentConnection` (`control`, `transcript`, `prompt()`), `restart()` replaces the worker (durable resumes from SQLite), and `release()` stops it and lets the next tab take over.
|
|
33
|
+
|
|
34
|
+
pi-server 1.0.4 calls `unref()` on a `setTimeout` handle when it accepts a connection, which browsers don't have. `PortListener` wraps that one call so its timers get a no-op `unref`.
|
|
20
35
|
|
|
21
36
|
## Develop
|
|
22
37
|
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createRemoteServiceBinding
|
|
3
|
+
} from "@earendil-works/chord";
|
|
4
|
+
import { BACKGROUND_CONTEXT } from "@earendil-works/chord/context";
|
|
5
|
+
import { Client, createClientServiceTransport } from "@earendil-works/pi-client";
|
|
6
|
+
import {
|
|
7
|
+
AGENT_SESSION,
|
|
8
|
+
AgentControl,
|
|
9
|
+
AgentSessions,
|
|
10
|
+
AgentTranscript
|
|
11
|
+
} from "./services.js";
|
|
12
|
+
import { PortChannel } from "./wire.js";
|
|
13
|
+
function attached(client) {
|
|
14
|
+
return new Promise((resolve) => {
|
|
15
|
+
const stop = client.onAttachmentChange(() => {
|
|
16
|
+
stop();
|
|
17
|
+
resolve();
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
async function connectAgent(endpoint, options = {}) {
|
|
22
|
+
const context = options.context ?? BACKGROUND_CONTEXT;
|
|
23
|
+
const channel = new PortChannel(endpoint);
|
|
24
|
+
const serverId = await channel.hello;
|
|
25
|
+
const client = await Client.connect({ serverId, transportFactory: channel.transport });
|
|
26
|
+
const server = createRemoteServiceBinding({
|
|
27
|
+
services: [AgentSessions],
|
|
28
|
+
transport: createClientServiceTransport(client, () => ({ serverId })),
|
|
29
|
+
bound: true
|
|
30
|
+
});
|
|
31
|
+
const ready = attached(client);
|
|
32
|
+
await server.use(AgentSessions).attach(AGENT_SESSION, context);
|
|
33
|
+
await ready;
|
|
34
|
+
const session = createRemoteServiceBinding({
|
|
35
|
+
services: [AgentControl, AgentTranscript],
|
|
36
|
+
transport: createClientServiceTransport(client, () => client.attachment),
|
|
37
|
+
bound: true
|
|
38
|
+
});
|
|
39
|
+
const control = session.use(AgentControl);
|
|
40
|
+
const transcript = session.use(AgentTranscript).state;
|
|
41
|
+
await session.ready(context);
|
|
42
|
+
return {
|
|
43
|
+
serverId,
|
|
44
|
+
control,
|
|
45
|
+
transcript,
|
|
46
|
+
async prompt(text, whenBusy = "followUp") {
|
|
47
|
+
const sent = await control.send({ text, whenBusy, requestId: null }, context);
|
|
48
|
+
if (!sent.accepted) throw new Error(`${sent.error.code}: ${sent.error.message}`);
|
|
49
|
+
const settled = await control.wait(sent.submissionId, context);
|
|
50
|
+
if (settled.status !== "done") throw new Error(`unanswered: ${settled.reason}`);
|
|
51
|
+
return settled.text;
|
|
52
|
+
},
|
|
53
|
+
async close() {
|
|
54
|
+
await server.use(AgentSessions).detach(context);
|
|
55
|
+
await session.dispose(context);
|
|
56
|
+
await server.dispose(context);
|
|
57
|
+
await client.dispose();
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
export {
|
|
62
|
+
connectAgent
|
|
63
|
+
};
|
|
64
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/client.ts"],
|
|
4
|
+
"sourcesContent": ["import {\n type Context,\n createRemoteServiceBinding,\n type ReplicatedState,\n} from '@earendil-works/chord';\nimport { BACKGROUND_CONTEXT } from '@earendil-works/chord/context';\nimport { Client, createClientServiceTransport } from '@earendil-works/pi-client';\nimport type { ConversationView } from '@earendil-works/pi-durable';\nimport {\n AGENT_SESSION,\n AgentControl,\n AgentSessions,\n AgentTranscript,\n type SendMode,\n} from './services.ts';\nimport { PortChannel, type PortEndpoint } from './wire.ts';\n\nexport interface AgentConnection {\n readonly serverId: string;\n readonly control: AgentControl;\n readonly transcript: ReplicatedState<ConversationView>;\n prompt(text: string, whenBusy?: SendMode): Promise<string>;\n close(): Promise<void>;\n}\n\nfunction attached(client: Client): Promise<void> {\n return new Promise((resolve) => {\n const stop = client.onAttachmentChange(() => {\n stop();\n resolve();\n });\n });\n}\n\nexport async function connectAgent(\n endpoint: PortEndpoint,\n options: { context?: Context } = {}\n): Promise<AgentConnection> {\n const context = options.context ?? BACKGROUND_CONTEXT;\n const channel = new PortChannel(endpoint);\n const serverId = await channel.hello;\n const client = await Client.connect({ serverId, transportFactory: channel.transport });\n const server = createRemoteServiceBinding({\n services: [AgentSessions],\n transport: createClientServiceTransport(client, () => ({ serverId })),\n bound: true,\n });\n const ready = attached(client);\n await server.use(AgentSessions).attach(AGENT_SESSION, context);\n await ready;\n const session = createRemoteServiceBinding({\n services: [AgentControl, AgentTranscript],\n transport: createClientServiceTransport(client, () => client.attachment),\n bound: true,\n });\n const control = session.use(AgentControl);\n const transcript = session.use(AgentTranscript).state;\n await session.ready(context);\n return {\n serverId,\n control,\n transcript,\n async prompt(text, whenBusy = 'followUp') {\n const sent = await control.send({ text, whenBusy, requestId: null }, context);\n if (!sent.accepted) throw new Error(`${sent.error.code}: ${sent.error.message}`);\n const settled = await control.wait(sent.submissionId, context);\n if (settled.status !== 'done') throw new Error(`unanswered: ${settled.reason}`);\n return settled.text;\n },\n async close() {\n await server.use(AgentSessions).detach(context);\n await session.dispose(context);\n await server.dispose(context);\n await client.dispose();\n },\n };\n}\n"],
|
|
5
|
+
"mappings": "AAAA;AAAA,EAEE;AAAA,OAEK;AACP,SAAS,0BAA0B;AACnC,SAAS,QAAQ,oCAAoC;AAErD;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AACP,SAAS,mBAAsC;AAU/C,SAAS,SAAS,QAA+B;AAC/C,SAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,UAAM,OAAO,OAAO,mBAAmB,MAAM;AAC3C,WAAK;AACL,cAAQ;AAAA,IACV,CAAC;AAAA,EACH,CAAC;AACH;AAEA,eAAsB,aACpB,UACA,UAAiC,CAAC,GACR;AAC1B,QAAM,UAAU,QAAQ,WAAW;AACnC,QAAM,UAAU,IAAI,YAAY,QAAQ;AACxC,QAAM,WAAW,MAAM,QAAQ;AAC/B,QAAM,SAAS,MAAM,OAAO,QAAQ,EAAE,UAAU,kBAAkB,QAAQ,UAAU,CAAC;AACrF,QAAM,SAAS,2BAA2B;AAAA,IACxC,UAAU,CAAC,aAAa;AAAA,IACxB,WAAW,6BAA6B,QAAQ,OAAO,EAAE,SAAS,EAAE;AAAA,IACpE,OAAO;AAAA,EACT,CAAC;AACD,QAAM,QAAQ,SAAS,MAAM;AAC7B,QAAM,OAAO,IAAI,aAAa,EAAE,OAAO,eAAe,OAAO;AAC7D,QAAM;AACN,QAAM,UAAU,2BAA2B;AAAA,IACzC,UAAU,CAAC,cAAc,eAAe;AAAA,IACxC,WAAW,6BAA6B,QAAQ,MAAM,OAAO,UAAU;AAAA,IACvE,OAAO;AAAA,EACT,CAAC;AACD,QAAM,UAAU,QAAQ,IAAI,YAAY;AACxC,QAAM,aAAa,QAAQ,IAAI,eAAe,EAAE;AAChD,QAAM,QAAQ,MAAM,OAAO;AAC3B,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,MAAM,OAAO,MAAM,WAAW,YAAY;AACxC,YAAM,OAAO,MAAM,QAAQ,KAAK,EAAE,MAAM,UAAU,WAAW,KAAK,GAAG,OAAO;AAC5E,UAAI,CAAC,KAAK,SAAU,OAAM,IAAI,MAAM,GAAG,KAAK,MAAM,IAAI,KAAK,KAAK,MAAM,OAAO,EAAE;AAC/E,YAAM,UAAU,MAAM,QAAQ,KAAK,KAAK,cAAc,OAAO;AAC7D,UAAI,QAAQ,WAAW,OAAQ,OAAM,IAAI,MAAM,eAAe,QAAQ,MAAM,EAAE;AAC9E,aAAO,QAAQ;AAAA,IACjB;AAAA,IACA,MAAM,QAAQ;AACZ,YAAM,OAAO,IAAI,aAAa,EAAE,OAAO,OAAO;AAC9C,YAAM,QAAQ,QAAQ,OAAO;AAC7B,YAAM,OAAO,QAAQ,OAAO;AAC5B,YAAM,OAAO,QAAQ;AAAA,IACvB;AAAA,EACF;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/control.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AssistantEntry,
|
|
3
|
+
ConversationBusy
|
|
4
|
+
} from "@earendil-works/pi-durable";
|
|
5
|
+
import { answerText } from "./agent.js";
|
|
6
|
+
function submissionId(value) {
|
|
7
|
+
const id = Number(value);
|
|
8
|
+
return /^[1-9][0-9]*$/.test(value) && Number.isSafeInteger(id) ? id : void 0;
|
|
9
|
+
}
|
|
10
|
+
function operationError(error) {
|
|
11
|
+
if (error instanceof ConversationBusy) return { code: "busy", message: error.message };
|
|
12
|
+
return { code: "failed", message: error instanceof Error ? error.message : String(error) };
|
|
13
|
+
}
|
|
14
|
+
async function accepted(operation) {
|
|
15
|
+
try {
|
|
16
|
+
const result = await operation();
|
|
17
|
+
const id = typeof result === "number" ? result : result.id;
|
|
18
|
+
return { accepted: true, submissionId: String(id), error: null };
|
|
19
|
+
} catch (error) {
|
|
20
|
+
return { accepted: false, submissionId: null, error: operationError(error) };
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function createAgentControl(harness, conversation) {
|
|
24
|
+
return {
|
|
25
|
+
send: (request, context) => accepted(
|
|
26
|
+
() => conversation.submit(
|
|
27
|
+
{
|
|
28
|
+
type: "input",
|
|
29
|
+
content: request.text,
|
|
30
|
+
whenBusy: request.whenBusy,
|
|
31
|
+
...request.requestId ? { requestId: request.requestId } : {}
|
|
32
|
+
},
|
|
33
|
+
context
|
|
34
|
+
)
|
|
35
|
+
),
|
|
36
|
+
async wait(id, context) {
|
|
37
|
+
const parsed = submissionId(id);
|
|
38
|
+
const submission = parsed === void 0 ? void 0 : await harness.submission(parsed, context);
|
|
39
|
+
if (!submission) throw new Error(`unknown submission ${id}`);
|
|
40
|
+
const settled = await submission.wait(context);
|
|
41
|
+
const { answer, reason } = settled;
|
|
42
|
+
if (answer === void 0) return { status: "unanswered", text: null, reason: String(reason) };
|
|
43
|
+
const entry = await harness.commit((tx) => tx.entry(AssistantEntry, answer), context);
|
|
44
|
+
return { status: "done", text: answerText(entry?.model?.[0]?.content), reason: null };
|
|
45
|
+
},
|
|
46
|
+
async withdraw(id, context) {
|
|
47
|
+
const parsed = submissionId(id);
|
|
48
|
+
if (parsed === void 0) return { outcome: "not_found" };
|
|
49
|
+
const result = await harness.abortSubmission(parsed, context, conversation.id);
|
|
50
|
+
if (result === "aborted") return { outcome: "withdrawn" };
|
|
51
|
+
return { outcome: result === "not_found" ? "not_found" : "already_placed" };
|
|
52
|
+
},
|
|
53
|
+
abort: (context) => conversation.abort(context),
|
|
54
|
+
compact: (instructions, context) => accepted(() => conversation.compact(instructions ?? void 0, context))
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
export {
|
|
58
|
+
createAgentControl,
|
|
59
|
+
submissionId
|
|
60
|
+
};
|
|
61
|
+
//# sourceMappingURL=control.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/control.ts"],
|
|
4
|
+
"sourcesContent": ["import {\n AssistantEntry,\n type Conversation,\n ConversationBusy,\n type EntryId,\n type Harness,\n type SubmissionId,\n} from '@earendil-works/pi-durable';\nimport { answerText } from './agent.ts';\nimport type { AgentControl, OperationError, SendResponse } from './services.ts';\n\nexport function submissionId(value: string): SubmissionId | undefined {\n const id = Number(value);\n return /^[1-9][0-9]*$/.test(value) && Number.isSafeInteger(id) ? (id as SubmissionId) : undefined;\n}\n\nfunction operationError(error: unknown): OperationError {\n if (error instanceof ConversationBusy) return { code: 'busy', message: error.message };\n return { code: 'failed', message: error instanceof Error ? error.message : String(error) };\n}\n\nasync function accepted(operation: () => Promise<{ id: number } | number>): Promise<SendResponse> {\n try {\n const result = await operation();\n const id = typeof result === 'number' ? result : result.id;\n return { accepted: true, submissionId: String(id), error: null };\n } catch (error) {\n return { accepted: false, submissionId: null, error: operationError(error) };\n }\n}\n\nexport function createAgentControl(harness: Harness, conversation: Conversation): AgentControl {\n return {\n send: (request, context) =>\n accepted(() =>\n conversation.submit(\n {\n type: 'input',\n content: request.text,\n whenBusy: request.whenBusy,\n ...(request.requestId ? { requestId: request.requestId } : {}),\n },\n context\n )\n ),\n async wait(id, context) {\n const parsed = submissionId(id);\n const submission =\n parsed === undefined ? undefined : await harness.submission(parsed, context);\n if (!submission) throw new Error(`unknown submission ${id}`);\n const settled = await submission.wait(context);\n const { answer, reason } = settled as { answer?: EntryId; reason?: string };\n if (answer === undefined) return { status: 'unanswered', text: null, reason: String(reason) };\n const entry = await harness.commit((tx) => tx.entry(AssistantEntry, answer), context);\n return { status: 'done', text: answerText(entry?.model?.[0]?.content), reason: null };\n },\n async withdraw(id, context) {\n const parsed = submissionId(id);\n if (parsed === undefined) return { outcome: 'not_found' };\n const result = await harness.abortSubmission(parsed, context, conversation.id);\n if (result === 'aborted') return { outcome: 'withdrawn' };\n return { outcome: result === 'not_found' ? 'not_found' : 'already_placed' };\n },\n abort: (context) => conversation.abort(context),\n compact: (instructions, context) =>\n accepted(() => conversation.compact(instructions ?? undefined, context)),\n };\n}\n"],
|
|
5
|
+
"mappings": "AAAA;AAAA,EACE;AAAA,EAEA;AAAA,OAIK;AACP,SAAS,kBAAkB;AAGpB,SAAS,aAAa,OAAyC;AACpE,QAAM,KAAK,OAAO,KAAK;AACvB,SAAO,gBAAgB,KAAK,KAAK,KAAK,OAAO,cAAc,EAAE,IAAK,KAAsB;AAC1F;AAEA,SAAS,eAAe,OAAgC;AACtD,MAAI,iBAAiB,iBAAkB,QAAO,EAAE,MAAM,QAAQ,SAAS,MAAM,QAAQ;AACrF,SAAO,EAAE,MAAM,UAAU,SAAS,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,EAAE;AAC3F;AAEA,eAAe,SAAS,WAA0E;AAChG,MAAI;AACF,UAAM,SAAS,MAAM,UAAU;AAC/B,UAAM,KAAK,OAAO,WAAW,WAAW,SAAS,OAAO;AACxD,WAAO,EAAE,UAAU,MAAM,cAAc,OAAO,EAAE,GAAG,OAAO,KAAK;AAAA,EACjE,SAAS,OAAO;AACd,WAAO,EAAE,UAAU,OAAO,cAAc,MAAM,OAAO,eAAe,KAAK,EAAE;AAAA,EAC7E;AACF;AAEO,SAAS,mBAAmB,SAAkB,cAA0C;AAC7F,SAAO;AAAA,IACL,MAAM,CAAC,SAAS,YACd;AAAA,MAAS,MACP,aAAa;AAAA,QACX;AAAA,UACE,MAAM;AAAA,UACN,SAAS,QAAQ;AAAA,UACjB,UAAU,QAAQ;AAAA,UAClB,GAAI,QAAQ,YAAY,EAAE,WAAW,QAAQ,UAAU,IAAI,CAAC;AAAA,QAC9D;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,IACF,MAAM,KAAK,IAAI,SAAS;AACtB,YAAM,SAAS,aAAa,EAAE;AAC9B,YAAM,aACJ,WAAW,SAAY,SAAY,MAAM,QAAQ,WAAW,QAAQ,OAAO;AAC7E,UAAI,CAAC,WAAY,OAAM,IAAI,MAAM,sBAAsB,EAAE,EAAE;AAC3D,YAAM,UAAU,MAAM,WAAW,KAAK,OAAO;AAC7C,YAAM,EAAE,QAAQ,OAAO,IAAI;AAC3B,UAAI,WAAW,OAAW,QAAO,EAAE,QAAQ,cAAc,MAAM,MAAM,QAAQ,OAAO,MAAM,EAAE;AAC5F,YAAM,QAAQ,MAAM,QAAQ,OAAO,CAAC,OAAO,GAAG,MAAM,gBAAgB,MAAM,GAAG,OAAO;AACpF,aAAO,EAAE,QAAQ,QAAQ,MAAM,WAAW,OAAO,QAAQ,CAAC,GAAG,OAAO,GAAG,QAAQ,KAAK;AAAA,IACtF;AAAA,IACA,MAAM,SAAS,IAAI,SAAS;AAC1B,YAAM,SAAS,aAAa,EAAE;AAC9B,UAAI,WAAW,OAAW,QAAO,EAAE,SAAS,YAAY;AACxD,YAAM,SAAS,MAAM,QAAQ,gBAAgB,QAAQ,SAAS,aAAa,EAAE;AAC7E,UAAI,WAAW,UAAW,QAAO,EAAE,SAAS,YAAY;AACxD,aAAO,EAAE,SAAS,WAAW,cAAc,cAAc,iBAAiB;AAAA,IAC5E;AAAA,IACA,OAAO,CAAC,YAAY,aAAa,MAAM,OAAO;AAAA,IAC9C,SAAS,CAAC,cAAc,YACtB,SAAS,MAAM,aAAa,QAAQ,gBAAgB,QAAW,OAAO,CAAC;AAAA,EAC3E;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/host.js
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createRemoteServiceEndpoint,
|
|
3
|
+
RemoteServiceProvider
|
|
4
|
+
} from "@earendil-works/chord";
|
|
5
|
+
import { BACKGROUND_CONTEXT } from "@earendil-works/chord/context";
|
|
6
|
+
import {
|
|
7
|
+
Server,
|
|
8
|
+
SessionNotFoundError
|
|
9
|
+
} from "@earendil-works/pi-server";
|
|
10
|
+
import { createAgentControl } from "./control.js";
|
|
11
|
+
import { AGENT_SESSION, AgentControl, AgentSessions, AgentTranscript } from "./services.js";
|
|
12
|
+
import { PortListener } from "./wire.js";
|
|
13
|
+
function endpointFor(provider) {
|
|
14
|
+
const endpoint = createRemoteServiceEndpoint(provider);
|
|
15
|
+
return {
|
|
16
|
+
invokeService: (call, publish, context) => endpoint.invoke(call, (id, update) => publish(id, update, context), context),
|
|
17
|
+
release: () => endpoint.dispose()
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
function sessionServices(provider) {
|
|
21
|
+
return {
|
|
22
|
+
attachClient: () => endpointFor(provider),
|
|
23
|
+
close: async () => {
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
function serverServices() {
|
|
28
|
+
return {
|
|
29
|
+
attachClient(presentation) {
|
|
30
|
+
const provider = new RemoteServiceProvider([{ service: AgentSessions, mode: "singleton" }]);
|
|
31
|
+
provider.provide(AgentSessions, {
|
|
32
|
+
attach: (sessionId, context) => presentation.attachSession(sessionId, context),
|
|
33
|
+
detach: (context) => presentation.detachSession(context)
|
|
34
|
+
});
|
|
35
|
+
const attachment = endpointFor(provider);
|
|
36
|
+
return {
|
|
37
|
+
invokeService: attachment.invokeService,
|
|
38
|
+
release(context) {
|
|
39
|
+
attachment.release(context);
|
|
40
|
+
provider.dispose();
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
async function hostAgent(agent, options = {}) {
|
|
47
|
+
const context = options.context ?? BACKGROUND_CONTEXT;
|
|
48
|
+
const serverId = options.serverId ?? crypto.randomUUID();
|
|
49
|
+
const state = await agent.root.viewState(context);
|
|
50
|
+
const provider = new RemoteServiceProvider([
|
|
51
|
+
{ service: AgentControl, mode: "singleton" },
|
|
52
|
+
{ service: AgentTranscript, mode: "singleton" }
|
|
53
|
+
]);
|
|
54
|
+
provider.provide(AgentControl, createAgentControl(agent.harness, agent.root));
|
|
55
|
+
provider.provide(AgentTranscript, { state });
|
|
56
|
+
const host = {
|
|
57
|
+
serverServices: serverServices(),
|
|
58
|
+
async resolveSession(sessionId) {
|
|
59
|
+
if (sessionId !== AGENT_SESSION)
|
|
60
|
+
throw new SessionNotFoundError(`unknown session ${sessionId}`);
|
|
61
|
+
return { id: sessionId };
|
|
62
|
+
},
|
|
63
|
+
openSession: async () => sessionServices(provider)
|
|
64
|
+
};
|
|
65
|
+
const listener = new PortListener(serverId);
|
|
66
|
+
const server = await new Server(host, { serverId, listeners: [listener] }).start();
|
|
67
|
+
return {
|
|
68
|
+
serverId,
|
|
69
|
+
agent,
|
|
70
|
+
connect: (endpoint) => listener.connect(endpoint),
|
|
71
|
+
async close() {
|
|
72
|
+
await server.close();
|
|
73
|
+
provider.dispose();
|
|
74
|
+
state.dispose();
|
|
75
|
+
await agent.close();
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
export {
|
|
80
|
+
hostAgent
|
|
81
|
+
};
|
|
82
|
+
//# sourceMappingURL=host.js.map
|
package/dist/host.js.map
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/host.ts"],
|
|
4
|
+
"sourcesContent": ["import {\n type Context,\n createRemoteServiceEndpoint,\n RemoteServiceProvider,\n} from '@earendil-works/chord';\nimport { BACKGROUND_CONTEXT } from '@earendil-works/chord/context';\nimport {\n type RoutedServerServiceHost,\n type RoutedSessionAttachment,\n type RoutedSessionHandle,\n Server,\n type ServerHost,\n SessionNotFoundError,\n} from '@earendil-works/pi-server';\nimport type { Agent } from './agent.ts';\nimport { createAgentControl } from './control.ts';\nimport { AGENT_SESSION, AgentControl, AgentSessions, AgentTranscript } from './services.ts';\nimport { type PortEndpoint, PortListener } from './wire.ts';\n\nexport interface AgentHost {\n readonly serverId: string;\n readonly agent: Agent;\n connect(endpoint: PortEndpoint): void;\n close(): Promise<void>;\n}\n\nfunction endpointFor(provider: RemoteServiceProvider): RoutedSessionAttachment {\n const endpoint = createRemoteServiceEndpoint(provider);\n return {\n invokeService: (call, publish, context) =>\n endpoint.invoke(call, (id, update) => publish(id, update, context), context),\n release: () => endpoint.dispose(),\n };\n}\n\nfunction sessionServices(provider: RemoteServiceProvider): RoutedSessionHandle {\n return {\n attachClient: () => endpointFor(provider),\n close: async () => {},\n };\n}\n\nfunction serverServices(): RoutedServerServiceHost {\n return {\n attachClient(presentation) {\n const provider = new RemoteServiceProvider([{ service: AgentSessions, mode: 'singleton' }]);\n provider.provide(AgentSessions, {\n attach: (sessionId, context) => presentation.attachSession(sessionId, context),\n detach: (context) => presentation.detachSession(context),\n });\n const attachment = endpointFor(provider);\n return {\n invokeService: attachment.invokeService,\n release(context) {\n attachment.release(context);\n provider.dispose();\n },\n };\n },\n };\n}\n\nexport async function hostAgent(\n agent: Agent,\n options: { serverId?: string; context?: Context } = {}\n): Promise<AgentHost> {\n const context = options.context ?? BACKGROUND_CONTEXT;\n const serverId = options.serverId ?? crypto.randomUUID();\n const state = await agent.root.viewState(context);\n const provider = new RemoteServiceProvider([\n { service: AgentControl, mode: 'singleton' },\n { service: AgentTranscript, mode: 'singleton' },\n ]);\n provider.provide(AgentControl, createAgentControl(agent.harness, agent.root));\n provider.provide(AgentTranscript, { state });\n const host: ServerHost = {\n serverServices: serverServices(),\n async resolveSession(sessionId) {\n if (sessionId !== AGENT_SESSION)\n throw new SessionNotFoundError(`unknown session ${sessionId}`);\n return { id: sessionId };\n },\n openSession: async () => sessionServices(provider),\n };\n const listener = new PortListener(serverId);\n const server = await new Server(host, { serverId, listeners: [listener] }).start();\n return {\n serverId,\n agent,\n connect: (endpoint) => listener.connect(endpoint),\n async close() {\n await server.close();\n provider.dispose();\n state.dispose();\n await agent.close();\n },\n };\n}\n"],
|
|
5
|
+
"mappings": "AAAA;AAAA,EAEE;AAAA,EACA;AAAA,OACK;AACP,SAAS,0BAA0B;AACnC;AAAA,EAIE;AAAA,EAEA;AAAA,OACK;AAEP,SAAS,0BAA0B;AACnC,SAAS,eAAe,cAAc,eAAe,uBAAuB;AAC5E,SAA4B,oBAAoB;AAShD,SAAS,YAAY,UAA0D;AAC7E,QAAM,WAAW,4BAA4B,QAAQ;AACrD,SAAO;AAAA,IACL,eAAe,CAAC,MAAM,SAAS,YAC7B,SAAS,OAAO,MAAM,CAAC,IAAI,WAAW,QAAQ,IAAI,QAAQ,OAAO,GAAG,OAAO;AAAA,IAC7E,SAAS,MAAM,SAAS,QAAQ;AAAA,EAClC;AACF;AAEA,SAAS,gBAAgB,UAAsD;AAC7E,SAAO;AAAA,IACL,cAAc,MAAM,YAAY,QAAQ;AAAA,IACxC,OAAO,YAAY;AAAA,IAAC;AAAA,EACtB;AACF;AAEA,SAAS,iBAA0C;AACjD,SAAO;AAAA,IACL,aAAa,cAAc;AACzB,YAAM,WAAW,IAAI,sBAAsB,CAAC,EAAE,SAAS,eAAe,MAAM,YAAY,CAAC,CAAC;AAC1F,eAAS,QAAQ,eAAe;AAAA,QAC9B,QAAQ,CAAC,WAAW,YAAY,aAAa,cAAc,WAAW,OAAO;AAAA,QAC7E,QAAQ,CAAC,YAAY,aAAa,cAAc,OAAO;AAAA,MACzD,CAAC;AACD,YAAM,aAAa,YAAY,QAAQ;AACvC,aAAO;AAAA,QACL,eAAe,WAAW;AAAA,QAC1B,QAAQ,SAAS;AACf,qBAAW,QAAQ,OAAO;AAC1B,mBAAS,QAAQ;AAAA,QACnB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAEA,eAAsB,UACpB,OACA,UAAoD,CAAC,GACjC;AACpB,QAAM,UAAU,QAAQ,WAAW;AACnC,QAAM,WAAW,QAAQ,YAAY,OAAO,WAAW;AACvD,QAAM,QAAQ,MAAM,MAAM,KAAK,UAAU,OAAO;AAChD,QAAM,WAAW,IAAI,sBAAsB;AAAA,IACzC,EAAE,SAAS,cAAc,MAAM,YAAY;AAAA,IAC3C,EAAE,SAAS,iBAAiB,MAAM,YAAY;AAAA,EAChD,CAAC;AACD,WAAS,QAAQ,cAAc,mBAAmB,MAAM,SAAS,MAAM,IAAI,CAAC;AAC5E,WAAS,QAAQ,iBAAiB,EAAE,MAAM,CAAC;AAC3C,QAAM,OAAmB;AAAA,IACvB,gBAAgB,eAAe;AAAA,IAC/B,MAAM,eAAe,WAAW;AAC9B,UAAI,cAAc;AAChB,cAAM,IAAI,qBAAqB,mBAAmB,SAAS,EAAE;AAC/D,aAAO,EAAE,IAAI,UAAU;AAAA,IACzB;AAAA,IACA,aAAa,YAAY,gBAAgB,QAAQ;AAAA,EACnD;AACA,QAAM,WAAW,IAAI,aAAa,QAAQ;AAC1C,QAAM,SAAS,MAAM,IAAI,OAAO,MAAM,EAAE,UAAU,WAAW,CAAC,QAAQ,EAAE,CAAC,EAAE,MAAM;AACjF,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,SAAS,CAAC,aAAa,SAAS,QAAQ,QAAQ;AAAA,IAChD,MAAM,QAAQ;AACZ,YAAM,OAAO,MAAM;AACnB,eAAS,QAAQ;AACjB,YAAM,QAAQ;AACd,YAAM,MAAM,MAAM;AAAA,IACpB;AAAA,EACF;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -1,12 +1,44 @@
|
|
|
1
1
|
import { answerText, openAgent } from "./agent.js";
|
|
2
|
+
import { connectAgent } from "./client.js";
|
|
3
|
+
import { createAgentControl } from "./control.js";
|
|
4
|
+
import { hostAgent } from "./host.js";
|
|
2
5
|
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
} from "./
|
|
6
|
+
AGENT_LOCK,
|
|
7
|
+
startAgent
|
|
8
|
+
} from "./owner.js";
|
|
9
|
+
import {
|
|
10
|
+
AGENT_SESSION,
|
|
11
|
+
AgentControl,
|
|
12
|
+
AgentSessions,
|
|
13
|
+
AgentTranscript
|
|
14
|
+
} from "./services.js";
|
|
15
|
+
import {
|
|
16
|
+
AGENT_DIRECTORY,
|
|
17
|
+
openMemorySqliteStorage,
|
|
18
|
+
openOpfsSqliteStorage,
|
|
19
|
+
WasmSqliteDatabase
|
|
20
|
+
} from "./sqlite.js";
|
|
21
|
+
import { PortChannel, PortListener, withUnrefTimers } from "./wire.js";
|
|
22
|
+
import { serveConnections } from "./worker.js";
|
|
6
23
|
export {
|
|
24
|
+
AGENT_DIRECTORY,
|
|
25
|
+
AGENT_LOCK,
|
|
26
|
+
AGENT_SESSION,
|
|
27
|
+
AgentControl,
|
|
28
|
+
AgentSessions,
|
|
29
|
+
AgentTranscript,
|
|
30
|
+
PortChannel,
|
|
31
|
+
PortListener,
|
|
32
|
+
WasmSqliteDatabase,
|
|
7
33
|
answerText,
|
|
8
34
|
connectAgent,
|
|
35
|
+
createAgentControl,
|
|
36
|
+
hostAgent,
|
|
9
37
|
openAgent,
|
|
10
|
-
|
|
38
|
+
openMemorySqliteStorage,
|
|
39
|
+
openOpfsSqliteStorage,
|
|
40
|
+
serveConnections,
|
|
41
|
+
startAgent,
|
|
42
|
+
withUnrefTimers
|
|
11
43
|
};
|
|
12
44
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/index.ts"],
|
|
4
|
-
"sourcesContent": ["export { type Agent, type AgentOptions, answerText, openAgent } from './agent.ts';\nexport {\n type
|
|
5
|
-
"mappings": "AAAA,SAAwC,YAAY,iBAAiB;AACrE;AAAA,
|
|
4
|
+
"sourcesContent": ["export { type Agent, type AgentOptions, answerText, openAgent } from './agent.ts';\nexport { type AgentConnection, connectAgent } from './client.ts';\nexport { createAgentControl } from './control.ts';\nexport { type AgentHost, hostAgent } from './host.ts';\nexport {\n AGENT_LOCK,\n type AgentOwner,\n type AgentWorker,\n type OwnerOptions,\n startAgent,\n} from './owner.ts';\nexport {\n AGENT_SESSION,\n AgentControl,\n AgentSessions,\n AgentTranscript,\n type OperationError,\n type PromptResult,\n type SendMode,\n type SendRequest,\n type SendResponse,\n} from './services.ts';\nexport {\n AGENT_DIRECTORY,\n type OpfsSqliteOptions,\n openMemorySqliteStorage,\n openOpfsSqliteStorage,\n WasmSqliteDatabase,\n} from './sqlite.ts';\nexport { PortChannel, type PortEndpoint, PortListener, withUnrefTimers } from './wire.ts';\nexport { serveConnections, type WorkerScope } from './worker.ts';\n"],
|
|
5
|
+
"mappings": "AAAA,SAAwC,YAAY,iBAAiB;AACrE,SAA+B,oBAAoB;AACnD,SAAS,0BAA0B;AACnC,SAAyB,iBAAiB;AAC1C;AAAA,EACE;AAAA,EAIA;AAAA,OACK;AACP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAMK;AACP;AAAA,EACE;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,aAAgC,cAAc,uBAAuB;AAC9E,SAAS,wBAA0C;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
const names = /* @__PURE__ */ new Map();
|
|
2
|
+
function vfsName(directory) {
|
|
3
|
+
let name = names.get(directory);
|
|
4
|
+
if (!name) {
|
|
5
|
+
name = `slicc-agent-${names.size}`;
|
|
6
|
+
names.set(directory, name);
|
|
7
|
+
}
|
|
8
|
+
return name;
|
|
9
|
+
}
|
|
10
|
+
function holdLock(locks, name) {
|
|
11
|
+
return new Promise((acquired, failed) => {
|
|
12
|
+
locks.request(name, { mode: "exclusive" }, () => new Promise((release) => acquired(release))).catch(failed);
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
async function opaque(root, directory) {
|
|
16
|
+
let dir = root;
|
|
17
|
+
try {
|
|
18
|
+
for (const part of [...directory.split("/").filter(Boolean), ".opaque"]) {
|
|
19
|
+
dir = await dir.getDirectoryHandle(part);
|
|
20
|
+
}
|
|
21
|
+
} catch {
|
|
22
|
+
return void 0;
|
|
23
|
+
}
|
|
24
|
+
return dir;
|
|
25
|
+
}
|
|
26
|
+
async function acquirable(dir) {
|
|
27
|
+
for await (const handle of dir.values()) {
|
|
28
|
+
if (handle.kind !== "file") continue;
|
|
29
|
+
try {
|
|
30
|
+
const access = await handle.createSyncAccessHandle();
|
|
31
|
+
access.close();
|
|
32
|
+
} catch {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
const pause = (ms) => new Promise((resume) => setTimeout(resume, ms));
|
|
39
|
+
async function settlePool(directory, access = {}) {
|
|
40
|
+
const root = await (access.root ?? (() => navigator.storage.getDirectory()))();
|
|
41
|
+
const dir = await opaque(root, directory);
|
|
42
|
+
if (!dir) return;
|
|
43
|
+
const attempts = access.attempts ?? 50;
|
|
44
|
+
for (let attempt = 1; attempt <= attempts; attempt++) {
|
|
45
|
+
if (await acquirable(dir)) return;
|
|
46
|
+
if (attempt < attempts) await pause(access.delayMs ?? 100);
|
|
47
|
+
}
|
|
48
|
+
throw new Error(`SQLite pool ${directory} is still in use by another worker`);
|
|
49
|
+
}
|
|
50
|
+
export {
|
|
51
|
+
holdLock,
|
|
52
|
+
settlePool,
|
|
53
|
+
vfsName
|
|
54
|
+
};
|
|
55
|
+
//# sourceMappingURL=opfs-pool.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/opfs-pool.ts"],
|
|
4
|
+
"sourcesContent": ["export interface PoolAccess {\n root?: () => Promise<FileSystemDirectoryHandle>;\n locks?: LockManager;\n attempts?: number;\n delayMs?: number;\n}\n\ninterface SyncAccessible {\n createSyncAccessHandle(): Promise<{ close(): void }>;\n}\n\nconst names = new Map<string, string>();\n\nexport function vfsName(directory: string): string {\n let name = names.get(directory);\n if (!name) {\n name = `slicc-agent-${names.size}`;\n names.set(directory, name);\n }\n return name;\n}\n\nexport function holdLock(locks: LockManager, name: string): Promise<() => void> {\n return new Promise((acquired, failed) => {\n locks\n .request(name, { mode: 'exclusive' }, () => new Promise<void>((release) => acquired(release)))\n .catch(failed);\n });\n}\n\nasync function opaque(\n root: FileSystemDirectoryHandle,\n directory: string\n): Promise<FileSystemDirectoryHandle | undefined> {\n let dir = root;\n try {\n for (const part of [...directory.split('/').filter(Boolean), '.opaque']) {\n dir = await dir.getDirectoryHandle(part);\n }\n } catch {\n return undefined;\n }\n return dir;\n}\n\nasync function acquirable(dir: FileSystemDirectoryHandle): Promise<boolean> {\n for await (const handle of dir.values()) {\n if (handle.kind !== 'file') continue;\n try {\n const access = await (handle as unknown as SyncAccessible).createSyncAccessHandle();\n access.close();\n } catch {\n return false;\n }\n }\n return true;\n}\n\nconst pause = (ms: number) => new Promise((resume) => setTimeout(resume, ms));\n\nexport async function settlePool(directory: string, access: PoolAccess = {}): Promise<void> {\n const root = await (access.root ?? (() => navigator.storage.getDirectory()))();\n const dir = await opaque(root, directory);\n if (!dir) return;\n const attempts = access.attempts ?? 50;\n for (let attempt = 1; attempt <= attempts; attempt++) {\n if (await acquirable(dir)) return;\n if (attempt < attempts) await pause(access.delayMs ?? 100);\n }\n throw new Error(`SQLite pool ${directory} is still in use by another worker`);\n}\n"],
|
|
5
|
+
"mappings": "AAWA,MAAM,QAAQ,oBAAI,IAAoB;AAE/B,SAAS,QAAQ,WAA2B;AACjD,MAAI,OAAO,MAAM,IAAI,SAAS;AAC9B,MAAI,CAAC,MAAM;AACT,WAAO,eAAe,MAAM,IAAI;AAChC,UAAM,IAAI,WAAW,IAAI;AAAA,EAC3B;AACA,SAAO;AACT;AAEO,SAAS,SAAS,OAAoB,MAAmC;AAC9E,SAAO,IAAI,QAAQ,CAAC,UAAU,WAAW;AACvC,UACG,QAAQ,MAAM,EAAE,MAAM,YAAY,GAAG,MAAM,IAAI,QAAc,CAAC,YAAY,SAAS,OAAO,CAAC,CAAC,EAC5F,MAAM,MAAM;AAAA,EACjB,CAAC;AACH;AAEA,eAAe,OACb,MACA,WACgD;AAChD,MAAI,MAAM;AACV,MAAI;AACF,eAAW,QAAQ,CAAC,GAAG,UAAU,MAAM,GAAG,EAAE,OAAO,OAAO,GAAG,SAAS,GAAG;AACvE,YAAM,MAAM,IAAI,mBAAmB,IAAI;AAAA,IACzC;AAAA,EACF,QAAQ;AACN,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAEA,eAAe,WAAW,KAAkD;AAC1E,mBAAiB,UAAU,IAAI,OAAO,GAAG;AACvC,QAAI,OAAO,SAAS,OAAQ;AAC5B,QAAI;AACF,YAAM,SAAS,MAAO,OAAqC,uBAAuB;AAClF,aAAO,MAAM;AAAA,IACf,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;AAEA,MAAM,QAAQ,CAAC,OAAe,IAAI,QAAQ,CAAC,WAAW,WAAW,QAAQ,EAAE,CAAC;AAE5E,eAAsB,WAAW,WAAmB,SAAqB,CAAC,GAAkB;AAC1F,QAAM,OAAO,OAAO,OAAO,SAAS,MAAM,UAAU,QAAQ,aAAa,IAAI;AAC7E,QAAM,MAAM,MAAM,OAAO,MAAM,SAAS;AACxC,MAAI,CAAC,IAAK;AACV,QAAM,WAAW,OAAO,YAAY;AACpC,WAAS,UAAU,GAAG,WAAW,UAAU,WAAW;AACpD,QAAI,MAAM,WAAW,GAAG,EAAG;AAC3B,QAAI,UAAU,SAAU,OAAM,MAAM,OAAO,WAAW,GAAG;AAAA,EAC3D;AACA,QAAM,IAAI,MAAM,eAAe,SAAS,oCAAoC;AAC9E;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/owner.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { connectAgent } from "./client.js";
|
|
2
|
+
import { holdLock } from "./opfs-pool.js";
|
|
3
|
+
const AGENT_LOCK = "slicc-agent";
|
|
4
|
+
async function startAgent(options) {
|
|
5
|
+
const release = await holdLock(options.locks ?? navigator.locks, AGENT_LOCK);
|
|
6
|
+
const channel = options.channel ?? (() => new MessageChannel());
|
|
7
|
+
let worker;
|
|
8
|
+
try {
|
|
9
|
+
worker = options.worker();
|
|
10
|
+
} catch (error) {
|
|
11
|
+
release();
|
|
12
|
+
throw error;
|
|
13
|
+
}
|
|
14
|
+
return {
|
|
15
|
+
connect() {
|
|
16
|
+
const { port1, port2 } = channel();
|
|
17
|
+
worker.postMessage({ connect: port2 }, [port2]);
|
|
18
|
+
return connectAgent(port1);
|
|
19
|
+
},
|
|
20
|
+
restart() {
|
|
21
|
+
worker.terminate();
|
|
22
|
+
worker = options.worker();
|
|
23
|
+
},
|
|
24
|
+
release() {
|
|
25
|
+
worker.terminate();
|
|
26
|
+
release();
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
export {
|
|
31
|
+
AGENT_LOCK,
|
|
32
|
+
startAgent
|
|
33
|
+
};
|
|
34
|
+
//# sourceMappingURL=owner.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/owner.ts"],
|
|
4
|
+
"sourcesContent": ["import { type AgentConnection, connectAgent } from './client.ts';\nimport { holdLock } from './opfs-pool.ts';\nimport type { PortEndpoint } from './wire.ts';\n\nexport const AGENT_LOCK = 'slicc-agent';\n\nexport interface AgentWorker {\n postMessage(message: unknown, transfer: Transferable[]): void;\n terminate(): void;\n}\n\nexport interface AgentOwner {\n connect(): Promise<AgentConnection>;\n restart(): void;\n release(): void;\n}\n\nexport interface OwnerOptions {\n worker: () => AgentWorker;\n locks?: LockManager;\n channel?: () => { port1: PortEndpoint; port2: Transferable };\n}\n\nexport async function startAgent(options: OwnerOptions): Promise<AgentOwner> {\n const release = await holdLock(options.locks ?? navigator.locks, AGENT_LOCK);\n const channel = options.channel ?? (() => new MessageChannel());\n let worker: AgentWorker;\n try {\n worker = options.worker();\n } catch (error) {\n release();\n throw error;\n }\n return {\n connect() {\n const { port1, port2 } = channel();\n worker.postMessage({ connect: port2 }, [port2]);\n return connectAgent(port1);\n },\n restart() {\n worker.terminate();\n worker = options.worker();\n },\n release() {\n worker.terminate();\n release();\n },\n };\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAA+B,oBAAoB;AACnD,SAAS,gBAAgB;AAGlB,MAAM,aAAa;AAmB1B,eAAsB,WAAW,SAA4C;AAC3E,QAAM,UAAU,MAAM,SAAS,QAAQ,SAAS,UAAU,OAAO,UAAU;AAC3E,QAAM,UAAU,QAAQ,YAAY,MAAM,IAAI,eAAe;AAC7D,MAAI;AACJ,MAAI;AACF,aAAS,QAAQ,OAAO;AAAA,EAC1B,SAAS,OAAO;AACd,YAAQ;AACR,UAAM;AAAA,EACR;AACA,SAAO;AAAA,IACL,UAAU;AACR,YAAM,EAAE,OAAO,MAAM,IAAI,QAAQ;AACjC,aAAO,YAAY,EAAE,SAAS,MAAM,GAAG,CAAC,KAAK,CAAC;AAC9C,aAAO,aAAa,KAAK;AAAA,IAC3B;AAAA,IACA,UAAU;AACR,aAAO,UAAU;AACjB,eAAS,QAAQ,OAAO;AAAA,IAC1B;AAAA,IACA,UAAU;AACR,aAAO,UAAU;AACjB,cAAQ;AAAA,IACV;AAAA,EACF;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/services.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { defineService } from "@earendil-works/chord";
|
|
2
|
+
const AgentControl = defineService("slicc.agent.control");
|
|
3
|
+
const AgentTranscript = defineService("slicc.agent.transcript");
|
|
4
|
+
const AgentSessions = defineService("slicc.agent.sessions");
|
|
5
|
+
const AGENT_SESSION = "agent";
|
|
6
|
+
export {
|
|
7
|
+
AGENT_SESSION,
|
|
8
|
+
AgentControl,
|
|
9
|
+
AgentSessions,
|
|
10
|
+
AgentTranscript
|
|
11
|
+
};
|
|
12
|
+
//# sourceMappingURL=services.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/services.ts"],
|
|
4
|
+
"sourcesContent": ["import { type Context, defineService, type ReplicatedState } from '@earendil-works/chord';\nimport type { ConversationView } from '@earendil-works/pi-durable';\n\nexport type SendMode = 'steer' | 'followUp' | 'reject';\n\nexport interface SendRequest {\n text: string;\n whenBusy: SendMode;\n requestId: string | null;\n}\n\nexport interface OperationError {\n code: string;\n message: string;\n}\n\nexport type SendResponse =\n | { accepted: true; submissionId: string; error: null }\n | { accepted: false; submissionId: null; error: OperationError };\n\nexport type PromptResult =\n | { status: 'done'; text: string; reason: null }\n | { status: 'unanswered'; text: null; reason: string };\n\nexport interface AgentControl {\n send(request: SendRequest, context: Context): Promise<SendResponse>;\n wait(submissionId: string, context: Context): Promise<PromptResult>;\n withdraw(\n submissionId: string,\n context: Context\n ): Promise<{ outcome: 'withdrawn' | 'already_placed' | 'not_found' }>;\n abort(context: Context): Promise<void>;\n compact(instructions: string | null, context: Context): Promise<SendResponse>;\n}\n\nexport const AgentControl = defineService<AgentControl>('slicc.agent.control');\n\nexport interface AgentTranscript {\n readonly state: ReplicatedState<ConversationView>;\n}\n\nexport const AgentTranscript = defineService<AgentTranscript>('slicc.agent.transcript');\n\nexport interface AgentSessions {\n attach(sessionId: string, context: Context): Promise<void>;\n detach(context: Context): Promise<void>;\n}\n\nexport const AgentSessions = defineService<AgentSessions>('slicc.agent.sessions');\n\nexport const AGENT_SESSION = 'agent';\n"],
|
|
5
|
+
"mappings": "AAAA,SAAuB,qBAA2C;AAmC3D,MAAM,eAAe,cAA4B,qBAAqB;AAMtE,MAAM,kBAAkB,cAA+B,wBAAwB;AAO/E,MAAM,gBAAgB,cAA6B,sBAAsB;AAEzE,MAAM,gBAAgB;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/sqlite.js
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import {
|
|
2
|
+
SqliteStorage
|
|
3
|
+
} from "@earendil-works/pi-durable/storage/sqlite";
|
|
4
|
+
import init, {} from "@sqlite.org/sqlite-wasm";
|
|
5
|
+
import { holdLock, settlePool, vfsName } from "./opfs-pool.js";
|
|
6
|
+
class SerialQueue {
|
|
7
|
+
#tail = Promise.resolve();
|
|
8
|
+
run(operation) {
|
|
9
|
+
const result = this.#tail.then(operation);
|
|
10
|
+
this.#tail = result.catch(() => {
|
|
11
|
+
});
|
|
12
|
+
return result;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
class Statements {
|
|
16
|
+
#db;
|
|
17
|
+
#cache = /* @__PURE__ */ new Map();
|
|
18
|
+
constructor(db) {
|
|
19
|
+
this.#db = db;
|
|
20
|
+
}
|
|
21
|
+
bound(sql, params) {
|
|
22
|
+
let statement = this.#cache.get(sql);
|
|
23
|
+
if (!statement) {
|
|
24
|
+
statement = this.#db.prepare(sql);
|
|
25
|
+
this.#cache.set(sql, statement);
|
|
26
|
+
}
|
|
27
|
+
statement.reset(true);
|
|
28
|
+
if (params.length > 0) statement.bind([...params]);
|
|
29
|
+
return statement;
|
|
30
|
+
}
|
|
31
|
+
finalize() {
|
|
32
|
+
for (const statement of this.#cache.values()) statement.finalize();
|
|
33
|
+
this.#cache.clear();
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
class WasmSqliteExecutor {
|
|
37
|
+
db;
|
|
38
|
+
statements;
|
|
39
|
+
constructor(db, statements) {
|
|
40
|
+
this.db = db;
|
|
41
|
+
this.statements = statements;
|
|
42
|
+
}
|
|
43
|
+
async exec(sql) {
|
|
44
|
+
this.check();
|
|
45
|
+
this.db.exec(sql);
|
|
46
|
+
}
|
|
47
|
+
async run(sql, ...params) {
|
|
48
|
+
this.check();
|
|
49
|
+
this.statements.bound(sql, params).stepReset();
|
|
50
|
+
}
|
|
51
|
+
async get(sql, ...params) {
|
|
52
|
+
this.check();
|
|
53
|
+
const statement = this.statements.bound(sql, params);
|
|
54
|
+
const row = statement.step() ? statement.get({}) : void 0;
|
|
55
|
+
statement.reset();
|
|
56
|
+
return row;
|
|
57
|
+
}
|
|
58
|
+
async all(sql, ...params) {
|
|
59
|
+
this.check();
|
|
60
|
+
const statement = this.statements.bound(sql, params);
|
|
61
|
+
const rows = [];
|
|
62
|
+
while (statement.step()) rows.push(statement.get({}));
|
|
63
|
+
statement.reset();
|
|
64
|
+
return rows;
|
|
65
|
+
}
|
|
66
|
+
check() {
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
class WasmSqliteTransaction extends WasmSqliteExecutor {
|
|
70
|
+
active = true;
|
|
71
|
+
check() {
|
|
72
|
+
if (!this.active) throw new Error("SQLite transaction handle is no longer active");
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
class WasmSqliteDatabase extends WasmSqliteExecutor {
|
|
76
|
+
#queue = new SerialQueue();
|
|
77
|
+
#closed;
|
|
78
|
+
constructor(db, closed = () => {
|
|
79
|
+
}) {
|
|
80
|
+
super(db, new Statements(db));
|
|
81
|
+
this.#closed = closed;
|
|
82
|
+
}
|
|
83
|
+
exec(sql) {
|
|
84
|
+
return this.#queue.run(() => super.exec(sql));
|
|
85
|
+
}
|
|
86
|
+
run(sql, ...params) {
|
|
87
|
+
return this.#queue.run(() => super.run(sql, ...params));
|
|
88
|
+
}
|
|
89
|
+
get(sql, ...params) {
|
|
90
|
+
return this.#queue.run(() => super.get(sql, ...params));
|
|
91
|
+
}
|
|
92
|
+
all(sql, ...params) {
|
|
93
|
+
return this.#queue.run(() => super.all(sql, ...params));
|
|
94
|
+
}
|
|
95
|
+
transaction(callback) {
|
|
96
|
+
return this.#queue.run(async () => {
|
|
97
|
+
const transaction = new WasmSqliteTransaction(this.db, this.statements);
|
|
98
|
+
this.db.exec("BEGIN IMMEDIATE");
|
|
99
|
+
try {
|
|
100
|
+
const result = await callback(transaction);
|
|
101
|
+
this.db.exec("COMMIT");
|
|
102
|
+
return result;
|
|
103
|
+
} catch (error) {
|
|
104
|
+
this.#rollback(error);
|
|
105
|
+
throw error;
|
|
106
|
+
} finally {
|
|
107
|
+
transaction.active = false;
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
close() {
|
|
112
|
+
return this.#queue.run(() => {
|
|
113
|
+
try {
|
|
114
|
+
this.statements.finalize();
|
|
115
|
+
this.db.close();
|
|
116
|
+
} finally {
|
|
117
|
+
this.#closed();
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
#rollback(cause) {
|
|
122
|
+
try {
|
|
123
|
+
this.db.exec("ROLLBACK");
|
|
124
|
+
} catch (error) {
|
|
125
|
+
throw new AggregateError([cause, error], "SQLite rollback failed");
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
const AGENT_DIRECTORY = "/.slicc/agent";
|
|
130
|
+
async function openMemorySqliteStorage() {
|
|
131
|
+
const sqlite3 = await init();
|
|
132
|
+
return SqliteStorage.open(new WasmSqliteDatabase(new sqlite3.oo1.DB(":memory:", "c")));
|
|
133
|
+
}
|
|
134
|
+
async function openOpfsSqliteStorage(options = {}) {
|
|
135
|
+
const directory = options.directory ?? AGENT_DIRECTORY;
|
|
136
|
+
const release = await holdLock(
|
|
137
|
+
options.locks ?? navigator.locks,
|
|
138
|
+
`slicc-agent-sqlite:${directory}`
|
|
139
|
+
);
|
|
140
|
+
try {
|
|
141
|
+
await settlePool(directory, options);
|
|
142
|
+
const sqlite3 = await (options.load ?? init)();
|
|
143
|
+
const pool = await sqlite3.installOpfsSAHPoolVfs({ name: vfsName(directory), directory });
|
|
144
|
+
const db = new pool.OpfsSAHPoolDb(options.file ?? "/agent.sqlite");
|
|
145
|
+
db.exec("PRAGMA journal_mode = TRUNCATE");
|
|
146
|
+
return await SqliteStorage.open(new WasmSqliteDatabase(db, release));
|
|
147
|
+
} catch (error) {
|
|
148
|
+
release();
|
|
149
|
+
throw error;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
export {
|
|
153
|
+
AGENT_DIRECTORY,
|
|
154
|
+
WasmSqliteDatabase,
|
|
155
|
+
openMemorySqliteStorage,
|
|
156
|
+
openOpfsSqliteStorage
|
|
157
|
+
};
|
|
158
|
+
//# sourceMappingURL=sqlite.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/sqlite.ts"],
|
|
4
|
+
"sourcesContent": ["import type { Storage } from '@earendil-works/pi-durable';\nimport {\n type SqliteDatabase,\n type SqliteExecutor,\n SqliteStorage,\n type SqliteValue,\n} from '@earendil-works/pi-durable/storage/sqlite';\nimport init, { type Database, type PreparedStatement } from '@sqlite.org/sqlite-wasm';\nimport { holdLock, type PoolAccess, settlePool, vfsName } from './opfs-pool.ts';\n\ntype Row = Record<string, unknown>;\n\nclass SerialQueue {\n #tail: Promise<unknown> = Promise.resolve();\n\n run<T>(operation: () => T | Promise<T>): Promise<T> {\n const result = this.#tail.then(operation);\n this.#tail = result.catch(() => {});\n return result;\n }\n}\n\nclass Statements {\n readonly #db: Database;\n readonly #cache = new Map<string, PreparedStatement>();\n\n constructor(db: Database) {\n this.#db = db;\n }\n\n bound(sql: string, params: readonly SqliteValue[]): PreparedStatement {\n let statement = this.#cache.get(sql);\n if (!statement) {\n statement = this.#db.prepare(sql);\n this.#cache.set(sql, statement);\n }\n statement.reset(true);\n if (params.length > 0) statement.bind([...params]);\n return statement;\n }\n\n finalize(): void {\n for (const statement of this.#cache.values()) statement.finalize();\n this.#cache.clear();\n }\n}\n\nclass WasmSqliteExecutor implements SqliteExecutor {\n protected readonly db: Database;\n protected readonly statements: Statements;\n\n constructor(db: Database, statements: Statements) {\n this.db = db;\n this.statements = statements;\n }\n\n async exec(sql: string): Promise<void> {\n this.check();\n this.db.exec(sql);\n }\n\n async run(sql: string, ...params: SqliteValue[]): Promise<void> {\n this.check();\n this.statements.bound(sql, params).stepReset();\n }\n\n async get<T extends object>(sql: string, ...params: SqliteValue[]): Promise<T | undefined> {\n this.check();\n const statement = this.statements.bound(sql, params);\n const row = statement.step() ? (statement.get({}) as Row as T) : undefined;\n statement.reset();\n return row;\n }\n\n async all<T extends object>(sql: string, ...params: SqliteValue[]): Promise<T[]> {\n this.check();\n const statement = this.statements.bound(sql, params);\n const rows: T[] = [];\n while (statement.step()) rows.push(statement.get({}) as Row as T);\n statement.reset();\n return rows;\n }\n\n protected check(): void {}\n}\n\nclass WasmSqliteTransaction extends WasmSqliteExecutor {\n active = true;\n\n protected override check(): void {\n if (!this.active) throw new Error('SQLite transaction handle is no longer active');\n }\n}\n\nexport class WasmSqliteDatabase extends WasmSqliteExecutor implements SqliteDatabase {\n readonly #queue = new SerialQueue();\n readonly #closed: () => void;\n\n constructor(db: Database, closed: () => void = () => {}) {\n super(db, new Statements(db));\n this.#closed = closed;\n }\n\n override exec(sql: string): Promise<void> {\n return this.#queue.run(() => super.exec(sql));\n }\n\n override run(sql: string, ...params: SqliteValue[]): Promise<void> {\n return this.#queue.run(() => super.run(sql, ...params));\n }\n\n override get<T extends object>(sql: string, ...params: SqliteValue[]): Promise<T | undefined> {\n return this.#queue.run(() => super.get<T>(sql, ...params));\n }\n\n override all<T extends object>(sql: string, ...params: SqliteValue[]): Promise<T[]> {\n return this.#queue.run(() => super.all<T>(sql, ...params));\n }\n\n transaction<T>(callback: (transaction: SqliteExecutor) => Promise<T>): Promise<T> {\n return this.#queue.run(async () => {\n const transaction = new WasmSqliteTransaction(this.db, this.statements);\n this.db.exec('BEGIN IMMEDIATE');\n try {\n const result = await callback(transaction);\n this.db.exec('COMMIT');\n return result;\n } catch (error) {\n this.#rollback(error);\n throw error;\n } finally {\n transaction.active = false;\n }\n });\n }\n\n close(): Promise<void> {\n return this.#queue.run(() => {\n try {\n this.statements.finalize();\n this.db.close();\n } finally {\n this.#closed();\n }\n });\n }\n\n #rollback(cause: unknown): void {\n try {\n this.db.exec('ROLLBACK');\n } catch (error) {\n throw new AggregateError([cause, error], 'SQLite rollback failed');\n }\n }\n}\n\nexport interface OpfsSqliteOptions extends PoolAccess {\n directory?: string;\n file?: string;\n load?: typeof init;\n}\n\nexport const AGENT_DIRECTORY = '/.slicc/agent';\n\nexport async function openMemorySqliteStorage(): Promise<Storage> {\n const sqlite3 = await init();\n return SqliteStorage.open(new WasmSqliteDatabase(new sqlite3.oo1.DB(':memory:', 'c')));\n}\n\nexport async function openOpfsSqliteStorage(options: OpfsSqliteOptions = {}): Promise<Storage> {\n const directory = options.directory ?? AGENT_DIRECTORY;\n const release = await holdLock(\n options.locks ?? navigator.locks,\n `slicc-agent-sqlite:${directory}`\n );\n try {\n await settlePool(directory, options);\n const sqlite3 = await (options.load ?? init)();\n const pool = await sqlite3.installOpfsSAHPoolVfs({ name: vfsName(directory), directory });\n const db = new pool.OpfsSAHPoolDb(options.file ?? '/agent.sqlite');\n db.exec('PRAGMA journal_mode = TRUNCATE');\n return await SqliteStorage.open(new WasmSqliteDatabase(db, release));\n } catch (error) {\n release();\n throw error;\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type Context, type ReplicatedState } from '@earendil-works/chord';
|
|
2
|
+
import type { ConversationView } from '@earendil-works/pi-durable';
|
|
3
|
+
import { AgentControl, type SendMode } from './services.ts';
|
|
4
|
+
import { type PortEndpoint } from './wire.ts';
|
|
5
|
+
export interface AgentConnection {
|
|
6
|
+
readonly serverId: string;
|
|
7
|
+
readonly control: AgentControl;
|
|
8
|
+
readonly transcript: ReplicatedState<ConversationView>;
|
|
9
|
+
prompt(text: string, whenBusy?: SendMode): Promise<string>;
|
|
10
|
+
close(): Promise<void>;
|
|
11
|
+
}
|
|
12
|
+
export declare function connectAgent(endpoint: PortEndpoint, options?: {
|
|
13
|
+
context?: Context;
|
|
14
|
+
}): Promise<AgentConnection>;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { type Conversation, type Harness, type SubmissionId } from '@earendil-works/pi-durable';
|
|
2
|
+
import type { AgentControl } from './services.ts';
|
|
3
|
+
export declare function submissionId(value: string): SubmissionId | undefined;
|
|
4
|
+
export declare function createAgentControl(harness: Harness, conversation: Conversation): AgentControl;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type Context } from '@earendil-works/chord';
|
|
2
|
+
import type { Agent } from './agent.ts';
|
|
3
|
+
import { type PortEndpoint } from './wire.ts';
|
|
4
|
+
export interface AgentHost {
|
|
5
|
+
readonly serverId: string;
|
|
6
|
+
readonly agent: Agent;
|
|
7
|
+
connect(endpoint: PortEndpoint): void;
|
|
8
|
+
close(): Promise<void>;
|
|
9
|
+
}
|
|
10
|
+
export declare function hostAgent(agent: Agent, options?: {
|
|
11
|
+
serverId?: string;
|
|
12
|
+
context?: Context;
|
|
13
|
+
}): Promise<AgentHost>;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,2 +1,9 @@
|
|
|
1
1
|
export { type Agent, type AgentOptions, answerText, openAgent } from './agent.ts';
|
|
2
|
-
export { type
|
|
2
|
+
export { type AgentConnection, connectAgent } from './client.ts';
|
|
3
|
+
export { createAgentControl } from './control.ts';
|
|
4
|
+
export { type AgentHost, hostAgent } from './host.ts';
|
|
5
|
+
export { AGENT_LOCK, type AgentOwner, type AgentWorker, type OwnerOptions, startAgent, } from './owner.ts';
|
|
6
|
+
export { AGENT_SESSION, AgentControl, AgentSessions, AgentTranscript, type OperationError, type PromptResult, type SendMode, type SendRequest, type SendResponse, } from './services.ts';
|
|
7
|
+
export { AGENT_DIRECTORY, type OpfsSqliteOptions, openMemorySqliteStorage, openOpfsSqliteStorage, WasmSqliteDatabase, } from './sqlite.ts';
|
|
8
|
+
export { PortChannel, type PortEndpoint, PortListener, withUnrefTimers } from './wire.ts';
|
|
9
|
+
export { serveConnections, type WorkerScope } from './worker.ts';
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export interface PoolAccess {
|
|
2
|
+
root?: () => Promise<FileSystemDirectoryHandle>;
|
|
3
|
+
locks?: LockManager;
|
|
4
|
+
attempts?: number;
|
|
5
|
+
delayMs?: number;
|
|
6
|
+
}
|
|
7
|
+
export declare function vfsName(directory: string): string;
|
|
8
|
+
export declare function holdLock(locks: LockManager, name: string): Promise<() => void>;
|
|
9
|
+
export declare function settlePool(directory: string, access?: PoolAccess): Promise<void>;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type AgentConnection } from './client.ts';
|
|
2
|
+
import type { PortEndpoint } from './wire.ts';
|
|
3
|
+
export declare const AGENT_LOCK = "slicc-agent";
|
|
4
|
+
export interface AgentWorker {
|
|
5
|
+
postMessage(message: unknown, transfer: Transferable[]): void;
|
|
6
|
+
terminate(): void;
|
|
7
|
+
}
|
|
8
|
+
export interface AgentOwner {
|
|
9
|
+
connect(): Promise<AgentConnection>;
|
|
10
|
+
restart(): void;
|
|
11
|
+
release(): void;
|
|
12
|
+
}
|
|
13
|
+
export interface OwnerOptions {
|
|
14
|
+
worker: () => AgentWorker;
|
|
15
|
+
locks?: LockManager;
|
|
16
|
+
channel?: () => {
|
|
17
|
+
port1: PortEndpoint;
|
|
18
|
+
port2: Transferable;
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
export declare function startAgent(options: OwnerOptions): Promise<AgentOwner>;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { type Context, type ReplicatedState } from '@earendil-works/chord';
|
|
2
|
+
import type { ConversationView } from '@earendil-works/pi-durable';
|
|
3
|
+
export type SendMode = 'steer' | 'followUp' | 'reject';
|
|
4
|
+
export interface SendRequest {
|
|
5
|
+
text: string;
|
|
6
|
+
whenBusy: SendMode;
|
|
7
|
+
requestId: string | null;
|
|
8
|
+
}
|
|
9
|
+
export interface OperationError {
|
|
10
|
+
code: string;
|
|
11
|
+
message: string;
|
|
12
|
+
}
|
|
13
|
+
export type SendResponse = {
|
|
14
|
+
accepted: true;
|
|
15
|
+
submissionId: string;
|
|
16
|
+
error: null;
|
|
17
|
+
} | {
|
|
18
|
+
accepted: false;
|
|
19
|
+
submissionId: null;
|
|
20
|
+
error: OperationError;
|
|
21
|
+
};
|
|
22
|
+
export type PromptResult = {
|
|
23
|
+
status: 'done';
|
|
24
|
+
text: string;
|
|
25
|
+
reason: null;
|
|
26
|
+
} | {
|
|
27
|
+
status: 'unanswered';
|
|
28
|
+
text: null;
|
|
29
|
+
reason: string;
|
|
30
|
+
};
|
|
31
|
+
export interface AgentControl {
|
|
32
|
+
send(request: SendRequest, context: Context): Promise<SendResponse>;
|
|
33
|
+
wait(submissionId: string, context: Context): Promise<PromptResult>;
|
|
34
|
+
withdraw(submissionId: string, context: Context): Promise<{
|
|
35
|
+
outcome: 'withdrawn' | 'already_placed' | 'not_found';
|
|
36
|
+
}>;
|
|
37
|
+
abort(context: Context): Promise<void>;
|
|
38
|
+
compact(instructions: string | null, context: Context): Promise<SendResponse>;
|
|
39
|
+
}
|
|
40
|
+
export declare const AgentControl: import("@earendil-works/chord").Service<AgentControl>;
|
|
41
|
+
export interface AgentTranscript {
|
|
42
|
+
readonly state: ReplicatedState<ConversationView>;
|
|
43
|
+
}
|
|
44
|
+
export declare const AgentTranscript: import("@earendil-works/chord").Service<AgentTranscript>;
|
|
45
|
+
export interface AgentSessions {
|
|
46
|
+
attach(sessionId: string, context: Context): Promise<void>;
|
|
47
|
+
detach(context: Context): Promise<void>;
|
|
48
|
+
}
|
|
49
|
+
export declare const AgentSessions: import("@earendil-works/chord").Service<AgentSessions>;
|
|
50
|
+
export declare const AGENT_SESSION = "agent";
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { Storage } from '@earendil-works/pi-durable';
|
|
2
|
+
import { type SqliteDatabase, type SqliteExecutor, type SqliteValue } from '@earendil-works/pi-durable/storage/sqlite';
|
|
3
|
+
import init, { type Database, type PreparedStatement } from '@sqlite.org/sqlite-wasm';
|
|
4
|
+
import { type PoolAccess } from './opfs-pool.ts';
|
|
5
|
+
declare class Statements {
|
|
6
|
+
#private;
|
|
7
|
+
constructor(db: Database);
|
|
8
|
+
bound(sql: string, params: readonly SqliteValue[]): PreparedStatement;
|
|
9
|
+
finalize(): void;
|
|
10
|
+
}
|
|
11
|
+
declare class WasmSqliteExecutor implements SqliteExecutor {
|
|
12
|
+
protected readonly db: Database;
|
|
13
|
+
protected readonly statements: Statements;
|
|
14
|
+
constructor(db: Database, statements: Statements);
|
|
15
|
+
exec(sql: string): Promise<void>;
|
|
16
|
+
run(sql: string, ...params: SqliteValue[]): Promise<void>;
|
|
17
|
+
get<T extends object>(sql: string, ...params: SqliteValue[]): Promise<T | undefined>;
|
|
18
|
+
all<T extends object>(sql: string, ...params: SqliteValue[]): Promise<T[]>;
|
|
19
|
+
protected check(): void;
|
|
20
|
+
}
|
|
21
|
+
export declare class WasmSqliteDatabase extends WasmSqliteExecutor implements SqliteDatabase {
|
|
22
|
+
#private;
|
|
23
|
+
constructor(db: Database, closed?: () => void);
|
|
24
|
+
exec(sql: string): Promise<void>;
|
|
25
|
+
run(sql: string, ...params: SqliteValue[]): Promise<void>;
|
|
26
|
+
get<T extends object>(sql: string, ...params: SqliteValue[]): Promise<T | undefined>;
|
|
27
|
+
all<T extends object>(sql: string, ...params: SqliteValue[]): Promise<T[]>;
|
|
28
|
+
transaction<T>(callback: (transaction: SqliteExecutor) => Promise<T>): Promise<T>;
|
|
29
|
+
close(): Promise<void>;
|
|
30
|
+
}
|
|
31
|
+
export interface OpfsSqliteOptions extends PoolAccess {
|
|
32
|
+
directory?: string;
|
|
33
|
+
file?: string;
|
|
34
|
+
load?: typeof init;
|
|
35
|
+
}
|
|
36
|
+
export declare const AGENT_DIRECTORY = "/.slicc/agent";
|
|
37
|
+
export declare function openMemorySqliteStorage(): Promise<Storage>;
|
|
38
|
+
export declare function openOpfsSqliteStorage(options?: OpfsSqliteOptions): Promise<Storage>;
|
|
39
|
+
export {};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { ByteTransportFactory } from '@earendil-works/pi-client';
|
|
2
|
+
import type { ServerListener } from '@earendil-works/pi-server';
|
|
3
|
+
type ByteConnectionAcceptor = Parameters<ServerListener['start']>[0];
|
|
4
|
+
export interface PortEndpoint {
|
|
5
|
+
postMessage(message: unknown, transfer?: Transferable[]): void;
|
|
6
|
+
addEventListener(type: 'message', listener: (event: MessageEvent) => void): void;
|
|
7
|
+
addEventListener(type: 'close' | 'messageerror' | 'error', listener: (event: Event) => void): void;
|
|
8
|
+
start?(): void;
|
|
9
|
+
close?(): void;
|
|
10
|
+
}
|
|
11
|
+
export declare function withUnrefTimers<T>(run: () => T, scope?: {
|
|
12
|
+
setTimeout: typeof setTimeout;
|
|
13
|
+
}): T;
|
|
14
|
+
export declare class PortListener implements ServerListener {
|
|
15
|
+
#private;
|
|
16
|
+
constructor(serverId: string);
|
|
17
|
+
start(accept: ByteConnectionAcceptor): Promise<void>;
|
|
18
|
+
close(): Promise<void>;
|
|
19
|
+
connect(endpoint: PortEndpoint): void;
|
|
20
|
+
}
|
|
21
|
+
export declare class PortChannel {
|
|
22
|
+
#private;
|
|
23
|
+
readonly hello: Promise<string>;
|
|
24
|
+
constructor(endpoint: PortEndpoint);
|
|
25
|
+
readonly transport: ByteTransportFactory;
|
|
26
|
+
}
|
|
27
|
+
export {};
|
package/dist/wire.js
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
function post(endpoint, chunk) {
|
|
2
|
+
const copy = chunk.slice();
|
|
3
|
+
endpoint.postMessage(copy, [copy.buffer]);
|
|
4
|
+
}
|
|
5
|
+
function unrefable(id) {
|
|
6
|
+
return {
|
|
7
|
+
unref() {
|
|
8
|
+
return this;
|
|
9
|
+
},
|
|
10
|
+
ref() {
|
|
11
|
+
return this;
|
|
12
|
+
},
|
|
13
|
+
[Symbol.toPrimitive]: () => id
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
function withUnrefTimers(run, scope = globalThis) {
|
|
17
|
+
const original = scope.setTimeout;
|
|
18
|
+
scope.setTimeout = ((...args) => {
|
|
19
|
+
const id = original(...args);
|
|
20
|
+
return typeof id === "number" ? unrefable(id) : id;
|
|
21
|
+
});
|
|
22
|
+
try {
|
|
23
|
+
return run();
|
|
24
|
+
} finally {
|
|
25
|
+
scope.setTimeout = original;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
class PortConnection {
|
|
29
|
+
closed = false;
|
|
30
|
+
#endpoint;
|
|
31
|
+
#ended;
|
|
32
|
+
constructor(endpoint, ended) {
|
|
33
|
+
this.#endpoint = endpoint;
|
|
34
|
+
this.#ended = ended;
|
|
35
|
+
}
|
|
36
|
+
async send(chunk) {
|
|
37
|
+
if (this.closed) throw new Error("connection is closed");
|
|
38
|
+
post(this.#endpoint, chunk);
|
|
39
|
+
}
|
|
40
|
+
close(finalChunk) {
|
|
41
|
+
if (this.closed) return;
|
|
42
|
+
if (finalChunk) post(this.#endpoint, finalChunk);
|
|
43
|
+
this.closed = true;
|
|
44
|
+
this.#endpoint.postMessage({ close: true });
|
|
45
|
+
this.#endpoint.close?.();
|
|
46
|
+
this.#ended();
|
|
47
|
+
}
|
|
48
|
+
end() {
|
|
49
|
+
this.closed = true;
|
|
50
|
+
this.#ended();
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
class PortListener {
|
|
54
|
+
#serverId;
|
|
55
|
+
#connections = /* @__PURE__ */ new Set();
|
|
56
|
+
#accept;
|
|
57
|
+
constructor(serverId) {
|
|
58
|
+
this.#serverId = serverId;
|
|
59
|
+
}
|
|
60
|
+
async start(accept) {
|
|
61
|
+
this.#accept = accept;
|
|
62
|
+
}
|
|
63
|
+
async close() {
|
|
64
|
+
for (const connection of [...this.#connections]) connection.close();
|
|
65
|
+
this.#accept = void 0;
|
|
66
|
+
}
|
|
67
|
+
connect(endpoint) {
|
|
68
|
+
const accept = this.#accept;
|
|
69
|
+
if (!accept) throw new Error("agent host is not listening");
|
|
70
|
+
const connection = new PortConnection(endpoint, () => this.#connections.delete(connection));
|
|
71
|
+
this.#connections.add(connection);
|
|
72
|
+
const handler = withUnrefTimers(() => accept(connection));
|
|
73
|
+
endpoint.addEventListener("message", ({ data }) => {
|
|
74
|
+
if (data instanceof Uint8Array) handler.onData(data);
|
|
75
|
+
else if (data?.close) {
|
|
76
|
+
connection.end();
|
|
77
|
+
handler.onClose();
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
const failed = (event) => {
|
|
81
|
+
connection.end();
|
|
82
|
+
handler.onError(new Error(`agent port ${event.type}`));
|
|
83
|
+
};
|
|
84
|
+
endpoint.addEventListener("messageerror", failed);
|
|
85
|
+
endpoint.addEventListener("close", () => {
|
|
86
|
+
if (connection.closed) return;
|
|
87
|
+
connection.end();
|
|
88
|
+
handler.onClose();
|
|
89
|
+
});
|
|
90
|
+
endpoint.start?.();
|
|
91
|
+
endpoint.postMessage({ hello: this.#serverId });
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
class PortChannel {
|
|
95
|
+
hello;
|
|
96
|
+
#endpoint;
|
|
97
|
+
#queue = [];
|
|
98
|
+
#handlers;
|
|
99
|
+
#ended;
|
|
100
|
+
#greet;
|
|
101
|
+
#refuse;
|
|
102
|
+
constructor(endpoint) {
|
|
103
|
+
this.#endpoint = endpoint;
|
|
104
|
+
this.hello = new Promise((greet, refuse) => {
|
|
105
|
+
this.#greet = greet;
|
|
106
|
+
this.#refuse = refuse;
|
|
107
|
+
});
|
|
108
|
+
endpoint.addEventListener("message", ({ data }) => this.#receive(data));
|
|
109
|
+
endpoint.addEventListener(
|
|
110
|
+
"messageerror",
|
|
111
|
+
(event) => this.#end(new Error(`agent port ${event.type}`))
|
|
112
|
+
);
|
|
113
|
+
endpoint.addEventListener("error", (event) => this.#end(new Error(`agent port ${event.type}`)));
|
|
114
|
+
endpoint.addEventListener("close", () => this.#end("close"));
|
|
115
|
+
endpoint.start?.();
|
|
116
|
+
}
|
|
117
|
+
transport = (handlers) => {
|
|
118
|
+
this.#handlers = handlers;
|
|
119
|
+
for (const chunk of this.#queue.splice(0)) handlers.onData(chunk);
|
|
120
|
+
if (this.#ended) this.#deliver(this.#ended);
|
|
121
|
+
return {
|
|
122
|
+
send: async (chunk) => {
|
|
123
|
+
if (this.#ended) throw new Error("agent port is closed");
|
|
124
|
+
post(this.#endpoint, chunk);
|
|
125
|
+
},
|
|
126
|
+
close: () => {
|
|
127
|
+
if (this.#ended) return;
|
|
128
|
+
this.#ended = "close";
|
|
129
|
+
this.#refuse(new Error("agent port closed before hello"));
|
|
130
|
+
this.#endpoint.postMessage({ close: true });
|
|
131
|
+
this.#endpoint.close?.();
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
};
|
|
135
|
+
#receive(frame) {
|
|
136
|
+
if (frame instanceof Uint8Array) {
|
|
137
|
+
if (this.#handlers) this.#handlers.onData(frame);
|
|
138
|
+
else this.#queue.push(frame);
|
|
139
|
+
} else if ("hello" in frame) {
|
|
140
|
+
this.#greet(frame.hello);
|
|
141
|
+
} else {
|
|
142
|
+
this.#end("close");
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
#end(reason) {
|
|
146
|
+
if (this.#ended) return;
|
|
147
|
+
this.#ended = reason;
|
|
148
|
+
this.#refuse(reason === "close" ? new Error("agent port closed before hello") : reason);
|
|
149
|
+
this.#deliver(reason);
|
|
150
|
+
}
|
|
151
|
+
#deliver(reason) {
|
|
152
|
+
const handlers = this.#handlers;
|
|
153
|
+
if (!handlers) return;
|
|
154
|
+
this.#handlers = void 0;
|
|
155
|
+
if (reason === "close") handlers.onClose();
|
|
156
|
+
else handlers.onError(reason);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
export {
|
|
160
|
+
PortChannel,
|
|
161
|
+
PortListener,
|
|
162
|
+
withUnrefTimers
|
|
163
|
+
};
|
|
164
|
+
//# sourceMappingURL=wire.js.map
|
package/dist/wire.js.map
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/wire.ts"],
|
|
4
|
+
"sourcesContent": ["import type { ByteTransportFactory, ByteTransportHandlers } from '@earendil-works/pi-client';\nimport type { ServerListener } from '@earendil-works/pi-server';\n\ntype ByteConnectionAcceptor = Parameters<ServerListener['start']>[0];\ntype ByteConnection = Parameters<ByteConnectionAcceptor>[0];\ntype ByteConnectionHandler = ReturnType<ByteConnectionAcceptor>;\n\nexport interface PortEndpoint {\n postMessage(message: unknown, transfer?: Transferable[]): void;\n addEventListener(type: 'message', listener: (event: MessageEvent) => void): void;\n addEventListener(\n type: 'close' | 'messageerror' | 'error',\n listener: (event: Event) => void\n ): void;\n start?(): void;\n close?(): void;\n}\n\ntype Frame = { hello: string } | { close: true } | Uint8Array;\n\nfunction post(endpoint: PortEndpoint, chunk: Uint8Array): void {\n const copy = chunk.slice();\n endpoint.postMessage(copy, [copy.buffer]);\n}\n\nfunction unrefable(id: number): object {\n return {\n unref() {\n return this;\n },\n ref() {\n return this;\n },\n [Symbol.toPrimitive]: () => id,\n };\n}\n\nexport function withUnrefTimers<T>(\n run: () => T,\n scope: { setTimeout: typeof setTimeout } = globalThis\n): T {\n const original = scope.setTimeout;\n scope.setTimeout = ((...args: Parameters<typeof setTimeout>) => {\n const id: unknown = original(...args);\n return typeof id === 'number' ? unrefable(id) : id;\n }) as typeof setTimeout;\n try {\n return run();\n } finally {\n scope.setTimeout = original;\n }\n}\n\nclass PortConnection implements ByteConnection {\n closed = false;\n readonly #endpoint: PortEndpoint;\n readonly #ended: () => void;\n\n constructor(endpoint: PortEndpoint, ended: () => void) {\n this.#endpoint = endpoint;\n this.#ended = ended;\n }\n\n async send(chunk: Uint8Array): Promise<void> {\n if (this.closed) throw new Error('connection is closed');\n post(this.#endpoint, chunk);\n }\n\n close(finalChunk?: Uint8Array): void {\n if (this.closed) return;\n if (finalChunk) post(this.#endpoint, finalChunk);\n this.closed = true;\n this.#endpoint.postMessage({ close: true } satisfies Frame);\n this.#endpoint.close?.();\n this.#ended();\n }\n\n end(): void {\n this.closed = true;\n this.#ended();\n }\n}\n\nexport class PortListener implements ServerListener {\n readonly #serverId: string;\n readonly #connections = new Set<PortConnection>();\n #accept: ByteConnectionAcceptor | undefined;\n\n constructor(serverId: string) {\n this.#serverId = serverId;\n }\n\n async start(accept: ByteConnectionAcceptor): Promise<void> {\n this.#accept = accept;\n }\n\n async close(): Promise<void> {\n for (const connection of [...this.#connections]) connection.close();\n this.#accept = undefined;\n }\n\n connect(endpoint: PortEndpoint): void {\n const accept = this.#accept;\n if (!accept) throw new Error('agent host is not listening');\n const connection = new PortConnection(endpoint, () => this.#connections.delete(connection));\n this.#connections.add(connection);\n const handler: ByteConnectionHandler = withUnrefTimers(() => accept(connection));\n endpoint.addEventListener('message', ({ data }) => {\n if (data instanceof Uint8Array) handler.onData(data);\n else if ((data as { close?: boolean } | null)?.close) {\n connection.end();\n handler.onClose();\n }\n });\n const failed = (event: Event) => {\n connection.end();\n handler.onError(new Error(`agent port ${event.type}`));\n };\n endpoint.addEventListener('messageerror', failed);\n endpoint.addEventListener('close', () => {\n if (connection.closed) return;\n connection.end();\n handler.onClose();\n });\n endpoint.start?.();\n endpoint.postMessage({ hello: this.#serverId } satisfies Frame);\n }\n}\n\nexport class PortChannel {\n readonly hello: Promise<string>;\n readonly #endpoint: PortEndpoint;\n readonly #queue: Uint8Array[] = [];\n #handlers: ByteTransportHandlers | undefined;\n #ended: 'close' | Error | undefined;\n #greet!: (serverId: string) => void;\n #refuse!: (error: Error) => void;\n\n constructor(endpoint: PortEndpoint) {\n this.#endpoint = endpoint;\n this.hello = new Promise((greet, refuse) => {\n this.#greet = greet;\n this.#refuse = refuse;\n });\n endpoint.addEventListener('message', ({ data }) => this.#receive(data as Frame));\n endpoint.addEventListener('messageerror', (event) =>\n this.#end(new Error(`agent port ${event.type}`))\n );\n endpoint.addEventListener('error', (event) => this.#end(new Error(`agent port ${event.type}`)));\n endpoint.addEventListener('close', () => this.#end('close'));\n endpoint.start?.();\n }\n\n readonly transport: ByteTransportFactory = (handlers) => {\n this.#handlers = handlers;\n for (const chunk of this.#queue.splice(0)) handlers.onData(chunk);\n if (this.#ended) this.#deliver(this.#ended);\n return {\n send: async (chunk) => {\n if (this.#ended) throw new Error('agent port is closed');\n post(this.#endpoint, chunk);\n },\n close: () => {\n if (this.#ended) return;\n this.#ended = 'close';\n this.#refuse(new Error('agent port closed before hello'));\n this.#endpoint.postMessage({ close: true } satisfies Frame);\n this.#endpoint.close?.();\n },\n };\n };\n\n #receive(frame: Frame): void {\n if (frame instanceof Uint8Array) {\n if (this.#handlers) this.#handlers.onData(frame);\n else this.#queue.push(frame);\n } else if ('hello' in frame) {\n this.#greet(frame.hello);\n } else {\n this.#end('close');\n }\n }\n\n #end(reason: 'close' | Error): void {\n if (this.#ended) return;\n this.#ended = reason;\n this.#refuse(reason === 'close' ? new Error('agent port closed before hello') : reason);\n this.#deliver(reason);\n }\n\n #deliver(reason: 'close' | Error): void {\n const handlers = this.#handlers;\n if (!handlers) return;\n this.#handlers = undefined;\n if (reason === 'close') handlers.onClose();\n else handlers.onError(reason);\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/worker.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
function serveConnections(scope, host) {
|
|
2
|
+
scope.addEventListener("message", ({ data }) => {
|
|
3
|
+
const port = data?.connect;
|
|
4
|
+
if (!port) return;
|
|
5
|
+
host.then(
|
|
6
|
+
(ready) => ready.connect(port),
|
|
7
|
+
() => port.close?.()
|
|
8
|
+
);
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
export {
|
|
12
|
+
serveConnections
|
|
13
|
+
};
|
|
14
|
+
//# sourceMappingURL=worker.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/worker.ts"],
|
|
4
|
+
"sourcesContent": ["import type { AgentHost } from './host.ts';\nimport type { PortEndpoint } from './wire.ts';\n\nexport interface WorkerScope {\n addEventListener(type: 'message', listener: (event: MessageEvent) => void): void;\n}\n\nexport function serveConnections(scope: WorkerScope, host: Promise<AgentHost>): void {\n scope.addEventListener('message', ({ data }) => {\n const port = (data as { connect?: PortEndpoint } | null)?.connect;\n if (!port) return;\n host.then(\n (ready) => ready.connect(port),\n () => port.close?.()\n );\n });\n}\n"],
|
|
5
|
+
"mappings": "AAOO,SAAS,iBAAiB,OAAoB,MAAgC;AACnF,QAAM,iBAAiB,WAAW,CAAC,EAAE,KAAK,MAAM;AAC9C,UAAM,OAAQ,MAA4C;AAC1D,QAAI,CAAC,KAAM;AACX,SAAK;AAAA,MACH,CAAC,UAAU,MAAM,QAAQ,IAAI;AAAA,MAC7B,MAAM,KAAK,QAAQ;AAAA,IACrB;AAAA,EACF,CAAC;AACH;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-ecoverse/slicc-agent",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "SLICC's agent core on pi durable: an agent worker, session storage, remoting and pi extensions",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -59,6 +59,10 @@
|
|
|
59
59
|
"dependencies": {
|
|
60
60
|
"@earendil-works/chord": "1.0.4",
|
|
61
61
|
"@earendil-works/pi-ai": "1.0.4",
|
|
62
|
-
"@earendil-works/pi-
|
|
62
|
+
"@earendil-works/pi-client": "1.0.4",
|
|
63
|
+
"@earendil-works/pi-durable": "1.0.4",
|
|
64
|
+
"@earendil-works/pi-protocol": "1.0.4",
|
|
65
|
+
"@earendil-works/pi-server": "1.0.4",
|
|
66
|
+
"@sqlite.org/sqlite-wasm": "3.53.4-build2"
|
|
63
67
|
}
|
|
64
68
|
}
|
package/dist/port.js
DELETED
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
function isRequest(data) {
|
|
2
|
-
const value = data;
|
|
3
|
-
return typeof value?.id === "number" && typeof value.prompt === "string";
|
|
4
|
-
}
|
|
5
|
-
function reason(error) {
|
|
6
|
-
return error instanceof Error ? error.message : String(error);
|
|
7
|
-
}
|
|
8
|
-
function serveAgent(endpoint, agent) {
|
|
9
|
-
endpoint.addEventListener("message", ({ data }) => {
|
|
10
|
-
if (!isRequest(data)) return;
|
|
11
|
-
agent.then((ready) => ready.prompt(data.prompt)).then(
|
|
12
|
-
(text) => endpoint.postMessage({ id: data.id, text }),
|
|
13
|
-
(error) => endpoint.postMessage({ id: data.id, error: reason(error) })
|
|
14
|
-
);
|
|
15
|
-
});
|
|
16
|
-
endpoint.start?.();
|
|
17
|
-
}
|
|
18
|
-
function failure(event) {
|
|
19
|
-
const message = event.message;
|
|
20
|
-
return new Error(message ? `agent failed: ${message}` : `agent failed: ${event.type}`);
|
|
21
|
-
}
|
|
22
|
-
function connectAgent(endpoint) {
|
|
23
|
-
const pending = /* @__PURE__ */ new Map();
|
|
24
|
-
let broken;
|
|
25
|
-
let next = 0;
|
|
26
|
-
const fail = (event) => {
|
|
27
|
-
broken = failure(event);
|
|
28
|
-
for (const call of pending.values()) call.reject(broken);
|
|
29
|
-
pending.clear();
|
|
30
|
-
};
|
|
31
|
-
endpoint.addEventListener("message", ({ data }) => {
|
|
32
|
-
const reply = data;
|
|
33
|
-
const call = pending.get(reply.id);
|
|
34
|
-
if (!call) return;
|
|
35
|
-
pending.delete(reply.id);
|
|
36
|
-
if ("error" in reply) call.reject(new Error(reply.error));
|
|
37
|
-
else call.resolve(reply.text);
|
|
38
|
-
});
|
|
39
|
-
endpoint.addEventListener("error", fail);
|
|
40
|
-
endpoint.addEventListener("messageerror", fail);
|
|
41
|
-
endpoint.start?.();
|
|
42
|
-
return {
|
|
43
|
-
prompt(text) {
|
|
44
|
-
if (broken) return Promise.reject(broken);
|
|
45
|
-
const id = ++next;
|
|
46
|
-
return new Promise((resolve, reject) => {
|
|
47
|
-
pending.set(id, { resolve, reject });
|
|
48
|
-
endpoint.postMessage({ id, prompt: text });
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
export {
|
|
54
|
-
connectAgent,
|
|
55
|
-
serveAgent
|
|
56
|
-
};
|
|
57
|
-
//# sourceMappingURL=port.js.map
|
package/dist/port.js.map
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../src/port.ts"],
|
|
4
|
-
"sourcesContent": ["import type { Agent } from './agent.ts';\n\nexport interface AgentRequest {\n id: number;\n prompt: string;\n}\n\nexport type AgentReply = { id: number; text: string } | { id: number; error: string };\n\nexport interface MessageEndpoint {\n postMessage(message: unknown): void;\n addEventListener(type: 'message', listener: (event: MessageEvent) => void): void;\n addEventListener(type: 'error' | 'messageerror', listener: (event: Event) => void): void;\n start?(): void;\n}\n\nfunction isRequest(data: unknown): data is AgentRequest {\n const value = data as Partial<AgentRequest> | null;\n return typeof value?.id === 'number' && typeof value.prompt === 'string';\n}\n\nfunction reason(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\nexport function serveAgent(endpoint: MessageEndpoint, agent: Promise<Agent>): void {\n endpoint.addEventListener('message', ({ data }) => {\n if (!isRequest(data)) return;\n agent\n .then((ready) => ready.prompt(data.prompt))\n .then(\n (text) => endpoint.postMessage({ id: data.id, text } satisfies AgentReply),\n (error: unknown) =>\n endpoint.postMessage({ id: data.id, error: reason(error) } satisfies AgentReply)\n );\n });\n endpoint.start?.();\n}\n\nexport interface AgentClient {\n prompt(text: string): Promise<string>;\n}\n\nfunction failure(event: Event): Error {\n const message = (event as ErrorEvent).message;\n return new Error(message ? `agent failed: ${message}` : `agent failed: ${event.type}`);\n}\n\nexport function connectAgent(endpoint: MessageEndpoint): AgentClient {\n const pending = new Map<number, { resolve(text: string): void; reject(error: Error): void }>();\n let broken: Error | undefined;\n let next = 0;\n const fail = (event: Event) => {\n broken = failure(event);\n for (const call of pending.values()) call.reject(broken);\n pending.clear();\n };\n endpoint.addEventListener('message', ({ data }) => {\n const reply = data as AgentReply;\n const call = pending.get(reply.id);\n if (!call) return;\n pending.delete(reply.id);\n if ('error' in reply) call.reject(new Error(reply.error));\n else call.resolve(reply.text);\n });\n endpoint.addEventListener('error', fail);\n endpoint.addEventListener('messageerror', fail);\n endpoint.start?.();\n return {\n prompt(text) {\n if (broken) return Promise.reject(broken);\n const id = ++next;\n return new Promise((resolve, reject) => {\n pending.set(id, { resolve, reject });\n endpoint.postMessage({ id, prompt: text } satisfies AgentRequest);\n });\n },\n };\n}\n"],
|
|
5
|
-
"mappings": "AAgBA,SAAS,UAAU,MAAqC;AACtD,QAAM,QAAQ;AACd,SAAO,OAAO,OAAO,OAAO,YAAY,OAAO,MAAM,WAAW;AAClE;AAEA,SAAS,OAAO,OAAwB;AACtC,SAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAC9D;AAEO,SAAS,WAAW,UAA2B,OAA6B;AACjF,WAAS,iBAAiB,WAAW,CAAC,EAAE,KAAK,MAAM;AACjD,QAAI,CAAC,UAAU,IAAI,EAAG;AACtB,UACG,KAAK,CAAC,UAAU,MAAM,OAAO,KAAK,MAAM,CAAC,EACzC;AAAA,MACC,CAAC,SAAS,SAAS,YAAY,EAAE,IAAI,KAAK,IAAI,KAAK,CAAsB;AAAA,MACzE,CAAC,UACC,SAAS,YAAY,EAAE,IAAI,KAAK,IAAI,OAAO,OAAO,KAAK,EAAE,CAAsB;AAAA,IACnF;AAAA,EACJ,CAAC;AACD,WAAS,QAAQ;AACnB;AAMA,SAAS,QAAQ,OAAqB;AACpC,QAAM,UAAW,MAAqB;AACtC,SAAO,IAAI,MAAM,UAAU,iBAAiB,OAAO,KAAK,iBAAiB,MAAM,IAAI,EAAE;AACvF;AAEO,SAAS,aAAa,UAAwC;AACnE,QAAM,UAAU,oBAAI,IAAyE;AAC7F,MAAI;AACJ,MAAI,OAAO;AACX,QAAM,OAAO,CAAC,UAAiB;AAC7B,aAAS,QAAQ,KAAK;AACtB,eAAW,QAAQ,QAAQ,OAAO,EAAG,MAAK,OAAO,MAAM;AACvD,YAAQ,MAAM;AAAA,EAChB;AACA,WAAS,iBAAiB,WAAW,CAAC,EAAE,KAAK,MAAM;AACjD,UAAM,QAAQ;AACd,UAAM,OAAO,QAAQ,IAAI,MAAM,EAAE;AACjC,QAAI,CAAC,KAAM;AACX,YAAQ,OAAO,MAAM,EAAE;AACvB,QAAI,WAAW,MAAO,MAAK,OAAO,IAAI,MAAM,MAAM,KAAK,CAAC;AAAA,QACnD,MAAK,QAAQ,MAAM,IAAI;AAAA,EAC9B,CAAC;AACD,WAAS,iBAAiB,SAAS,IAAI;AACvC,WAAS,iBAAiB,gBAAgB,IAAI;AAC9C,WAAS,QAAQ;AACjB,SAAO;AAAA,IACL,OAAO,MAAM;AACX,UAAI,OAAQ,QAAO,QAAQ,OAAO,MAAM;AACxC,YAAM,KAAK,EAAE;AACb,aAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,gBAAQ,IAAI,IAAI,EAAE,SAAS,OAAO,CAAC;AACnC,iBAAS,YAAY,EAAE,IAAI,QAAQ,KAAK,CAAwB;AAAA,MAClE,CAAC;AAAA,IACH;AAAA,EACF;AACF;",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|
package/dist/types/port.d.ts
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import type { Agent } from './agent.ts';
|
|
2
|
-
export interface AgentRequest {
|
|
3
|
-
id: number;
|
|
4
|
-
prompt: string;
|
|
5
|
-
}
|
|
6
|
-
export type AgentReply = {
|
|
7
|
-
id: number;
|
|
8
|
-
text: string;
|
|
9
|
-
} | {
|
|
10
|
-
id: number;
|
|
11
|
-
error: string;
|
|
12
|
-
};
|
|
13
|
-
export interface MessageEndpoint {
|
|
14
|
-
postMessage(message: unknown): void;
|
|
15
|
-
addEventListener(type: 'message', listener: (event: MessageEvent) => void): void;
|
|
16
|
-
addEventListener(type: 'error' | 'messageerror', listener: (event: Event) => void): void;
|
|
17
|
-
start?(): void;
|
|
18
|
-
}
|
|
19
|
-
export declare function serveAgent(endpoint: MessageEndpoint, agent: Promise<Agent>): void;
|
|
20
|
-
export interface AgentClient {
|
|
21
|
-
prompt(text: string): Promise<string>;
|
|
22
|
-
}
|
|
23
|
-
export declare function connectAgent(endpoint: MessageEndpoint): AgentClient;
|