@ai-ecoverse/slicc-agent 3.15.0 → 3.16.1

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 (57) hide show
  1. package/README.md +19 -3
  2. package/bin/agent +1 -1
  3. package/dist/control.js +49 -33
  4. package/dist/control.js.map +2 -2
  5. package/dist/gelatiere/index.js +307 -0
  6. package/dist/gelatiere/index.js.map +7 -0
  7. package/dist/gelatiere/store.js +122 -0
  8. package/dist/gelatiere/store.js.map +7 -0
  9. package/dist/main.js +14 -0
  10. package/dist/main.js.map +2 -2
  11. package/dist/memory/extract.js +252 -0
  12. package/dist/memory/extract.js.map +7 -0
  13. package/dist/memory/index.js +26 -2
  14. package/dist/memory/index.js.map +2 -2
  15. package/dist/memory/prompt.js.map +2 -2
  16. package/dist/memory/runtime.js +1 -1
  17. package/dist/memory/runtime.js.map +2 -2
  18. package/dist/scoops/cli.js +2 -0
  19. package/dist/scoops/cli.js.map +2 -2
  20. package/dist/scoops/index.js +4 -3
  21. package/dist/scoops/index.js.map +2 -2
  22. package/dist/scoops/requests.js +2 -1
  23. package/dist/scoops/requests.js.map +2 -2
  24. package/dist/services.js.map +2 -2
  25. package/dist/settings.js +12 -9
  26. package/dist/settings.js.map +2 -2
  27. package/dist/skills/index.js +1 -0
  28. package/dist/skills/index.js.map +2 -2
  29. package/dist/spectrum/agent-port.js +6 -2
  30. package/dist/spectrum/agent-port.js.map +2 -2
  31. package/dist/spectrum/messages.js +4 -3
  32. package/dist/spectrum/messages.js.map +2 -2
  33. package/dist/spectrum/settings-port.js +3 -2
  34. package/dist/spectrum/settings-port.js.map +2 -2
  35. package/dist/sprinkles/index.js +37 -29
  36. package/dist/sprinkles/index.js.map +2 -2
  37. package/dist/sprinkles/sprinkles.js +6 -0
  38. package/dist/sprinkles/sprinkles.js.map +2 -2
  39. package/dist/types/gelatiere/index.d.ts +39 -0
  40. package/dist/types/gelatiere/store.d.ts +34 -0
  41. package/dist/types/memory/extract.d.ts +48 -0
  42. package/dist/types/memory/index.d.ts +1 -0
  43. package/dist/types/memory/prompt.d.ts +2 -0
  44. package/dist/types/memory/runtime.d.ts +3 -0
  45. package/dist/types/scoops/cli.d.ts +1 -0
  46. package/dist/types/scoops/index.d.ts +1 -0
  47. package/dist/types/scoops/requests.d.ts +1 -1
  48. package/dist/types/services.d.ts +2 -0
  49. package/dist/types/spectrum/agent-port.d.ts +1 -1
  50. package/dist/types/sprinkles/index.d.ts +1 -0
  51. package/dist/types/sprinkles/sprinkles.d.ts +3 -0
  52. package/package.json +1 -1
  53. package/packages/vfs-root/agents/gelatiere.md +15 -0
  54. package/packages/vfs-root/gelatiere/GELATIERE.md +19 -0
  55. package/packages/vfs-root/skills/gelatiere/SKILL.md +30 -0
  56. package/packages/vfs-root/skills/memory/SKILL.md +4 -0
  57. package/packages/vfs-root/sprinkles/suggestions.shtml +1 -1
