@ai-ecoverse/slicc-agent 7.7.0 → 7.7.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.
package/README.md CHANGED
@@ -426,7 +426,7 @@ The `memory` section, read before every request, holds the first 200 lines (at m
426
426
 
427
427
  **`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.
428
428
 
429
- **Extraction.** Durable compacts a long conversation into a summary; before it does, a `beforeCompact` hook (which never declines or replaces the summary) schedules a `slicc.memory-extract` task for the cone. A cone that has been idle for `memory.idleMinutes` (30 by default, `0` turns the idle trigger off) gets the same task. Each cone's conversation has one background `slicc.memory-idle` task (listed in the session document `slicc.memory-idle`), which pi durable keeps asleep between checks: it wakes `idleMinutes` after the conversation last changed (the newest entry's message time, or when it noticed the change), shows in the monitor as `memory idle watch`, schedules the extraction if it hasn't changed since, and sleeps again. The sleep is stored, so the idle time counts across reloads and while no worker runs, and settings are read at each wake. A cone that goes away (frozen, or its conversation replaced by New conversation) ends its task; a new cone gets one. The task runs only when there is something new since the last extraction (a cursor per conversation in the session document `slicc.memory-extract`), makes one model call, with the cone's model unless `memory.extractModel` names a cheaper one (`provider/model`), and writes the proposed entries through `memory_write`'s writer into the cone's memory file, within its 16 KB. The transcript it sends is redacted first, its prompt forbids secrets, and the writer redacts again. `"memory": { "extract": false }` in `~/.pi/agent/settings.json` turns extraction off. v6's curator scoop, three-way merge, curation ledger and dreamer aren't ported.
429
+ **Extraction.** Durable compacts a long conversation into a summary; before it does, a `beforeCompact` hook (which never declines or replaces the summary) schedules a `slicc.memory-extract` task for the cone. A cone that has been idle for `memory.idleMinutes` (30 by default, `0` turns the idle trigger off) gets the same task. Each cone's conversation has one background `slicc.memory-idle` task (listed in the session document `slicc.memory-idle`), which pi durable keeps asleep between checks: it wakes `idleMinutes` after the newest message time among the entries it hasn't seen yet, so a turn that ends while it sleeps counts from that turn, and entries without a message (a skipped lick, a sprinkle note, an approval verdict) don't push the time back, shows in the monitor as `memory idle watch`, schedules the extraction if it hasn't changed since, and sleeps again. The sleep is stored, so the idle time counts across reloads and while no worker runs, and settings are read at each wake. A cone that goes away (frozen, or its conversation replaced by New conversation) ends its task; a new cone gets one. The task runs only when there is something new since the last extraction (a cursor per conversation in the session document `slicc.memory-extract`), makes one model call, with the cone's model unless `memory.extractModel` names a cheaper one (`provider/model`), and writes the proposed entries through `memory_write`'s writer into the cone's memory file, within its 16 KB. The transcript it sends is redacted first, its prompt forbids secrets, and the writer redacts again. `"memory": { "extract": false }` in `~/.pi/agent/settings.json` turns extraction off. v6's curator scoop, three-way merge, curation ledger and dreamer aren't ported.
430
430
 
431
431
  **The gelatiere.** SLICC's resident advisor, as in v6, is a scoop with the built-in `gelatiere` role. That role has its own memory (`memory: { scope: user, path: gelatiere }`), so it keeps its notes there.
432
432
  - `gelatiere init` starts the scoop in the first cone, writes `~/.pi/agent/GELATIERE.md` (its procedure, which the user can edit) unless it exists, and installs one job `# name: gelatiere` / `0 3 * * * agent send scoop:<handle> --lick=cron 'Nightly pass: …'` in the scoop's own crontab (root's without kernel users), replacing an older one. At start, a gelatiere job an earlier version installed without `--lick=cron` gets it, as migrated jobs do. `gelatiere status` shows its schedule from there.
@@ -216,6 +216,17 @@ const IdleDoc = defineDoc({
216
216
  scope: "session",
217
217
  initial: () => ({ tasks: {} })
218
218
  });
219
+ function changedAt(entries, seen, now) {
220
+ let newest = Number.NEGATIVE_INFINITY;
221
+ for (const entry of entries) {
222
+ if (entry.id <= seen.tail) continue;
223
+ for (const message of entry.model ?? [])
224
+ if (typeof message.timestamp === "number")
225
+ newest = Math.max(newest, Math.min(message.timestamp, now));
226
+ }
227
+ if (newest === Number.NEGATIVE_INFINITY) return seen.tail < 0 ? now : seen.since;
228
+ return Math.max(seen.since, newest);
229
+ }
219
230
  function idleTask(ready, schedule, settings) {
220
231
  return defineTask({
221
232
  name: IDLE_TASK,
@@ -238,18 +249,14 @@ function idleTask(ready, schedule, settings) {
238
249
  const idleMs = (on ? idleMinutes : DEFAULT_IDLE_MINUTES) * 6e4;
239
250
  const now = runtime.now();
240
251
  const view = await runtime.context(conversation, context);
241
- const last = view.entries.at(-1);
242
- const tail = last?.id ?? 0;
252
+ const tail = view.entries.at(-1)?.id ?? 0;
243
253
  const seen = task.state.checkpoint;
244
- if (tail !== seen.tail) {
245
- const stamp = last?.model?.at(-1)?.timestamp;
246
- const since = typeof stamp === "number" && stamp <= now ? stamp : now;
247
- return runtime.sleepUntil(since + idleMs, { phase: "watch", tail, since }, context);
248
- }
249
- const due = seen.since + idleMs;
250
- if (now < due) return runtime.sleepUntil(due, seen, context);
254
+ const since = tail === seen.tail ? seen.since : changedAt(view.entries, seen, now);
255
+ const next = { phase: "watch", tail, since };
256
+ const due = since + idleMs;
257
+ if (now < due) return runtime.sleepUntil(due, next, context);
251
258
  if (on) await schedule(conversation, tail, context, who.id);
252
- return runtime.sleepUntil(now + idleMs, seen, context);
259
+ return runtime.sleepUntil(now + idleMs, next, context);
253
260
  }
254
261
  },
255
262
  abort: (task, runtime, context) => runtime.commit(async (tx) => {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
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 type TaskId,\n} from '@earendil-works/pi-durable';\nimport { boundedDeltas } from '../checkpoints.ts';\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 checkpointWhen: boundedDeltas,\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, {\n at: tail as EntryId,\n });\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 = (\n conversation: number,\n tail: number,\n context: Context,\n cone?: string\n) => 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, cone) => {\n const host = await ready;\n const chosen = await settings(context);\n const who = cone\n ? { kind: 'cone' as const, id: cone }\n : 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 dir = host.agentDir ?? `${host.home}/.pi/agent`;\n const read = await host.env.readTextFile(`${dir}/settings.json`, context);\n return extractSettings(read.ok ? read.value : undefined);\n };\n}\n\nexport const IDLE_TASK = 'slicc.memory-idle';\n\nexport const IdleDoc = defineDoc<{ tasks: Record<string, number> }>({\n kind: 'slicc.memory-idle',\n checkpointWhen: boundedDeltas,\n version: 1,\n scope: 'session',\n initial: () => ({ tasks: {} }),\n});\n\ntype IdleState = { phase: 'watch'; tail: number; since: number };\n\nexport function idleTask(\n ready: Promise<Host>,\n schedule: Schedule,\n settings: (context: Context) => Promise<ExtractSettings>\n) {\n return defineTask<{ cone: string }, IdleState, null>({\n name: IDLE_TASK,\n version: 1,\n initial: () => ({ phase: 'watch', tail: -1, since: 0 }),\n phases: {\n async watch(task, runtime, context) {\n const host = await ready;\n const conversation = runtime.conversationId;\n const key = String(conversation);\n const who = whoIs(host.agents.state(), conversation);\n if (who.kind !== 'cone')\n return runtime.commit(async (tx) => {\n const doc = await tx.doc(IdleDoc);\n if (doc.tasks[key] === Number(task.id)) delete doc.tasks[key];\n return { status: 'terminal', outcome: { status: 'completed', result: null } };\n }, context);\n const { extract, idleMinutes } = await settings(context);\n const on = extract && idleMinutes > 0;\n const idleMs = (on ? idleMinutes : DEFAULT_IDLE_MINUTES) * 60_000;\n const now = runtime.now();\n const view = await runtime.context(conversation, context);\n const last = view.entries.at(-1);\n const tail = last?.id ?? 0;\n const seen = task.state.checkpoint;\n if (tail !== seen.tail) {\n const stamp = last?.model?.at(-1)?.timestamp;\n const since = typeof stamp === 'number' && stamp <= now ? stamp : now;\n return runtime.sleepUntil(since + idleMs, { phase: 'watch', tail, since }, context);\n }\n const due = seen.since + idleMs;\n if (now < due) return runtime.sleepUntil(due, seen, context);\n if (on) await schedule(conversation, tail, context, who.id);\n return runtime.sleepUntil(now + idleMs, seen, context);\n },\n },\n abort: (task, runtime, context) =>\n runtime.commit(async (tx) => {\n const doc = await tx.doc(IdleDoc);\n const key = String(runtime.conversationId);\n if (doc.tasks[key] === Number(task.id)) delete doc.tasks[key];\n return { status: 'terminal', outcome: { status: 'aborted' } };\n }, context),\n });\n}\n\nexport function idleWatch(\n host: Pick<Host, 'agents' | 'harness'>,\n task: ReturnType<typeof idleTask>,\n context: Context\n): () => void {\n let stopped = false;\n let line = Promise.resolve();\n const ensure = () => {\n line = line\n .then(async () => {\n if (stopped) return;\n await host.harness.commit(async (tx) => {\n const doc = await tx.doc(IdleDoc);\n for (const [id, cone] of Object.entries(host.agents.state().cones)) {\n const key = String(cone.conversation);\n const known = doc.tasks[key];\n const record = known === undefined ? undefined : await tx.task(known as TaskId);\n if (record && record.state.status !== 'terminal') continue;\n doc.tasks[key] = (await tx.createTask(\n task,\n { cone: id },\n {\n ownership: { kind: 'conversation' },\n conversationId: cone.conversation as ConversationId,\n background: true,\n }\n )) as number;\n }\n }, context);\n })\n .catch(() => undefined);\n };\n const off = host.agents.onChange(ensure);\n ensure();\n return () => {\n stopped = true;\n off();\n };\n}\n"],
5
- "mappings": "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",
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 type TaskId,\n} from '@earendil-works/pi-durable';\nimport { boundedDeltas } from '../checkpoints.ts';\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 checkpointWhen: boundedDeltas,\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, {\n at: tail as EntryId,\n });\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 = (\n conversation: number,\n tail: number,\n context: Context,\n cone?: string\n) => 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, cone) => {\n const host = await ready;\n const chosen = await settings(context);\n const who = cone\n ? { kind: 'cone' as const, id: cone }\n : 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 dir = host.agentDir ?? `${host.home}/.pi/agent`;\n const read = await host.env.readTextFile(`${dir}/settings.json`, context);\n return extractSettings(read.ok ? read.value : undefined);\n };\n}\n\nexport const IDLE_TASK = 'slicc.memory-idle';\n\nexport const IdleDoc = defineDoc<{ tasks: Record<string, number> }>({\n kind: 'slicc.memory-idle',\n checkpointWhen: boundedDeltas,\n version: 1,\n scope: 'session',\n initial: () => ({ tasks: {} }),\n});\n\ntype IdleState = { phase: 'watch'; tail: number; since: number };\n\nfunction changedAt(\n entries: readonly { id: number; model?: readonly { timestamp?: unknown }[] }[],\n seen: IdleState,\n now: number\n): number {\n let newest = Number.NEGATIVE_INFINITY;\n for (const entry of entries) {\n if (entry.id <= seen.tail) continue;\n for (const message of entry.model ?? [])\n if (typeof message.timestamp === 'number')\n newest = Math.max(newest, Math.min(message.timestamp, now));\n }\n if (newest === Number.NEGATIVE_INFINITY) return seen.tail < 0 ? now : seen.since;\n return Math.max(seen.since, newest);\n}\n\nexport function idleTask(\n ready: Promise<Host>,\n schedule: Schedule,\n settings: (context: Context) => Promise<ExtractSettings>\n) {\n return defineTask<{ cone: string }, IdleState, null>({\n name: IDLE_TASK,\n version: 1,\n initial: () => ({ phase: 'watch', tail: -1, since: 0 }),\n phases: {\n async watch(task, runtime, context) {\n const host = await ready;\n const conversation = runtime.conversationId;\n const key = String(conversation);\n const who = whoIs(host.agents.state(), conversation);\n if (who.kind !== 'cone')\n return runtime.commit(async (tx) => {\n const doc = await tx.doc(IdleDoc);\n if (doc.tasks[key] === Number(task.id)) delete doc.tasks[key];\n return { status: 'terminal', outcome: { status: 'completed', result: null } };\n }, context);\n const { extract, idleMinutes } = await settings(context);\n const on = extract && idleMinutes > 0;\n const idleMs = (on ? idleMinutes : DEFAULT_IDLE_MINUTES) * 60_000;\n const now = runtime.now();\n const view = await runtime.context(conversation, context);\n const tail = view.entries.at(-1)?.id ?? 0;\n const seen = task.state.checkpoint;\n const since = tail === seen.tail ? seen.since : changedAt(view.entries, seen, now);\n const next: IdleState = { phase: 'watch', tail, since };\n const due = since + idleMs;\n if (now < due) return runtime.sleepUntil(due, next, context);\n if (on) await schedule(conversation, tail, context, who.id);\n return runtime.sleepUntil(now + idleMs, next, context);\n },\n },\n abort: (task, runtime, context) =>\n runtime.commit(async (tx) => {\n const doc = await tx.doc(IdleDoc);\n const key = String(runtime.conversationId);\n if (doc.tasks[key] === Number(task.id)) delete doc.tasks[key];\n return { status: 'terminal', outcome: { status: 'aborted' } };\n }, context),\n });\n}\n\nexport function idleWatch(\n host: Pick<Host, 'agents' | 'harness'>,\n task: ReturnType<typeof idleTask>,\n context: Context\n): () => void {\n let stopped = false;\n let line = Promise.resolve();\n const ensure = () => {\n line = line\n .then(async () => {\n if (stopped) return;\n await host.harness.commit(async (tx) => {\n const doc = await tx.doc(IdleDoc);\n for (const [id, cone] of Object.entries(host.agents.state().cones)) {\n const key = String(cone.conversation);\n const known = doc.tasks[key];\n const record = known === undefined ? undefined : await tx.task(known as TaskId);\n if (record && record.state.status !== 'terminal') continue;\n doc.tasks[key] = (await tx.createTask(\n task,\n { cone: id },\n {\n ownership: { kind: 'conversation' },\n conversationId: cone.conversation as ConversationId,\n background: true,\n }\n )) as number;\n }\n }, context);\n })\n .catch(() => undefined);\n };\n const off = host.agents.onChange(ensure);\n ensure();\n return () => {\n stopped = true;\n off();\n };\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-ecoverse/slicc-agent",
3
- "version": "7.7.0",
3
+ "version": "7.7.1",
4
4
  "description": "SLICC's agent core on pi durable: an agent worker, session storage, remoting and pi extensions",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -65,7 +65,7 @@
65
65
  "release": "semantic-release"
66
66
  },
67
67
  "devDependencies": {
68
- "@ai-ecoverse/slicc-kernel": "1.153.0",
68
+ "@ai-ecoverse/slicc-kernel": "1.154.0",
69
69
  "@ai-ecoverse/slicc-scrub": "1.1.0",
70
70
  "@ai-ecoverse/slicc-shared-web": "2.12.0",
71
71
  "@ai-ecoverse/slicc-spectrum": "1.107.1",