@kodax-ai/kodax 0.7.69 → 0.7.71

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 (87) hide show
  1. package/CHANGELOG.md +187 -1
  2. package/LICENSE +158 -191
  3. package/README.md +100 -17
  4. package/README_CN.md +77 -11
  5. package/config-templates/integrations/a2a.example.jsonc +62 -18
  6. package/dist/chunks/{agent-DQRXT6M7.js → agent-X2EWQADL.js} +1 -1
  7. package/dist/chunks/argument-completer-BLNXDO2F.js +2 -0
  8. package/dist/chunks/chunk-23X6REO4.js +46 -0
  9. package/dist/chunks/{chunk-Y4WOTWUC.js → chunk-44QXPEEE.js} +1 -1
  10. package/dist/chunks/chunk-4DY756KL.js +70 -0
  11. package/dist/chunks/{chunk-AWMTNUDS.js → chunk-52JSZ77P.js} +1 -1
  12. package/dist/chunks/{chunk-I4TPQEJN.js → chunk-64NQBOSN.js} +1 -1
  13. package/dist/chunks/{chunk-URC6ZI6P.js → chunk-A2TFSV5M.js} +1 -1
  14. package/dist/chunks/{chunk-ZG4DMYBS.js → chunk-A574DBTV.js} +2 -2
  15. package/dist/chunks/{chunk-VAZ25MDX.js → chunk-F7KGYHJ3.js} +2 -2
  16. package/dist/chunks/{chunk-M7TCFYTO.js → chunk-GVTM4Z6O.js} +66 -22
  17. package/dist/chunks/{chunk-PA76WUBL.js → chunk-PHH4TRGF.js} +2 -2
  18. package/dist/chunks/chunk-PVUXONQB.js +366 -0
  19. package/dist/chunks/{chunk-WW2O2DEP.js → chunk-SX2WFHIS.js} +1 -1
  20. package/dist/chunks/{chunk-4WIODYOH.js → chunk-T7LYK53M.js} +2 -2
  21. package/dist/chunks/{chunk-F7C7J6IM.js → chunk-VRSVNU2Y.js} +3 -3
  22. package/dist/chunks/chunk-Y5XKAN7C.js +755 -0
  23. package/dist/chunks/{chunk-NGJURAY5.js → chunk-Z535BARK.js} +4 -4
  24. package/dist/chunks/chunk-ZJIMT5I3.js +78 -0
  25. package/dist/chunks/compaction-config-5KKKXYFT.js +2 -0
  26. package/dist/chunks/{construction-bootstrap-GH6RW6ST.js → construction-bootstrap-OXDDDW6O.js} +1 -1
  27. package/dist/chunks/{dist-DRBKYVHF.js → dist-GOUP4YVE.js} +1 -1
  28. package/dist/chunks/{dist-RWL2RBO4.js → dist-IXJRJ27U.js} +1 -1
  29. package/dist/chunks/host-VRKDAN26.js +2 -0
  30. package/dist/chunks/run-manager-Z3W6MDO7.js +2 -0
  31. package/dist/chunks/{utils-QYE5Y73D.js → utils-CFOXDCB5.js} +1 -1
  32. package/dist/index.d.ts +18 -18
  33. package/dist/index.js +6 -6
  34. package/dist/kodax_cli.js +1365 -1296
  35. package/dist/provider-capabilities.json +101 -57
  36. package/dist/runtime-worker.js +1224 -1156
  37. package/dist/sdk-a2a.d.ts +187 -22
  38. package/dist/sdk-a2a.js +8 -7
  39. package/dist/sdk-agent.d.ts +49 -20
  40. package/dist/sdk-agent.js +1 -1
  41. package/dist/sdk-coding.d.ts +33 -25
  42. package/dist/sdk-coding.js +1 -1
  43. package/dist/sdk-experimental-memory.d.ts +1 -1
  44. package/dist/sdk-experimental-memory.js +1 -1
  45. package/dist/sdk-llm.d.ts +7 -7
  46. package/dist/sdk-llm.js +1 -1
  47. package/dist/sdk-mcp.d.ts +2 -2
  48. package/dist/sdk-mcp.js +1 -1
  49. package/dist/sdk-media.d.ts +1 -1
  50. package/dist/sdk-media.js +1 -1
  51. package/dist/sdk-repl.d.ts +17 -17
  52. package/dist/sdk-repl.js +1 -1
  53. package/dist/sdk-runtime.d.ts +96 -17
  54. package/dist/sdk-runtime.js +1 -1
  55. package/dist/sdk-session.d.ts +7 -7
  56. package/dist/sdk-session.js +1 -1
  57. package/dist/sdk-skills.js +1 -1
  58. package/dist/semantic-worker.js +10 -10
  59. package/dist/types-chunks/{base.d-CYjtB68X.d.ts → base.d-Cz_rwpOi.d.ts} +2 -1
  60. package/dist/types-chunks/{bash-prefix-extractor.d-BkIA8Wto.d.ts → bash-prefix-extractor.d-YoiZl5yt.d.ts} +15 -5
  61. package/dist/types-chunks/{capability-learning.d-WtsyRv3O.d.ts → capability-learning.d-_lR0J4iR.d.ts} +1 -1
  62. package/dist/types-chunks/{capability.d-3C62G8Eq.d.ts → capability.d-K664nHOS.d.ts} +21 -6
  63. package/dist/types-chunks/{capsule.d-xJvfh4YR.d.ts → capsule.d-CK5Pfz1k.d.ts} +3 -3
  64. package/dist/types-chunks/{commands.d-DkPfcQWG.d.ts → commands.d-D6g2jq8V.d.ts} +4 -4
  65. package/dist/types-chunks/{guardrail.d-R7AiGfrI.d.ts → guardrail.d-DdeDWnVu.d.ts} +3 -3
  66. package/dist/types-chunks/{guardrail.d-6ZDbNbHO.d.ts → guardrail.d-DilYC1dh.d.ts} +1 -1
  67. package/dist/types-chunks/{manager.d-CoEuPRAo.d.ts → manager.d-B67TmPO1.d.ts} +27 -3
  68. package/dist/types-chunks/{public-api.d-7G4--RZE.d.ts → public-api.d-BvCp5VkW.d.ts} +3 -3
  69. package/dist/types-chunks/{resolver.d-CIVoGc97.d.ts → resolver.d-ssgNSlrh.d.ts} +2 -2
  70. package/dist/types-chunks/{run-manager.d-BaXtkryp.d.ts → run-manager.d-DxU3SSGR.d.ts} +1 -1
  71. package/dist/types-chunks/{sdk-session-BQccrODn.d.ts → sdk-session-wGpa7X_U.d.ts} +4 -4
  72. package/dist/types-chunks/{types.d-CJR7t6iW.d.ts → types.d-CEZZSY9s.d.ts} +2 -2
  73. package/dist/types-chunks/{types.d-BtC4yLYO.d.ts → types.d-CRCaLt_s.d.ts} +55 -7
  74. package/dist/types-chunks/{types.d-TTvpAGWf.d.ts → types.d-Cqaw71Ax.d.ts} +4 -2
  75. package/dist/types-chunks/{types.d-C9YHEAmA.d.ts → types.d-CzsLFqmf.d.ts} +5 -5
  76. package/dist/types-chunks/{utils.d-BbB5jzi1.d.ts → utils.d-Cb6vdvFM.d.ts} +5 -5
  77. package/docs/SDK_EMBEDDER_GUIDE.md +357 -55
  78. package/package.json +5 -2
  79. package/dist/chunks/argument-completer-RSK6CCGP.js +0 -2
  80. package/dist/chunks/chunk-NGHQIGVW.js +0 -46
  81. package/dist/chunks/chunk-PQ3XUSY2.js +0 -357
  82. package/dist/chunks/chunk-UJEMSPM5.js +0 -78
  83. package/dist/chunks/chunk-VYKFM3TB.js +0 -751
  84. package/dist/chunks/chunk-Z4PPLJF2.js +0 -60
  85. package/dist/chunks/compaction-config-ABLL6UH3.js +0 -2
  86. package/dist/chunks/host-7SAB4WEC.js +0 -2
  87. package/dist/chunks/run-manager-XMDKTONJ.js +0 -2
