@ai-ecoverse/slicc-agent 4.1.0 → 4.3.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 (42) hide show
  1. package/README.md +7 -1
  2. package/dist/client.js +3 -1
  3. package/dist/client.js.map +2 -2
  4. package/dist/control.js +3 -2
  5. package/dist/control.js.map +2 -2
  6. package/dist/host.js +18 -8
  7. package/dist/host.js.map +3 -3
  8. package/dist/index.js +6 -0
  9. package/dist/index.js.map +2 -2
  10. package/dist/main.js +18 -11
  11. package/dist/main.js.map +2 -2
  12. package/dist/memory/runtime.js +1 -1
  13. package/dist/memory/runtime.js.map +2 -2
  14. package/dist/preferred.js +14 -0
  15. package/dist/preferred.js.map +7 -0
  16. package/dist/readonly.js +2 -1
  17. package/dist/readonly.js.map +2 -2
  18. package/dist/services.js.map +2 -2
  19. package/dist/settings.js +18 -2
  20. package/dist/settings.js.map +3 -3
  21. package/dist/spectrum/agent-port.js +6 -0
  22. package/dist/spectrum/agent-port.js.map +2 -2
  23. package/dist/spectrum/settings-port.js +23 -8
  24. package/dist/spectrum/settings-port.js.map +2 -2
  25. package/dist/titles/generate.js +33 -13
  26. package/dist/titles/generate.js.map +2 -2
  27. package/dist/titles/labels.js +207 -0
  28. package/dist/titles/labels.js.map +7 -0
  29. package/dist/titles/shown.js +12 -0
  30. package/dist/titles/shown.js.map +7 -0
  31. package/dist/types/client.d.ts +1 -0
  32. package/dist/types/control.d.ts +2 -1
  33. package/dist/types/host.d.ts +2 -0
  34. package/dist/types/index.d.ts +1 -1
  35. package/dist/types/preferred.d.ts +5 -0
  36. package/dist/types/services.d.ts +2 -0
  37. package/dist/types/settings.d.ts +5 -1
  38. package/dist/types/spectrum/agent-port.d.ts +1 -0
  39. package/dist/types/titles/generate.d.ts +13 -1
  40. package/dist/types/titles/labels.d.ts +39 -0
  41. package/dist/types/titles/shown.d.ts +2 -0
  42. package/package.json +2 -2
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/spectrum/agent-port.ts"],
4
- "sourcesContent": ["import type {\n Agent,\n AgentEvents,\n AgentPort,\n AgentStatus,\n FrozenCone,\n Message,\n Outgoing,\n SlashCommand,\n SystemMessage,\n Thinking,\n UserMessage,\n} from '@ai-ecoverse/slicc-spectrum/ui';\nimport { BACKGROUND_CONTEXT } from '@earendil-works/chord/context';\nimport type { ConversationView } from '@earendil-works/pi-durable';\nimport type { AgentConnection } from '../client.ts';\nimport { contextRatio, contextTokens } from '../context.ts';\nimport { shrink } from '../images/fit.ts';\nimport { entryLick } from '../licks/state.ts';\nimport { type VaultHold, vaultHold } from '../secrets/view.ts';\nimport type { AgentSettingsChange, AgentSummary, Command } from '../services.ts';\nimport { attempt } from './attempt.ts';\nimport { Emitter } from './emitter.ts';\nimport { isBusy, queued, toMessages } from './messages.ts';\nimport { readUrl, sendAttachments } from './outgoing.ts';\n\nexport const CONE = 'cone';\n\nconst thinking: Record<string, Thinking> = {\n off: 'off',\n minimal: 'low',\n low: 'low',\n medium: 'medium',\n high: 'high',\n xhigh: 'high',\n max: 'high',\n};\n\ninterface AgentDoc {\n model?: { provider: string; modelId: string };\n thinkingLevel?: string;\n}\n\nfunction status(view: ConversationView | undefined, messages: readonly Message[]): AgentStatus {\n if (isBusy(view)) {\n const live = view?.docs['pi.live'] as { tools?: { status: string }[] } | undefined;\n return live?.tools?.some((slot) => slot.status === 'running') ? 'working' : 'thinking';\n }\n const last = messages.at(-1);\n return last?.role === 'assistant' && last.status === 'error' ? 'error' : 'idle';\n}\n\nfunction answers(messages: readonly Message[]): string[] {\n return messages\n .filter((message) => message.role === 'assistant' && message.status !== 'streaming')\n .map((message) => message.id);\n}\n\nexport function slashCommand({ kind, ...command }: Command): SlashCommand {\n return kind === 'action' ? command : { ...command, kind };\n}\n\nexport type AgentHold = VaultHold;\n\nexport type AgentAdapterEvents = AgentEvents & { hold: AgentHold | null };\n\nfunction sameHold(a: AgentHold | null, b: AgentHold | null): boolean {\n return a === b || (a !== null && b !== null && a.reason === b.reason && a.held === b.held);\n}\n\ninterface Pending {\n agentId: string;\n message: UserMessage;\n dropped: boolean;\n release(): void;\n released: Promise<void>;\n}\n\nexport class AgentAdapter extends Emitter<AgentAdapterEvents> implements AgentPort {\n readonly #connection: AgentConnection;\n readonly #ready: Promise<void>;\n readonly #messages = new Map<string, Message[]>();\n #active: string | null = null;\n #creating = new Map<string, Promise<string | null>>();\n #hold: AgentHold | null = null;\n readonly #outbox = new Map<string, Pending>();\n readonly #notices = new Map<string, { at: number; message: SystemMessage }[]>();\n readonly #unread = new Map<string, number>();\n readonly #seen = new Map<string, Set<string>>();\n readonly #queuedAt = new Map<string, Map<string, { at: number; seen: boolean }>>();\n #opened: ReadonlySet<string> | null = null;\n readonly #agentCommandsCache = new Map<string, Command[]>();\n readonly #agentCommandsLoading = new Set<string>();\n #agentCommandsGeneration = 0;\n\n constructor(connection: AgentConnection) {\n super();\n this.#connection = connection;\n this.#hold = this.hold();\n connection.vault?.subscribe(() => {\n const next = this.hold();\n if (sameHold(next, this.#hold)) return;\n this.#hold = next;\n if (!next) for (const pending of this.#outbox.values()) pending.release();\n this.emit('hold', next);\n this.emit('agents', this.list());\n });\n connection.frozen?.subscribe(() => {\n this.emit('frozen', this.frozen());\n this.emit('agents', this.list());\n });\n this.#ready = new Promise((resolve) => {\n connection.transcript.subscribe(() => {\n this.#refresh();\n resolve();\n });\n connection.deliveries.subscribe(() => this.#refresh());\n connection.views?.subscribe(() => this.#refresh());\n connection.agents?.subscribe(() => this.#refresh());\n });\n connection.settings?.state.subscribe(() => this.emit('agents', this.list()));\n const stale = () => {\n this.#agentCommandsGeneration += 1;\n this.#agentCommandsLoading.clear();\n this.#agentCommandsCache.clear();\n this.emit('agents', this.list());\n };\n connection.commands?.subscribe(stale);\n connection.privateRevision?.subscribe(stale);\n }\n\n #summary(): readonly AgentSummary[] {\n const agents = this.#connection.agents?.value?.agents;\n return agents?.length\n ? agents\n : [{ id: CONE, name: 'sliccy', kind: 'cone', parentId: null, role: null }];\n }\n\n #view(agentId: string = this.active()): ConversationView | undefined {\n const views = this.#connection.views?.value;\n const view = views?.[agentId];\n if (view) return view;\n const cone = this.#connection.agents?.value?.active ?? CONE;\n return agentId === cone ? this.#connection.transcript.value : undefined;\n }\n\n #refresh(): void {\n this.#active ??= this.#connection.agents?.value?.active ?? null;\n if (!this.#opened && (!this.#connection.agents || this.#connection.agents.value))\n this.#opened = new Set(this.#summary().map((summary) => summary.id));\n for (const summary of this.#summary()) this.#stamp(summary.id, this.#view(summary.id));\n this.emit('agents', this.list());\n let unread = false;\n for (const summary of this.#summary()) unread = this.#update(summary.id) || unread;\n for (const id of [...this.#messages.keys(), ...this.#notices.keys(), ...this.#seen.keys()])\n if (!this.#summary().some((summary) => summary.id === id)) {\n this.#messages.delete(id);\n this.#notices.delete(id);\n this.#seen.delete(id);\n this.#unread.delete(id);\n this.#queuedAt.delete(id);\n }\n if (unread) this.emit('agents', this.list());\n }\n\n #count(agentId: string, view: ConversationView, messages: readonly Message[]): boolean {\n const ids = answers(messages);\n const known = this.#seen.get(agentId);\n if (agentId === this.active() || (!known && (!this.#opened || this.#opened.has(agentId)))) {\n this.#seen.set(agentId, new Set(ids));\n return false;\n }\n const seen = known ?? new Set<string>();\n this.#seen.set(agentId, seen);\n const fresh = isBusy(view) ? [] : ids.filter((id) => !seen.has(id));\n if (!fresh.length) return false;\n for (const id of fresh) seen.add(id);\n this.#unread.set(agentId, (this.#unread.get(agentId) ?? 0) + fresh.length);\n return true;\n }\n\n notify(text: string, agentId: string = this.active()): void {\n const notes = this.#notices.get(agentId) ?? [];\n notes.push({\n at: this.#messages.get(agentId)?.length ?? 0,\n message: {\n id: `n${crypto.randomUUID()}`,\n role: 'system',\n kind: 'notice',\n text,\n createdAt: Date.now(),\n },\n });\n this.#notices.set(agentId, notes);\n this.emit('messages', agentId);\n }\n\n #report(agentId: string): (text: string) => void {\n return (text) => this.notify(text, agentId);\n }\n\n #update(agentId: string): boolean {\n const view = this.#view(agentId);\n if (!view) return false;\n const before = this.#messages.get(agentId) ?? [];\n const after = toMessages(view, this.#connection.deliveries.value);\n this.#messages.set(agentId, after);\n if (before.length !== after.length) this.emit('messages', agentId);\n else {\n const changed = after.filter(\n (message, index) => JSON.stringify(message) !== JSON.stringify(before[index])\n );\n if (changed.length === 1) this.emit('message', { agentId, message: changed[0] as Message });\n else if (changed.length > 1) this.emit('messages', agentId);\n }\n return this.#count(agentId, view, after);\n }\n\n ready(): Promise<void> {\n return this.#ready;\n }\n\n hold(): AgentHold | null {\n return vaultHold(this.#connection.vault?.value);\n }\n\n list(): readonly Agent[] {\n const rows = this.#connection.frozen?.value ?? [];\n return this.#summary().map((summary) => {\n const view = this.#view(summary.id);\n const doc = view?.docs['pi.agent'] as AgentDoc | undefined;\n const title = rows.find((row) => row.id === summary.id)?.title;\n const model = doc?.model ? `${doc.model.provider}/${doc.model.modelId}` : '';\n const models = this.#connection.settings?.state.value?.models ?? [];\n return {\n id: summary.id,\n name: summary.role ? `${summary.name} \u00B7 ${summary.role}` : summary.name,\n kind: summary.kind,\n parentId: summary.parentId,\n status: this.#status(summary.id, status(view, this.#messages.get(summary.id) ?? [])),\n model,\n contextFill: contextRatio(contextTokens(view), model, models) ?? 0,\n unread: this.#unread.get(summary.id) ?? 0,\n thinking: thinking[doc?.thinkingLevel ?? 'off'] ?? 'off',\n ...(title ? { title } : {}),\n };\n });\n }\n\n active(): string {\n const known = this.#summary();\n if (this.#active && this.#creating.has(this.#active)) return this.#active;\n if (this.#active && known.some((summary) => summary.id === this.#active)) return this.#active;\n return this.#connection.agents?.value?.active ?? CONE;\n }\n\n select(id: string): void {\n if (this.#creating.has(id)) {\n this.#active = id;\n this.emit('active', id);\n return;\n }\n const found = this.#summary().find((summary) => summary.id === id);\n if (!found) return;\n this.#active = id;\n this.#seen.set(id, new Set(answers(this.#messages.get(id) ?? [])));\n const read = this.#unread.delete(id);\n if (found.kind === 'cone')\n attempt(\n this.#connection.control.selectCone(id, BACKGROUND_CONTEXT),\n 'switch cones',\n this.#report(id)\n );\n this.emit('active', id);\n this.emit('messages', id);\n if (read) this.emit('agents', this.list());\n }\n\n messages(agentId: string = this.active()): readonly Message[] {\n const shown = this.#messages.get(agentId) ?? [];\n const notes = this.#notices.get(agentId);\n if (!notes) return shown;\n const at = (index: number) => notes.filter((note) => note.at === index).map((n) => n.message);\n const late = notes.filter((note) => note.at >= shown.length).map((note) => note.message);\n return [...shown.flatMap((message, index) => [...at(index), message]), ...late];\n }\n\n async send(agentId: string, input: string | Outgoing): Promise<void> {\n const outgoing = typeof input === 'string' ? { text: input } : input;\n const attachments = outgoing.attachments?.length\n ? await sendAttachments(outgoing.attachments, readUrl, shrink, (ref) =>\n this.#connection.control.image(ref, BACKGROUND_CONTEXT)\n ).catch((error: Error) => {\n this.notify(error.message, agentId);\n return null;\n })\n : [];\n if (!attachments) return;\n const target = this.#creating.has(agentId) ? await this.#creating.get(agentId) : agentId;\n if (target === null) throw new Error('The scoop could not be created.');\n const pending = target && this.#held(target) ? this.#pending(target, outgoing) : undefined;\n if (pending) await pending.released;\n if (pending?.dropped) return this.#settle(pending);\n const at = Date.now();\n const sent = await this.#connection.control\n .send(\n {\n text: outgoing.text,\n whenBusy: outgoing.mode === 'queue' ? 'followUp' : 'steer',\n requestId: crypto.randomUUID(),\n agentId: target || null,\n ...(attachments.length ? { attachments } : {}),\n },\n BACKGROUND_CONTEXT\n )\n .finally(() => pending && this.#settle(pending));\n if (!sent.accepted) throw new Error(sent.error.message);\n this.#sent(target || agentId, `q${sent.submissionId}`, at);\n if (pending?.dropped)\n attempt(\n this.#connection.control.unqueue(pending.agentId, sent.submissionId, BACKGROUND_CONTEXT),\n 'remove queued messages',\n this.#report(pending.agentId)\n );\n }\n\n #stamps(agentId: string): Map<string, { at: number; seen: boolean }> {\n const stamps = this.#queuedAt.get(agentId) ?? new Map();\n this.#queuedAt.set(agentId, stamps);\n return stamps;\n }\n\n #sent(agentId: string, id: string, at: number): void {\n const stamps = this.#stamps(agentId);\n const known = stamps.get(id);\n stamps.set(id, { at: Math.min(at, known?.at ?? at), seen: known?.seen ?? false });\n }\n\n #stamp(agentId: string, view: ConversationView | undefined): void {\n const stamps = this.#stamps(agentId);\n const ids = new Set(queued(view).map((message) => message.id));\n const now = Date.now();\n for (const id of ids) stamps.set(id, { at: stamps.get(id)?.at ?? now, seen: true });\n for (const [id, stamp] of stamps)\n if (!ids.has(id) && (stamp.seen || (!ids.size && !isBusy(view)))) stamps.delete(id);\n }\n\n #pending(agentId: string, outgoing: Outgoing): Pending {\n const id = `p${crypto.randomUUID()}`;\n const message: UserMessage = {\n id,\n role: 'user',\n text: outgoing.text,\n createdAt: Date.now(),\n ...(outgoing.attachments?.length ? { attachments: [...outgoing.attachments] } : {}),\n queued: true,\n mode: outgoing.mode === 'queue' ? 'queue' : 'steer',\n };\n const { promise: released, resolve: release } = Promise.withResolvers<void>();\n const pending = { agentId, message, dropped: false, release, released };\n this.#outbox.set(id, pending);\n this.emit('messages', agentId);\n return pending;\n }\n\n #settle(pending: Pending): void {\n this.#outbox.delete(pending.message.id);\n this.emit('messages', pending.agentId);\n }\n\n async rewind(agentId: string, messageId: string): Promise<Outgoing | null> {\n const rewound = await this.#connection.control.rewindAgent(\n agentId,\n messageId,\n BACKGROUND_CONTEXT\n );\n if (!rewound.done) return null;\n return rewound.attachments?.length\n ? { text: rewound.text, attachments: rewound.attachments }\n : { text: rewound.text };\n }\n\n stop(agentId: string = this.active()): void {\n attempt(\n this.#connection.control.stopAgent(agentId, BACKGROUND_CONTEXT),\n 'stop agents',\n this.#report(agentId)\n );\n }\n\n busy(agentId: string = this.active()): boolean {\n return isBusy(this.#view(agentId)) || this.#held(agentId);\n }\n\n #status(agentId: string, shown: AgentStatus): AgentStatus {\n return shown === 'idle' && this.#held(agentId) ? 'working' : shown;\n }\n\n #held(agentId: string): boolean {\n return this.#hold !== null && agentId === (this.#connection.agents?.value?.active ?? CONE);\n }\n\n queue(agentId: string = this.active()): readonly UserMessage[] {\n const pending = [...this.#outbox.values()]\n .filter((item) => item.agentId === agentId && !item.dropped)\n .map((item) => item.message);\n const stamps = this.#queuedAt.get(agentId);\n const at = (id: string) => stamps?.get(id)?.at ?? 0;\n return [...queued(this.#view(agentId), at), ...pending];\n }\n\n unqueue(agentId: string, messageId: string): void {\n const pending = this.#outbox.get(messageId);\n if (pending) {\n pending.dropped = true;\n pending.release();\n this.emit('messages', pending.agentId);\n return;\n }\n attempt(\n this.#connection.control.unqueue(agentId, messageId.replace(/^q/, ''), BACKGROUND_CONTEXT),\n 'remove queued messages',\n this.#report(agentId)\n );\n }\n\n suggestion(): string | null {\n return null;\n }\n\n answer(): void {}\n\n resolveLick(agentId: string, messageId: string, state: 'confirmed' | 'dismissed'): void {\n const message = this.messages(agentId).find((candidate) => candidate.id === messageId);\n const entry = this.#view(agentId)?.entries.find(\n (candidate) => `e${candidate.id}` === messageId\n );\n const lick = message?.role === 'lick' && entry ? entryLick(entry) : undefined;\n if (lick)\n attempt(\n this.#connection.control.resolveAgentLick(agentId, lick.id, state, BACKGROUND_CONTEXT),\n 'answer licks',\n this.#report(agentId)\n );\n }\n\n compact(agentId: string = this.active()): void {\n attempt(\n this.#connection.control.compactAgent(agentId, null, BACKGROUND_CONTEXT),\n 'compact conversations',\n this.#report(agentId)\n );\n }\n\n clear(agentId: string = this.active()): void {\n attempt(\n this.#connection.control.newChat(agentId, BACKGROUND_CONTEXT),\n 'start new chats',\n this.#report(agentId)\n );\n }\n\n freeze(agentId: string = this.active()): void {\n attempt(\n this.#connection.control.freeze(agentId, BACKGROUND_CONTEXT),\n 'freeze cones',\n this.#report(agentId)\n );\n }\n\n #configure(agentId: string, change: Partial<AgentSettingsChange>): void {\n attempt(\n this.#connection.control.configure(\n {\n model: change.model ?? null,\n thinkingLevel: change.thinkingLevel ?? null,\n agentId,\n },\n BACKGROUND_CONTEXT\n ),\n 'change agent settings',\n this.#report(agentId)\n );\n }\n\n setModel(agentId: string, model: string): void {\n const at = model.indexOf('/');\n if (at <= 0) return;\n this.#configure(agentId, {\n model: { provider: model.slice(0, at), modelId: model.slice(at + 1) },\n });\n }\n\n setThinking(agentId: string, level: Thinking): void {\n this.#configure(agentId, { thinkingLevel: level });\n }\n\n async older(): Promise<readonly Message[]> {\n return [];\n }\n\n #fetchCommands(agentId: string): void {\n if (this.#agentCommandsLoading.has(agentId)) return;\n if (typeof this.#connection.control.agentCommands !== 'function') return;\n this.#agentCommandsLoading.add(agentId);\n const generation = this.#agentCommandsGeneration;\n this.#connection.control\n .agentCommands(agentId, BACKGROUND_CONTEXT)\n .then((commands) => {\n if (generation !== this.#agentCommandsGeneration) return;\n this.#agentCommandsCache.set(agentId, commands);\n this.emit('agents', this.list());\n })\n .catch(() => undefined)\n .finally(() => {\n if (generation === this.#agentCommandsGeneration)\n this.#agentCommandsLoading.delete(agentId);\n });\n }\n\n commands(agentId: string = this.active()): readonly SlashCommand[] {\n const cached = this.#agentCommandsCache.get(agentId);\n if (cached) return cached.map(slashCommand);\n this.#fetchCommands(agentId);\n return (this.#connection.commands?.value ?? []).map(slashCommand);\n }\n\n createScoop(parentId: string, name: string): Agent {\n const provisional = `scoop:creating-${crypto.randomUUID()}`;\n const created = this.#connection.control.createScoop(parentId, name, BACKGROUND_CONTEXT).then(\n (result) => result.id,\n () => null\n );\n this.#creating.set(provisional, created);\n void created.then((id) => {\n if (this.#active !== provisional) return;\n this.#active = id ?? parentId;\n this.emit('active', this.#active);\n this.emit('messages', this.#active);\n });\n return {\n id: provisional,\n name,\n kind: 'scoop',\n parentId,\n status: 'idle',\n model: '',\n contextFill: 0,\n unread: 0,\n };\n }\n\n async drop(agentId: string): Promise<void> {\n const parent = this.#summary().find((summary) => summary.id === agentId)?.parentId ?? CONE;\n const dropped = await this.#connection.control.drop(agentId, BACKGROUND_CONTEXT);\n if (dropped.error !== null) throw new Error(dropped.error);\n if (this.#active === agentId) this.select(parent);\n }\n\n async createCone(name: string): Promise<Agent> {\n const created = await this.#connection.control.createCone(name, BACKGROUND_CONTEXT);\n if (created.error !== null) throw new Error(created.error);\n return {\n id: created.id,\n name: name.trim(),\n kind: 'cone',\n parentId: null,\n status: 'idle',\n model: '',\n contextFill: 0,\n unread: 0,\n };\n }\n\n frozen(): readonly FrozenCone[] {\n return (this.#connection.frozen?.value ?? []).map(\n ({ id, name, title, model, messages, frozenAt, kind, live, thawedAs }) => ({\n id,\n name,\n title,\n model,\n messages,\n frozenAt,\n kind,\n live,\n ...(thawedAs ? { thawedAs } : {}),\n })\n );\n }\n\n thaw(id: string): Agent | null {\n const record = this.frozen().find((item) => item.id === id);\n if (!record) return null;\n if (record.live) {\n const running = this.list().find((agent) => agent.id === id) ?? null;\n if (running) this.select(id);\n return running;\n }\n const provisional = `thawing-${crypto.randomUUID()}`;\n const thawed = this.#connection.control.thaw(id, BACKGROUND_CONTEXT).then(\n (result) => result.id,\n () => null\n );\n this.#creating.set(provisional, thawed);\n void thawed.then((cone) => {\n if (this.#active !== provisional) return;\n this.#active = cone ?? CONE;\n this.emit('active', this.#active);\n this.emit('messages', this.#active);\n });\n return {\n id: provisional,\n name: record.name,\n kind: 'cone',\n parentId: null,\n status: 'idle',\n model: record.model,\n contextFill: 0,\n unread: 0,\n };\n }\n\n discard(id: string): void {\n attempt(\n this.#connection.control.discard(id, BACKGROUND_CONTEXT),\n 'discard frozen cones',\n this.#report(this.active())\n );\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type {\n Agent,\n AgentEvents,\n AgentPort,\n AgentStatus,\n FrozenCone,\n Message,\n Outgoing,\n SlashCommand,\n SystemMessage,\n Thinking,\n UserMessage,\n} from '@ai-ecoverse/slicc-spectrum/ui';\nimport { BACKGROUND_CONTEXT } from '@earendil-works/chord/context';\nimport type { ConversationView } from '@earendil-works/pi-durable';\nimport type { AgentConnection } from '../client.ts';\nimport { contextRatio, contextTokens } from '../context.ts';\nimport { shrink } from '../images/fit.ts';\nimport { entryLick } from '../licks/state.ts';\nimport { type VaultHold, vaultHold } from '../secrets/view.ts';\nimport type { AgentSettingsChange, AgentSummary, Command } from '../services.ts';\nimport { shownLabel } from '../titles/shown.ts';\nimport { attempt } from './attempt.ts';\nimport { Emitter } from './emitter.ts';\nimport { isBusy, queued, toMessages } from './messages.ts';\nimport { readUrl, sendAttachments } from './outgoing.ts';\n\nexport const CONE = 'cone';\n\nconst thinking: Record<string, Thinking> = {\n off: 'off',\n minimal: 'low',\n low: 'low',\n medium: 'medium',\n high: 'high',\n xhigh: 'high',\n max: 'high',\n};\n\ninterface AgentDoc {\n model?: { provider: string; modelId: string };\n thinkingLevel?: string;\n}\n\nfunction status(view: ConversationView | undefined, messages: readonly Message[]): AgentStatus {\n if (isBusy(view)) {\n const live = view?.docs['pi.live'] as { tools?: { status: string }[] } | undefined;\n return live?.tools?.some((slot) => slot.status === 'running') ? 'working' : 'thinking';\n }\n const last = messages.at(-1);\n return last?.role === 'assistant' && last.status === 'error' ? 'error' : 'idle';\n}\n\nfunction answers(messages: readonly Message[]): string[] {\n return messages\n .filter((message) => message.role === 'assistant' && message.status !== 'streaming')\n .map((message) => message.id);\n}\n\nexport function slashCommand({ kind, ...command }: Command): SlashCommand {\n return kind === 'action' ? command : { ...command, kind };\n}\n\nexport type AgentHold = VaultHold;\n\nexport type AgentAdapterEvents = AgentEvents & { hold: AgentHold | null };\n\nfunction sameHold(a: AgentHold | null, b: AgentHold | null): boolean {\n return a === b || (a !== null && b !== null && a.reason === b.reason && a.held === b.held);\n}\n\ninterface Pending {\n agentId: string;\n message: UserMessage;\n dropped: boolean;\n release(): void;\n released: Promise<void>;\n}\n\nexport class AgentAdapter extends Emitter<AgentAdapterEvents> implements AgentPort {\n readonly #connection: AgentConnection;\n readonly #ready: Promise<void>;\n readonly #messages = new Map<string, Message[]>();\n #active: string | null = null;\n #creating = new Map<string, Promise<string | null>>();\n #hold: AgentHold | null = null;\n readonly #outbox = new Map<string, Pending>();\n readonly #notices = new Map<string, { at: number; message: SystemMessage }[]>();\n readonly #unread = new Map<string, number>();\n readonly #seen = new Map<string, Set<string>>();\n readonly #queuedAt = new Map<string, Map<string, { at: number; seen: boolean }>>();\n #opened: ReadonlySet<string> | null = null;\n readonly #agentCommandsCache = new Map<string, Command[]>();\n readonly #agentCommandsLoading = new Set<string>();\n #agentCommandsGeneration = 0;\n\n constructor(connection: AgentConnection) {\n super();\n this.#connection = connection;\n this.#hold = this.hold();\n connection.vault?.subscribe(() => {\n const next = this.hold();\n if (sameHold(next, this.#hold)) return;\n this.#hold = next;\n if (!next) for (const pending of this.#outbox.values()) pending.release();\n this.emit('hold', next);\n this.emit('agents', this.list());\n });\n connection.frozen?.subscribe(() => {\n this.emit('frozen', this.frozen());\n this.emit('agents', this.list());\n });\n this.#ready = new Promise((resolve) => {\n connection.transcript.subscribe(() => {\n this.#refresh();\n resolve();\n });\n connection.deliveries.subscribe(() => this.#refresh());\n connection.views?.subscribe(() => this.#refresh());\n connection.agents?.subscribe(() => this.#refresh());\n });\n connection.settings?.state.subscribe(() => this.emit('agents', this.list()));\n const stale = () => {\n this.#agentCommandsGeneration += 1;\n this.#agentCommandsLoading.clear();\n this.#agentCommandsCache.clear();\n this.emit('agents', this.list());\n };\n connection.commands?.subscribe(stale);\n connection.privateRevision?.subscribe(stale);\n }\n\n #summary(): readonly AgentSummary[] {\n const agents = this.#connection.agents?.value?.agents;\n return agents?.length\n ? agents\n : [{ id: CONE, name: 'sliccy', kind: 'cone', parentId: null, role: null }];\n }\n\n #view(agentId: string = this.active()): ConversationView | undefined {\n const views = this.#connection.views?.value;\n const view = views?.[agentId];\n if (view) return view;\n const cone = this.#connection.agents?.value?.active ?? CONE;\n return agentId === cone ? this.#connection.transcript.value : undefined;\n }\n\n #refresh(): void {\n this.#active ??= this.#connection.agents?.value?.active ?? null;\n if (!this.#opened && (!this.#connection.agents || this.#connection.agents.value))\n this.#opened = new Set(this.#summary().map((summary) => summary.id));\n for (const summary of this.#summary()) this.#stamp(summary.id, this.#view(summary.id));\n this.emit('agents', this.list());\n let unread = false;\n for (const summary of this.#summary()) unread = this.#update(summary.id) || unread;\n for (const id of [...this.#messages.keys(), ...this.#notices.keys(), ...this.#seen.keys()])\n if (!this.#summary().some((summary) => summary.id === id)) {\n this.#messages.delete(id);\n this.#notices.delete(id);\n this.#seen.delete(id);\n this.#unread.delete(id);\n this.#queuedAt.delete(id);\n }\n if (unread) this.emit('agents', this.list());\n }\n\n #count(agentId: string, view: ConversationView, messages: readonly Message[]): boolean {\n const ids = answers(messages);\n const known = this.#seen.get(agentId);\n if (agentId === this.active() || (!known && (!this.#opened || this.#opened.has(agentId)))) {\n this.#seen.set(agentId, new Set(ids));\n return false;\n }\n const seen = known ?? new Set<string>();\n this.#seen.set(agentId, seen);\n const fresh = isBusy(view) ? [] : ids.filter((id) => !seen.has(id));\n if (!fresh.length) return false;\n for (const id of fresh) seen.add(id);\n this.#unread.set(agentId, (this.#unread.get(agentId) ?? 0) + fresh.length);\n return true;\n }\n\n notify(text: string, agentId: string = this.active()): void {\n const notes = this.#notices.get(agentId) ?? [];\n notes.push({\n at: this.#messages.get(agentId)?.length ?? 0,\n message: {\n id: `n${crypto.randomUUID()}`,\n role: 'system',\n kind: 'notice',\n text,\n createdAt: Date.now(),\n },\n });\n this.#notices.set(agentId, notes);\n this.emit('messages', agentId);\n }\n\n #report(agentId: string): (text: string) => void {\n return (text) => this.notify(text, agentId);\n }\n\n #update(agentId: string): boolean {\n const view = this.#view(agentId);\n if (!view) return false;\n const before = this.#messages.get(agentId) ?? [];\n const after = toMessages(view, this.#connection.deliveries.value);\n this.#messages.set(agentId, after);\n if (before.length !== after.length) this.emit('messages', agentId);\n else {\n const changed = after.filter(\n (message, index) => JSON.stringify(message) !== JSON.stringify(before[index])\n );\n if (changed.length === 1) this.emit('message', { agentId, message: changed[0] as Message });\n else if (changed.length > 1) this.emit('messages', agentId);\n }\n return this.#count(agentId, view, after);\n }\n\n ready(): Promise<void> {\n return this.#ready;\n }\n\n hold(): AgentHold | null {\n return vaultHold(this.#connection.vault?.value);\n }\n\n list(): readonly Agent[] {\n const rows = this.#connection.frozen?.value ?? [];\n return this.#summary().map((summary) => {\n const view = this.#view(summary.id);\n const doc = view?.docs['pi.agent'] as AgentDoc | undefined;\n const title = rows.find((row) => row.id === summary.id)?.title;\n const model = doc?.model ? `${doc.model.provider}/${doc.model.modelId}` : '';\n const models = this.#connection.settings?.state.value?.models ?? [];\n return {\n id: summary.id,\n name: summary.role ? `${summary.name} \u00B7 ${summary.role}` : summary.name,\n kind: summary.kind,\n parentId: summary.parentId,\n status: this.#status(summary.id, status(view, this.#messages.get(summary.id) ?? [])),\n model,\n contextFill: contextRatio(contextTokens(view), model, models) ?? 0,\n unread: this.#unread.get(summary.id) ?? 0,\n thinking: thinking[doc?.thinkingLevel ?? 'off'] ?? 'off',\n ...(title ? { title } : {}),\n };\n });\n }\n\n active(): string {\n const known = this.#summary();\n if (this.#active && this.#creating.has(this.#active)) return this.#active;\n if (this.#active && known.some((summary) => summary.id === this.#active)) return this.#active;\n return this.#connection.agents?.value?.active ?? CONE;\n }\n\n select(id: string): void {\n if (this.#creating.has(id)) {\n this.#active = id;\n this.emit('active', id);\n return;\n }\n const found = this.#summary().find((summary) => summary.id === id);\n if (!found) return;\n this.#active = id;\n this.#seen.set(id, new Set(answers(this.#messages.get(id) ?? [])));\n const read = this.#unread.delete(id);\n if (found.kind === 'cone')\n attempt(\n this.#connection.control.selectCone(id, BACKGROUND_CONTEXT),\n 'switch cones',\n this.#report(id)\n );\n this.emit('active', id);\n this.emit('messages', id);\n if (read) this.emit('agents', this.list());\n }\n\n messages(agentId: string = this.active()): readonly Message[] {\n const shown = this.#messages.get(agentId) ?? [];\n const notes = this.#notices.get(agentId);\n if (!notes) return shown;\n const at = (index: number) => notes.filter((note) => note.at === index).map((n) => n.message);\n const late = notes.filter((note) => note.at >= shown.length).map((note) => note.message);\n return [...shown.flatMap((message, index) => [...at(index), message]), ...late];\n }\n\n async send(agentId: string, input: string | Outgoing): Promise<void> {\n const outgoing = typeof input === 'string' ? { text: input } : input;\n const attachments = outgoing.attachments?.length\n ? await sendAttachments(outgoing.attachments, readUrl, shrink, (ref) =>\n this.#connection.control.image(ref, BACKGROUND_CONTEXT)\n ).catch((error: Error) => {\n this.notify(error.message, agentId);\n return null;\n })\n : [];\n if (!attachments) return;\n const target = this.#creating.has(agentId) ? await this.#creating.get(agentId) : agentId;\n if (target === null) throw new Error('The scoop could not be created.');\n const pending = target && this.#held(target) ? this.#pending(target, outgoing) : undefined;\n if (pending) await pending.released;\n if (pending?.dropped) return this.#settle(pending);\n const at = Date.now();\n const sent = await this.#connection.control\n .send(\n {\n text: outgoing.text,\n whenBusy: outgoing.mode === 'queue' ? 'followUp' : 'steer',\n requestId: crypto.randomUUID(),\n agentId: target || null,\n ...(attachments.length ? { attachments } : {}),\n },\n BACKGROUND_CONTEXT\n )\n .finally(() => pending && this.#settle(pending));\n if (!sent.accepted) throw new Error(sent.error.message);\n this.#sent(target || agentId, `q${sent.submissionId}`, at);\n if (pending?.dropped)\n attempt(\n this.#connection.control.unqueue(pending.agentId, sent.submissionId, BACKGROUND_CONTEXT),\n 'remove queued messages',\n this.#report(pending.agentId)\n );\n }\n\n #stamps(agentId: string): Map<string, { at: number; seen: boolean }> {\n const stamps = this.#queuedAt.get(agentId) ?? new Map();\n this.#queuedAt.set(agentId, stamps);\n return stamps;\n }\n\n #sent(agentId: string, id: string, at: number): void {\n const stamps = this.#stamps(agentId);\n const known = stamps.get(id);\n stamps.set(id, { at: Math.min(at, known?.at ?? at), seen: known?.seen ?? false });\n }\n\n #stamp(agentId: string, view: ConversationView | undefined): void {\n const stamps = this.#stamps(agentId);\n const ids = new Set(queued(view).map((message) => message.id));\n const now = Date.now();\n for (const id of ids) stamps.set(id, { at: stamps.get(id)?.at ?? now, seen: true });\n for (const [id, stamp] of stamps)\n if (!ids.has(id) && (stamp.seen || (!ids.size && !isBusy(view)))) stamps.delete(id);\n }\n\n #pending(agentId: string, outgoing: Outgoing): Pending {\n const id = `p${crypto.randomUUID()}`;\n const message: UserMessage = {\n id,\n role: 'user',\n text: outgoing.text,\n createdAt: Date.now(),\n ...(outgoing.attachments?.length ? { attachments: [...outgoing.attachments] } : {}),\n queued: true,\n mode: outgoing.mode === 'queue' ? 'queue' : 'steer',\n };\n const { promise: released, resolve: release } = Promise.withResolvers<void>();\n const pending = { agentId, message, dropped: false, release, released };\n this.#outbox.set(id, pending);\n this.emit('messages', agentId);\n return pending;\n }\n\n #settle(pending: Pending): void {\n this.#outbox.delete(pending.message.id);\n this.emit('messages', pending.agentId);\n }\n\n async rewind(agentId: string, messageId: string): Promise<Outgoing | null> {\n const rewound = await this.#connection.control.rewindAgent(\n agentId,\n messageId,\n BACKGROUND_CONTEXT\n );\n if (!rewound.done) return null;\n return rewound.attachments?.length\n ? { text: rewound.text, attachments: rewound.attachments }\n : { text: rewound.text };\n }\n\n stop(agentId: string = this.active()): void {\n attempt(\n this.#connection.control.stopAgent(agentId, BACKGROUND_CONTEXT),\n 'stop agents',\n this.#report(agentId)\n );\n }\n\n busy(agentId: string = this.active()): boolean {\n return isBusy(this.#view(agentId)) || this.#held(agentId);\n }\n\n #status(agentId: string, shown: AgentStatus): AgentStatus {\n return shown === 'idle' && this.#held(agentId) ? 'working' : shown;\n }\n\n #held(agentId: string): boolean {\n return this.#hold !== null && agentId === (this.#connection.agents?.value?.active ?? CONE);\n }\n\n queue(agentId: string = this.active()): readonly UserMessage[] {\n const pending = [...this.#outbox.values()]\n .filter((item) => item.agentId === agentId && !item.dropped)\n .map((item) => item.message);\n const stamps = this.#queuedAt.get(agentId);\n const at = (id: string) => stamps?.get(id)?.at ?? 0;\n return [...queued(this.#view(agentId), at), ...pending];\n }\n\n unqueue(agentId: string, messageId: string): void {\n const pending = this.#outbox.get(messageId);\n if (pending) {\n pending.dropped = true;\n pending.release();\n this.emit('messages', pending.agentId);\n return;\n }\n attempt(\n this.#connection.control.unqueue(agentId, messageId.replace(/^q/, ''), BACKGROUND_CONTEXT),\n 'remove queued messages',\n this.#report(agentId)\n );\n }\n\n suggestion(): string | null {\n return null;\n }\n\n answer(): void {}\n\n resolveLick(agentId: string, messageId: string, state: 'confirmed' | 'dismissed'): void {\n const message = this.messages(agentId).find((candidate) => candidate.id === messageId);\n const entry = this.#view(agentId)?.entries.find(\n (candidate) => `e${candidate.id}` === messageId\n );\n const lick = message?.role === 'lick' && entry ? entryLick(entry) : undefined;\n if (lick)\n attempt(\n this.#connection.control.resolveAgentLick(agentId, lick.id, state, BACKGROUND_CONTEXT),\n 'answer licks',\n this.#report(agentId)\n );\n }\n\n compact(agentId: string = this.active()): void {\n attempt(\n this.#connection.control.compactAgent(agentId, null, BACKGROUND_CONTEXT),\n 'compact conversations',\n this.#report(agentId)\n );\n }\n\n clear(agentId: string = this.active()): void {\n attempt(\n this.#connection.control.newChat(agentId, BACKGROUND_CONTEXT),\n 'start new chats',\n this.#report(agentId)\n );\n }\n\n freeze(agentId: string = this.active()): void {\n attempt(\n this.#connection.control.freeze(agentId, BACKGROUND_CONTEXT),\n 'freeze cones',\n this.#report(agentId)\n );\n }\n\n #configure(agentId: string, change: Partial<AgentSettingsChange>): void {\n attempt(\n this.#connection.control.configure(\n {\n model: change.model ?? null,\n thinkingLevel: change.thinkingLevel ?? null,\n agentId,\n },\n BACKGROUND_CONTEXT\n ),\n 'change agent settings',\n this.#report(agentId)\n );\n }\n\n setModel(agentId: string, model: string): void {\n const at = model.indexOf('/');\n if (at <= 0) return;\n this.#configure(agentId, {\n model: { provider: model.slice(0, at), modelId: model.slice(at + 1) },\n });\n }\n\n setThinking(agentId: string, level: Thinking): void {\n this.#configure(agentId, { thinkingLevel: level });\n }\n\n async older(): Promise<readonly Message[]> {\n return [];\n }\n\n #fetchCommands(agentId: string): void {\n if (this.#agentCommandsLoading.has(agentId)) return;\n if (typeof this.#connection.control.agentCommands !== 'function') return;\n this.#agentCommandsLoading.add(agentId);\n const generation = this.#agentCommandsGeneration;\n this.#connection.control\n .agentCommands(agentId, BACKGROUND_CONTEXT)\n .then((commands) => {\n if (generation !== this.#agentCommandsGeneration) return;\n this.#agentCommandsCache.set(agentId, commands);\n this.emit('agents', this.list());\n })\n .catch(() => undefined)\n .finally(() => {\n if (generation === this.#agentCommandsGeneration)\n this.#agentCommandsLoading.delete(agentId);\n });\n }\n\n commands(agentId: string = this.active()): readonly SlashCommand[] {\n const cached = this.#agentCommandsCache.get(agentId);\n if (cached) return cached.map(slashCommand);\n this.#fetchCommands(agentId);\n return (this.#connection.commands?.value ?? []).map(slashCommand);\n }\n\n createScoop(parentId: string, name: string): Agent {\n const provisional = `scoop:creating-${crypto.randomUUID()}`;\n const created = this.#connection.control.createScoop(parentId, name, BACKGROUND_CONTEXT).then(\n (result) => result.id,\n () => null\n );\n this.#creating.set(provisional, created);\n void created.then((id) => {\n if (this.#active !== provisional) return;\n this.#active = id ?? parentId;\n this.emit('active', this.#active);\n this.emit('messages', this.#active);\n });\n return {\n id: provisional,\n name,\n kind: 'scoop',\n parentId,\n status: 'idle',\n model: '',\n contextFill: 0,\n unread: 0,\n };\n }\n\n async drop(agentId: string): Promise<void> {\n const parent = this.#summary().find((summary) => summary.id === agentId)?.parentId ?? CONE;\n const dropped = await this.#connection.control.drop(agentId, BACKGROUND_CONTEXT);\n if (dropped.error !== null) throw new Error(dropped.error);\n if (this.#active === agentId) this.select(parent);\n }\n\n async createCone(name: string): Promise<Agent> {\n const created = await this.#connection.control.createCone(name, BACKGROUND_CONTEXT);\n if (created.error !== null) throw new Error(created.error);\n return {\n id: created.id,\n name: name.trim(),\n kind: 'cone',\n parentId: null,\n status: 'idle',\n model: '',\n contextFill: 0,\n unread: 0,\n };\n }\n\n async toolLabel(agentId: string, callIds: readonly string[]): Promise<string | null> {\n const shown = shownLabel(this.#connection.labels?.value, callIds);\n if (shown !== undefined) return shown;\n return this.#connection.control\n .toolLabel(agentId, [...callIds], BACKGROUND_CONTEXT)\n .catch(() => null);\n }\n\n frozen(): readonly FrozenCone[] {\n return (this.#connection.frozen?.value ?? []).map(\n ({ id, name, title, model, messages, frozenAt, kind, live, thawedAs }) => ({\n id,\n name,\n title,\n model,\n messages,\n frozenAt,\n kind,\n live,\n ...(thawedAs ? { thawedAs } : {}),\n })\n );\n }\n\n thaw(id: string): Agent | null {\n const record = this.frozen().find((item) => item.id === id);\n if (!record) return null;\n if (record.live) {\n const running = this.list().find((agent) => agent.id === id) ?? null;\n if (running) this.select(id);\n return running;\n }\n const provisional = `thawing-${crypto.randomUUID()}`;\n const thawed = this.#connection.control.thaw(id, BACKGROUND_CONTEXT).then(\n (result) => result.id,\n () => null\n );\n this.#creating.set(provisional, thawed);\n void thawed.then((cone) => {\n if (this.#active !== provisional) return;\n this.#active = cone ?? CONE;\n this.emit('active', this.#active);\n this.emit('messages', this.#active);\n });\n return {\n id: provisional,\n name: record.name,\n kind: 'cone',\n parentId: null,\n status: 'idle',\n model: record.model,\n contextFill: 0,\n unread: 0,\n };\n }\n\n discard(id: string): void {\n attempt(\n this.#connection.control.discard(id, BACKGROUND_CONTEXT),\n 'discard frozen cones',\n this.#report(this.active())\n );\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,4 +1,5 @@
1
1
  import { BACKGROUND_CONTEXT } from "@earendil-works/chord/context";
2
+ import { preferredModel } from "../preferred.js";
2
3
  import { attempt } from "./attempt.js";
3
4
  import { Emitter } from "./emitter.js";
4
5
  import { mcpLogin } from "./mcp-login.js";
@@ -53,14 +54,28 @@ class SettingsAdapter extends Emitter {
53
54
  this.emit("settings", this.get());
54
55
  }
55
56
  models() {
56
- return this.#state().models.map(({ id, label, provider, kind, reasoning, images }) => ({
57
- id,
58
- label,
59
- provider,
60
- kind,
61
- reasoning,
62
- ...images === void 0 ? {} : { images }
63
- }));
57
+ const choices = this.#state().models;
58
+ const groups = Map.groupBy(choices, ({ id }) => id.slice(0, id.indexOf("/")));
59
+ const chosen = new Set(
60
+ [...groups].map(
61
+ ([providerId, group]) => preferredModel(
62
+ providerId,
63
+ group.map((choice) => ({ id: choice.id.slice(providerId.length + 1), choice }))
64
+ )?.choice
65
+ )
66
+ );
67
+ return choices.map((choice) => {
68
+ const { id, label, provider, kind, reasoning, images } = choice;
69
+ return {
70
+ id,
71
+ label,
72
+ provider,
73
+ kind,
74
+ reasoning,
75
+ ...images === void 0 ? {} : { images },
76
+ ...chosen.has(choice) ? { default: true } : {}
77
+ };
78
+ });
64
79
  }
