@tangle-network/agent-app 0.49.24 → 0.49.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assistant/index.js +5 -5
- package/dist/channels/components.d.ts +37 -0
- package/dist/channels/context.d.ts +14 -0
- package/dist/channels/hooks.d.ts +35 -0
- package/dist/channels/index.d.ts +7 -0
- package/dist/channels/index.js +727 -0
- package/dist/channels/index.js.map +1 -0
- package/dist/channels/numbers.d.ts +32 -0
- package/dist/channels/payment.d.ts +16 -0
- package/dist/channels/types.d.ts +96 -0
- package/dist/channels/ui.d.ts +15 -0
- package/dist/{chunk-WAMJSZYB.js → chunk-4S36HWE4.js} +1 -1
- package/dist/{chunk-WHHMYVDB.js → chunk-VIL6MHHQ.js} +4 -4
- package/dist/design-canvas-react/index.js +4 -4
- package/dist/design-canvas-react/lazy.js +1 -1
- package/dist/hosted-agent/index.d.ts +11 -4
- package/dist/hosted-agent/index.js +26 -8
- package/dist/hosted-agent/index.js.map +1 -1
- package/dist/stories/fixtures/channels.d.ts +18 -0
- package/dist/web-react/index.js +6 -6
- package/package.json +11 -1
- /package/dist/{chunk-WAMJSZYB.js.map → chunk-4S36HWE4.js.map} +0 -0
- /package/dist/{chunk-WHHMYVDB.js.map → chunk-VIL6MHHQ.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/channels/context.tsx","../../src/channels/hooks.ts","../../src/channels/components.tsx","../../src/channels/numbers.ts","../../src/channels/ui.tsx","../../src/channels/payment.tsx"],"sourcesContent":["import { createContext, useContext, type ReactNode } from 'react'\nimport type { ChannelsClient } from './types'\n\nconst Context = createContext<{ client: ChannelsClient; pollInterval: number | false } | null>(null)\n\n/** Mount below authentication. Changing scope remounts all channel state. */\nexport function ChannelsProvider({ client, pollInterval = 3000, children }: {\n client: ChannelsClient\n /** Poll after a read settles, never concurrently. false disables polling. */\n pollInterval?: number | false\n children: ReactNode\n}) {\n if (pollInterval !== false && (!Number.isFinite(pollInterval) || pollInterval < 100)) {\n throw new Error('ChannelsProvider pollInterval must be false or at least 100 ms.')\n }\n return <Context.Provider value={{ client, pollInterval }} key={client.scope}>{children}</Context.Provider>\n}\n\nexport function useChannelsClient(): ChannelsClient {\n return useChannelsContext().client\n}\n\nexport function useChannelsContext() {\n const value = useContext(Context)\n if (!value) throw new Error('Channel components and hooks require ChannelsProvider.')\n return value\n}\n","import { useCallback, useEffect, useRef } from 'react'\nimport { confirmWrite, rejectWrite, useAsyncResource, useConfirmedMutation } from '../web-react/async'\nimport type { AsyncLoadContext, AsyncResourceState, MutationOutcome } from '../web-react/async'\nimport { useChannelsContext } from './context'\nimport type { ChannelVerification, ConnectChannelInput, Line, LineTransport } from './types'\n\n/** Reuse the kit's abort/sequence-aware read machine; polling never overlaps a read. */\nexport function useChannelResource<T>(load: (context: AsyncLoadContext) => Promise<T>, deps: readonly unknown[], enabled = true, poll = true): AsyncResourceState<T> {\n const { client, pollInterval } = useChannelsContext()\n const resource = useAsyncResource({ load, deps: [client, client.scope, ...deps], enabled })\n useEffect(() => {\n if (!enabled || !poll || pollInterval === false || resource.status === 'loading' || resource.status === 'idle') return\n const delay = typeof document !== 'undefined' && document.hidden ? Math.max(15000, pollInterval) : pollInterval\n const timer = setTimeout(resource.retry, delay)\n return () => clearTimeout(timer)\n }, [enabled, poll, pollInterval, resource.status, resource.retry, resource])\n return enabled ? resource : { status: 'idle', retry: resource.retry }\n}\n\n/** Single-flight on top of the shared confirmed-write primitive. */\nexport function useChannelMutation<I, O>(mutate: (input: I, context: AsyncLoadContext) => Promise<O>, onSucceeded?: (value: O) => void) {\n const { client } = useChannelsContext()\n const lock = useRef<object | null>(null)\n const mutation = useConfirmedMutation<I, O>({\n mutate: async (input, context) => confirmWrite(await mutate(input, context)),\n onSucceeded,\n })\n const { reset, run } = mutation\n useEffect(() => {\n reset()\n return () => { lock.current = null; reset() }\n }, [client, client.scope, reset])\n const singleRun = useCallback(async (input: I): Promise<MutationOutcome<O>> => {\n if (lock.current) return rejectWrite('Another channel operation is still in progress.')\n const token = {}\n lock.current = token\n try { return await run(input) }\n finally { if (lock.current === token) lock.current = null }\n }, [run])\n return { ...mutation, run: singleRun }\n}\n\nexport function useChannels() {\n const { client } = useChannelsContext()\n return useChannelResource(() => client.lines.list(), [])\n}\n\nexport function useChannelConnections(transport: LineTransport) {\n const { client } = useChannelsContext()\n return useChannelResource(context => client.setup.connections(transport, context), [transport], true, false)\n}\n\nexport function useWhatsAppNumbers(connectionId: string) {\n const { client } = useChannelsContext()\n return useChannelResource(context => client.setup.whatsappNumbers(connectionId, context), [connectionId], !!connectionId, false)\n}\n\n/** Connect only acquires/binds a line. It never labels it verified or activates replies. */\nexport function useConnectChannel(onConnected?: (line: Line) => void) {\n const { client } = useChannelsContext()\n return useChannelMutation<ConnectChannelInput, Line>(async input => {\n if (input.kind === 'connection') {\n if (!input.input.connectionId.trim()) throw new Error('Choose a connection.')\n if (input.input.transport === 'whatsapp' && !input.input.phoneNumberId.trim()) throw new Error('Choose a WhatsApp number.')\n return client.lines.fromConnection(input.input)\n }\n if (input.kind === 'order') {\n if (!input.orderId.trim()) throw new Error('Choose a number order.')\n return client.setup.fromOrder(input.orderId)\n }\n const address = input.address.trim().toLowerCase()\n if (!input.connectionId.trim() || !/^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(address)) throw new Error('Choose a connection and enter a valid mailbox address.')\n return client.setup.fromEmail({ connectionId: input.connectionId, address })\n }, onConnected)\n}\n\nexport function verificationExpired(test: ChannelVerification, now = Date.now()): boolean {\n return test.expired || (test.status !== 'verified' && test.expiresAt <= now)\n}\n\nexport type VerificationAction = 'start' | 'resume' | 'send' | 'activate' | 'reset'\n\nexport function useChannel(lineId: string) {\n const { client } = useChannelsContext()\n const resource = useChannelResource(async context => {\n const [line, verification] = await Promise.all([client.lines.get(lineId), client.setup.verification(lineId, context)])\n if (line.id !== lineId || (line.attachment && line.attachment.lineId !== lineId) || (verification && verification.lineId !== lineId)) throw new Error('Channel binding changed. Reload channel setup.')\n return { line, verification }\n }, [lineId], !!lineId)\n const mutation = useChannelMutation<{ action: VerificationAction; confirm?: boolean }, void>(async ({ action, confirm }) => {\n if (resource.status !== 'ready') throw new Error('Read the current channel state before changing it.')\n const { line, verification: test } = resource.value\n if (line.id !== lineId) throw new Error('Channel binding changed. Reload channel setup.')\n if (action !== 'reset' && line.status !== 'active') throw new Error('This line is not active.')\n if (action === 'start') {\n if (test && test.status !== 'revoked') throw new Error('Stop the existing test before starting another.')\n await client.setup.start(lineId)\n return\n }\n if (!test || test.lineId !== lineId) throw new Error('Start a channel test first.')\n if (action === 'reset') {\n if (confirm !== true) throw new Error('Confirm stopping this test.')\n await client.setup.reset(lineId, test.id)\n return\n }\n if (verificationExpired(test)) throw new Error('This test expired. Stop it and start a new test.')\n if (action === 'activate') {\n if (test.status !== 'verified') throw new Error('Verify inbound delivery and the test reply before activation.')\n const activated = await client.setup.activate(lineId, test.id)\n if (activated.id !== lineId || activated.status !== 'active' || activated.attachment?.status !== 'active' || activated.attachment.lineId !== lineId) throw new Error('Messaging activation was not confirmed.')\n } else if (action === 'send') {\n if (test.status !== 'received') throw new Error('Wait for the inbound test message before sending a reply.')\n await client.setup.send(lineId, test.id)\n } else {\n if (test.status !== 'configuring' && test.status !== 'uncertain') throw new Error('This test is not waiting for a setup check.')\n await client.setup.resume(lineId, test.id)\n }\n }, resource.retry)\n useEffect(() => mutation.reset(), [lineId, mutation.reset])\n return { resource, ...mutation }\n}\n\nexport function useChannelConversations(lineId: string) {\n const { client } = useChannelsContext()\n return useChannelResource(async () => {\n const threads = await client.lines.threads(lineId).list()\n if (threads.some(thread => thread.lineId !== lineId)) throw new Error('The thread response belongs to another line.')\n return threads\n }, [lineId], !!lineId)\n}\n\nexport function useChannelConversation(lineId: string, threadId: string) {\n const { client } = useChannelsContext()\n return useChannelResource(async () => {\n const messages = await client.lines.threads(lineId).messages(threadId)\n // The SDK returns newest first. Never mutate it, and never render another thread.\n if (messages.some(message => message.threadId !== threadId)) throw new Error('The message response belongs to another conversation.')\n return [...new Map(messages.map(message => [message.id, message])).values()]\n .sort((a, b) => a.createdAt.localeCompare(b.createdAt) || a.id.localeCompare(b.id))\n }, [lineId, threadId], !!lineId && !!threadId)\n}\n","import { useEffect, useId, useState } from 'react'\nimport { useChannelsClient } from './context'\nimport { useChannel, useChannelConnections, useChannelConversation, useChannelConversations, useChannels, useConnectChannel, useWhatsAppNumbers, verificationExpired } from './hooks'\nimport { NUMBER_CHARGE_NOTICE, NUMBER_RETRY_NOTICE, numberStage, useNumberChannel } from './numbers'\nimport type { Line, LineTransport } from './types'\nimport { ChannelFailure, ChannelState, buttonClass, primaryButtonClass, inputClass, panelClass } from './ui'\n\nconst labels: Record<LineTransport, string> = { imessage: 'iMessage', whatsapp: 'WhatsApp', sms: 'SMS', email: 'Email' }\n\n/** User-facing deep links, not provider API calls. Invalid addresses render as text only. */\nexport function channelMessageLink(line: Line, instruction: string): string | null {\n if (line.transport === 'email') {\n if (!/^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(line.address)) return null\n return `mailto:${encodeURIComponent(line.address)}?subject=Channel%20test&body=${encodeURIComponent(instruction)}`\n }\n const address = line.routerAddress ?? line.address\n if (!/^\\+[1-9]\\d{7,14}$/.test(address)) return null\n if (line.transport === 'whatsapp') return `https://wa.me/${address.slice(1)}?text=${encodeURIComponent(instruction)}`\n return `sms:${address}?body=${encodeURIComponent(line.connect ?? instruction)}`\n}\n\nfunction CopyLine({ text }: { text: string }) {\n const [message, setMessage] = useState('')\n return <div className=\"space-y-2\"><div className=\"flex items-start gap-2 rounded-md bg-muted p-3\">\n <code className=\"min-w-0 flex-1 break-all\">{text}</code>\n <button type=\"button\" className={buttonClass} aria-label={`Copy ${text}`} onClick={async () => {\n try { await navigator.clipboard.writeText(text); setMessage('Copied') }\n catch { setMessage('Copy is unavailable. Select and copy the displayed text.') }\n }}>Copy</button>\n </div>{message && <p role=\"status\" className=\"text-sm\">{message}</p>}</div>\n}\n\n/** Delivery evidence comes exclusively from the host. An acquired line is not proof. */\nexport function ChannelVerificationPanel({ lineId, className = '' }: { lineId: string; className?: string }) {\n const channel = useChannel(lineId)\n const [confirming, setConfirming] = useState(false)\n useEffect(() => setConfirming(false), [lineId])\n const busy = channel.state.status === 'pending'\n const act = (action: 'start' | 'resume' | 'send' | 'activate') => void channel.run({ action })\n return <section className={`${panelClass} ${className}`} aria-label=\"Channel verification\" aria-busy={busy}>\n <h2 className=\"text-lg font-semibold\">Verify this channel</h2>\n <ChannelState resource={channel.resource} empty=\"The channel is unavailable.\">{({ line, verification: test }) => {\n const expired = test ? verificationExpired(test) : false\n const active = line.status === 'active' && line.attachment?.status === 'active'\n const link = test ? channelMessageLink(line, test.instruction) : null\n return <>\n <p className=\"break-all font-medium\">{labels[line.transport]} · {line.address}</p>\n {line.status !== 'active' ? <p role=\"alert\">This line is {line.status}. Check the line’s status before testing or activating it.</p>\n : active && test?.status === 'verified' && !expired ? <>\n <p role=\"status\">Both directions verified. Answering {labels[line.transport]} messages.</p>\n <p>{line.connect && line.routerAddress ? `Share this: send ${line.connect} to ${line.routerAddress}.` : 'Share this channel address so people can start a conversation.'}</p>\n <CopyLine text={line.connect ?? line.address} />\n </>\n : !test || test.status === 'revoked' ? <>\n {active && <p>Attached to an agent, but no completed delivery test was returned.</p>}\n <p>Test inbound delivery and a reply before this agent answers people.</p>\n <button type=\"button\" className={primaryButtonClass} disabled={busy} onClick={() => act('start')}>Start channel test</button>\n </> : expired ? <p role=\"alert\">This test expired. Stop it before starting a new test.</p>\n : test.status === 'verified' ? <><p>Both directions verified. Messaging is not turned on yet.</p>\n <button type=\"button\" className={primaryButtonClass} disabled={busy} onClick={() => act('activate')}>Turn on messaging</button></>\n : test.status === 'configuring' ? <><p>Connecting incoming messages to Tangle.</p>\n <button type=\"button\" className={primaryButtonClass} disabled={busy} onClick={() => act('resume')}>Check setup</button></>\n : test.status === 'waiting' ? <>\n {line.connect && line.routerAddress && <><p>First, send this to {line.routerAddress}. The router replies with your agent’s number.</p><CopyLine text={line.connect} /></>}\n <p>{line.transport === 'email' ? `From another email address, send this line to ${line.address}.` : line.connect ? 'Then send this in the conversation the router opens:' : `From your phone, send this to ${line.address}:`}</p>\n <CopyLine text={test.instruction} />\n {link && <a className={primaryButtonClass} href={link} target={line.transport === 'whatsapp' ? '_blank' : undefined} rel=\"noopener noreferrer\">Open {line.transport === 'email' ? 'Email' : line.transport === 'whatsapp' ? 'WhatsApp' : 'Messages'}</a>}\n </> : test.status === 'received' ? <><p role=\"status\">Your test message arrived.</p><button type=\"button\" className={primaryButtonClass} disabled={busy} onClick={() => act('send')}>Send test reply</button></>\n : test.status === 'sending' ? <p role=\"status\">The test reply is being sent. Do not send another.</p>\n : test.status === 'uncertain' ? <><p role=\"alert\">The reply result is uncertain. Check its status instead of sending another.</p><button type=\"button\" className={primaryButtonClass} disabled={busy} onClick={() => act('resume')}>Check reply status</button></>\n : test.status === 'needs_review' ? <p role=\"alert\">Incoming message setup needs operator review. No delivery success is claimed.</p>\n : <p>Reply with the CONFIRM line in the message you received, in the same conversation.</p>}\n {test?.error && <p role=\"alert\" className=\"text-destructive\">{test.error}</p>}\n {test && test.status !== 'revoked' && !(active && test.status === 'verified' && !expired) && <>\n {!confirming ? <button type=\"button\" className={buttonClass} disabled={busy} onClick={() => setConfirming(true)}>Stop this test</button> : <fieldset className=\"space-y-2\"><legend>Stop this test and revoke its setup?</legend>\n <button type=\"button\" className={buttonClass} disabled={busy} onClick={async () => { const outcome = await channel.run({ action: 'reset', confirm: true }); if (outcome.succeeded) setConfirming(false) }}>Confirm stop</button>{' '}\n <button type=\"button\" className={buttonClass} disabled={busy} onClick={() => setConfirming(false)}>Keep testing</button>\n </fieldset>}\n </>}\n <button type=\"button\" className={buttonClass} disabled={busy} onClick={channel.resource.retry}>Refresh channel status</button>\n </>\n }}</ChannelState>\n <ChannelFailure state={channel.state} />\n </section>\n}\n\nfunction ConnectionPicker({ transport, onConnected }: { transport: Exclude<LineTransport, 'sms'>; onConnected: (line: Line) => void }) {\n const client = useChannelsClient()\n const connections = useChannelConnections(transport)\n const [connectionId, setConnectionId] = useState('')\n const [numberId, setNumberId] = useState('')\n const [address, setAddress] = useState('')\n const [showNumbers, setShowNumbers] = useState(false)\n const numbers = useWhatsAppNumbers(showNumbers ? connectionId : '')\n const connect = useConnectChannel(onConnected)\n const busy = connect.state.status === 'pending'\n useEffect(() => { setConnectionId(''); setNumberId(''); setShowNumbers(false); setAddress('') }, [client, transport])\n useEffect(() => {\n if (connections.status === 'ready' && connections.value.length === 1) setConnectionId(connections.value[0]!.id)\n }, [connections])\n useEffect(() => {\n if (numbers.status === 'ready' && numbers.value.length === 1) setNumberId(numbers.value[0]!.id)\n }, [numbers])\n return <>\n <ChannelState resource={connections} empty=\"No eligible Hub connections. Connect an identity in your app’s Integrations first.\">{items => <form className=\"space-y-4\" onSubmit={event => {\n event.preventDefault()\n if (transport === 'email') void connect.run({ kind: 'email', connectionId, address })\n else if (transport === 'whatsapp') void connect.run({ kind: 'connection', input: { transport, connectionId, phoneNumberId: numberId } })\n else void connect.run({ kind: 'connection', input: { transport, connectionId } })\n }}>\n <label className=\"block\">Connection<select className={inputClass} value={connectionId} disabled={busy} onChange={event => { setConnectionId(event.target.value); setNumberId(''); setShowNumbers(false) }} required>\n <option value=\"\">Choose a connection</option>{items.map(item => <option key={item.id} value={item.id}>{item.account ?? item.displayName}</option>)}\n </select></label>\n {transport === 'email' && <label className=\"block\">Mailbox address<input className={inputClass} type=\"email\" required value={address} disabled={busy} onChange={event => setAddress(event.target.value)} placeholder=\"hello@example.com\" /></label>}\n {transport === 'whatsapp' && <>\n <button type=\"button\" className={buttonClass} disabled={!connectionId || busy} onClick={() => { setShowNumbers(true); numbers.retry() }}>Show owned numbers</button>\n {showNumbers && <ChannelState resource={numbers} empty=\"This connection has no eligible WhatsApp numbers.\">{owned => <label className=\"block\">WhatsApp number<select className={inputClass} required value={numberId} disabled={busy} onChange={event => setNumberId(event.target.value)}>\n <option value=\"\">Choose a number</option>{owned.map(number => <option key={number.id} value={number.id}>{number.address}</option>)}\n </select></label>}</ChannelState>}\n </>}\n <p className=\"text-sm text-muted-foreground\">Connect an owned line, then test both directions. Each person must message first. Permissions remain managed in Tangle Hub.</p>\n <button type=\"submit\" className={primaryButtonClass} disabled={busy || !connectionId || (transport === 'email' && !address.trim()) || (transport === 'whatsapp' && (!numberId || !showNumbers || numbers.status !== 'ready'))}>{busy ? 'Connecting…' : 'Connect channel'}</button>\n </form>}</ChannelState>\n <ChannelFailure state={connect.state} />\n </>\n}\n\nexport interface ChannelConnectProps { transport: LineTransport; initialLineId?: string; className?: string }\n\n/** Select an existing line or connect an owned source, then verify it. */\nexport function ChannelConnect({ transport, initialLineId = '', className = '' }: ChannelConnectProps) {\n const [selected, setSelected] = useState(initialLineId)\n const lines = useChannels()\n const id = useId()\n useEffect(() => setSelected(initialLineId), [transport, initialLineId])\n return <div className={`space-y-4 ${className}`}>\n <section className={panelClass} aria-label={`${labels[transport]} channel`}>\n <h2 className=\"text-lg font-semibold\">Connect {labels[transport]}</h2>\n {transport === 'imessage' && <p>Use a free shared iMessage identity, or acquire a dedicated number. A shared identity requires a connect message to the router first.</p>}\n <ChannelState resource={lines} empty=\"No lines connected to this agent yet.\">{all => {\n const matching = all.filter(line => line.transport === transport && line.status !== 'released')\n return matching.length ? <><label htmlFor={id}>Existing line</label><select id={id} className={inputClass} value={selected} onChange={event => setSelected(event.target.value)}>\n <option value=\"\">Choose or connect a line</option>{matching.map(line => <option value={line.id} key={line.id}>{line.label ?? line.address}</option>)}\n </select></> : <p>No {labels[transport]} lines connected yet.</p>\n }}</ChannelState>\n {!selected && transport !== 'sms' && <ConnectionPicker key={transport} transport={transport} onConnected={line => { setSelected(line.id); lines.retry() }} />}\n {selected && <button type=\"button\" className={buttonClass} onClick={() => setSelected('')}>Choose another line</button>}\n </section>\n {selected ? <ChannelVerificationPanel key={selected} lineId={selected} /> : (transport === 'sms' || transport === 'imessage') && <NumberChannel transport={transport} />}\n </div>\n}\n\ntype TransportPanelProps = Omit<ChannelConnectProps, 'transport'>\nexport function IMessageChannel(props: TransportPanelProps) { return <ChannelConnect {...props} transport=\"imessage\" /> }\nexport function WhatsAppChannel(props: TransportPanelProps) { return <ChannelConnect {...props} transport=\"whatsapp\" /> }\nexport function EmailChannel(props: TransportPanelProps) { return <ChannelConnect {...props} transport=\"email\" /> }\nexport function SMSChannel(props: TransportPanelProps) { return <ChannelConnect {...props} transport=\"sms\" /> }\n\n/** A transport's managed number activation, with explicit consent and cancellation. */\nexport function NumberChannel({ transport, className = '' }: { transport: 'sms' | 'imessage'; className?: string }) {\n const client = useChannelsClient()\n const number = useNumberChannel(transport)\n const [consent, setConsent] = useState(false)\n const [cancelId, setCancelId] = useState<string | null>(null)\n const [lineId, setLineId] = useState('')\n const connect = useConnectChannel(line => setLineId(line.id))\n useEffect(() => { setConsent(false) }, [number.quote?.token])\n useEffect(() => { setCancelId(null); setLineId(''); connect.reset() }, [transport, client, connect.reset])\n const busy = number.state.status === 'pending' || connect.state.status === 'pending'\n const money = (cents: number) => new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(cents / 100)\n return <section className={`${panelClass} ${className}`} aria-label={`${labels[transport]} number`} aria-busy={busy}>\n <h2 className=\"text-lg font-semibold\">Get a {labels[transport]} number</h2>\n {!client.ordering ? <p>Number ordering is unavailable. Existing numbers are unchanged.</p> : <ChannelState resource={number.resource} empty=\"Number availability has not been confirmed.\">{({ readiness, complete }) => <>\n {!readiness.configured && <p>New orders are disabled. Existing numbers still require separate cancellation.</p>}\n {!complete && <p role=\"alert\">The order list is incomplete. New purchases are disabled until ownership can be confirmed.</p>}\n {number.held && <p>This agent already holds an order for this transport. Cancel and release it before ordering another.</p>}\n <button type=\"button\" className={primaryButtonClass} disabled={busy || !complete || !readiness.configured || !readiness.transports.includes(transport) || !!number.held || number.uncertain} onClick={() => void number.run({ action: 'quote' })}>See activation price</button>\n </>}</ChannelState>}\n {number.quote && <div className=\"space-y-3 rounded-md border border-border p-4\">\n <h3 className=\"font-medium\">Activation · {money(number.quote.activationCents)}</h3>\n <p className=\"whitespace-pre-wrap\">{number.quote.terms || 'Published activation terms are missing. Ordering is disabled.'}</p>\n <p>{NUMBER_CHARGE_NOTICE}</p>\n <label className=\"flex items-start gap-2\"><input type=\"checkbox\" checked={consent} disabled={busy} onChange={event => setConsent(event.target.checked)} />I authorize this one-time activation charge. The number is not live until channel setup is complete.</label>\n {!number.uncertain && number.quote.expiresAt <= Date.now() && <p role=\"alert\">This quote expired. Read a new activation price before ordering.</p>}\n <button type=\"button\" className={primaryButtonClass} disabled={busy || !consent || !number.quote.terms.trim() || (!number.uncertain && number.quote.expiresAt <= Date.now())} onClick={() => void number.run({ action: 'purchase', consent })}>{number.uncertain ? 'Retry approved purchase' : `Order for ${money(number.quote.activationCents)}`}</button>\n </div>}\n {number.uncertain && <p role=\"alert\">The purchase response was lost or refused. Its outcome is not confirmed. {NUMBER_RETRY_NOTICE}</p>}\n {number.orders.map(order => <article key={order.id} className=\"space-y-3 rounded-md border border-border p-4\">\n <h3 className=\"break-all font-medium\">{order.address ?? 'Your number order'}</h3><p>{numberStage(order)}</p>\n {order.errorCode && <p role=\"status\">Status: {order.errorCode.replace(/_/g, ' ')}</p>}\n {order.status === 'ready_for_setup' && order.cancellation === 'none' && <><p>The number is acquired, not delivery-verified. Test it before connecting conversations.</p>\n <button type=\"button\" className={buttonClass} disabled={busy} onClick={() => void connect.run({ kind: 'order', orderId: order.id })}>Test this number</button></>}\n <button type=\"button\" className={buttonClass} disabled={busy} onClick={() => void number.run({ action: 'advance', orderId: order.id })}>Check progress</button>{' '}\n {order.cancellation === 'none' && (cancelId !== order.id ? <button type=\"button\" className={buttonClass} disabled={busy} onClick={() => setCancelId(order.id)}>Cancel number</button> : <fieldset className=\"space-y-2\"><legend>Cancel this number? Access will be removed. Paid activation is not automatically refunded.</legend>\n <button type=\"button\" className={buttonClass} disabled={busy} onClick={async () => { const result = await number.run({ action: 'cancel', orderId: order.id, confirm: true }); if (result.succeeded) setCancelId(null) }}>Confirm cancellation</button>{' '}\n <button type=\"button\" className={buttonClass} disabled={busy} onClick={() => setCancelId(null)}>Keep number</button>\n </fieldset>)}\n </article>)}\n <button type=\"button\" className={buttonClass} disabled={busy} onClick={number.resource.retry}>Refresh number status</button>\n <p className=\"text-sm text-muted-foreground\">SMS and iMessage are separate activations. Buying a number does not enable WhatsApp.</p>\n <ChannelFailure state={number.state} /><ChannelFailure state={connect.state} />\n {lineId && <ChannelVerificationPanel lineId={lineId} key={lineId} />}\n </section>\n}\n\n/** Read-only history. Messages stay on their original channel; no synthetic composer. */\nexport function ChannelConversation({ lineId, threadId, className = '' }: { lineId: string; threadId: string; className?: string }) {\n const messages = useChannelConversation(lineId, threadId)\n return <section className={`${panelClass} ${className}`} aria-label=\"Channel conversation\">\n <h3 className=\"font-semibold\">Conversation</h3>\n <ChannelState resource={messages} empty=\"No messages in this conversation yet.\">{items => <ol role=\"log\" aria-live=\"polite\" aria-label=\"Messages\" className=\"space-y-3\">\n {items.map(message => <li key={message.id} className=\"space-y-1 rounded-md bg-muted p-3\" data-direction={message.direction}>\n <p className=\"text-xs text-muted-foreground\">{message.direction === 'in' ? 'Incoming' : 'Outgoing'} · {message.kind} · <time dateTime={message.createdAt}>{message.createdAt}</time></p>\n <p className=\"whitespace-pre-wrap break-words\">{message.text ?? 'Non-text message'}</p>\n <p className=\"text-xs\">{message.status}{message.errorCode ? ` · ${message.errorCode}` : ''}</p>\n </li>)}\n </ol>}</ChannelState>\n <button type=\"button\" className={buttonClass} onClick={messages.retry}>Refresh messages</button>\n </section>\n}\n\n/** A channel's separate member threads. Changing line never reuses another line's selection. */\nexport function ChannelConversations({ lineId, className = '' }: { lineId: string; className?: string }) {\n const threads = useChannelConversations(lineId)\n const [selected, setSelected] = useState<{ lineId: string; threadId: string } | null>(null)\n const threadId = selected?.lineId === lineId ? selected.threadId : ''\n return <div className={`space-y-4 ${className}`}>\n <section className={panelClass} aria-label=\"Channel conversations\"><h2 className=\"text-lg font-semibold\">Conversations</h2>\n <ChannelState resource={threads} empty=\"No conversations yet. Members must message the line first.\">{items => <nav aria-label=\"Choose a conversation\" className=\"flex flex-wrap gap-2\">\n {items.map(thread => <button type=\"button\" key={thread.id} className={buttonClass} aria-pressed={thread.id === threadId} onClick={() => setSelected({ lineId, threadId: thread.id })}>{thread.memberId} · {thread.status}</button>)}\n </nav>}</ChannelState>\n </section>\n {threadId && <ChannelConversation key={`${lineId}:${threadId}`} lineId={lineId} threadId={threadId} />}\n </div>\n}\n","import { useEffect, useRef, useState } from 'react'\nimport type { HubNumberOrder, HubNumberQuote } from '@tangle-network/hub-sdk'\nimport { useChannelsContext } from './context'\nimport { useChannelMutation, useChannelResource } from './hooks'\n\nexport const NUMBER_CHARGE_NOTICE = 'This is a one-time activation charge. Tangle does not bill recurring carrier rental for this number. Your provider’s message rates are separate.'\nexport const NUMBER_RETRY_NOTICE = 'Retry uses the same approved quote, not a new purchase. Do not order again while the result is unknown.'\n\nexport function holdsNumber(order: HubNumberOrder): boolean {\n return order.cancellation !== 'released' && order.cancellation !== 'abandoned'\n}\n\nexport function numberStage(order: HubNumberOrder): string {\n if (order.cancellation === 'released' || order.cancellation === 'abandoned') return 'Released'\n if (order.cancellation === 'needs_review') return 'Release needs review'\n if (order.cancellation === 'requested' || order.status === 'cancelled') return 'Cancelling'\n if (order.status === 'needs_review') return 'Needs operator review'\n if (order.status === 'ready_for_setup') return 'Number acquired — channel setup next'\n if (order.errorCode === 'funding_required') return 'Add funds to continue'\n if (order.errorCode === 'sms_not_ready') return 'Waiting for SMS activation'\n return 'Provisioning'\n}\n\n/** Hub owns prices, terms, idempotency, funding, and cancellation. */\nexport function useNumberChannel(transport: 'sms' | 'imessage') {\n const { client } = useChannelsContext()\n const ordering = client.ordering\n const reference = ordering?.references[transport]\n const [quote, setQuote] = useState<HubNumberQuote | null>(null)\n const [receipts, setReceipts] = useState<HubNumberOrder[]>([])\n const [uncertain, setUncertain] = useState(false)\n // Set synchronously at dispatch, before React renders another click.\n const attempted = useRef(false)\n const resource = useChannelResource(async ({ signal }) => {\n if (!ordering || !reference) throw new Error('Number ordering is unavailable.')\n const readiness = await ordering.numbers.readiness()\n const orders: HubNumberOrder[] = []\n const seen = new Set<string>()\n let cursor: string | undefined\n let complete = false\n do {\n if (signal.aborted) throw new Error('Number read was superseded.')\n const page = await ordering.numbers.list({ clientReference: reference, cursor, limit: 100 })\n if (page.orders.some(order => order.clientReference !== reference || order.transport !== transport)) throw new Error('The number list belongs to another binding.')\n orders.push(...page.orders)\n if (page.nextCursor == null) {\n // Older Hub responses omit cursor metadata. An incomplete page cannot\n // establish that this agent has no order, even when the page is empty.\n complete = page.nextCursor === null\n break\n }\n if (seen.has(page.nextCursor) || seen.size >= 100) throw new Error('The number list could not be read completely.')\n seen.add(page.nextCursor)\n cursor = page.nextCursor\n } while (cursor)\n return { readiness, orders, complete }\n }, [ordering, reference, transport], !!ordering)\n const loaded = resource.status === 'ready' ? resource.value : null\n const orders = [...new Map([...(loaded?.orders ?? []), ...receipts].map(order => [order.id, order])).values()]\n const held = orders.find(holdsNumber) ?? null\n\n const mutation = useChannelMutation<\n | { action: 'quote' }\n | { action: 'purchase'; consent: boolean }\n | { action: 'advance'; orderId: string }\n | { action: 'cancel'; orderId: string; confirm: boolean }, void\n >(async (input, { signal }) => {\n if (!ordering || !reference) throw new Error('Number ordering is unavailable.')\n if (input.action === 'quote') {\n if (attempted.current) throw new Error('The previous purchase is unresolved. Retry its approved quote or check progress.')\n if (!loaded?.complete || !loaded.readiness.configured || !loaded.readiness.transports.includes(transport)) throw new Error('Number availability and existing orders must be confirmed first.')\n if (held) throw new Error('This agent already holds a number order for this transport.')\n const { quote: next } = await ordering.numbers.quote({ transport, clientReference: reference })\n if (next.transport !== transport || (next.clientReference !== undefined && next.clientReference !== reference)) throw new Error('The quote belongs to another binding.')\n if (!signal.aborted) setQuote(next)\n return\n }\n if (input.action === 'purchase') {\n if (!quote || !input.consent) throw new Error('Approve the quoted activation charge before ordering.')\n if (!quote.terms.trim() || !Number.isSafeInteger(quote.activationCents) || quote.activationCents < 0 || !quote.token) throw new Error('A valid price and published activation terms are required.')\n if (held) throw new Error('This agent already holds a number order for this transport.')\n if (!attempted.current && (!loaded?.complete || !loaded.readiness.configured || quote.expiresAt <= Date.now())) throw new Error('The quote or order information expired. Read the current state first.')\n attempted.current = true\n try {\n const { order } = await ordering.numbers.create({ quoteToken: quote.token, consent: true, clientReference: reference, transport })\n if (order.clientReference !== reference || order.transport !== transport) throw new Error('The purchase response belongs to another binding. Check progress before ordering again.')\n if (!signal.aborted) {\n // Preserve the acknowledged order before a follow-up read can fail.\n setReceipts(current => [order, ...current.filter(item => item.id !== order.id)])\n setQuote(null)\n setUncertain(false)\n }\n } catch (error) {\n if (!signal.aborted) setUncertain(true)\n throw error\n } finally {\n if (!signal.aborted) resource.retry()\n }\n return\n }\n if (!orders.some(order => order.id === input.orderId)) throw new Error('Read this number order before changing it.')\n if (input.action === 'cancel' && !input.confirm) throw new Error('Confirm cancelling this number.')\n const { order } = await ordering.numbers[input.action](input.orderId)\n if (order.id !== input.orderId || order.clientReference !== reference || order.transport !== transport) throw new Error('The number response belongs to another binding.')\n if (!signal.aborted) {\n setReceipts(current => [order, ...current.filter(item => item.id !== order.id)])\n resource.retry()\n }\n })\n useEffect(() => {\n setQuote(null); setReceipts([]); setUncertain(false); attempted.current = false\n mutation.reset()\n }, [client, client.scope, transport, reference, mutation.reset])\n useEffect(() => {\n if (loaded) setReceipts(current => current.filter(receipt => !loaded.orders.some(order => order.id === receipt.id)))\n if (loaded?.orders.some(holdsNumber)) {\n setQuote(null); setUncertain(false); attempted.current = false\n } else if (!uncertain && !quote && loaded?.complete && orders.length > 0 && orders.every(order => !holdsNumber(order))) {\n attempted.current = false\n }\n }, [loaded])\n return { resource, orders, held, quote, uncertain, ...mutation }\n}\n","import type { ReactNode } from 'react'\nimport { AsyncView, MutationStatus, type AsyncResourceState, type MutationState } from '../web-react/async'\n\nexport const panelClass = 'space-y-4 rounded-xl border border-border bg-card p-6 text-foreground'\nexport const buttonClass = 'inline-flex items-center justify-center rounded-md border border-border px-3 py-2 text-sm font-medium transition hover:bg-accent focus-visible:outline focus-visible:outline-2 focus-visible:outline-ring focus-visible:outline-offset-2 disabled:cursor-not-allowed disabled:opacity-50'\nexport const primaryButtonClass = `${buttonClass} border-primary bg-primary text-primary-foreground hover:bg-primary/90`\nexport const inputClass = 'block w-full rounded-md border border-input bg-background px-3 py-2 text-foreground focus-visible:outline focus-visible:outline-2 focus-visible:outline-ring'\n\nexport function ChannelFailure({ state }: { state: MutationState<unknown> }) {\n return state.status === 'failed' ? <MutationStatus state={state} className=\"text-sm text-destructive\" /> : null\n}\n\n/** Channel copy around the shared async view, not a second state renderer. */\nexport function ChannelState<T>({ resource, children, empty }: { resource: AsyncResourceState<T>; children: (value: T) => ReactNode; empty: string }) {\n return <AsyncView state={resource} empty={{ title: empty, action: { label: 'Refresh', onClick: resource.retry } }}\n loadingLabel=\"Loading channel…\" retryLabel=\"Try again\"\n renderIdle={() => <p className=\"text-sm text-muted-foreground\">Choose a channel to continue.</p>}>\n {children}\n </AsyncView>\n}\n","import { useEffect, useState } from 'react'\nimport { useChannelsContext } from './context'\nimport { useChannelMutation, useChannelResource } from './hooks'\nimport { ChannelState, ChannelFailure, buttonClass, primaryButtonClass, panelClass } from './ui'\n\n/** Only same-origin relative paths or credential-free HTTPS checkout URLs. */\nexport function safeCheckoutUrl(value: string): string {\n if (/^\\/(?!\\/)/.test(value) && !/[\\\\\\s]/.test(value)) return value\n const url = new URL(value)\n if (url.protocol !== 'https:' || url.username || url.password) throw new Error('Checkout returned an unsafe URL.')\n return url.href\n}\n\nexport function useLinePayment(lineId: string) {\n const { client } = useChannelsContext()\n const resource = useChannelResource(context => {\n if (!client.payment) throw new Error('Payments are unavailable for this line.')\n return client.payment.read(lineId, context)\n }, [lineId], !!lineId && !!client.payment)\n const mutation = useChannelMutation<{ consent: boolean }, string>(async ({ consent }) => {\n if (!client.payment || resource.status !== 'ready') throw new Error('Read the current allowance before checkout.')\n const { plan, allowance, terms } = resource.value\n if (!consent || !terms.trim()) throw new Error('Accept the published terms before checkout.')\n if (!plan.configured || !plan.paid || !Number.isFinite(plan.paid.priceUsdMonthly) || plan.paid.priceUsdMonthly <= 0 || allowance.decision !== 'paywall' || !allowance.paywall || allowance.tier !== 'free') throw new Error('Checkout is not available for this allowance.')\n return safeCheckoutUrl((await client.payment.checkout(lineId)).url)\n })\n useEffect(() => mutation.reset(), [lineId, mutation.reset])\n return { resource, ...mutation, available: !!client.payment }\n}\n\n/** End-user allowance/checkout page, not number acquisition or creator payouts. */\nexport function LinePayPage({ lineId, className = '' }: { lineId: string; className?: string }) {\n const payment = useLinePayment(lineId)\n const [consentedTerms, setConsentedTerms] = useState<string | null>(null)\n const currentTerms = payment.resource.status === 'ready'\n ? JSON.stringify([lineId, payment.resource.value.terms, payment.resource.value.plan.paid])\n : null\n const consent = currentTerms !== null && consentedTerms === currentTerms\n return <section className={`${panelClass} ${className}`} aria-label=\"Line subscription\">\n <h2 className=\"text-lg font-semibold\">Your line plan</h2>\n {!payment.available ? <p>Payments are unavailable for this line.</p> : <ChannelState resource={payment.resource} empty=\"No plan is available.\">{value => <>\n <h3 className=\"font-medium\">{value.label}</h3>\n <p>{value.allowance.turns.used} of {value.allowance.turns.limit} turns used today.</p>\n {value.allowance.tier === 'paid' ? <p role=\"status\">Your paid allowance is active.{value.allowance.decision === 'allowance_reached' ? ' Its current limit is exhausted; another checkout cannot lift it.' : ''}</p> : value.allowance.decision === 'paywall' && value.plan.paid ? <>\n <p>{new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(value.plan.paid.priceUsdMonthly)} per month · {value.plan.paid.turnsPerDay} turns per day.</p>\n <p className=\"whitespace-pre-wrap\">{value.terms || 'Plan terms are unavailable. Checkout is disabled.'}</p>\n <label className=\"flex items-start gap-2\"><input type=\"checkbox\" checked={consent} onChange={event => setConsentedTerms(event.target.checked ? currentTerms : null)} />I accept these subscription terms.</label>\n <button type=\"button\" className={primaryButtonClass} disabled={!consent || !value.terms.trim() || payment.state.status === 'pending'} onClick={() => void payment.run({ consent })}>Continue to checkout</button>\n <p className=\"text-sm text-muted-foreground\">Payment is confirmed by the server after settlement, not by returning to this page.</p>\n </> : <p>{value.allowance.decision === 'allowance_reached' ? 'Your allowance is exhausted. Paying does not lift this limit today.' : 'Your current allowance is available.'} Resets at {value.allowance.resetsAt}.</p>}\n <button type=\"button\" className={buttonClass} onClick={payment.resource.retry}>Check payment status</button>\n </>}</ChannelState>}\n {payment.state.status === 'succeeded' && consent && <a className=\"underline\" href={payment.state.value} rel=\"noopener noreferrer\">Open secure checkout</a>}\n <ChannelFailure state={payment.state} />\n </section>\n}\n"],"mappings":"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","names":["useEffect","useState","useEffect","useRef","useRef","orders","order","useEffect","jsx","jsx","useState","useEffect","useEffect","useState","Fragment","jsx","jsxs","useEffect","useState"]}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { HubNumberOrder, HubNumberQuote } from '@tangle-network/hub-sdk';
|
|
2
|
+
export declare const NUMBER_CHARGE_NOTICE = "This is a one-time activation charge. Tangle does not bill recurring carrier rental for this number. Your provider\u2019s message rates are separate.";
|
|
3
|
+
export declare const NUMBER_RETRY_NOTICE = "Retry uses the same approved quote, not a new purchase. Do not order again while the result is unknown.";
|
|
4
|
+
export declare function holdsNumber(order: HubNumberOrder): boolean;
|
|
5
|
+
export declare function numberStage(order: HubNumberOrder): string;
|
|
6
|
+
/** Hub owns prices, terms, idempotency, funding, and cancellation. */
|
|
7
|
+
export declare function useNumberChannel(transport: 'sms' | 'imessage'): {
|
|
8
|
+
state: import("../web-react/async").MutationState<void>;
|
|
9
|
+
reset: () => void;
|
|
10
|
+
run: (input: {
|
|
11
|
+
action: 'quote';
|
|
12
|
+
} | {
|
|
13
|
+
action: 'purchase';
|
|
14
|
+
consent: boolean;
|
|
15
|
+
} | {
|
|
16
|
+
action: 'advance';
|
|
17
|
+
orderId: string;
|
|
18
|
+
} | {
|
|
19
|
+
action: 'cancel';
|
|
20
|
+
orderId: string;
|
|
21
|
+
confirm: boolean;
|
|
22
|
+
}) => Promise<import("../web-react/async").MutationOutcome<void>>;
|
|
23
|
+
resource: import("../web-react/async").AsyncResourceState<{
|
|
24
|
+
readiness: import("@tangle-network/hub-sdk").HubNumberReadiness;
|
|
25
|
+
orders: HubNumberOrder[];
|
|
26
|
+
complete: boolean;
|
|
27
|
+
}>;
|
|
28
|
+
orders: HubNumberOrder[];
|
|
29
|
+
held: HubNumberOrder | null;
|
|
30
|
+
quote: HubNumberQuote | null;
|
|
31
|
+
uncertain: boolean;
|
|
32
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** Only same-origin relative paths or credential-free HTTPS checkout URLs. */
|
|
2
|
+
export declare function safeCheckoutUrl(value: string): string;
|
|
3
|
+
export declare function useLinePayment(lineId: string): {
|
|
4
|
+
state: import("../web-react/async").MutationState<string>;
|
|
5
|
+
reset: () => void;
|
|
6
|
+
run: (input: {
|
|
7
|
+
consent: boolean;
|
|
8
|
+
}) => Promise<import("../web-react/async").MutationOutcome<string>>;
|
|
9
|
+
resource: import("../web-react/async").AsyncResourceState<import("./types").LinePayment>;
|
|
10
|
+
available: boolean;
|
|
11
|
+
};
|
|
12
|
+
/** End-user allowance/checkout page, not number acquisition or creator payouts. */
|
|
13
|
+
export declare function LinePayPage({ lineId, className }: {
|
|
14
|
+
lineId: string;
|
|
15
|
+
className?: string;
|
|
16
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import type { Line, LineFromConnectionInput, LinesClient, LineTransport } from '@tangle-network/sandbox';
|
|
2
|
+
import type { HubAllowanceDecision, HubAllowancePlan, HubManagedNumbersClient } from '@tangle-network/hub-sdk';
|
|
3
|
+
export type { Line, LineAttachment, LineFromConnectionInput, LineMessage, LineThread, LineTransport } from '@tangle-network/sandbox';
|
|
4
|
+
export type { HubNumberOrder, HubNumberQuote, HubNumberReadiness } from '@tangle-network/hub-sdk';
|
|
5
|
+
/** Public evidence only. The host never returns a confirmation secret or sender identity. */
|
|
6
|
+
export interface ChannelVerification {
|
|
7
|
+
id: string;
|
|
8
|
+
lineId: string;
|
|
9
|
+
status: 'configuring' | 'waiting' | 'received' | 'sending' | 'uncertain' | 'sent' | 'verified' | 'revoked' | 'needs_review';
|
|
10
|
+
instruction: string;
|
|
11
|
+
expiresAt: number;
|
|
12
|
+
expired: boolean;
|
|
13
|
+
error: string | null;
|
|
14
|
+
}
|
|
15
|
+
export interface ChannelConnection {
|
|
16
|
+
id: string;
|
|
17
|
+
displayName: string;
|
|
18
|
+
account: string | null;
|
|
19
|
+
}
|
|
20
|
+
export interface ChannelNumber {
|
|
21
|
+
id: string;
|
|
22
|
+
address: string;
|
|
23
|
+
}
|
|
24
|
+
export interface ChannelReadContext {
|
|
25
|
+
signal: AbortSignal;
|
|
26
|
+
}
|
|
27
|
+
export interface LinePayment {
|
|
28
|
+
label: string;
|
|
29
|
+
plan: HubAllowancePlan;
|
|
30
|
+
allowance: HubAllowanceDecision;
|
|
31
|
+
/** Published terms, required before checkout is offered. */
|
|
32
|
+
terms: string;
|
|
33
|
+
}
|
|
34
|
+
export type ConnectChannelInput = {
|
|
35
|
+
kind: 'connection';
|
|
36
|
+
input: LineFromConnectionInput;
|
|
37
|
+
} | {
|
|
38
|
+
kind: 'email';
|
|
39
|
+
connectionId: string;
|
|
40
|
+
address: string;
|
|
41
|
+
} | {
|
|
42
|
+
kind: 'order';
|
|
43
|
+
orderId: string;
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* The one authenticated, agent-scoped boundary. Keep this object stable.
|
|
47
|
+
*
|
|
48
|
+
* `lines` delegates to Sandbox SDK client.lines (list MUST be scoped to this
|
|
49
|
+
* agent). `setup` is the host's existing Hub/hosted-agent control plane: it
|
|
50
|
+
* verifies ownership and delivery before sandbox.lines.attach, and never runs
|
|
51
|
+
* provider HTTP calls in the browser. No execution, webhook, credential,
|
|
52
|
+
* consent, or billing authority is implemented by this React subpath.
|
|
53
|
+
*
|
|
54
|
+
* Sandbox 0.51+ supplies fromConnection/get/threads; it does not supply email
|
|
55
|
+
* creation, managed-order binding, or delivery verification. Those operations
|
|
56
|
+
* deliberately remain injected instead of pretending they exist on the SDK.
|
|
57
|
+
* The hosted-agent export is server-only. Its existing attachLine operation
|
|
58
|
+
* belongs in setup.activate after proof validation, never in this browser bundle.
|
|
59
|
+
*/
|
|
60
|
+
export interface ChannelsClient {
|
|
61
|
+
/** Changes whenever the authenticated agent or owner changes. Not an auth credential. */
|
|
62
|
+
scope: string;
|
|
63
|
+
lines: Pick<LinesClient, 'fromConnection' | 'get' | 'threads'> & {
|
|
64
|
+
list(): Promise<Line[]>;
|
|
65
|
+
};
|
|
66
|
+
setup: {
|
|
67
|
+
connections(transport: LineTransport, context: ChannelReadContext): Promise<ChannelConnection[]>;
|
|
68
|
+
whatsappNumbers(connectionId: string, context: ChannelReadContext): Promise<ChannelNumber[]>;
|
|
69
|
+
fromEmail(input: {
|
|
70
|
+
connectionId: string;
|
|
71
|
+
address: string;
|
|
72
|
+
}): Promise<Line>;
|
|
73
|
+
fromOrder(orderId: string): Promise<Line>;
|
|
74
|
+
verification(lineId: string, context: ChannelReadContext): Promise<ChannelVerification | null>;
|
|
75
|
+
start(lineId: string): Promise<ChannelVerification>;
|
|
76
|
+
resume(lineId: string, testId: string): Promise<ChannelVerification>;
|
|
77
|
+
send(lineId: string, testId: string): Promise<ChannelVerification>;
|
|
78
|
+
reset(lineId: string, testId: string): Promise<void>;
|
|
79
|
+
/** Recheck proof and ownership server-side; attach through sandbox.lines. */
|
|
80
|
+
activate(lineId: string, testId: string): Promise<Line>;
|
|
81
|
+
};
|
|
82
|
+
/** Optional owner-only number ordering, delegated to hub.numbers. */
|
|
83
|
+
ordering?: {
|
|
84
|
+
numbers: Pick<HubManagedNumbersClient, 'readiness' | 'list' | 'quote' | 'create' | 'advance' | 'cancel'>;
|
|
85
|
+
references: Record<'sms' | 'imessage', string>;
|
|
86
|
+
};
|
|
87
|
+
/** Optional member pay page. The host derives member/meter from authentication. */
|
|
88
|
+
payment?: {
|
|
89
|
+
/** Read Hub allowance plan/status, never admit a turn or grant a paid tier. */
|
|
90
|
+
read(lineId: string, context: ChannelReadContext): Promise<LinePayment>;
|
|
91
|
+
/** Host-owned checkout; only a payment settlement may grant Hub allowance. */
|
|
92
|
+
checkout(lineId: string): Promise<{
|
|
93
|
+
url: string;
|
|
94
|
+
}>;
|
|
95
|
+
};
|
|
96
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import { type AsyncResourceState, type MutationState } from '../web-react/async';
|
|
3
|
+
export declare const panelClass = "space-y-4 rounded-xl border border-border bg-card p-6 text-foreground";
|
|
4
|
+
export declare const buttonClass = "inline-flex items-center justify-center rounded-md border border-border px-3 py-2 text-sm font-medium transition hover:bg-accent focus-visible:outline focus-visible:outline-2 focus-visible:outline-ring focus-visible:outline-offset-2 disabled:cursor-not-allowed disabled:opacity-50";
|
|
5
|
+
export declare const primaryButtonClass = "inline-flex items-center justify-center rounded-md border border-border px-3 py-2 text-sm font-medium transition hover:bg-accent focus-visible:outline focus-visible:outline-2 focus-visible:outline-ring focus-visible:outline-offset-2 disabled:cursor-not-allowed disabled:opacity-50 border-primary bg-primary text-primary-foreground hover:bg-primary/90";
|
|
6
|
+
export declare const inputClass = "block w-full rounded-md border border-input bg-background px-3 py-2 text-foreground focus-visible:outline focus-visible:outline-2 focus-visible:outline-ring";
|
|
7
|
+
export declare function ChannelFailure({ state }: {
|
|
8
|
+
state: MutationState<unknown>;
|
|
9
|
+
}): import("react").JSX.Element | null;
|
|
10
|
+
/** Channel copy around the shared async view, not a second state renderer. */
|
|
11
|
+
export declare function ChannelState<T>({ resource, children, empty }: {
|
|
12
|
+
resource: AsyncResourceState<T>;
|
|
13
|
+
children: (value: T) => ReactNode;
|
|
14
|
+
empty: string;
|
|
15
|
+
}): import("react").JSX.Element;
|
|
@@ -4,9 +4,6 @@ import {
|
|
|
4
4
|
charAt,
|
|
5
5
|
charBefore
|
|
6
6
|
} from "./chunk-3HUFJ5LO.js";
|
|
7
|
-
import {
|
|
8
|
-
AsyncView
|
|
9
|
-
} from "./chunk-3UBAO3N5.js";
|
|
10
7
|
import {
|
|
11
8
|
stepActivityFlowTrace
|
|
12
9
|
} from "./chunk-FBVLEGEG.js";
|
|
@@ -31,6 +28,9 @@ import {
|
|
|
31
28
|
filterCommandPaletteItems,
|
|
32
29
|
groupCommandPaletteItems
|
|
33
30
|
} from "./chunk-SJWIZT7B.js";
|
|
31
|
+
import {
|
|
32
|
+
AsyncView
|
|
33
|
+
} from "./chunk-3UBAO3N5.js";
|
|
34
34
|
import {
|
|
35
35
|
attachmentPartsFromMessageParts
|
|
36
36
|
} from "./chunk-4PZE7XAM.js";
|
|
@@ -6167,4 +6167,4 @@ export {
|
|
|
6167
6167
|
useThinkingSeconds,
|
|
6168
6168
|
ChatMessages
|
|
6169
6169
|
};
|
|
6170
|
-
//# sourceMappingURL=chunk-
|
|
6170
|
+
//# sourceMappingURL=chunk-VIL6MHHQ.js.map
|
|
@@ -43,10 +43,6 @@ import {
|
|
|
43
43
|
selectTickStep
|
|
44
44
|
} from "../chunk-KWN5PGXM.js";
|
|
45
45
|
import "../chunk-Q2QKEV6J.js";
|
|
46
|
-
import {
|
|
47
|
-
DesignCanvasChromeLazy,
|
|
48
|
-
DesignCanvasLazy
|
|
49
|
-
} from "../chunk-WAMJSZYB.js";
|
|
50
46
|
import "../chunk-ZN5J47UX.js";
|
|
51
47
|
import {
|
|
52
48
|
DEFAULT_INSERT_TEMPLATES,
|
|
@@ -89,6 +85,10 @@ import {
|
|
|
89
85
|
setPagePropsCommand,
|
|
90
86
|
ungroupElementCommand
|
|
91
87
|
} from "../chunk-FEVVF6JJ.js";
|
|
88
|
+
import {
|
|
89
|
+
DesignCanvasChromeLazy,
|
|
90
|
+
DesignCanvasLazy
|
|
91
|
+
} from "../chunk-4S36HWE4.js";
|
|
92
92
|
import {
|
|
93
93
|
assertSceneMediaSrc,
|
|
94
94
|
bleedAwareExportBounds,
|
|
@@ -24,7 +24,7 @@ export interface HostedAgentConfig {
|
|
|
24
24
|
profile: AgentProfile;
|
|
25
25
|
/** Backend harness type, such as `opencode`; the runtime default when omitted. */
|
|
26
26
|
harness?: string;
|
|
27
|
-
/** The owner's
|
|
27
|
+
/** The owner's address on the chosen transport: E.164 phone or email. */
|
|
28
28
|
owner: string;
|
|
29
29
|
/** Texts Hub answers per person per UTC day. Default 20. */
|
|
30
30
|
freeTurnsPerDay?: number;
|
|
@@ -76,9 +76,12 @@ export declare class HostedAgentError extends Error {
|
|
|
76
76
|
}
|
|
77
77
|
export declare function createHostedAgent(config: HostedAgentConfig): {
|
|
78
78
|
/**
|
|
79
|
-
* Attach
|
|
80
|
-
*
|
|
81
|
-
*
|
|
79
|
+
* Attach a Hub connection the developer owns as this agent's line:
|
|
80
|
+
* an Inkbox iMessage identity (default), its email mailbox, or a Linq
|
|
81
|
+
* WhatsApp number (`phoneNumberId`). The owner's address must match the
|
|
82
|
+
* transport. In `shared` mode (default) the owner and anyone who texts it
|
|
83
|
+
* each get their own box and thread; `personal` admits the owner only.
|
|
84
|
+
* With `voice` (iMessage lines only), calls to the
|
|
82
85
|
* line reach the caller's box and thread through that ph0ny agent; Hub
|
|
83
86
|
* admits only members, so a caller texts once before calling. Safe to
|
|
84
87
|
* repeat with the same config. Hub refuses a changed profile, box or
|
|
@@ -89,6 +92,10 @@ export declare function createHostedAgent(config: HostedAgentConfig): {
|
|
|
89
92
|
* route would also answer.
|
|
90
93
|
*/
|
|
91
94
|
attachLine(connectionId: string, options?: {
|
|
95
|
+
transport?: 'imessage' | 'email' | 'whatsapp';
|
|
96
|
+
mode?: 'personal' | 'shared';
|
|
97
|
+
/** Required for WhatsApp because one Linq connection may own several numbers. */
|
|
98
|
+
phoneNumberId?: string;
|
|
92
99
|
voice?: LineVoiceOptions;
|
|
93
100
|
}): Promise<Line>;
|
|
94
101
|
};
|
|
@@ -27,6 +27,7 @@ function conversationProfile(profile) {
|
|
|
27
27
|
}
|
|
28
28
|
var PERSON = { context: "own", tools: "act" };
|
|
29
29
|
var E164 = /^\+[1-9]\d{6,14}$/;
|
|
30
|
+
var EMAIL = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
30
31
|
var HostedAgentError = class extends Error {
|
|
31
32
|
constructor(code, message) {
|
|
32
33
|
super(message);
|
|
@@ -36,7 +37,7 @@ var HostedAgentError = class extends Error {
|
|
|
36
37
|
code;
|
|
37
38
|
};
|
|
38
39
|
function createHostedAgent(config) {
|
|
39
|
-
if (!E164.test(config.owner)) throw new HostedAgentError("owner_not_e164", "owner must be an E.164 phone number
|
|
40
|
+
if (!E164.test(config.owner) && !EMAIL.test(config.owner)) throw new HostedAgentError("owner_not_e164", "owner must be an E.164 phone number or email address.");
|
|
40
41
|
const policy = { ...DEFAULT_BOX_POLICY, ...config.box };
|
|
41
42
|
const sandbox = new Sandbox({ apiKey: config.apiKey, baseUrl: config.sandboxUrl ?? "https://sandbox.tangle.tools", timeoutMs: 2e4 });
|
|
42
43
|
const backend = { ...config.harness ? { type: config.harness } : {}, profile: conversationProfile(config.profile) };
|
|
@@ -50,9 +51,12 @@ function createHostedAgent(config) {
|
|
|
50
51
|
};
|
|
51
52
|
return {
|
|
52
53
|
/**
|
|
53
|
-
* Attach
|
|
54
|
-
*
|
|
55
|
-
*
|
|
54
|
+
* Attach a Hub connection the developer owns as this agent's line:
|
|
55
|
+
* an Inkbox iMessage identity (default), its email mailbox, or a Linq
|
|
56
|
+
* WhatsApp number (`phoneNumberId`). The owner's address must match the
|
|
57
|
+
* transport. In `shared` mode (default) the owner and anyone who texts it
|
|
58
|
+
* each get their own box and thread; `personal` admits the owner only.
|
|
59
|
+
* With `voice` (iMessage lines only), calls to the
|
|
56
60
|
* line reach the caller's box and thread through that ph0ny agent; Hub
|
|
57
61
|
* admits only members, so a caller texts once before calling. Safe to
|
|
58
62
|
* repeat with the same config. Hub refuses a changed profile, box or
|
|
@@ -63,13 +67,27 @@ function createHostedAgent(config) {
|
|
|
63
67
|
* route would also answer.
|
|
64
68
|
*/
|
|
65
69
|
async attachLine(connectionId, options = {}) {
|
|
66
|
-
const
|
|
70
|
+
const transport = options.transport ?? "imessage";
|
|
71
|
+
const mode = options.mode ?? "shared";
|
|
72
|
+
if (transport === "email" && !EMAIL.test(config.owner))
|
|
73
|
+
throw new HostedAgentError("owner_transport_mismatch", "email lines require an email owner address.");
|
|
74
|
+
if (transport !== "email" && !E164.test(config.owner))
|
|
75
|
+
throw new HostedAgentError("owner_transport_mismatch", `${transport} lines require an E.164 owner address.`);
|
|
76
|
+
if (transport === "whatsapp" && !options.phoneNumberId)
|
|
77
|
+
throw new HostedAgentError("phone_number_required", "WhatsApp lines require phoneNumberId.");
|
|
78
|
+
if (transport !== "whatsapp" && options.phoneNumberId)
|
|
79
|
+
throw new HostedAgentError("phone_number_not_allowed", "phoneNumberId is only valid for WhatsApp lines.");
|
|
80
|
+
if (options.voice && transport !== "imessage")
|
|
81
|
+
throw new HostedAgentError("voice_transport_unsupported", "Voice is supported only on iMessage lines.");
|
|
82
|
+
const line = await sandbox.lines.fromConnection(
|
|
83
|
+
transport === "whatsapp" ? { connectionId, transport, phoneNumberId: options.phoneNumberId, clientReference: "hosted-agent" } : { connectionId, transport, clientReference: "hosted-agent" }
|
|
84
|
+
);
|
|
67
85
|
await sandbox.lines.attach({
|
|
68
86
|
number: line.id,
|
|
69
|
-
mode
|
|
87
|
+
mode,
|
|
70
88
|
members: [{ address: config.owner, role: "owner" }],
|
|
71
|
-
unknownSenders: "guest",
|
|
72
|
-
roles: { owner: PERSON, guest: PERSON },
|
|
89
|
+
unknownSenders: mode === "shared" ? "guest" : "reject",
|
|
90
|
+
roles: mode === "shared" ? { owner: PERSON, guest: PERSON } : { owner: PERSON },
|
|
73
91
|
respond: { kind: "agent", backend },
|
|
74
92
|
limits: { turnsPerMemberPerDay: config.freeTurnsPerDay ?? 20 },
|
|
75
93
|
instance: { keyPrefix: PERSON_KEY_PREFIX, create },
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/hosted-agent/index.ts"],"sourcesContent":["import type { AgentProfile, BackendConfig, LineVoiceOptions } from '@tangle-network/sandbox'\nimport { type Line, type LineInstanceCreate, Sandbox } from '@tangle-network/sandbox/core'\n\n/**\n * A hosted agent: people text or call a line, and each person is answered\n * from their own isolated sandbox. The developer's Tangle API key pays for\n * every box, model turn, reply and call.\n *\n * The platform does the work. {@link HostedAgent.attachLine} attaches the\n * line to Tangle Hub with one sandbox per person: Hub routes each text and\n * call to the person's box (the named instance {@link PERSON_KEY_PREFIX} plus\n * a hash of their number), keeps one thread per person, handles STOP and\n * START, counts each person's texts per day, and sends the reply. A call on\n * the line runs in the same box and thread, so text and voice share one memory.\n */\n\nexport interface HostedAgentConfig {\n /** The developer's Tangle API key. */\n apiKey: string\n /**\n * The persona every person's box runs. A profile without `model.default`\n * runs {@link DEFAULT_HOSTED_MODEL}, and a profile without `tools` runs with\n * {@link CONVERSATION_TOOLS_OFF} turned off. Set `tools` to choose your own,\n * for example on a harness that cannot turn those tools off.\n */\n profile: AgentProfile\n /** Backend harness type, such as `opencode`; the runtime default when omitted. */\n harness?: string\n /** The owner's own phone (E.164), the line's first member. */\n owner: string\n /** Texts Hub answers per person per UTC day. Default 20. */\n freeTurnsPerDay?: number\n box?: Partial<BoxPolicy>\n sandboxUrl?: string\n}\n\nexport interface BoxPolicy {\n cpuCores: number\n memoryMB: number\n diskGB: number\n idleTimeoutSeconds: number\n maxLifetimeSeconds: number\n deleteAfterStoppedSeconds: number\n /** Egress allow-list. The default reaches the model router only. */\n allowDomains: string[]\n}\n\n/**\n * Two cores and a 2 GB disk cost what one core and 10 GB cost: both bill the\n * platform's hourly floor. A person's box starts OpenCode on their first text\n * and after every idle stop, and that start is CPU-bound: on one core it took\n * 6.1 s after a resume and 8.4 s on a new box, on two cores 3.5 s and 3.4 s\n * (production, 2026-09-24). A disk no larger than the platform's warm seed\n * lets a new person's box be claimed from the warm pool: create took 2.1-2.7 s\n * instead of 5.3-7.0 s (2026-09-25).\n */\nexport const DEFAULT_BOX_POLICY: BoxPolicy = {\n cpuCores: 2, memoryMB: 2048, diskGB: 2,\n idleTimeoutSeconds: 600, maxLifetimeSeconds: 86_400, deleteAfterStoppedSeconds: 7 * 86_400,\n allowDomains: ['router.tangle.tools'],\n}\n\n/**\n * Each person's box is the developer's named instance with this prefix. It is\n * the key this kit used before Hub routed its texts, so every existing\n * person keeps their box.\n */\nexport const PERSON_KEY_PREFIX = 'hosted:'\n\n/**\n * Harness tools a texting or calling assistant does not use. Their\n * descriptions present every turn as coding work: a shell, file search,\n * sub-agents, to-do lists, skills and web fetch. File read, write and edit\n * stay, so a persona can keep notes such as `memory.md`.\n */\nexport const CONVERSATION_TOOLS_OFF = ['bash', 'glob', 'grep', 'task', 'todowrite', 'webfetch', 'skill'] as const\n\n/**\n * The model for a profile without `model.default`. It gave the most useful\n * on-topic replies among four Router models on the same five texts and calls\n * (2026-09-23), within the latency of the others.\n */\nexport const DEFAULT_HOSTED_MODEL = 'openai/gpt-5.6-luna'\n\n/** The profile a person's box runs: the developer's profile over the conversation defaults. */\nfunction conversationProfile(profile: AgentProfile): AgentProfile {\n return {\n ...profile,\n model: { ...profile.model, default: profile.model?.default ?? DEFAULT_HOSTED_MODEL },\n // A profile that sets `tools` owns its tool set.\n ...(profile.tools ? {} : {\n tools: Object.fromEntries(CONVERSATION_TOOLS_OFF.map(tool => [tool, false])),\n // The sandbox's preview policy grants the shell unless its permission\n // is denied, so turning the tool off alone leaves the shell in place.\n permissions: { bash: 'deny' as const, ...profile.permissions },\n }),\n }\n}\n\n/** Each person runs in their own box, so no member shares a disk and each may use the persona's tools. */\nconst PERSON = { context: 'own', tools: 'act' } as const\nconst E164 = /^\\+[1-9]\\d{6,14}$/\n\nexport class HostedAgentError extends Error {\n constructor(readonly code: string, message: string) { super(message); this.name = 'HostedAgentError' }\n}\n\nexport function createHostedAgent(config: HostedAgentConfig) {\n if (!E164.test(config.owner)) throw new HostedAgentError('owner_not_e164', 'owner must be an E.164 phone number, such as +15550100001.')\n const policy = { ...DEFAULT_BOX_POLICY, ...config.box }\n const sandbox = new Sandbox({ apiKey: config.apiKey, baseUrl: config.sandboxUrl ?? 'https://sandbox.tangle.tools', timeoutMs: 20_000 })\n const backend: BackendConfig = { ...(config.harness ? { type: config.harness as BackendConfig['type'] } : {}), profile: conversationProfile(config.profile) }\n const create: LineInstanceCreate = {\n name: 'hosted-person',\n resources: { cpuCores: policy.cpuCores, memoryMB: policy.memoryMB, diskGB: policy.diskGB },\n egressPolicy: { mode: 'strict', allowDomains: policy.allowDomains, includeImplicitDomains: false },\n idleTimeoutSeconds: policy.idleTimeoutSeconds, maxLifetimeSeconds: policy.maxLifetimeSeconds,\n deleteAfterStoppedSeconds: policy.deleteAfterStoppedSeconds,\n }\n\n return {\n /**\n * Attach an Inkbox iMessage identity, connected to Hub under the\n * developer's account, as this agent's line: the owner and anyone who\n * texts it each get their own box and thread. With `voice`, calls to the\n * line reach the caller's box and thread through that ph0ny agent; Hub\n * admits only members, so a caller texts once before calling. Safe to\n * repeat with the same config. Hub refuses a changed profile, box or\n * limit on an attached line: detach it first\n * (`DELETE /v1/lines/:id/attachment`). Each person keeps their box and\n * its memory, and their thread starts over. Remove any Hub event\n * subscription on the connection first; Hub refuses a line that another\n * route would also answer.\n */\n async attachLine(connectionId: string, options: { voice?: LineVoiceOptions } = {}): Promise<Line> {\n const line = await sandbox.lines.fromConnection({ connectionId, transport: 'imessage', clientReference: 'hosted-agent' })\n await sandbox.lines.attach({\n number: line.id,\n mode: 'shared',\n members: [{ address: config.owner, role: 'owner' }],\n unknownSenders: 'guest',\n roles: { owner: PERSON, guest: PERSON },\n respond: { kind: 'agent', backend },\n limits: { turnsPerMemberPerDay: config.freeTurnsPerDay ?? 20 },\n instance: { keyPrefix: PERSON_KEY_PREFIX, create },\n clientReference: 'hosted-agent',\n })\n if (options.voice) await sandbox.lines.enableVoice(line.id, options.voice)\n return sandbox.lines.get(line.id)\n },\n }\n}\n\nexport type HostedAgent = ReturnType<typeof createHostedAgent>\n"],"mappings":";AACA,SAA6C,eAAe;AAuDrD,IAAM,qBAAgC;AAAA,EAC3C,UAAU;AAAA,EAAG,UAAU;AAAA,EAAM,QAAQ;AAAA,EACrC,oBAAoB;AAAA,EAAK,oBAAoB;AAAA,EAAQ,2BAA2B,IAAI;AAAA,EACpF,cAAc,CAAC,qBAAqB;AACtC;AAOO,IAAM,oBAAoB;AAQ1B,IAAM,yBAAyB,CAAC,QAAQ,QAAQ,QAAQ,QAAQ,aAAa,YAAY,OAAO;AAOhG,IAAM,uBAAuB;AAGpC,SAAS,oBAAoB,SAAqC;AAChE,SAAO;AAAA,IACL,GAAG;AAAA,IACH,OAAO,EAAE,GAAG,QAAQ,OAAO,SAAS,QAAQ,OAAO,WAAW,qBAAqB;AAAA;AAAA,IAEnF,GAAI,QAAQ,QAAQ,CAAC,IAAI;AAAA,MACvB,OAAO,OAAO,YAAY,uBAAuB,IAAI,UAAQ,CAAC,MAAM,KAAK,CAAC,CAAC;AAAA;AAAA;AAAA,MAG3E,aAAa,EAAE,MAAM,QAAiB,GAAG,QAAQ,YAAY;AAAA,IAC/D;AAAA,EACF;AACF;AAGA,IAAM,SAAS,EAAE,SAAS,OAAO,OAAO,MAAM;AAC9C,IAAM,OAAO;AAEN,IAAM,mBAAN,cAA+B,MAAM;AAAA,EAC1C,YAAqB,MAAc,SAAiB;AAAE,UAAM,OAAO;AAA9C;AAAiD,SAAK,OAAO;AAAA,EAAmB;AAAA,EAAhF;AACvB;AAEO,SAAS,kBAAkB,QAA2B;AAC3D,MAAI,CAAC,KAAK,KAAK,OAAO,KAAK,EAAG,OAAM,IAAI,iBAAiB,kBAAkB,4DAA4D;AACvI,QAAM,SAAS,EAAE,GAAG,oBAAoB,GAAG,OAAO,IAAI;AACtD,QAAM,UAAU,IAAI,QAAQ,EAAE,QAAQ,OAAO,QAAQ,SAAS,OAAO,cAAc,gCAAgC,WAAW,IAAO,CAAC;AACtI,QAAM,UAAyB,EAAE,GAAI,OAAO,UAAU,EAAE,MAAM,OAAO,QAAiC,IAAI,CAAC,GAAI,SAAS,oBAAoB,OAAO,OAAO,EAAE;AAC5J,QAAM,SAA6B;AAAA,IACjC,MAAM;AAAA,IACN,WAAW,EAAE,UAAU,OAAO,UAAU,UAAU,OAAO,UAAU,QAAQ,OAAO,OAAO;AAAA,IACzF,cAAc,EAAE,MAAM,UAAU,cAAc,OAAO,cAAc,wBAAwB,MAAM;AAAA,IACjG,oBAAoB,OAAO;AAAA,IAAoB,oBAAoB,OAAO;AAAA,IAC1E,2BAA2B,OAAO;AAAA,EACpC;AAEA,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAcL,MAAM,WAAW,cAAsB,UAAwC,CAAC,GAAkB;AAChG,YAAM,OAAO,MAAM,QAAQ,MAAM,eAAe,EAAE,cAAc,WAAW,YAAY,iBAAiB,eAAe,CAAC;AACxH,YAAM,QAAQ,MAAM,OAAO;AAAA,QACzB,QAAQ,KAAK;AAAA,QACb,MAAM;AAAA,QACN,SAAS,CAAC,EAAE,SAAS,OAAO,OAAO,MAAM,QAAQ,CAAC;AAAA,QAClD,gBAAgB;AAAA,QAChB,OAAO,EAAE,OAAO,QAAQ,OAAO,OAAO;AAAA,QACtC,SAAS,EAAE,MAAM,SAAS,QAAQ;AAAA,QAClC,QAAQ,EAAE,sBAAsB,OAAO,mBAAmB,GAAG;AAAA,QAC7D,UAAU,EAAE,WAAW,mBAAmB,OAAO;AAAA,QACjD,iBAAiB;AAAA,MACnB,CAAC;AACD,UAAI,QAAQ,MAAO,OAAM,QAAQ,MAAM,YAAY,KAAK,IAAI,QAAQ,KAAK;AACzE,aAAO,QAAQ,MAAM,IAAI,KAAK,EAAE;AAAA,IAClC;AAAA,EACF;AACF;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/hosted-agent/index.ts"],"sourcesContent":["import type { AgentProfile, BackendConfig, LineVoiceOptions } from '@tangle-network/sandbox'\nimport { type Line, type LineInstanceCreate, Sandbox } from '@tangle-network/sandbox/core'\n\n/**\n * A hosted agent: people text or call a line, and each person is answered\n * from their own isolated sandbox. The developer's Tangle API key pays for\n * every box, model turn, reply and call.\n *\n * The platform does the work. {@link HostedAgent.attachLine} attaches the\n * line to Tangle Hub with one sandbox per person: Hub routes each text and\n * call to the person's box (the named instance {@link PERSON_KEY_PREFIX} plus\n * a hash of their number), keeps one thread per person, handles STOP and\n * START, counts each person's texts per day, and sends the reply. A call on\n * the line runs in the same box and thread, so text and voice share one memory.\n */\n\nexport interface HostedAgentConfig {\n /** The developer's Tangle API key. */\n apiKey: string\n /**\n * The persona every person's box runs. A profile without `model.default`\n * runs {@link DEFAULT_HOSTED_MODEL}, and a profile without `tools` runs with\n * {@link CONVERSATION_TOOLS_OFF} turned off. Set `tools` to choose your own,\n * for example on a harness that cannot turn those tools off.\n */\n profile: AgentProfile\n /** Backend harness type, such as `opencode`; the runtime default when omitted. */\n harness?: string\n /** The owner's address on the chosen transport: E.164 phone or email. */\n owner: string\n /** Texts Hub answers per person per UTC day. Default 20. */\n freeTurnsPerDay?: number\n box?: Partial<BoxPolicy>\n sandboxUrl?: string\n}\n\nexport interface BoxPolicy {\n cpuCores: number\n memoryMB: number\n diskGB: number\n idleTimeoutSeconds: number\n maxLifetimeSeconds: number\n deleteAfterStoppedSeconds: number\n /** Egress allow-list. The default reaches the model router only. */\n allowDomains: string[]\n}\n\n/**\n * Two cores and a 2 GB disk cost what one core and 10 GB cost: both bill the\n * platform's hourly floor. A person's box starts OpenCode on their first text\n * and after every idle stop, and that start is CPU-bound: on one core it took\n * 6.1 s after a resume and 8.4 s on a new box, on two cores 3.5 s and 3.4 s\n * (production, 2026-09-24). A disk no larger than the platform's warm seed\n * lets a new person's box be claimed from the warm pool: create took 2.1-2.7 s\n * instead of 5.3-7.0 s (2026-09-25).\n */\nexport const DEFAULT_BOX_POLICY: BoxPolicy = {\n cpuCores: 2, memoryMB: 2048, diskGB: 2,\n idleTimeoutSeconds: 600, maxLifetimeSeconds: 86_400, deleteAfterStoppedSeconds: 7 * 86_400,\n allowDomains: ['router.tangle.tools'],\n}\n\n/**\n * Each person's box is the developer's named instance with this prefix. It is\n * the key this kit used before Hub routed its texts, so every existing\n * person keeps their box.\n */\nexport const PERSON_KEY_PREFIX = 'hosted:'\n\n/**\n * Harness tools a texting or calling assistant does not use. Their\n * descriptions present every turn as coding work: a shell, file search,\n * sub-agents, to-do lists, skills and web fetch. File read, write and edit\n * stay, so a persona can keep notes such as `memory.md`.\n */\nexport const CONVERSATION_TOOLS_OFF = ['bash', 'glob', 'grep', 'task', 'todowrite', 'webfetch', 'skill'] as const\n\n/**\n * The model for a profile without `model.default`. It gave the most useful\n * on-topic replies among four Router models on the same five texts and calls\n * (2026-09-23), within the latency of the others.\n */\nexport const DEFAULT_HOSTED_MODEL = 'openai/gpt-5.6-luna'\n\n/** The profile a person's box runs: the developer's profile over the conversation defaults. */\nfunction conversationProfile(profile: AgentProfile): AgentProfile {\n return {\n ...profile,\n model: { ...profile.model, default: profile.model?.default ?? DEFAULT_HOSTED_MODEL },\n // A profile that sets `tools` owns its tool set.\n ...(profile.tools ? {} : {\n tools: Object.fromEntries(CONVERSATION_TOOLS_OFF.map(tool => [tool, false])),\n // The sandbox's preview policy grants the shell unless its permission\n // is denied, so turning the tool off alone leaves the shell in place.\n permissions: { bash: 'deny' as const, ...profile.permissions },\n }),\n }\n}\n\n/** Each person runs in their own box, so no member shares a disk and each may use the persona's tools. */\nconst PERSON = { context: 'own', tools: 'act' } as const\nconst E164 = /^\\+[1-9]\\d{6,14}$/\nconst EMAIL = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/\n\nexport class HostedAgentError extends Error {\n constructor(readonly code: string, message: string) { super(message); this.name = 'HostedAgentError' }\n}\n\nexport function createHostedAgent(config: HostedAgentConfig) {\n if (!E164.test(config.owner) && !EMAIL.test(config.owner)) throw new HostedAgentError('owner_not_e164', 'owner must be an E.164 phone number or email address.')\n const policy = { ...DEFAULT_BOX_POLICY, ...config.box }\n const sandbox = new Sandbox({ apiKey: config.apiKey, baseUrl: config.sandboxUrl ?? 'https://sandbox.tangle.tools', timeoutMs: 20_000 })\n const backend: BackendConfig = { ...(config.harness ? { type: config.harness as BackendConfig['type'] } : {}), profile: conversationProfile(config.profile) }\n const create: LineInstanceCreate = {\n name: 'hosted-person',\n resources: { cpuCores: policy.cpuCores, memoryMB: policy.memoryMB, diskGB: policy.diskGB },\n egressPolicy: { mode: 'strict', allowDomains: policy.allowDomains, includeImplicitDomains: false },\n idleTimeoutSeconds: policy.idleTimeoutSeconds, maxLifetimeSeconds: policy.maxLifetimeSeconds,\n deleteAfterStoppedSeconds: policy.deleteAfterStoppedSeconds,\n }\n\n return {\n /**\n * Attach a Hub connection the developer owns as this agent's line:\n * an Inkbox iMessage identity (default), its email mailbox, or a Linq\n * WhatsApp number (`phoneNumberId`). The owner's address must match the\n * transport. In `shared` mode (default) the owner and anyone who texts it\n * each get their own box and thread; `personal` admits the owner only.\n * With `voice` (iMessage lines only), calls to the\n * line reach the caller's box and thread through that ph0ny agent; Hub\n * admits only members, so a caller texts once before calling. Safe to\n * repeat with the same config. Hub refuses a changed profile, box or\n * limit on an attached line: detach it first\n * (`DELETE /v1/lines/:id/attachment`). Each person keeps their box and\n * its memory, and their thread starts over. Remove any Hub event\n * subscription on the connection first; Hub refuses a line that another\n * route would also answer.\n */\n async attachLine(connectionId: string, options: {\n transport?: 'imessage' | 'email' | 'whatsapp'\n mode?: 'personal' | 'shared'\n /** Required for WhatsApp because one Linq connection may own several numbers. */\n phoneNumberId?: string\n voice?: LineVoiceOptions\n } = {}): Promise<Line> {\n const transport = options.transport ?? 'imessage'\n const mode = options.mode ?? 'shared'\n if (transport === 'email' && !EMAIL.test(config.owner))\n throw new HostedAgentError('owner_transport_mismatch', 'email lines require an email owner address.')\n if (transport !== 'email' && !E164.test(config.owner))\n throw new HostedAgentError('owner_transport_mismatch', `${transport} lines require an E.164 owner address.`)\n if (transport === 'whatsapp' && !options.phoneNumberId)\n throw new HostedAgentError('phone_number_required', 'WhatsApp lines require phoneNumberId.')\n if (transport !== 'whatsapp' && options.phoneNumberId)\n throw new HostedAgentError('phone_number_not_allowed', 'phoneNumberId is only valid for WhatsApp lines.')\n if (options.voice && transport !== 'imessage')\n throw new HostedAgentError('voice_transport_unsupported', 'Voice is supported only on iMessage lines.')\n const line = await sandbox.lines.fromConnection(\n transport === 'whatsapp'\n ? { connectionId, transport, phoneNumberId: options.phoneNumberId!, clientReference: 'hosted-agent' }\n : { connectionId, transport, clientReference: 'hosted-agent' },\n )\n await sandbox.lines.attach({\n number: line.id,\n mode,\n members: [{ address: config.owner, role: 'owner' }],\n unknownSenders: mode === 'shared' ? 'guest' : 'reject',\n roles: mode === 'shared' ? { owner: PERSON, guest: PERSON } : { owner: PERSON },\n respond: { kind: 'agent', backend },\n limits: { turnsPerMemberPerDay: config.freeTurnsPerDay ?? 20 },\n instance: { keyPrefix: PERSON_KEY_PREFIX, create },\n clientReference: 'hosted-agent',\n })\n if (options.voice) await sandbox.lines.enableVoice(line.id, options.voice)\n return sandbox.lines.get(line.id)\n },\n }\n}\n\nexport type HostedAgent = ReturnType<typeof createHostedAgent>\n"],"mappings":";AACA,SAA6C,eAAe;AAuDrD,IAAM,qBAAgC;AAAA,EAC3C,UAAU;AAAA,EAAG,UAAU;AAAA,EAAM,QAAQ;AAAA,EACrC,oBAAoB;AAAA,EAAK,oBAAoB;AAAA,EAAQ,2BAA2B,IAAI;AAAA,EACpF,cAAc,CAAC,qBAAqB;AACtC;AAOO,IAAM,oBAAoB;AAQ1B,IAAM,yBAAyB,CAAC,QAAQ,QAAQ,QAAQ,QAAQ,aAAa,YAAY,OAAO;AAOhG,IAAM,uBAAuB;AAGpC,SAAS,oBAAoB,SAAqC;AAChE,SAAO;AAAA,IACL,GAAG;AAAA,IACH,OAAO,EAAE,GAAG,QAAQ,OAAO,SAAS,QAAQ,OAAO,WAAW,qBAAqB;AAAA;AAAA,IAEnF,GAAI,QAAQ,QAAQ,CAAC,IAAI;AAAA,MACvB,OAAO,OAAO,YAAY,uBAAuB,IAAI,UAAQ,CAAC,MAAM,KAAK,CAAC,CAAC;AAAA;AAAA;AAAA,MAG3E,aAAa,EAAE,MAAM,QAAiB,GAAG,QAAQ,YAAY;AAAA,IAC/D;AAAA,EACF;AACF;AAGA,IAAM,SAAS,EAAE,SAAS,OAAO,OAAO,MAAM;AAC9C,IAAM,OAAO;AACb,IAAM,QAAQ;AAEP,IAAM,mBAAN,cAA+B,MAAM;AAAA,EAC1C,YAAqB,MAAc,SAAiB;AAAE,UAAM,OAAO;AAA9C;AAAiD,SAAK,OAAO;AAAA,EAAmB;AAAA,EAAhF;AACvB;AAEO,SAAS,kBAAkB,QAA2B;AAC3D,MAAI,CAAC,KAAK,KAAK,OAAO,KAAK,KAAK,CAAC,MAAM,KAAK,OAAO,KAAK,EAAG,OAAM,IAAI,iBAAiB,kBAAkB,uDAAuD;AAC/J,QAAM,SAAS,EAAE,GAAG,oBAAoB,GAAG,OAAO,IAAI;AACtD,QAAM,UAAU,IAAI,QAAQ,EAAE,QAAQ,OAAO,QAAQ,SAAS,OAAO,cAAc,gCAAgC,WAAW,IAAO,CAAC;AACtI,QAAM,UAAyB,EAAE,GAAI,OAAO,UAAU,EAAE,MAAM,OAAO,QAAiC,IAAI,CAAC,GAAI,SAAS,oBAAoB,OAAO,OAAO,EAAE;AAC5J,QAAM,SAA6B;AAAA,IACjC,MAAM;AAAA,IACN,WAAW,EAAE,UAAU,OAAO,UAAU,UAAU,OAAO,UAAU,QAAQ,OAAO,OAAO;AAAA,IACzF,cAAc,EAAE,MAAM,UAAU,cAAc,OAAO,cAAc,wBAAwB,MAAM;AAAA,IACjG,oBAAoB,OAAO;AAAA,IAAoB,oBAAoB,OAAO;AAAA,IAC1E,2BAA2B,OAAO;AAAA,EACpC;AAEA,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAiBL,MAAM,WAAW,cAAsB,UAMnC,CAAC,GAAkB;AACrB,YAAM,YAAY,QAAQ,aAAa;AACvC,YAAM,OAAO,QAAQ,QAAQ;AAC7B,UAAI,cAAc,WAAW,CAAC,MAAM,KAAK,OAAO,KAAK;AACnD,cAAM,IAAI,iBAAiB,4BAA4B,6CAA6C;AACtG,UAAI,cAAc,WAAW,CAAC,KAAK,KAAK,OAAO,KAAK;AAClD,cAAM,IAAI,iBAAiB,4BAA4B,GAAG,SAAS,wCAAwC;AAC7G,UAAI,cAAc,cAAc,CAAC,QAAQ;AACvC,cAAM,IAAI,iBAAiB,yBAAyB,uCAAuC;AAC7F,UAAI,cAAc,cAAc,QAAQ;AACtC,cAAM,IAAI,iBAAiB,4BAA4B,iDAAiD;AAC1G,UAAI,QAAQ,SAAS,cAAc;AACjC,cAAM,IAAI,iBAAiB,+BAA+B,4CAA4C;AACxG,YAAM,OAAO,MAAM,QAAQ,MAAM;AAAA,QAC/B,cAAc,aACV,EAAE,cAAc,WAAW,eAAe,QAAQ,eAAgB,iBAAiB,eAAe,IAClG,EAAE,cAAc,WAAW,iBAAiB,eAAe;AAAA,MACjE;AACA,YAAM,QAAQ,MAAM,OAAO;AAAA,QACzB,QAAQ,KAAK;AAAA,QACb;AAAA,QACA,SAAS,CAAC,EAAE,SAAS,OAAO,OAAO,MAAM,QAAQ,CAAC;AAAA,QAClD,gBAAgB,SAAS,WAAW,UAAU;AAAA,QAC9C,OAAO,SAAS,WAAW,EAAE,OAAO,QAAQ,OAAO,OAAO,IAAI,EAAE,OAAO,OAAO;AAAA,QAC9E,SAAS,EAAE,MAAM,SAAS,QAAQ;AAAA,QAClC,QAAQ,EAAE,sBAAsB,OAAO,mBAAmB,GAAG;AAAA,QAC7D,UAAU,EAAE,WAAW,mBAAmB,OAAO;AAAA,QACjD,iBAAiB;AAAA,MACnB,CAAC;AACD,UAAI,QAAQ,MAAO,OAAM,QAAQ,MAAM,YAAY,KAAK,IAAI,QAAQ,KAAK;AACzE,aAAO,QAAQ,MAAM,IAAI,KAAK,EAAE;AAAA,IAClC;AAAA,EACF;AACF;","names":[]}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ChannelsClient, ChannelVerification, Line, LineAttachment, LineMessage, LineThread, HubNumberOrder, LinePayment } from '../../channels/types';
|
|
2
|
+
export declare function channelLine(overrides?: Partial<Line>): Line;
|
|
3
|
+
export declare function channelAttachment(): LineAttachment;
|
|
4
|
+
export declare function channelTest(status?: ChannelVerification['status']): ChannelVerification;
|
|
5
|
+
export declare function channelMessage(id?: string, overrides?: Partial<LineMessage>): LineMessage;
|
|
6
|
+
export declare function numberOrder(overrides?: Partial<HubNumberOrder>): HubNumberOrder;
|
|
7
|
+
/** Deterministic, in-memory SDK-shaped fixture. Never connects to a provider. */
|
|
8
|
+
export declare function createChannelsFixture(): {
|
|
9
|
+
client: ChannelsClient;
|
|
10
|
+
state: {
|
|
11
|
+
line: Line;
|
|
12
|
+
test: ChannelVerification | null;
|
|
13
|
+
messages: LineMessage[];
|
|
14
|
+
orders: HubNumberOrder[];
|
|
15
|
+
payment: LinePayment;
|
|
16
|
+
};
|
|
17
|
+
thread: LineThread;
|
|
18
|
+
};
|
package/dist/web-react/index.js
CHANGED
|
@@ -128,7 +128,7 @@ import {
|
|
|
128
128
|
withoutRecordGridCreated,
|
|
129
129
|
withoutRecordGridRemoved,
|
|
130
130
|
withoutRecordGridUpdate
|
|
131
|
-
} from "../chunk-
|
|
131
|
+
} from "../chunk-VIL6MHHQ.js";
|
|
132
132
|
import {
|
|
133
133
|
SessionHistoryPanel,
|
|
134
134
|
formatSessionTimestamp,
|
|
@@ -139,7 +139,10 @@ import {
|
|
|
139
139
|
import {
|
|
140
140
|
MENTION_PILL_CLASS
|
|
141
141
|
} from "../chunk-3HUFJ5LO.js";
|
|
142
|
-
import
|
|
142
|
+
import {
|
|
143
|
+
tabTerminalConnectionId,
|
|
144
|
+
useSandboxTerminalConnection
|
|
145
|
+
} from "../chunk-BATKJP3P.js";
|
|
143
146
|
import "../chunk-FBVLEGEG.js";
|
|
144
147
|
import {
|
|
145
148
|
EvidenceLineageTable,
|
|
@@ -191,10 +194,7 @@ import {
|
|
|
191
194
|
usePopover
|
|
192
195
|
} from "../chunk-4I76LTZS.js";
|
|
193
196
|
import "../chunk-SJWIZT7B.js";
|
|
194
|
-
import
|
|
195
|
-
tabTerminalConnectionId,
|
|
196
|
-
useSandboxTerminalConnection
|
|
197
|
-
} from "../chunk-BATKJP3P.js";
|
|
197
|
+
import "../chunk-3UBAO3N5.js";
|
|
198
198
|
import "../chunk-FQYAJYPW.js";
|
|
199
199
|
import {
|
|
200
200
|
ATTACHMENT_ACCEPT
|