@aglit-ai/sdk 0.1.9 → 0.1.11

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 CHANGED
@@ -9,6 +9,188 @@ MIT-licensed SDK for building against the Aglit AI desktop API and tool runtime.
9
9
 
10
10
  > You must have the Aglit AI desktop app/API running locally (localhost). Set `SERVER_PORT`/`PORT`/`AGLIT_DESKTOP_BASE_URL` as needed.
11
11
 
12
+ ## Encrypted private website content
13
+
14
+ The trusted `/_sites/bootstrap` shell can render a normal device-local website
15
+ without exposing its HTML, scripts, assets or HTTP API bodies to the gateway:
16
+
17
+ ```ts
18
+ import { createAuthenticatedSiteRuntimeFetch, mountEncryptedSite } from '@aglit-ai/sdk/browser';
19
+
20
+ const response = await fetch('/_sites/context', { credentials: 'same-origin', cache: 'no-store' });
21
+ if (!response.ok) throw new Error('Sign in to this site’s team');
22
+ const context = await response.json();
23
+ const runtimeFetch = await createAuthenticatedSiteRuntimeFetch({
24
+ deviceId: context.site.deviceId,
25
+ authenticatedEmail: context.user.email,
26
+ });
27
+ const surface = await mountEncryptedSite(document.querySelector('#site')!, {
28
+ siteName: context.site.name,
29
+ deviceId: context.site.deviceId,
30
+ agentId: context.site.agentId,
31
+ initialPath: new URLSearchParams(location.hash.slice(1)).get('path') ?? '/',
32
+ runtimeFetch,
33
+ });
34
+ // On teardown: await surface.destroy();
35
+ ```
36
+
37
+ Serve the SDK's standalone `site-worker.js` as the trusted, authenticated
38
+ `/_sites/worker.js`, with `Service-Worker-Allowed: /` and a JavaScript content type.
39
+ Serve the bundled shell code at `/_sites/bootstrap.js`. The shell, worker,
40
+ context and existing `/remote-access/api/...` endpoints remain protected by their
41
+ canonical server authentication and route allowlists. All other site content is
42
+ requested only through the generated `proxyPrivateSiteRequest` operation inside
43
+ the existing signed and encrypted runtime channel. A bare site cookie never
44
+ replaces that channel.
45
+
46
+ The page holds the canonical endpoint crypto; the worker receives no keys. An
47
+ in-memory MessageChannel binds the exact top-level shell, one-use initial iframe
48
+ navigation, and browser-owned child client IDs. It returns decrypted responses
49
+ locally to the iframe. It never fetches site content directly or stores content
50
+ or keys in CacheStorage, IndexedDB, or a service-worker cache. Responses use
51
+ `no-store` and a same-origin CSP; external content requests fail closed. The
52
+ worker leaves only gateway-reserved `/_sites/*` and `/remote-access/api/*`
53
+ requests to their existing authenticated handlers.
54
+
55
+ The persistent shell must stay alive. A closed shell, rejected membership,
56
+ missing endpoint verification, or unavailable crypto returns an error rather
57
+ than plaintext fallback. A worker restart loses client custody, so the shell
58
+ reconnects and reloads a newly owned iframe. Site documents share their site's
59
+ origin and code trust; the iframe is a rendering surface, **not** an isolation
60
+ boundary between the site's own scripts and its Aglit SDK capabilities.
61
+
62
+ Shell links use `/_sites/bootstrap#path=<encoded-local-path>` so application
63
+ paths and queries stay browser-local during worker navigation. Keep secrets out
64
+ of ordinary URLs entered before the worker controls the page, as those initial
65
+ network URLs are necessarily routing metadata.
66
+
67
+ The first version supports bounded HTTP request bodies up to 1 MiB and individual
68
+ responses up to 8 MiB. Open-ended media ranges are bounded to 1 MiB segments.
69
+ Site cookies, worker registrations, external resources, WebSockets and unbounded
70
+ HTTP streams are not forwarded by the content bridge. Use the direct WebRTC
71
+ primitive below for live app/desktop video and input.
72
+
73
+ ## Private-site app and desktop streams
74
+
75
+ Browser applications can import `@aglit-ai/sdk/browser` without loading local
76
+ credential-file or IPC helpers. The default SDK entrypoint remains for local
77
+ desktop/server integration.
78
+
79
+ `createDirectRemoteStreamTransport(binding, { baseUrl, runtimeFetch })` connects
80
+ to the existing authenticated direct WebRTC runtime. `mountAppStream`,
81
+ `mountDesktopStream`, and `mountRemoteStream` provide framework-neutral video,
82
+ connection status, and optional pointer/keyboard controls. They use the existing
83
+ OpenAPI-generated signaling routes and direct ICE/TURN media, with the same
84
+ `aglit-input-reliable` and `aglit-input-volatile` input channels as Aglit.
85
+
86
+ ```ts
87
+ import { createAuthenticatedSiteRuntimeFetch, createDirectRemoteStreamTransport, mountAppStream } from '@aglit-ai/sdk/browser';
88
+
89
+ // binding comes from your authorized backend's exact persisted game chat/window:
90
+ // binding: { agentId, deviceId, sessionId, streamId,
91
+ // target: { streamType: 'app', appId, windowId } }
92
+ const response = await fetch('/_sites/context', { credentials: 'same-origin', cache: 'no-store' });
93
+ if (!response.ok) throw new Error('Sign in to this site’s team');
94
+ const context = await response.json();
95
+ if (context.site.deviceId !== binding.deviceId) throw new Error('Game device changed');
96
+ const encryptedRuntimeFetch = await createAuthenticatedSiteRuntimeFetch({
97
+ deviceId: context.site.deviceId,
98
+ authenticatedEmail: context.user.email,
99
+ runtimeSessionId: binding.sessionId,
100
+ });
101
+ const surface = mountAppStream(document.querySelector('#game')!, {
102
+ title: "slurp's up!",
103
+ interactive: true,
104
+ transport: createDirectRemoteStreamTransport(binding, {
105
+ baseUrl: window.location.origin,
106
+ runtimeFetch: encryptedRuntimeFetch,
107
+ captureSpeakerAudio: true,
108
+ }),
109
+ });
110
+ await surface.connect();
111
+ // During component/page cleanup:
112
+ await surface.destroy();
113
+ ```
114
+
115
+ For a desktop, use `mountDesktopStream` with
116
+ `target: { streamType: 'desktop', displayId }` and omit `interactive`.
117
+ Desktop streams are view-only in this version: interactive desktop mounting is
118
+ rejected, and the direct transport rejects desktop control/input. Display video
119
+ pixels can differ from logical native coordinates on Retina or scaled captures;
120
+ desktop control requires the canonical verified logical display-bounds mapping.
121
+ Supply a real persisted chat
122
+ session, the original registered device, and an exact enumerated app window or
123
+ display authorized by the backend. IDs select a target; they never grant access.
124
+ The primitive does not launch apps or create account, team, or device grants.
125
+
126
+ `createAuthenticatedSiteRuntimeFetch` enrolls this browser through the existing
127
+ runtime auth/key flow. Obtain its account and device from authenticated
128
+ `/_sites/context`, never a URL claim. It reuses canonical nonextractable browser
129
+ keys, device identity verification and existing managed pins or fingerprint
130
+ confirmation UI. A configured computer verification code is requested through
131
+ a real code dialog; denied access and API-key requirements remain failures.
132
+ The separate site cookie stays HttpOnly. No cookie or device key is copied into
133
+ application JavaScript. The optional `runtimeSessionId` is an existing task/chat
134
+ used to select the serving runtime generation; the auth flow allocates its own
135
+ separate crypto session.
136
+
137
+ The resulting **authenticated, encrypted runtime fetch** receives the unchanged
138
+ generated `/remote-access/api/...` URLs on the site
139
+ origin. The site gateway preserves their exact paths and queries, including
140
+ URL-bound encryption proofs; it never rewrites them behind another API prefix.
141
+ Passing ordinary `fetch` with a site cookie does not establish that runtime
142
+ session. No broad local SDK token, browser cookie export, or provider-terminated
143
+ media fallback is introduced.
144
+
145
+ The adapter signs exact encrypted bytes and URLs with the canonical v2 proof,
146
+ then accepts only authenticated nonce-bound successful responses. It has one
147
+ account/device scope per page; reload after account, device or credential changes.
148
+
149
+ App input is off by default. With `interactive: true`, the user explicitly selects
150
+ **Take control** after the authorized direct input channel opens. Pointer input
151
+ is mapped to the contained video rather than its letterbox. The default mode
152
+ sends the canonical complete `keyEvent: press` gestures, including
153
+ browser keyboard repeats, and complete clicks or left-button drags on pointer
154
+ release. Interrupted pointer gestures are cancelled locally. It never leaves a
155
+ native key or button waiting for a later release from a vanished viewer.
156
+
157
+ For games that need continuous movement, explicitly set `keyboardMode: 'hold'`.
158
+ Connection then requires the actual packaged native helper's advertised app
159
+ held-key capability. It sends existing `down`/`up` events with one `keyHoldId`
160
+ per physical hold; `keyHoldRenew: true` renewals preserve that identity and never repeat key-down
161
+ callbacks. Simultaneous keys remain down together. Renewal runs every 250 ms
162
+ only while the video has focus, the page is visible, and this surface has control.
163
+ Blur, hidden pages, key-up and teardown stop renewal before sending releases.
164
+ Queued down/renew packets expire before native admission; a renewal cannot
165
+ create a hold. Native down/renew also requires the captured window to remain
166
+ the foreground app's exact focused window, without focusing or changing that window.
167
+
168
+ The native helper holds one exclusive app-keyboard owner per process,
169
+ bound to its process birth and admitted exact window. App input and global
170
+ keyboard events from another viewer cannot release or interrupt those keys.
171
+ Existing authorized desktop pointer control may change focus; that stops app
172
+ key renewal. A missing renewal expires its two-second native
173
+ lease, checked every 100 ms; helper EOF/orderly shutdown also releases them. Cleanup uses the
174
+ captured process identity and never focuses or resolves a replacement app.
175
+ Expired/released hold IDs cannot be renewed into a new hold. Older runtimes
176
+ reject held mode; they continue to support viewing and complete presses.
177
+ Native activation and real game acceptance still require the new signed helper.
178
+ An abrupt helper crash, SIGKILL or a suspended native process cannot run cleanup;
179
+ the expiry guarantee requires the native helper to remain scheduled and healthy.
180
+ Live button-held dragging and standalone modifier holds remain unsupported.
181
+ Audio starts muted to respect browser playback requirements; a site can expose
182
+ its own sound control using `surface.video.muted`. All viewers currently share
183
+ the same running app and player state. Native held mode admits one input owner while
184
+ keys are held; the Take control button itself grants no additional authority.
185
+
186
+ Call `surface.destroy()` on unmount to abort signaling, cancel pending input, and
187
+ delete only this exact viewer session. An auth/signaling failure closes the
188
+ peer; create a new surface after restoring the authorized runtime session.
189
+ Offer creation and local SDP readiness are bounded separately inside the full
190
+ 60-second startup deadline, matching the web viewer's browser compatibility path.
191
+
192
+ See `examples/site-game.ts` for the minimal game wrapper.
193
+
12
194
  ## Install