65
80
  accounts() {
66
81
  return this.#state().accounts.map(({ needs, ...account }) => {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/spectrum/settings-port.ts"],
4
- "sourcesContent": ["import type {\n Account,\n ModelOption,\n Settings,\n SettingsEvents,\n SettingsPort,\n} from '@ai-ecoverse/slicc-spectrum/ui';\nimport { BACKGROUND_CONTEXT } from '@earendil-works/chord/context';\nimport type { AgentSettings, SettingsState, SignIn } from '../services.ts';\nimport type { AgentAdapter } from './agent-port.ts';\nimport { attempt } from './attempt.ts';\nimport { Emitter } from './emitter.ts';\nimport { mcpLogin } from './mcp-login.ts';\n\nexport const SETTINGS_KEY = 'slicc-agent.settings';\n\nconst defaults: Settings = {\n color: 'system',\n model: '',\n thinking: 'off',\n sendOnEnter: true,\n showThinking: false,\n diffStyle: 'unified',\n};\n\ntype Local = Omit<Settings, 'model' | 'thinking'>;\n\nexport type Sealed = { sealed: true };\n\nexport type Login = (\n providerId: string,\n signIn: () => Promise<SignIn | null>,\n options: { signal: AbortSignal }\n) => Promise<string | Sealed>;\n\nexport const SIGN_IN_NEEDS_VAULT = \"Adobe sign-in needs seven's vault: update seven\";\n\nexport class SettingsAdapter extends Emitter<SettingsEvents> implements SettingsPort {\n readonly #settings: AgentSettings;\n readonly #agent: AgentAdapter;\n readonly #storage: Pick<Storage, 'getItem' | 'setItem'> | null;\n readonly #login: Login | null;\n readonly #mcpLogin: Login;\n readonly #signing = new Map<string, AbortController>();\n #local: Local;\n\n constructor(\n settings: AgentSettings,\n agent: AgentAdapter,\n storage: Pick<Storage, 'getItem' | 'setItem'> | null,\n login: Login | null = null,\n mcp: Login = mcpLogin() as Login\n ) {\n super();\n this.#mcpLogin = mcp;\n this.#settings = settings;\n this.#agent = agent;\n this.#storage = storage;\n this.#login = login;\n const saved = JSON.parse(storage?.getItem(SETTINGS_KEY) ?? '{}') as Partial<Local>;\n const { model: _model, thinking: _thinking, ...local } = { ...defaults, ...saved };\n this.#local = local;\n settings.state.subscribe(() => {\n this.emit('accounts', this.accounts());\n this.emit('settings', this.get());\n });\n agent.on('agents', () => this.emit('settings', this.get()));\n }\n\n #state(): SettingsState {\n return this.#settings.state.value ?? { models: [], accounts: [] };\n }\n\n get(): Settings {\n const cone = this.#agent.list()[0];\n return { ...this.#local, model: cone?.model ?? '', thinking: cone?.thinking ?? 'off' };\n }\n\n update(patch: Partial<Settings>): void {\n const { model, thinking, ...local } = patch;\n const id = this.#agent.active();\n if (model !== undefined) this.#agent.setModel(id, model);\n if (thinking !== undefined) this.#agent.setThinking(id, thinking);\n this.#local = { ...this.#local, ...local };\n this.#storage?.setItem(SETTINGS_KEY, JSON.stringify(this.#local));\n this.emit('settings', this.get());\n }\n\n models(): readonly ModelOption[] {\n return this.#state().models.map(({ id, label, provider, kind, reasoning, images }) => ({\n id,\n label,\n provider,\n kind,\n reasoning,\n ...(images === undefined ? {} : { images }),\n }));\n }\n\n accounts(): readonly Account[] {\n return this.#state().accounts.map(({ needs, ...account }) => {\n const shown = this.#signing.has(account.id)\n ? { ...account, status: 'signing-in' as const }\n : account;\n return needs ? { ...shown, needs } : shown;\n });\n }\n\n async connect(id: string, secret?: string, fields?: Record<string, string>): Promise<void> {\n const account = this.#state().accounts.find((candidate) => candidate.id === id);\n const mcp = account?.group === 'mcp';\n const login = account?.auth !== 'oauth' ? null : mcp ? this.#mcpLogin : this.#login;\n const given = fields ? { ...fields } : null;\n if (secret !== undefined) {\n await this.#settings.connect(id, secret, given, BACKGROUND_CONTEXT);\n return;\n }\n if (account?.auth === 'oauth' && !login) throw new Error(`${id} needs a sign-in`);\n if (!login) {\n if (account?.store !== 'vault') throw new Error(`${id} needs an API key`);\n await this.#settings.connect(id, null, given, BACKGROUND_CONTEXT);\n return;\n }\n const answer = await this.#signIn(id, login);\n if (typeof answer === 'string') {\n if (!mcp) throw new Error(SIGN_IN_NEEDS_VAULT);\n await this.#settings.connect(id, answer, given, BACKGROUND_CONTEXT);\n return;\n }\n if (answer?.sealed !== true) throw new Error(`${id}'s sign-in didn't finish`);\n await this.#settings.connect(id, null, given, BACKGROUND_CONTEXT);\n }\n\n async #signIn(id: string, login: Login): Promise<string | Sealed> {\n const controller = new AbortController();\n this.#signing\n .get(id)\n ?.abort(new DOMException('A newer sign-in replaced this one.', 'AbortError'));\n this.#signing.set(id, controller);\n this.emit('accounts', this.accounts());\n try {\n const key = await login(\n id,\n () => this.#settings.signIn(id, BACKGROUND_CONTEXT) as Promise<SignIn | null>,\n {\n signal: controller.signal,\n }\n );\n controller.signal.throwIfAborted();\n return key;\n } finally {\n if (this.#signing.get(id) === controller) this.#signing.delete(id);\n this.emit('accounts', this.accounts());\n }\n }\n\n cancel(id: string): void {\n const controller = this.#signing.get(id);\n if (!controller) return;\n this.#signing.delete(id);\n controller.abort(new DOMException('The sign-in was cancelled.', 'AbortError'));\n this.emit('accounts', this.accounts());\n }\n\n disconnect(id: string): void {\n attempt(this.#settings.disconnect(id, BACKGROUND_CONTEXT), 'disconnect accounts', (text) =>\n this.#agent.notify(text)\n );\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type {\n Account,\n ModelOption,\n Settings,\n SettingsEvents,\n SettingsPort,\n} from '@ai-ecoverse/slicc-spectrum/ui';\nimport { BACKGROUND_CONTEXT } from '@earendil-works/chord/context';\nimport { preferredModel } from '../preferred.ts';\nimport type { AgentSettings, SettingsState, SignIn } from '../services.ts';\nimport type { AgentAdapter } from './agent-port.ts';\nimport { attempt } from './attempt.ts';\nimport { Emitter } from './emitter.ts';\nimport { mcpLogin } from './mcp-login.ts';\n\nexport const SETTINGS_KEY = 'slicc-agent.settings';\n\nconst defaults: Settings = {\n color: 'system',\n model: '',\n thinking: 'off',\n sendOnEnter: true,\n showThinking: false,\n diffStyle: 'unified',\n};\n\ntype Local = Omit<Settings, 'model' | 'thinking'>;\n\nexport type Sealed = { sealed: true };\n\nexport type Login = (\n providerId: string,\n signIn: () => Promise<SignIn | null>,\n options: { signal: AbortSignal }\n) => Promise<string | Sealed>;\n\nexport const SIGN_IN_NEEDS_VAULT = \"Adobe sign-in needs seven's vault: update seven\";\n\nexport class SettingsAdapter extends Emitter<SettingsEvents> implements SettingsPort {\n readonly #settings: AgentSettings;\n readonly #agent: AgentAdapter;\n readonly #storage: Pick<Storage, 'getItem' | 'setItem'> | null;\n readonly #login: Login | null;\n readonly #mcpLogin: Login;\n readonly #signing = new Map<string, AbortController>();\n #local: Local;\n\n constructor(\n settings: AgentSettings,\n agent: AgentAdapter,\n storage: Pick<Storage, 'getItem' | 'setItem'> | null,\n login: Login | null = null,\n mcp: Login = mcpLogin() as Login\n ) {\n super();\n this.#mcpLogin = mcp;\n this.#settings = settings;\n this.#agent = agent;\n this.#storage = storage;\n this.#login = login;\n const saved = JSON.parse(storage?.getItem(SETTINGS_KEY) ?? '{}') as Partial<Local>;\n const { model: _model, thinking: _thinking, ...local } = { ...defaults, ...saved };\n this.#local = local;\n settings.state.subscribe(() => {\n this.emit('accounts', this.accounts());\n this.emit('settings', this.get());\n });\n agent.on('agents', () => this.emit('settings', this.get()));\n }\n\n #state(): SettingsState {\n return this.#settings.state.value ?? { models: [], accounts: [] };\n }\n\n get(): Settings {\n const cone = this.#agent.list()[0];\n return { ...this.#local, model: cone?.model ?? '', thinking: cone?.thinking ?? 'off' };\n }\n\n update(patch: Partial<Settings>): void {\n const { model, thinking, ...local } = patch;\n const id = this.#agent.active();\n if (model !== undefined) this.#agent.setModel(id, model);\n if (thinking !== undefined) this.#agent.setThinking(id, thinking);\n this.#local = { ...this.#local, ...local };\n this.#storage?.setItem(SETTINGS_KEY, JSON.stringify(this.#local));\n this.emit('settings', this.get());\n }\n\n models(): readonly ModelOption[] {\n const choices = this.#state().models;\n const groups = Map.groupBy(choices, ({ id }) => id.slice(0, id.indexOf('/')));\n const chosen = new Set(\n [...groups].map(\n ([providerId, group]) =>\n preferredModel(\n providerId,\n group.map((choice) => ({ id: choice.id.slice(providerId.length + 1), choice }))\n )?.choice\n )\n );\n return choices.map((choice) => {\n const { id, label, provider, kind, reasoning, images } = choice;\n return {\n id,\n label,\n provider,\n kind,\n reasoning,\n ...(images === undefined ? {} : { images }),\n ...(chosen.has(choice) ? { default: true } : {}),\n };\n });\n }\n\n accounts(): readonly Account[] {\n return this.#state().accounts.map(({ needs, ...account }) => {\n const shown = this.#signing.has(account.id)\n ? { ...account, status: 'signing-in' as const }\n : account;\n return needs ? { ...shown, needs } : shown;\n });\n }\n\n async connect(id: string, secret?: string, fields?: Record<string, string>): Promise<void> {\n const account = this.#state().accounts.find((candidate) => candidate.id === id);\n const mcp = account?.group === 'mcp';\n const login = account?.auth !== 'oauth' ? null : mcp ? this.#mcpLogin : this.#login;\n const given = fields ? { ...fields } : null;\n if (secret !== undefined) {\n await this.#settings.connect(id, secret, given, BACKGROUND_CONTEXT);\n return;\n }\n if (account?.auth === 'oauth' && !login) throw new Error(`${id} needs a sign-in`);\n if (!login) {\n if (account?.store !== 'vault') throw new Error(`${id} needs an API key`);\n await this.#settings.connect(id, null, given, BACKGROUND_CONTEXT);\n return;\n }\n const answer = await this.#signIn(id, login);\n if (typeof answer === 'string') {\n if (!mcp) throw new Error(SIGN_IN_NEEDS_VAULT);\n await this.#settings.connect(id, answer, given, BACKGROUND_CONTEXT);\n return;\n }\n if (answer?.sealed !== true) throw new Error(`${id}'s sign-in didn't finish`);\n await this.#settings.connect(id, null, given, BACKGROUND_CONTEXT);\n }\n\n async #signIn(id: string, login: Login): Promise<string | Sealed> {\n const controller = new AbortController();\n this.#signing\n .get(id)\n ?.abort(new DOMException('A newer sign-in replaced this one.', 'AbortError'));\n this.#signing.set(id, controller);\n this.emit('accounts', this.accounts());\n try {\n const key = await login(\n id,\n () => this.#settings.signIn(id, BACKGROUND_CONTEXT) as Promise<SignIn | null>,\n {\n signal: controller.signal,\n }\n );\n controller.signal.throwIfAborted();\n return key;\n } finally {\n if (this.#signing.get(id) === controller) this.#signing.delete(id);\n this.emit('accounts', this.accounts());\n }\n }\n\n cancel(id: string): void {\n const controller = this.#signing.get(id);\n if (!controller) return;\n this.#signing.delete(id);\n controller.abort(new DOMException('The sign-in was cancelled.', 'AbortError'));\n this.emit('accounts', this.accounts());\n }\n\n disconnect(id: string): void {\n attempt(this.#settings.disconnect(id, BACKGROUND_CONTEXT), 'disconnect accounts', (text) =>\n this.#agent.notify(text)\n );\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -3,14 +3,21 @@ import { extractSettings } from "../memory/extract.js";
3
3
  import { redactSecrets } from "../memory/format.js";
4
4
  import { scrubText } from "../secrets/scrub.js";
5
5
  const TITLE_PROMPT = "Give this chat a title of three to six words that says what it is about, in the language the user writes in. Answer with the title only, without quotes.";
6
+ const LABEL_PROMPT = 'Label this group of tool runs in three to eight words, as an imperative that says what they do, such as "Check the failing build logs". Answer with the label only, without quotes.';
6
7
  const TRANSCRIPT_LENGTH = 4e3;
7
- function titlesOn(text) {
8
+ function settingOn(text, key) {
8
9
  try {
9
- return JSON.parse(text ?? "{}").titles !== false;
10
+ return JSON.parse(text ?? "{}")[key] !== false;
10
11
  } catch {
11
12
  return true;
12
13
  }
13
14
  }
15
+ function titlesOn(text) {
16
+ return settingOn(text, "titles");
17
+ }
18
+ function labelsOn(text) {
19
+ return settingOn(text, "toolLabels");
20
+ }
14
21
  function settingsText(env, home, dir = `${home}/.pi/agent`) {
15
22
  return async (context) => {
16
23
  const read = await env.readTextFile(`${dir}/settings.json`, context);
@@ -23,44 +30,57 @@ function textOf(entry) {
23
30
  function transcriptOf(entries) {
24
31
  return entries.filter((entry) => entry.kind === "pi.user" || entry.kind === "pi.assistant").map((entry) => `${entry.kind === "pi.user" ? "User" : "Agent"}: ${textOf(entry)}`).join("\n");
25
32
  }
26
- async function scrubTranscript(entries) {
27
- const raw = transcriptOf(entries);
33
+ async function scrubbed(raw) {
28
34
  const redacted = redactSecrets(raw).text;
29
- const scrubbed = await scrubText(redacted);
30
- return scrubbed.slice(0, TRANSCRIPT_LENGTH);
35
+ return (await scrubText(redacted)).slice(0, TRANSCRIPT_LENGTH);
31
36
  }
32
- function titler(models, settings) {
33
- return async (transcript, model, context) => {
37
+ function scrubTranscript(entries) {
38
+ return scrubbed(transcriptOf(entries));
39
+ }
40
+ function smallModel(models, settings, key, prompt, max) {
41
+ return async (transcript, model, context, admit = () => true) => {
34
42
  const text = await settings(context);
35
- if (!titlesOn(text)) return void 0;
43
+ if (!settingOn(text, key)) return void 0;
36
44
  const chosen = extractSettings(text).model;
37
45
  const [provider, ...rest] = model.split("/");
38
46
  const ref = chosen ?? (provider && rest.length ? { provider, modelId: rest.join("/") } : null);
39
47
  const found = ref ? models.getModel(ref.provider, ref.modelId) : void 0;
40
- if (!found || !transcript) return void 0;
48
+ if (!found || !transcript || !admit()) return void 0;
41
49
  const message = await models.completeSimple(
42
50
  found,
43
51
  {
44
52
  messages: [
45
53
  {
46
54
  role: "user",
47
- content: [{ type: "text", text: `${TITLE_PROMPT}
55
+ content: [{ type: "text", text: `${prompt}
48
56
 
49
57
  ${transcript}` }],
50
58
  timestamp: Date.now()
51
59
  }
52
60
  ]
53
61
  },
54
- { maxTokens: 40 }
62
+ { maxTokens: max }
55
63
  );
56
64
  if (message.stopReason === "error") return void 0;
57
- return message.content.map((part) => part.type === "text" ? part.text : "").join("").trim();
65
+ const said = message.content.map((part) => part.type === "text" ? part.text : "").join("").trim();
66
+ return { text: said, answer: message };
58
67
  };
59
68
  }
69
+ function titler(models, settings) {
70
+ const ask = smallModel(models, settings, "titles", TITLE_PROMPT, 40);
71
+ return async (transcript, model, context) => (await ask(transcript, model, context))?.text;
72
+ }
73
+ function labeler(models, settings) {
74
+ return smallModel(models, settings, "toolLabels", LABEL_PROMPT, 40);
75
+ }
60
76
  export {
77
+ LABEL_PROMPT,
61
78
  TITLE_PROMPT,
62
79
  TRANSCRIPT_LENGTH,
80
+ labeler,
81
+ labelsOn,
63
82
  scrubTranscript,
83
+ scrubbed,
64
84
  settingsText,
65
85
  titler,
66
86
  titlesOn,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/titles/generate.ts"],
4
- "sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport type { Message } from '@earendil-works/pi-ai';\nimport type { Models } from '@earendil-works/pi-ai/models';\nimport type { EntryRecord } from '@earendil-works/pi-durable';\nimport type { ExecutionEnv } from '@earendil-works/pi-durable/env';\nimport { answerText } from '../agent.ts';\nimport { extractSettings } from '../memory/extract.ts';\nimport { redactSecrets } from '../memory/format.ts';\nimport { scrubText } from '../secrets/scrub.ts';\n\nexport const TITLE_PROMPT =\n 'Give this chat a title of three to six words that says what it is about, in the language the user writes in. Answer with the title only, without quotes.';\n\nexport const TRANSCRIPT_LENGTH = 4000;\n\nexport function titlesOn(text: string | undefined): boolean {\n try {\n return (JSON.parse(text ?? '{}') as { titles?: unknown }).titles !== false;\n } catch {\n return true;\n }\n}\n\nexport function settingsText(\n env: Pick<ExecutionEnv, 'readTextFile'>,\n home: string,\n dir = `${home}/.pi/agent`\n): (context: Context) => Promise<string | undefined> {\n return async (context) => {\n const read = await env.readTextFile(`${dir}/settings.json`, context);\n return read.ok ? read.value : undefined;\n };\n}\n\nfunction textOf(entry: EntryRecord): string {\n return answerText(entry.model?.[0]?.content).trim();\n}\n\nexport function transcriptOf(entries: readonly EntryRecord[]): string {\n return entries\n .filter((entry) => entry.kind === 'pi.user' || entry.kind === 'pi.assistant')\n .map((entry) => `${entry.kind === 'pi.user' ? 'User' : 'Agent'}: ${textOf(entry)}`)\n .join('\\n');\n}\n\nexport async function scrubTranscript(entries: readonly EntryRecord[]): Promise<string> {\n const raw = transcriptOf(entries);\n const redacted = redactSecrets(raw).text;\n const scrubbed = await scrubText(redacted);\n return scrubbed.slice(0, TRANSCRIPT_LENGTH);\n}\n\nexport function titler(\n models: Models,\n settings: (context: Context) => Promise<string | undefined>\n) {\n return async (\n transcript: string,\n model: string,\n context: Context\n ): Promise<string | undefined> => {\n const text = await settings(context);\n if (!titlesOn(text)) return undefined;\n const chosen = extractSettings(text).model;\n const [provider, ...rest] = model.split('/');\n const ref = chosen ?? (provider && rest.length ? { provider, modelId: rest.join('/') } : null);\n const found = ref ? models.getModel(ref.provider, ref.modelId) : undefined;\n if (!found || !transcript) return undefined;\n const message = await models.completeSimple(\n found,\n {\n messages: [\n {\n role: 'user',\n content: [{ type: 'text', text: `${TITLE_PROMPT}\\n\\n${transcript}` }],\n timestamp: Date.now(),\n } as Message,\n ],\n },\n { maxTokens: 40 }\n );\n if (message.stopReason === 'error') return undefined;\n return message.content\n .map((part) => (part.type === 'text' ? part.text : ''))\n .join('')\n .trim();\n };\n}\n"],
5
- "mappings": "AAKA,SAAS,kBAAkB;AAC3B,SAAS,uBAAuB;AAChC,SAAS,qBAAqB;AAC9B,SAAS,iBAAiB;AAEnB,MAAM,eACX;AAEK,MAAM,oBAAoB;AAE1B,SAAS,SAAS,MAAmC;AAC1D,MAAI;AACF,WAAQ,KAAK,MAAM,QAAQ,IAAI,EAA2B,WAAW;AAAA,EACvE,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,aACd,KACA,MACA,MAAM,GAAG,IAAI,cACsC;AACnD,SAAO,OAAO,YAAY;AACxB,UAAM,OAAO,MAAM,IAAI,aAAa,GAAG,GAAG,kBAAkB,OAAO;AACnE,WAAO,KAAK,KAAK,KAAK,QAAQ;AAAA,EAChC;AACF;AAEA,SAAS,OAAO,OAA4B;AAC1C,SAAO,WAAW,MAAM,QAAQ,CAAC,GAAG,OAAO,EAAE,KAAK;AACpD;AAEO,SAAS,aAAa,SAAyC;AACpE,SAAO,QACJ,OAAO,CAAC,UAAU,MAAM,SAAS,aAAa,MAAM,SAAS,cAAc,EAC3E,IAAI,CAAC,UAAU,GAAG,MAAM,SAAS,YAAY,SAAS,OAAO,KAAK,OAAO,KAAK,CAAC,EAAE,EACjF,KAAK,IAAI;AACd;AAEA,eAAsB,gBAAgB,SAAkD;AACtF,QAAM,MAAM,aAAa,OAAO;AAChC,QAAM,WAAW,cAAc,GAAG,EAAE;AACpC,QAAM,WAAW,MAAM,UAAU,QAAQ;AACzC,SAAO,SAAS,MAAM,GAAG,iBAAiB;AAC5C;AAEO,SAAS,OACd,QACA,UACA;AACA,SAAO,OACL,YACA,OACA,YACgC;AAChC,UAAM,OAAO,MAAM,SAAS,OAAO;AACnC,QAAI,CAAC,SAAS,IAAI,EAAG,QAAO;AAC5B,UAAM,SAAS,gBAAgB,IAAI,EAAE;AACrC,UAAM,CAAC,UAAU,GAAG,IAAI,IAAI,MAAM,MAAM,GAAG;AAC3C,UAAM,MAAM,WAAW,YAAY,KAAK,SAAS,EAAE,UAAU,SAAS,KAAK,KAAK,GAAG,EAAE,IAAI;AACzF,UAAM,QAAQ,MAAM,OAAO,SAAS,IAAI,UAAU,IAAI,OAAO,IAAI;AACjE,QAAI,CAAC,SAAS,CAAC,WAAY,QAAO;AAClC,UAAM,UAAU,MAAM,OAAO;AAAA,MAC3B;AAAA,MACA;AAAA,QACE,UAAU;AAAA,UACR;AAAA,YACE,MAAM;AAAA,YACN,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,GAAG,YAAY;AAAA;AAAA,EAAO,UAAU,GAAG,CAAC;AAAA,YACpE,WAAW,KAAK,IAAI;AAAA,UACtB;AAAA,QACF;AAAA,MACF;AAAA,MACA,EAAE,WAAW,GAAG;AAAA,IAClB;AACA,QAAI,QAAQ,eAAe,QAAS,QAAO;AAC3C,WAAO,QAAQ,QACZ,IAAI,CAAC,SAAU,KAAK,SAAS,SAAS,KAAK,OAAO,EAAG,EACrD,KAAK,EAAE,EACP,KAAK;AAAA,EACV;AACF;",
4
+ "sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport type { AssistantMessage, Message } from '@earendil-works/pi-ai';\nimport type { Models } from '@earendil-works/pi-ai/models';\nimport type { EntryRecord } from '@earendil-works/pi-durable';\nimport type { ExecutionEnv } from '@earendil-works/pi-durable/env';\nimport { answerText } from '../agent.ts';\nimport { extractSettings } from '../memory/extract.ts';\nimport { redactSecrets } from '../memory/format.ts';\nimport { scrubText } from '../secrets/scrub.ts';\n\nexport const TITLE_PROMPT =\n 'Give this chat a title of three to six words that says what it is about, in the language the user writes in. Answer with the title only, without quotes.';\n\nexport const LABEL_PROMPT =\n 'Label this group of tool runs in three to eight words, as an imperative that says what they do, such as \"Check the failing build logs\". Answer with the label only, without quotes.';\n\nexport const TRANSCRIPT_LENGTH = 4000;\n\ntype Switch = 'titles' | 'toolLabels';\n\nfunction settingOn(text: string | undefined, key: Switch): boolean {\n try {\n return (JSON.parse(text ?? '{}') as Record<string, unknown>)[key] !== false;\n } catch {\n return true;\n }\n}\n\nexport function titlesOn(text: string | undefined): boolean {\n return settingOn(text, 'titles');\n}\n\nexport function labelsOn(text: string | undefined): boolean {\n return settingOn(text, 'toolLabels');\n}\n\nexport function settingsText(\n env: Pick<ExecutionEnv, 'readTextFile'>,\n home: string,\n dir = `${home}/.pi/agent`\n): (context: Context) => Promise<string | undefined> {\n return async (context) => {\n const read = await env.readTextFile(`${dir}/settings.json`, context);\n return read.ok ? read.value : undefined;\n };\n}\n\nfunction textOf(entry: EntryRecord): string {\n return answerText(entry.model?.[0]?.content).trim();\n}\n\nexport function transcriptOf(entries: readonly EntryRecord[]): string {\n return entries\n .filter((entry) => entry.kind === 'pi.user' || entry.kind === 'pi.assistant')\n .map((entry) => `${entry.kind === 'pi.user' ? 'User' : 'Agent'}: ${textOf(entry)}`)\n .join('\\n');\n}\n\nexport async function scrubbed(raw: string): Promise<string> {\n const redacted = redactSecrets(raw).text;\n return (await scrubText(redacted)).slice(0, TRANSCRIPT_LENGTH);\n}\n\nexport function scrubTranscript(entries: readonly EntryRecord[]): Promise<string> {\n return scrubbed(transcriptOf(entries));\n}\n\ntype Settings = (context: Context) => Promise<string | undefined>;\n\nexport type Asked = { text: string; answer: AssistantMessage };\n\nfunction smallModel(models: Models, settings: Settings, key: Switch, prompt: string, max: number) {\n return async (\n transcript: string,\n model: string,\n context: Context,\n admit: () => boolean = () => true\n ): Promise<Asked | undefined> => {\n const text = await settings(context);\n if (!settingOn(text, key)) return undefined;\n const chosen = extractSettings(text).model;\n const [provider, ...rest] = model.split('/');\n const ref = chosen ?? (provider && rest.length ? { provider, modelId: rest.join('/') } : null);\n const found = ref ? models.getModel(ref.provider, ref.modelId) : undefined;\n if (!found || !transcript || !admit()) return undefined;\n const message = await models.completeSimple(\n found,\n {\n messages: [\n {\n role: 'user',\n content: [{ type: 'text', text: `${prompt}\\n\\n${transcript}` }],\n timestamp: Date.now(),\n } as Message,\n ],\n },\n { maxTokens: max }\n );\n if (message.stopReason === 'error') return undefined;\n const said = message.content\n .map((part) => (part.type === 'text' ? part.text : ''))\n .join('')\n .trim();\n return { text: said, answer: message };\n };\n}\n\nexport function titler(models: Models, settings: Settings) {\n const ask = smallModel(models, settings, 'titles', TITLE_PROMPT, 40);\n return async (transcript: string, model: string, context: Context) =>\n (await ask(transcript, model, context))?.text;\n}\n\nexport function labeler(models: Models, settings: Settings) {\n return smallModel(models, settings, 'toolLabels', LABEL_PROMPT, 40);\n}\n\nexport type Labeler = ReturnType<typeof labeler>;\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,207 @@
1
+ import { replicatedState } from "@earendil-works/chord";
2
+ import { BACKGROUND_CONTEXT } from "@earendil-works/chord/context";
3
+ import {
4
+ defineDoc,
5
+ UsageDoc
6
+ } from "@earendil-works/pi-durable";
7
+ import { answerText } from "../agent.js";
8
+ import { vaultHold } from "../secrets/view.js";
9
+ import { scrubbed } from "./generate.js";
10
+ import { labelKey, shownLabel } from "./shown.js";
11
+ const LabelsDoc = defineDoc({
12
+ kind: "slicc.labels",
13
+ version: 1,
14
+ scope: "session",
15
+ initial: () => ({ labels: {} })
16
+ });
17
+ const LABELS_KEPT = 500;
18
+ const LABELS_PER_MINUTE = 6;
19
+ const MINUTE = 6e4;
20
+ const LABEL_WORDS = 8;
21
+ const LABEL_LENGTH = 80;
22
+ const OUTPUT_LENGTH = 600;
23
+ const LABEL_WAIT_MS = 3e4;
24
+ function callIdsOf(views) {
25
+ const ids = /* @__PURE__ */ new Set();
26
+ for (const view of views)
27
+ for (const entry of view.entries) {
28
+ const content = entry.model?.[0]?.content;
29
+ if (entry.kind === "pi.assistant" && Array.isArray(content)) {
30
+ for (const block of content)
31
+ if (block.type === "toolCall" && block.id) ids.add(block.id);
32
+ }
33
+ }
34
+ return ids;
35
+ }
36
+ function visibleLabels(labels, sources, views) {
37
+ const pick = () => {
38
+ const ids = callIdsOf(views());
39
+ return Object.fromEntries(
40
+ Object.entries(labels.value).filter(
41
+ ([key]) => key.split(" ").every((id) => ids.has(id))
42
+ )
43
+ );
44
+ };
45
+ const state = replicatedState(pick());
46
+ let queued = false;
47
+ const update = () => {
48
+ if (queued) return;
49
+ queued = true;
50
+ queueMicrotask(() => {
51
+ queued = false;
52
+ const next = pick();
53
+ const before = state.value;
54
+ const keys = Object.keys(next);
55
+ if (keys.length !== Object.keys(before).length || keys.some((key) => before[key] !== next[key]))
56
+ state.replace(BACKGROUND_CONTEXT, next);
57
+ });
58
+ };
59
+ const offs = [labels, ...sources].map((source) => source.subscribe(update));
60
+ return {
61
+ state,
62
+ dispose() {
63
+ for (const off of offs) off();
64
+ }
65
+ };
66
+ }
67
+ function watchLabel(labels, waitMs = LABEL_WAIT_MS) {
68
+ return (_agentId, callIds) => {
69
+ const found = shownLabel(labels.value, callIds);
70
+ if (found !== void 0 || !callIds.length) return Promise.resolve(found ?? null);
71
+ return new Promise((resolve) => {
72
+ const done = (label) => {
73
+ clearTimeout(timer);
74
+ off();
75
+ resolve(label);
76
+ };
77
+ const timer = setTimeout(() => done(null), waitMs);
78
+ const off = labels.subscribe(() => {
79
+ const label = shownLabel(labels.value, callIds);
80
+ if (label !== void 0) done(label);
81
+ });
82
+ });
83
+ };
84
+ }
85
+ function cleanLabel(text) {
86
+ return text.replace(/\s+/g, " ").trim().replace(/^["'“”]+|["'“”]+$/g, "").replace(/\.+$/, "").trim().split(" ").slice(0, LABEL_WORDS).join(" ").slice(0, LABEL_LENGTH);
87
+ }
88
+ function clusterOf(entries, callIds) {
89
+ const calls = /* @__PURE__ */ new Map();
90
+ const results = /* @__PURE__ */ new Map();
91
+ for (const entry of entries) {
92
+ const said = entry.model?.[0];
93
+ if (entry.kind === "pi.assistant" && Array.isArray(said?.content)) {
94
+ for (const block of said.content)
95
+ if (block.type === "toolCall" && block.id)
96
+ calls.set(block.id, {
97
+ name: block.name ?? "tool",
98
+ input: JSON.stringify(block.arguments ?? {})
99
+ });
100
+ }
101
+ if (entry.kind === "pi.tool-result" && said?.toolCallId)
102
+ results.set(said.toolCallId, {
103
+ output: answerText(said.content),
104
+ at: said.timestamp ?? 0
105
+ });
106
+ }
107
+ const runs = [];
108
+ for (const id of callIds) {
109
+ const call = calls.get(id);
110
+ const result = results.get(id);
111
+ if (!call || !result) return void 0;
112
+ runs.push({ ...call, ...result });
113
+ }
114
+ return runs;
115
+ }
116
+ function describe(runs) {
117
+ return runs.map((run) => `${run.name}: ${run.input}
118
+ => ${run.output.slice(0, OUTPUT_LENGTH)}`).join("\n\n");
119
+ }
120
+ async function spend(tx, conversation, answer) {
121
+ const models = (await tx.doc(UsageDoc, conversation)).models;
122
+ const key = `${answer.provider}/${answer.model}`;
123
+ const usage = answer.usage;
124
+ const total = Object.hasOwn(models, key) ? models[key] : void 0;
125
+ if (!total) {
126
+ models[key] = JSON.parse(JSON.stringify(usage));
127
+ return;
128
+ }
129
+ for (const field of ["input", "output", "cacheRead", "cacheWrite", "totalTokens"])
130
+ total[field] += usage[field];
131
+ for (const field of ["input", "output", "cacheRead", "cacheWrite", "total"])
132
+ total.cost[field] += usage.cost[field];
133
+ }
134
+ function modelName(model) {
135
+ return model ? `${model.provider}/${model.modelId}` : "";
136
+ }
137
+ async function attachLabels(options, context = BACKGROUND_CONTEXT) {
138
+ const { harness, agents } = options;
139
+ const now = options.now ?? Date.now;
140
+ const started = now();
141
+ const cap = options.perMinute ?? LABELS_PER_MINUTE;
142
+ const loaded = await harness.commit(
143
+ async (tx) => ({ ...(await tx.doc(LabelsDoc)).labels }),
144
+ context
145
+ );
146
+ const labels = replicatedState(loaded);
147
+ const flying = /* @__PURE__ */ new Map();
148
+ const spent = /* @__PURE__ */ new Map();
149
+ const admit = (agentId) => () => {
150
+ const at = now();
151
+ const recent = (spent.get(agentId) ?? []).filter((time) => at - time < MINUTE);
152
+ spent.set(agentId, recent);
153
+ if (recent.length >= cap) return false;
154
+ recent.push(at);
155
+ return true;
156
+ };
157
+ const keep = (key, label, conversation, answer) => harness.commit(async (tx) => {
158
+ const doc = await tx.doc(LabelsDoc);
159
+ doc.labels[key] = label;
160
+ const keys = Object.keys(doc.labels);
161
+ for (const old of keys.slice(0, Math.max(0, keys.length - LABELS_KEPT)))
162
+ delete doc.labels[old];
163
+ await spend(tx, conversation, answer);
164
+ return { ...doc.labels };
165
+ }, BACKGROUND_CONTEXT);
166
+ const make = async (agentId, callIds, key) => {
167
+ const conversation = await agents.conversation(agentId, BACKGROUND_CONTEXT);
168
+ if (!conversation) return null;
169
+ const runs = clusterOf((await conversation.context(BACKGROUND_CONTEXT)).entries, callIds);
170
+ if (!runs || Math.max(...runs.map((run) => run.at)) < started) return null;
171
+ if (vaultHold(options.vault?.value)) return null;
172
+ const text = await scrubbed(describe(runs));
173
+ const model = modelName((await conversation.agent(BACKGROUND_CONTEXT)).model);
174
+ const asked = await options.label(text, model, BACKGROUND_CONTEXT, admit(agentId));
175
+ if (!asked) return null;
176
+ const label = cleanLabel(asked.text);
177
+ labels.replace(BACKGROUND_CONTEXT, await keep(key, label, conversation.id, asked.answer));
178
+ return label || null;
179
+ };
180
+ return {
181
+ labels,
182
+ label(agentId, callIds, _context) {
183
+ if (!callIds.length) return Promise.resolve(null);
184
+ const key = labelKey(callIds);
185
+ const found = shownLabel(labels.value, callIds);
186
+ if (found !== void 0) return Promise.resolve(found);
187
+ const known = flying.get(key);
188
+ if (known !== void 0) return known;
189
+ const made = make(agentId, callIds, key).catch(() => null).finally(() => flying.delete(key));
190
+ flying.set(key, made);
191
+ return made;
192
+ }
193
+ };
194
+ }
195
+ export {
196
+ LABELS_KEPT,
197
+ LABELS_PER_MINUTE,
198
+ LABEL_WAIT_MS,
199
+ LabelsDoc,
200
+ attachLabels,
201
+ cleanLabel,
202
+ clusterOf,
203
+ describe,
204
+ visibleLabels,
205
+ watchLabel
206
+ };
207
+ //# sourceMappingURL=labels.js.map