@finalai/final-agent-provider 0.11.0 → 0.13.0
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/README.md +1 -14
- package/dist/modules/integrations.d.mts +0 -1
- package/dist/modules/integrations.mjs +0 -2
- package/dist/modules/integrations.mjs.map +2 -2
- package/dist/modules/sandboxes/computer.d.mts +2 -4
- package/dist/modules/sandboxes/computer.mjs +110 -480
- package/dist/modules/sandboxes/computer.mjs.map +2 -2
- package/dist/modules/sandboxes/computers-api.d.mts +6 -1
- package/dist/modules/sandboxes/computers-api.mjs.map +2 -2
- package/dist/modules/sandboxes/device-api.d.mts +8 -48
- package/dist/modules/sandboxes/device-api.mjs +2 -92
- package/dist/modules/sandboxes/device-api.mjs.map +2 -2
- package/dist/modules/sandboxes/device.d.mts +1 -1
- package/dist/modules/sandboxes/device.mjs.map +2 -2
- package/dist/modules/sandboxes/environment.d.mts +4 -0
- package/dist/modules/sandboxes/environment.mjs.map +2 -2
- package/dist/modules/sandboxes/local.d.mts +2 -1
- package/dist/modules/sandboxes/local.mjs +64 -8
- package/dist/modules/sandboxes/local.mjs.map +2 -2
- package/dist/modules/sandboxes/session.mjs +0 -1
- package/dist/modules/sandboxes/session.mjs.map +2 -2
- package/package.json +5 -57
- package/WORKSPACE-EGRESS.md +0 -184
- package/dist/modules/authoring-final-channel.d.mts +0 -3
- package/dist/modules/authoring-final-channel.mjs +0 -10
- package/dist/modules/authoring-final-channel.mjs.map +0 -7
- package/dist/modules/channel-factories.d.mts +0 -40
- package/dist/modules/channel-factories.mjs +0 -212
- package/dist/modules/channel-factories.mjs.map +0 -7
- package/dist/modules/channels/content.d.mts +0 -6
- package/dist/modules/channels/content.mjs +0 -31
- package/dist/modules/channels/content.mjs.map +0 -7
- package/dist/modules/channels/delivery.d.mts +0 -54
- package/dist/modules/channels/delivery.mjs +0 -243
- package/dist/modules/channels/delivery.mjs.map +0 -7
- package/dist/modules/channels/direct-messaging.d.mts +0 -5
- package/dist/modules/channels/direct-messaging.mjs +0 -8
- package/dist/modules/channels/direct-messaging.mjs.map +0 -7
- package/dist/modules/channels/fake-platform.d.mts +0 -36
- package/dist/modules/channels/fake-platform.mjs +0 -168
- package/dist/modules/channels/fake-platform.mjs.map +0 -7
- package/dist/modules/channels/group-messaging.d.mts +0 -5
- package/dist/modules/channels/group-messaging.mjs +0 -8
- package/dist/modules/channels/group-messaging.mjs.map +0 -7
- package/dist/modules/channels/inbound.d.mts +0 -52
- package/dist/modules/channels/inbound.mjs +0 -42
- package/dist/modules/channels/inbound.mjs.map +0 -7
- package/dist/modules/channels/index.d.mts +0 -9
- package/dist/modules/channels/index.mjs +0 -10
- package/dist/modules/channels/index.mjs.map +0 -7
- package/dist/modules/channels/messaging-channel.d.mts +0 -7
- package/dist/modules/channels/messaging-channel.mjs +0 -99
- package/dist/modules/channels/messaging-channel.mjs.map +0 -7
- package/dist/modules/channels/projection.d.mts +0 -36
- package/dist/modules/channels/projection.mjs +0 -233
- package/dist/modules/channels/projection.mjs.map +0 -7
- package/dist/modules/channels/prompt.d.mts +0 -72
- package/dist/modules/channels/prompt.mjs +0 -208
- package/dist/modules/channels/prompt.mjs.map +0 -7
- package/dist/modules/channels/routing.d.mts +0 -5
- package/dist/modules/channels/routing.mjs +0 -53
- package/dist/modules/channels/routing.mjs.map +0 -7
- package/dist/modules/sandboxes/access.d.mts +0 -34
- package/dist/modules/sandboxes/access.mjs +0 -15
- package/dist/modules/sandboxes/access.mjs.map +0 -7
- package/dist/modules/sandboxes/egress-bootstrap.d.mts +0 -3
- package/dist/modules/sandboxes/egress-bootstrap.mjs +0 -929
- package/dist/modules/sandboxes/egress-bootstrap.mjs.map +0 -7
- package/dist/modules/sandboxes/egress.d.mts +0 -94
- package/dist/modules/sandboxes/egress.mjs +0 -1561
- package/dist/modules/sandboxes/egress.mjs.map +0 -7
package/WORKSPACE-EGRESS.md
DELETED
|
@@ -1,184 +0,0 @@
|
|
|
1
|
-
# Workspace Device egress integration
|
|
2
|
-
|
|
3
|
-
`/sandboxes/egress` provides a provider-neutral TCP/UDP/DNS exit through an approved Final Device. IPv6 application egress is blocked; kernel and root-owned neighbor discovery remain available; link neighbor discovery remains available for inbound provider control. Selecting a Device intentionally permits access to its reachable LAN and services addressed through its tailnet IP, under its owner/process permission. Grants exclude other workspaces and unapproved Devices. Accepted provider control and preview TCP replies retain their physical return path; this capability routes outbound workspace connections.
|
|
4
|
-
|
|
5
|
-
## Host composition and identity
|
|
6
|
-
|
|
7
|
-
Final Device egress is an opinionated implementation of the generic
|
|
8
|
-
`SandboxNetworkingExtensionIface`, not a requirement on sandbox implementations or
|
|
9
|
-
a public replacement-Device protocol. Compose it with a provider through the same
|
|
10
|
-
`networking` option used by other networking implementations:
|
|
11
|
-
|
|
12
|
-
```ts
|
|
13
|
-
const networking = createWorkspaceDeviceNetworking({ api: deviceApi, needed });
|
|
14
|
-
const computers = createComputerSandboxProvider({ computersApi, networking });
|
|
15
|
-
const daytona = createDaytonaSandboxProvider({ networking });
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
The factories are exported from `/sandboxes/egress`, `/sandboxes/computer`, and
|
|
19
|
-
`@finalai/daytona-agent-provider/sandboxes/daytona`, respectively. Keep the trusted
|
|
20
|
-
Device access through the extension's configured `access`. A custom provider accepts
|
|
21
|
-
the same generic extension: call `enabled`, validate its workspace identity, call
|
|
22
|
-
`prepare` before provider fencing, and use `attachSandboxNetworking` after
|
|
23
|
-
obtaining its raw handle. Supply the saved policy, restore/fence callbacks,
|
|
24
|
-
namespace existence check and deferred setup. Physical teardown calls `suspend`
|
|
25
|
-
even if another cleanup step fails. Providers needing earlier namespace setup can
|
|
26
|
-
use `controller` and `withSandboxNetworking` directly, as Final Computer does.
|
|
27
|
-
|
|
28
|
-
The extension's configured `access({ organizationId, projectId, actingUserId })`
|
|
29
|
-
returns the Device API origin and a token. Device
|
|
30
|
-
operations use the current actor; background lease reconciliation may use the stored
|
|
31
|
-
lease owner instead of a present turn author. Hosts must authorize both forms of work.
|
|
32
|
-
Admission, policy persistence, and cleanup also make trusted service calls with
|
|
33
|
-
`actingUserId: null`. Enabling workspace Device networking requires access
|
|
34
|
-
accepted by those service endpoints for that case, separate from user-bound grants.
|
|
35
|
-
Returning `null` refuses the call before HTTP; a user-token-only configuration cannot
|
|
36
|
-
start or clean up networked workspaces. Its host must add service authentication
|
|
37
|
-
before enabling this extension. The SDK never reuses a user's cached grant for a
|
|
38
|
-
service call or another user: it asks the configured access on every call.
|
|
39
|
-
|
|
40
|
-
The extension owns Device authorization, enrollment, helper installation, leases
|
|
41
|
-
and failover. Providers need no Device networking implementation. Its generic
|
|
42
|
-
controller also authorizes external editor access and reports its expiry limit;
|
|
43
|
-
providers must bound editor grants by that deadline. The Device executable and
|
|
44
|
-
hosted control services remain separate first-party implementations.
|
|
45
|
-
|
|
46
|
-
The public Computers API accepts an optional browser credential `expiresAt` bound.
|
|
47
|
-
Managed editors request that bound and refuse a backend response that exceeds it;
|
|
48
|
-
an older API rejecting the field cannot silently grant longer access.
|
|
49
|
-
|
|
50
|
-
1. Assign one stable host-owned identity per logical workspace and persist whether networking is managed. Supply that identity to start, resume, editors and teardown.
|
|
51
|
-
2. Save the actual direct policy before a boot fence. Prefer a provider fence; explicitly unsupported providers use the logged startup-window fallback.
|
|
52
|
-
3. Construct the manager with the raw session and privileged executor, then declare its policy on the provider handle with the connection's grant and current-actor reader.
|
|
53
|
-
4. Detach on runtime shutdown. On physical stop/deletion, attempt fencing, lease cleanup and provider cleanup independently; retain saved desired settings for recreation.
|
|
54
|
-
|
|
55
|
-
Inject the Device API URL, trusted Device credential callback, stable workspace ID, current acting user, and the raw Linux sandbox session. Pass that unguarded session into `createWorkspaceEgressManager`, then use `withWorkspaceEgress` to declare its execution policy and lifecycle hooks on the returned handle. `withWorkspaceEgress` constructs a checked handle through the generic SDK constructor, so direct callers are safe too. `defineSandboxProvider` automatically adds any host admission policy on start and resume without reinstalling the Device policy. Using a guarded session for privileged controller execution recursively deadlocks. `/sandboxes/egress-bootstrap` exports the exact controller source for custom privileged executors and manual acceptance. Custom executors run `python3 -I -c source source` with JSON on stdin, or execute a root-owned file providing `__file__`; the source argument permits persistence for later controller launches.
|
|
56
|
-
|
|
57
|
-
The host owns the logical workspace identity and durable networking registration. Each identity must name one physical workspace at a time; do not reuse it for unrelated resources or derive authorization from opaque provider state. Editor contexts supply the same `workspaceId` as start/resume. When `networking` is configured, legacy editor states also require that host identity so the predicate can determine management before granting access. Managed editor startup is guarded. Missing enrolled workspace nodes automatically recover using the approved desired configuration.
|
|
58
|
-
|
|
59
|
-
Supply the extension's `enabled` predicate through `createWorkspaceDeviceNetworking({ needed, api })`. Configuring an API client alone does not opt in workspaces. Keep the predicate true for every workspace with saved networking policy, including an explicitly disabled allow-all route and after new opt-in is removed. Teardown can leave a provider boot fence, so management and the Device API remain required until the stable workspace is deleted. The adapters also remember management within their current process; that cannot replace durable host discovery after a restart. Hosts filter potential provider capabilities per workspace. `additionalCapabilities` offers known positive capabilities while the rest of pre-start availability stays unknown; Computer uses it for egress without claiming Screen or ComputerUse availability.
|
|
60
|
-
|
|
61
|
-
Before creating or booting a fenced namespace, call `preserveWorkspaceNetworkPolicy` with its actual current provider policy. This atomically saves only a missing policy; a temporary boot fence must never replace a saved choice. Both providers call the extension's `prepare` hook for durable create-if-absent behavior, and its `suspend` hook during physical teardown. The Final Device extension implements those hooks through the Device API. The initialization endpoint is deliberately separate: an older Device API rejects boot before changing provider policy rather than misinterpreting the operation as a policy update.
|
|
62
|
-
|
|
63
|
-
Custom networking hooks own resources allocated before they reject and must clean up those resources. A failed attachment must not revoke another connection’s shared lease. The Device API must expire provisioning leases whose create acknowledgement is lost; a client cannot revoke an ID it never received, and cannot activate it without the acknowledged enrollment.
|
|
64
|
-
|
|
65
|
-
Adapters must supply `afterGuard` to restore restrictive saved policies or undo an outer fence. Missing restoration refuses Configure/Disable rather than acknowledging an unrestricted direct route. Provider instances retain remembered managed-workspace and prepared-policy identifiers for their lifetime; include that inventory in host capacity planning. Durable host registration remains required across process replacement.
|
|
66
|
-
|
|
67
|
-
## Boot, resume and saved policy
|
|
68
|
-
|
|
69
|
-
Prefer provider networking fences before boot or rebase, then apply the transport policy only after the manager guards the namespace. Daytona defaults to a structured-warning fallback only when the account explicitly rejects custom networking; `workspaceNetworkingBootFence: true` requires the provider fence, and `false` deliberately selects command-readiness protection. Startup/resumed background traffic before controller initialization is outside the fallback guarantee. A connection whose grant does not both write files and run commands is passive for networking: it records nothing, never recovers a route, and cannot wake a managed paused/stopped Computer or stopped/archived Daytona sandbox; a connection whose grant does both must resume it through guarded initialization. A sandbox's id is its workspace identity for Device egress; provider resource names remain private. A new sandbox starts from its setup's network policy. Missing or unreadable provider policy is denied. Fallback identities escape delimiters; Device egress identities must fit the 256-character protocol limit without restricting ordinary sandbox identities; supply a bounded host identity for longer provider names. Disable before capturing an image; do not clone a live managed identity.
|
|
70
|
-
|
|
71
|
-
Device configuration and Disable ask for approval by default through the public sandbox tool policy, and can be explicitly preauthorized. Consent selects the Device’s destination controls, including private access; provider destination allowlists do not constrain traffic inside the tunnel. Retain the saved direct-access policy and restore it on Disable. Unknown initial policy prevents configuration because restoration would be undefined. Disable before changing the saved direct-access policy. In-tunnel destination allowlists are unsupported. Configure failures intentionally retain a blocked route and desired settings until successful configuration or explicit disable; they never silently restore direct access.
|
|
72
|
-
|
|
73
|
-
Daytona treats block-all, CIDR allowlists, and domain allowlists as mutually exclusive. Its adapter explicitly clears domain allowlists on block/unblock, including legacy servers that clear only CIDRs; eligible-account acceptance must verify actual enforcement before admission, especially whether settings accepted on a stopped or archived workspace are applied during boot. Settings read-back alone does not prove that boot-time fence. See [Daytona network limits](https://www.daytona.io/docs/en/network-limits/).
|
|
74
|
-
|
|
75
|
-
Provider instance deletion does not erase saved logical-workspace networking. Recreating the same logical workspace deliberately recovers it; never reuse a workspace identifier for an unrelated workspace. A new managed attachment must preserve the initial policy through Device API even if the instance is already running.
|
|
76
|
-
|
|
77
|
-
An authorized Configure that subsequently fails carries a separate persistent choice marker, including before enrollment begins; resolver/firewall restoration artifacts do not authorize returning to direct access. The marker survives loss of volatile controller state and clears only after explicit Disable or an authorized direct-policy restoration. Custom providers may supply `namespaceExists` from their own authoritative API so deletion cleanup revokes leases without entering a deleted namespace. In-guest reboots and direct provider administration are outside the host lifecycle fence; route restarts through the adapter to retain boot-time protection.
|
|
78
|
-
|
|
79
|
-
## Execution policy boundary
|
|
80
|
-
|
|
81
|
-
The generic handle constructor applies the handle's `executionPolicy` automatically.
|
|
82
|
-
The provider factory composes any host policy supplied in the start context before
|
|
83
|
-
that policy, including when the handle was already constructed. The Final
|
|
84
|
-
integration declares Device authorization, lease readiness and deferred setup;
|
|
85
|
-
it does not install its own execution wrapper. The generic factory does not read
|
|
86
|
-
the acting user, call Device API, select Devices or decide approvals.
|
|
87
|
-
|
|
88
|
-
Policy checks cover the standard commands, mutations and MCP channels. Configure
|
|
89
|
-
and Disable retain separate authorization so they can repair blocked networking.
|
|
90
|
-
The direct-network policy setter performs its own authorization and repair without
|
|
91
|
-
requiring a working tunnel. Host-supplied admission still applies to that setter.
|
|
92
|
-
A workspace shares one network namespace; checks on new operations do not isolate
|
|
93
|
-
already-running processes by user. The Linux firewall independently enforces
|
|
94
|
-
physical-egress denial.
|
|
95
|
-
|
|
96
|
-
## Authority, grants and shutdown
|
|
97
|
-
|
|
98
|
-
Device Configure/Disable require write access and lifecycle authority. Direct network-policy operations require execute or write access plus lifecycle authority; a grant without files write and shell execute cannot mutate networking. MCP projections enforce tool consent; hosts exposing handle RPC must install their own operation-consent hook. Configure and Disable default to Ask. Only exact `network.egress.configure`/`network.egress.disable` action preauthorization can waive the default; broad lifecycle/network Allow cannot. Status honors `network.egress.status` and `egress` policy. MCP hosts pass lifecycle authority and action input to the policy-key helper, then use `resolveSandboxToolPolicy` so any applicable Ask wins for Configure/Disable. Status retains ordinary first-match read-policy precedence for `network.egress.status` and `egress`. This release exposes status to grants that write files and run commands, with lifecycle authority. Foreground actor changes suspend as the currently acting user, including a trusted userless host, and block until an explicit route choice; background connections do not revoke another actor's chosen route. Userless execution cannot inherit someone else's Device access.
|
|
99
|
-
|
|
100
|
-
Passive connections skip recovery and deferred setup. Pass the connection's grant to `withWorkspaceEgress`; never initialize a manager for a passive connection. They may inspect files but cannot repair a blocked route or run setup. A connection whose grant writes files and runs commands must configure or explicitly disable first. Networking changes do not run deferred repository/setup work; that runs before the next guarded execution, write, or foreground MCP endpoint/channel acquisition.
|
|
101
|
-
|
|
102
|
-
The manager owns renewal; failure observers cannot interrupt networking or claim cleanup. Editor access by another actor is refused before changing the owner’s route. A successful direct-policy retry clears a previous provider-update failure, including for userless workspaces. `close()` detaches a handle; runtime shutdown must not disrupt another connection sharing the workspace. `close(true)` attempts the namespace fence and suspends every lease for an explicit workspace stop/deletion, even if its cached lease has been replaced. Failure cleanup revokes only a lease that this handle created. Provider teardown independently attempts the fence, resource cleanup and lease revocation, and reports failures after cleanup. Removing boot admission must still permit teardown; hosts distinguish it with the predicate's `teardown` flag. Desired settings survive stop and lease expiry.
|
|
103
|
-
|
|
104
|
-
Teardown preserves the current provider policy before applying a fence. If eligibility or initial-policy persistence is unavailable for an unknown workspace, teardown still stops the resource, attempts suspension, and reports the error; it does not overwrite an unrelated policy. Host-managed boot waits for eligibility and saved policy recovery. Managed Computer creation and write/resume disable automatic wake. Passive read attachments do not change provider settings; migrate existing resources through a write attachment before relying on managed wake protection. Daytona’s preferred-fence fallback retains the documented startup window.
|
|
105
|
-
|
|
106
|
-
Handles opened before opt-in check admission before new commands, stdin, policy changes, and MCP messages; they must reconnect when the shared workspace becomes managed. Existing processes and previously issued editor/preview credentials still share that namespace and are not revoked by opting in or changing the exit. Hosts must close existing interactive sessions before allowing a different actor to use a personal Device on a shared workspace. Editor grants report a refresh deadline at least ninety seconds before lease expiry; refresh rechecks and renews networking, while closing an editor detaches without revoking an agent’s shared lease. Both providers persist management learned after start when parking; the durable host registration remains authoritative across restarts. Unsupported provider fences intentionally leave namespace-only denial; that retains the documented boot window, and does not authorize physical egress after guard readiness.
|
|
107
|
-
|
|
108
|
-
A failed route mutation from another actor must pass a fresh Device API authorization check before changing the namespace. The current actor's still-active owned lease permits local cleanup during a control-plane outage. MCP batches received before a transient guard failure are retained until a later authorized receive; business requests are never replayed.
|
|
109
|
-
|
|
110
|
-
## Controller, recovery and status
|
|
111
|
-
|
|
112
|
-
The controller verifies helper hashes/architecture, maintains physical-egress and IPv6 denial, and repairs managed DNS after network-manager rewrites. Disable restores resolver/firewall ownership, including after loss of volatile state, and clears persisted identity. Only discovered namespace-local nested bridges may forward through the exit; unrelated physical hosts cannot use the workspace as a router. Cooperative root/NET_ADMIN workloads can override their own namespace, so this is not a security boundary against hostile root.
|
|
113
|
-
|
|
114
|
-
The enrollment API returns `provisioning`; activation alone publishes grants and returns `active`. Configure and disable serialize through a bounded root-owned claim, refreshed every minute and expiring within five minutes after its last refresh. A superseded operation cannot activate an enrollment or undo a later disable. Each claim-acquisition or controller-stability wait is capped at three minutes; recovery can perform more than one wait. Lease deadlines use server wall time; retry throttles use monotonic time. Clocks must stay within one minute of the server.
|
|
115
|
-
|
|
116
|
-
A live, nonexpired controller survives exit/control outages without repeated helper transfers. Renewal attempts are spaced at least 30 seconds apart; failed automatic re-enrollment has a 60-second cooldown. Explicit configure can retry sooner. Failed control-state recovery has a separate five-second backoff across guarded calls. Foreground operations and delivered MCP traffic with an active route, including stdin writes and MCP messages, recheck namespace ownership through a privileged provider exec with a 100 ms cache. Recovery/configuration serializes them. Direct unconfigured handles make no background namespace probes. Handle lifetime bounds host renewal.
|
|
117
|
-
|
|
118
|
-
Workspace helpers enable shields-up to reject unsolicited tunnel connections independently of control-server grants. Recovery after a crashed namespace controller can take about thirty seconds and retains denial throughout. The default privileged launcher sends source and input on stdin, keeping shell arguments small. Custom argv-based executors must stay below Linux’s per-argument size limit; cached execution uses a short digest-pinned loader.
|
|
119
|
-
|
|
120
|
-
Status reports error when no denial tier can be installed; blocked is reserved for confirmed namespace denial. Configuration validation after guard installation leaves blocked status, rather than stale direct/device status. Cleanup errors are retained with the original failure and reported through onFailure.
|
|
121
|
-
|
|
122
|
-
## MCP and editor transports
|
|
123
|
-
|
|
124
|
-
Managed screen grants are view-only; interactive input uses the guarded computer-use MCP channel. URL MCP endpoints are converted to owned message channels so actor and policy checks also apply to cached endpoints.
|
|
125
|
-
|
|
126
|
-
Managed MCP uses guarded message channels. Hosted tools named `egress`, `network.egress`, or its configure/disable/status keys receive their server-name prefix in projections, retaining the ordinary hosted-tool approval policy. Handle RPC clients must negotiate `mcpChannels` for local MCP/computer-use, or use the MCP projection; legacy URL-only clients do not receive an unguarded URL. Known unsupported provider firewalls cannot restore a restrictive direct-access policy: Disable leaves namespace traffic blocked and reports the missing provider capability rather than claiming restoration succeeded.
|
|
127
|
-
|
|
128
|
-
Generic MCP channel transports poll while idle (with backoff capped at one second) to deliver server notifications and requests. Hosted discovery clients consume messages only while requests are pending; explicit catalog reads refresh the server listing. URL-backed HTTP transports may independently reconnect their notification stream, which rechecks admission and can refresh a Device lease. Cache hosts should close idle channels if they require strictly passive discovery. These private catalog clients do not service idle server requests or guarantee idle notifications; servers may end an idle session. An already-ended owned HTTP channel reconnects before sending a business request. Once a request may have been sent, failure is reported without replay; the following call reconnects. Buffer limits deliberately close a flooding channel. New HTTP streams and received messages also recheck admission. A notification-stream error alone does not cancel independent tool requests. A terminal receive failure closes the channel and evicts its cached client; the next call reconnects without replaying a tool invocation. Close cached clients when their handle closes. URL-backed MCP channels retain the SDK request timeout and per-request SSE recovery behavior; a failed POST does not fail unrelated requests. Each parsed receive queue rejects batches exceeding 4,096 messages or 16 MiB. This bounds retained queues per guard layer, not the HTTP/SSE parser’s allocation before delivery; hosts must bound transport response bodies independently. Closing attempts bounded server-session termination, then closes the local transport.
|
|
129
|
-
|
|
130
|
-
Background MCP queue polling retains the last authorized owner and does not run workspace setup for empty queues. Delivered messages and stream reconnects pass through admission; active routes also recheck namespace ownership. Direct and choice-blocked handles skip background namespace probes. Business requests still require the current foreground actor; catalog/protocol replies retain the last authorized owner. Opening a hosted endpoint initially requires foreground admission; hosts may authorize those discovery operations explicitly. Automatic retries of incomplete catalogs have a one-second cooldown; explicit catalog refreshes retry immediately. Failed deferred setup is reported and retried on the next guarded use. Setup steps must tolerate partial execution, as during host recovery. Generic workspace IDs may be opaque strings; Device egress requires a stable ID of at most 256 characters with no control characters.
|
|
131
|
-
|
|
132
|
-
## Provider prerequisites and limits
|
|
133
|
-
|
|
134
|
-
Custom images require root, the netfilter owner match, Python 3.8 or newer, TUN, IPv4 policy routing, `iproute2`, and IPv4/IPv6 netfilter before opting in. Direct status/disable remain usable on images without iptables; blocking and restoration of an installed guard still require netfilter tools. Blocking/disable do not require TUN. A missing executor or prerequisite cannot silently remove a guard; repair the image or use the provider's administrative API. Disabling automatic wake on a managed Computer is deliberate: boot must pass through its fence. Providers claiming boot fencing must verify its behavior on creation, stop/archive and resume; an accepted API request alone is insufficient. Preferred fencing may instead log the documented startup-window fallback. Paid-provider tests are manual and opt-in. Modal's tested VM kernel lacks policy routing and is rejected before enrollment.
|
|
135
|
-
|
|
136
|
-
Install the guard before exceeding 128 existing external control connections. Connection overflow installs the closed full guard before returning an error, falling back to minimal denial if needed. Emergency denial preserves loopback except DNS stubs and ACK replies on captured inbound control sockets. If no control socket could be captured, use the provider administrative API to repair the image. Device-mode listener discovery takes a few seconds. New preview listeners opened while blocked need another configure/block refresh. Helper router-mapping cleanup is best effort on graceful shutdown; abrupt process death or router failure can leave mappings for the router to remove.
|
|
137
|
-
|
|
138
|
-
Existing-connection overflow applies during capture as well as configuration and installs denial before reporting failure. Missing firewall tools require repair through the provider administrative API; absent legacy NAT/mangle modules can be restored from filter-only denial.
|
|
139
|
-
|
|
140
|
-
Existing untracked provider-control reply exceptions require root-owned established sockets; non-root listeners cannot preserve a physical outbound connection. Normally tracked inbound services still use conntrack reply direction, including services running as ordinary users. A failed temporary-artifact removal is treated as failed configuration and rolls back the route; fix the provider file API and retry.
|
|
141
|
-
|
|
142
|
-
The controller preserves at most 256 inbound TCP listener ports, preferring already retained ports and privileged provider listeners. Additional listeners do not crash the controller; their physical-network replies may be blocked. A root-owned `listener-limit.json` diagnostic records saturation. This limit does not affect outbound connections through the Device.
|
|
143
|
-
|
|
144
|
-
The workspace helper internal DNS address (100.100.100.100) is blocked; configure a Device-reachable DNS server instead. MagicDNS and host split-DNS integration remain outside this capability.
|
|
145
|
-
|
|
146
|
-
The local kernel acceptance image uses Python 3.13; Python 3.8 is the controller syntax/stdlib floor, rather than a separately exercised provider image. Run manual prerequisite acceptance for each custom image before rollout.
|
|
147
|
-
|
|
148
|
-
## Compatibility and repair
|
|
149
|
-
|
|
150
|
-
Configuring workspace admission requires MCP channel negotiation (or the MCP projection) even for currently unmanaged write handles. Admission checks intentionally run before new commands and messages so handles opened before opt-in cannot bypass a later change; an unavailable admission authority refuses execution. Legacy URL-only handle-RPC clients must upgrade before enabling this host integration. Read handles remain passive. `onSandboxStop` and `onSandboxDelete` represent authorized physical lifecycle actions; viewer disconnect and runtime detach use `onRuntimeShutdown`.
|
|
151
|
-
|
|
152
|
-
Controller error status prevents guarded execution and triggers authorized recovery. If the local enrollment deadline has already elapsed, renewal requests fresh enrollment rather than restarting without credentials. A lost original DNS backup requires provider-administrative resolver repair; the controller never treats managed resolver content as the original. Resolver restoration replaces an unexpected dangling symlink without following its target.
|
|
153
|
-
|
|
154
|
-
Raw guarded MCP channels retain received batches until a later authorized receive. The hosted MCP client treats a receive or authorization failure as terminal and reports uncertainty to outstanding callers; reconnect on the next operation, without replaying a business call. Transport-only notification-stream errors leave independent POSTs alone; failed admission closes the whole owned channel deliberately.
|
|
155
|
-
|
|
156
|
-
The namespace must remain under the adapter's networking ownership. External privileged firewall flushes/reloads and direct provider or in-guest reboots bypass this lifecycle; stop execution and initialize the adapter again after such operations. Blocked status records successful denial installation by the owned controller, not continuous attestation against a privileged process rewriting the firewall. This mechanism is not a boundary against namespace root/NET_ADMIN. Supported nested-container filter reordering remains covered by the earlier mangle guard.
|
|
157
|
-
|
|
158
|
-
Workspace identity must be stable before the first managed start. Changing from a derived identity to another ID while reusing the same physical resource is an unsupported identity migration; disable and retire the old managed workspace first. The SDK does not infer authorization or a saved policy from provider-private state. Passive recovery is a read-only viewer operation for all hosts, independent of egress opt-in, matching the provider interface contract.
|
|
159
|
-
|
|
160
|
-
A claim's five-minute expiry is a renewable lease, not an absolute total operation deadline. Hosts must provide bounded custom execution/upload/restoration hooks; a live owner awaiting an unbounded custom promise can keep refreshing its claim. Built-in public API calls have request deadlines. Failure cleanup attempts provider deny-all when namespace denial cannot be confirmed. With confirmed namespace denial, an unrestricted saved provider policy may be restored while the namespace stays blocked; Disable separately restores direct namespace access and the fresh saved policy.
|
|
161
|
-
|
|
162
|
-
Computer service bookkeeping uses the raw setup session's public file operation, whose directory creation waits through the public process API before the file write. It runs before deferred repository or service setup, including on fenced fresh read attachments. Helper publication atomically installs executable permissions so a concurrent restart cannot observe a temporary non-executable binary.
|
|
163
|
-
|
|
164
|
-
Disabling and reconfiguring is required after changing namespace bridge layouts,
|
|
165
|
-
so stale owned routing rules are removed. Reinitialize after enabling IPv6 kernel
|
|
166
|
-
support that was unavailable during the controller prerequisite probe. Symlinked
|
|
167
|
-
`/etc/hosts` images require a regular hosts file before Device configuration.
|
|
168
|
-
|
|
169
|
-
Supply `fetch` to `withWorkspaceEgress`/`withWorkspaceAdmission` for a custom MCP
|
|
170
|
-
transport; Computer integrations supply `networking.fetch` through `createWorkspaceDeviceNetworking({ mcpFetch })`. Artifact download
|
|
171
|
-
fetch and MCP fetch are independent dependencies. A first failed attempt without
|
|
172
|
-
NET_ADMIN can be explicitly disabled when the controller proves no networking
|
|
173
|
-
changes were possible; prior managed namespaces still require privileged repair.
|
|
174
|
-
|
|
175
|
-
Namespace-local bridge subnets take precedence over exit routes. Use bridge ranges
|
|
176
|
-
that do not overlap private destinations you need to reach through the Device.
|
|
177
|
-
|
|
178
|
-
Daytona cannot verify pre-boot enforcement from control-plane policy readback for a stopped sandbox. Its default resume path logs the startup window, starts the sandbox, then installs provider and namespace denial before agent tools. Explicitly requiring a verified boot fence refuses that stopped resume.
|
|
179
|
-
|
|
180
|
-
The default Computer and Daytona executors use passwordless sudo in the workload session. This is cooperative routing, not a security boundary against that workload: it can change its own firewall. For containment, supply an out-of-band privilegedRun through the composable controller and remove workload root/sudo and CAP_NET_RAW/CAP_NET_ADMIN. AF_PACKET frames can bypass IP netfilter; the raw-IP packet probes do not establish containment for link-layer injection. Killing processes and closing existing access remain available while execution admission is denied.
|
|
181
|
-
|
|
182
|
-
The retained direct policy is the actual provider policy, including existing platform-required destinations. Device policy persistence supports at most 1,024 entries of 253 characters; managed policy admission rejects unsupported snapshots before changing provider rules. Hosts own complete RPC authorization through authorizeOperation, including any consent required for Egress. An audit-only hook is not an authorization policy.
|
|
183
|
-
|
|
184
|
-
Custom privileged executors must launch the trusted system interpreter with a sanitized environment, including loader variables, before running supplied Python source. The controller additionally pins system command lookup and strips custom TLS trust overrides from its owned helpers.
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { defineRoomSurface } from "@finalai/agent-definition/authoring";
|
|
2
|
-
import {
|
|
3
|
-
finalChannel as createFinalChannel
|
|
4
|
-
} from "./channel-factories.mjs";
|
|
5
|
-
export * from "./channel-factories.mjs";
|
|
6
|
-
const finalChannel = (options = {}) => defineRoomSurface(createFinalChannel(options));
|
|
7
|
-
export {
|
|
8
|
-
finalChannel
|
|
9
|
-
};
|
|
10
|
-
//# sourceMappingURL=authoring-final-channel.mjs.map
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../../src/authoring-final-channel.ts"],
|
|
4
|
-
"sourcesContent": ["import { defineRoomSurface } from '@finalai/agent-definition/authoring';\nimport {\n finalChannel as createFinalChannel,\n type FinalChannelOptionsIface,\n} from \"./channel-factories.mjs\";\nexport * from \"./channel-factories.mjs\";\nexport const finalChannel = (\n options: FinalChannelOptionsIface = {},\n): ReturnType<typeof createFinalChannel> => defineRoomSurface(createFinalChannel(options));\n"],
|
|
5
|
-
"mappings": "AAAA,SAAS,yBAAyB;AAClC;AAAA,EACE,gBAAgB;AAAA,OAEX;AACP,cAAc;AACP,MAAM,eAAe,CAC1B,UAAoC,CAAC,MACK,kBAAkB,mBAAmB,OAAO,CAAC;",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import type { SessionUpdate } from '@agentclientprotocol/sdk/experimental/v2';
|
|
2
|
-
import { TurnPolicy, type RoomAuthIface, type RoomSurfaceDefinitionIface, type RoomInputResponseIface, type ChannelMessageCompletedDataIface, type ChannelMessageDeltaDataIface, type RoomReceiveContextIface, type ChannelTurnEventDataIface, type ChannelTurnStateIface, type ConversationMessageIface, type DecisionCreatedDataIface, type DecisionResolvedDataIface, type JsonValue } from '@finalai/agent-provider';
|
|
3
|
-
export { RoomPrincipalType, type RoomAuthIface } from '@finalai/agent-provider';
|
|
4
|
-
export interface ChannelAdmissionIface {
|
|
5
|
-
auth: RoomAuthIface | null;
|
|
6
|
-
context?: string[];
|
|
7
|
-
title?: string;
|
|
8
|
-
}
|
|
9
|
-
export interface FinalMessageContextIface extends RoomReceiveContextIface {
|
|
10
|
-
defaultAuth: RoomAuthIface | null;
|
|
11
|
-
}
|
|
12
|
-
export interface FinalChannelContextIface {
|
|
13
|
-
final: {
|
|
14
|
-
post(message: ChannelMessageCompletedDataIface['message']): Promise<ConversationMessageIface>;
|
|
15
|
-
};
|
|
16
|
-
}
|
|
17
|
-
export interface FinalSessionContextIface {
|
|
18
|
-
sessionId: string | null;
|
|
19
|
-
turnId: string;
|
|
20
|
-
state: ChannelTurnStateIface<JsonValue>;
|
|
21
|
-
}
|
|
22
|
-
export type FinalChannelEventHandler<Data> = (data: Data, channel: FinalChannelContextIface, ctx: FinalSessionContextIface) => void | Promise<void>;
|
|
23
|
-
export interface FinalChannelEventHandlersIface {
|
|
24
|
-
'message.delta'?: FinalChannelEventHandler<ChannelMessageDeltaDataIface>;
|
|
25
|
-
'message.completed'?: FinalChannelEventHandler<ChannelMessageCompletedDataIface>;
|
|
26
|
-
'turn.started'?: FinalChannelEventHandler<ChannelTurnEventDataIface>;
|
|
27
|
-
'turn.completed'?: FinalChannelEventHandler<ChannelTurnEventDataIface>;
|
|
28
|
-
'decision.created'?: FinalChannelEventHandler<DecisionCreatedDataIface>;
|
|
29
|
-
'decision.resolved'?: FinalChannelEventHandler<DecisionResolvedDataIface>;
|
|
30
|
-
}
|
|
31
|
-
export interface FinalChannelOptionsIface {
|
|
32
|
-
turnPolicy?: TurnPolicy | `${TurnPolicy}`;
|
|
33
|
-
updateIntervalMs?: number;
|
|
34
|
-
route?: RoomSurfaceDefinitionIface<JsonValue, JsonValue>['route'];
|
|
35
|
-
onMessage?: (ctx: FinalMessageContextIface, message: ConversationMessageIface) => ChannelAdmissionIface | null | Promise<ChannelAdmissionIface | null>;
|
|
36
|
-
onInputResponse?: (ctx: FinalMessageContextIface, response: RoomInputResponseIface) => ChannelAdmissionIface | null | Promise<ChannelAdmissionIface | null>;
|
|
37
|
-
events?: FinalChannelEventHandlersIface;
|
|
38
|
-
renderer?: (update: SessionUpdate, channel: FinalChannelContextIface, ctx: FinalSessionContextIface, next: () => Promise<void>) => void | Promise<void>;
|
|
39
|
-
}
|
|
40
|
-
export declare const finalChannel: (inputOptions?: FinalChannelOptionsIface) => RoomSurfaceDefinitionIface<JsonValue, JsonValue>;
|
|
@@ -1,212 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
RoomPrincipalType,
|
|
3
|
-
SemanticChannelEventKind,
|
|
4
|
-
TurnPolicy,
|
|
5
|
-
roomAuthSchema,
|
|
6
|
-
channelMessageCompletedSchema,
|
|
7
|
-
decisionCreatedDataSchema,
|
|
8
|
-
decisionResolvedDataSchema,
|
|
9
|
-
defineRoomSurface,
|
|
10
|
-
jsonObjectSchema
|
|
11
|
-
} from "@finalai/agent-provider";
|
|
12
|
-
import * as z from "zod";
|
|
13
|
-
import { contentBlocksOf } from "./channels/content.mjs";
|
|
14
|
-
import { conversationEventInboundSchema, messageInboundSchema } from "./channels/inbound.mjs";
|
|
15
|
-
import { createMessagingChannel } from "./channels/messaging-channel.mjs";
|
|
16
|
-
import { RoomPrincipalType as RoomPrincipalType2 } from "@finalai/agent-provider";
|
|
17
|
-
const admissionSchema = z.object({
|
|
18
|
-
auth: roomAuthSchema.nullable(),
|
|
19
|
-
context: z.array(z.string()).optional(),
|
|
20
|
-
title: z.string().optional()
|
|
21
|
-
});
|
|
22
|
-
const optionsSchema = z.object({
|
|
23
|
-
turnPolicy: z.nativeEnum(TurnPolicy).default(TurnPolicy.Steer),
|
|
24
|
-
updateIntervalMs: z.number().finite().nonnegative().optional()
|
|
25
|
-
});
|
|
26
|
-
const defaultAuthOf = (author) => {
|
|
27
|
-
if (author == null) return null;
|
|
28
|
-
return {
|
|
29
|
-
principalId: author.userId ?? author.definitionId ?? author.id,
|
|
30
|
-
principalType: author.userId == null ? RoomPrincipalType.Service : RoomPrincipalType.User,
|
|
31
|
-
authenticator: "final",
|
|
32
|
-
displayName: author.displayName,
|
|
33
|
-
attributes: { participant_id: author.id }
|
|
34
|
-
};
|
|
35
|
-
};
|
|
36
|
-
const finalChannel = (inputOptions = {}) => {
|
|
37
|
-
const options = {
|
|
38
|
-
...inputOptions,
|
|
39
|
-
events: inputOptions.events == null ? void 0 : { ...inputOptions.events }
|
|
40
|
-
};
|
|
41
|
-
const callbacks = {
|
|
42
|
-
onMessage: options.onMessage,
|
|
43
|
-
onInputResponse: options.onInputResponse,
|
|
44
|
-
renderer: options.renderer,
|
|
45
|
-
route: options.route,
|
|
46
|
-
...options.events
|
|
47
|
-
};
|
|
48
|
-
for (const [name, callback] of Object.entries(callbacks))
|
|
49
|
-
if (callback != null && !z.function().safeParse(callback).success)
|
|
50
|
-
throw new Error(`Final channel ${name} must be a function`);
|
|
51
|
-
const supportedEvents = /* @__PURE__ */ new Set([
|
|
52
|
-
"message.delta",
|
|
53
|
-
"message.completed",
|
|
54
|
-
"turn.started",
|
|
55
|
-
"turn.completed",
|
|
56
|
-
"decision.created",
|
|
57
|
-
"decision.resolved"
|
|
58
|
-
]);
|
|
59
|
-
for (const kind of Object.keys(options.events ?? {}))
|
|
60
|
-
if (!supportedEvents.has(kind)) throw new Error(`unsupported Final channel event: ${kind}`);
|
|
61
|
-
const config = optionsSchema.parse(options);
|
|
62
|
-
const base = createMessagingChannel({ ...config, route: options.route });
|
|
63
|
-
const receive = async (input, ctx) => {
|
|
64
|
-
if (conversationEventInboundSchema.safeParse(input).success) return base.receive(input, ctx);
|
|
65
|
-
const parsed = messageInboundSchema.parse(input);
|
|
66
|
-
const participants = ctx.participants ?? await ctx.platform.conversations.participants(parsed.conversationId);
|
|
67
|
-
const author = participants.find(
|
|
68
|
-
(participant) => participant.id === parsed.authorParticipantId
|
|
69
|
-
);
|
|
70
|
-
const message = ctx.message ?? await ctx.platform.conversations.postMessage({
|
|
71
|
-
...parsed,
|
|
72
|
-
content: contentBlocksOf(parsed.content)
|
|
73
|
-
});
|
|
74
|
-
if (ctx.definitionId == null) throw new Error("Final channel has no agent definition");
|
|
75
|
-
const messageContext = {
|
|
76
|
-
...ctx,
|
|
77
|
-
defaultAuth: defaultAuthOf(author)
|
|
78
|
-
};
|
|
79
|
-
const accepted = options.onMessage == null ? { auth: messageContext.defaultAuth } : await options.onMessage(messageContext, message);
|
|
80
|
-
const admission = accepted == null ? null : admissionSchema.parse(accepted);
|
|
81
|
-
if (admission == null) return { conversationId: parsed.conversationId };
|
|
82
|
-
const context = admission.context ?? [];
|
|
83
|
-
const injected = context.map((text) => ({ type: "text", text }));
|
|
84
|
-
return base.receive(
|
|
85
|
-
{
|
|
86
|
-
...parsed,
|
|
87
|
-
content: [...context.map((text) => ({ type: "text", text })), ...parsed.content]
|
|
88
|
-
},
|
|
89
|
-
{
|
|
90
|
-
...ctx,
|
|
91
|
-
platform: {
|
|
92
|
-
...ctx.platform,
|
|
93
|
-
sessions: {
|
|
94
|
-
...ctx.platform.sessions,
|
|
95
|
-
prompt: (args) => ctx.platform.sessions.prompt({
|
|
96
|
-
...args,
|
|
97
|
-
auth: admission.auth,
|
|
98
|
-
title: admission.title
|
|
99
|
-
})
|
|
100
|
-
}
|
|
101
|
-
},
|
|
102
|
-
message: { ...message, content: [...injected, ...message.content] }
|
|
103
|
-
}
|
|
104
|
-
);
|
|
105
|
-
};
|
|
106
|
-
const channelContext = (ctx) => {
|
|
107
|
-
const { conversation, agentParticipant } = ctx;
|
|
108
|
-
if (conversation == null || agentParticipant == null || agentParticipant.definitionId == null)
|
|
109
|
-
throw new Error("Final platform context requires a Final conversation");
|
|
110
|
-
return {
|
|
111
|
-
final: {
|
|
112
|
-
post: async (message) => {
|
|
113
|
-
if (ctx.platform.conversations.dispatchMessage == null)
|
|
114
|
-
throw new Error("channel dispatch is unavailable");
|
|
115
|
-
const posted = await ctx.platform.conversations.postMessage({
|
|
116
|
-
conversationId: conversation.conversationId,
|
|
117
|
-
authorParticipantId: agentParticipant.id,
|
|
118
|
-
content: contentBlocksOf(message.content),
|
|
119
|
-
clientMessageId: `channel:${ctx.turn.id}:${message.id}`,
|
|
120
|
-
originTurnId: ctx.turn.id
|
|
121
|
-
});
|
|
122
|
-
await ctx.platform.conversations.dispatchMessage({
|
|
123
|
-
conversationId: posted.conversationId,
|
|
124
|
-
messageId: posted.messageId
|
|
125
|
-
});
|
|
126
|
-
return posted;
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
};
|
|
130
|
-
};
|
|
131
|
-
const sessionContext = (ctx) => ({
|
|
132
|
-
sessionId: ctx.sessionId ?? null,
|
|
133
|
-
turnId: ctx.turn.id,
|
|
134
|
-
state: ctx.state
|
|
135
|
-
});
|
|
136
|
-
const wrap = (schema, handler) => handler == null ? void 0 : async (event, ctx) => handler(schema.parse(event.data), channelContext(ctx), sessionContext(ctx));
|
|
137
|
-
const authoredEvents = {
|
|
138
|
-
[SemanticChannelEventKind.MessageCompleted]: wrap(
|
|
139
|
-
channelMessageCompletedSchema,
|
|
140
|
-
options.events?.["message.completed"]
|
|
141
|
-
),
|
|
142
|
-
[SemanticChannelEventKind.MessageDelta]: wrap(
|
|
143
|
-
z.object({ messageId: z.string(), delta: jsonObjectSchema }),
|
|
144
|
-
options.events?.["message.delta"]
|
|
145
|
-
),
|
|
146
|
-
[SemanticChannelEventKind.TurnStarted]: wrap(
|
|
147
|
-
z.object({ turnId: z.string() }),
|
|
148
|
-
options.events?.["turn.started"]
|
|
149
|
-
),
|
|
150
|
-
[SemanticChannelEventKind.TurnCompleted]: wrap(
|
|
151
|
-
z.object({ turnId: z.string(), stopReason: z.string().nullable() }),
|
|
152
|
-
options.events?.["turn.completed"]
|
|
153
|
-
),
|
|
154
|
-
"decision.created": wrap(decisionCreatedDataSchema, options.events?.["decision.created"]),
|
|
155
|
-
"decision.resolved": wrap(decisionResolvedDataSchema, options.events?.["decision.resolved"])
|
|
156
|
-
};
|
|
157
|
-
const events = { ...base.events };
|
|
158
|
-
for (const [kind, handler] of Object.entries(authoredEvents))
|
|
159
|
-
if (handler != null) events[kind] = handler;
|
|
160
|
-
return Object.freeze(
|
|
161
|
-
defineRoomSurface({
|
|
162
|
-
...base,
|
|
163
|
-
config: jsonObjectSchema.parse(config),
|
|
164
|
-
configure: (next) => finalChannel({ ...options, ...optionsSchema.parse({ ...config, ...next }) }),
|
|
165
|
-
receive,
|
|
166
|
-
respond: async (input, ctx) => {
|
|
167
|
-
const conversation = z.string().min(1).parse(ctx.conversationId);
|
|
168
|
-
const participants = await ctx.platform.conversations.participants(conversation);
|
|
169
|
-
const author = participants.find(
|
|
170
|
-
(participant) => participant.id === input.actorParticipantId
|
|
171
|
-
);
|
|
172
|
-
const defaultAuth = defaultAuthOf(author);
|
|
173
|
-
let admission = { auth: defaultAuth };
|
|
174
|
-
if (options.onInputResponse != null) {
|
|
175
|
-
const accepted = await options.onInputResponse({ ...ctx, defaultAuth }, input);
|
|
176
|
-
if (accepted == null) return { resolved: false };
|
|
177
|
-
admission = admissionSchema.parse(accepted);
|
|
178
|
-
}
|
|
179
|
-
if (ctx.platform.sessions.respond == null)
|
|
180
|
-
throw new Error("response submission is unavailable");
|
|
181
|
-
return ctx.platform.sessions.respond({ ...input, auth: admission.auth });
|
|
182
|
-
},
|
|
183
|
-
cancel: async (ctx) => {
|
|
184
|
-
if (ctx.agentParticipantId == null) return { accepted: false };
|
|
185
|
-
const conversation = z.string().min(1).parse(ctx.conversationId);
|
|
186
|
-
const participants = await ctx.platform.conversations.participants(conversation);
|
|
187
|
-
const agent = participants.find((participant) => participant.id === ctx.agentParticipantId);
|
|
188
|
-
if (agent?.sessionId == null) return { accepted: false };
|
|
189
|
-
await ctx.platform.sessions.cancel({
|
|
190
|
-
participantId: agent.id,
|
|
191
|
-
reason: ctx.cancelReason,
|
|
192
|
-
throughMessageSeq: ctx.cancelThroughMessageSeq
|
|
193
|
-
});
|
|
194
|
-
return { accepted: true };
|
|
195
|
-
},
|
|
196
|
-
project: async (update, ctx) => {
|
|
197
|
-
const next = () => options.events?.["message.completed"] == null ? base.project(update, ctx) : Promise.resolve();
|
|
198
|
-
if (options.renderer != null) {
|
|
199
|
-
await options.renderer(update, channelContext(ctx), sessionContext(ctx), next);
|
|
200
|
-
return;
|
|
201
|
-
}
|
|
202
|
-
await next();
|
|
203
|
-
},
|
|
204
|
-
events: Object.freeze(events)
|
|
205
|
-
})
|
|
206
|
-
);
|
|
207
|
-
};
|
|
208
|
-
export {
|
|
209
|
-
RoomPrincipalType2 as RoomPrincipalType,
|
|
210
|
-
finalChannel
|
|
211
|
-
};
|
|
212
|
-
//# sourceMappingURL=channel-factories.mjs.map
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../../src/channel-factories.ts"],
|
|
4
|
-
"sourcesContent": ["import type { ContentBlock, SessionUpdate } from '@agentclientprotocol/sdk/experimental/v2';\nimport {\n RoomPrincipalType,\n SemanticChannelEventKind,\n TurnPolicy,\n roomAuthSchema,\n channelMessageCompletedSchema,\n decisionCreatedDataSchema,\n decisionResolvedDataSchema,\n defineRoomSurface,\n jsonObjectSchema,\n type RoomAuthIface,\n type RoomSurfaceDefinitionIface,\n type RoomEventHandler,\n type RoomInputResponseIface,\n type ChannelMessageCompletedDataIface,\n type ChannelMessageDeltaDataIface,\n type RoomReceiveContextIface,\n type ChannelTurnEventDataIface,\n type ChannelTurnStateIface,\n type ConversationMessageIface,\n type DecisionCreatedDataIface,\n type DecisionResolvedDataIface,\n type JsonValue,\n type ProjectionContextIface,\n} from '@finalai/agent-provider';\nimport * as z from 'zod';\nimport { contentBlocksOf } from \"./channels/content.mjs\";\nimport { conversationEventInboundSchema, messageInboundSchema } from \"./channels/inbound.mjs\";\nimport { createMessagingChannel } from \"./channels/messaging-channel.mjs\";\n\nexport { RoomPrincipalType, type RoomAuthIface } from '@finalai/agent-provider';\n\nexport interface ChannelAdmissionIface {\n auth: RoomAuthIface | null;\n context?: string[];\n title?: string;\n}\n\nexport interface FinalMessageContextIface extends RoomReceiveContextIface {\n defaultAuth: RoomAuthIface | null;\n}\n\nexport interface FinalChannelContextIface {\n final: {\n post(message: ChannelMessageCompletedDataIface['message']): Promise<ConversationMessageIface>;\n };\n}\nexport interface FinalSessionContextIface {\n sessionId: string | null;\n turnId: string;\n state: ChannelTurnStateIface<JsonValue>;\n}\nexport type FinalChannelEventHandler<Data> = (\n data: Data,\n channel: FinalChannelContextIface,\n ctx: FinalSessionContextIface,\n) => void | Promise<void>;\nexport interface FinalChannelEventHandlersIface {\n 'message.delta'?: FinalChannelEventHandler<ChannelMessageDeltaDataIface>;\n 'message.completed'?: FinalChannelEventHandler<ChannelMessageCompletedDataIface>;\n 'turn.started'?: FinalChannelEventHandler<ChannelTurnEventDataIface>;\n 'turn.completed'?: FinalChannelEventHandler<ChannelTurnEventDataIface>;\n 'decision.created'?: FinalChannelEventHandler<DecisionCreatedDataIface>;\n 'decision.resolved'?: FinalChannelEventHandler<DecisionResolvedDataIface>;\n}\n\nexport interface FinalChannelOptionsIface {\n turnPolicy?: TurnPolicy | `${TurnPolicy}`;\n updateIntervalMs?: number;\n route?: RoomSurfaceDefinitionIface<JsonValue, JsonValue>['route'];\n onMessage?: (\n ctx: FinalMessageContextIface,\n message: ConversationMessageIface,\n ) => ChannelAdmissionIface | null | Promise<ChannelAdmissionIface | null>;\n onInputResponse?: (\n ctx: FinalMessageContextIface,\n response: RoomInputResponseIface,\n ) => ChannelAdmissionIface | null | Promise<ChannelAdmissionIface | null>;\n events?: FinalChannelEventHandlersIface;\n renderer?: (\n update: SessionUpdate,\n channel: FinalChannelContextIface,\n ctx: FinalSessionContextIface,\n next: () => Promise<void>,\n ) => void | Promise<void>;\n}\n\nconst admissionSchema = z.object({\n auth: roomAuthSchema.nullable(),\n context: z.array(z.string()).optional(),\n title: z.string().optional(),\n});\n\nconst optionsSchema = z.object({\n turnPolicy: z.nativeEnum(TurnPolicy).default(TurnPolicy.Steer),\n updateIntervalMs: z.number().finite().nonnegative().optional(),\n});\n\n// A room member as a principal: a person by user id, an agent by definition id.\nconst defaultAuthOf = (\n author: import('@finalai/agent-provider').ParticipantIface | undefined,\n): RoomAuthIface | null => {\n if (author == null) return null;\n return {\n principalId: author.userId ?? author.definitionId ?? author.id,\n principalType: author.userId == null ? RoomPrincipalType.Service : RoomPrincipalType.User,\n authenticator: 'final',\n displayName: author.displayName,\n attributes: { participant_id: author.id },\n };\n};\n\nexport const finalChannel = (\n inputOptions: FinalChannelOptionsIface = {},\n): RoomSurfaceDefinitionIface<JsonValue, JsonValue> => {\n const options = {\n ...inputOptions,\n events: inputOptions.events == null ? undefined : { ...inputOptions.events },\n };\n const callbacks = {\n onMessage: options.onMessage,\n onInputResponse: options.onInputResponse,\n renderer: options.renderer,\n route: options.route,\n ...options.events,\n };\n for (const [name, callback] of Object.entries(callbacks))\n if (callback != null && !z.function().safeParse(callback).success)\n throw new Error(`Final channel ${name} must be a function`);\n const supportedEvents = new Set([\n 'message.delta',\n 'message.completed',\n 'turn.started',\n 'turn.completed',\n 'decision.created',\n 'decision.resolved',\n ]);\n for (const kind of Object.keys(options.events ?? {}))\n if (!supportedEvents.has(kind)) throw new Error(`unsupported Final channel event: ${kind}`);\n const config = optionsSchema.parse(options);\n const base = createMessagingChannel({ ...config, route: options.route });\n const receive: typeof base.receive = async (input, ctx) => {\n if (conversationEventInboundSchema.safeParse(input).success) return base.receive(input, ctx);\n const parsed = messageInboundSchema.parse(input);\n const participants =\n ctx.participants ?? (await ctx.platform.conversations.participants(parsed.conversationId));\n const author = participants.find(\n (participant) => participant.id === parsed.authorParticipantId,\n );\n const message =\n ctx.message ??\n (await ctx.platform.conversations.postMessage({\n ...parsed,\n content: contentBlocksOf(parsed.content),\n }));\n if (ctx.definitionId == null) throw new Error('Final channel has no agent definition');\n const messageContext: FinalMessageContextIface = {\n ...ctx,\n defaultAuth: defaultAuthOf(author),\n };\n const accepted =\n options.onMessage == null\n ? { auth: messageContext.defaultAuth }\n : await options.onMessage(messageContext, message);\n const admission = accepted == null ? null : admissionSchema.parse(accepted);\n if (admission == null) return { conversationId: parsed.conversationId };\n const context = admission.context ?? [];\n const injected: ContentBlock[] = context.map((text) => ({ type: 'text', text }));\n return base.receive(\n {\n ...parsed,\n content: [...context.map((text) => ({ type: 'text', text })), ...parsed.content],\n },\n {\n ...ctx,\n platform: {\n ...ctx.platform,\n sessions: {\n ...ctx.platform.sessions,\n prompt: (args) =>\n ctx.platform.sessions.prompt({\n ...args,\n auth: admission.auth,\n title: admission.title,\n }),\n },\n },\n message: { ...message, content: [...injected, ...message.content] },\n },\n );\n };\n const channelContext = (ctx: ProjectionContextIface): FinalChannelContextIface => {\n const { conversation, agentParticipant } = ctx;\n if (conversation == null || agentParticipant == null || agentParticipant.definitionId == null)\n throw new Error('Final platform context requires a Final conversation');\n return {\n final: {\n post: async (message) => {\n if (ctx.platform.conversations.dispatchMessage == null)\n throw new Error('channel dispatch is unavailable');\n const posted = await ctx.platform.conversations.postMessage({\n conversationId: conversation.conversationId,\n authorParticipantId: agentParticipant.id,\n content: contentBlocksOf(message.content),\n clientMessageId: `channel:${ctx.turn.id}:${message.id}`,\n originTurnId: ctx.turn.id,\n });\n await ctx.platform.conversations.dispatchMessage({\n conversationId: posted.conversationId,\n messageId: posted.messageId,\n });\n return posted;\n },\n },\n };\n };\n const sessionContext = (ctx: ProjectionContextIface): FinalSessionContextIface => ({\n sessionId: ctx.sessionId ?? null,\n turnId: ctx.turn.id,\n state: ctx.state,\n });\n const wrap = <Data>(\n schema: z.ZodType<Data>,\n handler: FinalChannelEventHandler<Data> | undefined,\n ): RoomEventHandler | undefined =>\n handler == null\n ? undefined\n : async (event, ctx) =>\n handler(schema.parse(event.data), channelContext(ctx), sessionContext(ctx));\n const authoredEvents = {\n [SemanticChannelEventKind.MessageCompleted]: wrap(\n channelMessageCompletedSchema,\n options.events?.['message.completed'],\n ),\n [SemanticChannelEventKind.MessageDelta]: wrap(\n z.object({ messageId: z.string(), delta: jsonObjectSchema }),\n options.events?.['message.delta'],\n ),\n [SemanticChannelEventKind.TurnStarted]: wrap(\n z.object({ turnId: z.string() }),\n options.events?.['turn.started'],\n ),\n [SemanticChannelEventKind.TurnCompleted]: wrap(\n z.object({ turnId: z.string(), stopReason: z.string().nullable() }),\n options.events?.['turn.completed'],\n ),\n 'decision.created': wrap(decisionCreatedDataSchema, options.events?.['decision.created']),\n 'decision.resolved': wrap(decisionResolvedDataSchema, options.events?.['decision.resolved']),\n };\n const events = { ...base.events };\n for (const [kind, handler] of Object.entries(authoredEvents))\n if (handler != null) events[kind] = handler;\n return Object.freeze(\n defineRoomSurface({\n ...base,\n config: jsonObjectSchema.parse(config),\n configure: (next) =>\n finalChannel({ ...options, ...optionsSchema.parse({ ...config, ...next }) }),\n receive,\n respond: async (input, ctx) => {\n const conversation = z.string().min(1).parse(ctx.conversationId);\n const participants = await ctx.platform.conversations.participants(conversation);\n const author = participants.find(\n (participant) => participant.id === input.actorParticipantId,\n );\n const defaultAuth = defaultAuthOf(author);\n let admission: ChannelAdmissionIface = { auth: defaultAuth };\n if (options.onInputResponse != null) {\n const accepted = await options.onInputResponse({ ...ctx, defaultAuth }, input);\n if (accepted == null) return { resolved: false };\n admission = admissionSchema.parse(accepted);\n }\n if (ctx.platform.sessions.respond == null)\n throw new Error('response submission is unavailable');\n return ctx.platform.sessions.respond({ ...input, auth: admission.auth });\n },\n cancel: async (ctx) => {\n if (ctx.agentParticipantId == null) return { accepted: false };\n const conversation = z.string().min(1).parse(ctx.conversationId);\n const participants = await ctx.platform.conversations.participants(conversation);\n const agent = participants.find((participant) => participant.id === ctx.agentParticipantId);\n if (agent?.sessionId == null) return { accepted: false };\n await ctx.platform.sessions.cancel({\n participantId: agent.id,\n reason: ctx.cancelReason,\n throughMessageSeq: ctx.cancelThroughMessageSeq,\n });\n return { accepted: true };\n },\n project: async (update, ctx) => {\n const next = () =>\n options.events?.['message.completed'] == null\n ? base.project(update, ctx)\n : Promise.resolve();\n if (options.renderer != null) {\n await options.renderer(update, channelContext(ctx), sessionContext(ctx), next);\n return;\n }\n await next();\n },\n events: Object.freeze(events),\n }),\n );\n};\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": ["RoomPrincipalType"]
|
|
7
|
-
}
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import type { ContentBlock } from '@agentclientprotocol/sdk/experimental/v2';
|
|
2
|
-
import { type JsonObject, type JsonValue } from '@finalai/agent-provider';
|
|
3
|
-
export declare const contentBlockOf: (value: JsonValue | undefined) => ContentBlock | null;
|
|
4
|
-
export declare const contentBlocksOf: (value: JsonValue | undefined) => ContentBlock[];
|
|
5
|
-
export declare const jsonOfContentBlock: (block: ContentBlock) => JsonObject;
|
|
6
|
-
export declare const jsonOfContent: (content: ContentBlock[]) => JsonObject[];
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { contentBlockSchemas, ContentBlockType } from "@finalai/acp-session";
|
|
2
|
-
import { jsonObjectSchema } from "@finalai/agent-provider";
|
|
3
|
-
const contentBlockOf = (value) => {
|
|
4
|
-
if (value == null) {
|
|
5
|
-
return null;
|
|
6
|
-
}
|
|
7
|
-
const text = contentBlockSchemas[ContentBlockType.Text].safeParse(value);
|
|
8
|
-
if (text.success) return text.data;
|
|
9
|
-
const image = contentBlockSchemas[ContentBlockType.Image].safeParse(value);
|
|
10
|
-
if (image.success) return image.data;
|
|
11
|
-
const audio = contentBlockSchemas[ContentBlockType.Audio].safeParse(value);
|
|
12
|
-
if (audio.success) return audio.data;
|
|
13
|
-
const link = contentBlockSchemas[ContentBlockType.ResourceLink].safeParse(value);
|
|
14
|
-
if (link.success) return link.data;
|
|
15
|
-
const resource = contentBlockSchemas[ContentBlockType.Resource].safeParse(value);
|
|
16
|
-
if (resource.success) return resource.data;
|
|
17
|
-
return null;
|
|
18
|
-
};
|
|
19
|
-
const contentBlocksOf = (value) => Array.isArray(value) ? value.flatMap((item) => contentBlockOf(item) ?? []) : [];
|
|
20
|
-
const jsonOfContentBlock = (block) => {
|
|
21
|
-
const parsed = jsonObjectSchema.safeParse(JSON.parse(JSON.stringify(block)));
|
|
22
|
-
return parsed.success ? parsed.data : { type: "text", text: "" };
|
|
23
|
-
};
|
|
24
|
-
const jsonOfContent = (content) => content.map(jsonOfContentBlock);
|
|
25
|
-
export {
|
|
26
|
-
contentBlockOf,
|
|
27
|
-
contentBlocksOf,
|
|
28
|
-
jsonOfContent,
|
|
29
|
-
jsonOfContentBlock
|
|
30
|
-
};
|
|
31
|
-
//# sourceMappingURL=content.mjs.map
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../../../src/channels/content.ts"],
|
|
4
|
-
"sourcesContent": ["import type { ContentBlock } from '@agentclientprotocol/sdk/experimental/v2';\nimport { contentBlockSchemas, ContentBlockType } from '@finalai/acp-session';\nimport { jsonObjectSchema, type JsonObject, type JsonValue } from '@finalai/agent-provider';\n\n// Content blocks cross JSON boundaries (Postgres jsonb, projection state, wire\n// bodies). Only the standard ACP block types survive the trip back; custom\n// `_`-prefixed blocks are dropped rather than trusted.\nexport const contentBlockOf = (value: JsonValue | undefined): ContentBlock | null => {\n if (value == null) {\n return null;\n }\n const text = contentBlockSchemas[ContentBlockType.Text].safeParse(value);\n if (text.success) return text.data;\n const image = contentBlockSchemas[ContentBlockType.Image].safeParse(value);\n if (image.success) return image.data;\n const audio = contentBlockSchemas[ContentBlockType.Audio].safeParse(value);\n if (audio.success) return audio.data;\n const link = contentBlockSchemas[ContentBlockType.ResourceLink].safeParse(value);\n if (link.success) return link.data;\n const resource = contentBlockSchemas[ContentBlockType.Resource].safeParse(value);\n if (resource.success) return resource.data;\n return null;\n};\n\nexport const contentBlocksOf = (value: JsonValue | undefined): ContentBlock[] =>\n Array.isArray(value) ? value.flatMap((item) => contentBlockOf(item) ?? []) : [];\n\nexport const jsonOfContentBlock = (block: ContentBlock): JsonObject => {\n const parsed = jsonObjectSchema.safeParse(JSON.parse(JSON.stringify(block)));\n return parsed.success ? parsed.data : { type: 'text', text: '' };\n};\n\nexport const jsonOfContent = (content: ContentBlock[]): JsonObject[] =>\n content.map(jsonOfContentBlock);\n"],
|
|
5
|
-
"mappings": "AACA,SAAS,qBAAqB,wBAAwB;AACtD,SAAS,wBAAyD;AAK3D,MAAM,iBAAiB,CAAC,UAAsD;AACnF,MAAI,SAAS,MAAM;AACjB,WAAO;AAAA,EACT;AACA,QAAM,OAAO,oBAAoB,iBAAiB,IAAI,EAAE,UAAU,KAAK;AACvE,MAAI,KAAK,QAAS,QAAO,KAAK;AAC9B,QAAM,QAAQ,oBAAoB,iBAAiB,KAAK,EAAE,UAAU,KAAK;AACzE,MAAI,MAAM,QAAS,QAAO,MAAM;AAChC,QAAM,QAAQ,oBAAoB,iBAAiB,KAAK,EAAE,UAAU,KAAK;AACzE,MAAI,MAAM,QAAS,QAAO,MAAM;AAChC,QAAM,OAAO,oBAAoB,iBAAiB,YAAY,EAAE,UAAU,KAAK;AAC/E,MAAI,KAAK,QAAS,QAAO,KAAK;AAC9B,QAAM,WAAW,oBAAoB,iBAAiB,QAAQ,EAAE,UAAU,KAAK;AAC/E,MAAI,SAAS,QAAS,QAAO,SAAS;AACtC,SAAO;AACT;AAEO,MAAM,kBAAkB,CAAC,UAC9B,MAAM,QAAQ,KAAK,IAAI,MAAM,QAAQ,CAAC,SAAS,eAAe,IAAI,KAAK,CAAC,CAAC,IAAI,CAAC;AAEzE,MAAM,qBAAqB,CAAC,UAAoC;AACrE,QAAM,SAAS,iBAAiB,UAAU,KAAK,MAAM,KAAK,UAAU,KAAK,CAAC,CAAC;AAC3E,SAAO,OAAO,UAAU,OAAO,OAAO,EAAE,MAAM,QAAQ,MAAM,GAAG;AACjE;AAEO,MAAM,gBAAgB,CAAC,YAC5B,QAAQ,IAAI,kBAAkB;",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|