@jskit-ai/agent-docs 0.1.194 → 0.1.196
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/guide/agent/app-extras/assistant-conversation.md +2608 -72
- package/guide/agent/app-extras/assistant-voice.md +244 -0
- package/guide/agent/app-extras/assistant.md +189 -20
- package/guide/agent/app-extras/realtime.md +6 -0
- package/guide/agent/index.md +1 -0
- package/package.json +1 -1
- package/patterns/feature-package/example/booking-engine/package.json +1 -1
- package/patterns/minimal-foundation/example/package.json +4 -4
- package/patterns/shell-foundation/example/package.json +5 -5
- package/patterns/shell-foundation/example/packages/main/package.json +1 -1
- package/reference/autogen/PATTERN_INDEX.md +35 -35
- package/reference/autogen/README.md +2 -0
- package/reference/autogen/packages/assistant-core.md +974 -9
- package/reference/autogen/packages/assistant-runtime.md +126 -28
- package/reference/autogen/packages/assistant-voice-sherpa.md +82 -0
- package/reference/autogen/packages/assistant-voice.md +227 -0
- package/reference/autogen/packages/kernel.md +10 -0
- package/reference/autogen/packages/realtime.md +5 -2
- package/skills/jskit/references/pattern-index.md +35 -35
- package/skills/jskit/references/patterns/app/minimal-foundation/example/package.json +4 -4
- package/skills/jskit/references/patterns/app/shell-foundation/example/package.json +5 -5
- package/skills/jskit/references/patterns/app/shell-foundation/example/packages/main/package.json +1 -1
- package/skills/jskit/references/patterns/auth/supabase-auth/example/package.json +1 -1
- package/skills/jskit/references/patterns/connectors/calendar-cli/example/package.json +4 -4
- package/skills/jskit/references/patterns/crud/json-api-resource-package/example/packages/books/package.json +2 -2
- package/skills/jskit/references/patterns/database/mysql-application/example/package.json +1 -1
- package/skills/jskit/references/patterns/database/postgres-application/example/package.json +1 -1
- package/skills/jskit/references/patterns/realtime/realtime-application/example/package.json +2 -2
- package/skills/jskit/references/patterns/server/feature-package/example/booking-engine/package.json +1 -1
- package/skills/jskit/references/patterns/users/user-administration-server/example/packages/users/package.json +2 -2
- package/skills/jskit/references/patterns/users/user-administration-server/example/packages/users-workspace/package.json +3 -3
|
@@ -2,27 +2,487 @@
|
|
|
2
2
|
|
|
3
3
|
# Embeddable assistant conversations
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
Start with the [standard Assistant integration](./assistant.md): one common
|
|
6
|
+
runtime, supplied server integration and `AssistantConversationClientElement`.
|
|
7
|
+
This reference describes that setup, its configuration and optional lower-level
|
|
8
|
+
contracts. Custom storage and native engines use the same supplied binding; they
|
|
9
|
+
do not require private conversation routes or a hand-written element adapter.
|
|
10
|
+
|
|
11
|
+
`@jskit-ai/assistant-core` owns the runtime, storage contract, transcript policy
|
|
12
|
+
and base `AssistantConversationElement`. `@jskit-ai/assistant-runtime` supplies
|
|
13
|
+
authorized HTTP/realtime integration and the component's client binding. Database
|
|
14
|
+
repositories and settings are optional. Neither ordinary file storage nor the
|
|
15
|
+
browser binding requires an editor, application database or particular avatar.
|
|
16
|
+
Choose one transcript owner for a conversation. The [ordinary API](./assistant.md#the-ordinary-api)
|
|
17
|
+
is the starting point; [advanced hosts](#advanced-host-facilities) and
|
|
18
|
+
[testing](#testing-entrypoints) are separate reference areas.
|
|
19
|
+
|
|
20
|
+
### Supplied server integration
|
|
21
|
+
|
|
22
|
+
`@jskit-ai/assistant-runtime/server` exports `AssistantFeature`,
|
|
23
|
+
`createAssistantActions`, `registerAssistantRoutes` and
|
|
24
|
+
`registerConversationSubscriptions`. The feature accepts an application-supplied
|
|
25
|
+
`assistant.conversations` capability containing `createConversationRuntime()`
|
|
26
|
+
or an application facade with the same authorized `open({ id, context })` contract.
|
|
27
|
+
That runtime may use file storage or any adapter satisfying the storage contract.
|
|
28
|
+
Its authorized operations use the existing action catalogue and HTTP router.
|
|
29
|
+
Read, send, cancel and delivery inspection use the existing `createAssistantApi`
|
|
30
|
+
request client, including its surface header and workspace path. Inspection is
|
|
31
|
+
an authenticated POST operation because it may recover and save native receipts;
|
|
32
|
+
it never dispatches another model request. Browser inputs cannot set the actor,
|
|
33
|
+
native host or engine context.
|
|
34
|
+
`readConversation(id, { beforeTurnId, limit, signal })` optionally requests an
|
|
35
|
+
earlier or bounded page through the same authenticated GET. Its query contains
|
|
36
|
+
only the supplied `beforeTurnId` and `limit`; omitting both keeps the facade's
|
|
37
|
+
existing read default. The response retains current state alongside the page's
|
|
38
|
+
`conversationLog` and `pagination`. The existing transcript reader selects the
|
|
39
|
+
turns, caps positive limits at 100 and treats zero as unbounded. An application
|
|
40
|
+
facade forwards explicit page options to that reader; it must not read all
|
|
41
|
+
history and then slice the result.
|
|
42
|
+
Goal read/update use that same authorized boundary. Unsupported engines return
|
|
43
|
+
no current goal and reject goal commands; the runtime retains current segment,
|
|
44
|
+
goal identity and budget checks. Goal attachments are authorized identifiers,
|
|
45
|
+
never browser-supplied files or paths. A canonical client needs only `request`;
|
|
46
|
+
`requestStream` is required only when using the older `streamChat` method.
|
|
47
|
+
When the host supplies `runtime.realtime` and `runtime.events`, subscriptions
|
|
48
|
+
share that connection and detach when the feature shuts down. Detaching the
|
|
49
|
+
feature does not close the application-owned runtime or stop its active turns.
|
|
50
|
+
|
|
51
|
+
Applications can expose their existing authorized settings, model selection and
|
|
52
|
+
replacement operations through the same facade. Declare
|
|
53
|
+
`conversationConfigurationSchema`, `conversationSelectionSchema` or
|
|
54
|
+
`conversationReplacementSchema` on `assistant.conversations` using the existing
|
|
55
|
+
`createSchema` input format. The feature registers only declared operations under
|
|
56
|
+
the same surface path:
|
|
57
|
+
|
|
58
|
+
| Schema | HTTP operation | Request body | Facade method |
|
|
59
|
+
| --- | --- | --- | --- |
|
|
60
|
+
| `conversationConfigurationSchema` | `PATCH /conversations/:conversationId/configuration` | `{ configuration: value }` | `configure(value)` |
|
|
61
|
+
| `conversationSelectionSchema` | `POST /conversations/:conversationId/selection` | `{ selection: value }` | `select(value)` |
|
|
62
|
+
| `conversationReplacementSchema` | `POST /conversations/:conversationId/replacement` | `{ replacement: value }` | `replace(value)` |
|
|
63
|
+
|
|
64
|
+
Each schema describes the inner application choice. The facade rechecks current
|
|
65
|
+
access and resolves that choice through the application's existing policy before
|
|
66
|
+
using the common runtime. Schemas validate input; they do not grant access to
|
|
67
|
+
models or credentials. Keep engine, host configuration and continuity briefings
|
|
68
|
+
server-owned. A replacement choice should retain its original `operationId` and
|
|
69
|
+
observed `expectedSegmentId`: the runtime rejects a stale predecessor and can
|
|
70
|
+
identify retries of the same operation. The application still decides which
|
|
71
|
+
replacement choices are permitted.
|
|
72
|
+
|
|
73
|
+
Omitting a schema leaves its operation unregistered. Colleague, for example,
|
|
74
|
+
exposes its existing catalogue selection action and keeps its system prompt
|
|
75
|
+
server-owned. `wake()` remains server-side; this integration registers no browser
|
|
76
|
+
wake operation.
|
|
77
|
+
|
|
78
|
+
Use `createAssistantApi().configureConversation(id, value, { signal })`,
|
|
79
|
+
`selectConversation(id, value, { signal })` or
|
|
80
|
+
`replaceConversation(id, value, { signal })` from the existing picker or controls.
|
|
81
|
+
These preserve the shared surface header and encoded conversation identity.
|
|
82
|
+
When registering the lower-level routes/actions directly, pass the same schema
|
|
83
|
+
options to both `registerAssistantRoutes` and `createAssistantActions`.
|
|
84
|
+
|
|
85
|
+
Installed package discovery registers `AssistantFeature` automatically. A host
|
|
86
|
+
provider should supply `assistant.conversations`; it must not also register the
|
|
87
|
+
same conversation routes, actions or socket subscriptions. One feature owns those
|
|
88
|
+
registrations for the application's shared realtime connection.
|
|
89
|
+
|
|
90
|
+
For a plain Fastify application, `registerFastifyConversations` supplies the same
|
|
91
|
+
hosting assembly as configurable shorthand:
|
|
8
92
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
93
|
+
```js
|
|
94
|
+
import { registerFastifyConversations } from "@jskit-ai/assistant-runtime/server";
|
|
95
|
+
|
|
96
|
+
await registerFastifyConversations(app, {
|
|
97
|
+
runtime, // application-created common runtime and storage
|
|
98
|
+
config: appConfig, // surfaces and HTTP/realtime configuration
|
|
99
|
+
env: process.env,
|
|
100
|
+
async authenticate(request) { // trusted HTTP or reconstructed socket request
|
|
101
|
+
return authenticateSession(request); // application context, or null to deny
|
|
102
|
+
},
|
|
103
|
+
bootstrap: ({ context }) => ({ application: { subjectId: context.subjectId } })
|
|
104
|
+
});
|
|
105
|
+
app.addHook("onClose", () => runtime.close());
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
An application with one surface can declare it once for both server and client:
|
|
109
|
+
|
|
110
|
+
```js
|
|
111
|
+
import { defineAssistantSurface } from "@jskit-ai/assistant-runtime/shared";
|
|
112
|
+
|
|
113
|
+
const appConfig = {
|
|
114
|
+
...defineAssistantSurface("home"),
|
|
115
|
+
// Other existing HTTP, realtime and application configuration.
|
|
116
|
+
};
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
The required ID names that one surface; the defaults are root placement and
|
|
120
|
+
global assistant settings. Options such as `pagesRoot`, `requiresWorkspace`,
|
|
121
|
+
`accessPolicyId` and `enabled` retain their existing surface meanings. A workspace
|
|
122
|
+
surface defaults to workspace settings. The helper expands the existing
|
|
123
|
+
`surfaceDefinitions` and `assistantSurfaces` maps; it does not infer extra surfaces
|
|
124
|
+
or grant access. Use those maps directly when an application needs multiple
|
|
125
|
+
surfaces or a separate settings surface. Authentication and Origin/CSRF policy
|
|
126
|
+
still belong to the host.
|
|
127
|
+
|
|
128
|
+
`runtime`, `config` and `authenticate` are required. `env` defaults to
|
|
129
|
+
`process.env`; `bootstrap` is optional and adds application bootstrap data after
|
|
130
|
+
authentication. The helper does not create conversations, select a model, choose
|
|
131
|
+
storage or infer authorization from browser fields. Authentication may be async;
|
|
132
|
+
return a context object or throw an application error. A missing context is denied.
|
|
133
|
+
Apply the application's normal session, Origin/CSRF and socket-handshake rules
|
|
134
|
+
there. The returned trusted fields extend the action context; the runtime's own
|
|
135
|
+
`authorize` still controls each conversation operation and subscription.
|
|
136
|
+
|
|
137
|
+
The helper assembles `AssistantFeature`, the HTTP/action/bootstrap/event providers
|
|
138
|
+
and one realtime provider. It closes that hosting on Fastify `preClose`. The
|
|
139
|
+
supplied runtime remains application-owned: close it in `onClose`, as above.
|
|
140
|
+
Register once per Fastify app; all its chats share the supplied realtime service.
|
|
141
|
+
Transport paths/options remain in `config`, and the caller owns Fastify options
|
|
142
|
+
and plugins. Install `@jskit-ai/realtime` when using this helper. It does not add a
|
|
143
|
+
database dependency. An existing JSKIT host should use normal feature registration
|
|
144
|
+
on its existing services instead. Custom hosts can still use the lower-level
|
|
145
|
+
feature, route, action and subscription contracts described below.
|
|
146
|
+
|
|
147
|
+
Conversation IDs are route parameters. Fastify defaults to a 100-character
|
|
148
|
+
parameter limit; if the application's IDs exceed that, set a bounded
|
|
149
|
+
`routerOptions.maxParamLength` when creating Fastify, for example
|
|
150
|
+
`Fastify({ routerOptions: { maxParamLength: 1024 } })`. The helper accepts an
|
|
151
|
+
existing server and does not change its router configuration.
|
|
152
|
+
|
|
153
|
+
When an existing host uses a different request-access policy, it can supply the
|
|
154
|
+
optional server capability `assistant.conversation.access`:
|
|
155
|
+
|
|
156
|
+
```js
|
|
157
|
+
{
|
|
158
|
+
router, // existing router.register interface with the host's request guard
|
|
159
|
+
wrapAction, // wraps an existing definition with the host's action-access owner
|
|
160
|
+
subscribeActionId, // fixed server-owned product action id
|
|
161
|
+
requestPolicy: "host"
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
The feature passes only canonical conversation routes through that router and
|
|
166
|
+
only canonical conversation definitions through `wrapAction`. It sets the
|
|
167
|
+
subscribe definition's ID before wrapping it. The host must preserve the existing
|
|
168
|
+
handler, input validation and conversation authorization while applying its
|
|
169
|
+
request guard and trusted action context. Settings and legacy database routes
|
|
170
|
+
and definitions keep their existing authentication. An incomplete access facility
|
|
171
|
+
fails startup.
|
|
172
|
+
|
|
173
|
+
The default subscription policy is `authenticated`. An explicit `host` policy
|
|
174
|
+
requires a distinct fixed subscribe action and uses the shared connection's
|
|
175
|
+
`readRequest()`: this reconstructs the original handshake and revalidates any
|
|
176
|
+
configured authentication. Required hosted authentication still rejects missing,
|
|
177
|
+
revoked or changed identity. Local requests can lack an actor, so the host action
|
|
178
|
+
must apply its original local request and WebSocket-origin checks. Socket rooms,
|
|
179
|
+
action selection and request policy remain server-owned. Local events omit actor
|
|
180
|
+
metadata; hosted events retain the real actor and existing per-event access checks.
|
|
181
|
+
|
|
182
|
+
Database support is an optional peer dependency. Supplying a conversation runtime
|
|
183
|
+
does not import database repositories, create the legacy chat service or read a
|
|
184
|
+
host database, even if an unrelated database capability is present. An optional
|
|
185
|
+
`assistant.settings` service enables the existing settings routes. Without a
|
|
186
|
+
supplied conversation runtime, the existing database integration requires
|
|
187
|
+
`runtime.database`; its repositories and migrations remain available. The
|
|
188
|
+
database facade uses the same common runtime with its SQL conversation storage;
|
|
189
|
+
that optional storage does not change the server-integration contract.
|
|
190
|
+
|
|
191
|
+
### Supplied client integration
|
|
192
|
+
|
|
193
|
+
Use `AssistantConversationClientElement` from `@jskit-ai/assistant-runtime/client`
|
|
194
|
+
for a conversation initialized by the application-owned common runtime:
|
|
195
|
+
|
|
196
|
+
```vue
|
|
197
|
+
<AssistantConversationClientElement
|
|
198
|
+
ref="conversation"
|
|
199
|
+
:conversation-id="conversationId"
|
|
200
|
+
:attachments="attachments"
|
|
201
|
+
:suggestions="suggestions"
|
|
202
|
+
/>
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
Only `conversation-id` is required for the standard card. The optional feature
|
|
206
|
+
props above use the same binding. The element uses the current configured
|
|
207
|
+
surface/workspace HTTP path and the application's
|
|
208
|
+
existing authenticated realtime socket. It does not create another connection.
|
|
209
|
+
`surface-id`, `endpoint`, `hostSurfaceId` and `workspaceSlug` can supply an application's
|
|
210
|
+
existing placement mapping; the server still resolves and authorizes that scope.
|
|
211
|
+
It exposes `focus()` and `runtime`. Its slots receive their existing scoped
|
|
212
|
+
fields plus that same retained `runtime`; a `composer-tools` button can pass it
|
|
213
|
+
to the voice controller without constructing another binding.
|
|
214
|
+
|
|
215
|
+
Without application overrides, the binding uses the existing authenticated
|
|
216
|
+
placement identity, standard HTTP client and surface path. Applications with a
|
|
217
|
+
different bootstrap subject or request policy configure it once before mounting:
|
|
218
|
+
|
|
219
|
+
```js
|
|
220
|
+
import { configureAssistantConversations } from "@jskit-ai/assistant-runtime/client";
|
|
221
|
+
|
|
222
|
+
configureAssistantConversations(app, {
|
|
223
|
+
actorKey: () => bootstrap.value.subjectId,
|
|
224
|
+
request: http.request, // the application's existing HTTP and Origin/CSRF policy
|
|
225
|
+
clearDraftOn: "accepted"
|
|
226
|
+
});
|
|
227
|
+
app.mount("#app");
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
These defaults belong to that Vue app. `actorKey` accepts a value, ref or getter;
|
|
231
|
+
changing or clearing it retires the previous actor's retained browser state.
|
|
232
|
+
The browser subject key never grants server access. Server authentication,
|
|
233
|
+
conversation authorization and subscription guards remain unchanged. The voice
|
|
234
|
+
starter supplies its existing local request policy here, once for all five cards.
|
|
235
|
+
|
|
236
|
+
The optional per-card `actor-key` overrides the default. An omitted or undefined
|
|
237
|
+
value inherits it; an explicit empty value makes the reader inactive rather than
|
|
238
|
+
reusing the previous actor. `api` can supply a static client created with
|
|
239
|
+
`createAssistantApi`, either once in the app defaults or as a per-card override.
|
|
240
|
+
An omitted or null per-card `api` inherits the app's API or request function.
|
|
241
|
+
Otherwise the standard HTTP client is used. No option creates another realtime
|
|
242
|
+
connection. `clear-draft-on="accepted"` keeps typed input until acceptance;
|
|
243
|
+
`"dispatch"` remains the default when neither the app nor the card changes it.
|
|
244
|
+
Use consistent API and draft options for readers of the same retained target.
|
|
245
|
+
|
|
246
|
+
Custom presentation can call `useAssistantConversation()` with the same
|
|
247
|
+
identity/options and render `AssistantConversationElement` with its `adapter`.
|
|
248
|
+
The composable exposes the same `runtime` and `error` refs; it is unnecessary for
|
|
249
|
+
an application that only adds standard component slots.
|
|
250
|
+
|
|
251
|
+
The composable's `presentation` option accepts a value, ref or getter. Supply transcript
|
|
252
|
+
fields `assistantLabel`, `systemLabel`, `welcomeMessage`, `variant`, `visible`,
|
|
253
|
+
`userMessageFormat` and `progressPreviewLimit`, and composer fields `ariaLabel`,
|
|
254
|
+
`submitAriaLabel`, `submitLabel`, `placeholder` and `rows`. `layout: "compact"`
|
|
255
|
+
keeps the existing compact composer. These change presentation through the
|
|
256
|
+
supplied adapter; they do not replace its history, actions or delivery state.
|
|
257
|
+
An empty `welcomeMessage` hides the welcome text. `previewMessage` supplies the
|
|
258
|
+
single live user preview described below while the conversation is available.
|
|
259
|
+
|
|
260
|
+
Text views and `runtime.retain()` readers share draft, delivery and subscription
|
|
261
|
+
state for an exact actor, endpoint, surface, workspace and conversation. Release
|
|
262
|
+
a retained handle with `release()`. Final view/reader release detaches browser
|
|
263
|
+
observers; it does not stop server work. A lost receipt remains uncertain with
|
|
264
|
+
**Check delivery** until inspection or canonical history proves acceptance.
|
|
265
|
+
Reconnection reads state and does not resend the request.
|
|
266
|
+
|
|
267
|
+
Applications that create nonvisual tasks after setup can capture
|
|
268
|
+
`useAssistantConversationFactory(commonOptions)` once during setup, then call
|
|
269
|
+
the returned `acquire(taskOptions)` function as tasks are created or restored.
|
|
270
|
+
It returns the same `runtime`, `adapter` and `error` refs plus `release()`.
|
|
271
|
+
Each task uses the existing retained conversation registry; another reader of
|
|
272
|
+
the same identity shares its draft and delivery state. Release a task's handle
|
|
273
|
+
when the application removes that task. Ending the setup scope releases its
|
|
274
|
+
remaining handles, and the factory cannot acquire new handles afterward.
|
|
275
|
+
Hidden tasks can keep `active: true` while only their presentation is hidden.
|
|
276
|
+
|
|
277
|
+
When an application must save or create its logical conversation before Send,
|
|
278
|
+
use that exact application-selected ID and keep its reader inactive until
|
|
279
|
+
creation succeeds. The binding's `submitPrepared(payload, { messageId,
|
|
280
|
+
prepare, onAccepted })` uses the standard composer's draft and attachment cleanup
|
|
281
|
+
around the retained runtime's existing prepared submission. Supply ordinary
|
|
282
|
+
message/display attachments and declared `data`; an already authored canonical
|
|
283
|
+
`payload.request` is also accepted. `working` supplies the application's native
|
|
284
|
+
turn eligibility when its product state also includes non-native work such as
|
|
285
|
+
routing. The binding captures Send or Steer before preparation; retry keeps that
|
|
286
|
+
choice and the original message identity, text, settings and files.
|
|
287
|
+
|
|
288
|
+
`prepare(captured, { signal })` performs only the application's existing save,
|
|
289
|
+
creation and authorization prerequisites, then returns its canonical request or
|
|
290
|
+
`false` to cancel before dispatch. The optimistic entry exists before those awaits.
|
|
291
|
+
Presentation metadata may reflect a newer draft. `onSubmit` runs before preparation
|
|
292
|
+
for application pending-state updates; `isCurrent` preserves the application's
|
|
293
|
+
additional close/lifetime guard. `clearDraft: false` leaves a user's draft untouched
|
|
294
|
+
for an application-generated message. The optional attachment owner has the same
|
|
295
|
+
`clearAttachments({ accepted: true, attachmentIds })` contract as the standard
|
|
296
|
+
composer; acceptance removes only the submitted files, preserving later uploads.
|
|
297
|
+
|
|
298
|
+
Pass `retryMessageId` for an existing failed or uncertain message. An application
|
|
299
|
+
may supply its authoritative `retry` receipt projection when routing persists it
|
|
300
|
+
outside the current local delivery list. Failed delivery reuses the captured
|
|
301
|
+
payload; uncertain delivery only inspects the original receipt, never running
|
|
302
|
+
preparation or sending again. An accepted restored receipt applies idempotent
|
|
303
|
+
composer cleanup. Ordinary `runtime.submitPrepared` remains available to hosts
|
|
304
|
+
that already own their composer; it requires an authored `payload.request`.
|
|
305
|
+
|
|
306
|
+
Both paths use the existing API, abort and receipt owner. It rechecks the actor,
|
|
307
|
+
known access denial, disposal and abort after preparation. A preparation exception
|
|
308
|
+
returns a failed result with its original message/code; ambiguous failure after
|
|
309
|
+
dispatch returns `{ ok: false, status: "uncertain", error, code? }`. The server's
|
|
310
|
+
original admission policy still authorizes every request. Prepared submission
|
|
311
|
+
does not relax ordinary `send()` readiness or capability checks, create a
|
|
312
|
+
placeholder snapshot, or infer a conversation identity.
|
|
313
|
+
|
|
314
|
+
After creation, an active retained handle can call `runtime.cancel()` even while
|
|
315
|
+
its first read or Start response is pending. Inactive, unidentified, retired or
|
|
316
|
+
access-denied targets cannot dispatch cancellation. The standard element still
|
|
317
|
+
uses the loaded snapshot's Stop availability; product task controls can retain
|
|
318
|
+
their original earlier Stop boundary.
|
|
319
|
+
|
|
320
|
+
For a voice binding, `projectConversationVoiceState({ turns, status, interimReply })` from
|
|
321
|
+
`@jskit-ai/assistant-voice/client` projects those same retained turns into the
|
|
322
|
+
voice owner's `messages` and `streamingReply`. It keeps user message IDs and
|
|
323
|
+
stable assistant IDs across native partial/final representations. Commentary
|
|
324
|
+
and reasoning remain activity rather than spoken answers. A transient assistant
|
|
325
|
+
message explicitly marked `origin: "application"` is held until its saved
|
|
326
|
+
notification; it does not suppress that completed notification. The projector
|
|
327
|
+
stores no history and owns no subscription or playback queue. Application
|
|
328
|
+
progress acknowledgements remain an explicit presentation policy.
|
|
329
|
+
|
|
330
|
+
An application can supply a selected transient `interimReply` through its
|
|
331
|
+
authorized conversation read and events. The standard binding displays it only
|
|
332
|
+
on its exact `turnId`, without changing saved messages or acknowledging pending
|
|
333
|
+
delivery. Supply `null` when the actual answer replaces it or the operation
|
|
334
|
+
ends. The voice projection uses that same descriptor. A provider's optional
|
|
335
|
+
`outputId` identifies one output across its live and saved representations, so
|
|
336
|
+
a spoken acknowledgement and a later answer remain distinct. Older messages
|
|
337
|
+
without this field retain their existing identity; neither reads nor projections
|
|
338
|
+
backfill history. This field does not replace message IDs or admission receipts.
|
|
339
|
+
|
|
340
|
+
`useAssistantConversation({ clearDraftOn: "accepted", queueWhileSending: false })`
|
|
341
|
+
preserves an application's existing acceptance-based composer. The typed draft
|
|
342
|
+
stays visible until its exact delivery is accepted. A canonical receipt or
|
|
343
|
+
explicit delivery inspection can clear it before the HTTP response arrives;
|
|
344
|
+
a newer draft is kept. After a pre-admission failure, pressing Send again on the
|
|
345
|
+
same trimmed draft reuses that submission's message ID and captured data. A
|
|
346
|
+
different submission replaces that reference; it never selects an older matching
|
|
347
|
+
failure from history. Uncertain delivery still requires **Check delivery**.
|
|
348
|
+
Voice sends keep their own authored capture and do not consume the typed draft.
|
|
349
|
+
|
|
350
|
+
With `queueWhileSending: false`, the composer is disabled during admission and
|
|
351
|
+
overlapping typed or retained voice sends return `false` before calling the
|
|
352
|
+
supplied API. A runtime that supports sending while working still permits the
|
|
353
|
+
next request after admission settles; the host owns its meaning. Queuing is part
|
|
354
|
+
of the initial retained conversation composition, so use consistent options for
|
|
355
|
+
mirrored readers of the same identity. The default is `clearDraftOn: "dispatch"`
|
|
356
|
+
and the existing queuing behavior for a runtime that advertises steering.
|
|
357
|
+
|
|
358
|
+
Set `draftWhileLoading: true` when people should be able to type before the
|
|
359
|
+
application resolves its conversation ID or while the first read is unavailable.
|
|
360
|
+
This keeps one local input scoped to the real actor, endpoint, surface and
|
|
361
|
+
workspace. It creates no conversation, receipt or placeholder identity, and Send
|
|
362
|
+
still requires the existing ready, authorized conversation. An explicit submit
|
|
363
|
+
before readiness returns `false`; the application can show its existing loading
|
|
364
|
+
or offline feedback while the words remain in the composer.
|
|
365
|
+
|
|
366
|
+
Once the actual ID resolves, the input moves into that conversation's retained
|
|
367
|
+
draft. If it already has a different draft, the existing text-submission helper
|
|
368
|
+
appends the new text with a newline without sending; identical text stays once.
|
|
369
|
+
A known target keeps its own draft when loading or reconnecting, and changing
|
|
370
|
+
targets never copies the previous target's draft. Actor or scope changes clear
|
|
371
|
+
unresolved input synchronously; authorization denial still clears and disables
|
|
372
|
+
the resolved composer. This option does not share unresolved input between
|
|
373
|
+
separate views: shared retention begins with the actual conversation ID.
|
|
374
|
+
The default is `false`.
|
|
375
|
+
|
|
376
|
+
Custom presentation can supply `useAssistantConversation({ onEvent(event) {} })`
|
|
377
|
+
to receive notifications such as an application's `{ type: "application" }`
|
|
378
|
+
invalidation. The existing subscriber checks the event identity/order and applies
|
|
379
|
+
its stream snapshot, or schedules its canonical read, before invoking the hook.
|
|
380
|
+
Keep transcript rendering on `adapter`/`runtime`; the hook does not replace that
|
|
381
|
+
state handling. An application can use its own invalidation to coalesce an
|
|
382
|
+
authorized product-state refresh without fetching product state for every text
|
|
383
|
+
chunk. Only current, active readers receive callbacks. Releasing a view removes
|
|
384
|
+
its callback even when a mirrored view or `runtime.retain()` keeps the shared
|
|
385
|
+
subscription alive; retaining a runtime does not retain a released view's hook.
|
|
386
|
+
Hooks report their own errors. A throwing or rejected hook cannot interrupt
|
|
387
|
+
other readers or shared delivery, and no additional socket connection is created.
|
|
388
|
+
|
|
389
|
+
An application can supply a `data` object to the standard element, or a value,
|
|
390
|
+
ref or getter to `useAssistantConversation({ data })`. A new typed submission
|
|
391
|
+
captures it with the message in the existing delivery entry; a retry keeps the
|
|
392
|
+
original data even if the current selection has changed. A retained voice reader
|
|
393
|
+
can pass its own utterance capture with
|
|
394
|
+
`runtime.send({ message: text, data: capturedData }, { messageId })`; this leaves
|
|
395
|
+
the typed draft and its data untouched. The server must explicitly declare an
|
|
396
|
+
allowed `conversationDataSchema`. Data stays untrusted message input and never
|
|
397
|
+
supplies authenticated identity, authorization context or native configuration.
|
|
398
|
+
With `AssistantFeature`, expose that `json-rest-schema` schema as
|
|
399
|
+
`assistant.conversations.conversationDataSchema`; the feature forwards it to both
|
|
400
|
+
existing route and action validators. Explicit library composition passes the
|
|
401
|
+
same schema to `registerAssistantRoutes` and `createAssistantActions` through
|
|
402
|
+
their `conversationDataSchema` option. Without a declared schema, browser data is
|
|
403
|
+
not forwarded. The application still checks access to any resource identified by
|
|
404
|
+
those fields before using it.
|
|
405
|
+
|
|
406
|
+
`attachments` and `suggestions` accept their existing shared composable results
|
|
407
|
+
directly, or reactive presentation adapters. Upload and suggestion services remain
|
|
408
|
+
application-supplied, as described below. Set `questions` to `true` for the
|
|
409
|
+
standard retained answer form, or supply its small configuration described below.
|
|
410
|
+
Custom question and model presentation adapters remain available; model controls
|
|
411
|
+
do not expose a raw engine or credential-selection endpoint. Native goal controls use the runtime's
|
|
412
|
+
declared command and budget capabilities. Unsupported controls stay absent.
|
|
413
|
+
|
|
414
|
+
### Existing bounded-task commands
|
|
415
|
+
|
|
416
|
+
An application whose assistant already returns one final result from an
|
|
417
|
+
authenticated command can use the same `useAssistantConversation` binding with
|
|
418
|
+
an explicit `boundedTask`. This mode retains its browser-local request history
|
|
419
|
+
and mounted lifetime. It does not acquire a canonical conversation, realtime
|
|
420
|
+
subscription, delivery ledger or server transcript.
|
|
421
|
+
|
|
422
|
+
```js
|
|
423
|
+
const binding = useAssistantConversation({
|
|
424
|
+
boundedTask: {
|
|
425
|
+
command: existingCommand, // the actual useCommand owner: run and isRunning
|
|
426
|
+
endpoint: assistantEndpoint,
|
|
427
|
+
scope: () => selectedSessionId.value,
|
|
428
|
+
input: messages => ({ messages: messages.map(({ content, role, table }) => ({ content, role, table })) }),
|
|
429
|
+
result: response => ({ content: response.answer, role: "assistant", sql: response.sql }),
|
|
430
|
+
onResult(response, { message, scope }) {
|
|
431
|
+
// Optional application presentation, using the captured submitted record.
|
|
432
|
+
presentResult(response, { table: message.table, sessionId: scope });
|
|
433
|
+
}
|
|
434
|
+
},
|
|
435
|
+
active: helperAvailable,
|
|
436
|
+
data: () => ({ table: selectedTable.value }),
|
|
437
|
+
presentation: {
|
|
438
|
+
assistantLabel: "Copilot", variant: "task", visible: true,
|
|
439
|
+
placeholder: "Ask about this table…", submitOnEnter: false,
|
|
440
|
+
submitOnModifierEnter: true
|
|
441
|
+
}
|
|
442
|
+
});
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
Pass `binding.adapter.value` to `AssistantConversationElement`, retaining product
|
|
446
|
+
slots and actions. The binding owns `runtime.value.draft`, ordered `messages`,
|
|
447
|
+
`submit()` and `clearHistory()`. History-only clearing preserves the draft; call
|
|
448
|
+
it at the application's existing hydration boundary. The command keeps its
|
|
449
|
+
normal authenticated HTTP, running state and error feedback. Required command
|
|
450
|
+
and input/result mappings are validated; endpoint and scope are explicit.
|
|
451
|
+
|
|
452
|
+
Submission captures `data` into the user record, appends it and clears the draft
|
|
453
|
+
before invoking the command once. It maps a truthy final response only while the
|
|
454
|
+
component remains mounted and the captured application scope is still current,
|
|
455
|
+
then calls optional `onResult`. Scope is a local presentation fence, not request
|
|
456
|
+
authority. Input mapping and the server's existing action schema determine which
|
|
457
|
+
fields are sent. Command errors propagate unchanged; a failed request leaves its
|
|
458
|
+
local authored row and does not create a receipt or automatic retry. Successful
|
|
459
|
+
submission retains the original undefined return value.
|
|
460
|
+
|
|
461
|
+
`active` gates new submissions. Changing availability or hiding the panel does
|
|
462
|
+
not cancel admitted work. Disposal discards late presentation through the same
|
|
463
|
+
owner; native cleanup remains with the existing server task. The bounded mode
|
|
464
|
+
defaults to modifier-Enter submission and ordinary Enter for a new line. Its
|
|
465
|
+
index-derived message keys are display identities, never proof of admission.
|
|
14
466
|
|
|
15
467
|
## Ownership
|
|
16
468
|
|
|
17
469
|
| JSKIT owns | The application supplies |
|
|
18
470
|
| --- | --- |
|
|
19
|
-
| Bubbles, rich text, reasoning groups, long-message expansion, scroll following, composer, delivery controls, working status, suggestions, model and goal controls, file and question UI | Conversation selection, labels,
|
|
471
|
+
| Bubbles, rich text, reasoning groups, long-message expansion, scroll following, composer, delivery controls, working status, suggestions, model and goal controls, file and question UI | Conversation selection, labels, optional presentation and authorized feature services |
|
|
472
|
+
| Supplied client binding: draft, loading/error state, delivery, actions, streaming and reconnect | Real conversation identity and endpoint, authenticated host context and application policy |
|
|
20
473
|
| Turn grouping, message deduplication, final-answer replacement and history pagination | Storage adapter, authorized scope, transaction/locking implementation, retention, migrations and attachment bytes |
|
|
21
|
-
| Codex JSON-RPC and notification classification, detached-turn completion
|
|
474
|
+
| Native process/connection coordination, shared-runtime reconnect/recovery, Codex JSON-RPC and notification classification, detached-turn completion and OpenCode HTTP/SSE | Authorized connection/account facilities, existing managed execution/environment, permissions, tools and model policy |
|
|
22
475
|
| Display of supplied configuration and optional editing controls | Authoritative configuration, permitted changes and server validation |
|
|
23
476
|
|
|
477
|
+
`AssistantConversationClientElement` and `useAssistantConversation` construct the
|
|
478
|
+
generic adapter and retain its state for the conversation. Applications using
|
|
479
|
+
these supplied bindings do not implement unused optional adapter areas. A custom
|
|
480
|
+
low-level `AssistantConversationElement` adapter instead supplies its own draft,
|
|
481
|
+
loading/errors and action implementations through the contract below.
|
|
482
|
+
|
|
24
483
|
No global store or router is installed by the element. Multiple assistants can
|
|
25
|
-
coexist
|
|
484
|
+
coexist using the supplied binding or custom adapters. The application mounts it
|
|
485
|
+
in a pane with a
|
|
26
486
|
definite height and `min-height: 0`. The composer does not shrink under transcript
|
|
27
487
|
pressure. Bubbles use the application's Vuetify theme colors.
|
|
28
488
|
|
|
@@ -76,6 +536,16 @@ available for an explicit retry. Clear the error when retrying succeeds.
|
|
|
76
536
|
control the corresponding transcript presentation. `userMessageFormat` is
|
|
77
537
|
`"formatted"` by default; `"plain"` preserves literal user-authored text.
|
|
78
538
|
|
|
539
|
+
For live speech that has not been admitted, supply
|
|
540
|
+
`adapter.conversation.previewMessage = { messageId, text }` (the original
|
|
541
|
+
voice capture's `id` is also accepted). The element displays one pending user
|
|
542
|
+
message after merging canonical and delivery turns. A matching canonical or
|
|
543
|
+
delivery message ID suppresses that preview, so an admitted utterance appears
|
|
544
|
+
once. The application owns updating or clearing its current capture. Preview
|
|
545
|
+
text never enters saved history, the retained runtime's turns or receipt
|
|
546
|
+
reconciliation; it cannot acknowledge a pending or uncertain send. Keep it out
|
|
547
|
+
of `adapter.conversation.turns`.
|
|
548
|
+
|
|
79
549
|
### Message delivery
|
|
80
550
|
|
|
81
551
|
Use `createAssistantMessageDelivery` from `/client/conversation` (or the
|
|
@@ -115,7 +585,7 @@ or generated prompts; it still uses the same delivery controller.
|
|
|
115
585
|
|
|
116
586
|
`send(payload, options?)` inserts the message synchronously, before invoking
|
|
117
587
|
`deliver`. Payloads must be JSON-compatible and are copied at submission. They
|
|
118
|
-
require `message`; optional `displayMessage` and
|
|
588
|
+
require nonempty `message` or `displayAttachments`; optional `displayMessage` and
|
|
119
589
|
`displayAttachments` control the visible bubble. Additional fields travel to
|
|
120
590
|
`deliver` unchanged. Include settings and attachment IDs in the payload so a
|
|
121
591
|
retry retains the original request even if the composer settings later change. Each send creates a stable `messageId`, or
|
|
@@ -127,6 +597,17 @@ Success removes the pending treatment but retains the bubble until authoritative
|
|
|
127
597
|
history contains its `user.messageId`, so it cannot flicker away before history
|
|
128
598
|
arrives. Older histories without IDs can match by text and timestamp.
|
|
129
599
|
|
|
600
|
+
A host that saves an authored row before native admission must project
|
|
601
|
+
`user.receipt: false` on that row until it has exact admission evidence. This
|
|
602
|
+
transient read/patch qualifier keeps one visible bubble with its pending or
|
|
603
|
+
uncertain delivery state; it cannot settle a receipt race or discard a restored
|
|
604
|
+
request. Omission preserves the normal accepted authored-row contract. Keep the
|
|
605
|
+
qualifier out of persisted history, prompt history and message fingerprints.
|
|
606
|
+
An explicit successful delivery or exact receipt inspection can still accept
|
|
607
|
+
the request. A failed native read remains unknown; it must never trigger replay.
|
|
608
|
+
An error or unsuccessful response with `status: "uncertain"` retains the delivery
|
|
609
|
+
for inspection, and `send` continues to refuse that uncertain message ID.
|
|
610
|
+
|
|
130
611
|
For native-agent steering, set `composer.queueWhileSending: true` to accept more
|
|
131
612
|
text messages while delivery is outstanding. Custom composers call
|
|
132
613
|
`send(payload, { queue: true })`. Each message appears immediately and keeps its
|
|
@@ -156,6 +637,21 @@ any persistence needed to retain unsent messages across a browser reload.
|
|
|
156
637
|
Transport promises should settle on admission, not after the full AI answer;
|
|
157
638
|
streaming and provider execution remain separately observable application work.
|
|
158
639
|
|
|
640
|
+
For canonical receipt observation, supply reactive `receiptTurns` and
|
|
641
|
+
`uncertainOnError: true`. Unknown transport failures then retain an uncertain
|
|
642
|
+
entry for inspection; they do not expose Retry, Edit or Cancel. Explicit
|
|
643
|
+
pre-admission rejection can still return `{ ok: false, error }`. Use `accept(id)`
|
|
644
|
+
only after the backend confirms admission. `restoreUncertain(request, savedTurns)`
|
|
645
|
+
restores the public pending request without sending it again.
|
|
646
|
+
|
|
647
|
+
The optional `send(..., { onAccepted })` callback acknowledges a captured UI
|
|
648
|
+
attachment queue. HTTP acceptance, matching canonical reconciliation and explicit
|
|
649
|
+
`accept(id)` notify it once. Retry retains the original callback and payload.
|
|
650
|
+
The callback stays private in memory; it is absent from messages, serialized
|
|
651
|
+
payloads and persisted receipts. An acknowledgement callback must report its own
|
|
652
|
+
UI errors without reversing accepted delivery. A full browser reload still needs
|
|
653
|
+
the application's existing saved-draft attachment state.
|
|
654
|
+
|
|
159
655
|
### Turns and messages
|
|
160
656
|
|
|
161
657
|
```js
|
|
@@ -299,6 +795,37 @@ requests. Disposal cancels requests; late results are ignored. `items`, `loading
|
|
|
299
795
|
Selection edits the draft; submission remains explicit. The standalone example
|
|
300
796
|
includes a separate suggestion integration and server prompt.
|
|
301
797
|
|
|
798
|
+
For the supplied client element, bind that same owner to the exposed canonical
|
|
799
|
+
target. `app.suggest` below is the application's authorized suggestion operation,
|
|
800
|
+
separate from ordinary conversation send:
|
|
801
|
+
|
|
802
|
+
```js
|
|
803
|
+
const conversation = ref(null);
|
|
804
|
+
const target = computed(() => conversation.value?.runtime || null);
|
|
805
|
+
const suggestions = useAssistantSuggestions({
|
|
806
|
+
active: () => target.value?.available.value === true &&
|
|
807
|
+
target.value.snapshot.value?.status === "ready",
|
|
808
|
+
requestKey: () => target.value?.identity || null,
|
|
809
|
+
draft: () => target.value?.draft.value || "",
|
|
810
|
+
configuration: () => ({ integrationId: "suggestions" }),
|
|
811
|
+
generate(input) {
|
|
812
|
+
return app.suggest({ ...input, conversationId: target.value.identity.conversationId });
|
|
813
|
+
},
|
|
814
|
+
onSelect(prompt) {
|
|
815
|
+
if (!target.value?.available.value) return false;
|
|
816
|
+
target.value.draft.value = prompt;
|
|
817
|
+
return true;
|
|
818
|
+
}
|
|
819
|
+
});
|
|
820
|
+
```
|
|
821
|
+
|
|
822
|
+
Include the host's suggestion policy and visible-reader state in `active`.
|
|
823
|
+
The complete identity in `requestKey` invalidates prior actors and targets;
|
|
824
|
+
changing a suggestion model in `configuration` affects only that generator.
|
|
825
|
+
The host operation derives its authenticated actor and checks the conversation
|
|
826
|
+
itself. When a view closes while voice retains its draft, disable that view's
|
|
827
|
+
suggestion owner; late results are discarded by the existing composable.
|
|
828
|
+
|
|
302
829
|
### Models and application configuration
|
|
303
830
|
|
|
304
831
|
Use `adapter.models` for `AssistantModelControl`:
|
|
@@ -319,7 +846,18 @@ Use `adapter.models` for `AssistantModelControl`:
|
|
|
319
846
|
|
|
320
847
|
Selections edit application draft configuration. `apply()` saves it and closes
|
|
321
848
|
the chooser unless it resolves `false`. The app handles errors and sets `saving`
|
|
322
|
-
and `changesDisabled
|
|
849
|
+
and `changesDisabled`, and includes its admission rules in `canSave`. More than
|
|
850
|
+
six model choices get searchable selection.
|
|
851
|
+
The canonical element's `models` prop and `useAssistantConversation({ models })`
|
|
852
|
+
accept the same owner with plain fields, refs or computed fields. Its controls
|
|
853
|
+
are shown only while the canonical target is available. The application keeps
|
|
854
|
+
the actor-scoped catalogue, draft selection and authorized apply/reload callbacks;
|
|
855
|
+
the binding does not copy those settings or send native configuration.
|
|
856
|
+
|
|
857
|
+
An application with its own existing model dialog can put its launcher in
|
|
858
|
+
`composer-tools` and keep that dialog's selection and save callback. This preserves
|
|
859
|
+
the standard composer and delivery controls while retaining application-specific
|
|
860
|
+
catalogue and access policy.
|
|
323
861
|
The standalone component additionally provides `before-choices`, `model-note`,
|
|
324
862
|
`provider-controls`, and `footer` slots for account-specific controls. The
|
|
325
863
|
aggregate's `configurationMode` applies to its separate custom configuration
|
|
@@ -351,9 +889,31 @@ accumulated **active running time**, and `sampledAt` is its sample time in epoch
|
|
|
351
889
|
**milliseconds**. The UI adds time only while active. Omit elapsed time if the
|
|
352
890
|
backend cannot supply it truthfully. The clock hides in a narrow conversation
|
|
353
891
|
pane; the light stays visible and details retain the time. Reduced motion uses
|
|
354
|
-
a steady red light.
|
|
355
|
-
|
|
356
|
-
|
|
892
|
+
a steady red light. Pause follows the command's declared behavior:
|
|
893
|
+
`pauseInterruptsTurn: true` means it stops the current turn and keeps the goal;
|
|
894
|
+
otherwise it prevents further automatic turns while Stop interrupts current work.
|
|
895
|
+
Likewise, `cancelInterruptsTurn` determines whether Cancel stops current work while
|
|
896
|
+
clearing the goal. The supplied client binding derives these fields and supported
|
|
897
|
+
callbacks from runtime capabilities; applications using it need no custom goal
|
|
898
|
+
adapter. For a low-level adapter, omit `goal` or use `enabled: false` when goals
|
|
899
|
+
are unsupported. The supplied binding hides unsupported controls automatically.
|
|
900
|
+
|
|
901
|
+
The supplied binding owns goal reads, command state and message receipts. It
|
|
902
|
+
refreshes on goal events, reconnect and window focus, with a 30-second passive
|
|
903
|
+
read deadline and no goal poll. Passive failures remain local to the control.
|
|
904
|
+
An explicit `goal: true` option also enables reads when an application's retained
|
|
905
|
+
goal may belong to a different target from its visible conversation. Such an
|
|
906
|
+
authorized facade can return `{ status, goal, target: { segmentId, capabilities },
|
|
907
|
+
routing? }` from its existing goal read. The target and declared goal capabilities
|
|
908
|
+
must come from that same native read; the client does not infer an engine or
|
|
909
|
+
reconstruct native expectations. Standalone goal reads still return a goal or
|
|
910
|
+
null. An object-valued `goal` presentation adapter remains supported.
|
|
911
|
+
|
|
912
|
+
Custom product presentation can use the retained runtime's `goalView`,
|
|
913
|
+
`goalState`, `goalLoadError`, `changeGoal` and `refreshGoal` instead of creating
|
|
914
|
+
another remote resource. New commands capture that view's target and goal ID.
|
|
915
|
+
A failed message command keeps its original UUID and request for explicit retry;
|
|
916
|
+
unknown delivery is inspected, never automatically resent.
|
|
357
917
|
|
|
358
918
|
### Files
|
|
359
919
|
|
|
@@ -386,6 +946,20 @@ the app's deletion endpoint must preserve files already accepted by a message,
|
|
|
386
946
|
including when a browser loses the acknowledgement. Never infer acceptance from
|
|
387
947
|
an HTTP request merely starting.
|
|
388
948
|
|
|
949
|
+
The supplied canonical client captures the attachment owner and submitted IDs.
|
|
950
|
+
It acknowledges only those IDs when HTTP, canonical history or explicit delivery
|
|
951
|
+
inspection confirms acceptance, preserving newer ready and uploading files.
|
|
952
|
+
Uncertain delivery retains its selected receipts until confirmation. Only IDs
|
|
953
|
+
are sent to the conversation API; attachment-only requests preserve empty
|
|
954
|
+
authored text. The runtime's attachment capability and unresolved upload state
|
|
955
|
+
gate submission. A disabled Send leaves ordinary textarea editing, including
|
|
956
|
+
newlines, available.
|
|
957
|
+
|
|
958
|
+
Upload scopes must track authenticated user, workspace and conversation changes.
|
|
959
|
+
Use the application's saved-draft retention when uploaded files must survive a
|
|
960
|
+
view closing or a browser reload. The binding's acknowledgement never calls
|
|
961
|
+
delete or abandon; those remain the upload owner's explicit policy.
|
|
962
|
+
|
|
389
963
|
The shared `AssistantAttachmentQueue`, `AssistantMessageAttachments` and
|
|
390
964
|
`AssistantAttachmentPreview` provide queue, sent-file list and preview UI.
|
|
391
965
|
Set `adapter.attachments.open(receipt)` for queue preview. Sent-file rendering
|
|
@@ -398,14 +972,65 @@ picker, screenshot producer or project file browser remains an application tool.
|
|
|
398
972
|
|
|
399
973
|
### Questions
|
|
400
974
|
|
|
975
|
+
Set `questions` on `AssistantConversationClientElement`, or pass
|
|
976
|
+
`questions: true` to `useAssistantConversation`, to enable ordinary chat answer
|
|
977
|
+
fields. The retained conversation selects the latest authorized completed reply,
|
|
978
|
+
ignores an in-progress reply and stops at a subsequent user message. It owns the
|
|
979
|
+
answers, choice, dismissal and submitted-form state; no application selector or
|
|
980
|
+
reset watcher is needed.
|
|
981
|
+
|
|
982
|
+
```vue
|
|
983
|
+
<AssistantConversationClientElement :conversation-id="conversationId" questions />
|
|
984
|
+
```
|
|
985
|
+
|
|
986
|
+
The standard form requires all numbered answers, appends optional composer
|
|
987
|
+
context and sends one ordinary message through the same delivery entry. Choosing
|
|
988
|
+
a suggested-answer chip waits for Send and uses that choice's submitted value.
|
|
989
|
+
**Answer normally instead** keeps the draft. Submitted forms hide while delivery
|
|
990
|
+
is pending; retries keep the original formatted answer, and uncertain delivery
|
|
991
|
+
uses **Check delivery**.
|
|
992
|
+
|
|
993
|
+
Mirrored standard elements for the same target share the form. Answers and
|
|
994
|
+
dismissal survive a text panel closing while voice retains that conversation.
|
|
995
|
+
A different target or authenticated actor has separate state, even when the
|
|
996
|
+
question text is identical. Loss of authorized availability clears the form.
|
|
997
|
+
|
|
998
|
+
An optional `extraChoice` adds a product-specific fallback option to each select;
|
|
999
|
+
matching values are not duplicated. No extra option is added by default:
|
|
1000
|
+
|
|
1001
|
+
```vue
|
|
1002
|
+
<AssistantConversationClientElement
|
|
1003
|
+
:conversation-id="conversationId"
|
|
1004
|
+
:questions="{ extraChoice: { label: 'I am not sure', value: 'I am not sure' } }"
|
|
1005
|
+
/>
|
|
1006
|
+
```
|
|
1007
|
+
|
|
1008
|
+
Use the same question configuration for mirrored views. The first active
|
|
1009
|
+
standard reader supplies the shared optional choice policy. Setting `questions`
|
|
1010
|
+
to `false` or omitting it hides that view's question controls.
|
|
1011
|
+
|
|
1012
|
+
For custom presentation, the same prop accepts an explicit question owner or the
|
|
1013
|
+
existing question adapter. `useAssistantQuestions` from
|
|
1014
|
+
`/client/conversation-questions` supplies the extracted answer state to a custom
|
|
1015
|
+
composer. Supply its authorized completed message and optional `extraChoice`;
|
|
1016
|
+
`latestAssistantMessageAwaitingUserReply` from `/shared/conversation` implements
|
|
1017
|
+
the standard message selection. A custom owner remains responsible for its
|
|
1018
|
+
message source and scope resets.
|
|
1019
|
+
|
|
401
1020
|
`AssistantQuestionInputs` renders numbered question fields and suggested-answer
|
|
402
1021
|
chips. Pass `questions`, optional `selectItems` keyed by question name, `choices`,
|
|
403
1022
|
`v-model:answers`, and `v-model:choice`; handle `dismiss`. Questions contain
|
|
404
1023
|
`{ name, number, label, choices }`; choices use `{ value, label, selectLabel }`.
|
|
405
1024
|
The aggregate accepts the same fields under `adapter.questions`, with
|
|
406
1025
|
`setAnswers`, `setChoice`, and `dismiss` callbacks. Existing question parsers and
|
|
407
|
-
submission formatters are exported from `/shared/conversation`.
|
|
408
|
-
|
|
1026
|
+
submission formatters are exported from `/shared/conversation`.
|
|
1027
|
+
|
|
1028
|
+
For a custom composer, `capture(draft)` returns `{ message, questionText }` without
|
|
1029
|
+
changing state. Keep the existing complete-answer and application admission
|
|
1030
|
+
checks, then call `markSubmitted(questionText)` at local submission and deliver
|
|
1031
|
+
the captured message through the normal action. `reset()` clears local question
|
|
1032
|
+
state. Native permission prompts and tool responses retain their separate
|
|
1033
|
+
authorized owners; these prose fields introduce no answer endpoint.
|
|
409
1034
|
|
|
410
1035
|
### Incremental answers
|
|
411
1036
|
|
|
@@ -427,9 +1052,33 @@ await transcript.writeConversationUserMessage(scope, { messageId: requestId, tex
|
|
|
427
1052
|
const { conversationLog, pagination } = await transcript.readConversationLogPage(scope, { limit: 20 });
|
|
428
1053
|
```
|
|
429
1054
|
|
|
430
|
-
The memory adapter is explicitly transient.
|
|
431
|
-
|
|
432
|
-
|
|
1055
|
+
The memory adapter is explicitly transient. For a single-process application,
|
|
1056
|
+
use the supplied file adapter with an application-owned private directory:
|
|
1057
|
+
|
|
1058
|
+
```js
|
|
1059
|
+
import { createFileConversationStorage } from "@jskit-ai/assistant-core/server/conversation";
|
|
1060
|
+
|
|
1061
|
+
const storage = createFileConversationStorage({ directory: "/var/lib/my-app/conversations" });
|
|
1062
|
+
const transcript = createConversationTranscript({ storage });
|
|
1063
|
+
```
|
|
1064
|
+
|
|
1065
|
+
Share one adapter instance across the application's conversations. It serializes
|
|
1066
|
+
writes per scope, flushes a replacement file before publishing it atomically,
|
|
1067
|
+
and retains messages across application restarts. Saved files use mode `0600`;
|
|
1068
|
+
a newly created directory uses `0700`. Existing directory permissions remain
|
|
1069
|
+
the operator's responsibility. Scopes are hashed into filenames and checked
|
|
1070
|
+
against each saved document. JSON-compatible message metadata and attachment
|
|
1071
|
+
descriptors are stored; attachment bytes remain application-owned.
|
|
1072
|
+
|
|
1073
|
+
Damaged JSON or an unsupported format causes an explicit error and is never
|
|
1074
|
+
silently replaced with an empty conversation. Back up this directory with the
|
|
1075
|
+
application's attachment data. This adapter is for one writer process: use an
|
|
1076
|
+
application database or another adapter with cross-process transactions when
|
|
1077
|
+
multiple servers write the same conversations.
|
|
1078
|
+
|
|
1079
|
+
Applications may supply SQL, filesystem, document-store, or other storage.
|
|
1080
|
+
JSKIT does not choose the directory or create database tables. The storage
|
|
1081
|
+
object implements:
|
|
433
1082
|
|
|
434
1083
|
```js
|
|
435
1084
|
{
|
|
@@ -444,7 +1093,8 @@ client-supplied scope directly into storage. Apply the same authorization to
|
|
|
444
1093
|
history, sends, stops, deletion and attachment reads. The memory reference
|
|
445
1094
|
adapter requires a nonempty string; other adapters may use structured scopes.
|
|
446
1095
|
|
|
447
|
-
Each callback receives these asynchronous operations
|
|
1096
|
+
Each callback receives these asynchronous operations. The first six serve the
|
|
1097
|
+
transcript service. All nine are mandatory for `createConversationRuntime()`:
|
|
448
1098
|
|
|
449
1099
|
| Transaction operation | Contract |
|
|
450
1100
|
| --- | --- |
|
|
@@ -452,15 +1102,20 @@ Each callback receives these asynchronous operations:
|
|
|
452
1102
|
| `readTurn(id)` | Detached turn snapshot in the client model above, or null if absent. |
|
|
453
1103
|
| `nextTurnId()` | Allocate an ID after the current tail under the write lock. |
|
|
454
1104
|
| `hasMessage(messageId)` | Check uniqueness across the whole scoped conversation. |
|
|
455
|
-
| `appendMessage(turnId, message)` | Save `{ role, text, messageId, at }
|
|
1105
|
+
| `appendMessage(turnId, message)` | Save `{ role, text, messageId, at }` and any supplied `attachments`, `origin` and `turnMetadata`; user or application messages may start turns. |
|
|
456
1106
|
| `replaceAssistant(turnId, message)` | Idempotently replace the final answer for that exact turn, preserving its existing timestamp/identity. |
|
|
1107
|
+
| `readMetadata()` | Detached conversation metadata object; return `{}` for an empty scope. |
|
|
1108
|
+
| `writeMetadata(value)` | Replace conversation metadata within this transaction, committing atomically with its turn/message changes. |
|
|
1109
|
+
| `updateTurnMetadata(turnId, patch)` | Shallow-merge a detached patch into an existing turn's metadata; preserve unrelated keys and reject absent turns. |
|
|
457
1110
|
|
|
458
1111
|
`write` serializes mutations for the same scope, including duplicate checks and
|
|
459
1112
|
ID allocation. A successful return means the write is durable for that adapter.
|
|
460
1113
|
Database adapters should use a transaction; filesystem adapters must publish
|
|
461
1114
|
the message only after its attachment references and metadata are recoverable.
|
|
462
1115
|
Errors reject the operation and must remain visible to the caller. Do not
|
|
463
|
-
acknowledge a failed save.
|
|
1116
|
+
acknowledge a failed save. A failed runtime transaction must leave both transcript
|
|
1117
|
+
and metadata unchanged; serializing independently durable file writes is insufficient.
|
|
1118
|
+
Reads must not expose mutable backing objects.
|
|
464
1119
|
Use storage-level locks or transactions when several processes share storage;
|
|
465
1120
|
an in-process promise queue alone does not provide that guarantee.
|
|
466
1121
|
|
|
@@ -489,28 +1144,1463 @@ Run the reusable adapter checks against an isolated fixture:
|
|
|
489
1144
|
|
|
490
1145
|
```js
|
|
491
1146
|
import { verifyConversationStorageContract } from "@jskit-ai/assistant-core/testing/conversation-storage";
|
|
492
|
-
await verifyConversationStorageContract(storage);
|
|
1147
|
+
await verifyConversationStorageContract(storage, { runtime: true });
|
|
493
1148
|
```
|
|
494
1149
|
|
|
495
1150
|
Those checks cover scoped isolation, concurrent duplicate writes, stable
|
|
496
1151
|
ordering, pagination, final replacement, attachments and detached reads. An
|
|
497
|
-
|
|
1152
|
+
adapter selected with `runtime: true` also checks all metadata operations, scope
|
|
1153
|
+
isolation, patch preservation and combined transcript/metadata rollback. Use a
|
|
1154
|
+
fresh isolated fixture for each invocation; omit the option only for transcript-only
|
|
1155
|
+
adapters. An app must additionally test its authentication, crash/reopen behavior,
|
|
498
1156
|
multi-process writes, failed commits and attachment cleanup.
|
|
499
1157
|
|
|
500
1158
|
## Provider contract
|
|
501
1159
|
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
1160
|
+
### Application conversation operations
|
|
1161
|
+
|
|
1162
|
+
`createConversationRuntime()` currently supplies direct API inference and basic
|
|
1163
|
+
Claude, Codex and OpenCode conversations through the same application operations. The API path uses
|
|
1164
|
+
the existing AI connection resolver and an application-owned storage choice. It
|
|
1165
|
+
requires no native CLI or engine lifecycle callbacks:
|
|
1166
|
+
|
|
1167
|
+
```js
|
|
1168
|
+
import { createAiConnectionResolver } from "@jskit-ai/connectors-catalog/server/ai";
|
|
1169
|
+
import { createConversationRuntime, createFileConversationStorage } from "@jskit-ai/assistant-core/server/conversation";
|
|
505
1170
|
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
1171
|
+
const connections = createAiConnectionResolver({
|
|
1172
|
+
configuration: integrationConfiguration,
|
|
1173
|
+
authorize: authorizeModelConnection
|
|
1174
|
+
});
|
|
1175
|
+
const runtime = createConversationRuntime({
|
|
1176
|
+
engine: "api",
|
|
1177
|
+
connections,
|
|
1178
|
+
defaultIntegrationId: "assistant",
|
|
1179
|
+
storage: createFileConversationStorage({ directory: privateConversationDirectory }),
|
|
1180
|
+
authorize: ({ context, conversationId, operation }) => canAccessConversation(context, conversationId, operation)
|
|
1181
|
+
});
|
|
1182
|
+
const conversation = await runtime.open({
|
|
1183
|
+
id: applicationConversationId,
|
|
1184
|
+
context: authenticatedContext,
|
|
1185
|
+
configuration: { systemPrompt: "Answer concisely." }
|
|
1186
|
+
});
|
|
1187
|
+
const unsubscribe = await conversation.subscribe(event => publishToAuthorizedClient(event));
|
|
1188
|
+
const receipt = await conversation.send({ messageId: applicationMessageId, text: "Hello" });
|
|
1189
|
+
const snapshot = await conversation.read();
|
|
1190
|
+
await conversation.wait(); // optional: wait for current work, without polling
|
|
1191
|
+
await conversation.configure({ systemPrompt: "Use the updated instructions." });
|
|
1192
|
+
// await conversation.cancel(); // stop current model work
|
|
1193
|
+
unsubscribe();
|
|
1194
|
+
await runtime.close(); // application shutdown
|
|
1195
|
+
```
|
|
1196
|
+
|
|
1197
|
+
Authorization belongs to the application and is checked on each operation and
|
|
1198
|
+
before subscription output. The existing connection resolver separately checks
|
|
1199
|
+
model-account access before every request. Secrets stay in its server-side result.
|
|
1200
|
+
`context` is never written into conversation storage.
|
|
1201
|
+
|
|
1202
|
+
`open({ id, context })` resumes saved configuration. Supplying different initial
|
|
1203
|
+
configuration to an existing conversation is rejected; change it explicitly with
|
|
1204
|
+
`configure()` while the conversation is idle. Closing the runtime or disposing a
|
|
1205
|
+
conversation stops its current inference and releases observers, retaining history.
|
|
1206
|
+
|
|
1207
|
+
`engine` and `host` on the runtime are defaults. `open({ id, context, engine, host,
|
|
1208
|
+
configuration })` may choose them per conversation. A saved conversation resumes
|
|
1209
|
+
its saved engine when `engine` is omitted, even when the runtime default differs.
|
|
1210
|
+
The host is a server-side facility, never browser input: it supplies the worktree,
|
|
1211
|
+
credential environment and execution owner for that conversation. Different
|
|
1212
|
+
conversations in one runtime can use different hosts. An open handle rejects a
|
|
1213
|
+
second, conflicting host; native bindings also validate their saved worktree and
|
|
1214
|
+
credential scope on reopen. Environment values are not persisted.
|
|
1215
|
+
|
|
1216
|
+
An existing application using the extracted Codex owner can supply
|
|
1217
|
+
`host.conversation({ id, context })` to bind its original transcript, native
|
|
1218
|
+
identity, delivery journal and lifecycle facilities. That bound path uses those
|
|
1219
|
+
existing records; it does not create a second runtime record or backfill history.
|
|
1220
|
+
This is an advanced host integration, not required for ordinary file/custom
|
|
1221
|
+
transaction storage.
|
|
1222
|
+
|
|
1223
|
+
Such a server integration may open a handle with `representation: "native"` when
|
|
1224
|
+
its existing command contract requires the original result. The option belongs
|
|
1225
|
+
to that handle and each invocation, not the cached conversation or saved state.
|
|
1226
|
+
Send/Cancel preserve the original result and its captured session when supplied;
|
|
1227
|
+
raw goal controls retain their exact native expectations. Canonical handles still
|
|
1228
|
+
share the same queue, native owner and receipts, and receive the ordinary canonical
|
|
1229
|
+
projection. HTTP actions do not accept this option, and canonical events never
|
|
1230
|
+
include the raw native result. A known native ID alone does not prove successful
|
|
1231
|
+
admission; the original bound sender must confirm delivery.
|
|
1232
|
+
|
|
1233
|
+
### Bounded native structured output
|
|
1234
|
+
|
|
1235
|
+
Claude, Codex and OpenCode report `capabilities.structuredOutput: true`. A server
|
|
1236
|
+
may supply `configuration.outputSchema` when opening a conversation, or change it
|
|
1237
|
+
with `configure()` while idle. The schema resumes with the existing saved
|
|
1238
|
+
configuration. It is not a browser-authored message field or a permission grant.
|
|
1239
|
+
|
|
1240
|
+
```js
|
|
1241
|
+
await conversation.configure({
|
|
1242
|
+
outputSchema: {
|
|
1243
|
+
type: "object",
|
|
1244
|
+
additionalProperties: false,
|
|
1245
|
+
properties: { answer: { type: "string", maxLength: 1_200 } },
|
|
1246
|
+
required: ["answer"]
|
|
1247
|
+
}
|
|
1248
|
+
});
|
|
1249
|
+
```
|
|
1250
|
+
|
|
1251
|
+
The original bounded schema validator rejects unsupported or unbounded schemas
|
|
1252
|
+
before dispatch. Objects require every declared property and reject additional
|
|
1253
|
+
properties; strings need a finite `maxLength` or string enum, and arrays need a
|
|
1254
|
+
finite `maxItems`. Boolean and null values are also supported. Below the root,
|
|
1255
|
+
`anyOf` may contain up to 64 closed object alternatives. The largest alternative
|
|
1256
|
+
counts toward the response bound; their sizes are not added together. Alternatives
|
|
1257
|
+
count toward the existing nesting limit. The other limits remain 64 KiB of schema,
|
|
1258
|
+
eight nesting levels, and 64 properties or enum values.
|
|
1259
|
+
The worst-case JSON response, including escaped string characters, must fit the
|
|
1260
|
+
driver's `limits.maxOutputCharacters` (64,000 by default).
|
|
1261
|
+
|
|
1262
|
+
Claude uses its native schema flag and restarts the owned process when the schema
|
|
1263
|
+
changes, retaining the conversation. Codex uses its native turn schema setting.
|
|
1264
|
+
OpenCode uses the existing schema instruction and removes a single JSON fence
|
|
1265
|
+
from the completed answer; raw output remains subject to the output bound, and
|
|
1266
|
+
the schema instruction counts toward `limits.maxInputCharacters`. Applications
|
|
1267
|
+
still validate the returned result before applying product behavior. Setting
|
|
1268
|
+
`outputSchema: undefined` restores ordinary output. The API driver reports this
|
|
1269
|
+
capability as false and rejects the option before resolving an account or sending.
|
|
1270
|
+
|
|
1271
|
+
### Engine selection and native conversation replacement
|
|
1272
|
+
|
|
1273
|
+
`select()` changes the engine or model while retaining native histories. Supply
|
|
1274
|
+
the current segment and a stable operation ID, just as for an ordinary accepted
|
|
1275
|
+
application operation:
|
|
1276
|
+
|
|
1277
|
+
```js
|
|
1278
|
+
await conversation.select({
|
|
1279
|
+
operationId: selectionOperationId,
|
|
1280
|
+
expectedSegmentId: (await conversation.read()).segmentId,
|
|
1281
|
+
engine: "codex",
|
|
1282
|
+
configuration: { systemPrompt: instructions, model: "gpt-6.1-sol" }
|
|
1283
|
+
});
|
|
1284
|
+
```
|
|
1285
|
+
|
|
1286
|
+
Selecting a native engine used earlier restores its latest retained binding.
|
|
1287
|
+
Changing a model on the current engine retains that native conversation. Before
|
|
1288
|
+
the next request, JSKIT supplies missed messages, corrections and removed message
|
|
1289
|
+
identities as quoted history. Unchanged messages are not injected again. All
|
|
1290
|
+
corrections are included, even outside the recent-history window; if they exceed
|
|
1291
|
+
`maxContinuityCharacters`, delivery fails with an instruction to renew the context
|
|
1292
|
+
with a briefing. Nothing is silently dropped. The application remains responsible
|
|
1293
|
+
for authorizing edits to its transcript.
|
|
1294
|
+
|
|
1295
|
+
Selection requires idle work and confirmed process cleanup. An uncertain receipt
|
|
1296
|
+
may remain with another selected engine, but the same message ID cannot be sent
|
|
1297
|
+
there again. Return to its original engine and use `inspectDelivery()` to resolve
|
|
1298
|
+
it. Selection never resends the pending request. The API driver has no native
|
|
1299
|
+
thread to restore and starts a new context with the recorded continuity when
|
|
1300
|
+
selected from a different engine.
|
|
1301
|
+
|
|
1302
|
+
One application conversation can retain its transcript across several native
|
|
1303
|
+
conversations. To deliberately start a successor, read its current `segmentId`
|
|
1304
|
+
and supply a stable operation ID:
|
|
1305
|
+
|
|
1306
|
+
```js
|
|
1307
|
+
const before = await conversation.read();
|
|
1308
|
+
await conversation.replace({
|
|
1309
|
+
operationId: applicationOperationId,
|
|
1310
|
+
expectedSegmentId: before.segmentId,
|
|
1311
|
+
reason: "engine-change", // or model-change, renewal
|
|
1312
|
+
engine: "codex",
|
|
1313
|
+
configuration: { systemPrompt: "Continue the agreed work.", model: "gpt-6.1-sol" },
|
|
1314
|
+
briefing: savedWorkSummary
|
|
1315
|
+
});
|
|
1316
|
+
await conversation.send({ messageId: nextMessageId, text: "Continue." });
|
|
1317
|
+
```
|
|
1318
|
+
|
|
1319
|
+
Replacement performs no inference. It saves the operation, confirms predecessor
|
|
1320
|
+
cleanup, retains the predecessor's native binding and installs the successor.
|
|
1321
|
+
The first native submission carries the saved briefing and bounded recent written
|
|
1322
|
+
history; later native turns do not repeat that history. The API driver supplies
|
|
1323
|
+
the same continuity with its subsequent request history. Visible user messages
|
|
1324
|
+
remain exactly the submitted text. Carry-over includes up to 30 recent messages,
|
|
1325
|
+
with each text bounded to 2,000 characters and truncation/omission marked. The
|
|
1326
|
+
combined budget is `limits.maxContinuityCharacters` (128,000 by default); an
|
|
1327
|
+
oversized supplied briefing fails before changing the binding.
|
|
1328
|
+
|
|
1329
|
+
The operation requires idle work and resolved delivery. A failed cleanup or commit
|
|
1330
|
+
leaves the saved replacement pending, blocking new sends and configuration changes.
|
|
1331
|
+
Retry exactly the same operation ID and arguments after fixing the problem,
|
|
1332
|
+
including after application restart. A completed retry returns the prior receipt.
|
|
1333
|
+
No previous request is replayed. `configure()` still supports compatible settings
|
|
1334
|
+
changes on the current native history; it does not silently replace that history.
|
|
1335
|
+
|
|
1336
|
+
Conversation Undo/rewind is not exposed. Completed historical branch markers
|
|
1337
|
+
remain readable, and their admission receipts remain reserved. An unfinished
|
|
1338
|
+
historical Undo blocks opening or replacing the conversation and fails offline
|
|
1339
|
+
preflight without changing its saved state. Complete that operation with the
|
|
1340
|
+
previous release before upgrading; the runtime does not guess a new boundary.
|
|
1341
|
+
Application checkpoints and file restoration remain separate application actions.
|
|
1342
|
+
|
|
1343
|
+
The unreleased common runtime now validates its versioned metadata. Earlier local
|
|
1344
|
+
development records with an older runtime version fail with an explicit export/restart
|
|
1345
|
+
message; normal startup does not rewrite them. Consumer migration of published
|
|
1346
|
+
application records requires that application's explicit upgrade process.
|
|
1347
|
+
|
|
1348
|
+
### Admission and recovery
|
|
1349
|
+
|
|
1350
|
+
`send()` returns after admission; generation continues independently of its HTTP
|
|
1351
|
+
request. The application supplies a stable ID and reuses it for uncertain retries.
|
|
1352
|
+
Repeated IDs return their saved receipt; reusing an ID with different content is
|
|
1353
|
+
rejected. An accepted receipt confirms application admission, not model completion.
|
|
1354
|
+
`read()` returns `conversationLog`, `streaming`, `status`, `phase`, `error`, `configuration`
|
|
1355
|
+
and `capabilities`. Events include `accepted`, `message` snapshots and `settled`,
|
|
1356
|
+
plus common `phase` updates and `error` when an owned process cannot be cleaned up.
|
|
1357
|
+
A `transcript` event carries a saved `upsert-turn` patch from the existing transcript
|
|
1358
|
+
owner. The supplied binding merges it directly and preserves patches received
|
|
1359
|
+
while a history read is in flight. A later independent canonical read remains
|
|
1360
|
+
authoritative; applications do not need their own patch queue or parser.
|
|
1361
|
+
Every driver uses the same phases: `preparing` before dispatch, `working` after
|
|
1362
|
+
admission, `compacting` during observed context compaction, and `retrying` during
|
|
1363
|
+
a native retry. An empty string clears the phase after work settles, including
|
|
1364
|
+
failure or cancellation. Not every engine reports every intermediate phase.
|
|
1365
|
+
Native busy/idle flags and status sentences are not application phase values;
|
|
1366
|
+
the runtime's `status` and `settled` event determine completion.
|
|
1367
|
+
|
|
1368
|
+
Treat message IDs as opaque. Some engines retain an ID from streaming through
|
|
1369
|
+
storage; Claude's temporary stream blocks and saved native snapshots have
|
|
1370
|
+
different IDs. `message-complete` retires the temporary block. The runtime's
|
|
1371
|
+
`streaming` state contains live output, while `conversationLog` contains saved
|
|
1372
|
+
messages. Do not save temporary blocks or assume that every answer in a turn
|
|
1373
|
+
has the same ID. Native snapshot identities match recovered history.
|
|
1374
|
+
|
|
1375
|
+
All engines accept the same configuration fields: required `systemPrompt`, plus
|
|
1376
|
+
optional `integrationId`, `model` and `effort`. API and OpenCode require an
|
|
1377
|
+
authorized integration; Claude and Codex can instead use their native login.
|
|
1378
|
+
With an integration, an explicit `model` must match its exact resolved model.
|
|
1379
|
+
Omit `effort` for the provider default. API passes standard effort levels
|
|
1380
|
+
(`none`, `minimal`, `low`, `medium`, `high`, `xhigh`) through its installed SDK;
|
|
1381
|
+
Claude validates its CLI levels (`low`, `medium`, `high`, `xhigh`, `max`), and
|
|
1382
|
+
OpenCode validates the chosen effort against that exact native model's variants.
|
|
1383
|
+
Codex validates its native levels and the selected external model's allowed
|
|
1384
|
+
efforts. Unsupported choices fail instead of being silently replaced.
|
|
1385
|
+
|
|
1386
|
+
Pass those common fields directly to the runtime. It normalizes new configuration
|
|
1387
|
+
inputs for the conversation's actual engine. Set an application-selected default
|
|
1388
|
+
connection once when constructing the runtime:
|
|
1389
|
+
|
|
1390
|
+
```js
|
|
1391
|
+
const runtime = createConversationRuntime({
|
|
1392
|
+
engine, connections, storage, authorize,
|
|
1393
|
+
defaultIntegrationId: "assistant"
|
|
1394
|
+
});
|
|
1395
|
+
await runtime.open({
|
|
1396
|
+
id: conversationId, context, engine,
|
|
1397
|
+
configuration: {
|
|
1398
|
+
systemPrompt: instructions,
|
|
1399
|
+
integrationId: selectedIntegrationId,
|
|
1400
|
+
model: selectedModel,
|
|
1401
|
+
effort: selectedEffort
|
|
1402
|
+
}
|
|
1403
|
+
});
|
|
1404
|
+
```
|
|
1405
|
+
|
|
1406
|
+
The default ID is an explicit application choice. It applies only when API or
|
|
1407
|
+
OpenCode requires a connection and no ID was supplied. Claude and Codex keep
|
|
1408
|
+
native login when the ID is absent; an explicit ID is honored by every engine.
|
|
1409
|
+
New inputs to `open`, `configure`, selection and replacement use the same
|
|
1410
|
+
normalization; existing saved configuration is not rewritten on resume.
|
|
1411
|
+
`normalizeConversationConfiguration` remains available from the same
|
|
1412
|
+
`/server/conversation` entry for applications that need to normalize an object
|
|
1413
|
+
before passing it on, but ordinary callers do not need a separate helper call.
|
|
1414
|
+
Normalization omits undefined optional fields so the configuration survives a JSON
|
|
1415
|
+
round trip, but preserves exact values and invalid input for existing validation.
|
|
1416
|
+
It does not resolve credentials, select a substitute model or change engine
|
|
1417
|
+
capabilities. An empty integration catalogue can support an API read-only starter;
|
|
1418
|
+
missing connections or credentials still fail when Send resolves them. Changing
|
|
1419
|
+
settings on an existing conversation uses `configure()` or authorized selection,
|
|
1420
|
+
not a rewritten configuration at startup. No saved data is migrated by normalization.
|
|
1421
|
+
|
|
1422
|
+
`wake({ messageId, text })` starts an application-initiated turn, such as an update
|
|
1423
|
+
from a watched session. It uses the same admission, cancellation and duplicate
|
|
1424
|
+
receipt rules but authorizes `operation: "wake"` separately before dispatch.
|
|
1425
|
+
The turn records `origin: "application"` and a `system` transcript message, not a
|
|
1426
|
+
user message. Native/API input identifies the observation as data within existing
|
|
1427
|
+
authority; it is never promoted to model system instructions. The application
|
|
1428
|
+
still owns its watch/assignment policy and action permissions. A public chat
|
|
1429
|
+
endpoint must not expose wake authority merely because its user can send text.
|
|
1430
|
+
|
|
1431
|
+
Both `send()` and `wake()` accept optional `data`, a JSON object containing
|
|
1432
|
+
application observations or the request's captured UI focus. JSKIT saves it with
|
|
1433
|
+
the accepted message and includes it as application data in model input, without
|
|
1434
|
+
changing the stable system prompt or visible user text. Text and serialized data
|
|
1435
|
+
share the configured input-size limit. A retry must retain the same data as well
|
|
1436
|
+
as the same message ID and text. Supply trusted authorization separately through
|
|
1437
|
+
the conversation context; this data never grants permissions.
|
|
1438
|
+
|
|
1439
|
+
Saved turns distinguish complete, failed, cancelled and interrupted work. Error
|
|
1440
|
+
messages are not assistant replies. After restart, accepted work is marked
|
|
1441
|
+
interrupted and is never replayed. API reservations interrupted before admission
|
|
1442
|
+
were never dispatched; only another explicit send can start them. Storage failure
|
|
1443
|
+
retains unsaved output in the current handle and prevents new submissions through
|
|
1444
|
+
that handle. Repair storage and call `retrySave()` to save the retained result;
|
|
1445
|
+
this never reruns inference. Disposal and shutdown also retry that pending save
|
|
1446
|
+
and report failure without discarding it. Do not blindly resend the message.
|
|
1447
|
+
|
|
1448
|
+
Native acknowledgement can be lost after the engine received a message.
|
|
1449
|
+
`inspectDelivery({ messageId })` checks its native history and returns an accepted
|
|
1450
|
+
receipt only when that message is found. It also recovers output for an already
|
|
1451
|
+
accepted message interrupted by restart. Output remains interrupted unless the
|
|
1452
|
+
adapter can prove native completion; Codex checks the native turn's saved status.
|
|
1453
|
+
Completed application turns, cancellation/failure status and saved application-tool
|
|
1454
|
+
receipts are retained. Repeated inspection does not duplicate messages or execute
|
|
1455
|
+
tools. An application tool without a verified result keeps the turn interrupted,
|
|
1456
|
+
even if native history reports completion; inspect that operation's target before
|
|
1457
|
+
requesting another execution. API connections have no native history to inspect and keep their saved
|
|
1458
|
+
receipt without claiming recovery.
|
|
1459
|
+
|
|
1460
|
+
Recovery commits output and receipts together. If storage fails, restore access
|
|
1461
|
+
and retry the inspection; native work is not repeated. An unknown result leaves
|
|
1462
|
+
the submission unresolved. A message in another segment must be inspected after
|
|
1463
|
+
selecting that segment's engine; recovery never revives a rewound turn. Neither
|
|
1464
|
+
inspection nor reopening sends the message again. If native process cleanup fails,
|
|
1465
|
+
the handle reports unavailable and rejects new work; `cancel()` retries that exact
|
|
1466
|
+
owned cleanup rather than starting another process.
|
|
1467
|
+
An optional `limits.timeoutMs` deadline aborts work and waits for owned cleanup;
|
|
1468
|
+
a failed cleanup is still reported as unavailable rather than as a successful stop.
|
|
1469
|
+
|
|
1470
|
+
The supplied memory and file adapters also provide `readMetadata()`,
|
|
1471
|
+
`writeMetadata(value)` and `updateTurnMetadata(turnId, patch)` inside the existing
|
|
1472
|
+
storage transactions. Runtime state occupies `metadata.runtime` at conversation
|
|
1473
|
+
and turn level. Custom adapters must commit that state with accepted messages in
|
|
1474
|
+
the same transaction. The file adapter remains one-writer storage.
|
|
1475
|
+
|
|
1476
|
+
`createConversationStorage({ readRecord, writeRecord, deleteRecord })` is exported
|
|
1477
|
+
for custom single-writer record stores. Each record contains `{ turns: Map,
|
|
1478
|
+
metadata: object }`; a missing record uses an empty map and metadata object. Its
|
|
1479
|
+
copy-on-write transaction methods match the supplied memory/file adapters. It
|
|
1480
|
+
calls `writeRecord(id, draft, transaction)` to commit a successful transaction.
|
|
1481
|
+
Record formats that store the canonical transcript can use `transaction.readTurn()`
|
|
1482
|
+
while serializing, without copying the transcript's ordering and role logic.
|
|
1483
|
+
Publish the new in-memory record only after its durable write succeeds. The adapter
|
|
1484
|
+
serializes writes through one instance, not across processes. A database-backed
|
|
1485
|
+
application with concurrent writers must implement `read`/`write` using its own
|
|
1486
|
+
database transaction or hold that transaction across record loading and commit.
|
|
1487
|
+
|
|
1488
|
+
The current API driver supports application action tools and authorized attachments.
|
|
1489
|
+
It reports steering and native goals as unsupported. Limits can bound input, output and history characters, output
|
|
1490
|
+
tokens and request duration. Oversized history fails explicitly; it is not silently
|
|
1491
|
+
truncated or presented as native compaction. These capabilities will be extended
|
|
1492
|
+
as their shared integrations are migrated.
|
|
1493
|
+
|
|
1494
|
+
API replies use the shared streaming completion parser. Internal reasoning and
|
|
1495
|
+
provider tool markup stay out of visible replies, including tags split across
|
|
1496
|
+
chunks. Structured and text-encoded application calls use the same authorized
|
|
1497
|
+
execution and saved receipts. A completed response is required before executing
|
|
1498
|
+
a call; interrupted streams do not imply permission to execute or retry it.
|
|
1499
|
+
Output limits count raw provider text, including hidden blocks. Code indentation
|
|
1500
|
+
and blank lines remain intact in ordinary API answers.
|
|
1501
|
+
|
|
1502
|
+
The API driver uses the existing assistant tool-loop policy: up to sixteen tool
|
|
1503
|
+
rounds, then three recovery passes with tools disabled. Progress-only narration
|
|
1504
|
+
is retained as commentary rather than accepted as the final answer. An available
|
|
1505
|
+
clock action marked `current-time` runs before inference for time questions only
|
|
1506
|
+
when it requires no parameters, after connection authorization and turn admission.
|
|
1507
|
+
Uncertain application effects stop the turn; recovery never silently retries them.
|
|
1508
|
+
The legacy database service delegates to the same loop through its transactional
|
|
1509
|
+
adapter over the existing conversation, message and request-claim rows.
|
|
1510
|
+
|
|
1511
|
+
### Authorized attachments
|
|
1512
|
+
|
|
1513
|
+
Upload storage, file access and retention belong to the application. Pass an
|
|
1514
|
+
`attachments` resolver to `createConversationRuntime()` and send opaque IDs:
|
|
1515
|
+
|
|
1516
|
+
```js
|
|
1517
|
+
const runtime = createConversationRuntime({
|
|
1518
|
+
storage, authorize, connections,
|
|
1519
|
+
attachments: {
|
|
1520
|
+
async resolve({ attachmentIds, context, conversationId, signal }) {
|
|
1521
|
+
// Check access to each ID for this actor and conversation before reading.
|
|
1522
|
+
const files = await uploads.readAuthorized({ attachmentIds, context, conversationId, signal });
|
|
1523
|
+
return {
|
|
1524
|
+
attachments: files.map(file => ({
|
|
1525
|
+
attachmentId: file.id, fileName: file.name, size: file.bytes.length
|
|
1526
|
+
})),
|
|
1527
|
+
content: files.map(file => ({
|
|
1528
|
+
type: "image", image: file.bytes, mediaType: file.mediaType
|
|
1529
|
+
}))
|
|
1530
|
+
};
|
|
1531
|
+
}
|
|
1532
|
+
}
|
|
1533
|
+
});
|
|
1534
|
+
await conversation.send({ messageId, text: "Describe this image", attachmentIds: [uploadId] });
|
|
1535
|
+
```
|
|
1536
|
+
|
|
1537
|
+
The resolver returns receipts in the requested order and AI SDK content parts.
|
|
1538
|
+
All four engines accept extracted `{ type: "text", text }` and
|
|
1539
|
+
`{ type: "image", image: Uint8Array, mediaType }` parts. Supported image media
|
|
1540
|
+
types are PNG, JPEG, WebP and GIF; the selected model must also support image
|
|
1541
|
+
input. The API driver additionally accepts `{ type: "file", data: Uint8Array,
|
|
1542
|
+
mediaType, filename? }` when supported by its provider. Native drivers reject
|
|
1543
|
+
other binary files explicitly; extract their text or supported page images in
|
|
1544
|
+
the application's resolver. JSKIT does not fetch attachment URLs or open
|
|
1545
|
+
caller-supplied paths.
|
|
1546
|
+
|
|
1547
|
+
The native Codex owner maps already authorized local image descriptors to native
|
|
1548
|
+
input internally. The host retains upload authorization, leases and storage;
|
|
1549
|
+
applications do not assemble a separate native image-input mapper. This does not
|
|
1550
|
+
change the common resolver's byte-content contract.
|
|
1551
|
+
|
|
1552
|
+
A send accepts at most ten distinct IDs and may contain an empty `text` when it
|
|
1553
|
+
has attachments. The resolver must return one receipt per ID with `attachmentId`,
|
|
1554
|
+
`fileName` and nonnegative integer `size`; an optional `reference` labels it in
|
|
1555
|
+
the transcript. Extra receipt fields are discarded. Transcript and delivery
|
|
1556
|
+
receipts never store file bytes. Native engines may retain their own input in
|
|
1557
|
+
native history. Retain authorized uploads according to the application's policy.
|
|
1558
|
+
|
|
1559
|
+
`limits.maxAttachmentBytes` defaults to 8 MiB for all file content used in a
|
|
1560
|
+
dispatch, including history. API history re-resolves saved files before each
|
|
1561
|
+
request. Conversation replacement re-resolves attachments included in its
|
|
1562
|
+
retained briefing; returning to native history supplies files in changed
|
|
1563
|
+
messages. Unavailable files or denied access fail before dispatch, rather than
|
|
1564
|
+
silently dropping content. Identical accepted retries return their saved receipt
|
|
1565
|
+
without rereading or resending files. Attachment capability is enabled only when
|
|
1566
|
+
the application supplies a resolver.
|
|
1567
|
+
|
|
1568
|
+
### Application tools
|
|
1569
|
+
|
|
1570
|
+
Pass the application's existing JSKIT action catalogue as `actions` to
|
|
1571
|
+
`createConversationRuntime()`. The common runtime uses `createServiceToolCatalog`
|
|
1572
|
+
for authorized discovery, input/output contracts and execution through the
|
|
1573
|
+
`automation` channel. It does not create another action registry or bypass action
|
|
1574
|
+
permissions. The conversation's `authorize` callback is checked again with
|
|
1575
|
+
`operation: "tool"` before executing a requested action. The action receives the
|
|
1576
|
+
turn's abort `signal` in its trusted execution context.
|
|
1577
|
+
|
|
1578
|
+
Applications with narrower rules for particular turns may supply
|
|
1579
|
+
`toolPolicy({ actionId, kind, context })`, returning a boolean. For example, an
|
|
1580
|
+
application notification can allow only `kind === "query"`. This policy filters
|
|
1581
|
+
discovery and is checked again at execution, including calls using a previously
|
|
1582
|
+
loaded contract. It supplements action permissions; it cannot grant an action
|
|
1583
|
+
that the actor otherwise cannot use. The lower-level `createServiceToolCatalog`
|
|
1584
|
+
accepts the same callback as `isActionAvailable`.
|
|
1585
|
+
|
|
1586
|
+
The API adapter sends these tool schemas using the selected provider's supported
|
|
1587
|
+
protocol and continues after receiving each result. It resolves the same
|
|
1588
|
+
authorized connection again before every inference. No reply-or-tool JSON
|
|
1589
|
+
envelope is required in application instructions. Codex carries calls through its
|
|
1590
|
+
native dynamic-tool protocol. Its search, contract and execute entry points stay
|
|
1591
|
+
fixed for the native thread while action availability and permissions are checked
|
|
1592
|
+
through the current catalogue. Claude uses the same fixed discovery tools through
|
|
1593
|
+
its SDK MCP control connection. JSKIT matches each request to its admitted native
|
|
1594
|
+
tool identity and arguments before shared execution. OpenCode's native plugin
|
|
1595
|
+
uses a private authenticated loopback connection owned by its adapter. Each call
|
|
1596
|
+
must match a native tool part in the currently admitted turn before shared
|
|
1597
|
+
execution. Neither path requires a separately configured tool service.
|
|
1598
|
+
|
|
1599
|
+
Calls and results are committed in the accepted turn's
|
|
1600
|
+
`metadata.applicationTools`. A reservation is saved before execution, and a result
|
|
1601
|
+
is saved before inference continues. Repeated call IDs within that admitted turn
|
|
1602
|
+
return their recorded result; conflicting arguments are rejected. A later user
|
|
1603
|
+
or application turn owns separate call identities, even if the model reuses an
|
|
1604
|
+
ID. An interrupted reservation without a
|
|
1605
|
+
result or a server failure after execution stops the turn with an unknown-outcome
|
|
1606
|
+
error instead of executing again. Applications
|
|
1607
|
+
still own action-level idempotency and inspection of uncertain external effects.
|
|
1608
|
+
The runtime cannot infer whether two different call IDs mean the same mutation.
|
|
1609
|
+
|
|
1610
|
+
Events with `type: "tool"` carry the call's `id`, `name`, `arguments`, `status` and,
|
|
1611
|
+
after completion, `result`. The saved result distinguishes action success from
|
|
1612
|
+
failure. If a result cannot be saved, `retrySave()` persists the retained result
|
|
1613
|
+
without rerunning the action. Cancellation waits for an already invoked action to
|
|
1614
|
+
settle; actions must honor their signal where cancellation is supported.
|
|
1615
|
+
|
|
1616
|
+
`limits.maxToolCalls` defaults to 32 per turn, including repeat requests.
|
|
1617
|
+
`maxToolArgumentBytes` and `maxToolResultBytes` use the service catalogue's existing
|
|
1618
|
+
defaults (32 KiB and 48 KiB). These bounds also apply during streaming and before
|
|
1619
|
+
call reservation; discovery switches to bounded catalogue entry points using the
|
|
1620
|
+
existing `maxDirectTools` and `discoveryPageSize` settings.
|
|
1621
|
+
|
|
1622
|
+
### Steering an active native turn
|
|
1623
|
+
|
|
1624
|
+
Codex, Claude and OpenCode report `capabilities.steering: true`. Send a distinct
|
|
1625
|
+
message ID with `steer: true` while the conversation is working:
|
|
1626
|
+
|
|
1627
|
+
```js
|
|
1628
|
+
await conversation.send({
|
|
1629
|
+
messageId: newMessageId,
|
|
1630
|
+
text: "Keep the existing layout and change only the colours.",
|
|
1631
|
+
steer: true
|
|
1632
|
+
});
|
|
1633
|
+
```
|
|
510
1634
|
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
1635
|
+
The runtime checks authorization with `operation: "steer"`, resolves any supplied
|
|
1636
|
+
`attachmentIds`, and records a separate application turn only after native
|
|
1637
|
+
admission. Earlier displayed output retains its identity. Application actions
|
|
1638
|
+
already in flight keep their original actor and saved result. A repeated accepted
|
|
1639
|
+
message returns its receipt without resending.
|
|
1640
|
+
|
|
1641
|
+
Codex uses its active-turn steering operation. Claude interrupts the current
|
|
1642
|
+
generation, waits for the native interruption barrier, and sends the instruction
|
|
1643
|
+
through the same process. OpenCode admits the instruction in the same session
|
|
1644
|
+
and follows the new input through completion. No adapter recreates the native
|
|
1645
|
+
conversation to imitate steering.
|
|
1646
|
+
|
|
1647
|
+
Steering an idle conversation fails; submit a normal message instead. A rejected
|
|
1648
|
+
instruction does not become an accepted history entry. A lost acknowledgement
|
|
1649
|
+
remains uncertain until `inspectDelivery()` finds native proof. `cancel()` stops
|
|
1650
|
+
the whole active run, including steering and pending native work. A configured
|
|
1651
|
+
request deadline applies to the whole run; steering does not extend it. API
|
|
1652
|
+
connections report steering as unsupported.
|
|
1653
|
+
|
|
1654
|
+
### Native goals
|
|
1655
|
+
|
|
1656
|
+
Codex and Claude expose `capabilities.goals`; only Codex exposes
|
|
1657
|
+
`capabilities.goalBudgets`. API and OpenCode conversations report goals as
|
|
1658
|
+
unsupported. These operations belong
|
|
1659
|
+
to the server runtime; an application calls them through its authorized transport.
|
|
1660
|
+
|
|
1661
|
+
```js
|
|
1662
|
+
const state = await conversation.read();
|
|
1663
|
+
await conversation.updateGoal({
|
|
1664
|
+
action: "set",
|
|
1665
|
+
messageId: goalCommandId, // Required when goalCommands.set.delivery is "message".
|
|
1666
|
+
expectedSegmentId: state.segmentId,
|
|
1667
|
+
objective: "Implement and verify the requested change.",
|
|
1668
|
+
tokenBudget: 40_000 // Optional; only when goalBudgets is supported.
|
|
1669
|
+
});
|
|
1670
|
+
|
|
1671
|
+
const goal = await conversation.readGoal();
|
|
1672
|
+
await conversation.updateGoal({
|
|
1673
|
+
action: "pause",
|
|
1674
|
+
expectedSegmentId: (await conversation.read()).segmentId,
|
|
1675
|
+
expectedGoalId: goal.id
|
|
1676
|
+
});
|
|
1677
|
+
```
|
|
1678
|
+
|
|
1679
|
+
Commands whose `goalCommands[action].delivery` is `"message"` require a stable
|
|
1680
|
+
`messageId`. Resume, pause and cancel require the current `expectedGoalId` and
|
|
1681
|
+
`expectedSegmentId`, preventing controls from acting on another native
|
|
1682
|
+
conversation or goal.
|
|
1683
|
+
Commands are authorized with `operation: "goal"`; inspection uses `readGoal`.
|
|
1684
|
+
|
|
1685
|
+
A host-bound native conversation can report `segmentId: null` before its first
|
|
1686
|
+
thread exists. Pass that explicit value from the latest read; do not invent an
|
|
1687
|
+
ID or omit the expected identity. The original native owner allows only the
|
|
1688
|
+
initial Set to prepare a thread from that state. Later controls use the actual
|
|
1689
|
+
thread and current goal identity. A stopped or replaced thread does not become
|
|
1690
|
+
an unstarted conversation; its existing recovery and replacement checks still
|
|
1691
|
+
apply. The supplied client binding preserves the value automatically.
|
|
1692
|
+
|
|
1693
|
+
Claude uses its original native `/goal` commands through ordinary message
|
|
1694
|
+
admission. Set and resume require idle work; an unfinished goal must be cleared
|
|
1695
|
+
before another is set. Pause stops the current work while retaining the goal.
|
|
1696
|
+
Cancel proves the current work has stopped before sending `/goal clear`, so it
|
|
1697
|
+
also requires a stable `messageId`. Failed cleanup blocks clear and leaves cleanup
|
|
1698
|
+
retryable through `cancel()`. These commands accept neither attachments nor a token
|
|
1699
|
+
budget. After replacement or engine change, an ordinary Send must deliver the
|
|
1700
|
+
pending continuity briefing before a goal can start.
|
|
1701
|
+
|
|
1702
|
+
Codex uses native goal controls and may attach a goal to ordinary active work.
|
|
1703
|
+
Starting another goal after completion requires the completed goal's ID. These
|
|
1704
|
+
controls accept neither attachments nor authored message receipts. They return
|
|
1705
|
+
the native goal state; clearing returns `null`. Codex pause/clear prevents future
|
|
1706
|
+
automatic continuations but does not interrupt an already running turn;
|
|
1707
|
+
`cancel()` stops that work.
|
|
1708
|
+
|
|
1709
|
+
Claude Set, Resume and Clear return the same admission receipt as `send()`.
|
|
1710
|
+
Reusing an accepted command's `messageId` returns its receipt without executing
|
|
1711
|
+
it again. A lost acknowledgement stays uncertain until `inspectDelivery()` finds
|
|
1712
|
+
native proof. Codex controls use the existing native goal identity checks;
|
|
1713
|
+
refresh `readGoal()` after a failed control before choosing another action.
|
|
1714
|
+
Older Codex goal-message records remain inspection-only and require offline
|
|
1715
|
+
inspection; native goal state cannot prove an authored message receipt.
|
|
1716
|
+
|
|
1717
|
+
Codex schedules its own continuations. The native turn owner preserves its
|
|
1718
|
+
transcript grouping and saved output. When an admitted request continues, JSKIT
|
|
1719
|
+
checks access again and retains that request's actor, tool budget and effect
|
|
1720
|
+
receipts. A goal control alone grants no application-tool authority. A terminal
|
|
1721
|
+
goal notification does not settle the run until its final native turn and
|
|
1722
|
+
persistence callbacks have finished. The configured request deadline covers the
|
|
1723
|
+
admitted request, including its continuations. `read()` includes the last observed
|
|
1724
|
+
`goal`; `readGoal()` queries native state without pausing or resuming it, including
|
|
1725
|
+
work started by another native client. Reopening does not resume unobserved goal
|
|
1726
|
+
work automatically.
|
|
1727
|
+
|
|
1728
|
+
After restart, call `inspectDelivery({ messageId })` for an actual authored Send
|
|
1729
|
+
or steering message. Codex checks that exact native receipt and recovers output
|
|
1730
|
+
through the original native turn owner when the saved thread and run establish
|
|
1731
|
+
ownership. Saved cancellation, failure and application-tool receipts remain
|
|
1732
|
+
authoritative. An older binding without the original run record, or a run owned
|
|
1733
|
+
by a different request, reports a `recoveryLimitation` and preserves stored
|
|
1734
|
+
history. Inspection never sends the message again. Use `readGoal()` to inspect
|
|
1735
|
+
native goal controls; they have no authored message receipt to reconstruct.
|
|
1736
|
+
|
|
1737
|
+
### Native coding tools
|
|
1738
|
+
|
|
1739
|
+
The native drivers disable built-in tools by default. An application that intends
|
|
1740
|
+
to run coding agents can explicitly grant them in its server-side host:
|
|
1741
|
+
|
|
1742
|
+
```js
|
|
1743
|
+
host: { workdir: applicationWorkdir, nativeTools: true }
|
|
1744
|
+
```
|
|
1745
|
+
|
|
1746
|
+
`conversation.capabilities.nativeTools` reports this grant. Codex can execute
|
|
1747
|
+
commands and edit files; Claude enables its built-in command, file and notebook
|
|
1748
|
+
tools; OpenCode enables its command, file and task-list tools. The application
|
|
1749
|
+
still controls the execution account, working directory and environment. This
|
|
1750
|
+
grant uses that account's filesystem permissions and adds no OS sandbox. A
|
|
1751
|
+
multi-user host must supply its managed execution facility and appropriate
|
|
1752
|
+
isolation. User messages and model configuration cannot enable these tools.
|
|
1753
|
+
|
|
1754
|
+
Ambient hooks, MCP integrations and skills remain disabled. Native subagents and
|
|
1755
|
+
interactive permission questions are not enabled by this grant. Application
|
|
1756
|
+
`actions` continue through the authorized, durable tool executor above. The API
|
|
1757
|
+
driver does not provide native coding tools.
|
|
1758
|
+
|
|
1759
|
+
Hosts that require a session command wrapper can also supply its absolute
|
|
1760
|
+
executable path:
|
|
1761
|
+
|
|
1762
|
+
```js
|
|
1763
|
+
host: {
|
|
1764
|
+
workdir: applicationWorkdir,
|
|
1765
|
+
nativeTools: true,
|
|
1766
|
+
commandWrapper: sessionCommandExecutable
|
|
1767
|
+
}
|
|
1768
|
+
```
|
|
1769
|
+
|
|
1770
|
+
The executable receives the original shell command as one argument and runs under
|
|
1771
|
+
the configured host identity and environment. It owns the application's command
|
|
1772
|
+
policy and execution. JSKIT owns the Codex hook, Claude SDK callback and OpenCode
|
|
1773
|
+
tool interception that deliver the command. It quotes both the executable and
|
|
1774
|
+
command without evaluating the command first. A missing or invalid wrapper is
|
|
1775
|
+
an error; there is no unwrapped fallback.
|
|
1776
|
+
|
|
1777
|
+
Codex installs and trusts only this exact hook in its thread configuration; it
|
|
1778
|
+
does not write the shared CLI account configuration. Claude retains safe mode
|
|
1779
|
+
while registering the explicit SDK callback. Other ambient hooks stay disabled.
|
|
1780
|
+
The wrapper applies to shell commands, not native file-editing tools; the managed
|
|
1781
|
+
execution identity remains the filesystem boundary for those tools.
|
|
1782
|
+
|
|
1783
|
+
### Supplied Claude integration
|
|
1784
|
+
|
|
1785
|
+
Install Claude Code and log in with `claude auth login` under the application's
|
|
1786
|
+
server identity. The ordinary single-user host uses that account and owned POSIX
|
|
1787
|
+
process groups. It requires no Vibe64, Genesis or application-specific startup
|
|
1788
|
+
callbacks:
|
|
1789
|
+
|
|
1790
|
+
```js
|
|
1791
|
+
const runtime = createConversationRuntime({
|
|
1792
|
+
engine: "claude",
|
|
1793
|
+
host: { workdir: applicationWorkdir },
|
|
1794
|
+
storage: createFileConversationStorage({ directory: privateConversationDirectory }),
|
|
1795
|
+
authorize: ({ context, conversationId, operation }) => canAccessConversation(context, conversationId, operation)
|
|
1796
|
+
});
|
|
1797
|
+
const conversation = await runtime.open({
|
|
1798
|
+
id: applicationConversationId,
|
|
1799
|
+
context: authenticatedContext,
|
|
1800
|
+
configuration: { systemPrompt: "Answer concisely.", model: "haiku", effort: "low" }
|
|
1801
|
+
});
|
|
1802
|
+
await conversation.send({ messageId: applicationMessageId, text: "Hello" });
|
|
1803
|
+
await conversation.wait();
|
|
1804
|
+
await runtime.close();
|
|
1805
|
+
```
|
|
1806
|
+
|
|
1807
|
+
The native identity, working directory and credential-home binding remain in
|
|
1808
|
+
server-side runtime metadata. Prompt changes replace the process while preserving
|
|
1809
|
+
native history; compatible model/effort changes use native controls. Account changes
|
|
1810
|
+
cannot silently continue the previous account's conversation. Application access
|
|
1811
|
+
is checked again after native preparation and before dispatch. Built-in tools are
|
|
1812
|
+
disabled unless the server grants `host.nativeTools`. Optional application `actions` use the shared
|
|
1813
|
+
executor and authorized attachments use the common resolver above. Steering uses
|
|
1814
|
+
the shared operation described above. Goals use the extracted native commands
|
|
1815
|
+
through the same admission and delivery path; see Native goals above.
|
|
1816
|
+
The application-tool transport was verified with Claude Code 2.1.287.
|
|
1817
|
+
|
|
1818
|
+
Native login checks use the extracted production status reader. Concurrent reads
|
|
1819
|
+
share a query; public account details may be reused for 30 seconds while native
|
|
1820
|
+
credential-file metadata is unchanged. File changes invalidate the result,
|
|
1821
|
+
failed reads are retryable, and signed-out JSON is distinct from a failed query.
|
|
1822
|
+
On macOS, only concurrent reads are shared because native credentials may live
|
|
1823
|
+
in the keychain. JSKIT does not parse credential contents for this cache.
|
|
1824
|
+
The extracted native account guard prevents a different signed-in Claude account
|
|
1825
|
+
from resuming owned history. Authorized external-provider connections can rotate
|
|
1826
|
+
their API keys while retaining the native conversation; application authorization
|
|
1827
|
+
still decides which connection the caller may use.
|
|
1828
|
+
Compatible connection/key changes use the production flag-then-model control
|
|
1829
|
+
sequence on the existing process. Selected connection credentials and model
|
|
1830
|
+
overrides are installed after initialization, rather than in process arguments
|
|
1831
|
+
or its startup environment. Returning to the native connection clears those
|
|
1832
|
+
overrides and restores any explicit standalone host routing. Changed system
|
|
1833
|
+
instructions still require a confirmed stop and resume of the same native history.
|
|
1834
|
+
|
|
1835
|
+
An authorized API connection can use the same `connections` resolver and
|
|
1836
|
+
`{ systemPrompt, integrationId, effort }` configuration as Codex. JSKIT configures
|
|
1837
|
+
the selected provider's endpoint, exact model, background models and compaction
|
|
1838
|
+
window. The native `[1m]` context flag is added where required; it does not rename
|
|
1839
|
+
the model. No native subscription login is required for this connection. Use
|
|
1840
|
+
`select()` with a complete configuration to change provider or return to the
|
|
1841
|
+
native account while retaining the Claude conversation. An explicit `model` must
|
|
1842
|
+
match the connection. The connection resolver authorizes the current key before
|
|
1843
|
+
dispatch; rotating an authorized external-provider key preserves the conversation.
|
|
1844
|
+
|
|
1845
|
+
Advanced hosts may supply `host.execution` with `start`/`stop`, `host.env`,
|
|
1846
|
+
`host.commands.claude` and `host.limits`. The common execution request carries the
|
|
1847
|
+
executable, arguments, directory, environment, stream requirement and resource
|
|
1848
|
+
limits. The supplied local facility rejects unenforceable resource limits. It
|
|
1849
|
+
does not recover ownership of an unknown process after a hard application crash;
|
|
1850
|
+
that requires operator cleanup or a host with durable execution ownership. Clean
|
|
1851
|
+
runtime shutdown drains its owned processes and preserves native history for resume.
|
|
1852
|
+
|
|
1853
|
+
Advanced execution hosts can use `createConversationProcessIdentity()` from
|
|
1854
|
+
`/server/conversation` for the extracted Linux process-group ownership checks.
|
|
1855
|
+
Supply the environment variable names carrying the runtime token and command
|
|
1856
|
+
hash. `capture()` records the leader's start time; `inspect()` checks the leader
|
|
1857
|
+
and marked descendant groups; `stop()` rechecks ownership before signalling and
|
|
1858
|
+
escalation. Ambiguous or unreadable ownership does not authorize signalling.
|
|
1859
|
+
The caller must validate its persisted metadata contract before requesting these
|
|
1860
|
+
operations and must retain managed-execution authority where one exists. This
|
|
1861
|
+
facility leaves native server sharing and conversation lifetime to their owners.
|
|
1862
|
+
|
|
1863
|
+
### Supplied Codex integration
|
|
1864
|
+
|
|
1865
|
+
Use the same operations, storage and authorization with `engine: "codex"` after
|
|
1866
|
+
installing Codex and running `codex login` as the server identity. Configuration
|
|
1867
|
+
takes `systemPrompt` and optional `model` and `effort`; omit the latter two to use
|
|
1868
|
+
the CLI defaults. `host.commands.codex` can select the executable. The host must
|
|
1869
|
+
support POSIX process ownership and private Unix sockets. Codex must support
|
|
1870
|
+
paginated thread history; the shared-driver check used Codex 0.160.0.
|
|
1871
|
+
|
|
1872
|
+
The common driver uses the extracted `createCodexAppServerRuntime()` and
|
|
1873
|
+
`CodexAppServerAgentProvider`. Conversations own separate native threads and
|
|
1874
|
+
observer connections on a shared server per account/runtime scope. Stopping or
|
|
1875
|
+
closing one conversation leaves other consumers' work and shared server intact. The
|
|
1876
|
+
former process-per-conversation launcher has been removed.
|
|
1877
|
+
|
|
1878
|
+
The existing owner preserves per-directory startup serialization, waiter
|
|
1879
|
+
revalidation, compatibility and liveness checks, verified retirement, helper
|
|
1880
|
+
preparation and startup/publication cleanup. Execution and credential facilities
|
|
1881
|
+
supply host policy; native threads remain independent.
|
|
1882
|
+
The extracted `runCodexAppServerProcess()` leader already owns startup catalogue
|
|
1883
|
+
preparation and the combined Codex/history-adapter lifetime. Its caller supplies
|
|
1884
|
+
the command, runtime directory, runtime token and offered model definitions;
|
|
1885
|
+
the shared runtime coordinates reuse and recovery around this leader.
|
|
1886
|
+
|
|
1887
|
+
The existing native connection/thread owner has also moved into
|
|
1888
|
+
`CodexAppServerAgentProvider` under `/server/codex-provider`. It consumes that shared
|
|
1889
|
+
runtime with execution and credential-state facilities. Its native behavior includes
|
|
1890
|
+
helper token activation/refresh, bounded inventories, control recovery, goals,
|
|
1891
|
+
hook trust, exact command interruption and history export. The existing
|
|
1892
|
+
native consumer now supplies only its host policy and application callbacks. The common
|
|
1893
|
+
driver uses this backend. Ordinary applications use the common conversation API
|
|
1894
|
+
with its supplied execution and credential defaults.
|
|
1895
|
+
|
|
1896
|
+
Changed instructions use the existing thread-control restoration and verification
|
|
1897
|
+
before sending new work; they do not replace the shared process. Ordinary turns
|
|
1898
|
+
preserve the instruction binding. Native compaction remains the engine's job.
|
|
1899
|
+
JSKIT owns the local history adapter used for model-provider changes. It keeps
|
|
1900
|
+
native history intact and translates only outgoing requests. For qualified
|
|
1901
|
+
DeepSeek and GLM models, an OpenAI encrypted compaction is supplemented with the
|
|
1902
|
+
exact readable records from its saved native boundary. Recovery is bounded and
|
|
1903
|
+
fails explicitly for unsupported or ambiguous history, including native Undo or
|
|
1904
|
+
fork records. It never silently truncates, retries inference or rewrites a rollout.
|
|
1905
|
+
|
|
1906
|
+
For a supported external provider, supply the existing `connections` resolver and
|
|
1907
|
+
select `{ systemPrompt, integrationId, effort }`. For example, an integration
|
|
1908
|
+
configured as `deepseek/deepseek-flash` uses that exact model and its private
|
|
1909
|
+
credential. The driver supplies the native catalogue, Responses configuration and
|
|
1910
|
+
history route. No CLI OpenAI login is needed for that external connection. Use
|
|
1911
|
+
`select()` with a complete configuration when changing connection/provider; it
|
|
1912
|
+
retains the Codex thread. `configure()` patches the current configuration. An
|
|
1913
|
+
explicit `model` must agree with the authorized integration. Changing a provider's
|
|
1914
|
+
account requires restoring that account or opening a new conversation.
|
|
1915
|
+
|
|
1916
|
+
The supplied host uses a stable private account/runtime directory, independent
|
|
1917
|
+
of the conversation's workdir. `host.runtimeDirectory` can select an absolute
|
|
1918
|
+
application-owned base directory. Runtime metadata, locks, the history adapter
|
|
1919
|
+
and the Unix socket belong to this shared scope. Closing one conversation retains
|
|
1920
|
+
the service while another conversation uses it. Closing the last consumer stops
|
|
1921
|
+
the service and preserves native history. Runtime retirement requires the existing
|
|
1922
|
+
execution or persisted native process-identity proof.
|
|
1923
|
+
|
|
1924
|
+
The same owner coordinates provider acquisition, account invalidation and lost
|
|
1925
|
+
observation across consumers. Recovery first attempts to stop the affected native
|
|
1926
|
+
thread. If that cannot be confirmed, it records every affected consumer's admission
|
|
1927
|
+
barrier before stopping the shared service. A failed storage write does not leave
|
|
1928
|
+
native work running; failed stop or final persistence remains retryable. Recovery
|
|
1929
|
+
uses the recorded native owner after reopening and never replays the request.
|
|
1930
|
+
Applications using the common runtime do not supply Codex recovery callbacks.
|
|
1931
|
+
|
|
1932
|
+
A custom Codex execution facility supplies `run()` for capture/detached requests
|
|
1933
|
+
and `stop()` with confirmed scope cleanup. The supplied local facility supports
|
|
1934
|
+
both. An existing managed native integration may supply `host.codex({ providerId })`
|
|
1935
|
+
with its prepared runtime, parameters, credential-state and execution facilities,
|
|
1936
|
+
configuration and native options. This is a host-policy seam for existing managed
|
|
1937
|
+
integrations; it supplies no alternative native process or protocol algorithm.
|
|
1938
|
+
The public editor uses it to retain its account/login owner, resource profiles,
|
|
1939
|
+
runtime packs and managed stop authority while its Main conversation uses the
|
|
1940
|
+
same common runtime and original native owner.
|
|
1941
|
+
|
|
1942
|
+
Codex's native `turn/start` response is the admission receipt. A lost response
|
|
1943
|
+
keeps the request uncertain; `inspectDelivery({ messageId })` searches the latest
|
|
1944
|
+
20 native turns for its exact client message ID. If it cannot find proof, delivery
|
|
1945
|
+
remains uncertain and the runtime does not resend. The basic driver disables
|
|
1946
|
+
native tools unless the server grants `host.nativeTools`; hooks and ambient MCP integrations stay disabled through the shared policy
|
|
1947
|
+
used by isolated assistants. Application tools use the shared executor described
|
|
1948
|
+
above, as do authorized attachments, steering and the native goal operations
|
|
1949
|
+
described above.
|
|
1950
|
+
|
|
1951
|
+
### Supplied OpenCode integration
|
|
1952
|
+
|
|
1953
|
+
Install OpenCode 1.18.31 and use `engine: "opencode"` with the same `connections`
|
|
1954
|
+
resolver and `{ systemPrompt, integrationId }` configuration as the API driver.
|
|
1955
|
+
The selected integration supplies the authorized provider, model, context/output
|
|
1956
|
+
limits and API key. The native configuration includes that exact model definition,
|
|
1957
|
+
so OpenCode's own catalogue need not contain the same entry;
|
|
1958
|
+
there is no second account configuration. `host.commands.opencode` can select
|
|
1959
|
+
the installed executable. The supplied host requires POSIX process ownership.
|
|
1960
|
+
|
|
1961
|
+
The host starts a shared authenticated loopback server on first use. Conversations
|
|
1962
|
+
on the same account and runtime directory acquire that owner and retain separate
|
|
1963
|
+
native session IDs. Its private
|
|
1964
|
+
credential home contains only the selected account; ordinary project plugins,
|
|
1965
|
+
configuration and external skills are disabled. Native history is kept under
|
|
1966
|
+
`host.stateDirectory`, defaulting to `.assistant/native/opencode` beneath the
|
|
1967
|
+
working directory, in the shared account directory's `opencode.db`. Keep this directory alongside application conversation storage
|
|
1968
|
+
across restarts. The private process directory is removed only after confirmed
|
|
1969
|
+
cleanup. The local host has the hard-crash ownership limitation described above.
|
|
1970
|
+
|
|
1971
|
+
Instructions are installed through the native system transformation hook before
|
|
1972
|
+
each inference, including after automatic compaction. Updating instructions keeps
|
|
1973
|
+
the same process and native conversation without appending a user message. Each
|
|
1974
|
+
turn selects its configured native model and tool permissions independently of
|
|
1975
|
+
other conversations. A selected model must be available in that server's native
|
|
1976
|
+
catalogue; unavailable definitions fail before dispatch.
|
|
1977
|
+
A different account requires a new conversation or restoration of the original
|
|
1978
|
+
connection. Built-in tools require the server's `host.nativeTools` grant; optional
|
|
1979
|
+
application `actions` use the shared executor. Attachments and steering use the
|
|
1980
|
+
shared operations above. Native goals are not supported by this driver.
|
|
1981
|
+
|
|
1982
|
+
With the registered execution guard, tool-free agents retain native tool definitions
|
|
1983
|
+
behind approval so included providers can accept the request. This does not grant
|
|
1984
|
+
file or command access: the plugin rejects every ungranted native call before
|
|
1985
|
+
execution, including child sessions, and fails closed if its binding is missing
|
|
1986
|
+
or unreadable. Without that registry, native tools remain denied. Application
|
|
1987
|
+
tools still require the current authorized catalog and shared executor.
|
|
1988
|
+
|
|
1989
|
+
The native prompt response acknowledges admission. Lost acknowledgement remains
|
|
1990
|
+
uncertain; inspection searches the latest 100 native messages for the exact
|
|
1991
|
+
submitted ID and never resends. Event observation is established before dispatch;
|
|
1992
|
+
losing it uses the original native interrupt/idle check for that exact session.
|
|
1993
|
+
If session stopping cannot be confirmed, the shared owner stops that exact server
|
|
1994
|
+
and fails its affected peers. Closing an ordinary conversation releases its own
|
|
1995
|
+
binding; the final user releases the server. A saved process reference alone does
|
|
1996
|
+
not authorize an unopened conversation to stop the shared service.
|
|
1997
|
+
The shared native turn observer follows automatic compaction continuations and
|
|
1998
|
+
waits for a stable completed answer and native idle state, including after tool
|
|
1999
|
+
rounds. If a successful response contains only reasoning, it requests the missing
|
|
2000
|
+
final answer once within the same admitted application turn. A second empty
|
|
2001
|
+
response fails explicitly; the original request is not submitted again.
|
|
2002
|
+
Native event filtering excludes errors from older turns or another conversation.
|
|
2003
|
+
|
|
2004
|
+
Managed applications that already own an OpenCode service can supply
|
|
2005
|
+
`host.opencode({ connection, context })`. It returns the existing shared `runtime`,
|
|
2006
|
+
absolute `runtimeDirectory`, `registryPath` and `databasePath`, the authorized
|
|
2007
|
+
`selected` connection identity, and lazy `prepareServer()` application preparation.
|
|
2008
|
+
The common driver passes that preparation to the same runtime's
|
|
2009
|
+
`startPreparedProcess`; native acquisition and startup stay inside the shared
|
|
2010
|
+
owner. The host retains execution policy, credential resolution and durable
|
|
2011
|
+
restart/stop proof. Ordinary apps use the supplied facility without writing one.
|
|
2012
|
+
Existing development bindings from the earlier private `history.db` implementation
|
|
2013
|
+
are rejected before inference; changing the stored path does not migrate native
|
|
2014
|
+
history. Their offline conversion remains unfinished.
|
|
2015
|
+
Managed consumers retain their application-specific error presentation.
|
|
2016
|
+
|
|
2017
|
+
## Advanced host facilities
|
|
2018
|
+
|
|
2019
|
+
Ordinary applications use the [five entrypoints](./assistant.md#the-ordinary-api)
|
|
2020
|
+
and the supplied runtime's engine defaults. The following modules support hosts
|
|
2021
|
+
that already own one of the facilities in this table. They share the runtime's
|
|
2022
|
+
native implementations; they are not separate conversation integration recipes.
|
|
2023
|
+
Keep authorization, credentials, storage and verified cleanup with their named
|
|
2024
|
+
owners. A custom host does not create another sender, monitor or receipt journal.
|
|
2025
|
+
|
|
2026
|
+
| Core subpath | Existing host responsibility |
|
|
2027
|
+
| --- | --- |
|
|
2028
|
+
| `/server/codex-configuration` | Account configuration, offered-model policy and authorized Helper isolation. |
|
|
2029
|
+
| `/server/codex-process` | Managed execution, credential-home/runtime scope and native process-leader launch. |
|
|
2030
|
+
| `/server/codex-provider` | Compose the original provider owner with host account, execution and history-preservation facilities. |
|
|
2031
|
+
| `/server/codex-turn` | Compose the original run owner with the application's admitted transcript, checkpoint and publication effects. |
|
|
2032
|
+
| `/server/codex-events` | Project authorized native event identities and history facts into product status. |
|
|
2033
|
+
| `/server/claude-process` | Managed process launch and authorized account/model/usage inspection. |
|
|
2034
|
+
| `/server/claude-turn` | Compose the original retained conversation owner with application admission, persistence and result effects. |
|
|
2035
|
+
| `/server/claude-history` | Inventory and preserve authorized native history for explicit retirement. |
|
|
2036
|
+
| `/server/opencode-process` | Managed shared-service execution, account setup and native instruction/environment plugin configuration. |
|
|
2037
|
+
| `/server/opencode-turn` | Bounded renewal and authorized prompt/result projection for an existing native host. |
|
|
2038
|
+
| `/server/opencode-client` | Preserve and retire authorized native history through the existing shared client. |
|
|
2039
|
+
| `/shared/native-providers` | Native provider/model facts; the application still chooses its offerings and permissions. |
|
|
2040
|
+
|
|
2041
|
+
The advanced storage, transcript, hook and process-identity helpers already
|
|
2042
|
+
exported by `/server/conversation` support an application's existing storage or
|
|
2043
|
+
execution boundary. They are not required beside `createConversationRuntime`
|
|
2044
|
+
and a supplied storage adapter. The sections below explain their contracts.
|
|
2045
|
+
No additional host aggregator or engine-specific entrypoint is needed for normal
|
|
2046
|
+
API, Codex, Claude or OpenCode conversations.
|
|
2047
|
+
|
|
2048
|
+
### Testing entrypoints
|
|
2049
|
+
|
|
2050
|
+
These entries require disposable fixtures and explicit cleanup. They never grant
|
|
2051
|
+
account access and are not an application startup path.
|
|
2052
|
+
|
|
2053
|
+
| Core subpath | Fixture purpose |
|
|
2054
|
+
| --- | --- |
|
|
2055
|
+
| `/testing/conversation-storage` | Exercise a custom adapter's transcript, scope, atomicity and rollback contract. |
|
|
2056
|
+
| `/testing/native-codex` | Drive the original native protocol/history/catalogue probes against an isolated executable or endpoint. |
|
|
2057
|
+
| `/testing/native-opencode` | Drive the original loopback client against an isolated host fixture. |
|
|
2058
|
+
| `/server/native-history` | Retain the shared retirement-contract entry used by existing archive/deletion fixtures. Engine integrations use their engine-specific retirement operations. |
|
|
2059
|
+
|
|
2060
|
+
### Existing scoped native hosts
|
|
2061
|
+
|
|
2062
|
+
An application with an existing admitted scoped-operation owner can use the same
|
|
2063
|
+
created runtime's `runScopedTurn({ operations, scope, input, context,
|
|
2064
|
+
executionProfile })`. Supply that owner, whose existing methods are
|
|
2065
|
+
`createEphemeralConversation`, `startEphemeralConversationTurn`,
|
|
2066
|
+
`waitForEphemeralConversationTurn` and `stopEphemeralConversation`; each receives
|
|
2067
|
+
`(scope, input, context)`. The operations retain their current account, profile
|
|
2068
|
+
and access checks. Their native execution uses the same runtime instance. This
|
|
2069
|
+
is a server-only host facility with no HTTP or socket command.
|
|
2070
|
+
|
|
2071
|
+
When an existing host must create a native identity before opening a retained
|
|
2072
|
+
handle, its admitted server operation can call
|
|
2073
|
+
`runtime.createNativeConversation({ id, context, input })`. Here `id` is the
|
|
2074
|
+
existing parent session or Helper scope, not a placeholder native identity. The
|
|
2075
|
+
runtime checks access with operation `create`, then calls only its configured
|
|
2076
|
+
`host.conversation({ id, context, input, operation: "create" })`. This host branch
|
|
2077
|
+
must return its prepared `{ sessionId: id, engine, native, input, context }` using
|
|
2078
|
+
the same native owner and authorized application configuration. It must not build
|
|
2079
|
+
a Main binding or open another conversation. The existing driver performs Create;
|
|
2080
|
+
the original native owner allocates and records the real identity. The application
|
|
2081
|
+
still owns profile validation, parent receipts and failed-creation cleanup.
|
|
2082
|
+
This advanced host operation has no HTTP, socket or client counterpart and accepts
|
|
2083
|
+
no caller-supplied engine, native descriptor or host override. Ordinary standalone
|
|
2084
|
+
applications keep using `open()` and their existing storage contract.
|
|
2085
|
+
|
|
2086
|
+
The same advanced host can prepare native readiness before retaining a handle
|
|
2087
|
+
with `runtime.ensureNativeConversation({ id, context })`. Authorization uses
|
|
2088
|
+
operation `ensure`; only the configured host supplies the prepared
|
|
2089
|
+
`{ sessionId: id, engine, native, context }`. The existing driver and native owner
|
|
2090
|
+
perform readiness. This operation creates no placeholder identity or additional
|
|
2091
|
+
retained runtime handle. Closure during host preparation prevents acquisition.
|
|
2092
|
+
|
|
2093
|
+
`runtime.inspectNativeTemporaryActivity({ id, context })` authorizes
|
|
2094
|
+
`inspectTemporaryActivity` and reads the selected owner's existing temporary-work
|
|
2095
|
+
inventory. Its configured host supplies `{ sessionId: id, engine, native, context }`.
|
|
2096
|
+
It opens no Main conversation and remains available during cleanup after runtime
|
|
2097
|
+
closure. Native restoration and activity exclusions are those of the same owner;
|
|
2098
|
+
there is no additional cache. This advanced server operation has no browser route
|
|
2099
|
+
or caller-supplied owner/engine override.
|
|
2100
|
+
|
|
2101
|
+
An existing application's renewal transaction can invoke
|
|
2102
|
+
`runtime.generateNativeRenewalHandover({ id, context, input })` and
|
|
2103
|
+
`runtime.seedNativeRenewalHandover({ id, context, input })`. The runtime authorizes
|
|
2104
|
+
`generateRenewalHandover` or `seedRenewalHandover` and obtains
|
|
2105
|
+
`{ sessionId: id, engine, native, context, input }` only from its configured host.
|
|
2106
|
+
The driver uses the same native owner and application configuration/storage
|
|
2107
|
+
effects. Neither operation opens a retained handle or replaces the application's
|
|
2108
|
+
hidden-successor transaction. These advanced server operations have no browser
|
|
2109
|
+
transport and accept no caller-selected engine, native owner or host. Applications
|
|
2110
|
+
retain approved handover, private access, leases and ACK/commit policy.
|
|
2111
|
+
|
|
2112
|
+
An application's already-authorized renewal cleanup can release its predecessor
|
|
2113
|
+
or successor evidence with
|
|
2114
|
+
`runtime.releaseNativeRenewalPredecessorProcessExitProof({ id, context, input })`
|
|
2115
|
+
and `runtime.releaseNativeRenewalSuccessorProcessExitProof({ id, context, input })`.
|
|
2116
|
+
These trusted server operations obtain matching `{ sessionId: id, engine, native,
|
|
2117
|
+
context, input }` synchronously from the configured host. They open no conversation,
|
|
2118
|
+
admit no inference and remain retryable after runtime closure. They have no browser
|
|
2119
|
+
endpoint and accept no caller-selected owner or engine. The host must preserve
|
|
2120
|
+
its existing renewal authorization before native cleanup.
|
|
2121
|
+
|
|
2122
|
+
The same native owners preserve their original contracts: Codex validates
|
|
2123
|
+
application renewal authority before its lifecycle queue, then reads saved runtime
|
|
2124
|
+
facts inside that queue and releases persisted proof only after shared-peer checks;
|
|
2125
|
+
Claude closes through its existing entry/terminal cleanup; OpenCode predecessor
|
|
2126
|
+
cleanup awaits close before consuming proof, while successor cleanup only consumes
|
|
2127
|
+
proof. OpenCode retains its original distinction between a resolved failed close
|
|
2128
|
+
(which still consumes proof) and rejected close (which leaves proof retryable).
|
|
2129
|
+
Applications retain result projection and successful binding retirement.
|
|
2130
|
+
|
|
2131
|
+
`runtime.disposeNative({ namespace, sessionId, context, options })` also supports
|
|
2132
|
+
cleanup when no ready retained handle exists. It authorizes operation `dispose`
|
|
2133
|
+
and requests `{ id: sessionId, context, input: options, operation: "dispose" }`
|
|
2134
|
+
from its configured host. The response must carry the matching `sessionId` and
|
|
2135
|
+
`namespace`, plus `engine`, `native`, `context` and `options`. The same driver
|
|
2136
|
+
performs native cleanup through the existing owner. A failed cleanup remains
|
|
2137
|
+
retryable, including after runtime closure; no Main record is opened. Ready
|
|
2138
|
+
retained handles keep their existing queue and retire only after native and
|
|
2139
|
+
awaited application completion succeed. An unready local handle retains its
|
|
2140
|
+
existing early-retirement fence before falling back to the configured owner.
|
|
2141
|
+
These server-only operations accept no caller-selected owner or engine and add
|
|
2142
|
+
no browser endpoints. Ordinary applications still use their conversation handle.
|
|
2143
|
+
|
|
2144
|
+
Historical native records can predate an application's retained scope identity.
|
|
2145
|
+
An advanced host can clean up those exact records through
|
|
2146
|
+
`runtime.interruptNativeDetachedConversation({ id, context, input })` or
|
|
2147
|
+
`runtime.deleteNativeDetachedConversation({ id, context, input })`. Authorization
|
|
2148
|
+
uses `interruptDetachedConversation` or `deleteDetachedConversation`; the configured
|
|
2149
|
+
host returns `{ sessionId: id, engine, native, context, input }`. The same drivers
|
|
2150
|
+
invoke the original native control owners without constructing a scope, opening
|
|
2151
|
+
Main or acquiring a retained queue. Native identity, profile and shutdown checks
|
|
2152
|
+
remain with those owners. Cleanup still checks caller authority after runtime
|
|
2153
|
+
closure; the runtime does not add a new closed-state veto to an existing retry.
|
|
2154
|
+
|
|
2155
|
+
Saved-history maintenance uses
|
|
2156
|
+
`runtime.listNativeConversationStorage({ id, context, binding })` and
|
|
2157
|
+
`runtime.retireNativeConversationHistory({ id, context, binding })`. These are
|
|
2158
|
+
trusted server storage operations: the host must already authorize the inventory
|
|
2159
|
+
or archive transaction, preserve its idle/writer exclusion rules and provide the
|
|
2160
|
+
retirement preservation effect. The configured host receives operations
|
|
2161
|
+
`listNativeConversationStorage` or `retireConversationHistory` and returns
|
|
2162
|
+
`{ sessionId: id, engine, native, context, binding }`. `engine` must match the
|
|
2163
|
+
same saved `binding.engineId`; current conversation selection is not consulted.
|
|
2164
|
+
The existing native owners enumerate, export and delete history. These calls add
|
|
2165
|
+
no inference authorization or runtime-closure rule to the original storage policy.
|
|
2166
|
+
All four operations are server-only, accept no caller-supplied native owner or
|
|
2167
|
+
host, and have no HTTP, socket or client route. Ordinary applications continue
|
|
2168
|
+
using their conversation handles.
|
|
2169
|
+
|
|
2170
|
+
`runtime.closeNativeProject({ context, input })` is trusted server lifecycle
|
|
2171
|
+
cleanup, like `runtime.close()`. The application must already authorize the
|
|
2172
|
+
project operation; it does not use conversation authorization or expose a browser
|
|
2173
|
+
operation. The configured host receives `{ context, input, operation: "closeProject" }`
|
|
2174
|
+
and synchronously returns `{ engine, native, context, input }` with its existing
|
|
2175
|
+
native owner and application preparation. Codex, Claude and OpenCode support this
|
|
2176
|
+
advanced operation. It opens no conversation or retained handle and remains
|
|
2177
|
+
retryable after runtime closure, including when credentials have been revoked.
|
|
2178
|
+
|
|
2179
|
+
`runtime.invalidateNativeRuntimes({ context, input })` uses the same trusted
|
|
2180
|
+
server boundary for account changes and shutdown. Its configured host receives
|
|
2181
|
+
operation `"invalidateRuntimes"` and supplies the existing native owner
|
|
2182
|
+
synchronously. The runtime returns the native operation directly: shutdown
|
|
2183
|
+
fences and synchronous errors are not deferred behind another await. The host
|
|
2184
|
+
retains account policy and result presentation; the native owner retains its
|
|
2185
|
+
original cleanup sequence and retryable failures. No conversation is opened,
|
|
2186
|
+
and failed cleanup can be retried after runtime closure. This is not a browser
|
|
2187
|
+
operation or a way to submit a caller-selected native owner.
|
|
2188
|
+
|
|
2189
|
+
`runtime.reconcileNativeSessions({ context, sessions, options })` restores an
|
|
2190
|
+
application's saved session collection through those same configured native
|
|
2191
|
+
owners. The host synchronously supplies `{ engine, native, context, sessions,
|
|
2192
|
+
options }` for operation `"reconcileSessions"`. The application retains its
|
|
2193
|
+
session inventory, authorization and storage codecs. Codex retains its original
|
|
2194
|
+
concurrent recovery and prune coordination; OpenCode retains per-session failure
|
|
2195
|
+
results and continues; Claude retains sequential, fail-fast restoration.
|
|
2196
|
+
|
|
2197
|
+
`runtime.unsubscribeNativeSessions({ context, sessions })` supplies startup
|
|
2198
|
+
subscription reset for Codex through operation `"unsubscribeSessions"` and a
|
|
2199
|
+
synchronous `{ engine, native, context, sessions }` descriptor. It does not select
|
|
2200
|
+
or stop every engine. These trusted server lifecycle calls open no conversation,
|
|
2201
|
+
create no admission receipt and replay no submission. They return the original
|
|
2202
|
+
owner operation directly, preserving its error and retry behavior. They are not
|
|
2203
|
+
browser operations; ordinary applications continue using conversation handles.
|
|
2204
|
+
|
|
2205
|
+
The original coordinator creates a native conversation only when `input` has no
|
|
2206
|
+
`conversationId` or `threadId`. It awaits `context.onEvent` for the native thread
|
|
2207
|
+
before Start and for the native turn before waiting. Keep durable task ownership
|
|
2208
|
+
in those awaited host writes, rather than a presentation subscription. Abort
|
|
2209
|
+
stops the exact native turn and waits for the existing Stop proof; a Stop failure
|
|
2210
|
+
retains precedence over the earlier wait or abort error. Parent-write failures
|
|
2211
|
+
propagate to the application's existing cleanup owner.
|
|
2212
|
+
|
|
2213
|
+
Repeated calls retain the supplied conversation ID and return the current native
|
|
2214
|
+
`threadId` and `turnId`. The coordinator creates no transcript, placeholder ID or
|
|
2215
|
+
receipt registry, and does not delete the conversation between responses. The
|
|
2216
|
+
optional `executionProfile` argument is an already-validated audit snapshot copied
|
|
2217
|
+
only into the final result; it grants no authority and does not alter early
|
|
2218
|
+
failure envelopes. The application retains native deletion, durable cleanup
|
|
2219
|
+
receipts and retry policy. Ordinary standalone conversations continue using
|
|
2220
|
+
`open`, `send`, `wait` and `cancel`.
|
|
2221
|
+
|
|
2222
|
+
An existing bounded structured-response workflow can compose those same native
|
|
2223
|
+
turns with `runBoundedAssistantToolLoop` from `@jskit-ai/assistant-core/server`.
|
|
2224
|
+
Supply the application instructions as `prompt`, a final-only `outputSchema`,
|
|
2225
|
+
the existing `toolCatalog` and trusted `toolContext`, `signal`, and the response
|
|
2226
|
+
policy. The shared owner derives a closed final-or-tool schema from the catalogue,
|
|
2227
|
+
parses the reply, executes one selected action through `createConversationTools`,
|
|
2228
|
+
and constructs the next untrusted-result prompt. The application supplies no
|
|
2229
|
+
reply parser, action selector or next-prompt callback.
|
|
2230
|
+
|
|
2231
|
+
`policy.maximumResponses` counts completed responses. The existing
|
|
2232
|
+
`complete(prompt, { timeoutMs, outputSchema })` facility runs one admitted native
|
|
2233
|
+
turn and returns its `{ ok, text }` result; the loop returns the parsed final value.
|
|
2234
|
+
`policy.timeoutMs` applies to each response, not the intervening application
|
|
2235
|
+
operation. An operation requested by the last allowed response still completes
|
|
2236
|
+
before `limitError` is thrown. There is no extra inference or recovery pass.
|
|
2237
|
+
`invalidResponseError` and `failureError` may supply application error defaults;
|
|
2238
|
+
provider failure messages/codes and thrown host errors retain their existing path.
|
|
2239
|
+
|
|
2240
|
+
Catalogue descriptors are resolved before each response to construct its bounded
|
|
2241
|
+
schema, including a final response; no action executes for a final result. The
|
|
2242
|
+
shared owner creates call IDs and permits one application operation per response.
|
|
2243
|
+
This structured transport does not register native tools or grant new native
|
|
2244
|
+
permissions. Keep application access checks, schema validation and result bounds
|
|
2245
|
+
in the action definitions, and retain the existing scoped turn's native isolation.
|
|
2246
|
+
|
|
2247
|
+
This bounded path explicitly uses transient call ownership: it serializes the
|
|
2248
|
+
operation and retains its result in memory for that response, without claiming
|
|
2249
|
+
durable replay or creating an authored turn. A host exception is rethrown as the
|
|
2250
|
+
same exception after safe result classification, ending the loop before another
|
|
2251
|
+
native response; it is never put in a tool result or context. Applications retain
|
|
2252
|
+
their existing parent cleanup receipt and restart policy. Canonical conversation
|
|
2253
|
+
tool execution still requires its authorization, durable reservation/result
|
|
2254
|
+
writer and event facilities.
|
|
2255
|
+
Use this bounded operation for a task with an existing completed-response budget,
|
|
2256
|
+
transient results and immediate failure policy. Ordinary conversations use the
|
|
2257
|
+
runtime's durable tool calls. Disabling built-in native tools alone is not a reason
|
|
2258
|
+
to select the bounded protocol: both paths can execute supplied application actions
|
|
2259
|
+
through the same catalogue and execution owner.
|
|
2260
|
+
|
|
2261
|
+
### Native instruction lifecycle
|
|
2262
|
+
|
|
2263
|
+
The created conversation runtime owns instruction installation and refresh.
|
|
2264
|
+
Ordinary applications supply their current system prompt and authorized host
|
|
2265
|
+
facilities through that runtime; they do not construct separate instruction
|
|
2266
|
+
adapters. Native history remains intact, and instructions do not become authored
|
|
2267
|
+
user messages. None of the native owners imports an application or a
|
|
2268
|
+
project-guidance compiler.
|
|
2269
|
+
|
|
2270
|
+
| Native owner | Host supplies | Owner retains |
|
|
2271
|
+
| --- | --- | --- |
|
|
2272
|
+
| Claude turn | Current prompt/configuration and the existing process facility | Serialized preparation, compatible native settings updates, replacement against the same history and installation invalidation |
|
|
2273
|
+
| Codex provider | Current instructions and its existing execution/environment/history facilities | Developer instruction installation, per-thread control serialization and verified recovery |
|
|
2274
|
+
| OpenCode plugin | Registered conversation bindings and optional host prompt composition | Native/host transformer selection, instruction contributions and compaction/deletion invalidation |
|
|
2275
|
+
|
|
2276
|
+
Claude's retained conversation owner owns instruction preparation and the
|
|
2277
|
+
ordered turn receiver. Managed hosts use `createClaudeConversationOwner` from
|
|
2278
|
+
`/server/claude-turn`; ordinary applications use the common runtime. Neither
|
|
2279
|
+
assembles a second turn receiver or supplies a parallel stop implementation.
|
|
2280
|
+
Its activity query restores saved entries through the existing storage/acquisition
|
|
2281
|
+
facilities before inspecting its own native state. Its history-idle query reads
|
|
2282
|
+
that same state; the host separately checks durable process receipts and actual
|
|
2283
|
+
terminal activity. No second activity cache or restoration loop is required.
|
|
2284
|
+
|
|
2285
|
+
The owner serializes preparation with native controls and preserves the original
|
|
2286
|
+
control-interrupt followed by mandatory process drain. A failed drain stays an
|
|
2287
|
+
error. The host retains execution policy, durable cleanup and publication at
|
|
2288
|
+
their existing boundaries.
|
|
2289
|
+
|
|
2290
|
+
Preparation uses the current system prompt, context identity, instruction mode,
|
|
2291
|
+
settings and model. Complete dedicated contexts replace native instructions;
|
|
2292
|
+
append mode retains native coding defaults. Compatible live updates install
|
|
2293
|
+
native flag settings before selecting a model. Failed controls retire the
|
|
2294
|
+
process; active incompatible updates are rejected without invalidating ordinary
|
|
2295
|
+
steering. Lost instruction installations are invalidated before new work.
|
|
2296
|
+
|
|
2297
|
+
The same owner prepares instruction flags for an explicitly opened native
|
|
2298
|
+
terminal. The flags disable Claude's native system-prompt snapshot so resumed
|
|
2299
|
+
history receives the current prompt before compaction. A host launches the
|
|
2300
|
+
prepared terminal arguments unchanged and proves cleanup before replacement.
|
|
2301
|
+
|
|
2302
|
+
Codex's existing provider distinguishes a socket reconnect, native process loss
|
|
2303
|
+
and a different native conversation. It serializes controls per thread,
|
|
2304
|
+
pauses/resumes an active goal only when still authorized, verifies the owned
|
|
2305
|
+
shell environment after reload and never retries the caller's operation.
|
|
2306
|
+
Changed instructions or an unknown installation update native configuration and
|
|
2307
|
+
add one explicit developer-context revision before new work. Acknowledged ordinary
|
|
2308
|
+
turns and socket reconnects do not repeat it. Compaction rebuilds context with the
|
|
2309
|
+
current configuration; restricted consumers retain their own execution environment.
|
|
2310
|
+
The same provider owner selects and replaces cached providers, constructs native
|
|
2311
|
+
connections, acquires the shared runtime and retries an unconfirmed observation
|
|
2312
|
+
stop before reconnecting. It retains the original command-control subscription and
|
|
2313
|
+
native project-hook trust refresh before resume. A host supplies authorized account,
|
|
2314
|
+
context, settings and managed execution preparation at their existing read points.
|
|
2315
|
+
The existing run owner coordinates native readiness and Send preparation as well.
|
|
2316
|
+
Managed readiness acquires its provider before entering the supplied startup gate;
|
|
2317
|
+
Send acquisition stays inside that gate. Hosts supply authorized configuration,
|
|
2318
|
+
identity storage and application health effects. Native preparation, observation,
|
|
2319
|
+
managed registration and abandoned-start recovery retain their original order.
|
|
2320
|
+
The same owner selects the first available managed provider matching the existing
|
|
2321
|
+
session, workdir and thread. It reads its existing shared provider maps; a host
|
|
2322
|
+
does not scan or duplicate that native inventory.
|
|
2323
|
+
Ordinary Send uses the same preparation with its tool settings and durable binding.
|
|
2324
|
+
The same native provider owner coordinates retained-thread release, drains its
|
|
2325
|
+
existing notification queue and selects cached, shared or persisted-runtime
|
|
2326
|
+
cleanup before classifying exit proof. The host supplies current identity values
|
|
2327
|
+
and configured managed execution; application admission, renewal policy and
|
|
2328
|
+
terminal/attachment cleanup remain outside that native release sequence.
|
|
2329
|
+
The same provider owner releases preserved process-exit proof only after its
|
|
2330
|
+
original shared-peer and persisted-runtime checks. It reuses the existing
|
|
2331
|
+
disk-locked stop and post-stop existence evidence; authorized lifecycle policy,
|
|
2332
|
+
metadata-to-host preparation and application result/error projection stay with
|
|
2333
|
+
the host.
|
|
2334
|
+
For supplied provider sessions, output preparation returns identity and lazy runtime
|
|
2335
|
+
options; the run owner performs acquisition through its existing provider selector.
|
|
2336
|
+
The provider owner also performs managed-thread registration, recovery scheduling
|
|
2337
|
+
and the before-resume observation guard using that same run owner. The host supplies
|
|
2338
|
+
the authorized current run record and its original execution context.
|
|
2339
|
+
For an advanced interactive terminal, the same run owner exposes full thread
|
|
2340
|
+
readiness and visible-terminal runtime attachment as separate native operations.
|
|
2341
|
+
The host keeps application admission and authorized PTY preparation between them:
|
|
2342
|
+
readiness precedes source/account checks, and runtime attachment follows the
|
|
2343
|
+
startup gate's current session/environment read. Both operations use the existing
|
|
2344
|
+
shared provider inventory; they do not create a second terminal-native owner.
|
|
2345
|
+
Interactive-terminal attachment grants also use this owner
|
|
2346
|
+
(`allowTerminalAttachments`): it selects the existing retained native session
|
|
2347
|
+
and issues the original grant before the host writes to the PTY. The host keeps
|
|
2348
|
+
attachment admission, Git policy and the PTY namespace.
|
|
2349
|
+
The same owner also retains native-target terminal attachment bookkeeping. It
|
|
2350
|
+
reuses the host's existing terminal snapshot before attaching, records the
|
|
2351
|
+
returned terminal ID on that target, and clears matching IDs after the host's
|
|
2352
|
+
existing PTY close resolves. The host supplies only its original read, close
|
|
2353
|
+
and namespace functions; a thrown close keeps the native attachment retryable.
|
|
2354
|
+
Project cleanup restores the same Helper records, closes the scoped native
|
|
2355
|
+
conversations and selects their providers through these existing owners.
|
|
2356
|
+
Conversation preparation also applies Helper restoration or ordinary-thread
|
|
2357
|
+
ledger exclusion before admission. Native activity and plan-usage queries read
|
|
2358
|
+
the same owned maps; hosts retain authorization and result presentation.
|
|
2359
|
+
|
|
2360
|
+
`createOpenCodeConversationPlugin` from `/server/opencode-process` owns the native
|
|
2361
|
+
system hook and event invalidation. An existing host that composes its own
|
|
2362
|
+
instructions may supply `resolveHostInstructions(id)`, returning
|
|
2363
|
+
`{ conversation, instructions }`. `conversation` is the selected registry row's
|
|
2364
|
+
native conversation binding; `instructions` is the original host descriptor
|
|
2365
|
+
`{ identity, read, placement }` or `null` for an unowned prompt. Prompt composition
|
|
2366
|
+
and its errors remain host-owned.
|
|
2367
|
+
|
|
2368
|
+
The plugin resolves the host selection first, then lets the selected native or
|
|
2369
|
+
host instruction owner resolve its current input again. Its native registry path
|
|
2370
|
+
remains the path captured at plugin creation; the host resolver retains its own
|
|
2371
|
+
lookup policy. The two existing caches stay separate, and events invalidate host
|
|
2372
|
+
then native state. Do not add application event/system-hook fanout around this
|
|
2373
|
+
plugin.
|
|
2374
|
+
|
|
2375
|
+
Omit a host descriptor's `identity` to read current text before every inference.
|
|
2376
|
+
Supply one only when it covers every source of prompt content; unchanged identities
|
|
2377
|
+
share a pending or completed read. Changed identity, compaction or deletion forces
|
|
2378
|
+
a fresh read. Failed reads remain retryable, and an invalidated pending read cannot
|
|
2379
|
+
install stale text. `placement: "append"` preserves native defaults;
|
|
2380
|
+
`"replace"` supplies the complete system context. A conversation ID or project
|
|
2381
|
+
path alone is not a content revision.
|
|
2382
|
+
|
|
2383
|
+
`createConversationHookBridge({ resolveConversation, readInstructions })` connects
|
|
2384
|
+
an existing native hook to the same ownership boundary. An absent binding returns
|
|
2385
|
+
`{ kind: "unclaimed" }`; `{ delivery: "native" }` returns
|
|
2386
|
+
`{ kind: "delivery", text: "" }`, yielding to the engine's native system field.
|
|
2387
|
+
`{ delivery: "hook" }` reads the complete current instructions on every invocation
|
|
2388
|
+
and returns `{ kind: "delivery", text }`. The host must propagate lookup and
|
|
2389
|
+
instruction failures. `readInstructions` is required only for hook delivery.
|
|
2390
|
+
Independent native IDs sharing a directory do not share
|
|
2391
|
+
ownership. Keep bindings through browser closure and resolve native ancestry
|
|
2392
|
+
through a trusted engine API before inheriting a parent's binding. The bridge
|
|
2393
|
+
owns no durable registry and never imports a project compiler.
|
|
2394
|
+
|
|
2395
|
+
`createConversationSystemPrompt()` remains the low-level installation primitive
|
|
2396
|
+
for adapters and custom transports. `ensure({ systemPrompt, contextIdentity,
|
|
2397
|
+
install, retained })` acknowledges only successful installation, serializes updates
|
|
2398
|
+
and rejects invalidation during installation. `isCurrent()` checks the acknowledged
|
|
2399
|
+
binding; it is not a provider health check. `retained: false` supports protocols
|
|
2400
|
+
requiring a system field on every request without adding history entries.
|
|
2401
|
+
|
|
2402
|
+
Instruction updates grant no permissions. Keep discovery bounded, authorize every
|
|
2403
|
+
tool execution against current application state, and preserve ordinary message
|
|
2404
|
+
text. These adapters do not own credentials, application tools, durable storage,
|
|
2405
|
+
process admission or UI. Native integration changes belong here; applications
|
|
2406
|
+
continue to own their content and resource facilities. Model caching, token
|
|
2407
|
+
accounting and the engine's own compaction remain native behavior.
|
|
2408
|
+
|
|
2409
|
+
API-model apps can use `createAiConnectionClient` with an authorized AI integration resolver (see [Assistant](./assistant.md)), or `createAiClient` for existing environment-based configurations, and the tool-catalog helpers
|
|
2410
|
+
from `@jskit-ai/assistant-core/server`, or the complete assistant runtime.
|
|
2411
|
+
Ordinary native conversations use `createConversationRuntime` and the supplied
|
|
2412
|
+
server/client integration described above. The native facilities below support
|
|
2413
|
+
hosts that already own managed execution, credential homes, authorized native
|
|
2414
|
+
history or archive preservation. Each host supplies those existing facilities
|
|
2415
|
+
while JSKIT retains the native protocol and lifecycle implementation. The common
|
|
2416
|
+
runtime supplies its own defaults, so ordinary applications need no engine-specific
|
|
2417
|
+
assembly. Native server entry points expose only the operations required by
|
|
2418
|
+
these hosts; implementation helpers remain private. Isolated host tests obtain
|
|
2419
|
+
the original Codex client, history-adapter, event and catalogue probes from
|
|
2420
|
+
`/testing/native-codex`, or the original OpenCode client from
|
|
2421
|
+
`/testing/native-opencode`. Tests own their executable or endpoint,
|
|
2422
|
+
authorization and cleanup; these entries do not supply another conversation
|
|
2423
|
+
runtime.
|
|
2424
|
+
|
|
2425
|
+
Claude's JSON control client is internal to the shared native implementation.
|
|
2426
|
+
Use the common runtime for conversations and the documented process host facility
|
|
2427
|
+
when supplying execution. Native admission, ordered events, bounded frames and
|
|
2428
|
+
control deadlines remain owned by JSKIT; applications do not assemble that
|
|
2429
|
+
protocol themselves.
|
|
2430
|
+
The ordinary Claude driver uses the same retained-entry owner for process launch,
|
|
2431
|
+
Send, steering, completion and verified Stop. Its binding storage and application
|
|
2432
|
+
tools retain their admission and cleanup order. Confirmed disposal retires only
|
|
2433
|
+
that entry; unconfirmed cleanup remains available for retry before reopening.
|
|
2434
|
+
The retained owner also handles process readiness, scoped Start, saved-execution
|
|
2435
|
+
recovery and verified native-history deletion. A managed host retains account
|
|
2436
|
+
and profile authorization and removes its saved receipt at the original point
|
|
2437
|
+
after native deletion succeeds.
|
|
2438
|
+
The owner classifies native goal and usage updates after raw observer forwarding,
|
|
2439
|
+
prepares native history/resume and terminal arguments, and stops selected entries
|
|
2440
|
+
from its existing live inventory. A managed host supplies authorized terminal
|
|
2441
|
+
configuration and performs its own PTY launch; account, storage and publication
|
|
2442
|
+
effects retain their original awaited positions.
|
|
2443
|
+
Managed Main and scoped conversations acquire this same owner through the common
|
|
2444
|
+
Claude driver; they do not supply native Send/read/wait/Stop operation dictionaries.
|
|
2445
|
+
The existing authorized acquisition and broad application cleanup facilities
|
|
2446
|
+
remain explicit. One shared command lifetime follows the actual admitted
|
|
2447
|
+
completion, preserving duplicate delivery, steering, cleanup retry and tool drains.
|
|
2448
|
+
|
|
2449
|
+
`/server/claude-turn` supplies the retained owner used by the common Claude
|
|
2450
|
+
driver and managed native bindings. Its internal turn receiver owns native
|
|
2451
|
+
acknowledgement, stream blocks, nested-event exclusion, background-task
|
|
2452
|
+
completion and the interruption barrier for steering. Normalized owner events
|
|
2453
|
+
let the host persist admissions and publish application state; receiver
|
|
2454
|
+
completion alone does not prove process exit. `claudeNativeMessageId()`
|
|
2455
|
+
retains the same stable native message IDs used by the Vibe64 adapter.
|
|
2456
|
+
The turn owner preserves the production receiver's block lifecycle: deltas are
|
|
2457
|
+
temporary, block-stop removes them, and completed snapshots use their native
|
|
2458
|
+
UUIDs. It does not group distinct history snapshots into a synthetic reply.
|
|
2459
|
+
The retained owner's `createAccountQueries({ accountIdentity, createProcess })`
|
|
2460
|
+
composes the existing native model/usage query owner with its own process inventory
|
|
2461
|
+
and closing state. A managed host supplies its authorized identity reader and
|
|
2462
|
+
account-process factory. The original query caches, pending-query sharing and
|
|
2463
|
+
retryable cleanup remain unchanged; account queries share already-owned processes
|
|
2464
|
+
and the same conversation shutdown boundary. Matching owned processes
|
|
2465
|
+
are reused; temporary query processes must prove cleanup. Failed stops remain
|
|
2466
|
+
owned and are retried before another native query. `invalidate()` drains pending
|
|
2467
|
+
queries, clears both caches and retries retained cleanup while new work is
|
|
2468
|
+
blocked. For managed bindings, the existing retained conversation owner also
|
|
2469
|
+
owns the original project and account cleanup sequence: it groups retained
|
|
2470
|
+
contexts, drains queries before full account cleanup and retains the synchronous
|
|
2471
|
+
closing fence. The host supplies current storage facts, authorized context,
|
|
2472
|
+
terminal cleanup and product error conversion. These trusted server lifecycle
|
|
2473
|
+
operations use the configured common runtime and are not browser actions.
|
|
2474
|
+
The account owner's usage normalization retains only native windows whose values and reset times
|
|
2475
|
+
are valid; it does not invent refreshed allowance. Product model offerings and
|
|
2476
|
+
account visibility remain application configuration. The same subpath supplies
|
|
2477
|
+
`claudeCatalogueModels(initialization)` for the original native model and effort
|
|
2478
|
+
projection, retaining native IDs, display-name fallbacks and effort labels.
|
|
2479
|
+
It performs no query and does not change the account owner's cached result;
|
|
2480
|
+
hosts still choose their offerings, defaults and visibility.
|
|
2481
|
+
|
|
2482
|
+
The configured Claude owner also acquires retained entries itself. Application
|
|
2483
|
+
preparation supplies authorized context, original open options and fixed product
|
|
2484
|
+
postconditions; native open and saved-entry restoration stay in the same owner.
|
|
2485
|
+
Account binding, verified Stop and identity persistence run between the original
|
|
2486
|
+
application configuration guards and settings preparation. The owner applies its
|
|
2487
|
+
native closing checks after the application's replacement and PTY guards; an
|
|
2488
|
+
optional application session-closing fact remains lazy. Passive retained-turn
|
|
2489
|
+
projection reads the same entry and saved receipt without opening a native session.
|
|
2490
|
+
Credential selection, storage and actual terminal access remain host facilities.
|
|
2491
|
+
The same owner reads its closing state and retained turn snapshot; application
|
|
2492
|
+
preparation supplies the current authorized session-closing fact, saved receipt
|
|
2493
|
+
and PTY state at the original read points. Configured history retirement applies
|
|
2494
|
+
its native busy check before the existing history-preservation operation, while
|
|
2495
|
+
the application retains saved execution and terminal writer facts.
|
|
2496
|
+
|
|
2497
|
+
The retained owner handles the extracted native goal commands and status
|
|
2498
|
+
interpretation using its history, activity, interrupt and ordinary Send operations. Pause interrupts the
|
|
2499
|
+
work and retains the goal; Cancel interrupts and sends `/goal clear`. Revision
|
|
2500
|
+
checks use the native conversation, creation time and objective. This native
|
|
2501
|
+
facility is shared by managed native bindings and the common runtime. The runtime
|
|
2502
|
+
supplies its existing serialized stop and Send operations, so clearing a goal
|
|
2503
|
+
cannot bypass admission, authorization, delivery inspection or cleanup proof.
|
|
2504
|
+
|
|
2505
|
+
`/server/claude-history` supplies `readClaudeHistory`; native identity validation
|
|
2506
|
+
and message-block mapping stay inside the owner. Pass an authorized
|
|
2507
|
+
`configRoot`, `workdir` and native `conversationId` to the reader; these
|
|
2508
|
+
operations do not authorize an account or conversation.
|
|
2509
|
+
Reads preserve the selected rewind branch and tolerate only an unfinished final
|
|
2510
|
+
line in a growing transcript. `listClaudeConversationStorage` enumerates the
|
|
2511
|
+
specified binding's native conversations. Export and deletion policy remain with
|
|
2512
|
+
the host application. After confirming its temporary conversation has stopped,
|
|
2513
|
+
the retained owner removes that conversation's active native transcript.
|
|
2514
|
+
The host removes its ownership receipt only after the awaited operation succeeds.
|
|
2515
|
+
The owner's internal deletion retains the original missing-file behavior and
|
|
2516
|
+
propagates lookup and removal failures; native path lookup stays private. It does
|
|
2517
|
+
not perform archival preservation or remove other native history files.
|
|
2518
|
+
`retireClaudeConversationHistory({ configRoot, binding, beforeDelete, requireIdle,
|
|
2519
|
+
signal })` provides the extracted native inventory, branch export and safe removal.
|
|
2520
|
+
The host supplies authorization, preservation and writer exclusion; `requireIdle`
|
|
2521
|
+
must reject any active writer. It works with archived source directories and
|
|
2522
|
+
leaves shared settings and unrelated native conversations intact.
|
|
2523
|
+
|
|
2524
|
+
Hosts with an existing execution gateway or credential-home policy use
|
|
2525
|
+
`/server/claude-process` for
|
|
2526
|
+
`createClaudeCodeProcess`, `claudeCodeArguments`, `claudeFlagSettings` and
|
|
2527
|
+
`claudeModelConfiguration`. `claudeFlagSettings({ effort, providerEnv, commandHook })`
|
|
2528
|
+
assembles the extracted native settings. A managed host supplies an authorized
|
|
2529
|
+
`commandHook: { command, timeout }`; JSKIT formats the native command-tool hook.
|
|
2530
|
+
Advanced hosts can still supply explicit `hooks` when no `commandHook` is selected.
|
|
2531
|
+
Install these settings before selecting the model through the existing
|
|
2532
|
+
native turn owner's instruction lifecycle.
|
|
2533
|
+
`verifyClaudeProviderKey(provider, apiKey, fetchImpl)` on the same subpath
|
|
2534
|
+
performs the original single Messages compatibility request for an authorized
|
|
2535
|
+
external provider's `claudeBaseUrl` and first model ID. It uses the supplied
|
|
2536
|
+
fetch operation and preserves the 30-second deadline, redirect rejection,
|
|
2537
|
+
64-token output bound, failure-body cancellation and exact Messages validation.
|
|
2538
|
+
It returns no upstream body or credential and does not save keys. The host keeps
|
|
2539
|
+
selected/optional engine-check order, account authorization and key replacement
|
|
2540
|
+
policy. Both compatibility probes retain their sanitized connection errors;
|
|
2541
|
+
neither operation is a conversation or an account store.
|
|
2542
|
+
`readClaudeCodeAuthStatus` uses a supplied capture runner for native account
|
|
2543
|
+
inspection. Hosts select the executable and credential home and retain their
|
|
2544
|
+
execution policy; the reader returns only public account fields. The ordinary
|
|
2545
|
+
runtime supplies this integration automatically.
|
|
2546
|
+
The retained owner stops its native process or recovers its durable execution
|
|
2547
|
+
identity through the supplied host. Its internal stop operation preserves the
|
|
2548
|
+
original stop-proof contract and throws `claude_stop_unconfirmed` when cleanup
|
|
2549
|
+
cannot be confirmed. Process and execution identity remain owned until cleanup
|
|
2550
|
+
succeeds; application persistence and run-state publication happen afterwards.
|
|
2551
|
+
`bindClaudeConversationAccount` applies the same native-account guard to an
|
|
2552
|
+
existing binding, with the owner's stop and persistence operations. Existing
|
|
2553
|
+
managed integrations retain their binding storage and credential resolution.
|
|
2554
|
+
The configuration helper accepts `{ providerId, model }` and the authorized
|
|
2555
|
+
connection's `{ apiKey, baseUrl }`, returning native model flags and environment.
|
|
2556
|
+
Applications choose permitted connections; JSKIT owns the provider rules.
|
|
2557
|
+
The retained turn owner's existing `prepareTerminal` result also carries the
|
|
2558
|
+
native updater-disable setting in its `env`, after the authorized terminal
|
|
2559
|
+
environment. Hosts keep managed PTY launch, credential policy and their earlier
|
|
2560
|
+
environment layers; they do not reconstruct that native setting themselves.
|
|
2561
|
+
Its same conversation owner performs `stopForTerminal`: native active-turn and
|
|
2562
|
+
closing checks, the existing authorized account binding, then verified Stop.
|
|
2563
|
+
The host supplies its application closing fact and retains subsequent Git,
|
|
2564
|
+
environment and managed PTY preparation.
|
|
2565
|
+
Startup uses an execution
|
|
2566
|
+
facility with `start({ command, args, cwd, env, stream })` and `stop(id)`;
|
|
2567
|
+
`start` returns `{ id, stdin, stdout }` for duplex protocols, and a successful
|
|
2568
|
+
stop must return `{ scopeEmpty: true }`. Engine flags, environment normalization
|
|
2569
|
+
and the JSON initialization handshake stay inside JSKIT.
|
|
2570
|
+
|
|
2571
|
+
Without a replacement execution facility, Claude startup uses the supplied local
|
|
2572
|
+
POSIX process-group implementation. This is a trusted single-user host, not a
|
|
2573
|
+
sandbox or resource limiter. It does not grant a permission bypass by default.
|
|
2574
|
+
The native executable and its account login must already exist. Call `stop()`
|
|
2575
|
+
and await its proof on shutdown; closing the protocol stream alone does not
|
|
2576
|
+
prove that native work stopped. Failed cleanup remains an error or an unconfirmed
|
|
2577
|
+
proof, and the owning application must retain its stop handle. Managed applications
|
|
2578
|
+
such as Vibe64 replace execution to enforce identity, resources and scope cleanup.
|
|
2579
|
+
|
|
2580
|
+
For native history retirement, `nativeConversationStoragePolicy(engineId)` from
|
|
2581
|
+
`/server/conversation` describes the engine's writer requirements. Codex's native
|
|
2582
|
+
deletion transaction owns writer locking. Claude and OpenCode require host writer
|
|
2583
|
+
exclusion; their policy supplies `matchesProcess({ executable, commandLine })`,
|
|
2584
|
+
where `commandLine` contains NUL-separated arguments. OpenCode permits a managed
|
|
2585
|
+
server exception only after the host independently verifies that server's identity.
|
|
2586
|
+
The host still authorizes retirement, inventories processes, verifies ownership
|
|
2587
|
+
and holds its exclusion lock until the operation finishes. Matching a command
|
|
2588
|
+
name alone never establishes a managed identity.
|
|
2589
|
+
`retireNativeConversation` from `/server/native-history` owns the shared retirement
|
|
2590
|
+
sequence used by all three native integrations. Its existing `beforeDelete`
|
|
2591
|
+
contract requires `{ preserved: true, exclusive: true }` after exporting every
|
|
2592
|
+
inspected conversation. It rechecks exact export revisions and inventory before
|
|
2593
|
+
removal, then verifies absence. A failed proof, changed history or unconfirmed
|
|
2594
|
+
deletion remains an error; application retention and archive publication stay
|
|
2595
|
+
outside this operation.
|
|
2596
|
+
|
|
2597
|
+
`retireCodexConversationHistory(provider, binding, { toolHomeSource, beforeDelete,
|
|
2598
|
+
signal, errorPrefix })` from `/server/codex-provider` composes that sequence for
|
|
2599
|
+
an already authorized Codex provider. It checks the complete idle family, exact
|
|
2600
|
+
saved directory, paginated history and safe native rollout files before exporting
|
|
2601
|
+
and deleting. The host supplies its authorized native home and preservation
|
|
2602
|
+
callback; it still owns provider acquisition, archive publication and retirement
|
|
2603
|
+
policy.
|
|
514
2604
|
|
|
515
2605
|
### Live native-provider text
|
|
516
2606
|
|
|
@@ -520,14 +2610,16 @@ call `update(scope, { turnId, messageId, role, delta })` for incremental text or
|
|
|
520
2610
|
`update(scope, { turnId, messageId, text })` for a provider snapshot. Deltas retain
|
|
521
2611
|
all whitespace; snapshots replace text. A changed update returns
|
|
522
2612
|
`{ revision, messages }`; an unchanged snapshot or a completed item returns `null`.
|
|
523
|
-
Messages carry `status: "inProgress"`.
|
|
524
|
-
and
|
|
2613
|
+
Messages carry `status: "inProgress"`. Preserve the provider's message IDs. If
|
|
2614
|
+
the provider proves that different live and saved IDs represent one output,
|
|
2615
|
+
carry that same `outputId` in both representations to retain stable output
|
|
2616
|
+
identity. It does not replace either message ID or an admission receipt.
|
|
525
2617
|
|
|
526
|
-
The Codex
|
|
527
|
-
role) and `assistant_delta` (including raw `delta`)
|
|
528
|
-
its deltas to retain its role.
|
|
529
|
-
|
|
530
|
-
|
|
2618
|
+
The Codex run owner interprets `assistant_started` (including assistant/commentary
|
|
2619
|
+
role) and `assistant_delta` (including raw `delta`), starting each stream item before
|
|
2620
|
+
its deltas to retain its role. OpenCode's shared runtime reads text from its
|
|
2621
|
+
normalized native message rows, preserving whitespace at the partial-text
|
|
2622
|
+
boundary; applications do not parse those rows for live output.
|
|
531
2623
|
|
|
532
2624
|
Send snapshots over the application's existing transport and include `read(scope)`
|
|
533
2625
|
in its authorized history read for reconnects. On the client,
|
|
@@ -536,16 +2628,18 @@ overlays live messages without mutating saved turns. Saved message IDs win over
|
|
|
536
2628
|
matching live messages. Consumers such as final-answer speech and answer actions
|
|
537
2629
|
must wait until the message no longer has `status: "inProgress"`.
|
|
538
2630
|
|
|
539
|
-
After successfully saving an item, call `complete(scope, messageId)`
|
|
540
|
-
the returned snapshot with the saved turn.
|
|
541
|
-
until the next native turn. Call `clear(scope)`
|
|
2631
|
+
After successfully saving an item, call `complete(scope, messageId)` with the
|
|
2632
|
+
live item's message ID and publish the returned snapshot with the saved turn.
|
|
2633
|
+
Completed items reject late chunks until the next native turn. Call `clear(scope)`
|
|
2634
|
+
when work stops or the scope is
|
|
542
2635
|
retired. Applications must reject stale provider events before updating a stream;
|
|
543
2636
|
the buffer does not authorize events or determine which native turn is current.
|
|
544
2637
|
The buffer survives browser reconnects while its server process remains alive.
|
|
545
2638
|
Provider history and application persistence remain responsible for process-restart
|
|
546
2639
|
recovery; partial text is never persisted as a completed answer by this API.
|
|
547
2640
|
|
|
548
|
-
|
|
2641
|
+
For isolated host tests, the original Codex client in `/testing/native-codex`
|
|
2642
|
+
takes `{ endpoint, maxMessageBytes, requestTimeoutMs,
|
|
549
2643
|
WebSocketImpl }`. It connects to a WebSocket or `unix://` endpoint, then
|
|
550
2644
|
`initialize({ clientInfo, capabilities })` performs the native handshake.
|
|
551
2645
|
`request(method, params, { signal })`, `subscribe(callback)`,
|
|
@@ -555,19 +2649,215 @@ Set transport limits appropriate to the host; the default payload limit is
|
|
|
555
2649
|
unbounded. A request abort retires the local request; interrupt a running native
|
|
556
2650
|
turn with `turn/interrupt` when cancellation must stop provider work.
|
|
557
2651
|
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
2652
|
+
Managed Codex hosts use `createCodexAppServerRunOwner` from
|
|
2653
|
+
`/server/codex-turn`. It constructs the native notification queue from the
|
|
2654
|
+
supplied request-context and logging facilities and its own shutdown fence.
|
|
2655
|
+
The same original owner coordinates receipts, observation, goals, steering, interruption,
|
|
2656
|
+
scoped turns and private renewal execution against the supplied application
|
|
2657
|
+
store and publication policy. Detached waiting and run-transition helpers stay
|
|
2658
|
+
inside that owner; hosts do not compose another observer or delivery journal.
|
|
2659
|
+
|
|
2660
|
+
This advanced constructor adapts an existing managed host's retained run store
|
|
2661
|
+
and application effects. Ordinary applications use `createConversationRuntime`
|
|
2662
|
+
with their chosen conversation storage; they do not configure the following
|
|
2663
|
+
run-owner facilities themselves.
|
|
2664
|
+
|
|
2665
|
+
| Existing run-owner input | Host supplies | Shared owner retains |
|
|
2666
|
+
| --- | --- | --- |
|
|
2667
|
+
| `createRuntime`, `createStore`, `namespace`, `normalizeRunState`, `deliveryStateMetadataKey` | Existing store access, identity and persisted-schema adapters; bounded reads stay separate from full runtime hydration | Native run transitions, receipt ordering and recovery |
|
|
2668
|
+
| `providerSessions` | The shared provider `owner`, authorized `context`, `keyFields` and `connectionPolicy` | Managed registrations, provider acquisition, reuse, native identity checks and shared recovery; application closure/current-record fences surround the original native connection checks |
|
|
2669
|
+
| `conversationPreparation.context` and `.scope` | Authorized session or scope, workdir, account/profile policy and settings | Native acquisition and isolation verification |
|
|
2670
|
+
| `conversationPreparation.execution` and `.control` | Validated input/output limits, application admission, settings and result projection | Turn dispatch, stable-account checks, current-turn guards, stop and completion state |
|
|
2671
|
+
| `conversationPreparation.response`, `.expired`, `.failure` | Existing product result/error contracts | Native outcome and cleanup evidence |
|
|
2672
|
+
| `messageMetadata`, `checkpoint`, `publish`, notices | Authenticated authorship, application metadata, checkpoints and publication | When each original effect runs relative to native delivery and completion |
|
|
2673
|
+
| `serverClosingError` | Existing application error message, code and retryable value | The managed lifecycle shutdown assertion and its invocation points; error construction occurs only after the native closing fence |
|
|
2674
|
+
| `captureContext`, `runInContext`, `debugLog`, `debugError` | Request-context isolation and application logging | Notification queue, shutdown fence and recovery coordination |
|
|
2675
|
+
|
|
2676
|
+
Managed observation supplies the provider key and application error presentation;
|
|
2677
|
+
the run owner chooses retirement through its existing provider lifecycle. A
|
|
2678
|
+
presenter can read the current managed record without allocating another map.
|
|
2679
|
+
The ordinary driver's existing native close path remains internal to JSKIT.
|
|
2680
|
+
|
|
2681
|
+
`conversationPreparation.admissionError` reports application admission policy.
|
|
2682
|
+
Supply the already-configured shared isolation owner once as
|
|
2683
|
+
`conversationPreparation.isolation`; the run owner uses that same instance for
|
|
2684
|
+
compatibility checks and scoped create/resume/turn execution. The execution
|
|
2685
|
+
projection does not return another isolation reference. These preparation
|
|
2686
|
+
functions supply policy and application effects, not alternate native algorithms. The optional `helperThreads` ledger is described below and
|
|
2687
|
+
is not required for ordinary conversations or stable-account checks.
|
|
2688
|
+
|
|
2689
|
+
For a bound Codex conversation, the application's existing `prepareInput`
|
|
2690
|
+
projects its final authorized actor into `actorContext`. The driver passes that
|
|
2691
|
+
same value to receipt attribution and turn preparation; it does not interpret
|
|
2692
|
+
application-specific user fields. Derive this value from the authenticated
|
|
2693
|
+
application context, not a browser-supplied identity.
|
|
2694
|
+
|
|
2695
|
+
Its `checkManagedConnection` and `restoreLoadedThread` operations also own
|
|
2696
|
+
native status/control checks, loaded-thread pagination, observation before resume
|
|
2697
|
+
and exact run reconciliation. The existing host supplies current-session
|
|
2698
|
+
admission and closure checks, lazy resume settings, managed registration and
|
|
2699
|
+
ready-state persistence at their original sequence points. The same run owner
|
|
2700
|
+
selects the active provider for Stop and the retained failed-observation owner.
|
|
2701
|
+
Observation recovery acquires its shared provider directly without taking an
|
|
2702
|
+
active-turn shortcut. Its original reconciliation map and prune waiter retain
|
|
2703
|
+
the full loaded-thread attempt and readiness fallback, including application
|
|
2704
|
+
health/error handling and result projection. The host wraps that same native
|
|
2705
|
+
readiness operation with authorization and persistence; it does not dispatch a
|
|
2706
|
+
separate native fallback. The same run owner's private lifecycle facet owns
|
|
2707
|
+
the original wellbeing timers, closing fence, generation, session-closure and
|
|
2708
|
+
reconciliation-task collections. Shutdown retains its cached two-sweep operation:
|
|
2709
|
+
start invalidation before draining existing work, preserve original error
|
|
2710
|
+
priority, then complete the second sweep. The host supplies project inventory,
|
|
2711
|
+
authorized preparation and result policy. Its private reconciliation facet owns
|
|
2712
|
+
the original batch restoration, concurrent Helper inventory, stale-generation
|
|
2713
|
+
pruning and startup unsubscribe/retirement. It uses that same task/generation
|
|
2714
|
+
state. Session and receipt reads, source-write leases and result presentation
|
|
2715
|
+
remain supplied by the host; no second native recovery loop is created.
|
|
2716
|
+
The run owner's `outputProvider` keeps active-provider reuse ahead of lazy host
|
|
2717
|
+
configuration. Its `runtimeForVisibleTerminal` retains the existing authorized
|
|
2718
|
+
provider choice, persisted project hook trust and tracked runtime acquisition;
|
|
2719
|
+
the host still prepares and launches its terminal. These paths intentionally
|
|
2720
|
+
retain their distinct configuration timing.
|
|
2721
|
+
The existing goal owner also selects first-goal readiness before native acquisition;
|
|
2722
|
+
later goal controls retain their authorized pinned command context. The host
|
|
2723
|
+
supplies readiness health/storage policy and the stored-goal/result projection.
|
|
2724
|
+
The provider-selection facet restores Helper ownership before model discovery or
|
|
2725
|
+
account description, retaining supplied-provider reuse, lifecycle gating and the
|
|
2726
|
+
original per-connection catalog cache. Authorized scope/account preparation,
|
|
2727
|
+
deadlines and result policy remain with the host.
|
|
2728
|
+
Legacy detached host operations use that same owner's `acquireDetachedThread`,
|
|
2729
|
+
`detachedThreadId` and `dispatchDetachedTurn`. The ID projection accepts the
|
|
2730
|
+
original native response shapes and requested-thread fallback after either
|
|
2731
|
+
ordinary acquisition or retained Helper preparation. Applications retain
|
|
2732
|
+
account/profile policy, bounds and result policy around those operations. The run owner's optional
|
|
2733
|
+
`helperThreads` facility owns durable native-thread identity, mutation/start and
|
|
2734
|
+
cleanup coordination, provider retirement, queued restart restoration and
|
|
2735
|
+
isolated Helper inventory. Restoration revalidates the same ledger revision and
|
|
2736
|
+
native identity before attachment; failed retirement retains retryable records.
|
|
2737
|
+
A managed host supplies its
|
|
2738
|
+
codec-bound ledger owner and retains its authorized storage root and selection
|
|
2739
|
+
policy; it does not implement another ledger or retirement algorithm.
|
|
2740
|
+
The ledger owner supplies `matchesExecutionProfile(recorded, expected)` using its
|
|
2741
|
+
canonical profile snapshot. A host can retain its existing shorthand-profile
|
|
2742
|
+
policy through that named method. Native control checks ownership,
|
|
2743
|
+
lifecycle, provider, workdir and turn identity before invoking that policy.
|
|
2744
|
+
The host retains admission/frozen-state projection. Native conversation commands
|
|
2745
|
+
call the same Helper owner directly for thread lookup and retirement, without a
|
|
2746
|
+
host inspection callback. A returned retirement promise retains the original
|
|
2747
|
+
admission-release order; native dispatch remains awaited.
|
|
2748
|
+
It also rejects ordinary access to a retained Helper thread and aggregates
|
|
2749
|
+
restoration failures without changing their retryability. The host decides when
|
|
2750
|
+
successful restoration is required and supplies its authorized storage root.
|
|
2751
|
+
Stable-account capture and completion validation also belong to the shared run
|
|
2752
|
+
owner, independently of the optional retained Helper ledger. They preserve the
|
|
2753
|
+
account check after native resume, and the exact current-turn guard before
|
|
2754
|
+
completion changes state. Hosts supply profile/output policy and synchronous
|
|
2755
|
+
application result projection; they do not mutate native completion state.
|
|
2756
|
+
|
|
2757
|
+
Internally the retained run owner composes its original journal, message and
|
|
2758
|
+
conversation commands, delivery, output, reasoning, notification observation,
|
|
2759
|
+
readiness, result settlement, recovery and control operations. Each responsibility
|
|
2760
|
+
retains its original collections; dependent operations receive those same owner
|
|
2761
|
+
instances. The shared prompt-delivery marker remains in the coordinator because
|
|
2762
|
+
both recovery and message admission use it. No second observer, provider,
|
|
2763
|
+
transcript or dispatch implementation is introduced. Existing host imports remain
|
|
2764
|
+
unchanged. Progress length, finalization grace, snapshot-recovery bounds and
|
|
2765
|
+
native outcome names remain private native defaults, rather than host tuning
|
|
2766
|
+
options.
|
|
2767
|
+
`codexContextUsageFromNotification` from the existing `/server/codex-events`
|
|
2768
|
+
host facility interprets native token-usage updates and validates their counts
|
|
2769
|
+
and context window. Applications retain current-conversation checks, storage
|
|
2770
|
+
and renewal presentation around the returned usage facts.
|
|
2771
|
+
|
|
2772
|
+
`createCodexHelperThreadLedgerOwner({ executionProfile, snapshotExecutionProfile,
|
|
2773
|
+
errorPrefix })` from `/server/codex-turn` binds the host's strict persisted-profile
|
|
2774
|
+
codec. The optional snapshot codec defaults to that codec; use the existing
|
|
2775
|
+
normalizer when accepted inputs need normalization before strict persistence.
|
|
2776
|
+
The returned `createCodexHelperThreadLedger({ projectRuntimeRoot })` retains the
|
|
2777
|
+
original versioned record shape, immutable native identity, revision comparisons,
|
|
2778
|
+
atomic writes, file locks and retryable cleanup. Moving this owner does not
|
|
2779
|
+
migrate or reinterpret existing records. It uses the shared kernel's
|
|
2780
|
+
`tryAcquireExclusiveFileLock` from `/server/support`; `errorCode` retains a host's
|
|
2781
|
+
existing failure contract where required. These are advanced host facilities;
|
|
2782
|
+
ordinary persistent conversations use the supplied runtime and storage.
|
|
2783
|
+
For that advanced integration, `/server/codex-turn` also exposes
|
|
2784
|
+
`sendPreparedCodexAppServerHelperTurn(provider, prepared, helperIsolation)`.
|
|
2785
|
+
The host first validates its policy and supplies `prepared` with `threadId`,
|
|
2786
|
+
`input`, `executionProfile` and `turnSettings(cwd)`. The shared operation obtains
|
|
2787
|
+
the existing isolation facility's execution context, sends the native turn and
|
|
2788
|
+
returns the frozen `{ executionProfile, input, turn }` result. Scoped runtime
|
|
2789
|
+
work uses the same native statement. Ordinary applications use the conversation
|
|
2790
|
+
runtime and do not need this host facility.
|
|
2791
|
+
The same run owner recovers an abandoned starting claim before native turn
|
|
2792
|
+
selection: it retains the original 15-second grace, requires both native IDs to
|
|
2793
|
+
be absent and checks that no prompt delivery is active. Managed hosts may supply
|
|
2794
|
+
`admissionTaskFinished(session, run)` for their existing task-completion policy;
|
|
2795
|
+
they do not supply the claim classifier or its failed-run persistence sequence.
|
|
2796
|
+
`/server/codex-events` retains only the event identities, thread-history
|
|
2797
|
+
projections and context-refresh facts used by existing host policy. The native
|
|
2798
|
+
renewal owner resumes the exact predecessor or creates/adopts the successor,
|
|
2799
|
+
then inspects its snapshot for the original operation, missing turns, unreadable
|
|
2800
|
+
predecessor history or unrelated successor history. The host supplies lazy
|
|
2801
|
+
settings and authorized identity/receipt storage, retaining renewal approval,
|
|
2802
|
+
metadata names, user-facing errors and retry policy. Hidden successor preparation
|
|
2803
|
+
does not open or publish an ordinary Main conversation. The snapshot classifier
|
|
2804
|
+
is now internal to that native owner.
|
|
2805
|
+
|
|
2806
|
+
`/server/codex-provider` supplies the original provider, shared acquisition and
|
|
2807
|
+
thread-selection operations. Its existing provider owner also selects cached or
|
|
2808
|
+
uncached verified shutdown. `stopProvider` uses the supplied host `stopRuntime`
|
|
2809
|
+
facility when no retained provider exists; `stopThreadBeforeRelease` preserves
|
|
2810
|
+
the original shared-peer check before allowing process shutdown as the fallback
|
|
2811
|
+
for an unconfirmed thread stop. The host retains its application cleanup and
|
|
2812
|
+
must still obtain the existing exit proof. `/server/codex-process` supplies the shared runtime
|
|
2813
|
+
and process-leader operations for an existing execution host.
|
|
2814
|
+
`/server/codex-configuration` supplies model/account configuration and the
|
|
2815
|
+
verified Helper isolation facility. Its `codexCatalogRows` reads the original
|
|
2816
|
+
array or native `data` response without copying its rows; an optional lazy error
|
|
2817
|
+
factory preserves a managed host's existing failure type. `codexCatalogModels`
|
|
2818
|
+
and `codexCatalogReasoningEfforts` retain native IDs, display-name fallbacks and
|
|
2819
|
+
reasoning-option order. Hosts choose offerings, defaults, connection visibility
|
|
2820
|
+
and product labels; they may use the original row fields `model`, `hidden`,
|
|
2821
|
+
`isDefault` and `defaultReasoningEffort` for that policy.
|
|
2822
|
+
`codexProviderFileConfiguration(provider, key, codexHome)` formats the original
|
|
2823
|
+
private `config.toml` text from the host's authorized provider record and key.
|
|
2824
|
+
The record supplies `id`, `label`, `baseUrl`, `webSearch` and the selected first
|
|
2825
|
+
model's `id` and `defaultThinking`; the native catalogue path remains
|
|
2826
|
+
`<codexHome>/models.json`. The formatter does not read or write credentials.
|
|
2827
|
+
`verifyCodexProviderKey(provider, apiKey, fetchImpl = fetch)` performs the original
|
|
2828
|
+
single Responses compatibility request using that first model. It retains the
|
|
2829
|
+
30-second deadline, redirect rejection, 64-token output bound, failure-body
|
|
2830
|
+
cancellation and exact successful-response validation. It returns no provider
|
|
2831
|
+
response or credential. Hosts authorize the provider record and key before
|
|
2832
|
+
verification and preserve their existing connection until verification and
|
|
2833
|
+
required runtime invalidation succeed; they own private file writes and account
|
|
2834
|
+
publication. The formatter's returned text contains the key and must stay in
|
|
2835
|
+
private server storage.
|
|
2836
|
+
`codexConfiguredModelCatalog` formats a host-selected model and effort without
|
|
2837
|
+
querying a provider. The host decides whether to include that configured row. Its ordinary thread/turn formatters and
|
|
2838
|
+
`codexInteractiveArguments` serialize native request and CLI shapes from the
|
|
2839
|
+
host's authorized settings; they do not choose sandbox, approval or managed
|
|
2840
|
+
execution policy. A host that already provides the execution sandbox can select
|
|
2841
|
+
`externalSandbox: true` in `codexAppServerTurnSettings`; the codec supplies the
|
|
2842
|
+
native external-sandbox/network fields. The default grants no such policy.
|
|
2843
|
+
The isolation owner's `threadStartSettings(settings, threadSource)` only adds
|
|
2844
|
+
native start metadata, so retained conversations can reuse it with their own
|
|
2845
|
+
source label without applying Helper policy.
|
|
2846
|
+
`normalizeCodexThreadId` retains the native UUID grammar used
|
|
2847
|
+
for interactive resume and saved native identities. A host that has selected a
|
|
2848
|
+
restricted, read-only scope can apply `codexAppServerReadOnlyThreadSettings`
|
|
2849
|
+
to its prepared thread settings: it overlays the original empty native facility
|
|
2850
|
+
lists and read-only sandbox. The host retains selection and preparation of the
|
|
2851
|
+
tool-free configuration; this formatter does not replace isolation verification.
|
|
2852
|
+
Ordinary conversations use the common runtime; Helper isolation is not a
|
|
2853
|
+
substitute policy for a retained conversation.
|
|
2854
|
+
The same isolation owner formats authorized Helper start, resume and turn
|
|
2855
|
+
settings after profile validation. It preserves the verified configuration object
|
|
2856
|
+
and the distinct start/resume field sets; hosts retain their permission and
|
|
2857
|
+
output-bound policy.
|
|
2858
|
+
|
|
2859
|
+
For isolated host tests, `createOpenCodeServerClient` from
|
|
2860
|
+
`/testing/native-opencode` accepts a loopback HTTP `baseUrl`, `directory`, credentials,
|
|
571
2861
|
and optional `fetchImpl`. It exposes native sessions, prompt, interruption,
|
|
572
2862
|
messages, status, events, model/agent catalogues and account operations. Response
|
|
573
2863
|
and event reads are bounded. `allowAttachmentDirectories` defaults to false;
|
|
@@ -575,11 +2865,253 @@ enabling it grants the native conversation access to parent directories of
|
|
|
575
2865
|
supplied attachments. Only the host can make that permission decision after
|
|
576
2866
|
resolving and authorizing each file descriptor.
|
|
577
2867
|
|
|
2868
|
+
Its `readConversationStorage`, `readConversationStoragePage`,
|
|
2869
|
+
`listConversationChildren` and `listConversationsForDirectory` operations read
|
|
2870
|
+
native persisted records without initializing a live project directory. Inventories
|
|
2871
|
+
reject incomplete results; page reads preserve the native continuation cursor and
|
|
2872
|
+
support cancellation. `allowConversationAttachments` updates only that native
|
|
2873
|
+
conversation's retained file permissions. These operations do not authorize the
|
|
2874
|
+
caller or implement retention policy.
|
|
2875
|
+
|
|
2876
|
+
`/server/opencode-client` supplies
|
|
2877
|
+
`retireOpenCodeConversationHistory(storageClient, controlClient, binding,
|
|
2878
|
+
{ beforeDelete, signal })`. A managed host supplies its authorized shared storage
|
|
2879
|
+
client, control client scoped to the exact saved directory, saved binding and
|
|
2880
|
+
durable preservation callback. The operation owns native family inspection,
|
|
2881
|
+
bounded export and deletion through the existing preserve/recheck/remove/verify
|
|
2882
|
+
sequence. Its private inspection rejects busy or foreign-directory children;
|
|
2883
|
+
export retains native records and text projections, checks message and cursor
|
|
2884
|
+
identity, and bounds pages, duration and output. Incomplete exports never
|
|
2885
|
+
authorize deletion. Process acquisition, access policy and archive persistence
|
|
2886
|
+
remain with the host.
|
|
2887
|
+
|
|
2888
|
+
`/server/opencode-turn` also owns `runOpenCodeConversationTurn` for an approved,
|
|
2889
|
+
bounded native request. It first checks the exact input's history, dispatches
|
|
2890
|
+
only when that result is absent, and rechecks admission if waiting fails. Hosts
|
|
2891
|
+
supply the authorized native input/model/agent, deadline and optional diagnostic
|
|
2892
|
+
or error-construction hooks. It does not choose renewal policy or create another
|
|
2893
|
+
conversation store.
|
|
2894
|
+
|
|
2895
|
+
An existing renewal host may pass a fourth argument containing
|
|
2896
|
+
`{ expectedThreadId, forbiddenThreadId, requireFreshHistory, completeResult, close }`.
|
|
2897
|
+
The owner checks native identity and optional bounded freshness before reading a
|
|
2898
|
+
lazily supplied `input.prompt`, then keeps its separate receipt-history read.
|
|
2899
|
+
For seeding, `completeResult(result)` awaits the host's acknowledgement validation
|
|
2900
|
+
and durable metadata; only success invokes its original `close()` facility.
|
|
2901
|
+
Native failure or failed result projection does not add automatic cleanup.
|
|
2902
|
+
The result includes the original freshness fact and exit proof. Prompt grammar,
|
|
2903
|
+
authorization and private successor storage stay with the host. Ordinary chats
|
|
2904
|
+
use the common runtime's existing operations.
|
|
2905
|
+
|
|
2906
|
+
`/server/opencode-turn` exposes `openCodeRowsForInput` for the existing host's
|
|
2907
|
+
presentation of authorized native history. Its `openCodeMessageError(message)`
|
|
2908
|
+
extracts the original native error text; a host can apply its application-specific
|
|
2909
|
+
diagnostics afterward without decoding another native error envelope.
|
|
2910
|
+
Managed turn control and completion stay in `createOpenCodeSharedRuntime`;
|
|
2911
|
+
the host supplies its existing `readError(message)` diagnostic projection to that
|
|
2912
|
+
owner. Native history interpretation and observation loss remain internal.
|
|
2913
|
+
The same owner answers temporary activity from its original conversation map,
|
|
2914
|
+
excluding cosmetic reasoning-summary work. The host validates its session ID and
|
|
2915
|
+
keeps application preparation gates; it does not scan native entries itself.
|
|
2916
|
+
The shared runtime's internal observer retains production readiness, failure
|
|
2917
|
+
propagation and awaited closure; waiting for readiness preserves native event
|
|
2918
|
+
order. Its internal admission inspection uses the bounded native user-message lookup: a missing or unreadable receipt is
|
|
2919
|
+
unknown, never permission to resend. The shared runtime owns admitted-turn
|
|
2920
|
+
steering and the original one-attempt missing-answer recovery. Its existing
|
|
2921
|
+
monitor projection preserves completed reasoning before recovery advances the
|
|
2922
|
+
native input boundary. Applications retain their authorization and durable
|
|
2923
|
+
delivery receipts. The shared runtime's internal dispatch preserves event readiness before
|
|
2924
|
+
the application's admission hook, then records the attempted native send and
|
|
2925
|
+
submits the original resumed prompt. Delivery defaults to queue; a host steering
|
|
2926
|
+
through its existing connected observer supplies `input.delivery: "steer"` and
|
|
2927
|
+
omits the new-observer wait. The owner reads `options.signal` after readiness, so
|
|
2928
|
+
a retained host can resolve its current turn's cancellation signal at that same
|
|
2929
|
+
boundary. The host keeps its durable acceptance policy: an authored row and its
|
|
2930
|
+
publication may precede advancing the observed input ID. Main, managed temporary
|
|
2931
|
+
and standalone consumers use this same dispatch operation.
|
|
2932
|
+
For a retained target that can refresh during readiness or admission, the
|
|
2933
|
+
existing options object can supply `client` and `conversationId` getters. The
|
|
2934
|
+
owner reads them, then the prompt envelope, after the admission hook completes.
|
|
2935
|
+
The shared runtime retains native completion, interruption and verified failure
|
|
2936
|
+
cleanup. Its projection and after-close notifications preserve application output
|
|
2937
|
+
validation and checkpoint ordering without moving product policy into the native
|
|
2938
|
+
layer. Unconfirmed cleanup retains the active native record for retry. The common
|
|
2939
|
+
driver uses that same sender, native admission gate, monitor and Stop owner. Its
|
|
2940
|
+
connection, local execution, tool, attachment and structured-output facilities
|
|
2941
|
+
adapt to that owner. Tool effects await the exact authored delivery receipt;
|
|
2942
|
+
rejected steering preserves the previous active request. `/server/opencode-process` exports
|
|
2943
|
+
`createOpenCodeServerProcess`
|
|
2944
|
+
for advanced managed integration. The common driver's internal configuration
|
|
2945
|
+
uses that same server startup, authentication, version, observation and cleanup
|
|
2946
|
+
implementation. `openCodeProcessEnvironment(baseEnv, { cacheRoot, dbPath,
|
|
2947
|
+
inlineConfig, outputTokenMax, password })` assembles native flags, cache,
|
|
2948
|
+
loopback proxy bypass and supplied inline configuration. A managed host supplies
|
|
2949
|
+
its isolated environment, credential roots, approved plugin configuration and
|
|
2950
|
+
application command hooks around those native values. That boundary preserves
|
|
2951
|
+
the host's existing permission and process policies while using the same native
|
|
2952
|
+
startup, cleanup and acquisition owner.
|
|
2953
|
+
The same owner selects retained scoped targets, projects persistent native
|
|
2954
|
+
status and admission, reconciles restored turns, and reads final native output
|
|
2955
|
+
before application validation. A retained host binding supplies its existing
|
|
2956
|
+
owner with authorized configuration and application effects; the common driver
|
|
2957
|
+
invokes that owner directly for Main and scoped conversations. It does not route
|
|
2958
|
+
native operations back through an application controller. Process acquisition
|
|
2959
|
+
and native event observation stay with the shared owner, using the original
|
|
2960
|
+
managed-execution, storage and publication facilities. Managed hosts retain their
|
|
2961
|
+
existing storage transactions, saved-run fences, authorized profile limits and
|
|
2962
|
+
publication. This internal binding adaptation is not a second application
|
|
2963
|
+
integration API; ordinary applications use the common conversation runtime.
|
|
2964
|
+
The lower-level operation takes an owned `execution` facility, private environment,
|
|
2965
|
+
absolute `workdir` and `privateRoot`, and authorized `{ providerId, apiKey }`
|
|
2966
|
+
connections. It removes the private root only after confirmed host cleanup.
|
|
2967
|
+
Readiness keeps the production 30-second deadline, bounded health probes, strict
|
|
2968
|
+
`healthy: true` response and exact trimmed version check. Startup timeout errors
|
|
2969
|
+
retain the last health failure as their cause. Authentication follows readiness
|
|
2970
|
+
once per provider; it uses the caller's cancellation signal, not the readiness
|
|
2971
|
+
deadline. A successful stop retains its complete proof for repeated calls; an
|
|
2972
|
+
unconfirmed or rejected stop remains retryable. The execution facility receives
|
|
2973
|
+
the native stop reason and three-second termination grace period.
|
|
2974
|
+
The application still owns its database path and retention. Startup errors retain
|
|
2975
|
+
the execution ID and cleanup proof; optional host `readLogs()` supplies bounded
|
|
2976
|
+
diagnostics. If cleanup is unconfirmed, the error also retains `retryCleanup`,
|
|
2977
|
+
the same native stop operation. The shared owner holds that operation until exit
|
|
2978
|
+
is verified and blocks replacement startup while cleanup remains unconfirmed.
|
|
2979
|
+
Its `cleanupPending` flag lets a managed host retry cleanup during project close
|
|
2980
|
+
or shutdown even when acquisition never returned a server. An unconfirmed stop
|
|
2981
|
+
leaves state intact for host recovery. Ordinary
|
|
2982
|
+
applications use `createConversationRuntime()` rather than assembling these
|
|
2983
|
+
native setup options.
|
|
2984
|
+
|
|
2985
|
+
For an advanced host's connection-setup flow, the same subpath provides
|
|
2986
|
+
`readOpenCodeCatalog({ privateRoot, workdir, createServerProcess })`. It writes
|
|
2987
|
+
OpenCode's non-secret public catalogue identity, calls the supplied server factory
|
|
2988
|
+
with `{ dbPath, privateRoot, providerConnections: [], workdir }`, reads providers
|
|
2989
|
+
and agents, and awaits confirmed Stop before returning. A cleanup failure takes
|
|
2990
|
+
precedence over a captured read error. The host retains managed execution,
|
|
2991
|
+
catalogue caching and model eligibility. `readOpenCodeZenModelIds({ fetchImpl,
|
|
2992
|
+
timeoutMs })` reads the bounded credential-free public model list without
|
|
2993
|
+
borrowing an account key.
|
|
2994
|
+
|
|
2995
|
+
`verifyOpenCodeApiKey({ apiKey, agentId, modelId, modelProviderId, privateRoot,
|
|
2996
|
+
workdir, prepareCommand })` owns the original isolated auth-file write, one pure
|
|
2997
|
+
native request, result classification and credential cleanup. Its synchronous
|
|
2998
|
+
`prepareCommand({ providerId, modelId, privateRoot, workdir, outputTokenMax })`
|
|
2999
|
+
returns the host's existing command operation. The owner calls that operation
|
|
3000
|
+
once with `{ args, maxBuffer, mode, timeout }`; the host supplies approved
|
|
3001
|
+
execution identity, credential-home policy and environment. Preparation runs
|
|
3002
|
+
after the auth write, outside the command-failure sanitization boundary and
|
|
3003
|
+
inside the credential-cleanup `finally`. This preserves setup errors separately
|
|
3004
|
+
from sanitized invocation failures. The request keeps its 16-token output limit,
|
|
3005
|
+
256-KiB capture bound and 30-second deadline. The host supplies an isolated
|
|
3006
|
+
tool-free agent configuration; `agentId` defaults to the existing common
|
|
3007
|
+
`jskit-assistant` agent name. Hosts may translate the shared `assistant_opencode_*`
|
|
3008
|
+
error categories to their existing public errors without copying native logic.
|
|
3009
|
+
These operations support host account setup; ordinary conversation consumers
|
|
3010
|
+
continue to use the common runtime.
|
|
3011
|
+
|
|
3012
|
+
The same subpath exposes the extracted `createOpenCodeSharedRuntime` for hosts
|
|
3013
|
+
whose managed service is shared by several conversation consumers. It owns server
|
|
3014
|
+
reuse, pending acquisition, verified final release and exact-server recovery.
|
|
3015
|
+
Its retained Main turn and monitor maps also back the original
|
|
3016
|
+
`interruptTurn(key, target, { threadId, writeRun })` operation. The host selects
|
|
3017
|
+
the authorized target, supplies a saved identity only when no target exists, and
|
|
3018
|
+
binds its existing run writer. Ordinary Stop retains the five-second native
|
|
3019
|
+
acknowledgement, retryable failure flags, admission release and current-monitor
|
|
3020
|
+
wait. It does not add an idle query. The existing observation-loss branch keeps
|
|
3021
|
+
its stronger native-stop or exact-process-exit proof. `turnSnapshot` supplies the
|
|
3022
|
+
same result fields to host readers.
|
|
3023
|
+
The bound OpenCode driver uses the same owner's `readSessionState` for canonical
|
|
3024
|
+
and native reads. Application context, saved identity and terminal-state facilities
|
|
3025
|
+
retain their original read order. Passive reads do not prepare or start a native
|
|
3026
|
+
server; ordinary consumers use the common conversation read API.
|
|
3027
|
+
The same native host subpath supplies `openCodeModel(selection, executionProfile)`
|
|
3028
|
+
for the existing selected-model shape. It retains the original model fallback,
|
|
3029
|
+
profile effort override and native `{ id, providerID, variant? }` formatting.
|
|
3030
|
+
Hosts authorize the account and validate their execution profile before calling;
|
|
3031
|
+
the formatter does not select a workload or grant tools. Ordinary applications
|
|
3032
|
+
continue to pass common configuration to `createConversationRuntime`.
|
|
3033
|
+
Before native identity selection or publication, `ensureOpenCodeSession` and
|
|
3034
|
+
the shared scoped create/select operations await the actual directory's startup.
|
|
3035
|
+
The scoped client's `prepareDirectory` uses the authenticated, read-only native
|
|
3036
|
+
`GET /path` operation with the normal 30-second initialization bound. Global
|
|
3037
|
+
health and v2 session reads alone do not initialize the directory used by native
|
|
3038
|
+
prompt and Stop. Preparation performs no inference, preserves the saved native
|
|
3039
|
+
identity on failure, and is retried through the existing acquisition operation.
|
|
3040
|
+
There is no permanent readiness cache or change to Stop's five-second deadline.
|
|
3041
|
+
`inspectSessionReadiness(target)` retains the original health/session read and
|
|
3042
|
+
captured server facts. The host revalidates its session, selection and closure
|
|
3043
|
+
before accepting that result. Inside its existing write admission,
|
|
3044
|
+
`recoverSessionReadiness(target, recovery, prepare)` stops only the captured
|
|
3045
|
+
unhealthy process still owned by this runtime, clears a missing native session
|
|
3046
|
+
only on the same target, and invokes the host's existing preparation. A lazy
|
|
3047
|
+
`recovery.key` preserves a key read after the awaited stop. The separate
|
|
3048
|
+
`recoverObservation(target, threadId, commit)` confirms that exact thread is idle
|
|
3049
|
+
before the host's existing saved-run comparison and commit. Its synchronous
|
|
3050
|
+
`needsObservationRecovery(key, run)` predicate uses the same owned monitor map;
|
|
3051
|
+
an existing host checks it before write admission and again after refreshing its
|
|
3052
|
+
saved run inside that admission. The host's full current-run comparison remains
|
|
3053
|
+
inside its storage transaction. Recovery does not interrupt or start native work.
|
|
3054
|
+
Healthy inspection does not require write admission.
|
|
3055
|
+
The same owner's `beginMonitor(key, target, turn, options)` retains the original
|
|
3056
|
+
Main admission, native completion, compaction phase and failure-cleanup sequence.
|
|
3057
|
+
It uses the host's already-prepared run record and existing observation adapter.
|
|
3058
|
+
The three application effects are `writeRun`, `projectMessages` and
|
|
3059
|
+
`completeResult`: run persistence, transcript projection and final notices.
|
|
3060
|
+
The owner's history projection supplies ordered reasoning facts (`messageId`,
|
|
3061
|
+
`partId`, unmodified `text`, `createdAt` and `complete`) to its reasoning presenter,
|
|
3062
|
+
with separate message-level `complete` and `flush` options. Native part selection
|
|
3063
|
+
and end-marker decoding stay inside JSKIT. The host keeps saved-message identity,
|
|
3064
|
+
headline policy, first-write timestamp formatting and publication.
|
|
3065
|
+
Final-response inputs are read after admission and the Active write. Observer
|
|
3066
|
+
closure precedes notices and the final write. The existing `onRetired` resource
|
|
3067
|
+
continuation starts only after identity-safe monitor removal; its original
|
|
3068
|
+
fire-and-forget cleanup does not become an awaited native shutdown step.
|
|
3069
|
+
The same temporary records also back `createConversation`, `selectConversation`,
|
|
3070
|
+
`runConversationTurn`, `readConversation`, `readPersistentConversation` and
|
|
3071
|
+
`waitForConversationTurn`. These methods retain native creation and model/agent
|
|
3072
|
+
selection, active-turn steering, admission, completion and history-read ordering.
|
|
3073
|
+
Scoped completion also uses the owner's existing native row and structured-result
|
|
3074
|
+
codecs. Applications receive normalized messages for publication and retain their
|
|
3075
|
+
raw-output bounds and result metadata; native structured unwrapping follows that
|
|
3076
|
+
bound at its original late read point.
|
|
3077
|
+
The host supplies authorized targets, profile policy and native message-ID mapping;
|
|
3078
|
+
its existing observation presenter, registry write, bounded result projection,
|
|
3079
|
+
publication and checkpoint effects run at their original positions. Selection
|
|
3080
|
+
awaits registry publication after recording the native identity. Turn admission
|
|
3081
|
+
attaches completion notices before returning; bounded Helper waits retain the
|
|
3082
|
+
saved completion even when a caller supplies another timeout. Ordinary consumers
|
|
3083
|
+
continue to use `createConversationRuntime()`.
|
|
3084
|
+
`releaseTarget` drains the exact active native threads and retained completions
|
|
3085
|
+
before the existing shared release. The host keeps prior PTY/resource cleanup
|
|
3086
|
+
and its registry-removal notification. The same temporary records back native
|
|
3087
|
+
`stopConversation` and `deleteConversation`: persistent Stop retains its separate
|
|
3088
|
+
idle confirmation, and deletion precedes observer retirement and awaited
|
|
3089
|
+
registry removal. Hosts retain access checks, Helper scope release and durable
|
|
3090
|
+
parent cleanup receipts; those are not replaced by process disposal.
|
|
3091
|
+
For an existing scoped binding, explicit deletion calls
|
|
3092
|
+
`preparation.existing(input, context, { operation: "delete" })`. The host resolves
|
|
3093
|
+
its scope/persistence policy from that current context. Read, wait and Stop use
|
|
3094
|
+
the same preparation without the deletion descriptor; closing the handle still
|
|
3095
|
+
uses its supplied close operation. The driver does not interpret application
|
|
3096
|
+
scope fields to choose deletion policy.
|
|
3097
|
+
A host supplies authorized startup and its application removal notification.
|
|
3098
|
+
`openCodeServerForDirectory` keeps each consumer's native client scoped to its
|
|
3099
|
+
working directory. `openCodeEnvironmentForDirectory` and
|
|
3100
|
+
`openCodeEnvironmentForSession` use supplied private bindings, choosing the most
|
|
3101
|
+
specific directory or following at most 32 native parents. Hosts retain the
|
|
3102
|
+
binding values and application authorization. `writeBindings(path, scope, rows)`
|
|
3103
|
+
publishes each consumer's contribution through one private atomic registry;
|
|
3104
|
+
updating or removing one scope retains the others. `readOpenCodeEnvironments`
|
|
3105
|
+
reads that registry for native plugin lookup. The common driver uses this same
|
|
3106
|
+
acquisition layer. Its plugin resolves the current instructions, environment,
|
|
3107
|
+
command wrapper and application-tool bridge by the registered native session.
|
|
3108
|
+
Changing one conversation's grant does not change another's permissions.
|
|
3109
|
+
|
|
578
3110
|
## Companions and templates
|
|
579
3111
|
|
|
580
3112
|
A companion can receive an app-selected layer containing conversation state and
|
|
581
3113
|
`submitText`, rather than searching the DOM. `createAssistantTextSubmission`
|
|
582
|
-
from `/client/conversation
|
|
3114
|
+
from `/client/conversation` builds that action from
|
|
583
3115
|
`{ getState, setDraft, submit, afterDraftChange }`. State is
|
|
584
3116
|
`{ id, active, draft, canSend, turnActive }`. It preserves existing drafts,
|
|
585
3117
|
supports `{ sendImmediately: false }`, waits briefly for send readiness, and
|
|
@@ -587,23 +3119,27 @@ checks draft/ownership again before using the canonical submit action.
|
|
|
587
3119
|
Pass an `AbortSignal` and abort it when selection, visibility or ownership
|
|
588
3120
|
changes, including switching away and back to the same retained conversation.
|
|
589
3121
|
|
|
590
|
-
The
|
|
591
|
-
|
|
592
|
-
|
|
3122
|
+
The `assistant-core/examples/conversation` directory demonstrates lower-level
|
|
3123
|
+
UI, delivery and HTTP primitives. For a new conversation application, start with
|
|
3124
|
+
`assistant-voice/examples/conversation`: speech is optional, and its text cards
|
|
3125
|
+
and headless task use the supplied runtime, transport and browser binding.
|
|
3126
|
+
The lower-level example has a Node backend and no editor dependency. It runs without credentials using a labelled demo
|
|
593
3127
|
provider; an explicit integration configuration enables real AI inference.
|
|
594
3128
|
Its backend validates configuration independently of the UI and accepts a
|
|
595
3129
|
replacement storage module. See its README for commands and scope limits.
|
|
596
3130
|
|
|
597
3131
|
## Observation loss and composer responsiveness
|
|
598
3132
|
|
|
599
|
-
`CodexAppServerJsonRpcClient({ endpoint,
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
3133
|
+
For isolated host tests, `CodexAppServerJsonRpcClient({ endpoint,
|
|
3134
|
+
onDisconnect(error) })` from `/testing/native-codex` reports an unexpected close,
|
|
3135
|
+
socket error, or unreadable message once for the current connection. An explicit
|
|
3136
|
+
`close()` does not call the callback. Obsolete socket events cannot settle current
|
|
3137
|
+
requests or reach subscribers. A test harness using this low-level transport owns
|
|
3138
|
+
callback failures, verified cancellation and reconnection. Ordinary applications
|
|
3139
|
+
use `createConversationRuntime()` for that native coordination and retain their
|
|
3140
|
+
admission and presentation policy. A transport disconnect is not proof that native
|
|
3141
|
+
execution stopped. OpenCode's SSE iterator ending likewise does not prove that
|
|
3142
|
+
the native session is idle; the shared native owner establishes that separately.
|
|
607
3143
|
|
|
608
3144
|
Update `draft` synchronously in `setDraft`. Keep the composer mounted during
|
|
609
3145
|
external state changes, and change `canSend`, `canStop`, `stopPending` and labels
|