@ai-ecoverse/slicc-agent 6.19.14 → 7.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/README.md +11 -29
  2. package/dist/agent.js +10 -1
  3. package/dist/agent.js.map +2 -2
  4. package/dist/agents.js +2 -0
  5. package/dist/agents.js.map +2 -2
  6. package/dist/approvals/policy.js +2 -0
  7. package/dist/approvals/policy.js.map +2 -2
  8. package/dist/approvals/record.js +2 -0
  9. package/dist/approvals/record.js.map +2 -2
  10. package/dist/checkpoints.js +5 -0
  11. package/dist/checkpoints.js.map +7 -0
  12. package/dist/codemode/index.js +2 -0
  13. package/dist/codemode/index.js.map +2 -2
  14. package/dist/cone.js +2 -0
  15. package/dist/cone.js.map +2 -2
  16. package/dist/guests/guard.js +3 -0
  17. package/dist/guests/guard.js.map +2 -2
  18. package/dist/guests/review.js +4 -1
  19. package/dist/guests/review.js.map +2 -2
  20. package/dist/guests/scoop.js +3 -0
  21. package/dist/guests/scoop.js.map +2 -2
  22. package/dist/guests/seats.js +2 -0
  23. package/dist/guests/seats.js.map +2 -2
  24. package/dist/guests/tags.js +2 -0
  25. package/dist/guests/tags.js.map +2 -2
  26. package/dist/licks/boot.js +2 -0
  27. package/dist/licks/boot.js.map +2 -2
  28. package/dist/licks/licks.js +2 -0
  29. package/dist/licks/licks.js.map +2 -2
  30. package/dist/mcp/allow.js +2 -0
  31. package/dist/mcp/allow.js.map +2 -2
  32. package/dist/memory/extract.js +2 -0
  33. package/dist/memory/extract.js.map +2 -2
  34. package/dist/monitor/index.js +3 -1
  35. package/dist/monitor/index.js.map +2 -2
  36. package/dist/pause.js +26 -0
  37. package/dist/pause.js.map +7 -0
  38. package/dist/preferred.js +6 -6
  39. package/dist/preferred.js.map +1 -1
  40. package/dist/scoops/extension.js +2 -1
  41. package/dist/scoops/extension.js.map +2 -2
  42. package/dist/scoops/service.js +2 -0
  43. package/dist/scoops/service.js.map +2 -2
  44. package/dist/secrets/legacy.js +2 -0
  45. package/dist/secrets/legacy.js.map +2 -2
  46. package/dist/selection.js +2 -0
  47. package/dist/selection.js.map +2 -2
  48. package/dist/services.js.map +2 -2
  49. package/dist/spectrum/monitor-port.js +10 -2
  50. package/dist/spectrum/monitor-port.js.map +2 -2
  51. package/dist/sprinkles/sprinkles.js +2 -0
  52. package/dist/sprinkles/sprinkles.js.map +2 -2
  53. package/dist/titles/labels.js +2 -0
  54. package/dist/titles/labels.js.map +2 -2
  55. package/dist/types/agent.d.ts +4 -0
  56. package/dist/types/checkpoints.d.ts +1 -0
  57. package/dist/types/pause.d.ts +6 -0
  58. package/dist/types/services.d.ts +4 -2
  59. package/dist/workflow/run.js +5 -0
  60. package/dist/workflow/run.js.map +2 -2
  61. package/package.json +12 -12
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/licks/licks.ts"],
4
- "sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport {\n type Conversation,\n defineDoc,\n type Harness,\n type SubmissionId,\n} from '@earendil-works/pi-durable';\nimport { distinctTaints, type InheritedTaint, LickTaintDoc } from '../guests/guard.ts';\nimport { scrubTexts } from '../secrets/scrub.ts';\nimport {\n clip,\n formatLick,\n type Lick,\n type LickAction,\n type LickChannel,\n type LickSeverity,\n type LickTarget,\n lickId,\n} from './lick.ts';\nimport { entryLick, LICK_STATE_KIND, type LickDecision, lickDecisions } from './state.ts';\nimport { skipEntry, skips, type Triage, type Verdict } from './triage.ts';\n\nexport interface LickEvent {\n channel: LickChannel;\n source: string;\n title: string;\n text: string;\n body?: string;\n items?: string[];\n count?: number;\n target: LickTarget;\n coalesce?: boolean;\n severity?: LickSeverity;\n eventId?: string;\n at?: number;\n triage?: string;\n summary?: string;\n report?: string;\n taints?: readonly InheritedTaint[];\n}\n\nfunction card(event: LickEvent): Pick<Lick, 'summary' | 'report'> {\n return {\n ...(event.summary ? { summary: event.summary } : {}),\n ...(event.report ? { report: event.report } : {}),\n };\n}\n\ntype Pending = {\n lick: Lick;\n items: string[];\n body: string | null;\n taints?: InheritedTaint[];\n};\n\nfunction tainted(taints: readonly InheritedTaint[]): Pick<Pending, 'taints'> {\n return taints.length ? { taints: distinctTaints(taints) } : {};\n}\n\ntype Queue = {\n submission: number | null;\n pending: Pending | null;\n};\n\ntype OutboxState = {\n queues: Record<string, Queue>;\n events: Record<string, string[]>;\n};\n\nexport const LicksOutbox = defineDoc<OutboxState>({\n kind: 'slicc.licks',\n version: 1,\n scope: 'session',\n initial: () => ({ queues: {}, events: {} }),\n});\n\nexport interface LickHandler {\n confirm?(lick: Lick, reason: string | undefined, context: Context): Promise<string | undefined>;\n dismiss?(lick: Lick, reason: string | undefined, context: Context): Promise<string | undefined>;\n}\n\nexport interface LickFound {\n lick: Lick;\n decision: LickDecision | undefined;\n}\n\nexport interface Licks {\n deliver(event: LickEvent, context: Context): Promise<void>;\n flush(context: Context): Promise<void>;\n find(conversation: Conversation, id: string, context: Context): Promise<LickFound | undefined>;\n decide(\n conversation: Conversation,\n id: string,\n action: LickAction,\n reason: string | undefined,\n context: Context\n ): Promise<string>;\n handle(channel: LickChannel, handler: LickHandler): void;\n pending(context: Context): Promise<number>;\n forget(target: LickTarget, channel: LickChannel, source: string, context: Context): Promise<void>;\n guard(check: (target: LickTarget) => Blocked | undefined): void;\n triage(judge: Triage): void;\n}\n\nexport type Blocked = { id: string; name: string; at: number };\n\nexport function frozenNotice(event: LickEvent, blocked: Blocked): LickEvent {\n return {\n channel: event.channel,\n source: `frozen:${event.source}`,\n title: `${blocked.name} is frozen`,\n text: `${event.title} was skipped`,\n body: `${event.target} belongs to ${blocked.name}, which is frozen (${blocked.id}). Thaw it in the Freezer to get its licks again.`,\n target: 'cone',\n severity: 'warn',\n eventId: `${blocked.id}|${event.target}|${event.channel}|${event.source}|${blocked.at}`,\n ...(event.taints ? { taints: event.taints } : {}),\n };\n}\n\nexport interface LicksHost {\n harness: Harness;\n resolve(target: LickTarget, channel: LickChannel, context: Context): Promise<Conversation>;\n runnable?(conversation: Conversation, context: Context): Promise<boolean>;\n}\n\nexport const MAX_ITEMS = 50;\nexport const MAX_EVENTS = 64;\n\nexport function render(pending: Pending): Lick {\n const shown = pending.items.slice(0, MAX_ITEMS);\n const more = pending.items.length - shown.length;\n const lines = [\n ...(pending.body ? [pending.body] : []),\n ...shown,\n ...(more > 0 ? [`\u2026 and ${more} more`] : []),\n ];\n const { body: _, ...lick } = pending.lick;\n return lines.length ? { ...lick, body: lines.join('\\n') } : lick;\n}\n\nconst RANK: Record<LickSeverity, number> = { warn: 1, error: 2 };\n\nexport function strongest(\n a: LickSeverity | undefined,\n b: LickSeverity | undefined\n): LickSeverity | undefined {\n if (!a) return b;\n if (!b) return a;\n return RANK[b] > RANK[a] ? b : a;\n}\n\nfunction merge(pending: Pending, event: LickEvent, now: number): Pending {\n const items = [...pending.items];\n for (const item of event.items ?? []) if (!items.includes(item)) items.push(item);\n const { severity: _, summary: __, report: ___, ...lick } = pending.lick;\n const severity = strongest(pending.lick.severity, event.severity);\n const taints = tainted([...(pending.taints ?? []), ...(event.taints ?? [])]);\n return {\n lick: {\n ...lick,\n title: event.title,\n text: event.text,\n count: pending.lick.count + (event.count ?? 1),\n at: event.at ?? now,\n ...(severity ? { severity } : {}),\n ...card(event),\n },\n items,\n body: event.body ?? pending.body,\n ...taints,\n };\n}\n\nfunction fresh(event: LickEvent, now: number, listed: LickAction[] | undefined): Pending {\n return {\n lick: {\n id: lickId(),\n channel: event.channel,\n source: event.source,\n title: event.title,\n text: event.text,\n count: event.count ?? 1,\n at: now,\n ...(listed ? { actions: listed } : {}),\n ...(event.severity ? { severity: event.severity } : {}),\n ...card(event),\n },\n items: [...new Set(event.items ?? [])],\n body: event.body ?? null,\n ...tainted(event.taints ?? []),\n };\n}\n\nfunction keyOf(event: LickEvent): string {\n const base = `${event.target}|${event.channel}|${event.source}`;\n return event.coalesce === false ? `${base}|${event.eventId ?? lickId()}` : base;\n}\n\nexport async function scrubbedLick(event: LickEvent): Promise<LickEvent> {\n const fields = (['title', 'text', 'body', 'triage', 'summary', 'report'] as const).filter(\n (key) => event[key] !== undefined\n );\n const items = event.items ?? [];\n const scrubbed = await scrubTexts([...fields.map((key) => event[key] as string), ...items]);\n const out: LickEvent = { ...event };\n fields.forEach((key, at) => {\n out[key] = scrubbed[at] as string;\n });\n if (event.items) out.items = scrubbed.slice(fields.length);\n if (out.summary !== undefined) out.summary = clip(out.summary);\n return out;\n}\n\nasync function marked(harness: Harness, pending: Pending, id: string, context: Context) {\n const taints = pending.taints;\n if (!taints) return;\n await harness.commit(async (tx) => {\n (await tx.doc(LickTaintDoc)).licks[id] = taints;\n }, context);\n}\n\nexport function createLicks(host: Promise<LicksHost>): Licks {\n const handlers = new Map<LickChannel, LickHandler>();\n let lock: Promise<unknown> = Promise.resolve();\n const serial = <T>(operation: () => Promise<T>): Promise<T> => {\n const run = lock.then(operation, operation);\n lock = run.catch(() => undefined);\n return run;\n };\n\n const actions = (channel: LickChannel): LickAction[] | undefined => {\n const handler = handlers.get(channel);\n const list: LickAction[] = [];\n if (handler?.confirm) list.push('confirm');\n if (handler?.dismiss) list.push('dismiss');\n return list.length ? list : undefined;\n };\n\n async function previous(\n harness: Harness,\n id: number | null,\n context: Context\n ): Promise<'queued' | 'withdrawn' | 'gone'> {\n if (id === null) return 'gone';\n const submission = await harness.submission(id as SubmissionId, context);\n const record = await submission?.status(context);\n if (record?.status === 'queued') return 'queued';\n return record?.status === 'unanswered' && record.reason === 'aborted' ? 'withdrawn' : 'gone';\n }\n\n async function settled(key: string, context: Context): Promise<Queue | undefined> {\n const { harness } = await host;\n const queue = (await harness.snapshot(LicksOutbox, context))?.queues[key];\n if (!queue) return undefined;\n const before = await previous(harness, queue.submission, context);\n if (before === 'queued') return undefined;\n if (queue.pending && before === 'gone') return queue;\n await harness.commit(async (tx) => {\n const doc = await tx.doc(LicksOutbox);\n if (doc.queues[key]?.submission === queue.submission) delete doc.queues[key];\n }, context);\n return undefined;\n }\n\n async function flushKey(key: string, context: Context): Promise<void> {\n const { harness, resolve, runnable } = await host;\n const queue = await settled(key, context);\n if (!queue?.pending) return;\n const lick = render(queue.pending);\n const target = key.split('|')[0] as LickTarget;\n const conversation = await resolve(target, queue.pending.lick.channel, context);\n if (runnable && !(await runnable(conversation, context))) return;\n await marked(harness, queue.pending, lick.id, context);\n const submission = await conversation.submit(\n {\n type: 'input',\n content: formatLick(lick),\n whenBusy: 'steer',\n requestId: `lick:${lick.id}`,\n },\n context\n );\n await harness.commit(async (tx) => {\n const doc = await tx.doc(LicksOutbox);\n doc.queues[key] = { submission: submission.id, pending: null };\n }, context);\n }\n\n async function flushAll(context: Context): Promise<void> {\n const { harness } = await host;\n const keys = Object.keys((await harness.snapshot(LicksOutbox, context))?.queues ?? {});\n for (const key of keys) await flushKey(key, context);\n }\n\n async function find(\n conversation: Conversation,\n id: string,\n context: Context\n ): Promise<LickFound | undefined> {\n const { entries } = await conversation.context(context);\n const lick = entries.map(entryLick).find((candidate) => candidate?.id === id);\n if (!lick) return undefined;\n return { lick, decision: lickDecisions(entries).get(id) };\n }\n\n async function judged(event: LickEvent, context: Context): Promise<Verdict | undefined> {\n if (!judge || !event.triage) return undefined;\n return judge(event, context).catch(() => undefined);\n }\n\n async function seenBefore(event: LickEvent, context: Context): Promise<boolean> {\n if (event.eventId === undefined) return false;\n const { harness } = await host;\n const events = (await harness.snapshot(LicksOutbox, context))?.events ?? {};\n return (events[`${event.channel}|${event.source}`] ?? []).includes(event.eventId);\n }\n\n async function record(event: LickEvent, verdict: Verdict, now: number, context: Context) {\n const conversation = await (await host).resolve(event.target, event.channel, context);\n await conversation.submit(skipEntry(event, verdict, now), context);\n }\n\n let check: ((target: LickTarget) => Blocked | undefined) | undefined;\n let judge: Triage | undefined;\n return {\n guard(next) {\n check = next;\n },\n triage(next) {\n judge = next;\n },\n async deliver(raw, context) {\n const incoming = await scrubbedLick(raw);\n const blocked = check?.(incoming.target);\n const event = blocked ? frozenNotice(incoming, blocked) : incoming;\n if (judge && event.triage && (await seenBefore(event, context))) return;\n const verdict = await judged(event, context);\n const skipped = skips(verdict);\n return serial(async () => {\n const { harness } = await host;\n const key = keyOf(event);\n await settled(key, context);\n const stream = `${event.channel}|${event.source}`;\n const now = event.at ?? Date.now();\n if (skipped) {\n if (await seenBefore(event, context)) return;\n await record(event, verdict, now, context);\n }\n const accepted = await harness.commit(async (tx) => {\n const doc = await tx.doc(LicksOutbox);\n const seen = doc.events[stream] ?? [];\n if (event.eventId !== undefined) {\n if (seen.includes(event.eventId)) return false;\n doc.events[stream] = [...seen, event.eventId].slice(-MAX_EVENTS);\n }\n if (skipped) return false;\n const queue = doc.queues[key] ?? { submission: null, pending: null };\n queue.pending = queue.pending\n ? merge(queue.pending, event, now)\n : fresh(event, now, actions(event.channel));\n doc.queues[key] = queue;\n return true;\n }, context);\n if (accepted) await flushKey(key, context);\n });\n },\n flush: (context) => serial(() => flushAll(context)),\n find,\n async decide(conversation, id, action, reason, context) {\n const found = await find(conversation, id, context);\n if (!found) throw new Error(`There is no lick ${id} in this conversation.`);\n if (found.decision) throw new Error(`Lick ${id} is already ${found.decision}.`);\n if (!found.lick.actions?.includes(action))\n throw new Error(`Lick ${id} (${found.lick.channel}) has no ${action} action.`);\n const run = handlers.get(found.lick.channel)?.[action];\n if (!run) throw new Error(`Lick ${id} (${found.lick.channel}) has no ${action} action.`);\n return (await run(found.lick, reason, context)) ?? `Lick ${id} ${action}ed.`;\n },\n handle(channel, handler) {\n handlers.set(channel, handler);\n },\n forget(target, channel, source, context) {\n return serial(async () => {\n const { harness } = await host;\n const key = `${target}|${channel}|${source}`;\n await harness.commit(async (tx) => {\n const doc = await tx.doc(LicksOutbox);\n const queue = doc.queues[key];\n if (queue?.pending) queue.pending = null;\n }, context);\n });\n },\n async pending(context) {\n const { harness } = await host;\n const queues = (await harness.snapshot(LicksOutbox, context))?.queues ?? {};\n return Object.values(queues).filter((queue) => queue.submission !== null || queue.pending)\n .length;\n },\n };\n}\n\nexport { LICK_STATE_KIND };\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport {\n type Conversation,\n defineDoc,\n type Harness,\n type SubmissionId,\n} from '@earendil-works/pi-durable';\nimport { deltasOnly } from '../checkpoints.ts';\nimport { distinctTaints, type InheritedTaint, LickTaintDoc } from '../guests/guard.ts';\nimport { scrubTexts } from '../secrets/scrub.ts';\nimport {\n clip,\n formatLick,\n type Lick,\n type LickAction,\n type LickChannel,\n type LickSeverity,\n type LickTarget,\n lickId,\n} from './lick.ts';\nimport { entryLick, LICK_STATE_KIND, type LickDecision, lickDecisions } from './state.ts';\nimport { skipEntry, skips, type Triage, type Verdict } from './triage.ts';\n\nexport interface LickEvent {\n channel: LickChannel;\n source: string;\n title: string;\n text: string;\n body?: string;\n items?: string[];\n count?: number;\n target: LickTarget;\n coalesce?: boolean;\n severity?: LickSeverity;\n eventId?: string;\n at?: number;\n triage?: string;\n summary?: string;\n report?: string;\n taints?: readonly InheritedTaint[];\n}\n\nfunction card(event: LickEvent): Pick<Lick, 'summary' | 'report'> {\n return {\n ...(event.summary ? { summary: event.summary } : {}),\n ...(event.report ? { report: event.report } : {}),\n };\n}\n\ntype Pending = {\n lick: Lick;\n items: string[];\n body: string | null;\n taints?: InheritedTaint[];\n};\n\nfunction tainted(taints: readonly InheritedTaint[]): Pick<Pending, 'taints'> {\n return taints.length ? { taints: distinctTaints(taints) } : {};\n}\n\ntype Queue = {\n submission: number | null;\n pending: Pending | null;\n};\n\ntype OutboxState = {\n queues: Record<string, Queue>;\n events: Record<string, string[]>;\n};\n\nexport const LicksOutbox = defineDoc<OutboxState>({\n kind: 'slicc.licks',\n checkpointWhen: deltasOnly,\n version: 1,\n scope: 'session',\n initial: () => ({ queues: {}, events: {} }),\n});\n\nexport interface LickHandler {\n confirm?(lick: Lick, reason: string | undefined, context: Context): Promise<string | undefined>;\n dismiss?(lick: Lick, reason: string | undefined, context: Context): Promise<string | undefined>;\n}\n\nexport interface LickFound {\n lick: Lick;\n decision: LickDecision | undefined;\n}\n\nexport interface Licks {\n deliver(event: LickEvent, context: Context): Promise<void>;\n flush(context: Context): Promise<void>;\n find(conversation: Conversation, id: string, context: Context): Promise<LickFound | undefined>;\n decide(\n conversation: Conversation,\n id: string,\n action: LickAction,\n reason: string | undefined,\n context: Context\n ): Promise<string>;\n handle(channel: LickChannel, handler: LickHandler): void;\n pending(context: Context): Promise<number>;\n forget(target: LickTarget, channel: LickChannel, source: string, context: Context): Promise<void>;\n guard(check: (target: LickTarget) => Blocked | undefined): void;\n triage(judge: Triage): void;\n}\n\nexport type Blocked = { id: string; name: string; at: number };\n\nexport function frozenNotice(event: LickEvent, blocked: Blocked): LickEvent {\n return {\n channel: event.channel,\n source: `frozen:${event.source}`,\n title: `${blocked.name} is frozen`,\n text: `${event.title} was skipped`,\n body: `${event.target} belongs to ${blocked.name}, which is frozen (${blocked.id}). Thaw it in the Freezer to get its licks again.`,\n target: 'cone',\n severity: 'warn',\n eventId: `${blocked.id}|${event.target}|${event.channel}|${event.source}|${blocked.at}`,\n ...(event.taints ? { taints: event.taints } : {}),\n };\n}\n\nexport interface LicksHost {\n harness: Harness;\n resolve(target: LickTarget, channel: LickChannel, context: Context): Promise<Conversation>;\n runnable?(conversation: Conversation, context: Context): Promise<boolean>;\n}\n\nexport const MAX_ITEMS = 50;\nexport const MAX_EVENTS = 64;\n\nexport function render(pending: Pending): Lick {\n const shown = pending.items.slice(0, MAX_ITEMS);\n const more = pending.items.length - shown.length;\n const lines = [\n ...(pending.body ? [pending.body] : []),\n ...shown,\n ...(more > 0 ? [`\u2026 and ${more} more`] : []),\n ];\n const { body: _, ...lick } = pending.lick;\n return lines.length ? { ...lick, body: lines.join('\\n') } : lick;\n}\n\nconst RANK: Record<LickSeverity, number> = { warn: 1, error: 2 };\n\nexport function strongest(\n a: LickSeverity | undefined,\n b: LickSeverity | undefined\n): LickSeverity | undefined {\n if (!a) return b;\n if (!b) return a;\n return RANK[b] > RANK[a] ? b : a;\n}\n\nfunction merge(pending: Pending, event: LickEvent, now: number): Pending {\n const items = [...pending.items];\n for (const item of event.items ?? []) if (!items.includes(item)) items.push(item);\n const { severity: _, summary: __, report: ___, ...lick } = pending.lick;\n const severity = strongest(pending.lick.severity, event.severity);\n const taints = tainted([...(pending.taints ?? []), ...(event.taints ?? [])]);\n return {\n lick: {\n ...lick,\n title: event.title,\n text: event.text,\n count: pending.lick.count + (event.count ?? 1),\n at: event.at ?? now,\n ...(severity ? { severity } : {}),\n ...card(event),\n },\n items,\n body: event.body ?? pending.body,\n ...taints,\n };\n}\n\nfunction fresh(event: LickEvent, now: number, listed: LickAction[] | undefined): Pending {\n return {\n lick: {\n id: lickId(),\n channel: event.channel,\n source: event.source,\n title: event.title,\n text: event.text,\n count: event.count ?? 1,\n at: now,\n ...(listed ? { actions: listed } : {}),\n ...(event.severity ? { severity: event.severity } : {}),\n ...card(event),\n },\n items: [...new Set(event.items ?? [])],\n body: event.body ?? null,\n ...tainted(event.taints ?? []),\n };\n}\n\nfunction keyOf(event: LickEvent): string {\n const base = `${event.target}|${event.channel}|${event.source}`;\n return event.coalesce === false ? `${base}|${event.eventId ?? lickId()}` : base;\n}\n\nexport async function scrubbedLick(event: LickEvent): Promise<LickEvent> {\n const fields = (['title', 'text', 'body', 'triage', 'summary', 'report'] as const).filter(\n (key) => event[key] !== undefined\n );\n const items = event.items ?? [];\n const scrubbed = await scrubTexts([...fields.map((key) => event[key] as string), ...items]);\n const out: LickEvent = { ...event };\n fields.forEach((key, at) => {\n out[key] = scrubbed[at] as string;\n });\n if (event.items) out.items = scrubbed.slice(fields.length);\n if (out.summary !== undefined) out.summary = clip(out.summary);\n return out;\n}\n\nasync function marked(harness: Harness, pending: Pending, id: string, context: Context) {\n const taints = pending.taints;\n if (!taints) return;\n await harness.commit(async (tx) => {\n (await tx.doc(LickTaintDoc)).licks[id] = taints;\n }, context);\n}\n\nexport function createLicks(host: Promise<LicksHost>): Licks {\n const handlers = new Map<LickChannel, LickHandler>();\n let lock: Promise<unknown> = Promise.resolve();\n const serial = <T>(operation: () => Promise<T>): Promise<T> => {\n const run = lock.then(operation, operation);\n lock = run.catch(() => undefined);\n return run;\n };\n\n const actions = (channel: LickChannel): LickAction[] | undefined => {\n const handler = handlers.get(channel);\n const list: LickAction[] = [];\n if (handler?.confirm) list.push('confirm');\n if (handler?.dismiss) list.push('dismiss');\n return list.length ? list : undefined;\n };\n\n async function previous(\n harness: Harness,\n id: number | null,\n context: Context\n ): Promise<'queued' | 'withdrawn' | 'gone'> {\n if (id === null) return 'gone';\n const submission = await harness.submission(id as SubmissionId, context);\n const record = await submission?.status(context);\n if (record?.status === 'queued') return 'queued';\n return record?.status === 'unanswered' && record.reason === 'aborted' ? 'withdrawn' : 'gone';\n }\n\n async function settled(key: string, context: Context): Promise<Queue | undefined> {\n const { harness } = await host;\n const queue = (await harness.snapshot(LicksOutbox, context))?.queues[key];\n if (!queue) return undefined;\n const before = await previous(harness, queue.submission, context);\n if (before === 'queued') return undefined;\n if (queue.pending && before === 'gone') return queue;\n await harness.commit(async (tx) => {\n const doc = await tx.doc(LicksOutbox);\n if (doc.queues[key]?.submission === queue.submission) delete doc.queues[key];\n }, context);\n return undefined;\n }\n\n async function flushKey(key: string, context: Context): Promise<void> {\n const { harness, resolve, runnable } = await host;\n const queue = await settled(key, context);\n if (!queue?.pending) return;\n const lick = render(queue.pending);\n const target = key.split('|')[0] as LickTarget;\n const conversation = await resolve(target, queue.pending.lick.channel, context);\n if (runnable && !(await runnable(conversation, context))) return;\n await marked(harness, queue.pending, lick.id, context);\n const submission = await conversation.submit(\n {\n type: 'input',\n content: formatLick(lick),\n whenBusy: 'steer',\n requestId: `lick:${lick.id}`,\n },\n context\n );\n await harness.commit(async (tx) => {\n const doc = await tx.doc(LicksOutbox);\n doc.queues[key] = { submission: submission.id, pending: null };\n }, context);\n }\n\n async function flushAll(context: Context): Promise<void> {\n const { harness } = await host;\n const keys = Object.keys((await harness.snapshot(LicksOutbox, context))?.queues ?? {});\n for (const key of keys) await flushKey(key, context);\n }\n\n async function find(\n conversation: Conversation,\n id: string,\n context: Context\n ): Promise<LickFound | undefined> {\n const { entries } = await conversation.context(context);\n const lick = entries.map(entryLick).find((candidate) => candidate?.id === id);\n if (!lick) return undefined;\n return { lick, decision: lickDecisions(entries).get(id) };\n }\n\n async function judged(event: LickEvent, context: Context): Promise<Verdict | undefined> {\n if (!judge || !event.triage) return undefined;\n return judge(event, context).catch(() => undefined);\n }\n\n async function seenBefore(event: LickEvent, context: Context): Promise<boolean> {\n if (event.eventId === undefined) return false;\n const { harness } = await host;\n const events = (await harness.snapshot(LicksOutbox, context))?.events ?? {};\n return (events[`${event.channel}|${event.source}`] ?? []).includes(event.eventId);\n }\n\n async function record(event: LickEvent, verdict: Verdict, now: number, context: Context) {\n const conversation = await (await host).resolve(event.target, event.channel, context);\n await conversation.submit(skipEntry(event, verdict, now), context);\n }\n\n let check: ((target: LickTarget) => Blocked | undefined) | undefined;\n let judge: Triage | undefined;\n return {\n guard(next) {\n check = next;\n },\n triage(next) {\n judge = next;\n },\n async deliver(raw, context) {\n const incoming = await scrubbedLick(raw);\n const blocked = check?.(incoming.target);\n const event = blocked ? frozenNotice(incoming, blocked) : incoming;\n if (judge && event.triage && (await seenBefore(event, context))) return;\n const verdict = await judged(event, context);\n const skipped = skips(verdict);\n return serial(async () => {\n const { harness } = await host;\n const key = keyOf(event);\n await settled(key, context);\n const stream = `${event.channel}|${event.source}`;\n const now = event.at ?? Date.now();\n if (skipped) {\n if (await seenBefore(event, context)) return;\n await record(event, verdict, now, context);\n }\n const accepted = await harness.commit(async (tx) => {\n const doc = await tx.doc(LicksOutbox);\n const seen = doc.events[stream] ?? [];\n if (event.eventId !== undefined) {\n if (seen.includes(event.eventId)) return false;\n doc.events[stream] = [...seen, event.eventId].slice(-MAX_EVENTS);\n }\n if (skipped) return false;\n const queue = doc.queues[key] ?? { submission: null, pending: null };\n queue.pending = queue.pending\n ? merge(queue.pending, event, now)\n : fresh(event, now, actions(event.channel));\n doc.queues[key] = queue;\n return true;\n }, context);\n if (accepted) await flushKey(key, context);\n });\n },\n flush: (context) => serial(() => flushAll(context)),\n find,\n async decide(conversation, id, action, reason, context) {\n const found = await find(conversation, id, context);\n if (!found) throw new Error(`There is no lick ${id} in this conversation.`);\n if (found.decision) throw new Error(`Lick ${id} is already ${found.decision}.`);\n if (!found.lick.actions?.includes(action))\n throw new Error(`Lick ${id} (${found.lick.channel}) has no ${action} action.`);\n const run = handlers.get(found.lick.channel)?.[action];\n if (!run) throw new Error(`Lick ${id} (${found.lick.channel}) has no ${action} action.`);\n return (await run(found.lick, reason, context)) ?? `Lick ${id} ${action}ed.`;\n },\n handle(channel, handler) {\n handlers.set(channel, handler);\n },\n forget(target, channel, source, context) {\n return serial(async () => {\n const { harness } = await host;\n const key = `${target}|${channel}|${source}`;\n await harness.commit(async (tx) => {\n const doc = await tx.doc(LicksOutbox);\n const queue = doc.queues[key];\n if (queue?.pending) queue.pending = null;\n }, context);\n });\n },\n async pending(context) {\n const { harness } = await host;\n const queues = (await harness.snapshot(LicksOutbox, context))?.queues ?? {};\n return Object.values(queues).filter((queue) => queue.submission !== null || queue.pending)\n .length;\n },\n };\n}\n\nexport { LICK_STATE_KIND };\n"],
5
+ "mappings": "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",
6
6
  "names": ["record"]
