@ai-ecoverse/slicc-agent 7.4.1 → 7.5.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 (56) hide show
  1. package/README.md +2 -2
  2. package/dist/agent.js +7 -0
  3. package/dist/agent.js.map +2 -2
  4. package/dist/agents.js +2 -2
  5. package/dist/agents.js.map +2 -2
  6. package/dist/approvals/index.js +8 -3
  7. package/dist/approvals/index.js.map +2 -2
  8. package/dist/approvals/policy.js +2 -2
  9. package/dist/approvals/policy.js.map +2 -2
  10. package/dist/approvals/record.js +2 -2
  11. package/dist/approvals/record.js.map +2 -2
  12. package/dist/checkpoints.js +4 -0
  13. package/dist/checkpoints.js.map +2 -2
  14. package/dist/clock.js +1 -0
  15. package/dist/clock.js.map +7 -0
  16. package/dist/codemode/index.js +2 -2
  17. package/dist/codemode/index.js.map +2 -2
  18. package/dist/cone.js +2 -2
  19. package/dist/cone.js.map +2 -2
  20. package/dist/guests/guard.js +3 -3
  21. package/dist/guests/guard.js.map +2 -2
  22. package/dist/guests/review.js +2 -2
  23. package/dist/guests/review.js.map +2 -2
  24. package/dist/guests/scoop.js +3 -3
  25. package/dist/guests/scoop.js.map +2 -2
  26. package/dist/guests/seats.js +2 -2
  27. package/dist/guests/seats.js.map +2 -2
  28. package/dist/guests/tags.js +2 -2
  29. package/dist/guests/tags.js.map +2 -2
  30. package/dist/index.js.map +2 -2
  31. package/dist/licks/boot.js +2 -2
  32. package/dist/licks/boot.js.map +2 -2
  33. package/dist/licks/licks.js +2 -2
  34. package/dist/licks/licks.js.map +2 -2
  35. package/dist/mcp/allow.js +2 -2
  36. package/dist/mcp/allow.js.map +2 -2
  37. package/dist/memory/extract.js +3 -3
  38. package/dist/memory/extract.js.map +2 -2
  39. package/dist/scoops/service.js +2 -2
  40. package/dist/scoops/service.js.map +2 -2
  41. package/dist/secrets/legacy.js +2 -2
  42. package/dist/secrets/legacy.js.map +2 -2
  43. package/dist/selection.js +2 -2
  44. package/dist/selection.js.map +2 -2
  45. package/dist/sprinkles/sprinkles.js +2 -2
  46. package/dist/sprinkles/sprinkles.js.map +2 -2
  47. package/dist/titles/labels.js +2 -2
  48. package/dist/titles/labels.js.map +2 -2
  49. package/dist/types/agent.d.ts +2 -0
  50. package/dist/types/approvals/index.d.ts +2 -0
  51. package/dist/types/checkpoints.d.ts +3 -0
  52. package/dist/types/clock.d.ts +4 -0
  53. package/dist/types/index.d.ts +1 -0
  54. package/dist/workflow/run.js +2 -2
  55. package/dist/workflow/run.js.map +2 -2
  56. package/package.json +2 -2
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/guests/seats.ts"],
4
- "sourcesContent": ["import { defineDoc } from '@earendil-works/pi-durable';\nimport type { ApprovalTier, DecidedBy } from '../approvals/record.ts';\nimport { deltasOnly } from '../checkpoints.ts';\nimport {\n DAY_MS,\n DEFAULT_GUEST_POLICY,\n type GuestPolicy,\n gateKind,\n gateRefusal,\n MAX_SEAT_DAYS,\n} from './policy.ts';\n\nexport type GuestGate = ApprovalTier | 'off';\n\nexport type GuestGates = {\n messages: GuestGate;\n tools: GuestGate;\n};\n\nexport type GuestDraft = {\n id: string;\n label: string;\n agentId: string;\n expiresAt: number | null;\n gates: GuestGates;\n};\n\nexport type SeatSubmission = { id: string; approval: string | null; tools: GuestGate };\n\nexport type AlwaysTool = {\n approval: string;\n tool: string;\n argumentsHash: string;\n arguments: string;\n by: DecidedBy | null;\n at: number;\n};\n\nexport type ShownAlwaysTool = Omit<AlwaysTool, 'argumentsHash'>;\n\nexport type SeatRecord = GuestDraft & {\n createdAt: number;\n epoch: number;\n revoked: number | null;\n delivered?: string[];\n submissions?: SeatSubmission[];\n always?: { by: DecidedBy | null; approval: string; at: number };\n alwaysTools?: AlwaysTool[];\n};\n\nexport interface GuestSeat {\n id: string;\n label: string;\n agentId: string;\n createdAt: number;\n expiresAt: number | null;\n gates: GuestGates;\n connected: boolean;\n pending: number;\n alwaysTools: ShownAlwaysTool[];\n}\n\nexport type GuestsState = { seats: Record<string, SeatRecord> };\n\nexport const GuestsDoc = defineDoc<GuestsState>({\n kind: 'slicc.guests',\n checkpointWhen: deltasOnly,\n version: 1,\n scope: 'session',\n initial: () => ({ seats: {} }),\n});\n\nexport const MAX_SEAT_MS = MAX_SEAT_DAYS * DAY_MS;\nexport const MAX_LABEL = 64;\nconst MAX_ID = 256;\n\nexport function cleanLabel(raw: unknown): string {\n return String(raw ?? '')\n .replace(/[\\p{Cc}\\p{Cf}]/gu, ' ')\n .replace(/\\s+/g, ' ')\n .trim()\n .slice(0, MAX_LABEL);\n}\n\nexport function isGate(value: unknown): value is GuestGate {\n if (typeof value !== 'string') return false;\n if (value === 'user' || value === 'agent' || value === 'cone' || value === 'off') return true;\n return /^scoop:\\S+$/.test(value);\n}\n\nexport function checkDraft(\n draft: GuestDraft,\n now: number,\n policy: GuestPolicy = DEFAULT_GUEST_POLICY\n): string | null {\n if (typeof draft?.id !== 'string' || !draft.id || draft.id.length > MAX_ID)\n return 'A guest seat needs an id from the tray hub.';\n if (!cleanLabel(draft.label)) return 'A guest seat needs a label.';\n if (!isGate(draft.gates?.messages) || !isGate(draft.gates?.tools))\n return 'A guest seat needs a known gate for messages and for tools.';\n if (!policy.allowedMessageGates.includes(gateKind(draft.gates.messages)))\n return gateRefusal(\"a guest's messages\", policy.allowedMessageGates);\n if (!policy.allowedToolGates.includes(gateKind(draft.gates.tools)))\n return gateRefusal(\"a guest's tool calls\", policy.allowedToolGates);\n const { expiresAt } = draft;\n if (expiresAt !== null && (typeof expiresAt !== 'number' || !Number.isFinite(expiresAt)))\n return 'A guest seat expires at a time, or never.';\n if (expiresAt !== null && expiresAt <= now) return 'That expiry has already passed.';\n const days = policy.maxSeatDays;\n if (expiresAt !== null && expiresAt - now > days * DAY_MS)\n return `A guest seat lasts at most ${days} ${days === 1 ? 'day' : 'days'}.`;\n return null;\n}\n\nexport function liveSeat(seat: SeatRecord | undefined, now: number): boolean {\n return Boolean(\n seat && seat.revoked === null && (seat.expiresAt === null || seat.expiresAt > now)\n );\n}\n"],
5
- "mappings": "AAAA,SAAS,iBAAiB;AAE1B,SAAS,kBAAkB;AAC3B;AAAA,EACE;AAAA,EACA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAsDA,MAAM,YAAY,UAAuB;AAAA,EAC9C,MAAM;AAAA,EACN,gBAAgB;AAAA,EAChB,SAAS;AAAA,EACT,OAAO;AAAA,EACP,SAAS,OAAO,EAAE,OAAO,CAAC,EAAE;AAC9B,CAAC;AAEM,MAAM,cAAc,gBAAgB;AACpC,MAAM,YAAY;AACzB,MAAM,SAAS;AAER,SAAS,WAAW,KAAsB;AAC/C,SAAO,OAAO,OAAO,EAAE,EACpB,QAAQ,oBAAoB,GAAG,EAC/B,QAAQ,QAAQ,GAAG,EACnB,KAAK,EACL,MAAM,GAAG,SAAS;AACvB;AAEO,SAAS,OAAO,OAAoC;AACzD,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,MAAI,UAAU,UAAU,UAAU,WAAW,UAAU,UAAU,UAAU,MAAO,QAAO;AACzF,SAAO,cAAc,KAAK,KAAK;AACjC;AAEO,SAAS,WACd,OACA,KACA,SAAsB,sBACP;AACf,MAAI,OAAO,OAAO,OAAO,YAAY,CAAC,MAAM,MAAM,MAAM,GAAG,SAAS;AAClE,WAAO;AACT,MAAI,CAAC,WAAW,MAAM,KAAK,EAAG,QAAO;AACrC,MAAI,CAAC,OAAO,MAAM,OAAO,QAAQ,KAAK,CAAC,OAAO,MAAM,OAAO,KAAK;AAC9D,WAAO;AACT,MAAI,CAAC,OAAO,oBAAoB,SAAS,SAAS,MAAM,MAAM,QAAQ,CAAC;AACrE,WAAO,YAAY,sBAAsB,OAAO,mBAAmB;AACrE,MAAI,CAAC,OAAO,iBAAiB,SAAS,SAAS,MAAM,MAAM,KAAK,CAAC;AAC/D,WAAO,YAAY,wBAAwB,OAAO,gBAAgB;AACpE,QAAM,EAAE,UAAU,IAAI;AACtB,MAAI,cAAc,SAAS,OAAO,cAAc,YAAY,CAAC,OAAO,SAAS,SAAS;AACpF,WAAO;AACT,MAAI,cAAc,QAAQ,aAAa,IAAK,QAAO;AACnD,QAAM,OAAO,OAAO;AACpB,MAAI,cAAc,QAAQ,YAAY,MAAM,OAAO;AACjD,WAAO,8BAA8B,IAAI,IAAI,SAAS,IAAI,QAAQ,MAAM;AAC1E,SAAO;AACT;AAEO,SAAS,SAAS,MAA8B,KAAsB;AAC3E,SAAO;AAAA,IACL,QAAQ,KAAK,YAAY,SAAS,KAAK,cAAc,QAAQ,KAAK,YAAY;AAAA,EAChF;AACF;",
4
+ "sourcesContent": ["import { defineDoc } from '@earendil-works/pi-durable';\nimport type { ApprovalTier, DecidedBy } from '../approvals/record.ts';\nimport { boundedDeltas } from '../checkpoints.ts';\nimport {\n DAY_MS,\n DEFAULT_GUEST_POLICY,\n type GuestPolicy,\n gateKind,\n gateRefusal,\n MAX_SEAT_DAYS,\n} from './policy.ts';\n\nexport type GuestGate = ApprovalTier | 'off';\n\nexport type GuestGates = {\n messages: GuestGate;\n tools: GuestGate;\n};\n\nexport type GuestDraft = {\n id: string;\n label: string;\n agentId: string;\n expiresAt: number | null;\n gates: GuestGates;\n};\n\nexport type SeatSubmission = { id: string; approval: string | null; tools: GuestGate };\n\nexport type AlwaysTool = {\n approval: string;\n tool: string;\n argumentsHash: string;\n arguments: string;\n by: DecidedBy | null;\n at: number;\n};\n\nexport type ShownAlwaysTool = Omit<AlwaysTool, 'argumentsHash'>;\n\nexport type SeatRecord = GuestDraft & {\n createdAt: number;\n epoch: number;\n revoked: number | null;\n delivered?: string[];\n submissions?: SeatSubmission[];\n always?: { by: DecidedBy | null; approval: string; at: number };\n alwaysTools?: AlwaysTool[];\n};\n\nexport interface GuestSeat {\n id: string;\n label: string;\n agentId: string;\n createdAt: number;\n expiresAt: number | null;\n gates: GuestGates;\n connected: boolean;\n pending: number;\n alwaysTools: ShownAlwaysTool[];\n}\n\nexport type GuestsState = { seats: Record<string, SeatRecord> };\n\nexport const GuestsDoc = defineDoc<GuestsState>({\n kind: 'slicc.guests',\n checkpointWhen: boundedDeltas,\n version: 1,\n scope: 'session',\n initial: () => ({ seats: {} }),\n});\n\nexport const MAX_SEAT_MS = MAX_SEAT_DAYS * DAY_MS;\nexport const MAX_LABEL = 64;\nconst MAX_ID = 256;\n\nexport function cleanLabel(raw: unknown): string {\n return String(raw ?? '')\n .replace(/[\\p{Cc}\\p{Cf}]/gu, ' ')\n .replace(/\\s+/g, ' ')\n .trim()\n .slice(0, MAX_LABEL);\n}\n\nexport function isGate(value: unknown): value is GuestGate {\n if (typeof value !== 'string') return false;\n if (value === 'user' || value === 'agent' || value === 'cone' || value === 'off') return true;\n return /^scoop:\\S+$/.test(value);\n}\n\nexport function checkDraft(\n draft: GuestDraft,\n now: number,\n policy: GuestPolicy = DEFAULT_GUEST_POLICY\n): string | null {\n if (typeof draft?.id !== 'string' || !draft.id || draft.id.length > MAX_ID)\n return 'A guest seat needs an id from the tray hub.';\n if (!cleanLabel(draft.label)) return 'A guest seat needs a label.';\n if (!isGate(draft.gates?.messages) || !isGate(draft.gates?.tools))\n return 'A guest seat needs a known gate for messages and for tools.';\n if (!policy.allowedMessageGates.includes(gateKind(draft.gates.messages)))\n return gateRefusal(\"a guest's messages\", policy.allowedMessageGates);\n if (!policy.allowedToolGates.includes(gateKind(draft.gates.tools)))\n return gateRefusal(\"a guest's tool calls\", policy.allowedToolGates);\n const { expiresAt } = draft;\n if (expiresAt !== null && (typeof expiresAt !== 'number' || !Number.isFinite(expiresAt)))\n return 'A guest seat expires at a time, or never.';\n if (expiresAt !== null && expiresAt <= now) return 'That expiry has already passed.';\n const days = policy.maxSeatDays;\n if (expiresAt !== null && expiresAt - now > days * DAY_MS)\n return `A guest seat lasts at most ${days} ${days === 1 ? 'day' : 'days'}.`;\n return null;\n}\n\nexport function liveSeat(seat: SeatRecord | undefined, now: number): boolean {\n return Boolean(\n seat && seat.revoked === null && (seat.expiresAt === null || seat.expiresAt > now)\n );\n}\n"],
5
+ "mappings": "AAAA,SAAS,iBAAiB;AAE1B,SAAS,qBAAqB;AAC9B;AAAA,EACE;AAAA,EACA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAsDA,MAAM,YAAY,UAAuB;AAAA,EAC9C,MAAM;AAAA,EACN,gBAAgB;AAAA,EAChB,SAAS;AAAA,EACT,OAAO;AAAA,EACP,SAAS,OAAO,EAAE,OAAO,CAAC,EAAE;AAC9B,CAAC;AAEM,MAAM,cAAc,gBAAgB;AACpC,MAAM,YAAY;AACzB,MAAM,SAAS;AAER,SAAS,WAAW,KAAsB;AAC/C,SAAO,OAAO,OAAO,EAAE,EACpB,QAAQ,oBAAoB,GAAG,EAC/B,QAAQ,QAAQ,GAAG,EACnB,KAAK,EACL,MAAM,GAAG,SAAS;AACvB;AAEO,SAAS,OAAO,OAAoC;AACzD,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,MAAI,UAAU,UAAU,UAAU,WAAW,UAAU,UAAU,UAAU,MAAO,QAAO;AACzF,SAAO,cAAc,KAAK,KAAK;AACjC;AAEO,SAAS,WACd,OACA,KACA,SAAsB,sBACP;AACf,MAAI,OAAO,OAAO,OAAO,YAAY,CAAC,MAAM,MAAM,MAAM,GAAG,SAAS;AAClE,WAAO;AACT,MAAI,CAAC,WAAW,MAAM,KAAK,EAAG,QAAO;AACrC,MAAI,CAAC,OAAO,MAAM,OAAO,QAAQ,KAAK,CAAC,OAAO,MAAM,OAAO,KAAK;AAC9D,WAAO;AACT,MAAI,CAAC,OAAO,oBAAoB,SAAS,SAAS,MAAM,MAAM,QAAQ,CAAC;AACrE,WAAO,YAAY,sBAAsB,OAAO,mBAAmB;AACrE,MAAI,CAAC,OAAO,iBAAiB,SAAS,SAAS,MAAM,MAAM,KAAK,CAAC;AAC/D,WAAO,YAAY,wBAAwB,OAAO,gBAAgB;AACpE,QAAM,EAAE,UAAU,IAAI;AACtB,MAAI,cAAc,SAAS,OAAO,cAAc,YAAY,CAAC,OAAO,SAAS,SAAS;AACpF,WAAO;AACT,MAAI,cAAc,QAAQ,aAAa,IAAK,QAAO;AACnD,QAAM,OAAO,OAAO;AACpB,MAAI,cAAc,QAAQ,YAAY,MAAM,OAAO;AACjD,WAAO,8BAA8B,IAAI,IAAI,SAAS,IAAI,QAAQ,MAAM;AAC1E,SAAO;AACT;AAEO,SAAS,SAAS,MAA8B,KAAsB;AAC3E,SAAO;AAAA,IACL,QAAQ,KAAK,YAAY,SAAS,KAAK,cAAc,QAAQ,KAAK,YAAY;AAAA,EAChF;AACF;",
6
6
  "names": []
