@ai-ecoverse/slicc-agent 1.2.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 -6
- 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 +26 -4
- package/dist/index.js.map +2 -2
- 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/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 +7 -1
- package/dist/types/owner.d.ts +21 -0
- package/dist/types/services.d.ts +50 -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 +4 -1
- 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,18 +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
|
-
- `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.
|
|
20
|
-
- `
|
|
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`.
|
|
21
35
|
|
|
22
36
|
## Develop
|
|
23
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,22 +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";
|
|
6
15
|
import {
|
|
7
16
|
AGENT_DIRECTORY,
|
|
8
17
|
openMemorySqliteStorage,
|
|
9
18
|
openOpfsSqliteStorage,
|
|
10
19
|
WasmSqliteDatabase
|
|
11
20
|
} from "./sqlite.js";
|
|
21
|
+
import { PortChannel, PortListener, withUnrefTimers } from "./wire.js";
|
|
22
|
+
import { serveConnections } from "./worker.js";
|
|
12
23
|
export {
|
|
13
24
|
AGENT_DIRECTORY,
|
|
25
|
+
AGENT_LOCK,
|
|
26
|
+
AGENT_SESSION,
|
|
27
|
+
AgentControl,
|
|
28
|
+
AgentSessions,
|
|
29
|
+
AgentTranscript,
|
|
30
|
+
PortChannel,
|
|
31
|
+
PortListener,
|
|
14
32
|
WasmSqliteDatabase,
|
|
15
33
|
answerText,
|
|
16
34
|
connectAgent,
|
|
35
|
+
createAgentControl,
|
|
36
|
+
hostAgent,
|
|
17
37
|
openAgent,
|
|
18
38
|
openMemorySqliteStorage,
|
|
19
39
|
openOpfsSqliteStorage,
|
|
20
|
-
|
|
40
|
+
serveConnections,
|
|
41
|
+
startAgent,
|
|
42
|
+
withUnrefTimers
|
|
21
43
|
};
|
|
22
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
|
}
|
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
|
+
}
|
|
@@ -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,3 +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';
|
|
3
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,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,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,7 +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-client": "1.0.4",
|
|
62
63
|
"@earendil-works/pi-durable": "1.0.4",
|
|
64
|
+
"@earendil-works/pi-protocol": "1.0.4",
|
|
65
|
+
"@earendil-works/pi-server": "1.0.4",
|
|
63
66
|
"@sqlite.org/sqlite-wasm": "3.53.4-build2"
|
|
64
67
|
}
|
|
65
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;
|