@ai-ecoverse/slicc-agent 2.2.0 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/README.md +34 -2
  2. package/dist/agent-worker.js +6 -0
  3. package/dist/agent-worker.js.map +7 -0
  4. package/dist/client.js +4 -1
  5. package/dist/client.js.map +2 -2
  6. package/dist/credentials.js +100 -0
  7. package/dist/credentials.js.map +7 -0
  8. package/dist/host.js +15 -3
  9. package/dist/host.js.map +2 -2
  10. package/dist/index.js +30 -1
  11. package/dist/index.js.map +2 -2
  12. package/dist/listener.js +73 -0
  13. package/dist/listener.js.map +7 -0
  14. package/dist/main.js +46 -0
  15. package/dist/main.js.map +7 -0
  16. package/dist/net.js +39 -0
  17. package/dist/net.js.map +7 -0
  18. package/dist/patches.js +24 -0
  19. package/dist/patches.js.map +7 -0
  20. package/dist/prompt.js +18 -0
  21. package/dist/prompt.js.map +7 -0
  22. package/dist/settings.js +77 -0
  23. package/dist/settings.js.map +7 -0
  24. package/dist/spectrum/index.js +10 -2
  25. package/dist/spectrum/index.js.map +3 -3
  26. package/dist/spectrum/settings-port.js +73 -0
  27. package/dist/spectrum/settings-port.js.map +7 -0
  28. package/dist/types/agent-worker.d.ts +1 -0
  29. package/dist/types/client.d.ts +2 -0
  30. package/dist/types/credentials.d.ts +12 -0
  31. package/dist/types/host.d.ts +3 -1
  32. package/dist/types/index.d.ts +8 -1
  33. package/dist/types/listener.d.ts +11 -0
  34. package/dist/types/main.d.ts +23 -0
  35. package/dist/types/net.d.ts +19 -0
  36. package/dist/types/patches.d.ts +10 -0
  37. package/dist/types/prompt.d.ts +1 -0
  38. package/dist/types/settings.d.ts +37 -0
  39. package/dist/types/spectrum/index.d.ts +6 -1
  40. package/dist/types/spectrum/settings-port.d.ts +17 -0
  41. package/dist/types/wire.d.ts +6 -10
  42. package/dist/wire.js +1 -67
  43. package/dist/wire.js.map +2 -2
  44. package/package.json +9 -3