7
7
  }
@@ -1,10 +1,10 @@
1
1
  import { defineDoc } from "@earendil-works/pi-durable";
2
- import { deltasOnly } from "../checkpoints.js";
2
+ import { boundedDeltas } from "../checkpoints.js";
3
3
  import { TAINT_TAG } from "../kernel/sightings.js";
4
4
  import { canonical, distinctTaints, runTaints } from "./guard.js";
5
5
  const TaintTagDoc = defineDoc({
6
6
  kind: "slicc.taint-tags",
7
- checkpointWhen: deltasOnly,
7
+ checkpointWhen: boundedDeltas,
8
8
  version: 1,
9
9
  scope: "session",
10
10
  initial: () => ({ tags: {} })
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/guests/tags.ts"],
4
- "sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport { type ConversationId, defineDoc, type Harness } from '@earendil-works/pi-durable';\nimport { deltasOnly } from '../checkpoints.ts';\nimport { TAINT_TAG } from '../kernel/sightings.ts';\nimport { canonical, distinctTaints, type InheritedTaint, runTaints } from './guard.ts';\n\nexport const TaintTagDoc = defineDoc<{ tags: Record<string, InheritedTaint[]> }>({\n kind: 'slicc.taint-tags',\n checkpointWhen: deltasOnly,\n version: 1,\n scope: 'session',\n initial: () => ({ tags: {} }),\n});\n\nconst keyOf = (taints: readonly InheritedTaint[]) =>\n canonical(\n distinctTaints(taints)\n .map((taint) => [taint.seat, taint.tier])\n .sort()\n );\n\nexport function taintTag(\n harness: Pick<Harness, 'commit'>,\n taints: readonly InheritedTaint[],\n context: Context\n): Promise<string> {\n const key = keyOf(taints);\n return harness.commit(async (tx) => {\n const doc = await tx.doc(TaintTagDoc);\n const found = Object.entries(doc.tags).find(([, kept]) => keyOf(kept) === key)?.[0];\n if (found) return `${TAINT_TAG}${found}`;\n const id = crypto.randomUUID();\n doc.tags[id] = distinctTaints(taints);\n return `${TAINT_TAG}${id}`;\n }, context);\n}\n\nexport async function taggedTaints(\n harness: Pick<Harness, 'snapshot'>,\n tags: readonly string[],\n context: Context\n): Promise<InheritedTaint[]> {\n const ours = tags.filter((tag) => tag.startsWith(TAINT_TAG));\n if (!ours.length) return [];\n const kept = (await harness.snapshot(TaintTagDoc, context))?.tags ?? {};\n return distinctTaints(\n ours.flatMap((tag) => kept[tag.slice(TAINT_TAG.length)] ?? [{ seat: null, tier: '' }])\n );\n}\n\nexport function runTag(\n harness: () => Pick<Harness, 'commit' | 'submission' | 'snapshot'> | undefined\n) {\n return async (conversationId: number, context: Context): Promise<string | undefined> => {\n const held = harness();\n if (!held) return undefined;\n const taints = await runTaints(held, conversationId as ConversationId, context);\n return taints.length ? taintTag(held, taints, context) : undefined;\n };\n}\n"],
5
- "mappings": "AACA,SAA8B,iBAA+B;AAC7D,SAAS,kBAAkB;AAC3B,SAAS,iBAAiB;AAC1B,SAAS,WAAW,gBAAqC,iBAAiB;AAEnE,MAAM,cAAc,UAAsD;AAAA,EAC/E,MAAM;AAAA,EACN,gBAAgB;AAAA,EAChB,SAAS;AAAA,EACT,OAAO;AAAA,EACP,SAAS,OAAO,EAAE,MAAM,CAAC,EAAE;AAC7B,CAAC;AAED,MAAM,QAAQ,CAAC,WACb;AAAA,EACE,eAAe,MAAM,EAClB,IAAI,CAAC,UAAU,CAAC,MAAM,MAAM,MAAM,IAAI,CAAC,EACvC,KAAK;AACV;AAEK,SAAS,SACd,SACA,QACA,SACiB;AACjB,QAAM,MAAM,MAAM,MAAM;AACxB,SAAO,QAAQ,OAAO,OAAO,OAAO;AAClC,UAAM,MAAM,MAAM,GAAG,IAAI,WAAW;AACpC,UAAM,QAAQ,OAAO,QAAQ,IAAI,IAAI,EAAE,KAAK,CAAC,CAAC,EAAE,IAAI,MAAM,MAAM,IAAI,MAAM,GAAG,IAAI,CAAC;AAClF,QAAI,MAAO,QAAO,GAAG,SAAS,GAAG,KAAK;AACtC,UAAM,KAAK,OAAO,WAAW;AAC7B,QAAI,KAAK,EAAE,IAAI,eAAe,MAAM;AACpC,WAAO,GAAG,SAAS,GAAG,EAAE;AAAA,EAC1B,GAAG,OAAO;AACZ;AAEA,eAAsB,aACpB,SACA,MACA,SAC2B;AAC3B,QAAM,OAAO,KAAK,OAAO,CAAC,QAAQ,IAAI,WAAW,SAAS,CAAC;AAC3D,MAAI,CAAC,KAAK,OAAQ,QAAO,CAAC;AAC1B,QAAM,QAAQ,MAAM,QAAQ,SAAS,aAAa,OAAO,IAAI,QAAQ,CAAC;AACtE,SAAO;AAAA,IACL,KAAK,QAAQ,CAAC,QAAQ,KAAK,IAAI,MAAM,UAAU,MAAM,CAAC,KAAK,CAAC,EAAE,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC;AAAA,EACvF;AACF;AAEO,SAAS,OACd,SACA;AACA,SAAO,OAAO,gBAAwB,YAAkD;AACtF,UAAM,OAAO,QAAQ;AACrB,QAAI,CAAC,KAAM,QAAO;AAClB,UAAM,SAAS,MAAM,UAAU,MAAM,gBAAkC,OAAO;AAC9E,WAAO,OAAO,SAAS,SAAS,MAAM,QAAQ,OAAO,IAAI;AAAA,EAC3D;AACF;",
4
+ "sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport { type ConversationId, defineDoc, type Harness } from '@earendil-works/pi-durable';\nimport { boundedDeltas } from '../checkpoints.ts';\nimport { TAINT_TAG } from '../kernel/sightings.ts';\nimport { canonical, distinctTaints, type InheritedTaint, runTaints } from './guard.ts';\n\nexport const TaintTagDoc = defineDoc<{ tags: Record<string, InheritedTaint[]> }>({\n kind: 'slicc.taint-tags',\n checkpointWhen: boundedDeltas,\n version: 1,\n scope: 'session',\n initial: () => ({ tags: {} }),\n});\n\nconst keyOf = (taints: readonly InheritedTaint[]) =>\n canonical(\n distinctTaints(taints)\n .map((taint) => [taint.seat, taint.tier])\n .sort()\n );\n\nexport function taintTag(\n harness: Pick<Harness, 'commit'>,\n taints: readonly InheritedTaint[],\n context: Context\n): Promise<string> {\n const key = keyOf(taints);\n return harness.commit(async (tx) => {\n const doc = await tx.doc(TaintTagDoc);\n const found = Object.entries(doc.tags).find(([, kept]) => keyOf(kept) === key)?.[0];\n if (found) return `${TAINT_TAG}${found}`;\n const id = crypto.randomUUID();\n doc.tags[id] = distinctTaints(taints);\n return `${TAINT_TAG}${id}`;\n }, context);\n}\n\nexport async function taggedTaints(\n harness: Pick<Harness, 'snapshot'>,\n tags: readonly string[],\n context: Context\n): Promise<InheritedTaint[]> {\n const ours = tags.filter((tag) => tag.startsWith(TAINT_TAG));\n if (!ours.length) return [];\n const kept = (await harness.snapshot(TaintTagDoc, context))?.tags ?? {};\n return distinctTaints(\n ours.flatMap((tag) => kept[tag.slice(TAINT_TAG.length)] ?? [{ seat: null, tier: '' }])\n );\n}\n\nexport function runTag(\n harness: () => Pick<Harness, 'commit' | 'submission' | 'snapshot'> | undefined\n) {\n return async (conversationId: number, context: Context): Promise<string | undefined> => {\n const held = harness();\n if (!held) return undefined;\n const taints = await runTaints(held, conversationId as ConversationId, context);\n return taints.length ? taintTag(held, taints, context) : undefined;\n };\n}\n"],
5
+ "mappings": "AACA,SAA8B,iBAA+B;AAC7D,SAAS,qBAAqB;AAC9B,SAAS,iBAAiB;AAC1B,SAAS,WAAW,gBAAqC,iBAAiB;AAEnE,MAAM,cAAc,UAAsD;AAAA,EAC/E,MAAM;AAAA,EACN,gBAAgB;AAAA,EAChB,SAAS;AAAA,EACT,OAAO;AAAA,EACP,SAAS,OAAO,EAAE,MAAM,CAAC,EAAE;AAC7B,CAAC;AAED,MAAM,QAAQ,CAAC,WACb;AAAA,EACE,eAAe,MAAM,EAClB,IAAI,CAAC,UAAU,CAAC,MAAM,MAAM,MAAM,IAAI,CAAC,EACvC,KAAK;AACV;AAEK,SAAS,SACd,SACA,QACA,SACiB;AACjB,QAAM,MAAM,MAAM,MAAM;AACxB,SAAO,QAAQ,OAAO,OAAO,OAAO;AAClC,UAAM,MAAM,MAAM,GAAG,IAAI,WAAW;AACpC,UAAM,QAAQ,OAAO,QAAQ,IAAI,IAAI,EAAE,KAAK,CAAC,CAAC,EAAE,IAAI,MAAM,MAAM,IAAI,MAAM,GAAG,IAAI,CAAC;AAClF,QAAI,MAAO,QAAO,GAAG,SAAS,GAAG,KAAK;AACtC,UAAM,KAAK,OAAO,WAAW;AAC7B,QAAI,KAAK,EAAE,IAAI,eAAe,MAAM;AACpC,WAAO,GAAG,SAAS,GAAG,EAAE;AAAA,EAC1B,GAAG,OAAO;AACZ;AAEA,eAAsB,aACpB,SACA,MACA,SAC2B;AAC3B,QAAM,OAAO,KAAK,OAAO,CAAC,QAAQ,IAAI,WAAW,SAAS,CAAC;AAC3D,MAAI,CAAC,KAAK,OAAQ,QAAO,CAAC;AAC1B,QAAM,QAAQ,MAAM,QAAQ,SAAS,aAAa,OAAO,IAAI,QAAQ,CAAC;AACtE,SAAO;AAAA,IACL,KAAK,QAAQ,CAAC,QAAQ,KAAK,IAAI,MAAM,UAAU,MAAM,CAAC,KAAK,CAAC,EAAE,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC;AAAA,EACvF;AACF;AAEO,SAAS,OACd,SACA;AACA,SAAO,OAAO,gBAAwB,YAAkD;AACtF,UAAM,OAAO,QAAQ;AACrB,QAAI,CAAC,KAAM,QAAO;AAClB,UAAM,SAAS,MAAM,UAAU,MAAM,gBAAkC,OAAO;AAC9E,WAAO,OAAO,SAAS,SAAS,MAAM,QAAQ,OAAO,IAAI;AAAA,EAC3D;AACF;",
6
6
  "names": []
