@tangle-network/agent-app 0.49.33 → 0.49.35
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/bin/tangle-agent/home-maintenance.py +280 -0
- package/bin/tangle-agent/home.mjs +166 -0
- package/bin/tangle-agent/provision.mjs +168 -0
- package/bin/tangle-agent/runtime.mjs +151 -0
- package/bin/tangle-agent.mjs +26 -0
- package/dist/VaultPane-2SJSP6TS.js +7 -0
- package/dist/app-auth/index.d.ts +14 -2
- package/dist/app-auth/index.js +3 -3
- package/dist/app-auth/index.js.map +1 -1
- package/dist/assistant/index.js +7 -7
- package/dist/chat-react/index.js +1 -1
- package/dist/chat-routes/index.js +4 -4
- package/dist/chat-store/index.js +2 -2
- package/dist/{chunk-FCUWRMAF.js → chunk-56SVXAIK.js} +14 -9
- package/dist/chunk-56SVXAIK.js.map +1 -0
- package/dist/chunk-6CV66YOB.js +99 -0
- package/dist/chunk-6CV66YOB.js.map +1 -0
- package/dist/{chunk-V4YGTOCS.js → chunk-MIFVOYZ7.js} +5 -5
- package/dist/{chunk-FXWNNHPC.js → chunk-MVIIH2EQ.js} +165 -72
- package/dist/chunk-MVIIH2EQ.js.map +1 -0
- package/dist/{chunk-TNWQA5XJ.js → chunk-NLNM5PP6.js} +5 -4
- package/dist/chunk-NLNM5PP6.js.map +1 -0
- package/dist/{chunk-4PZE7XAM.js → chunk-S3344RBY.js} +4 -4
- package/dist/{chunk-4S36HWE4.js → chunk-WAMJSZYB.js} +1 -1
- package/dist/{chunk-VIL6MHHQ.js → chunk-XTLYE2VO.js} +6 -6
- package/dist/design-canvas-react/index.js +1 -1
- package/dist/design-canvas-react/lazy.js +1 -1
- package/dist/hosted-agent/general-skill.d.ts +2 -0
- package/dist/hosted-agent/general.d.ts +53 -0
- package/dist/hosted-agent/index.d.ts +9 -1
- package/dist/hosted-agent/index.js +294 -7
- package/dist/hosted-agent/index.js.map +1 -1
- package/dist/hosted-agent/react/LineBilling.d.ts +3 -0
- package/dist/hosted-agent/react/LineMembers.d.ts +3 -0
- package/dist/hosted-agent/react/LineSetup.d.ts +3 -0
- package/dist/hosted-agent/react/contracts.d.ts +136 -0
- package/dist/hosted-agent/react/index.d.ts +5 -0
- package/dist/hosted-agent/react/index.js +573 -0
- package/dist/hosted-agent/react/index.js.map +1 -0
- package/dist/hosted-agent/react/lines.css +110 -0
- package/dist/hosted-agent/react/styles.d.ts +2 -0
- package/dist/hosted-agent/react/styles.js +1 -0
- package/dist/hosted-agent/react/styles.js.map +1 -0
- package/dist/hosted-agent/workflows.d.ts +17 -0
- package/dist/{mention-editor-4PYZJM5A.js → mention-editor-ELAINF6H.js} +3 -2
- package/dist/mention-editor-ELAINF6H.js.map +1 -0
- package/dist/platform/index.js +1 -1
- package/dist/platform/sso.d.ts +74 -7
- package/dist/profile/home.d.ts +23 -0
- package/dist/profile/index.d.ts +1 -0
- package/dist/profile/index.js +9 -1
- package/dist/profile/index.js.map +1 -1
- package/dist/public-consultation/index.js +4 -4
- package/dist/stories/hosted-agent/fixtures.d.ts +34 -0
- package/dist/teams/index.js +5 -5
- package/dist/teams/invitations-api.js +4 -4
- package/dist/theme/tokens.css +20 -3
- package/dist/vault/ConfirmDialog.d.ts +1 -1
- package/dist/vault/VaultPane.d.ts +2 -2
- package/dist/vault/contracts.d.ts +8 -0
- package/dist/vault/index.js +1 -1
- package/dist/vault/lazy.d.ts +3 -3
- package/dist/vault/lazy.js +1 -1
- package/dist/vault/lazy.js.map +1 -1
- package/dist/web-react/index.js +23 -23
- package/package.json +47 -14
- package/dist/VaultPane-32HVWZ6A.js +0 -7
- package/dist/chunk-FCUWRMAF.js.map +0 -1
- package/dist/chunk-FXWNNHPC.js.map +0 -1
- package/dist/chunk-TNWQA5XJ.js.map +0 -1
- package/dist/mention-editor-4PYZJM5A.js.map +0 -1
- /package/dist/{VaultPane-32HVWZ6A.js.map → VaultPane-2SJSP6TS.js.map} +0 -0
- /package/dist/{chunk-V4YGTOCS.js.map → chunk-MIFVOYZ7.js.map} +0 -0
- /package/dist/{chunk-4PZE7XAM.js.map → chunk-S3344RBY.js.map} +0 -0
- /package/dist/{chunk-4S36HWE4.js.map → chunk-WAMJSZYB.js.map} +0 -0
- /package/dist/{chunk-VIL6MHHQ.js.map → chunk-XTLYE2VO.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/hosted-agent/react/LineSetup.tsx","../../../src/hosted-agent/react/LineMembers.tsx","../../../src/hosted-agent/react/LineBilling.tsx"],"sourcesContent":["import { useEffect, useLayoutEffect, useRef, useState } from 'react'\nimport type { Line } from '@tangle-network/sandbox'\nimport type {\n LineBoxMode, LineConnectionOption, LineIdentityKind, LineIdentityOption,\n LineSetupLine, LineSetupProps, LineSetupSnapshot,\n} from './contracts'\n\nconst useBrowserLayoutEffect = typeof document !== 'undefined' ? useLayoutEffect : useEffect\n\nconst TRANSPORT: Record<Line['transport'], string> = {\n imessage: 'iMessage', sms: 'SMS', whatsapp: 'WhatsApp', email: 'Email',\n}\nconst IDENTITY: Record<LineIdentityKind, string> = {\n handle: 'Inkbox handle', number: 'Dedicated number', email: 'Email line',\n}\n\ntype Choice = { key: string; connection: LineConnectionOption; identity: LineIdentityOption }\n\nfunction message(error: unknown): string {\n return error instanceof Error ? error.message : 'The line request failed. Try again.'\n}\n\nfunction dateTime(value: string): string {\n const date = new Date(value)\n return Number.isNaN(date.getTime()) ? value : date.toLocaleString()\n}\n\nfunction choicesFor(snapshot: LineSetupSnapshot): Choice[] {\n return (snapshot.connections ?? []).flatMap(connection => connection.identities.map((identity, index) => ({\n key: `${connection.id}:${index}`,\n connection,\n identity,\n })))\n}\n\nfunction canOfferReconnect(line: LineSetupLine, choice: Choice): boolean {\n if (line.connectionId !== choice.connection.id || line.transport !== choice.identity.transport || line.targetId !== null) return false\n if (line.providerNumberId === null) return line.transport !== 'whatsapp' && !choice.identity.phoneNumberId && !choice.identity.requiresPhoneNumberId\n return choice.identity.phoneNumberId === line.providerNumberId ||\n (!choice.identity.phoneNumberId && choice.identity.requiresPhoneNumberId === true)\n}\n\nfunction matchesLineIdentity(line: LineSetupLine, identity: LineIdentityOption, numberId: string): boolean {\n if (line.transport !== identity.transport) return false\n if (line.providerNumberId === null) return line.transport !== 'whatsapp' && !identity.phoneNumberId && !identity.requiresPhoneNumberId\n return numberId === line.providerNumberId\n}\n\n/** Set up an owned identity through a host route backed by sandbox.lines and Hub. */\nexport function LineSetup({ client, scopeKey, initialTargetId, canManage, onNotice }: LineSetupProps) {\n const clientRef = useRef(client)\n clientRef.current = client\n const sequence = useRef(0)\n const incarnation = useRef(0)\n const confirmButton = useRef<HTMLButtonElement>(null)\n const heading = useRef<HTMLHeadingElement>(null)\n const disconnectTriggers = useRef(new Map<string, HTMLButtonElement>())\n const restoreFocusTo = useRef<string | null>(null)\n useBrowserLayoutEffect(() => {\n incarnation.current++\n return () => { incarnation.current++ }\n }, [scopeKey, initialTargetId])\n const [loadedSnapshot, setLoadedSnapshot] = useState<{ scopeKey: string; initialTargetId: string | undefined; value: LineSetupSnapshot } | null>(null)\n const snapshot = loadedSnapshot?.scopeKey === scopeKey && loadedSnapshot.initialTargetId === initialTargetId ? loadedSnapshot.value : null\n const [loading, setLoading] = useState(true)\n const [refreshFailed, setRefreshFailed] = useState(false)\n const [busy, setBusy] = useState(false)\n const [error, setError] = useState<string | null>(null)\n const [kind, setKind] = useState<LineIdentityKind>('handle')\n const [choiceKey, setChoiceKey] = useState('')\n const [targetId, setTargetId] = useState(initialTargetId ?? '')\n const [boxMode, setBoxMode] = useState<LineBoxMode>('per-member')\n const [phoneNumberId, setPhoneNumberId] = useState('')\n const [confirmLineId, setConfirmLineId] = useState<string | null>(null)\n\n function stillCurrent(requestIncarnation: number): boolean {\n return requestIncarnation === incarnation.current\n }\n\n async function reload(requestIncarnation: number): Promise<LineSetupSnapshot | null> {\n const request = ++sequence.current\n try {\n const next = await clientRef.current.load()\n if (request !== sequence.current || !stillCurrent(requestIncarnation)) return null\n setLoadedSnapshot({ scopeKey, initialTargetId, value: next })\n setError(null)\n setRefreshFailed(false)\n setTargetId(current => next.targets.some(target => target.id === current)\n ? current : next.targets.find(target => target.id === initialTargetId)?.id ?? next.targets[0]?.id ?? '')\n setLoading(false)\n return next\n } catch (cause) {\n if (request !== sequence.current || !stillCurrent(requestIncarnation)) return null\n setError(message(cause))\n setRefreshFailed(true)\n setLoading(false)\n return null\n }\n }\n\n function retry() {\n if (loading) return\n setLoading(true)\n void reload(incarnation.current)\n }\n\n function closeConfirmation(lineId: string) {\n restoreFocusTo.current = lineId\n setConfirmLineId(null)\n }\n\n useEffect(() => {\n setLoading(true)\n setLoadedSnapshot(null)\n setError(null)\n setRefreshFailed(false)\n setBusy(false)\n setKind('handle')\n setChoiceKey('')\n setTargetId(initialTargetId ?? '')\n setBoxMode('per-member')\n setPhoneNumberId('')\n setConfirmLineId(null)\n restoreFocusTo.current = null\n void reload(incarnation.current)\n return () => { sequence.current++ }\n }, [scopeKey, initialTargetId])\n\n useEffect(() => {\n if (confirmLineId) confirmButton.current?.focus()\n }, [confirmLineId])\n\n useEffect(() => {\n const lineId = restoreFocusTo.current\n if (confirmLineId !== null || lineId === null) return\n restoreFocusTo.current = null\n const trigger = disconnectTriggers.current.get(lineId)\n if (trigger && !trigger.disabled) trigger.focus()\n else heading.current?.focus()\n }, [confirmLineId, snapshot, refreshFailed])\n\n const allChoices = snapshot ? choicesFor(snapshot) : []\n const choices = snapshot ? allChoices.filter(choice => {\n const occupied = snapshot.lines.filter(line => line.status !== 'released' && line.transport === choice.identity.transport)\n const [line] = occupied\n return occupied.length === 0 || (occupied.length === 1 && line !== undefined && canOfferReconnect(line, choice))\n }) : []\n const availableKinds = (['handle', 'number', 'email'] as const).filter(value => choices.some(choice => choice.identity.kind === value))\n const selectedKind = availableKinds.includes(kind) ? kind : availableKinds[0]\n const kindChoices = choices.filter(choice => choice.identity.kind === selectedKind)\n const selected = kindChoices.find(choice => choice.key === choiceKey) ?? kindChoices[0]\n const selectedTarget = snapshot?.targets.find(target => target.id === targetId)\n const selectedMode = selectedTarget?.modes.includes(boxMode) ? boxMode : selectedTarget?.modes[0]\n const enteredNumber = selected?.identity.phoneNumberId ?? phoneNumberId.trim()\n const needsNumber = selected?.identity.transport === 'whatsapp' || selected?.identity.requiresPhoneNumberId\n const occupied = selected && snapshot?.lines.filter(line => line.status !== 'released' && line.transport === selected.identity.transport)\n const [occupiedLine] = occupied || []\n const reconnectChoice = Boolean(selected && occupied?.length === 1 && occupiedLine && canOfferReconnect(occupiedLine, selected))\n const reconnecting = Boolean(reconnectChoice && selected && occupiedLine &&\n matchesLineIdentity(occupiedLine, selected.identity, enteredNumber))\n const blockedByExistingLine = Boolean(occupied?.length && !reconnecting)\n\n async function connect() {\n if (!selected || !selectedTarget || !selectedMode || (needsNumber && !enteredNumber) || blockedByExistingLine || busy || loading || refreshFailed) return\n const requestIncarnation = incarnation.current\n setBusy(true)\n setError(null)\n try {\n await clientRef.current.connect({\n connectionId: selected.connection.id,\n transport: selected.identity.transport,\n ...(enteredNumber ? { phoneNumberId: enteredNumber } : {}),\n targetId: selectedTarget.id,\n boxMode: selectedMode,\n })\n if (!stillCurrent(requestIncarnation)) return\n const updated = await reload(requestIncarnation)\n if (!stillCurrent(requestIncarnation)) return\n if (!updated) {\n onNotice?.({ kind: 'error', message: 'Line connected, but its current status could not be loaded.' })\n return\n }\n const line = updated.lines.find(item => item.status !== 'released' && item.connectionId === selected.connection.id &&\n item.transport === selected.identity.transport && item.providerNumberId === (enteredNumber || null))\n onNotice?.({ kind: 'success', message: line?.answering ? 'The line is answering.' : 'Line connected. Add a member to start answers.' })\n } catch (cause) {\n if (!stillCurrent(requestIncarnation)) return\n const detail = message(cause)\n setError(detail)\n onNotice?.({ kind: 'error', message: detail })\n } finally {\n if (stillCurrent(requestIncarnation)) setBusy(false)\n }\n }\n\n async function disconnect(lineId: string) {\n if (busy || loading || refreshFailed) return\n const requestIncarnation = incarnation.current\n setBusy(true)\n setError(null)\n try {\n await clientRef.current.disconnect(lineId)\n if (!stillCurrent(requestIncarnation)) return\n const updated = await reload(requestIncarnation)\n if (!stillCurrent(requestIncarnation)) return\n closeConfirmation(lineId)\n onNotice?.(updated\n ? { kind: 'success', message: 'The line stopped answering.' }\n : { kind: 'error', message: 'Line disconnected, but its current status could not be loaded.' })\n } catch (cause) {\n if (!stillCurrent(requestIncarnation)) return\n const detail = message(cause)\n setError(detail)\n onNotice?.({ kind: 'error', message: detail })\n } finally {\n if (stillCurrent(requestIncarnation)) setBusy(false)\n }\n }\n\n return <section className=\"tangle-lines\" aria-label=\"Agent lines\">\n <header className=\"tangle-lines__heading\">\n <div>\n <h2 ref={heading} tabIndex={-1}>Lines</h2>\n {snapshot && <p>{snapshot.workspaceName} can use one line per transport.</p>}\n </div>\n {snapshot && <span className=\"tangle-lines__count\">{snapshot.lines.filter(line => line.answering).length} answering</span>}\n </header>\n\n {error && <div className=\"tangle-lines__error\" role=\"alert\">\n <span>{error}</span>\n {(!snapshot || refreshFailed) && <button type=\"button\" disabled={loading} onClick={retry}>Retry</button>}\n </div>}\n {loading && <p className=\"tangle-lines__muted\" role=\"status\">Loading lines…</p>}\n\n {snapshot && <>\n <div className=\"tangle-lines__list\">\n {snapshot.lines.filter(line => line.status !== 'released').length === 0 &&\n <p className=\"tangle-lines__empty\">No line answers for this workspace yet.</p>}\n {snapshot.lines.filter(line => line.status !== 'released').map(line =>\n <div className=\"tangle-lines__row\" key={line.id}>\n <div className=\"tangle-lines__row-main\">\n <span className=\"tangle-lines__eyebrow\">{TRANSPORT[line.transport]} · {line.answering ? 'Answering' : line.targetId === null ? 'Disconnected' : line.status === 'active' ? 'Awaiting member' : line.status}</span>\n <strong>{line.routerAddress ?? line.address}</strong>\n {line.connect && line.targetId !== null && <p>To reach this agent, text <code>{line.connect}</code>.</p>}\n <p>{line.targetLabel ?? 'No agent or box assigned'}{line.boxMode && ` · ${line.boxMode === 'per-member' ? 'one box per member' : 'shared box'}`}</p>\n {line.lastTurn.kind === 'latest' ? <p>Last turn: {line.lastTurn.status} · {dateTime(line.lastTurn.at)}</p>\n : <p>{line.lastTurn.kind === 'none' ? 'No turns yet.' : 'Turn history unavailable.'}</p>}\n </div>\n {canManage && line.canDisconnect && line.targetId !== null && line.status === 'active' && <div className=\"tangle-lines__row-actions\">\n {confirmLineId === line.id ? <div className=\"tangle-lines__confirm\" role=\"group\" aria-label={`Disconnect ${line.address}?`}>\n <span>Disconnect this line?</span>\n <button ref={confirmButton} type=\"button\" className=\"tangle-lines__danger\" disabled={busy || loading || refreshFailed} onClick={() => void disconnect(line.id)}>Disconnect</button>\n <button type=\"button\" disabled={busy} onClick={() => closeConfirmation(line.id)}>Keep line</button>\n </div> : <button ref={node => { if (node) disconnectTriggers.current.set(line.id, node); else disconnectTriggers.current.delete(line.id) }} type=\"button\" className=\"tangle-lines__quiet\" disabled={busy || loading || refreshFailed} onClick={() => setConfirmLineId(line.id)}>Disconnect line</button>}\n </div>}\n </div>)}\n </div>\n\n {canManage && (snapshot.connections === null || choices.length > 0 || allChoices.length === 0) && <div className=\"tangle-lines__setup\">\n <h3>Connect an identity you own</h3>\n <p>Choose an identity already connected in Hub. Billing details appear below.</p>\n {snapshot.connections === null ? <div className=\"tangle-lines__error\" role=\"status\">\n <span>Hub connections could not be read. Check Hub access and try again.</span>\n <button type=\"button\" disabled={loading} onClick={retry}>Retry</button>\n </div>\n : availableKinds.length === 0 ? <p className=\"tangle-lines__empty\">\n Connect an owned handle, number, or mailbox in Hub, then <button type=\"button\" disabled={loading} onClick={retry}>reload</button>.\n </p>\n : <>\n <div className=\"tangle-lines__kinds\" role=\"group\" aria-label=\"Identity type\">\n {availableKinds.map(value => <button key={value} type=\"button\" aria-pressed={selectedKind === value} onClick={() => { setKind(value); setChoiceKey(''); setPhoneNumberId('') }}>{IDENTITY[value]}</button>)}\n </div>\n <div className=\"tangle-lines__fields\">\n <label>Owned connection\n <select value={selected?.key ?? ''} onChange={event => { setChoiceKey(event.target.value); setPhoneNumberId('') }}>\n {kindChoices.map(choice => <option key={choice.key} value={choice.key}>{choice.connection.label} · {choice.identity.label}</option>)}\n </select>\n </label>\n <label>Agent or box\n <select value={selectedTarget?.id ?? ''} onChange={event => setTargetId(event.target.value)}>\n {snapshot.targets.map(target => <option key={target.id} value={target.id}>{target.label} · {target.kind}</option>)}\n </select>\n </label>\n {selectedTarget && selectedTarget.modes.length > 1 && <fieldset>\n <legend>Conversation space</legend>\n <label><input type=\"radio\" name={`line-box-mode-${scopeKey}`} value=\"per-member\" checked={selectedMode === 'per-member'} disabled={!selectedTarget?.modes.includes('per-member')} onChange={() => setBoxMode('per-member')} /> One box per member</label>\n <label><input type=\"radio\" name={`line-box-mode-${scopeKey}`} value=\"shared\" checked={selectedMode === 'shared'} disabled={!selectedTarget?.modes.includes('shared')} onChange={() => setBoxMode('shared')} /> Shared box for a team</label>\n </fieldset>}\n {needsNumber && !selected?.identity.phoneNumberId && <label>Existing provider number ID\n <input value={phoneNumberId} onChange={event => setPhoneNumberId(event.target.value)} autoComplete=\"off\" placeholder=\"Number ID from Hub\" />\n </label>}\n </div>\n {reconnectChoice && blockedByExistingLine && <p className=\"tangle-lines__warning\">Enter this line's current provider number ID to reconnect.</p>}\n <button className=\"tangle-lines__primary\" type=\"button\" disabled={busy || loading || refreshFailed || !selected || !selectedTarget || !selectedMode || (needsNumber && !enteredNumber) || blockedByExistingLine} onClick={() => void connect()}>\n {busy ? (reconnectChoice ? 'Reconnecting…' : 'Connecting…') : `${reconnectChoice ? 'Reconnect' : 'Connect'} ${selected ? TRANSPORT[selected.identity.transport] : 'line'}`}\n </button>\n </>}\n </div>}\n </>}\n </section>\n}\n","import { useEffect, useLayoutEffect, useRef, useState } from 'react'\nimport type { LineMember } from '@tangle-network/sandbox'\nimport type { LineMembersProps } from './contracts'\n\nconst useBrowserLayoutEffect = typeof document !== 'undefined' ? useLayoutEffect : useEffect\n\nconst STATUS: Record<LineMember['status'], { label: string; detail: string }> = {\n invited: { label: 'Awaiting consent', detail: 'The first message from this address confirms consent.' },\n active: { label: 'Consented', detail: 'This member has sent a message to the line.' },\n stopped: { label: 'Stopped', detail: 'The member texted STOP. Only a message from them with START resumes replies.' },\n removed: { label: 'Removed', detail: 'This address no longer has access to the line.' },\n}\n\nfunction errorMessage(cause: unknown): string {\n return cause instanceof Error ? cause.message : 'The member request failed. Try again.'\n}\n\n/** Manage Hub line members while keeping consent and STOP authority with the sender. */\nexport function LineMembers({ lineId, scopeKey, client, roles, canManage, canAdd = true, maxMembers, allowRoleChange = true, allowRemoveLastOwner = false, onNotice }: LineMembersProps) {\n const clientRef = useRef(client)\n clientRef.current = client\n const sequence = useRef(0)\n const incarnation = useRef(0)\n const confirmButton = useRef<HTMLButtonElement>(null)\n const heading = useRef<HTMLHeadingElement>(null)\n const removeTriggers = useRef(new Map<string, HTMLButtonElement>())\n const restoreFocusTo = useRef<string | null>(null)\n useBrowserLayoutEffect(() => {\n incarnation.current++\n return () => { incarnation.current++ }\n }, [lineId, scopeKey])\n const [memberSnapshot, setMemberSnapshot] = useState<{ lineId: string; scopeKey: string; members: LineMember[] } | null>(null)\n const [error, setError] = useState<string | null>(null)\n const [refreshFailed, setRefreshFailed] = useState(false)\n const [address, setAddress] = useState('')\n const [label, setLabel] = useState('')\n const [role, setRole] = useState('member')\n const [busy, setBusy] = useState<string | null>(null)\n const [confirmId, setConfirmId] = useState<string | null>(null)\n const members = memberSnapshot?.lineId === lineId && memberSnapshot.scopeKey === scopeKey ? memberSnapshot.members : null\n\n function stillCurrent(requestIncarnation: number): boolean {\n return requestIncarnation === incarnation.current\n }\n\n async function reload(requestIncarnation: number): Promise<boolean> {\n const request = ++sequence.current\n try {\n const next = await clientRef.current.list(lineId)\n if (request !== sequence.current || !stillCurrent(requestIncarnation)) return false\n setMemberSnapshot({ lineId, scopeKey, members: next })\n setError(null)\n setRefreshFailed(false)\n return true\n } catch (cause) {\n if (request !== sequence.current || !stillCurrent(requestIncarnation)) return false\n setError(errorMessage(cause))\n setRefreshFailed(true)\n return false\n }\n }\n\n async function retry() {\n if (busy) return\n const requestIncarnation = incarnation.current\n setBusy('refresh')\n try { await reload(requestIncarnation) }\n finally { if (stillCurrent(requestIncarnation)) setBusy(null) }\n }\n\n function closeConfirmation(memberId: string) {\n restoreFocusTo.current = memberId\n setConfirmId(null)\n }\n\n useEffect(() => {\n setMemberSnapshot(null)\n setError(null)\n setRefreshFailed(false)\n setAddress('')\n setLabel('')\n setRole('member')\n setConfirmId(null)\n restoreFocusTo.current = null\n setBusy(null)\n void reload(incarnation.current)\n return () => { sequence.current++ }\n }, [lineId, scopeKey])\n\n useEffect(() => {\n if (confirmId) confirmButton.current?.focus()\n }, [confirmId])\n\n useEffect(() => {\n const memberId = restoreFocusTo.current\n if (confirmId !== null || memberId === null) return\n restoreFocusTo.current = null\n const trigger = removeTriggers.current.get(memberId)\n if (trigger && !trigger.disabled) trigger.focus()\n else heading.current?.focus()\n }, [confirmId, members, refreshFailed])\n\n const selectedRole = roles.some(item => item.value === role) ? role : roles.find(item => item.value === 'member')?.value ?? roles[0]?.value ?? ''\n const activeMembers = members?.filter(member => member.status !== 'removed').length ?? 0\n const activeOwners = members?.filter(member => member.role === 'owner' && member.status !== 'removed').length ?? 0\n\n async function mutate(key: string, action: () => Promise<void>, success: string, onSuccess?: () => void) {\n if (busy || refreshFailed) return\n const requestIncarnation = incarnation.current\n setBusy(key)\n setError(null)\n try {\n await action()\n if (!stillCurrent(requestIncarnation)) return\n const refreshed = await reload(requestIncarnation)\n if (!stillCurrent(requestIncarnation)) return\n onSuccess?.()\n onNotice?.(refreshed\n ? { kind: 'success', message: success }\n : { kind: 'error', message: 'Member updated, but the current member list could not be loaded.' })\n } catch (cause) {\n if (!stillCurrent(requestIncarnation)) return\n const detail = errorMessage(cause)\n setError(detail)\n onNotice?.({ kind: 'error', message: detail })\n } finally {\n if (stillCurrent(requestIncarnation)) setBusy(null)\n }\n }\n\n function add() {\n const trimmed = address.trim()\n if (!trimmed || !selectedRole || members === null || (maxMembers !== undefined && activeMembers >= maxMembers)) return\n void mutate('add', () => clientRef.current.add(lineId, { address: trimmed, role: selectedRole, ...(label.trim() ? { label: label.trim() } : {}) }), 'Member invited. Their first message confirms consent.', () => {\n setAddress('')\n setLabel('')\n })\n }\n\n return <section className=\"tangle-lines\" aria-label=\"Line members\">\n <header className=\"tangle-lines__heading\">\n <div><h2 ref={heading} tabIndex={-1}>Members</h2><p>Each address controls its own consent and STOP state.</p></div>\n {members && <span className=\"tangle-lines__count\">{activeMembers} {activeMembers === 1 ? 'member' : 'members'}</span>}\n </header>\n {error && <div className=\"tangle-lines__error\" role=\"alert\">\n <span>{error}</span>\n {(members === null || refreshFailed) && <button type=\"button\" disabled={busy !== null} onClick={() => void retry()}>Retry</button>}\n </div>}\n {members === null ? !error && <p className=\"tangle-lines__muted\" role=\"status\">Loading members…</p> : <div className=\"tangle-lines__list\">\n {members.length === 0 && <p className=\"tangle-lines__empty\">{canAdd ? 'No members yet. Invite an address that you own or have permission to add.' : 'No members yet.'}</p>}\n {members.map(member => {\n const lastOwner = member.role === 'owner' && activeOwners <= 1\n const editable = canManage && member.status !== 'removed' && (!lastOwner || allowRemoveLastOwner)\n return <div className=\"tangle-lines__row\" key={member.id}>\n <div className=\"tangle-lines__row-main\">\n <span className={`tangle-lines__state tangle-lines__state--${member.status}`}>{STATUS[member.status].label}</span>\n <strong>{member.label || member.address}</strong>\n {member.label && <p>{member.address}</p>}\n <p>{STATUS[member.status].detail}</p>\n </div>\n <div className=\"tangle-lines__row-actions\">\n {editable ? <>\n {allowRoleChange && !lastOwner ? <label className=\"tangle-lines__compact-label\">Role\n <select value={member.role} disabled={busy !== null || refreshFailed} onChange={event => void mutate(member.id, () => clientRef.current.update(lineId, member.id, { role: event.target.value }), 'Role updated.')}>\n {!roles.some(item => item.value === member.role) && <option value={member.role}>{member.role}</option>}\n {roles.map(item => <option key={item.value} value={item.value}>{item.label}{item.tools === 'chat' ? ' · chat only' : ''}</option>)}\n </select>\n </label> : <span className=\"tangle-lines__role\">{roles.find(item => item.value === member.role)?.label ?? member.role}</span>}\n {confirmId === member.id ? <div className=\"tangle-lines__confirm\" role=\"group\" aria-label={`Remove ${member.label || member.address}?`}>\n <span>Remove {member.label || member.address}?</span>\n <button ref={confirmButton} type=\"button\" className=\"tangle-lines__danger\" disabled={busy !== null || refreshFailed} onClick={() => void mutate(member.id, () => clientRef.current.remove(lineId, member.id), 'Member removed.', () => closeConfirmation(member.id))}>Remove</button>\n <button type=\"button\" disabled={busy !== null} onClick={() => closeConfirmation(member.id)}>Keep</button>\n </div> : <button ref={node => { if (node) removeTriggers.current.set(member.id, node); else removeTriggers.current.delete(member.id) }} type=\"button\" className=\"tangle-lines__quiet\" disabled={busy !== null || refreshFailed} onClick={() => setConfirmId(member.id)}>Remove</button>}\n </> : <span className=\"tangle-lines__role\">{roles.find(item => item.value === member.role)?.label ?? member.role}</span>}\n </div>\n </div>\n })}\n </div>}\n\n {canManage && canAdd && members !== null && (maxMembers === undefined || activeMembers < maxMembers) && <div className=\"tangle-lines__setup\">\n <h3>Invite a member</h3>\n <p>Hub marks the address as invited. The member confirms by messaging the line.</p>\n <div className=\"tangle-lines__fields\">\n <label>Phone number or email\n <input value={address} onChange={event => setAddress(event.target.value)} autoComplete=\"off\" placeholder=\"+15551234567 or name@example.com\" />\n </label>\n <label>Name (optional)\n <input value={label} onChange={event => setLabel(event.target.value)} placeholder=\"Member name\" />\n </label>\n <label>Role\n <select value={selectedRole} disabled={roles.length === 0} onChange={event => setRole(event.target.value)}>\n {roles.map(item => <option key={item.value} value={item.value}>{item.label}{item.tools === 'chat' ? ' · chat only' : ''}</option>)}\n </select>\n </label>\n </div>\n <button type=\"button\" className=\"tangle-lines__primary\" disabled={busy !== null || refreshFailed || !address.trim() || !selectedRole} onClick={add}>\n {busy === 'add' ? 'Inviting…' : 'Invite member'}\n </button>\n </div>}\n </section>\n}\n","import type { LineBillingProps } from './contracts'\n\nfunction resetTime(value: string): string {\n const date = new Date(value)\n return Number.isNaN(date.getTime()) ? value : date.toLocaleString()\n}\n\n/** Read-only billing facts from the line attachment and Hub allowance status. */\nexport function LineBilling({ view }: LineBillingProps) {\n return <section className=\"tangle-lines\" aria-label=\"Line billing\">\n <header className=\"tangle-lines__heading\">\n <div><h2>Billing</h2><p>{view.lineAddress}</p></div>\n </header>\n <dl className=\"tangle-lines__billing\">\n <div>\n <dt>Line</dt>\n <dd>{view.linePayer.kind === 'workspace' ? view.linePayer.label : 'Payer not verified'}</dd>\n {view.linePayer.kind === 'workspace' && <dd className=\"tangle-lines__billing-detail\">Charged to {view.workspaceName}.</dd>}\n </div>\n <div>\n <dt>Agent turns</dt>\n <dd>{view.turnPayer.kind === 'unverified' ? 'Payer not verified' : view.turnPayer.label}</dd>\n {view.turnPayer.kind === 'member' && <dd className=\"tangle-lines__billing-detail\">Each member pays for their own admitted turns.</dd>}\n </div>\n <div>\n <dt>Allowance</dt>\n {view.allowance ? <>\n <dd>{view.allowance.turnsPerMemberPerDay} turns per member each day</dd>\n {view.allowance.used !== undefined && view.allowance.limit !== undefined &&\n <dd className=\"tangle-lines__billing-detail\">{view.allowance.used} of {view.allowance.limit} turns used{view.allowance.resetsAt ? ` · resets ${resetTime(view.allowance.resetsAt)}` : ''}</dd>}\n </> : <dd>No allowance reported</dd>}\n </div>\n </dl>\n </section>\n}\n"],"mappings":";AAAA,SAAS,WAAW,iBAAiB,QAAQ,gBAAgB;AA8NrD,SA8CM,UA9CN,KACa,YADb;AAvNR,IAAM,yBAAyB,OAAO,aAAa,cAAc,kBAAkB;AAEnF,IAAM,YAA+C;AAAA,EACnD,UAAU;AAAA,EAAY,KAAK;AAAA,EAAO,UAAU;AAAA,EAAY,OAAO;AACjE;AACA,IAAM,WAA6C;AAAA,EACjD,QAAQ;AAAA,EAAiB,QAAQ;AAAA,EAAoB,OAAO;AAC9D;AAIA,SAAS,QAAQ,OAAwB;AACvC,SAAO,iBAAiB,QAAQ,MAAM,UAAU;AAClD;AAEA,SAAS,SAAS,OAAuB;AACvC,QAAM,OAAO,IAAI,KAAK,KAAK;AAC3B,SAAO,OAAO,MAAM,KAAK,QAAQ,CAAC,IAAI,QAAQ,KAAK,eAAe;AACpE;AAEA,SAAS,WAAW,UAAuC;AACzD,UAAQ,SAAS,eAAe,CAAC,GAAG,QAAQ,gBAAc,WAAW,WAAW,IAAI,CAAC,UAAU,WAAW;AAAA,IACxG,KAAK,GAAG,WAAW,EAAE,IAAI,KAAK;AAAA,IAC9B;AAAA,IACA;AAAA,EACF,EAAE,CAAC;AACL;AAEA,SAAS,kBAAkB,MAAqB,QAAyB;AACvE,MAAI,KAAK,iBAAiB,OAAO,WAAW,MAAM,KAAK,cAAc,OAAO,SAAS,aAAa,KAAK,aAAa,KAAM,QAAO;AACjI,MAAI,KAAK,qBAAqB,KAAM,QAAO,KAAK,cAAc,cAAc,CAAC,OAAO,SAAS,iBAAiB,CAAC,OAAO,SAAS;AAC/H,SAAO,OAAO,SAAS,kBAAkB,KAAK,oBAC3C,CAAC,OAAO,SAAS,iBAAiB,OAAO,SAAS,0BAA0B;AACjF;AAEA,SAAS,oBAAoB,MAAqB,UAA8B,UAA2B;AACzG,MAAI,KAAK,cAAc,SAAS,UAAW,QAAO;AAClD,MAAI,KAAK,qBAAqB,KAAM,QAAO,KAAK,cAAc,cAAc,CAAC,SAAS,iBAAiB,CAAC,SAAS;AACjH,SAAO,aAAa,KAAK;AAC3B;AAGO,SAAS,UAAU,EAAE,QAAQ,UAAU,iBAAiB,WAAW,SAAS,GAAmB;AACpG,QAAM,YAAY,OAAO,MAAM;AAC/B,YAAU,UAAU;AACpB,QAAM,WAAW,OAAO,CAAC;AACzB,QAAM,cAAc,OAAO,CAAC;AAC5B,QAAM,gBAAgB,OAA0B,IAAI;AACpD,QAAM,UAAU,OAA2B,IAAI;AAC/C,QAAM,qBAAqB,OAAO,oBAAI,IAA+B,CAAC;AACtE,QAAM,iBAAiB,OAAsB,IAAI;AACjD,yBAAuB,MAAM;AAC3B,gBAAY;AACZ,WAAO,MAAM;AAAE,kBAAY;AAAA,IAAU;AAAA,EACvC,GAAG,CAAC,UAAU,eAAe,CAAC;AAC9B,QAAM,CAAC,gBAAgB,iBAAiB,IAAI,SAAqG,IAAI;AACrJ,QAAM,WAAW,gBAAgB,aAAa,YAAY,eAAe,oBAAoB,kBAAkB,eAAe,QAAQ;AACtI,QAAM,CAAC,SAAS,UAAU,IAAI,SAAS,IAAI;AAC3C,QAAM,CAAC,eAAe,gBAAgB,IAAI,SAAS,KAAK;AACxD,QAAM,CAAC,MAAM,OAAO,IAAI,SAAS,KAAK;AACtC,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAwB,IAAI;AACtD,QAAM,CAAC,MAAM,OAAO,IAAI,SAA2B,QAAQ;AAC3D,QAAM,CAAC,WAAW,YAAY,IAAI,SAAS,EAAE;AAC7C,QAAM,CAAC,UAAU,WAAW,IAAI,SAAS,mBAAmB,EAAE;AAC9D,QAAM,CAAC,SAAS,UAAU,IAAI,SAAsB,YAAY;AAChE,QAAM,CAAC,eAAe,gBAAgB,IAAI,SAAS,EAAE;AACrD,QAAM,CAAC,eAAe,gBAAgB,IAAI,SAAwB,IAAI;AAEtE,WAAS,aAAa,oBAAqC;AACzD,WAAO,uBAAuB,YAAY;AAAA,EAC5C;AAEA,iBAAe,OAAO,oBAA+D;AACnF,UAAM,UAAU,EAAE,SAAS;AAC3B,QAAI;AACF,YAAM,OAAO,MAAM,UAAU,QAAQ,KAAK;AAC1C,UAAI,YAAY,SAAS,WAAW,CAAC,aAAa,kBAAkB,EAAG,QAAO;AAC9E,wBAAkB,EAAE,UAAU,iBAAiB,OAAO,KAAK,CAAC;AAC5D,eAAS,IAAI;AACb,uBAAiB,KAAK;AACtB,kBAAY,aAAW,KAAK,QAAQ,KAAK,YAAU,OAAO,OAAO,OAAO,IACpE,UAAU,KAAK,QAAQ,KAAK,YAAU,OAAO,OAAO,eAAe,GAAG,MAAM,KAAK,QAAQ,CAAC,GAAG,MAAM,EAAE;AACzG,iBAAW,KAAK;AAChB,aAAO;AAAA,IACT,SAAS,OAAO;AACd,UAAI,YAAY,SAAS,WAAW,CAAC,aAAa,kBAAkB,EAAG,QAAO;AAC9E,eAAS,QAAQ,KAAK,CAAC;AACvB,uBAAiB,IAAI;AACrB,iBAAW,KAAK;AAChB,aAAO;AAAA,IACT;AAAA,EACF;AAEA,WAAS,QAAQ;AACf,QAAI,QAAS;AACb,eAAW,IAAI;AACf,SAAK,OAAO,YAAY,OAAO;AAAA,EACjC;AAEA,WAAS,kBAAkB,QAAgB;AACzC,mBAAe,UAAU;AACzB,qBAAiB,IAAI;AAAA,EACvB;AAEA,YAAU,MAAM;AACd,eAAW,IAAI;AACf,sBAAkB,IAAI;AACtB,aAAS,IAAI;AACb,qBAAiB,KAAK;AACtB,YAAQ,KAAK;AACb,YAAQ,QAAQ;AAChB,iBAAa,EAAE;AACf,gBAAY,mBAAmB,EAAE;AACjC,eAAW,YAAY;AACvB,qBAAiB,EAAE;AACnB,qBAAiB,IAAI;AACrB,mBAAe,UAAU;AACzB,SAAK,OAAO,YAAY,OAAO;AAC/B,WAAO,MAAM;AAAE,eAAS;AAAA,IAAU;AAAA,EACpC,GAAG,CAAC,UAAU,eAAe,CAAC;AAE9B,YAAU,MAAM;AACd,QAAI,cAAe,eAAc,SAAS,MAAM;AAAA,EAClD,GAAG,CAAC,aAAa,CAAC;AAElB,YAAU,MAAM;AACd,UAAM,SAAS,eAAe;AAC9B,QAAI,kBAAkB,QAAQ,WAAW,KAAM;AAC/C,mBAAe,UAAU;AACzB,UAAM,UAAU,mBAAmB,QAAQ,IAAI,MAAM;AACrD,QAAI,WAAW,CAAC,QAAQ,SAAU,SAAQ,MAAM;AAAA,QAC3C,SAAQ,SAAS,MAAM;AAAA,EAC9B,GAAG,CAAC,eAAe,UAAU,aAAa,CAAC;AAE3C,QAAM,aAAa,WAAW,WAAW,QAAQ,IAAI,CAAC;AACtD,QAAM,UAAU,WAAW,WAAW,OAAO,YAAU;AACrD,UAAMA,YAAW,SAAS,MAAM,OAAO,CAAAC,UAAQA,MAAK,WAAW,cAAcA,MAAK,cAAc,OAAO,SAAS,SAAS;AACzH,UAAM,CAAC,IAAI,IAAID;AACf,WAAOA,UAAS,WAAW,KAAMA,UAAS,WAAW,KAAK,SAAS,UAAa,kBAAkB,MAAM,MAAM;AAAA,EAChH,CAAC,IAAI,CAAC;AACN,QAAM,iBAAkB,CAAC,UAAU,UAAU,OAAO,EAAY,OAAO,WAAS,QAAQ,KAAK,YAAU,OAAO,SAAS,SAAS,KAAK,CAAC;AACtI,QAAM,eAAe,eAAe,SAAS,IAAI,IAAI,OAAO,eAAe,CAAC;AAC5E,QAAM,cAAc,QAAQ,OAAO,YAAU,OAAO,SAAS,SAAS,YAAY;AAClF,QAAM,WAAW,YAAY,KAAK,YAAU,OAAO,QAAQ,SAAS,KAAK,YAAY,CAAC;AACtF,QAAM,iBAAiB,UAAU,QAAQ,KAAK,YAAU,OAAO,OAAO,QAAQ;AAC9E,QAAM,eAAe,gBAAgB,MAAM,SAAS,OAAO,IAAI,UAAU,gBAAgB,MAAM,CAAC;AAChG,QAAM,gBAAgB,UAAU,SAAS,iBAAiB,cAAc,KAAK;AAC7E,QAAM,cAAc,UAAU,SAAS,cAAc,cAAc,UAAU,SAAS;AACtF,QAAM,WAAW,YAAY,UAAU,MAAM,OAAO,UAAQ,KAAK,WAAW,cAAc,KAAK,cAAc,SAAS,SAAS,SAAS;AACxI,QAAM,CAAC,YAAY,IAAI,YAAY,CAAC;AACpC,QAAM,kBAAkB,QAAQ,YAAY,UAAU,WAAW,KAAK,gBAAgB,kBAAkB,cAAc,QAAQ,CAAC;AAC/H,QAAM,eAAe,QAAQ,mBAAmB,YAAY,gBAC1D,oBAAoB,cAAc,SAAS,UAAU,aAAa,CAAC;AACrE,QAAM,wBAAwB,QAAQ,UAAU,UAAU,CAAC,YAAY;AAEvE,iBAAe,UAAU;AACvB,QAAI,CAAC,YAAY,CAAC,kBAAkB,CAAC,gBAAiB,eAAe,CAAC,iBAAkB,yBAAyB,QAAQ,WAAW,cAAe;AACnJ,UAAM,qBAAqB,YAAY;AACvC,YAAQ,IAAI;AACZ,aAAS,IAAI;AACb,QAAI;AACF,YAAM,UAAU,QAAQ,QAAQ;AAAA,QAC9B,cAAc,SAAS,WAAW;AAAA,QAClC,WAAW,SAAS,SAAS;AAAA,QAC7B,GAAI,gBAAgB,EAAE,eAAe,cAAc,IAAI,CAAC;AAAA,QACxD,UAAU,eAAe;AAAA,QACzB,SAAS;AAAA,MACX,CAAC;AACD,UAAI,CAAC,aAAa,kBAAkB,EAAG;AACvC,YAAM,UAAU,MAAM,OAAO,kBAAkB;AAC/C,UAAI,CAAC,aAAa,kBAAkB,EAAG;AACvC,UAAI,CAAC,SAAS;AACZ,mBAAW,EAAE,MAAM,SAAS,SAAS,8DAA8D,CAAC;AACpG;AAAA,MACF;AACA,YAAM,OAAO,QAAQ,MAAM,KAAK,UAAQ,KAAK,WAAW,cAAc,KAAK,iBAAiB,SAAS,WAAW,MAC9G,KAAK,cAAc,SAAS,SAAS,aAAa,KAAK,sBAAsB,iBAAiB,KAAK;AACrG,iBAAW,EAAE,MAAM,WAAW,SAAS,MAAM,YAAY,2BAA2B,iDAAiD,CAAC;AAAA,IACxI,SAAS,OAAO;AACd,UAAI,CAAC,aAAa,kBAAkB,EAAG;AACvC,YAAM,SAAS,QAAQ,KAAK;AAC5B,eAAS,MAAM;AACf,iBAAW,EAAE,MAAM,SAAS,SAAS,OAAO,CAAC;AAAA,IAC/C,UAAE;AACA,UAAI,aAAa,kBAAkB,EAAG,SAAQ,KAAK;AAAA,IACrD;AAAA,EACF;AAEA,iBAAe,WAAW,QAAgB;AACxC,QAAI,QAAQ,WAAW,cAAe;AACtC,UAAM,qBAAqB,YAAY;AACvC,YAAQ,IAAI;AACZ,aAAS,IAAI;AACb,QAAI;AACF,YAAM,UAAU,QAAQ,WAAW,MAAM;AACzC,UAAI,CAAC,aAAa,kBAAkB,EAAG;AACvC,YAAM,UAAU,MAAM,OAAO,kBAAkB;AAC/C,UAAI,CAAC,aAAa,kBAAkB,EAAG;AACvC,wBAAkB,MAAM;AACxB,iBAAW,UACP,EAAE,MAAM,WAAW,SAAS,8BAA8B,IAC1D,EAAE,MAAM,SAAS,SAAS,iEAAiE,CAAC;AAAA,IAClG,SAAS,OAAO;AACd,UAAI,CAAC,aAAa,kBAAkB,EAAG;AACvC,YAAM,SAAS,QAAQ,KAAK;AAC5B,eAAS,MAAM;AACf,iBAAW,EAAE,MAAM,SAAS,SAAS,OAAO,CAAC;AAAA,IAC/C,UAAE;AACA,UAAI,aAAa,kBAAkB,EAAG,SAAQ,KAAK;AAAA,IACrD;AAAA,EACF;AAEA,SAAO,qBAAC,aAAQ,WAAU,gBAAe,cAAW,eAClD;AAAA,yBAAC,YAAO,WAAU,yBAChB;AAAA,2BAAC,SACC;AAAA,4BAAC,QAAG,KAAK,SAAS,UAAU,IAAI,mBAAK;AAAA,QACpC,YAAY,qBAAC,OAAG;AAAA,mBAAS;AAAA,UAAc;AAAA,WAAgC;AAAA,SAC1E;AAAA,MACC,YAAY,qBAAC,UAAK,WAAU,uBAAuB;AAAA,iBAAS,MAAM,OAAO,UAAQ,KAAK,SAAS,EAAE;AAAA,QAAO;AAAA,SAAU;AAAA,OACrH;AAAA,IAEC,SAAS,qBAAC,SAAI,WAAU,uBAAsB,MAAK,SAClD;AAAA,0BAAC,UAAM,iBAAM;AAAA,OACX,CAAC,YAAY,kBAAkB,oBAAC,YAAO,MAAK,UAAS,UAAU,SAAS,SAAS,OAAO,mBAAK;AAAA,OACjG;AAAA,IACC,WAAW,oBAAC,OAAE,WAAU,uBAAsB,MAAK,UAAS,iCAAc;AAAA,IAE1E,YAAY,iCACX;AAAA,2BAAC,SAAI,WAAU,sBACZ;AAAA,iBAAS,MAAM,OAAO,UAAQ,KAAK,WAAW,UAAU,EAAE,WAAW,KACpE,oBAAC,OAAE,WAAU,uBAAsB,qDAAuC;AAAA,QAC3E,SAAS,MAAM,OAAO,UAAQ,KAAK,WAAW,UAAU,EAAE,IAAI,UAC7D,qBAAC,SAAI,WAAU,qBACb;AAAA,+BAAC,SAAI,WAAU,0BACb;AAAA,iCAAC,UAAK,WAAU,yBAAyB;AAAA,wBAAU,KAAK,SAAS;AAAA,cAAE;AAAA,cAAI,KAAK,YAAY,cAAc,KAAK,aAAa,OAAO,iBAAiB,KAAK,WAAW,WAAW,oBAAoB,KAAK;AAAA,eAAO;AAAA,YAC3M,oBAAC,YAAQ,eAAK,iBAAiB,KAAK,SAAQ;AAAA,YAC3C,KAAK,WAAW,KAAK,aAAa,QAAQ,qBAAC,OAAE;AAAA;AAAA,cAA0B,oBAAC,UAAM,eAAK,SAAQ;AAAA,cAAO;AAAA,eAAC;AAAA,YACpG,qBAAC,OAAG;AAAA,mBAAK,eAAe;AAAA,cAA4B,KAAK,WAAW,SAAM,KAAK,YAAY,eAAe,uBAAuB,YAAY;AAAA,eAAG;AAAA,YAC/I,KAAK,SAAS,SAAS,WAAW,qBAAC,OAAE;AAAA;AAAA,cAAY,KAAK,SAAS;AAAA,cAAO;AAAA,cAAI,SAAS,KAAK,SAAS,EAAE;AAAA,eAAE,IAClG,oBAAC,OAAG,eAAK,SAAS,SAAS,SAAS,kBAAkB,6BAA4B;AAAA,aACxF;AAAA,UACC,aAAa,KAAK,iBAAiB,KAAK,aAAa,QAAQ,KAAK,WAAW,YAAY,oBAAC,SAAI,WAAU,6BACtG,4BAAkB,KAAK,KAAK,qBAAC,SAAI,WAAU,yBAAwB,MAAK,SAAQ,cAAY,cAAc,KAAK,OAAO,KACrH;AAAA,gCAAC,UAAK,mCAAqB;AAAA,YAC3B,oBAAC,YAAO,KAAK,eAAe,MAAK,UAAS,WAAU,wBAAuB,UAAU,QAAQ,WAAW,eAAe,SAAS,MAAM,KAAK,WAAW,KAAK,EAAE,GAAG,wBAAU;AAAA,YAC1K,oBAAC,YAAO,MAAK,UAAS,UAAU,MAAM,SAAS,MAAM,kBAAkB,KAAK,EAAE,GAAG,uBAAS;AAAA,aAC5F,IAAS,oBAAC,YAAO,KAAK,UAAQ;AAAE,gBAAI,KAAM,oBAAmB,QAAQ,IAAI,KAAK,IAAI,IAAI;AAAA,gBAAQ,oBAAmB,QAAQ,OAAO,KAAK,EAAE;AAAA,UAAE,GAAG,MAAK,UAAS,WAAU,uBAAsB,UAAU,QAAQ,WAAW,eAAe,SAAS,MAAM,iBAAiB,KAAK,EAAE,GAAG,6BAAe,GACjS;AAAA,aAfsC,KAAK,EAgB7C,CAAM;AAAA,SACV;AAAA,MAEC,cAAc,SAAS,gBAAgB,QAAQ,QAAQ,SAAS,KAAK,WAAW,WAAW,MAAM,qBAAC,SAAI,WAAU,uBAC/G;AAAA,4BAAC,QAAG,yCAA2B;AAAA,QAC/B,oBAAC,OAAE,wFAA0E;AAAA,QAC5E,SAAS,gBAAgB,OAAO,qBAAC,SAAI,WAAU,uBAAsB,MAAK,UACzE;AAAA,8BAAC,UAAK,gFAAkE;AAAA,UACxE,oBAAC,YAAO,MAAK,UAAS,UAAU,SAAS,SAAS,OAAO,mBAAK;AAAA,WAChE,IACI,eAAe,WAAW,IAAI,qBAAC,OAAE,WAAU,uBAAsB;AAAA;AAAA,UACR,oBAAC,YAAO,MAAK,UAAS,UAAU,SAAS,SAAS,OAAO,oBAAM;AAAA,UAAS;AAAA,WACnI,IACI,iCACA;AAAA,8BAAC,SAAI,WAAU,uBAAsB,MAAK,SAAQ,cAAW,iBAC1D,yBAAe,IAAI,WAAS,oBAAC,YAAmB,MAAK,UAAS,gBAAc,iBAAiB,OAAO,SAAS,MAAM;AAAE,oBAAQ,KAAK;AAAG,yBAAa,EAAE;AAAG,6BAAiB,EAAE;AAAA,UAAE,GAAI,mBAAS,KAAK,KAArJ,KAAuJ,CAAS,GAC5M;AAAA,UACA,qBAAC,SAAI,WAAU,wBACb;AAAA,iCAAC,WAAM;AAAA;AAAA,cACL,oBAAC,YAAO,OAAO,UAAU,OAAO,IAAI,UAAU,WAAS;AAAE,6BAAa,MAAM,OAAO,KAAK;AAAG,iCAAiB,EAAE;AAAA,cAAE,GAC7G,sBAAY,IAAI,YAAU,qBAAC,YAAwB,OAAO,OAAO,KAAM;AAAA,uBAAO,WAAW;AAAA,gBAAM;AAAA,gBAAI,OAAO,SAAS;AAAA,mBAA5E,OAAO,GAA2E,CAAS,GACrI;AAAA,eACF;AAAA,YACA,qBAAC,WAAM;AAAA;AAAA,cACL,oBAAC,YAAO,OAAO,gBAAgB,MAAM,IAAI,UAAU,WAAS,YAAY,MAAM,OAAO,KAAK,GACvF,mBAAS,QAAQ,IAAI,YAAU,qBAAC,YAAuB,OAAO,OAAO,IAAK;AAAA,uBAAO;AAAA,gBAAM;AAAA,gBAAI,OAAO;AAAA,mBAAtD,OAAO,EAAoD,CAAS,GACnH;AAAA,eACF;AAAA,YACC,kBAAkB,eAAe,MAAM,SAAS,KAAK,qBAAC,cACrD;AAAA,kCAAC,YAAO,gCAAkB;AAAA,cAC1B,qBAAC,WAAM;AAAA,oCAAC,WAAM,MAAK,SAAQ,MAAM,iBAAiB,QAAQ,IAAI,OAAM,cAAa,SAAS,iBAAiB,cAAc,UAAU,CAAC,gBAAgB,MAAM,SAAS,YAAY,GAAG,UAAU,MAAM,WAAW,YAAY,GAAG;AAAA,gBAAE;AAAA,iBAAmB;AAAA,cACjP,qBAAC,WAAM;AAAA,oCAAC,WAAM,MAAK,SAAQ,MAAM,iBAAiB,QAAQ,IAAI,OAAM,UAAS,SAAS,iBAAiB,UAAU,UAAU,CAAC,gBAAgB,MAAM,SAAS,QAAQ,GAAG,UAAU,MAAM,WAAW,QAAQ,GAAG;AAAA,gBAAE;AAAA,iBAAsB;AAAA,eACtO;AAAA,YACC,eAAe,CAAC,UAAU,SAAS,iBAAiB,qBAAC,WAAM;AAAA;AAAA,cAC1D,oBAAC,WAAM,OAAO,eAAe,UAAU,WAAS,iBAAiB,MAAM,OAAO,KAAK,GAAG,cAAa,OAAM,aAAY,sBAAqB;AAAA,eAC5I;AAAA,aACF;AAAA,UACC,mBAAmB,yBAAyB,oBAAC,OAAE,WAAU,yBAAwB,wEAA0D;AAAA,UAC5I,oBAAC,YAAO,WAAU,yBAAwB,MAAK,UAAS,UAAU,QAAQ,WAAW,iBAAiB,CAAC,YAAY,CAAC,kBAAkB,CAAC,gBAAiB,eAAe,CAAC,iBAAkB,uBAAuB,SAAS,MAAM,KAAK,QAAQ,GAC1O,iBAAQ,kBAAkB,uBAAkB,qBAAiB,GAAG,kBAAkB,cAAc,SAAS,IAAI,WAAW,UAAU,SAAS,SAAS,SAAS,IAAI,MAAM,IAC1K;AAAA,WACF;AAAA,SACN;AAAA,OACF;AAAA,KACF;AACF;;;AC5SA,SAAS,aAAAE,YAAW,mBAAAC,kBAAiB,UAAAC,SAAQ,YAAAC,iBAAgB;AA6IvD,SAoBkB,YAAAC,WApBb,OAAAC,MAAL,QAAAC,aAAA;AAzIN,IAAMC,0BAAyB,OAAO,aAAa,cAAcN,mBAAkBD;AAEnF,IAAM,SAA0E;AAAA,EAC9E,SAAS,EAAE,OAAO,oBAAoB,QAAQ,wDAAwD;AAAA,EACtG,QAAQ,EAAE,OAAO,aAAa,QAAQ,8CAA8C;AAAA,EACpF,SAAS,EAAE,OAAO,WAAW,QAAQ,+EAA+E;AAAA,EACpH,SAAS,EAAE,OAAO,WAAW,QAAQ,iDAAiD;AACxF;AAEA,SAAS,aAAa,OAAwB;AAC5C,SAAO,iBAAiB,QAAQ,MAAM,UAAU;AAClD;AAGO,SAAS,YAAY,EAAE,QAAQ,UAAU,QAAQ,OAAO,WAAW,SAAS,MAAM,YAAY,kBAAkB,MAAM,uBAAuB,OAAO,SAAS,GAAqB;AACvL,QAAM,YAAYE,QAAO,MAAM;AAC/B,YAAU,UAAU;AACpB,QAAM,WAAWA,QAAO,CAAC;AACzB,QAAM,cAAcA,QAAO,CAAC;AAC5B,QAAM,gBAAgBA,QAA0B,IAAI;AACpD,QAAM,UAAUA,QAA2B,IAAI;AAC/C,QAAM,iBAAiBA,QAAO,oBAAI,IAA+B,CAAC;AAClE,QAAM,iBAAiBA,QAAsB,IAAI;AACjD,EAAAK,wBAAuB,MAAM;AAC3B,gBAAY;AACZ,WAAO,MAAM;AAAE,kBAAY;AAAA,IAAU;AAAA,EACvC,GAAG,CAAC,QAAQ,QAAQ,CAAC;AACrB,QAAM,CAAC,gBAAgB,iBAAiB,IAAIJ,UAA6E,IAAI;AAC7H,QAAM,CAAC,OAAO,QAAQ,IAAIA,UAAwB,IAAI;AACtD,QAAM,CAAC,eAAe,gBAAgB,IAAIA,UAAS,KAAK;AACxD,QAAM,CAAC,SAAS,UAAU,IAAIA,UAAS,EAAE;AACzC,QAAM,CAAC,OAAO,QAAQ,IAAIA,UAAS,EAAE;AACrC,QAAM,CAAC,MAAM,OAAO,IAAIA,UAAS,QAAQ;AACzC,QAAM,CAAC,MAAM,OAAO,IAAIA,UAAwB,IAAI;AACpD,QAAM,CAAC,WAAW,YAAY,IAAIA,UAAwB,IAAI;AAC9D,QAAM,UAAU,gBAAgB,WAAW,UAAU,eAAe,aAAa,WAAW,eAAe,UAAU;AAErH,WAAS,aAAa,oBAAqC;AACzD,WAAO,uBAAuB,YAAY;AAAA,EAC5C;AAEA,iBAAe,OAAO,oBAA8C;AAClE,UAAM,UAAU,EAAE,SAAS;AAC3B,QAAI;AACF,YAAM,OAAO,MAAM,UAAU,QAAQ,KAAK,MAAM;AAChD,UAAI,YAAY,SAAS,WAAW,CAAC,aAAa,kBAAkB,EAAG,QAAO;AAC9E,wBAAkB,EAAE,QAAQ,UAAU,SAAS,KAAK,CAAC;AACrD,eAAS,IAAI;AACb,uBAAiB,KAAK;AACtB,aAAO;AAAA,IACT,SAAS,OAAO;AACd,UAAI,YAAY,SAAS,WAAW,CAAC,aAAa,kBAAkB,EAAG,QAAO;AAC9E,eAAS,aAAa,KAAK,CAAC;AAC5B,uBAAiB,IAAI;AACrB,aAAO;AAAA,IACT;AAAA,EACF;AAEA,iBAAe,QAAQ;AACrB,QAAI,KAAM;AACV,UAAM,qBAAqB,YAAY;AACvC,YAAQ,SAAS;AACjB,QAAI;AAAE,YAAM,OAAO,kBAAkB;AAAA,IAAE,UACvC;AAAU,UAAI,aAAa,kBAAkB,EAAG,SAAQ,IAAI;AAAA,IAAE;AAAA,EAChE;AAEA,WAAS,kBAAkB,UAAkB;AAC3C,mBAAe,UAAU;AACzB,iBAAa,IAAI;AAAA,EACnB;AAEA,EAAAH,WAAU,MAAM;AACd,sBAAkB,IAAI;AACtB,aAAS,IAAI;AACb,qBAAiB,KAAK;AACtB,eAAW,EAAE;AACb,aAAS,EAAE;AACX,YAAQ,QAAQ;AAChB,iBAAa,IAAI;AACjB,mBAAe,UAAU;AACzB,YAAQ,IAAI;AACZ,SAAK,OAAO,YAAY,OAAO;AAC/B,WAAO,MAAM;AAAE,eAAS;AAAA,IAAU;AAAA,EACpC,GAAG,CAAC,QAAQ,QAAQ,CAAC;AAErB,EAAAA,WAAU,MAAM;AACd,QAAI,UAAW,eAAc,SAAS,MAAM;AAAA,EAC9C,GAAG,CAAC,SAAS,CAAC;AAEd,EAAAA,WAAU,MAAM;AACd,UAAM,WAAW,eAAe;AAChC,QAAI,cAAc,QAAQ,aAAa,KAAM;AAC7C,mBAAe,UAAU;AACzB,UAAM,UAAU,eAAe,QAAQ,IAAI,QAAQ;AACnD,QAAI,WAAW,CAAC,QAAQ,SAAU,SAAQ,MAAM;AAAA,QAC3C,SAAQ,SAAS,MAAM;AAAA,EAC9B,GAAG,CAAC,WAAW,SAAS,aAAa,CAAC;AAEtC,QAAM,eAAe,MAAM,KAAK,UAAQ,KAAK,UAAU,IAAI,IAAI,OAAO,MAAM,KAAK,UAAQ,KAAK,UAAU,QAAQ,GAAG,SAAS,MAAM,CAAC,GAAG,SAAS;AAC/I,QAAM,gBAAgB,SAAS,OAAO,YAAU,OAAO,WAAW,SAAS,EAAE,UAAU;AACvF,QAAM,eAAe,SAAS,OAAO,YAAU,OAAO,SAAS,WAAW,OAAO,WAAW,SAAS,EAAE,UAAU;AAEjH,iBAAe,OAAO,KAAa,QAA6B,SAAiB,WAAwB;AACvG,QAAI,QAAQ,cAAe;AAC3B,UAAM,qBAAqB,YAAY;AACvC,YAAQ,GAAG;AACX,aAAS,IAAI;AACb,QAAI;AACF,YAAM,OAAO;AACb,UAAI,CAAC,aAAa,kBAAkB,EAAG;AACvC,YAAM,YAAY,MAAM,OAAO,kBAAkB;AACjD,UAAI,CAAC,aAAa,kBAAkB,EAAG;AACvC,kBAAY;AACZ,iBAAW,YACP,EAAE,MAAM,WAAW,SAAS,QAAQ,IACpC,EAAE,MAAM,SAAS,SAAS,mEAAmE,CAAC;AAAA,IACpG,SAAS,OAAO;AACd,UAAI,CAAC,aAAa,kBAAkB,EAAG;AACvC,YAAM,SAAS,aAAa,KAAK;AACjC,eAAS,MAAM;AACf,iBAAW,EAAE,MAAM,SAAS,SAAS,OAAO,CAAC;AAAA,IAC/C,UAAE;AACA,UAAI,aAAa,kBAAkB,EAAG,SAAQ,IAAI;AAAA,IACpD;AAAA,EACF;AAEA,WAAS,MAAM;AACb,UAAM,UAAU,QAAQ,KAAK;AAC7B,QAAI,CAAC,WAAW,CAAC,gBAAgB,YAAY,QAAS,eAAe,UAAa,iBAAiB,WAAa;AAChH,SAAK,OAAO,OAAO,MAAM,UAAU,QAAQ,IAAI,QAAQ,EAAE,SAAS,SAAS,MAAM,cAAc,GAAI,MAAM,KAAK,IAAI,EAAE,OAAO,MAAM,KAAK,EAAE,IAAI,CAAC,EAAG,CAAC,GAAG,yDAAyD,MAAM;AACjN,iBAAW,EAAE;AACb,eAAS,EAAE;AAAA,IACb,CAAC;AAAA,EACH;AAEA,SAAO,gBAAAM,MAAC,aAAQ,WAAU,gBAAe,cAAW,gBAClD;AAAA,oBAAAA,MAAC,YAAO,WAAU,yBAChB;AAAA,sBAAAA,MAAC,SAAI;AAAA,wBAAAD,KAAC,QAAG,KAAK,SAAS,UAAU,IAAI,qBAAO;AAAA,QAAK,gBAAAA,KAAC,OAAE,mEAAqD;AAAA,SAAI;AAAA,MAC5G,WAAW,gBAAAC,MAAC,UAAK,WAAU,uBAAuB;AAAA;AAAA,QAAc;AAAA,QAAE,kBAAkB,IAAI,WAAW;AAAA,SAAU;AAAA,OAChH;AAAA,IACC,SAAS,gBAAAA,MAAC,SAAI,WAAU,uBAAsB,MAAK,SAClD;AAAA,sBAAAD,KAAC,UAAM,iBAAM;AAAA,OACX,YAAY,QAAQ,kBAAkB,gBAAAA,KAAC,YAAO,MAAK,UAAS,UAAU,SAAS,MAAM,SAAS,MAAM,KAAK,MAAM,GAAG,mBAAK;AAAA,OAC3H;AAAA,IACC,YAAY,OAAO,CAAC,SAAS,gBAAAA,KAAC,OAAE,WAAU,uBAAsB,MAAK,UAAS,mCAAgB,IAAO,gBAAAC,MAAC,SAAI,WAAU,sBAClH;AAAA,cAAQ,WAAW,KAAK,gBAAAD,KAAC,OAAE,WAAU,uBAAuB,mBAAS,8EAA8E,mBAAkB;AAAA,MACrK,QAAQ,IAAI,YAAU;AACrB,cAAM,YAAY,OAAO,SAAS,WAAW,gBAAgB;AAC7D,cAAM,WAAW,aAAa,OAAO,WAAW,cAAc,CAAC,aAAa;AAC5E,eAAO,gBAAAC,MAAC,SAAI,WAAU,qBACpB;AAAA,0BAAAA,MAAC,SAAI,WAAU,0BACb;AAAA,4BAAAD,KAAC,UAAK,WAAW,4CAA4C,OAAO,MAAM,IAAK,iBAAO,OAAO,MAAM,EAAE,OAAM;AAAA,YAC3G,gBAAAA,KAAC,YAAQ,iBAAO,SAAS,OAAO,SAAQ;AAAA,YACvC,OAAO,SAAS,gBAAAA,KAAC,OAAG,iBAAO,SAAQ;AAAA,YACpC,gBAAAA,KAAC,OAAG,iBAAO,OAAO,MAAM,EAAE,QAAO;AAAA,aACnC;AAAA,UACA,gBAAAA,KAAC,SAAI,WAAU,6BACZ,qBAAW,gBAAAC,MAAAF,WAAA,EACT;AAAA,+BAAmB,CAAC,YAAY,gBAAAE,MAAC,WAAM,WAAU,+BAA8B;AAAA;AAAA,cAC9E,gBAAAA,MAAC,YAAO,OAAO,OAAO,MAAM,UAAU,SAAS,QAAQ,eAAe,UAAU,WAAS,KAAK,OAAO,OAAO,IAAI,MAAM,UAAU,QAAQ,OAAO,QAAQ,OAAO,IAAI,EAAE,MAAM,MAAM,OAAO,MAAM,CAAC,GAAG,eAAe,GAC7M;AAAA,iBAAC,MAAM,KAAK,UAAQ,KAAK,UAAU,OAAO,IAAI,KAAK,gBAAAD,KAAC,YAAO,OAAO,OAAO,MAAO,iBAAO,MAAK;AAAA,gBAC5F,MAAM,IAAI,UAAQ,gBAAAC,MAAC,YAAwB,OAAO,KAAK,OAAQ;AAAA,uBAAK;AAAA,kBAAO,KAAK,UAAU,SAAS,oBAAiB;AAAA,qBAArF,KAAK,KAAmF,CAAS;AAAA,iBACnI;AAAA,eACF,IAAW,gBAAAD,KAAC,UAAK,WAAU,sBAAsB,gBAAM,KAAK,UAAQ,KAAK,UAAU,OAAO,IAAI,GAAG,SAAS,OAAO,MAAK;AAAA,YACrH,cAAc,OAAO,KAAK,gBAAAC,MAAC,SAAI,WAAU,yBAAwB,MAAK,SAAQ,cAAY,UAAU,OAAO,SAAS,OAAO,OAAO,KACjI;AAAA,8BAAAA,MAAC,UAAK;AAAA;AAAA,gBAAQ,OAAO,SAAS,OAAO;AAAA,gBAAQ;AAAA,iBAAC;AAAA,cAC9C,gBAAAD,KAAC,YAAO,KAAK,eAAe,MAAK,UAAS,WAAU,wBAAuB,UAAU,SAAS,QAAQ,eAAe,SAAS,MAAM,KAAK,OAAO,OAAO,IAAI,MAAM,UAAU,QAAQ,OAAO,QAAQ,OAAO,EAAE,GAAG,mBAAmB,MAAM,kBAAkB,OAAO,EAAE,CAAC,GAAG,oBAAM;AAAA,cAC5Q,gBAAAA,KAAC,YAAO,MAAK,UAAS,UAAU,SAAS,MAAM,SAAS,MAAM,kBAAkB,OAAO,EAAE,GAAG,kBAAI;AAAA,eAClG,IAAS,gBAAAA,KAAC,YAAO,KAAK,UAAQ;AAAE,kBAAI,KAAM,gBAAe,QAAQ,IAAI,OAAO,IAAI,IAAI;AAAA,kBAAQ,gBAAe,QAAQ,OAAO,OAAO,EAAE;AAAA,YAAE,GAAG,MAAK,UAAS,WAAU,uBAAsB,UAAU,SAAS,QAAQ,eAAe,SAAS,MAAM,aAAa,OAAO,EAAE,GAAG,oBAAM;AAAA,aAChR,IAAM,gBAAAA,KAAC,UAAK,WAAU,sBAAsB,gBAAM,KAAK,UAAQ,KAAK,UAAU,OAAO,IAAI,GAAG,SAAS,OAAO,MAAK,GACnH;AAAA,aArB6C,OAAO,EAsBtD;AAAA,MACF,CAAC;AAAA,OACH;AAAA,IAEC,aAAa,UAAU,YAAY,SAAS,eAAe,UAAa,gBAAgB,eAAe,gBAAAC,MAAC,SAAI,WAAU,uBACrH;AAAA,sBAAAD,KAAC,QAAG,6BAAe;AAAA,MACnB,gBAAAA,KAAC,OAAE,0FAA4E;AAAA,MAC/E,gBAAAC,MAAC,SAAI,WAAU,wBACb;AAAA,wBAAAA,MAAC,WAAM;AAAA;AAAA,UACL,gBAAAD,KAAC,WAAM,OAAO,SAAS,UAAU,WAAS,WAAW,MAAM,OAAO,KAAK,GAAG,cAAa,OAAM,aAAY,oCAAmC;AAAA,WAC9I;AAAA,QACA,gBAAAC,MAAC,WAAM;AAAA;AAAA,UACL,gBAAAD,KAAC,WAAM,OAAO,OAAO,UAAU,WAAS,SAAS,MAAM,OAAO,KAAK,GAAG,aAAY,eAAc;AAAA,WAClG;AAAA,QACA,gBAAAC,MAAC,WAAM;AAAA;AAAA,UACL,gBAAAD,KAAC,YAAO,OAAO,cAAc,UAAU,MAAM,WAAW,GAAG,UAAU,WAAS,QAAQ,MAAM,OAAO,KAAK,GACrG,gBAAM,IAAI,UAAQ,gBAAAC,MAAC,YAAwB,OAAO,KAAK,OAAQ;AAAA,iBAAK;AAAA,YAAO,KAAK,UAAU,SAAS,oBAAiB;AAAA,eAArF,KAAK,KAAmF,CAAS,GACnI;AAAA,WACF;AAAA,SACF;AAAA,MACA,gBAAAD,KAAC,YAAO,MAAK,UAAS,WAAU,yBAAwB,UAAU,SAAS,QAAQ,iBAAiB,CAAC,QAAQ,KAAK,KAAK,CAAC,cAAc,SAAS,KAC5I,mBAAS,QAAQ,mBAAc,iBAClC;AAAA,OACF;AAAA,KACF;AACF;;;AC7LM,SAeoB,YAAAG,WAff,OAAAC,MAAL,QAAAC,aAAA;AATN,SAAS,UAAU,OAAuB;AACxC,QAAM,OAAO,IAAI,KAAK,KAAK;AAC3B,SAAO,OAAO,MAAM,KAAK,QAAQ,CAAC,IAAI,QAAQ,KAAK,eAAe;AACpE;AAGO,SAAS,YAAY,EAAE,KAAK,GAAqB;AACtD,SAAO,gBAAAA,MAAC,aAAQ,WAAU,gBAAe,cAAW,gBAClD;AAAA,oBAAAD,KAAC,YAAO,WAAU,yBAChB,0BAAAC,MAAC,SAAI;AAAA,sBAAAD,KAAC,QAAG,qBAAO;AAAA,MAAK,gBAAAA,KAAC,OAAG,eAAK,aAAY;AAAA,OAAI,GAChD;AAAA,IACA,gBAAAC,MAAC,QAAG,WAAU,yBACZ;AAAA,sBAAAA,MAAC,SACC;AAAA,wBAAAD,KAAC,QAAG,kBAAI;AAAA,QACR,gBAAAA,KAAC,QAAI,eAAK,UAAU,SAAS,cAAc,KAAK,UAAU,QAAQ,sBAAqB;AAAA,QACtF,KAAK,UAAU,SAAS,eAAe,gBAAAC,MAAC,QAAG,WAAU,gCAA+B;AAAA;AAAA,UAAY,KAAK;AAAA,UAAc;AAAA,WAAC;AAAA,SACvH;AAAA,MACA,gBAAAA,MAAC,SACC;AAAA,wBAAAD,KAAC,QAAG,yBAAW;AAAA,QACf,gBAAAA,KAAC,QAAI,eAAK,UAAU,SAAS,eAAe,uBAAuB,KAAK,UAAU,OAAM;AAAA,QACvF,KAAK,UAAU,SAAS,YAAY,gBAAAA,KAAC,QAAG,WAAU,gCAA+B,4DAA8C;AAAA,SAClI;AAAA,MACA,gBAAAC,MAAC,SACC;AAAA,wBAAAD,KAAC,QAAG,uBAAS;AAAA,QACZ,KAAK,YAAY,gBAAAC,MAAAF,WAAA,EAChB;AAAA,0BAAAE,MAAC,QAAI;AAAA,iBAAK,UAAU;AAAA,YAAqB;AAAA,aAA0B;AAAA,UAClE,KAAK,UAAU,SAAS,UAAa,KAAK,UAAU,UAAU,UAC7D,gBAAAA,MAAC,QAAG,WAAU,gCAAgC;AAAA,iBAAK,UAAU;AAAA,YAAK;AAAA,YAAK,KAAK,UAAU;AAAA,YAAM;AAAA,YAAY,KAAK,UAAU,WAAW,gBAAa,UAAU,KAAK,UAAU,QAAQ,CAAC,KAAK;AAAA,aAAG;AAAA,WAC7L,IAAM,gBAAAD,KAAC,QAAG,mCAAqB;AAAA,SACjC;AAAA,OACF;AAAA,KACF;AACF;","names":["occupied","line","useEffect","useLayoutEffect","useRef","useState","Fragment","jsx","jsxs","useBrowserLayoutEffect","Fragment","jsx","jsxs"]}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
.tangle-lines {
|
|
2
|
+
--tl-text: var(--line-kit-text, var(--text-primary, var(--fg, #17212b)));
|
|
3
|
+
--tl-muted: var(--line-kit-muted, var(--text-secondary, var(--fg-muted, #64748b)));
|
|
4
|
+
--tl-border: var(--line-kit-border, var(--border-default, var(--line-subtle, #dbe1e7)));
|
|
5
|
+
--tl-surface: var(--line-kit-surface, var(--bg-input, #fff));
|
|
6
|
+
--tl-accent: var(--line-kit-accent, var(--brand-primary, #2856a1));
|
|
7
|
+
--tl-accent-foreground: var(--line-kit-accent-foreground, hsl(var(--primary-foreground, 0 0% 100%)));
|
|
8
|
+
--tl-success: var(--line-kit-success, var(--success-11, hsl(var(--success, 160 84% 26%))));
|
|
9
|
+
--tl-warning: var(--line-kit-warning, var(--warning-11, hsl(var(--warning-strong, 41 96% 27%))));
|
|
10
|
+
--tl-danger: var(--line-kit-danger, var(--text-danger, #a62f3b));
|
|
11
|
+
color: var(--tl-text);
|
|
12
|
+
font: inherit;
|
|
13
|
+
width: 100%;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.tangle-lines * { box-sizing: border-box; }
|
|
17
|
+
.tangle-lines h2, .tangle-lines h3, .tangle-lines p { margin: 0; }
|
|
18
|
+
.tangle-lines h2 { font-size: 1.35rem; font-weight: 650; line-height: 1.25; }
|
|
19
|
+
.tangle-lines h3 { font-size: 1.05rem; font-weight: 650; line-height: 1.3; }
|
|
20
|
+
.tangle-lines p { line-height: 1.45; }
|
|
21
|
+
.tangle-lines button, .tangle-lines input, .tangle-lines select { font: inherit; }
|
|
22
|
+
.tangle-lines button { cursor: pointer; }
|
|
23
|
+
.tangle-lines button:disabled { cursor: not-allowed; opacity: .55; }
|
|
24
|
+
.tangle-lines :is(button, input, select):focus-visible { outline: 2px solid var(--tl-accent); outline-offset: 2px; }
|
|
25
|
+
.tangle-lines h2:focus-visible { outline: 2px solid var(--tl-accent); outline-offset: 4px; }
|
|
26
|
+
|
|
27
|
+
.tangle-lines__heading {
|
|
28
|
+
align-items: start;
|
|
29
|
+
display: flex;
|
|
30
|
+
gap: 1rem;
|
|
31
|
+
justify-content: space-between;
|
|
32
|
+
margin-bottom: 1rem;
|
|
33
|
+
}
|
|
34
|
+
.tangle-lines__heading p { color: var(--tl-muted); font-size: .91rem; margin-top: .25rem; }
|
|
35
|
+
.tangle-lines__count { color: var(--tl-muted); font-size: .82rem; padding-top: .25rem; white-space: nowrap; }
|
|
36
|
+
.tangle-lines__list { border-top: 1px solid var(--tl-border); }
|
|
37
|
+
.tangle-lines__row {
|
|
38
|
+
align-items: start;
|
|
39
|
+
border-bottom: 1px solid var(--tl-border);
|
|
40
|
+
display: flex;
|
|
41
|
+
gap: 1rem;
|
|
42
|
+
justify-content: space-between;
|
|
43
|
+
padding: 1rem 0;
|
|
44
|
+
}
|
|
45
|
+
.tangle-lines__row-main { min-width: 0; }
|
|
46
|
+
.tangle-lines__row-main strong { display: block; font-size: 1rem; font-weight: 600; overflow-wrap: anywhere; }
|
|
47
|
+
.tangle-lines__row-main p { color: var(--tl-muted); font-size: .85rem; margin-top: .3rem; }
|
|
48
|
+
.tangle-lines__row-main code { color: var(--tl-text); font-size: .92em; font-weight: 600; white-space: nowrap; }
|
|
49
|
+
.tangle-lines__eyebrow { color: var(--tl-muted); display: block; font-size: .79rem; margin-bottom: .25rem; }
|
|
50
|
+
.tangle-lines__row-actions { align-items: end; display: flex; flex-direction: column; gap: .5rem; }
|
|
51
|
+
.tangle-lines__row-actions button, .tangle-lines__error button, .tangle-lines__empty button { background: none; border: 0; color: var(--tl-accent); padding: .25rem 0; white-space: nowrap; }
|
|
52
|
+
.tangle-lines__row-actions button:hover, .tangle-lines__error button:hover, .tangle-lines__empty button:hover { text-decoration: underline; }
|
|
53
|
+
.tangle-lines__confirm { align-items: center; display: flex; flex-wrap: wrap; gap: .65rem; justify-content: flex-end; max-width: 20rem; }
|
|
54
|
+
.tangle-lines__confirm span { color: var(--tl-muted); font-size: .84rem; }
|
|
55
|
+
.tangle-lines__confirm .tangle-lines__danger { color: var(--tl-danger); }
|
|
56
|
+
.tangle-lines__state { color: var(--tl-muted); display: inline-block; font-size: .79rem; margin-bottom: .25rem; }
|
|
57
|
+
.tangle-lines__state--active { color: var(--tl-success); }
|
|
58
|
+
.tangle-lines__state--stopped { color: var(--tl-danger); }
|
|
59
|
+
.tangle-lines__state--invited { color: var(--tl-warning); }
|
|
60
|
+
.tangle-lines__role { color: var(--tl-muted); font-size: .86rem; white-space: nowrap; }
|
|
61
|
+
.tangle-lines__compact-label { color: var(--tl-muted); font-size: .8rem; }
|
|
62
|
+
.tangle-lines__compact-label select { margin-top: .2rem; min-width: 8rem; }
|
|
63
|
+
|
|
64
|
+
.tangle-lines__setup { border: 1px solid var(--tl-border); border-radius: .8rem; margin-top: 1.25rem; padding: 1.15rem; }
|
|
65
|
+
.tangle-lines__setup > p { color: var(--tl-muted); font-size: .87rem; margin-top: .35rem; }
|
|
66
|
+
.tangle-lines__kinds { display: flex; flex-wrap: wrap; gap: .4rem; margin-top: 1rem; }
|
|
67
|
+
.tangle-lines__kinds button {
|
|
68
|
+
background: transparent;
|
|
69
|
+
border: 1px solid var(--tl-border);
|
|
70
|
+
border-radius: .5rem;
|
|
71
|
+
color: var(--tl-muted);
|
|
72
|
+
padding: .48rem .72rem;
|
|
73
|
+
}
|
|
74
|
+
.tangle-lines__kinds button[aria-pressed="true"] { background: var(--tl-text); border-color: var(--tl-text); color: var(--tl-surface); }
|
|
75
|
+
.tangle-lines__fields { display: grid; gap: .9rem; grid-template-columns: repeat(2, minmax(0, 1fr)); margin: 1rem 0; }
|
|
76
|
+
.tangle-lines__fields > label, .tangle-lines__compact-label { display: flex; flex-direction: column; gap: .28rem; }
|
|
77
|
+
.tangle-lines__fields > label, .tangle-lines__fields legend { color: var(--tl-text); font-size: .85rem; font-weight: 550; }
|
|
78
|
+
.tangle-lines__fields :is(input:not([type="radio"]), select), .tangle-lines__compact-label select {
|
|
79
|
+
background: var(--tl-surface);
|
|
80
|
+
border: 1px solid var(--tl-border);
|
|
81
|
+
border-radius: .45rem;
|
|
82
|
+
color: var(--tl-text);
|
|
83
|
+
min-height: 2.5rem;
|
|
84
|
+
padding: .45rem .65rem;
|
|
85
|
+
width: 100%;
|
|
86
|
+
}
|
|
87
|
+
.tangle-lines__fields fieldset { border: 0; display: flex; flex-direction: column; gap: .45rem; margin: 0; min-width: 0; padding: 0; }
|
|
88
|
+
.tangle-lines__fields fieldset label { align-items: center; color: var(--tl-muted); display: flex; font-size: .85rem; gap: .45rem; }
|
|
89
|
+
.tangle-lines__fields fieldset input { accent-color: var(--tl-accent); margin: 0; }
|
|
90
|
+
.tangle-lines__primary { background: var(--tl-accent); border: 1px solid var(--tl-accent); border-radius: .5rem; color: var(--tl-accent-foreground); font-size: .88rem; font-weight: 600; min-height: 2.55rem; padding: .55rem .85rem; }
|
|
91
|
+
.tangle-lines__primary:hover:not(:disabled) { filter: brightness(.92); }
|
|
92
|
+
.tangle-lines__muted, .tangle-lines__empty { color: var(--tl-muted); font-size: .88rem; padding: .7rem 0; }
|
|
93
|
+
.tangle-lines__error { align-items: center; border: 1px solid var(--tl-danger); border-radius: .5rem; color: var(--tl-danger); display: flex; font-size: .85rem; gap: .7rem; justify-content: space-between; margin-bottom: .8rem; padding: .65rem .8rem; }
|
|
94
|
+
.tangle-lines__warning { color: var(--tl-warning); font-size: .85rem; margin-bottom: .8rem !important; }
|
|
95
|
+
|
|
96
|
+
.tangle-lines__billing { border-top: 1px solid var(--tl-border); margin: 0; }
|
|
97
|
+
.tangle-lines__billing > div { border-bottom: 1px solid var(--tl-border); display: grid; gap: .1rem 1rem; grid-template-columns: 9rem minmax(0, 1fr); padding: .85rem 0; }
|
|
98
|
+
.tangle-lines__billing dt { color: var(--tl-muted); font-size: .85rem; }
|
|
99
|
+
.tangle-lines__billing dd { font-size: .91rem; font-weight: 600; margin: 0; }
|
|
100
|
+
.tangle-lines__billing dd.tangle-lines__billing-detail { color: var(--tl-muted); font-size: .81rem; font-weight: 400; grid-column: 2; }
|
|
101
|
+
|
|
102
|
+
@media (max-width: 600px) {
|
|
103
|
+
.tangle-lines__heading { align-items: start; }
|
|
104
|
+
.tangle-lines__row { flex-direction: column; gap: .7rem; }
|
|
105
|
+
.tangle-lines__row-actions { align-items: start; width: 100%; }
|
|
106
|
+
.tangle-lines__confirm { justify-content: flex-start; }
|
|
107
|
+
.tangle-lines__fields { grid-template-columns: 1fr; }
|
|
108
|
+
.tangle-lines__billing > div { grid-template-columns: 1fr; }
|
|
109
|
+
.tangle-lines__billing dd.tangle-lines__billing-detail { grid-column: 1; }
|
|
110
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
//# sourceMappingURL=styles.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export interface AgentHomeWorkflowOptions {
|
|
2
|
+
/** Stable name for idempotent operator reconciliation. */
|
|
3
|
+
instanceKey: string;
|
|
4
|
+
lineId: string;
|
|
5
|
+
ownerAddress: string;
|
|
6
|
+
timeZone: string;
|
|
7
|
+
/** Additional owner-authorized watch instructions, not a schedule override. */
|
|
8
|
+
heartbeatInstructions?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface AgentHomeWorkflow {
|
|
11
|
+
name: string;
|
|
12
|
+
purpose: 'heartbeat' | 'consolidation';
|
|
13
|
+
/** JSON is valid YAML. Install with the published HubClient.workflows API. */
|
|
14
|
+
yaml: string;
|
|
15
|
+
}
|
|
16
|
+
/** Platform owns timing, admission, approvals, persistence and DM delivery. */
|
|
17
|
+
export declare function agentHomeWorkflows(options: AgentHomeWorkflowOptions): AgentHomeWorkflow[];
|
|
@@ -288,7 +288,7 @@ function buildMentionExtension(tiptap, trigger, suggestion) {
|
|
|
288
288
|
suggestion
|
|
289
289
|
});
|
|
290
290
|
}
|
|
291
|
-
var EDITOR_CLASS = "w-full whitespace-pre-wrap break-words bg-transparent px-1.5 py-1 text-base leading-6 text-foreground outline-none";
|
|
291
|
+
var EDITOR_CLASS = "w-full whitespace-pre-wrap break-words bg-transparent px-1.5 py-1 text-base leading-6 text-foreground focus:outline-none";
|
|
292
292
|
var FETCH_DEBOUNCE_MS = 100;
|
|
293
293
|
function mentionsKey(mentions) {
|
|
294
294
|
return JSON.stringify(mentions.map((item) => item.id));
|
|
@@ -351,6 +351,7 @@ function createMentionEditor(tiptap) {
|
|
|
351
351
|
role: "textbox",
|
|
352
352
|
"aria-multiline": "true",
|
|
353
353
|
"aria-label": "Message input",
|
|
354
|
+
"data-composer-input": "",
|
|
354
355
|
"aria-haspopup": "listbox"
|
|
355
356
|
},
|
|
356
357
|
handleKeyDown: (_view, event) => {
|
|
@@ -523,4 +524,4 @@ export {
|
|
|
523
524
|
loadMentionEditor,
|
|
524
525
|
loadTiptapModules
|
|
525
526
|
};
|
|
526
|
-
//# sourceMappingURL=mention-editor-
|
|
527
|
+
//# sourceMappingURL=mention-editor-ELAINF6H.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/web-react/mention-editor.tsx","../src/web-react/mention-list.tsx","../src/web-react/mention-serialize.ts"],"sourcesContent":["/**\n * The mention-capable rich input behind `ChatComposer`'s `mention` prop.\n *\n * The five `@tiptap/*` packages are OPTIONAL peers, and every value access to\n * them goes through `loadTiptapModules()`'s dynamic `import()` — never a\n * static named import. A bundler resolves a missing optional peer to a stub\n * module with a default export only, so a static `import { mergeAttributes }`\n * fails the CONSUMER'S build even when nothing ever renders the mention path\n * (measured against Vite 7's `__vite-optional-peer-dep` stub). The dynamic\n * form builds clean and defers the failure to the first actual editor load,\n * where it throws with the missing packages named — the same doctrine as\n * `sequences-react`'s transcription peer. Type-only imports are erased and\n * stay allowed.\n */\n\nimport type { MentionOptions } from '@tiptap/extension-mention'\nimport type { SuggestionKeyDownProps, SuggestionProps } from '@tiptap/suggestion'\nimport type * as TiptapCore from '@tiptap/core'\nimport type * as TiptapExtensionMention from '@tiptap/extension-mention'\nimport type * as TiptapReact from '@tiptap/react'\nimport type * as TiptapStarterKit from '@tiptap/starter-kit'\nimport type * as TiptapSuggestion from '@tiptap/suggestion'\nimport { useEffect, useId, useRef, useState, type ComponentType, type ReactNode } from 'react'\n\nimport { PopoverSurface } from './controls'\nimport { MENTION_PILL_CLASS } from './mention-pill'\nimport { MentionList, type MentionListHandle } from './mention-list'\nimport {\n collectMentions,\n parseMentionValue,\n serializeMentionDoc,\n type MentionDocNode,\n} from './mention-serialize'\nimport type { ComposerMentionProp, MentionItem } from './use-file-mentions'\n\n/** The loaded namespaces of the five optional peers, as one bag. */\nexport interface TiptapModules {\n core: typeof TiptapCore\n extensionMention: typeof TiptapExtensionMention\n react: typeof TiptapReact\n starterKit: typeof TiptapStarterKit\n suggestion: typeof TiptapSuggestion\n}\n\nconst PEERS_MISSING_MESSAGE =\n 'ChatComposer `mention` needs the @tiptap/* optional peers — install @tiptap/core, @tiptap/extension-mention, @tiptap/pm, @tiptap/react, @tiptap/starter-kit and @tiptap/suggestion (>=3.28.0 <4.0.0; @tiptap/suggestion also peers on @floating-ui/dom)'\n\n/** The rejection shapes bundlers produce for an UNINSTALLED module. Only\n * these get enriched with the install-set message — a transient chunk-fetch\n * failure keeps its own cause so diagnostics do not mislabel it as a missing\n * dependency. An unmatched resolution error stays unchanged, so the\n * classifier can only under-enrich. */\nconst RESOLUTION_FAILURE = /could not resolve|cannot find module|failed to resolve/i\n\n/** Resolve the optional peers, or throw naming every one of them. */\nexport async function loadTiptapModules(): Promise<TiptapModules> {\n let modules: TiptapModules\n try {\n const [core, extensionMention, react, starterKit, suggestion] = await Promise.all([\n import('@tiptap/core'),\n import('@tiptap/extension-mention'),\n import('@tiptap/react'),\n import('@tiptap/starter-kit'),\n import('@tiptap/suggestion'),\n ])\n modules = { core, extensionMention, react, starterKit, suggestion }\n } catch (error) {\n if (error instanceof Error && RESOLUTION_FAILURE.test(error.message)) {\n throw new Error(PEERS_MISSING_MESSAGE, { cause: error })\n }\n throw error\n }\n // A CommonJS-in-production bundler stubs a missing optional peer as a\n // SILENT empty namespace rather than a throwing module. Validate the\n // members the editor actually calls, so that shape still fails loud with\n // the peers named instead of as an undefined-property crash mid-render.\n if (\n typeof modules.core.mergeAttributes !== 'function' ||\n typeof modules.react.useEditor !== 'function' ||\n modules.react.EditorContent === undefined ||\n modules.extensionMention.default === undefined ||\n modules.starterKit.default === undefined ||\n typeof modules.suggestion.exitSuggestion !== 'function'\n ) {\n throw new Error(PEERS_MISSING_MESSAGE)\n }\n return modules\n}\n\nexport interface MentionEditorProps {\n value: string\n onChange: (value: string) => void\n /** Fired when Enter (no Shift, popover closed, not composing) should send. */\n onSubmit: () => void\n placeholder: string\n disabled?: boolean\n autoFocus?: boolean\n /** Pixel floor for the input box. The composer computes it from `minRows`\n * against its own line metrics, so the two input modes cannot drift. */\n minHeight: number\n /** Pixel height the input grows to before it scrolls. */\n maxHeight: number\n mention: ComposerMentionProp\n /** Basic input shown during server rendering and editor initialization. */\n fallback: ReactNode\n /** Registers a focus callback the composer wires to Cmd/Ctrl+L; called with\n * `null` on unmount so the composer never focuses a destroyed editor. */\n registerFocus?: (focus: (() => void) | null) => void\n /**\n * Clipboard files pulled off a paste. Returns true when consumed so the\n * editor suppresses its default text paste — the same funnel the textarea\n * path routes through to `onAttach`.\n */\n onPasteFiles?: (files: FileList) => boolean\n}\n\n/** Only text, hard breaks, and atomic mention pills — no marks, no formatting. */\nexport function buildComposerStarterKit(tiptap: TiptapModules) {\n return tiptap.starterKit.default.configure({\n blockquote: false,\n bold: false,\n bulletList: false,\n code: false,\n codeBlock: false,\n dropcursor: false,\n heading: false,\n horizontalRule: false,\n italic: false,\n link: false,\n listItem: false,\n listKeymap: false,\n orderedList: false,\n strike: false,\n trailingNode: false,\n underline: false,\n })\n}\n\n/**\n * The mention node: an inline atom (`inline: true, selectable: false,\n * atom: true` from `@tiptap/extension-mention`) carrying `{ id, label, kind }`,\n * rendered as a themed pill showing the label with the full id in `title`. Atom\n * means the cursor can't enter it and a single backspace removes the whole\n * thing. The `suggestion` config is supplied by the caller so the popover's\n * fetch/keyboard wiring stays in the component.\n */\nexport function buildMentionExtension(\n tiptap: TiptapModules,\n trigger: string,\n suggestion: MentionOptions['suggestion'],\n) {\n const { mergeAttributes } = tiptap.core\n return tiptap.extensionMention.default\n .extend({\n addAttributes() {\n return {\n id: {\n default: null,\n parseHTML: (element) => element.getAttribute('data-id'),\n renderHTML: (attrs) => (attrs.id ? { 'data-id': attrs.id } : {}),\n },\n label: {\n default: null,\n parseHTML: (element) => element.getAttribute('data-label'),\n renderHTML: (attrs) => (attrs.label ? { 'data-label': attrs.label } : {}),\n },\n kind: {\n default: null,\n parseHTML: (element) => element.getAttribute('data-kind'),\n renderHTML: (attrs) => (attrs.kind ? { 'data-kind': attrs.kind } : {}),\n },\n }\n },\n })\n .configure({\n HTMLAttributes: { class: MENTION_PILL_CLASS },\n renderText: ({ node }) => `${trigger}${node.attrs.id}`,\n renderHTML: ({ options, node }) => [\n 'span',\n mergeAttributes(options.HTMLAttributes, {\n title: node.attrs.id ?? undefined,\n }),\n `${trigger}${node.attrs.label ?? node.attrs.id}`,\n ],\n suggestion,\n })\n}\n\n/** Matches the composer textarea's own text treatment so the two input modes\n * render identically (`text-base leading-6`, the same padding). The compound\n * `focus:outline-none` (never the bare utility, which only ties the\n * tokens.css `:focus-visible` floor) is required for the same reason as on\n * that textarea: the composer card this editor mounts into draws the\n * keyboard indicator through `focus-within:`, so a second outline inside the\n * card would double the ring. */\nconst EDITOR_CLASS =\n 'w-full whitespace-pre-wrap break-words bg-transparent px-1.5 py-1 text-base leading-6 text-foreground focus:outline-none'\n\n/** Trailing debounce on the suggestion fetch so a fast typist doesn't hit the\n * provider once per keystroke. Fixed rather than a prop — 100ms is well under\n * perceived latency and the monotonic `requestId` already makes any race\n * harmless, so there's no tuning knob worth exposing. */\nconst FETCH_DEBOUNCE_MS = 100\n\n/** Stable key for a mention set — order- and id-sensitive, ignores label/kind\n * so cosmetic re-fetches of the same id don't read as a change. JSON-encoded:\n * ids are workspace paths that may contain spaces, so a plain join would make\n * `[\"a b\",\"c\"]` and `[\"a\",\"b c\"]` collide and suppress a real change. */\nfunction mentionsKey(mentions: MentionItem[]): string {\n return JSON.stringify(mentions.map((item) => item.id))\n}\n\n/** `React.lazy` payload: resolve the peers, then build the component. */\nexport async function loadMentionEditor(): Promise<{\n default: ComponentType<MentionEditorProps>\n}> {\n return { default: createMentionEditor(await loadTiptapModules()) }\n}\n\n/**\n * Builds the mention-capable rich input against loaded tiptap modules. Loaded\n * lazily by `ChatComposer` so textarea-only consumers never pull TipTap into\n * their initial bundle. It preserves every textarea behavior — controlled\n * plain-text `value`, Enter to send / Shift+Enter for a newline, placeholder,\n * disabled, autofocus, file-paste routing — and adds `@`-mention pills backed\n * by an async provider.\n *\n * The suggestion list renders through `PopoverSurface` anchored to the input\n * box, like the composer's slash menu: an in-place panel is a panel the host\n * clips away (see the popover canon in AGENTS.md).\n */\nexport function createMentionEditor(tiptap: TiptapModules): ComponentType<MentionEditorProps> {\n const { EditorContent, useEditor } = tiptap.react\n const { exitSuggestion } = tiptap.suggestion\n\n return function MentionEditor({\n value,\n onChange,\n onSubmit,\n placeholder,\n disabled,\n autoFocus,\n minHeight,\n maxHeight,\n mention,\n fallback,\n registerFocus,\n onPasteFiles,\n }: MentionEditorProps) {\n const trigger = mention.trigger ?? '@'\n\n const [open, setOpen] = useState(false)\n const [query, setQuery] = useState('')\n const [items, setItems] = useState<MentionItem[]>([])\n const [loading, setLoading] = useState(false)\n const [errored, setErrored] = useState(false)\n // Mirror of the list's highlight, for `aria-activedescendant` — keyboard\n // focus never leaves the editor, so the highlighted row must be announced\n // through the editable element itself.\n const [activeIndex, setActiveIndex] = useState(0)\n const listboxId = useId()\n\n // The input box PopoverSurface anchors to, and the portaled panel itself.\n const anchorRef = useRef<HTMLDivElement>(null)\n const panelRef = useRef<HTMLDivElement>(null)\n\n // Live refs keep the once-built editor's callbacks reading current props\n // and state without rebuilding the editor (which would drop cursor +\n // content).\n const openRef = useRef(false)\n const commandRef = useRef<((item: MentionItem) => void) | null>(null)\n const listRef = useRef<MentionListHandle | null>(null)\n // Grows for the component's lifetime — one entry per distinct id the user\n // actually PICKED (or that arrived in the document as a mention node).\n // Deliberately never fed from fetch results: restore-eligibility must\n // track selection, not visibility. Uncapped; fine for a chat composer's\n // lifetime, worth an LRU only if this outlives that.\n const knownRef = useRef<Map<string, MentionItem>>(new Map())\n const requestIdRef = useRef(0)\n // Last mention-id set reported via `onMentionsChange`, so a programmatic\n // restore that lands on the same mentions doesn't re-fire the callback.\n // Seeded with the EMPTY-set key (not ''): the initial document can never\n // hold pills (`knownRef` starts empty), so the first computed key is the\n // empty set's — starting anywhere else fires a spurious `[]` callback.\n const lastMentionsKeyRef = useRef(mentionsKey([]))\n const onSubmitRef = useRef(onSubmit)\n onSubmitRef.current = onSubmit\n const onChangeRef = useRef(onChange)\n onChangeRef.current = onChange\n const onMentionsChangeRef = useRef(mention.onMentionsChange)\n onMentionsChangeRef.current = mention.onMentionsChange\n const onPasteFilesRef = useRef(onPasteFiles)\n onPasteFilesRef.current = onPasteFiles\n const fetchItemsRef = useRef(mention.fetchItems)\n fetchItemsRef.current = mention.fetchItems\n\n const editor = useEditor(\n {\n immediatelyRender: false,\n editable: !disabled,\n autofocus: autoFocus ? 'end' : false,\n content: parseMentionValue(value, knownRef.current),\n editorProps: {\n attributes: {\n class: EDITOR_CLASS,\n role: 'textbox',\n 'aria-multiline': 'true',\n 'aria-label': 'Message input',\n 'data-composer-input': '',\n 'aria-haspopup': 'listbox',\n },\n handleKeyDown: (_view, event) => {\n if (event.key !== 'Enter' || event.shiftKey) return false\n // Composition (IME) commits via Enter — never send mid-composition.\n if (event.isComposing || event.keyCode === 229) return false\n // Popover open ⇒ Enter belongs to the suggestion plugin (selects).\n if (openRef.current) return false\n event.preventDefault()\n onSubmitRef.current()\n return true\n },\n handlePaste: (_view, event) => {\n const files = event.clipboardData?.files\n if (files && files.length > 0 && onPasteFilesRef.current) {\n return onPasteFilesRef.current(files)\n }\n return false\n },\n },\n onUpdate: ({ editor }) => {\n const json = editor.getJSON() as MentionDocNode\n onChangeRef.current(serializeMentionDoc(json))\n const mentions = collectMentions(json)\n for (const item of mentions) knownRef.current.set(item.id, item)\n // Report only when the mention SET changed — typing prose around an\n // unchanged pill must not re-fire the callback on every keystroke.\n const key = mentionsKey(mentions)\n if (key !== lastMentionsKeyRef.current) {\n lastMentionsKeyRef.current = key\n onMentionsChangeRef.current?.(mentions)\n }\n },\n extensions: [\n buildComposerStarterKit(tiptap),\n buildMentionExtension(tiptap, trigger, {\n char: trigger,\n allowSpaces: false,\n // Items are fetched by this component (so it can model loading and\n // error states), not by the suggestion plugin.\n items: () => [],\n command: ({ editor, range, props }) => {\n const item = props as unknown as MentionItem\n editor\n .chain()\n .focus()\n .insertContentAt(range, [\n {\n type: 'mention',\n attrs: {\n id: item.id,\n label: item.label,\n kind: item.kind ?? null,\n },\n },\n { type: 'text', text: ' ' },\n ])\n .run()\n knownRef.current.set(item.id, item)\n },\n render: () => ({\n onStart: (props: SuggestionProps) => {\n openRef.current = true\n commandRef.current = props.command\n setOpen(true)\n setQuery(props.query)\n },\n onUpdate: (props: SuggestionProps) => {\n commandRef.current = props.command\n setQuery(props.query)\n },\n onKeyDown: (props: SuggestionKeyDownProps) => {\n if (props.event.key === 'Escape') {\n exitSuggestion(props.view)\n return true\n }\n return listRef.current?.onKeyDown(props.event) ?? false\n },\n onExit: () => {\n openRef.current = false\n commandRef.current = null\n setOpen(false)\n setItems([])\n setLoading(false)\n setErrored(false)\n },\n }),\n }),\n ],\n },\n [],\n )\n\n // Fetch matches for the active query, trailing-debounced so a fast typist\n // doesn't hit the provider once per keystroke. The monotonic id still\n // drops stale responses (belt-and-suspenders against a provider that\n // resolves out of order); the debounce timer itself is cancelled by the\n // effect cleanup whenever `query` changes again before it fires.\n useEffect(() => {\n if (!open) return\n const requestId = (requestIdRef.current += 1)\n setLoading(true)\n setErrored(false)\n const timer = setTimeout(() => {\n // The provider is called INSIDE the chain so a synchronous throw lands\n // in the same `.catch` as a rejection — outside it, the exception\n // escapes the timer callback and the panel is stuck on \"Searching…\".\n Promise.resolve()\n .then(() => fetchItemsRef.current(query))\n .then((results) => {\n if (requestId !== requestIdRef.current) return\n // Results are NOT written into `knownRef`: merely appearing in a\n // suggestion list must not make an id restore-eligible, or a\n // later restore would promote prose the user typed (never picked)\n // into a structured mention. Only a pick (`command`) or a mention\n // already in the document teaches the restore path an id.\n setItems(results)\n setLoading(false)\n })\n .catch(() => {\n if (requestId !== requestIdRef.current) return\n setErrored(true)\n setLoading(false)\n })\n }, FETCH_DEBOUNCE_MS)\n return () => clearTimeout(timer)\n }, [open, query])\n\n // Programmatic `value` changes\n // re-parse into the document; `@<id>` runs come back as pills only for\n // ids already in `knownRef` at parse time. That conservatism is the\n // contract, not a gap: the serialized form cannot distinguish a PICKED\n // pill from `@text` the user simply typed (a pick is the only thing that\n // creates a mention in-session), so resolving unknown runs against the\n // provider at hydration would fabricate structured attachments from\n // plain prose. An id never fetched or inserted in this session therefore\n // stays literal text, exactly as it would have if typed. Guarded against\n // the onChange→value feedback loop by comparing the serialized form.\n //\n // `setContent` runs with `emitUpdate: false` (this is not a user edit),\n // so `onMentionsChange` — normally wired only off `onUpdate` — is fired\n // here explicitly, but only when the restored mention set actually\n // differs from the last one reported, so a restore that changes plain\n // text around unchanged mentions doesn't re-fire spuriously.\n useEffect(() => {\n if (!editor) return\n if (serializeMentionDoc(editor.getJSON() as MentionDocNode) === value) return\n const doc = parseMentionValue(value, knownRef.current)\n editor.commands.setContent(doc, { emitUpdate: false })\n const mentions = collectMentions(doc)\n const key = mentionsKey(mentions)\n if (key !== lastMentionsKeyRef.current) {\n lastMentionsKeyRef.current = key\n onMentionsChangeRef.current?.(mentions)\n }\n }, [editor, value])\n\n useEffect(() => {\n editor?.setEditable(!disabled)\n }, [editor, disabled])\n\n // Announce the popover through the editable element: focus stays in the\n // editor while ↑/↓ move the listbox highlight, so without\n // `aria-activedescendant` a screen reader hears nothing change. Set\n // imperatively — `editorProps.attributes` is fixed at editor creation.\n useEffect(() => {\n if (!editor) return\n const dom = editor.view.dom\n dom.setAttribute('aria-expanded', open ? 'true' : 'false')\n if (open) dom.setAttribute('aria-controls', listboxId)\n else dom.removeAttribute('aria-controls')\n const hasRows = open && !loading && !errored && items.length > 0\n if (hasRows) dom.setAttribute('aria-activedescendant', `${listboxId}-opt-${activeIndex}`)\n else dom.removeAttribute('aria-activedescendant')\n }, [editor, open, loading, errored, items.length, activeIndex, listboxId])\n\n useEffect(() => {\n if (!editor || !registerFocus) return\n registerFocus(() => editor.commands.focus())\n // Unregister on unmount so the composer's focus shortcut can never call\n // into a destroyed editor.\n return () => registerFocus(null)\n }, [editor, registerFocus])\n\n if (!editor) return fallback\n\n return (\n <div className=\"relative\">\n <div ref={anchorRef} className=\"overflow-y-auto\" style={{ maxHeight, minHeight }}>\n <EditorContent editor={editor} />\n {value.length === 0 && (\n <div className=\"pointer-events-none absolute left-1.5 top-1 text-base leading-6 text-muted-foreground\">\n {placeholder}\n </div>\n )}\n </div>\n\n {/* The panel's surface classes (and the consumer's `popoverClassName`)\n live on MentionList itself; the surface only places it. The outer\n scroll keeps rows reachable when the viewport cap undercuts the\n list's own max height. */}\n <PopoverSurface\n open={open}\n triggerRef={anchorRef}\n panelRef={panelRef}\n className=\"overflow-y-auto\"\n >\n <MentionList\n ref={listRef}\n id={listboxId}\n items={items}\n loading={loading}\n error={errored}\n emptyText={mention.emptyText}\n renderItem={mention.renderItem}\n onSelect={(item) => commandRef.current?.(item)}\n onActiveChange={setActiveIndex}\n className={mention.popoverClassName}\n />\n </PopoverSurface>\n </div>\n )\n }\n}\n","import { forwardRef, useEffect, useImperativeHandle, useRef, useState } from 'react'\nimport type { ReactNode } from 'react'\n\nimport { joinClasses } from './class-names'\nimport type { MentionItem } from './use-file-mentions'\n\n/** Imperative surface the editor's suggestion keymap drives. */\nexport interface MentionListHandle {\n /** Returns true when the key was consumed — the editor must not also act. */\n onKeyDown: (event: KeyboardEvent) => boolean\n}\n\nexport interface MentionListProps {\n items: MentionItem[]\n loading: boolean\n error: boolean\n /** Shown when the fetch resolved to zero items. Default \"No matches\". */\n emptyText?: string\n renderItem?: (item: MentionItem) => ReactNode\n onSelect: (item: MentionItem) => void\n /** Root `role=\"listbox\"` element id; option rows derive stable ids from it\n * (`<id>-opt-<index>`) so the editor can point `aria-activedescendant` at\n * the highlighted row. */\n id?: string\n /** Fired whenever the highlight moves (keys, hover, or a result-set\n * re-home) — what keeps the editor's `aria-activedescendant` current. */\n onActiveChange?: (index: number) => void\n /** Extra classes merged onto the panel's root element, applied last so\n * they win over the component's own. */\n className?: string\n}\n\n/**\n * The mention suggestion list: a flat, keyboard-driven menu with loading,\n * empty, and error states. Selection is owned here so ↑/↓ and Enter/Tab\n * resolve against the highlighted row; every key it handles is reported\n * consumed so the composer's Enter-to-send never fires while open. The panel\n * carries its own surface classes but no positioning — the editor places it\n * through `PopoverSurface`, per the popover canon.\n */\nexport const MentionList = forwardRef<MentionListHandle, MentionListProps>(\n function MentionList(\n { items, loading, error, emptyText = 'No matches', renderItem, onSelect, id, onActiveChange, className },\n ref,\n ) {\n const [selected, setSelected] = useState(0)\n // Mirrors `selected` so the key handler reads the live index even when keys\n // arrive faster than React commits (e.g. synchronous test-driven calls).\n const selectedRef = useRef(0)\n const onActiveChangeRef = useRef(onActiveChange)\n onActiveChangeRef.current = onActiveChange\n // One entry per rendered row, so the highlight can be scrolled into view\n // from an effect (once per highlight move) instead of a ref callback\n // (which re-fires on every re-render of the row).\n const optionRefs = useRef<Array<HTMLButtonElement | null>>([])\n const move = (next: number) => {\n selectedRef.current = next\n setSelected(next)\n onActiveChangeRef.current?.(next)\n }\n\n // Keyboard navigation must keep the highlight visible inside the panel's\n // own scroll (`max-h-64 overflow-y-auto`).\n useEffect(() => {\n optionRefs.current[selected]?.scrollIntoView({ block: 'nearest' })\n }, [selected, items])\n\n // A new result set re-homes the highlight to the top.\n useEffect(() => {\n move(0)\n }, [items])\n\n useImperativeHandle(\n ref,\n () => ({\n onKeyDown(event) {\n // The handler must agree with the render: while loading or errored\n // the rows are not painted, so `items` still holding the PREVIOUS\n // query's results must not make Enter select an invisible file.\n const count = loading || error ? 0 : items.length\n switch (event.key) {\n case 'ArrowDown':\n if (count > 0) move((selectedRef.current + 1) % count)\n return true\n case 'ArrowUp':\n if (count > 0) move((selectedRef.current - 1 + count) % count)\n return true\n case 'Enter':\n // Consume even with nothing to select so the message never\n // submits while the popover is open. Clamp the index: a\n // shrunken result set re-homes the highlight in an effect, and\n // a key can arrive before that effect commits.\n if (count > 0) onSelect(items[Math.min(selectedRef.current, count - 1)]!)\n return true\n case 'Tab':\n // Tab selects only when a row exists; with nothing selectable\n // (loading, error, empty) it must fall through to normal focus\n // navigation — consuming it would trap keyboard focus.\n if (count === 0) return false\n onSelect(items[Math.min(selectedRef.current, count - 1)]!)\n return true\n default:\n return false\n }\n },\n }),\n [items, loading, error, onSelect],\n )\n\n return (\n <div\n role=\"listbox\"\n id={id}\n aria-label=\"File mentions\"\n className={joinClasses(\n 'max-h-64 min-w-[16rem] max-w-sm overflow-y-auto rounded-xl border border-border',\n 'bg-popover p-1 text-popover-foreground shadow-lg',\n className,\n )}\n >\n {loading && (\n <div className=\"flex items-center gap-2 px-2.5 py-2 text-sm text-muted-foreground\">\n <span className=\"h-3.5 w-3.5 animate-spin rounded-full border-2 border-primary border-t-transparent\" />\n Searching…\n </div>\n )}\n\n {!loading && error && (\n <div className=\"px-2.5 py-2 text-sm text-destructive\">Couldn’t load matches</div>\n )}\n\n {!loading && !error && items.length === 0 && (\n <div className=\"px-2.5 py-2 text-sm text-muted-foreground\">{emptyText}</div>\n )}\n\n {!loading &&\n !error &&\n items.map((item, index) => {\n const active = index === selected\n return (\n <button\n type=\"button\"\n key={item.id}\n id={id ? `${id}-opt-${index}` : undefined}\n role=\"option\"\n aria-selected={active}\n ref={(el) => {\n optionRefs.current[index] = el\n }}\n // Pointer down only PREVENTS the focus steal (a blur would\n // tear down the suggestion mid-select); selection happens on\n // click so mouse, touch, keyboard, and assistive-technology\n // synthesized activations all share the native path — and a\n // secondary-button press never inserts a mention.\n onMouseDown={(event) => {\n event.preventDefault()\n }}\n onClick={() => onSelect(item)}\n // Routes through the same path arrows use so the imperative\n // Enter/Tab handler (which reads `selectedRef`, not `selected`)\n // agrees with the row the pointer is over.\n onMouseEnter={() => move(index)}\n className={joinClasses(\n 'flex w-full items-center gap-2 rounded-lg px-2.5 py-1.5 text-left text-sm',\n active ? 'bg-primary/10 text-foreground' : 'text-muted-foreground',\n )}\n >\n {renderItem ? (\n renderItem(item)\n ) : (\n <>\n <span className=\"truncate text-foreground\">{item.label}</span>\n {item.detail && (\n <span className=\"ml-auto truncate text-xs text-muted-foreground\">\n {item.detail}\n </span>\n )}\n </>\n )}\n </button>\n )\n })}\n </div>\n )\n },\n)\n","/**\n * Round-trip between the mention editor's document and the composer's plain\n * `value` string. A mention node serializes to `@<id>`; parsing restores a\n * pill only for ids the session already knows (fetched, inserted, or\n * restored), so unknown `@…` runs stay literal text.\n *\n * The document shape is kept structural (no TipTap import) so this round-trip\n * is unit-testable on its own and never pulls the editor chunk into a bundle.\n */\n\nimport { charAt, charBefore, PATH_CONTINUATION_CHAR, WORD_CHAR } from './mention-boundaries'\nimport type { MentionItem } from './use-file-mentions'\n\n/**\n * The subset of a TipTap/ProseMirror JSON document the mention editor\n * produces: a `doc` of paragraphs, each holding text, hard breaks, and atomic\n * mention nodes.\n */\nexport interface MentionDocNode {\n type: string\n text?: string\n attrs?: { id?: string | null; label?: string | null; kind?: string | null }\n content?: MentionDocNode[]\n}\n\nconst MENTION_TYPE = 'mention'\n\n/**\n * Plain-text serialization of the editor document. Paragraphs join with a\n * newline, hard breaks become a newline, and a mention node becomes `@<id>` —\n * the stable text form the controlled `value` carries.\n */\nexport function serializeMentionDoc(doc: MentionDocNode): string {\n return (doc.content ?? []).map(serializeBlock).join('\\n')\n}\n\nfunction serializeBlock(node: MentionDocNode): string {\n if (node.type === 'paragraph') {\n return (node.content ?? []).map(serializeInline).join('')\n }\n return ''\n}\n\nfunction serializeInline(node: MentionDocNode): string {\n if (node.type === 'text') return node.text ?? ''\n if (node.type === 'hardBreak') return '\\n'\n if (node.type === MENTION_TYPE) return `@${node.attrs?.id ?? ''}`\n return ''\n}\n\n/** Every mention node in the document, in order, as `MentionItem`s. */\nexport function collectMentions(doc: MentionDocNode): MentionItem[] {\n const out: MentionItem[] = []\n const walk = (node: MentionDocNode) => {\n if (node.type === MENTION_TYPE && node.attrs?.id) {\n out.push({\n id: node.attrs.id,\n label: node.attrs.label ?? node.attrs.id,\n kind: node.attrs.kind ?? undefined,\n })\n }\n for (const child of node.content ?? []) walk(child)\n }\n walk(doc)\n return out\n}\n\n/**\n * Parse a controlled `value` string back into an editor document. `@<id>`\n * runs that match a currently-known mention restore as atomic mention nodes;\n * every other `@…` stays literal text. Each line becomes a paragraph — a\n * deliberate asymmetry with `serializeMentionDoc`, which also flattens an\n * in-paragraph `hardBreak` to `\\n`: the string round-trips byte-identically,\n * but a restored Shift+Enter comes back as a paragraph break. The two render\n * identically under the composer's styling (no paragraph margins), so the\n * plain-text `value` contract stays the source of truth.\n */\nexport function parseMentionValue(\n value: string,\n known: Map<string, MentionItem>,\n): MentionDocNode {\n const lines = value.split('\\n')\n return {\n type: 'doc',\n content: lines.map((line) => ({\n type: 'paragraph',\n content: parseLine(line, known),\n })),\n }\n}\n\nfunction parseLine(line: string, known: Map<string, MentionItem>): MentionDocNode[] {\n const content: MentionDocNode[] = []\n let text = ''\n const flush = () => {\n if (text) {\n content.push({ type: 'text', text })\n text = ''\n }\n }\n\n let i = 0\n while (i < line.length) {\n // A word character right before the `@` means it is part of a longer\n // token (an email local part, a handle), never a mention start — the\n // same rule the transcript segmenter applies to persisted messages.\n // Read as a full code point: an astral letter's low surrogate would\n // fail WORD_CHAR and let an embedded `@` start a mention.\n const before = charBefore(line, i)\n const partOfLongerToken = before !== undefined && WORD_CHAR.test(before)\n if (line[i] === '@' && !partOfLongerToken) {\n const id = matchKnownId(line, i + 1, known)\n if (id) {\n const item = known.get(id)!\n flush()\n content.push({\n type: MENTION_TYPE,\n attrs: {\n id: item.id,\n label: item.label,\n kind: item.kind ?? null,\n },\n })\n i += 1 + id.length\n continue\n }\n }\n text += line[i]\n i += 1\n }\n flush()\n return content\n}\n\n/**\n * The longest known id that starts at `pos` and ends on a boundary: end of\n * line, or any full character (never a lone surrogate) that could not\n * continue the same path — the transcript segmenter's rule, so `@a.tsx,`\n * restores its pill while `@a.tsx.bak` never matches the shorter `a.tsx`\n * mid-filename. The scan is linear in `known.size` per `@` run on purpose:\n * `known` holds only the ids PICKED this session (a handful), never the\n * file index, so an index structure would buy nothing.\n */\nfunction matchKnownId(\n line: string,\n pos: number,\n known: Map<string, MentionItem>,\n): string | null {\n let best: string | null = null\n for (const id of known.keys()) {\n if (id.length === 0) continue\n if (best !== null && id.length <= best.length) continue\n if (!line.startsWith(id, pos)) continue\n const next = charAt(line, pos + id.length)\n if (next !== undefined && PATH_CONTINUATION_CHAR.test(next)) continue\n best = id\n }\n return best\n}\n"],"mappings":"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","names":["useEffect","useRef","useState","MentionList","jsx","jsxs","useState","useRef","editor","useEffect"]}
|
package/dist/platform/index.js
CHANGED
package/dist/platform/sso.d.ts
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* never imports agent-runtime, an auth framework, or a database driver.
|
|
7
|
-
* WebCrypto only — runs in workerd without node compatibility flags.
|
|
2
|
+
* Tangle SSO for agent apps. OIDC callers use the registered runtime client
|
|
3
|
+
* with S256 PKCE. Legacy API-key callers remain explicit during migration.
|
|
4
|
+
* OIDC never falls back to cross-site exchange. Account matching and native
|
|
5
|
+
* session cookie minting remain shared.
|
|
8
6
|
*/
|
|
9
7
|
/** Define configuration options for managing SSO state including secret, lifetime, and clock injection */
|
|
10
8
|
export interface SsoStateConfig {
|
|
@@ -43,6 +41,39 @@ export interface TangleSsoAuthClient {
|
|
|
43
41
|
}): string;
|
|
44
42
|
exchange(code: string): Promise<TangleSsoExchangeResult>;
|
|
45
43
|
}
|
|
44
|
+
/** Token fields the callback persists from the runtime's OIDC exchange. */
|
|
45
|
+
export interface TangleOidcSsoTokens {
|
|
46
|
+
accessToken: string;
|
|
47
|
+
tokenType: string;
|
|
48
|
+
expiresIn?: number;
|
|
49
|
+
refreshToken?: string;
|
|
50
|
+
idToken?: string;
|
|
51
|
+
scope?: string;
|
|
52
|
+
}
|
|
53
|
+
/** Verified identity returned by the runtime's OIDC userinfo lookup. */
|
|
54
|
+
export interface TangleOidcSsoUser {
|
|
55
|
+
id: string;
|
|
56
|
+
email: string;
|
|
57
|
+
emailVerified: true;
|
|
58
|
+
name?: string;
|
|
59
|
+
}
|
|
60
|
+
/** The caller needs only the OIDC operations used by this callback. Keeping
|
|
61
|
+
* this structural prevents legacy consumers from needing the optional
|
|
62
|
+
* Runtime peer just to load the SSO declarations. */
|
|
63
|
+
export interface TangleOidcSsoAuthClient {
|
|
64
|
+
authorizeUrl(options: {
|
|
65
|
+
state: string;
|
|
66
|
+
codeChallenge: string;
|
|
67
|
+
nonce?: string;
|
|
68
|
+
prompt?: 'login' | 'consent' | 'none';
|
|
69
|
+
loginHint?: string;
|
|
70
|
+
}): string;
|
|
71
|
+
exchange(code: string, codeVerifier: string): Promise<{
|
|
72
|
+
tokens: TangleOidcSsoTokens;
|
|
73
|
+
user: TangleOidcSsoUser;
|
|
74
|
+
}>;
|
|
75
|
+
revoke(token: string, tokenTypeHint?: 'access_token' | 'refresh_token'): Promise<void>;
|
|
76
|
+
}
|
|
46
77
|
/** Local account shape required by the verified Tangle SSO account policy.
|
|
47
78
|
* Consumers map their user/link tables into this shape before resolving an
|
|
48
79
|
* exchange. */
|
|
@@ -148,6 +179,30 @@ export interface TangleSsoAccountStore {
|
|
|
148
179
|
planTier: string | null;
|
|
149
180
|
}): Promise<void>;
|
|
150
181
|
}
|
|
182
|
+
/**
|
|
183
|
+
* OIDC tokens are server-side credentials, not API keys. Implementations must
|
|
184
|
+
* encrypt them at rest and bind them to the exact local session. Refresh and
|
|
185
|
+
* disconnect must serialize updates to the same grant.
|
|
186
|
+
*/
|
|
187
|
+
export interface TangleOidcSsoAccountStore extends Omit<TangleSsoAccountStore, 'saveTangleLink'> {
|
|
188
|
+
/** Atomically bind the grant to this exact session. If persistence partially
|
|
189
|
+
* fails, `deleteSession` must remove every row and credential for it. */
|
|
190
|
+
saveTangleLink(input: {
|
|
191
|
+
userId: string;
|
|
192
|
+
sessionToken: string;
|
|
193
|
+
tangleUserId: string;
|
|
194
|
+
email: string;
|
|
195
|
+
name: string | null;
|
|
196
|
+
tokens: TangleOidcSsoTokens;
|
|
197
|
+
accessTokenExpiresAt: Date;
|
|
198
|
+
}): Promise<void>;
|
|
199
|
+
/** Remove an unpublished local session if callback persistence fails.
|
|
200
|
+
* This must also remove any grant rows or credentials partially written by
|
|
201
|
+
* a rejected `saveTangleLink`, keyed by this exact session token. */
|
|
202
|
+
deleteSession(input: {
|
|
203
|
+
sessionToken: string;
|
|
204
|
+
}): Promise<void>;
|
|
205
|
+
}
|
|
151
206
|
/** Successful-login context handed to the `setSessionCookie` seam. */
|
|
152
207
|
export interface TangleSsoSessionCookieArgs {
|
|
153
208
|
/** Session token returned by `store.createSession`. */
|
|
@@ -221,6 +276,7 @@ export interface BetterAuthSessionCookieMinterOptions {
|
|
|
221
276
|
export declare function createBetterAuthSessionCookieMinter(auth: BetterAuthSessionCookieSource, options?: BetterAuthSessionCookieMinterOptions): (args: TangleSsoSessionCookieArgs) => Promise<string[]>;
|
|
222
277
|
/** Define configuration options for handling Tangle SSO authentication and session management */
|
|
223
278
|
export interface TangleSsoHandlerOptions {
|
|
279
|
+
protocol?: 'legacy';
|
|
224
280
|
auth: TangleSsoAuthClient;
|
|
225
281
|
store: TangleSsoAccountStore;
|
|
226
282
|
/** HMAC secret for the state cookie. */
|
|
@@ -262,6 +318,17 @@ export interface TangleSsoHandlerOptions {
|
|
|
262
318
|
log?: (message: string, error?: unknown) => void;
|
|
263
319
|
now?: () => number;
|
|
264
320
|
}
|
|
321
|
+
/** Registered OIDC client and token store. The OIDC caller supplies the Runtime PKCE helper. */
|
|
322
|
+
export interface TangleOidcSsoHandlerOptions extends Omit<TangleSsoHandlerOptions, 'protocol' | 'auth' | 'store'> {
|
|
323
|
+
protocol: 'oidc';
|
|
324
|
+
auth: TangleOidcSsoAuthClient;
|
|
325
|
+
store: TangleOidcSsoAccountStore;
|
|
326
|
+
/** Pass the Runtime's `createPkcePair` helper from the OIDC-only caller. */
|
|
327
|
+
createPkcePair: () => Promise<{
|
|
328
|
+
verifier: string;
|
|
329
|
+
challenge: string;
|
|
330
|
+
}>;
|
|
331
|
+
}
|
|
265
332
|
/** Define handlers for SSO start and callback routes managing authentication flow and session cookies */
|
|
266
333
|
export interface TangleSsoHandlers {
|
|
267
334
|
/** GET start route: mint + sign state, set the state cookie, 302 to the
|
|
@@ -275,4 +342,4 @@ export interface TangleSsoHandlers {
|
|
|
275
342
|
callback(request: Request): Promise<Response>;
|
|
276
343
|
}
|
|
277
344
|
/** Create Tangle SSO handlers to manage authentication state, callbacks, and session cookies */
|
|
278
|
-
export declare function createTangleSsoHandlers(opts: TangleSsoHandlerOptions): TangleSsoHandlers;
|
|
345
|
+
export declare function createTangleSsoHandlers(opts: TangleSsoHandlerOptions | TangleOidcSsoHandlerOptions): TangleSsoHandlers;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { AgentProfile, AgentProfileFileMount } from '@tangle-network/agent-interface';
|
|
2
|
+
/** Unicode code-point budgets. The installed writer also bounds UTF-8 bytes. */
|
|
3
|
+
export declare const DEFAULT_HOME_LIMITS: {
|
|
4
|
+
readonly 'AGENTS.md': 12000;
|
|
5
|
+
readonly 'SOUL.md': 8000;
|
|
6
|
+
readonly 'IDENTITY.md': 4000;
|
|
7
|
+
readonly 'USER.md': 4000;
|
|
8
|
+
readonly 'MEMORY.md': 16000;
|
|
9
|
+
readonly 'BOOTSTRAP.md': 8000;
|
|
10
|
+
readonly dailyNote: 12000;
|
|
11
|
+
readonly skill: 32000;
|
|
12
|
+
};
|
|
13
|
+
export declare const DEFAULT_AGENT_HOME = "/var/lib/tangle-agent/home";
|
|
14
|
+
/** Canonical seed records. The trusted image installer materializes these once. */
|
|
15
|
+
export declare function defaultHomeFiles(): AgentProfileFileMount[];
|
|
16
|
+
/**
|
|
17
|
+
* Compose home instructions, not per-turn writes to persistent memory.
|
|
18
|
+
* The published install-home command consumes defaultHomeFiles with the same
|
|
19
|
+
* inline resource format as other profile seeds. A privileged, one-time phase
|
|
20
|
+
* is necessary: ordinary resources.files writes run as the agent and cannot
|
|
21
|
+
* make a same-UID shell respect caps or platform file ownership.
|
|
22
|
+
*/
|
|
23
|
+
export declare function withDefaultAgentHome(profile: AgentProfile): AgentProfile;
|
package/dist/profile/index.d.ts
CHANGED
|
@@ -172,3 +172,4 @@ export { assertSkillDeliveryDisjoint, composeShellResources, composeSkills, corp
|
|
|
172
172
|
export type { ComposedSkills, ComposeShellResourcesInput, CorpusEntry, CorpusLoadResult, GlobModules, LoadCorpusOptions, ParsedSkill, SkillDeliveryMode, SkillEntry, SkillFrontmatter, } from '../skills/index';
|
|
173
173
|
export { diffProfileFingerprints, fingerprintAgentProfile, formatProfileDrift, } from './fingerprint';
|
|
174
174
|
export type { ProfileDrift, ProfileDriftEntry, ProfileFingerprint, ProfileFingerprintContext, } from './fingerprint';
|
|
175
|
+
export { DEFAULT_HOME_LIMITS, defaultHomeFiles, withDefaultAgentHome } from './home';
|
package/dist/profile/index.js
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DEFAULT_HOME_LIMITS,
|
|
3
|
+
defaultHomeFiles,
|
|
4
|
+
withDefaultAgentHome
|
|
5
|
+
} from "../chunk-6CV66YOB.js";
|
|
1
6
|
import {
|
|
2
7
|
assertSkillDeliveryDisjoint,
|
|
3
8
|
composeShellResources,
|
|
@@ -86,6 +91,7 @@ function makeEvolvableSection(input) {
|
|
|
86
91
|
return { id: input.id, title: input.title, body, evolvable: true };
|
|
87
92
|
}
|
|
88
93
|
export {
|
|
94
|
+
DEFAULT_HOME_LIMITS,
|
|
89
95
|
DEFAULT_MAX_SYSTEM_PROMPT_BYTES,
|
|
90
96
|
assertProfilePromptWithinBudget,
|
|
91
97
|
assertSkillDeliveryDisjoint,
|
|
@@ -94,6 +100,7 @@ export {
|
|
|
94
100
|
composeShellResources,
|
|
95
101
|
composeSkills,
|
|
96
102
|
corpusSkills,
|
|
103
|
+
defaultHomeFiles,
|
|
97
104
|
diffProfileFingerprints,
|
|
98
105
|
fingerprintAgentProfile,
|
|
99
106
|
formatProfileDrift,
|
|
@@ -111,6 +118,7 @@ export {
|
|
|
111
118
|
skillMountPath,
|
|
112
119
|
skillRefs,
|
|
113
120
|
stripComments,
|
|
114
|
-
userSkillMounts
|
|
121
|
+
userSkillMounts,
|
|
122
|
+
withDefaultAgentHome
|
|
115
123
|
};
|
|
116
124
|
//# sourceMappingURL=index.js.map
|