13
195
 
14
196
  ```bash
@@ -94,6 +276,10 @@ socket config.
94
276
 
95
277
  Always include `x_aglit_ai_tool_reason` and pass structured arguments rather than raw shell strings. The IPC broker
96
278
  also enforces the same tool policies you see in the UI, including approval flows and logging.
279
+ Scripts started from an Aglit chat terminal inherit `AGLIT_AGENT_ID`, `AGLIT_SESSION_ID`, and
280
+ `AGLIT_SDK_MODULE_PATH`. Load the bundled SDK with `require(process.env.AGLIT_SDK_MODULE_PATH)`
281
+ to avoid an older workspace copy. `executeTool` uses the inherited session ID automatically; an
282
+ explicit request `_meta.sessionId` takes precedence.
97
283
 
98
284
  ## Examples
99
285
 
@@ -140,67 +326,23 @@ await executeTool({
140
326
 
141
327
  ### Browser Automation
142
328
 
143
- ```ts
144
- // Navigate first
145
- const nav = await executeTool({
146
- agentId: 'root-agent',
147
- request: {
148
- jsonrpc: '2.0',
149
- id: 'browser-nav',
150
- params: {
151
- name: 'p1_browser',
152
- arguments: {
153
- action: 'navigate',
154
- url: 'https://example.com',
155
- x_aglit_ai_tool_reason: 'SDK example: warm up page',
156
- },
157
- },
158
- context: { sessionId: 'sdk-browser', agentId: 'root-agent' },
159
- },
160
- });
161
-
162
- const windowId = (nav.data?.result as any)?.windowId as string;
163
-
164
- // Inject JavaScript in that session
165
- await executeTool({
166
- agentId: 'root-agent',
167
- request: {
168
- jsonrpc: '2.0',
169
- id: 'browser-js',
170
- params: {
171
- name: 'p1_browser',
172
- arguments: {
173
- action: 'execute_javascript',
174
- windowId,
175
- code: 'document.title',
176
- x_aglit_ai_tool_reason: 'SDK example: read page title',
177
- },
178
- },
179
- context: { sessionId: 'sdk-browser', agentId: 'root-agent' },
180
- },
181
- });
182
- ```
183
-
184
- ### Office App Automations (macOS)
329
+ Use the SDK compatibility adapter for structured browser actions. The adapter executes
330
+ only `p1_codex_browser` registry calls, preserves chat authorization and approvals,
331
+ and returns the exact native tab ID. Page evaluation is read-only; use native
332
+ click/fill actions to change a page.
185
333
 
186
334
  ```ts
