@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.
- package/CHANGELOG.md +187 -1
- package/LICENSE +158 -191
- package/README.md +100 -17
- package/README_CN.md +77 -11
- package/config-templates/integrations/a2a.example.jsonc +62 -18
- package/dist/chunks/{agent-DQRXT6M7.js → agent-X2EWQADL.js} +1 -1
- package/dist/chunks/argument-completer-BLNXDO2F.js +2 -0
- package/dist/chunks/chunk-23X6REO4.js +46 -0
- package/dist/chunks/{chunk-Y4WOTWUC.js → chunk-44QXPEEE.js} +1 -1
- package/dist/chunks/chunk-4DY756KL.js +70 -0
- package/dist/chunks/{chunk-AWMTNUDS.js → chunk-52JSZ77P.js} +1 -1
- package/dist/chunks/{chunk-I4TPQEJN.js → chunk-64NQBOSN.js} +1 -1
- package/dist/chunks/{chunk-URC6ZI6P.js → chunk-A2TFSV5M.js} +1 -1
- package/dist/chunks/{chunk-ZG4DMYBS.js → chunk-A574DBTV.js} +2 -2
- package/dist/chunks/{chunk-VAZ25MDX.js → chunk-F7KGYHJ3.js} +2 -2
- package/dist/chunks/{chunk-M7TCFYTO.js → chunk-GVTM4Z6O.js} +66 -22
- package/dist/chunks/{chunk-PA76WUBL.js → chunk-PHH4TRGF.js} +2 -2
- package/dist/chunks/chunk-PVUXONQB.js +366 -0
- package/dist/chunks/{chunk-WW2O2DEP.js → chunk-SX2WFHIS.js} +1 -1
- package/dist/chunks/{chunk-4WIODYOH.js → chunk-T7LYK53M.js} +2 -2
- package/dist/chunks/{chunk-F7C7J6IM.js → chunk-VRSVNU2Y.js} +3 -3
- package/dist/chunks/chunk-Y5XKAN7C.js +755 -0
- package/dist/chunks/{chunk-NGJURAY5.js → chunk-Z535BARK.js} +4 -4
- package/dist/chunks/chunk-ZJIMT5I3.js +78 -0
- package/dist/chunks/compaction-config-5KKKXYFT.js +2 -0
- package/dist/chunks/{construction-bootstrap-GH6RW6ST.js → construction-bootstrap-OXDDDW6O.js} +1 -1
- package/dist/chunks/{dist-DRBKYVHF.js → dist-GOUP4YVE.js} +1 -1
- package/dist/chunks/{dist-RWL2RBO4.js → dist-IXJRJ27U.js} +1 -1
- package/dist/chunks/host-VRKDAN26.js +2 -0
- package/dist/chunks/run-manager-Z3W6MDO7.js +2 -0
- package/dist/chunks/{utils-QYE5Y73D.js → utils-CFOXDCB5.js} +1 -1
- package/dist/index.d.ts +18 -18
- package/dist/index.js +6 -6
- package/dist/kodax_cli.js +1365 -1296
- package/dist/provider-capabilities.json +101 -57
- package/dist/runtime-worker.js +1224 -1156
- package/dist/sdk-a2a.d.ts +187 -22
- package/dist/sdk-a2a.js +8 -7
- package/dist/sdk-agent.d.ts +49 -20
- package/dist/sdk-agent.js +1 -1
- package/dist/sdk-coding.d.ts +33 -25
- package/dist/sdk-coding.js +1 -1
- package/dist/sdk-experimental-memory.d.ts +1 -1
- package/dist/sdk-experimental-memory.js +1 -1
- package/dist/sdk-llm.d.ts +7 -7
- package/dist/sdk-llm.js +1 -1
- package/dist/sdk-mcp.d.ts +2 -2
- package/dist/sdk-mcp.js +1 -1
- package/dist/sdk-media.d.ts +1 -1
- package/dist/sdk-media.js +1 -1
- package/dist/sdk-repl.d.ts +17 -17
- package/dist/sdk-repl.js +1 -1
- package/dist/sdk-runtime.d.ts +96 -17
- package/dist/sdk-runtime.js +1 -1
- package/dist/sdk-session.d.ts +7 -7
- package/dist/sdk-session.js +1 -1
- package/dist/sdk-skills.js +1 -1
- package/dist/semantic-worker.js +10 -10
- package/dist/types-chunks/{base.d-CYjtB68X.d.ts → base.d-Cz_rwpOi.d.ts} +2 -1
- package/dist/types-chunks/{bash-prefix-extractor.d-BkIA8Wto.d.ts → bash-prefix-extractor.d-YoiZl5yt.d.ts} +15 -5
- package/dist/types-chunks/{capability-learning.d-WtsyRv3O.d.ts → capability-learning.d-_lR0J4iR.d.ts} +1 -1
- package/dist/types-chunks/{capability.d-3C62G8Eq.d.ts → capability.d-K664nHOS.d.ts} +21 -6
- package/dist/types-chunks/{capsule.d-xJvfh4YR.d.ts → capsule.d-CK5Pfz1k.d.ts} +3 -3
- package/dist/types-chunks/{commands.d-DkPfcQWG.d.ts → commands.d-D6g2jq8V.d.ts} +4 -4
- package/dist/types-chunks/{guardrail.d-R7AiGfrI.d.ts → guardrail.d-DdeDWnVu.d.ts} +3 -3
- package/dist/types-chunks/{guardrail.d-6ZDbNbHO.d.ts → guardrail.d-DilYC1dh.d.ts} +1 -1
- package/dist/types-chunks/{manager.d-CoEuPRAo.d.ts → manager.d-B67TmPO1.d.ts} +27 -3
- package/dist/types-chunks/{public-api.d-7G4--RZE.d.ts → public-api.d-BvCp5VkW.d.ts} +3 -3
- package/dist/types-chunks/{resolver.d-CIVoGc97.d.ts → resolver.d-ssgNSlrh.d.ts} +2 -2
- package/dist/types-chunks/{run-manager.d-BaXtkryp.d.ts → run-manager.d-DxU3SSGR.d.ts} +1 -1
- package/dist/types-chunks/{sdk-session-BQccrODn.d.ts → sdk-session-wGpa7X_U.d.ts} +4 -4
- package/dist/types-chunks/{types.d-CJR7t6iW.d.ts → types.d-CEZZSY9s.d.ts} +2 -2
- package/dist/types-chunks/{types.d-BtC4yLYO.d.ts → types.d-CRCaLt_s.d.ts} +55 -7
- package/dist/types-chunks/{types.d-TTvpAGWf.d.ts → types.d-Cqaw71Ax.d.ts} +4 -2
- package/dist/types-chunks/{types.d-C9YHEAmA.d.ts → types.d-CzsLFqmf.d.ts} +5 -5
- package/dist/types-chunks/{utils.d-BbB5jzi1.d.ts → utils.d-Cb6vdvFM.d.ts} +5 -5
- package/docs/SDK_EMBEDDER_GUIDE.md +357 -55
- package/package.json +5 -2
- package/dist/chunks/argument-completer-RSK6CCGP.js +0 -2
- package/dist/chunks/chunk-NGHQIGVW.js +0 -46
- package/dist/chunks/chunk-PQ3XUSY2.js +0 -357
- package/dist/chunks/chunk-UJEMSPM5.js +0 -78
- package/dist/chunks/chunk-VYKFM3TB.js +0 -751
- package/dist/chunks/chunk-Z4PPLJF2.js +0 -60
- package/dist/chunks/compaction-config-ABLL6UH3.js +0 -2
- package/dist/chunks/host-7SAB4WEC.js +0 -2
- 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.
|
|
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.
|
|
1332
|
-
// => { contextWindow:
|
|
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-
|
|
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.
|
|
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` (
|
|
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` |
|
|
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
|
|
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.`
|
|
2933
|
-
|
|
2934
|
-
|
|
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`
|
|
2952
|
-
|
|
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
|
-
|
|
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
|
-
|
|
3244
|
-
|
|
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
|
|
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 {
|
|
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
|
-
|
|
3337
|
-
|
|
3338
|
-
|
|
3339
|
-
|
|
3340
|
-
|
|
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
|
|
3363
|
-
to `server.handle(request)` behind their own TLS
|
|
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
|
|
3536
|
-
|
|
3537
|
-
are deliberately excluded from the control journal so
|
|
3538
|
-
|
|
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
|
|
3747
|
-
runs,
|
|
3748
|
-
|
|
3749
|
-
|
|
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.
|
|
3762
|
-
|
|
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
|
-
|
|
4037
|
+
enableKodaXDaemonOwner,
|
|
4038
|
+
getKodaXRuntimeOwnerState,
|
|
3768
4039
|
} from '@kodax-ai/kodax/runtime';
|
|
3769
4040
|
|
|
3770
|
-
const
|
|
3771
|
-
|
|
3772
|
-
|
|
3773
|
-
|
|
3774
|
-
|
|
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
|
-
|
|
3780
|
-
|
|
3781
|
-
|
|
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
|