@parlor/chat 0.3.0 → 0.3.2

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.
@@ -0,0 +1,56 @@
1
+ /** How long a pushed turn waits for its computer to confirm it before it goes back to the queue. */
2
+ export const PICKUP_MS = 5_000;
3
+ export function turnPush(deps) {
4
+ const unconfirmed = new Map();
5
+ const push = async (owner, hostId) => {
6
+ // The newest stream: during a handover the older one belongs to a draining service.
7
+ const connection = deps.relay.computer(owner, hostId).at(-1);
8
+ const principal = connection?.principal;
9
+ if (!connection || principal?.kind !== "host")
10
+ return;
11
+ const host = {
12
+ scope: connection.scope,
13
+ userId: principal.userId,
14
+ host: principal,
15
+ };
16
+ const turn = await deps.session.claim(host, deps.db());
17
+ if (!turn)
18
+ return;
19
+ const frame = { type: "claimed", turn };
20
+ try {
21
+ connection.send(JSON.stringify(frame));
22
+ }
23
+ catch {
24
+ await deps.session.release(host, turn.id, turn.turnId);
25
+ return;
26
+ }
27
+ const timer = setTimeout(() => {
28
+ unconfirmed.delete(turn.turnId);
29
+ deps.session.release(host, turn.id, turn.turnId).catch((error) => {
30
+ console.warn("[parlor] chat: returning an unconfirmed turn failed:", error);
31
+ });
32
+ }, deps.pickupMs ?? PICKUP_MS);
33
+ timer.unref?.();
34
+ unconfirmed.set(turn.turnId, timer);
35
+ };
36
+ return {
37
+ offer(owner, hostId) {
38
+ push(owner, hostId).catch((error) => {
39
+ console.warn("[parlor] chat: pushing a turn failed; its computer claims it:", error);
40
+ });
41
+ },
42
+ confirm(turnId) {
43
+ const timer = unconfirmed.get(turnId);
44
+ if (!timer)
45
+ return;
46
+ clearTimeout(timer);
47
+ unconfirmed.delete(turnId);
48
+ },
49
+ close() {
50
+ for (const timer of unconfirmed.values())
51
+ clearTimeout(timer);
52
+ unconfirmed.clear();
53
+ },
54
+ };
55
+ }
56
+ //# sourceMappingURL=push.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"push.js","sourceRoot":"","sources":["../../src/server/push.ts"],"names":[],"mappings":"AASA,oGAAoG;AACpG,MAAM,CAAC,MAAM,SAAS,GAAG,KAAK,CAAC;AAe/B,MAAM,UAAU,QAAQ,CAAC,IAKxB;IACC,MAAM,WAAW,GAAG,IAAI,GAAG,EAAyC,CAAC;IAErE,MAAM,IAAI,GAAG,KAAK,EAAE,KAAgB,EAAE,MAAc,EAAE,EAAE;QACtD,oFAAoF;QACpF,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QAC7D,MAAM,SAAS,GAAG,UAAU,EAAE,SAAS,CAAC;QACxC,IAAI,CAAC,UAAU,IAAI,SAAS,EAAE,IAAI,KAAK,MAAM;YAAE,OAAO;QACtD,MAAM,IAAI,GAAa;YACrB,KAAK,EAAE,UAAU,CAAC,KAAK;YACvB,MAAM,EAAE,SAAS,CAAC,MAAM;YACxB,IAAI,EAAE,SAAS;SAChB,CAAC;QACF,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;QACvD,IAAI,CAAC,IAAI;YAAE,OAAO;QAClB,MAAM,KAAK,GAAe,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;QACpD,IAAI,CAAC;YACH,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC;QACzC,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;YACvD,OAAO;QACT,CAAC;QACD,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;YAC5B,WAAW,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YAChC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;gBACxE,OAAO,CAAC,IAAI,CAAC,sDAAsD,EAAE,KAAK,CAAC,CAAC;YAC9E,CAAC,CAAC,CAAC;QACL,CAAC,EAAE,IAAI,CAAC,QAAQ,IAAI,SAAS,CAAC,CAAC;QAC9B,KAAgC,CAAC,KAAK,EAAE,EAAE,CAAC;QAC5C,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IACtC,CAAC,CAAC;IAEF,OAAO;QACL,KAAK,CAAC,KAAK,EAAE,MAAM;YACjB,IAAI,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;gBAC3C,OAAO,CAAC,IAAI,CACV,+DAA+D,EAC/D,KAAK,CACN,CAAC;YACJ,CAAC,CAAC,CAAC;QACL,CAAC;QACD,OAAO,CAAC,MAAM;YACZ,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YACtC,IAAI,CAAC,KAAK;gBAAE,OAAO;YACnB,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,WAAW,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAC7B,CAAC;QACD,KAAK;YACH,KAAK,MAAM,KAAK,IAAI,WAAW,CAAC,MAAM,EAAE;gBAAE,YAAY,CAAC,KAAK,CAAC,CAAC;YAC9D,WAAW,CAAC,KAAK,EAAE,CAAC;QACtB,CAAC;KACF,CAAC;AACJ,CAAC","sourcesContent":["import type { HostId, TurnId } from \"@parlor/core\";\n\nimport type { ChatOwner } from \"../model.js\";\nimport type { PushedTurn } from \"../protocol.js\";\nimport type { HostSession } from \"./actions.js\";\nimport type { LiveRelay } from \"./live.js\";\nimport type { ChatHost } from \"./party.js\";\nimport type { ChatDb } from \"./store.js\";\n\n/** How long a pushed turn waits for its computer to confirm it before it goes back to the queue. */\nexport const PICKUP_MS = 5_000;\n\n/**\n * Hands a turn to its computer the moment it is queued, saving the computer the round trip\n * of being woken and claiming it. Best effort: a turn it cannot push, or that the computer\n * never confirms, stays with or goes back to `hostClaim`.\n */\nexport interface TurnPush {\n /** A turn was just queued for `hostId`; claims it and pushes it if the computer is listening. */\n offer(owner: ChatOwner, hostId: HostId): void;\n /** The computer called about `turnId`, so it has the turn. */\n confirm(turnId: TurnId): void;\n close(): void;\n}\n\nexport function turnPush(deps: {\n readonly relay: LiveRelay;\n readonly session: HostSession;\n readonly db: () => ChatDb;\n readonly pickupMs?: number | undefined;\n}): TurnPush {\n const unconfirmed = new Map<TurnId, ReturnType<typeof setTimeout>>();\n\n const push = async (owner: ChatOwner, hostId: HostId) => {\n // The newest stream: during a handover the older one belongs to a draining service.\n const connection = deps.relay.computer(owner, hostId).at(-1);\n const principal = connection?.principal;\n if (!connection || principal?.kind !== \"host\") return;\n const host: ChatHost = {\n scope: connection.scope,\n userId: principal.userId,\n host: principal,\n };\n const turn = await deps.session.claim(host, deps.db());\n if (!turn) return;\n const frame: PushedTurn = { type: \"claimed\", turn };\n try {\n connection.send(JSON.stringify(frame));\n } catch {\n await deps.session.release(host, turn.id, turn.turnId);\n return;\n }\n const timer = setTimeout(() => {\n unconfirmed.delete(turn.turnId);\n deps.session.release(host, turn.id, turn.turnId).catch((error: unknown) => {\n console.warn(\"[parlor] chat: returning an unconfirmed turn failed:\", error);\n });\n }, deps.pickupMs ?? PICKUP_MS);\n (timer as { unref?: () => void }).unref?.();\n unconfirmed.set(turn.turnId, timer);\n };\n\n return {\n offer(owner, hostId) {\n push(owner, hostId).catch((error: unknown) => {\n console.warn(\n \"[parlor] chat: pushing a turn failed; its computer claims it:\",\n error,\n );\n });\n },\n confirm(turnId) {\n const timer = unconfirmed.get(turnId);\n if (!timer) return;\n clearTimeout(timer);\n unconfirmed.delete(turnId);\n },\n close() {\n for (const timer of unconfirmed.values()) clearTimeout(timer);\n unconfirmed.clear();\n },\n };\n}\n"]}
@@ -104,6 +104,8 @@ export interface ChatStore {
104
104
  readonly devices?: readonly string[] | undefined;
105
105
  /** Left as it was when the host does not report it. */
106
106
  readonly presenceDue?: Readonly<Record<string, number>> | undefined;
107
+ /** When the host looked for its agents; left as it was when this report is not that. */
108
+ readonly agentsCheckedAt?: number | undefined;
107
109
  readonly online: boolean;
108
110
  readonly ttlMs: number;
109
111
  }): Promise<{
@@ -1 +1 @@
1
- {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/server/store.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/server/store.ts"],"names":[],"mappings":"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"}
@@ -459,18 +459,20 @@ export const sqlChatStore = {
459
459
  const ttlMs = report.online ? report.ttlMs : 0;
460
460
  const [row] = await query(tx, sql `insert into parlor_chat_hosts
461
461
  (id, scope, owner_id, grant_id, protocol, name, platform, agents, devices,
462
- presence_due, online_until, announced_online)
462
+ presence_due, agents_checked_at, online_until, announced_online)
463
463
  values (${host.hostId}, ${owner.scope}, ${owner.userId}, ${host.grantId},
464
464
  ${report.protocol}, ${report.name}, ${report.platform},
465
465
  ${JSON.stringify(report.agents)}::jsonb,
466
466
  ${report.devices ? JSON.stringify(report.devices) : null}::jsonb,
467
467
  ${report.presenceDue ? JSON.stringify(report.presenceDue) : null}::jsonb,
468
+ ${report.agentsCheckedAt === undefined ? null : new Date(report.agentsCheckedAt).toISOString()}::timestamptz,
468
469
  ${leaseSql(ttlMs)}, ${report.online})
469
470
  on conflict (id) do update set
470
471
  grant_id = excluded.grant_id, protocol = excluded.protocol, name = excluded.name,
471
472
  platform = excluded.platform, agents = excluded.agents,
472
473
  devices = coalesce(excluded.devices, parlor_chat_hosts.devices),
473
474
  presence_due = coalesce(excluded.presence_due, parlor_chat_hosts.presence_due),
475
+ agents_checked_at = coalesce(excluded.agents_checked_at, parlor_chat_hosts.agents_checked_at),
474
476
  online_until = excluded.online_until,
475
477
  announced_online = excluded.announced_online
476
478
  where parlor_chat_hosts.owner_id = excluded.owner_id
@@ -491,7 +493,7 @@ export const sqlChatStore = {
491
493
  },
492
494
  async listHosts(db, owner) {
493
495
  return query(db, sql `select id, grant_id, protocol, name, platform, agents, devices, presence_due,
494
- online_until > now() as online
496
+ ${ms("agents_checked_at")} as agents_checked_at, online_until > now() as online
495
497
  from parlor_chat_hosts
496
498
  where owner_id = ${owner.userId} and scope = ${owner.scope}
497
499
  order by name, id`, z
@@ -504,12 +506,14 @@ export const sqlChatStore = {
504
506
  agents: z.array(agentInfoSchema),
505
507
  devices: z.array(z.string()).nullable(),
506
508
  presence_due: z.record(z.string(), z.number()).nullable(),
509
+ agents_checked_at: count.nullable(),
507
510
  online: z.boolean(),
508
511
  })
509
- .transform(({ grant_id, devices, presence_due, ...host }) => ({
512
+ .transform(({ grant_id, devices, presence_due, agents_checked_at, ...host }) => ({
510
513
  ...host,
511
514
  ...(devices ? { devices } : {}),
512
515
  ...(presence_due ? { presenceDue: presence_due } : {}),
516
+ ...(agents_checked_at === null ? {} : { agentsCheckedAt: agents_checked_at }),
513
517
  grantId: grant_id,
514
518
  })));
515
519
  },
@@ -1 +1 @@
1
- {"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/server/store.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from \"node:crypto\";\n\nimport {\n ActionError,\n asId,\n type DeviceId,\n type GrantId,\n type HostId,\n type HostPrincipal,\n type ItemId,\n type ThreadId,\n type TurnId,\n type UserId,\n} from \"@parlor/core\";\nimport { type SQL, sql } from \"drizzle-orm\";\nimport type { PgDatabase, PgQueryResultHKT } from \"drizzle-orm/pg-core\";\nimport { z } from \"zod\";\n\nimport { idSchema } from \"../id-schemas.js\";\nimport { chatItemBodies, type ChatItemBody, type ChatItemKind } from \"../items.js\";\nimport type { HostItemDraft, LoggedItem, UserItemKind } from \"../items.js\";\nimport type {\n AgentInfo,\n AgentRef,\n ChatOwner,\n ClaimedTurn,\n HostSummary,\n ThreadPage,\n ThreadSummary,\n TurnOutcome,\n TurnSignals,\n TurnStatus,\n} from \"../model.js\";\nimport {\n agentInfoSchema,\n itemRow,\n threadRow,\n toLoggedItem,\n toThreadSummary,\n TURN_FAILURES,\n turnStateSchema,\n} from \"./rows.js\";\n\nexport type ChatDb = PgDatabase<PgQueryResultHKT>;\n\nexport type UserDraft = {\n [K in UserItemKind]: {\n readonly id: LoggedItem<K>[\"id\"];\n readonly kind: K;\n readonly body: LoggedItem<K>[\"body\"];\n };\n}[UserItemKind];\n\nexport interface NewTurn {\n readonly turnId: TurnId;\n readonly agent: AgentRef;\n readonly clientId: string;\n readonly deviceId: DeviceId | undefined;\n /** The most turns a thread may have waiting; checked under the thread's lock. */\n readonly maxQueued: number;\n}\n\nexport interface TurnHolder {\n readonly owner: ChatOwner;\n readonly threadId: ThreadId;\n readonly hostId: HostId;\n}\n\n/** A turn's holder with whether it runs now: running, and its lease not yet lapsed. */\nexport interface StreamTurn extends TurnHolder {\n readonly live: boolean;\n}\n\n/** What one reconciliation changed in a scope, for the sync events it must emit. */\nexport interface OlderItems {\n readonly items: readonly LoggedItem[];\n readonly turns: readonly TurnStatus[];\n readonly hasMore: boolean;\n}\n\nexport interface ThreadCursor {\n readonly updatedAt: number;\n readonly id: ThreadId;\n}\n\nexport interface OlderThreads {\n readonly threads: readonly ThreadSummary[];\n readonly hasMore: boolean;\n}\n\nexport interface Lapsed extends ChatOwner {\n readonly threads: readonly ThreadId[];\n readonly hosts: readonly HostId[];\n}\n\n/**\n * The thread log in SQL. Every function filters by the owner's scope and user in the same\n * statement, so another user's thread id reads as not found. Appends lock the thread row\n * to assign gap-free `seq` and run in the action's transaction so the thread's sync event\n * commits with them.\n */\nexport interface ChatStore {\n listThreads(db: ChatDb, owner: ChatOwner): Promise<ThreadSummary[]>;\n readPage(\n db: ChatDb,\n owner: ChatOwner,\n threadId: ThreadId | undefined,\n limit: number,\n ): Promise<Omit<ThreadPage, \"open\">>;\n /** Items before `beforeSeq`, oldest first, with their turns; `hasMore` while older ones remain. */\n readItemsBefore?(\n db: ChatDb,\n owner: ChatOwner,\n threadId: ThreadId,\n beforeSeq: number,\n limit: number,\n ): Promise<OlderItems>;\n /** Threads updated before `before`, newest first, after the ones `listThreads` returned. */\n listThreadsBefore?(\n db: ChatDb,\n owner: ChatOwner,\n before: ThreadCursor,\n limit: number,\n ): Promise<OlderThreads>;\n readItemsAfter(\n db: ChatDb,\n owner: ChatOwner,\n threadId: ThreadId,\n afterSeq: number,\n ): Promise<LoggedItem[]>;\n /** With `turn`, also queues it; the thread is created on its first prompt. */\n appendUserItems(\n tx: ChatDb,\n owner: ChatOwner,\n input: {\n readonly threadId: ThreadId;\n readonly turnId: TurnId;\n readonly items: readonly UserDraft[];\n readonly turn?: NewTurn | undefined;\n },\n ): Promise<LoggedItem[]>;\n claimTurn(\n tx: ChatDb,\n owner: ChatOwner,\n host: HostPrincipal,\n leaseMs: number,\n ): Promise<Omit<ClaimedTurn, \"context\" | \"token\" | \"device\"> | undefined>;\n /** Whether anything waits for `expireLapsed`; one read, so the idle path writes nothing. */\n hasLapsed(db: ChatDb, owner: ChatOwner | undefined): Promise<boolean>;\n /**\n * Fails the running turns whose lease lapsed (`host_lost`) and marks the computers whose\n * liveness lapsed offline, for `owner` or, without one, for everyone. Commit it apart\n * from any claim, because a claim that finds nothing aborts its own transaction.\n */\n expireLapsed(tx: ChatDb, owner: ChatOwner | undefined): Promise<Lapsed[]>;\n /**\n * Puts a claimed turn back in the queue when the host never learned of the claim.\n * Refused (false) once the turn has an agent item or is no longer running.\n */\n releaseClaim(\n tx: ChatDb,\n owner: ChatOwner,\n host: HostPrincipal,\n turnId: TurnId,\n ): Promise<boolean>;\n appendHostItems(\n tx: ChatDb,\n owner: ChatOwner,\n host: HostPrincipal,\n turnId: TurnId,\n items: readonly HostItemDraft[],\n leaseMs: number,\n ): Promise<{\n readonly threadId: ThreadId;\n readonly leaseUntil: number;\n /** What the renewed turn token binds, so the caller need not read the turn again. */\n readonly clientId: string;\n readonly agentId: string;\n }>;\n /** Once per turn; a second call with any outcome is refused and changes nothing. */\n finishTurn(\n tx: ChatDb,\n owner: ChatOwner,\n host: HostPrincipal,\n turnId: TurnId,\n outcome: TurnOutcome,\n ): Promise<{ readonly threadId: ThreadId }>;\n readSignals(\n db: ChatDb,\n owner: ChatOwner,\n host: HostPrincipal,\n turns: readonly { readonly turnId: TurnId; readonly afterSeq: number }[],\n ): Promise<TurnSignals[]>;\n reportHost(\n tx: ChatDb,\n owner: ChatOwner,\n host: HostPrincipal,\n report: {\n readonly protocol: number;\n readonly name: string;\n readonly platform: string;\n readonly agents: readonly AgentInfo[];\n /** Left as it was when the host does not report it. */\n readonly devices?: readonly string[] | undefined;\n /** Left as it was when the host does not report it. */\n readonly presenceDue?: Readonly<Record<string, number>> | undefined;\n readonly online: boolean;\n readonly ttlMs: number;\n },\n ): Promise<{ readonly onlineUntil: number }>;\n /**\n * One UPDATE of `online_until`. `returned` is true when the sidebar had been told the\n * computer was offline, so the caller announces its return.\n */\n heartbeat(\n db: ChatDb,\n owner: ChatOwner,\n host: HostPrincipal,\n ttlMs: number,\n ): Promise<{ readonly returned: boolean }>;\n listHosts(db: ChatDb, owner: ChatOwner): Promise<StoredHost[]>;\n /** Drops the owner's computer rows whose grant is gone. */\n pruneHosts(tx: ChatDb, owner: ChatOwner, hostIds: readonly HostId[]): Promise<void>;\n turnHolder(db: ChatDb, turnId: TurnId): Promise<StreamTurn | undefined>;\n}\n\nexport interface StoredHost extends HostSummary {\n readonly grantId: GrantId;\n readonly protocol: number;\n}\n\nfunction rowsOf(result: unknown): unknown[] {\n if (Array.isArray(result)) return result;\n const rows = (result as { rows?: unknown[] } | null)?.rows;\n return rows ?? [];\n}\n\nasync function query<S extends z.ZodType>(\n db: ChatDb,\n statement: SQL,\n schema: S,\n): Promise<z.output<S>[]> {\n const result = await db.execute(statement);\n return rowsOf(result).map((row) => schema.parse(row));\n}\n\nconst count = z.coerce.number();\nconst ms = (column: string) => sql.raw(`(extract(epoch from ${column}) * 1000)::bigint`);\nconst ITEM_COLUMNS = sql`id, thread_id, turn_id, seq, kind, body, ${ms(\"created_at\")} as at`;\nconst ITEM_COLUMNS_OF_I = sql`i.id, i.thread_id, i.turn_id, i.seq, i.kind, i.body, ${ms(\"i.created_at\")} as at`;\nconst THREAD_COLUMNS = sql`id, title, host_id, agent_id, last_seq, ${ms(\"updated_at\")} as updated_at`;\n\nexport const THREAD_LIST_LIMIT = 200;\nconst TITLE_CHARS = 80;\nconst TERMINAL_STATES: ReadonlySet<string> = new Set([\"done\", \"failed\", \"cancelled\"]);\n\nconst threadMissing = () => new ActionError(\"not_found\", \"Thread not found\");\nconst turnMissing = () => new ActionError(\"not_found\", \"Turn not found\");\n\nconst inList = (values: readonly string[]) =>\n sql.join(\n values.map((value) => sql`${value}`),\n sql`, `,\n );\n\nconst authorOf = (kind: ChatItemKind) => chatItemBodies[kind].author;\n\nfunction titleOf(text: string): string {\n const firstLine = text.trim().split(\"\\n\")[0] ?? \"\";\n return [...firstLine].slice(0, TITLE_CHARS).join(\"\");\n}\n\ninterface ItemInsert {\n readonly id: ItemId;\n readonly turnId: TurnId;\n readonly kind: ChatItemKind;\n readonly body: unknown;\n}\n\ninterface LogLock {\n readonly lastSeq: number;\n readonly turnState: string | undefined;\n /** Running and its lease has not lapsed: the only turn a host may write to. */\n readonly leased: boolean;\n}\n\n/** The only place the log is locked: the thread row, then the turn row. */\nasync function lockLog(\n tx: ChatDb,\n owner: ChatOwner,\n threadId: ThreadId,\n turnId?: TurnId,\n): Promise<LogLock> {\n const [row] = await query(\n tx,\n sql`select last_seq from parlor_chat_threads\n where id = ${threadId} and scope = ${owner.scope} and owner_id = ${owner.userId}\n for update`,\n z.object({ last_seq: count }),\n );\n if (!row) throw threadMissing();\n if (turnId === undefined) {\n return { lastSeq: row.last_seq, turnState: undefined, leased: false };\n }\n const [turn] = await query(\n tx,\n sql`select state, coalesce(state = 'running' and lease_until >= now(), false) as leased\n from parlor_chat_turns where id = ${turnId} and thread_id = ${threadId} for update`,\n z.object({ state: z.string(), leased: z.boolean() }),\n );\n return {\n lastSeq: row.last_seq,\n turnState: turn?.state,\n leased: turn?.leased ?? false,\n };\n}\n\nasync function insertItems(\n tx: ChatDb,\n threadId: ThreadId,\n lastSeq: number,\n rows: readonly ItemInsert[],\n): Promise<number> {\n if (rows.length === 0) return lastSeq;\n const values = rows.map(\n (row, index) =>\n sql`(${row.id}, ${threadId}, ${row.turnId}, ${lastSeq + index + 1}, ${authorOf(row.kind)}, ${row.kind}, ${JSON.stringify(row.body)}::jsonb)`,\n );\n await tx.execute(\n sql`insert into parlor_chat_items (id, thread_id, turn_id, seq, author, kind, body)\n values ${sql.join(values, sql`, `)}`,\n );\n const next = lastSeq + rows.length;\n await tx.execute(\n sql`update parlor_chat_threads set last_seq = ${next}, updated_at = now() where id = ${threadId}`,\n );\n return next;\n}\n\nasync function itemThreads(\n tx: ChatDb,\n ids: readonly ItemId[],\n): Promise<Map<string, string>> {\n if (ids.length === 0) return new Map();\n const rows = await query(\n tx,\n sql`select id, thread_id from parlor_chat_items where id in (${inList(ids)})`,\n z.object({ id: z.string(), thread_id: z.string() }),\n );\n return new Map(rows.map((row) => [row.id, row.thread_id]));\n}\n\nasync function itemsById(\n db: ChatDb,\n threadId: ThreadId,\n ids: readonly ItemId[],\n): Promise<LoggedItem[]> {\n if (ids.length === 0) return [];\n const rows = await query(\n db,\n sql`select ${ITEM_COLUMNS} from parlor_chat_items\n where thread_id = ${threadId} and id in (${inList(ids)}) order by seq`,\n itemRow,\n );\n return rows.map(toLoggedItem);\n}\n\nconst turnRow = z.object({\n id: idSchema(\"turn\"),\n thread_id: idSchema(\"thread\"),\n host_id: idSchema(\"host\"),\n agent_id: z.string(),\n state: turnStateSchema,\n lapsed: z.boolean(),\n failure: z.enum(TURN_FAILURES).nullable(),\n});\n\nasync function createTurn(\n tx: ChatDb,\n owner: ChatOwner,\n threadId: ThreadId,\n turn: NewTurn,\n promptItemId: ItemId,\n): Promise<void> {\n const [existing] = await query(\n tx,\n sql`select thread_id, owner_id, scope from parlor_chat_turns where id = ${turn.turnId}`,\n z.object({ thread_id: z.string(), owner_id: z.string(), scope: z.string() }),\n );\n if (existing) {\n const sameThread =\n existing.thread_id === threadId &&\n existing.owner_id === owner.userId &&\n existing.scope === owner.scope;\n if (!sameThread) throw turnMissing();\n return;\n }\n const [queued] = await query(\n tx,\n sql`select count(*) as waiting from parlor_chat_turns\n where thread_id = ${threadId} and state = 'queued'`,\n z.object({ waiting: count }),\n );\n if ((queued?.waiting ?? 0) >= turn.maxQueued) {\n throw new ActionError(\n \"invalid_input\",\n \"Too many prompts are waiting for the agent; wait for it to catch up.\",\n );\n }\n await tx.execute(\n sql`insert into parlor_chat_turns\n (id, thread_id, scope, owner_id, host_id, agent_id, client_id, device_id, prompt_item_id, state)\n values (${turn.turnId}, ${threadId}, ${owner.scope}, ${owner.userId}, ${turn.agent.hostId},\n ${turn.agent.agentId}, ${turn.clientId}, ${turn.deviceId ?? null}, ${promptItemId}, 'queued')`,\n );\n}\n\nasync function admitsUserItem(\n tx: ChatDb,\n threadId: ThreadId,\n turnId: TurnId,\n draft: UserDraft,\n): Promise<boolean> {\n if (draft.kind === \"prompt\") return true;\n if (draft.kind === \"answer\") {\n const askId = draft.body.askId;\n const [ask] = await query(\n tx,\n sql`select turn_id, body->>'approval' as approval from parlor_chat_items\n where thread_id = ${threadId} and id = ${askId} and kind = 'ask'`,\n z.object({ turn_id: z.string(), approval: z.string().nullable() }),\n );\n if (!ask || ask.turn_id !== turnId)\n throw new ActionError(\"not_found\", \"Question not found\");\n if (ask.approval === \"computer\") {\n throw new ActionError(\"forbidden\", \"This question is answered on the computer.\");\n }\n const { signed } = draft.body;\n if (ask.approval === \"presence\" && signed?.signature.presence !== \"user\") {\n throw new ActionError(\"forbidden\", \"This question needs a presence check.\");\n }\n if (ask.approval === \"device\" && !signed) {\n throw new ActionError(\n \"forbidden\",\n \"This question is answered on the device that asked it.\",\n );\n }\n const [state] = await query(\n tx,\n sql`select state from parlor_chat_turns where id = ${turnId} for update`,\n z.object({ state: z.string() }),\n );\n const [standing] = await query(\n tx,\n sql`select a.id from parlor_chat_items a\n where a.thread_id = ${threadId} and a.kind = 'answer' and a.body->>'askId' = ${askId}\n and not exists (\n select 1 from parlor_chat_items e\n where e.thread_id = ${threadId} and e.kind = 'event'\n and e.body->>'event' = 'answer_rejected' and e.body->>'itemId' = a.id)\n limit 1`,\n z.object({ id: z.string() }),\n );\n if (standing) {\n throw new ActionError(\"invalid_input\", \"This question was already answered.\");\n }\n if (state?.state !== \"running\") {\n throw new ActionError(\n \"invalid_input\",\n \"The agent is no longer waiting for this answer.\",\n );\n }\n return true;\n }\n const [turn] = await query(\n tx,\n sql`select state from parlor_chat_turns where id = ${turnId} and thread_id = ${threadId}`,\n z.object({ state: z.string() }),\n );\n if (!turn) throw turnMissing();\n if (TERMINAL_STATES.has(turn.state)) return false;\n if (turn.state === \"queued\") {\n await tx.execute(\n sql`update parlor_chat_turns\n set state = 'cancelled', outcome = '{\"state\":\"cancelled\"}'::jsonb, finished_at = now()\n where id = ${turnId} and state = 'queued'`,\n );\n }\n return true;\n}\n\nconst lostItemId = () => asId(\"item\", `lost.${randomUUID()}`);\n\nconst ownedBy = (owner: ChatOwner | undefined) =>\n owner ? sql`and owner_id = ${owner.userId} and scope = ${owner.scope}` : sql``;\n\nasync function lapsedFor(db: ChatDb, owner: ChatOwner | undefined): Promise<boolean> {\n const [found] = await query(\n db,\n sql`select (\n exists (select 1 from parlor_chat_turns\n where state = 'running' and lease_until < now() ${ownedBy(owner)})\n or exists (select 1 from parlor_chat_hosts\n where announced_online and online_until <= now() ${ownedBy(owner)})\n ) as lapsed`,\n z.object({ lapsed: z.boolean() }),\n );\n return found?.lapsed ?? false;\n}\n\nasync function expireLapsed(tx: ChatDb, owner: ChatOwner | undefined): Promise<Lapsed[]> {\n const changes = new Map<string, Lapsed & { threads: ThreadId[]; hosts: HostId[] }>();\n const changed = (scope: string, userId: UserId) => {\n const key = `${scope}\\u0000${userId}`;\n let change = changes.get(key);\n if (!change) changes.set(key, (change = { scope, userId, threads: [], hosts: [] }));\n return change;\n };\n const lapsed = await query(\n tx,\n sql`select distinct scope, owner_id, thread_id from parlor_chat_turns\n where state = 'running' and lease_until < now() ${ownedBy(owner)}\n order by thread_id`,\n z.object({\n scope: z.string(),\n owner_id: idSchema(\"user\"),\n thread_id: idSchema(\"thread\"),\n }),\n );\n for (const { scope, owner_id: userId, thread_id: threadId } of lapsed) {\n const { lastSeq } = await lockLog(tx, { scope, userId }, threadId);\n const expired = await query(\n tx,\n sql`update parlor_chat_turns\n set state = 'failed', outcome = '{\"state\":\"failed\",\"reason\":\"host_lost\"}'::jsonb,\n finished_at = now()\n where thread_id = ${threadId} and state = 'running' and lease_until < now()\n returning id`,\n z.object({ id: idSchema(\"turn\") }),\n );\n if (expired.length === 0) continue;\n await insertItems(\n tx,\n threadId,\n lastSeq,\n expired.map(({ id }) => ({\n id: lostItemId(),\n turnId: id,\n kind: \"event\",\n body: { event: \"host_lost\" } satisfies ChatItemBody<\"event\">,\n })),\n );\n changed(scope, userId).threads.push(threadId);\n }\n const offline = await query(\n tx,\n sql`update parlor_chat_hosts set announced_online = false\n where announced_online and online_until <= now() ${ownedBy(owner)}\n returning id, scope, owner_id`,\n z.object({ id: idSchema(\"host\"), scope: z.string(), owner_id: idSchema(\"user\") }),\n );\n for (const { id, scope, owner_id: userId } of offline) {\n changed(scope, userId).hosts.push(id);\n }\n return [...changes.values()];\n}\n\nasync function lockRunningTurn(\n tx: ChatDb,\n owner: ChatOwner,\n host: HostPrincipal,\n turnId: TurnId,\n): Promise<{\n readonly threadId: ThreadId;\n readonly lastSeq: number;\n readonly clientId: string;\n readonly agentId: string;\n}> {\n const [found] = await query(\n tx,\n sql`select thread_id, client_id, agent_id from parlor_chat_turns\n where id = ${turnId} and host_id = ${host.hostId}\n and owner_id = ${owner.userId} and scope = ${owner.scope}`,\n z.object({\n thread_id: idSchema(\"thread\"),\n client_id: z.string(),\n agent_id: z.string(),\n }),\n );\n if (!found) throw turnMissing();\n const { lastSeq, leased } = await lockLog(tx, owner, found.thread_id, turnId);\n if (!leased) throw new ActionError(\"forbidden\", \"The turn is not running.\");\n return {\n threadId: found.thread_id,\n lastSeq,\n clientId: found.client_id,\n agentId: found.agent_id,\n };\n}\n\nconst leaseSql = (leaseMs: number) =>\n sql`now() + ${leaseMs}::float8 * interval '1 millisecond'`;\n\nasync function requireThread(db: ChatDb, owner: ChatOwner, threadId: ThreadId) {\n const [thread] = await query(\n db,\n sql`select id from parlor_chat_threads\n where id = ${threadId} and owner_id = ${owner.userId} and scope = ${owner.scope}`,\n z.object({ id: z.string() }),\n );\n if (!thread) throw threadMissing();\n}\n\n/** The turns that `items`, a run of consecutive seqs, belong to. */\nasync function turnsOf(\n db: ChatDb,\n threadId: ThreadId,\n items: readonly LoggedItem[],\n): Promise<TurnStatus[]> {\n const first = items[0];\n const last = items.at(-1);\n if (!first || !last) return [];\n const turns = await query(\n db,\n sql`select id, thread_id, host_id, agent_id, state,\n coalesce(state = 'running' and lease_until < now(), false) as lapsed,\n case when state = 'failed' then outcome->>'reason' end as failure\n from parlor_chat_turns\n where thread_id = ${threadId}\n and id in (select turn_id from parlor_chat_items\n where thread_id = ${threadId} and seq between ${first.seq} and ${last.seq})\n order by created_at, id`,\n turnRow,\n );\n return turns.map((turn) => ({\n turnId: turn.id,\n state: turn.lapsed ? \"failed\" : turn.state,\n agent: { hostId: turn.host_id, agentId: turn.agent_id },\n failure: turn.lapsed ? \"host_lost\" : (turn.failure ?? undefined),\n }));\n}\n\nexport const sqlChatStore: ChatStore = {\n async listThreads(db, owner) {\n const rows = await query(\n db,\n sql`select ${THREAD_COLUMNS} from parlor_chat_threads\n where owner_id = ${owner.userId} and scope = ${owner.scope}\n order by updated_at desc, id limit ${THREAD_LIST_LIMIT}`,\n threadRow,\n );\n return rows.map(toThreadSummary);\n },\n\n async readPage(db, owner, threadId, limit) {\n const [row] = await query(\n db,\n threadId\n ? sql`select ${THREAD_COLUMNS} from parlor_chat_threads\n where id = ${threadId} and owner_id = ${owner.userId} and scope = ${owner.scope}`\n : sql`select ${THREAD_COLUMNS} from parlor_chat_threads\n where owner_id = ${owner.userId} and scope = ${owner.scope}\n order by updated_at desc, id limit 1`,\n threadRow,\n );\n if (!row) return { thread: undefined, items: [], turns: [], hasMore: false };\n const rows = await query(\n db,\n sql`select ${ITEM_COLUMNS} from parlor_chat_items\n where thread_id = ${row.id} order by seq desc limit ${limit + 1}`,\n itemRow,\n );\n const hasMore = rows.length > limit;\n const items = rows.slice(0, limit).reverse().map(toLoggedItem);\n return {\n thread: toThreadSummary(row),\n items,\n turns: await turnsOf(db, row.id, items),\n hasMore,\n };\n },\n\n async readItemsBefore(db, owner, threadId, beforeSeq, limit) {\n const rows = await query(\n db,\n sql`select ${ITEM_COLUMNS_OF_I} from parlor_chat_items i\n join parlor_chat_threads t on t.id = i.thread_id\n where t.id = ${threadId} and t.owner_id = ${owner.userId} and t.scope = ${owner.scope}\n and i.seq < ${beforeSeq}\n order by i.seq desc limit ${limit + 1}`,\n itemRow,\n );\n if (rows.length === 0) await requireThread(db, owner, threadId);\n const items = rows.slice(0, limit).reverse().map(toLoggedItem);\n return {\n items,\n turns: await turnsOf(db, threadId, items),\n hasMore: rows.length > limit,\n };\n },\n\n async listThreadsBefore(db, owner, before, limit) {\n const updated = ms(\"updated_at\");\n const rows = await query(\n db,\n sql`select ${THREAD_COLUMNS} from parlor_chat_threads\n where owner_id = ${owner.userId} and scope = ${owner.scope}\n and (${updated} < ${before.updatedAt}\n or (${updated} = ${before.updatedAt} and id < ${before.id}))\n order by updated_at desc, id desc limit ${limit + 1}`,\n threadRow,\n );\n return {\n threads: rows.slice(0, limit).map(toThreadSummary),\n hasMore: rows.length > limit,\n };\n },\n\n async readItemsAfter(db, owner, threadId, afterSeq) {\n const rows = await query(\n db,\n sql`select ${ITEM_COLUMNS_OF_I} from parlor_chat_items i\n join parlor_chat_threads t on t.id = i.thread_id\n where t.id = ${threadId} and t.owner_id = ${owner.userId} and t.scope = ${owner.scope}\n and i.seq > ${afterSeq}\n order by i.seq`,\n itemRow,\n );\n if (rows.length === 0) await requireThread(db, owner, threadId);\n return rows.map(toLoggedItem);\n },\n\n async appendUserItems(tx, owner, input) {\n const { threadId, turnId, items, turn } = input;\n if (turn) {\n const prompt = items.find((draft) => draft.kind === \"prompt\");\n await tx.execute(\n sql`insert into parlor_chat_threads (id, scope, owner_id, title, host_id, agent_id)\n values (${threadId}, ${owner.scope}, ${owner.userId},\n ${prompt?.kind === \"prompt\" ? titleOf(prompt.body.text) : \"\"},\n ${turn.agent.hostId}, ${turn.agent.agentId})\n on conflict (id) do nothing`,\n );\n }\n const { lastSeq } = await lockLog(tx, owner, threadId);\n if (turn) {\n const prompt = items.find((draft) => draft.kind === \"prompt\");\n if (!prompt) throw new ActionError(\"invalid_input\", \"A turn starts with a prompt.\");\n await createTurn(tx, owner, threadId, turn, prompt.id);\n }\n const known = await itemThreads(\n tx,\n items.map((draft) => draft.id),\n );\n const fresh: UserDraft[] = [];\n for (const draft of items) {\n const where = known.get(draft.id);\n if (where === undefined) {\n if (await admitsUserItem(tx, threadId, turnId, draft)) fresh.push(draft);\n } else if (where !== threadId) {\n throw threadMissing();\n }\n }\n await insertItems(\n tx,\n threadId,\n lastSeq,\n fresh.map((draft) => ({\n id: draft.id,\n turnId,\n kind: draft.kind,\n body: draft.body,\n })),\n );\n if (turn) {\n await tx.execute(\n sql`update parlor_chat_threads set host_id = ${turn.agent.hostId}, agent_id = ${turn.agent.agentId}\n where id = ${threadId}`,\n );\n }\n return itemsById(\n tx,\n threadId,\n items.map((draft) => draft.id),\n );\n },\n\n async hasLapsed(db, owner) {\n return lapsedFor(db, owner);\n },\n\n expireLapsed,\n\n async claimTurn(tx, owner, host, leaseMs) {\n const [claimed] = await query(\n tx,\n sql`select t.id, t.thread_id, t.agent_id, t.client_id, t.prompt_item_id\n from parlor_chat_turns t\n where t.host_id = ${host.hostId} and t.owner_id = ${owner.userId}\n and t.scope = ${owner.scope} and t.state = 'queued'\n and not exists (\n select 1 from parlor_chat_turns earlier\n where earlier.thread_id = t.thread_id and earlier.state in ('queued', 'running')\n and (earlier.created_at, earlier.id) < (t.created_at, t.id))\n order by t.created_at, t.id\n limit 1\n for update of t skip locked`,\n z.object({\n id: idSchema(\"turn\"),\n thread_id: idSchema(\"thread\"),\n agent_id: z.string(),\n client_id: z.string(),\n prompt_item_id: idSchema(\"item\"),\n }),\n );\n if (!claimed) return undefined;\n const [lease] = await query(\n tx,\n sql`update parlor_chat_turns set state = 'running', lease_until = ${leaseSql(leaseMs)}\n where id = ${claimed.id}\n returning ${ms(\"lease_until\")} as lease_until`,\n z.object({ lease_until: count }),\n );\n const [prompt] = await itemsById(tx, claimed.thread_id, [claimed.prompt_item_id]);\n if (!lease || prompt?.kind !== \"prompt\") {\n throw new ActionError(\"internal\", \"The claimed turn lost its prompt.\");\n }\n return {\n id: claimed.thread_id,\n turnId: claimed.id,\n agent: { hostId: host.hostId, agentId: claimed.agent_id },\n clientId: claimed.client_id,\n prompt,\n leaseUntil: lease.lease_until,\n };\n },\n\n async releaseClaim(tx, owner, host, turnId) {\n const released = await query(\n tx,\n sql`update parlor_chat_turns set state = 'queued', lease_until = null\n where id = ${turnId} and host_id = ${host.hostId} and owner_id = ${owner.userId}\n and scope = ${owner.scope} and state = 'running' and lease_until >= now()\n and not exists (\n select 1 from parlor_chat_items i\n where i.turn_id = parlor_chat_turns.id and i.author <> 'user')\n returning thread_id`,\n z.object({ thread_id: idSchema(\"thread\") }),\n );\n return released.length > 0;\n },\n\n async appendHostItems(tx, owner, host, turnId, items, leaseMs) {\n const { threadId, lastSeq, clientId, agentId } = await lockRunningTurn(\n tx,\n owner,\n host,\n turnId,\n );\n const known = await itemThreads(\n tx,\n items.map((draft) => draft.id),\n );\n const fresh: HostItemDraft[] = [];\n for (const draft of items) {\n const where = known.get(draft.id);\n if (where === undefined) fresh.push(draft);\n else if (where !== threadId)\n throw new ActionError(\"forbidden\", \"That item id is taken.\");\n }\n await insertItems(\n tx,\n threadId,\n lastSeq,\n fresh.map((draft) => ({\n id: draft.id,\n turnId,\n kind: draft.kind,\n body: draft.body,\n })),\n );\n const [lease] = await query(\n tx,\n sql`update parlor_chat_turns set lease_until = ${leaseSql(leaseMs)}\n where id = ${turnId} returning ${ms(\"lease_until\")} as lease_until`,\n z.object({ lease_until: count }),\n );\n return { threadId, leaseUntil: lease?.lease_until ?? 0, clientId, agentId };\n },\n\n async finishTurn(tx, owner, host, turnId, outcome) {\n const { threadId } = await lockRunningTurn(tx, owner, host, turnId);\n await tx.execute(\n sql`update parlor_chat_turns\n set state = ${outcome.state}, outcome = ${JSON.stringify(outcome)}::jsonb, finished_at = now()\n where id = ${turnId}`,\n );\n await tx.execute(\n sql`update parlor_chat_threads set updated_at = now() where id = ${threadId}`,\n );\n return { threadId };\n },\n\n async readSignals(db, owner, host, turns) {\n if (turns.length === 0) return [];\n const wanted = turns.map(\n ({ turnId, afterSeq }) => sql`(${turnId}::text, ${afterSeq}::bigint)`,\n );\n const rows = await query(\n db,\n sql`select ${ITEM_COLUMNS_OF_I}\n from parlor_chat_items i\n join parlor_chat_turns t on t.id = i.turn_id and t.thread_id = i.thread_id\n join (values ${sql.join(wanted, sql`, `)}) as q(turn_id, after_seq)\n on q.turn_id = i.turn_id\n where t.host_id = ${host.hostId} and t.owner_id = ${owner.userId}\n and t.scope = ${owner.scope} and i.kind in ('answer', 'cancel')\n and i.seq > q.after_seq\n order by i.turn_id, i.seq`,\n itemRow,\n );\n const byTurn = new Map<TurnId, LoggedItem<\"answer\" | \"cancel\">[]>();\n for (const item of rows.map(toLoggedItem)) {\n if (item.kind !== \"answer\" && item.kind !== \"cancel\") continue;\n const list = byTurn.get(item.turnId) ?? [];\n list.push(item);\n byTurn.set(item.turnId, list);\n }\n return [...byTurn].map(([turnId, items]) => ({ turnId, items }));\n },\n\n async reportHost(tx, owner, host, report) {\n const ttlMs = report.online ? report.ttlMs : 0;\n const [row] = await query(\n tx,\n sql`insert into parlor_chat_hosts\n (id, scope, owner_id, grant_id, protocol, name, platform, agents, devices,\n presence_due, online_until, announced_online)\n values (${host.hostId}, ${owner.scope}, ${owner.userId}, ${host.grantId},\n ${report.protocol}, ${report.name}, ${report.platform},\n ${JSON.stringify(report.agents)}::jsonb,\n ${report.devices ? JSON.stringify(report.devices) : null}::jsonb,\n ${report.presenceDue ? JSON.stringify(report.presenceDue) : null}::jsonb,\n ${leaseSql(ttlMs)}, ${report.online})\n on conflict (id) do update set\n grant_id = excluded.grant_id, protocol = excluded.protocol, name = excluded.name,\n platform = excluded.platform, agents = excluded.agents,\n devices = coalesce(excluded.devices, parlor_chat_hosts.devices),\n presence_due = coalesce(excluded.presence_due, parlor_chat_hosts.presence_due),\n online_until = excluded.online_until,\n announced_online = excluded.announced_online\n where parlor_chat_hosts.owner_id = excluded.owner_id\n and parlor_chat_hosts.scope = excluded.scope\n returning ${ms(\"online_until\")} as online_until`,\n z.object({ online_until: count }),\n );\n if (!row)\n throw new ActionError(\"forbidden\", \"This computer belongs to another account.\");\n return { onlineUntil: row.online_until };\n },\n\n async heartbeat(db, owner, host, ttlMs) {\n const [beat] = await query(\n db,\n sql`update parlor_chat_hosts as h\n set online_until = ${leaseSql(ttlMs)}, announced_online = true\n from (select announced_online as was from parlor_chat_hosts where id = ${host.hostId}) as prev\n where h.id = ${host.hostId} and h.owner_id = ${owner.userId}\n and h.scope = ${owner.scope} and h.grant_id = ${host.grantId}\n returning prev.was as was`,\n z.object({ was: z.boolean() }),\n );\n return { returned: beat !== undefined && !beat.was };\n },\n\n async listHosts(db, owner) {\n return query(\n db,\n sql`select id, grant_id, protocol, name, platform, agents, devices, presence_due,\n online_until > now() as online\n from parlor_chat_hosts\n where owner_id = ${owner.userId} and scope = ${owner.scope}\n order by name, id`,\n z\n .object({\n id: idSchema(\"host\"),\n grant_id: idSchema(\"grant\"),\n protocol: z.number().int(),\n name: z.string(),\n platform: z.string(),\n agents: z.array(agentInfoSchema),\n devices: z.array(z.string()).nullable(),\n presence_due: z.record(z.string(), z.number()).nullable(),\n online: z.boolean(),\n })\n .transform(({ grant_id, devices, presence_due, ...host }) => ({\n ...host,\n ...(devices ? { devices } : {}),\n ...(presence_due ? { presenceDue: presence_due } : {}),\n grantId: grant_id,\n })),\n );\n },\n\n async pruneHosts(tx, owner, hostIds) {\n if (hostIds.length === 0) return;\n await tx.execute(\n sql`delete from parlor_chat_hosts\n where owner_id = ${owner.userId} and scope = ${owner.scope} and id in (${inList(hostIds)})`,\n );\n },\n\n async turnHolder(db, turnId) {\n const [row] = await query(\n db,\n sql`select scope, owner_id, thread_id, host_id,\n coalesce(state = 'running' and lease_until >= now(), false) as live\n from parlor_chat_turns where id = ${turnId}`,\n z.object({\n scope: z.string(),\n owner_id: idSchema(\"user\"),\n thread_id: idSchema(\"thread\"),\n host_id: idSchema(\"host\"),\n live: z.boolean(),\n }),\n );\n return row\n ? {\n owner: { scope: row.scope, userId: row.owner_id },\n threadId: row.thread_id,\n hostId: row.host_id,\n live: row.live,\n }\n : undefined;\n },\n};\n"]}
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/server/store.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from \"node:crypto\";\n\nimport {\n ActionError,\n asId,\n type DeviceId,\n type GrantId,\n type HostId,\n type HostPrincipal,\n type ItemId,\n type ThreadId,\n type TurnId,\n type UserId,\n} from \"@parlor/core\";\nimport { type SQL, sql } from \"drizzle-orm\";\nimport type { PgDatabase, PgQueryResultHKT } from \"drizzle-orm/pg-core\";\nimport { z } from \"zod\";\n\nimport { idSchema } from \"../id-schemas.js\";\nimport { chatItemBodies, type ChatItemBody, type ChatItemKind } from \"../items.js\";\nimport type { HostItemDraft, LoggedItem, UserItemKind } from \"../items.js\";\nimport type {\n AgentInfo,\n AgentRef,\n ChatOwner,\n ClaimedTurn,\n HostSummary,\n ThreadPage,\n ThreadSummary,\n TurnOutcome,\n TurnSignals,\n TurnStatus,\n} from \"../model.js\";\nimport {\n agentInfoSchema,\n itemRow,\n threadRow,\n toLoggedItem,\n toThreadSummary,\n TURN_FAILURES,\n turnStateSchema,\n} from \"./rows.js\";\n\nexport type ChatDb = PgDatabase<PgQueryResultHKT>;\n\nexport type UserDraft = {\n [K in UserItemKind]: {\n readonly id: LoggedItem<K>[\"id\"];\n readonly kind: K;\n readonly body: LoggedItem<K>[\"body\"];\n };\n}[UserItemKind];\n\nexport interface NewTurn {\n readonly turnId: TurnId;\n readonly agent: AgentRef;\n readonly clientId: string;\n readonly deviceId: DeviceId | undefined;\n /** The most turns a thread may have waiting; checked under the thread's lock. */\n readonly maxQueued: number;\n}\n\nexport interface TurnHolder {\n readonly owner: ChatOwner;\n readonly threadId: ThreadId;\n readonly hostId: HostId;\n}\n\n/** A turn's holder with whether it runs now: running, and its lease not yet lapsed. */\nexport interface StreamTurn extends TurnHolder {\n readonly live: boolean;\n}\n\n/** What one reconciliation changed in a scope, for the sync events it must emit. */\nexport interface OlderItems {\n readonly items: readonly LoggedItem[];\n readonly turns: readonly TurnStatus[];\n readonly hasMore: boolean;\n}\n\nexport interface ThreadCursor {\n readonly updatedAt: number;\n readonly id: ThreadId;\n}\n\nexport interface OlderThreads {\n readonly threads: readonly ThreadSummary[];\n readonly hasMore: boolean;\n}\n\nexport interface Lapsed extends ChatOwner {\n readonly threads: readonly ThreadId[];\n readonly hosts: readonly HostId[];\n}\n\n/**\n * The thread log in SQL. Every function filters by the owner's scope and user in the same\n * statement, so another user's thread id reads as not found. Appends lock the thread row\n * to assign gap-free `seq` and run in the action's transaction so the thread's sync event\n * commits with them.\n */\nexport interface ChatStore {\n listThreads(db: ChatDb, owner: ChatOwner): Promise<ThreadSummary[]>;\n readPage(\n db: ChatDb,\n owner: ChatOwner,\n threadId: ThreadId | undefined,\n limit: number,\n ): Promise<Omit<ThreadPage, \"open\">>;\n /** Items before `beforeSeq`, oldest first, with their turns; `hasMore` while older ones remain. */\n readItemsBefore?(\n db: ChatDb,\n owner: ChatOwner,\n threadId: ThreadId,\n beforeSeq: number,\n limit: number,\n ): Promise<OlderItems>;\n /** Threads updated before `before`, newest first, after the ones `listThreads` returned. */\n listThreadsBefore?(\n db: ChatDb,\n owner: ChatOwner,\n before: ThreadCursor,\n limit: number,\n ): Promise<OlderThreads>;\n readItemsAfter(\n db: ChatDb,\n owner: ChatOwner,\n threadId: ThreadId,\n afterSeq: number,\n ): Promise<LoggedItem[]>;\n /** With `turn`, also queues it; the thread is created on its first prompt. */\n appendUserItems(\n tx: ChatDb,\n owner: ChatOwner,\n input: {\n readonly threadId: ThreadId;\n readonly turnId: TurnId;\n readonly items: readonly UserDraft[];\n readonly turn?: NewTurn | undefined;\n },\n ): Promise<LoggedItem[]>;\n claimTurn(\n tx: ChatDb,\n owner: ChatOwner,\n host: HostPrincipal,\n leaseMs: number,\n ): Promise<Omit<ClaimedTurn, \"context\" | \"token\" | \"device\"> | undefined>;\n /** Whether anything waits for `expireLapsed`; one read, so the idle path writes nothing. */\n hasLapsed(db: ChatDb, owner: ChatOwner | undefined): Promise<boolean>;\n /**\n * Fails the running turns whose lease lapsed (`host_lost`) and marks the computers whose\n * liveness lapsed offline, for `owner` or, without one, for everyone. Commit it apart\n * from any claim, because a claim that finds nothing aborts its own transaction.\n */\n expireLapsed(tx: ChatDb, owner: ChatOwner | undefined): Promise<Lapsed[]>;\n /**\n * Puts a claimed turn back in the queue when the host never learned of the claim.\n * Refused (false) once the turn has an agent item or is no longer running.\n */\n releaseClaim(\n tx: ChatDb,\n owner: ChatOwner,\n host: HostPrincipal,\n turnId: TurnId,\n ): Promise<boolean>;\n appendHostItems(\n tx: ChatDb,\n owner: ChatOwner,\n host: HostPrincipal,\n turnId: TurnId,\n items: readonly HostItemDraft[],\n leaseMs: number,\n ): Promise<{\n readonly threadId: ThreadId;\n readonly leaseUntil: number;\n /** What the renewed turn token binds, so the caller need not read the turn again. */\n readonly clientId: string;\n readonly agentId: string;\n }>;\n /** Once per turn; a second call with any outcome is refused and changes nothing. */\n finishTurn(\n tx: ChatDb,\n owner: ChatOwner,\n host: HostPrincipal,\n turnId: TurnId,\n outcome: TurnOutcome,\n ): Promise<{ readonly threadId: ThreadId }>;\n readSignals(\n db: ChatDb,\n owner: ChatOwner,\n host: HostPrincipal,\n turns: readonly { readonly turnId: TurnId; readonly afterSeq: number }[],\n ): Promise<TurnSignals[]>;\n reportHost(\n tx: ChatDb,\n owner: ChatOwner,\n host: HostPrincipal,\n report: {\n readonly protocol: number;\n readonly name: string;\n readonly platform: string;\n readonly agents: readonly AgentInfo[];\n /** Left as it was when the host does not report it. */\n readonly devices?: readonly string[] | undefined;\n /** Left as it was when the host does not report it. */\n readonly presenceDue?: Readonly<Record<string, number>> | undefined;\n /** When the host looked for its agents; left as it was when this report is not that. */\n readonly agentsCheckedAt?: number | undefined;\n readonly online: boolean;\n readonly ttlMs: number;\n },\n ): Promise<{ readonly onlineUntil: number }>;\n /**\n * One UPDATE of `online_until`. `returned` is true when the sidebar had been told the\n * computer was offline, so the caller announces its return.\n */\n heartbeat(\n db: ChatDb,\n owner: ChatOwner,\n host: HostPrincipal,\n ttlMs: number,\n ): Promise<{ readonly returned: boolean }>;\n listHosts(db: ChatDb, owner: ChatOwner): Promise<StoredHost[]>;\n /** Drops the owner's computer rows whose grant is gone. */\n pruneHosts(tx: ChatDb, owner: ChatOwner, hostIds: readonly HostId[]): Promise<void>;\n turnHolder(db: ChatDb, turnId: TurnId): Promise<StreamTurn | undefined>;\n}\n\nexport interface StoredHost extends HostSummary {\n readonly grantId: GrantId;\n readonly protocol: number;\n}\n\nfunction rowsOf(result: unknown): unknown[] {\n if (Array.isArray(result)) return result;\n const rows = (result as { rows?: unknown[] } | null)?.rows;\n return rows ?? [];\n}\n\nasync function query<S extends z.ZodType>(\n db: ChatDb,\n statement: SQL,\n schema: S,\n): Promise<z.output<S>[]> {\n const result = await db.execute(statement);\n return rowsOf(result).map((row) => schema.parse(row));\n}\n\nconst count = z.coerce.number();\nconst ms = (column: string) => sql.raw(`(extract(epoch from ${column}) * 1000)::bigint`);\nconst ITEM_COLUMNS = sql`id, thread_id, turn_id, seq, kind, body, ${ms(\"created_at\")} as at`;\nconst ITEM_COLUMNS_OF_I = sql`i.id, i.thread_id, i.turn_id, i.seq, i.kind, i.body, ${ms(\"i.created_at\")} as at`;\nconst THREAD_COLUMNS = sql`id, title, host_id, agent_id, last_seq, ${ms(\"updated_at\")} as updated_at`;\n\nexport const THREAD_LIST_LIMIT = 200;\nconst TITLE_CHARS = 80;\nconst TERMINAL_STATES: ReadonlySet<string> = new Set([\"done\", \"failed\", \"cancelled\"]);\n\nconst threadMissing = () => new ActionError(\"not_found\", \"Thread not found\");\nconst turnMissing = () => new ActionError(\"not_found\", \"Turn not found\");\n\nconst inList = (values: readonly string[]) =>\n sql.join(\n values.map((value) => sql`${value}`),\n sql`, `,\n );\n\nconst authorOf = (kind: ChatItemKind) => chatItemBodies[kind].author;\n\nfunction titleOf(text: string): string {\n const firstLine = text.trim().split(\"\\n\")[0] ?? \"\";\n return [...firstLine].slice(0, TITLE_CHARS).join(\"\");\n}\n\ninterface ItemInsert {\n readonly id: ItemId;\n readonly turnId: TurnId;\n readonly kind: ChatItemKind;\n readonly body: unknown;\n}\n\ninterface LogLock {\n readonly lastSeq: number;\n readonly turnState: string | undefined;\n /** Running and its lease has not lapsed: the only turn a host may write to. */\n readonly leased: boolean;\n}\n\n/** The only place the log is locked: the thread row, then the turn row. */\nasync function lockLog(\n tx: ChatDb,\n owner: ChatOwner,\n threadId: ThreadId,\n turnId?: TurnId,\n): Promise<LogLock> {\n const [row] = await query(\n tx,\n sql`select last_seq from parlor_chat_threads\n where id = ${threadId} and scope = ${owner.scope} and owner_id = ${owner.userId}\n for update`,\n z.object({ last_seq: count }),\n );\n if (!row) throw threadMissing();\n if (turnId === undefined) {\n return { lastSeq: row.last_seq, turnState: undefined, leased: false };\n }\n const [turn] = await query(\n tx,\n sql`select state, coalesce(state = 'running' and lease_until >= now(), false) as leased\n from parlor_chat_turns where id = ${turnId} and thread_id = ${threadId} for update`,\n z.object({ state: z.string(), leased: z.boolean() }),\n );\n return {\n lastSeq: row.last_seq,\n turnState: turn?.state,\n leased: turn?.leased ?? false,\n };\n}\n\nasync function insertItems(\n tx: ChatDb,\n threadId: ThreadId,\n lastSeq: number,\n rows: readonly ItemInsert[],\n): Promise<number> {\n if (rows.length === 0) return lastSeq;\n const values = rows.map(\n (row, index) =>\n sql`(${row.id}, ${threadId}, ${row.turnId}, ${lastSeq + index + 1}, ${authorOf(row.kind)}, ${row.kind}, ${JSON.stringify(row.body)}::jsonb)`,\n );\n await tx.execute(\n sql`insert into parlor_chat_items (id, thread_id, turn_id, seq, author, kind, body)\n values ${sql.join(values, sql`, `)}`,\n );\n const next = lastSeq + rows.length;\n await tx.execute(\n sql`update parlor_chat_threads set last_seq = ${next}, updated_at = now() where id = ${threadId}`,\n );\n return next;\n}\n\nasync function itemThreads(\n tx: ChatDb,\n ids: readonly ItemId[],\n): Promise<Map<string, string>> {\n if (ids.length === 0) return new Map();\n const rows = await query(\n tx,\n sql`select id, thread_id from parlor_chat_items where id in (${inList(ids)})`,\n z.object({ id: z.string(), thread_id: z.string() }),\n );\n return new Map(rows.map((row) => [row.id, row.thread_id]));\n}\n\nasync function itemsById(\n db: ChatDb,\n threadId: ThreadId,\n ids: readonly ItemId[],\n): Promise<LoggedItem[]> {\n if (ids.length === 0) return [];\n const rows = await query(\n db,\n sql`select ${ITEM_COLUMNS} from parlor_chat_items\n where thread_id = ${threadId} and id in (${inList(ids)}) order by seq`,\n itemRow,\n );\n return rows.map(toLoggedItem);\n}\n\nconst turnRow = z.object({\n id: idSchema(\"turn\"),\n thread_id: idSchema(\"thread\"),\n host_id: idSchema(\"host\"),\n agent_id: z.string(),\n state: turnStateSchema,\n lapsed: z.boolean(),\n failure: z.enum(TURN_FAILURES).nullable(),\n});\n\nasync function createTurn(\n tx: ChatDb,\n owner: ChatOwner,\n threadId: ThreadId,\n turn: NewTurn,\n promptItemId: ItemId,\n): Promise<void> {\n const [existing] = await query(\n tx,\n sql`select thread_id, owner_id, scope from parlor_chat_turns where id = ${turn.turnId}`,\n z.object({ thread_id: z.string(), owner_id: z.string(), scope: z.string() }),\n );\n if (existing) {\n const sameThread =\n existing.thread_id === threadId &&\n existing.owner_id === owner.userId &&\n existing.scope === owner.scope;\n if (!sameThread) throw turnMissing();\n return;\n }\n const [queued] = await query(\n tx,\n sql`select count(*) as waiting from parlor_chat_turns\n where thread_id = ${threadId} and state = 'queued'`,\n z.object({ waiting: count }),\n );\n if ((queued?.waiting ?? 0) >= turn.maxQueued) {\n throw new ActionError(\n \"invalid_input\",\n \"Too many prompts are waiting for the agent; wait for it to catch up.\",\n );\n }\n await tx.execute(\n sql`insert into parlor_chat_turns\n (id, thread_id, scope, owner_id, host_id, agent_id, client_id, device_id, prompt_item_id, state)\n values (${turn.turnId}, ${threadId}, ${owner.scope}, ${owner.userId}, ${turn.agent.hostId},\n ${turn.agent.agentId}, ${turn.clientId}, ${turn.deviceId ?? null}, ${promptItemId}, 'queued')`,\n );\n}\n\nasync function admitsUserItem(\n tx: ChatDb,\n threadId: ThreadId,\n turnId: TurnId,\n draft: UserDraft,\n): Promise<boolean> {\n if (draft.kind === \"prompt\") return true;\n if (draft.kind === \"answer\") {\n const askId = draft.body.askId;\n const [ask] = await query(\n tx,\n sql`select turn_id, body->>'approval' as approval from parlor_chat_items\n where thread_id = ${threadId} and id = ${askId} and kind = 'ask'`,\n z.object({ turn_id: z.string(), approval: z.string().nullable() }),\n );\n if (!ask || ask.turn_id !== turnId)\n throw new ActionError(\"not_found\", \"Question not found\");\n if (ask.approval === \"computer\") {\n throw new ActionError(\"forbidden\", \"This question is answered on the computer.\");\n }\n const { signed } = draft.body;\n if (ask.approval === \"presence\" && signed?.signature.presence !== \"user\") {\n throw new ActionError(\"forbidden\", \"This question needs a presence check.\");\n }\n if (ask.approval === \"device\" && !signed) {\n throw new ActionError(\n \"forbidden\",\n \"This question is answered on the device that asked it.\",\n );\n }\n const [state] = await query(\n tx,\n sql`select state from parlor_chat_turns where id = ${turnId} for update`,\n z.object({ state: z.string() }),\n );\n const [standing] = await query(\n tx,\n sql`select a.id from parlor_chat_items a\n where a.thread_id = ${threadId} and a.kind = 'answer' and a.body->>'askId' = ${askId}\n and not exists (\n select 1 from parlor_chat_items e\n where e.thread_id = ${threadId} and e.kind = 'event'\n and e.body->>'event' = 'answer_rejected' and e.body->>'itemId' = a.id)\n limit 1`,\n z.object({ id: z.string() }),\n );\n if (standing) {\n throw new ActionError(\"invalid_input\", \"This question was already answered.\");\n }\n if (state?.state !== \"running\") {\n throw new ActionError(\n \"invalid_input\",\n \"The agent is no longer waiting for this answer.\",\n );\n }\n return true;\n }\n const [turn] = await query(\n tx,\n sql`select state from parlor_chat_turns where id = ${turnId} and thread_id = ${threadId}`,\n z.object({ state: z.string() }),\n );\n if (!turn) throw turnMissing();\n if (TERMINAL_STATES.has(turn.state)) return false;\n if (turn.state === \"queued\") {\n await tx.execute(\n sql`update parlor_chat_turns\n set state = 'cancelled', outcome = '{\"state\":\"cancelled\"}'::jsonb, finished_at = now()\n where id = ${turnId} and state = 'queued'`,\n );\n }\n return true;\n}\n\nconst lostItemId = () => asId(\"item\", `lost.${randomUUID()}`);\n\nconst ownedBy = (owner: ChatOwner | undefined) =>\n owner ? sql`and owner_id = ${owner.userId} and scope = ${owner.scope}` : sql``;\n\nasync function lapsedFor(db: ChatDb, owner: ChatOwner | undefined): Promise<boolean> {\n const [found] = await query(\n db,\n sql`select (\n exists (select 1 from parlor_chat_turns\n where state = 'running' and lease_until < now() ${ownedBy(owner)})\n or exists (select 1 from parlor_chat_hosts\n where announced_online and online_until <= now() ${ownedBy(owner)})\n ) as lapsed`,\n z.object({ lapsed: z.boolean() }),\n );\n return found?.lapsed ?? false;\n}\n\nasync function expireLapsed(tx: ChatDb, owner: ChatOwner | undefined): Promise<Lapsed[]> {\n const changes = new Map<string, Lapsed & { threads: ThreadId[]; hosts: HostId[] }>();\n const changed = (scope: string, userId: UserId) => {\n const key = `${scope}\\u0000${userId}`;\n let change = changes.get(key);\n if (!change) changes.set(key, (change = { scope, userId, threads: [], hosts: [] }));\n return change;\n };\n const lapsed = await query(\n tx,\n sql`select distinct scope, owner_id, thread_id from parlor_chat_turns\n where state = 'running' and lease_until < now() ${ownedBy(owner)}\n order by thread_id`,\n z.object({\n scope: z.string(),\n owner_id: idSchema(\"user\"),\n thread_id: idSchema(\"thread\"),\n }),\n );\n for (const { scope, owner_id: userId, thread_id: threadId } of lapsed) {\n const { lastSeq } = await lockLog(tx, { scope, userId }, threadId);\n const expired = await query(\n tx,\n sql`update parlor_chat_turns\n set state = 'failed', outcome = '{\"state\":\"failed\",\"reason\":\"host_lost\"}'::jsonb,\n finished_at = now()\n where thread_id = ${threadId} and state = 'running' and lease_until < now()\n returning id`,\n z.object({ id: idSchema(\"turn\") }),\n );\n if (expired.length === 0) continue;\n await insertItems(\n tx,\n threadId,\n lastSeq,\n expired.map(({ id }) => ({\n id: lostItemId(),\n turnId: id,\n kind: \"event\",\n body: { event: \"host_lost\" } satisfies ChatItemBody<\"event\">,\n })),\n );\n changed(scope, userId).threads.push(threadId);\n }\n const offline = await query(\n tx,\n sql`update parlor_chat_hosts set announced_online = false\n where announced_online and online_until <= now() ${ownedBy(owner)}\n returning id, scope, owner_id`,\n z.object({ id: idSchema(\"host\"), scope: z.string(), owner_id: idSchema(\"user\") }),\n );\n for (const { id, scope, owner_id: userId } of offline) {\n changed(scope, userId).hosts.push(id);\n }\n return [...changes.values()];\n}\n\nasync function lockRunningTurn(\n tx: ChatDb,\n owner: ChatOwner,\n host: HostPrincipal,\n turnId: TurnId,\n): Promise<{\n readonly threadId: ThreadId;\n readonly lastSeq: number;\n readonly clientId: string;\n readonly agentId: string;\n}> {\n const [found] = await query(\n tx,\n sql`select thread_id, client_id, agent_id from parlor_chat_turns\n where id = ${turnId} and host_id = ${host.hostId}\n and owner_id = ${owner.userId} and scope = ${owner.scope}`,\n z.object({\n thread_id: idSchema(\"thread\"),\n client_id: z.string(),\n agent_id: z.string(),\n }),\n );\n if (!found) throw turnMissing();\n const { lastSeq, leased } = await lockLog(tx, owner, found.thread_id, turnId);\n if (!leased) throw new ActionError(\"forbidden\", \"The turn is not running.\");\n return {\n threadId: found.thread_id,\n lastSeq,\n clientId: found.client_id,\n agentId: found.agent_id,\n };\n}\n\nconst leaseSql = (leaseMs: number) =>\n sql`now() + ${leaseMs}::float8 * interval '1 millisecond'`;\n\nasync function requireThread(db: ChatDb, owner: ChatOwner, threadId: ThreadId) {\n const [thread] = await query(\n db,\n sql`select id from parlor_chat_threads\n where id = ${threadId} and owner_id = ${owner.userId} and scope = ${owner.scope}`,\n z.object({ id: z.string() }),\n );\n if (!thread) throw threadMissing();\n}\n\n/** The turns that `items`, a run of consecutive seqs, belong to. */\nasync function turnsOf(\n db: ChatDb,\n threadId: ThreadId,\n items: readonly LoggedItem[],\n): Promise<TurnStatus[]> {\n const first = items[0];\n const last = items.at(-1);\n if (!first || !last) return [];\n const turns = await query(\n db,\n sql`select id, thread_id, host_id, agent_id, state,\n coalesce(state = 'running' and lease_until < now(), false) as lapsed,\n case when state = 'failed' then outcome->>'reason' end as failure\n from parlor_chat_turns\n where thread_id = ${threadId}\n and id in (select turn_id from parlor_chat_items\n where thread_id = ${threadId} and seq between ${first.seq} and ${last.seq})\n order by created_at, id`,\n turnRow,\n );\n return turns.map((turn) => ({\n turnId: turn.id,\n state: turn.lapsed ? \"failed\" : turn.state,\n agent: { hostId: turn.host_id, agentId: turn.agent_id },\n failure: turn.lapsed ? \"host_lost\" : (turn.failure ?? undefined),\n }));\n}\n\nexport const sqlChatStore: ChatStore = {\n async listThreads(db, owner) {\n const rows = await query(\n db,\n sql`select ${THREAD_COLUMNS} from parlor_chat_threads\n where owner_id = ${owner.userId} and scope = ${owner.scope}\n order by updated_at desc, id limit ${THREAD_LIST_LIMIT}`,\n threadRow,\n );\n return rows.map(toThreadSummary);\n },\n\n async readPage(db, owner, threadId, limit) {\n const [row] = await query(\n db,\n threadId\n ? sql`select ${THREAD_COLUMNS} from parlor_chat_threads\n where id = ${threadId} and owner_id = ${owner.userId} and scope = ${owner.scope}`\n : sql`select ${THREAD_COLUMNS} from parlor_chat_threads\n where owner_id = ${owner.userId} and scope = ${owner.scope}\n order by updated_at desc, id limit 1`,\n threadRow,\n );\n if (!row) return { thread: undefined, items: [], turns: [], hasMore: false };\n const rows = await query(\n db,\n sql`select ${ITEM_COLUMNS} from parlor_chat_items\n where thread_id = ${row.id} order by seq desc limit ${limit + 1}`,\n itemRow,\n );\n const hasMore = rows.length > limit;\n const items = rows.slice(0, limit).reverse().map(toLoggedItem);\n return {\n thread: toThreadSummary(row),\n items,\n turns: await turnsOf(db, row.id, items),\n hasMore,\n };\n },\n\n async readItemsBefore(db, owner, threadId, beforeSeq, limit) {\n const rows = await query(\n db,\n sql`select ${ITEM_COLUMNS_OF_I} from parlor_chat_items i\n join parlor_chat_threads t on t.id = i.thread_id\n where t.id = ${threadId} and t.owner_id = ${owner.userId} and t.scope = ${owner.scope}\n and i.seq < ${beforeSeq}\n order by i.seq desc limit ${limit + 1}`,\n itemRow,\n );\n if (rows.length === 0) await requireThread(db, owner, threadId);\n const items = rows.slice(0, limit).reverse().map(toLoggedItem);\n return {\n items,\n turns: await turnsOf(db, threadId, items),\n hasMore: rows.length > limit,\n };\n },\n\n async listThreadsBefore(db, owner, before, limit) {\n const updated = ms(\"updated_at\");\n const rows = await query(\n db,\n sql`select ${THREAD_COLUMNS} from parlor_chat_threads\n where owner_id = ${owner.userId} and scope = ${owner.scope}\n and (${updated} < ${before.updatedAt}\n or (${updated} = ${before.updatedAt} and id < ${before.id}))\n order by updated_at desc, id desc limit ${limit + 1}`,\n threadRow,\n );\n return {\n threads: rows.slice(0, limit).map(toThreadSummary),\n hasMore: rows.length > limit,\n };\n },\n\n async readItemsAfter(db, owner, threadId, afterSeq) {\n const rows = await query(\n db,\n sql`select ${ITEM_COLUMNS_OF_I} from parlor_chat_items i\n join parlor_chat_threads t on t.id = i.thread_id\n where t.id = ${threadId} and t.owner_id = ${owner.userId} and t.scope = ${owner.scope}\n and i.seq > ${afterSeq}\n order by i.seq`,\n itemRow,\n );\n if (rows.length === 0) await requireThread(db, owner, threadId);\n return rows.map(toLoggedItem);\n },\n\n async appendUserItems(tx, owner, input) {\n const { threadId, turnId, items, turn } = input;\n if (turn) {\n const prompt = items.find((draft) => draft.kind === \"prompt\");\n await tx.execute(\n sql`insert into parlor_chat_threads (id, scope, owner_id, title, host_id, agent_id)\n values (${threadId}, ${owner.scope}, ${owner.userId},\n ${prompt?.kind === \"prompt\" ? titleOf(prompt.body.text) : \"\"},\n ${turn.agent.hostId}, ${turn.agent.agentId})\n on conflict (id) do nothing`,\n );\n }\n const { lastSeq } = await lockLog(tx, owner, threadId);\n if (turn) {\n const prompt = items.find((draft) => draft.kind === \"prompt\");\n if (!prompt) throw new ActionError(\"invalid_input\", \"A turn starts with a prompt.\");\n await createTurn(tx, owner, threadId, turn, prompt.id);\n }\n const known = await itemThreads(\n tx,\n items.map((draft) => draft.id),\n );\n const fresh: UserDraft[] = [];\n for (const draft of items) {\n const where = known.get(draft.id);\n if (where === undefined) {\n if (await admitsUserItem(tx, threadId, turnId, draft)) fresh.push(draft);\n } else if (where !== threadId) {\n throw threadMissing();\n }\n }\n await insertItems(\n tx,\n threadId,\n lastSeq,\n fresh.map((draft) => ({\n id: draft.id,\n turnId,\n kind: draft.kind,\n body: draft.body,\n })),\n );\n if (turn) {\n await tx.execute(\n sql`update parlor_chat_threads set host_id = ${turn.agent.hostId}, agent_id = ${turn.agent.agentId}\n where id = ${threadId}`,\n );\n }\n return itemsById(\n tx,\n threadId,\n items.map((draft) => draft.id),\n );\n },\n\n async hasLapsed(db, owner) {\n return lapsedFor(db, owner);\n },\n\n expireLapsed,\n\n async claimTurn(tx, owner, host, leaseMs) {\n const [claimed] = await query(\n tx,\n sql`select t.id, t.thread_id, t.agent_id, t.client_id, t.prompt_item_id\n from parlor_chat_turns t\n where t.host_id = ${host.hostId} and t.owner_id = ${owner.userId}\n and t.scope = ${owner.scope} and t.state = 'queued'\n and not exists (\n select 1 from parlor_chat_turns earlier\n where earlier.thread_id = t.thread_id and earlier.state in ('queued', 'running')\n and (earlier.created_at, earlier.id) < (t.created_at, t.id))\n order by t.created_at, t.id\n limit 1\n for update of t skip locked`,\n z.object({\n id: idSchema(\"turn\"),\n thread_id: idSchema(\"thread\"),\n agent_id: z.string(),\n client_id: z.string(),\n prompt_item_id: idSchema(\"item\"),\n }),\n );\n if (!claimed) return undefined;\n const [lease] = await query(\n tx,\n sql`update parlor_chat_turns set state = 'running', lease_until = ${leaseSql(leaseMs)}\n where id = ${claimed.id}\n returning ${ms(\"lease_until\")} as lease_until`,\n z.object({ lease_until: count }),\n );\n const [prompt] = await itemsById(tx, claimed.thread_id, [claimed.prompt_item_id]);\n if (!lease || prompt?.kind !== \"prompt\") {\n throw new ActionError(\"internal\", \"The claimed turn lost its prompt.\");\n }\n return {\n id: claimed.thread_id,\n turnId: claimed.id,\n agent: { hostId: host.hostId, agentId: claimed.agent_id },\n clientId: claimed.client_id,\n prompt,\n leaseUntil: lease.lease_until,\n };\n },\n\n async releaseClaim(tx, owner, host, turnId) {\n const released = await query(\n tx,\n sql`update parlor_chat_turns set state = 'queued', lease_until = null\n where id = ${turnId} and host_id = ${host.hostId} and owner_id = ${owner.userId}\n and scope = ${owner.scope} and state = 'running' and lease_until >= now()\n and not exists (\n select 1 from parlor_chat_items i\n where i.turn_id = parlor_chat_turns.id and i.author <> 'user')\n returning thread_id`,\n z.object({ thread_id: idSchema(\"thread\") }),\n );\n return released.length > 0;\n },\n\n async appendHostItems(tx, owner, host, turnId, items, leaseMs) {\n const { threadId, lastSeq, clientId, agentId } = await lockRunningTurn(\n tx,\n owner,\n host,\n turnId,\n );\n const known = await itemThreads(\n tx,\n items.map((draft) => draft.id),\n );\n const fresh: HostItemDraft[] = [];\n for (const draft of items) {\n const where = known.get(draft.id);\n if (where === undefined) fresh.push(draft);\n else if (where !== threadId)\n throw new ActionError(\"forbidden\", \"That item id is taken.\");\n }\n await insertItems(\n tx,\n threadId,\n lastSeq,\n fresh.map((draft) => ({\n id: draft.id,\n turnId,\n kind: draft.kind,\n body: draft.body,\n })),\n );\n const [lease] = await query(\n tx,\n sql`update parlor_chat_turns set lease_until = ${leaseSql(leaseMs)}\n where id = ${turnId} returning ${ms(\"lease_until\")} as lease_until`,\n z.object({ lease_until: count }),\n );\n return { threadId, leaseUntil: lease?.lease_until ?? 0, clientId, agentId };\n },\n\n async finishTurn(tx, owner, host, turnId, outcome) {\n const { threadId } = await lockRunningTurn(tx, owner, host, turnId);\n await tx.execute(\n sql`update parlor_chat_turns\n set state = ${outcome.state}, outcome = ${JSON.stringify(outcome)}::jsonb, finished_at = now()\n where id = ${turnId}`,\n );\n await tx.execute(\n sql`update parlor_chat_threads set updated_at = now() where id = ${threadId}`,\n );\n return { threadId };\n },\n\n async readSignals(db, owner, host, turns) {\n if (turns.length === 0) return [];\n const wanted = turns.map(\n ({ turnId, afterSeq }) => sql`(${turnId}::text, ${afterSeq}::bigint)`,\n );\n const rows = await query(\n db,\n sql`select ${ITEM_COLUMNS_OF_I}\n from parlor_chat_items i\n join parlor_chat_turns t on t.id = i.turn_id and t.thread_id = i.thread_id\n join (values ${sql.join(wanted, sql`, `)}) as q(turn_id, after_seq)\n on q.turn_id = i.turn_id\n where t.host_id = ${host.hostId} and t.owner_id = ${owner.userId}\n and t.scope = ${owner.scope} and i.kind in ('answer', 'cancel')\n and i.seq > q.after_seq\n order by i.turn_id, i.seq`,\n itemRow,\n );\n const byTurn = new Map<TurnId, LoggedItem<\"answer\" | \"cancel\">[]>();\n for (const item of rows.map(toLoggedItem)) {\n if (item.kind !== \"answer\" && item.kind !== \"cancel\") continue;\n const list = byTurn.get(item.turnId) ?? [];\n list.push(item);\n byTurn.set(item.turnId, list);\n }\n return [...byTurn].map(([turnId, items]) => ({ turnId, items }));\n },\n\n async reportHost(tx, owner, host, report) {\n const ttlMs = report.online ? report.ttlMs : 0;\n const [row] = await query(\n tx,\n sql`insert into parlor_chat_hosts\n (id, scope, owner_id, grant_id, protocol, name, platform, agents, devices,\n presence_due, agents_checked_at, online_until, announced_online)\n values (${host.hostId}, ${owner.scope}, ${owner.userId}, ${host.grantId},\n ${report.protocol}, ${report.name}, ${report.platform},\n ${JSON.stringify(report.agents)}::jsonb,\n ${report.devices ? JSON.stringify(report.devices) : null}::jsonb,\n ${report.presenceDue ? JSON.stringify(report.presenceDue) : null}::jsonb,\n ${report.agentsCheckedAt === undefined ? null : new Date(report.agentsCheckedAt).toISOString()}::timestamptz,\n ${leaseSql(ttlMs)}, ${report.online})\n on conflict (id) do update set\n grant_id = excluded.grant_id, protocol = excluded.protocol, name = excluded.name,\n platform = excluded.platform, agents = excluded.agents,\n devices = coalesce(excluded.devices, parlor_chat_hosts.devices),\n presence_due = coalesce(excluded.presence_due, parlor_chat_hosts.presence_due),\n agents_checked_at = coalesce(excluded.agents_checked_at, parlor_chat_hosts.agents_checked_at),\n online_until = excluded.online_until,\n announced_online = excluded.announced_online\n where parlor_chat_hosts.owner_id = excluded.owner_id\n and parlor_chat_hosts.scope = excluded.scope\n returning ${ms(\"online_until\")} as online_until`,\n z.object({ online_until: count }),\n );\n if (!row)\n throw new ActionError(\"forbidden\", \"This computer belongs to another account.\");\n return { onlineUntil: row.online_until };\n },\n\n async heartbeat(db, owner, host, ttlMs) {\n const [beat] = await query(\n db,\n sql`update parlor_chat_hosts as h\n set online_until = ${leaseSql(ttlMs)}, announced_online = true\n from (select announced_online as was from parlor_chat_hosts where id = ${host.hostId}) as prev\n where h.id = ${host.hostId} and h.owner_id = ${owner.userId}\n and h.scope = ${owner.scope} and h.grant_id = ${host.grantId}\n returning prev.was as was`,\n z.object({ was: z.boolean() }),\n );\n return { returned: beat !== undefined && !beat.was };\n },\n\n async listHosts(db, owner) {\n return query(\n db,\n sql`select id, grant_id, protocol, name, platform, agents, devices, presence_due,\n ${ms(\"agents_checked_at\")} as agents_checked_at, online_until > now() as online\n from parlor_chat_hosts\n where owner_id = ${owner.userId} and scope = ${owner.scope}\n order by name, id`,\n z\n .object({\n id: idSchema(\"host\"),\n grant_id: idSchema(\"grant\"),\n protocol: z.number().int(),\n name: z.string(),\n platform: z.string(),\n agents: z.array(agentInfoSchema),\n devices: z.array(z.string()).nullable(),\n presence_due: z.record(z.string(), z.number()).nullable(),\n agents_checked_at: count.nullable(),\n online: z.boolean(),\n })\n .transform(({ grant_id, devices, presence_due, agents_checked_at, ...host }) => ({\n ...host,\n ...(devices ? { devices } : {}),\n ...(presence_due ? { presenceDue: presence_due } : {}),\n ...(agents_checked_at === null ? {} : { agentsCheckedAt: agents_checked_at }),\n grantId: grant_id,\n })),\n );\n },\n\n async pruneHosts(tx, owner, hostIds) {\n if (hostIds.length === 0) return;\n await tx.execute(\n sql`delete from parlor_chat_hosts\n where owner_id = ${owner.userId} and scope = ${owner.scope} and id in (${inList(hostIds)})`,\n );\n },\n\n async turnHolder(db, turnId) {\n const [row] = await query(\n db,\n sql`select scope, owner_id, thread_id, host_id,\n coalesce(state = 'running' and lease_until >= now(), false) as live\n from parlor_chat_turns where id = ${turnId}`,\n z.object({\n scope: z.string(),\n owner_id: idSchema(\"user\"),\n thread_id: idSchema(\"thread\"),\n host_id: idSchema(\"host\"),\n live: z.boolean(),\n }),\n );\n return row\n ? {\n owner: { scope: row.scope, userId: row.owner_id },\n threadId: row.thread_id,\n hostId: row.host_id,\n live: row.live,\n }\n : undefined;\n },\n};\n"]}
package/dist/tags.d.ts CHANGED
@@ -4,4 +4,6 @@ export declare const CHAT_TAG = "parlor_chat";
4
4
  export declare const HOSTS_TAG = "parlor_chat_hosts";
5
5
  /** Emitted when a signed-in tab is in use, carrying no thread, item or text; it wakes no host's claim loop. */
6
6
  export declare const CHAT_ACTIVITY_TAG = "parlor_chat_activity";
7
+ /** Emitted when a tab asks the user's computers to look again for the agents installed on them. */
8
+ export declare const CHAT_AGENTS_CHECK_TAG = "parlor_chat_agents_check";
7
9
  //# sourceMappingURL=tags.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tags.d.ts","sourceRoot":"","sources":["../src/tags.ts"],"names":[],"mappings":"AAAA,4GAA4G;AAC5G,eAAO,MAAM,QAAQ,gBAAgB,CAAC;AACtC,iEAAiE;AACjE,eAAO,MAAM,SAAS,sBAAsB,CAAC;AAC7C,+GAA+G;AAC/G,eAAO,MAAM,iBAAiB,yBAAyB,CAAC"}
1
+ {"version":3,"file":"tags.d.ts","sourceRoot":"","sources":["../src/tags.ts"],"names":[],"mappings":"AAAA,4GAA4G;AAC5G,eAAO,MAAM,QAAQ,gBAAgB,CAAC;AACtC,iEAAiE;AACjE,eAAO,MAAM,SAAS,sBAAsB,CAAC;AAC7C,+GAA+G;AAC/G,eAAO,MAAM,iBAAiB,yBAAyB,CAAC;AACxD,mGAAmG;AACnG,eAAO,MAAM,qBAAqB,6BAA6B,CAAC"}
package/dist/tags.js CHANGED
@@ -4,4 +4,6 @@ export const CHAT_TAG = "parlor_chat";
4
4
  export const HOSTS_TAG = "parlor_chat_hosts";
5
5
  /** Emitted when a signed-in tab is in use, carrying no thread, item or text; it wakes no host's claim loop. */
6
6
  export const CHAT_ACTIVITY_TAG = "parlor_chat_activity";
7
+ /** Emitted when a tab asks the user's computers to look again for the agents installed on them. */
8
+ export const CHAT_AGENTS_CHECK_TAG = "parlor_chat_agents_check";
7
9
  //# sourceMappingURL=tags.js.map
package/dist/tags.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"tags.js","sourceRoot":"","sources":["../src/tags.ts"],"names":[],"mappings":"AAAA,4GAA4G;AAC5G,MAAM,CAAC,MAAM,QAAQ,GAAG,aAAa,CAAC;AACtC,iEAAiE;AACjE,MAAM,CAAC,MAAM,SAAS,GAAG,mBAAmB,CAAC;AAC7C,+GAA+G;AAC/G,MAAM,CAAC,MAAM,iBAAiB,GAAG,sBAAsB,CAAC","sourcesContent":["/** Every chat write emits `[\"parlor_chat\", threadId]`, the one event that wakes hosts and other devices. */\nexport const CHAT_TAG = \"parlor_chat\";\n/** Emitted when a computer comes, goes or changes its agents. */\nexport const HOSTS_TAG = \"parlor_chat_hosts\";\n/** Emitted when a signed-in tab is in use, carrying no thread, item or text; it wakes no host's claim loop. */\nexport const CHAT_ACTIVITY_TAG = \"parlor_chat_activity\";\n"]}
1
+ {"version":3,"file":"tags.js","sourceRoot":"","sources":["../src/tags.ts"],"names":[],"mappings":"AAAA,4GAA4G;AAC5G,MAAM,CAAC,MAAM,QAAQ,GAAG,aAAa,CAAC;AACtC,iEAAiE;AACjE,MAAM,CAAC,MAAM,SAAS,GAAG,mBAAmB,CAAC;AAC7C,+GAA+G;AAC/G,MAAM,CAAC,MAAM,iBAAiB,GAAG,sBAAsB,CAAC;AACxD,mGAAmG;AACnG,MAAM,CAAC,MAAM,qBAAqB,GAAG,0BAA0B,CAAC","sourcesContent":["/** Every chat write emits `[\"parlor_chat\", threadId]`, the one event that wakes hosts and other devices. */\nexport const CHAT_TAG = \"parlor_chat\";\n/** Emitted when a computer comes, goes or changes its agents. */\nexport const HOSTS_TAG = \"parlor_chat_hosts\";\n/** Emitted when a signed-in tab is in use, carrying no thread, item or text; it wakes no host's claim loop. */\nexport const CHAT_ACTIVITY_TAG = \"parlor_chat_activity\";\n/** Emitted when a tab asks the user's computers to look again for the agents installed on them. */\nexport const CHAT_AGENTS_CHECK_TAG = \"parlor_chat_agents_check\";\n"]}
package/dist/view.d.ts CHANGED
@@ -20,6 +20,8 @@ export type ChatItem<C = unknown> = {
20
20
  readonly state: TurnState;
21
21
  readonly failure: TurnFailure | undefined;
22
22
  readonly mine: boolean;
23
+ /** This device's prompt on its way, shown before the server stored it. */
24
+ readonly sending?: boolean | undefined;
23
25
  } | {
24
26
  readonly kind: "reply" | "reasoning";
25
27
  readonly id: ItemId;
@@ -1 +1 @@
1
- {"version":3,"file":"view.d.ts","sourceRoot":"","sources":["../src/view.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"view.d.ts","sourceRoot":"","sources":["../src/view.ts"],"names":[],"mappings":"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"}
package/dist/view.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"view.js","sourceRoot":"","sources":["../src/view.ts"],"names":[],"mappings":"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","sourcesContent":["import { asId, type DeviceId, type ItemId, type TurnId } from \"@parlor/core\";\n\nimport type { ChatEvent, FoldedEvent } from \"./events.js\";\nimport type { ChatItemBody, ChatItemKind, LoggedItem } from \"./items.js\";\nimport type {\n AgentRef,\n OpenItem,\n OpenTurn,\n TurnFailure,\n TurnState,\n TurnStatus,\n} from \"./model.js\";\n\nexport type AskState =\n | { readonly state: \"open\" }\n | { readonly state: \"answered\"; readonly choice: string }\n | { readonly state: \"closed\" };\n\n/** What a chat renders. `confirm` is the app's own `confirm: true` card, placed in its turn. */\nexport type ChatItem<C = unknown> =\n | {\n readonly kind: \"prompt\";\n readonly id: ItemId;\n readonly turnId: TurnId;\n readonly text: string;\n readonly agent: AgentRef;\n readonly state: TurnState;\n readonly failure: TurnFailure | undefined;\n readonly mine: boolean;\n }\n | {\n readonly kind: \"reply\" | \"reasoning\";\n readonly id: ItemId;\n readonly turnId: TurnId;\n readonly text: string;\n readonly streaming: boolean;\n readonly interrupted: boolean;\n }\n | {\n readonly kind: \"tool\";\n readonly id: ItemId;\n readonly turnId: TurnId;\n readonly title: string;\n readonly tool: ChatItemBody<\"tool\">[\"tool\"] | undefined;\n readonly status: \"pending\" | \"running\" | ChatItemBody<\"tool\">[\"status\"];\n readonly detail: string | undefined;\n }\n | {\n readonly kind: \"plan\";\n readonly id: ItemId;\n readonly turnId: TurnId;\n readonly entries: ChatItemBody<\"plan\">[\"entries\"];\n readonly streaming: boolean;\n }\n | {\n readonly kind: \"ask\";\n readonly id: ItemId;\n readonly turnId: TurnId;\n readonly question: string;\n readonly choices: ChatItemBody<\"ask\">[\"choices\"];\n readonly form: ChatItemBody<\"ask\">[\"form\"];\n readonly approval: ChatItemBody<\"ask\">[\"approval\"];\n readonly answer: AskState;\n }\n | {\n readonly kind: \"confirm\";\n readonly id: ItemId;\n readonly turnId: TurnId;\n readonly confirmation: C;\n }\n | {\n readonly kind: \"event\";\n readonly id: ItemId;\n readonly turnId: TurnId;\n readonly event: Exclude<ChatEvent, FoldedEvent>;\n readonly detail: string | undefined;\n };\n\n/** Log kinds that settle another item instead of rendering on their own. */\nexport type FoldedKind = Extract<ChatItemKind, \"answer\" | \"cancel\">;\n\nexport interface ThreadConfirmation {\n readonly id: string;\n readonly threadId?: string | undefined;\n}\n\nexport interface FoldInput<C extends ThreadConfirmation> {\n readonly items: readonly LoggedItem[];\n readonly open: readonly OpenTurn[];\n readonly turns: readonly TurnStatus[];\n readonly confirmations: readonly C[];\n readonly deviceId: DeviceId | undefined;\n}\n\n/** How each log kind enters the fold; keyed by every kind, so a new kind fails to build here. */\nexport type ItemFolders = {\n readonly [K in ChatItemKind]: (item: LoggedItem<K>) => void;\n};\n\nconst TERMINAL: ReadonlySet<TurnState> = new Set([\"done\", \"failed\", \"cancelled\"]);\n\n/**\n * Pure and O(items + open): the same input gives the same list with stable ids, so a\n * durable item replacing its open twin keeps its React row.\n */\nexport function foldThread<C extends ThreadConfirmation>(\n input: FoldInput<C>,\n): readonly ChatItem<C>[] {\n const states = new Map<TurnId, TurnState>();\n for (const turn of input.turns) states.set(turn.turnId, turn.state);\n const stateOf = (turnId: TurnId): TurnState => states.get(turnId) ?? \"done\";\n const failures = new Map<TurnId, TurnFailure>();\n for (const turn of input.turns) {\n if (turn.failure) failures.set(turn.turnId, turn.failure);\n }\n const rejected = new Set<ItemId>();\n for (const item of input.items) {\n if (\n item.kind === \"event\" &&\n item.body.event === \"answer_rejected\" &&\n item.body.itemId\n ) {\n rejected.add(item.body.itemId);\n }\n }\n\n const rows: ChatItem<C>[] = [];\n const durable = new Set<ItemId>();\n const asks = new Map<ItemId, number>();\n const prompts = new Map<TurnId, number>();\n const cancelled = new Set<TurnId>();\n\n const push = (row: ChatItem<C>) => {\n rows.push(row);\n return rows.length - 1;\n };\n\n const folders: ItemFolders = {\n prompt: (item) => {\n prompts.set(\n item.turnId,\n push({\n kind: \"prompt\",\n id: item.id,\n turnId: item.turnId,\n text: item.body.text,\n agent: item.body.agent,\n state: stateOf(item.turnId),\n failure: failures.get(item.turnId),\n mine:\n input.deviceId !== undefined && item.body.signed?.deviceId === input.deviceId,\n }),\n );\n },\n answer: (item) => {\n const at = asks.get(item.body.askId);\n if (at === undefined || rejected.has(item.id)) return;\n const row = rows[at];\n if (row?.kind === \"ask\" && row.answer.state === \"open\") {\n rows[at] = {\n ...row,\n answer: { state: \"answered\", choice: item.body.choice },\n };\n }\n },\n cancel: (item) => {\n cancelled.add(item.turnId);\n },\n reply: (item) => {\n push({\n kind: \"reply\",\n id: item.id,\n turnId: item.turnId,\n text: item.body.text,\n streaming: false,\n interrupted: item.body.interrupted === true,\n });\n },\n reasoning: (item) => {\n push({\n kind: \"reasoning\",\n id: item.id,\n turnId: item.turnId,\n text: item.body.text,\n streaming: false,\n interrupted: false,\n });\n },\n tool: (item) => {\n push({\n kind: \"tool\",\n id: item.id,\n turnId: item.turnId,\n title: item.body.title,\n tool: item.body.tool,\n status: item.body.status,\n detail: item.body.detail,\n });\n },\n plan: (item) => {\n push({\n kind: \"plan\",\n id: item.id,\n turnId: item.turnId,\n entries: item.body.entries,\n streaming: false,\n });\n },\n ask: (item) => {\n asks.set(\n item.id,\n push({\n kind: \"ask\",\n id: item.id,\n turnId: item.turnId,\n question: item.body.question,\n choices: item.body.choices,\n form: item.body.form,\n approval: item.body.approval,\n answer: { state: \"open\" },\n }),\n );\n },\n event: (item) => {\n const { event, itemId, choice } = item.body;\n if (event === \"answered_on_computer\") {\n const at = itemId === undefined ? undefined : asks.get(itemId);\n const row = at === undefined ? undefined : rows[at];\n if (\n at !== undefined &&\n choice !== undefined &&\n row?.kind === \"ask\" &&\n row.answer.state === \"open\"\n ) {\n rows[at] = { ...row, answer: { state: \"answered\", choice } };\n }\n return;\n }\n push({\n kind: \"event\",\n id: item.id,\n turnId: item.turnId,\n event,\n detail: item.body.detail,\n });\n },\n };\n\n for (const item of input.items) {\n durable.add(item.id);\n fold(folders, item);\n }\n\n for (const [turnId, at] of prompts) {\n const row = rows[at];\n if (row?.kind === \"prompt\" && cancelled.has(turnId) && !TERMINAL.has(row.state)) {\n rows[at] = { ...row, state: \"cancelled\" };\n }\n }\n for (const at of asks.values()) {\n const row = rows[at];\n if (row?.kind === \"ask\" && row.answer.state === \"open\") {\n if (TERMINAL.has(stateOf(row.turnId)) || cancelled.has(row.turnId)) {\n rows[at] = { ...row, answer: { state: \"closed\" } };\n }\n }\n }\n\n for (const turn of input.open) {\n if (TERMINAL.has(stateOf(turn.turnId))) continue;\n for (const open of turn.items) {\n if (durable.has(open.id)) continue;\n rows.push(openRow(open, turn.turnId));\n }\n }\n\n const threadId = input.items[0]?.threadId ?? input.open[0]?.threadId;\n if (threadId !== undefined) {\n const running = input.turns.find((turn) => turn.state === \"running\")?.turnId;\n const turnId = running ?? input.items.at(-1)?.turnId ?? input.open[0]?.turnId;\n if (turnId !== undefined) {\n for (const confirmation of input.confirmations) {\n if (confirmation.threadId !== threadId) continue;\n rows.push({\n kind: \"confirm\",\n id: asId(\"item\", confirmation.id),\n turnId,\n confirmation,\n });\n }\n }\n }\n return rows;\n}\n\nfunction fold(folders: ItemFolders, item: LoggedItem): void {\n switch (item.kind) {\n case \"prompt\":\n return folders.prompt(item);\n case \"answer\":\n return folders.answer(item);\n case \"cancel\":\n return folders.cancel(item);\n case \"reply\":\n return folders.reply(item);\n case \"reasoning\":\n return folders.reasoning(item);\n case \"tool\":\n return folders.tool(item);\n case \"plan\":\n return folders.plan(item);\n case \"ask\":\n return folders.ask(item);\n case \"event\":\n return folders.event(item);\n }\n}\n\nfunction openRow<C>(open: OpenItem, turnId: TurnId): ChatItem<C> {\n switch (open.kind) {\n case \"reply\":\n case \"reasoning\":\n return {\n kind: open.kind,\n id: open.id,\n turnId,\n text: open.text,\n streaming: true,\n interrupted: false,\n };\n case \"tool\":\n return {\n kind: \"tool\",\n id: open.id,\n turnId,\n title: open.title,\n tool: undefined,\n status: open.status,\n detail: undefined,\n };\n case \"plan\":\n return {\n kind: \"plan\",\n id: open.id,\n turnId,\n entries: open.entries,\n streaming: true,\n };\n }\n}\n"]}
1
+ {"version":3,"file":"view.js","sourceRoot":"","sources":["../src/view.ts"],"names":[],"mappings":"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","sourcesContent":["import { asId, type DeviceId, type ItemId, type TurnId } from \"@parlor/core\";\n\nimport type { ChatEvent, FoldedEvent } from \"./events.js\";\nimport type { ChatItemBody, ChatItemKind, LoggedItem } from \"./items.js\";\nimport type {\n AgentRef,\n OpenItem,\n OpenTurn,\n TurnFailure,\n TurnState,\n TurnStatus,\n} from \"./model.js\";\n\nexport type AskState =\n | { readonly state: \"open\" }\n | { readonly state: \"answered\"; readonly choice: string }\n | { readonly state: \"closed\" };\n\n/** What a chat renders. `confirm` is the app's own `confirm: true` card, placed in its turn. */\nexport type ChatItem<C = unknown> =\n | {\n readonly kind: \"prompt\";\n readonly id: ItemId;\n readonly turnId: TurnId;\n readonly text: string;\n readonly agent: AgentRef;\n readonly state: TurnState;\n readonly failure: TurnFailure | undefined;\n readonly mine: boolean;\n /** This device's prompt on its way, shown before the server stored it. */\n readonly sending?: boolean | undefined;\n }\n | {\n readonly kind: \"reply\" | \"reasoning\";\n readonly id: ItemId;\n readonly turnId: TurnId;\n readonly text: string;\n readonly streaming: boolean;\n readonly interrupted: boolean;\n }\n | {\n readonly kind: \"tool\";\n readonly id: ItemId;\n readonly turnId: TurnId;\n readonly title: string;\n readonly tool: ChatItemBody<\"tool\">[\"tool\"] | undefined;\n readonly status: \"pending\" | \"running\" | ChatItemBody<\"tool\">[\"status\"];\n readonly detail: string | undefined;\n }\n | {\n readonly kind: \"plan\";\n readonly id: ItemId;\n readonly turnId: TurnId;\n readonly entries: ChatItemBody<\"plan\">[\"entries\"];\n readonly streaming: boolean;\n }\n | {\n readonly kind: \"ask\";\n readonly id: ItemId;\n readonly turnId: TurnId;\n readonly question: string;\n readonly choices: ChatItemBody<\"ask\">[\"choices\"];\n readonly form: ChatItemBody<\"ask\">[\"form\"];\n readonly approval: ChatItemBody<\"ask\">[\"approval\"];\n readonly answer: AskState;\n }\n | {\n readonly kind: \"confirm\";\n readonly id: ItemId;\n readonly turnId: TurnId;\n readonly confirmation: C;\n }\n | {\n readonly kind: \"event\";\n readonly id: ItemId;\n readonly turnId: TurnId;\n readonly event: Exclude<ChatEvent, FoldedEvent>;\n readonly detail: string | undefined;\n };\n\n/** Log kinds that settle another item instead of rendering on their own. */\nexport type FoldedKind = Extract<ChatItemKind, \"answer\" | \"cancel\">;\n\nexport interface ThreadConfirmation {\n readonly id: string;\n readonly threadId?: string | undefined;\n}\n\nexport interface FoldInput<C extends ThreadConfirmation> {\n readonly items: readonly LoggedItem[];\n readonly open: readonly OpenTurn[];\n readonly turns: readonly TurnStatus[];\n readonly confirmations: readonly C[];\n readonly deviceId: DeviceId | undefined;\n}\n\n/** How each log kind enters the fold; keyed by every kind, so a new kind fails to build here. */\nexport type ItemFolders = {\n readonly [K in ChatItemKind]: (item: LoggedItem<K>) => void;\n};\n\nconst TERMINAL: ReadonlySet<TurnState> = new Set([\"done\", \"failed\", \"cancelled\"]);\n\n/**\n * Pure and O(items + open): the same input gives the same list with stable ids, so a\n * durable item replacing its open twin keeps its React row.\n */\nexport function foldThread<C extends ThreadConfirmation>(\n input: FoldInput<C>,\n): readonly ChatItem<C>[] {\n const states = new Map<TurnId, TurnState>();\n for (const turn of input.turns) states.set(turn.turnId, turn.state);\n const stateOf = (turnId: TurnId): TurnState => states.get(turnId) ?? \"done\";\n const failures = new Map<TurnId, TurnFailure>();\n for (const turn of input.turns) {\n if (turn.failure) failures.set(turn.turnId, turn.failure);\n }\n const rejected = new Set<ItemId>();\n for (const item of input.items) {\n if (\n item.kind === \"event\" &&\n item.body.event === \"answer_rejected\" &&\n item.body.itemId\n ) {\n rejected.add(item.body.itemId);\n }\n }\n\n const rows: ChatItem<C>[] = [];\n const durable = new Set<ItemId>();\n const asks = new Map<ItemId, number>();\n const prompts = new Map<TurnId, number>();\n const cancelled = new Set<TurnId>();\n\n const push = (row: ChatItem<C>) => {\n rows.push(row);\n return rows.length - 1;\n };\n\n const folders: ItemFolders = {\n prompt: (item) => {\n prompts.set(\n item.turnId,\n push({\n kind: \"prompt\",\n id: item.id,\n turnId: item.turnId,\n text: item.body.text,\n agent: item.body.agent,\n state: stateOf(item.turnId),\n failure: failures.get(item.turnId),\n mine:\n input.deviceId !== undefined && item.body.signed?.deviceId === input.deviceId,\n }),\n );\n },\n answer: (item) => {\n const at = asks.get(item.body.askId);\n if (at === undefined || rejected.has(item.id)) return;\n const row = rows[at];\n if (row?.kind === \"ask\" && row.answer.state === \"open\") {\n rows[at] = {\n ...row,\n answer: { state: \"answered\", choice: item.body.choice },\n };\n }\n },\n cancel: (item) => {\n cancelled.add(item.turnId);\n },\n reply: (item) => {\n push({\n kind: \"reply\",\n id: item.id,\n turnId: item.turnId,\n text: item.body.text,\n streaming: false,\n interrupted: item.body.interrupted === true,\n });\n },\n reasoning: (item) => {\n push({\n kind: \"reasoning\",\n id: item.id,\n turnId: item.turnId,\n text: item.body.text,\n streaming: false,\n interrupted: false,\n });\n },\n tool: (item) => {\n push({\n kind: \"tool\",\n id: item.id,\n turnId: item.turnId,\n title: item.body.title,\n tool: item.body.tool,\n status: item.body.status,\n detail: item.body.detail,\n });\n },\n plan: (item) => {\n push({\n kind: \"plan\",\n id: item.id,\n turnId: item.turnId,\n entries: item.body.entries,\n streaming: false,\n });\n },\n ask: (item) => {\n asks.set(\n item.id,\n push({\n kind: \"ask\",\n id: item.id,\n turnId: item.turnId,\n question: item.body.question,\n choices: item.body.choices,\n form: item.body.form,\n approval: item.body.approval,\n answer: { state: \"open\" },\n }),\n );\n },\n event: (item) => {\n const { event, itemId, choice } = item.body;\n if (event === \"answered_on_computer\") {\n const at = itemId === undefined ? undefined : asks.get(itemId);\n const row = at === undefined ? undefined : rows[at];\n if (\n at !== undefined &&\n choice !== undefined &&\n row?.kind === \"ask\" &&\n row.answer.state === \"open\"\n ) {\n rows[at] = { ...row, answer: { state: \"answered\", choice } };\n }\n return;\n }\n push({\n kind: \"event\",\n id: item.id,\n turnId: item.turnId,\n event,\n detail: item.body.detail,\n });\n },\n };\n\n for (const item of input.items) {\n durable.add(item.id);\n fold(folders, item);\n }\n\n for (const [turnId, at] of prompts) {\n const row = rows[at];\n if (row?.kind === \"prompt\" && cancelled.has(turnId) && !TERMINAL.has(row.state)) {\n rows[at] = { ...row, state: \"cancelled\" };\n }\n }\n for (const at of asks.values()) {\n const row = rows[at];\n if (row?.kind === \"ask\" && row.answer.state === \"open\") {\n if (TERMINAL.has(stateOf(row.turnId)) || cancelled.has(row.turnId)) {\n rows[at] = { ...row, answer: { state: \"closed\" } };\n }\n }\n }\n\n for (const turn of input.open) {\n if (TERMINAL.has(stateOf(turn.turnId))) continue;\n for (const open of turn.items) {\n if (durable.has(open.id)) continue;\n rows.push(openRow(open, turn.turnId));\n }\n }\n\n const threadId = input.items[0]?.threadId ?? input.open[0]?.threadId;\n if (threadId !== undefined) {\n const running = input.turns.find((turn) => turn.state === \"running\")?.turnId;\n const turnId = running ?? input.items.at(-1)?.turnId ?? input.open[0]?.turnId;\n if (turnId !== undefined) {\n for (const confirmation of input.confirmations) {\n if (confirmation.threadId !== threadId) continue;\n rows.push({\n kind: \"confirm\",\n id: asId(\"item\", confirmation.id),\n turnId,\n confirmation,\n });\n }\n }\n }\n return rows;\n}\n\nfunction fold(folders: ItemFolders, item: LoggedItem): void {\n switch (item.kind) {\n case \"prompt\":\n return folders.prompt(item);\n case \"answer\":\n return folders.answer(item);\n case \"cancel\":\n return folders.cancel(item);\n case \"reply\":\n return folders.reply(item);\n case \"reasoning\":\n return folders.reasoning(item);\n case \"tool\":\n return folders.tool(item);\n case \"plan\":\n return folders.plan(item);\n case \"ask\":\n return folders.ask(item);\n case \"event\":\n return folders.event(item);\n }\n}\n\nfunction openRow<C>(open: OpenItem, turnId: TurnId): ChatItem<C> {\n switch (open.kind) {\n case \"reply\":\n case \"reasoning\":\n return {\n kind: open.kind,\n id: open.id,\n turnId,\n text: open.text,\n streaming: true,\n interrupted: false,\n };\n case \"tool\":\n return {\n kind: \"tool\",\n id: open.id,\n turnId,\n title: open.title,\n tool: undefined,\n status: open.status,\n detail: undefined,\n };\n case \"plan\":\n return {\n kind: \"plan\",\n id: open.id,\n turnId,\n entries: open.entries,\n streaming: true,\n };\n }\n}\n"]}