@tangle-network/agent-app 0.49.33 → 0.49.34
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 +6 -6
- 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-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/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/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 +22 -22
- 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/{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[];
|
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
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/profile/index.ts"],"sourcesContent":["/**\n * Profile composer + evolvable-section seam for agent products.\n *\n * The standard \"load a deployable AgentProfile, including skills, plus the\n * skills the end user added to their own instance\" entry point. A product holds\n * a canonical base `AgentProfile` (role/environment/tool-conventions rendered\n * into `prompt.systemPrompt`, baseline skills, baseline MCP). At deploy/turn\n * time it layers four file-mount channels onto `resources.files` —\n *\n * 1. skills — the always-mounted product skill corpus\n * 2. knowledge — a second always-mounted corpus (domain knowledge pack)\n * 3. registry — the tier-gated installable registry (free -> boot-mounted)\n * 4. userSkills — per-user / per-workspace skills the END USER adds to their\n * own instance, mounted at `~/.claude/skills/<id>/SKILL.md`\n * exactly like the registry's free tier\n *\n * plus an optional MCP overlay (delegation + per-turn app-tool side channel), a\n * per-turn `systemPrompt` override, and a `name` override. The canonical\n * `mergeAgentProfiles` contract makes `mcp` last-wins per key (base -> overlay),\n * concatenates `resources` arrays (base ++ overlay), and shallow-merges `prompt`\n * so an overlay carrying only `systemPrompt` overrides it while keeping base\n * instructions. The compose algebra is DATA — the product injects the base\n * profile, the channel mounts (built with the `skills` subpath primitives), the\n * delegation/app-tool MCP map, and the override strings; nothing here reaches\n * for env, a glob, or a specific product's profile.\n *\n * The evolvable-section seam is the loader closure. A product's single\n * self-improvable domain section (the one `applyDomainPatch` targets) loads its\n * body from a deployed markdown override, falling back to an in-tree baseline.\n * The `import.meta.glob('<lit>', ...)` literal must stay at the CONSUMER call\n * site (Vite static-analyzes it), so `makeEvolvableSection` takes the loader as\n * a closure and a REQUIRED `baseline` — it never constructs a glob and never\n * defaults the baseline, so a product can't render an empty learned-guidance\n * section. `stripComments` is the shared \"is this addendum really empty?\" test.\n */\n\nimport type {\n AgentProfile,\n AgentProfileFileMount,\n AgentProfileMcpServer,\n AgentProfileResourceRef,\n} from '@tangle-network/agent-interface'\nimport { mergeAgentProfiles } from '@tangle-network/agent-interface'\nimport { profile } from '@tangle-network/agent-eval'\nimport {\n composeShellResources,\n registrySkills,\n skillMountPath,\n type ComposeShellResourcesInput,\n type SkillEntry,\n} from '../skills/index'\nimport { assertSystemPromptWithinBudget, type ComposeProfileBudget } from './budget'\n\n/** The prompt byte budget lives in `./budget` (import-free) so `/sandbox` can\n * run the same gate without pulling agent-eval through this module. Re-exported\n * here so the published `/profile` surface is unchanged. */\nexport {\n assertProfilePromptWithinBudget,\n assertSystemPromptWithinBudget,\n DEFAULT_MAX_SYSTEM_PROMPT_BYTES,\n largestPromptSections,\n type ComposeProfileBudget,\n} from './budget'\n\n/** Re-expose the agent-eval section/render substrate so a product wires the\n * evolvable surface through ONE subpath: `makeEvolvableSection` builds the\n * section, `profile.renderProfile` renders it, `profile.applyDomainPatch` lets\n * the loop patch it by id. The rendering/patching engine stays in agent-eval;\n * reach it through this namespace (re-exporting the bare fns would leak\n * agent-eval's un-nameable AgentProfile type into our generated d.ts). */\nexport { profile }\n\n/** The file-mount channels layered onto `resources.files`. The first three\n * mirror {@link ComposeShellResourcesInput}; `userSkills` is the per-user /\n * per-workspace channel — skills the END USER added to their own instance,\n * mounted at the harness skill-discovery path like the registry's free tier. */\nexport interface ProfileChannels {\n /** Always-mounted skill corpus (pass `corpusSkills(...)`). */\n skills?: AgentProfileFileMount[]\n /** Always-mounted knowledge corpus (pass `corpusSkills(...)` for the pack). */\n knowledge?: AgentProfileFileMount[]\n /** Single-file evolvable / learned-guidance corpora, if mounted as files. */\n evolvable?: AgentProfileFileMount[]\n /** Tier-gated installable registry (pass the registry array; free tier is\n * mounted, paid is install-on-demand). Gated through {@link registrySkills}. */\n registry?: SkillEntry[]\n /** Per-user / per-workspace skills the end user adds to their own instance.\n * Mounted at `~/.claude/skills/<id>/SKILL.md`, the same harness path the\n * registry uses, so a user skill and a registry skill with the same id\n * collide deterministically (the user skill, appended last, wins). */\n userSkills?: UserSkill[]\n /** Final skip filter applied to the composed mount list by mount `path`. */\n filesPredicate?: (mount: AgentProfileFileMount) => boolean\n /** Typed `resources.skills` channel — refs the platform materializer places\n * at the harness-native skill dir (see {@link skillRefs} and\n * `@tangle-network/agent-app/skills-placement`'s `composeSkillsForHarness`).\n * The successor to path-baked mounts: `registry`/`userSkills` above mount\n * files at the hardcoded claude-code path via {@link skillMountPath};\n * `skillRefs` instead rides the provider-neutral `resources.skills` field\n * the platform resolves per harness. */\n skillRefs?: AgentProfileResourceRef[]\n /** Tier passed to {@link registrySkills} for the `registry` channel.\n * Previously hardcoded `'free'`; default unchanged. */\n registryTier?: string\n}\n\n/** A per-user / per-workspace skill: an id and an inline `SKILL.md` body. The\n * user-facing analogue of a registry {@link SkillEntry} with no tier gate —\n * every user skill is mounted (the user opted in by adding it). */\nexport interface UserSkill {\n id: string\n /** Inline `SKILL.md` body mounted at {@link skillMountPath}. */\n skillMd: string\n}\n\n/** Overlay overrides applied on top of the channel mounts. */\nexport interface ProfileOverlay {\n /** Extra MCP servers merged into the profile `mcp` map (last-wins per key over\n * the base servers). The product builds this from its delegation MCP entry\n * and any per-turn app-tool side-channel servers. An absent/`undefined` entry\n * is dropped — pass only the servers that resolved (fail-closed at the seam,\n * not here). */\n mcp?: Record<string, AgentProfileMcpServer>\n /** Per-turn system-prompt override. When set, replaces the base\n * `prompt.systemPrompt` while keeping base `prompt.instructions`. When unset,\n * the base prompt passes through unchanged. */\n systemPrompt?: string\n /** Extra instruction lines merged onto the active prompt (e.g. a per-turn\n * domain/integration directive). Appended to base `prompt.instructions` by\n * the SDK merge. */\n instructions?: string[]\n /** Profile `name` override. When unset, the base name is kept. */\n name?: string\n}\n\n/** Project per-user skills onto SDK file mounts at the harness skill-discovery\n * path. No tier gate — a user skill is mounted because the user added it.\n * Sorted by path for determinism (matches {@link registrySkills}). */\nexport function userSkillMounts(userSkills: UserSkill[]): AgentProfileFileMount[] {\n return userSkills\n .map(\n (s) =>\n ({\n path: skillMountPath(s.id),\n resource: { kind: 'inline', name: s.id, content: s.skillMd },\n }) satisfies AgentProfileFileMount,\n )\n .sort((a, b) => a.path.localeCompare(b.path))\n}\n\n/**\n * Compose a deployable `AgentProfile` from a canonical base plus the four\n * file-mount channels and the overlay overrides.\n *\n * Files: base `resources.files` come first; the four channels follow in\n * `skills -> knowledge -> evolvable -> registry -> userSkills` order (so a\n * userSkill that mounts at the same path as a registry skill is the last write\n * and wins). MCP: base servers first, the overlay `mcp` last (last-wins per\n * key). Prompt: the overlay `systemPrompt`, when set, replaces the base one;\n * base instructions are preserved. Name: the overlay `name`, when set, wins.\n *\n * The merge delegates to the SDK `mergeAgentProfiles` (overlay-wins on records,\n * arrays concatenated) — the deterministic algebra is the overlay we hand it,\n * not a hand-rolled spread. `mergeAgentProfiles(base, overlay)` returns\n * `undefined` only when BOTH are `undefined`; `base` is always defined here, so\n * the result is non-`undefined` by construction and we assert that to the caller.\n *\n * The composed `prompt.systemPrompt` is byte-budgeted here — the single point\n * where the FINAL prompt exists ({@link assertSystemPromptWithinBudget};\n * default {@link DEFAULT_MAX_SYSTEM_PROMPT_BYTES}, `warnOnly` escape hatch).\n */\nexport function composeAgentProfile(\n base: AgentProfile,\n channels: ProfileChannels = {},\n overlay: ProfileOverlay = {},\n budget: ComposeProfileBudget = {},\n): AgentProfile {\n const shellInput: ComposeShellResourcesInput = {\n skills: channels.skills,\n knowledge: channels.knowledge,\n evolvable: channels.evolvable,\n registry: channels.registry\n ? registrySkills(channels.registry, channels.registryTier ?? 'free')\n : undefined,\n predicate: channels.filesPredicate,\n }\n const channelFiles = composeShellResources(shellInput)\n const userFiles = channels.userSkills ? userSkillMounts(channels.userSkills) : []\n const overlayFiles = channels.filesPredicate\n ? userFiles.filter(channels.filesPredicate)\n : userFiles\n const files = [...channelFiles, ...overlayFiles]\n\n const promptOverlay: { systemPrompt?: string; instructions?: string[] } = {}\n if (overlay.systemPrompt) promptOverlay.systemPrompt = overlay.systemPrompt\n if (overlay.instructions && overlay.instructions.length > 0) promptOverlay.instructions = overlay.instructions\n\n const overlayProfile: AgentProfile = {\n ...(overlay.name ? { name: overlay.name } : {}),\n ...(Object.keys(promptOverlay).length > 0 ? { prompt: promptOverlay } : {}),\n ...(overlay.mcp ? { mcp: overlay.mcp } : {}),\n resources: {\n files,\n ...(channels.skillRefs && channels.skillRefs.length > 0 ? { skills: channels.skillRefs } : {}),\n },\n }\n\n const merged = mergeAgentProfiles(base, overlayProfile)\n if (!merged)\n throw new Error('composeAgentProfile: mergeAgentProfiles returned undefined for a defined base')\n // Byte-budget gate on the FINAL composed systemPrompt — this is the single\n // point where every channel and overlay has been merged in.\n const systemPrompt = merged.prompt?.systemPrompt\n if (typeof systemPrompt === 'string') assertSystemPromptWithinBudget(systemPrompt, budget)\n return pruneEmptyResourceChannels(merged)\n}\n\n/** Drop absent and empty resource channels the canonical merge normalizes in,\n * so the composed profile's wire payload carries\n * only the channels that actually have content — one canonical shape every app\n * emits, instead of a sidecar payload full of empty arrays. */\nfunction pruneEmptyResourceChannels(profile: AgentProfile): AgentProfile {\n if (!profile.resources) return profile\n const kept = Object.fromEntries(\n Object.entries(profile.resources).filter(([, value]) =>\n value !== undefined && !(Array.isArray(value) && value.length === 0),\n ),\n ) as AgentProfile['resources']\n const out: AgentProfile = { ...profile, resources: kept }\n if (kept && Object.keys(kept).length === 0) delete out.resources\n return out\n}\n\n/** True body of an addendum file with HTML comments stripped — an all-comment\n * placeholder counts as empty, so the loader falls back to the baseline. */\nexport function stripComments(raw: string): string {\n return raw.replace(/<!--[\\s\\S]*?-->/g, '').trim()\n}\n\n/** Inputs to {@link makeEvolvableSection}. */\nexport interface EvolvableSectionInput {\n /** Section id the self-improvement loop targets with `applyDomainPatch`. */\n id: string\n /** Section title rendered as `### <title>`. */\n title: string\n /**\n * Load the deployed section body. The CONSUMER supplies this closure and runs\n * its own `import.meta.glob('<lit>', { eager: true, query: '?raw', import:\n * 'default' })` inside it — the literal must stay at the call site so Vite can\n * static-analyze it; a glob constructed here would not resolve the product's\n * files. Return the raw markdown (comments and all); `makeEvolvableSection`\n * applies {@link stripComments} to decide whether it is really populated.\n */\n load: () => string\n /**\n * The in-tree fallback body, used when `load()` returns an\n * all-comments/empty placeholder. REQUIRED — no internal default — so a\n * product can never accidentally render an empty evolvable section.\n */\n baseline: string\n}\n\n/**\n * Build the one evolvable (`evolvable: true`) domain section whose body comes\n * from the product's loader, falling back to the required baseline when the\n * loaded body is empty after stripping comments. Returns the agent-eval\n * `AgentProfileSection` shape — drop it straight into `prodProfile`'s shipped\n * sections. The loader is the only seam; the empty-vs-populated rule and the\n * baseline fallback are the lifted algebra.\n */\nexport function makeEvolvableSection(input: EvolvableSectionInput): profile.AgentProfileSection {\n const loaded = input.load()\n const body = stripComments(loaded) ? loaded.trim() : input.baseline\n return { id: input.id, title: input.title, body, evolvable: true }\n}\n\nexport {\n assertSkillDeliveryDisjoint,\n composeShellResources,\n composeSkills,\n corpusSkills,\n loadMarkdownCorpus,\n mergeComposedSkills,\n parseCorpusSkills,\n parseSkillFrontmatter,\n registrySkills,\n renderInlineSkills,\n renderSkillIndex,\n skillEntryFromMarkdown,\n skillMountPath,\n skillRefs,\n} from '../skills/index'\nexport type {\n ComposedSkills,\n ComposeShellResourcesInput,\n CorpusEntry,\n CorpusLoadResult,\n GlobModules,\n LoadCorpusOptions,\n ParsedSkill,\n SkillDeliveryMode,\n SkillEntry,\n SkillFrontmatter,\n} from '../skills/index'\nexport {\n diffProfileFingerprints,\n fingerprintAgentProfile,\n formatProfileDrift,\n} from './fingerprint'\nexport type {\n ProfileDrift,\n ProfileDriftEntry,\n ProfileFingerprint,\n ProfileFingerprintContext,\n} from './fingerprint'\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AA0CA,SAAS,0BAA0B;AACnC,SAAS,eAAe;AA+FjB,SAAS,gBAAgB,YAAkD;AAChF,SAAO,WACJ;AAAA,IACC,CAAC,OACE;AAAA,MACC,MAAM,eAAe,EAAE,EAAE;AAAA,MACzB,UAAU,EAAE,MAAM,UAAU,MAAM,EAAE,IAAI,SAAS,EAAE,QAAQ;AAAA,IAC7D;AAAA,EACJ,EACC,KAAK,CAAC,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,IAAI,CAAC;AAChD;AAuBO,SAAS,oBACd,MACA,WAA4B,CAAC,GAC7B,UAA0B,CAAC,GAC3B,SAA+B,CAAC,GAClB;AACd,QAAM,aAAyC;AAAA,IAC7C,QAAQ,SAAS;AAAA,IACjB,WAAW,SAAS;AAAA,IACpB,WAAW,SAAS;AAAA,IACpB,UAAU,SAAS,WACf,eAAe,SAAS,UAAU,SAAS,gBAAgB,MAAM,IACjE;AAAA,IACJ,WAAW,SAAS;AAAA,EACtB;AACA,QAAM,eAAe,sBAAsB,UAAU;AACrD,QAAM,YAAY,SAAS,aAAa,gBAAgB,SAAS,UAAU,IAAI,CAAC;AAChF,QAAM,eAAe,SAAS,iBAC1B,UAAU,OAAO,SAAS,cAAc,IACxC;AACJ,QAAM,QAAQ,CAAC,GAAG,cAAc,GAAG,YAAY;AAE/C,QAAM,gBAAoE,CAAC;AAC3E,MAAI,QAAQ,aAAc,eAAc,eAAe,QAAQ;AAC/D,MAAI,QAAQ,gBAAgB,QAAQ,aAAa,SAAS,EAAG,eAAc,eAAe,QAAQ;AAElG,QAAM,iBAA+B;AAAA,IACnC,GAAI,QAAQ,OAAO,EAAE,MAAM,QAAQ,KAAK,IAAI,CAAC;AAAA,IAC7C,GAAI,OAAO,KAAK,aAAa,EAAE,SAAS,IAAI,EAAE,QAAQ,cAAc,IAAI,CAAC;AAAA,IACzE,GAAI,QAAQ,MAAM,EAAE,KAAK,QAAQ,IAAI,IAAI,CAAC;AAAA,IAC1C,WAAW;AAAA,MACT;AAAA,MACA,GAAI,SAAS,aAAa,SAAS,UAAU,SAAS,IAAI,EAAE,QAAQ,SAAS,UAAU,IAAI,CAAC;AAAA,IAC9F;AAAA,EACF;AAEA,QAAM,SAAS,mBAAmB,MAAM,cAAc;AACtD,MAAI,CAAC;AACH,UAAM,IAAI,MAAM,+EAA+E;AAGjG,QAAM,eAAe,OAAO,QAAQ;AACpC,MAAI,OAAO,iBAAiB,SAAU,gCAA+B,cAAc,MAAM;AACzF,SAAO,2BAA2B,MAAM;AAC1C;AAMA,SAAS,2BAA2BA,UAAqC;AACvE,MAAI,CAACA,SAAQ,UAAW,QAAOA;AAC/B,QAAM,OAAO,OAAO;AAAA,IAClB,OAAO,QAAQA,SAAQ,SAAS,EAAE;AAAA,MAAO,CAAC,CAAC,EAAE,KAAK,MAChD,UAAU,UAAa,EAAE,MAAM,QAAQ,KAAK,KAAK,MAAM,WAAW;AAAA,IACpE;AAAA,EACF;AACA,QAAM,MAAoB,EAAE,GAAGA,UAAS,WAAW,KAAK;AACxD,MAAI,QAAQ,OAAO,KAAK,IAAI,EAAE,WAAW,EAAG,QAAO,IAAI;AACvD,SAAO;AACT;AAIO,SAAS,cAAc,KAAqB;AACjD,SAAO,IAAI,QAAQ,oBAAoB,EAAE,EAAE,KAAK;AAClD;AAiCO,SAAS,qBAAqB,OAA2D;AAC9F,QAAM,SAAS,MAAM,KAAK;AAC1B,QAAM,OAAO,cAAc,MAAM,IAAI,OAAO,KAAK,IAAI,MAAM;AAC3D,SAAO,EAAE,IAAI,MAAM,IAAI,OAAO,MAAM,OAAO,MAAM,WAAW,KAAK;AACnE;","names":["profile"]}
|
|
1
|
+
{"version":3,"sources":["../../src/profile/index.ts"],"sourcesContent":["/**\n * Profile composer + evolvable-section seam for agent products.\n *\n * The standard \"load a deployable AgentProfile, including skills, plus the\n * skills the end user added to their own instance\" entry point. A product holds\n * a canonical base `AgentProfile` (role/environment/tool-conventions rendered\n * into `prompt.systemPrompt`, baseline skills, baseline MCP). At deploy/turn\n * time it layers four file-mount channels onto `resources.files` —\n *\n * 1. skills — the always-mounted product skill corpus\n * 2. knowledge — a second always-mounted corpus (domain knowledge pack)\n * 3. registry — the tier-gated installable registry (free -> boot-mounted)\n * 4. userSkills — per-user / per-workspace skills the END USER adds to their\n * own instance, mounted at `~/.claude/skills/<id>/SKILL.md`\n * exactly like the registry's free tier\n *\n * plus an optional MCP overlay (delegation + per-turn app-tool side channel), a\n * per-turn `systemPrompt` override, and a `name` override. The canonical\n * `mergeAgentProfiles` contract makes `mcp` last-wins per key (base -> overlay),\n * concatenates `resources` arrays (base ++ overlay), and shallow-merges `prompt`\n * so an overlay carrying only `systemPrompt` overrides it while keeping base\n * instructions. The compose algebra is DATA — the product injects the base\n * profile, the channel mounts (built with the `skills` subpath primitives), the\n * delegation/app-tool MCP map, and the override strings; nothing here reaches\n * for env, a glob, or a specific product's profile.\n *\n * The evolvable-section seam is the loader closure. A product's single\n * self-improvable domain section (the one `applyDomainPatch` targets) loads its\n * body from a deployed markdown override, falling back to an in-tree baseline.\n * The `import.meta.glob('<lit>', ...)` literal must stay at the CONSUMER call\n * site (Vite static-analyzes it), so `makeEvolvableSection` takes the loader as\n * a closure and a REQUIRED `baseline` — it never constructs a glob and never\n * defaults the baseline, so a product can't render an empty learned-guidance\n * section. `stripComments` is the shared \"is this addendum really empty?\" test.\n */\n\nimport type {\n AgentProfile,\n AgentProfileFileMount,\n AgentProfileMcpServer,\n AgentProfileResourceRef,\n} from '@tangle-network/agent-interface'\nimport { mergeAgentProfiles } from '@tangle-network/agent-interface'\nimport { profile } from '@tangle-network/agent-eval'\nimport {\n composeShellResources,\n registrySkills,\n skillMountPath,\n type ComposeShellResourcesInput,\n type SkillEntry,\n} from '../skills/index'\nimport { assertSystemPromptWithinBudget, type ComposeProfileBudget } from './budget'\n\n/** The prompt byte budget lives in `./budget` (import-free) so `/sandbox` can\n * run the same gate without pulling agent-eval through this module. Re-exported\n * here so the published `/profile` surface is unchanged. */\nexport {\n assertProfilePromptWithinBudget,\n assertSystemPromptWithinBudget,\n DEFAULT_MAX_SYSTEM_PROMPT_BYTES,\n largestPromptSections,\n type ComposeProfileBudget,\n} from './budget'\n\n/** Re-expose the agent-eval section/render substrate so a product wires the\n * evolvable surface through ONE subpath: `makeEvolvableSection` builds the\n * section, `profile.renderProfile` renders it, `profile.applyDomainPatch` lets\n * the loop patch it by id. The rendering/patching engine stays in agent-eval;\n * reach it through this namespace (re-exporting the bare fns would leak\n * agent-eval's un-nameable AgentProfile type into our generated d.ts). */\nexport { profile }\n\n/** The file-mount channels layered onto `resources.files`. The first three\n * mirror {@link ComposeShellResourcesInput}; `userSkills` is the per-user /\n * per-workspace channel — skills the END USER added to their own instance,\n * mounted at the harness skill-discovery path like the registry's free tier. */\nexport interface ProfileChannels {\n /** Always-mounted skill corpus (pass `corpusSkills(...)`). */\n skills?: AgentProfileFileMount[]\n /** Always-mounted knowledge corpus (pass `corpusSkills(...)` for the pack). */\n knowledge?: AgentProfileFileMount[]\n /** Single-file evolvable / learned-guidance corpora, if mounted as files. */\n evolvable?: AgentProfileFileMount[]\n /** Tier-gated installable registry (pass the registry array; free tier is\n * mounted, paid is install-on-demand). Gated through {@link registrySkills}. */\n registry?: SkillEntry[]\n /** Per-user / per-workspace skills the end user adds to their own instance.\n * Mounted at `~/.claude/skills/<id>/SKILL.md`, the same harness path the\n * registry uses, so a user skill and a registry skill with the same id\n * collide deterministically (the user skill, appended last, wins). */\n userSkills?: UserSkill[]\n /** Final skip filter applied to the composed mount list by mount `path`. */\n filesPredicate?: (mount: AgentProfileFileMount) => boolean\n /** Typed `resources.skills` channel — refs the platform materializer places\n * at the harness-native skill dir (see {@link skillRefs} and\n * `@tangle-network/agent-app/skills-placement`'s `composeSkillsForHarness`).\n * The successor to path-baked mounts: `registry`/`userSkills` above mount\n * files at the hardcoded claude-code path via {@link skillMountPath};\n * `skillRefs` instead rides the provider-neutral `resources.skills` field\n * the platform resolves per harness. */\n skillRefs?: AgentProfileResourceRef[]\n /** Tier passed to {@link registrySkills} for the `registry` channel.\n * Previously hardcoded `'free'`; default unchanged. */\n registryTier?: string\n}\n\n/** A per-user / per-workspace skill: an id and an inline `SKILL.md` body. The\n * user-facing analogue of a registry {@link SkillEntry} with no tier gate —\n * every user skill is mounted (the user opted in by adding it). */\nexport interface UserSkill {\n id: string\n /** Inline `SKILL.md` body mounted at {@link skillMountPath}. */\n skillMd: string\n}\n\n/** Overlay overrides applied on top of the channel mounts. */\nexport interface ProfileOverlay {\n /** Extra MCP servers merged into the profile `mcp` map (last-wins per key over\n * the base servers). The product builds this from its delegation MCP entry\n * and any per-turn app-tool side-channel servers. An absent/`undefined` entry\n * is dropped — pass only the servers that resolved (fail-closed at the seam,\n * not here). */\n mcp?: Record<string, AgentProfileMcpServer>\n /** Per-turn system-prompt override. When set, replaces the base\n * `prompt.systemPrompt` while keeping base `prompt.instructions`. When unset,\n * the base prompt passes through unchanged. */\n systemPrompt?: string\n /** Extra instruction lines merged onto the active prompt (e.g. a per-turn\n * domain/integration directive). Appended to base `prompt.instructions` by\n * the SDK merge. */\n instructions?: string[]\n /** Profile `name` override. When unset, the base name is kept. */\n name?: string\n}\n\n/** Project per-user skills onto SDK file mounts at the harness skill-discovery\n * path. No tier gate — a user skill is mounted because the user added it.\n * Sorted by path for determinism (matches {@link registrySkills}). */\nexport function userSkillMounts(userSkills: UserSkill[]): AgentProfileFileMount[] {\n return userSkills\n .map(\n (s) =>\n ({\n path: skillMountPath(s.id),\n resource: { kind: 'inline', name: s.id, content: s.skillMd },\n }) satisfies AgentProfileFileMount,\n )\n .sort((a, b) => a.path.localeCompare(b.path))\n}\n\n/**\n * Compose a deployable `AgentProfile` from a canonical base plus the four\n * file-mount channels and the overlay overrides.\n *\n * Files: base `resources.files` come first; the four channels follow in\n * `skills -> knowledge -> evolvable -> registry -> userSkills` order (so a\n * userSkill that mounts at the same path as a registry skill is the last write\n * and wins). MCP: base servers first, the overlay `mcp` last (last-wins per\n * key). Prompt: the overlay `systemPrompt`, when set, replaces the base one;\n * base instructions are preserved. Name: the overlay `name`, when set, wins.\n *\n * The merge delegates to the SDK `mergeAgentProfiles` (overlay-wins on records,\n * arrays concatenated) — the deterministic algebra is the overlay we hand it,\n * not a hand-rolled spread. `mergeAgentProfiles(base, overlay)` returns\n * `undefined` only when BOTH are `undefined`; `base` is always defined here, so\n * the result is non-`undefined` by construction and we assert that to the caller.\n *\n * The composed `prompt.systemPrompt` is byte-budgeted here — the single point\n * where the FINAL prompt exists ({@link assertSystemPromptWithinBudget};\n * default {@link DEFAULT_MAX_SYSTEM_PROMPT_BYTES}, `warnOnly` escape hatch).\n */\nexport function composeAgentProfile(\n base: AgentProfile,\n channels: ProfileChannels = {},\n overlay: ProfileOverlay = {},\n budget: ComposeProfileBudget = {},\n): AgentProfile {\n const shellInput: ComposeShellResourcesInput = {\n skills: channels.skills,\n knowledge: channels.knowledge,\n evolvable: channels.evolvable,\n registry: channels.registry\n ? registrySkills(channels.registry, channels.registryTier ?? 'free')\n : undefined,\n predicate: channels.filesPredicate,\n }\n const channelFiles = composeShellResources(shellInput)\n const userFiles = channels.userSkills ? userSkillMounts(channels.userSkills) : []\n const overlayFiles = channels.filesPredicate\n ? userFiles.filter(channels.filesPredicate)\n : userFiles\n const files = [...channelFiles, ...overlayFiles]\n\n const promptOverlay: { systemPrompt?: string; instructions?: string[] } = {}\n if (overlay.systemPrompt) promptOverlay.systemPrompt = overlay.systemPrompt\n if (overlay.instructions && overlay.instructions.length > 0) promptOverlay.instructions = overlay.instructions\n\n const overlayProfile: AgentProfile = {\n ...(overlay.name ? { name: overlay.name } : {}),\n ...(Object.keys(promptOverlay).length > 0 ? { prompt: promptOverlay } : {}),\n ...(overlay.mcp ? { mcp: overlay.mcp } : {}),\n resources: {\n files,\n ...(channels.skillRefs && channels.skillRefs.length > 0 ? { skills: channels.skillRefs } : {}),\n },\n }\n\n const merged = mergeAgentProfiles(base, overlayProfile)\n if (!merged)\n throw new Error('composeAgentProfile: mergeAgentProfiles returned undefined for a defined base')\n // Byte-budget gate on the FINAL composed systemPrompt — this is the single\n // point where every channel and overlay has been merged in.\n const systemPrompt = merged.prompt?.systemPrompt\n if (typeof systemPrompt === 'string') assertSystemPromptWithinBudget(systemPrompt, budget)\n return pruneEmptyResourceChannels(merged)\n}\n\n/** Drop absent and empty resource channels the canonical merge normalizes in,\n * so the composed profile's wire payload carries\n * only the channels that actually have content — one canonical shape every app\n * emits, instead of a sidecar payload full of empty arrays. */\nfunction pruneEmptyResourceChannels(profile: AgentProfile): AgentProfile {\n if (!profile.resources) return profile\n const kept = Object.fromEntries(\n Object.entries(profile.resources).filter(([, value]) =>\n value !== undefined && !(Array.isArray(value) && value.length === 0),\n ),\n ) as AgentProfile['resources']\n const out: AgentProfile = { ...profile, resources: kept }\n if (kept && Object.keys(kept).length === 0) delete out.resources\n return out\n}\n\n/** True body of an addendum file with HTML comments stripped — an all-comment\n * placeholder counts as empty, so the loader falls back to the baseline. */\nexport function stripComments(raw: string): string {\n return raw.replace(/<!--[\\s\\S]*?-->/g, '').trim()\n}\n\n/** Inputs to {@link makeEvolvableSection}. */\nexport interface EvolvableSectionInput {\n /** Section id the self-improvement loop targets with `applyDomainPatch`. */\n id: string\n /** Section title rendered as `### <title>`. */\n title: string\n /**\n * Load the deployed section body. The CONSUMER supplies this closure and runs\n * its own `import.meta.glob('<lit>', { eager: true, query: '?raw', import:\n * 'default' })` inside it — the literal must stay at the call site so Vite can\n * static-analyze it; a glob constructed here would not resolve the product's\n * files. Return the raw markdown (comments and all); `makeEvolvableSection`\n * applies {@link stripComments} to decide whether it is really populated.\n */\n load: () => string\n /**\n * The in-tree fallback body, used when `load()` returns an\n * all-comments/empty placeholder. REQUIRED — no internal default — so a\n * product can never accidentally render an empty evolvable section.\n */\n baseline: string\n}\n\n/**\n * Build the one evolvable (`evolvable: true`) domain section whose body comes\n * from the product's loader, falling back to the required baseline when the\n * loaded body is empty after stripping comments. Returns the agent-eval\n * `AgentProfileSection` shape — drop it straight into `prodProfile`'s shipped\n * sections. The loader is the only seam; the empty-vs-populated rule and the\n * baseline fallback are the lifted algebra.\n */\nexport function makeEvolvableSection(input: EvolvableSectionInput): profile.AgentProfileSection {\n const loaded = input.load()\n const body = stripComments(loaded) ? loaded.trim() : input.baseline\n return { id: input.id, title: input.title, body, evolvable: true }\n}\n\nexport {\n assertSkillDeliveryDisjoint,\n composeShellResources,\n composeSkills,\n corpusSkills,\n loadMarkdownCorpus,\n mergeComposedSkills,\n parseCorpusSkills,\n parseSkillFrontmatter,\n registrySkills,\n renderInlineSkills,\n renderSkillIndex,\n skillEntryFromMarkdown,\n skillMountPath,\n skillRefs,\n} from '../skills/index'\nexport type {\n ComposedSkills,\n ComposeShellResourcesInput,\n CorpusEntry,\n CorpusLoadResult,\n GlobModules,\n LoadCorpusOptions,\n ParsedSkill,\n SkillDeliveryMode,\n SkillEntry,\n SkillFrontmatter,\n} from '../skills/index'\nexport {\n diffProfileFingerprints,\n fingerprintAgentProfile,\n formatProfileDrift,\n} from './fingerprint'\nexport type {\n ProfileDrift,\n ProfileDriftEntry,\n ProfileFingerprint,\n ProfileFingerprintContext,\n} from './fingerprint'\n\nexport { DEFAULT_HOME_LIMITS, defaultHomeFiles, withDefaultAgentHome } from './home'\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0CA,SAAS,0BAA0B;AACnC,SAAS,eAAe;AA+FjB,SAAS,gBAAgB,YAAkD;AAChF,SAAO,WACJ;AAAA,IACC,CAAC,OACE;AAAA,MACC,MAAM,eAAe,EAAE,EAAE;AAAA,MACzB,UAAU,EAAE,MAAM,UAAU,MAAM,EAAE,IAAI,SAAS,EAAE,QAAQ;AAAA,IAC7D;AAAA,EACJ,EACC,KAAK,CAAC,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,IAAI,CAAC;AAChD;AAuBO,SAAS,oBACd,MACA,WAA4B,CAAC,GAC7B,UAA0B,CAAC,GAC3B,SAA+B,CAAC,GAClB;AACd,QAAM,aAAyC;AAAA,IAC7C,QAAQ,SAAS;AAAA,IACjB,WAAW,SAAS;AAAA,IACpB,WAAW,SAAS;AAAA,IACpB,UAAU,SAAS,WACf,eAAe,SAAS,UAAU,SAAS,gBAAgB,MAAM,IACjE;AAAA,IACJ,WAAW,SAAS;AAAA,EACtB;AACA,QAAM,eAAe,sBAAsB,UAAU;AACrD,QAAM,YAAY,SAAS,aAAa,gBAAgB,SAAS,UAAU,IAAI,CAAC;AAChF,QAAM,eAAe,SAAS,iBAC1B,UAAU,OAAO,SAAS,cAAc,IACxC;AACJ,QAAM,QAAQ,CAAC,GAAG,cAAc,GAAG,YAAY;AAE/C,QAAM,gBAAoE,CAAC;AAC3E,MAAI,QAAQ,aAAc,eAAc,eAAe,QAAQ;AAC/D,MAAI,QAAQ,gBAAgB,QAAQ,aAAa,SAAS,EAAG,eAAc,eAAe,QAAQ;AAElG,QAAM,iBAA+B;AAAA,IACnC,GAAI,QAAQ,OAAO,EAAE,MAAM,QAAQ,KAAK,IAAI,CAAC;AAAA,IAC7C,GAAI,OAAO,KAAK,aAAa,EAAE,SAAS,IAAI,EAAE,QAAQ,cAAc,IAAI,CAAC;AAAA,IACzE,GAAI,QAAQ,MAAM,EAAE,KAAK,QAAQ,IAAI,IAAI,CAAC;AAAA,IAC1C,WAAW;AAAA,MACT;AAAA,MACA,GAAI,SAAS,aAAa,SAAS,UAAU,SAAS,IAAI,EAAE,QAAQ,SAAS,UAAU,IAAI,CAAC;AAAA,IAC9F;AAAA,EACF;AAEA,QAAM,SAAS,mBAAmB,MAAM,cAAc;AACtD,MAAI,CAAC;AACH,UAAM,IAAI,MAAM,+EAA+E;AAGjG,QAAM,eAAe,OAAO,QAAQ;AACpC,MAAI,OAAO,iBAAiB,SAAU,gCAA+B,cAAc,MAAM;AACzF,SAAO,2BAA2B,MAAM;AAC1C;AAMA,SAAS,2BAA2BA,UAAqC;AACvE,MAAI,CAACA,SAAQ,UAAW,QAAOA;AAC/B,QAAM,OAAO,OAAO;AAAA,IAClB,OAAO,QAAQA,SAAQ,SAAS,EAAE;AAAA,MAAO,CAAC,CAAC,EAAE,KAAK,MAChD,UAAU,UAAa,EAAE,MAAM,QAAQ,KAAK,KAAK,MAAM,WAAW;AAAA,IACpE;AAAA,EACF;AACA,QAAM,MAAoB,EAAE,GAAGA,UAAS,WAAW,KAAK;AACxD,MAAI,QAAQ,OAAO,KAAK,IAAI,EAAE,WAAW,EAAG,QAAO,IAAI;AACvD,SAAO;AACT;AAIO,SAAS,cAAc,KAAqB;AACjD,SAAO,IAAI,QAAQ,oBAAoB,EAAE,EAAE,KAAK;AAClD;AAiCO,SAAS,qBAAqB,OAA2D;AAC9F,QAAM,SAAS,MAAM,KAAK;AAC1B,QAAM,OAAO,cAAc,MAAM,IAAI,OAAO,KAAK,IAAI,MAAM;AAC3D,SAAO,EAAE,IAAI,MAAM,IAAI,OAAO,MAAM,OAAO,MAAM,WAAW,KAAK;AACnE;","names":["profile"]}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createChatTurnRoutes,
|
|
3
3
|
streamChatRouteAsSandboxEvents
|
|
4
|
-
} from "../chunk-
|
|
5
|
-
import "../chunk-ZSSCVT6H.js";
|
|
4
|
+
} from "../chunk-MIFVOYZ7.js";
|
|
6
5
|
import "../chunk-EA4UVS4T.js";
|
|
7
|
-
import "../chunk-
|
|
8
|
-
import "../chunk-
|
|
6
|
+
import "../chunk-ZSSCVT6H.js";
|
|
7
|
+
import "../chunk-S3344RBY.js";
|
|
9
8
|
import "../chunk-X47R2IVO.js";
|
|
10
9
|
import "../chunk-TXD5HXLE.js";
|
|
10
|
+
import "../chunk-ZVEEWGDK.js";
|
|
11
11
|
import "../chunk-3NM4GCAY.js";
|
|
12
12
|
import "../chunk-ZMMIQOFI.js";
|
|
13
13
|
import "../chunk-M3K2HVQD.js";
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { LineMember } from '@tangle-network/sandbox';
|
|
2
|
+
import type { LineBillingView, LineMembersClient, LineSetupClient, LineSetupSnapshot } from '../../hosted-agent/react';
|
|
3
|
+
export declare const workspaceName = "Example workspace";
|
|
4
|
+
export declare const setupEmpty: LineSetupSnapshot;
|
|
5
|
+
export declare const setupConnected: LineSetupSnapshot;
|
|
6
|
+
export declare const setupDisconnected: LineSetupSnapshot;
|
|
7
|
+
export declare const setupMultiNumberDisconnected: LineSetupSnapshot;
|
|
8
|
+
export declare const setupManualNumberReconnect: LineSetupSnapshot;
|
|
9
|
+
export declare function createSetupClient(initial: LineSetupSnapshot): LineSetupClient;
|
|
10
|
+
/** Let a mutation succeed, fail its next read once, then allow Retry to recover. */
|
|
11
|
+
export declare function createSetupRefreshFailureClient(initial: LineSetupSnapshot): LineSetupClient;
|
|
12
|
+
export declare const memberRoles: readonly [{
|
|
13
|
+
readonly value: 'owner';
|
|
14
|
+
readonly label: 'Owner';
|
|
15
|
+
readonly tools: 'act';
|
|
16
|
+
}, {
|
|
17
|
+
readonly value: 'manager';
|
|
18
|
+
readonly label: 'Manager';
|
|
19
|
+
readonly tools: 'act';
|
|
20
|
+
}, {
|
|
21
|
+
readonly value: 'member';
|
|
22
|
+
readonly label: 'Member';
|
|
23
|
+
readonly tools: 'act';
|
|
24
|
+
}, {
|
|
25
|
+
readonly value: 'chat-only';
|
|
26
|
+
readonly label: 'Guest';
|
|
27
|
+
readonly tools: 'chat';
|
|
28
|
+
}];
|
|
29
|
+
export declare const members: LineMember[];
|
|
30
|
+
export declare function createMembersClient(initial: LineMember[]): LineMembersClient;
|
|
31
|
+
/** Retain the successful write while the next member list read fails once. */
|
|
32
|
+
export declare function createMembersRefreshFailureClient(initial: LineMember[]): LineMembersClient;
|
|
33
|
+
export declare const billingUnverified: LineBillingView;
|
|
34
|
+
export declare const billingVerified: LineBillingView;
|
package/dist/teams/index.js
CHANGED
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
generateInviteToken,
|
|
3
|
-
isInviteTokenShape,
|
|
4
|
-
validateInviteToken
|
|
5
|
-
} from "../chunk-DJ4VJIH5.js";
|
|
6
1
|
import {
|
|
7
2
|
INVITATION_EXPIRY_DAYS,
|
|
8
3
|
generateInvitationToken,
|
|
@@ -12,6 +7,11 @@ import {
|
|
|
12
7
|
parseInvitationPermission,
|
|
13
8
|
renderInvitationEmail
|
|
14
9
|
} from "../chunk-2DRYTJHI.js";
|
|
10
|
+
import {
|
|
11
|
+
generateInviteToken,
|
|
12
|
+
isInviteTokenShape,
|
|
13
|
+
validateInviteToken
|
|
14
|
+
} from "../chunk-DJ4VJIH5.js";
|
|
15
15
|
import {
|
|
16
16
|
ASSIGNABLE_WORKSPACE_ROLES,
|
|
17
17
|
ORGANIZATION_ROLES,
|
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
SeatLimitError
|
|
3
|
-
} from "../chunk-MEUNTJL5.js";
|
|
4
|
-
import "../chunk-DJ4VJIH5.js";
|
|
5
1
|
import {
|
|
6
2
|
generateInvitationToken,
|
|
7
3
|
getInvitationExpiresAt,
|
|
@@ -9,6 +5,10 @@ import {
|
|
|
9
5
|
normalizeInvitationEmail,
|
|
10
6
|
parseInvitationPermission
|
|
11
7
|
} from "../chunk-2DRYTJHI.js";
|
|
8
|
+
import {
|
|
9
|
+
SeatLimitError
|
|
10
|
+
} from "../chunk-MEUNTJL5.js";
|
|
11
|
+
import "../chunk-DJ4VJIH5.js";
|
|
12
12
|
import {
|
|
13
13
|
hasWorkspaceRole
|
|
14
14
|
} from "../chunk-6XIAPIW6.js";
|