7
7
  }
package/dist/mcp/allow.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { defineDoc } from "@earendil-works/pi-durable";
2
+ import { deltasOnly } from "../checkpoints.js";
2
3
  import { mcpNamespace, nameMatcher } from "./config.js";
3
4
  const RESOURCE_NAMES = /* @__PURE__ */ new Set([
4
5
  "list_mcp_resources",
@@ -7,6 +8,7 @@ const RESOURCE_NAMES = /* @__PURE__ */ new Set([
7
8
  ]);
8
9
  const McpAllowDoc = defineDoc({
9
10
  kind: "slicc.mcp",
11
+ checkpointWhen: deltasOnly,
10
12
  version: 1,
11
13
  scope: "conversation",
12
14
  history: "rewindable",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/mcp/allow.ts"],
4
- "sourcesContent": ["import type { CodemodeTool } from '@earendil-works/pi-codemode';\nimport { defineDoc } from '@earendil-works/pi-durable';\nimport { mcpNamespace, nameMatcher } from './config.ts';\n\nexport const RESOURCE_NAMES: ReadonlySet<string> = new Set([\n 'list_mcp_resources',\n 'list_mcp_resource_templates',\n 'read_mcp_resource',\n]);\n\nexport const McpAllowDoc = defineDoc<{ allow: string[] | null }>({\n kind: 'slicc.mcp',\n version: 1,\n scope: 'conversation',\n history: 'rewindable',\n fork: 'asOf',\n initial: () => ({ allow: null }),\n});\n\nexport interface CodemodeExtra {\n tools: CodemodeTool[];\n globals: CodemodeTool[];\n}\n\nexport function mcpEntries(names: readonly string[] | undefined): string[] | null {\n if (!names) return null;\n return names.filter((name) => name.startsWith('mcp__') || RESOURCE_NAMES.has(name));\n}\n\nexport function allows(allow: readonly string[] | null, name: string): boolean {\n return allow === null || nameMatcher(allow)(name);\n}\n\nexport function serverAllowed(allow: readonly string[] | null, server: string): boolean {\n if (allow === null) return true;\n const prefix = `${mcpNamespace(server)}__`;\n return allow.some((entry) => {\n if (entry.startsWith(prefix)) return true;\n if (!entry.includes('*')) return false;\n return prefix.startsWith(entry.slice(0, entry.indexOf('*')));\n });\n}\n"],
5
- "mappings": "AACA,SAAS,iBAAiB;AAC1B,SAAS,cAAc,mBAAmB;AAEnC,MAAM,iBAAsC,oBAAI,IAAI;AAAA,EACzD;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAEM,MAAM,cAAc,UAAsC;AAAA,EAC/D,MAAM;AAAA,EACN,SAAS;AAAA,EACT,OAAO;AAAA,EACP,SAAS;AAAA,EACT,MAAM;AAAA,EACN,SAAS,OAAO,EAAE,OAAO,KAAK;AAChC,CAAC;AAOM,SAAS,WAAW,OAAuD;AAChF,MAAI,CAAC,MAAO,QAAO;AACnB,SAAO,MAAM,OAAO,CAAC,SAAS,KAAK,WAAW,OAAO,KAAK,eAAe,IAAI,IAAI,CAAC;AACpF;AAEO,SAAS,OAAO,OAAiC,MAAuB;AAC7E,SAAO,UAAU,QAAQ,YAAY,KAAK,EAAE,IAAI;AAClD;AAEO,SAAS,cAAc,OAAiC,QAAyB;AACtF,MAAI,UAAU,KAAM,QAAO;AAC3B,QAAM,SAAS,GAAG,aAAa,MAAM,CAAC;AACtC,SAAO,MAAM,KAAK,CAAC,UAAU;AAC3B,QAAI,MAAM,WAAW,MAAM,EAAG,QAAO;AACrC,QAAI,CAAC,MAAM,SAAS,GAAG,EAAG,QAAO;AACjC,WAAO,OAAO,WAAW,MAAM,MAAM,GAAG,MAAM,QAAQ,GAAG,CAAC,CAAC;AAAA,EAC7D,CAAC;AACH;",
4
+ "sourcesContent": ["import type { CodemodeTool } from '@earendil-works/pi-codemode';\nimport { defineDoc } from '@earendil-works/pi-durable';\nimport { deltasOnly } from '../checkpoints.ts';\nimport { mcpNamespace, nameMatcher } from './config.ts';\n\nexport const RESOURCE_NAMES: ReadonlySet<string> = new Set([\n 'list_mcp_resources',\n 'list_mcp_resource_templates',\n 'read_mcp_resource',\n]);\n\nexport const McpAllowDoc = defineDoc<{ allow: string[] | null }>({\n kind: 'slicc.mcp',\n checkpointWhen: deltasOnly,\n version: 1,\n scope: 'conversation',\n history: 'rewindable',\n fork: 'asOf',\n initial: () => ({ allow: null }),\n});\n\nexport interface CodemodeExtra {\n tools: CodemodeTool[];\n globals: CodemodeTool[];\n}\n\nexport function mcpEntries(names: readonly string[] | undefined): string[] | null {\n if (!names) return null;\n return names.filter((name) => name.startsWith('mcp__') || RESOURCE_NAMES.has(name));\n}\n\nexport function allows(allow: readonly string[] | null, name: string): boolean {\n return allow === null || nameMatcher(allow)(name);\n}\n\nexport function serverAllowed(allow: readonly string[] | null, server: string): boolean {\n if (allow === null) return true;\n const prefix = `${mcpNamespace(server)}__`;\n return allow.some((entry) => {\n if (entry.startsWith(prefix)) return true;\n if (!entry.includes('*')) return false;\n return prefix.startsWith(entry.slice(0, entry.indexOf('*')));\n });\n}\n"],
5
+ "mappings": "AACA,SAAS,iBAAiB;AAC1B,SAAS,kBAAkB;AAC3B,SAAS,cAAc,mBAAmB;AAEnC,MAAM,iBAAsC,oBAAI,IAAI;AAAA,EACzD;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAEM,MAAM,cAAc,UAAsC;AAAA,EAC/D,MAAM;AAAA,EACN,gBAAgB;AAAA,EAChB,SAAS;AAAA,EACT,OAAO;AAAA,EACP,SAAS;AAAA,EACT,MAAM;AAAA,EACN,SAAS,OAAO,EAAE,OAAO,KAAK;AAChC,CAAC;AAOM,SAAS,WAAW,OAAuD;AAChF,MAAI,CAAC,MAAO,QAAO;AACnB,SAAO,MAAM,OAAO,CAAC,SAAS,KAAK,WAAW,OAAO,KAAK,eAAe,IAAI,IAAI,CAAC;AACpF;AAEO,SAAS,OAAO,OAAiC,MAAuB;AAC7E,SAAO,UAAU,QAAQ,YAAY,KAAK,EAAE,IAAI;AAClD;AAEO,SAAS,cAAc,OAAiC,QAAyB;AACtF,MAAI,UAAU,KAAM,QAAO;AAC3B,QAAM,SAAS,GAAG,aAAa,MAAM,CAAC;AACtC,SAAO,MAAM,KAAK,CAAC,UAAU;AAC3B,QAAI,MAAM,WAAW,MAAM,EAAG,QAAO;AACrC,QAAI,CAAC,MAAM,SAAS,GAAG,EAAG,QAAO;AACjC,WAAO,OAAO,WAAW,MAAM,MAAM,GAAG,MAAM,QAAQ,GAAG,CAAC,CAAC;AAAA,EAC7D,CAAC;AACH;",
6
6
  "names": []
7
7
  }
@@ -5,6 +5,7 @@ import {
5
5
  defineTask,
6
6
  hook
7
7
  } from "@earendil-works/pi-durable";
8
+ import { deltasOnly } from "../checkpoints.js";
8
9
  import { MAX_BYTES, parseMemory, redactSecrets, serializeMemory } from "./format.js";
9
10
  import { whoIs } from "./prompt.js";
10
11
  import { placeOf } from "./store.js";
@@ -13,6 +14,7 @@ const TRANSCRIPT_CHARS = 6e4;
13
14
  const DEFAULT_IDLE_MINUTES = 30;
14
15
  const ExtractDoc = defineDoc({
15
16
  kind: "slicc.memory-extract",
17
+ checkpointWhen: deltasOnly,
16
18
  version: 1,
17
19
  scope: "session",
18
20
  initial: () => ({ cursors: {}, pending: {} })
@@ -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} 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, {\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 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": "AACA,SAAS,0BAA0B;AAEnC;AAAA,EACE;AAAA,EAEA;AAAA,EACA;AAAA,EAEA;AAAA,OAEK;AAEP,SAAS,WAAW,aAAa,eAAe,uBAAuB;AACvE,SAAoB,aAAa;AACjC,SAAqB,eAAe;AAE7B,MAAM,eAAe;AACrB,MAAM,mBAAmB;AACzB,MAAM,uBAAuB;AAO7B,MAAM,aAAa,UAAwB;AAAA,EAChD,MAAM;AAAA,EACN,SAAS;AAAA,EACT,OAAO;AAAA,EACP,SAAS,OAAO,EAAE,SAAS,CAAC,GAAG,SAAS,CAAC,EAAE;AAC7C,CAAC;AAIM,SAAS,gBAAgB,MAA2C;AACzE,MAAI,SAA+E,CAAC;AACpF,MAAI;AACF,UAAM,SAAS,KAAK,MAAM,QAAQ,IAAI;AACtC,aAAS,OAAO,UAAU,OAAO,OAAO,WAAW,WAAW,OAAO,SAAS,CAAC;AAAA,EACjF,QAAQ;AACN,aAAS,CAAC;AAAA,EACZ;AACA,QAAM,QAAQ,OAAO,OAAO,iBAAiB,WAAW,OAAO,aAAa,MAAM,GAAG,IAAI,CAAC;AAC1F,QAAM,QACJ,MAAM,UAAU,KAAK,MAAM,CAAC,KAAK,MAAM,MAAM,CAAC,EAAE,KAAK,GAAG,IACpD,EAAE,UAAU,MAAM,CAAC,GAAG,SAAS,MAAM,MAAM,CAAC,EAAE,KAAK,GAAG,EAAE,IACxD;AACN,QAAM,OACJ,OAAO,OAAO,gBAAgB,YAAY,OAAO,eAAe,IAC5D,OAAO,cACP;AACN,SAAO,EAAE,SAAS,OAAO,YAAY,OAAO,OAAO,aAAa,KAAK;AACvE;AAEA,SAAS,OAAO,SAA0B;AACxC,QAAM,UAAW,QAAkC;AACnD,MAAI,OAAO,YAAY,SAAU,QAAO;AACxC,MAAI,CAAC,MAAM,QAAQ,OAAO,EAAG,QAAO;AACpC,SAAO,QACJ;AAAA,IAAI,CAAC,SACJ,KAAK,SAAS,SACT,KAAK,QAAQ,KACd,KAAK,SAAS,aACZ,SAAS,KAAK,IAAI,MAClB;AAAA,EACR,EACC,OAAO,OAAO,EACd,KAAK,IAAI;AACd;AAEO,SAAS,aAAa,UAAsC;AACjE,QAAM,QAAkB,CAAC;AACzB,aAAW,WAAW,UAAU;AAC9B,QAAI,QAAQ,SAAS,UAAU,QAAQ,SAAS,YAAa;AAC7D,UAAM,OAAO,OAAO,OAAO,EAAE,KAAK;AAClC,QAAI,KAAM,OAAM,KAAK,GAAG,QAAQ,SAAS,SAAS,SAAS,OAAO,KAAK,IAAI,EAAE;AAAA,EAC/E;AACA,QAAM,SAAS,cAAc,MAAM,KAAK,MAAM,CAAC,EAAE;AACjD,SAAO,OAAO,SAAS,mBAAmB,OAAO,MAAM,CAAC,gBAAgB,IAAI;AAC9E;AAEO,MAAM,iBAAiB;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,EAAE,KAAK,IAAI;AAIX,MAAM,OAAO,oBAAI,IAAI,CAAC,QAAQ,YAAY,SAAS,CAAC;AAE7C,SAAS,UAAU,MAA0B;AAClD,QAAM,QAAQ,KAAK,QAAQ,GAAG;AAC9B,QAAM,MAAM,KAAK,YAAY,GAAG;AAChC,MAAI,QAAQ,KAAK,MAAM,MAAO,QAAO,CAAC;AACtC,MAAI;AACJ,MAAI;AACF,aAAS,KAAK,MAAM,KAAK,MAAM,OAAO,MAAM,CAAC,CAAC;AAAA,EAChD,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACA,MAAI,CAAC,MAAM,QAAQ,MAAM,EAAG,QAAO,CAAC;AACpC,QAAM,MAAkB,CAAC;AACzB,aAAW,QAAQ,QAAqC;AACtD,QAAI,CAAC,QAAQ,OAAO,SAAS,SAAU;AACvC,UAAM,EAAE,SAAS,OAAO,MAAM,IAAI,IAAI;AACtC,QAAI,OAAO,YAAY,YAAY,OAAO,UAAU,YAAY,OAAO,SAAS;AAC9E;AACF,QAAI,CAAC,QAAQ,KAAK,KAAK,CAAC,MAAM,KAAK,KAAK,CAAC,KAAK,KAAK,EAAG;AACtD,QAAI,KAAK;AAAA,MACP;AAAA,MACA;AAAA,MACA;AAAA,MACA,KAAK,OAAO,QAAQ,YAAY,KAAK,IAAI,GAAG,IAAK,MAAoB;AAAA,IACvE,CAAC;AAAA,EACH;AACA,SAAO,IAAI,MAAM,GAAG,EAAE;AACxB;AAMA,eAAe,KAAK,MAAY,OAAc,OAAmB,SAAkB;AACjF,MAAI,QAAQ;AACZ,aAAW,YAAY,OAAO;AAC5B,QAAI;AACF,YAAM,KAAK,MAAM,OAAO,OAAO,EAAE,MAAM,QAAQ,GAAG,SAAS,GAAG,OAAO;AACrE;AAAA,IACF,QAAQ;AACN;AAAA,IACF;AAAA,EACF;AACA,MAAI,MAAO,OAAM,KAAK,QAAQ,OAAO,EAAE,MAAM,MAAM,MAAS;AAC5D,SAAO;AACT;AAEO,SAAS,YACd,OACA,UACA;AACA,SAAO,WAAwC;AAAA,IAC7C,MAAM;AAAA,IACN,SAAS;AAAA,IACT,SAAS,OAAO,EAAE,OAAO,UAAU;AAAA,IACnC,QAAQ;AAAA,MACN,MAAM,QAAQ,MAAM,SAAS,SAAS;AACpC,cAAM,EAAE,MAAM,OAAO,KAAK,IAAI,KAAK;AACnC,cAAM,MAAM,OAAO,QAAQ,cAAc;AACzC,cAAM,OAAO,CAAC,WACZ,QAAQ,OAAO,OAAO,OAAO;AAC3B,gBAAM,MAAM,MAAM,GAAG,IAAI,UAAU;AACnC,cAAI,QAAQ,GAAG,IAAI,KAAK,IAAI,IAAI,QAAQ,GAAG,KAAK,GAAG,IAAI;AACvD,iBAAO,IAAI,QAAQ,GAAG;AACtB,iBAAO,EAAE,QAAQ,YAAY,SAAS,EAAE,QAAQ,aAAa,OAAO,EAAE;AAAA,QACxE,GAAG,OAAO;AACZ,YAAI,EAAE,MAAM,SAAS,OAAO,GAAG,QAAS,QAAO,KAAK,EAAE,OAAO,GAAG,SAAS,aAAa,CAAC;AACvF,cAAM,OAAO,MAAM;AACnB,cAAM,QAAQ,QAAQ,KAAK,MAAM,IAAI;AACrC,cAAM,OAAO,MAAM,QAAQ,QAAQ,QAAQ,gBAAgB,SAAS;AAAA,UAClE,IAAI;AAAA,QACN,CAAC;AACD,cAAM,QAAQ,KAAK,QAAQ;AAAA,UAAQ,CAAC,OAAO,UACzC,MAAM,KAAK,QAAS,KAAK,cAAc,KAAK,IAA2B,CAAC;AAAA,QAC1E;AACA,cAAM,aAAa,aAAa,KAAK;AACrC,YAAI,CAAC,SAAS,CAAC,WAAY,QAAO,KAAK,EAAE,OAAO,GAAG,SAAS,cAAc,CAAC;AAC3E,cAAM,MAAM,KAAK,MAAM,UAAU,MAAM,QAAQ,MAAM,OAAO,GAAG;AAC/D,cAAM,QAAQ,MAAM,QAAQ,OAAO,SAAS,IAAI,UAAU,IAAI,OAAO,IAAI;AACzE,YAAI,CAAC,MAAO,QAAO,KAAK,EAAE,OAAO,GAAG,SAAS,WAAW,CAAC;AACzD,cAAM,UAAU,MAAM,KAAK,MAAM,KAAK,MAAM,MAAM,OAAO;AACzD,cAAM,SAAS,UAAU,gBAAgB,YAAY,QAAQ,IAAI,CAAC,IAAI;AACtE,cAAM,MAAM,KAAK,IAAI;AACrB,cAAM,UAAU,MAAM,QAAQ,OAAO;AAAA,UACnC;AAAA,UACA;AAAA,YACE,UAAU;AAAA,cACR,EAAE,MAAM,UAAU,SAAS,gBAAgB,WAAW,IAAI;AAAA,cAC1D;AAAA,gBACE,MAAM;AAAA,gBACN,SAAS;AAAA,kBACP;AAAA,oBACE,MAAM;AAAA,oBACN,MAAM,4BAA4B,YAAY,IAAI,YAAY,EAAE,OAAO,MAAM,EAAE,MAAM;AAAA,EAAO,MAAM;AAAA;AAAA;AAAA,EAAqC,UAAU;AAAA;AAAA,kBACnJ;AAAA,gBACF;AAAA,gBACA,WAAW;AAAA,cACb;AAAA,YACF;AAAA,UACF;AAAA,UACA,EAAE,WAAW,KAAM,QAAQ,QAAQ,QAAQ,gBAAgB,OAAO;AAAA,QACpE;AACA,gBAAQ,OAAO,eAAe;AAC9B,YAAI,QAAQ,eAAe,QAAS,QAAO,KAAK,EAAE,OAAO,GAAG,SAAS,cAAc,CAAC;AACpF,cAAM,QAAQ,MAAM,KAAK,MAAM,OAAO,UAAU,OAAO,OAAkB,CAAC,GAAG,OAAO;AACpF,eAAO,KAAK,EAAE,OAAO,SAAS,KAAK,CAAC;AAAA,MACtC;AAAA,IACF;AAAA,IACA,OAAO,CAAC,OAAO,SAAS,YACtB,QAAQ,OAAO,OAAO,OAAO;AAC3B,cAAQ,MAAM,GAAG,IAAI,UAAU,GAAG,QAAQ,OAAO,QAAQ,cAAc,CAAC;AACxE,aAAO,EAAE,QAAQ,YAAY,SAAS,EAAE,QAAQ,UAAU,EAAE;AAAA,IAC9D,GAAG,OAAO;AAAA,EACd,CAAC;AACH;AASO,SAAS,eAAe,UAAoB;AACjD,SAAO,KAAK,gBAAgB;AAAA,IAC1B,cAAc,YAAY,KAAK;AAC7B,YAAM,OAAO,WAAW,QAAQ,OAAO,CAAC,KAAK,UAAU,KAAK,IAAI,KAAK,MAAM,EAAE,GAAG,CAAC;AACjF,WAAK,SAAS,IAAI,gBAAgB,MAAM,kBAAkB,EAAE,MAAM,MAAM,KAAK;AAC7E,aAAO;AAAA,IACT;AAAA,EACF,CAAC;AACH;AAEO,SAAS,UACd,OACA,MACA,UACU;AACV,SAAO,OAAO,cAAc,MAAM,SAAS,SAAS;AAClD,UAAM,OAAO,MAAM;AACnB,UAAM,SAAS,MAAM,SAAS,OAAO;AACrC,UAAM,MAAM,OACR,EAAE,MAAM,QAAiB,IAAI,KAAK,IAClC,MAAM,KAAK,OAAO,MAAM,GAAG,YAAY;AAC3C,QAAI,IAAI,SAAS,OAAQ,QAAO;AAChC,WAAO,KAAK,QAAQ,OAAO,OAAO,OAAO;AACvC,YAAM,MAAM,MAAM,GAAG,IAAI,UAAU;AACnC,YAAM,MAAM,OAAO,YAAY;AAC/B,YAAM,QAAQ,IAAI,QAAQ,GAAG,KAAK;AAClC,UAAI,IAAI,QAAQ,GAAG,MAAM,UAAa,QAAQ,MAAO,QAAO;AAC5D,UAAI,QAAQ,GAAG,IAAK,MAAM,GAAG;AAAA,QAC3B;AAAA,QACA,EAAE,MAAM,IAAI,IAAI,OAAO,MAAM,OAAO,OAAO,MAAM;AAAA,QACjD;AAAA,UACE,WAAW,EAAE,MAAM,eAAe;AAAA,UAClC,gBAAgB;AAAA,UAChB,YAAY;AAAA,QACd;AAAA,MACF;AACA,aAAO;AAAA,IACT,GAAG,OAAO;AAAA,EACZ;AACF;AAEO,SAAS,eAAe,OAAsB;AACnD,SAAO,OAAO,YAA+C;AAC3D,UAAM,OAAO,MAAM;AACnB,UAAM,MAAM,KAAK,YAAY,GAAG,KAAK,IAAI;AACzC,UAAM,OAAO,MAAM,KAAK,IAAI,aAAa,GAAG,GAAG,kBAAkB,OAAO;AACxE,WAAO,gBAAgB,KAAK,KAAK,KAAK,QAAQ,MAAS;AAAA,EACzD;AACF;AAEO,SAAS,UACd,MACA,UACA,UACA,SACA,SACA,MAAoB,KAAK,KACb;AACZ,QAAM,OAAO,oBAAI,IAA6C;AAC9D,QAAM,QAAQ,YAAY;AACxB,UAAM,EAAE,aAAa,QAAQ,IAAI,MAAM,SAAS,OAAO;AACvD,QAAI,CAAC,WAAW,gBAAgB,EAAG;AACnC,eAAW,QAAQ,OAAO,OAAO,KAAK,OAAO,MAAM,EAAE,KAAK,GAAG;AAC3D,YAAM,eAAe,MAAM,KAAK,QAAQ;AAAA,QACtC,KAAK;AAAA,QACL;AAAA,MACF;AACA,YAAM,OAAO,MAAM,cAAc,QAAQ,OAAO;AAChD,YAAM,OAAO,MAAM,QAAQ,GAAG,EAAE,GAAG,MAAM;AACzC,YAAM,OAAO,KAAK,IAAI,KAAK,YAAY;AACvC,UAAI,CAAC,QAAQ,KAAK,SAAS,MAAM;AAC/B,aAAK,IAAI,KAAK,cAAc,EAAE,MAAM,OAAO,IAAI,EAAE,CAAC;AAClD;AAAA,MACF;AACA,UAAI,IAAI,IAAI,KAAK,SAAS,cAAc;AACtC,cAAM,SAAS,KAAK,cAAc,MAAM,OAAO;AAAA,IACnD;AAAA,EACF;AACA,MAAI,UAAU;AACd,QAAM,QAAQ,YAAY,MAAM;AAC9B,QAAI,QAAS;AACb,cAAU;AACV,SAAK,MAAM,EACR,MAAM,MAAM,MAAS,EACrB,QAAQ,MAAM;AACb,gBAAU;AAAA,IACZ,CAAC;AAAA,EACL,GAAG,OAAO;AACV,SAAO,MAAM,cAAc,KAAK;AAClC;",
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 { deltasOnly } 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: deltasOnly,\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 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
6
  "names": []
7
7
  }
@@ -325,7 +325,9 @@ class Session {
325
325
  background: node.background,
326
326
  aborting: node.abortRequested ? this.#since(`abort:${id}`) : null,
327
327
  since: this.#since(`task:${id}:${status}`),
328
- waits: node.state.status === "waiting" ? node.state.on.length : 0
328
+ waits: node.state.status === "waiting" ? node.state.on.length : 0,
329
+ until: node.state.status === "sleeping" ? node.state.until : null,
330
+ blocked: node.blocked ?? null
329
331
  };
330
332
  }
331
333
  #process(entry, groups, programs) {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/monitor/index.ts"],
4
- "sourcesContent": ["import {\n type Context,\n type JsonValue,\n type ReplicatedState,\n replicatedState,\n} from '@earendil-works/chord';\nimport { BACKGROUND_CONTEXT } from '@earendil-works/chord/context';\nimport type {\n CommitPublication,\n ConversationView,\n Harness,\n TaskGraph,\n TaskGraphNode,\n TaskGraphWatch,\n TaskRecord,\n UsageState,\n} from '@earendil-works/pi-durable';\nimport { type AgentsState, live } from '../agents.ts';\nimport { contextTokens } from '../context.ts';\nimport type {\n MonitorAgent,\n MonitorFeed,\n MonitorLease,\n MonitorProblem,\n MonitorProcess,\n MonitorTask,\n} from '../services.ts';\nimport { commandLine, program, redacted } from './redact.ts';\n\nexport interface PsEntry {\n pid: number;\n ppid?: number;\n pgid?: number;\n argv?: readonly string[];\n tty?: unknown;\n started?: number;\n state?: string;\n uid?: number;\n rss?: number;\n utime?: number;\n stime?: number;\n}\n\nexport interface CpuMark {\n started: number;\n total: number;\n at: number;\n}\n\nexport interface CpuReading {\n marks: Map<number, CpuMark>;\n shares: Map<number, number | null>;\n}\n\nfunction timed(entry: PsEntry): entry is PsEntry & { utime: number; stime: number } {\n return typeof entry.utime === 'number' && typeof entry.stime === 'number';\n}\n\nexport function cpuShares(\n entries: readonly PsEntry[],\n previous: ReadonlyMap<number, CpuMark>,\n at: number\n): CpuReading {\n const marks = new Map<number, CpuMark>();\n const shares = new Map<number, number | null>();\n for (const entry of entries) {\n if (!timed(entry)) continue;\n const mark = { started: entry.started ?? 0, total: entry.utime + entry.stime, at };\n const last = previous.get(entry.pid);\n marks.set(entry.pid, mark);\n shares.set(\n entry.pid,\n last && last.started === mark.started && mark.total >= last.total && at > last.at\n ? Math.round(((mark.total - last.total) / (at - last.at)) * 1000) / 1000\n : null\n );\n }\n return { marks, shares };\n}\n\nexport function agentCpu(processes: readonly MonitorProcess[], agentId: string): number | null {\n if (!processes.some((row) => typeof row.cpu === 'number')) return null;\n let sum = 0;\n for (const row of processes) if (row.agentId === agentId) sum += row.cpu ?? 0;\n return Math.round(sum * 1000) / 1000;\n}\n\nexport interface Timers {\n set(run: () => void, ms: number): unknown;\n clear(handle: unknown): void;\n}\n\nexport interface MonitorOptions {\n harness: Pick<Harness, 'watchTaskGraph' | 'usage' | 'subscribeCommits' | 'inspect'>;\n agents: { state(): Readonly<AgentsState>; onChange(listener: () => void): () => void };\n views: ReplicatedState<Record<string, ConversationView>>;\n kernel?: { ps?(): Promise<readonly PsEntry[]> };\n groups?: { owned(owner: number): number[] };\n users?: () => Promise<readonly { name: string; uid: number }[]>;\n now?: () => number;\n timers?: Timers;\n ttlMs?: number;\n psMs?: number;\n psTimeoutMs?: number;\n throttleMs?: number;\n usageMs?: number;\n context?: Context;\n}\n\nexport interface Monitor {\n readonly state: ReplicatedState<MonitorFeed>;\n readonly viewState: ReplicatedState<MonitorFeed>;\n open(lease: string | null, resync: boolean, context: Context): Promise<MonitorLease>;\n close(lease: string, context: Context): Promise<void>;\n active(): boolean;\n dispose(): Promise<void>;\n}\n\nexport const TTL_MS = 30000;\nexport const PS_MS = 2000;\nexport const PS_TIMEOUT_MS = 5000;\nexport const THROTTLE_MS = 1000;\nexport const USAGE_MS = 2000;\nexport const PROBLEMS_MAX = 50;\n\nexport const globalTimers: Timers = {\n set: (run, ms) => setTimeout(run, ms),\n clear: (handle) => clearTimeout(handle as ReturnType<typeof setTimeout>),\n};\n\nexport function emptyFeed(): MonitorFeed {\n return {\n open: false,\n at: 0,\n usage: null,\n agents: [],\n tasks: [],\n processes: null,\n kernel: 'checking',\n problems: [],\n paused: false,\n };\n}\n\ninterface Live {\n run?: unknown;\n generation?: { attempt: number; retry?: { at: number; error: string } };\n tools?: { name: string; status: string; taskId?: unknown }[];\n}\n\ninterface ModelMessage {\n stopReason?: string;\n errorMessage?: string;\n}\n\nexport function totals(usage: UsageState): { tokens: number; cost: number; free: boolean } {\n let tokens = 0;\n let cost = 0;\n for (const bucket of [usage.models, usage.tools])\n for (const entry of Object.values(bucket)) {\n tokens += entry.totalTokens ?? 0;\n cost += entry.cost?.total ?? 0;\n }\n return { tokens, cost, free: cost === 0 };\n}\n\nfunction phaseOf(node: TaskGraphNode): string {\n return 'phase' in node.state ? node.state.phase : node.state.outcome;\n}\n\nfunction lastAssistant(view: ConversationView | undefined) {\n const entries = view?.entries ?? [];\n for (let at = entries.length - 1; at >= 0; at--) {\n const entry = entries[at] as (typeof entries)[number];\n if (entry.kind === 'pi.assistant')\n return { id: String(entry.id), message: (entry.model?.[0] ?? {}) as ModelMessage };\n }\n return undefined;\n}\n\nfunction conversations(state: Readonly<AgentsState>): Map<number, string> {\n const out = new Map<number, string>();\n for (const [id, scoop] of Object.entries(state.scoops)) out.set(scoop.conversation, id);\n for (const [id, cone] of Object.entries(state.cones)) out.set(cone.conversation, id);\n return out;\n}\n\nfunction liveAgents(state: Readonly<AgentsState>) {\n const cones = Object.entries(state.cones).map(([id, cone]) => ({\n id,\n name: cone.name,\n kind: 'cone' as const,\n parentId: null,\n conversation: cone.conversation,\n }));\n const scoops = Object.entries(state.scoops)\n .filter(([, scoop]) => live(scoop))\n .map(([id, scoop]) => ({\n id,\n name: scoop.name,\n kind: 'scoop' as const,\n parentId: scoop.parent,\n conversation: scoop.conversation,\n }));\n return [...cones, ...scoops];\n}\n\nexport function narrowed(feed: MonitorFeed, programs: ReadonlyMap<number, string>): MonitorFeed {\n if (!feed.processes) return feed;\n return {\n ...feed,\n processes: feed.processes.map((row) => ({ ...row, command: programs.get(row.pid) ?? '' })),\n };\n}\n\ninterface Settings {\n now: () => number;\n timers: Timers;\n context: Context;\n psMs: number;\n psTimeoutMs: number;\n throttleMs: number;\n usageMs: number;\n}\n\ntype Agent = ReturnType<typeof liveAgents>[number];\n\nclass Session {\n stopped = false;\n readonly #options: MonitorOptions;\n readonly #settings: Settings;\n readonly #publish: (feed: MonitorFeed, view: MonitorFeed) => void;\n readonly #off: (() => void)[] = [];\n readonly #handles = new Set<unknown>();\n #watch: TaskGraphWatch | undefined;\n #graph: TaskGraph = { tasks: {} };\n #usage: UsageState | null = null;\n #processes: readonly PsEntry[] | null = null;\n #cpu: CpuReading = { marks: new Map(), shares: new Map() };\n #kernel: MonitorFeed['kernel'];\n #users = new Map<number, string>();\n #paused = false;\n #problems: MonitorProblem[] = [];\n #seen = new Map<string, number>();\n #next = new Map<string, number>();\n #build: unknown;\n #usageTimer: unknown;\n #lastBuild = Number.NEGATIVE_INFINITY;\n #last = '';\n\n constructor(\n options: MonitorOptions,\n settings: Settings,\n publish: (feed: MonitorFeed, view: MonitorFeed) => void\n ) {\n this.#options = options;\n this.#settings = settings;\n this.#publish = publish;\n this.#kernel = options.kernel?.ps ? 'checking' : 'unreachable';\n }\n\n start(): void {\n const options = this.#options;\n this.#off.push(\n options.harness.subscribeCommits((publication) => this.#commit(publication)),\n options.agents.onChange(() => this.schedule()),\n options.views.subscribe(() => this.schedule())\n );\n void options.harness.watchTaskGraph(this.#settings.context).then(\n (watch) => this.#watching(watch),\n () => undefined\n );\n void options.users?.().then(\n (list) => {\n this.#users = new Map(list.map((user) => [user.uid, user.name]));\n this.schedule();\n },\n () => undefined\n );\n this.#poll();\n this.refresh();\n }\n\n stop(): void {\n this.stopped = true;\n for (const off of this.#off) off();\n for (const handle of this.#handles) this.#settings.timers.clear(handle);\n this.#handles.clear();\n void this.#watch?.stop();\n }\n\n refresh(): void {\n void this.#readUsage();\n void this.#options.harness.inspect(this.#settings.context).then(\n (inspection) => {\n this.#paused = inspection.scheduling === 'paused';\n this.schedule();\n },\n () => undefined\n );\n this.schedule();\n }\n\n schedule(): void {\n if (this.stopped || this.#build !== undefined) return;\n const wait = Math.max(0, this.#lastBuild + this.#settings.throttleMs - this.#settings.now());\n this.#build = this.#later(() => {\n this.#build = undefined;\n this.#emit();\n }, wait);\n }\n\n #later(task: () => void, ms: number): unknown {\n if (this.stopped) return undefined;\n const handle = this.#settings.timers.set(() => {\n this.#handles.delete(handle);\n if (!this.stopped) task();\n }, ms);\n this.#handles.add(handle);\n return handle;\n }\n\n #watching(watch: TaskGraphWatch): void {\n if (this.stopped) {\n void watch.stop();\n return;\n }\n this.#watch = watch;\n this.#graph = watch.value;\n this.schedule();\n watch.start(async (graph) => {\n this.#graph = graph;\n this.schedule();\n });\n }\n\n #readUsage(): Promise<void> {\n return this.#options.harness.usage(this.#settings.context).then(\n (usage) => {\n this.#usage = usage;\n this.schedule();\n },\n () => undefined\n );\n }\n\n #poll(): void {\n const kernel = this.#options.kernel;\n if (!kernel?.ps) return;\n let settled = false;\n const settle = (entries: readonly PsEntry[] | null) => {\n if (settled || this.stopped) return;\n settled = true;\n this.#settings.timers.clear(timeout);\n this.#handles.delete(timeout);\n this.#processes = entries;\n if (entries) this.#cpu = cpuShares(entries, this.#cpu.marks, this.#settings.now());\n this.#kernel = entries ? 'ok' : 'unreachable';\n this.schedule();\n this.#later(() => this.#poll(), this.#settings.psMs);\n };\n const timeout = this.#later(() => settle(null), this.#settings.psTimeoutMs);\n kernel.ps().then(settle, () => settle(null));\n }\n\n #problem(entry: MonitorProblem): void {\n if (this.#problems.some((known) => known.id === entry.id)) return;\n this.#problems.unshift(entry);\n this.#problems.length = Math.min(this.#problems.length, PROBLEMS_MAX);\n }\n\n #commit(publication: CommitPublication): void {\n let changed = false;\n for (const change of publication.changes) {\n if (change.type === 'task') changed = this.#failed(change.value) || changed;\n else if (change.type === 'document' && change.record.kind === 'pi.usage')\n this.#usageChanged();\n }\n if (changed) this.schedule();\n }\n\n #failed(task: TaskRecord<JsonValue, JsonValue, JsonValue>): boolean {\n if (task.state.status !== 'terminal') return false;\n const outcome = task.state.outcome;\n if (outcome.status !== 'faulted' && outcome.status !== 'orphaned') return false;\n this.#problem({\n id: `task:${task.id}`,\n at: this.#settings.now(),\n agentId: conversations(this.#options.agents.state()).get(task.conversationId) ?? null,\n kind: 'task',\n message: redacted(outcome.status === 'faulted' ? outcome.error.message : outcome.reason),\n });\n return true;\n }\n\n #usageChanged(): void {\n if (this.#usageTimer !== undefined) return;\n this.#usageTimer = this.#later(() => {\n this.#usageTimer = undefined;\n void this.#readUsage();\n }, this.#settings.usageMs);\n }\n\n #since(key: string): number {\n const at = this.#seen.get(key) ?? this.#settings.now();\n this.#next.set(key, at);\n return at;\n }\n\n #agent(agent: Agent, view: ConversationView | undefined, tools: Map<string, string>) {\n const liveDoc = view?.docs['pi.live'] as Live | undefined;\n for (const slot of liveDoc?.tools ?? [])\n if (slot.taskId !== undefined) tools.set(String(slot.taskId), slot.name);\n const inbox = view?.docs['pi.inbox'] as { items?: unknown[] } | undefined;\n const doc = view?.docs['pi.agent'] as\n | { model?: { provider: string; modelId: string } }\n | undefined;\n const generation = liveDoc?.generation;\n const retry = generation?.retry\n ? {\n attempt: generation.attempt,\n since: this.#since(`retry:${agent.id}`),\n error: redacted(generation.retry.error),\n }\n : null;\n if (retry)\n this.#problem({\n id: `retry:${agent.id}:${retry.since}`,\n at: retry.since,\n agentId: agent.id,\n kind: 'retry',\n message: retry.error,\n });\n const last = lastAssistant(view);\n const failed =\n last?.message.stopReason === 'error' ? redacted(last.message.errorMessage || 'error') : null;\n if (failed && last)\n this.#problem({\n id: `turn:${last.id}`,\n at: this.#settings.now(),\n agentId: agent.id,\n kind: 'turn',\n message: failed,\n });\n return {\n id: agent.id,\n name: agent.name,\n kind: agent.kind,\n parentId: agent.parentId,\n model: doc?.model ? `${doc.model.provider}/${doc.model.modelId}` : null,\n running: Boolean(liveDoc?.run),\n queued: inbox?.items?.length ?? 0,\n tool: liveDoc?.tools?.find((slot) => slot.status === 'running')?.name ?? null,\n retry,\n failed,\n context: contextTokens(view),\n };\n }\n\n #task(node: TaskGraphNode, owners: Map<number, string>, details: Map<string, string>) {\n const id = String(node.id);\n const status = node.state.status;\n return {\n id,\n kind: node.kind,\n detail: details.get(id) ?? null,\n agentId: owners.get(node.conversationId) ?? null,\n owner: node.owner === undefined ? null : String(node.owner),\n status,\n phase: phaseOf(node),\n background: node.background,\n aborting: node.abortRequested ? this.#since(`abort:${id}`) : null,\n since: this.#since(`task:${id}:${status}`),\n waits: node.state.status === 'waiting' ? node.state.on.length : 0,\n };\n }\n\n #process(entry: PsEntry, groups: Map<number, string>, programs: Map<number, string>) {\n const argv = entry.argv ?? [];\n const pgid = entry.pgid ?? entry.pid;\n const zombie = entry.state === 'Z';\n programs.set(entry.pid, program(argv));\n const uid = entry.uid;\n return {\n pid: entry.pid,\n ppid: entry.ppid ?? 0,\n pgid,\n user:\n uid === undefined ? null : (this.#users.get(uid) ?? (uid === 0 ? 'root' : `uid ${uid}`)),\n agentId: groups.get(pgid) ?? null,\n command: commandLine(argv),\n state: zombie ? ('Z' as const) : ('S' as const),\n started: entry.started ?? 0,\n zombie: zombie ? this.#since(`zombie:${entry.pid}`) : null,\n tty: Boolean(entry.tty),\n rss: typeof entry.rss === 'number' ? entry.rss : null,\n ...(timed(entry)\n ? { utime: entry.utime, stime: entry.stime, cpu: this.#cpu.shares.get(entry.pid) ?? null }\n : {}),\n };\n }\n\n #emit(): void {\n this.#lastBuild = this.#settings.now();\n this.#next = new Map();\n const registry = this.#options.agents.state();\n const views = this.#options.views.value ?? {};\n const agents = liveAgents(registry);\n const details = new Map<string, string>();\n const rows: MonitorAgent[] = agents.map((agent) =>\n this.#agent(agent, views[agent.id], details)\n );\n for (const scoop of Object.values(registry.scoops))\n details.set(String(scoop.anchor), scoop.name);\n const owners = conversations(registry);\n const tasks: MonitorTask[] = Object.values(this.#graph.tasks).map((node) =>\n this.#task(node, owners, details)\n );\n const groups = new Map<number, string>();\n for (const agent of agents)\n for (const pgid of this.#options.groups?.owned(agent.conversation) ?? [])\n groups.set(pgid, agent.id);\n const programs = new Map<number, string>();\n const entries = this.#processes;\n const processes: MonitorProcess[] | null =\n entries?.map((entry) => this.#process(entry, groups, programs)) ?? null;\n if (entries?.some(timed))\n for (const row of rows) row.cpu = agentCpu(processes as MonitorProcess[], row.id);\n this.#seen = this.#next;\n const feed: MonitorFeed = {\n open: true,\n at: this.#lastBuild,\n usage: this.#usage ? totals(this.#usage) : null,\n agents: rows,\n tasks,\n processes,\n kernel: this.#kernel,\n problems: [...this.#problems],\n paused: this.#paused,\n };\n const key = JSON.stringify({ ...feed, at: 0 });\n if (key === this.#last) return;\n this.#last = key;\n this.#publish(feed, narrowed(feed, programs));\n }\n}\n\nexport function createMonitor(options: MonitorOptions): Monitor {\n const settings: Settings = {\n now: options.now ?? Date.now,\n timers: options.timers ?? globalTimers,\n context: options.context ?? BACKGROUND_CONTEXT,\n psMs: options.psMs ?? PS_MS,\n psTimeoutMs: options.psTimeoutMs ?? PS_TIMEOUT_MS,\n throttleMs: options.throttleMs ?? THROTTLE_MS,\n usageMs: options.usageMs ?? USAGE_MS,\n };\n const { now, timers, context } = settings;\n const ttlMs = options.ttlMs ?? TTL_MS;\n const state = replicatedState<MonitorFeed>(emptyFeed());\n const viewState = replicatedState<MonitorFeed>(emptyFeed());\n const leases = new Map<string, number>();\n let session: Session | undefined;\n let sweep: unknown;\n const publish = (feed: MonitorFeed, view: MonitorFeed) => {\n state.replace(context, feed);\n viewState.replace(context, view);\n };\n const stop = () => {\n if (sweep !== undefined) timers.clear(sweep);\n sweep = undefined;\n const current = session;\n if (!current) return;\n session = undefined;\n current.stop();\n publish(\n { ...(state.value as MonitorFeed), open: false },\n { ...(viewState.value as MonitorFeed), open: false }\n );\n };\n const arm = () => {\n if (sweep !== undefined) timers.clear(sweep);\n sweep = timers.set(expire, Math.max(0, Math.min(...leases.values()) - now()));\n };\n const expire = () => {\n sweep = undefined;\n const at = now();\n for (const [lease, until] of [...leases]) if (until <= at) leases.delete(lease);\n if (leases.size) arm();\n else stop();\n };\n return {\n state,\n viewState,\n async open(lease, resync) {\n const id = lease ?? crypto.randomUUID();\n leases.set(id, now() + ttlMs);\n arm();\n if (!session) {\n session = new Session(options, settings, publish);\n session.start();\n } else if (resync) session.refresh();\n return { lease: id, ttlMs };\n },\n async close(lease) {\n if (!leases.delete(lease)) return;\n if (leases.size) arm();\n else stop();\n },\n active: () => session !== undefined,\n async dispose() {\n leases.clear();\n stop();\n },\n };\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import {\n type Context,\n type JsonValue,\n type ReplicatedState,\n replicatedState,\n} from '@earendil-works/chord';\nimport { BACKGROUND_CONTEXT } from '@earendil-works/chord/context';\nimport type {\n CommitPublication,\n ConversationView,\n Harness,\n TaskGraph,\n TaskGraphNode,\n TaskGraphWatch,\n TaskRecord,\n UsageState,\n} from '@earendil-works/pi-durable';\nimport { type AgentsState, live } from '../agents.ts';\nimport { contextTokens } from '../context.ts';\nimport type {\n MonitorAgent,\n MonitorFeed,\n MonitorLease,\n MonitorProblem,\n MonitorProcess,\n MonitorTask,\n} from '../services.ts';\nimport { commandLine, program, redacted } from './redact.ts';\n\nexport interface PsEntry {\n pid: number;\n ppid?: number;\n pgid?: number;\n argv?: readonly string[];\n tty?: unknown;\n started?: number;\n state?: string;\n uid?: number;\n rss?: number;\n utime?: number;\n stime?: number;\n}\n\nexport interface CpuMark {\n started: number;\n total: number;\n at: number;\n}\n\nexport interface CpuReading {\n marks: Map<number, CpuMark>;\n shares: Map<number, number | null>;\n}\n\nfunction timed(entry: PsEntry): entry is PsEntry & { utime: number; stime: number } {\n return typeof entry.utime === 'number' && typeof entry.stime === 'number';\n}\n\nexport function cpuShares(\n entries: readonly PsEntry[],\n previous: ReadonlyMap<number, CpuMark>,\n at: number\n): CpuReading {\n const marks = new Map<number, CpuMark>();\n const shares = new Map<number, number | null>();\n for (const entry of entries) {\n if (!timed(entry)) continue;\n const mark = { started: entry.started ?? 0, total: entry.utime + entry.stime, at };\n const last = previous.get(entry.pid);\n marks.set(entry.pid, mark);\n shares.set(\n entry.pid,\n last && last.started === mark.started && mark.total >= last.total && at > last.at\n ? Math.round(((mark.total - last.total) / (at - last.at)) * 1000) / 1000\n : null\n );\n }\n return { marks, shares };\n}\n\nexport function agentCpu(processes: readonly MonitorProcess[], agentId: string): number | null {\n if (!processes.some((row) => typeof row.cpu === 'number')) return null;\n let sum = 0;\n for (const row of processes) if (row.agentId === agentId) sum += row.cpu ?? 0;\n return Math.round(sum * 1000) / 1000;\n}\n\nexport interface Timers {\n set(run: () => void, ms: number): unknown;\n clear(handle: unknown): void;\n}\n\nexport interface MonitorOptions {\n harness: Pick<Harness, 'watchTaskGraph' | 'usage' | 'subscribeCommits' | 'inspect'>;\n agents: { state(): Readonly<AgentsState>; onChange(listener: () => void): () => void };\n views: ReplicatedState<Record<string, ConversationView>>;\n kernel?: { ps?(): Promise<readonly PsEntry[]> };\n groups?: { owned(owner: number): number[] };\n users?: () => Promise<readonly { name: string; uid: number }[]>;\n now?: () => number;\n timers?: Timers;\n ttlMs?: number;\n psMs?: number;\n psTimeoutMs?: number;\n throttleMs?: number;\n usageMs?: number;\n context?: Context;\n}\n\nexport interface Monitor {\n readonly state: ReplicatedState<MonitorFeed>;\n readonly viewState: ReplicatedState<MonitorFeed>;\n open(lease: string | null, resync: boolean, context: Context): Promise<MonitorLease>;\n close(lease: string, context: Context): Promise<void>;\n active(): boolean;\n dispose(): Promise<void>;\n}\n\nexport const TTL_MS = 30000;\nexport const PS_MS = 2000;\nexport const PS_TIMEOUT_MS = 5000;\nexport const THROTTLE_MS = 1000;\nexport const USAGE_MS = 2000;\nexport const PROBLEMS_MAX = 50;\n\nexport const globalTimers: Timers = {\n set: (run, ms) => setTimeout(run, ms),\n clear: (handle) => clearTimeout(handle as ReturnType<typeof setTimeout>),\n};\n\nexport function emptyFeed(): MonitorFeed {\n return {\n open: false,\n at: 0,\n usage: null,\n agents: [],\n tasks: [],\n processes: null,\n kernel: 'checking',\n problems: [],\n paused: false,\n };\n}\n\ninterface Live {\n run?: unknown;\n generation?: { attempt: number; retry?: { at: number; error: string } };\n tools?: { name: string; status: string; taskId?: unknown }[];\n}\n\ninterface ModelMessage {\n stopReason?: string;\n errorMessage?: string;\n}\n\nexport function totals(usage: UsageState): { tokens: number; cost: number; free: boolean } {\n let tokens = 0;\n let cost = 0;\n for (const bucket of [usage.models, usage.tools])\n for (const entry of Object.values(bucket)) {\n tokens += entry.totalTokens ?? 0;\n cost += entry.cost?.total ?? 0;\n }\n return { tokens, cost, free: cost === 0 };\n}\n\nfunction phaseOf(node: TaskGraphNode): string {\n return 'phase' in node.state ? node.state.phase : node.state.outcome;\n}\n\nfunction lastAssistant(view: ConversationView | undefined) {\n const entries = view?.entries ?? [];\n for (let at = entries.length - 1; at >= 0; at--) {\n const entry = entries[at] as (typeof entries)[number];\n if (entry.kind === 'pi.assistant')\n return { id: String(entry.id), message: (entry.model?.[0] ?? {}) as ModelMessage };\n }\n return undefined;\n}\n\nfunction conversations(state: Readonly<AgentsState>): Map<number, string> {\n const out = new Map<number, string>();\n for (const [id, scoop] of Object.entries(state.scoops)) out.set(scoop.conversation, id);\n for (const [id, cone] of Object.entries(state.cones)) out.set(cone.conversation, id);\n return out;\n}\n\nfunction liveAgents(state: Readonly<AgentsState>) {\n const cones = Object.entries(state.cones).map(([id, cone]) => ({\n id,\n name: cone.name,\n kind: 'cone' as const,\n parentId: null,\n conversation: cone.conversation,\n }));\n const scoops = Object.entries(state.scoops)\n .filter(([, scoop]) => live(scoop))\n .map(([id, scoop]) => ({\n id,\n name: scoop.name,\n kind: 'scoop' as const,\n parentId: scoop.parent,\n conversation: scoop.conversation,\n }));\n return [...cones, ...scoops];\n}\n\nexport function narrowed(feed: MonitorFeed, programs: ReadonlyMap<number, string>): MonitorFeed {\n if (!feed.processes) return feed;\n return {\n ...feed,\n processes: feed.processes.map((row) => ({ ...row, command: programs.get(row.pid) ?? '' })),\n };\n}\n\ninterface Settings {\n now: () => number;\n timers: Timers;\n context: Context;\n psMs: number;\n psTimeoutMs: number;\n throttleMs: number;\n usageMs: number;\n}\n\ntype Agent = ReturnType<typeof liveAgents>[number];\n\nclass Session {\n stopped = false;\n readonly #options: MonitorOptions;\n readonly #settings: Settings;\n readonly #publish: (feed: MonitorFeed, view: MonitorFeed) => void;\n readonly #off: (() => void)[] = [];\n readonly #handles = new Set<unknown>();\n #watch: TaskGraphWatch | undefined;\n #graph: TaskGraph = { tasks: {} };\n #usage: UsageState | null = null;\n #processes: readonly PsEntry[] | null = null;\n #cpu: CpuReading = { marks: new Map(), shares: new Map() };\n #kernel: MonitorFeed['kernel'];\n #users = new Map<number, string>();\n #paused = false;\n #problems: MonitorProblem[] = [];\n #seen = new Map<string, number>();\n #next = new Map<string, number>();\n #build: unknown;\n #usageTimer: unknown;\n #lastBuild = Number.NEGATIVE_INFINITY;\n #last = '';\n\n constructor(\n options: MonitorOptions,\n settings: Settings,\n publish: (feed: MonitorFeed, view: MonitorFeed) => void\n ) {\n this.#options = options;\n this.#settings = settings;\n this.#publish = publish;\n this.#kernel = options.kernel?.ps ? 'checking' : 'unreachable';\n }\n\n start(): void {\n const options = this.#options;\n this.#off.push(\n options.harness.subscribeCommits((publication) => this.#commit(publication)),\n options.agents.onChange(() => this.schedule()),\n options.views.subscribe(() => this.schedule())\n );\n void options.harness.watchTaskGraph(this.#settings.context).then(\n (watch) => this.#watching(watch),\n () => undefined\n );\n void options.users?.().then(\n (list) => {\n this.#users = new Map(list.map((user) => [user.uid, user.name]));\n this.schedule();\n },\n () => undefined\n );\n this.#poll();\n this.refresh();\n }\n\n stop(): void {\n this.stopped = true;\n for (const off of this.#off) off();\n for (const handle of this.#handles) this.#settings.timers.clear(handle);\n this.#handles.clear();\n void this.#watch?.stop();\n }\n\n refresh(): void {\n void this.#readUsage();\n void this.#options.harness.inspect(this.#settings.context).then(\n (inspection) => {\n this.#paused = inspection.scheduling === 'paused';\n this.schedule();\n },\n () => undefined\n );\n this.schedule();\n }\n\n schedule(): void {\n if (this.stopped || this.#build !== undefined) return;\n const wait = Math.max(0, this.#lastBuild + this.#settings.throttleMs - this.#settings.now());\n this.#build = this.#later(() => {\n this.#build = undefined;\n this.#emit();\n }, wait);\n }\n\n #later(task: () => void, ms: number): unknown {\n if (this.stopped) return undefined;\n const handle = this.#settings.timers.set(() => {\n this.#handles.delete(handle);\n if (!this.stopped) task();\n }, ms);\n this.#handles.add(handle);\n return handle;\n }\n\n #watching(watch: TaskGraphWatch): void {\n if (this.stopped) {\n void watch.stop();\n return;\n }\n this.#watch = watch;\n this.#graph = watch.value;\n this.schedule();\n watch.start(async (graph) => {\n this.#graph = graph;\n this.schedule();\n });\n }\n\n #readUsage(): Promise<void> {\n return this.#options.harness.usage(this.#settings.context).then(\n (usage) => {\n this.#usage = usage;\n this.schedule();\n },\n () => undefined\n );\n }\n\n #poll(): void {\n const kernel = this.#options.kernel;\n if (!kernel?.ps) return;\n let settled = false;\n const settle = (entries: readonly PsEntry[] | null) => {\n if (settled || this.stopped) return;\n settled = true;\n this.#settings.timers.clear(timeout);\n this.#handles.delete(timeout);\n this.#processes = entries;\n if (entries) this.#cpu = cpuShares(entries, this.#cpu.marks, this.#settings.now());\n this.#kernel = entries ? 'ok' : 'unreachable';\n this.schedule();\n this.#later(() => this.#poll(), this.#settings.psMs);\n };\n const timeout = this.#later(() => settle(null), this.#settings.psTimeoutMs);\n kernel.ps().then(settle, () => settle(null));\n }\n\n #problem(entry: MonitorProblem): void {\n if (this.#problems.some((known) => known.id === entry.id)) return;\n this.#problems.unshift(entry);\n this.#problems.length = Math.min(this.#problems.length, PROBLEMS_MAX);\n }\n\n #commit(publication: CommitPublication): void {\n let changed = false;\n for (const change of publication.changes) {\n if (change.type === 'task') changed = this.#failed(change.value) || changed;\n else if (change.type === 'document' && change.record.kind === 'pi.usage')\n this.#usageChanged();\n }\n if (changed) this.schedule();\n }\n\n #failed(task: TaskRecord<JsonValue, JsonValue, JsonValue>): boolean {\n if (task.state.status !== 'terminal') return false;\n const outcome = task.state.outcome;\n if (outcome.status !== 'faulted' && outcome.status !== 'orphaned') return false;\n this.#problem({\n id: `task:${task.id}`,\n at: this.#settings.now(),\n agentId: conversations(this.#options.agents.state()).get(task.conversationId) ?? null,\n kind: 'task',\n message: redacted(outcome.status === 'faulted' ? outcome.error.message : outcome.reason),\n });\n return true;\n }\n\n #usageChanged(): void {\n if (this.#usageTimer !== undefined) return;\n this.#usageTimer = this.#later(() => {\n this.#usageTimer = undefined;\n void this.#readUsage();\n }, this.#settings.usageMs);\n }\n\n #since(key: string): number {\n const at = this.#seen.get(key) ?? this.#settings.now();\n this.#next.set(key, at);\n return at;\n }\n\n #agent(agent: Agent, view: ConversationView | undefined, tools: Map<string, string>) {\n const liveDoc = view?.docs['pi.live'] as Live | undefined;\n for (const slot of liveDoc?.tools ?? [])\n if (slot.taskId !== undefined) tools.set(String(slot.taskId), slot.name);\n const inbox = view?.docs['pi.inbox'] as { items?: unknown[] } | undefined;\n const doc = view?.docs['pi.agent'] as\n | { model?: { provider: string; modelId: string } }\n | undefined;\n const generation = liveDoc?.generation;\n const retry = generation?.retry\n ? {\n attempt: generation.attempt,\n since: this.#since(`retry:${agent.id}`),\n error: redacted(generation.retry.error),\n }\n : null;\n if (retry)\n this.#problem({\n id: `retry:${agent.id}:${retry.since}`,\n at: retry.since,\n agentId: agent.id,\n kind: 'retry',\n message: retry.error,\n });\n const last = lastAssistant(view);\n const failed =\n last?.message.stopReason === 'error' ? redacted(last.message.errorMessage || 'error') : null;\n if (failed && last)\n this.#problem({\n id: `turn:${last.id}`,\n at: this.#settings.now(),\n agentId: agent.id,\n kind: 'turn',\n message: failed,\n });\n return {\n id: agent.id,\n name: agent.name,\n kind: agent.kind,\n parentId: agent.parentId,\n model: doc?.model ? `${doc.model.provider}/${doc.model.modelId}` : null,\n running: Boolean(liveDoc?.run),\n queued: inbox?.items?.length ?? 0,\n tool: liveDoc?.tools?.find((slot) => slot.status === 'running')?.name ?? null,\n retry,\n failed,\n context: contextTokens(view),\n };\n }\n\n #task(node: TaskGraphNode, owners: Map<number, string>, details: Map<string, string>) {\n const id = String(node.id);\n const status = node.state.status;\n return {\n id,\n kind: node.kind,\n detail: details.get(id) ?? null,\n agentId: owners.get(node.conversationId) ?? null,\n owner: node.owner === undefined ? null : String(node.owner),\n status,\n phase: phaseOf(node),\n background: node.background,\n aborting: node.abortRequested ? this.#since(`abort:${id}`) : null,\n since: this.#since(`task:${id}:${status}`),\n waits: node.state.status === 'waiting' ? node.state.on.length : 0,\n until: node.state.status === 'sleeping' ? node.state.until : null,\n blocked: node.blocked ?? null,\n };\n }\n\n #process(entry: PsEntry, groups: Map<number, string>, programs: Map<number, string>) {\n const argv = entry.argv ?? [];\n const pgid = entry.pgid ?? entry.pid;\n const zombie = entry.state === 'Z';\n programs.set(entry.pid, program(argv));\n const uid = entry.uid;\n return {\n pid: entry.pid,\n ppid: entry.ppid ?? 0,\n pgid,\n user:\n uid === undefined ? null : (this.#users.get(uid) ?? (uid === 0 ? 'root' : `uid ${uid}`)),\n agentId: groups.get(pgid) ?? null,\n command: commandLine(argv),\n state: zombie ? ('Z' as const) : ('S' as const),\n started: entry.started ?? 0,\n zombie: zombie ? this.#since(`zombie:${entry.pid}`) : null,\n tty: Boolean(entry.tty),\n rss: typeof entry.rss === 'number' ? entry.rss : null,\n ...(timed(entry)\n ? { utime: entry.utime, stime: entry.stime, cpu: this.#cpu.shares.get(entry.pid) ?? null }\n : {}),\n };\n }\n\n #emit(): void {\n this.#lastBuild = this.#settings.now();\n this.#next = new Map();\n const registry = this.#options.agents.state();\n const views = this.#options.views.value ?? {};\n const agents = liveAgents(registry);\n const details = new Map<string, string>();\n const rows: MonitorAgent[] = agents.map((agent) =>\n this.#agent(agent, views[agent.id], details)\n );\n for (const scoop of Object.values(registry.scoops))\n details.set(String(scoop.anchor), scoop.name);\n const owners = conversations(registry);\n const tasks: MonitorTask[] = Object.values(this.#graph.tasks).map((node) =>\n this.#task(node, owners, details)\n );\n const groups = new Map<number, string>();\n for (const agent of agents)\n for (const pgid of this.#options.groups?.owned(agent.conversation) ?? [])\n groups.set(pgid, agent.id);\n const programs = new Map<number, string>();\n const entries = this.#processes;\n const processes: MonitorProcess[] | null =\n entries?.map((entry) => this.#process(entry, groups, programs)) ?? null;\n if (entries?.some(timed))\n for (const row of rows) row.cpu = agentCpu(processes as MonitorProcess[], row.id);\n this.#seen = this.#next;\n const feed: MonitorFeed = {\n open: true,\n at: this.#lastBuild,\n usage: this.#usage ? totals(this.#usage) : null,\n agents: rows,\n tasks,\n processes,\n kernel: this.#kernel,\n problems: [...this.#problems],\n paused: this.#paused,\n };\n const key = JSON.stringify({ ...feed, at: 0 });\n if (key === this.#last) return;\n this.#last = key;\n this.#publish(feed, narrowed(feed, programs));\n }\n}\n\nexport function createMonitor(options: MonitorOptions): Monitor {\n const settings: Settings = {\n now: options.now ?? Date.now,\n timers: options.timers ?? globalTimers,\n context: options.context ?? BACKGROUND_CONTEXT,\n psMs: options.psMs ?? PS_MS,\n psTimeoutMs: options.psTimeoutMs ?? PS_TIMEOUT_MS,\n throttleMs: options.throttleMs ?? THROTTLE_MS,\n usageMs: options.usageMs ?? USAGE_MS,\n };\n const { now, timers, context } = settings;\n const ttlMs = options.ttlMs ?? TTL_MS;\n const state = replicatedState<MonitorFeed>(emptyFeed());\n const viewState = replicatedState<MonitorFeed>(emptyFeed());\n const leases = new Map<string, number>();\n let session: Session | undefined;\n let sweep: unknown;\n const publish = (feed: MonitorFeed, view: MonitorFeed) => {\n state.replace(context, feed);\n viewState.replace(context, view);\n };\n const stop = () => {\n if (sweep !== undefined) timers.clear(sweep);\n sweep = undefined;\n const current = session;\n if (!current) return;\n session = undefined;\n current.stop();\n publish(\n { ...(state.value as MonitorFeed), open: false },\n { ...(viewState.value as MonitorFeed), open: false }\n );\n };\n const arm = () => {\n if (sweep !== undefined) timers.clear(sweep);\n sweep = timers.set(expire, Math.max(0, Math.min(...leases.values()) - now()));\n };\n const expire = () => {\n sweep = undefined;\n const at = now();\n for (const [lease, until] of [...leases]) if (until <= at) leases.delete(lease);\n if (leases.size) arm();\n else stop();\n };\n return {\n state,\n viewState,\n async open(lease, resync) {\n const id = lease ?? crypto.randomUUID();\n leases.set(id, now() + ttlMs);\n arm();\n if (!session) {\n session = new Session(options, settings, publish);\n session.start();\n } else if (resync) session.refresh();\n return { lease: id, ttlMs };\n },\n async close(lease) {\n if (!leases.delete(lease)) return;\n if (leases.size) arm();\n else stop();\n },\n active: () => session !== undefined,\n async dispose() {\n leases.clear();\n stop();\n },\n };\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
package/dist/pause.js ADDED
@@ -0,0 +1,26 @@
1
+ function tick(clock, delayMs, signal) {
2
+ return new Promise((resolve, reject) => {
3
+ const abort = () => {
4
+ disarm();
5
+ reject(signal?.reason);
6
+ };
7
+ const disarm = clock.setTimer(() => {
8
+ signal?.removeEventListener("abort", abort);
9
+ resolve();
10
+ }, delayMs);
11
+ signal?.addEventListener("abort", abort, { once: true });
12
+ });
13
+ }
14
+ async function pauseUntil(clock, until, context) {
15
+ const signal = context.abortSignal;
16
+ for (; ; ) {
17
+ signal?.throwIfAborted();
18
+ const remaining = until - clock.now();
19
+ if (remaining <= 0) return;
20
+ await tick(clock, remaining, signal);
21
+ }
22
+ }
23
+ export {
24
+ pauseUntil
25
+ };
26
+ //# sourceMappingURL=pause.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/pause.ts"],
4
+ "sourcesContent": ["import type { Context } from '@earendil-works/chord';\n\nexport interface PhaseClock {\n now(): number;\n setTimer(callback: () => void, delayMs: number): () => void;\n}\n\nfunction tick(clock: PhaseClock, delayMs: number, signal: AbortSignal | undefined) {\n return new Promise<void>((resolve, reject) => {\n const abort = () => {\n disarm();\n reject(signal?.reason);\n };\n const disarm = clock.setTimer(() => {\n signal?.removeEventListener('abort', abort);\n resolve();\n }, delayMs);\n signal?.addEventListener('abort', abort, { once: true });\n });\n}\n\nexport async function pauseUntil(clock: PhaseClock, until: number, context: Context) {\n const signal = context.abortSignal;\n for (;;) {\n signal?.throwIfAborted();\n const remaining = until - clock.now();\n if (remaining <= 0) return;\n await tick(clock, remaining, signal);\n }\n}\n"],
5
+ "mappings": "AAOA,SAAS,KAAK,OAAmB,SAAiB,QAAiC;AACjF,SAAO,IAAI,QAAc,CAAC,SAAS,WAAW;AAC5C,UAAM,QAAQ,MAAM;AAClB,aAAO;AACP,aAAO,QAAQ,MAAM;AAAA,IACvB;AACA,UAAM,SAAS,MAAM,SAAS,MAAM;AAClC,cAAQ,oBAAoB,SAAS,KAAK;AAC1C,cAAQ;AAAA,IACV,GAAG,OAAO;AACV,YAAQ,iBAAiB,SAAS,OAAO,EAAE,MAAM,KAAK,CAAC;AAAA,EACzD,CAAC;AACH;AAEA,eAAsB,WAAW,OAAmB,OAAe,SAAkB;AACnF,QAAM,SAAS,QAAQ;AACvB,aAAS;AACP,YAAQ,eAAe;AACvB,UAAM,YAAY,QAAQ,MAAM,IAAI;AACpC,QAAI,aAAa,EAAG;AACpB,UAAM,KAAK,OAAO,WAAW,MAAM;AAAA,EACrC;AACF;",
6
+ "names": []
7
+ }
package/dist/preferred.js CHANGED
@@ -5,7 +5,7 @@ const PREFERRED_MODELS = {
5
5
  anthropic: "claude-opus-4-8",
6
6
  openai: "gpt-5.5",
7
7
  azure: "gpt-5.4",
8
- nvidia: "nvidia/nemotron-3-ultra-550b-a55b",
8
+ nvidia: "nvidia/nemotron-3.5-super-vl-120b-a12b",
9
9
  deepseek: "deepseek-v4-pro",
10
10
  google: "gemini-3.1-pro-preview",
11
11
  "google-vertex": "gemini-3.1-pro-preview",
@@ -28,17 +28,17 @@ const PREFERRED_MODELS = {
28
28
  opencode: "kimi-k2.6",
29
29
  "opencode-go": "kimi-k3",
30
30
  "kimi-coding": "kimi-for-coding",
31
- "openai-codex": "gpt-5.5",
31
+ "openai-codex": "gpt-6.1-sol",
32
32
  meta: "muse-spark-1.3",
33
33
  "cloudflare-workers-ai": "@cf/moonshotai/kimi-k2.6",
34
34
  "cloudflare-ai-gateway": "workers-ai/@cf/moonshotai/kimi-k2.6",
35
35
  "qwen-token-plan": "qwen3.7-max",
36
36
  "qwen-token-plan-cn": "qwen3.7-max",
37
37
  "qwen-token-plan-individual": "qwen3.8-max",
38
- xiaomi: "mimo-v2.5-pro",
39
- "xiaomi-token-plan-cn": "mimo-v2.5-pro",
40
- "xiaomi-token-plan-ams": "mimo-v2.5-pro",
41
- "xiaomi-token-plan-sgp": "mimo-v2.5-pro"
38
+ xiaomi: "mimo-v2.6-pro",
39
+ "xiaomi-token-plan-cn": "mimo-v2.6-pro",
40
+ "xiaomi-token-plan-ams": "mimo-v2.6-pro",
41
+ "xiaomi-token-plan-sgp": "mimo-v2.6-pro"
42
42
  };
43
43
  function preferredModel(providerId, available) {
44
44
  const preferredId = PREFERRED_MODELS[providerId];
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/preferred.ts"],
4
- "sourcesContent": ["export const BEDROCK = 'amazon-bedrock';\n\nexport const PREFERRED_MODELS: Readonly<Record<string, string>> = {\n [BEDROCK]: 'us.anthropic.claude-sonnet-5-5',\n 'ant-ling': 'Ring-2.6-1T',\n anthropic: 'claude-opus-4-8',\n openai: 'gpt-5.5',\n azure: 'gpt-5.4',\n nvidia: 'nvidia/nemotron-3-ultra-550b-a55b',\n deepseek: 'deepseek-v4-pro',\n google: 'gemini-3.1-pro-preview',\n 'google-vertex': 'gemini-3.1-pro-preview',\n openrouter: 'moonshotai/kimi-k2.6',\n 'vercel-ai-gateway': 'zai/glm-5.1',\n xai: 'grok-4.7',\n groq: 'openai/gpt-oss-120b',\n cerebras: 'gpt-oss-120b',\n zai: 'glm-5.3',\n 'zai-coding-cn': 'glm-5.3',\n mistral: 'devstral-medium-latest',\n minimax: 'MiniMax-M2.7',\n 'minimax-cn': 'MiniMax-M2.7',\n moonshotai: 'kimi-k2.6',\n 'moonshotai-cn': 'kimi-k2.6',\n huggingface: 'moonshotai/Kimi-K2.6',\n fireworks: 'accounts/fireworks/models/kimi-k3',\n together: 'moonshotai/Kimi-K3',\n baseten: 'zai-org/GLM-5.2',\n opencode: 'kimi-k2.6',\n 'opencode-go': 'kimi-k3',\n 'kimi-coding': 'kimi-for-coding',\n 'openai-codex': 'gpt-5.5',\n meta: 'muse-spark-1.3',\n 'cloudflare-workers-ai': '@cf/moonshotai/kimi-k2.6',\n 'cloudflare-ai-gateway': 'workers-ai/@cf/moonshotai/kimi-k2.6',\n 'qwen-token-plan': 'qwen3.7-max',\n 'qwen-token-plan-cn': 'qwen3.7-max',\n 'qwen-token-plan-individual': 'qwen3.8-max',\n xiaomi: 'mimo-v2.5-pro',\n 'xiaomi-token-plan-cn': 'mimo-v2.5-pro',\n 'xiaomi-token-plan-ams': 'mimo-v2.5-pro',\n 'xiaomi-token-plan-sgp': 'mimo-v2.5-pro',\n};\n\nexport function preferredModel<T extends { id: string }>(\n providerId: string,\n available: readonly T[]\n): T | undefined {\n const preferredId = PREFERRED_MODELS[providerId];\n return available.find((model) => model.id === preferredId) ?? available[0];\n}\n"],
4
+ "sourcesContent": ["export const BEDROCK = 'amazon-bedrock';\n\nexport const PREFERRED_MODELS: Readonly<Record<string, string>> = {\n [BEDROCK]: 'us.anthropic.claude-sonnet-5-5',\n 'ant-ling': 'Ring-2.6-1T',\n anthropic: 'claude-opus-4-8',\n openai: 'gpt-5.5',\n azure: 'gpt-5.4',\n nvidia: 'nvidia/nemotron-3.5-super-vl-120b-a12b',\n deepseek: 'deepseek-v4-pro',\n google: 'gemini-3.1-pro-preview',\n 'google-vertex': 'gemini-3.1-pro-preview',\n openrouter: 'moonshotai/kimi-k2.6',\n 'vercel-ai-gateway': 'zai/glm-5.1',\n xai: 'grok-4.7',\n groq: 'openai/gpt-oss-120b',\n cerebras: 'gpt-oss-120b',\n zai: 'glm-5.3',\n 'zai-coding-cn': 'glm-5.3',\n mistral: 'devstral-medium-latest',\n minimax: 'MiniMax-M2.7',\n 'minimax-cn': 'MiniMax-M2.7',\n moonshotai: 'kimi-k2.6',\n 'moonshotai-cn': 'kimi-k2.6',\n huggingface: 'moonshotai/Kimi-K2.6',\n fireworks: 'accounts/fireworks/models/kimi-k3',\n together: 'moonshotai/Kimi-K3',\n baseten: 'zai-org/GLM-5.2',\n opencode: 'kimi-k2.6',\n 'opencode-go': 'kimi-k3',\n 'kimi-coding': 'kimi-for-coding',\n 'openai-codex': 'gpt-6.1-sol',\n meta: 'muse-spark-1.3',\n 'cloudflare-workers-ai': '@cf/moonshotai/kimi-k2.6',\n 'cloudflare-ai-gateway': 'workers-ai/@cf/moonshotai/kimi-k2.6',\n 'qwen-token-plan': 'qwen3.7-max',\n 'qwen-token-plan-cn': 'qwen3.7-max',\n 'qwen-token-plan-individual': 'qwen3.8-max',\n xiaomi: 'mimo-v2.6-pro',\n 'xiaomi-token-plan-cn': 'mimo-v2.6-pro',\n 'xiaomi-token-plan-ams': 'mimo-v2.6-pro',\n 'xiaomi-token-plan-sgp': 'mimo-v2.6-pro',\n};\n\nexport function preferredModel<T extends { id: string }>(\n providerId: string,\n available: readonly T[]\n): T | undefined {\n const preferredId = PREFERRED_MODELS[providerId];\n return available.find((model) => model.id === preferredId) ?? available[0];\n}\n"],
5
5
  "mappings": "AAAO,MAAM,UAAU;AAEhB,MAAM,mBAAqD;AAAA,EAChE,CAAC,OAAO,GAAG;AAAA,EACX,YAAY;AAAA,EACZ,WAAW;AAAA,EACX,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,UAAU;AAAA,EACV,QAAQ;AAAA,EACR,iBAAiB;AAAA,EACjB,YAAY;AAAA,EACZ,qBAAqB;AAAA,EACrB,KAAK;AAAA,EACL,MAAM;AAAA,EACN,UAAU;AAAA,EACV,KAAK;AAAA,EACL,iBAAiB;AAAA,EACjB,SAAS;AAAA,EACT,SAAS;AAAA,EACT,cAAc;AAAA,EACd,YAAY;AAAA,EACZ,iBAAiB;AAAA,EACjB,aAAa;AAAA,EACb,WAAW;AAAA,EACX,UAAU;AAAA,EACV,SAAS;AAAA,EACT,UAAU;AAAA,EACV,eAAe;AAAA,EACf,eAAe;AAAA,EACf,gBAAgB;AAAA,EAChB,MAAM;AAAA,EACN,yBAAyB;AAAA,EACzB,yBAAyB;AAAA,EACzB,mBAAmB;AAAA,EACnB,sBAAsB;AAAA,EACtB,8BAA8B;AAAA,EAC9B,QAAQ;AAAA,EACR,wBAAwB;AAAA,EACxB,yBAAyB;AAAA,EACzB,yBAAyB;AAC3B;AAEO,SAAS,eACd,YACA,WACe;AACf,QAAM,cAAc,iBAAiB,UAAU;AAC/C,SAAO,UAAU,KAAK,CAAC,UAAU,MAAM,OAAO,WAAW,KAAK,UAAU,CAAC;AAC3E;",
6
6
  "names": []
7
7
  }
@@ -12,6 +12,7 @@ import { distinctTaints, runTaints } from "../guests/guard.js";
12
12
  import { HOME } from "../kernel/env.js";
13
13
  import { normalize, resolve } from "../kernel/paths.js";
14
14
  import { LEGACY } from "../layout.js";
15
+ import { pauseUntil } from "../pause.js";
15
16
  import { FROM_KIND } from "./from.js";
16
17
  import { ScoopWorkDoc } from "./service.js";
17
18
  import { cardOf } from "./summary.js";
@@ -103,7 +104,7 @@ function waitTask(lookup) {
103
104
  const open = state.scoops.filter((id) => !(id in state.done) && feeding.has(id));
104
105
  const expired = runtime.now() >= task.input.deadline;
105
106
  if (open.length && !expired) {
106
- await runtime.sleep(Math.min(runtime.now() + 300, task.input.deadline), context);
107
+ await pauseUntil(runtime, Math.min(runtime.now() + 300, task.input.deadline), context);
107
108
  continue;
108
109
  }
109
110
  const names = (id) => agents.state().scoops[id]?.name ?? id;