187
- await executeTool({
188
- agentId: 'root-agent',
189
- request: {
190
- jsonrpc: '2.0',
191
- id: 'app-01',
192
- params: {
193
- name: 'p1_office',
194
- arguments: {
195
- tool: 'app',
196
- app: 'calendar',
197
- action: 'list',
198
- x_aglit_ai_tool_reason: 'SDK example: list calendar events',
199
- },
200
- },
201
- context: { sessionId: 'sdk-app', agentId: 'root-agent' },
202
- },
335
+ import { executeNativeControl, unwrapNativeControlResult } from '@aglit-ai/sdk';
336
+
337
+ const call = (args: Record<string, unknown>, reason: string) => executeNativeControl({
338
+ toolName: 'p1_codex_browser', agentId: 'root-agent', sessionId: 'sdk-browser', args, reason,
203
339
  });
340
+ const nav = unwrapNativeControlResult(await call(
341
+ { action: 'navigate', url: 'https://example.com' }, 'Open example.com'
342
+ ));
343
+ if (!nav?.tabId) throw new Error('Native browser returned no exact tab ID');
344
+ await call({ action: 'execute_javascript', tabId: nav.tabId, code: 'document.title' },
345
+ 'Read the page title');
204
346
  ```
205
347
 
206
348
  ### Create Chat Sessions
@@ -240,3 +382,39 @@ The SDK resolves the current OpenAPI source files from `@aglit-ai/types`:
240
382
  - Desktop API: `@aglit-ai/types/schema/api.openapi.yaml`
241
383
  - Tool Builder API: `@aglit-ai/types/schema/tools-api.openapi.yaml`
242
384
  - Provider SPI API: `@aglit-ai/types/schema/provider-spi.openapi.yaml`
385
+
386
+ ## Native control migration
387
+
388
+ `executeNativeControl({toolName, args, reason, agentId, sessionId})` adapts structured
389
+ custom-tool browser/app actions to `p1_codex_browser` and `p1_codex_computer`. Existing
390
+ native execute/capture/stream/focus calls pass through. The retired IDs remain rejected.
391
+
392
+ ```ts
393
+ import { executeNativeControl } from '@aglit-ai/sdk';
394
+ const result = await executeNativeControl({
395
+ toolName: 'p1_codex_browser',
396
+ args: {action: 'navigate', url: 'https://example.com'},
397
+ reason: 'Open the requested example page',
398
+ agentId, sessionId,
399
+ });
400
+ ```
401
+
402
+ The adapter keeps exact native targets, preserves typing/submit flags, returns original
403
+ proof and familiar result fields, and never replays an uncertain mutation. Page evaluation
404
+ is read-only. Authored DOM input plans use `aglitNativeClick(element)` and
405
+ `aglitNativeFill(element, text)` during inspection; the adapter performs those actions
406
+ through native input APIs afterward. No page mutation, CDP or terminal fallback is used.
407
+ Custom runtimes package this same SDK adapter locally, so they need no new SDK dependency.
408
+
409
+ Native control still requires the agent's existing unrestricted profiles and explicit
410
+ access. Upload/file permissions remain separate native grants. Downloads return a saved
411
+ path directly; old download progress stream IDs are unsupported. App window requests are
412
+ verified against the private capture receipt before input; ambiguous/mismatched targets
413
+ fail. Encoded recording and some legacy desktop-only APIs have no native equivalent.
414
+
415
+ ## Build and deployment
416
+
417
+ Run `pnpm run build` before `pnpm run deploy`. Deploy requires the compiled
418
+ entrypoints and bundled OpenAPI schemas, then publishes the already built SDK
419
+ using the version committed in `package.json` and existing registry credentials.
420
+ Deploy does not rebuild, bump versions or launch an interactive registry login.
package/browser.d.ts ADDED
@@ -0,0 +1 @@
1
+ export * from './dist/browser';
package/browser.js ADDED
@@ -0,0 +1,2 @@
1
+ // Separate entry keeps Node-only local credential and IPC modules out of browser bundles.
2
+ module.exports = require('./dist/browser.js');