@ai-ecoverse/slicc-agent 3.13.1 → 3.15.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 +22 -5
- package/bin/agent +1 -1
- package/dist/agents.js.map +2 -2
- package/dist/client.js +5 -1
- package/dist/client.js.map +2 -2
- package/dist/control.js +14 -1
- package/dist/control.js.map +2 -2
- package/dist/host.js +6 -2
- package/dist/host.js.map +2 -2
- package/dist/main.js +19 -2
- package/dist/main.js.map +2 -2
- package/dist/memory/context.js +91 -0
- package/dist/memory/context.js.map +7 -0
- package/dist/memory/format.js +251 -0
- package/dist/memory/format.js.map +7 -0
- package/dist/memory/index.js +32 -0
- package/dist/memory/index.js.map +7 -0
- package/dist/memory/prompt.js +175 -0
- package/dist/memory/prompt.js.map +7 -0
- package/dist/memory/runtime.js +194 -0
- package/dist/memory/runtime.js.map +7 -0
- package/dist/memory/store.js +153 -0
- package/dist/memory/store.js.map +7 -0
- package/dist/roles/frontmatter.js +9 -4
- package/dist/roles/frontmatter.js.map +2 -2
- package/dist/roles/roles.js +55 -11
- package/dist/roles/roles.js.map +2 -2
- package/dist/scoops/cli.js +3 -0
- package/dist/scoops/cli.js.map +2 -2
- package/dist/scoops/index.js +5 -3
- package/dist/scoops/index.js.map +2 -2
- package/dist/scoops/requests.js +1 -1
- package/dist/scoops/requests.js.map +2 -2
- package/dist/scoops/service.js +22 -4
- package/dist/scoops/service.js.map +2 -2
- package/dist/scoops/sync.js +1 -0
- package/dist/scoops/sync.js.map +2 -2
- package/dist/services.js.map +2 -2
- package/dist/skills/index.js +8 -1
- package/dist/skills/index.js.map +2 -2
- package/dist/spectrum/index.js +5 -1
- package/dist/spectrum/index.js.map +3 -3
- package/dist/spectrum/memory-port.js +64 -0
- package/dist/spectrum/memory-port.js.map +7 -0
- package/dist/types/agents.d.ts +7 -0
- package/dist/types/client.d.ts +3 -0
- package/dist/types/control.d.ts +2 -1
- package/dist/types/host.d.ts +2 -0
- package/dist/types/memory/context.d.ts +27 -0
- package/dist/types/memory/format.d.ts +67 -0
- package/dist/types/memory/index.d.ts +12 -0
- package/dist/types/memory/prompt.d.ts +48 -0
- package/dist/types/memory/runtime.d.ts +45 -0
- package/dist/types/memory/store.d.ts +41 -0
- package/dist/types/roles/frontmatter.d.ts +1 -1
- package/dist/types/roles/roles.d.ts +8 -0
- package/dist/types/scoops/cli.d.ts +1 -0
- package/dist/types/scoops/index.d.ts +1 -0
- package/dist/types/scoops/requests.d.ts +1 -1
- package/dist/types/scoops/service.d.ts +3 -0
- package/dist/types/services.d.ts +15 -0
- package/dist/types/spectrum/index.d.ts +3 -0
- package/dist/types/spectrum/memory-port.d.ts +12 -0
- package/package.json +4 -4
- package/packages/vfs-root/skills/memory/SKILL.md +48 -0
package/README.md
CHANGED
|
@@ -156,20 +156,20 @@ gone /home/notes/old.md
|
|
|
156
156
|
|
|
157
157
|
### Cones and scoops
|
|
158
158
|
|
|
159
|
-
A **cone** is a top-level agent the user talks to; a **scoop** is a helper agent with its own conversation and its own folder, `/scoops/<handle>/`. The session document `slicc.agents` is the registry. The first cone's id is `cone`, later ones `cone-<n>`, counting up only. A scoop's id is `scoop:<handle>`, where the handle is its name made safe for a folder; a handle that is or was ever taken gets `-2`, `-3` and so on, so ids and folders are never reused. `AgentControl` adds `selectCone`, `createCone`, `createScoop`, `stopAgent`, `drop` (stop a scoop
|
|
159
|
+
A **cone** is a top-level agent the user talks to; a **scoop** is a helper agent with its own conversation and its own folder, `/scoops/<handle>/`. The session document `slicc.agents` is the registry. The first cone's id is `cone`, later ones `cone-<n>`, counting up only. A scoop's id is `scoop:<handle>`, where the handle is its name made safe for a folder; a handle that is or was ever taken gets `-2`, `-3` and so on, so ids and folders are never reused. `AgentControl` adds `selectCone`, `createCone`, `createScoop`, `stopAgent`, `drop` (stop a scoop, as `agent stop` does) and `unqueue(agentId, submissionId)`; `send` and `configure` take an `agentId`. Its errors are sentences the UI can show as they are, such as "A cone named harbor already exists." The transcript service adds `agents`, a summary of every cone and scoop, and `views`, one durable view per live agent, which the spectrum adapter shows in the agents rail (the header picker lists cones only). With spectrum ≥ 1.23.0 the rail drops scoops and the picker creates cones; the adapter needs spectrum ≥ 1.28.0, whose Drop confirmation says the working folder is deleted. A scoop created from the rail has a provisional id until the worker answers; the adapter then moves the selection, and sends, to the id the worker gave it.
|
|
160
160
|
|
|
161
|
-
Each scoop is owned by a background durable task, its **anchor**, so Stop in its cone doesn't end it; `agent stop`
|
|
161
|
+
Each scoop is owned by a background durable task, its **anchor**, so Stop in its cone doesn't end it; `agent stop` and Drop do. They keep its transcript in durable storage and its reports in `/scoops/<handle>/reports/`, which report licks point at, and remove its working folder `/scoops/<handle>/workspace`. Each request handed to a scoop gets a **reporter** task that waits for the run, writes the answer to `/scoops/<handle>/reports/<task id>.md` and reports it to whoever asked. Requests from a cone's or a scoop's bash report into that agent's `pi.inbox` as a lick, through the licks outbox, so they steer and coalesce like any lick; requests from a terminal report nowhere, and a user typing in a scoop's chat gets the answer there. In a scoop's chat, a request someone else sent shows who sent it (`UserMessage.from`): the asking cone's or scoop's name, with `origin: 'agent'` so spectrum (≥ 1.26.0) shows it as that agent's request with an "Agent" badge, or `terminal` without an origin. Once the request is placed, the reporter appends a `slicc.from` entry (`{ from, entry }`) without model messages, and the adapter sets `from` on that entry's message. A request withdrawn before it was placed leaves no entry. The report lick's text is `[scoop <name> (<role>) <status>]`, and its body has the report's path and line count and a 1,000-character preview. A scoop's prompt has a `scoop` section with its name, its cone, its folders and its limits. The `agent` command is taught by the built-in `agent` skill (below), whose description says that roles exist and that `agent list --agents` lists them.
|
|
162
162
|
|
|
163
163
|
**The `agent` command.** One bash script is installed as `agent` and `subagent` in `$PNPM_HOME/bin`, so the user, a cone or a scoop runs it in any bash. It writes a request (NUL-separated fields: `slicc-agent/1`, the name it was called by, `$SLICC_AGENT`, the working directory, its pid, argc, the arguments and stdin) to `/var/lib/slicc/agent/requests/in/` and polls `out/` for the answer and exit code; the worker writes `<id>.ack` when it picks a request up, and the script gives up after 20 s without one ("the SLICC agent isn't running", exit 1). The kernel environment exports `SLICC_AGENT`, `PI_PROVIDER`, `PI_MODEL` and `PI_REASONING_LEVEL` into every agent command, so the worker knows the caller. `/var/lib/slicc/agent/scoops/<handle>/metadata.json` mirrors each scoop's state for scripts.
|
|
164
164
|
- `agent [options] --prompt <text>` (or `--file <path>`, or `-` for stdin, or v6's `agent <cwd> <allowed-commands> <prompt>`) is **synchronous**: an ephemeral scoop runs, and the command prints its final answer and exits 0, or prints the reason on stderr and exits 1. Afterwards the scoop is stopped, its folder removed, its conversation marked dropped and its transcript archived as Markdown in `/tmp/agent-sessions/<handle>.md` (`/home/sessions/` with `--persist-session`, nowhere with `--no-persist-session`). The script traps TERM, INT and HUP and cancels the scoop, and the worker checks the script's pid in the kernel's process table every second for a SIGKILL. If the caller's run is still going when the bash call dies (a bash timeout), the scoop continues and its answer arrives as a `bash` lick; `--background-after <seconds>` does the same on purpose. Sync calls nest up to three deep. Allowed commands in v6's form are told to the scoop, not enforced.
|
|
165
165
|
- `agent --async …` (= `subagent spawn …`) starts a persistent scoop and prints its handle; only cones start them. `list [--agents]`, `status`, `rename`, `send [--follow-up]`, `wait [--timeout <s>] [--notify]` (30 s by default; `--notify` returns at once and brings the answers together in one `scoop-wait` lick) and `stop` manage them. From a scoop, `agent send parent "<note>"` posts a progress note to its cone as a lick, coalesced per scoop.
|
|
166
166
|
- Options: `--name`, `--agent <role>`, `--model <provider/model>`, `--thinking <level>` or `--effort low|medium|high|max`, `--tools <a,b>` (or `auto`/`full`), `--read-only <paths>`, `--system-prompt[-file]`. `--schema-b64`, `--tools output`, `--session`, `--resume`, `--image`, `--no-escalate`, `--minimal` and `--usage` answer "not supported in SLICC yet".
|
|
167
167
|
|
|
168
|
-
**Roles** are pi agent files: Markdown with a frontmatter subset (`name`, `description`, `tools`, `model`, `thinking`, …) and the role's prompt as the body, added to the scoop's instructions. The built-ins are `scout`, `worker`, `reviewer`, `oracle` and `delegate`. Later sources replace earlier ones by name: built-ins, then packages in the kernel's `node_modules` that name a folder in `pi-subagents.agents` or `pi.subagents.agents`, then `~/.pi/agent/agents/`. A project's `.pi/agents/` will count only in trusted folders, and folders can't be trusted yet. `~/.pi/agent/settings.json` can override roles (`subagents.agentOverrides`) and set the limits `subagents.maxLiveScoops` (16 live scoops per cone) and `subagents.maxPerTurn` (64 scoops started per cone turn), which the worker counts for both kinds.
|
|
168
|
+
**Roles** are pi agent files: Markdown with a frontmatter subset (`name`, `description`, `tools`, `model`, `thinking`, `memory`, `inheritProjectContext`, `inheritGlobalContext`, …) and the role's prompt as the body, added to the scoop's instructions. The built-ins are `scout`, `worker`, `reviewer`, `oracle` and `delegate`. Later sources replace earlier ones by name: built-ins, then packages in the kernel's `node_modules` that name a folder in `pi-subagents.agents` or `pi.subagents.agents`, then `~/.pi/agent/agents/`. A project's `.pi/agents/` will count only in trusted folders, and folders can't be trusted yet. `~/.pi/agent/settings.json` can override roles (`subagents.agentOverrides`) and set the limits `subagents.maxLiveScoops` (16 live scoops per cone) and `subagents.maxPerTurn` (64 scoops started per cone turn), which the worker counts for both kinds.
|
|
169
169
|
|
|
170
170
|
**Isolation, for now.** The file tools of a scoop write only under its folder and `/tmp`, plus the cwd of a v6-form call, and read those, their cone's working folder (`/home` by default) and `--read-only` paths; paths are normalized, `..` included, before the check. bash isn't confined, and the scoop is told so: the guard is a guardrail, not a boundary. A scoop's commands share one process group (see Bash and files on slicc-kernel), and stopping the scoop signals it, together with any group recorded for the scoop on an older kernel.
|
|
171
171
|
|
|
172
|
-
**Rewind.** Dropping a turn never waits for scoops. Scoops the dropped turn created are stopped and marked gone; requests it handed to older scoops are aborted and their reports withdrawn; scoops it stopped come back idle. The `slicc.rewound` entry lists them, and its notice says "Stopped scoop X. Restored scoop Y."
|
|
172
|
+
**Rewind.** Dropping a turn never waits for scoops. Scoops the dropped turn created are stopped and marked gone; requests it handed to older scoops are aborted and their reports withdrawn; scoops it stopped come back idle, with a fresh, empty working folder. The `slicc.rewound` entry lists them, and its notice says "Stopped scoop X. Restored scoop Y."
|
|
173
173
|
|
|
174
174
|
### Skills and prompt templates
|
|
175
175
|
|
|
@@ -209,7 +209,7 @@ The worker watches the roots and reloads on change. A `skills` prompt section, g
|
|
|
209
209
|
|
|
210
210
|
**v6 skills.** None of the 29 skills SLICC v6 ships runs here unchanged. They name v6 paths (`/shared`, `/workspace`) or commands this kernel doesn't have. By what they wait for:
|
|
211
211
|
- **Rewritten here:** `delegation` (now the `agent` skill), `automation` (now `licks`), `skill-authoring`, `sprinkles` and `welcome`.
|
|
212
|
-
- **Memory (
|
|
212
|
+
- **Memory:** `memory` is rewritten here (below); its `status`, `log`, `curate` and `dream` and the curation ledger are not ported. `gelatiere` comes in PR 14c; `wiki` needs its `wiki` CLI.
|
|
213
213
|
- **The freezer (PR 15):** `transcript-export`.
|
|
214
214
|
- **The tray hub, upskill or the Install/Update panel:** `upgrade` (`upgrade apply`, bios#70), `handoff`, `slicc`, `ssh`, `cherry`.
|
|
215
215
|
- **A browser and CDP:** `playwright-cli`, `computer`, `v86`.
|
|
@@ -237,6 +237,23 @@ The model can write a short JavaScript program that calls its tools, with the `c
|
|
|
237
237
|
- **Overrides.** `runAgentWorker` takes `codemodeWasm` and `codemodeWorker` to override both, for bundled hosts.
|
|
238
238
|
|
|
239
239
|
||||||| parent of d578592 (wip: sprinkles)
|
|
240
|
+
### Memory
|
|
241
|
+
|
|
242
|
+
pi has no memory store or memory tool; its coding agent loads context files, and [pi-subagents](https://github.com/nicobailon/pi-subagents) gives a role a `MEMORY.md`. Both are ported here over `ExecutionEnv`.
|
|
243
|
+
|
|
244
|
+
**Context files.** As in pi, `~/.pi/agent/AGENTS.md` and then one file per folder from `/` down to the working directory (the first of `AGENTS.override.md`, `AGENTS.md`, `AGENTS.MD`, `CLAUDE.md`, `CLAUDE.MD`) form the `project_context` section, read before every request. Only files inside `/home` (and a scoop's `/scoops` folder) load, after following links: files in mounted folders or other roots are user content from outside SLICC and wait for folder trust, so the section lists them as not loaded and why. A scoop with a role gets project files unless the role says `inheritProjectContext: false`, and the global file only with `inheritGlobalContext: true`, as in pi-subagents.
|
|
245
|
+
|
|
246
|
+
**Memory files.** One `MEMORY.md` per scope, plain markdown: `## <section>`, `### <title>`, an optional `tag: user|feedback|project` line, then the body. Text outside a `###` is kept and shown as an entry named after its section.
|
|
247
|
+
- `global`, shared: `~/.pi/agent/memory/MEMORY.md`;
|
|
248
|
+
- a cone: `~/.pi/agent/memory/<cone id>/MEMORY.md`;
|
|
249
|
+
- a role with `memory: { scope: user, path: <path> }`: `~/.pi/agent/agent-memory/<path>/MEMORY.md`, pi-subagents' layout, so role files move between the two; with `scope: project`, `<root>/.pi/agent-memory/<path>/MEMORY.md` under the nearest folder with `.pi` or `.git`, inside `/home`.
|
|
250
|
+
|
|
251
|
+
The `memory` section, read before every request, holds the first 200 lines (at most 16 KB) of each file the agent sees, as reference data rather than instructions: a cone sees global and its own, a scoop global read-only and its role's. Changes show from the next request on.
|
|
252
|
+
|
|
253
|
+
**`memory_write`** saves, replaces (same section and title) or removes one entry in the caller's own file, or in `global` for cones. Writes to a file are serialized and re-read the file, so edits by hand survive. A file holds 16 KB; a write over that must make it smaller. Anything that looks like a secret (API keys, tokens, `Authorization` values, private keys, `password=`, launch URLs with a `#key`) is redacted before it's written. A role with `memory` and write tools gets `memory_write` in its tool list.
|
|
254
|
+
|
|
255
|
+
**The panel.** The transcript service adds `memories` and `memoryScopes`, and `AgentControl` `memorySave` and `memoryRemove`; `createAgentModel()` returns a `MemoryAdapter` for spectrum's `MemoryPort`, with `scopes()` (global, cones, roles) for its picker. `memory show [<scope>]` and `memory scopes` in bash print the same files, and the built-in `memory` skill teaches all of it.
|
|
256
|
+
|
|
240
257
|
### Sprinkles
|
|
241
258
|
|
|
242
259
|
A sprinkle is a small HTML panel next to the chat. pi has nothing for this; it's SLICC's UI, rendered by slicc-spectrum (≥ 1.23) through its `SprinklePort`.
|
package/bin/agent
CHANGED
package/dist/agents.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/agents.ts"],
|
|
4
|
-
"sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport {\n type Conversation,\n type ConversationId,\n defineDoc,\n type Harness,\n type Tx,\n} from '@earendil-works/pi-durable';\nimport { ConeDoc } from './cone.ts';\n\nexport const FIRST_CONE = 'cone';\n\nexport type ConeRecord = { name: string; conversation: number };\n\nexport type Mark = { in: number; at: number };\n\nexport type ScoopRecord = {\n name: string;\n folder: string;\n cone: string;\n conversation: number;\n anchor: number;\n role: string | null;\n kind: 'async' | 'sync';\n parent: string;\n depth: number;\n roots: { write: string[]; read: string[] } | null;\n created: Mark | null;\n dropped: Mark | null;\n gone: boolean;\n};\n\nexport function live(record: ScoopRecord): boolean {\n return record.dropped === null && !record.gone;\n}\n\nexport type AgentsState = {\n cones: Record<string, ConeRecord>;\n active: string;\n next: number;\n scoops: Record<string, ScoopRecord>;\n};\n\nexport const AgentsDoc = defineDoc<AgentsState>({\n kind: 'slicc.agents',\n version: 1,\n scope: 'session',\n initial: () => ({ cones: {}, active: FIRST_CONE, next: 2, scoops: {} }),\n});\n\nexport const SCOOP_PREFIX = 'scoop:';\n\nexport function scoopId(folder: string): string {\n return `${SCOOP_PREFIX}${folder}`;\n}\n\nexport function slug(name: string): string {\n const cleaned = name\n .toLowerCase()\n .replace(/[^a-z0-9._-]+/g, '-')\n .replace(/^[-._]+|[-._]+$/g, '')\n .slice(0, 48);\n return cleaned || 'scoop';\n}\n\nexport function freeFolder(state: Pick<AgentsState, 'scoops'>, name: string): string {\n const base = slug(name);\n if (!state.scoops[scoopId(base)]) return base;\n for (let n = 2; ; n++) if (!state.scoops[scoopId(`${base}-${n}`)]) return `${base}-${n}`;\n}\n\nexport interface Agents {\n state(): Readonly<AgentsState>;\n cone(): Conversation;\n activeCone(): string;\n conversation(agentId: string, context: Context): Promise<Conversation | undefined>;\n selectCone(id: string, context: Context): Promise<void>;\n createCone(name: string, context: Context): Promise<string>;\n switchCone(conversation: Conversation, context: Context): Promise<void>;\n update<T>(change: (tx: Tx, state: AgentsState) => T | Promise<T>, context: Context): Promise<T>;\n onChange(listener: (state: Readonly<AgentsState>) => void): () => void;\n onCone(listener: (conversation: Conversation) => void | Promise<void>): () => void;\n}\n\nasync function initial(harness: Harness, root: Conversation, context: Context) {\n return harness.commit(async (tx) => {\n const doc = await tx.doc(AgentsDoc);\n if (!doc.cones[FIRST_CONE]) {\n const legacy = (await tx.doc(ConeDoc)).conversation;\n doc.cones[FIRST_CONE] = { name: 'sliccy', conversation: legacy ?? root.id };\n }\n if (!doc.cones[doc.active]) doc.active = FIRST_CONE;\n return JSON.parse(JSON.stringify(doc)) as AgentsState;\n }, context);\n}\n\nexport async function openAgents(\n harness: Harness,\n root: Conversation,\n context: Context\n): Promise<Agents> {\n let state = await initial(harness, root, context);\n const handles = new Map<number, Conversation>([[root.id, root]]);\n const handle = async (id: number, using: Context) => {\n const known = handles.get(id);\n if (known) return known;\n const found = await harness.conversation(id as ConversationId, using);\n if (found) handles.set(id, found);\n return found;\n };\n let cone =\n (await handle((state.cones[state.active] as ConeRecord).conversation, context)) ?? root;\n const changes = new Set<(state: Readonly<AgentsState>) => void>();\n const cones = new Set<(conversation: Conversation) => void | Promise<void>>();\n const notifyCone = async (next: Conversation) => {\n cone = next;\n await Promise.all([...cones].map((listener) => listener(next)));\n };\n const update: Agents['update'] = async (change, using) => {\n const [result, next] = await harness.commit(async (tx) => {\n const doc = await tx.doc(AgentsDoc);\n const value = await change(tx, doc as AgentsState);\n return [value, JSON.parse(JSON.stringify(doc)) as AgentsState] as const;\n }, using);\n state = next;\n for (const listener of changes) listener(state);\n return result;\n };\n const agents: Agents = {\n state: () => state,\n cone: () => cone,\n activeCone: () => state.active,\n async conversation(agentId, using) {\n const id = state.cones[agentId]?.conversation ?? state.scoops[agentId]?.conversation;\n return id === undefined ? undefined : handle(id, using);\n },\n async selectCone(id, using) {\n const record = state.cones[id];\n if (!record) throw new Error(`no cone ${id}`);\n if (state.active === id) return;\n await update((_tx, doc) => {\n doc.active = id;\n }, using);\n await notifyCone((await handle(record.conversation, using)) as Conversation);\n },\n async createCone(name, using) {\n const wanted = name.trim();\n if (!wanted) throw new Error('A cone needs a name.');\n if (Object.values(state.cones).some((record) => record.name === wanted))\n throw new Error(`A cone named ${wanted} already exists.`);\n const conversation = await harness.createConversation(\n {\n ownership: { kind: 'ownerless' },\n agent: { model: (await cone.agent(using)).model ?? null },\n },\n using\n );\n handles.set(conversation.id, conversation);\n return update((_tx, doc) => {\n const id = `cone-${doc.next}`;\n doc.next += 1;\n doc.cones[id] = { name: wanted, conversation: conversation.id };\n return id;\n }, using);\n },\n async switchCone(conversation, using) {\n handles.set(conversation.id, conversation);\n await update(async (tx, doc) => {\n (doc.cones[doc.active] as ConeRecord).conversation = conversation.id;\n (await tx.doc(ConeDoc)).conversation = conversation.id;\n }, using);\n await notifyCone(conversation);\n },\n update,\n onChange(listener) {\n changes.add(listener);\n return () => changes.delete(listener);\n },\n onCone(listener) {\n cones.add(listener);\n return () => cones.delete(listener);\n },\n };\n return agents;\n}\n"],
|
|
5
|
-
"mappings": "AACA;AAAA,EAGE;AAAA,OAGK;AACP,SAAS,eAAe;
|
|
4
|
+
"sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport {\n type Conversation,\n type ConversationId,\n defineDoc,\n type Harness,\n type Tx,\n} from '@earendil-works/pi-durable';\nimport { ConeDoc } from './cone.ts';\nimport type { RoleMemory } from './memory/store.ts';\n\nexport const FIRST_CONE = 'cone';\n\nexport type ConeRecord = { name: string; conversation: number };\n\nexport type Mark = { in: number; at: number };\n\nexport type ScoopRecord = {\n name: string;\n folder: string;\n cone: string;\n conversation: number;\n anchor: number;\n role: string | null;\n memory?: RoleMemory;\n origin?: string;\n context?: { project: boolean; global: boolean };\n kind: 'async' | 'sync';\n parent: string;\n depth: number;\n roots: { write: string[]; read: string[] } | null;\n created: Mark | null;\n dropped: Mark | null;\n gone: boolean;\n};\n\nexport function live(record: ScoopRecord): boolean {\n return record.dropped === null && !record.gone;\n}\n\nexport type AgentsState = {\n cones: Record<string, ConeRecord>;\n active: string;\n next: number;\n scoops: Record<string, ScoopRecord>;\n};\n\nexport const AgentsDoc = defineDoc<AgentsState>({\n kind: 'slicc.agents',\n version: 1,\n scope: 'session',\n initial: () => ({ cones: {}, active: FIRST_CONE, next: 2, scoops: {} }),\n});\n\nexport const SCOOP_PREFIX = 'scoop:';\n\nexport function scoopId(folder: string): string {\n return `${SCOOP_PREFIX}${folder}`;\n}\n\nexport function slug(name: string): string {\n const cleaned = name\n .toLowerCase()\n .replace(/[^a-z0-9._-]+/g, '-')\n .replace(/^[-._]+|[-._]+$/g, '')\n .slice(0, 48);\n return cleaned || 'scoop';\n}\n\nexport function freeFolder(state: Pick<AgentsState, 'scoops'>, name: string): string {\n const base = slug(name);\n if (!state.scoops[scoopId(base)]) return base;\n for (let n = 2; ; n++) if (!state.scoops[scoopId(`${base}-${n}`)]) return `${base}-${n}`;\n}\n\nexport interface Agents {\n state(): Readonly<AgentsState>;\n cone(): Conversation;\n activeCone(): string;\n conversation(agentId: string, context: Context): Promise<Conversation | undefined>;\n selectCone(id: string, context: Context): Promise<void>;\n createCone(name: string, context: Context): Promise<string>;\n switchCone(conversation: Conversation, context: Context): Promise<void>;\n update<T>(change: (tx: Tx, state: AgentsState) => T | Promise<T>, context: Context): Promise<T>;\n onChange(listener: (state: Readonly<AgentsState>) => void): () => void;\n onCone(listener: (conversation: Conversation) => void | Promise<void>): () => void;\n}\n\nasync function initial(harness: Harness, root: Conversation, context: Context) {\n return harness.commit(async (tx) => {\n const doc = await tx.doc(AgentsDoc);\n if (!doc.cones[FIRST_CONE]) {\n const legacy = (await tx.doc(ConeDoc)).conversation;\n doc.cones[FIRST_CONE] = { name: 'sliccy', conversation: legacy ?? root.id };\n }\n if (!doc.cones[doc.active]) doc.active = FIRST_CONE;\n return JSON.parse(JSON.stringify(doc)) as AgentsState;\n }, context);\n}\n\nexport async function openAgents(\n harness: Harness,\n root: Conversation,\n context: Context\n): Promise<Agents> {\n let state = await initial(harness, root, context);\n const handles = new Map<number, Conversation>([[root.id, root]]);\n const handle = async (id: number, using: Context) => {\n const known = handles.get(id);\n if (known) return known;\n const found = await harness.conversation(id as ConversationId, using);\n if (found) handles.set(id, found);\n return found;\n };\n let cone =\n (await handle((state.cones[state.active] as ConeRecord).conversation, context)) ?? root;\n const changes = new Set<(state: Readonly<AgentsState>) => void>();\n const cones = new Set<(conversation: Conversation) => void | Promise<void>>();\n const notifyCone = async (next: Conversation) => {\n cone = next;\n await Promise.all([...cones].map((listener) => listener(next)));\n };\n const update: Agents['update'] = async (change, using) => {\n const [result, next] = await harness.commit(async (tx) => {\n const doc = await tx.doc(AgentsDoc);\n const value = await change(tx, doc as AgentsState);\n return [value, JSON.parse(JSON.stringify(doc)) as AgentsState] as const;\n }, using);\n state = next;\n for (const listener of changes) listener(state);\n return result;\n };\n const agents: Agents = {\n state: () => state,\n cone: () => cone,\n activeCone: () => state.active,\n async conversation(agentId, using) {\n const id = state.cones[agentId]?.conversation ?? state.scoops[agentId]?.conversation;\n return id === undefined ? undefined : handle(id, using);\n },\n async selectCone(id, using) {\n const record = state.cones[id];\n if (!record) throw new Error(`no cone ${id}`);\n if (state.active === id) return;\n await update((_tx, doc) => {\n doc.active = id;\n }, using);\n await notifyCone((await handle(record.conversation, using)) as Conversation);\n },\n async createCone(name, using) {\n const wanted = name.trim();\n if (!wanted) throw new Error('A cone needs a name.');\n if (Object.values(state.cones).some((record) => record.name === wanted))\n throw new Error(`A cone named ${wanted} already exists.`);\n const conversation = await harness.createConversation(\n {\n ownership: { kind: 'ownerless' },\n agent: { model: (await cone.agent(using)).model ?? null },\n },\n using\n );\n handles.set(conversation.id, conversation);\n return update((_tx, doc) => {\n const id = `cone-${doc.next}`;\n doc.next += 1;\n doc.cones[id] = { name: wanted, conversation: conversation.id };\n return id;\n }, using);\n },\n async switchCone(conversation, using) {\n handles.set(conversation.id, conversation);\n await update(async (tx, doc) => {\n (doc.cones[doc.active] as ConeRecord).conversation = conversation.id;\n (await tx.doc(ConeDoc)).conversation = conversation.id;\n }, using);\n await notifyCone(conversation);\n },\n update,\n onChange(listener) {\n changes.add(listener);\n return () => changes.delete(listener);\n },\n onCone(listener) {\n cones.add(listener);\n return () => cones.delete(listener);\n },\n };\n return agents;\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/client.js
CHANGED
|
@@ -44,7 +44,9 @@ async function connectAgent(endpoint, options = {}) {
|
|
|
44
44
|
agents,
|
|
45
45
|
views,
|
|
46
46
|
commands,
|
|
47
|
-
sprinkles
|
|
47
|
+
sprinkles,
|
|
48
|
+
memories,
|
|
49
|
+
memoryScopes
|
|
48
50
|
} = session.use(AgentTranscript);
|
|
49
51
|
const settings = session.use(AgentSettings);
|
|
50
52
|
await session.ready(context);
|
|
@@ -57,6 +59,8 @@ async function connectAgent(endpoint, options = {}) {
|
|
|
57
59
|
views,
|
|
58
60
|
commands,
|
|
59
61
|
sprinkles,
|
|
62
|
+
memories,
|
|
63
|
+
memoryScopes,
|
|
60
64
|
settings,
|
|
61
65
|
async prompt(text, whenBusy = "followUp") {
|
|
62
66
|
const sent = await control.send({ text, whenBusy, requestId: null }, context);
|
package/dist/client.js.map
CHANGED
|
@@ -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 AgentSettings,\n type AgentsSummary,\n AgentTranscript,\n type Command,\n type Delivered,\n type SendMode,\n} from './services.ts';\nimport type { Sprinkle } from './sprinkles/kind.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 deliveries: ReplicatedState<Record<string, Delivered>>;\n readonly agents?: ReplicatedState<AgentsSummary>;\n readonly views?: ReplicatedState<Record<string, ConversationView>>;\n readonly commands?: ReplicatedState<Command[]>;\n readonly sprinkles?: ReplicatedState<Sprinkle[]>;\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 {\n state: transcript,\n deliveries,\n agents,\n views,\n commands,\n sprinkles,\n } = session.use(AgentTranscript);\n const settings = session.use(AgentSettings);\n await session.ready(context);\n return {\n serverId,\n control,\n transcript,\n deliveries,\n agents,\n views,\n commands,\n sprinkles,\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;
|
|
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 type { MemoryEntry, MemoryScope } from './memory/format.ts';\nimport {\n AGENT_SESSION,\n AgentControl,\n AgentSessions,\n AgentSettings,\n type AgentsSummary,\n AgentTranscript,\n type Command,\n type Delivered,\n type SendMode,\n} from './services.ts';\nimport type { Sprinkle } from './sprinkles/kind.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 deliveries: ReplicatedState<Record<string, Delivered>>;\n readonly agents?: ReplicatedState<AgentsSummary>;\n readonly views?: ReplicatedState<Record<string, ConversationView>>;\n readonly commands?: ReplicatedState<Command[]>;\n readonly sprinkles?: ReplicatedState<Sprinkle[]>;\n readonly memories?: ReplicatedState<MemoryEntry[]>;\n readonly memoryScopes?: ReplicatedState<MemoryScope[]>;\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 {\n state: transcript,\n deliveries,\n agents,\n views,\n commands,\n sprinkles,\n memories,\n memoryScopes,\n } = session.use(AgentTranscript);\n const settings = session.use(AgentSettings);\n await session.ready(context);\n return {\n serverId,\n control,\n transcript,\n deliveries,\n agents,\n views,\n commands,\n sprinkles,\n memories,\n memoryScopes,\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;AAGrD;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEA;AAAA,OAIK;AAEP,SAAS,mBAAsC;AAkB/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,IACJ,OAAO;AAAA,IACP;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,IAAI,QAAQ,IAAI,eAAe;AAC/B,QAAM,WAAW,QAAQ,IAAI,aAAa;AAC1C,QAAM,QAAQ,MAAM,OAAO;AAC3B,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;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
|
}
|
package/dist/control.js
CHANGED
|
@@ -15,6 +15,18 @@ function created(run) {
|
|
|
15
15
|
(error) => ({ id: null, error: error.message })
|
|
16
16
|
);
|
|
17
17
|
}
|
|
18
|
+
function memoryControl(memory) {
|
|
19
|
+
return {
|
|
20
|
+
async memorySave(draft, context) {
|
|
21
|
+
if (!memory) throw new Error("This agent has no memory.");
|
|
22
|
+
const { id, ...rest } = draft;
|
|
23
|
+
return memory.save({ ...rest, ...id ? { id } : {} }, context);
|
|
24
|
+
},
|
|
25
|
+
async memoryRemove(id, context) {
|
|
26
|
+
return { removed: memory ? await memory.remove(id, context) : false };
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
}
|
|
18
30
|
function sprinkleControl(sprinkles) {
|
|
19
31
|
return {
|
|
20
32
|
sprinkleSend: async (id, payload, context) => sprinkles ? sprinkles.send(id, payload, context) : { delivered: false },
|
|
@@ -64,7 +76,7 @@ function busy(view) {
|
|
|
64
76
|
const inbox = view.docs["pi.inbox"];
|
|
65
77
|
return Boolean(live?.run) || Boolean(inbox?.items?.length);
|
|
66
78
|
}
|
|
67
|
-
function createAgentControl(harness, target, deliveries, licks, scoops, skills, sprinkles) {
|
|
79
|
+
function createAgentControl(harness, target, deliveries, licks, scoops, skills, sprinkles, memory) {
|
|
68
80
|
const cone = cones(target);
|
|
69
81
|
const current = () => cone.cone();
|
|
70
82
|
const agents = target.agents;
|
|
@@ -154,6 +166,7 @@ function createAgentControl(harness, target, deliveries, licks, scoops, skills,
|
|
|
154
166
|
},
|
|
155
167
|
withdraw: (id, context) => withdraw(current(), id, context),
|
|
156
168
|
...sprinkleControl(sprinkles),
|
|
169
|
+
...memoryControl(memory),
|
|
157
170
|
async unqueue(agentId, id, context) {
|
|
158
171
|
return withdraw(await conversationFor(agentId, context), id, context);
|
|
159
172
|
},
|
package/dist/control.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/control.ts"],
|
|
4
|
-
"sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport {\n type AgentChange,\n AssistantEntry,\n type Conversation,\n ConversationBusy,\n type ConversationView,\n type EntryId,\n type EntryRecord,\n type Harness,\n type SubmissionId,\n} from '@earendil-works/pi-durable';\nimport { answerText } from './agent.ts';\nimport type { Agents } from './agents.ts';\nimport type { Cones } from './cone.ts';\nimport type { Deliveries } from './deliveries.ts';\nimport type { LickSources, Licks } from './licks/index.ts';\nimport { LICK_STATE_KIND } from './licks/state.ts';\nimport type { Scoops, ScoopsRuntime } from './scoops/index.ts';\nimport type { AgentControl, Created, OperationError, SendResponse } from './services.ts';\nimport type { SkillsRuntime } from './skills/index.ts';\nimport type { SprinklesRuntime } from './sprinkles/index.ts';\n\nexport interface HostLicks {\n licks: Licks;\n sources: LickSources;\n}\n\nexport interface HostScoops {\n scoops: Scoops;\n runtime: ScoopsRuntime;\n}\n\ntype WithAgents = { agents?: Agents };\n\nexport function readable(out: string): string {\n const text = out.replace(/^(?:sub)?agent: /, '').trim();\n const sentence = text.charAt(0).toUpperCase() + text.slice(1);\n return /[.!?]$/.test(sentence) ? sentence : `${sentence}.`;\n}\n\nfunction created(run: () => Promise<string>): Promise<Created> {\n return run().then(\n (id) => ({ id, error: null }),\n (error: Error) => ({ id: null, error: error.message })\n );\n}\n\nfunction sprinkleControl(\n sprinkles: Pick<SprinklesRuntime, 'send' | 'call'> | undefined\n): Pick<AgentControl, 'sprinkleSend' | 'sprinkleCall'> {\n return {\n sprinkleSend: async (id, payload, context) =>\n sprinkles ? sprinkles.send(id, payload, context) : { delivered: false },\n async sprinkleCall(id, method, args, context) {\n if (!sprinkles) throw new Error('This agent has no sprinkles.');\n return sprinkles.call(id, method, args, context);\n },\n };\n}\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\nfunction cones(target: Conversation | Cones): Cones {\n if ('cone' in target) return target;\n return {\n cone: () => target,\n switchCone: async () => {\n throw new Error('this conversation has no cone to rewind');\n },\n };\n}\n\ninterface Turn {\n text: string;\n at: EntryId | null;\n}\n\nfunction lastTurn(view: ConversationView, messageId: string | null): Turn | null {\n const { entries } = view;\n const target = entries.findIndex((entry) => `e${entry.id}` === messageId);\n if (target < 0) return null;\n const turn = entries.slice(0, target).findLastIndex((entry) => entry.kind === 'pi.user');\n if (turn < 0) return null;\n const user = entries[turn] as EntryRecord;\n return { text: answerText(user.model?.[0]?.content), at: entries[turn - 1]?.id ?? null };\n}\n\nfunction busy(view: ConversationView): boolean {\n const live = view.docs['pi.live'] as { run?: unknown } | undefined;\n const inbox = view.docs['pi.inbox'] as { items?: unknown[] } | undefined;\n return Boolean(live?.run) || Boolean(inbox?.items?.length);\n}\n\nexport function createAgentControl(\n harness: Harness,\n target: Conversation | Cones,\n deliveries?: Deliveries,\n licks?: HostLicks,\n scoops?: HostScoops,\n skills?: Pick<SkillsRuntime, 'expand'>,\n sprinkles?: Pick<SprinklesRuntime, 'send' | 'call'>\n): AgentControl {\n const cone = cones(target);\n const current = () => cone.cone();\n const agents = (target as WithAgents).agents;\n const conversationFor = async (agentId: string | null | undefined, context: Context) => {\n if (!agentId || !agents) return current();\n const found = await agents.conversation(agentId, context);\n if (!found) throw new Error(`There is no agent ${agentId}.`);\n return found;\n };\n const withdraw = async (conversation: Conversation, id: string, context: Context) => {\n const parsed = submissionId(id);\n if (parsed === undefined) return { outcome: 'not_found' as const };\n const result = await harness.abortSubmission(parsed, context, conversation.id);\n if (result === 'aborted') return { outcome: 'withdrawn' as const };\n return {\n outcome: result === 'not_found' ? ('not_found' as const) : ('already_placed' as const),\n };\n };\n let lock: Promise<unknown> = Promise.resolve();\n const serial = <T>(operation: () => Promise<T>): Promise<T> => {\n const run = lock.then(operation, operation);\n lock = run.catch(() => undefined);\n return run;\n };\n return {\n send(request, context) {\n return serial(async () => {\n const text = skills ? await skills.expand(request.text, context) : request.text;\n const response = await accepted(async () =>\n (await conversationFor(request.agentId, context)).submit(\n {\n type: 'input',\n content: text,\n whenBusy: request.whenBusy,\n ...(request.requestId ? { requestId: request.requestId } : {}),\n },\n context\n )\n );\n const id = response.submissionId === null ? undefined : submissionId(response.submissionId);\n if (id !== undefined) deliveries?.expect(id, request.whenBusy);\n return response;\n });\n },\n rewind(messageId, context) {\n return serial(async () => {\n const conversation = current();\n const attached = await conversation.viewState(context);\n const view = attached.value;\n attached.dispose();\n const pending = (await harness.inspect(context)).submissions.some(\n (submission) => submission.conversationId === conversation.id\n );\n if (pending || busy(view)) return { done: false, text: null, reason: 'busy' };\n const turn = lastTurn(view, messageId);\n if (!turn) return { done: false, text: null, reason: 'no-turn' };\n const agent = view.docs['pi.agent'] as AgentChange | undefined;\n const ownership = { kind: 'ownerless' } as const;\n const fork =\n turn.at === null\n ? await harness.createConversation(\n {\n ownership,\n agent: {\n model: agent?.model ?? null,\n thinkingLevel: agent?.thinkingLevel ?? null,\n },\n },\n context\n )\n : await conversation.fork(turn.at, { ownership }, context);\n const changed =\n scoops && agents\n ? await scoops.scoops.rewound(agents.activeCone(), fork, context)\n : { stopped: [], restored: [] };\n await fork.submit(\n { type: 'write', entry: { kind: 'slicc.rewound', data: { turns: 1, ...changed } } },\n context\n );\n await cone.switchCone(fork, context);\n return { done: true, text: turn.text, reason: null };\n });\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 withdraw: (id, context) => withdraw(current(), id, context),\n ...sprinkleControl(sprinkles),\n async unqueue(agentId, id, context) {\n return withdraw(await conversationFor(agentId, context), id, context);\n },\n abort: (context) => current().abort(context),\n compact: (instructions, context) =>\n accepted(() => current().compact(instructions ?? undefined, context)),\n reset: (handoff, context) => current().reset(handoff ?? undefined, context),\n resolveLick(lickId, state, context) {\n return serial(async () => {\n if (!licks) return { done: false, text: null, error: 'this agent has no licks' };\n const conversation = current();\n const action = state === 'confirmed' ? 'confirm' : 'dismiss';\n try {\n const text = await licks.licks.decide(conversation, lickId, action, undefined, context);\n await conversation.submit(\n {\n type: 'write',\n entry: {\n kind: LICK_STATE_KIND,\n data: { lick: lickId, state, by: 'user' },\n model: [\n {\n role: 'user',\n content: `The user ${state} lick ${lickId}: ${text}`,\n timestamp: Date.now(),\n },\n ],\n },\n },\n context\n );\n return { done: true, text, error: null };\n } catch (error) {\n return { done: false, text: null, error: (error as Error).message };\n }\n });\n },\n async webhook(name, delivery, context) {\n const delivered = licks\n ? await licks.sources.webhook(\n name,\n {\n ...(delivery.id ? { id: delivery.id } : {}),\n headers: delivery.headers,\n body: delivery.body,\n },\n context\n )\n : false;\n return { delivered };\n },\n async stopAgent(agentId, context) {\n await (await conversationFor(agentId, context)).abort(context);\n },\n async selectCone(agentId, context) {\n await agents?.selectCone(agentId, context);\n },\n createCone(name, context) {\n return created(async () => {\n if (!agents) throw new Error('This agent has a single cone.');\n return agents.createCone(name, context);\n });\n },\n createScoop(parentId, name, context) {\n return created(async () => {\n if (!scoops) throw new Error('This agent has no scoops.');\n const roles = await scoops.runtime.roles(context);\n const answer = await scoops.scoops.spawn(\n { cone: parentId, name, prompts: [], fromAgent: false, limits: roles.limits },\n context\n );\n if (answer.code !== 0) throw new Error(readable(answer.out));\n return `scoop:${answer.out.trim()}`;\n });\n },\n drop(agentId, context) {\n return created(async () => {\n if (!scoops) throw new Error('This agent has no scoops.');\n const answer = await scoops.scoops.stop(agentId, false, context);\n if (answer.code !== 0) throw new Error(readable(answer.out));\n return agentId;\n });\n },\n configure: async (change, context) =>\n (await conversationFor(change.agentId, context)).configure(\n {\n ...(change.model ? { model: change.model } : {}),\n ...(change.thinkingLevel ? { thinkingLevel: change.thinkingLevel } : {}),\n },\n context\n ),\n };\n}\n"],
|
|
5
|
-
"mappings": "AACA;AAAA,EAEE;AAAA,EAEA;AAAA,OAMK;AACP,SAAS,kBAAkB;AAK3B,SAAS,uBAAuB;
|
|
4
|
+
"sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport {\n type AgentChange,\n AssistantEntry,\n type Conversation,\n ConversationBusy,\n type ConversationView,\n type EntryId,\n type EntryRecord,\n type Harness,\n type SubmissionId,\n} from '@earendil-works/pi-durable';\nimport { answerText } from './agent.ts';\nimport type { Agents } from './agents.ts';\nimport type { Cones } from './cone.ts';\nimport type { Deliveries } from './deliveries.ts';\nimport type { LickSources, Licks } from './licks/index.ts';\nimport { LICK_STATE_KIND } from './licks/state.ts';\nimport type { MemoryRuntime } from './memory/index.ts';\nimport type { Scoops, ScoopsRuntime } from './scoops/index.ts';\nimport type { AgentControl, Created, OperationError, SendResponse } from './services.ts';\nimport type { SkillsRuntime } from './skills/index.ts';\nimport type { SprinklesRuntime } from './sprinkles/index.ts';\n\nexport interface HostLicks {\n licks: Licks;\n sources: LickSources;\n}\n\nexport interface HostScoops {\n scoops: Scoops;\n runtime: ScoopsRuntime;\n}\n\ntype WithAgents = { agents?: Agents };\n\nexport function readable(out: string): string {\n const text = out.replace(/^(?:sub)?agent: /, '').trim();\n const sentence = text.charAt(0).toUpperCase() + text.slice(1);\n return /[.!?]$/.test(sentence) ? sentence : `${sentence}.`;\n}\n\nfunction created(run: () => Promise<string>): Promise<Created> {\n return run().then(\n (id) => ({ id, error: null }),\n (error: Error) => ({ id: null, error: error.message })\n );\n}\n\nfunction memoryControl(\n memory: Pick<MemoryRuntime, 'save' | 'remove'> | undefined\n): Pick<AgentControl, 'memorySave' | 'memoryRemove'> {\n return {\n async memorySave(draft, context) {\n if (!memory) throw new Error('This agent has no memory.');\n const { id, ...rest } = draft;\n return memory.save({ ...rest, ...(id ? { id } : {}) }, context);\n },\n async memoryRemove(id, context) {\n return { removed: memory ? await memory.remove(id, context) : false };\n },\n };\n}\n\nfunction sprinkleControl(\n sprinkles: Pick<SprinklesRuntime, 'send' | 'call'> | undefined\n): Pick<AgentControl, 'sprinkleSend' | 'sprinkleCall'> {\n return {\n sprinkleSend: async (id, payload, context) =>\n sprinkles ? sprinkles.send(id, payload, context) : { delivered: false },\n async sprinkleCall(id, method, args, context) {\n if (!sprinkles) throw new Error('This agent has no sprinkles.');\n return sprinkles.call(id, method, args, context);\n },\n };\n}\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\nfunction cones(target: Conversation | Cones): Cones {\n if ('cone' in target) return target;\n return {\n cone: () => target,\n switchCone: async () => {\n throw new Error('this conversation has no cone to rewind');\n },\n };\n}\n\ninterface Turn {\n text: string;\n at: EntryId | null;\n}\n\nfunction lastTurn(view: ConversationView, messageId: string | null): Turn | null {\n const { entries } = view;\n const target = entries.findIndex((entry) => `e${entry.id}` === messageId);\n if (target < 0) return null;\n const turn = entries.slice(0, target).findLastIndex((entry) => entry.kind === 'pi.user');\n if (turn < 0) return null;\n const user = entries[turn] as EntryRecord;\n return { text: answerText(user.model?.[0]?.content), at: entries[turn - 1]?.id ?? null };\n}\n\nfunction busy(view: ConversationView): boolean {\n const live = view.docs['pi.live'] as { run?: unknown } | undefined;\n const inbox = view.docs['pi.inbox'] as { items?: unknown[] } | undefined;\n return Boolean(live?.run) || Boolean(inbox?.items?.length);\n}\n\nexport function createAgentControl(\n harness: Harness,\n target: Conversation | Cones,\n deliveries?: Deliveries,\n licks?: HostLicks,\n scoops?: HostScoops,\n skills?: Pick<SkillsRuntime, 'expand'>,\n sprinkles?: Pick<SprinklesRuntime, 'send' | 'call'>,\n memory?: Pick<MemoryRuntime, 'save' | 'remove'>\n): AgentControl {\n const cone = cones(target);\n const current = () => cone.cone();\n const agents = (target as WithAgents).agents;\n const conversationFor = async (agentId: string | null | undefined, context: Context) => {\n if (!agentId || !agents) return current();\n const found = await agents.conversation(agentId, context);\n if (!found) throw new Error(`There is no agent ${agentId}.`);\n return found;\n };\n const withdraw = async (conversation: Conversation, id: string, context: Context) => {\n const parsed = submissionId(id);\n if (parsed === undefined) return { outcome: 'not_found' as const };\n const result = await harness.abortSubmission(parsed, context, conversation.id);\n if (result === 'aborted') return { outcome: 'withdrawn' as const };\n return {\n outcome: result === 'not_found' ? ('not_found' as const) : ('already_placed' as const),\n };\n };\n let lock: Promise<unknown> = Promise.resolve();\n const serial = <T>(operation: () => Promise<T>): Promise<T> => {\n const run = lock.then(operation, operation);\n lock = run.catch(() => undefined);\n return run;\n };\n return {\n send(request, context) {\n return serial(async () => {\n const text = skills ? await skills.expand(request.text, context) : request.text;\n const response = await accepted(async () =>\n (await conversationFor(request.agentId, context)).submit(\n {\n type: 'input',\n content: text,\n whenBusy: request.whenBusy,\n ...(request.requestId ? { requestId: request.requestId } : {}),\n },\n context\n )\n );\n const id = response.submissionId === null ? undefined : submissionId(response.submissionId);\n if (id !== undefined) deliveries?.expect(id, request.whenBusy);\n return response;\n });\n },\n rewind(messageId, context) {\n return serial(async () => {\n const conversation = current();\n const attached = await conversation.viewState(context);\n const view = attached.value;\n attached.dispose();\n const pending = (await harness.inspect(context)).submissions.some(\n (submission) => submission.conversationId === conversation.id\n );\n if (pending || busy(view)) return { done: false, text: null, reason: 'busy' };\n const turn = lastTurn(view, messageId);\n if (!turn) return { done: false, text: null, reason: 'no-turn' };\n const agent = view.docs['pi.agent'] as AgentChange | undefined;\n const ownership = { kind: 'ownerless' } as const;\n const fork =\n turn.at === null\n ? await harness.createConversation(\n {\n ownership,\n agent: {\n model: agent?.model ?? null,\n thinkingLevel: agent?.thinkingLevel ?? null,\n },\n },\n context\n )\n : await conversation.fork(turn.at, { ownership }, context);\n const changed =\n scoops && agents\n ? await scoops.scoops.rewound(agents.activeCone(), fork, context)\n : { stopped: [], restored: [] };\n await fork.submit(\n { type: 'write', entry: { kind: 'slicc.rewound', data: { turns: 1, ...changed } } },\n context\n );\n await cone.switchCone(fork, context);\n return { done: true, text: turn.text, reason: null };\n });\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 withdraw: (id, context) => withdraw(current(), id, context),\n ...sprinkleControl(sprinkles),\n ...memoryControl(memory),\n async unqueue(agentId, id, context) {\n return withdraw(await conversationFor(agentId, context), id, context);\n },\n abort: (context) => current().abort(context),\n compact: (instructions, context) =>\n accepted(() => current().compact(instructions ?? undefined, context)),\n reset: (handoff, context) => current().reset(handoff ?? undefined, context),\n resolveLick(lickId, state, context) {\n return serial(async () => {\n if (!licks) return { done: false, text: null, error: 'this agent has no licks' };\n const conversation = current();\n const action = state === 'confirmed' ? 'confirm' : 'dismiss';\n try {\n const text = await licks.licks.decide(conversation, lickId, action, undefined, context);\n await conversation.submit(\n {\n type: 'write',\n entry: {\n kind: LICK_STATE_KIND,\n data: { lick: lickId, state, by: 'user' },\n model: [\n {\n role: 'user',\n content: `The user ${state} lick ${lickId}: ${text}`,\n timestamp: Date.now(),\n },\n ],\n },\n },\n context\n );\n return { done: true, text, error: null };\n } catch (error) {\n return { done: false, text: null, error: (error as Error).message };\n }\n });\n },\n async webhook(name, delivery, context) {\n const delivered = licks\n ? await licks.sources.webhook(\n name,\n {\n ...(delivery.id ? { id: delivery.id } : {}),\n headers: delivery.headers,\n body: delivery.body,\n },\n context\n )\n : false;\n return { delivered };\n },\n async stopAgent(agentId, context) {\n await (await conversationFor(agentId, context)).abort(context);\n },\n async selectCone(agentId, context) {\n await agents?.selectCone(agentId, context);\n },\n createCone(name, context) {\n return created(async () => {\n if (!agents) throw new Error('This agent has a single cone.');\n return agents.createCone(name, context);\n });\n },\n createScoop(parentId, name, context) {\n return created(async () => {\n if (!scoops) throw new Error('This agent has no scoops.');\n const roles = await scoops.runtime.roles(context);\n const answer = await scoops.scoops.spawn(\n { cone: parentId, name, prompts: [], fromAgent: false, limits: roles.limits },\n context\n );\n if (answer.code !== 0) throw new Error(readable(answer.out));\n return `scoop:${answer.out.trim()}`;\n });\n },\n drop(agentId, context) {\n return created(async () => {\n if (!scoops) throw new Error('This agent has no scoops.');\n const answer = await scoops.scoops.stop(agentId, false, context);\n if (answer.code !== 0) throw new Error(readable(answer.out));\n return agentId;\n });\n },\n configure: async (change, context) =>\n (await conversationFor(change.agentId, context)).configure(\n {\n ...(change.model ? { model: change.model } : {}),\n ...(change.thinkingLevel ? { thinkingLevel: change.thinkingLevel } : {}),\n },\n context\n ),\n };\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/host.js
CHANGED
|
@@ -104,7 +104,8 @@ async function hostAgent(agent, options = {}) {
|
|
|
104
104
|
options.licks,
|
|
105
105
|
options.scoops,
|
|
106
106
|
options.skills,
|
|
107
|
-
options.sprinkles
|
|
107
|
+
options.sprinkles,
|
|
108
|
+
options.memory
|
|
108
109
|
)
|
|
109
110
|
);
|
|
110
111
|
provider.provide(AgentTranscript, {
|
|
@@ -113,7 +114,9 @@ async function hostAgent(agent, options = {}) {
|
|
|
113
114
|
agents: mounted.agents,
|
|
114
115
|
views: mounted.views,
|
|
115
116
|
commands: options.skills?.commands ?? replicatedState([]),
|
|
116
|
-
sprinkles: options.sprinkles?.sprinkles ?? replicatedState([])
|
|
117
|
+
sprinkles: options.sprinkles?.sprinkles ?? replicatedState([]),
|
|
118
|
+
memories: options.memory?.memories ?? replicatedState([]),
|
|
119
|
+
memoryScopes: options.memory?.scopes ?? replicatedState([])
|
|
117
120
|
});
|
|
118
121
|
provider.provide(AgentSettings, options.settings ?? noSettings);
|
|
119
122
|
const host = {
|
|
@@ -137,6 +140,7 @@ async function hostAgent(agent, options = {}) {
|
|
|
137
140
|
deliveries.dispose();
|
|
138
141
|
transcript.dispose();
|
|
139
142
|
mounted.dispose();
|
|
143
|
+
await options.memory?.close(context);
|
|
140
144
|
await options.sprinkles?.close(context);
|
|
141
145
|
await options.skills?.close(context);
|
|
142
146
|
await options.scoops?.runtime.close(context);
|
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 replicatedState,\n} from '@earendil-works/chord';\nimport { BACKGROUND_CONTEXT } from '@earendil-works/chord/context';\nimport type { ConversationView } from '@earendil-works/pi-durable';\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, type HostLicks, type HostScoops } from './control.ts';\nimport { trackDeliveries } from './deliveries.ts';\nimport { PortListener } from './listener.ts';\nimport {\n AGENT_SESSION,\n AgentControl,\n AgentSessions,\n AgentSettings,\n AgentTranscript,\n type Command,\n} from './services.ts';\nimport type { SkillsRuntime } from './skills/index.ts';\nimport type { SprinklesRuntime } from './sprinkles/index.ts';\nimport type { Sprinkle } from './sprinkles/kind.ts';\nimport { agentViews } from './views.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 signIn: async () => null,\n};\n\nasync function follow(agent: Agent, context: Context) {\n let attached = await agent.cone().viewState(context);\n const state = replicatedState<ConversationView>(attached.value);\n const mirror = (value: ConversationView, from: Context) => state.replace(from, value);\n let off = attached.subscribe(mirror);\n const stop = agent.onCone(async (conversation) => {\n const next = await conversation.viewState(context);\n off();\n attached.dispose();\n attached = next;\n state.replace(context, next.value);\n off = next.subscribe(mirror);\n });\n return {\n state,\n dispose() {\n stop();\n off();\n attached.dispose();\n },\n };\n}\n\nexport async function hostAgent(\n agent: Agent,\n options: {\n serverId?: string;\n context?: Context;\n settings?: AgentSettings;\n licks?: HostLicks;\n scoops?: HostScoops;\n skills?: SkillsRuntime;\n sprinkles?: SprinklesRuntime;\n } = {}\n): Promise<AgentHost> {\n const context = options.context ?? BACKGROUND_CONTEXT;\n const serverId = options.serverId ?? crypto.randomUUID();\n const transcript = await follow(agent, context);\n const { state } = transcript;\n const mounted = await agentViews(agent, context);\n const provider = new RemoteServiceProvider([\n { service: AgentControl, mode: 'singleton' },\n { service: AgentTranscript, mode: 'singleton' },\n { service: AgentSettings, mode: 'singleton' },\n ]);\n const deliveries = trackDeliveries(agent.harness, state, context);\n provider.provide(\n AgentControl,\n createAgentControl(\n agent.harness,\n agent,\n deliveries,\n options.licks,\n options.scoops,\n options.skills,\n options.sprinkles\n )\n );\n provider.provide(AgentTranscript, {\n state,\n deliveries: deliveries.state,\n agents: mounted.agents,\n views: mounted.views,\n commands: options.skills?.commands ?? replicatedState<Command[]>([]),\n sprinkles: options.sprinkles?.sprinkles ?? replicatedState<Sprinkle[]>([]),\n });\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 deliveries.dispose();\n transcript.dispose();\n mounted.dispose();\n await options.sprinkles?.close(context);\n await options.skills?.close(context);\n await options.scoops?.runtime.close(context);\n await options.licks?.sources.close(context);\n await agent.close();\n },\n };\n}\n"],
|
|
5
|
-
"mappings": "AAAA;AAAA,EAEE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,0BAA0B;AAEnC;AAAA,EAIE;AAAA,EAEA;AAAA,OACK;AAEP,SAAS,0BAA2D;AACpE,SAAS,uBAAuB;AAChC,SAAS,oBAAoB;
|
|
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 type { ConversationView } from '@earendil-works/pi-durable';\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, type HostLicks, type HostScoops } from './control.ts';\nimport { trackDeliveries } from './deliveries.ts';\nimport { PortListener } from './listener.ts';\nimport type { MemoryEntry, MemoryScope } from './memory/format.ts';\nimport type { MemoryRuntime } from './memory/index.ts';\nimport {\n AGENT_SESSION,\n AgentControl,\n AgentSessions,\n AgentSettings,\n AgentTranscript,\n type Command,\n} from './services.ts';\nimport type { SkillsRuntime } from './skills/index.ts';\nimport type { SprinklesRuntime } from './sprinkles/index.ts';\nimport type { Sprinkle } from './sprinkles/kind.ts';\nimport { agentViews } from './views.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 signIn: async () => null,\n};\n\nasync function follow(agent: Agent, context: Context) {\n let attached = await agent.cone().viewState(context);\n const state = replicatedState<ConversationView>(attached.value);\n const mirror = (value: ConversationView, from: Context) => state.replace(from, value);\n let off = attached.subscribe(mirror);\n const stop = agent.onCone(async (conversation) => {\n const next = await conversation.viewState(context);\n off();\n attached.dispose();\n attached = next;\n state.replace(context, next.value);\n off = next.subscribe(mirror);\n });\n return {\n state,\n dispose() {\n stop();\n off();\n attached.dispose();\n },\n };\n}\n\nexport async function hostAgent(\n agent: Agent,\n options: {\n serverId?: string;\n context?: Context;\n settings?: AgentSettings;\n licks?: HostLicks;\n scoops?: HostScoops;\n skills?: SkillsRuntime;\n sprinkles?: SprinklesRuntime;\n memory?: MemoryRuntime;\n } = {}\n): Promise<AgentHost> {\n const context = options.context ?? BACKGROUND_CONTEXT;\n const serverId = options.serverId ?? crypto.randomUUID();\n const transcript = await follow(agent, context);\n const { state } = transcript;\n const mounted = await agentViews(agent, context);\n const provider = new RemoteServiceProvider([\n { service: AgentControl, mode: 'singleton' },\n { service: AgentTranscript, mode: 'singleton' },\n { service: AgentSettings, mode: 'singleton' },\n ]);\n const deliveries = trackDeliveries(agent.harness, state, context);\n provider.provide(\n AgentControl,\n createAgentControl(\n agent.harness,\n agent,\n deliveries,\n options.licks,\n options.scoops,\n options.skills,\n options.sprinkles,\n options.memory\n )\n );\n provider.provide(AgentTranscript, {\n state,\n deliveries: deliveries.state,\n agents: mounted.agents,\n views: mounted.views,\n commands: options.skills?.commands ?? replicatedState<Command[]>([]),\n sprinkles: options.sprinkles?.sprinkles ?? replicatedState<Sprinkle[]>([]),\n memories: options.memory?.memories ?? replicatedState<MemoryEntry[]>([]),\n memoryScopes: options.memory?.scopes ?? replicatedState<MemoryScope[]>([]),\n });\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 deliveries.dispose();\n transcript.dispose();\n mounted.dispose();\n await options.memory?.close(context);\n await options.sprinkles?.close(context);\n await options.skills?.close(context);\n await options.scoops?.runtime.close(context);\n await options.licks?.sources.close(context);\n await agent.close();\n },\n };\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/main.js
CHANGED
|
@@ -18,6 +18,7 @@ import { createActivity } from "./kernel/activity.js";
|
|
|
18
18
|
import { HOME, kernelEnvironment, SliccKernelEnv } from "./kernel/env.js";
|
|
19
19
|
import { processGroups } from "./kernel/groups.js";
|
|
20
20
|
import { setupLicks } from "./licks/index.js";
|
|
21
|
+
import { setupMemory } from "./memory/index.js";
|
|
21
22
|
import { transportFetch } from "./net.js";
|
|
22
23
|
import { sliccPrompt } from "./prompt.js";
|
|
23
24
|
import { identity } from "./scoops/identity.js";
|
|
@@ -49,6 +50,7 @@ async function start(scope, port, options) {
|
|
|
49
50
|
const registry = createRegistry();
|
|
50
51
|
registry.install(CodingTools);
|
|
51
52
|
registry.install(sliccPrompt(facts));
|
|
53
|
+
const memory = setupMemory(registry);
|
|
52
54
|
const settings = await client.fs.readFile(`${HOME}/.pi/agent/settings.json`).catch(() => void 0);
|
|
53
55
|
const coding = CodingTools.tools;
|
|
54
56
|
const codemode = disabledBySettings(
|
|
@@ -64,7 +66,7 @@ async function start(scope, port, options) {
|
|
|
64
66
|
];
|
|
65
67
|
registry.install(codemodeExtension(codemode));
|
|
66
68
|
const licks = setupLicks(registry);
|
|
67
|
-
const scoops = setupScoops(registry, licks.licks, [...coding, ...codemode]);
|
|
69
|
+
const scoops = setupScoops(registry, licks.licks, [...coding, ...codemode, ...memory.tools]);
|
|
68
70
|
const skills = setupSkills(registry, licks.licks);
|
|
69
71
|
const activity = createActivity();
|
|
70
72
|
const groups = processGroups(client);
|
|
@@ -105,6 +107,17 @@ async function start(scope, port, options) {
|
|
|
105
107
|
},
|
|
106
108
|
BACKGROUND_CONTEXT
|
|
107
109
|
);
|
|
110
|
+
let roles;
|
|
111
|
+
const memoryRuntime = await memory.attach(
|
|
112
|
+
{
|
|
113
|
+
agents: agent.agents,
|
|
114
|
+
env: home,
|
|
115
|
+
home: HOME,
|
|
116
|
+
reloadMs: RELOAD_MS,
|
|
117
|
+
roles: async (using) => roles?.roles(using)
|
|
118
|
+
},
|
|
119
|
+
BACKGROUND_CONTEXT
|
|
120
|
+
);
|
|
108
121
|
const runtime = await scoops.attach(
|
|
109
122
|
{
|
|
110
123
|
harness: agent.harness,
|
|
@@ -115,16 +128,20 @@ async function start(scope, port, options) {
|
|
|
115
128
|
alive: alive(client),
|
|
116
129
|
reads: skillsRuntime.dirs,
|
|
117
130
|
sprinkle: sprinkles.command,
|
|
131
|
+
memory: memoryRuntime.command,
|
|
118
132
|
...options.assets ? { assets: options.assets } : {}
|
|
119
133
|
},
|
|
120
134
|
BACKGROUND_CONTEXT
|
|
121
135
|
);
|
|
136
|
+
roles = runtime;
|
|
137
|
+
await memoryRuntime.reload(BACKGROUND_CONTEXT).catch(() => void 0);
|
|
122
138
|
return hostAgent(agent, {
|
|
123
139
|
settings: await createAgentSettings(models, credentials, providers),
|
|
124
140
|
licks: { licks: licks.licks, sources },
|
|
125
141
|
scoops: { scoops: scoops.scoops, runtime },
|
|
126
142
|
skills: skillsRuntime,
|
|
127
|
-
sprinkles
|
|
143
|
+
sprinkles,
|
|
144
|
+
memory: memoryRuntime
|
|
128
145
|
});
|
|
129
146
|
}
|
|
130
147
|
function alive(client) {
|
package/dist/main.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/main.ts"],
|
|
4
|
-
"sourcesContent": ["import { attachKernel } from '@ai-ecoverse/slicc-kernel';\nimport { BACKGROUND_CONTEXT } from '@earendil-works/chord/context';\nimport type { CredentialStore, Provider } from '@earendil-works/pi-ai';\nimport {\n createRegistry,\n type ModelRef,\n type Storage,\n type ToolRegistration,\n} from '@earendil-works/pi-durable';\nimport { CodingTools } from '@earendil-works/pi-durable/tools';\nimport { type Agent, openAgent } from './agent.ts';\nimport {\n codemodeExtension,\n codemodeTool,\n disabledBySettings,\n locateWasm,\n sandboxFactory,\n} from './codemode/index.ts';\nimport { EncryptedCredentialStore } from './credentials.ts';\nimport { type AgentHost, hostAgent } from './host.ts';\nimport { createActivity } from './kernel/activity.ts';\nimport type { KernelClient } from './kernel/client.ts';\nimport { HOME, kernelEnvironment, SliccKernelEnv } from './kernel/env.ts';\nimport { processGroups } from './kernel/groups.ts';\nimport { setupLicks } from './licks/index.ts';\nimport { type Transport, transportFetch } from './net.ts';\nimport { sliccPrompt } from './prompt.ts';\nimport { identity } from './scoops/identity.ts';\nimport { type Assets, packageAssets, setupScoops } from './scoops/index.ts';\nimport {\n createAgentSettings,\n createSliccModels,\n DEFAULT_MODEL,\n sliccProviders,\n} from './settings.ts';\nimport { RELOAD_MS, setupSkills } from './skills/index.ts';\nimport { setupSprinkles } from './sprinkles/index.ts';\nimport { openOpfsSqliteStorage } from './sqlite.ts';\nimport { agentVersion, commandsOnPath, kernelBoot, transportName } from './system.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 assets?: Assets;\n codemodeWasm?: () => Promise<WebAssembly.Module | undefined>;\n codemodeWorker?: string | URL;\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 const native = scope.fetch.bind(scope);\n scope.fetch = transportFetch(client.transport, scope.location.origin, native);\n const facts = {\n version: await agentVersion((url) => native(url)),\n commands: await commandsOnPath(client),\n transport: transportName(client.transport),\n };\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(facts));\n const settings = await client.fs\n .readFile(`${HOME}/.pi/agent/settings.json`)\n .catch(() => undefined);\n const coding = CodingTools.tools as readonly ToolRegistration[];\n const codemode = disabledBySettings(\n settings === undefined ? undefined : new TextDecoder().decode(settings)\n )\n ? []\n : [\n codemodeTool({\n declared: coding,\n sandbox: sandboxFactory(\n options.codemodeWasm ?? (() => locateWasm(native, new URL(import.meta.url))),\n options.codemodeWorker\n ),\n }),\n ];\n registry.install(codemodeExtension(codemode));\n const licks = setupLicks(registry);\n const scoops = setupScoops(registry, licks.licks, [...coding, ...codemode]);\n const skills = setupSkills(registry, licks.licks);\n const activity = createActivity();\n const groups = processGroups(client);\n const model = options.model ?? DEFAULT_MODEL;\n let opened: Agent | undefined;\n const environment = kernelEnvironment(client, {\n activity,\n groups,\n identify: identity(() => opened?.agents, model),\n });\n const agent = await openAgent({\n models,\n model,\n storage: await (options.storage ?? (() => openOpfsSqliteStorage()))(),\n registry,\n env: environment,\n });\n opened = agent;\n const home = new SliccKernelEnv(client, { cwd: HOME });\n const sources = licks.attach(agent, { env: home, home: HOME, activity });\n await sources.start(BACKGROUND_CONTEXT);\n await sources.boot(\n { version: facts.version, boot: await kernelBoot(client) },\n BACKGROUND_CONTEXT\n );\n const skillsRuntime = await skills.attach(\n { env: home, home: HOME, assets: options.assets ?? packageAssets(), reloadMs: RELOAD_MS },\n BACKGROUND_CONTEXT\n );\n const sprinkles = await setupSprinkles().attach(\n {\n harness: agent.harness,\n agents: agent.agents,\n licks: licks.licks,\n env: home,\n assets: options.assets ?? packageAssets(),\n reloadMs: RELOAD_MS,\n },\n BACKGROUND_CONTEXT\n );\n const runtime = await scoops.attach(\n {\n harness: agent.harness,\n agents: agent.agents,\n groups,\n env: home,\n home: HOME,\n alive: alive(client),\n reads: skillsRuntime.dirs,\n sprinkle: sprinkles.command,\n ...(options.assets ? { assets: options.assets } : {}),\n },\n BACKGROUND_CONTEXT\n );\n return hostAgent(agent, {\n settings: await createAgentSettings(models, credentials, providers),\n licks: { licks: licks.licks, sources },\n scoops: { scoops: scoops.scoops, runtime },\n skills: skillsRuntime,\n sprinkles,\n });\n}\n\nexport function alive(client: KernelClient) {\n return async (pid: number): Promise<boolean> => {\n if (!client.ps) return true;\n return (await client.ps()).some((process) => process.pid === pid);\n };\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;AAC7B,SAAS,0BAA0B;AAEnC;AAAA,EACE;AAAA,OAIK;AACP,SAAS,mBAAmB;AAC5B,SAAqB,iBAAiB;AACtC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,gCAAgC;AACzC,SAAyB,iBAAiB;AAC1C,SAAS,sBAAsB;AAE/B,SAAS,MAAM,mBAAmB,sBAAsB;AACxD,SAAS,qBAAqB;AAC9B,SAAS,kBAAkB;AAC3B,SAAyB,sBAAsB;AAC/C,SAAS,mBAAmB;AAC5B,SAAS,gBAAgB;AACzB,SAAsB,
|
|
4
|
+
"sourcesContent": ["import { attachKernel } from '@ai-ecoverse/slicc-kernel';\nimport { BACKGROUND_CONTEXT } from '@earendil-works/chord/context';\nimport type { CredentialStore, Provider } from '@earendil-works/pi-ai';\nimport {\n createRegistry,\n type ModelRef,\n type Storage,\n type ToolRegistration,\n} from '@earendil-works/pi-durable';\nimport { CodingTools } from '@earendil-works/pi-durable/tools';\nimport { type Agent, openAgent } from './agent.ts';\nimport {\n codemodeExtension,\n codemodeTool,\n disabledBySettings,\n locateWasm,\n sandboxFactory,\n} from './codemode/index.ts';\nimport { EncryptedCredentialStore } from './credentials.ts';\nimport { type AgentHost, hostAgent } from './host.ts';\nimport { createActivity } from './kernel/activity.ts';\nimport type { KernelClient } from './kernel/client.ts';\nimport { HOME, kernelEnvironment, SliccKernelEnv } from './kernel/env.ts';\nimport { processGroups } from './kernel/groups.ts';\nimport { setupLicks } from './licks/index.ts';\nimport { setupMemory } from './memory/index.ts';\nimport { type Transport, transportFetch } from './net.ts';\nimport { sliccPrompt } from './prompt.ts';\nimport { identity } from './scoops/identity.ts';\nimport { type Assets, packageAssets, type ScoopsRuntime, setupScoops } from './scoops/index.ts';\nimport {\n createAgentSettings,\n createSliccModels,\n DEFAULT_MODEL,\n sliccProviders,\n} from './settings.ts';\nimport { RELOAD_MS, setupSkills } from './skills/index.ts';\nimport { setupSprinkles } from './sprinkles/index.ts';\nimport { openOpfsSqliteStorage } from './sqlite.ts';\nimport { agentVersion, commandsOnPath, kernelBoot, transportName } from './system.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 assets?: Assets;\n codemodeWasm?: () => Promise<WebAssembly.Module | undefined>;\n codemodeWorker?: string | URL;\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 const native = scope.fetch.bind(scope);\n scope.fetch = transportFetch(client.transport, scope.location.origin, native);\n const facts = {\n version: await agentVersion((url) => native(url)),\n commands: await commandsOnPath(client),\n transport: transportName(client.transport),\n };\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(facts));\n const memory = setupMemory(registry);\n const settings = await client.fs\n .readFile(`${HOME}/.pi/agent/settings.json`)\n .catch(() => undefined);\n const coding = CodingTools.tools as readonly ToolRegistration[];\n const codemode = disabledBySettings(\n settings === undefined ? undefined : new TextDecoder().decode(settings)\n )\n ? []\n : [\n codemodeTool({\n declared: coding,\n sandbox: sandboxFactory(\n options.codemodeWasm ?? (() => locateWasm(native, new URL(import.meta.url))),\n options.codemodeWorker\n ),\n }),\n ];\n registry.install(codemodeExtension(codemode));\n const licks = setupLicks(registry);\n const scoops = setupScoops(registry, licks.licks, [...coding, ...codemode, ...memory.tools]);\n const skills = setupSkills(registry, licks.licks);\n const activity = createActivity();\n const groups = processGroups(client);\n const model = options.model ?? DEFAULT_MODEL;\n let opened: Agent | undefined;\n const environment = kernelEnvironment(client, {\n activity,\n groups,\n identify: identity(() => opened?.agents, model),\n });\n const agent = await openAgent({\n models,\n model,\n storage: await (options.storage ?? (() => openOpfsSqliteStorage()))(),\n registry,\n env: environment,\n });\n opened = agent;\n const home = new SliccKernelEnv(client, { cwd: HOME });\n const sources = licks.attach(agent, { env: home, home: HOME, activity });\n await sources.start(BACKGROUND_CONTEXT);\n await sources.boot(\n { version: facts.version, boot: await kernelBoot(client) },\n BACKGROUND_CONTEXT\n );\n const skillsRuntime = await skills.attach(\n { env: home, home: HOME, assets: options.assets ?? packageAssets(), reloadMs: RELOAD_MS },\n BACKGROUND_CONTEXT\n );\n const sprinkles = await setupSprinkles().attach(\n {\n harness: agent.harness,\n agents: agent.agents,\n licks: licks.licks,\n env: home,\n assets: options.assets ?? packageAssets(),\n reloadMs: RELOAD_MS,\n },\n BACKGROUND_CONTEXT\n );\n let roles: ScoopsRuntime | undefined;\n const memoryRuntime = await memory.attach(\n {\n agents: agent.agents,\n env: home,\n home: HOME,\n reloadMs: RELOAD_MS,\n roles: async (using) => roles?.roles(using),\n },\n BACKGROUND_CONTEXT\n );\n const runtime = await scoops.attach(\n {\n harness: agent.harness,\n agents: agent.agents,\n groups,\n env: home,\n home: HOME,\n alive: alive(client),\n reads: skillsRuntime.dirs,\n sprinkle: sprinkles.command,\n memory: memoryRuntime.command,\n ...(options.assets ? { assets: options.assets } : {}),\n },\n BACKGROUND_CONTEXT\n );\n roles = runtime;\n await memoryRuntime.reload(BACKGROUND_CONTEXT).catch(() => undefined);\n return hostAgent(agent, {\n settings: await createAgentSettings(models, credentials, providers),\n licks: { licks: licks.licks, sources },\n scoops: { scoops: scoops.scoops, runtime },\n skills: skillsRuntime,\n sprinkles,\n memory: memoryRuntime,\n });\n}\n\nexport function alive(client: KernelClient) {\n return async (pid: number): Promise<boolean> => {\n if (!client.ps) return true;\n return (await client.ps()).some((process) => process.pid === pid);\n };\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": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|