@@ -0,0 +1,122 @@
1
+ import { redactSecrets } from "../memory/format.js";
2
+ import { SUGGESTIONS_DIR, SUGGESTIONS_STORE } from "../sprinkles/sprinkles.js";
3
+ const STORE_DIR = SUGGESTIONS_DIR;
4
+ const STORE = SUGGESTIONS_STORE;
5
+ const KINDS = ["skill", "use-case", "tip", "skill-idea", "issue"];
6
+ const MAX_OPEN = 50;
7
+ const PROMPTED = /* @__PURE__ */ new Set(["use-case", "skill-idea", "issue"]);
8
+ const ID = /^[a-z0-9][a-z0-9-]{0,63}$/;
9
+ const CONE = /^[a-z0-9][a-z0-9._-]{0,63}$/;
10
+ const SOURCE = /^https:\/\/[^\s"'<>]+\/SKILL\.md$/;
11
+ function text(value, limit) {
12
+ if (typeof value !== "string") return void 0;
13
+ const clean = redactSecrets(value.trim()).text;
14
+ return clean ? clean.slice(0, limit) : void 0;
15
+ }
16
+ function origin(skill, source) {
17
+ const url = new URL(source);
18
+ return `${skill} from ${url.host}${url.pathname}`;
19
+ }
20
+ function kindProblem(kind, skill, source, prompt) {
21
+ if (kind === "skill" && (!skill || !ID.test(skill)))
22
+ return "a skill needs its name (a-z, 0-9, -)";
23
+ if (kind === "skill" && (!source || !SOURCE.test(source)))
24
+ return "a skill needs its source, an https URL of its SKILL.md";
25
+ if (PROMPTED.has(kind) && !prompt) return `a ${kind} needs its prompt`;
26
+ return void 0;
27
+ }
28
+ function shapeProblem(url, cones) {
29
+ if (url !== void 0 && !/^https:\/\/[^\s"'<>]+$/.test(url)) return "url must be https";
30
+ if (cones.some((cone) => !CONE.test(cone))) return "cones must be cone ids";
31
+ return void 0;
32
+ }
33
+ function validate(candidate) {
34
+ if (!candidate || typeof candidate !== "object") return "not an object";
35
+ const item = candidate;
36
+ const id = typeof item.id === "string" ? item.id : "";
37
+ if (!ID.test(id)) return `id "${String(item.id)}" must be 1\u201364 of a-z, 0-9 and -`;
38
+ if (!KINDS.includes(item.kind)) return `${id}: kind must be one of ${KINDS.join(", ")}`;
39
+ const title = text(item.title, 120);
40
+ const body = text(item.body, 600);
41
+ if (!title || !body) return `${id}: needs a title and a body`;
42
+ const skill = typeof item.skill === "string" ? item.skill.trim() : void 0;
43
+ const source = typeof item.source === "string" ? item.source.trim() : void 0;
44
+ const url = typeof item.url === "string" ? item.url.trim() : void 0;
45
+ const cones = Array.isArray(item.cones) ? item.cones.filter((cone) => typeof cone === "string") : [];
46
+ const evidence = text(item.evidence, 300);
47
+ const prompt = text(item.prompt, 1e3);
48
+ const problem = kindProblem(item.kind, skill, source, prompt) ?? shapeProblem(url, cones);
49
+ if (problem) return `${id}: ${problem}`;
50
+ const installs = item.kind === "skill" ? { skill, source, install: origin(skill, source) } : {};
51
+ return {
52
+ id,
53
+ kind: item.kind,
54
+ title,
55
+ body,
56
+ ...evidence ? { evidence } : {},
57
+ ...installs,
58
+ ...prompt ? { prompt } : {},
59
+ ...url ? { url } : {},
60
+ cones,
61
+ createdAt: 0
62
+ };
63
+ }
64
+ function fold(store, candidates, now) {
65
+ const next = store.map((item) => ({ ...item }));
66
+ const rejected = [];
67
+ let added = 0;
68
+ let updated = 0;
69
+ if (!Array.isArray(candidates))
70
+ return { store: next, added, updated, rejected: ["not a JSON array"] };
71
+ for (const candidate of candidates) {
72
+ const checked = validate(candidate);
73
+ if (typeof checked === "string") {
74
+ rejected.push(checked);
75
+ continue;
76
+ }
77
+ const at = next.findIndex((item) => item.id === checked.id);
78
+ const old = next[at];
79
+ if (old) {
80
+ if (old.dismissedAt || old.takenAt) continue;
81
+ next[at] = { ...checked, createdAt: old.createdAt };
82
+ updated++;
83
+ } else if (open(next).length >= MAX_OPEN)
84
+ rejected.push(`${checked.id}: ${MAX_OPEN} suggestions are already open`);
85
+ else {
86
+ next.push({ ...checked, createdAt: now });
87
+ added++;
88
+ }
89
+ }
90
+ return { store: next, added, updated, rejected };
91
+ }
92
+ function open(store) {
93
+ return store.filter((item) => !item.dismissedAt && !item.takenAt);
94
+ }
95
+ async function readStore(env, context) {
96
+ const read = await env.readTextFile(STORE, context);
97
+ if (!read.ok) return null;
98
+ try {
99
+ const parsed = JSON.parse(read.value);
100
+ return Array.isArray(parsed) ? parsed.filter((item) => typeof validate(item) !== "string") : [];
101
+ } catch {
102
+ return [];
103
+ }
104
+ }
105
+ async function writeStore(env, store, context) {
106
+ await env.createDir(STORE_DIR, { recursive: true }, context);
107
+ const written = await env.writeFile(STORE, `${JSON.stringify(store, null, 2)}
108
+ `, context);
109
+ if (!written.ok) throw new Error(written.error.message);
110
+ }
111
+ export {
112
+ KINDS,
113
+ MAX_OPEN,
114
+ STORE,
115
+ STORE_DIR,
116
+ fold,
117
+ open,
118
+ readStore,
119
+ validate,
120
+ writeStore
121
+ };
122
+ //# sourceMappingURL=store.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/gelatiere/store.ts"],
4
+ "sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport type { ExecutionEnv } from '@earendil-works/pi-durable/env';\nimport { redactSecrets } from '../memory/format.ts';\nimport { SUGGESTIONS_DIR, SUGGESTIONS_STORE } from '../sprinkles/sprinkles.ts';\n\nexport const STORE_DIR = SUGGESTIONS_DIR;\nexport const STORE = SUGGESTIONS_STORE;\nexport const KINDS = ['skill', 'use-case', 'tip', 'skill-idea', 'issue'] as const;\nexport const MAX_OPEN = 50;\nconst PROMPTED = new Set<Kind>(['use-case', 'skill-idea', 'issue']);\n\nexport type Kind = (typeof KINDS)[number];\n\nexport interface Suggestion {\n id: string;\n kind: Kind;\n title: string;\n body: string;\n evidence?: string;\n skill?: string;\n source?: string;\n install?: string;\n prompt?: string;\n url?: string;\n cones: string[];\n createdAt: number;\n deliveredAt?: number;\n dismissedAt?: number;\n takenAt?: number;\n}\n\nconst ID = /^[a-z0-9][a-z0-9-]{0,63}$/;\nconst CONE = /^[a-z0-9][a-z0-9._-]{0,63}$/;\nconst SOURCE = /^https:\\/\\/[^\\s\"'<>]+\\/SKILL\\.md$/;\n\nfunction text(value: unknown, limit: number): string | undefined {\n if (typeof value !== 'string') return undefined;\n const clean = redactSecrets(value.trim()).text;\n return clean ? clean.slice(0, limit) : undefined;\n}\n\nfunction origin(skill: string | undefined, source: string | undefined): string {\n const url = new URL(source as string);\n return `${skill} from ${url.host}${url.pathname}`;\n}\n\nfunction kindProblem(\n kind: Kind,\n skill: string | undefined,\n source: string | undefined,\n prompt: string | undefined\n): string | undefined {\n if (kind === 'skill' && (!skill || !ID.test(skill)))\n return 'a skill needs its name (a-z, 0-9, -)';\n if (kind === 'skill' && (!source || !SOURCE.test(source)))\n return 'a skill needs its source, an https URL of its SKILL.md';\n if (PROMPTED.has(kind) && !prompt) return `a ${kind} needs its prompt`;\n return undefined;\n}\n\nfunction shapeProblem(url: string | undefined, cones: string[]): string | undefined {\n if (url !== undefined && !/^https:\\/\\/[^\\s\"'<>]+$/.test(url)) return 'url must be https';\n if (cones.some((cone) => !CONE.test(cone))) return 'cones must be cone ids';\n return undefined;\n}\n\nexport function validate(candidate: unknown): Suggestion | string {\n if (!candidate || typeof candidate !== 'object') return 'not an object';\n const item = candidate as Record<string, unknown>;\n const id = typeof item.id === 'string' ? item.id : '';\n if (!ID.test(id)) return `id \"${String(item.id)}\" must be 1\u201364 of a-z, 0-9 and -`;\n if (!KINDS.includes(item.kind as Kind)) return `${id}: kind must be one of ${KINDS.join(', ')}`;\n const title = text(item.title, 120);\n const body = text(item.body, 600);\n if (!title || !body) return `${id}: needs a title and a body`;\n const skill = typeof item.skill === 'string' ? item.skill.trim() : undefined;\n const source = typeof item.source === 'string' ? item.source.trim() : undefined;\n const url = typeof item.url === 'string' ? item.url.trim() : undefined;\n const cones = Array.isArray(item.cones)\n ? item.cones.filter((cone): cone is string => typeof cone === 'string')\n : [];\n const evidence = text(item.evidence, 300);\n const prompt = text(item.prompt, 1000);\n const problem = kindProblem(item.kind as Kind, skill, source, prompt) ?? shapeProblem(url, cones);\n if (problem) return `${id}: ${problem}`;\n const installs = item.kind === 'skill' ? { skill, source, install: origin(skill, source) } : {};\n return {\n id,\n kind: item.kind as Kind,\n title,\n body,\n ...(evidence ? { evidence } : {}),\n ...installs,\n ...(prompt ? { prompt } : {}),\n ...(url ? { url } : {}),\n cones,\n createdAt: 0,\n };\n}\n\nexport function fold(\n store: readonly Suggestion[],\n candidates: unknown,\n now: number\n): { store: Suggestion[]; added: number; updated: number; rejected: string[] } {\n const next = store.map((item) => ({ ...item }));\n const rejected: string[] = [];\n let added = 0;\n let updated = 0;\n if (!Array.isArray(candidates))\n return { store: next, added, updated, rejected: ['not a JSON array'] };\n for (const candidate of candidates) {\n const checked = validate(candidate);\n if (typeof checked === 'string') {\n rejected.push(checked);\n continue;\n }\n const at = next.findIndex((item) => item.id === checked.id);\n const old = next[at];\n if (old) {\n if (old.dismissedAt || old.takenAt) continue;\n next[at] = { ...checked, createdAt: old.createdAt };\n updated++;\n } else if (open(next).length >= MAX_OPEN)\n rejected.push(`${checked.id}: ${MAX_OPEN} suggestions are already open`);\n else {\n next.push({ ...checked, createdAt: now });\n added++;\n }\n }\n return { store: next, added, updated, rejected };\n}\n\nexport function open(store: readonly Suggestion[]): Suggestion[] {\n return store.filter((item) => !item.dismissedAt && !item.takenAt);\n}\n\nexport async function readStore(\n env: Pick<ExecutionEnv, 'readTextFile'>,\n context: Context\n): Promise<Suggestion[] | null> {\n const read = await env.readTextFile(STORE, context);\n if (!read.ok) return null;\n try {\n const parsed = JSON.parse(read.value) as unknown;\n return Array.isArray(parsed)\n ? parsed.filter((item): item is Suggestion => typeof validate(item) !== 'string')\n : [];\n } catch {\n return [];\n }\n}\n\nexport async function writeStore(\n env: Pick<ExecutionEnv, 'writeFile' | 'createDir'>,\n store: readonly Suggestion[],\n context: Context\n): Promise<void> {\n await env.createDir(STORE_DIR, { recursive: true }, context);\n const written = await env.writeFile(STORE, `${JSON.stringify(store, null, 2)}\\n`, context);\n if (!written.ok) throw new Error(written.error.message);\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
package/dist/main.js CHANGED
@@ -13,6 +13,7 @@ import {
13
13
  sandboxFactory
14
14
  } from "./codemode/index.js";
15
15
  import { EncryptedCredentialStore } from "./credentials.js";
16
+ import { lateGelatiere } from "./gelatiere/index.js";
16
17
  import { hostAgent } from "./host.js";
17
18
  import { createActivity } from "./kernel/activity.js";
18
19
  import { HOME, kernelEnvironment, SliccKernelEnv } from "./kernel/env.js";
@@ -96,8 +97,10 @@ async function start(scope, port, options) {
96
97
  { env: home, home: HOME, assets: options.assets ?? packageAssets(), reloadMs: RELOAD_MS },
97
98
  BACKGROUND_CONTEXT
98
99
  );
100
+ const gelatiere = lateGelatiere();
99
101
  const sprinkles = await setupSprinkles().attach(
100
102
  {
103
+ intercept: gelatiere.intercept,
101
104
  harness: agent.harness,
102
105
  agents: agent.agents,
103
106
  licks: licks.licks,
@@ -110,6 +113,7 @@ async function start(scope, port, options) {
110
113
  let roles;
111
114
  const memoryRuntime = await memory.attach(
112
115
  {
116
+ harness: agent.harness,
113
117
  agents: agent.agents,
114
118
  env: home,
115
119
  home: HOME,
@@ -129,11 +133,21 @@ async function start(scope, port, options) {
129
133
  reads: skillsRuntime.dirs,
130
134
  sprinkle: sprinkles.command,
131
135
  memory: memoryRuntime.command,
136
+ gelatiere: gelatiere.command,
132
137
  ...options.assets ? { assets: options.assets } : {}
133
138
  },
134
139
  BACKGROUND_CONTEXT
135
140
  );
136
141
  roles = runtime;
142
+ gelatiere.attach({
143
+ agents: agent.agents,
144
+ licks: licks.licks,
145
+ env: home,
146
+ home: HOME,
147
+ scoops: scoops.scoops,
148
+ roles: runtime.roles,
149
+ assets: options.assets ?? packageAssets()
150
+ });
137
151
  await memoryRuntime.reload(BACKGROUND_CONTEXT).catch(() => void 0);
138
152
  return hostAgent(agent, {
139
153
  settings: await createAgentSettings(models, credentials, providers),
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 { 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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",
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 { lateGelatiere } from './gelatiere/index.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 gelatiere = lateGelatiere();\n const sprinkles = await setupSprinkles().attach(\n {\n intercept: gelatiere.intercept,\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 harness: agent.harness,\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 gelatiere: gelatiere.command,\n ...(options.assets ? { assets: options.assets } : {}),\n },\n BACKGROUND_CONTEXT\n );\n roles = runtime;\n gelatiere.attach({\n agents: agent.agents,\n licks: licks.licks,\n env: home,\n home: HOME,\n scoops: scoops.scoops,\n roles: runtime.roles,\n assets: options.assets ?? packageAssets(),\n });\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": "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,SAAS,qBAAqB;AAC9B,SAAyB,iBAAiB;AAC1C,SAAS,sBAAsB;AAE/B,SAAS,MAAM,mBAAmB,sBAAsB;AACxD,SAAS,qBAAqB;AAC9B,SAAS,kBAAkB;AAC3B,SAAS,mBAAmB;AAC5B,SAAyB,sBAAsB;AAC/C,SAAS,mBAAmB;AAC5B,SAAS,gBAAgB;AACzB,SAAsB,eAAmC,mBAAmB;AAC5E;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,WAAW,mBAAmB;AACvC,SAAS,sBAAsB;AAC/B,SAAS,6BAA6B;AACtC,SAAS,cAAc,gBAAgB,YAAY,qBAAqB;AACxE,SAAS,YAAY,wBAA0C;AAoB/D,eAAe,MACb,OACA,MACA,SACoB;AACpB,QAAM,SAAuB,QAAQ,UAAU;AAC/C,QAAM,SAAS,MAAM,OAAO,MAAM,IAAI;AACtC,QAAM,SAAS,MAAM,MAAM,KAAK,KAAK;AACrC,QAAM,QAAQ,eAAe,OAAO,WAAW,MAAM,SAAS,QAAQ,MAAM;AAC5E,QAAM,QAAQ;AAAA,IACZ,SAAS,MAAM,aAAa,CAAC,QAAQ,OAAO,GAAG,CAAC;AAAA,IAChD,UAAU,MAAM,eAAe,MAAM;AAAA,IACrC,WAAW,cAAc,OAAO,SAAS;AAAA,EAC3C;AACA,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,YAAY,KAAK,CAAC;AACnC,QAAM,SAAS,YAAY,QAAQ;AACnC,QAAM,WAAW,MAAM,OAAO,GAC3B,SAAS,GAAG,IAAI,0BAA0B,EAC1C,MAAM,MAAM,MAAS;AACxB,QAAM,SAAS,YAAY;AAC3B,QAAM,WAAW;AAAA,IACf,aAAa,SAAY,SAAY,IAAI,YAAY,EAAE,OAAO,QAAQ;AAAA,EACxE,IACI,CAAC,IACD;AAAA,IACE,aAAa;AAAA,MACX,UAAU;AAAA,MACV,SAAS;AAAA,QACP,QAAQ,iBAAiB,MAAM,WAAW,QAAQ,IAAI,IAAI,YAAY,GAAG,CAAC;AAAA,QAC1E,QAAQ;AAAA,MACV;AAAA,IACF,CAAC;AAAA,EACH;AACJ,WAAS,QAAQ,kBAAkB,QAAQ,CAAC;AAC5C,QAAM,QAAQ,WAAW,QAAQ;AACjC,QAAM,SAAS,YAAY,UAAU,MAAM,OAAO,CAAC,GAAG,QAAQ,GAAG,UAAU,GAAG,OAAO,KAAK,CAAC;AAC3F,QAAM,SAAS,YAAY,UAAU,MAAM,KAAK;AAChD,QAAM,WAAW,eAAe;AAChC,QAAM,SAAS,cAAc,MAAM;AACnC,QAAM,QAAQ,QAAQ,SAAS;AAC/B,MAAI;AACJ,QAAM,cAAc,kBAAkB,QAAQ;AAAA,IAC5C;AAAA,IACA;AAAA,IACA,UAAU,SAAS,MAAM,QAAQ,QAAQ,KAAK;AAAA,EAChD,CAAC;AACD,QAAM,QAAQ,MAAM,UAAU;AAAA,IAC5B;AAAA,IACA;AAAA,IACA,SAAS,OAAO,QAAQ,YAAY,MAAM,sBAAsB,IAAI;AAAA,IACpE;AAAA,IACA,KAAK;AAAA,EACP,CAAC;AACD,WAAS;AACT,QAAM,OAAO,IAAI,eAAe,QAAQ,EAAE,KAAK,KAAK,CAAC;AACrD,QAAM,UAAU,MAAM,OAAO,OAAO,EAAE,KAAK,MAAM,MAAM,MAAM,SAAS,CAAC;AACvE,QAAM,QAAQ,MAAM,kBAAkB;AACtC,QAAM,QAAQ;AAAA,IACZ,EAAE,SAAS,MAAM,SAAS,MAAM,MAAM,WAAW,MAAM,EAAE;AAAA,IACzD;AAAA,EACF;AACA,QAAM,gBAAgB,MAAM,OAAO;AAAA,IACjC,EAAE,KAAK,MAAM,MAAM,MAAM,QAAQ,QAAQ,UAAU,cAAc,GAAG,UAAU,UAAU;AAAA,IACxF;AAAA,EACF;AACA,QAAM,YAAY,cAAc;AAChC,QAAM,YAAY,MAAM,eAAe,EAAE;AAAA,IACvC;AAAA,MACE,WAAW,UAAU;AAAA,MACrB,SAAS,MAAM;AAAA,MACf,QAAQ,MAAM;AAAA,MACd,OAAO,MAAM;AAAA,MACb,KAAK;AAAA,MACL,QAAQ,QAAQ,UAAU,cAAc;AAAA,MACxC,UAAU;AAAA,IACZ;AAAA,IACA;AAAA,EACF;AACA,MAAI;AACJ,QAAM,gBAAgB,MAAM,OAAO;AAAA,IACjC;AAAA,MACE,SAAS,MAAM;AAAA,MACf,QAAQ,MAAM;AAAA,MACd,KAAK;AAAA,MACL,MAAM;AAAA,MACN,UAAU;AAAA,MACV,OAAO,OAAO,UAAU,OAAO,MAAM,KAAK;AAAA,IAC5C;AAAA,IACA;AAAA,EACF;AACA,QAAM,UAAU,MAAM,OAAO;AAAA,IAC3B;AAAA,MACE,SAAS,MAAM;AAAA,MACf,QAAQ,MAAM;AAAA,MACd;AAAA,MACA,KAAK;AAAA,MACL,MAAM;AAAA,MACN,OAAO,MAAM,MAAM;AAAA,MACnB,OAAO,cAAc;AAAA,MACrB,UAAU,UAAU;AAAA,MACpB,QAAQ,cAAc;AAAA,MACtB,WAAW,UAAU;AAAA,MACrB,GAAI,QAAQ,SAAS,EAAE,QAAQ,QAAQ,OAAO,IAAI,CAAC;AAAA,IACrD;AAAA,IACA;AAAA,EACF;AACA,UAAQ;AACR,YAAU,OAAO;AAAA,IACf,QAAQ,MAAM;AAAA,IACd,OAAO,MAAM;AAAA,IACb,KAAK;AAAA,IACL,MAAM;AAAA,IACN,QAAQ,OAAO;AAAA,IACf,OAAO,QAAQ;AAAA,IACf,QAAQ,QAAQ,UAAU,cAAc;AAAA,EAC1C,CAAC;AACD,QAAM,cAAc,OAAO,kBAAkB,EAAE,MAAM,MAAM,MAAS;AACpE,SAAO,UAAU,OAAO;AAAA,IACtB,UAAU,MAAM,oBAAoB,QAAQ,aAAa,SAAS;AAAA,IAClE,OAAO,EAAE,OAAO,MAAM,OAAO,QAAQ;AAAA,IACrC,QAAQ,EAAE,QAAQ,OAAO,QAAQ,QAAQ;AAAA,IACzC,QAAQ;AAAA,IACR;AAAA,IACA,QAAQ;AAAA,EACV,CAAC;AACH;AAEO,SAAS,MAAM,QAAsB;AAC1C,SAAO,OAAO,QAAkC;AAC9C,QAAI,CAAC,OAAO,GAAI,QAAO;AACvB,YAAQ,MAAM,OAAO,GAAG,GAAG,KAAK,CAAC,YAAY,QAAQ,QAAQ,GAAG;AAAA,EAClE;AACF;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
6
  "names": []
7
7
  }
@@ -0,0 +1,252 @@
1
+ import { BACKGROUND_CONTEXT } from "@earendil-works/chord/context";
2
+ import {
3
+ CompactionTask,
4
+ defineDoc,
5
+ defineTask,
6
+ hook
7
+ } from "@earendil-works/pi-durable";
8
+ import { MAX_BYTES, parseMemory, redactSecrets, serializeMemory } from "./format.js";
9
+ import { whoIs } from "./prompt.js";
10
+ import { placeOf } from "./store.js";
11
+ const EXTRACT_TASK = "slicc.memory-extract";
12
+ const TRANSCRIPT_CHARS = 6e4;
13
+ const DEFAULT_IDLE_MINUTES = 30;
14
+ const ExtractDoc = defineDoc({
15
+ kind: "slicc.memory-extract",
16
+ version: 1,
17
+ scope: "session",
18
+ initial: () => ({ cursors: {}, pending: {} })
19
+ });
20
+ function extractSettings(text) {
21
+ let memory = {};
22
+ try {
23
+ const parsed = JSON.parse(text ?? "{}");
24
+ memory = parsed.memory && typeof parsed.memory === "object" ? parsed.memory : {};
25
+ } catch {
26
+ memory = {};
27
+ }
28
+ const named = typeof memory.extractModel === "string" ? memory.extractModel.split("/") : [];
29
+ const model = named.length >= 2 && named[0] && named.slice(1).join("/") ? { provider: named[0], modelId: named.slice(1).join("/") } : null;
30
+ const idle = typeof memory.idleMinutes === "number" && memory.idleMinutes >= 0 ? memory.idleMinutes : DEFAULT_IDLE_MINUTES;
31
+ return { extract: memory.extract !== false, model, idleMinutes: idle };
32
+ }
33
+ function textOf(message) {
34
+ const content = message.content;
35
+ if (typeof content === "string") return content;
36
+ if (!Array.isArray(content)) return "";
37
+ return content.map(
38
+ (part) => part.type === "text" ? part.text ?? "" : part.type === "toolCall" ? `[tool ${part.name}]` : ""
39
+ ).filter(Boolean).join("\n");
40
+ }
41
+ function transcriptOf(messages) {
42
+ const lines = [];
43
+ for (const message of messages) {
44
+ if (message.role !== "user" && message.role !== "assistant") continue;
45
+ const text = textOf(message).trim();
46
+ if (text) lines.push(`${message.role === "user" ? "User" : "Agent"}: ${text}`);
47
+ }
48
+ const joined = redactSecrets(lines.join("\n\n")).text;
49
+ return joined.length > TRANSCRIPT_CHARS ? joined.slice(-TRANSCRIPT_CHARS) : joined;
50
+ }
51
+ const EXTRACT_PROMPT = [
52
+ "You keep the long-term memory of a personal agent. Read the conversation excerpt and the current memory file, and propose memory entries worth keeping across conversations:",
53
+ '- who the user is (tag "user"), how they want the agent to work, including corrections (tag "feedback"), and durable facts about their projects (tag "project");',
54
+ "- only facts that will still matter in a later conversation; nothing about this one task, no transcripts, no guesses;",
55
+ "- never secrets: no keys, tokens, passwords, Authorization values or URLs that carry a key;",
56
+ "- to change an entry that exists, repeat its section and title with the new body; leave entries alone that need no change.",
57
+ 'Answer with a JSON array and nothing else: [{"section": "...", "title": "...", "body": "...", "tag": "user" | "feedback" | "project"}]. An empty array is a fine answer.'
58
+ ].join("\n");
59
+ const TAGS = /* @__PURE__ */ new Set(["user", "feedback", "project"]);
60
+ function proposals(text) {
61
+ const start = text.indexOf("[");
62
+ const end = text.lastIndexOf("]");
63
+ if (start < 0 || end < start) return [];
64
+ let parsed;
65
+ try {
66
+ parsed = JSON.parse(text.slice(start, end + 1));
67
+ } catch {
68
+ return [];
69
+ }
70
+ if (!Array.isArray(parsed)) return [];
71
+ const out = [];
72
+ for (const item of parsed) {
73
+ if (!item || typeof item !== "object") continue;
74
+ const { section, title, body, tag } = item;
75
+ if (typeof section !== "string" || typeof title !== "string" || typeof body !== "string")
76
+ continue;
77
+ if (!section.trim() || !title.trim() || !body.trim()) continue;
78
+ out.push({
79
+ section,
80
+ title,
81
+ body,
82
+ tag: typeof tag === "string" && TAGS.has(tag) ? tag : null
83
+ });
84
+ }
85
+ return out.slice(0, 20);
86
+ }
87
+ async function save(host, place, found, context) {
88
+ let saved = 0;
89
+ for (const proposal of found) {
90
+ try {
91
+ await host.files.change(place, { kind: "save", ...proposal }, context);
92
+ saved++;
93
+ } catch {
94
+ break;
95
+ }
96
+ }
97
+ if (saved) await host.changed(context).catch(() => void 0);
98
+ return saved;
99
+ }
100
+ function extractTask(ready, settings) {
101
+ return defineTask({
102
+ name: EXTRACT_TASK,
103
+ version: 1,
104
+ initial: () => ({ phase: "extract" }),
105
+ phases: {
106
+ async extract(task, runtime, context) {
107
+ const { cone, after, tail } = task.input;
108
+ const key = String(runtime.conversationId);
109
+ const done = (result) => runtime.commit(async (tx) => {
110
+ const doc = await tx.doc(ExtractDoc);
111
+ doc.cursors[key] = Math.max(doc.cursors[key] ?? 0, tail);
112
+ delete doc.pending[key];
113
+ return { status: "terminal", outcome: { status: "completed", result } };
114
+ }, context);
115
+ if (!(await settings(context)).extract) return done({ saved: 0, skipped: "turned off" });
116
+ const host = await ready;
117
+ const place = placeOf(host.home, cone);
118
+ const view = await runtime.context(runtime.conversationId, context, tail);
119
+ const fresh = view.entries.flatMap(
120
+ (entry, index) => entry.id > after ? view.contributions[index] : []
121
+ );
122
+ const transcript = transcriptOf(fresh);
123
+ if (!place || !transcript) return done({ saved: 0, skipped: "nothing new" });
124
+ const ref = task.input.model ?? (await runtime.agent(context)).model;
125
+ const model = ref ? runtime.models.getModel(ref.provider, ref.modelId) : void 0;
126
+ if (!model) return done({ saved: 0, skipped: "no model" });
127
+ const current = await host.files.read(place.file, context);
128
+ const memory = current ? serializeMemory(parseMemory(current.text)) : "";
129
+ const now = Date.now();
130
+ const message = await runtime.models.completeSimple(
131
+ model,
132
+ {
133
+ messages: [
134
+ { role: "system", content: EXTRACT_PROMPT, timestamp: now },
135
+ {
136
+ role: "user",
137
+ content: [
138
+ {
139
+ type: "text",
140
+ text: `<memory_file bytes_left="${MAX_BYTES - new TextEncoder().encode(memory).length}">
141
+ ${memory}</memory_file>
142
+
143
+ <conversation>
144
+ ${transcript}
145
+ </conversation>`
146
+ }
147
+ ],
148
+ timestamp: now
149
+ }
150
+ ]
151
+ },
152
+ { maxTokens: 2e3, signal: runtime.signal, cacheRetention: "none" }
153
+ );
154
+ runtime.signal.throwIfAborted();
155
+ if (message.stopReason === "error") return done({ saved: 0, skipped: "model error" });
156
+ const saved = await save(host, place, proposals(textOf(message)), context);
157
+ return done({ saved, skipped: null });
158
+ }
159
+ },
160
+ abort: (_task, runtime, context) => runtime.commit(async (tx) => {
161
+ delete (await tx.doc(ExtractDoc)).pending[String(runtime.conversationId)];
162
+ return { status: "terminal", outcome: { status: "aborted" } };
163
+ }, context)
164
+ });
165
+ }
166
+ function compactionHook(schedule) {
167
+ return hook(CompactionTask, {
168
+ beforeCompact(compaction, api) {
169
+ const tail = compaction.entries.reduce((max, entry) => Math.max(max, entry.id), 0);
170
+ void schedule(api.conversationId, tail, BACKGROUND_CONTEXT).catch(() => false);
171
+ return void 0;
172
+ }
173
+ });
174
+ }
175
+ function scheduler(ready, task, settings) {
176
+ return async (conversation, tail, context) => {
177
+ const host = await ready;
178
+ const chosen = await settings(context);
179
+ const who = whoIs(host.agents.state(), conversation);
180
+ if (who.kind !== "cone") return false;
181
+ return host.harness.commit(async (tx) => {
182
+ const doc = await tx.doc(ExtractDoc);
183
+ const key = String(conversation);
184
+ const after = doc.cursors[key] ?? 0;
185
+ if (doc.pending[key] !== void 0 || tail <= after) return false;
186
+ doc.pending[key] = await tx.createTask(
187
+ task,
188
+ { cone: who.id, after, tail, model: chosen.model },
189
+ {
190
+ ownership: { kind: "conversation" },
191
+ conversationId: conversation,
192
+ background: true
193
+ }
194
+ );
195
+ return true;
196
+ }, context);
197
+ };
198
+ }
199
+ function settingsReader(ready) {
200
+ return async (context) => {
201
+ const host = await ready;
202
+ const read = await host.env.readTextFile(`${host.home}/.pi/agent/settings.json`, context);
203
+ return extractSettings(read.ok ? read.value : void 0);
204
+ };
205
+ }
206
+ function idleWatch(host, schedule, settings, everyMs, context, now = Date.now) {
207
+ const seen = /* @__PURE__ */ new Map();
208
+ const check = async () => {
209
+ const { idleMinutes, extract } = await settings(context);
210
+ if (!extract || idleMinutes === 0) return;
211
+ for (const cone of Object.values(host.agents.state().cones)) {
212
+ const conversation = await host.harness.conversation(
213
+ cone.conversation,
214
+ context
215
+ );
216
+ const view = await conversation?.context(context);
217
+ const tail = view?.entries.at(-1)?.id ?? 0;
218
+ const last = seen.get(cone.conversation);
219
+ if (!last || last.tail !== tail) {
220
+ seen.set(cone.conversation, { tail, since: now() });
221
+ continue;
222
+ }
223
+ if (now() - last.since >= idleMinutes * 6e4)
224
+ await schedule(cone.conversation, tail, context);
225
+ }
226
+ };
227
+ let running = false;
228
+ const timer = setInterval(() => {
229
+ if (running) return;
230
+ running = true;
231
+ void check().catch(() => void 0).finally(() => {
232
+ running = false;
233
+ });
234
+ }, everyMs);
235
+ return () => clearInterval(timer);
236
+ }
237
+ export {
238
+ DEFAULT_IDLE_MINUTES,
239
+ EXTRACT_PROMPT,
240
+ EXTRACT_TASK,
241
+ ExtractDoc,
242
+ TRANSCRIPT_CHARS,
243
+ compactionHook,
244
+ extractSettings,
245
+ extractTask,
246
+ idleWatch,
247
+ proposals,
248
+ scheduler,
249
+ settingsReader,
250
+ transcriptOf
251
+ };
252
+ //# sourceMappingURL=extract.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/memory/extract.ts"],
4
+ "sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport { BACKGROUND_CONTEXT } from '@earendil-works/chord/context';\nimport type { Message } from '@earendil-works/pi-ai';\nimport {\n CompactionTask,\n type ConversationId,\n defineDoc,\n defineTask,\n type EntryId,\n hook,\n type ModelRef,\n} from '@earendil-works/pi-durable';\nimport type { MemoryTag } from './format.ts';\nimport { MAX_BYTES, parseMemory, redactSecrets, serializeMemory } from './format.ts';\nimport { type Host, whoIs } from './prompt.ts';\nimport { type Place, placeOf } from './store.ts';\n\nexport const EXTRACT_TASK = 'slicc.memory-extract';\nexport const TRANSCRIPT_CHARS = 60_000;\nexport const DEFAULT_IDLE_MINUTES = 30;\n\nexport type ExtractState = {\n cursors: Record<string, number>;\n pending: Record<string, number>;\n};\n\nexport const ExtractDoc = defineDoc<ExtractState>({\n kind: 'slicc.memory-extract',\n version: 1,\n scope: 'session',\n initial: () => ({ cursors: {}, pending: {} }),\n});\n\nexport type ExtractSettings = { extract: boolean; model: ModelRef | null; idleMinutes: number };\n\nexport function extractSettings(text: string | undefined): ExtractSettings {\n let memory: { extract?: unknown; extractModel?: unknown; idleMinutes?: unknown } = {};\n try {\n const parsed = JSON.parse(text ?? '{}') as { memory?: typeof memory };\n memory = parsed.memory && typeof parsed.memory === 'object' ? parsed.memory : {};\n } catch {\n memory = {};\n }\n const named = typeof memory.extractModel === 'string' ? memory.extractModel.split('/') : [];\n const model =\n named.length >= 2 && named[0] && named.slice(1).join('/')\n ? { provider: named[0], modelId: named.slice(1).join('/') }\n : null;\n const idle =\n typeof memory.idleMinutes === 'number' && memory.idleMinutes >= 0\n ? memory.idleMinutes\n : DEFAULT_IDLE_MINUTES;\n return { extract: memory.extract !== false, model, idleMinutes: idle };\n}\n\nfunction textOf(message: Message): string {\n const content = (message as { content?: unknown }).content;\n if (typeof content === 'string') return content;\n if (!Array.isArray(content)) return '';\n return content\n .map((part: { type?: string; text?: string; name?: string }) =>\n part.type === 'text'\n ? (part.text ?? '')\n : part.type === 'toolCall'\n ? `[tool ${part.name}]`\n : ''\n )\n .filter(Boolean)\n .join('\\n');\n}\n\nexport function transcriptOf(messages: readonly Message[]): string {\n const lines: string[] = [];\n for (const message of messages) {\n if (message.role !== 'user' && message.role !== 'assistant') continue;\n const text = textOf(message).trim();\n if (text) lines.push(`${message.role === 'user' ? 'User' : 'Agent'}: ${text}`);\n }\n const joined = redactSecrets(lines.join('\\n\\n')).text;\n return joined.length > TRANSCRIPT_CHARS ? joined.slice(-TRANSCRIPT_CHARS) : joined;\n}\n\nexport const EXTRACT_PROMPT = [\n 'You keep the long-term memory of a personal agent. Read the conversation excerpt and the current memory file, and propose memory entries worth keeping across conversations:',\n '- who the user is (tag \"user\"), how they want the agent to work, including corrections (tag \"feedback\"), and durable facts about their projects (tag \"project\");',\n '- only facts that will still matter in a later conversation; nothing about this one task, no transcripts, no guesses;',\n '- never secrets: no keys, tokens, passwords, Authorization values or URLs that carry a key;',\n '- to change an entry that exists, repeat its section and title with the new body; leave entries alone that need no change.',\n 'Answer with a JSON array and nothing else: [{\"section\": \"...\", \"title\": \"...\", \"body\": \"...\", \"tag\": \"user\" | \"feedback\" | \"project\"}]. An empty array is a fine answer.',\n].join('\\n');\n\nexport type Proposal = { section: string; title: string; body: string; tag: MemoryTag | null };\n\nconst TAGS = new Set(['user', 'feedback', 'project']);\n\nexport function proposals(text: string): Proposal[] {\n const start = text.indexOf('[');\n const end = text.lastIndexOf(']');\n if (start < 0 || end < start) return [];\n let parsed: unknown;\n try {\n parsed = JSON.parse(text.slice(start, end + 1));\n } catch {\n return [];\n }\n if (!Array.isArray(parsed)) return [];\n const out: Proposal[] = [];\n for (const item of parsed as Record<string, unknown>[]) {\n if (!item || typeof item !== 'object') continue;\n const { section, title, body, tag } = item;\n if (typeof section !== 'string' || typeof title !== 'string' || typeof body !== 'string')\n continue;\n if (!section.trim() || !title.trim() || !body.trim()) continue;\n out.push({\n section,\n title,\n body,\n tag: typeof tag === 'string' && TAGS.has(tag) ? (tag as MemoryTag) : null,\n });\n }\n return out.slice(0, 20);\n}\n\ntype Input = { cone: string; after: number; tail: number; model: ModelRef | null };\ntype State = { phase: 'extract' };\nexport type ExtractResult = { saved: number; skipped: string | null };\n\nasync function save(host: Host, place: Place, found: Proposal[], context: Context) {\n let saved = 0;\n for (const proposal of found) {\n try {\n await host.files.change(place, { kind: 'save', ...proposal }, context);\n saved++;\n } catch {\n break;\n }\n }\n if (saved) await host.changed(context).catch(() => undefined);\n return saved;\n}\n\nexport function extractTask(\n ready: Promise<Host>,\n settings: (context: Context) => Promise<ExtractSettings>\n) {\n return defineTask<Input, State, ExtractResult>({\n name: EXTRACT_TASK,\n version: 1,\n initial: () => ({ phase: 'extract' }),\n phases: {\n async extract(task, runtime, context) {\n const { cone, after, tail } = task.input;\n const key = String(runtime.conversationId);\n const done = (result: ExtractResult) =>\n runtime.commit(async (tx) => {\n const doc = await tx.doc(ExtractDoc);\n doc.cursors[key] = Math.max(doc.cursors[key] ?? 0, tail);\n delete doc.pending[key];\n return { status: 'terminal', outcome: { status: 'completed', result } };\n }, context);\n if (!(await settings(context)).extract) return done({ saved: 0, skipped: 'turned off' });\n const host = await ready;\n const place = placeOf(host.home, cone);\n const view = await runtime.context(runtime.conversationId, context, tail as EntryId);\n const fresh = view.entries.flatMap((entry, index) =>\n entry.id > after ? (view.contributions[index] as readonly Message[]) : []\n );\n const transcript = transcriptOf(fresh);\n if (!place || !transcript) return done({ saved: 0, skipped: 'nothing new' });\n const ref = task.input.model ?? (await runtime.agent(context)).model;\n const model = ref ? runtime.models.getModel(ref.provider, ref.modelId) : undefined;\n if (!model) return done({ saved: 0, skipped: 'no model' });\n const current = await host.files.read(place.file, context);\n const memory = current ? serializeMemory(parseMemory(current.text)) : '';\n const now = Date.now();\n const message = await runtime.models.completeSimple(\n model,\n {\n messages: [\n { role: 'system', content: EXTRACT_PROMPT, timestamp: now } as unknown as Message,\n {\n role: 'user',\n content: [\n {\n type: 'text',\n text: `<memory_file bytes_left=\"${MAX_BYTES - new TextEncoder().encode(memory).length}\">\\n${memory}</memory_file>\\n\\n<conversation>\\n${transcript}\\n</conversation>`,\n },\n ],\n timestamp: now,\n },\n ],\n },\n { maxTokens: 2000, signal: runtime.signal, cacheRetention: 'none' }\n );\n runtime.signal.throwIfAborted();\n if (message.stopReason === 'error') return done({ saved: 0, skipped: 'model error' });\n const saved = await save(host, place, proposals(textOf(message as Message)), context);\n return done({ saved, skipped: null });\n },\n },\n abort: (_task, runtime, context) =>\n runtime.commit(async (tx) => {\n delete (await tx.doc(ExtractDoc)).pending[String(runtime.conversationId)];\n return { status: 'terminal', outcome: { status: 'aborted' } };\n }, context),\n });\n}\n\nexport type Schedule = (conversation: number, tail: number, context: Context) => Promise<boolean>;\n\nexport function compactionHook(schedule: Schedule) {\n return hook(CompactionTask, {\n beforeCompact(compaction, api) {\n const tail = compaction.entries.reduce((max, entry) => Math.max(max, entry.id), 0);\n void schedule(api.conversationId, tail, BACKGROUND_CONTEXT).catch(() => false);\n return undefined;\n },\n });\n}\n\nexport function scheduler(\n ready: Promise<Host>,\n task: ReturnType<typeof extractTask>,\n settings: (context: Context) => Promise<ExtractSettings>\n): Schedule {\n return async (conversation, tail, context) => {\n const host = await ready;\n const chosen = await settings(context);\n const who = whoIs(host.agents.state(), conversation);\n if (who.kind !== 'cone') return false;\n return host.harness.commit(async (tx) => {\n const doc = await tx.doc(ExtractDoc);\n const key = String(conversation);\n const after = doc.cursors[key] ?? 0;\n if (doc.pending[key] !== undefined || tail <= after) return false;\n doc.pending[key] = (await tx.createTask(\n task,\n { cone: who.id, after, tail, model: chosen.model },\n {\n ownership: { kind: 'conversation' },\n conversationId: conversation as ConversationId,\n background: true,\n }\n )) as number;\n return true;\n }, context);\n };\n}\n\nexport function settingsReader(ready: Promise<Host>) {\n return async (context: Context): Promise<ExtractSettings> => {\n const host = await ready;\n const read = await host.env.readTextFile(`${host.home}/.pi/agent/settings.json`, context);\n return extractSettings(read.ok ? read.value : undefined);\n };\n}\n\nexport function idleWatch(\n host: Pick<Host, 'agents' | 'harness'>,\n schedule: Schedule,\n settings: (context: Context) => Promise<ExtractSettings>,\n everyMs: number,\n context: Context,\n now: () => number = Date.now\n): () => void {\n const seen = new Map<number, { tail: number; since: number }>();\n const check = async () => {\n const { idleMinutes, extract } = await settings(context);\n if (!extract || idleMinutes === 0) return;\n for (const cone of Object.values(host.agents.state().cones)) {\n const conversation = await host.harness.conversation(\n cone.conversation as ConversationId,\n context\n );\n const view = await conversation?.context(context);\n const tail = view?.entries.at(-1)?.id ?? 0;\n const last = seen.get(cone.conversation);\n if (!last || last.tail !== tail) {\n seen.set(cone.conversation, { tail, since: now() });\n continue;\n }\n if (now() - last.since >= idleMinutes * 60_000)\n await schedule(cone.conversation, tail, context);\n }\n };\n let running = false;\n const timer = setInterval(() => {\n if (running) return;\n running = true;\n void check()\n .catch(() => undefined)\n .finally(() => {\n running = false;\n });\n }, everyMs);\n return () => clearInterval(timer);\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -1,29 +1,53 @@
1
1
  import {
2
2
  defineExtension
3
3
  } from "@earendil-works/pi-durable";
4
+ import { compactionHook, extractTask, idleWatch, scheduler, settingsReader } from "./extract.js";
4
5
  import { contextSection, memorySection, memoryWriteTool } from "./prompt.js";
5
6
  import { attachMemory } from "./runtime.js";
6
7
  import { MEMORY_WRITE, whoIs } from "./prompt.js";
7
8
  import { USAGE } from "./runtime.js";
9
+ const IDLE_CHECK_MS = 6e4;
8
10
  function setupMemory(registry) {
9
11
  let connect = () => void 0;
10
12
  const ready = new Promise((resolve) => {
11
13
  connect = resolve;
12
14
  });
13
15
  const tool = memoryWriteTool(ready);
16
+ const settings = settingsReader(ready);
17
+ const task = extractTask(ready, settings);
18
+ const schedule = scheduler(ready, task, settings);
14
19
  const extension = defineExtension({
15
20
  name: "slicc-memory",
16
21
  sections: [contextSection(ready), memorySection(ready)],
17
- tools: [tool]
22
+ tools: [tool],
23
+ tasks: [task],
24
+ hooks: [compactionHook(schedule)]
18
25
  });
19
26
  registry.install(extension);
20
27
  return {
21
28
  extension,
22
29
  tools: [tool],
23
- attach: (options, context) => attachMemory(options, connect, context)
30
+ async attach(options, context) {
31
+ const runtime = await attachMemory(options, connect, context);
32
+ const stop = idleWatch(
33
+ await ready,
34
+ schedule,
35
+ settings,
36
+ options.idleCheckMs ?? IDLE_CHECK_MS,
37
+ context
38
+ );
39
+ return {
40
+ ...runtime,
41
+ async close(using) {
42
+ stop();
43
+ await runtime.close(using);
44
+ }
45
+ };
46
+ }
24
47
  };
25
48
  }
26
49
  export {
50
+ IDLE_CHECK_MS,
27
51
  MEMORY_WRITE,
28
52
  USAGE,
29
53
  setupMemory,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/memory/index.ts"],
4
- "sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport {\n defineExtension,\n type Extension,\n type Registry,\n type ToolRegistration,\n} from '@earendil-works/pi-durable';\nimport { contextSection, type Host, memorySection, memoryWriteTool } from './prompt.ts';\nimport { attachMemory, type MemoryAttach, type MemoryRuntime } from './runtime.ts';\n\nexport { MEMORY_WRITE, whoIs } from './prompt.ts';\nexport type { MemoryAttach, MemoryDraft, MemoryRuntime } from './runtime.ts';\nexport { USAGE } from './runtime.ts';\n\nexport interface MemorySetup {\n extension: Extension;\n tools: readonly ToolRegistration[];\n attach(options: MemoryAttach, context: Context): Promise<MemoryRuntime>;\n}\n\nexport function setupMemory(registry: Registry): MemorySetup {\n let connect: (host: Host) => void = () => undefined;\n const ready = new Promise<Host>((resolve) => {\n connect = resolve;\n });\n const tool = memoryWriteTool(ready);\n const extension = defineExtension({\n name: 'slicc-memory',\n sections: [contextSection(ready), memorySection(ready)],\n tools: [tool],\n });\n registry.install(extension);\n return {\n extension,\n tools: [tool],\n attach: (options, context) => attachMemory(options, connect, context),\n };\n}\n"],
5
- "mappings": "AACA;AAAA,EACE;AAAA,OAIK;AACP,SAAS,gBAA2B,eAAe,uBAAuB;AAC1E,SAAS,oBAA2D;AAEpE,SAAS,cAAc,aAAa;AAEpC,SAAS,aAAa;AAQf,SAAS,YAAY,UAAiC;AAC3D,MAAI,UAAgC,MAAM;AAC1C,QAAM,QAAQ,IAAI,QAAc,CAAC,YAAY;AAC3C,cAAU;AAAA,EACZ,CAAC;AACD,QAAM,OAAO,gBAAgB,KAAK;AAClC,QAAM,YAAY,gBAAgB;AAAA,IAChC,MAAM;AAAA,IACN,UAAU,CAAC,eAAe,KAAK,GAAG,cAAc,KAAK,CAAC;AAAA,IACtD,OAAO,CAAC,IAAI;AAAA,EACd,CAAC;AACD,WAAS,QAAQ,SAAS;AAC1B,SAAO;AAAA,IACL;AAAA,IACA,OAAO,CAAC,IAAI;AAAA,IACZ,QAAQ,CAAC,SAAS,YAAY,aAAa,SAAS,SAAS,OAAO;AAAA,EACtE;AACF;",
4
+ "sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport {\n defineExtension,\n type Extension,\n type Registry,\n type ToolRegistration,\n} from '@earendil-works/pi-durable';\nimport { compactionHook, extractTask, idleWatch, scheduler, settingsReader } from './extract.ts';\nimport { contextSection, type Host, memorySection, memoryWriteTool } from './prompt.ts';\nimport { attachMemory, type MemoryAttach, type MemoryRuntime } from './runtime.ts';\n\nexport { MEMORY_WRITE, whoIs } from './prompt.ts';\nexport type { MemoryAttach, MemoryDraft, MemoryRuntime } from './runtime.ts';\nexport { USAGE } from './runtime.ts';\n\nexport const IDLE_CHECK_MS = 60_000;\n\nexport interface MemorySetup {\n extension: Extension;\n tools: readonly ToolRegistration[];\n attach(options: MemoryAttach, context: Context): Promise<MemoryRuntime>;\n}\n\nexport function setupMemory(registry: Registry): MemorySetup {\n let connect: (host: Host) => void = () => undefined;\n const ready = new Promise<Host>((resolve) => {\n connect = resolve;\n });\n const tool = memoryWriteTool(ready);\n const settings = settingsReader(ready);\n const task = extractTask(ready, settings);\n const schedule = scheduler(ready, task, settings);\n const extension = defineExtension({\n name: 'slicc-memory',\n sections: [contextSection(ready), memorySection(ready)],\n tools: [tool],\n tasks: [task],\n hooks: [compactionHook(schedule)],\n });\n registry.install(extension);\n return {\n extension,\n tools: [tool],\n async attach(options, context) {\n const runtime = await attachMemory(options, connect, context);\n const stop = idleWatch(\n await ready,\n schedule,\n settings,\n options.idleCheckMs ?? IDLE_CHECK_MS,\n context\n );\n return {\n ...runtime,\n async close(using) {\n stop();\n await runtime.close(using);\n },\n };\n },\n };\n}\n"],
5
+ "mappings": "AACA;AAAA,EACE;AAAA,OAIK;AACP,SAAS,gBAAgB,aAAa,WAAW,WAAW,sBAAsB;AAClF,SAAS,gBAA2B,eAAe,uBAAuB;AAC1E,SAAS,oBAA2D;AAEpE,SAAS,cAAc,aAAa;AAEpC,SAAS,aAAa;AAEf,MAAM,gBAAgB;AAQtB,SAAS,YAAY,UAAiC;AAC3D,MAAI,UAAgC,MAAM;AAC1C,QAAM,QAAQ,IAAI,QAAc,CAAC,YAAY;AAC3C,cAAU;AAAA,EACZ,CAAC;AACD,QAAM,OAAO,gBAAgB,KAAK;AAClC,QAAM,WAAW,eAAe,KAAK;AACrC,QAAM,OAAO,YAAY,OAAO,QAAQ;AACxC,QAAM,WAAW,UAAU,OAAO,MAAM,QAAQ;AAChD,QAAM,YAAY,gBAAgB;AAAA,IAChC,MAAM;AAAA,IACN,UAAU,CAAC,eAAe,KAAK,GAAG,cAAc,KAAK,CAAC;AAAA,IACtD,OAAO,CAAC,IAAI;AAAA,IACZ,OAAO,CAAC,IAAI;AAAA,IACZ,OAAO,CAAC,eAAe,QAAQ,CAAC;AAAA,EAClC,CAAC;AACD,WAAS,QAAQ,SAAS;AAC1B,SAAO;AAAA,IACL;AAAA,IACA,OAAO,CAAC,IAAI;AAAA,IACZ,MAAM,OAAO,SAAS,SAAS;AAC7B,YAAM,UAAU,MAAM,aAAa,SAAS,SAAS,OAAO;AAC5D,YAAM,OAAO;AAAA,QACX,MAAM;AAAA,QACN;AAAA,QACA;AAAA,QACA,QAAQ,eAAe;AAAA,QACvB;AAAA,MACF;AACA,aAAO;AAAA,QACL,GAAG;AAAA,QACH,MAAM,MAAM,OAAO;AACjB,eAAK;AACL,gBAAM,QAAQ,MAAM,KAAK;AAAA,QAC3B;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;",
6
6
  "names": []
7
7
  }