@tangle-network/agent-app 0.60.179 → 0.60.181
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assistant/index.js +3 -1
- package/dist/assistant/index.js.map +1 -1
- package/dist/{chunk-QGOGX3DL.js → chunk-6ZUVYAKB.js} +466 -260
- package/dist/chunk-6ZUVYAKB.js.map +1 -0
- package/dist/chunk-ATQQCG4I.js +194 -0
- package/dist/chunk-ATQQCG4I.js.map +1 -0
- package/dist/{chunk-CPNJRLPN.js → chunk-WSEXFTN4.js} +46 -7
- package/dist/chunk-WSEXFTN4.js.map +1 -0
- package/dist/{chunk-N3CYCSKK.js → chunk-ZBB5KQ6B.js} +1 -1
- package/dist/chunk-ZBB5KQ6B.js.map +1 -0
- package/dist/hub-approvals/index.js +1 -1
- package/dist/hub-approvals/types.d.ts +16 -1
- package/dist/hub-approvals-react/feedback.d.ts +25 -5
- package/dist/hub-approvals-react/index.d.ts +1 -0
- package/dist/hub-approvals-react/index.js +413 -257
- package/dist/hub-approvals-react/index.js.map +1 -1
- package/dist/hub-approvals-react/media.d.ts +12 -0
- package/dist/public-consultation/index.d.ts +1 -1
- package/dist/teams/drizzle/access.d.ts +4 -0
- package/dist/teams/drizzle.js.map +1 -1
- package/dist/teams/invitations-api.d.ts +6 -3
- package/dist/teams/invitations-api.js +72 -39
- package/dist/teams/invitations-api.js.map +1 -1
- package/dist/teams/members-api.d.ts +2 -2
- package/dist/teams/members-api.js +1 -1
- package/dist/web-react/index.d.ts +2 -0
- package/dist/web-react/index.js +8 -1
- package/dist/web-react/review-inbox.d.ts +33 -0
- package/package.json +1 -1
- package/dist/chunk-CPNJRLPN.js.map +0 -1
- package/dist/chunk-N3CYCSKK.js.map +0 -1
- package/dist/chunk-QGOGX3DL.js.map +0 -1
package/dist/web-react/index.js
CHANGED
|
@@ -39,6 +39,7 @@ import {
|
|
|
39
39
|
QuestionOptionList,
|
|
40
40
|
ReasoningRow,
|
|
41
41
|
RecordGrid,
|
|
42
|
+
ReviewInbox,
|
|
42
43
|
RunDrillIn,
|
|
43
44
|
SeatPaywall,
|
|
44
45
|
Sparkline,
|
|
@@ -142,7 +143,7 @@ import {
|
|
|
142
143
|
withoutRecordGridCreated,
|
|
143
144
|
withoutRecordGridRemoved,
|
|
144
145
|
withoutRecordGridUpdate
|
|
145
|
-
} from "../chunk-
|
|
146
|
+
} from "../chunk-6ZUVYAKB.js";
|
|
146
147
|
import {
|
|
147
148
|
SessionHistoryPanel,
|
|
148
149
|
formatSessionTimestamp,
|
|
@@ -232,6 +233,10 @@ import {
|
|
|
232
233
|
ProviderLogo
|
|
233
234
|
} from "../chunk-RASGKEAL.js";
|
|
234
235
|
import "../chunk-LN5RBVGP.js";
|
|
236
|
+
import {
|
|
237
|
+
ReviewFeedbackForm
|
|
238
|
+
} from "../chunk-ATQQCG4I.js";
|
|
239
|
+
import "../chunk-WSEXFTN4.js";
|
|
235
240
|
import "../chunk-FQYAJYPW.js";
|
|
236
241
|
import {
|
|
237
242
|
ATTACHMENT_ACCEPT
|
|
@@ -375,6 +380,8 @@ export {
|
|
|
375
380
|
QuestionOptionList,
|
|
376
381
|
ReasoningRow,
|
|
377
382
|
RecordGrid,
|
|
383
|
+
ReviewFeedbackForm,
|
|
384
|
+
ReviewInbox,
|
|
378
385
|
ReviewQueuePanel,
|
|
379
386
|
RouteChunkBoundary,
|
|
380
387
|
RunDrillIn,
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
import type { HubActionPreview, HubApprovalFeedback } from '../hub-approvals';
|
|
3
|
+
import { type ReviewVerdict } from '../hub-approvals-react/feedback';
|
|
4
|
+
export interface ReviewInboxItem {
|
|
5
|
+
id: string;
|
|
6
|
+
revision: string | number;
|
|
7
|
+
title: string;
|
|
8
|
+
kind: string;
|
|
9
|
+
createdAt: number;
|
|
10
|
+
canReview: boolean;
|
|
11
|
+
input: unknown;
|
|
12
|
+
feedback?: HubApprovalFeedback;
|
|
13
|
+
preview?: HubActionPreview;
|
|
14
|
+
approveLabel?: string;
|
|
15
|
+
allowedVerdicts?: readonly ReviewVerdict[];
|
|
16
|
+
}
|
|
17
|
+
export interface ReviewInboxFeedback extends HubApprovalFeedback {
|
|
18
|
+
verdict: ReviewVerdict;
|
|
19
|
+
}
|
|
20
|
+
export interface ReviewInboxProps {
|
|
21
|
+
items: readonly ReviewInboxItem[];
|
|
22
|
+
/** An existing deep-linked item; selected once when the round begins, without reordering it. */
|
|
23
|
+
initialSelectedId?: string;
|
|
24
|
+
renderItem: (item: ReviewInboxItem) => ReactNode;
|
|
25
|
+
/** Resolves only after the verdict and its learning signal have been stored. */
|
|
26
|
+
onVerdict: (item: ReviewInboxItem, decision: ReviewInboxFeedback) => Promise<void>;
|
|
27
|
+
onFeedback?: (item: ReviewInboxItem, feedback: Omit<ReviewInboxFeedback, 'verdict'>) => Promise<void>;
|
|
28
|
+
loading?: boolean;
|
|
29
|
+
error?: string | null;
|
|
30
|
+
onRefresh?: () => void | Promise<void>;
|
|
31
|
+
}
|
|
32
|
+
/** One oldest-first review round. Domain rendering, storage and authority stay with the host. */
|
|
33
|
+
export declare function ReviewInbox({ items, initialSelectedId, renderItem, onVerdict, onFeedback, loading, error, onRefresh }: ReviewInboxProps): import("react").JSX.Element;
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/hub-approvals/types.ts","../src/hub-approvals/result.ts","../src/hub-approvals/unknown.ts","../src/hub-approvals/present.ts","../src/hub-approvals/index.ts"],"sourcesContent":["/**\n * The vocabulary of a Hub action that waits on a person: where it stands, what\n * it will do in the owner's words, and what it did once it ran.\n */\n\n/** Where one held action stands, as the dock, the list and the transcript show it. */\nexport type HubApprovalPhase =\n /** Held by Hub; the viewer can decide it. */\n | 'waiting'\n /** Held by Hub; someone else (the workspace owner) decides it. */\n | 'blocked'\n /** Approved here; the run has not started. */\n | 'queued'\n /** Approved; Hub is running the call. */\n | 'running'\n | 'done'\n /** Approved, but Hub could not run it. */\n | 'failed'\n /** The provider may have acted; verify there before requesting it again. */\n | 'unknown'\n | 'denied'\n | 'expired'\n\n/** Phases that still need someone's decision. */\nexport const HUB_APPROVAL_OPEN_PHASES: ReadonlySet<HubApprovalPhase> = new Set(['waiting', 'blocked'])\n/** Phases between a decision and its result. */\nexport const HUB_APPROVAL_ACTIVE_PHASES: ReadonlySet<HubApprovalPhase> = new Set(['queued', 'running'])\n\n/** A file a held write changes. Counts stay undefined until someone has read them. */\nexport interface HubActionFile {\n path: string\n /** A rename reads as a modification of its new path. */\n change: 'added' | 'modified' | 'deleted'\n additions?: number\n deletions?: number\n}\n\n/**\n * The change a held call serves, when it is one step of a larger change such\n * as a pull request. The dock then names the change rather than the step.\n */\nexport interface HubActionBundle {\n /** What the whole change does, e.g. `Open PR: Fix the hero on acme/site`. */\n title: string\n steps?: ReadonlyArray<{ label: string; state: 'done' | 'current' | 'upcoming' }>\n}\n\n/** Whether an open call waits for the owner, in the owner's words. */\nexport interface HubApprovalWait {\n /** True when nothing runs until the owner decides; false when a permission runs it. */\n owner: boolean\n /** One sentence: `Publishing always needs your approval.` or `Runs under this conversation’s auto-approve.` */\n reason: string\n /** A stable cause the host can test, e.g. `never_grantable`, `loop_guard`, `daily_cap`. */\n code?: string\n}\n\n/** One held Hub action and its decision, in the form every surface renders. */\nexport interface HubApprovalItem {\n /** Stable within a conversation, such as `<messageId>:<partId>`. */\n id: string\n /** `provider.action`, e.g. `github.pulls.propose`. */\n actionPath: string\n providerId: string\n /** The call's input as Hub received it. */\n input: unknown\n phase: HubApprovalPhase\n /** The connected account the action runs as, e.g. `octocat` or `ops@acme.com`. */\n account?: string | null\n requestedAt?: string | null\n /** When the decision window closes. */\n expiresAt?: string | null\n /** Hub's result, or a bounded projection of it from {@link summarizeHubResult}. */\n result?: unknown\n error?: string | null\n /** The standing permission granted with the approval. */\n grant?: { scope: string; expiresAt: string | null } | null\n /** Set when a permissions mode, not a person, approved it. */\n autoApproved?: string | null\n /**\n * For an open call, what happens next without the viewer: it waits for the\n * owner, and why, or it runs on its own once the agent's reply finishes.\n */\n wait?: HubApprovalWait | null\n /** Files with line counts read by the host, replacing those the input declares. */\n files?: readonly HubActionFile[]\n bundle?: HubActionBundle | null\n /** Previewing and teaching never execute a call. Live approval remains a separate action. */\n executionMode?: 'dry-run' | 'live'\n feedback?: HubApprovalFeedback & { savedAt?: string }\n /** Host-resolved presentation, e.g. a retained page screenshot or ad creative. No remote fetches. */\n preview?: HubActionPreview\n}\n\n/** The host authenticates the reviewer, binds provenance and computes the edit diff. */\nexport interface HubApprovalFeedback {\n score: number\n comment: string\n /** A teaching example only; never replaces the held input or authorizes execution. */\n editedInput?: unknown\n}\n\n/** A labelled value on a preview or a receipt. */\nexport interface HubActionField {\n label: string\n value: string\n href?: string\n /** Render in the monospace face: shas, ids, paths. */\n mono?: boolean\n}\n\n/** What a held call will do, shaped for its integration's renderer. */\nexport type HubActionPreview =\n | {\n kind: 'pull-request'\n repository?: string\n title?: string\n body?: string\n head?: string\n base?: string\n draft?: boolean\n files: readonly HubActionFile[]\n }\n | { kind: 'commit'; repository?: string; message?: string; tree?: string; parents: readonly string[] }\n | { kind: 'branch'; repository?: string; branch?: string; sha?: string }\n | { kind: 'files'; repository?: string; files: readonly HubActionFile[] }\n | { kind: 'issue'; repository?: string; number?: number; title?: string; body?: string; verdict?: string }\n | {\n kind: 'email'\n to: readonly string[]\n cc: readonly string[]\n bcc: readonly string[]\n subject?: string\n from?: string\n fromName?: string\n previewText?: string\n body?: string\n html: boolean\n reply: boolean\n }\n | {\n kind: 'post'\n channel: 'x' | 'linkedin' | 'slack'\n text: string\n /** The post's place, such as a Slack channel. */\n where?: string\n link?: { url: string; title?: string; description?: string }\n images?: readonly { url: string; alt?: string }[]\n }\n | { kind: 'ad'; placement: string; advertiser?: string; headline?: string; text?: string; image?: string; destination?: string; callToAction?: string }\n | { kind: 'page'; title?: string; url?: string; screenshot?: string }\n | { kind: 'payment'; label: string; amount?: number; currency?: string; customer?: string; description?: string }\n | { kind: 'speech'; text: string; voice?: string; seconds?: number }\n | { kind: 'call'; to?: string; from?: string; purpose?: string }\n | {\n kind: 'event'\n title?: string\n start?: string\n end?: string\n location?: string\n attendees: readonly string[]\n description?: string\n }\n | { kind: 'fields'; fields: readonly HubActionField[] }\n\n/** A held call in the owner's words. */\nexport interface HubActionPresentation {\n provider: { id: string; name: string }\n /** The imperative, e.g. `Open pull request`. */\n action: string\n /** One line naming the action and its object, e.g. `Open PR: Fix the hero on acme/site`. */\n title: string\n /** Where it acts: a repository, recipients, a channel or a customer. */\n target?: string\n preview: HubActionPreview\n}\n\n/** What a decided call did, as its receipt shows it. */\nexport interface HubActionReceipt {\n provider: { id: string; name: string }\n /** The result in past tense, e.g. `Opened PR #123` or `Sent email to ada@acme.com`. */\n title: string\n /** The result's own page, when it has one: the pull request, the post, the event. */\n href?: string\n status: 'done' | 'unknown' | 'failed' | 'denied' | 'expired' | 'running' | 'waiting'\n fields: readonly HubActionField[]\n files?: readonly HubActionFile[]\n media?: { kind: 'audio'; src: string; seconds?: number }\n error?: string\n /** Guidance for an uncertain outcome, separate from a confirmed failure. */\n warning?: string\n}\n","/**\n * Hub results, read the same way by every receipt: unwrapped from Hub's\n * envelope and bounded before a host persists them.\n */\n\nconst MAX_DEPTH = 4\nconst MAX_KEYS = 24\nconst MAX_ITEMS = 50\nconst MAX_STRING = 600\n\nexport function asRecord(value: unknown): Record<string, unknown> {\n return value && typeof value === 'object' && !Array.isArray(value) ? value as Record<string, unknown> : {}\n}\n\n/**\n * The provider's own result inside Hub's envelopes. `tools.invoke` answers\n * `{ result: … }` and a connector may wrap its body in `data`; a JSON string,\n * as an older host stored it, is parsed first.\n */\nexport function unwrapHubResult(value: unknown): unknown {\n let current: unknown = value\n if (typeof current === 'string') {\n const text = current\n try {\n current = JSON.parse(text)\n } catch {\n // An older host stored `Hub result: {…}`-style text with a JSON tail.\n const start = text.indexOf('{')\n if (start < 0) return value\n try {\n current = JSON.parse(text.slice(start))\n } catch {\n return value\n }\n }\n }\n for (let depth = 0; depth < 3; depth++) {\n const record = asRecord(current)\n const keys = Object.keys(record)\n const inner = keys.find((key) => key === 'result' || key === 'data')\n // Only a thin envelope is unwrapped; a body that happens to carry a `data`\n // field among its own fields is the result itself.\n if (!inner || keys.length > 3 || record[inner] === null || typeof record[inner] !== 'object') break\n current = record[inner]\n }\n return current\n}\n\nfunction bounded(value: unknown, depth: number): unknown {\n if (value === null || typeof value === 'boolean') return value\n if (typeof value === 'number') return Number.isFinite(value) ? value : null\n if (typeof value === 'string') return value.length > MAX_STRING ? `${value.slice(0, MAX_STRING - 1)}…` : value\n if (depth >= MAX_DEPTH) return undefined\n if (Array.isArray(value)) {\n return value.slice(0, MAX_ITEMS).map((item) => bounded(item, depth + 1)).filter((item) => item !== undefined)\n }\n if (typeof value === 'object') {\n const out: Record<string, unknown> = {}\n for (const [key, entry] of Object.entries(value as Record<string, unknown>).slice(0, MAX_KEYS)) {\n const kept = bounded(entry, depth + 1)\n if (kept !== undefined) out[key] = kept\n }\n return out\n }\n return undefined\n}\n\n/**\n * A bounded copy of a Hub result for a host to store with the decision: the\n * envelope removed, strings, arrays and nesting capped. Receipts read it in\n * place of the full result.\n */\nexport function summarizeHubResult(result: unknown): unknown {\n return bounded(unwrapHubResult(result), 0)\n}\n\n/** The first non-empty string among `keys`, searched one level into nested records. */\nexport function readText(value: unknown, ...keys: string[]): string | undefined {\n const record = asRecord(value)\n for (const key of keys) {\n const found = record[key]\n if (typeof found === 'string' && found.trim()) return found.trim()\n if (typeof found === 'number' && Number.isFinite(found)) return String(found)\n }\n return undefined\n}\n\nexport function readNumber(value: unknown, ...keys: string[]): number | undefined {\n const record = asRecord(value)\n for (const key of keys) {\n const found = record[key]\n if (typeof found === 'number' && Number.isFinite(found)) return found\n }\n return undefined\n}\n\n/** A link a result names for itself, in the order providers use them. */\nexport function resultLink(value: unknown): string | undefined {\n const url = readText(value, 'html_url', 'htmlLink', 'webLink', 'web_url', 'permalink', 'hosted_invoice_url', 'url', 'link')\n return url && /^https:\\/\\//.test(url) ? url : undefined\n}\n","/** Internal safety copy shared by uncertain-outcome surfaces. */\nexport const HUB_APPROVAL_UNKNOWN_WARNING = 'Check the provider before requesting it again'\n","/**\n * Hub actions in an owner's words. Known integrations read as what they do —\n * `github.pulls.propose` is \"Open PR: Fix the hero on acme/site\", `gmail.send`\n * is \"Send email to ada@acme.com\" — and any other Hub action falls back to its\n * own name and its input's scalar fields.\n */\n\nimport { asRecord, readNumber, readText, resultLink, unwrapHubResult } from './result'\nimport { HUB_APPROVAL_UNKNOWN_WARNING } from './unknown'\nimport type {\n HubActionField,\n HubActionFile,\n HubActionPresentation,\n HubActionPreview,\n HubActionReceipt,\n HubApprovalItem,\n} from './types'\n\nconst PROVIDER_NAMES: Record<string, string> = {\n github: 'GitHub',\n gitlab: 'GitLab',\n gmail: 'Gmail',\n google: 'Google',\n 'google-calendar': 'Google Calendar',\n 'microsoft-calendar': 'Outlook Calendar',\n 'outlook-mail': 'Outlook',\n outlook: 'Outlook',\n twitter: 'X',\n x: 'X',\n linkedin: 'LinkedIn',\n stripe: 'Stripe',\n phony: 'ph0ny',\n slack: 'Slack',\n notion: 'Notion',\n hubspot: 'HubSpot',\n linear: 'Linear',\n inkbox: 'Inkbox',\n resend: 'Resend',\n}\n\n/** A provider's display name: `github` is GitHub, `outlook-mail` is Outlook. */\nexport function hubProviderName(id: string): string {\n if (PROVIDER_NAMES[id]) return PROVIDER_NAMES[id]\n const words = id.split(/[-_\\s]+/).filter(Boolean)\n return words.map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(' ') || id\n}\n\n/** `provider.action` split at the first dot. */\nexport function splitHubActionPath(actionPath: string): { provider: string; action: string } {\n const separator = actionPath.indexOf('.')\n return separator < 0 ? { provider: '', action: actionPath } : { provider: actionPath.slice(0, separator), action: actionPath.slice(separator + 1) }\n}\n\nfunction text(value: unknown): string | undefined {\n return typeof value === 'string' && value.trim() ? value.trim() : undefined\n}\n\nfunction clipped(value: string, max = 80): string {\n const single = value.replace(/\\s+/g, ' ').trim()\n return single.length > max ? `${single.slice(0, max - 1).trimEnd()}…` : single\n}\n\nfunction list(value: unknown): string[] {\n if (typeof value === 'string') return value.split(/[,;]/).map((entry) => entry.trim()).filter(Boolean)\n if (!Array.isArray(value)) return []\n return value.flatMap((entry) => {\n if (typeof entry === 'string' && entry.trim()) return [entry.trim()]\n const record = asRecord(entry)\n const address = text(record.email) ?? text(record.address) ?? text(asRecord(record.emailAddress).address)\n return address ? [address] : []\n })\n}\n\nfunction people(addresses: readonly string[]): string {\n if (addresses.length === 0) return ''\n if (addresses.length === 1) return addresses[0]!\n return `${addresses[0]} and ${addresses.length - 1} more`\n}\n\nfunction shortSha(sha: string | undefined): string | undefined {\n return sha && /^[0-9a-f]{7,}$/i.test(sha) ? sha.slice(0, 7) : sha\n}\n\n// ---------- verbs ----------\n\nconst PAST: Record<string, string> = {\n add: 'added', approve: 'approved', archive: 'archived', assign: 'assigned', book: 'booked', cancel: 'canceled',\n capture: 'captured', charge: 'charged', clone: 'cloned', close: 'closed', comment: 'commented', create: 'created',\n deactivate: 'deactivated', delete: 'deleted', enroll: 'enrolled', export: 'exported', finalize: 'finalized',\n forward: 'forwarded', import: 'imported', ingest: 'ingested', invite: 'invited', like: 'liked', make: 'made',\n merge: 'merged', move: 'moved', open: 'opened', pay: 'paid', place: 'placed', post: 'posted', provision: 'provisioned',\n publish: 'published', put: 'put', react: 'reacted', refund: 'refunded', remove: 'removed', reply: 'replied',\n retweet: 'reposted', review: 'reviewed', run: 'ran', save: 'saved', schedule: 'scheduled', send: 'sent', set: 'set',\n share: 'shared', start: 'started', stop: 'stopped', submit: 'submitted', subscribe: 'subscribed',\n synthesize: 'synthesized', transfer: 'transferred', translate: 'translated', update: 'updated', upload: 'uploaded',\n upsert: 'saved', void: 'voided', watch: 'watched', write: 'wrote',\n}\n\n/** The past tense of an English verb this vocabulary uses. */\nexport function pastTense(verb: string): string {\n const lower = verb.toLowerCase()\n const past = PAST[lower] ?? (lower.endsWith('e') ? `${lower}d` : `${lower}ed`)\n return verb.charAt(0) === verb.charAt(0).toUpperCase() ? past.charAt(0).toUpperCase() + past.slice(1) : past\n}\n\nfunction words(action: string): string[] {\n return action\n .replace(/([a-z0-9])([A-Z])/g, '$1 $2')\n .split(/[._\\-\\s]+/)\n .map((word) => word.toLowerCase())\n .filter(Boolean)\n}\n\nfunction singular(word: string): string {\n if (word.endsWith('ies')) return `${word.slice(0, -3)}y`\n if (word.endsWith('sses') || word.endsWith('ss')) return word\n return word.endsWith('s') && word.length > 3 ? word.slice(0, -1) : word\n}\n\n/** A Hub action's own name as a sentence: `payment-intents.create` is \"Create payment intent\". */\nfunction genericVerb(action: string): { verb: string; noun: string } {\n const parts = words(action)\n const index = parts.findIndex((word) => word in PAST)\n if (index < 0) return { verb: '', noun: parts.join(' ') }\n const noun = [...parts.slice(0, index), ...parts.slice(index + 1)].map(singular).join(' ')\n return { verb: parts[index]!, noun }\n}\n\nfunction sentence(verb: string, noun: string): string {\n const phrase = [verb, noun].filter(Boolean).join(' ')\n return phrase.charAt(0).toUpperCase() + phrase.slice(1)\n}\n\n// ---------- inputs ----------\n\nfunction repository(input: Record<string, unknown>): string | undefined {\n const owner = text(input.owner)\n const repo = text(input.repo)\n if (owner && repo) return `${owner}/${repo}`\n return repo ?? text(input.repository)\n}\n\nfunction branchOf(ref: unknown): string | undefined {\n return text(ref)?.replace(/^refs\\/heads\\//, '')\n}\n\n/** The files a call writes, as its input declares them. */\nexport function hubActionFiles(actionPath: string, input: unknown): HubActionFile[] {\n const fields = asRecord(input)\n const { action } = splitHubActionPath(actionPath)\n const entries = action === 'git.createTree' ? fields.tree : fields.files\n if (!Array.isArray(entries)) return []\n return entries.flatMap((entry): HubActionFile[] => {\n const item = asRecord(entry)\n const path = text(item.path)\n if (!path) return []\n const deleted = item.delete === true || (item.sha === null && item.content === undefined)\n return [{ path, change: deleted ? 'deleted' : 'modified' }]\n })\n}\n\n/** Counted files replace declared ones by path; the declared order stays. */\nfunction mergedFiles(declared: readonly HubActionFile[], counted: readonly HubActionFile[] | undefined): HubActionFile[] {\n if (!counted || counted.length === 0) return [...declared]\n if (declared.length === 0) return [...counted]\n const byPath = new Map(counted.map((file) => [file.path, file]))\n return declared.map((file) => byPath.get(file.path) ?? file)\n}\n\nfunction emailPreview(input: Record<string, unknown>, reply: boolean): HubActionPreview {\n const from = asRecord(input.from)\n const htmlBody = typeof input.html === 'string' ? input.html : undefined\n return {\n kind: 'email',\n to: list(input.to ?? input.recipients),\n cc: list(input.cc),\n bcc: list(input.bcc),\n subject: text(input.subject),\n from: text(input.from) ?? text(from.email) ?? text(from.address),\n fromName: text(input.fromName) ?? text(input.from_name) ?? text(from.name),\n previewText: text(input.previewText) ?? text(input.preview_text) ?? text(input.preheader),\n // The payload is evidence: retain whitespace and prefer the actual HTML part.\n body: htmlBody ?? [input.body, input.text, input.comment].find((value): value is string => typeof value === 'string'),\n html: input.html === true || text(input.bodyType)?.toLowerCase() === 'html' || typeof input.html === 'string',\n reply,\n }\n}\n\nfunction speechSeconds(input: Record<string, unknown>): number | undefined {\n const spoken = text(input.text)\n if (!spoken) return undefined\n const speed = typeof input.speed === 'number' && input.speed > 0 ? input.speed : 1\n // About 2.5 spoken words a second at normal speed.\n return Math.max(1, Math.round(spoken.split(/\\s+/).length / 2.5 / speed))\n}\n\nfunction voiceName(input: Record<string, unknown>): string | undefined {\n const voice = text(input.voiceId)\n // An opaque provider id says nothing to an owner; a named voice does.\n if (voice && !/^[0-9a-f-]{16,}$/i.test(voice) && voice.length <= 32) return voice.charAt(0).toUpperCase() + voice.slice(1)\n return text(input.provider)\n}\n\nconst CURRENCY_DIGITS: Record<string, number> = { jpy: 0, krw: 0, vnd: 0, clp: 0, bhd: 3, kwd: 3, omr: 3 }\n\n/** A Stripe minor-unit amount in its currency: `4900` USD is `$49.00`. */\nexport function formatMinorAmount(amount: number, currency = 'usd'): string {\n const code = currency.toLowerCase()\n const digits = CURRENCY_DIGITS[code] ?? 2\n try {\n return new Intl.NumberFormat('en-US', { style: 'currency', currency: code.toUpperCase(), minimumFractionDigits: digits })\n .format(amount / 10 ** digits)\n } catch {\n return `${(amount / 10 ** digits).toFixed(digits)} ${code.toUpperCase()}`\n }\n}\n\n/** Scalar input fields, for an action no renderer knows. */\nfunction scalarFields(input: Record<string, unknown>, max = 6): HubActionField[] {\n return Object.entries(input).flatMap(([key, value]): HubActionField[] => {\n if (key.startsWith('__')) return []\n if (typeof value === 'string' && value.trim()) return [{ label: labelOf(key), value: clipped(value, 160) }]\n if (typeof value === 'number' || typeof value === 'boolean') return [{ label: labelOf(key), value: String(value) }]\n const strings = Array.isArray(value) ? value.filter((entry): entry is string => typeof entry === 'string') : []\n return strings.length > 0 ? [{ label: labelOf(key), value: clipped(strings.join(', '), 160) }] : []\n }).slice(0, max)\n}\n\nfunction labelOf(key: string): string {\n const spaced = key.replace(/([a-z0-9])([A-Z])/g, '$1 $2').replace(/[_-]+/g, ' ').trim().toLowerCase()\n return spaced.charAt(0).toUpperCase() + spaced.slice(1)\n}\n\n// ---------- presentation ----------\n\ntype Presenter = (input: Record<string, unknown>, item: Pick<HubApprovalItem, 'actionPath' | 'files'>) => Omit<HubActionPresentation, 'provider'>\n\nfunction pullRequest(input: Record<string, unknown>, item: Pick<HubApprovalItem, 'actionPath' | 'files'>): Omit<HubActionPresentation, 'provider'> {\n const repo = repository(input)\n const title = text(input.title) ?? 'Untitled change'\n return {\n action: 'Open pull request',\n title: `Open PR: ${clipped(title)}${repo ? ` on ${repo}` : ''}`,\n ...(repo ? { target: repo } : {}),\n preview: {\n kind: 'pull-request',\n repository: repo,\n title,\n body: text(input.body),\n head: branchOf(input.branch ?? input.head),\n base: text(input.base),\n draft: item.actionPath.endsWith('.propose') ? true : input.draft === true,\n files: mergedFiles(hubActionFiles(item.actionPath, input), item.files),\n },\n }\n}\n\nfunction issueLike(verb: string, noun: string): Presenter {\n return (input) => {\n const repo = repository(input)\n const number = readNumber(input, 'issue_number', 'pull_number', 'number')\n const title = text(input.title)\n const object = number !== undefined ? `${noun} #${number}` : title ? `${noun}: ${clipped(title)}` : noun\n return {\n action: sentence(verb, noun.toLowerCase()),\n title: `${sentence(verb, object)}${repo ? ` on ${repo}` : ''}`,\n ...(repo ? { target: repo } : {}),\n preview: {\n kind: 'issue',\n repository: repo,\n number,\n title,\n body: text(input.body),\n verdict: text(input.event)?.toLowerCase().replace(/_/g, ' '),\n },\n }\n }\n}\n\nfunction sendEmail(verb: 'Send' | 'Save draft', reply = false): Presenter {\n return (input) => {\n const preview = emailPreview(input, reply)\n const recipients = preview.kind === 'email' ? people(preview.to) : ''\n const action = reply ? 'Send reply' : verb === 'Send' ? 'Send email' : 'Save draft'\n return {\n action,\n title: reply ? 'Send reply' : `${action}${recipients ? ` to ${recipients}` : ''}`,\n ...(recipients ? { target: recipients } : {}),\n preview,\n }\n }\n}\n\nfunction post(channel: 'x' | 'linkedin', reply = false): Presenter {\n return (input) => {\n const body = typeof input.text === 'string' ? input.text : typeof input.commentary === 'string' ? input.commentary : ''\n const url = text(input.url)\n const where = channel === 'x' ? 'X' : 'LinkedIn'\n return {\n action: reply ? `Reply on ${where}` : `Post on ${where}`,\n title: `${reply ? 'Reply' : 'Post'} on ${where}${body ? `: “${clipped(body, 60)}”` : ''}`,\n target: where,\n preview: {\n kind: 'post',\n channel,\n text: body,\n images: (Array.isArray(input.images) ? input.images : input.image ? [input.image] : []).flatMap((image) => {\n const record = asRecord(image)\n const url = text(image) ?? text(record.url)\n return url ? [{ url, alt: text(record.alt) }] : []\n }),\n ...(url ? { link: { url, title: text(input.title), description: text(input.description) } } : {}),\n },\n }\n }\n}\n\nfunction payment(label: string): Presenter {\n return (input) => {\n const amount = readNumber(input, 'amount')\n const currency = text(input.currency)\n const customer = text(input.customerId) ?? text(input.customer) ?? text(input.email)\n const formatted = amount !== undefined ? formatMinorAmount(amount, currency) : undefined\n const object = formatted ? `${label} of ${formatted}` : label\n return {\n action: sentence('create', label),\n title: `${sentence('create', object)}${customer ? ` for ${customer}` : ''}`,\n ...(customer ? { target: customer } : {}),\n preview: { kind: 'payment', label, amount, currency, customer, description: text(input.description) },\n }\n }\n}\n\nfunction calendarEvent(input: Record<string, unknown>, _item?: Pick<HubApprovalItem, 'actionPath' | 'files'>): Omit<HubActionPresentation, 'provider'> {\n const title = text(input.summary) ?? text(input.title) ?? text(input.subject)\n return {\n action: 'Book event',\n title: title ? `Book “${clipped(title, 60)}”` : 'Book an event',\n preview: {\n kind: 'event',\n title,\n start: text(input.start) ?? text(asRecord(input.start).dateTime),\n end: text(input.end) ?? text(asRecord(input.end).dateTime),\n location: text(input.location) ?? text(asRecord(input.location).displayName),\n attendees: list(input.attendees),\n description: text(input.description),\n },\n }\n}\n\nconst PRESENTERS: Record<string, Presenter> = {\n 'github.pulls.propose': pullRequest,\n 'github.pulls.create': pullRequest,\n 'github.git.createTree': (input, item) => {\n const repo = repository(input)\n const files = mergedFiles(hubActionFiles(item.actionPath, input), item.files)\n return {\n action: 'Write files',\n title: `Write ${files.length === 1 ? files[0]!.path : `${files.length} files`}${repo ? ` to ${repo}` : ''}`,\n ...(repo ? { target: repo } : {}),\n preview: { kind: 'files', repository: repo, files },\n }\n },\n 'github.git.createCommit': (input) => {\n const repo = repository(input)\n const message = text(input.message)\n return {\n action: 'Create commit',\n title: `Commit${message ? ` “${clipped(message, 60)}”` : ''}${repo ? ` to ${repo}` : ''}`,\n ...(repo ? { target: repo } : {}),\n preview: { kind: 'commit', repository: repo, message, tree: text(input.tree), parents: list(input.parents) },\n }\n },\n 'github.git.createRef': (input) => {\n const repo = repository(input)\n const branch = branchOf(input.ref)\n return {\n action: 'Create branch',\n title: `Create branch ${branch ?? ''}${repo ? ` in ${repo}` : ''}`.replace(/\\s+/g, ' ').trim(),\n ...(repo ? { target: repo } : {}),\n preview: { kind: 'branch', repository: repo, branch, sha: text(input.sha) },\n }\n },\n 'github.pulls.merge': issueLike('merge', 'PR'),\n 'github.pulls.reviews.create': issueLike('review', 'PR'),\n 'github.issues.create': issueLike('open', 'Issue'),\n 'github.issues.update': issueLike('update', 'Issue'),\n 'github.issues.createComment': issueLike('comment on', 'Issue'),\n 'gmail.send': sendEmail('Send'),\n 'gmail.send_reply': sendEmail('Send', true),\n 'outlook-mail.send_message': sendEmail('Send'),\n 'outlook-mail.send_draft': sendEmail('Send'),\n 'outlook-mail.create_draft': sendEmail('Save draft'),\n 'outlook-mail.send_reply': sendEmail('Send', true),\n 'inkbox.email.send': sendEmail('Send'),\n 'resend.emails.send': sendEmail('Send'),\n 'resend.emails.reply': sendEmail('Send', true),\n 'twitter.tweets.create': post('x'),\n 'twitter.tweets.reply': post('x', true),\n 'x.tweets.create': post('x'),\n 'linkedin.posts.create': post('linkedin'),\n 'linkedin.shares.create': post('linkedin'),\n 'slack.post_message': (input) => {\n const channel = text(input.channel) ?? text(input.channelId)\n const where = channel ? (channel.startsWith('#') || /^[CDG][A-Z0-9]{6,}$/.test(channel) ? channel : `#${channel}`) : undefined\n return {\n action: 'Post in Slack',\n title: `Post in ${where ?? 'Slack'}`,\n ...(where ? { target: where } : {}),\n preview: { kind: 'post', channel: 'slack', text: text(input.text) ?? '', where },\n }\n },\n 'stripe.payment-intents.create': payment('payment'),\n 'stripe.payment-links.create': payment('payment link'),\n 'stripe.invoices.create': payment('invoice'),\n 'stripe.refunds.create': payment('refund'),\n 'stripe.subscriptions.create': payment('subscription'),\n 'phony.synthesize_speech': (input) => {\n const seconds = speechSeconds(input)\n const voice = voiceName(input)\n const detail = [voice, seconds ? `${seconds} s` : undefined].filter(Boolean)\n return {\n action: 'Create voice memo',\n title: `Create voice memo${detail.length ? ` (${detail.join(', ')})` : ''}`,\n preview: { kind: 'speech', text: text(input.text) ?? '', voice, seconds },\n }\n },\n 'phony.start_outbound_call': (input) => {\n const to = text(input.toNumber) ?? text(input.to)\n const mission = asRecord(input.mission)\n return {\n action: 'Start call',\n title: to ? `Call ${to}` : 'Start an outbound call',\n ...(to ? { target: to } : {}),\n preview: {\n kind: 'call',\n to,\n from: text(input.fromNumber),\n purpose: text(mission.goal) ?? text(mission.objective) ?? text(mission.summary) ?? text(input.missionId),\n },\n }\n },\n 'phony.create_agent': (input) => {\n const name = text(input.name)\n return {\n action: 'Create voice agent',\n title: `Create voice agent${name ? ` “${clipped(name, 60)}”` : ''}`,\n preview: { kind: 'fields', fields: scalarFields(input) },\n }\n },\n 'phony.update_agent': (input) => ({\n action: 'Update voice agent',\n title: 'Update a voice agent',\n preview: { kind: 'fields', fields: scalarFields(input) },\n }),\n 'phony.clone_voice': (input) => {\n const name = text(input.name)\n return {\n action: 'Clone voice',\n title: `Clone your voice${name ? ` as “${clipped(name, 60)}”` : ''}`,\n preview: { kind: 'fields', fields: scalarFields(input) },\n }\n },\n 'google-calendar.create_event': calendarEvent,\n 'google-calendar.book_slot': calendarEvent,\n 'google-calendar.update_event': (input, item) => {\n const event = calendarEvent(input, item)\n const title = event.preview.kind === 'event' ? event.preview.title : undefined\n return { ...event, action: 'Update event', title: title ? `Update “${clipped(title, 60)}”` : 'Update an event' }\n },\n 'microsoft-calendar.book_slot': calendarEvent,\n}\n\n/** A held call in the owner's words, with the preview its integration renders. */\nexport function presentHubAction(item: Pick<HubApprovalItem, 'actionPath' | 'providerId' | 'input' | 'files' | 'bundle' | 'preview'>): HubActionPresentation {\n const { provider: pathProvider, action } = splitHubActionPath(item.actionPath)\n const providerId = item.providerId || pathProvider\n const provider = { id: providerId, name: hubProviderName(providerId) }\n const input = asRecord(item.input)\n const known = PRESENTERS[item.actionPath]\n const presented = known\n ? known(input, item)\n : (() => {\n const { verb, noun } = genericVerb(action)\n const phrase = sentence(verb, noun) || action\n const object = text(input.name) ?? text(input.title) ?? text(input.subject) ?? text(input.label)\n return {\n action: phrase,\n title: object ? `${phrase} “${clipped(object, 60)}”` : phrase,\n preview: { kind: 'fields', fields: scalarFields(input) } as HubActionPreview,\n }\n })()\n return { provider, ...presented, ...(item.preview ? { preview: item.preview } : {}), ...(item.bundle ? { title: item.bundle.title } : {}) }\n}\n\n// ---------- receipts ----------\n\nfunction prField(result: Record<string, unknown>): HubActionField | undefined {\n const pull = asRecord(result.pullRequest)\n const number = readNumber(pull, 'number') ?? readNumber(result, 'number')\n if (number === undefined) return undefined\n return { label: 'Pull request', value: `#${number}`, href: resultLink(pull) ?? resultLink(result) }\n}\n\nfunction receiptTitle(item: HubApprovalItem, presentation: HubActionPresentation, result: Record<string, unknown>): string {\n const { action } = splitHubActionPath(item.actionPath)\n const input = asRecord(item.input)\n const pull = asRecord(result.pullRequest)\n const number = readNumber(pull, 'number') ?? readNumber(result, 'number')\n switch (item.actionPath) {\n case 'github.pulls.propose':\n case 'github.pulls.create':\n return number !== undefined ? `Opened PR #${number}` : 'Opened pull request'\n case 'github.git.createCommit': {\n const sha = shortSha(readText(result, 'sha'))\n return sha ? `Created commit ${sha}` : 'Created commit'\n }\n case 'github.git.createTree': {\n const files = hubActionFiles(item.actionPath, input).length\n return `Wrote ${files === 1 ? '1 file' : `${files} files`}`\n }\n case 'github.git.createRef':\n return `Created branch ${branchOf(readText(result, 'ref') ?? input.ref) ?? ''}`.trim()\n case 'github.issues.create':\n return number !== undefined ? `Opened issue #${number}` : 'Opened issue'\n case 'github.issues.createComment': {\n const on = readNumber(input, 'issue_number')\n return on !== undefined ? `Commented on #${on}` : 'Commented'\n }\n case 'github.pulls.reviews.create': {\n const on = readNumber(input, 'pull_number')\n return on !== undefined ? `Reviewed PR #${on}` : 'Reviewed pull request'\n }\n case 'github.pulls.merge': {\n const on = readNumber(input, 'pull_number')\n return on !== undefined ? `Merged PR #${on}` : 'Merged pull request'\n }\n case 'phony.synthesize_speech':\n return 'Created voice memo'\n }\n if (presentation.preview.kind === 'email') {\n const to = people(presentation.preview.to)\n if (presentation.preview.reply) return 'Sent reply'\n return action.includes('draft') && !action.startsWith('send') ? `Saved draft${to ? ` to ${to}` : ''}` : `Sent email${to ? ` to ${to}` : ''}`\n }\n if (presentation.preview.kind === 'post') {\n return presentation.preview.channel === 'slack' ? `Posted in ${presentation.preview.where ?? 'Slack'}` : `Posted on ${presentation.target ?? presentation.provider.name}`\n }\n if (presentation.preview.kind === 'event') {\n const verb = presentation.action === 'Update event' ? 'Updated' : 'Booked'\n return presentation.preview.title ? `${verb} “${clipped(presentation.preview.title, 60)}”` : `${verb} event`\n }\n if (presentation.preview.kind === 'call') return presentation.preview.to ? `Started call to ${presentation.preview.to}` : 'Started call'\n const [first = '', ...rest] = presentation.action.split(' ')\n return [pastTense(first), ...rest].join(' ')\n}\n\nfunction postLink(item: HubApprovalItem, result: Record<string, unknown>): string | undefined {\n const id = readText(result, 'id', 'urn', 'postUrn')\n if (!id) return resultLink(result)\n if (item.providerId === 'twitter' || item.providerId === 'x') return /^\\d+$/.test(id) ? `https://x.com/i/web/status/${id}` : resultLink(result)\n if (item.providerId === 'linkedin') return id.startsWith('urn:li:') ? `https://www.linkedin.com/feed/update/${id}/` : resultLink(result)\n return resultLink(result)\n}\n\nfunction receiptFields(item: HubApprovalItem, presentation: HubActionPresentation, result: Record<string, unknown>): HubActionField[] {\n const fields: HubActionField[] = []\n const preview = presentation.preview\n switch (preview.kind) {\n case 'pull-request': {\n const pr = prField(result)\n if (pr) fields.push(pr)\n const head = readText(result, 'branch') ?? preview.head\n const base = readText(result, 'base') ?? preview.base\n if (head && base) fields.push({ label: 'Branch', value: `${head} → ${base}`, mono: true })\n const commit = asRecord(result.commit)\n const sha = readText(commit, 'sha')\n if (sha) fields.push({ label: 'Commit', value: shortSha(sha)!, href: resultLink(commit), mono: true })\n const repo = readText(result, 'repository') ?? preview.repository\n if (repo) fields.push({ label: 'Repository', value: repo, href: `https://github.com/${repo}` })\n return fields\n }\n case 'commit': {\n const sha = readText(result, 'sha')\n if (sha) fields.push({ label: 'Commit', value: shortSha(sha)!, href: resultLink(result)?.includes('api.github.com') ? undefined : resultLink(result), mono: true })\n if (preview.message) fields.push({ label: 'Message', value: clipped(preview.message, 120) })\n return fields\n }\n case 'branch': {\n const branch = branchOf(readText(result, 'ref')) ?? preview.branch\n if (branch && preview.repository) fields.push({ label: 'Branch', value: branch, href: `https://github.com/${preview.repository}/tree/${branch}`, mono: true })\n return fields\n }\n case 'files': {\n const sha = readText(result, 'sha')\n if (sha) fields.push({ label: 'Tree', value: shortSha(sha)!, mono: true })\n return fields\n }\n case 'email': {\n if (preview.subject) fields.push({ label: 'Subject', value: clipped(preview.subject, 120) })\n if (preview.cc.length) fields.push({ label: 'Cc', value: preview.cc.join(', ') })\n return fields\n }\n case 'payment': {\n const amount = readNumber(result, 'amount', 'amount_total', 'total') ?? preview.amount\n const currency = readText(result, 'currency') ?? preview.currency\n if (amount !== undefined) fields.push({ label: 'Amount', value: formatMinorAmount(amount, currency) })\n const customer = readText(result, 'customer_email', 'customer') ?? preview.customer\n if (customer) fields.push({ label: 'Customer', value: customer, mono: customer.startsWith('cus_') })\n const status = readText(result, 'status')\n if (status) fields.push({ label: 'Status', value: status })\n return fields\n }\n case 'speech': {\n const seconds = readNumber(result, 'durationSeconds', 'duration')\n if (seconds !== undefined) fields.push({ label: 'Length', value: `${Math.round(seconds * 10) / 10} s` })\n if (preview.voice) fields.push({ label: 'Voice', value: preview.voice })\n return fields\n }\n case 'event': {\n if (preview.start) fields.push({ label: 'When', value: formatWhen(preview.start, preview.end) })\n if (preview.attendees.length) fields.push({ label: 'Guests', value: people(preview.attendees) })\n return fields\n }\n case 'call': {\n const status = readText(asRecord(result.call), 'status') ?? readText(result, 'status')\n if (status) fields.push({ label: 'Status', value: status })\n return fields\n }\n default: {\n const id = readText(result, 'id', 'number', 'key')\n if (id) fields.push({ label: 'ID', value: id, mono: true })\n const status = readText(result, 'status', 'state')\n if (status) fields.push({ label: 'Status', value: status })\n const name = readText(result, 'name', 'title')\n if (name) fields.push({ label: 'Name', value: clipped(name, 80) })\n return fields\n }\n }\n}\n\n/** A start and optional end in the viewer's locale: `Tue, Oct 14, 3:00 – 3:30 PM`. */\nexport function formatWhen(start: string, end?: string): string {\n const from = new Date(start)\n if (Number.isNaN(from.getTime())) return end ? `${start} – ${end}` : start\n const day = from.toLocaleDateString(undefined, { weekday: 'short', month: 'short', day: 'numeric' })\n const time = (date: Date) => date.toLocaleTimeString(undefined, { hour: 'numeric', minute: '2-digit' })\n const to = end ? new Date(end) : null\n if (!to || Number.isNaN(to.getTime())) return `${day}, ${time(from)}`\n return to.toDateString() === from.toDateString() ? `${day}, ${time(from)} – ${time(to)}` : `${day}, ${time(from)} – ${to.toLocaleDateString(undefined, { month: 'short', day: 'numeric' })}, ${time(to)}`\n}\n\nfunction receiptFiles(result: Record<string, unknown>): HubActionFile[] | undefined {\n if (!Array.isArray(result.files)) return undefined\n return result.files.flatMap((entry): HubActionFile[] => {\n const file = asRecord(entry)\n const path = text(file.path) ?? text(file.filename)\n if (!path) return []\n const status = text(file.status)\n const change = status === 'added' ? 'added' : status === 'removed' || status === 'deleted' ? 'deleted' : 'modified'\n return [{ path, change, additions: readNumber(file, 'additions'), deletions: readNumber(file, 'deletions') }]\n })\n}\n\n/** What a decided call did: its past-tense title, key fields, link, media and files. */\nexport function hubActionReceipt(item: HubApprovalItem): HubActionReceipt {\n const presentation = presentHubAction(item)\n const result = asRecord(unwrapHubResult(item.result))\n const status: HubActionReceipt['status'] = item.phase === 'done' || item.phase === 'unknown' || item.phase === 'failed' || item.phase === 'denied' || item.phase === 'expired'\n ? item.phase\n : item.phase === 'queued' || item.phase === 'running' ? 'running' : 'waiting'\n const base = { provider: presentation.provider, status }\n if (status === 'denied') return { ...base, title: `Denied: ${presentation.title}`, fields: [] }\n if (status === 'expired') return { ...base, title: `Expired: ${presentation.title}`, fields: [] }\n if (status === 'unknown') return { ...base, title: `May have run: ${presentation.title}`, fields: [], warning: HUB_APPROVAL_UNKNOWN_WARNING }\n if (status === 'failed') return { ...base, title: `Could not ${presentation.action.charAt(0).toLowerCase()}${presentation.action.slice(1)}`, fields: [], ...(item.error ? { error: item.error } : {}) }\n if (status !== 'done') return { ...base, title: presentation.title, fields: [] }\n const title = receiptTitle(item, presentation, result)\n const fields = receiptFields(item, presentation, result)\n const pull = asRecord(result.pullRequest)\n const href = presentation.preview.kind === 'post'\n ? postLink(item, result)\n : resultLink(pull) ?? (item.actionPath.startsWith('github.git.') ? undefined : resultLink(result))\n const audio = readText(result, 'audioUrl')\n const seconds = readNumber(result, 'durationSeconds', 'duration')\n const files = receiptFiles(result)\n return {\n ...base,\n title,\n ...(href ? { href } : {}),\n fields,\n ...(files && files.length ? { files } : {}),\n ...(audio && /^https:\\/\\//.test(audio) ? { media: { kind: 'audio' as const, src: audio, ...(seconds !== undefined ? { seconds } : {}) } } : {}),\n }\n}\n","/**\n * `./hub-approvals` — Hub actions that wait on a person, in the owner's words.\n *\n * Pure and browser-safe: a host maps its held calls into {@link HubApprovalItem}s\n * and every surface — the approval dock above the composer, the Approvals list\n * beside the conversation, and the receipts in the transcript — renders the\n * same presentation from them. Servers use {@link summarizeHubResult} to store\n * a bounded result with the decision.\n */\n\nimport type { HubApprovalPhase } from './types'\n\nexport * from './types'\nexport {\n formatMinorAmount,\n formatWhen,\n hubActionFiles,\n hubActionReceipt,\n hubProviderName,\n pastTense,\n presentHubAction,\n splitHubActionPath,\n} from './present'\nexport { summarizeHubResult, unwrapHubResult } from './result'\n\n/** A phase as a status label. */\nexport const HUB_APPROVAL_PHASE_LABELS: Record<HubApprovalPhase, string> = {\n waiting: 'Waiting for you',\n blocked: 'Waiting for the owner',\n queued: 'Queued',\n running: 'Running',\n done: 'Done',\n failed: 'Failed',\n unknown: 'May have run',\n denied: 'Denied',\n expired: 'Expired',\n}\n\n/**\n * The part type marking a user message that resumes the agent after a\n * decision. The message's text goes to the agent unchanged; a transcript\n * renders the decided actions' receipts in its place, never a chat bubble.\n */\nexport const HUB_APPROVAL_RESUME_PART = 'hub-approval-resume'\n\nexport interface HubApprovalResumePart {\n type: typeof HUB_APPROVAL_RESUME_PART\n /** The assistant message whose held calls this resume reports. */\n messageId: string\n}\n\n/** The resume marker among a message's parts, if it carries one. */\nexport function hubApprovalResumePart(parts: ReadonlyArray<Record<string, unknown>> | null | undefined): HubApprovalResumePart | null {\n for (const part of parts ?? []) {\n if (part?.type === HUB_APPROVAL_RESUME_PART && typeof part.messageId === 'string' && part.messageId) {\n return { type: HUB_APPROVAL_RESUME_PART, messageId: part.messageId }\n }\n }\n return null\n}\n"],"mappings":"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","names":["text","words","url"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/teams/members-api.ts"],"sourcesContent":["/**\n * Framework-neutral members API for the teams module: the invite / list /\n * update-role / remove / accept-invite logic, lifted out of any one app's route\n * file. Each app mounts these in its own route with its own auth — the handlers\n * take an already-authenticated `actor`, the parsed inputs, the `db` + `tables`\n * from `createTeamTables`, the product's user + workspace tables, and the\n * workspace-access API from `createWorkspaceAccess`. They return web-standard\n * `Response`s (available in Workers, Node 18+, Deno, browsers), so\n * \"framework-neutral\" is literal: no Remix/React-Router/Express import anywhere.\n *\n * Two OPTIONAL seams an app wires only if it needs them:\n * - `enforceSeat` — billing/seat-limit gate, called at invite time only when\n * the invite would consume a NEW billable seat. An app without seat billing\n * passes nothing and seats are never checked.\n * - `memberSyncSeam` — fire-and-forget propagation of membership changes to a\n * sandbox/external system (add on accept, role on update, remove on\n * delete). An app without sandbox sync passes nothing. Fail-soft by\n * contract: a thrown sync is caught and never blocks the DB mutation.\n *\n * Imports `drizzle-orm`, so this is a subpath, never re-exported from root.\n */\n\nimport { and, eq, isNull } from 'drizzle-orm'\nimport { INVITATION_EMAIL_HINT, isInvitationEmail } from './invitations'\nimport {\n type AssignableWorkspaceRole,\n type WorkspaceRole,\n canManageWorkspaceMemberRole,\n hasWorkspaceRole,\n isAssignableWorkspaceRole,\n organizationRoleGrantsWorkspaceOwner,\n} from './roles'\nimport { generateInviteToken } from './invite'\nimport type { TeamDatabase, WorkspaceAccessApi } from './drizzle/access'\nimport type { TeamParentTable, TeamTables } from './drizzle/schema'\n\n/** The authenticated caller — apps resolve this from their own session layer. */\nexport interface MembersApiActor {\n id: string\n email?: string | null\n}\n\n/**\n * The product's user table, narrowed to the columns the member queries read.\n * Adopters pass their real drizzle user table.\n */\nexport interface UserLookupTable {\n id: any\n name: any\n email: any\n}\n\n/**\n * The product's workspace table, narrowed to the columns the member queries\n * read. The handlers join it to resolve a workspace's organization (the same\n * table passed to createTeamTables as workspaceTable).\n */\nexport interface WorkspaceLookupTable {\n id: any\n organizationId: any\n}\n\n/**\n * Optional billing seat gate. The seam resolves the count input itself (it\n * owns the plan/seat model) and throws when over the limit; the handler turns a\n * thrown `SeatLimitError` into a 402. Called only when an invite would consume\n * a NEW seat (invitee is not already an org member and has no pending org\n * invite) — the same guard gtm uses, so the seam never fires on a no-op invite.\n */\nexport interface EnforceSeatSeam {\n (input: { actorId: string; organizationId: string }): Promise<void> | void\n}\n\n/** Thrown by an `enforceSeat` seam to deny an invite; serialized to a 402. */\nexport class SeatLimitError extends Error {\n readonly status: number\n readonly capability?: string\n readonly requiredPlan?: string\n constructor(message: string, opts: { status?: number; capability?: string; requiredPlan?: string } = {}) {\n super(message)\n this.name = 'SeatLimitError'\n this.status = opts.status ?? 402\n this.capability = opts.capability\n this.requiredPlan = opts.requiredPlan\n }\n}\n\n/**\n * Optional membership-change propagation to an external system (e.g. sandbox).\n * Every method is fire-and-forget and fail-soft: the handler awaits nothing and\n * swallows rejections, so an unavailable downstream never blocks the mutation.\n * Each fires only for members with a real `userId` (never email-only invites).\n */\nexport interface MemberSyncSeam {\n add?(input: { workspaceId: string; userId: string; role: WorkspaceRole }): Promise<void> | void\n role?(input: { workspaceId: string; userId: string; role: WorkspaceRole }): Promise<void> | void\n remove?(input: { workspaceId: string; userId: string }): Promise<void> | void\n}\n\n/** Define configuration options for managing team members and workspace access APIs */\nexport interface MembersApiOptions {\n db: TeamDatabase\n tables: TeamTables\n /** The product's user table (FK target passed to createTeamTables). */\n userTable: TeamParentTable & UserLookupTable\n /** The product's workspace table (FK target passed to createTeamTables). */\n workspaceTable: TeamParentTable & WorkspaceLookupTable\n /** Workspace-access API from createWorkspaceAccess — RBAC stays one source. */\n access: Pick<WorkspaceAccessApi, 'getWorkspaceAccess'>\n enforceSeat?: EnforceSeatSeam\n memberSyncSeam?: MemberSyncSeam\n}\n\n/** Define the structure of a workspace member entry with identification, role, and status details */\nexport interface MemberListEntry {\n id: string\n userId: string | null\n organizationMemberId: string | null\n role: WorkspaceRole\n name: string | null\n email: string | null\n invitedAt: Date | number | null\n acceptedAt: Date | number | null\n inherited: boolean\n}\n\n/**\n * Build the members API bound to one product's db/tables/access. Returns five\n * handlers; an app maps its route methods onto them (GET→list, POST→invite,\n * PATCH→updateRole, DELETE→remove; accept on its own route).\n */\nexport function createMembersApi(opts: MembersApiOptions) {\n const { db, tables, userTable, workspaceTable, access, enforceSeat, memberSyncSeam } = opts\n const { organizations, organizationMembers, workspaceMembers } = tables\n const users = userTable\n const workspaces = workspaceTable\n\n function fireSync(op: () => Promise<void> | void) {\n try {\n Promise.resolve(op()).catch(() => {})\n } catch {\n // seam threw synchronously — swallow; sync is best-effort by contract.\n }\n }\n\n async function listMembers(input: { workspaceId: string; actor: MembersApiActor }): Promise<Response> {\n const accessRow = await access.getWorkspaceAccess(input.workspaceId, input.actor.id)\n if (!accessRow) return Response.json({ error: 'Workspace not found' }, { status: 404 })\n\n const organizationId = (accessRow.workspace as { organizationId: string }).organizationId\n\n const [projectMembers, orgAdmins] = await Promise.all([\n db\n .select({\n id: workspaceMembers.id,\n organizationMemberId: workspaceMembers.organizationMemberId,\n userId: workspaceMembers.userId,\n role: workspaceMembers.role,\n invitedAt: workspaceMembers.invitedAt,\n acceptedAt: workspaceMembers.acceptedAt,\n inviteEmail: workspaceMembers.inviteEmail,\n userName: users.name,\n userEmail: users.email,\n })\n .from(workspaceMembers)\n .leftJoin(users, eq(users.id, workspaceMembers.userId))\n .where(eq(workspaceMembers.workspaceId, input.workspaceId)),\n db\n .select({\n id: organizationMembers.id,\n userId: organizationMembers.userId,\n orgRole: organizationMembers.role,\n userName: users.name,\n userEmail: users.email,\n createdAt: organizationMembers.createdAt,\n })\n .from(organizationMembers)\n .innerJoin(users, eq(users.id, organizationMembers.userId))\n .where(eq(organizationMembers.organizationId, organizationId)),\n ])\n\n const explicitOrgMemberIds = new Set(\n projectMembers.map((m: any) => m.organizationMemberId).filter(Boolean),\n )\n const members: MemberListEntry[] = [\n ...orgAdmins\n .filter((m: any) => organizationRoleGrantsWorkspaceOwner(m.orgRole) && !explicitOrgMemberIds.has(m.id))\n .map((m: any) => ({\n id: `org:${m.id}`,\n userId: m.userId,\n organizationMemberId: m.id,\n role: 'owner' as WorkspaceRole,\n name: m.userName,\n email: m.userEmail,\n invitedAt: m.createdAt,\n acceptedAt: m.createdAt,\n inherited: true,\n })),\n ...projectMembers.map((m: any) => ({\n id: m.id,\n userId: m.userId,\n organizationMemberId: m.organizationMemberId,\n role: m.role as WorkspaceRole,\n name: m.userName,\n email: m.userEmail ?? m.inviteEmail,\n invitedAt: m.invitedAt,\n acceptedAt: m.acceptedAt,\n inherited: false,\n })),\n ]\n\n return Response.json({ members, currentRole: accessRow.role, organizationId })\n }\n\n async function inviteMember(input: {\n workspaceId: string\n actor: MembersApiActor\n email?: string\n role?: string\n }): Promise<Response> {\n if (!input.email) return Response.json({ error: 'Missing email' }, { status: 400 })\n if (!isInvitationEmail(input.email)) return Response.json({ error: INVITATION_EMAIL_HINT }, { status: 400 })\n const requestedRole = input.role ?? 'editor'\n if (!isAssignableWorkspaceRole(requestedRole)) {\n return Response.json({ error: 'Invalid role. Must be viewer, editor, or admin.' }, { status: 400 })\n }\n const assignRole: AssignableWorkspaceRole = requestedRole\n const accessRow = await access.getWorkspaceAccess(input.workspaceId, input.actor.id, 'admin')\n if (!accessRow) return Response.json({ error: 'Workspace not found' }, { status: 404 })\n if (!hasWorkspaceRole(accessRow.role, assignRole)) {\n return Response.json({ error: 'Cannot assign a role higher than your own' }, { status: 403 })\n }\n\n const organizationId = (accessRow.workspace as { organizationId: string }).organizationId\n const normalizedEmail = input.email.toLowerCase().trim()\n const invitee = await getUserByEmail(normalizedEmail)\n const existingOrgMember = invitee ? await getOrganizationMember(organizationId, invitee.id) : null\n\n if (organizationRoleGrantsWorkspaceOwner(existingOrgMember?.role)) {\n return Response.json({ error: 'Organization admins already have project access' }, { status: 409 })\n }\n\n const existingInvite = await findExistingProjectInvite(input.workspaceId, normalizedEmail, invitee?.id)\n if (existingInvite) {\n return Response.json({ error: 'This collaborator is already invited to this project' }, { status: 409 })\n }\n\n if (enforceSeat && !existingOrgMember && !(await hasPendingOrgInvite(organizationId, normalizedEmail))) {\n try {\n await enforceSeat({ actorId: input.actor.id, organizationId })\n } catch (err) {\n if (err instanceof SeatLimitError) {\n return Response.json(\n { error: err.message, capability: err.capability, requiredPlan: err.requiredPlan },\n { status: err.status },\n )\n }\n throw err\n }\n }\n\n const token = generateInviteToken()\n const [member] = await db\n .insert(workspaceMembers)\n .values({\n workspaceId: input.workspaceId,\n organizationMemberId: existingOrgMember?.id ?? null,\n userId: invitee?.id ?? null,\n role: assignRole,\n invitedBy: input.actor.id,\n inviteEmail: normalizedEmail,\n inviteToken: token,\n })\n .returning()\n\n return Response.json({ member, inviteToken: token })\n }\n\n async function updateMemberRole(input: {\n workspaceId: string\n actor: MembersApiActor\n memberId?: string\n role?: string\n }): Promise<Response> {\n if (!input.memberId || !input.role) {\n return Response.json({ error: 'Missing memberId or role' }, { status: 400 })\n }\n if (input.memberId.startsWith('org:')) {\n return Response.json({ error: 'Organization owner/admin project access is managed at the organization level' }, { status: 403 })\n }\n if (!isAssignableWorkspaceRole(input.role)) {\n return Response.json({ error: 'Invalid role' }, { status: 400 })\n }\n\n const accessRow = await access.getWorkspaceAccess(input.workspaceId, input.actor.id, 'admin')\n if (!accessRow) return Response.json({ error: 'Workspace not found' }, { status: 404 })\n if (!hasWorkspaceRole(accessRow.role, input.role)) {\n return Response.json({ error: 'Cannot assign a role higher than your own' }, { status: 403 })\n }\n\n const [target] = await db\n .select({ id: workspaceMembers.id, role: workspaceMembers.role, userId: workspaceMembers.userId })\n .from(workspaceMembers)\n .where(and(eq(workspaceMembers.id, input.memberId), eq(workspaceMembers.workspaceId, input.workspaceId)))\n .limit(1)\n\n if (!target) return Response.json({ error: 'Member not found' }, { status: 404 })\n if (target.userId === input.actor.id) return Response.json({ error: 'Cannot change your own role' }, { status: 403 })\n if (!canManageWorkspaceMemberRole(accessRow.role, target.role as WorkspaceRole)) {\n return Response.json({ error: 'Cannot modify a member with equal or higher role' }, { status: 403 })\n }\n\n await db.update(workspaceMembers).set({ role: input.role }).where(eq(workspaceMembers.id, input.memberId))\n if (target.userId && memberSyncSeam?.role) {\n const userId = target.userId\n const nextRole = input.role as WorkspaceRole\n fireSync(() => memberSyncSeam.role!({ workspaceId: input.workspaceId, userId, role: nextRole }))\n }\n return Response.json({ success: true })\n }\n\n async function removeMember(input: {\n workspaceId: string\n actor: MembersApiActor\n memberId?: string\n }): Promise<Response> {\n if (!input.memberId) return Response.json({ error: 'Missing memberId' }, { status: 400 })\n if (input.memberId.startsWith('org:')) {\n return Response.json({ error: 'Organization owner/admin project access is managed at the organization level' }, { status: 403 })\n }\n\n const accessRow = await access.getWorkspaceAccess(input.workspaceId, input.actor.id, 'admin')\n if (!accessRow) return Response.json({ error: 'Workspace not found' }, { status: 404 })\n\n const [target] = await db\n .select({ id: workspaceMembers.id, role: workspaceMembers.role, userId: workspaceMembers.userId })\n .from(workspaceMembers)\n .where(and(eq(workspaceMembers.id, input.memberId), eq(workspaceMembers.workspaceId, input.workspaceId)))\n .limit(1)\n\n if (!target) return Response.json({ error: 'Member not found' }, { status: 404 })\n if (target.userId === input.actor.id) return Response.json({ error: 'Cannot remove yourself from this project' }, { status: 403 })\n if (!canManageWorkspaceMemberRole(accessRow.role, target.role as WorkspaceRole)) {\n return Response.json({ error: 'Cannot remove a member with equal or higher role' }, { status: 403 })\n }\n\n await db.delete(workspaceMembers).where(eq(workspaceMembers.id, input.memberId))\n if (target.userId && memberSyncSeam?.remove) {\n const userId = target.userId\n fireSync(() => memberSyncSeam.remove!({ workspaceId: input.workspaceId, userId }))\n }\n return Response.json({ success: true })\n }\n\n async function acceptInvite(input: { token?: string; actor: MembersApiActor }): Promise<Response> {\n if (!input.token || typeof input.token !== 'string') {\n return Response.json({ error: 'Missing invite token' }, { status: 400 })\n }\n\n const [invite] = await db\n .select()\n .from(workspaceMembers)\n .where(eq(workspaceMembers.inviteToken, input.token))\n .limit(1)\n\n if (!invite) return Response.json({ error: 'Invalid or expired invite' }, { status: 404 })\n if (invite.acceptedAt) return Response.json({ error: 'Invite already accepted' }, { status: 409 })\n\n if (invite.inviteEmail && invite.inviteEmail.toLowerCase() !== input.actor.email?.toLowerCase()) {\n return Response.json(\n { error: `This invite was sent to ${invite.inviteEmail}. Please sign in with that email.` },\n { status: 403 },\n )\n }\n\n const [workspace] = await db\n .select({ id: workspaces.id, organizationId: workspaces.organizationId })\n .from(workspaces)\n .where(eq(workspaces.id, invite.workspaceId))\n .limit(1)\n\n if (!workspace) return Response.json({ error: 'Project not found' }, { status: 404 })\n const organizationId = (workspace as { organizationId: string }).organizationId\n\n // Atomic accept: only succeeds if the token still exists and is unaccepted,\n // so two concurrent accepts can't both win.\n const updated = await db\n .update(workspaceMembers)\n .set({\n userId: input.actor.id,\n acceptedAt: new Date(),\n inviteToken: null,\n })\n .where(and(\n eq(workspaceMembers.inviteToken, input.token),\n isNull(workspaceMembers.acceptedAt),\n ))\n .returning({ id: workspaceMembers.id })\n\n if (updated.length === 0) return Response.json({ error: 'Invite already accepted' }, { status: 409 })\n\n const [orgMember] = await db\n .insert(organizationMembers)\n .values({\n organizationId,\n userId: input.actor.id,\n role: 'member',\n })\n .onConflictDoUpdate({\n target: [organizationMembers.organizationId, organizationMembers.userId],\n set: { updatedAt: new Date() },\n })\n .returning()\n\n await db\n .update(workspaceMembers)\n .set({ organizationMemberId: invite.organizationMemberId ?? orgMember!.id })\n .where(eq(workspaceMembers.id, updated[0]!.id))\n\n if (memberSyncSeam?.add) {\n const role = invite.role as WorkspaceRole\n fireSync(() => memberSyncSeam.add!({ workspaceId: invite.workspaceId, userId: input.actor.id, role }))\n }\n\n return Response.json({\n success: true,\n workspaceId: invite.workspaceId,\n role: invite.role,\n })\n }\n\n // ── internal query helpers ──\n\n async function getUserByEmail(email: string) {\n const [user] = await db\n .select({ id: users.id, email: users.email })\n .from(users)\n .where(eq(users.email, email))\n .limit(1)\n return user ?? null\n }\n\n async function getOrganizationMember(organizationId: string, userId: string) {\n const [member] = await db\n .select()\n .from(organizationMembers)\n .where(and(eq(organizationMembers.organizationId, organizationId), eq(organizationMembers.userId, userId)))\n .limit(1)\n return member ?? null\n }\n\n async function findExistingProjectInvite(workspaceId: string, email: string, userId?: string) {\n const byEmail = await db\n .select({ id: workspaceMembers.id })\n .from(workspaceMembers)\n .where(and(eq(workspaceMembers.workspaceId, workspaceId), eq(workspaceMembers.inviteEmail, email)))\n .limit(1)\n if (byEmail[0]) return byEmail[0]\n if (!userId) return null\n const byUser = await db\n .select({ id: workspaceMembers.id })\n .from(workspaceMembers)\n .where(and(eq(workspaceMembers.workspaceId, workspaceId), eq(workspaceMembers.userId, userId)))\n .limit(1)\n return byUser[0] ?? null\n }\n\n async function hasPendingOrgInvite(organizationId: string, email: string): Promise<boolean> {\n const [row] = await db\n .select({ id: workspaceMembers.id })\n .from(workspaceMembers)\n .innerJoin(workspaces, eq(workspaces.id, workspaceMembers.workspaceId))\n .where(and(\n eq(workspaces.organizationId, organizationId),\n eq(workspaceMembers.inviteEmail, email),\n isNull(workspaceMembers.acceptedAt),\n ))\n .limit(1)\n return Boolean(row)\n }\n\n return { listMembers, inviteMember, updateMemberRole, removeMember, acceptInvite }\n}\n"],"mappings":"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","names":[]}
|