@jskit-ai/agent-docs 0.1.250 → 0.1.251

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.
Files changed (33) hide show
  1. package/guide/agent/app-extras/assistant-conversation.md +267 -53
  2. package/guide/agent/app-extras/assistant.md +6 -1
  3. package/guide/agent/app-extras/realtime.md +48 -0
  4. package/guide/agent/app-setup/existing-application-migration.md +7 -1
  5. package/package.json +1 -1
  6. package/patterns/feature-package/example/booking-engine/package.json +1 -1
  7. package/patterns/minimal-foundation/example/package.json +3 -3
  8. package/patterns/shell-foundation/example/package.json +4 -4
  9. package/patterns/shell-foundation/example/packages/main/package.json +1 -1
  10. package/reference/autogen/KERNEL_MAP.md +6 -0
  11. package/reference/autogen/PATTERN_INDEX.md +37 -37
  12. package/reference/autogen/packages/assistant-core.md +29 -9
  13. package/reference/autogen/packages/crud-core.md +2 -2
  14. package/reference/autogen/packages/kernel.md +11 -0
  15. package/reference/autogen/packages/realtime.md +9 -0
  16. package/reference/autogen/packages/shell-web.md +6 -0
  17. package/skills/jskit/references/existing-application-migration.md +7 -1
  18. package/skills/jskit/references/pattern-index.md +37 -37
  19. package/skills/jskit/references/patterns/app/minimal-foundation/example/package.json +3 -3
  20. package/skills/jskit/references/patterns/app/shell-foundation/example/package.json +4 -4
  21. package/skills/jskit/references/patterns/app/shell-foundation/example/packages/main/package.json +1 -1
  22. package/skills/jskit/references/patterns/auth/accounts-hub/example/package.json +2 -2
  23. package/skills/jskit/references/patterns/auth/oidc-sign-in/example/package.json +2 -2
  24. package/skills/jskit/references/patterns/auth/supabase-auth/example/package.json +1 -1
  25. package/skills/jskit/references/patterns/connectors/calendar-cli/example/package.json +4 -4
  26. package/skills/jskit/references/patterns/crud/json-api-resource-package/PATTERN.md +7 -0
  27. package/skills/jskit/references/patterns/crud/json-api-resource-package/example/packages/books/package.json +2 -2
  28. package/skills/jskit/references/patterns/database/mysql-application/example/package.json +1 -1
  29. package/skills/jskit/references/patterns/database/postgres-application/example/package.json +1 -1
  30. package/skills/jskit/references/patterns/realtime/realtime-application/example/package.json +2 -2
  31. package/skills/jskit/references/patterns/server/feature-package/example/booking-engine/package.json +1 -1
  32. package/skills/jskit/references/patterns/users/user-administration-server/example/packages/users/package.json +2 -2
  33. package/skills/jskit/references/patterns/users/user-administration-server/example/packages/users-workspace/package.json +3 -3
@@ -84,7 +84,16 @@ Its authorized operations use the existing action catalogue and HTTP router.
84
84
  Read, send, cancel and delivery inspection use the existing `createAssistantApi`
85
85
  request client, including its surface header and workspace path. Inspection is
86
86
  an authenticated POST operation because it may recover and save native receipts;
87
- it never dispatches another model request. Browser inputs cannot set the actor,
87
+ it never dispatches another model request. Cancelling one message is
88
+ `POST …/deliveries/:messageId/cancel` (`cancelConversationDelivery`), which
89
+ always answers success for an unsent, unconfirmed or failed message, without
90
+ waiting for work queued in that conversation. The runtime records the message
91
+ as cancelled, so a send of it that is still queued, or arrives later, is refused
92
+ as not sent with `conversation_message_cancelled`. It also stops a submission
93
+ that holds the message, and afterwards discards its unconfirmed request or
94
+ pending native attempt. A message already accepted stays history. An
95
+ application conversation provides this as `cancelDelivery({ messageId })`;
96
+ without it the operation answers `conversation_unsupported`. Browser inputs cannot set the actor,
88
97
  native host or engine context.
89
98
  `readConversation(id, { beforeTurnId, limit, signal })` optionally requests an
90
99
  earlier or bounded page through the same authenticated GET. Its query contains
@@ -716,6 +725,10 @@ uses more layout work for long histories. The default is false and retains the
716
725
  existing remove-and-follow behavior and offscreen layout optimization.
717
726
  Scrolling upward within 160 CSS pixels of the transcript's top requests older
718
727
  history through `loadMore`, including touch, wheel and keyboard scrolling.
728
+ New replies are followed while the reader is within 48 CSS pixels of the end.
729
+ The reader's own upward touch, wheel or keyboard scrolling (Home, Page Up, Arrow
730
+ Up, Shift+Space) stops following from its first frame, even within that
731
+ distance; scrolling back down to the end resumes it.
719
732
  Only one request runs at a time, and prepending preserves the reading position.
720
733
  Initial positioning and conversation selection do not fetch older pages.
721
734
  `loadMoreError` pauses automatic loading; the older-messages button remains
@@ -877,7 +890,18 @@ message with attachments while an earlier delivery is pending.
877
890
 
878
891
  Retry keeps the same payload and message ID; the backend must deduplicate that
879
892
  ID. Edit moves failed text into the composer while preserving a newer draft;
880
- Cancel removes only the failed local entry. Neither action stops native work.
893
+ Cancel never removes a message on the browser's own authority. `cancel(id)`
894
+ marks a pending, uncertain or failed entry `cancelling` (shown as
895
+ Cancelling…, with Retry and Edit hidden), and a queued entry cancelled while
896
+ it waits is never delivered. The caller removes it with `remove(id)` once its
897
+ server agrees, or calls `cancelFailed(id, error)` to keep it with Cancel again
898
+ (by default "Couldn't reach the server — Cancel again"). The runtime's
899
+ `cancelMessage(id)` does all of this: it aborts that message's unsent request
900
+ and running delivery check, calls the server's delivery cancel, removes the
901
+ message on agreement and reloads. A cancel that could not reach the server is
902
+ retried on the next snapshot after reconnecting, including after a page reload
903
+ with `draftStorage`. Without an application `cancel` action, the element
904
+ removes only a failed message.
881
905
  Retry leaves the current draft untouched. Attachment bytes and removal on