package/README.md CHANGED
@@ -57,6 +57,28 @@ const agent = await openAgent({ models, model, storage, registry, env: kernelEnv
57
57
 
58
58
  A conversation without a `cwd` works in `/home`.
59
59
 
60
+ ### The worker seven starts
61
+
62
+ `@ai-ecoverse/slicc-agent/agent-worker` is a complete agent worker. It waits for the page kernel's port, then:
63
+ - attaches to the kernel and routes its cross-origin `fetch` through the kernel's transport when that transport isn't bound by CORS (slicc-node, slicc-swift or slicc-extension);
64
+ - opens the encrypted credential store, the AWS Bedrock provider and the session in OPFS SQLite;
65
+ - installs durable's coding tools and SLICC's system prompt, and serves the agent.
66
+
67
+ ```js
68
+ const owner = await startAgent({
69
+ worker: () => new Worker(new URL('@ai-ecoverse/slicc-agent/agent-worker', import.meta.url), { type: 'module' }),
70
+ kernel: { connect: () => kernel.connect() },
71
+ });
72
+ ```
73
+
74
+ `runAgentWorker(self, options)` is the same with its parts replaceable (model, providers, kernel attach, credentials, storage).
75
+
76
+ **Providers.** The first provider is AWS Bedrock with a Bedrock API key as the bearer token; the default model is `us.anthropic.claude-sonnet-5-5`. Bedrock sends no CORS headers, so it needs a CORS-free transport, and its account says so (`needs: 'cors-free-transport'`).
77
+
78
+ **Credentials.** They live in the IndexedDB database `slicc-agent-credentials`, sealed with AES-GCM under a non-extractable key that never leaves WebCrypto. They never enter the session database or the transcript, and followers never see them.
79
+
80
+ **Settings.** The `slicc.agent.settings` service shows the models and accounts and takes `connect(providerId, apiKey)` and `disconnect(providerId)`.
81
+
60
82
  ### In slicc-spectrum
61
83
 
62
84
  `@ai-ecoverse/slicc-agent/spectrum` turns an `AgentConnection` into slicc-spectrum's `AgentPort`, so `<slicc-app>` shows the real agent:
@@ -65,12 +87,22 @@ A conversation without a `cwd` works in `/home`.
65
87
  import { createAgentModel } from '@ai-ecoverse/slicc-agent/spectrum';
66
88
 
67
89
  const agent = await owner.connect();
68
- app.model = { ...createKernelModel({ kernel, root }), ...createAgentModel(agent) };
90
+ app.model = { ...createKernelModel({ kernel, root }), ...createAgentModel(agent, { storage: localStorage }) };
69
91
  ```
70
92
 
93
+ `createAgentModel()` returns the `agent` and `settings` ports. Settings keeps the UI preferences in `storage`; model and thinking belong to the conversation, and accounts and models come from the worker.
94
+
71
95
  The adapter keeps the replicated transcript and maps it on every update. User, assistant and compaction entries become messages; tool results fill in the tool calls they answer; the generation in flight shows as a streaming message; retries and running compactions show as system messages. `busy()` and `queue()` read durable's live and inbox documents. A plain send queues a follow-up, and a steer send steers, as slicc-spectrum's composer asks today. There is one cone so far; scoops, the freezer, licks and questions come later. slicc-spectrum is a peer dependency (≥ 1.7.0) used for types only.
72
96
 
73
- 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`.
97
+ ## Patched dependencies
98
+
99
+ Fixes to pi stay in this repository. [`patches/patches.json`](patches/patches.json) lists each one with its `kind`, `package`, `patchedVersion`, `reason`, `removeWhen` and `verify` command, and an optional `marker` (a file in the package and a string it must contain):
100
+
101
+ - `monkeypatch`: code that runs in users' browsers is patched at runtime. `applyPatches()` in [`src/patches.ts`](src/patches.ts) applies them once before pi is used: `PortListener.start()` calls it, so `hostAgent` and a `PortListener` used directly with pi's `Server` both get them. Each one checks that the code it replaces still looks as expected and throws, naming its manifest entry, if not. Today there is one: pi-server's `Server.accept` calls `unref()` on its handshake timer, which browsers don't have, so the patch gives numeric timer ids a no-op `unref`.
102
+ - `patch-package`: repository-only fixes go in `patches/<package>+<version>.patch`, applied by a `postinstall` that runs [patch-package](https://github.com/ds300/patch-package). There are none yet.
103
+ - `environment`: something the host must provide. pi-server imports `randomUUID` from `node:crypto`; slicc-bios serves a stub backed by `globalThis.crypto`, and the integration bundles alias it to `test/integration/shims/node-crypto.js`.
104
+
105
+ `npm run lint:patches` (part of `npm run lint`) fails when an installed version differs from `patchedVersion`, a marker is gone, a monkeypatch isn't named in `src/patches.ts`, a patch file and its entry don't match, or `renovate.json` doesn't route the package to the `patched dependencies` group with automerge off. That group covers every `@earendil-works/*` package, so a pi bump is always a reviewed PR: rerun each entry's `verify`, then move `patchedVersion` forward or drop the entry.
74
106
 
75
107
  ## Develop
76
108
 
@@ -0,0 +1,6 @@
1
+ import { runAgentWorker } from "./main.js";
2
+ const host = runAgentWorker(globalThis);
3
+ export {
4
+ host
5
+ };
6
+ //# sourceMappingURL=agent-worker.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/agent-worker.ts"],
4
+ "sourcesContent": ["import { type AgentWorkerScope, runAgentWorker } from './main.ts';\n\nexport const host = runAgentWorker(globalThis as unknown as AgentWorkerScope);\n"],
5
+ "mappings": "AAAA,SAAgC,sBAAsB;AAE/C,MAAM,OAAO,eAAe,UAAyC;",
6
+ "names": []
7
+ }
package/dist/client.js CHANGED
@@ -9,6 +9,7 @@ import {
9
9
  AgentSessions,
10
10
  AgentTranscript
11
11
  } from "./services.js";
12
+ import { AgentSettings } from "./settings.js";
12
13
  import { PortChannel } from "./wire.js";
13
14
  function attached(client) {
14
15
  return new Promise((resolve) => {
@@ -32,17 +33,19 @@ async function connectAgent(endpoint, options = {}) {
32
33
  await server.use(AgentSessions).attach(AGENT_SESSION, context);
33
34
  await ready;
34
35
  const session = createRemoteServiceBinding({
35
- services: [AgentControl, AgentTranscript],
36
+ services: [AgentControl, AgentTranscript, AgentSettings],
36
37
  transport: createClientServiceTransport(client, () => client.attachment),
37
38
  bound: true
38
39
  });
39
40
  const control = session.use(AgentControl);
40
41
  const transcript = session.use(AgentTranscript).state;
42
+ const settings = session.use(AgentSettings);
41
43
  await session.ready(context);
42
44
  return {
43
45
  serverId,
44
46
  control,
45
47
  transcript,
48
+ settings,
46
49
  async prompt(text, whenBusy = "followUp") {
47
50
  const sent = await control.send({ text, whenBusy, requestId: null }, context);
48
51
  if (!sent.accepted) throw new Error(`${sent.error.code}: ${sent.error.message}`);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
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;",
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 { AgentSettings } from './settings.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 readonly settings: AgentSettings;\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, AgentSettings],\n transport: createClientServiceTransport(client, () => client.attachment),\n bound: true,\n });\n const control = session.use(AgentControl);\n const transcript = session.use(AgentTranscript).state;\n const settings = session.use(AgentSettings);\n await session.ready(context);\n return {\n serverId,\n control,\n transcript,\n settings,\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,qBAAqB;AAC9B,SAAS,mBAAsC;AAW/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,iBAAiB,aAAa;AAAA,IACvD,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,WAAW,QAAQ,IAAI,aAAa;AAC1C,QAAM,QAAQ,MAAM,OAAO;AAC3B,SAAO;AAAA,IACL;AAAA,IACA;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
6
  "names": []
7
7
  }
@@ -0,0 +1,100 @@
1
+ const CREDENTIALS_DATABASE = "slicc-agent-credentials";
2
+ function request(operation) {
3
+ return new Promise((resolve, reject) => {
4
+ operation.onsuccess = () => resolve(operation.result);
5
+ operation.onerror = () => reject(operation.error);
6
+ });
7
+ }
8
+ function open(name) {
9
+ const opening = indexedDB.open(name, 1);
10
+ opening.onupgradeneeded = () => {
11
+ opening.result.createObjectStore("keys");
12
+ opening.result.createObjectStore("credentials");
13
+ };
14
+ return request(opening);
15
+ }
16
+ async function sealingKey(db) {
17
+ const keys = db.transaction("keys", "readonly").objectStore("keys");
18
+ const existing = await request(keys.get("aes"));
19
+ if (existing) return existing;
20
+ const key = await crypto.subtle.generateKey({ name: "AES-GCM", length: 256 }, false, [
21
+ "encrypt",
22
+ "decrypt"
23
+ ]);
24
+ const store = db.transaction("keys", "readwrite").objectStore("keys");
25
+ await request(store.add(key, "aes")).catch(() => void 0);
26
+ return await request(
27
+ db.transaction("keys", "readonly").objectStore("keys").get("aes")
28
+ );
29
+ }
30
+ class EncryptedCredentialStore {
31
+ #db;
32
+ #key;
33
+ #chains = /* @__PURE__ */ new Map();
34
+ constructor(db, key) {
35
+ this.#db = db;
36
+ this.#key = key;
37
+ }
38
+ static async open(name = CREDENTIALS_DATABASE) {
39
+ const db = await open(name);
40
+ return new EncryptedCredentialStore(db, await sealingKey(db));
41
+ }
42
+ #store(mode) {
43
+ return this.#db.transaction("credentials", mode).objectStore("credentials");
44
+ }
45
+ async read(providerId) {
46
+ const sealed = await request(this.#store("readonly").get(providerId));
47
+ if (!sealed) return void 0;
48
+ const plain = await crypto.subtle.decrypt(
49
+ { name: "AES-GCM", iv: sealed.iv },
50
+ this.#key,
51
+ sealed.data
52
+ );
53
+ return JSON.parse(new TextDecoder().decode(plain));
54
+ }
55
+ async list() {
56
+ const ids = await request(this.#store("readonly").getAllKeys());
57
+ const found = [];
58
+ for (const providerId of ids) {
59
+ const credential = await this.read(providerId);
60
+ if (credential) found.push({ providerId, type: credential.type });
61
+ }
62
+ return found;
63
+ }
64
+ #serial(providerId, operation) {
65
+ const result = (this.#chains.get(providerId) ?? Promise.resolve()).then(operation);
66
+ this.#chains.set(
67
+ providerId,
68
+ result.catch(() => void 0)
69
+ );
70
+ return result;
71
+ }
72
+ modify(providerId, fn) {
73
+ return this.#serial(providerId, async () => {
74
+ const current = await this.read(providerId);
75
+ const next = await fn(current);
76
+ if (next === void 0) return current;
77
+ const iv = crypto.getRandomValues(new Uint8Array(12));
78
+ const data = await crypto.subtle.encrypt(
79
+ { name: "AES-GCM", iv },
80
+ this.#key,
81
+ new TextEncoder().encode(JSON.stringify(next))
82
+ );
83
+ await request(this.#store("readwrite").put({ iv, data }, providerId));
84
+ return next;
85
+ });
86
+ }
87
+ delete(providerId) {
88
+ return this.#serial(providerId, async () => {
89
+ await request(this.#store("readwrite").delete(providerId));
90
+ });
91
+ }
92
+ close() {
93
+ this.#db.close();
94
+ }
95
+ }
96
+ export {
97
+ CREDENTIALS_DATABASE,
98
+ EncryptedCredentialStore
99
+ };
100
+ //# sourceMappingURL=credentials.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/credentials.ts"],
4
+ "sourcesContent": ["import type { Credential, CredentialInfo, CredentialStore } from '@earendil-works/pi-ai';\n\nexport const CREDENTIALS_DATABASE = 'slicc-agent-credentials';\n\ninterface Sealed {\n iv: Uint8Array<ArrayBuffer>;\n data: ArrayBuffer;\n}\n\nfunction request<T>(operation: IDBRequest<T>): Promise<T> {\n return new Promise((resolve, reject) => {\n operation.onsuccess = () => resolve(operation.result);\n operation.onerror = () => reject(operation.error);\n });\n}\n\nfunction open(name: string): Promise<IDBDatabase> {\n const opening = indexedDB.open(name, 1);\n opening.onupgradeneeded = () => {\n opening.result.createObjectStore('keys');\n opening.result.createObjectStore('credentials');\n };\n return request(opening);\n}\n\nasync function sealingKey(db: IDBDatabase): Promise<CryptoKey> {\n const keys = db.transaction('keys', 'readonly').objectStore('keys');\n const existing = (await request(keys.get('aes'))) as CryptoKey | undefined;\n if (existing) return existing;\n const key = await crypto.subtle.generateKey({ name: 'AES-GCM', length: 256 }, false, [\n 'encrypt',\n 'decrypt',\n ]);\n const store = db.transaction('keys', 'readwrite').objectStore('keys');\n await request(store.add(key, 'aes')).catch(() => undefined);\n return (await request(\n db.transaction('keys', 'readonly').objectStore('keys').get('aes')\n )) as CryptoKey;\n}\n\nexport class EncryptedCredentialStore implements CredentialStore {\n readonly #db: IDBDatabase;\n readonly #key: CryptoKey;\n readonly #chains = new Map<string, Promise<unknown>>();\n\n private constructor(db: IDBDatabase, key: CryptoKey) {\n this.#db = db;\n this.#key = key;\n }\n\n static async open(name = CREDENTIALS_DATABASE): Promise<EncryptedCredentialStore> {\n const db = await open(name);\n return new EncryptedCredentialStore(db, await sealingKey(db));\n }\n\n #store(mode: IDBTransactionMode): IDBObjectStore {\n return this.#db.transaction('credentials', mode).objectStore('credentials');\n }\n\n async read(providerId: string): Promise<Credential | undefined> {\n const sealed = (await request(this.#store('readonly').get(providerId))) as Sealed | undefined;\n if (!sealed) return undefined;\n const plain = await crypto.subtle.decrypt(\n { name: 'AES-GCM', iv: sealed.iv },\n this.#key,\n sealed.data\n );\n return JSON.parse(new TextDecoder().decode(plain)) as Credential;\n }\n\n async list(): Promise<readonly CredentialInfo[]> {\n const ids = (await request(this.#store('readonly').getAllKeys())) as string[];\n const found: CredentialInfo[] = [];\n for (const providerId of ids) {\n const credential = await this.read(providerId);\n if (credential) found.push({ providerId, type: credential.type });\n }\n return found;\n }\n\n #serial<T>(providerId: string, operation: () => Promise<T>): Promise<T> {\n const result = (this.#chains.get(providerId) ?? Promise.resolve()).then(operation);\n this.#chains.set(\n providerId,\n result.catch(() => undefined)\n );\n return result;\n }\n\n modify(\n providerId: string,\n fn: (current: Credential | undefined) => Promise<Credential | undefined>\n ): Promise<Credential | undefined> {\n return this.#serial(providerId, async () => {\n const current = await this.read(providerId);\n const next = await fn(current);\n if (next === undefined) return current;\n const iv = crypto.getRandomValues(new Uint8Array(12));\n const data = await crypto.subtle.encrypt(\n { name: 'AES-GCM', iv },\n this.#key,\n new TextEncoder().encode(JSON.stringify(next))\n );\n await request(this.#store('readwrite').put({ iv, data } satisfies Sealed, providerId));\n return next;\n });\n }\n\n delete(providerId: string): Promise<void> {\n return this.#serial(providerId, async () => {\n await request(this.#store('readwrite').delete(providerId));\n });\n }\n\n close(): void {\n this.#db.close();\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
package/dist/host.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  createRemoteServiceEndpoint,
3
- RemoteServiceProvider
3
+ RemoteServiceProvider,
4
+ replicatedState
4
5
  } from "@earendil-works/chord";
5
6
  import { BACKGROUND_CONTEXT } from "@earendil-works/chord/context";
6
7
  import {
@@ -8,8 +9,9 @@ import {
8
9
  SessionNotFoundError
9
10
  } from "@earendil-works/pi-server";
10
11
  import { createAgentControl } from "./control.js";
12
+ import { PortListener } from "./listener.js";
11
13
  import { AGENT_SESSION, AgentControl, AgentSessions, AgentTranscript } from "./services.js";
12
- import { PortListener } from "./wire.js";
14
+ import { AgentSettings } from "./settings.js";
13
15
  function endpointFor(provider) {
14
16
  const endpoint = createRemoteServiceEndpoint(provider);
15
17
  return {
@@ -43,16 +45,26 @@ function serverServices() {
43
45
  }
44
46
  };
45
47
  }
48
+ const noSettings = {
49
+ state: replicatedState({ models: [], accounts: [] }),
50
+ connect: async () => {
51
+ throw new Error("this agent has no accounts");
52
+ },
53
+ disconnect: async () => {
54
+ }
55
+ };
46
56
  async function hostAgent(agent, options = {}) {
47
57
  const context = options.context ?? BACKGROUND_CONTEXT;
48
58
  const serverId = options.serverId ?? crypto.randomUUID();
49
59
  const state = await agent.root.viewState(context);
50
60
  const provider = new RemoteServiceProvider([
51
61
  { service: AgentControl, mode: "singleton" },
52
- { service: AgentTranscript, mode: "singleton" }
62
+ { service: AgentTranscript, mode: "singleton" },
63
+ { service: AgentSettings, mode: "singleton" }
53
64
  ]);
54
65
  provider.provide(AgentControl, createAgentControl(agent.harness, agent.root));
55
66
  provider.provide(AgentTranscript, { state });
67
+ provider.provide(AgentSettings, options.settings ?? noSettings);
56
68
  const host = {
57
69
  serverServices: serverServices(),
58
70
  async resolveSession(sessionId) {
package/dist/host.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
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;",
4
+ "sourcesContent": ["import {\n type Context,\n createRemoteServiceEndpoint,\n RemoteServiceProvider,\n replicatedState,\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 { PortListener } from './listener.ts';\nimport { AGENT_SESSION, AgentControl, AgentSessions, AgentTranscript } from './services.ts';\nimport { AgentSettings } from './settings.ts';\nimport type { PortEndpoint } 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\nconst noSettings: AgentSettings = {\n state: replicatedState({ models: [], accounts: [] }),\n connect: async () => {\n throw new Error('this agent has no accounts');\n },\n disconnect: async () => {},\n};\n\nexport async function hostAgent(\n agent: Agent,\n options: { serverId?: string; context?: Context; settings?: AgentSettings } = {}\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 { service: AgentSettings, mode: 'singleton' },\n ]);\n provider.provide(AgentControl, createAgentControl(agent.harness, agent.root));\n provider.provide(AgentTranscript, { state });\n provider.provide(AgentSettings, options.settings ?? noSettings);\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": "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",
6
6
  "names": []
7
7
  }
package/dist/index.js CHANGED
@@ -1,25 +1,41 @@
1
1
  import { answerText, openAgent } from "./agent.js";
2
2
  import { connectAgent } from "./client.js";
3
3
  import { createAgentControl } from "./control.js";
4
+ import { CREDENTIALS_DATABASE, EncryptedCredentialStore } from "./credentials.js";
4
5
  import { hostAgent } from "./host.js";
5
6
  import { HOME, kernelEnvironment, SliccKernelEnv } from "./kernel/env.js";
7
+ import { PortListener } from "./listener.js";
8
+ import {
9
+ runAgentWorker
10
+ } from "./main.js";
11
+ import { transportFetch } from "./net.js";
6
12
  import {
7
13
  AGENT_LOCK,
8
14
  startAgent
9
15
  } from "./owner.js";
16
+ import { applyPatches, patchServerAccept } from "./patches.js";
17
+ import { SliccPrompt } from "./prompt.js";
10
18
  import {
11
19
  AGENT_SESSION,
12
20
  AgentControl,
13
21
  AgentSessions,
14
22
  AgentTranscript
15
23
  } from "./services.js";
24
+ import {
25
+ AgentSettings,
26
+ BEDROCK,
27
+ createAgentSettings,
28
+ createSliccModels,
29
+ DEFAULT_MODEL,
30
+ sliccProviders
31
+ } from "./settings.js";
16
32
  import {
17
33
  AGENT_DIRECTORY,
18
34
  openMemorySqliteStorage,
19
35
  openOpfsSqliteStorage,
20
36
  WasmSqliteDatabase
21
37
  } from "./sqlite.js";
22
- import { PortChannel, PortListener, withUnrefTimers } from "./wire.js";
38
+ import { PortChannel, withUnrefTimers } from "./wire.js";
23
39
  import { kernelPort, serveConnections } from "./worker.js";
24
40
  export {
25
41
  AGENT_DIRECTORY,
@@ -27,23 +43,36 @@ export {
27
43
  AGENT_SESSION,
28
44
  AgentControl,
29
45
  AgentSessions,
46
+ AgentSettings,
30
47
  AgentTranscript,
48
+ BEDROCK,
49
+ CREDENTIALS_DATABASE,
50
+ DEFAULT_MODEL,
51
+ EncryptedCredentialStore,
31
52
  HOME,
32
53
  PortChannel,
33
54
  PortListener,
34
55
  SliccKernelEnv,
56
+ SliccPrompt,
35
57
  WasmSqliteDatabase,
36
58
  answerText,
59
+ applyPatches,
37
60
  connectAgent,
38
61
  createAgentControl,
62
+ createAgentSettings,
63
+ createSliccModels,
39
64
  hostAgent,
40
65
  kernelEnvironment,
41
66
  kernelPort,
42
67
  openAgent,
43
68
  openMemorySqliteStorage,
44
69
  openOpfsSqliteStorage,
70
+ patchServerAccept,
71
+ runAgentWorker,
45
72
  serveConnections,
73
+ sliccProviders,
46
74
  startAgent,
75
+ transportFetch,
47
76
  withUnrefTimers
48
77
  };
49
78
  //# 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 { type AgentConnection, connectAgent } from './client.ts';\nexport { createAgentControl } from './control.ts';\nexport { type AgentHost, hostAgent } from './host.ts';\nexport type {\n KernelClient,\n KernelFs,\n KernelProcess,\n KernelStat,\n SpawnOptions,\n} from './kernel/client.ts';\nexport { HOME, type KernelEnvOptions, kernelEnvironment, SliccKernelEnv } from './kernel/env.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 type AgentSettingsChange,\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 { kernelPort, serveConnections, type WorkerScope } from './worker.ts';\n"],
5
- "mappings": "AAAA,SAAwC,YAAY,iBAAiB;AACrE,SAA+B,oBAAoB;AACnD,SAAS,0BAA0B;AACnC,SAAyB,iBAAiB;AAQ1C,SAAS,MAA6B,mBAAmB,sBAAsB;AAC/E;AAAA,EACE;AAAA,EAIA;AAAA,OACK;AACP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EAEA;AAAA,OAMK;AACP;AAAA,EACE;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,aAAgC,cAAc,uBAAuB;AAC9E,SAAS,YAAY,wBAA0C;",
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 { CREDENTIALS_DATABASE, EncryptedCredentialStore } from './credentials.ts';\nexport { type AgentHost, hostAgent } from './host.ts';\nexport type {\n KernelClient,\n KernelFs,\n KernelProcess,\n KernelStat,\n SpawnOptions,\n} from './kernel/client.ts';\nexport { HOME, type KernelEnvOptions, kernelEnvironment, SliccKernelEnv } from './kernel/env.ts';\nexport { PortListener } from './listener.ts';\nexport {\n type AgentWorkerOptions,\n type AgentWorkerScope,\n type KernelAttach,\n runAgentWorker,\n} from './main.ts';\nexport { type Transport, type TransportResponse, transportFetch } from './net.ts';\nexport {\n AGENT_LOCK,\n type AgentOwner,\n type AgentWorker,\n type OwnerOptions,\n startAgent,\n} from './owner.ts';\nexport { applyPatches, patchServerAccept } from './patches.ts';\nexport { SliccPrompt } from './prompt.ts';\nexport {\n AGENT_SESSION,\n AgentControl,\n AgentSessions,\n type AgentSettingsChange,\n AgentTranscript,\n type OperationError,\n type PromptResult,\n type SendMode,\n type SendRequest,\n type SendResponse,\n} from './services.ts';\nexport {\n type AccountState,\n AgentSettings,\n BEDROCK,\n createAgentSettings,\n createSliccModels,\n DEFAULT_MODEL,\n type ModelChoice,\n type SettingsState,\n sliccProviders,\n} from './settings.ts';\nexport {\n AGENT_DIRECTORY,\n type OpfsSqliteOptions,\n openMemorySqliteStorage,\n openOpfsSqliteStorage,\n WasmSqliteDatabase,\n} from './sqlite.ts';\nexport { PortChannel, type PortEndpoint, withUnrefTimers } from './wire.ts';\nexport { kernelPort, serveConnections, type WorkerScope } from './worker.ts';\n"],
5
+ "mappings": "AAAA,SAAwC,YAAY,iBAAiB;AACrE,SAA+B,oBAAoB;AACnD,SAAS,0BAA0B;AACnC,SAAS,sBAAsB,gCAAgC;AAC/D,SAAyB,iBAAiB;AAQ1C,SAAS,MAA6B,mBAAmB,sBAAsB;AAC/E,SAAS,oBAAoB;AAC7B;AAAA,EAIE;AAAA,OACK;AACP,SAAiD,sBAAsB;AACvE;AAAA,EACE;AAAA,EAIA;AAAA,OACK;AACP,SAAS,cAAc,yBAAyB;AAChD,SAAS,mBAAmB;AAC5B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EAEA;AAAA,OAMK;AACP;AAAA,EAEE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAGA;AAAA,OACK;AACP;AAAA,EACE;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,aAAgC,uBAAuB;AAChE,SAAS,YAAY,wBAA0C;",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,73 @@
1
+ import { applyPatches } from "./patches.js";
2
+ import { post } from "./wire.js";
3
+ class PortConnection {
4
+ closed = false;
5
+ #endpoint;
6
+ #ended;
7
+ constructor(endpoint, ended) {
8
+ this.#endpoint = endpoint;
9
+ this.#ended = ended;
10
+ }
11
+ async send(chunk) {
12
+ if (this.closed) throw new Error("connection is closed");
13
+ post(this.#endpoint, chunk);
14
+ }
15
+ close(finalChunk) {
16
+ if (this.closed) return;
17
+ if (finalChunk) post(this.#endpoint, finalChunk);
18
+ this.closed = true;
19
+ this.#endpoint.postMessage({ close: true });
20
+ this.#endpoint.close?.();
21
+ this.#ended();
22
+ }
23
+ end() {
24
+ this.closed = true;
25
+ this.#ended();
26
+ }
27
+ }
28
+ class PortListener {
29
+ #serverId;
30
+ #connections = /* @__PURE__ */ new Set();
31
+ #accept;
32
+ constructor(serverId) {
33
+ this.#serverId = serverId;
34
+ }
35
+ async start(accept) {
36
+ applyPatches();
37
+ this.#accept = accept;
38
+ }
39
+ async close() {
40
+ for (const connection of [...this.#connections]) connection.close();
41
+ this.#accept = void 0;
42
+ }
43
+ connect(endpoint) {
44
+ const accept = this.#accept;
45
+ if (!accept) throw new Error("agent host is not listening");
46
+ const connection = new PortConnection(endpoint, () => this.#connections.delete(connection));
47
+ this.#connections.add(connection);
48
+ const handler = accept(connection);
49
+ endpoint.addEventListener("message", ({ data }) => {
50
+ if (data instanceof Uint8Array) handler.onData(data);
51
+ else if (data?.close) {
52
+ connection.end();
53
+ handler.onClose();
54
+ }
55
+ });
56
+ const failed = (event) => {
57
+ connection.end();
58
+ handler.onError(new Error(`agent port ${event.type}`));
59
+ };
60
+ endpoint.addEventListener("messageerror", failed);
61
+ endpoint.addEventListener("close", () => {
62
+ if (connection.closed) return;
63
+ connection.end();
64
+ handler.onClose();
65
+ });
66
+ endpoint.start?.();
67
+ endpoint.postMessage({ hello: this.#serverId });
68
+ }
69
+ }
70
+ export {
71
+ PortListener
72
+ };
73
+ //# sourceMappingURL=listener.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/listener.ts"],
4
+ "sourcesContent": ["import type { ServerListener } from '@earendil-works/pi-server';\nimport { applyPatches } from './patches.ts';\nimport { type Frame, type PortEndpoint, post } from './wire.ts';\n\ntype ByteConnectionAcceptor = Parameters<ServerListener['start']>[0];\ntype ByteConnection = Parameters<ByteConnectionAcceptor>[0];\ntype ByteConnectionHandler = ReturnType<ByteConnectionAcceptor>;\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 applyPatches();\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 = 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"],
5
+ "mappings": "AACA,SAAS,oBAAoB;AAC7B,SAAwC,YAAY;AAMpD,MAAM,eAAyC;AAAA,EAC7C,SAAS;AAAA,EACA;AAAA,EACA;AAAA,EAET,YAAY,UAAwB,OAAmB;AACrD,SAAK,YAAY;AACjB,SAAK,SAAS;AAAA,EAChB;AAAA,EAEA,MAAM,KAAK,OAAkC;AAC3C,QAAI,KAAK,OAAQ,OAAM,IAAI,MAAM,sBAAsB;AACvD,SAAK,KAAK,WAAW,KAAK;AAAA,EAC5B;AAAA,EAEA,MAAM,YAA+B;AACnC,QAAI,KAAK,OAAQ;AACjB,QAAI,WAAY,MAAK,KAAK,WAAW,UAAU;AAC/C,SAAK,SAAS;AACd,SAAK,UAAU,YAAY,EAAE,OAAO,KAAK,CAAiB;AAC1D,SAAK,UAAU,QAAQ;AACvB,SAAK,OAAO;AAAA,EACd;AAAA,EAEA,MAAY;AACV,SAAK,SAAS;AACd,SAAK,OAAO;AAAA,EACd;AACF;AAEO,MAAM,aAAuC;AAAA,EACzC;AAAA,EACA,eAAe,oBAAI,IAAoB;AAAA,EAChD;AAAA,EAEA,YAAY,UAAkB;AAC5B,SAAK,YAAY;AAAA,EACnB;AAAA,EAEA,MAAM,MAAM,QAA+C;AACzD,iBAAa;AACb,SAAK,UAAU;AAAA,EACjB;AAAA,EAEA,MAAM,QAAuB;AAC3B,eAAW,cAAc,CAAC,GAAG,KAAK,YAAY,EAAG,YAAW,MAAM;AAClE,SAAK,UAAU;AAAA,EACjB;AAAA,EAEA,QAAQ,UAA8B;AACpC,UAAM,SAAS,KAAK;AACpB,QAAI,CAAC,OAAQ,OAAM,IAAI,MAAM,6BAA6B;AAC1D,UAAM,aAAa,IAAI,eAAe,UAAU,MAAM,KAAK,aAAa,OAAO,UAAU,CAAC;AAC1F,SAAK,aAAa,IAAI,UAAU;AAChC,UAAM,UAAiC,OAAO,UAAU;AACxD,aAAS,iBAAiB,WAAW,CAAC,EAAE,KAAK,MAAM;AACjD,UAAI,gBAAgB,WAAY,SAAQ,OAAO,IAAI;AAAA,eACzC,MAAqC,OAAO;AACpD,mBAAW,IAAI;AACf,gBAAQ,QAAQ;AAAA,MAClB;AAAA,IACF,CAAC;AACD,UAAM,SAAS,CAAC,UAAiB;AAC/B,iBAAW,IAAI;AACf,cAAQ,QAAQ,IAAI,MAAM,cAAc,MAAM,IAAI,EAAE,CAAC;AAAA,IACvD;AACA,aAAS,iBAAiB,gBAAgB,MAAM;AAChD,aAAS,iBAAiB,SAAS,MAAM;AACvC,UAAI,WAAW,OAAQ;AACvB,iBAAW,IAAI;AACf,cAAQ,QAAQ;AAAA,IAClB,CAAC;AACD,aAAS,QAAQ;AACjB,aAAS,YAAY,EAAE,OAAO,KAAK,UAAU,CAAiB;AAAA,EAChE;AACF;",
6
+ "names": []
7
+ }
package/dist/main.js ADDED
@@ -0,0 +1,46 @@
1
+ import { attachKernel } from "@ai-ecoverse/slicc-kernel";
2
+ import { createRegistry } from "@earendil-works/pi-durable";
3
+ import { CodingTools } from "@earendil-works/pi-durable/tools";
4
+ import { openAgent } from "./agent.js";
5
+ import { EncryptedCredentialStore } from "./credentials.js";
6
+ import { hostAgent } from "./host.js";
7
+ import { kernelEnvironment } from "./kernel/env.js";
8
+ import { transportFetch } from "./net.js";
9
+ import { SliccPrompt } from "./prompt.js";
10
+ import {
11
+ createAgentSettings,
12
+ createSliccModels,
13
+ DEFAULT_MODEL,
14
+ sliccProviders
15
+ } from "./settings.js";
16
+ import { openOpfsSqliteStorage } from "./sqlite.js";
17
+ import { kernelPort, serveConnections } from "./worker.js";
18
+ async function start(scope, port, options) {
19
+ const attach = options.attach ?? attachKernel;
20
+ const client = await attach(await port);
21
+ scope.fetch = transportFetch(client.transport, scope.location.origin, scope.fetch.bind(scope));
22
+ const credentials = await (options.credentials ?? (() => EncryptedCredentialStore.open()))();
23
+ const providers = options.providers ?? sliccProviders();
24
+ const models = createSliccModels(credentials, providers);
25
+ const registry = createRegistry();
26
+ registry.install(CodingTools);
27
+ registry.install(SliccPrompt);
28
+ const agent = await openAgent({
29
+ models,
30
+ model: options.model ?? DEFAULT_MODEL,
31
+ storage: await (options.storage ?? (() => openOpfsSqliteStorage()))(),
32
+ registry,
33
+ env: kernelEnvironment(client)
34
+ });
35
+ return hostAgent(agent, { settings: await createAgentSettings(models, credentials, providers) });
36
+ }
37
+ function runAgentWorker(scope, options = {}) {
38
+ const host = start(scope, kernelPort(scope), options);
39
+ host.catch(() => void 0);
40
+ serveConnections(scope, host);
41
+ return host;
42
+ }
43
+ export {
44
+ runAgentWorker
45
+ };
46
+ //# sourceMappingURL=main.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/main.ts"],
4
+ "sourcesContent": ["import { attachKernel } from '@ai-ecoverse/slicc-kernel';\nimport type { CredentialStore, Provider } from '@earendil-works/pi-ai';\nimport { createRegistry, type ModelRef, type Storage } from '@earendil-works/pi-durable';\nimport { CodingTools } from '@earendil-works/pi-durable/tools';\nimport { openAgent } from './agent.ts';\nimport { EncryptedCredentialStore } from './credentials.ts';\nimport { type AgentHost, hostAgent } from './host.ts';\nimport type { KernelClient } from './kernel/client.ts';\nimport { kernelEnvironment } from './kernel/env.ts';\nimport { type Transport, transportFetch } from './net.ts';\nimport { SliccPrompt } from './prompt.ts';\nimport {\n createAgentSettings,\n createSliccModels,\n DEFAULT_MODEL,\n sliccProviders,\n} from './settings.ts';\nimport { openOpfsSqliteStorage } from './sqlite.ts';\nimport { kernelPort, serveConnections, type WorkerScope } from './worker.ts';\n\nexport interface AgentWorkerScope extends WorkerScope {\n fetch: typeof fetch;\n location: { origin: string };\n}\n\nexport type KernelAttach = (port: MessagePort) => Promise<KernelClient & { transport: Transport }>;\n\nexport interface AgentWorkerOptions {\n model?: ModelRef;\n providers?: Provider[];\n attach?: KernelAttach;\n credentials?: () => Promise<CredentialStore>;\n storage?: () => Promise<Storage>;\n}\n\nasync function start(\n scope: AgentWorkerScope,\n port: Promise<MessagePort>,\n options: AgentWorkerOptions\n): Promise<AgentHost> {\n const attach: KernelAttach = options.attach ?? attachKernel;\n const client = await attach(await port);\n scope.fetch = transportFetch(client.transport, scope.location.origin, scope.fetch.bind(scope));\n const credentials = await (options.credentials ?? (() => EncryptedCredentialStore.open()))();\n const providers = options.providers ?? sliccProviders();\n const models = createSliccModels(credentials, providers);\n const registry = createRegistry();\n registry.install(CodingTools);\n registry.install(SliccPrompt);\n const agent = await openAgent({\n models,\n model: options.model ?? DEFAULT_MODEL,\n storage: await (options.storage ?? (() => openOpfsSqliteStorage()))(),\n registry,\n env: kernelEnvironment(client),\n });\n return hostAgent(agent, { settings: await createAgentSettings(models, credentials, providers) });\n}\n\nexport function runAgentWorker(\n scope: AgentWorkerScope,\n options: AgentWorkerOptions = {}\n): Promise<AgentHost> {\n const host = start(scope, kernelPort(scope), options);\n host.catch(() => undefined);\n serveConnections(scope, host);\n return host;\n}\n"],
5
+ "mappings": "AAAA,SAAS,oBAAoB;AAE7B,SAAS,sBAAmD;AAC5D,SAAS,mBAAmB;AAC5B,SAAS,iBAAiB;AAC1B,SAAS,gCAAgC;AACzC,SAAyB,iBAAiB;AAE1C,SAAS,yBAAyB;AAClC,SAAyB,sBAAsB;AAC/C,SAAS,mBAAmB;AAC5B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,6BAA6B;AACtC,SAAS,YAAY,wBAA0C;AAiB/D,eAAe,MACb,OACA,MACA,SACoB;AACpB,QAAM,SAAuB,QAAQ,UAAU;AAC/C,QAAM,SAAS,MAAM,OAAO,MAAM,IAAI;AACtC,QAAM,QAAQ,eAAe,OAAO,WAAW,MAAM,SAAS,QAAQ,MAAM,MAAM,KAAK,KAAK,CAAC;AAC7F,QAAM,cAAc,OAAO,QAAQ,gBAAgB,MAAM,yBAAyB,KAAK,IAAI;AAC3F,QAAM,YAAY,QAAQ,aAAa,eAAe;AACtD,QAAM,SAAS,kBAAkB,aAAa,SAAS;AACvD,QAAM,WAAW,eAAe;AAChC,WAAS,QAAQ,WAAW;AAC5B,WAAS,QAAQ,WAAW;AAC5B,QAAM,QAAQ,MAAM,UAAU;AAAA,IAC5B;AAAA,IACA,OAAO,QAAQ,SAAS;AAAA,IACxB,SAAS,OAAO,QAAQ,YAAY,MAAM,sBAAsB,IAAI;AAAA,IACpE;AAAA,IACA,KAAK,kBAAkB,MAAM;AAAA,EAC/B,CAAC;AACD,SAAO,UAAU,OAAO,EAAE,UAAU,MAAM,oBAAoB,QAAQ,aAAa,SAAS,EAAE,CAAC;AACjG;AAEO,SAAS,eACd,OACA,UAA8B,CAAC,GACX;AACpB,QAAM,OAAO,MAAM,OAAO,WAAW,KAAK,GAAG,OAAO;AACpD,OAAK,MAAM,MAAM,MAAS;AAC1B,mBAAiB,OAAO,IAAI;AAC5B,SAAO;AACT;",
6
+ "names": []
7
+ }
package/dist/net.js ADDED
@@ -0,0 +1,39 @@
1
+ const empty = /* @__PURE__ */ new Set([101, 103, 204, 205, 304]);
2
+ function stream(body) {
3
+ const iterator = body[Symbol.asyncIterator]();
4
+ return new ReadableStream({
5
+ async pull(controller) {
6
+ const next = await iterator.next();
7
+ if (next.done) controller.close();
8
+ else controller.enqueue(next.value);
9
+ },
10
+ async cancel() {
11
+ await iterator.return?.();
12
+ }
13
+ });
14
+ }
15
+ function transportFetch(transport, origin, native = globalThis.fetch.bind(globalThis)) {
16
+ return async (input, init) => {
17
+ const request = new Request(input, init);
18
+ if (new URL(request.url).origin === origin || transport.traits?.crossOrigin === "cors") {
19
+ return native(request);
20
+ }
21
+ const body = ["GET", "HEAD"].includes(request.method) ? void 0 : new Uint8Array(await request.arrayBuffer());
22
+ const response = await transport.fetch({
23
+ url: request.url,
24
+ method: request.method,
25
+ headers: [...request.headers],
26
+ ...body ? { body } : {},
27
+ signal: request.signal
28
+ });
29
+ return new Response(empty.has(response.status) ? null : stream(response.body), {
30
+ status: response.status,
31
+ statusText: response.statusText ?? "",
32
+ headers: response.headers
33
+ });
34
+ };
35
+ }
36
+ export {
37
+ transportFetch
38
+ };
39
+ //# sourceMappingURL=net.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/net.ts"],
4
+ "sourcesContent": ["export interface TransportResponse {\n status: number;\n statusText?: string;\n headers: HeadersInit;\n body: AsyncIterable<Uint8Array>;\n}\n\nexport interface Transport {\n traits?: { crossOrigin?: 'cors' | 'any' };\n fetch(request: {\n url: string;\n method: string;\n headers: [string, string][];\n body?: Uint8Array;\n signal?: AbortSignal;\n }): Promise<TransportResponse>;\n}\n\nconst empty = new Set([101, 103, 204, 205, 304]);\n\nfunction stream(body: AsyncIterable<Uint8Array>): ReadableStream<Uint8Array> {\n const iterator = body[Symbol.asyncIterator]();\n return new ReadableStream({\n async pull(controller) {\n const next = await iterator.next();\n if (next.done) controller.close();\n else controller.enqueue(next.value);\n },\n async cancel() {\n await iterator.return?.();\n },\n });\n}\n\nexport function transportFetch(\n transport: Transport,\n origin: string,\n native: typeof fetch = globalThis.fetch.bind(globalThis)\n): typeof fetch {\n return async (input, init) => {\n const request = new Request(input, init);\n if (new URL(request.url).origin === origin || transport.traits?.crossOrigin === 'cors') {\n return native(request);\n }\n const body = ['GET', 'HEAD'].includes(request.method)\n ? undefined\n : new Uint8Array(await request.arrayBuffer());\n const response = await transport.fetch({\n url: request.url,\n method: request.method,\n headers: [...request.headers],\n ...(body ? { body } : {}),\n signal: request.signal,\n });\n return new Response(empty.has(response.status) ? null : stream(response.body), {\n status: response.status,\n statusText: response.statusText ?? '',\n headers: response.headers,\n });\n };\n}\n"],
5
+ "mappings": "AAkBA,MAAM,QAAQ,oBAAI,IAAI,CAAC,KAAK,KAAK,KAAK,KAAK,GAAG,CAAC;AAE/C,SAAS,OAAO,MAA6D;AAC3E,QAAM,WAAW,KAAK,OAAO,aAAa,EAAE;AAC5C,SAAO,IAAI,eAAe;AAAA,IACxB,MAAM,KAAK,YAAY;AACrB,YAAM,OAAO,MAAM,SAAS,KAAK;AACjC,UAAI,KAAK,KAAM,YAAW,MAAM;AAAA,UAC3B,YAAW,QAAQ,KAAK,KAAK;AAAA,IACpC;AAAA,IACA,MAAM,SAAS;AACb,YAAM,SAAS,SAAS;AAAA,IAC1B;AAAA,EACF,CAAC;AACH;AAEO,SAAS,eACd,WACA,QACA,SAAuB,WAAW,MAAM,KAAK,UAAU,GACzC;AACd,SAAO,OAAO,OAAO,SAAS;AAC5B,UAAM,UAAU,IAAI,QAAQ,OAAO,IAAI;AACvC,QAAI,IAAI,IAAI,QAAQ,GAAG,EAAE,WAAW,UAAU,UAAU,QAAQ,gBAAgB,QAAQ;AACtF,aAAO,OAAO,OAAO;AAAA,IACvB;AACA,UAAM,OAAO,CAAC,OAAO,MAAM,EAAE,SAAS,QAAQ,MAAM,IAChD,SACA,IAAI,WAAW,MAAM,QAAQ,YAAY,CAAC;AAC9C,UAAM,WAAW,MAAM,UAAU,MAAM;AAAA,MACrC,KAAK,QAAQ;AAAA,MACb,QAAQ,QAAQ;AAAA,MAChB,SAAS,CAAC,GAAG,QAAQ,OAAO;AAAA,MAC5B,GAAI,OAAO,EAAE,KAAK,IAAI,CAAC;AAAA,MACvB,QAAQ,QAAQ;AAAA,IAClB,CAAC;AACD,WAAO,IAAI,SAAS,MAAM,IAAI,SAAS,MAAM,IAAI,OAAO,OAAO,SAAS,IAAI,GAAG;AAAA,MAC7E,QAAQ,SAAS;AAAA,MACjB,YAAY,SAAS,cAAc;AAAA,MACnC,SAAS,SAAS;AAAA,IACpB,CAAC;AAAA,EACH;AACF;",
6
+ "names": []
7
+ }