@@ -1296,7 +1296,7 @@ import {
1296
1296
  ```ts
1297
1297
  interface KodaXModelCapabilities {
1298
1298
  provider: string; // 'anthropic' | 'kimi' | 'ark-coding' | <custom-name>
1299
- model: string; // model id (e.g. 'claude-sonnet-4-6', 'kimi-k2.6')
1299
+ model: string; // model id (e.g. 'claude-sonnet-4-6', 'kimi-k2.7-code')
1300
1300
  displayName: string; // human label — falls back to model id
1301
1301
  supportsThinking: boolean; // native reasoning is available?
1302
1302
  reasoningCapability: 'native-budget' | 'native-effort' | 'native-toggle' | 'prompt-only' | 'none' | 'unknown'; // legacy mechanism label
@@ -1328,8 +1328,8 @@ for (const caps of listAllModelCapabilities()) {
1328
1328
  ```ts
1329
1329
  import { resolveModelCapabilities } from '@kodax-ai/kodax/llm';
1330
1330
 
1331
- const caps = resolveModelCapabilities('kimi', 'kimi-k2.6');
1332
- // => { contextWindow: 256_000, supportsThinking: true, reasoningProfile: { defaultEffort: 'high', ... }, ... }
1331
+ const caps = resolveModelCapabilities('kimi', 'kimi-k2.7-code');
1332
+ // => { contextWindow: 262_144, supportsThinking: true, reasoningProfile: { defaultEffort: 'high', ... }, ... }
1333
1333
  ```
1334
1334
 
1335
1335
  For picker/status UIs, use `reasoningProfile.supportedEfforts` and
@@ -1466,11 +1466,11 @@ and loaded into the in-memory `KODAX_PROVIDER_SNAPSHOTS` export. When upstream
1466
1466
  providers publish a new model or change a context-window cap, the JSON file is
1467
1467
  the patch site — the new value flows to runtime (via `buildProviderConfig`) AND
1468
1468
  to SDK consumers (via the getters) in a single edit. The current snapshot is
1469
- dated 2026-06-14 and includes the GPT-5.4, Kimi K2.7 Code, GLM-5.2, MiniMax
1469
+ dated 2026-07-16 and includes the GPT-5.4, Kimi K2.7 Code / HighSpeed, GLM-5.2, MiniMax
1470
1470
  M3/M2.7, DeepSeek V4, and Doubao Seed 2.0 route refreshes where supported. The
1471
1471
  test suite at
1472
1472
  [`packages/llm/src/providers/model-capabilities.test.ts`](../packages/llm/src/providers/model-capabilities.test.ts)
1473
- locks in specific values (e.g. kimi-k2.6 at 256K, deepseek-v4-pro at 1M)
1473
+ locks in specific values (e.g. the public Kimi lineup at 262,144 tokens, deepseek-v4-pro at 1M)
1474
1474
  so accidental drift is caught at PR time.
1475
1475
 
1476
1476
  The probe scripts that surveyed upstream APIs live at
@@ -1954,7 +1954,7 @@ Cli-bridge providers (`gemini-cli`, `codex-cli`) return their CLI binary's known
1954
1954
 
1955
1955
  ### Reference
1956
1956
 
1957
- - Source: `packages/llm/src/providers/verify-credential.ts` (orchestrator + classifier) + `verify-credential.test.ts` (19 unit tests) + `verify-credential-integration.test.ts` (10 real-key tests, gated on `KODAX_INTEGRATION_TEST=1`).
1957
+ - Source: `packages/llm/src/providers/verify-credential.ts` (orchestrator + classifier) + `verify-credential.test.ts` (27 unit tests) + `verify-credential-integration.test.ts` (12 gated real-key/fake-key tests, enabled by `KODAX_INTEGRATION_TEST=1`).
1958
1958
  - Data: `packages/llm/src/providers/provider-capabilities.json` `verifyStrategy` field per provider.
1959
1959
  - Design notes + probe matrix: [docs/features/v0.7.45.md FEATURE_216](features/v0.7.45.md#feature_216-provider-credential-verification-api).
1960
1960
 
@@ -2348,7 +2348,7 @@ The important creation options are:
2348
2348
  | `worker.resourceLimits` | unset | Optional V8 heap/stack limits; requires `isolation: 'worker'`. |
2349
2349
  | `worker.shutdownTimeoutMs` | `2000` | Grace before the parent terminates the Runtime Worker. |
2350
2350
  | `requirements.hardDispose` | `false` | Rejects inline and daemon forms; prevents an accidental weaker ownership form. |
2351
- | `homeDir` | OS user home | Root for `.kodax` config/state and default sessions. Use a private value in tests. |
2351
+ | `homeDir` | unset | When omitted, use the exact resolved `KODAX_HOME`. When set, this is the base directory that owns `.kodax`, with the same meaning as CLI `daemon --home`; daemon state/config live under `<homeDir>/.kodax`. |
2352
2352
  | `profile` | `'default'` | Daemon uniqueness and runtime configuration namespace. |
2353
2353
  | `sessionsDir` | `<homeDir>/.kodax/sessions` | Explicit session storage override. |
2354
2354
  | `daemonStartupTimeoutMs` | `60000` | Total cold-start/concurrent-owner wait budget. |
@@ -2429,10 +2429,20 @@ no explicit `daemonEndpoint` or `daemonTransport` is supplied it starts or reuse
2429
2429
  the local profile daemon. `connectKodaXRuntime()` is attach-only unless
2430
2430
  `autoStart: true` is passed.
2431
2431
 
2432
- SDK auto-start allows `daemonStartupTimeoutMs` (default 60 seconds) and
2433
- `daemonConnectTimeoutMs`. The longer startup budget covers cold machines and
2434
- concurrent test/desktop startup without weakening PID, endpoint, token, or
2435
- runtime-identity validation.
2432
+ SDK auto-start allows `daemonStartupTimeoutMs` (default 60 seconds) and
2433
+ `daemonConnectTimeoutMs`. The longer startup budget covers cold machines and
2434
+ concurrent test/desktop startup without weakening PID, endpoint, token, or
2435
+ runtime-identity validation.
2436
+
2437
+ `homeDir` and `KODAX_HOME` deliberately name different levels. Runtime SDK and
2438
+ CLI daemon `--home` accept the **base directory that contains `.kodax`**;
2439
+ lower-level `KODAX_HOME` points at the **data directory itself** and need not be
2440
+ named `.kodax`. To share the default CLI daemon, omit `homeDir`; this honors the
2441
+ exact resolved `KODAX_HOME`. Passing `os.homedir()` explicitly instead selects
2442
+ `<os.homedir()>/.kodax`, regardless of an ambient custom `KODAX_HOME`. For an
2443
+ isolated embedder namespace, pass a private base directory and expect data at
2444
+ `<homeDir>/.kodax`. Passing `~/.kodax` as `homeDir` would instead select
2445
+ `~/.kodax/.kodax` and a different daemon namespace.
2436
2446
 
2437
2447
  ### Worker-hosted embedded usage
2438
2448
 
@@ -2575,7 +2585,7 @@ Every `KodaXRuntime` exposes the same service set in inline, Worker, and daemon
2575
2585
  | `artifacts` | Create/get/delete runtime artifact references for file/image/video inputs. |
2576
2586
  | `status` | Runtime snapshot with sessions, runs, permissions, workflows, and daemon counters. |
2577
2587
  | `diagnostics` | Latest context-budget and tool-exposure decisions for GUI/debug surfaces. |
2578
- | `admin.agentRegistrations` | List/upsert/remove redacted external-agent registrations. With no plane, list is empty and mutations fail clearly. |
2588
+ | `admin.agentRegistrations` | List/upsert, atomically set `enabled` while preserving the full registration, or remove redacted external-agent registrations. Owner/revision-conditional mutation prevents a stale manager from changing a same-ID replacement. With no plane, list is empty and mutations fail clearly. |
2579
2589
  | `agents` | Check `enabled`, list/describe policy-filtered dispatchable agents, and preflight a selected route. |
2580
2590
  | `agentTasks` | Start/list/get/wait/continue/cancel/reconcile durable external-agent tasks and read their ordered event stream. |
2581
2591
 
@@ -2915,7 +2925,7 @@ fail clearly. Set
2915
2925
 
2916
2926
  | Surface | Methods | Contract |
2917
2927
  |---|---|---|
2918
- | `runtime.admin.agentRegistrations` | `list`, `upsert`, `remove` | Durable owner configuration. List results expose `credentialConfigured`, never a credential value. With no plane, `list()` is empty and mutations fail clearly. |
2928
+ | `runtime.admin.agentRegistrations` | `list`, `upsert`, `setEnabled`, `remove` | Durable owner configuration. `setEnabled` preserves the complete captured executor registration while changing admission. Mutations accept both `expectedConfigurationRevision` and `expectedManagementOwner`; `setEnabled` can also atomically `claimOwner` on an unowned registration and rejects another owner. List results expose `managementOwner` and `credentialConfigured`, never a credential value. The same contract is carried across the daemon transport. With no plane, `list()` is empty and mutations fail clearly. |
2919
2929
  | `runtime.agents` | `enabled`, `listDispatchable`, `describe`, `preflight` | Applies health, capability, effect, concurrency, credential-presence, configuration-revision, and host-policy checks before dispatch. |
2920
2930
  | `runtime.agentTasks` | `start`, `list`, `get`, `events`, `wait`, `sendInput`, `cancel`, `reconcile` | Durable snapshots and append-only events for external tasks. The task keeps the immutable registration/executor binding captured at start. |
2921
2931
 
@@ -2926,12 +2936,34 @@ is valid only while the task reports `input-required` or `auth-required`.
2926
2936
  `reconcile()` asks the bound executor for authoritative remote state after an
2927
2937
  owner restart or uncertain failure.
2928
2938
 
2939
+ For external tasks, the built-in stores persist an internal full registration
2940
+ snapshot before the public task ledger. It is keyed by Agent ID and revision,
2941
+ is never returned by task or daemon APIs, and lets an admitted task keep using
2942
+ its original executor route after registration update/removal and Runtime
2943
+ restart. The internal form fixes `enabled: true` and omits management ownership
2944
+ and health diagnostics. The task's public route summary is validated against that internal
2945
+ snapshot before recovery. Terminal task state is durable before the last
2946
+ unreferenced snapshot is removed; startup cleans crash-window orphans.
2947
+
2948
+ Custom `AgentExecutorPlaneStore` implementations should implement
2949
+ `loadTaskRegistrationSnapshots()` and `saveTaskRegistrationSnapshots()` as a
2950
+ pair and give one Runtime exclusive write ownership of that store. Omitting
2951
+ both remains compatible, but restart recovery then succeeds only while the
2952
+ exact current registration still exists. Store only non-secret executor config
2953
+ or secret references in `executorConfig`/`credentialRef`; the broker resolves
2954
+ the current referenced credential just in time, so removing a registration is
2955
+ not equivalent to revoking that credential at its issuer.
2956
+
2929
2957
  The owner plane has a terminal close contract. Closing it rejects every pending
2930
2958
  `wait()` (including a wait without `timeoutMs`), disposes its executor instances,
2931
2959
  and makes subsequent registration, catalog, preflight, and task calls reject
2932
- with `Agent executor plane is closed.` Repeated `close()` calls are safe. SDK
2933
- hosts should stop accepting work before closing the owner and must not retain a
2934
- plane service as a reusable handle after Runtime shutdown.
2960
+ with `Agent executor plane is closed.` One overall deadline covers admitted
2961
+ work plus executor disposal: the default is 30 seconds, and direct
2962
+ `createAgentExecutorPlane()` hosts may supply a positive finite
2963
+ `closeTimeoutMs`. A timeout rejects visibly even though already-admitted cleanup
2964
+ may finish in the background. Repeated `close()` calls are safe. SDK hosts
2965
+ should stop accepting work before closing the owner and must not retain a plane
2966
+ service as a reusable handle after Runtime shutdown.
2935
2967
 
2936
2968
  Restricted Workflow scripts use the same route as direct SDK calls. Both
2937
2969
  `wf.spawnAgent()` and `wf.runAgent()` validate and forward
@@ -2948,8 +2980,18 @@ silently falling back to the native child backend.
2948
2980
  each artifact before it materializes in the host boundary.
2949
2981
  - External agents may declare workspace effect `none` or `proposal`; direct
2950
2982
  workspace mutation is intentionally not a valid external registration.
2951
- - Use `expectedConfigurationRevision` to prevent dispatching against a stale
2952
- endpoint/configuration seen by an earlier catalog read.
2983
+ - Use `expectedConfigurationRevision` for dispatch. For registration mutations,
2984
+ compare both it and `expectedManagementOwner` so a same-revision ownership
2985
+ change cannot be overwritten from an earlier catalog read. Config managers
2986
+ should set a stable `managementOwner`; they may atomically claim an unowned
2987
+ legacy registration while disabling it, but must not mutate a registration
2988
+ owned by another manager.
2989
+ - Treat `configurationRevision` as the stable identity of immutable execution
2990
+ content, not as a small counter. The same content may deterministically reuse
2991
+ the same revision across remove/re-add or Runtime restart, but different
2992
+ endpoint, protocol, executor/auth config, capabilities, effects, Skills,
2993
+ modalities, or resource limits must never reuse it. Built-in A2A
2994
+ configuration derives it from content.
2953
2995
  - A remote start followed by uncertain local persistence is recorded as
2954
2996
  `unknown` with its executor reference preserved; reconcile it rather than
2955
2997
  blindly starting a duplicate. Stable idempotency keys protect retries.
@@ -3208,6 +3250,24 @@ time/body/redirects, and strips authorization on a cross-origin redirect. A
3208
3250
  custom `fetch` option is a trusted transport override: the embedder then owns
3209
3251
  equivalent DNS-to-connection binding in that transport or proxy.
3210
3252
 
3253
+ The selected interface must remain on the Card's trusted origin. KodaX parses
3254
+ typed Card-level and Skill-level security declarations: requirement objects are
3255
+ alternatives (OR), every scheme inside one object is conjunctive (AND), and an
3256
+ empty requirement is anonymous. A configured credential is used only when one
3257
+ complete requirement is satisfiable; protected Skills that the configured
3258
+ profile cannot satisfy are not advertised to the Runtime catalog.
3259
+
3260
+ The built-in profiles are HTTP Bearer and OAuth 2.0 Client Credentials. The
3261
+ OAuth profile pins the Card scheme, issuer, exact token endpoint, client ID,
3262
+ secret reference, scopes, optional RFC 8707 resource, and client authentication
3263
+ method. The external Authorization Server—not the Agent and not KodaX—issues
3264
+ the access token. KodaX resolves the client secret only for refresh, keeps an
3265
+ expiring token in process memory, coalesces refreshes, and retries one RPC once
3266
+ with a fresh token after `401`. Card, Agent RPC, and token endpoints remain
3267
+ separate safe-fetch trust boundaries, so a remote Agent cannot redirect a task
3268
+ payload to the token origin. API key, Basic, interactive OAuth, OIDC, mTLS, and
3269
+ multi-scheme AND requirements fail explicitly in the built-in client.
3270
+
3211
3271
  ```ts
3212
3272
  import {
3213
3273
  createA2AAgentExecutorFactory,
@@ -3217,7 +3277,8 @@ import { createKodaXRuntime } from '@kodax-ai/kodax/runtime';
3217
3277
 
3218
3278
  const client = {
3219
3279
  networkPolicy: {
3220
- allowedOrigins: ['https://reviewer.example'],
3280
+ // Card/RPC and OAuth token endpoints are separate trust boundaries.
3281
+ allowedOrigins: ['https://reviewer.example', 'https://identity.example'],
3221
3282
  allowPrivateAddresses: false,
3222
3283
  requestTimeoutMs: 10_000,
3223
3284
  maxResponseBytes: 1_048_576,
@@ -3240,8 +3301,13 @@ const runtime = await createKodaXRuntime({
3240
3301
  factories: [createA2AAgentExecutorFactory(client)],
3241
3302
  credentialBroker: {
3242
3303
  async withCredential(ref, use) {
3243
- if (ref !== 'a2a/reviewer') throw new Error('Unknown credential reference.');
3244
- return use(process.env.A2A_REVIEWER_TOKEN ?? '');
3304
+ const value = ref === 'a2a/reviewer'
3305
+ ? process.env.A2A_REVIEWER_TOKEN
3306
+ : ref === 'a2a/reviewer-client-secret'
3307
+ ? process.env.A2A_REVIEWER_CLIENT_SECRET
3308
+ : undefined;
3309
+ if (!value) throw new Error(`Missing credential for reference: ${ref}.`);
3310
+ return use(value);
3245
3311
  },
3246
3312
  },
3247
3313
  policy: ({ registration }) => ({ allowed: registration.effects.remote === 'read' }),
@@ -3260,10 +3326,40 @@ const started = await runtime.agentTasks.start({
3260
3326
  const terminal = await runtime.agentTasks.wait(started.taskId, 60_000);
3261
3327
  ```
3262
3328
 
3329
+ For OAuth, replace the legacy `credentialRef` input with the structured form;
3330
+ the same F258 `credentialBroker` must resolve `clientSecretRef`. The shared
3331
+ network policy must admit both origins, while each Card, RPC, and token request
3332
+ is still narrowed to its own exact origin:
3333
+
3334
+ ```ts
3335
+ const discovered = await discoverA2ARegistration({
3336
+ agentId: 'external:a2a-reviewer',
3337
+ agentCardUrl: 'https://reviewer.example/.well-known/agent-card.json',
3338
+ authentication: {
3339
+ type: 'oauth2-client-credentials',
3340
+ scheme: 'enterprise-oauth',
3341
+ issuer: 'https://identity.example/',
3342
+ tokenUrl: 'https://identity.example/oauth/token',
3343
+ clientId: 'kodax-reviewer',
3344
+ clientSecretRef: 'a2a/reviewer-client-secret',
3345
+ scopes: ['a2a.invoke'],
3346
+ resource: 'https://reviewer.example/',
3347
+ clientAuthentication: 'client-secret-basic',
3348
+ },
3349
+ effects: { remote: 'read' },
3350
+ }, client);
3351
+ ```
3352
+
3263
3353
  The executor supports durable task start/get, input continuation, cancel,
3264
3354
  reconcile, SSE events, and polling fallback. An ambiguous start is not retried
3265
3355
  automatically. A `credentialRef` is resolved just in time by the F258 broker;
3266
3356
  the registration, task store, and diagnostics never contain the credential.
3357
+ Authenticated SSE uses that same broker. JSON-RPC ID/version and task/context
3358
+ correlation are validated before an event is accepted; if a stream ends
3359
+ normally before a terminal snapshot, the executor resumes bounded polling.
3360
+ Streamed `artifactUpdate` chunks are accumulated by artifact ID according to
3361
+ `append`, and direct Message file Parts are preserved as authorized artifact
3362
+ references.
3267
3363
 
3268
3364
  ### Built-in configured path (no host code)
3269
3365
 
@@ -3277,6 +3373,24 @@ kodax a2a test reviewer
3277
3373
  kodax a2a call reviewer "Review this document"
3278
3374
  ```
3279
3375
 
3376
+ The no-code OAuth path stores only the environment-variable name for the client
3377
+ secret. It can be staged disabled and hot-activated later:
3378
+
3379
+ ```bash
3380
+ export A2A_REVIEWER_CLIENT_SECRET='provisioned-out-of-band'
3381
+ # PowerShell: $env:A2A_REVIEWER_CLIENT_SECRET='provisioned-out-of-band'
3382
+ # PowerShell: use one line or replace each trailing \ with a backtick.
3383
+ kodax a2a add reviewer https://reviewer.example/.well-known/agent-card.json \
3384
+ --disabled --effect read --oauth-scheme enterprise-oauth \
3385
+ --oauth-issuer https://identity.example/ \
3386
+ --oauth-token-url https://identity.example/oauth/token \
3387
+ --oauth-client-id kodax-reviewer \
3388
+ --oauth-client-secret-env A2A_REVIEWER_CLIENT_SECRET \
3389
+ --oauth-scope a2a.invoke --oauth-resource https://reviewer.example/
3390
+ kodax a2a enable reviewer
3391
+ kodax a2a disable reviewer
3392
+ ```
3393
+
3280
3394
  Embedded CLI Runtimes and the user-owned daemon automatically reconcile these
3281
3395
  entries as `external:<name>`. Discovery/update failure retains that entry's
3282
3396
  last-known-good registration; another entry can still update. The environment
@@ -3284,15 +3398,113 @@ broker resolves `credentialEnv` only at call time. Automatic Runtime
3284
3398
  registration accepts public HTTPS and exact loopback targets; explicit private
3285
3399
  network access remains an operator action on the direct CLI/SDK path.
3286
3400
 
3401
+ `enabled` is desired state in `a2a.json`, not a fabricated cross-process live
3402
+ flag. `a2a list` reports configured entries and that desired state. The owning
3403
+ Runtime's `admin.agentRegistrations.list()` is authoritative for applied
3404
+ registrations. Automatic reconciliation handles disables/removals first,
3405
+ skips unchanged peers, performs no Card or token request for disabled entries,
3406
+ and rediscovers before re-enable. Once the owning Runtime observes and applies
3407
+ the revision, disable blocks all new starts, including an explicit
3408
+ `external:<name>` target, but does not cancel or break an already admitted task.
3409
+ The CLI mutation returning is not cross-process acknowledgement. A failed
3410
+ activation remains retryable through the owning
3411
+ `ConfiguredA2ARuntimeHandle.reload()` even when the disk revision is unchanged;
3412
+ the passive `kodax integrations reload` command validates only its own process.
3413
+
3414
+ `kodax a2a test` performs Card discovery and security planning only. It never
3415
+ requests an OAuth access token; token acquisition starts at `a2a call` or the
3416
+ first Runtime dispatch.
3417
+
3287
3418
  Inbound publication is also no-code:
3288
3419
 
3289
3420
  ```bash
3290
3421
  export KODAX_A2A_TOKEN='replace-with-a-long-random-token'
3422
+ # PowerShell: $env:KODAX_A2A_TOKEN='replace-with-a-long-random-token'
3291
3423
  kodax a2a expose # Runtime default Agent
3292
3424
  kodax a2a expose document-agent # ~/.kodax/agents/document-agent.md
3293
3425
  kodax a2a serve --port 8765
3294
3426
  ```
3295
3427
 
3428
+ The fixed token above is the compatibility profile. For dynamic production
3429
+ tokens, configure KodaX as an OAuth Resource Server and point it at an external
3430
+ issuer:
3431
+
3432
+ ```bash
3433
+ kodax a2a expose document-agent --auth oauth2-jwt \
3434
+ --oauth-scheme enterprise-oauth \
3435
+ --oauth-issuer https://identity.example/ \
3436
+ --oauth-audience https://kodax.example/a2a \
3437
+ --oauth-jwks-url https://identity.example/.well-known/jwks.json \
3438
+ --oauth-token-url https://identity.example/oauth/token \
3439
+ --oauth-metadata-url https://identity.example/.well-known/oauth-authorization-server \
3440
+ --required-scope a2a.invoke
3441
+ kodax a2a serve --port 8765
3442
+ ```
3443
+
3444
+ The Authorization Server authenticates clients, provisions client IDs/secrets,
3445
+ issues/rotates/revokes tokens and, for JWT access tokens, signs them and
3446
+ publishes metadata/JWKS. The calling A2A
3447
+ client obtains a token out of band or with Client Credentials and sends it in
3448
+ the Bearer header. KodaX validates JWT type, asymmetric signature, issuer,
3449
+ audience, lifetime, subject, and required scopes before task lookup, then maps
3450
+ `sub` to the A2A principal. Missing/invalid credentials return `401`; a valid
3451
+ token without the required scope returns `403 insufficient_scope`. KodaX does
3452
+ not hold the issuer signing key or expose token, refresh, client-registration,
3453
+ login, or consent endpoints. Opaque-token introspection and mTLS deployments
3454
+ must use a host authentication adapter or reverse proxy. Offline JWT/JWKS
3455
+ validation also cannot observe immediate per-token revocation: use short access
3456
+ token lifetimes, signing-key rotation, or an introspecting proxy/adapter when
3457
+ that property is required.
3458
+
3459
+ #### Upgrade retained pre-realm tasks
3460
+
3461
+ Realm-aware task ownership intentionally has no normal-request legacy fallback:
3462
+ an authority switch must never adopt tasks merely because it reuses a subject.
3463
+ If a v0.7.70 task store must remain addressable after upgrading, stop the A2A
3464
+ server and first inspect an exact-owner migration plan:
3465
+
3466
+ ```bash
3467
+ kodax a2a migrate-tasks
3468
+ kodax a2a migrate-tasks --apply --confirm-server-stopped
3469
+
3470
+ # OAuth identity is token-specific, so provide the known historical subject.
3471
+ kodax a2a migrate-tasks --subject trusted-orchestrator
3472
+ ```
3473
+
3474
+ The configured Bearer profile supplies its fixed `principalId`; OAuth requires
3475
+ `--subject`. Dry-run does not rewrite `tasks.json`. Apply rekeys only exact
3476
+ matches, preserves unmatched records, and refuses a live task-store owner.
3477
+ Custom SDK hosts can plan multiple known owners without exposing raw tokens:
3478
+
3479
+ ```ts
3480
+ import { migrateA2ALegacyTaskOwners } from '@kodax-ai/kodax/a2a';
3481
+
3482
+ const mappings = [{
3483
+ securityRealm: 'oauth2-jwt:https://identity.example/',
3484
+ subject: 'trusted-orchestrator',
3485
+ }] as const;
3486
+ const plan = migrateA2ALegacyTaskOwners({
3487
+ dataDir: '/var/lib/kodax/a2a', mappings, apply: false,
3488
+ });
3489
+
3490
+ // After the host/operator verifies the plan:
3491
+ if (plan.matchedLegacyTaskCount > 0) {
3492
+ migrateA2ALegacyTaskOwners({
3493
+ dataDir: '/var/lib/kodax/a2a', mappings, apply: true,
3494
+ });
3495
+ }
3496
+ ```
3497
+
3498
+ The SDK also accepts `tenant` when a custom authentication adapter historically
3499
+ returned one. Two mappings that claim the same legacy owner for different
3500
+ realms are ambiguous and rejected; split or guessed ownership is never applied.
3501
+
3502
+ `a2a serve` resolves its Runtime provider in this order: explicit CLI option,
3503
+ environment, core configuration, then the built-in default. Provider-compatible
3504
+ model selection follows the normal hosted Runtime rule. A selected Markdown
3505
+ Agent may declare its own validated `provider`; remote A2A input cannot choose
3506
+ or override provider, model, reasoning, profile, workspace, or tools.
3507
+
3296
3508
  `expose` validates a named user Markdown Agent before writing its reference.
3297
3509
  `serve` loads configured MCP and Extensions before it resolves the execution
3298
3510
  binding or opens a socket. Native workspace read tools are admitted by
@@ -3305,10 +3517,14 @@ projection and never reveal the private Skill inventory.
3305
3517
  The running server pins Agent, Skill, workspace, tool registration, process and
3306
3518
  store revisions. Card/auth/limits can hot reload; execution-authority changes
3307
3519
  require an explicit restart. Managed contexts live below
3308
- `~/kodax_a2a_server_workspace/<profile>/contexts/`. Exact Skill scripts require
3520
+ `~/kodax_a2a_server_workspace/<runtime-profile>/contexts/<context-key>/`. Exact Skill scripts require
3309
3521
  `process: isolated`, an admitted `scripts/...` path, and a passing
3310
3522
  `kodax sandbox doctor`; KodaX never falls back to an unsandboxed shell.
3311
3523
 
3524
+ Every concrete file reached by `read`, `grep`, or `glob` is checked against the
3525
+ bound workspace. Child runs inherit ceilings for native reads, tools, Skills,
3526
+ and Skill scripts; they cannot expand the parent's admitted authority.
3527
+
3312
3528
  ### Publish one KodaX Agent
3313
3529
 
3314
3530
  Publication is host-owned and opt-in. The public card describes only the
@@ -3316,7 +3532,10 @@ configured Agent, media types, and skills. Authentication runs before task
3316
3532
  lookup; authorization runs per operation; task visibility is principal-scoped.
3317
3533
 
3318
3534
  ```ts
3319
- import { createKodaXA2AServer } from '@kodax-ai/kodax/a2a';
3535
+ import {
3536
+ createBearerEnvA2AAuthentication,
3537
+ createKodaXA2AServer,
3538
+ } from '@kodax-ai/kodax/a2a';
3320
3539
  import { createKodaXRuntime } from '@kodax-ai/kodax/runtime';
3321
3540
 
3322
3541
  const runtime = await createKodaXRuntime({ mode: 'embedded', isolation: 'inline' });
@@ -3332,16 +3551,12 @@ const server = createKodaXA2AServer({
3332
3551
  inputModes: ['text/plain'],
3333
3552
  outputModes: ['text/plain'],
3334
3553
  },
3335
- authentication: {
3336
- securitySchemes: { bearer: { httpAuthSecurityScheme: { scheme: 'Bearer' } } },
3337
- securityRequirements: [{ schemes: { bearer: { list: [] } } }],
3338
- async authenticate(request) {
3339
- return request.headers.get('authorization') === `Bearer ${process.env.KODAX_A2A_TOKEN}`
3340
- ? { subject: 'trusted-orchestrator', scopes: ['a2a'] }
3341
- : null;
3342
- },
3343
- },
3344
- async authorize({ principal }) { return principal.scopes.includes('a2a'); },
3554
+ authentication: createBearerEnvA2AAuthentication({
3555
+ type: 'bearer-env',
3556
+ tokenEnv: 'KODAX_A2A_TOKEN',
3557
+ principalId: 'trusted-orchestrator',
3558
+ }),
3559
+ async authorize({ principal }) { return principal.scopes.includes('a2a:invoke'); },
3345
3560
  limits: {
3346
3561
  maxRequestBytes: 1_048_576,
3347
3562
  maxPartBytes: 524_288,
@@ -3359,9 +3574,9 @@ const server = createKodaXA2AServer({
3359
3574
  const localBaseUrl = await server.listen({ hostname: '127.0.0.1', port: 0 });
3360
3575
  ```
3361
3576
 
3362
- Production hosts route `GET /.well-known/agent-card.json` and JSON-RPC `POST /`
3363
- to `server.handle(request)` behind their own TLS terminator. `POST /a2a` remains
3364
- an accepted compatibility alias. `listen()` waits for durable recovery before
3577
+ Production hosts route `GET /.well-known/agent-card.json` and canonical
3578
+ JSON-RPC `POST /a2a` to `server.handle(request)` behind their own TLS
3579
+ terminator. `POST /` remains an accepted compatibility alias. `listen()` waits for durable recovery before
3365
3580
  it resolves. A host that wires `handle()` directly may explicitly await
3366
3581
  `server.whenReady()` before it starts accepting traffic; `handle()` also waits
3367
3582
  for the same recovery promise. The durable edge store supports get/list,
@@ -3375,12 +3590,28 @@ default). When that bound is reached the response contains the current working
3375
3590
  task; it does not cancel the Runtime run, and clients can continue with
3376
3591
  `GetTask` or `SubscribeToTask`.
3377
3592
 
3593
+ When a task enters `INPUT_REQUIRED`, the next accepted input answers the pending
3594
+ interaction on the original Runtime run; it does not start a replacement run.
3595
+ History length and list filters are validated and bounded. Task listing uses a
3596
+ stable opaque cursor, while per-principal retention prunes only the oldest
3597
+ terminal records. Terminal subscriptions and failed-start resources are closed
3598
+ by their owning lifecycle.
3599
+
3378
3600
  Remote messages are ordinary user inputs. They cannot select provider, model,
3379
3601
  profile, tools, working directory, permission mode, or Runtime configuration.
3380
3602
  URL parts are rejected; inline raw/data parts are bounded and materialized under
3381
3603
  the server-owned data directory. Responses expose final approved output only,
3382
3604
  not system prompts, reasoning deltas, tool payloads, credentials, or local paths.
3383
3605
 
3606
+ Generated files are published only through the trusted output broker: a normal
3607
+ tool or Extension stages a file in the context's `.kodax-a2a-staging` area, or
3608
+ a successfully admitted `run_skill_script` promotes one of its declared
3609
+ outputs. The server rechecks that the result is a regular non-symlink file in
3610
+ the real bound workspace and applies part-size/output-mode limits before
3611
+ inlining it. A declaration from a failed Skill run, an ordinary `write`/`edit`
3612
+ elsewhere in the workspace, and a local path in model text never become A2A
3613
+ artifacts implicitly.
3614
+
3384
3615
  The normative baseline is A2A repository commit
3385
3616
  `2183794bfb9b67af4aee1be0a0ef726050642873`, protocol `1.0`, with
3386
3617
  `specification/a2a.proto` SHA-256
@@ -3410,12 +3641,28 @@ from renderer or model output. `connectKodaXRuntime()` is attach-only unless `au
3410
3641
  An explicit inline rollback policy blocks auto-start until the owner policy is
3411
3642
  explicitly changed back to daemon.
3412
3643
 
3644
+ For Electron, `homeDir` is still the CLI-style base directory, not
3645
+ `process.env.KODAX_HOME`. Packaged/asar applications may use `autoStart: true`
3646
+ directly; the SDK launches only the daemon child in Electron's Node execution
3647
+ mode and does not mutate the application's environment or start a second GUI
3648
+ instance. `ELECTRON_RUN_AS_NODE` exists only at the child exec boundary and is
3649
+ removed before daemon application code loads, so Bash, MCP, LSP, sandboxed
3650
+ commands, and ordinary external processes do not inherit Electron Node mode.
3651
+
3652
+ Packaged auto-start requires Electron's `RunAsNode` fuse, which Electron enables
3653
+ by default. If an embedder deliberately disables that fuse, the packaged
3654
+ executable cannot serve as a detached Node host: start the daemon with an
3655
+ ordinary Node/CLI process and use attach-only mode instead. A packaged
3656
+ `autoStart: true` timeout includes this fuse requirement in its diagnostic; the
3657
+ SDK does not relaunch the GUI or silently fall back to an inline Runtime.
3658
+
3413
3659
  ```ts
3414
3660
  import { connectKodaXRuntime } from '@kodax-ai/kodax/runtime';
3415
3661
 
3416
3662
  const runtime = await connectKodaXRuntime({
3417
3663
  profile: 'coder',
3418
3664
  autoStart: true,
3665
+ homeDir: coderRuntimeBaseDir, // owns <coderRuntimeBaseDir>/.kodax
3419
3666
  clientInfo: {
3420
3667
  name: 'kodax-space',
3421
3668
  version: '0.1.32',
@@ -3445,6 +3692,7 @@ const runtime = await connectKodaXRuntime({
3445
3692
  daemonSafeRunInput: 1,
3446
3693
  sharedSessionSettings: 1,
3447
3694
  durableRecoveryQueries: 1,
3695
+ daemonManagement: 1,
3448
3696
  },
3449
3697
  });
3450
3698
  ```
@@ -3532,10 +3780,13 @@ reset boundary.
3532
3780
 
3533
3781
  ### Durable mutations, stable ordering, and settings CAS
3534
3782
 
3535
- Every public control mutation uses a durable operation envelope. Credential
3536
- supply and Host Tool completion are connection-local reverse-result frames and
3537
- are deliberately excluded from the control journal so secrets/results are not
3538
- persisted. The SDK creates an operation ID for ordinary one-shot calls. A
3783
+ Every durable public control mutation uses an operation envelope. Credential
3784
+ and Host Tool register/revoke/supply/complete requests are reverse-bridge
3785
+ control frames and are deliberately excluded from the control journal so
3786
+ secrets/results are not persisted. They still enter the daemon management
3787
+ draining fence: once an atomic stop begins, they fail with typed `conflict` and
3788
+ cannot change reverse-bridge state. The SDK creates an operation ID for
3789
+ ordinary one-shot calls. A
3539
3790
  product-level retry after a lost response must reuse its own stable operation
3540
3791
  ID; changing its method, payload, resource, or authenticated principal is
3541
3792
  rejected.
@@ -3743,10 +3994,24 @@ remain valid only for embedded Runtime calls.
3743
3994
 
3744
3995
  ### Recovery queries and stop preflight
3745
3996
 
3746
- `runtime.status.preflight()` returns connected-client count, active and queued
3747
- runs, pending AskUser/permission records, blockers, and `canStop`. Treat this as
3748
- a required check before stop or inline rollback; the query does not stop the
3749
- daemon by itself. `runtime.operations.get()` reconciles durable mutations,
3997
+ `runtime.status.preflight()` returns the initialized logical-client count,
3998
+ active and queued runs, running/paused Workflows, every non-terminal External
3999
+ Agent task (including `unknown`), pending AskUser/permission records, blockers,
4000
+ and `canStop`. The background-work blockers are `active_workflows` and
4001
+ `active_agent_tasks`. The current facade counts as one; daemon self-connections
4002
+ and bounded health probes do not count. A second process changes the count to
4003
+ two, and its awaited `close()` makes the count converge back to one.
4004
+
4005
+ Preflight is useful for UI, but it is not a stop authorization token. Use
4006
+ `runtime.daemon.inspect()` to obtain one consistent management revision,
4007
+ verified owner fence, owner-policy revision, and preflight projection. Only
4008
+ `runtime.daemon.stopForInline()` atomically rechecks and commits a rollback.
4009
+ The management revision also advances when the preflight projection changes,
4010
+ so a Workflow or AgentTask lifecycle transition between inspect and commit
4011
+ invalidates the stale stop. Capability details
4012
+ `daemonManagement.backgroundWorkPreflight` and
4013
+ `daemonManagement.reverseBridgeDrainingFence` identify this complete contract.
4014
+ `runtime.operations.get()` reconciles durable mutations,
3750
4015
  `hostTools.getInvocation()` reconciles Host Tool metadata, and
3751
4016
  `permissions.listGrants()` returns the daemon-owned persistent grant set.
3752
4017
 
@@ -3758,27 +4023,64 @@ truth.
3758
4023
 
3759
4024
  ### Owner policy, rollback, and Electron boundary
3760
4025
 
3761
- Daemon and inline Coder use one profile fence. For an emergency rollback, stop
3762
- the daemon, switch the policy with revision CAS, then acquire the inline owner:
4026
+ Daemon and inline Coder use one profile fence. Do not compose
4027
+ `status.preflight()` with a low-level unconditional stop: another client or run
4028
+ can appear between those calls. The public rollback transaction gates new
4029
+ clients and mutations, rechecks the same Runtime and management/policy
4030
+ revisions, verifies there is no other client or active/queued/pending work,
4031
+ commits sticky inline policy while that daemon still owns the fence, and then
4032
+ requests shutdown.
3763
4033
 
3764
4034
  ```ts
3765
4035
  import {
3766
4036
  acquireKodaXInlineOwner,
3767
- setKodaXRuntimeOwnerMode,
4037
+ enableKodaXDaemonOwner,
4038
+ getKodaXRuntimeOwnerState,
3768
4039
  } from '@kodax-ai/kodax/runtime';
3769
4040
 
3770
- const policy = setKodaXRuntimeOwnerMode({
3771
- homeDir: kodaxHome,
3772
- profile: 'coder',
3773
- mode: 'inline',
3774
- expectedRevision: currentPolicyRevision,
4041
+ const management = await runtime.daemon.inspect();
4042
+ if (!management.preflight.canStop) {
4043
+ showRollbackBlockers(management.preflight.blockers);
4044
+ return;
4045
+ }
4046
+
4047
+ const rollback = await runtime.daemon.stopForInline({
4048
+ expectedRuntimeId: management.runtimeId,
4049
+ expectedRevision: management.revision,
4050
+ expectedOwnerPolicyRevision: management.ownerPolicy.revision,
4051
+ operation: { operationId: loadOrCreatePendingOperationId('coder-inline-rollback') },
3775
4052
  });
4053
+
4054
+ // `accepted` means inline policy is committed and shutdown is in progress.
4055
+ // Wait through the public owner-state query; never infer release from a PID.
4056
+ const shutdownDeadline = Date.now() + 30_000;
4057
+ while (getKodaXRuntimeOwnerState({ homeDir: kodaxHome, profile: 'coder' }).owner?.runtimeId
4058
+ === rollback.runtimeId) {
4059
+ if (Date.now() >= shutdownDeadline) throw new Error('Timed out waiting for daemon owner release.');
4060
+ await delay(25);
4061
+ }
4062
+ const releasedOwner = getKodaXRuntimeOwnerState({ homeDir: kodaxHome, profile: 'coder' });
4063
+ if (releasedOwner.ownerStatus !== 'unowned') {
4064
+ throw new Error('Coder profile acquired a different owner during rollback.');
4065
+ }
4066
+ await runtime.close(); // Detach only; it does not perform a second stop.
3776
4067
  const inlineOwner = acquireKodaXInlineOwner({ homeDir: kodaxHome, profile: 'coder' });
4068
+
4069
+ // Later, after the inline owner has released its fence:
4070
+ inlineOwner.close();
4071
+ const daemonPolicy = enableKodaXDaemonOwner({ homeDir: kodaxHome, profile: 'coder' });
4072
+ // daemonPolicy.revision is authoritative; no expectedRevision guess is needed.
3777
4073
  ```
3778
4074
 
3779
- The inline policy is sticky: later CLI auto-start is rejected until an explicit
3780
- CAS changes the policy back to `daemon`. Stale-owner handling validates the
3781
- owned lock/state and never kills a process merely because a PID was reused.
4075
+ Any management revision change, another logical client, active or queued run,
4076
+ running/paused Workflow, non-terminal/unknown AgentTask, pending
4077
+ AskUser/permission, or in-flight mutation returns structured `conflict`; the
4078
+ daemon remains running and policy remains unchanged. Draining also rejects
4079
+ credential and Host Tool state changes without journaling their secrets or
4080
+ results. The inline policy is sticky: later CLI auto-start is rejected until
4081
+ `enableKodaXDaemonOwner()` changes it back to `daemon`. `runtime.close()` still
4082
+ only detaches. Stale-owner handling validates the owned lock/state and never
4083
+ kills a process merely because a PID was reused.
3782
4084
 
3783
4085
  Keep all trusted objects in Electron Main: daemon token/endpoint, stable client
3784
4086
  identity, operation IDs, owner policy, keychain broker, Host Tool handlers, and