882
906
  acceptance remain with the application's attachment owner.
883
907
 
@@ -896,7 +920,10 @@ streaming and provider execution remain separately observable application work.
896
920
 
897
921
  For canonical receipt observation, supply reactive `receiptTurns` and
898
922
  `uncertainOnError: true`. Unknown transport failures then retain an uncertain
899
- entry for inspection; they do not expose Retry, Edit or Cancel. Explicit
923
+ entry for inspection; they expose Retry (the delivery check) and, with
924
+ `cancellableDelivery`, Cancel, which stays enabled while the check runs. The
925
+ runtime binding sets `cancellableDelivery`, because its server always accepts
926
+ a delivery cancel. Explicit
900
927
  pre-admission rejection can still return `{ ok: false, error }`. Use `accept(id)`
901
928
  only after the backend confirms admission. `restoreUncertain(request, savedTurns)`
902
929
  restores the public pending request without sending it again.
@@ -1203,7 +1230,7 @@ fields; disable or omit `models` independently when supplying fixed agent settin
1203
1230
 
1204
1231
  ```js
1205
1232
  {
1206
- enabled: true, pending: false, error: "",
1233
+ enabled: true, pending: false, stopPending: false, error: "",
1207
1234
  goal: { objective: "Finish the report", status: "active",
1208
1235
  elapsedSeconds: 120, sampledAt: Date.now() },
1209
1236
  set({ objective, tokenBudget }), pause(), resume()
@@ -1216,7 +1243,9 @@ surface failures in `error`; return `false` on unsuccessful creation. The shared
1216
1243
  `AssistantGoalControl` owns the form and presentation, not a scheduler or goal
1217
1244
  storage. Supported statuses are `active`, `paused`, `blocked`, `usageLimited`,
1218
1245
  `budgetLimited`, and `complete`. The UI offers creation only without an unfinished
1219
- goal and offers Pause/Resume only for their applicable statuses.
1246
+ goal and offers Pause/Resume only for their applicable statuses. `pending`
1247
+ disables Set and Resume; `stopPending` disables Pause and Cancel, which the
1248
+ supplied binding never blocks on message sending or unknown delivery.
1220
1249
 
1221
1250
  An active goal flashes red; a paused goal stays orange. `elapsedSeconds` is the
1222
1251
  accumulated **active running time**, and `sampledAt` is its sample time in epoch
@@ -2544,8 +2573,10 @@ message returns its receipt without resending.
2544
2573
 
2545
2574
  Codex uses its active-turn steering operation. Claude writes the instruction as a
2546
2575
  plain user message on the same process; Claude Code acknowledges it as queued and
2547
- reads it after the running tool call, so steering never interrupts a running
2548
- command. OpenCode admits the instruction in the same session
2576
+ reads it when the running tool call returns. Steering never interrupts a running
2577
+ command: Claude's Ctrl+B control moves a long foreground command to the background,
2578
+ where it keeps running, so Claude reads the instruction at its next step. OpenCode
2579
+ admits the instruction in the same session
2549
2580
  and follows the new input through completion. No adapter recreates the native
2550
2581
  conversation to imitate steering.
2551
2582
 
@@ -2754,9 +2785,10 @@ The native identity, working directory and credential-home binding remain in
2754
2785
  server-side runtime metadata. Prompt changes replace the process while preserving
2755
2786
  native history; compatible model/effort changes use native controls. Account changes
2756
2787
  are rejected by default. An application that owns its conversations and authorizes
2757
- the current workspace account may opt into `host.allowAccountChange: true`. The
2758
- existing account guard confirms the old process has stopped before saving the new
2759
- identity and resuming the same native history. Failed cleanup blocks adoption
2788
+ the current workspace account may opt into `host.allowAccountChange: true`. At
2789
+ the conversation's next idle admission, the existing account guard confirms the
2790
+ old process has stopped before saving the new identity and resuming the same
2791
+ native history; it never stops a running turn. Failed cleanup blocks adoption
2760
2792
  until the existing Stop recovery succeeds. Completed response receipts still
2761
2793
  require their original saved account; this option cannot rebind past effects. Application access
2762
2794
  is checked again after native preparation and before dispatch. Built-in tools are
@@ -2840,6 +2872,17 @@ observer connections on a shared server per account/runtime scope. Stopping or
2840
2872
  closing one conversation leaves other consumers' work and shared server intact. The
2841
2873
  former process-per-conversation launcher has been removed.
2842
2874
 
2875
+ Every Codex process this owner launches (the shared app-server in interactive and
2876
+ Helper mode, and a local `codexInteractiveArguments()` terminal) starts with
2877
+ `-c features.plugins=false -c features.apps=false`. Codex otherwise syncs the
2878
+ openai/plugins repository into `<CODEX_HOME>/.tmp/plugins` and downloads plugin,
2879
+ app and connector catalogues into `<CODEX_HOME>/cache` at start, roughly 100-250 MB
2880
+ per home that is refreshed and never pruned; no conversation uses either. A
2881
+ terminal started with `--remote` uses its app-server's setting. The same
2882
+ processes pass `-c features.daemon_auto_start=false`, because Codex's terminal
2883
+ (on by default in 0.163) otherwise starts a shared app-server daemon whose detached
2884
+ updater keeps it running after the terminal closes.
2885
+
2843
2886
  The existing owner preserves per-directory startup serialization, waiter
2844
2887
  revalidation, compatibility and liveness checks, verified retirement, helper
2845
2888
  preparation and startup/publication cleanup. Execution and credential facilities
@@ -2853,7 +2896,11 @@ The existing native connection/thread owner has also moved into
2853
2896
  `CodexAppServerAgentProvider` under `/server/codex-provider`. It consumes that shared
2854
2897
  runtime with execution and credential-state facilities. Its native behavior includes
2855
2898
  helper token activation/refresh, bounded inventories, control recovery, goals,
2856
- hook trust, exact command interruption and history export. The existing
2899
+ hook trust, exact command interruption and history export. Inventories bound
2900
+ the thread IDs they keep (1,000 threads, 512-character IDs), not the preview
2901
+ text Codex sends and the inventory discards; an existence check stops at the
2902
+ page that holds its thread. A refused inventory names the check and thread that
2903
+ failed. The existing
2857
2904
  native consumer now supplies only its host policy and application callbacks. The common
2858
2905
  driver uses this backend. Ordinary applications use the common conversation API
2859
2906
  with its supplied execution and credential defaults.
@@ -2920,16 +2967,39 @@ native work running; failed stop or final persistence remains retryable. Recover
2920
2967
  uses the recorded native owner after reopening and never replays the request.
2921
2968
  Applications using the common runtime do not supply Codex recovery callbacks.
2922
2969
 
2923
- OpenCode provider credentials are cached for a shared server lifetime. The
2924
- application's private `publish` operation enters that same owner's acquisition
2925
- fence after validating the selected provider. Pending starts drain before the
2926
- owner checks all consumers. Another provider, an unowned driver target or an
2927
- unconfirmed native terminal refuses publication before stopping work; the
2928
- previous saved credential stays available. When only owned selected users remain,
2929
- the original cleanup and verified server exit retire the native cache before
2930
- publishing. New acquisitions refuse during this interval and can retry afterwards.
2931
- The application supplies terminal inventory from its existing PTY owner; no
2932
- second process owner or credential refresh proxy is involved.
2970
+ OpenCode reads provider credentials into each directory's native instance and
2971
+ keeps them until that instance is disposed. An OpenCode connection change
2972
+ (connecting, replacing or removing a key) never stops, kills or waits for running
2973
+ turns, retained conversations or the shared server. After the application's
2974
+ private `publish` operation saves the change, the owner reads the host's saved
2975
+ connections from the invalidation preparation's `connections()`, writes each
2976
+ changed key to the running server (`PUT /auth/{provider}`), removes each removed
2977
+ one, and returns `{ closed: 0, ok: true }`. Directories with no turn in flight
2978
+ reload at once (`POST /instance/dispose`). A directory with a turn in flight
2979
+ reloads when its last turn there ends, so that turn finishes on the credentials
2980
+ it started with. New work in a directory still awaiting its reload (a new main,
2981
+ temporary or renewal turn, or a native terminal) waits for that reload and so
2982
+ never starts on credentials the server no longer holds; the only wait is that
2983
+ directory's own running turn, and a steer joins it. The server client records
2984
+ every directory it has loaded (`instanceDirectories()`), so a directory that
2985
+ only served storage reads reloads too, and `disposeInstance()` reloads one
2986
+ directory. `ensure(selected, start, readConnections)` applies the same update
2987
+ when an acquisition finds a changed key. It reads the saved connections instead
2988
+ of trusting the caller's, so a preparation resolved before the change cannot
2989
+ restore an older key. Without `readConnections`, or when a provider's route
2990
+ changes, the shared server is replaced as before. A turn whose provider was
2991
+ removed ends as a credential failure with "`<provider>` isn't connected. Connect
2992
+ it again, then send your message again." An unconfirmed native terminal still
2993
+ refuses the change before publication through the application's
2994
+ `assertConnectionChange`; no second process owner or credential refresh proxy is
2995
+ involved.
2996
+
2997
+ When the shared OpenCode server exits without being stopped, the owner stops
2998
+ what remains of it at once (reason `opencode-server-exited`) through the same
2999
+ stop as a failed health check, rather than leaving its execution and leftover
3000
+ processes until the next health check, connection change or close.
3001
+ `createOpenCodeServerProcess()` exposes that exit as `unexpectedExit`; hosts
3002
+ wire nothing for it.
2933
3003
 
2934
3004
  An application's selected provider credential change uses the existing owner's
2935
3005
  `invalidateRuntimes({ modelProviderId, includeOwned, toolHomeSource })` branch.
@@ -2944,6 +3014,26 @@ process retirement only after every loaded/cached thread is proved to belong to
2944
3014
  that provider; mixed-provider homes refuse retirement. Genuine account/socket
2945
3015
  changes retain the original whole-runtime invalidation and recovery.
2946
3016
 
3017
+ A Codex account change (the runtime lifecycle's `includeOwned` invalidation)
3018
+ never stops or waits for a running turn. A Codex process reads its credentials
3019
+ once, so runtimes whose loaded threads are idle stop at once through the
3020
+ verified stop, while a runtime with a running native turn is marked stale: its
3021
+ active goals are paused so it starts no further turn, its provider still serves
3022
+ that turn's output, steer and interrupt, never restarts its runtime and never
3023
+ marks the current account after a rejection. The owner result lists it under
3024
+ `deferred`; it stops through the same verified stop once its turn completes
3025
+ and its output is recorded, or before the session's next runtime starts. The
3026
+ application gives each sign-in its own runtime directory and provider key, so
3027
+ the next message starts at once on the current account. The run owner's
3028
+ `continueGoalAfterAccountChange(sessionId, { threadId })` option resumes a goal
3029
+ the change paused once the previous runtime stopped; without it the goal stays
3030
+ paused. A runtime that cannot be inspected stops as before. When the
3031
+ application's `identity.write` returns `supersededRuntimeDir` (the runtime
3032
+ directory the session recorded before), the owner removes that directory once
3033
+ its runtime has verifiably stopped; a stale runtime still finishing its turn
3034
+ keeps no exit proof when it stops. Helper cleanup treats an absent directory as
3035
+ retired.
3036
+
2947
3037
  The application owns independently saved orchestrator/provider records and
2948
3038
  validation before publication. This native lifecycle facility copies no keys
2949
3039
  and validates no other provider. Changed account fingerprints still require an
@@ -3137,9 +3227,11 @@ retained runtime handle. Closure during host preparation prevents acquisition.
3137
3227
  `inspectTemporaryActivity` and reads the selected owner's existing temporary-work
3138
3228
  inventory. Its configured host supplies `{ sessionId: id, engine, native, context }`.
3139
3229
  It opens no Main conversation and remains available during cleanup after runtime
3140
- closure. Native restoration and activity exclusions are those of the same owner;
3141
- there is no additional cache. This advanced server operation has no browser route
3142
- or caller-supplied owner/engine override.
3230
+ closure. Activity exclusions are those of the same owner; there is no additional
3231
+ cache. Claude answers from the temporary work this server runs and reopens no
3232
+ saved conversation: a turn an earlier server saved as active ended with that
3233
+ server. This advanced server operation has no browser route or caller-supplied
3234
+ owner/engine override.
3143
3235
 
3144
3236
  An existing application's renewal transaction can invoke
3145
3237
  `runtime.generateNativeRenewalHandover({ id, context, input })` and
@@ -3165,12 +3257,21 @@ its existing renewal authorization before native cleanup.
3165
3257
  The same native owners preserve their original contracts: Codex validates
3166
3258
  application renewal authority before its lifecycle queue, then reads saved runtime
3167
3259
  facts inside that queue and releases persisted proof only after shared-peer checks;
3168
- Claude closes through its existing entry/terminal cleanup; OpenCode predecessor
3260
+ Claude stops the session's retained work, closes its terminals and forgets the
3261
+ session, reopening no saved conversation and writing nothing for an idle one (the
3262
+ session's own close already recovered saved custody); OpenCode predecessor
3169
3263
  cleanup awaits close before consuming proof, while successor cleanup only consumes
3170
3264
  proof. OpenCode retains its original distinction between a resolved failed close
3171
3265
  (which still consumes proof) and rejected close (which leaves proof retryable).
3266
+ Deleting a scoped OpenCode conversation returns its scope's exit proof as
3267
+ `providerExit` and keeps none, because no renewal ever releases a scope's proof.
3172
3268
  Applications retain result projection and successful binding retirement.
3173
3269
 
3270
+ Closing a session stops what this server runs for it and then reopens only the
3271
+ saved custody an earlier server left (an execution whose exit is unproven, or a
3272
+ turn saved as active), so a verified stop can retire it. Claude leaves idle saved
3273
+ conversations closed and names an unreadable saved conversation by ID.
3274
+
3174
3275
  `runtime.disposeNative({ namespace, sessionId, context, options })` also supports
3175
3276
  cleanup when no ready retained handle exists. It authorizes operation `dispose`
3176
3277
  and requests `{ id: sessionId, context, input: options, operation: "dispose" }`
@@ -3184,6 +3285,21 @@ existing early-retirement fence before falling back to the configured owner.
3184
3285
  These server-only operations accept no caller-selected owner or engine and add
3185
3286
  no browser endpoints. Ordinary applications still use their conversation handle.
3186
3287
 
3288
+ A host can bind a logical conversation through its own `commands`, `read` and
3289
+ `readStream`, for example an application's temporary chat. `open()` keeps one
3290
+ entry for each such namespace, and browser handles opened per request share it.
3291
+ When the application deletes or archives that logical conversation, call
3292
+ `runtime.retireOpenConversation({ namespace, sessionId })` from the same trusted
3293
+ server transaction. It retires the open entry, closes its handles and listeners,
3294
+ and returns `{ retired: true }`. A namespace with no open entry returns
3295
+ `{ retired: false }`: nothing is opened to retire it. Native and storage-owned
3296
+ conversations are refused; they keep `dispose()` and `runtime.disposeNative()`.
3297
+ The application has already stopped and removed the logical conversation's work
3298
+ through its own commands; this operation stops nothing and has no browser endpoint.
3299
+ A logical conversation streams through its own `readStream`, so retiring or
3300
+ disposing it leaves the live reply of another conversation that shares its id,
3301
+ such as the session's Main conversation, unchanged.
3302
+
3187
3303
  Historical native records can predate an application's retained scope identity.
3188
3304
  An advanced host can clean up those exact records through
3189
3305
  `runtime.interruptNativeDetachedConversation({ id, context, input })` or
@@ -3209,8 +3325,13 @@ no inference authorization or runtime-closure rule to the original storage polic
3209
3325
  Storage work borrows the shared native process; it is not a user that keeps it
3210
3326
  running. Codex and OpenCode stop a process a storage read started about one
3211
3327
  minute after the last such read (so one maintenance pass shares it), unless a
3212
- conversation, terminal or helper uses it by then. Any real Codex use of that
3213
- provider claims it, and goal and plan-usage reads ignore a borrowed provider.
3328
+ conversation, terminal or helper uses it by then. Persistent (Temporary chat)
3329
+ conversations borrow it the same way: creating them, reading their history or
3330
+ status, stopping idle work, deleting them and changing their settings borrow the
3331
+ provider; a running persistent turn or an active goal holds it until it ends,
3332
+ and it then stops after the same idle minute unless something else uses it. Any
3333
+ other real Codex use of that provider claims it, and goal and plan-usage reads
3334
+ ignore a borrowed provider.
3214
3335
  All four operations are server-only, accept no caller-supplied native owner or
3215
3336
  host, and have no HTTP, socket or client route. Ordinary applications continue
3216
3337
  using their conversation handles.
@@ -3223,6 +3344,18 @@ and synchronously returns `{ engine, native, context, input }` with its existing
3223
3344
  native owner and application preparation. Codex, Claude and OpenCode support this
3224
3345
  advanced operation. It opens no conversation or retained handle and remains
3225
3346
  retryable after runtime closure, including when credentials have been revoked.
3347
+ Claude closes each of the project's sessions from its retained entries alone,
3348
+ independently and together, and then forgets them. A session that cannot close
3349
+ is reported by ID in `failed` (with a combined `error`) while every other session
3350
+ still stops.
3351
+ Codex stops each scoped conversation through the provider that observes it,
3352
+ then retires saved Helper records through the providers it stops, and stops every
3353
+ provider of the project's sessions, including one that a Temporary chat or a
3354
+ read borrowed and no Main thread names. A conversation
3355
+ that cannot be stopped is reported in `failed` while the others still stop. A
3356
+ saved Helper record that cannot be restored (for example, one whose account was
3357
+ removed) stays on disk for a later connection; the result reports it under
3358
+ `helperRestore.failed` with its session and thread instead of failing the stop.
3226
3359
 
3227
3360
  `runtime.invalidateNativeRuntimes({ context, input })` uses the same trusted
3228
3361
  server boundary for account changes and shutdown. Its configured host receives
@@ -3231,10 +3364,31 @@ synchronously. The runtime returns the native operation directly: shutdown
3231
3364
  fences and synchronous errors are not deferred behind another await. The host
3232
3365
  retains account policy and result presentation; the native owner retains its
3233
3366
  original cleanup sequence and retryable failures. No conversation is opened:
3234
- each native owner stops the processes this server runs, since none outlives the
3235
- server that started it, and saved conversations are not reopened to be stopped.
3236
- Failed cleanup can be retried after runtime closure. This is not a browser
3367
+ at shutdown each native owner stops the processes this server runs, since none
3368
+ outlives the server that started it, and saved conversations are not reopened to
3369
+ be stopped. Claude stops its sessions together, leaves an idle chat untouched
3370
+ (nothing to stop, nothing written) and reports a session that cannot stop by ID
3371
+ in `failed` while the others still stop. It stops them even when its
3372
+ account-query cleanup fails; that failure is still the call's result. Once
3373
+ shutdown has invalidated the owner, a later session close only releases retained
3374
+ work. Failed cleanup can be retried after runtime closure. This is not a browser
3237
3375
  operation or a way to submit a caller-selected native owner.
3376
+ A running Codex app-server keeps the credentials it started with. A Codex turn
3377
+ whose OpenAI credentials were rejected after a sign-out or account switch
3378
+ (ChatGPT's token-refresh failure, or a 401 from OpenAI) ends with "Codex (OpenAI)
3379
+ isn't connected. Sign in to Codex, then send your message again."; another
3380
+ provider's rejected key keeps its own error.
3381
+
3382
+ A Claude account change (sign-in, sign-out or switching accounts) stops and
3383
+ waits on nothing. Claude Code reads its credentials for every request, so a
3384
+ running turn continues on the newly connected account, and a conversation whose
3385
+ account signed out ends its next request at once with "Claude isn't connected.
3386
+ Sign in to Claude, then send your message again." The Claude owner only drops
3387
+ the account's cached model and usage reads, without waiting for cancelled reads
3388
+ to finish. Each conversation applies the account guard below at its next idle
3389
+ admission; a status read during a running turn keeps that turn. A provider-key
3390
+ change (`modelProviderId`) still stops the conversations using that provider,
3391
+ because their key is fixed when the process starts.
3238
3392
 
3239
3393
  `runtime.reconcileNativeSessions({ context, sessions, options })` restores an
3240
3394
  application's saved session collection through those same configured native
@@ -3442,8 +3596,9 @@ reuses the host's existing terminal snapshot before attaching, records the
3442
3596
  returned terminal ID on that target, and clears matching IDs after the host's
3443
3597
  existing PTY close resolves. The host supplies only its original read, close
3444
3598
  and namespace functions; a thrown close keeps the native attachment retryable.
3445
- Project cleanup restores the same Helper records, closes the scoped native
3446
- conversations and selects their providers through these existing owners.
3599
+ Project cleanup closes the scoped native conversations, then restores the same
3600
+ Helper records and selects their providers through these existing owners; a
3601
+ record it cannot restore is reported rather than keeping providers running.
3447
3602
  Conversation preparation also applies Helper restoration or ordinary-thread
3448
3603
  ledger exclusion before admission. Native activity and plan-usage queries read
3449
3604
  the same owned maps; hosts retain authorization and result presentation.
@@ -3587,12 +3742,19 @@ The turn owner preserves the production receiver's block lifecycle: deltas are
3587
3742
  temporary, block-stop removes them, and completed snapshots use their native
3588
3743
  UUIDs. It does not group distinct history snapshots into a synthetic reply.
3589
3744
  The retained owner's `createAccountQueries({ accountIdentity, createProcess })`
3590
- composes the existing native model/usage query owner with its own process inventory
3591
- and closing state. A managed host supplies its authorized identity reader and
3745
+ composes the existing native model/usage query owner with its own process inventory,
3746
+ configuration root and closing state. A managed host supplies its authorized identity reader and
3592
3747
  account-process factory. The original query caches, pending-query sharing and
3593
3748
  retryable cleanup remain unchanged; account queries share already-owned processes
3594
3749
  and the same conversation shutdown boundary. Matching owned processes
3595
- are reused; temporary query processes must prove cleanup. Failed stops remain
3750
+ are reused for plan usage; temporary query processes must prove cleanup. The
3751
+ model list is read through a temporary query process (a live conversation's list
3752
+ dates from its start) and kept for its account until the account's
3753
+ `.credentials.json` or the answering process's `command` changes by file
3754
+ metadata: sign-in, sign-out, a token refresh or plan change, or a Claude Code
3755
+ update. Rereading it otherwise starts nothing. A bare command name has no
3756
+ metadata, so only the credentials refresh its list. A started process exposes
3757
+ the `command` it ran. Failed stops remain
3596
3758
  owned and are retried before another native query. `invalidate()` drains pending
3597
3759
  queries, clears both caches and retries retained cleanup while new work is
3598
3760
  blocked. For managed bindings, the existing retained conversation owner also
@@ -3603,10 +3765,17 @@ terminal cleanup and product error conversion. These trusted server lifecycle
3603
3765
  operations use the configured common runtime and are not browser actions.
3604
3766
  The account owner's usage normalization retains only native windows whose values and reset times
3605
3767
  are valid; it does not invent refreshed allowance. Plan usage keeps one reading
3606
- per account. It reads through a matching live conversation process at most once
3607
- a minute; with none, or when that answer cannot prove it is current, it starts
3608
- one temporary query process at most every five minutes and stops it when the
3609
- read finishes. A refused fallback keeps the last reading. Only an answer carrying the usage
3768
+ per account in the server process, shared by every account-query owner, page and
3769
+ session that asks with the same `accountIdentity`, so that identity must name one
3770
+ account. A request that arrives during the account's read shares it. It reads
3771
+ through the asking owner's matching live conversation process at most once a
3772
+ minute per account; with none, or when that answer cannot prove it is current,
3773
+ it starts one temporary query process only when neither a verified reading nor a
3774
+ temporary read is from the last five minutes, and stops it when the read
3775
+ finishes. An account therefore starts at most one temporary Claude every five
3776
+ minutes however many pages poll, and none while nothing asks; clients should
3777
+ poll only while the page is visible. A refused fallback keeps the last reading.
3778
+ `invalidate()` forgets the readings that owner used. Only an answer carrying the usage
3610
3779
  endpoint's `limits` list replaces the reading: Claude Code's fallback answer,
3611
3780
  taken from the answering process's last response headers while its endpoint read
3612
3781
  fails or is held, can be hours old and has no age. A reading is returned as
@@ -3705,13 +3874,16 @@ policy. Both compatibility probes retain their sanitized connection errors;
3705
3874
  neither operation is a conversation or an account store.
3706
3875
  `readClaudeCodeAuthStatus` uses a supplied capture runner for native account
3707
3876
  inspection. Hosts select the executable and credential home and retain their
3708
- execution policy; the reader returns only public account fields. The ordinary
3709
- runtime supplies this integration automatically. When the runner reports that a
3710
- read timed out (`timedOut`) or was refused for now (`retryable`), the reader
3711
- returns Claude's last answer if the account files are unchanged and that answer
3712
- is under five minutes old, instead of reporting the account as signed out.
3713
- Such a read, with or without that answer, is reused for 30 seconds so a busy
3714
- host is not asked again by every caller.
3877
+ execution policy; the reader returns only public account fields. Reads of one
3878
+ account (configuration root, credential home and Claude identity variables)
3879
+ share one answer whichever runner or executable asks, kept until a sign-in,
3880
+ sign-out or token refresh changes `.credentials.json`; Claude Code's own
3881
+ `.claude.json` rewrites, during turns and after each status run, do not repeat
3882
+ the read. The ordinary runtime supplies this integration automatically. A read
3883
+ the runner reports as timed out (`timedOut`) or refused for now (`retryable`) is
3884
+ reported and reused for 30 seconds, so a busy host is not asked again by every
3885
+ caller; a read that ran and failed is asked again next time. macOS reuses
3886
+ nothing, because file metadata cannot show Keychain changes.
3715
3887
  The retained owner stops its native process or recovers its durable execution
3716
3888
  identity through the supplied host. Its internal stop operation preserves the
3717
3889
  original stop-proof contract and throws `claude_stop_unconfirmed` when cleanup
@@ -3826,6 +3998,14 @@ Set transport limits appropriate to the host; the default payload limit is
3826
3998
  unbounded. A request abort retires the local request; interrupt a running native
3827
3999
  turn with `turn/interrupt` when cancellation must stop provider work.
3828
4000
 
4001
+ `prepareCodexModelCatalog({ command, runtimeDir, bundled, additionalModels,
4002
+ codexHome, signal })` from the same entry point exports a runtime's
4003
+ `models.json` with the CLI's `debug models` and reuses it until its revision
4004
+ changes: the absolute command's file identity, `bundled`, `additionalModels`, or
4005
+ the metadata of `auth.json` and `config.toml` in `codexHome` (default
4006
+ `CODEX_HOME`, then `~/.codex`). A bare command name exports every time. Isolated
4007
+ tests pass their own `codexHome`.
4008
+
3829
4009
  The original public import contracts remain available alongside these host and
3830
4010
  testing surfaces. `/server/codex-client` exports `CodexAppServerJsonRpcClient`
3831
4011
  and `socketPathFromCodexAppServerEndpoint`; `/server/codex-events` retains its
@@ -3952,8 +4132,14 @@ call the same Helper owner directly for thread lookup and retirement, without a
3952
4132
  host inspection callback. A returned retirement promise retains the original
3953
4133
  admission-release order; native dispatch remains awaited.
3954
4134
  It also rejects ordinary access to a retained Helper thread and aggregates
3955
- restoration failures without changing their retryability. The host decides when
3956
- successful restoration is required and supplies its authorized storage root.
4135
+ restoration failures without changing their retryability. An unreadable ledger
4136
+ or a host notice the host refuses is reported as a restoration failure, not a
4137
+ rejection. The host decides when successful restoration is required and supplies
4138
+ its authorized storage root; the required-restoration error names the first
4139
+ failing session, thread and cause. Closing a session or project reports
4140
+ unrestorable records and still stops its live work. Reconciliation continues
4141
+ for every session and retires each stale provider independently. Account-change
4142
+ invalidation does not restore saved records.
3957
4143
  Stable-account capture and completion validation also belong to the shared run
3958
4144
  owner, independently of the optional retained Helper ledger. They preserve the
3959
4145
  account check after native resume, and the exact current-turn guard before
@@ -4025,6 +4211,17 @@ and `codexCatalogReasoningEfforts` retain native IDs, display-name fallbacks and
4025
4211
  reasoning-option order. Hosts choose offerings, defaults, connection visibility
4026
4212
  and product labels; they may use the original row fields `model`, `hidden`,
4027
4213
  `isDefault` and `defaultReasoningEffort` for that policy.
4214
+ `codexHomeDisposablePaths(codexHome, { retired = false })` returns the sorted
4215
+ absolute paths of Codex-owned entries no JSKIT-launched Codex uses: the plugin
4216
+ and app leftovers in `.tmp` (`plugins*`, `git-*`, `bundled-marketplaces`) and
4217
+ `cache` (`remote_plugin_catalog`, `codex_apps_tools`, `codex_apps_server_info`,
4218
+ `codex_app_directory`, `bundled_plugin_exclusions`). With `retired: true`, for a
4219
+ home that will not launch again, it returns `logs_<n>.sqlite` with its `-wal` and
4220
+ `-shm` files, `.tmp`, `cache` and `tmp`, and keeps auth, config, the catalogue,
4221
+ sessions and the state, goal, memory, queue and thread-history databases that
4222
+ retained conversations may still read. It never follows a linked home or
4223
+ subdirectory, returns a link as itself and reads no file content. The host
4224
+ removes the paths, and only while no Codex process uses that home.
4028
4225
  `codexProviderFileConfiguration(provider, key, codexHome)` formats the original
4029
4226
  private `config.toml` text from the host's authorized provider record and key.
4030
4227
  The record supplies `id`, `label`, `baseUrl`, `webSearch` and the selected first
@@ -4192,7 +4389,8 @@ unconfirmed or rejected stop remains retryable. The execution facility receives
4192
4389
  the native stop reason and three-second termination grace period.
4193
4390
  The application still owns its database path and retention. Startup errors retain
4194
4391
  the execution ID and cleanup proof; optional host `readLogs()` supplies bounded
4195
- diagnostics. If cleanup is unconfirmed, the error also retains `retryCleanup`,
4392
+ diagnostics. A server that exits before readiness names why in its message: its
4393
+ last stderr line as plain text, at most 300 characters. If cleanup is unconfirmed, the error also retains `retryCleanup`,
4196
4394
  the same native stop operation. The shared owner holds that operation until exit
4197
4395
  is verified and blocks replacement startup while cleanup remains unconfirmed.
4198
4396
  Its `cleanupPending` flag lets a managed host retry cleanup during project close
@@ -4336,7 +4534,14 @@ For an existing scoped binding, explicit deletion calls
4336
4534
  its scope/persistence policy from that current context. Read, wait and Stop use
4337
4535
  the same preparation without the deletion descriptor; closing the handle still
4338
4536
  uses its supplied close operation. The driver does not interpret application
4339
- scope fields to choose deletion policy.
4537
+ scope fields to choose deletion policy. A Codex ephemeral thread lives only in
4538
+ the process that created it, so deleting one this server no longer holds needs
4539
+ no native delete. Deleting a scoped conversation stops only what this server
4540
+ runs for the scope: its live providers, and a runtime whose earlier stop could
4541
+ not prove its exit (a runtime other providers share stays theirs). It never
4542
+ starts a provider to stop it, so it needs no account, tool home or workdir. Closing a session stops each
4543
+ persistent temporary conversation through the provider that observes it and
4544
+ reports those it could not stop, which stay retryable.
4340
4545
  A host supplies authorized startup and its application removal notification.
4341
4546
  `openCodeServerForDirectory` keeps each consumer's native client scoped to its
4342
4547
  working directory. `openCodeEnvironmentForDirectory` and
@@ -4504,9 +4709,18 @@ keep using the shared owner for steering and goal admission.
4504
4709
 
4505
4710
  Claude steering and active-turn goal Set/Resume never interrupt the running turn.
4506
4711
  The new input is written as a plain stream-json user message. Claude Code 2.1.307
4507
- acknowledges it with a `queued` command lifecycle event, finishes the running
4508
- foreground tool, then reads the input in the same loop (or in its next loop when
4509
- it arrives after the final model request). The ordinary receipt owner admits it
4712
+ acknowledges it with a `queued` command lifecycle event and reads the input when
4713
+ the running foreground tool call returns, in the same loop (or in its next loop when
4714
+ it arrives after the final model request). So that a long foreground command or a
4715
+ wait loop cannot hold the input for minutes, each steer sends the
4716
+ `background_tasks` control request (Claude's Ctrl+B equivalent) after its write,
4717
+ and again on each `task_started` event until Claude reads it. Foreground Bash
4718
+ commands and subagents keep running in the background and report completion with
4719
+ a task notification; the blocked tool call returns at once.
4720
+ Claude Code 2.1.308 supports the control. A refused control (an older CLI) only
4721
+ leaves the input waiting for the tool call and publishes the
4722
+ `steering_background_failed` diagnostic; `steering_backgrounded` reports success.
4723
+ The ordinary receipt owner admits it
4510
4724
  on that acknowledgement or replay. One native result can then finish the original
4511
4725
  and the queued input; inputs not yet started stay in flight until their own result.
4512
4726
  Interrupting for a steer would kill a running foreground tool such as a deploy.
@@ -172,6 +172,9 @@ NDJSON routes, conversation IDs, message rows and request-claim table remain the
172
172
  integration boundary. A tool reservation is committed to its original
173
173
  `tool_call` row before execution; the matching `tool_result` row retains the
174
174
  verified result. No second transcript is stored in conversation metadata.
175
+ Each chat turn opens its conversation in the runtime and the last of a
176
+ conversation's overlapping turns releases it when it settles, so a finished
177
+ conversation keeps no server memory; the next turn opens it again.
175
178
 
176
179
  The surface's bounded, client-selected history remains request context. It is
177
180
  authorized for attachments and sent to the API model without being inserted into
@@ -542,7 +545,9 @@ it, the UI shows sending status. A delivery failure offers Resend, Edit and Canc
542
545
  beside the message. Resend retains the original message ID, text, selected
543
546
  integration, history and attachment IDs; newer typing and newly attached files
544
547
  remain untouched. Edit moves failed text into the composer and preserves newer
545
- text. Cancel removes only that local failed entry.
548
+ text. Cancel asks the server to cancel that message; it shows Cancelling… and
549
+ leaves only once the server agrees, or stays with Cancel again when the server
550
+ cannot be reached.
546
551
 
547
552
  After acceptance, the runtime displays provider progress and streamed answers.
548
553
  An execution failure before an answer restores the request as a draft only if the
@@ -23,6 +23,14 @@ when an event has an explicit realtime name and audience. The event name,
23
23
  scope, payload, and reconnect behavior are public product contracts—not a
24
24
  generic “something changed” escape hatch.
25
25
 
26
+ The `event_scope` audience, the default for action entity-change and CRUD
27
+ `record.changed` events, delivers to the room of the event's `user`,
28
+ `workspace`, or `workspace_user` scope. An event without such a scope, for
29
+ example one written from a public route or by a job, reaches nobody and logs a
30
+ warning; `event_scope` never widens to all users. Broadcast to every signed-in
31
+ user only through the explicit `all_users` audience; a CRUD feature selects it
32
+ with `defineCrudJsonApiFeature({ realtimeAudience: "all_users" })`.
33
+
26
34
  Client features register listeners through `@jskit-ai/realtime`. Keep query
27
35
  invalidation close to the resource that owns the query keys.
28
36
 
@@ -83,6 +91,46 @@ The in-process adapter is correct for one server process. Set
83
91
  `REALTIME_REDIS_URL` only when multiple processes need a shared backplane.
84
92
  Credentials stay outside Git and never enter public client config.
85
93
 
94
+ ## Shutdown
95
+
96
+ Realtime shutdown refuses new Socket.IO requests and closes its engine, adapter
97
+ and Redis connections. Clients see their transport close, as with
98
+ `Server#close()`, so Socket.IO's own reconnection reaches the next server; the
99
+ server never sends a disconnect that would stop it. It never closes the HTTP
100
+ server: that belongs to its owner (Fastify closes its own after feature
101
+ shutdown), so another route's open WebSocket or stream does not hold realtime
102
+ shutdown. Code that passes its own `httpServer` to `createSocketIoServer` closes
103
+ that server itself after `closeSocketIoServer`.
104
+
105
+ ## Client updates
106
+
107
+ An open page keeps running the client code it loaded until it reloads, so after
108
+ an update it can run old code indefinitely. JSKIT's Vite plugin
109
+ (`createJskitClientBootstrapPlugin`) names each production-built HTML page's
110
+ build in `<meta name="jskit-client-build" content="...">`: a digest of the page,
111
+ whose content-hashed script and style names change whenever any built code or
112
+ style does. The same source builds the same id. Vite's development pages have
113
+ none.
114
+
115
+ On every connection, including the reconnection to a restarted server, the
116
+ realtime server sends `realtime.served-client-build` with `{ buildId }`: the
117
+ build of the page the app serves now, read from `dist/index.html` under
118
+ `runtime.app-root`. It is read for each connection, so a page rebuilt while the
119
+ server runs counts at once. With no built page, nothing is sent. The client
120
+ capability's `onServedClientBuild(listener)` calls the listener with each
121
+ announced id, and at once with the latest one; it returns the unsubscribe
122
+ function. There is no polling and no extra request.
123
+
124
+ When `@jskit-ai/shell-web` is installed, a page whose own build differs from the
125
+ served one reports an `app-recoverable` notice, a banner under the default
126
+ policy: "*Title* was updated. Reload to get the latest version.", where *Title*
127
+ is the page's `<title>` as it loaded ("This app" without one), with a **Reload**
128
+ action. The page never reloads by itself, because the person may be in the
129
+ middle of something. **Reload** checks that the server answers, then reloads
130
+ the same address. The notice appears once per served build: a reconnection to
131
+ the same build does not repeat it, closing it keeps it closed until a newer
132
+ build, and a server back on the page's own build withdraws it.
133
+
86
134
  ## Visible connection state
87
135
 
88
136
  A status indicator is optional product UI. When wanted, register it through the
@@ -67,7 +67,13 @@ or operation boundary. A Feature declares stable named `requires`, optional
67
67
  inputs, and `provides`; `setup()` receives those values directly and returns
68
68
  only the promised outputs. Named actions capture the exact feature APIs they
69
69
  need. Register structural catalogue entries during `setup()` and reserve
70
- `boot()` for long-lived work that starts after composition.
70
+ `boot()` for long-lived work that starts after composition. `shutdown()` stops
71
+ that work. The runtime shuts Features down in reverse order and keeps going
72
+ after one fails; it then rejects with an `AggregateError` holding one error per
73
+ failed Feature, each naming its `providerId`. While shutdown runs,
74
+ `diagnostics().providerShutdowns` lists every shutdown with its status
75
+ (`stopping`, `stopped` or `failed`) and duration, so a host can log which one is
76
+ slow.
71
77
 
72
78
  Capability ids, action ids, routes, events, resources, and placements are
73
79
  stable identities. They are not permission to fetch arbitrary dependencies by