7
7
  }
package/dist/index.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/index.ts"],
4
- "sourcesContent": ["export {\n ADOBE,\n ADOBE_PROXY,\n type AdobeConfig,\n type AdobeModelInfo,\n adobeClassifiers,\n adobeConfig,\n adobeIdentity,\n adobeModel,\n adobeProvider,\n adobeUsage,\n type Budget,\n SESSION_HEADER,\n VERSION_HEADER,\n withAdobeIdentity,\n} from './adobe.ts';\nexport { type Agent, type AgentOptions, answerText, openAgent } from './agent.ts';\nexport {\n APPROVAL_MS,\n type Approval,\n type ApprovalAnswer,\n type ApprovalDecided,\n type ApprovalDraft,\n type ApprovalJudge,\n type ApprovalKind,\n type ApprovalRequester,\n type ApprovalState,\n type Approvals,\n ApprovalsDoc,\n type ApprovalTier,\n type ApprovalVerdict,\n approvalJudge,\n attachApprovals,\n DEFAULT_POLICY,\n type DecidedBy,\n type Decider,\n JUDGE_ALLOW,\n JUDGE_DENY,\n JUDGE_KIND,\n JUDGE_MODEL,\n JUDGE_QUESTIONS,\n type Judgement,\n judgeModelOf,\n judgeSpender,\n judgeState,\n type OwnerIntent,\n ownerIntent,\n POLICY_FILE,\n REASONS,\n seedPolicy,\n VERDICT_REASONS,\n verdictOf,\n verdictRecorder,\n} from './approvals/index.ts';\nexport {\n type ApprovalPolicy,\n type ApprovalPolicyChange,\n type ApprovalPolicySet,\n DEFAULT_APPROVAL_POLICY,\n type SudoPolicy,\n} from './approvals/policy.ts';\nexport type { AgentSudo, SudoAnswer, SudoRequest } from './approvals/sudo.ts';\nexport {\n FILE_LIMIT,\n IMAGE_LIMIT,\n type SendAttachment,\n type ShownAttachment,\n splitUser,\n TOTAL_LIMIT,\n userParts,\n} from './attachments.ts';\nexport { type AgentConnection, connectAgent } from './client.ts';\nexport { createAgentControl } from './control.ts';\nexport {\n connectGuest,\n GUEST_ONLY,\n type GuestConnection,\n SEAT_REFUSED,\n} from './guests/client.ts';\nexport type { GuestOpened, Proposed, ProposeRequest } from './guests/index.ts';\nexport type { GateKind, GuestPolicy } from './guests/policy.ts';\nexport type { GuestDraft, GuestGate, GuestGates, GuestSeat } from './guests/seats.ts';\nexport { type AgentHost, type ConnectOptions, hostAgent } from './host.ts';\nexport type {\n KernelClient,\n KernelFs,\n KernelProcess,\n KernelStat,\n SpawnOptions,\n} from './kernel/client.ts';\nexport { HOME, type KernelEnvOptions, kernelEnvironment, SliccKernelEnv } from './kernel/env.ts';\nexport {\n adminOver,\n type KernelAdmin,\n type KernelGroup,\n type KernelGroups,\n type KernelUser,\n type KernelUsers,\n serveAdmin,\n type UserClient,\n type UserClients,\n type UserConnect,\n type UserSpec,\n userClients,\n} from './kernel/users.ts';\nexport {\n type AgentWorkerOptions,\n type AgentWorkerScope,\n type KernelAttach,\n runAgentWorker,\n} from './main.ts';\nexport {\n type Loopback,\n type Transport,\n type TransportResponse,\n transportFetch,\n} from './net.ts';\nexport {\n AGENT_LOCK,\n type AgentOwner,\n type AgentWorker,\n type KernelSource,\n type OwnerOptions,\n startAgent,\n} from './owner.ts';\nexport { SliccPrompt, sliccPrompt } from './prompt.ts';\nexport {\n type AgentPages,\n createPages,\n type PageBatch,\n type PageCaller,\n type PageRequest,\n type PageResult,\n type Pages,\n type PagesOpened,\n type PageTab,\n} from './remote/pages.ts';\nexport {\n type AccountState,\n AgentControl,\n AgentPagesService,\n AgentSettings,\n type AgentSettingsChange,\n AgentTranscript,\n type BudgetState,\n type ModelChoice,\n type OperationError,\n type PromptResult,\n type SendMode,\n type SendRequest,\n type SendResponse,\n type SettingsState,\n type SignIn,\n} from './services.ts';\nexport {\n BEDROCK,\n createAgentSettings,\n createSliccModels,\n DEFAULT_MODEL,\n DEFAULT_REGIONS,\n PREFERRED_MODELS,\n preferredModel,\n resolveDefaultModel,\n sliccProviders,\n withDefaultRegions,\n} from './settings.ts';\nexport {\n AGENT_DIRECTORY,\n type OpfsSqliteOptions,\n openMemorySqliteStorage,\n openOpfsSqliteStorage,\n WasmSqliteDatabase,\n} from './sqlite.ts';\nexport { type SystemFacts, systemSection } from './system.ts';\nexport { UPLOADS, type Uploads, upload } from './uploads.ts';\nexport { type PortEndpoint, PortLink } from './wire.ts';\nexport {\n type KernelPorts,\n kernelPort,\n kernelPorts,\n serveConnections,\n type WorkerScope,\n} from './worker.ts';\n"],
5
- "mappings": "AAAA;AAAA,EACE;AAAA,EACA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAwC,YAAY,iBAAiB;AACrE;AAAA,EACE;AAAA,EAUA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP;AAAA,EAIE;AAAA,OAEK;AAEP;AAAA,EACE;AAAA,EACA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAA+B,oBAAoB;AACnD,SAAS,0BAA0B;AACnC;AAAA,EACE;AAAA,EACA;AAAA,EAEA;AAAA,OACK;AAIP,SAA8C,iBAAiB;AAQ/D,SAAS,MAA6B,mBAAmB,sBAAsB;AAC/E;AAAA,EACE;AAAA,EAMA;AAAA,EAKA;AAAA,OACK;AACP;AAAA,EAIE;AAAA,OACK;AACP;AAAA,EAIE;AAAA,OACK;AACP;AAAA,EACE;AAAA,EAKA;AAAA,OACK;AACP,SAAS,aAAa,mBAAmB;AACzC;AAAA,EAEE;AAAA,OAQK;AACP;AAAA,EAEE;AAAA,EACA;AAAA,EACA;AAAA,EAEA;AAAA,OAUK;AACP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP;AAAA,EACE;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAA2B,qBAAqB;AAChD,SAAS,SAAuB,cAAc;AAC9C,SAA4B,gBAAgB;AAC5C;AAAA,EAEE;AAAA,EACA;AAAA,EACA;AAAA,OAEK;",
4
+ "sourcesContent": ["export {\n ADOBE,\n ADOBE_PROXY,\n type AdobeConfig,\n type AdobeModelInfo,\n adobeClassifiers,\n adobeConfig,\n adobeIdentity,\n adobeModel,\n adobeProvider,\n adobeUsage,\n type Budget,\n SESSION_HEADER,\n VERSION_HEADER,\n withAdobeIdentity,\n} from './adobe.ts';\nexport { type Agent, type AgentOptions, answerText, openAgent } from './agent.ts';\nexport {\n APPROVAL_MS,\n type Approval,\n type ApprovalAnswer,\n type ApprovalDecided,\n type ApprovalDraft,\n type ApprovalJudge,\n type ApprovalKind,\n type ApprovalRequester,\n type ApprovalState,\n type Approvals,\n ApprovalsDoc,\n type ApprovalTier,\n type ApprovalVerdict,\n approvalJudge,\n attachApprovals,\n DEFAULT_POLICY,\n type DecidedBy,\n type Decider,\n JUDGE_ALLOW,\n JUDGE_DENY,\n JUDGE_KIND,\n JUDGE_MODEL,\n JUDGE_QUESTIONS,\n type Judgement,\n judgeModelOf,\n judgeSpender,\n judgeState,\n type OwnerIntent,\n ownerIntent,\n POLICY_FILE,\n REASONS,\n seedPolicy,\n VERDICT_REASONS,\n verdictOf,\n verdictRecorder,\n} from './approvals/index.ts';\nexport {\n type ApprovalPolicy,\n type ApprovalPolicyChange,\n type ApprovalPolicySet,\n DEFAULT_APPROVAL_POLICY,\n type SudoPolicy,\n} from './approvals/policy.ts';\nexport type { AgentSudo, SudoAnswer, SudoRequest } from './approvals/sudo.ts';\nexport {\n FILE_LIMIT,\n IMAGE_LIMIT,\n type SendAttachment,\n type ShownAttachment,\n splitUser,\n TOTAL_LIMIT,\n userParts,\n} from './attachments.ts';\nexport { type AgentConnection, connectAgent } from './client.ts';\nexport type { Clock } from './clock.ts';\nexport { createAgentControl } from './control.ts';\nexport {\n connectGuest,\n GUEST_ONLY,\n type GuestConnection,\n SEAT_REFUSED,\n} from './guests/client.ts';\nexport type { GuestOpened, Proposed, ProposeRequest } from './guests/index.ts';\nexport type { GateKind, GuestPolicy } from './guests/policy.ts';\nexport type { GuestDraft, GuestGate, GuestGates, GuestSeat } from './guests/seats.ts';\nexport { type AgentHost, type ConnectOptions, hostAgent } from './host.ts';\nexport type {\n KernelClient,\n KernelFs,\n KernelProcess,\n KernelStat,\n SpawnOptions,\n} from './kernel/client.ts';\nexport { HOME, type KernelEnvOptions, kernelEnvironment, SliccKernelEnv } from './kernel/env.ts';\nexport {\n adminOver,\n type KernelAdmin,\n type KernelGroup,\n type KernelGroups,\n type KernelUser,\n type KernelUsers,\n serveAdmin,\n type UserClient,\n type UserClients,\n type UserConnect,\n type UserSpec,\n userClients,\n} from './kernel/users.ts';\nexport {\n type AgentWorkerOptions,\n type AgentWorkerScope,\n type KernelAttach,\n runAgentWorker,\n} from './main.ts';\nexport {\n type Loopback,\n type Transport,\n type TransportResponse,\n transportFetch,\n} from './net.ts';\nexport {\n AGENT_LOCK,\n type AgentOwner,\n type AgentWorker,\n type KernelSource,\n type OwnerOptions,\n startAgent,\n} from './owner.ts';\nexport { SliccPrompt, sliccPrompt } from './prompt.ts';\nexport {\n type AgentPages,\n createPages,\n type PageBatch,\n type PageCaller,\n type PageRequest,\n type PageResult,\n type Pages,\n type PagesOpened,\n type PageTab,\n} from './remote/pages.ts';\nexport {\n type AccountState,\n AgentControl,\n AgentPagesService,\n AgentSettings,\n type AgentSettingsChange,\n AgentTranscript,\n type BudgetState,\n type ModelChoice,\n type OperationError,\n type PromptResult,\n type SendMode,\n type SendRequest,\n type SendResponse,\n type SettingsState,\n type SignIn,\n} from './services.ts';\nexport {\n BEDROCK,\n createAgentSettings,\n createSliccModels,\n DEFAULT_MODEL,\n DEFAULT_REGIONS,\n PREFERRED_MODELS,\n preferredModel,\n resolveDefaultModel,\n sliccProviders,\n withDefaultRegions,\n} from './settings.ts';\nexport {\n AGENT_DIRECTORY,\n type OpfsSqliteOptions,\n openMemorySqliteStorage,\n openOpfsSqliteStorage,\n WasmSqliteDatabase,\n} from './sqlite.ts';\nexport { type SystemFacts, systemSection } from './system.ts';\nexport { UPLOADS, type Uploads, upload } from './uploads.ts';\nexport { type PortEndpoint, PortLink } from './wire.ts';\nexport {\n type KernelPorts,\n kernelPort,\n kernelPorts,\n serveConnections,\n type WorkerScope,\n} from './worker.ts';\n"],
5
+ "mappings": "AAAA;AAAA,EACE;AAAA,EACA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAwC,YAAY,iBAAiB;AACrE;AAAA,EACE;AAAA,EAUA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP;AAAA,EAIE;AAAA,OAEK;AAEP;AAAA,EACE;AAAA,EACA;AAAA,EAGA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAA+B,oBAAoB;AAEnD,SAAS,0BAA0B;AACnC;AAAA,EACE;AAAA,EACA;AAAA,EAEA;AAAA,OACK;AAIP,SAA8C,iBAAiB;AAQ/D,SAAS,MAA6B,mBAAmB,sBAAsB;AAC/E;AAAA,EACE;AAAA,EAMA;AAAA,EAKA;AAAA,OACK;AACP;AAAA,EAIE;AAAA,OACK;AACP;AAAA,EAIE;AAAA,OACK;AACP;AAAA,EACE;AAAA,EAKA;AAAA,OACK;AACP,SAAS,aAAa,mBAAmB;AACzC;AAAA,EAEE;AAAA,OAQK;AACP;AAAA,EAEE;AAAA,EACA;AAAA,EACA;AAAA,EAEA;AAAA,OAUK;AACP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP;AAAA,EACE;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAA2B,qBAAqB;AAChD,SAAS,SAAuB,cAAc;AAC9C,SAA4B,gBAAgB;AAC5C;AAAA,EAEE;AAAA,EACA;AAAA,EACA;AAAA,OAEK;",
6
6
  "names": []
7
7
  }
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  defineDoc
3
3
  } from "@earendil-works/pi-durable";
4
- import { deltasOnly } from "../checkpoints.js";
4
+ import { boundedDeltas } from "../checkpoints.js";
5
5
  import { shortCommand } from "../kernel/jobs.js";
6
6
  import { entryLick } from "./state.js";
7
7
  const LicksHostDoc = defineDoc({
8
8
  kind: "slicc.licks.host",
9
- checkpointWhen: deltasOnly,
9
+ checkpointWhen: boundedDeltas,
10
10
  version: 1,
11
11
  scope: "session",
12
12
  initial: () => ({ cron: {}, agent: null, boot: null, mark: 0, upgrades: 0 })
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/licks/boot.ts"],
4
- "sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport {\n type Conversation,\n defineDoc,\n type EntryRecord,\n type Harness,\n} from '@earendil-works/pi-durable';\nimport { deltasOnly } from '../checkpoints.ts';\nimport { type Job, type Jobs, shortCommand } from '../kernel/jobs.ts';\nimport type { Deliver } from './fswatch.ts';\nimport type { LickChannel } from './lick.ts';\nimport { entryLick } from './state.ts';\n\ntype HostState = {\n cron: Record<string, { task: number }>;\n agent: string | null;\n boot: string | null;\n mark: number;\n upgrades: number;\n jobs?: Job[];\n};\n\nexport const LicksHostDoc = defineDoc<HostState>({\n kind: 'slicc.licks.host',\n checkpointWhen: deltasOnly,\n version: 1,\n scope: 'session',\n initial: () => ({ cron: {}, agent: null, boot: null, mark: 0, upgrades: 0 }),\n});\n\nexport interface BootFacts {\n version: string;\n boot: string | null;\n}\n\nconst BOOT_CHANNELS: ReadonlySet<LickChannel> = new Set(['upgrade', 'session-reload']);\n\nexport const LISTED = 5;\n\ninterface Block {\n type?: string;\n id?: string;\n name?: string;\n arguments?: { command?: unknown };\n}\n\ntype Call = { id: string | undefined; command: string; at: number };\n\nfunction bashCalls(entries: readonly EntryRecord[], after: number): Call[] {\n const calls: Call[] = [];\n let reacting = false;\n for (const entry of entries) {\n if (entry.kind === 'pi.user') {\n const lick = entryLick(entry);\n reacting = lick !== undefined && BOOT_CHANNELS.has(lick.channel);\n }\n if (reacting || entry.kind !== 'pi.assistant' || Number(entry.id) <= after) continue;\n const message = entry.model?.[0] as { content?: unknown; timestamp?: unknown } | undefined;\n const at = typeof message?.timestamp === 'number' ? message.timestamp : 0;\n for (const block of Array.isArray(message?.content) ? (message.content as Block[]) : []) {\n const command = block.arguments?.command;\n if (block.type === 'toolCall' && block.name === 'bash' && typeof command === 'string')\n calls.push({ id: block.id, command, at });\n }\n }\n return calls;\n}\n\nexport function bashCommands(entries: readonly EntryRecord[], after: number): string[] {\n return bashCalls(entries, after).map((call) => call.command);\n}\n\nfunction ended(entries: readonly EntryRecord[]): Set<string> {\n const done = new Set<string>();\n for (const entry of entries) {\n if (entry.kind !== 'pi.tool-result') continue;\n const id = (entry.model?.[0] as { toolCallId?: unknown } | undefined)?.toolCallId;\n const codes = (\n (entry.data as { diagnostics?: { code?: unknown }[] } | undefined)?.diagnostics ?? []\n ).map((diagnostic) => diagnostic.code);\n if (typeof id === 'string' && !codes.includes('interrupted')) done.add(id);\n }\n return done;\n}\n\nexport function interruptedCommands(\n entries: readonly EntryRecord[],\n after: number,\n boot: string\n): string[] {\n const done = ended(entries);\n const booted = (Number(boot) + 1) * 1000;\n return bashCalls(entries, after)\n .filter((call) => call.at < booted && !(call.id !== undefined && done.has(call.id)))\n .map((call) => shortCommand(call.command));\n}\n\nfunction upgraded(from: string, to: string, upgrades: number) {\n return {\n channel: 'upgrade',\n source: 'slicc-agent',\n title: `slicc-agent ${from} \u2192 ${to}`,\n text: `The agent was updated from ${from} to ${to}.`,\n body: `Release notes: https://github.com/ai-ecoverse/slicc-agent/releases/tag/v${to}`,\n target: 'cone',\n eventId: `${from}->${to}#${upgrades}`,\n } as const;\n}\n\nexport function listed(all: readonly string[]): string[] {\n const commands = [...new Set(all)];\n const more = commands.length - LISTED;\n return more > 0 ? [...commands.slice(-LISTED), `\u2026and ${more} more`] : commands;\n}\n\nexport function backgroundList(jobs: readonly Job[]): string[] {\n return listed(jobs.map((job) => job.command));\n}\n\nfunction restartBody(interrupted: readonly string[], jobs: readonly Job[]): string {\n const cut = listed(interrupted);\n const background = backgroundList(jobs);\n const parts = [\n ...(cut.length ? [`Commands that were interrupted mid-run:\\n${cut.join('\\n')}`] : []),\n ...(background.length\n ? [`Background commands that are no longer running:\\n${background.join('\\n')}`]\n : []),\n ];\n return parts.length\n ? parts.join('\\n\\n')\n : 'Files are unchanged. Start again whatever you still need running.';\n}\n\nfunction restarted(boot: string, jobs: readonly Job[], interrupted: readonly string[]) {\n return {\n channel: 'session-reload',\n source: 'kernel',\n title: 'The kernel restarted',\n text: 'The page reloaded, so the kernel started fresh: processes from before the reload are gone.',\n body: restartBody(interrupted, jobs),\n target: 'cone',\n eventId: `boot:${boot}`,\n } as const;\n}\n\nexport async function bootLicks(\n harness: Harness,\n cone: Conversation,\n deliver: Deliver,\n facts: BootFacts,\n context: Context,\n jobs?: Jobs,\n listen?: () => void\n): Promise<void> {\n const state = await harness.snapshot(LicksHostDoc, context);\n listen?.();\n const previous = state?.agent ?? null;\n if (previous !== null && previous !== facts.version)\n await deliver(upgraded(previous, facts.version, state?.upgrades ?? 0), context);\n const { entries } = await cone.context(context);\n const before = state?.jobs ?? [];\n if (facts.boot !== null && state?.boot && facts.boot !== state.boot) {\n const commands = bashCommands(entries, state.mark);\n if (commands.length || before.length)\n await deliver(\n restarted(facts.boot, before, interruptedCommands(entries, state.mark, facts.boot)),\n context\n );\n } else if (facts.boot !== null && facts.boot === state?.boot) jobs?.adopt(before);\n const { entries: after } = await cone.context(context);\n const mark = Number(after.at(-1)?.id ?? 0);\n await harness.commit(async (tx) => {\n const doc = await tx.doc(LicksHostDoc);\n if (doc.agent !== null && doc.agent !== facts.version) doc.upgrades += 1;\n doc.agent = facts.version;\n if (facts.boot !== null && doc.boot !== facts.boot) {\n doc.boot = facts.boot;\n doc.mark = mark;\n }\n doc.jobs = jobs?.list() ?? [];\n }, context);\n}\n\nfunction recordJobs(harness: Harness, jobs: Jobs, context: Context): () => void {\n let chain: Promise<void> = Promise.resolve();\n return jobs.watch(() => {\n chain = chain\n .then(() =>\n harness.commit(async (tx) => {\n (await tx.doc(LicksHostDoc)).jobs = jobs.list();\n }, context)\n )\n .catch(() => undefined);\n return chain;\n });\n}\n\nexport function booting(\n harness: Harness,\n cone: () => Conversation,\n deliver: Deliver,\n jobs: Jobs | undefined\n) {\n let stopped = false;\n let off = () => undefined as void;\n return {\n async boot(facts: BootFacts, context: Context): Promise<void> {\n await bootLicks(harness, cone(), deliver, facts, context, jobs, () => {\n off();\n if (jobs && !stopped) off = recordJobs(harness, jobs, context);\n });\n },\n stop() {\n stopped = true;\n off();\n },\n };\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport {\n type Conversation,\n defineDoc,\n type EntryRecord,\n type Harness,\n} from '@earendil-works/pi-durable';\nimport { boundedDeltas } from '../checkpoints.ts';\nimport { type Job, type Jobs, shortCommand } from '../kernel/jobs.ts';\nimport type { Deliver } from './fswatch.ts';\nimport type { LickChannel } from './lick.ts';\nimport { entryLick } from './state.ts';\n\ntype HostState = {\n cron: Record<string, { task: number }>;\n agent: string | null;\n boot: string | null;\n mark: number;\n upgrades: number;\n jobs?: Job[];\n};\n\nexport const LicksHostDoc = defineDoc<HostState>({\n kind: 'slicc.licks.host',\n checkpointWhen: boundedDeltas,\n version: 1,\n scope: 'session',\n initial: () => ({ cron: {}, agent: null, boot: null, mark: 0, upgrades: 0 }),\n});\n\nexport interface BootFacts {\n version: string;\n boot: string | null;\n}\n\nconst BOOT_CHANNELS: ReadonlySet<LickChannel> = new Set(['upgrade', 'session-reload']);\n\nexport const LISTED = 5;\n\ninterface Block {\n type?: string;\n id?: string;\n name?: string;\n arguments?: { command?: unknown };\n}\n\ntype Call = { id: string | undefined; command: string; at: number };\n\nfunction bashCalls(entries: readonly EntryRecord[], after: number): Call[] {\n const calls: Call[] = [];\n let reacting = false;\n for (const entry of entries) {\n if (entry.kind === 'pi.user') {\n const lick = entryLick(entry);\n reacting = lick !== undefined && BOOT_CHANNELS.has(lick.channel);\n }\n if (reacting || entry.kind !== 'pi.assistant' || Number(entry.id) <= after) continue;\n const message = entry.model?.[0] as { content?: unknown; timestamp?: unknown } | undefined;\n const at = typeof message?.timestamp === 'number' ? message.timestamp : 0;\n for (const block of Array.isArray(message?.content) ? (message.content as Block[]) : []) {\n const command = block.arguments?.command;\n if (block.type === 'toolCall' && block.name === 'bash' && typeof command === 'string')\n calls.push({ id: block.id, command, at });\n }\n }\n return calls;\n}\n\nexport function bashCommands(entries: readonly EntryRecord[], after: number): string[] {\n return bashCalls(entries, after).map((call) => call.command);\n}\n\nfunction ended(entries: readonly EntryRecord[]): Set<string> {\n const done = new Set<string>();\n for (const entry of entries) {\n if (entry.kind !== 'pi.tool-result') continue;\n const id = (entry.model?.[0] as { toolCallId?: unknown } | undefined)?.toolCallId;\n const codes = (\n (entry.data as { diagnostics?: { code?: unknown }[] } | undefined)?.diagnostics ?? []\n ).map((diagnostic) => diagnostic.code);\n if (typeof id === 'string' && !codes.includes('interrupted')) done.add(id);\n }\n return done;\n}\n\nexport function interruptedCommands(\n entries: readonly EntryRecord[],\n after: number,\n boot: string\n): string[] {\n const done = ended(entries);\n const booted = (Number(boot) + 1) * 1000;\n return bashCalls(entries, after)\n .filter((call) => call.at < booted && !(call.id !== undefined && done.has(call.id)))\n .map((call) => shortCommand(call.command));\n}\n\nfunction upgraded(from: string, to: string, upgrades: number) {\n return {\n channel: 'upgrade',\n source: 'slicc-agent',\n title: `slicc-agent ${from} \u2192 ${to}`,\n text: `The agent was updated from ${from} to ${to}.`,\n body: `Release notes: https://github.com/ai-ecoverse/slicc-agent/releases/tag/v${to}`,\n target: 'cone',\n eventId: `${from}->${to}#${upgrades}`,\n } as const;\n}\n\nexport function listed(all: readonly string[]): string[] {\n const commands = [...new Set(all)];\n const more = commands.length - LISTED;\n return more > 0 ? [...commands.slice(-LISTED), `\u2026and ${more} more`] : commands;\n}\n\nexport function backgroundList(jobs: readonly Job[]): string[] {\n return listed(jobs.map((job) => job.command));\n}\n\nfunction restartBody(interrupted: readonly string[], jobs: readonly Job[]): string {\n const cut = listed(interrupted);\n const background = backgroundList(jobs);\n const parts = [\n ...(cut.length ? [`Commands that were interrupted mid-run:\\n${cut.join('\\n')}`] : []),\n ...(background.length\n ? [`Background commands that are no longer running:\\n${background.join('\\n')}`]\n : []),\n ];\n return parts.length\n ? parts.join('\\n\\n')\n : 'Files are unchanged. Start again whatever you still need running.';\n}\n\nfunction restarted(boot: string, jobs: readonly Job[], interrupted: readonly string[]) {\n return {\n channel: 'session-reload',\n source: 'kernel',\n title: 'The kernel restarted',\n text: 'The page reloaded, so the kernel started fresh: processes from before the reload are gone.',\n body: restartBody(interrupted, jobs),\n target: 'cone',\n eventId: `boot:${boot}`,\n } as const;\n}\n\nexport async function bootLicks(\n harness: Harness,\n cone: Conversation,\n deliver: Deliver,\n facts: BootFacts,\n context: Context,\n jobs?: Jobs,\n listen?: () => void\n): Promise<void> {\n const state = await harness.snapshot(LicksHostDoc, context);\n listen?.();\n const previous = state?.agent ?? null;\n if (previous !== null && previous !== facts.version)\n await deliver(upgraded(previous, facts.version, state?.upgrades ?? 0), context);\n const { entries } = await cone.context(context);\n const before = state?.jobs ?? [];\n if (facts.boot !== null && state?.boot && facts.boot !== state.boot) {\n const commands = bashCommands(entries, state.mark);\n if (commands.length || before.length)\n await deliver(\n restarted(facts.boot, before, interruptedCommands(entries, state.mark, facts.boot)),\n context\n );\n } else if (facts.boot !== null && facts.boot === state?.boot) jobs?.adopt(before);\n const { entries: after } = await cone.context(context);\n const mark = Number(after.at(-1)?.id ?? 0);\n await harness.commit(async (tx) => {\n const doc = await tx.doc(LicksHostDoc);\n if (doc.agent !== null && doc.agent !== facts.version) doc.upgrades += 1;\n doc.agent = facts.version;\n if (facts.boot !== null && doc.boot !== facts.boot) {\n doc.boot = facts.boot;\n doc.mark = mark;\n }\n doc.jobs = jobs?.list() ?? [];\n }, context);\n}\n\nfunction recordJobs(harness: Harness, jobs: Jobs, context: Context): () => void {\n let chain: Promise<void> = Promise.resolve();\n return jobs.watch(() => {\n chain = chain\n .then(() =>\n harness.commit(async (tx) => {\n (await tx.doc(LicksHostDoc)).jobs = jobs.list();\n }, context)\n )\n .catch(() => undefined);\n return chain;\n });\n}\n\nexport function booting(\n harness: Harness,\n cone: () => Conversation,\n deliver: Deliver,\n jobs: Jobs | undefined\n) {\n let stopped = false;\n let off = () => undefined as void;\n return {\n async boot(facts: BootFacts, context: Context): Promise<void> {\n await bootLicks(harness, cone(), deliver, facts, context, jobs, () => {\n off();\n if (jobs && !stopped) off = recordJobs(harness, jobs, context);\n });\n },\n stop() {\n stopped = true;\n off();\n },\n };\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  defineDoc
3
3
  } from "@earendil-works/pi-durable";
4
- import { deltasOnly } from "../checkpoints.js";
4
+ import { boundedDeltas } from "../checkpoints.js";
5
5
  import { distinctTaints, LickTaintDoc } from "../guests/guard.js";
6
6
  import { scrubTexts } from "../secrets/scrub.js";
7
7
  import {
@@ -22,7 +22,7 @@ function tainted(taints) {
22
22
  }
23
23
  const LicksOutbox = defineDoc({
24
24
  kind: "slicc.licks",
25
- checkpointWhen: deltasOnly,
25
+ checkpointWhen: boundedDeltas,
26
26
  version: 1,
27
27
  scope: "session",
28
28
  initial: () => ({ queues: {}, events: {} })
@@ -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 { 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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",
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 { boundedDeltas } 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: boundedDeltas,\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,5 +1,5 @@
1
1
  import { defineDoc } from "@earendil-works/pi-durable";
2
- import { deltasOnly } from "../checkpoints.js";
2
+ import { boundedDeltas } from "../checkpoints.js";
3
3
  import { mcpNamespace, nameMatcher } from "./config.js";
4
4
  const RESOURCE_NAMES = /* @__PURE__ */ new Set([
5
5
  "list_mcp_resources",
@@ -8,7 +8,7 @@ const RESOURCE_NAMES = /* @__PURE__ */ new Set([
8
8
  ]);
9
9
  const McpAllowDoc = defineDoc({
10
10
  kind: "slicc.mcp",
11
- checkpointWhen: deltasOnly,
11
+ checkpointWhen: boundedDeltas,
12
12
  version: 1,
13
13
  scope: "conversation",
14
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 { 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;",
4
+ "sourcesContent": ["import type { CodemodeTool } from '@earendil-works/pi-codemode';\nimport { defineDoc } from '@earendil-works/pi-durable';\nimport { boundedDeltas } 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: boundedDeltas,\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,qBAAqB;AAC9B,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,7 +5,7 @@ import {
5
5
  defineTask,
6
6
  hook
7
7
  } from "@earendil-works/pi-durable";
8
- import { deltasOnly } from "../checkpoints.js";
8
+ import { boundedDeltas } from "../checkpoints.js";
9
9
  import { MAX_BYTES, parseMemory, redactSecrets, serializeMemory } from "./format.js";
10
10
  import { whoIs } from "./prompt.js";
11
11
  import { placeOf } from "./store.js";
@@ -14,7 +14,7 @@ const TRANSCRIPT_CHARS = 6e4;
14
14
  const DEFAULT_IDLE_MINUTES = 30;
15
15
  const ExtractDoc = defineDoc({
16
16
  kind: "slicc.memory-extract",
17
- checkpointWhen: deltasOnly,
17
+ checkpointWhen: boundedDeltas,
18
18
  version: 1,
19
19
  scope: "session",
20
20
  initial: () => ({ cursors: {}, pending: {} })
@@ -211,7 +211,7 @@ function settingsReader(ready) {
211
211
  const IDLE_TASK = "slicc.memory-idle";
212
212
  const IdleDoc = defineDoc({
213
213
  kind: "slicc.memory-idle",
214
- checkpointWhen: deltasOnly,
214
+ checkpointWhen: boundedDeltas,
215
215
  version: 1,
216
216
  scope: "session",
217
217
  initial: () => ({ tasks: {} })
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/memory/extract.ts"],
4
- "sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport { BACKGROUND_CONTEXT } from '@earendil-works/chord/context';\nimport type { Message } from '@earendil-works/pi-ai';\nimport {\n CompactionTask,\n type ConversationId,\n defineDoc,\n defineTask,\n type EntryId,\n hook,\n type ModelRef,\n type TaskId,\n} from '@earendil-works/pi-durable';\nimport { 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 const IDLE_TASK = 'slicc.memory-idle';\n\nexport const IdleDoc = defineDoc<{ tasks: Record<string, number> }>({\n kind: 'slicc.memory-idle',\n checkpointWhen: deltasOnly,\n version: 1,\n scope: 'session',\n initial: () => ({ tasks: {} }),\n});\n\ntype IdleState = { phase: 'watch'; tail: number; since: number };\n\nexport function idleTask(\n ready: Promise<Host>,\n schedule: Schedule,\n settings: (context: Context) => Promise<ExtractSettings>\n) {\n return defineTask<{ cone: string }, IdleState, null>({\n name: IDLE_TASK,\n version: 1,\n initial: () => ({ phase: 'watch', tail: -1, since: 0 }),\n phases: {\n async watch(task, runtime, context) {\n const host = await ready;\n const conversation = runtime.conversationId;\n const key = String(conversation);\n const who = whoIs(host.agents.state(), conversation);\n if (who.kind !== 'cone')\n return runtime.commit(async (tx) => {\n const doc = await tx.doc(IdleDoc);\n if (doc.tasks[key] === Number(task.id)) delete doc.tasks[key];\n return { status: 'terminal', outcome: { status: 'completed', result: null } };\n }, context);\n const { extract, idleMinutes } = await settings(context);\n const on = extract && idleMinutes > 0;\n const idleMs = (on ? idleMinutes : DEFAULT_IDLE_MINUTES) * 60_000;\n const now = runtime.now();\n const view = await runtime.context(conversation, context);\n const last = view.entries.at(-1);\n const tail = last?.id ?? 0;\n const seen = task.state.checkpoint;\n if (tail !== seen.tail) {\n const stamp = last?.model?.at(-1)?.timestamp;\n const since = typeof stamp === 'number' && stamp <= now ? stamp : now;\n return runtime.sleepUntil(since + idleMs, { phase: 'watch', tail, since }, context);\n }\n const due = seen.since + idleMs;\n if (now < due) return runtime.sleepUntil(due, seen, context);\n if (on) await schedule(conversation, tail, context, who.id);\n return runtime.sleepUntil(now + idleMs, seen, context);\n },\n },\n abort: (task, runtime, context) =>\n runtime.commit(async (tx) => {\n const doc = await tx.doc(IdleDoc);\n const key = String(runtime.conversationId);\n if (doc.tasks[key] === Number(task.id)) delete doc.tasks[key];\n return { status: 'terminal', outcome: { status: 'aborted' } };\n }, context),\n });\n}\n\nexport function idleWatch(\n host: Pick<Host, 'agents' | 'harness'>,\n task: ReturnType<typeof idleTask>,\n context: Context\n): () => void {\n let stopped = false;\n let line = Promise.resolve();\n const ensure = () => {\n line = line\n .then(async () => {\n if (stopped) return;\n await host.harness.commit(async (tx) => {\n const doc = await tx.doc(IdleDoc);\n for (const [id, cone] of Object.entries(host.agents.state().cones)) {\n const key = String(cone.conversation);\n const known = doc.tasks[key];\n const record = known === undefined ? undefined : await tx.task(known as TaskId);\n if (record && record.state.status !== 'terminal') continue;\n doc.tasks[key] = (await tx.createTask(\n task,\n { cone: id },\n {\n ownership: { kind: 'conversation' },\n conversationId: cone.conversation as ConversationId,\n background: true,\n }\n )) as number;\n }\n }, context);\n })\n .catch(() => undefined);\n };\n const off = host.agents.onChange(ensure);\n ensure();\n return () => {\n stopped = true;\n off();\n };\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport { BACKGROUND_CONTEXT } from '@earendil-works/chord/context';\nimport type { Message } from '@earendil-works/pi-ai';\nimport {\n CompactionTask,\n type ConversationId,\n defineDoc,\n defineTask,\n type EntryId,\n hook,\n type ModelRef,\n type TaskId,\n} from '@earendil-works/pi-durable';\nimport { boundedDeltas } from '../checkpoints.ts';\nimport type { MemoryTag } from './format.ts';\nimport { MAX_BYTES, parseMemory, redactSecrets, serializeMemory } from './format.ts';\nimport { type Host, whoIs } from './prompt.ts';\nimport { type Place, placeOf } from './store.ts';\n\nexport const EXTRACT_TASK = 'slicc.memory-extract';\nexport const TRANSCRIPT_CHARS = 60_000;\nexport const DEFAULT_IDLE_MINUTES = 30;\n\nexport type ExtractState = {\n cursors: Record<string, number>;\n pending: Record<string, number>;\n};\n\nexport const ExtractDoc = defineDoc<ExtractState>({\n kind: 'slicc.memory-extract',\n checkpointWhen: boundedDeltas,\n version: 1,\n scope: 'session',\n initial: () => ({ cursors: {}, pending: {} }),\n});\n\nexport type ExtractSettings = { extract: boolean; model: ModelRef | null; idleMinutes: number };\n\nexport function extractSettings(text: string | undefined): ExtractSettings {\n let memory: { extract?: unknown; extractModel?: unknown; idleMinutes?: unknown } = {};\n try {\n const parsed = JSON.parse(text ?? '{}') as { memory?: typeof memory };\n memory = parsed.memory && typeof parsed.memory === 'object' ? parsed.memory : {};\n } catch {\n memory = {};\n }\n const named = typeof memory.extractModel === 'string' ? memory.extractModel.split('/') : [];\n const model =\n named.length >= 2 && named[0] && named.slice(1).join('/')\n ? { provider: named[0], modelId: named.slice(1).join('/') }\n : null;\n const idle =\n typeof memory.idleMinutes === 'number' && memory.idleMinutes >= 0\n ? memory.idleMinutes\n : DEFAULT_IDLE_MINUTES;\n return { extract: memory.extract !== false, model, idleMinutes: idle };\n}\n\nfunction textOf(message: Message): string {\n const content = (message as { content?: unknown }).content;\n if (typeof content === 'string') return content;\n if (!Array.isArray(content)) return '';\n return content\n .map((part: { type?: string; text?: string; name?: string }) =>\n part.type === 'text'\n ? (part.text ?? '')\n : part.type === 'toolCall'\n ? `[tool ${part.name}]`\n : ''\n )\n .filter(Boolean)\n .join('\\n');\n}\n\nexport function transcriptOf(messages: readonly Message[]): string {\n const lines: string[] = [];\n for (const message of messages) {\n if (message.role !== 'user' && message.role !== 'assistant') continue;\n const text = textOf(message).trim();\n if (text) lines.push(`${message.role === 'user' ? 'User' : 'Agent'}: ${text}`);\n }\n const joined = redactSecrets(lines.join('\\n\\n')).text;\n return joined.length > TRANSCRIPT_CHARS ? joined.slice(-TRANSCRIPT_CHARS) : joined;\n}\n\nexport const EXTRACT_PROMPT = [\n 'You keep the long-term memory of a personal agent. Read the conversation excerpt and the current memory file, and propose memory entries worth keeping across conversations:',\n '- who the user is (tag \"user\"), how they want the agent to work, including corrections (tag \"feedback\"), and durable facts about their projects (tag \"project\");',\n '- only facts that will still matter in a later conversation; nothing about this one task, no transcripts, no guesses;',\n '- never secrets: no keys, tokens, passwords, Authorization values or URLs that carry a key;',\n '- to change an entry that exists, repeat its section and title with the new body; leave entries alone that need no change.',\n 'Answer with a JSON array and nothing else: [{\"section\": \"...\", \"title\": \"...\", \"body\": \"...\", \"tag\": \"user\" | \"feedback\" | \"project\"}]. An empty array is a fine answer.',\n].join('\\n');\n\nexport type Proposal = { section: string; title: string; body: string; tag: MemoryTag | null };\n\nconst TAGS = new Set(['user', 'feedback', 'project']);\n\nexport function proposals(text: string): Proposal[] {\n const start = text.indexOf('[');\n const end = text.lastIndexOf(']');\n if (start < 0 || end < start) return [];\n let parsed: unknown;\n try {\n parsed = JSON.parse(text.slice(start, end + 1));\n } catch {\n return [];\n }\n if (!Array.isArray(parsed)) return [];\n const out: Proposal[] = [];\n for (const item of parsed as Record<string, unknown>[]) {\n if (!item || typeof item !== 'object') continue;\n const { section, title, body, tag } = item;\n if (typeof section !== 'string' || typeof title !== 'string' || typeof body !== 'string')\n continue;\n if (!section.trim() || !title.trim() || !body.trim()) continue;\n out.push({\n section,\n title,\n body,\n tag: typeof tag === 'string' && TAGS.has(tag) ? (tag as MemoryTag) : null,\n });\n }\n return out.slice(0, 20);\n}\n\ntype Input = { cone: string; after: number; tail: number; model: ModelRef | null };\ntype State = { phase: 'extract' };\nexport type ExtractResult = { saved: number; skipped: string | null };\n\nasync function save(host: Host, place: Place, found: Proposal[], context: Context) {\n let saved = 0;\n for (const proposal of found) {\n try {\n await host.files.change(place, { kind: 'save', ...proposal }, context);\n saved++;\n } catch {\n break;\n }\n }\n if (saved) await host.changed(context).catch(() => undefined);\n return saved;\n}\n\nexport function extractTask(\n ready: Promise<Host>,\n settings: (context: Context) => Promise<ExtractSettings>\n) {\n return defineTask<Input, State, ExtractResult>({\n name: EXTRACT_TASK,\n version: 1,\n initial: () => ({ phase: 'extract' }),\n phases: {\n async extract(task, runtime, context) {\n const { cone, after, tail } = task.input;\n const key = String(runtime.conversationId);\n const done = (result: ExtractResult) =>\n runtime.commit(async (tx) => {\n const doc = await tx.doc(ExtractDoc);\n doc.cursors[key] = Math.max(doc.cursors[key] ?? 0, tail);\n delete doc.pending[key];\n return { status: 'terminal', outcome: { status: 'completed', result } };\n }, context);\n if (!(await settings(context)).extract) return done({ saved: 0, skipped: 'turned off' });\n const host = await ready;\n const place = placeOf(host.home, cone);\n const view = await runtime.context(runtime.conversationId, context, {\n at: tail as EntryId,\n });\n const fresh = view.entries.flatMap((entry, index) =>\n entry.id > after ? (view.contributions[index] as readonly Message[]) : []\n );\n const transcript = transcriptOf(fresh);\n if (!place || !transcript) return done({ saved: 0, skipped: 'nothing new' });\n const ref = task.input.model ?? (await runtime.agent(context)).model;\n const model = ref ? runtime.models.getModel(ref.provider, ref.modelId) : undefined;\n if (!model) return done({ saved: 0, skipped: 'no model' });\n const current = await host.files.read(place.file, context);\n const memory = current ? serializeMemory(parseMemory(current.text)) : '';\n const now = Date.now();\n const message = await runtime.models.completeSimple(\n model,\n {\n messages: [\n { role: 'system', content: EXTRACT_PROMPT, timestamp: now } as unknown as Message,\n {\n role: 'user',\n content: [\n {\n type: 'text',\n text: `<memory_file bytes_left=\"${MAX_BYTES - new TextEncoder().encode(memory).length}\">\\n${memory}</memory_file>\\n\\n<conversation>\\n${transcript}\\n</conversation>`,\n },\n ],\n timestamp: now,\n },\n ],\n },\n { maxTokens: 2000, signal: runtime.signal, cacheRetention: 'none' }\n );\n runtime.signal.throwIfAborted();\n if (message.stopReason === 'error') return done({ saved: 0, skipped: 'model error' });\n const saved = await save(host, place, proposals(textOf(message as Message)), context);\n return done({ saved, skipped: null });\n },\n },\n abort: (_task, runtime, context) =>\n runtime.commit(async (tx) => {\n delete (await tx.doc(ExtractDoc)).pending[String(runtime.conversationId)];\n return { status: 'terminal', outcome: { status: 'aborted' } };\n }, context),\n });\n}\n\nexport type Schedule = (\n conversation: number,\n tail: number,\n context: Context,\n cone?: string\n) => Promise<boolean>;\n\nexport function compactionHook(schedule: Schedule) {\n return hook(CompactionTask, {\n beforeCompact(compaction, api) {\n const tail = compaction.entries.reduce((max, entry) => Math.max(max, entry.id), 0);\n void schedule(api.conversationId, tail, BACKGROUND_CONTEXT).catch(() => false);\n return undefined;\n },\n });\n}\n\nexport function scheduler(\n ready: Promise<Host>,\n task: ReturnType<typeof extractTask>,\n settings: (context: Context) => Promise<ExtractSettings>\n): Schedule {\n return async (conversation, tail, context, cone) => {\n const host = await ready;\n const chosen = await settings(context);\n const who = cone\n ? { kind: 'cone' as const, id: cone }\n : whoIs(host.agents.state(), conversation);\n if (who.kind !== 'cone') return false;\n return host.harness.commit(async (tx) => {\n const doc = await tx.doc(ExtractDoc);\n const key = String(conversation);\n const after = doc.cursors[key] ?? 0;\n if (doc.pending[key] !== undefined || tail <= after) return false;\n doc.pending[key] = (await tx.createTask(\n task,\n { cone: who.id, after, tail, model: chosen.model },\n {\n ownership: { kind: 'conversation' },\n conversationId: conversation as ConversationId,\n background: true,\n }\n )) as number;\n return true;\n }, context);\n };\n}\n\nexport function settingsReader(ready: Promise<Host>) {\n return async (context: Context): Promise<ExtractSettings> => {\n const host = await ready;\n const dir = host.agentDir ?? `${host.home}/.pi/agent`;\n const read = await host.env.readTextFile(`${dir}/settings.json`, context);\n return extractSettings(read.ok ? read.value : undefined);\n };\n}\n\nexport const IDLE_TASK = 'slicc.memory-idle';\n\nexport const IdleDoc = defineDoc<{ tasks: Record<string, number> }>({\n kind: 'slicc.memory-idle',\n checkpointWhen: boundedDeltas,\n version: 1,\n scope: 'session',\n initial: () => ({ tasks: {} }),\n});\n\ntype IdleState = { phase: 'watch'; tail: number; since: number };\n\nexport function idleTask(\n ready: Promise<Host>,\n schedule: Schedule,\n settings: (context: Context) => Promise<ExtractSettings>\n) {\n return defineTask<{ cone: string }, IdleState, null>({\n name: IDLE_TASK,\n version: 1,\n initial: () => ({ phase: 'watch', tail: -1, since: 0 }),\n phases: {\n async watch(task, runtime, context) {\n const host = await ready;\n const conversation = runtime.conversationId;\n const key = String(conversation);\n const who = whoIs(host.agents.state(), conversation);\n if (who.kind !== 'cone')\n return runtime.commit(async (tx) => {\n const doc = await tx.doc(IdleDoc);\n if (doc.tasks[key] === Number(task.id)) delete doc.tasks[key];\n return { status: 'terminal', outcome: { status: 'completed', result: null } };\n }, context);\n const { extract, idleMinutes } = await settings(context);\n const on = extract && idleMinutes > 0;\n const idleMs = (on ? idleMinutes : DEFAULT_IDLE_MINUTES) * 60_000;\n const now = runtime.now();\n const view = await runtime.context(conversation, context);\n const last = view.entries.at(-1);\n const tail = last?.id ?? 0;\n const seen = task.state.checkpoint;\n if (tail !== seen.tail) {\n const stamp = last?.model?.at(-1)?.timestamp;\n const since = typeof stamp === 'number' && stamp <= now ? stamp : now;\n return runtime.sleepUntil(since + idleMs, { phase: 'watch', tail, since }, context);\n }\n const due = seen.since + idleMs;\n if (now < due) return runtime.sleepUntil(due, seen, context);\n if (on) await schedule(conversation, tail, context, who.id);\n return runtime.sleepUntil(now + idleMs, seen, context);\n },\n },\n abort: (task, runtime, context) =>\n runtime.commit(async (tx) => {\n const doc = await tx.doc(IdleDoc);\n const key = String(runtime.conversationId);\n if (doc.tasks[key] === Number(task.id)) delete doc.tasks[key];\n return { status: 'terminal', outcome: { status: 'aborted' } };\n }, context),\n });\n}\n\nexport function idleWatch(\n host: Pick<Host, 'agents' | 'harness'>,\n task: ReturnType<typeof idleTask>,\n context: Context\n): () => void {\n let stopped = false;\n let line = Promise.resolve();\n const ensure = () => {\n line = line\n .then(async () => {\n if (stopped) return;\n await host.harness.commit(async (tx) => {\n const doc = await tx.doc(IdleDoc);\n for (const [id, cone] of Object.entries(host.agents.state().cones)) {\n const key = String(cone.conversation);\n const known = doc.tasks[key];\n const record = known === undefined ? undefined : await tx.task(known as TaskId);\n if (record && record.state.status !== 'terminal') continue;\n doc.tasks[key] = (await tx.createTask(\n task,\n { cone: id },\n {\n ownership: { kind: 'conversation' },\n conversationId: cone.conversation as ConversationId,\n background: true,\n }\n )) as number;\n }\n }, context);\n })\n .catch(() => undefined);\n };\n const off = host.agents.onChange(ensure);\n ensure();\n return () => {\n stopped = true;\n off();\n };\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -7,7 +7,7 @@ import {
7
7
  live,
8
8
  scoopId
9
9
  } from "../agents.js";
10
- import { deltasOnly } from "../checkpoints.js";
10
+ import { boundedDeltas } from "../checkpoints.js";
11
11
  import { CODEMODE } from "../codemode/index.js";
12
12
  import { distinctTaints, GuestTaintDoc } from "../guests/guard.js";
13
13
  import { LEGACY } from "../layout.js";
@@ -17,7 +17,7 @@ import { SCOOPS_ROOT } from "../layout.js";
17
17
  const LEDGER_SIZE = 512;
18
18
  const ScoopWorkDoc = defineDoc({
19
19
  kind: "slicc.scoops.work",
20
- checkpointWhen: deltasOnly,
20
+ checkpointWhen: boundedDeltas,
21
21
  version: 1,
22
22
  scope: "session",
23
23
  initial: () => ({ feeds: {}, waits: {}, ledger: {}, turns: {} })