@kodax-ai/kodax 0.7.78 → 0.7.80
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 +3556 -3197
- package/README.md +371 -277
- package/README_CN.md +295 -215
- package/config-templates/config.example.jsonc +24 -2
- package/config-templates/integrations/a2a.example.jsonc +23 -7
- package/dist/builtin/skill-creator/scripts/aggregate-benchmark.js +2 -1
- package/dist/builtin/skill-creator/scripts/analyze-benchmark.d.ts +1 -0
- package/dist/builtin/skill-creator/scripts/analyze-benchmark.js +4 -3
- package/dist/builtin/skill-creator/scripts/compare-runs.d.ts +1 -0
- package/dist/builtin/skill-creator/scripts/compare-runs.js +4 -3
- package/dist/builtin/skill-creator/scripts/generate-review.js +2 -1
- package/dist/builtin/skill-creator/scripts/grade-evals.d.ts +1 -0
- package/dist/builtin/skill-creator/scripts/grade-evals.js +4 -3
- package/dist/builtin/skill-creator/scripts/improve-description.js +2 -1
- package/dist/builtin/skill-creator/scripts/init-skill.d.ts +1 -0
- package/dist/builtin/skill-creator/scripts/init-skill.js +4 -3
- package/dist/builtin/skill-creator/scripts/install-skill.d.ts +1 -0
- package/dist/builtin/skill-creator/scripts/install-skill.js +4 -3
- package/dist/builtin/skill-creator/scripts/package-skill.d.ts +1 -0
- package/dist/builtin/skill-creator/scripts/package-skill.js +4 -3
- package/dist/builtin/skill-creator/scripts/quick-validate.d.ts +1 -0
- package/dist/builtin/skill-creator/scripts/quick-validate.js +4 -3
- package/dist/builtin/skill-creator/scripts/run-eval.d.ts +1 -0
- package/dist/builtin/skill-creator/scripts/run-eval.js +4 -3
- package/dist/builtin/skill-creator/scripts/run-loop.js +2 -1
- package/dist/builtin/skill-creator/scripts/run-trigger-eval.js +2 -1
- package/dist/builtin/skill-creator/scripts/utils.d.ts +3 -0
- package/dist/builtin/skill-creator/scripts/utils.js +5 -1
- package/dist/chunks/agent-5PHGNFW3.js +2 -0
- package/dist/chunks/argument-completer-FXRZFMTC.js +2 -0
- package/dist/chunks/chunk-6UENDY33.js +89 -0
- package/dist/chunks/{chunk-6FOJVETH.js → chunk-BWX4YZX3.js} +7 -7
- package/dist/chunks/chunk-CDCCTM5I.js +278 -0
- package/dist/chunks/chunk-CEB5FDND.js +810 -0
- package/dist/chunks/{chunk-2CFHBKRE.js → chunk-E7C4DRD2.js} +2 -2
- package/dist/chunks/chunk-EVXIJPZ6.js +218 -0
- package/dist/chunks/chunk-FQ7NVMSU.js +319 -0
- package/dist/chunks/chunk-GC4JBMBS.js +385 -0
- package/dist/chunks/{chunk-Z3KFRTSB.js → chunk-HTVLCHWH.js} +1 -1
- package/dist/chunks/chunk-IHPH3444.js +655 -0
- package/dist/chunks/{chunk-YAZQTC2L.js → chunk-KHEFUT6O.js} +1 -1
- package/dist/chunks/{chunk-7OEBJGHK.js → chunk-PMKAG4SZ.js} +1 -1
- package/dist/chunks/chunk-RPF5RGWZ.js +479 -0
- package/dist/chunks/{chunk-UID7BLAB.js → chunk-RXXKDR6Y.js} +1 -1
- package/dist/chunks/chunk-T5U4OP2N.js +292 -0
- package/dist/chunks/chunk-TRNAWSXP.js +123 -0
- package/dist/chunks/chunk-UZSHBQER.js +30 -0
- package/dist/chunks/{chunk-TQDBTTIM.js → chunk-VECRQTSD.js} +44 -44
- package/dist/chunks/compaction-config-6J6XCBJS.js +2 -0
- package/dist/chunks/{construction-bootstrap-VQLQGWPY.js → construction-bootstrap-KPCAE3VN.js} +1 -1
- package/dist/chunks/dist-RWEVZOFK.js +2 -0
- package/dist/chunks/{dist-BFT5YIGU.js → dist-WWL2DNIJ.js} +1 -1
- package/dist/chunks/host-OGS2R5J4.js +2 -0
- package/dist/chunks/run-manager-2YMM3BOT.js +2 -0
- package/dist/chunks/{utils-SID4HW2Q.js → utils-2YHRUZYC.js} +1 -1
- package/dist/index.d.ts +21 -20
- package/dist/index.js +4 -4
- package/dist/kodax_bootstrap.js +2 -1
- package/dist/kodax_cli.js +1850 -1476
- package/dist/kodax_resume.js +570 -34
- package/dist/provider-capabilities.json +85 -50
- package/dist/runtime-worker.js +1737 -1369
- package/dist/sandbox-workspace-session.js +285 -19
- package/dist/sdk-a2a.d.ts +19 -13
- package/dist/sdk-a2a.js +8 -8
- package/dist/sdk-agent.d.ts +80 -150
- package/dist/sdk-agent.js +1 -1
- package/dist/sdk-coding.d.ts +186 -66
- package/dist/sdk-coding.js +1 -1
- package/dist/sdk-experimental-memory.d.ts +3 -3
- 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 +1 -1
- 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 +31 -213
- package/dist/sdk-repl.js +2 -2
- package/dist/sdk-runtime.d.ts +230 -31
- package/dist/sdk-runtime.js +1 -1
- package/dist/sdk-sandbox.js +1 -1
- package/dist/sdk-session.d.ts +8 -8
- package/dist/sdk-session.js +1 -1
- package/dist/sdk-skills.js +1 -1
- package/dist/semantic-worker.js +283 -18
- package/dist/types-chunks/{base.d-4e74xDdy.d.ts → base.d-DxG0m09Z.d.ts} +1 -1
- package/dist/types-chunks/{bash-prefix-extractor.d-uAe2Oqda.d.ts → bash-prefix-extractor.d-DStqKIlu.d.ts} +31 -8
- package/dist/types-chunks/{capability-learning.d-CVsdHw4j.d.ts → capability-learning.d-iPSiAmc_.d.ts} +1 -1
- package/dist/types-chunks/{capsule.d-BlSv9l3V.d.ts → capsule.d-BuFJaYxA.d.ts} +14 -21
- package/dist/types-chunks/{types.d-BA-Jwpfs.d.ts → controller.d-C2aHqzHj.d.ts} +344 -11
- package/dist/types-chunks/{guardrail.d-CXDYRgZ3.d.ts → guardrail.d-DM-OK0m3.d.ts} +187 -116
- package/dist/types-chunks/{history-retrieval.d-DtCy7x64.d.ts → history-retrieval.d-DImTKgO6.d.ts} +16 -3
- package/dist/types-chunks/{oauth-login.d-Bgb4rdLN.d.ts → oauth-login.d-6i-vidxv.d.ts} +6 -2
- package/dist/types-chunks/{public-api.d-B3AohsxN.d.ts → public-api.d-CewTvUKV.d.ts} +335 -38
- package/dist/types-chunks/{repl.d-Ie_ZXb_U.d.ts → repl.d-DvXQtgr7.d.ts} +5 -5
- package/dist/types-chunks/{resolver.d-iAQ9ocLB.d.ts → resolver.d-C9IW4fmU.d.ts} +5 -2
- package/dist/types-chunks/{run-manager.d-D1twIhF9.d.ts → run-manager.d-C6ahvZqK.d.ts} +1 -1
- package/dist/types-chunks/{sdk-session-DB9KksIx.d.ts → sdk-session-C59RUrri.d.ts} +3 -3
- package/dist/types-chunks/shell-command-sets.d-Dp4UvPLL.d.ts +297 -0
- package/dist/types-chunks/{side-query.d-DTuLPcC5.d.ts → side-query.d-CP1LiRiH.d.ts} +8 -2
- package/dist/types-chunks/{types-D3g6XUQr.d.ts → types-Bf3Y3mW-.d.ts} +1 -1
- package/dist/types-chunks/{types.d-BH0ZkTGf.d.ts → types.d-BzpPDBPV.d.ts} +2 -2
- package/dist/types-chunks/{types.d-DIpZJKUl.d.ts → types.d-CgrXrFrq.d.ts} +16 -2
- package/dist/types-chunks/{types.d-DVDTIfB_.d.ts → types.d-yuJzE82x.d.ts} +4 -4
- package/dist/types-chunks/{utils.d-CVp6bFl9.d.ts → utils.d-BpACJmzt.d.ts} +24 -6
- package/docs/SDK_EMBEDDER_GUIDE.md +1148 -598
- package/package.json +6 -3
- package/dist/chunks/agent-ASP4MS3X.js +0 -2
- package/dist/chunks/argument-completer-JYQ7FX3W.js +0 -2
- package/dist/chunks/chunk-35PPHOQ2.js +0 -292
- package/dist/chunks/chunk-43QNNDHR.js +0 -29
- package/dist/chunks/chunk-5GO6FH7L.js +0 -458
- package/dist/chunks/chunk-5NDCSFOP.js +0 -78
- package/dist/chunks/chunk-6XM4B6K2.js +0 -48
- package/dist/chunks/chunk-6YZUE6NC.js +0 -240
- package/dist/chunks/chunk-KQLQYHWU.js +0 -386
- package/dist/chunks/chunk-NXO6GWSY.js +0 -46
- package/dist/chunks/chunk-T2XZTLYE.js +0 -316
- package/dist/chunks/chunk-VAT2QYXM.js +0 -765
- package/dist/chunks/chunk-YVRYHC4C.js +0 -655
- package/dist/chunks/compaction-config-BQSSIWK5.js +0 -2
- package/dist/chunks/dist-PCFE24YP.js +0 -2
- package/dist/chunks/host-UTFXCYYM.js +0 -2
- package/dist/chunks/run-manager-PJU3WIFJ.js +0 -2
- package/dist/types-chunks/guardrail.d-BRE_ErEj.d.ts +0 -156
|
@@ -28,17 +28,17 @@ are NOT obvious from inspecting the type definitions alone:
|
|
|
28
28
|
18. [External-agent executor plane](#18-external-agent-executor-plane-feature_258-v0767)
|
|
29
29
|
19. [Session surface filtering and cursor pagination](#19-session-surface-filtering-and-cursor-pagination-feature_261-v0767)
|
|
30
30
|
20. [Cost-disciplined workflow routing and telemetry](#20-cost-disciplined-workflow-routing-and-telemetry-feature_259-v0767)
|
|
31
|
-
21. [Experimental governed memory — `/experimental-memory`](#21-experimental-governed-memory--experimental-memory-feature_260--feature_275-v0768v0777)
|
|
31
|
+
21. [Experimental governed memory — `/experimental-memory`](#21-experimental-governed-memory--experimental-memory-feature_260--feature_275-v0768v0777)
|
|
32
32
|
22. [Bidirectional A2A 1.0 — `/a2a`](#22-bidirectional-a2a-10--a2a-feature_267-v0769)
|
|
33
33
|
23. [Shared Coder daemon for Space and IDE hosts](#23-shared-coder-daemon-for-space-and-ide-hosts-feature_269-v0769)
|
|
34
34
|
24. [Runtime-owned Auto Mode and plan-approval bridges](#24-runtime-owned-auto-mode-and-plan-approval-bridges-v0772v0773)
|
|
35
35
|
25. [Always-on context compaction and bounded transcript recovery](#25-always-on-context-compaction-and-bounded-transcript-recovery-v0774)
|
|
36
|
-
26. [Agent mailbox control versus SDK event telemetry](#26-agent-mailbox-control-versus-sdk-event-telemetry-v0774)
|
|
37
|
-
27. [Windows GUI background subprocess visibility](#27-windows-gui-background-subprocess-visibility-v0775)
|
|
38
|
-
28. [Host-configurable Shell Execution Contract](#28-host-configurable-shell-execution-contract-v0777)
|
|
39
|
-
29. [Evidence-gated background Skill learning](#29-evidence-gated-background-skill-learning-feature_263-v0778)
|
|
40
|
-
30. [Standalone sandbox SDK](#30-standalone-sandbox-sdk-v0778)
|
|
41
|
-
- [Learned Skill promotion reference](#learned-skill-promotion-reference-v0778)
|
|
36
|
+
26. [Agent mailbox control versus SDK event telemetry](#26-agent-mailbox-control-versus-sdk-event-telemetry-v0774)
|
|
37
|
+
27. [Windows GUI background subprocess visibility](#27-windows-gui-background-subprocess-visibility-v0775)
|
|
38
|
+
28. [Host-configurable Shell Execution Contract](#28-host-configurable-shell-execution-contract-v0777)
|
|
39
|
+
29. [Evidence-gated background Skill learning](#29-evidence-gated-background-skill-learning-feature_263-v0778)
|
|
40
|
+
30. [Standalone sandbox SDK](#30-standalone-sandbox-sdk-v0778)
|
|
41
|
+
- [Learned Skill promotion reference](#learned-skill-promotion-reference-v0778)
|
|
42
42
|
|
|
43
43
|
§1–§3 (and the Phase-7/8 MCP-popout surface in §1) land in v0.7.42
|
|
44
44
|
under FEATURE_186 (see [ADR-032](ADR.md#adr-032-sdk-embedder-surface-closure-feature_186-v0742)).
|
|
@@ -678,7 +678,10 @@ missing-storage warning by itself.
|
|
|
678
678
|
|
|
679
679
|
```ts
|
|
680
680
|
import { runKodaX } from '@kodax-ai/kodax/coding';
|
|
681
|
-
import {
|
|
681
|
+
import {
|
|
682
|
+
createSessionManager,
|
|
683
|
+
exportSessionBundle,
|
|
684
|
+
} from '@kodax-ai/kodax/session';
|
|
682
685
|
|
|
683
686
|
// One manager per host process; reuse across runs so the
|
|
684
687
|
// per-session write queue + append-watermark caches stay coherent.
|
|
@@ -687,6 +690,8 @@ const {
|
|
|
687
690
|
listSessions,
|
|
688
691
|
loadSession,
|
|
689
692
|
loadFullTranscript,
|
|
693
|
+
readFullTranscript,
|
|
694
|
+
readSessionCapture,
|
|
690
695
|
appendClientNotice,
|
|
691
696
|
compactSession,
|
|
692
697
|
} = createSessionManager();
|
|
@@ -720,6 +725,17 @@ const nextPage = recent.at(-1)?.cursor
|
|
|
720
725
|
: [];
|
|
721
726
|
const replay = await loadSession('s_my_chat');
|
|
722
727
|
const scrollback = await loadFullTranscript('s_my_chat');
|
|
728
|
+
const historyAbort = new AbortController();
|
|
729
|
+
const strictScrollback = await readFullTranscript('s_my_chat', {
|
|
730
|
+
timeoutMs: 15_000,
|
|
731
|
+
signal: historyAbort.signal,
|
|
732
|
+
});
|
|
733
|
+
const capture = await readSessionCapture('s_my_chat', {
|
|
734
|
+
timeoutMs: 15_000,
|
|
735
|
+
});
|
|
736
|
+
const bundle = await exportSessionBundle('s_my_chat', {
|
|
737
|
+
timeoutMs: 15_000,
|
|
738
|
+
});
|
|
723
739
|
const compacted = await compactSession('s_my_chat', { dryRun: true });
|
|
724
740
|
|
|
725
741
|
await appendClientNotice('s_my_chat', {
|
|
@@ -728,6 +744,14 @@ await appendClientNotice('s_my_chat', {
|
|
|
728
744
|
});
|
|
729
745
|
```
|
|
730
746
|
|
|
747
|
+
`readSessionCapture()` returns the active `data` and full `transcript` from one
|
|
748
|
+
immutable storage boundary. `readFullTranscript()` and
|
|
749
|
+
`readSessionCapture()` are strict read-only APIs: timeout/cancellation,
|
|
750
|
+
corruption, version drift, or resync fail explicitly and never trigger legacy
|
|
751
|
+
Session migration or recovery. `exportSessionBundle()` preserves the exact
|
|
752
|
+
main/sidecar bytes with hashes and compatibility diagnostics; use it for a
|
|
753
|
+
support or recovery bundle, not as proof that a Session can resume.
|
|
754
|
+
|
|
731
755
|
### Auto-resume selection in v0.7.74
|
|
732
756
|
|
|
733
757
|
With `session.autoResume: true` (or `resume: true`) and no explicit ID, KodaX
|
|
@@ -751,6 +775,9 @@ interface SessionManager {
|
|
|
751
775
|
listSessions(...): Promise<SessionSummary[]>;
|
|
752
776
|
loadSession(id): Promise<...>;
|
|
753
777
|
loadFullTranscript(id): Promise<...>;
|
|
778
|
+
readFullTranscript(id, options?): Promise<...>;
|
|
779
|
+
readConversationHistory(id, options?): Promise<...>;
|
|
780
|
+
readSessionCapture(id, options?): Promise<SessionReadCapture | null>;
|
|
754
781
|
appendClientNotice(id, opts): Promise<SessionTranscriptEntry | null>;
|
|
755
782
|
compactSession(id, opts?): Promise<CompactSessionResult>;
|
|
756
783
|
forkSession(id, opts?): Promise<...>;
|
|
@@ -776,13 +803,22 @@ Session persistence exposes three related but different layers:
|
|
|
776
803
|
| Need | Use | Meaning |
|
|
777
804
|
|---|---|---|
|
|
778
805
|
| Continue a model turn | `loadSession(id)` | Active branch only. This is the context KodaX would resume from. |
|
|
779
|
-
| Render
|
|
806
|
+
| Render the ordinary conversation | `readConversationHistory(id)` | SDK-resolved conversation order with proven compaction copies folded and ambiguity reported. |
|
|
807
|
+
| Render audit / raw scrollback | `readFullTranscript(id)` or `loadFullTranscript(id)` | Append-order physical entries, including archived islands and non-active branches. |
|
|
780
808
|
| Reuse TUI display projection | `SessionData.uiHistory` | Optional bounded replay cache. Interactive REPL sessions may write it; headless SDK sessions may not. |
|
|
781
809
|
|
|
782
|
-
For product UI,
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
810
|
+
For product UI, use `readConversationHistory(id)` for the ordinary chat and
|
|
811
|
+
keep `readFullTranscript(id)` available for an audit/details view.
|
|
812
|
+
`loadSession(id)` remains the model-context API. Do not assume `uiHistory`
|
|
813
|
+
exists; it is intentionally a small, lossy replay cache.
|
|
814
|
+
|
|
815
|
+
`loadSession(id)` and Runtime `sessions.load(id)` are pure snapshot reads. They
|
|
816
|
+
do not emit `session.loaded`; a host that explicitly loads a snapshot should
|
|
817
|
+
update its own selected/loaded UI state from the resolved promise. The
|
|
818
|
+
compatibility `session.loaded` event (and the CLI bridge's `onSessionStart`)
|
|
819
|
+
remains bound to the Provider Run execution boundary, where a Session actually
|
|
820
|
+
becomes active. Do not use that Run-lifecycle event as a generic data-load
|
|
821
|
+
notification.
|
|
786
822
|
|
|
787
823
|
`loadFullTranscript(id).transcriptEntries` is the structured host-facing
|
|
788
824
|
scrollback. Each entry has stable ownership and ordering fields:
|
|
@@ -806,14 +842,145 @@ interface SessionTranscriptEntry {
|
|
|
806
842
|
|
|
807
843
|
`entryId` is the physical lineage node id. `logicalId` is stable across
|
|
808
844
|
forked/cloned copies of the same transcript item, and `sourceEntryId` is present
|
|
809
|
-
on cloned entries to point back to the root physical source entry.
|
|
810
|
-
|
|
811
|
-
|
|
845
|
+
on cloned entries to point back to the root physical source entry. These fields
|
|
846
|
+
support audit inspection, but a host must not infer the complete ordinary-chat
|
|
847
|
+
fold merely by grouping them: legacy omissions and conflicting metadata require
|
|
848
|
+
lineage validation. Use `readConversationHistory()` instead of implementing
|
|
849
|
+
host-side folding, and never guess from `message.role`, content, timestamp,
|
|
850
|
+
`turnId`, or `[compacted]` placeholders.
|
|
812
851
|
Legacy entries without persisted provenance use `logicalId === entryId` and omit
|
|
813
852
|
`sourceEntryId`; treat that as "unknown/not cloned", not as content-based proof
|
|
814
853
|
that no older clone exists.
|
|
815
854
|
`loadFullTranscript()` still returns raw append-order scrollback; it does not
|
|
816
|
-
hide
|
|
855
|
+
hide archived islands or non-active branches, and it does not silently merge
|
|
856
|
+
branches. The host owns branch visibility, folding, and main-chat presentation.
|
|
857
|
+
|
|
858
|
+
`readConversationHistory()` is the supported folding boundary. Every returned
|
|
859
|
+
entry has a physical `boundaryId` and `auditEntryIds` naming all copies that the
|
|
860
|
+
SDK could prove represent the same interaction. Modern Sessions use
|
|
861
|
+
`logicalId` / `sourceEntryId`; legacy copies are folded only when a persisted
|
|
862
|
+
compaction boundary and one unique lineage suffix prove the relationship.
|
|
863
|
+
An inactive compaction epoch or non-leaf ancestor is crossed only when a
|
|
864
|
+
contiguous retained prefix has exact provenance and its complete parent path
|
|
865
|
+
predates the compaction in append order. Content-only legacy matching never
|
|
866
|
+
authorizes that fallback.
|
|
867
|
+
KodaX never globally sorts or deduplicates by content, timestamp, or `turnId`.
|
|
868
|
+
|
|
869
|
+
The result status is part of the contract:
|
|
870
|
+
|
|
871
|
+
- `resolved`: every fold and branch predecessor used by the projection was
|
|
872
|
+
proven. A persisted Session with no conversation records is also resolved
|
|
873
|
+
with empty `entries` and `issues`, including the normal interval after a Run
|
|
874
|
+
is accepted but before its body enters canonical history.
|
|
875
|
+
- `partial`: persisted lineage or transcript identity needed for a complete
|
|
876
|
+
projection of one or more existing conversation records is unavailable.
|
|
877
|
+
Available physical records are retained; Actor/Run state by itself does not
|
|
878
|
+
make an otherwise empty persisted conversation partial.
|
|
879
|
+
- `ambiguous`: multiple legacy interpretations remain. All candidates are
|
|
880
|
+
retained and `issues[]` explains why; the host must not present the result as
|
|
881
|
+
confidently deduplicated.
|
|
882
|
+
|
|
883
|
+
Issues are bounded diagnostic summaries: `occurrenceCount` is the number of
|
|
884
|
+
diagnostics represented, `entryCount` is the pre-bounding evidence-reference
|
|
885
|
+
count, and `entryIds` is a bounded exact sample. Conversation entries themselves
|
|
886
|
+
are never removed to make diagnostic metadata fit a transport page.
|
|
887
|
+
|
|
888
|
+
Runtime exposes the identical projection in embedded and daemon modes through
|
|
889
|
+
`sessions.conversation()`, `sessions.conversationPage()`, and
|
|
890
|
+
`sessions.conversationEntryChunk()`. Modern Session writes prepare a bounded
|
|
891
|
+
on-disk page index, so the first finite page reads only its metadata, fixed-size
|
|
892
|
+
index records, and requested inline bodies instead of materializing all history.
|
|
893
|
+
A source-boundary-fenced copy of the Session's minimal admission identity lets
|
|
894
|
+
the shared daemon authorize that bounded read before checking cursors, capacity,
|
|
895
|
+
or entry metadata; it does not perform a full Session `peek()` on a cache hit.
|
|
896
|
+
A cache-less older Session is upgraded by one canonical fallback read. Direct
|
|
897
|
+
and paged reads share the same `revision`, `sourceRevision`, status, issues, and
|
|
898
|
+
logical entry order. Page and chunk cursors are revision-fenced: if the Session
|
|
899
|
+
changes, the Runtime returns `resync_required` and the host must request a fresh
|
|
900
|
+
first page. Derived manifest, descriptor, and chunk reads have fixed allocation
|
|
901
|
+
ceilings; a corrupt or concurrently replaced generation is rejected rather
|
|
902
|
+
than trusted. Pages are fetched newest-tail-first while each page is internally in
|
|
903
|
+
forward order, so prepend each fetched page to reconstruct `conversation().entries`;
|
|
904
|
+
do not append pages in fetch order.
|
|
905
|
+
Request `requirements: { conversationHistory: 1 }` when connecting to a daemon
|
|
906
|
+
that must support this contract.
|
|
907
|
+
|
|
908
|
+
For write-side hosts that already own a newly produced append tail,
|
|
909
|
+
`await FileSessionStorage.prepareSessionAppend(id)` returns an authenticated
|
|
910
|
+
one-shot boundary or `null` when no canonical cache witness is available.
|
|
911
|
+
`appendPreparedSessionTail(id, delta)` appends only the supplied new linear
|
|
912
|
+
lineage, artifact, and extension records. The delta deliberately has no
|
|
913
|
+
historical-array field: its read and compute cost is bounded by the new tail,
|
|
914
|
+
and a stale boundary, prefix/sidecar change, duplicate identity, non-linear
|
|
915
|
+
lineage tail, or concurrent durable write fails with
|
|
916
|
+
`SessionReadError.code === 'data_changed'`. Reload, obtain a fresh boundary,
|
|
917
|
+
and rebuild the tail before retrying.
|
|
918
|
+
|
|
919
|
+
A non-null fulfilled append result is the reusable successor boundary. A
|
|
920
|
+
fulfilled `null` means the tail did commit exactly once, but the successor
|
|
921
|
+
could not be witnessed; reload before another append and do not retry that
|
|
922
|
+
tail. Use `appendSessionDelta(id, data)` for a full mutable snapshot, a `null`
|
|
923
|
+
prepared boundary, or any historical rewrite; that API performs the exact
|
|
924
|
+
canonical merge and persists index, nested-object, and post-helper mutations.
|
|
925
|
+
Data returned by `storage.load()` remains an ordinary mutable,
|
|
926
|
+
`structuredClone()`-compatible `KodaXSessionData` object. The derived page
|
|
927
|
+
cache is only a recoverable acceleration structure: its fixed-size identity
|
|
928
|
+
filter is accepted for appends only when its exact bundle revision and hash
|
|
929
|
+
match canonical metadata in the Session main file.
|
|
930
|
+
|
|
931
|
+
```ts
|
|
932
|
+
const baseline = await storage.prepareSessionAppend(sessionId);
|
|
933
|
+
if (baseline === null) {
|
|
934
|
+
await storage.appendSessionDelta(sessionId, completeMutableSnapshot);
|
|
935
|
+
} else {
|
|
936
|
+
const successor = await storage.appendPreparedSessionTail(sessionId, {
|
|
937
|
+
baseline,
|
|
938
|
+
title,
|
|
939
|
+
activeEntryId: newEntry.id,
|
|
940
|
+
lineageEntries: [newEntry],
|
|
941
|
+
});
|
|
942
|
+
if (successor === null) {
|
|
943
|
+
await storage.load(sessionId); // committed; resync before the next tail
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
```
|
|
947
|
+
|
|
948
|
+
Use a returned entry as a revision-fenced fork or rewind boundary instead of
|
|
949
|
+
guessing from content or a historical `turnId`:
|
|
950
|
+
|
|
951
|
+
```ts
|
|
952
|
+
const history = await runtime.sessions.conversation(sessionId);
|
|
953
|
+
const item = history?.entries.find((entry) => entry.boundaryId !== undefined);
|
|
954
|
+
if (history && item?.boundaryId) {
|
|
955
|
+
await runtime.sessions.fork({
|
|
956
|
+
sessionId,
|
|
957
|
+
historyBoundary: {
|
|
958
|
+
entryId: item.boundaryId,
|
|
959
|
+
sourceRevision: history.sourceRevision,
|
|
960
|
+
},
|
|
961
|
+
});
|
|
962
|
+
}
|
|
963
|
+
```
|
|
964
|
+
|
|
965
|
+
If the Session changes before the mutation, the Runtime returns
|
|
966
|
+
`resync_required`; an unknown boundary returns `null` and never falls back to a
|
|
967
|
+
different point.
|
|
968
|
+
|
|
969
|
+
The standalone session API accepts the same boundary (named `boundaryId` on
|
|
970
|
+
that surface) and fails closed with `null`:
|
|
971
|
+
|
|
972
|
+
```ts
|
|
973
|
+
const history = await manager.readConversationHistory(sessionId);
|
|
974
|
+
const item = history?.entries.at(-1);
|
|
975
|
+
if (history && item?.boundaryId) {
|
|
976
|
+
await manager.forkSession(sessionId, {
|
|
977
|
+
historyBoundary: {
|
|
978
|
+
boundaryId: item.boundaryId,
|
|
979
|
+
sourceRevision: history.sourceRevision,
|
|
980
|
+
},
|
|
981
|
+
});
|
|
982
|
+
}
|
|
983
|
+
```
|
|
817
984
|
|
|
818
985
|
Since v0.7.63, rewind audit markers are represented as
|
|
819
986
|
`type: 'rewind_marker'`. They are useful for host scrollback and audit UI, but
|
|
@@ -1328,7 +1495,8 @@ interface KodaXModelCapabilities {
|
|
|
1328
1495
|
supportedEfforts?: Array<{ value: string; isDefault?: boolean; isUserVisible?: boolean }>;
|
|
1329
1496
|
};
|
|
1330
1497
|
contextWindow?: number; // input tokens (provider default + per-model override cascade)
|
|
1331
|
-
maxOutputTokens?: number; // per-turn
|
|
1498
|
+
maxOutputTokens?: number; // per-turn output limit KodaX requests — see note below
|
|
1499
|
+
maxOutputTokensField?: 'max_tokens' | 'max_completion_tokens';
|
|
1332
1500
|
thinkingBudgetCap?: number; // tokens (native-budget providers only)
|
|
1333
1501
|
isDefault: boolean; // true for the provider's default model
|
|
1334
1502
|
}
|
|
@@ -1435,9 +1603,10 @@ for (const providerName of Object.keys(KODAX_PROVIDER_SNAPSHOTS)) {
|
|
|
1435
1603
|
|
|
1436
1604
|
### A note on `maxOutputTokens`
|
|
1437
1605
|
|
|
1438
|
-
`KodaXModelCapabilities.maxOutputTokens` is the **per-turn
|
|
1439
|
-
KodaX requests**, NOT the upstream "theoretical maximum".
|
|
1440
|
-
|
|
1606
|
+
`KodaXModelCapabilities.maxOutputTokens` is the **per-turn output-token limit
|
|
1607
|
+
KodaX requests**, NOT the upstream "theoretical maximum". For OpenAI-compatible
|
|
1608
|
+
routes, `maxOutputTokensField` reports whether that limit is serialized as
|
|
1609
|
+
`max_tokens` or `max_completion_tokens`. The two size concepts diverge because:
|
|
1441
1610
|
|
|
1442
1611
|
- **What upstream advertises is often unreliable.** A 2026-05 probe
|
|
1443
1612
|
against `zhipu-coding` / `kimi-code` / `minimax-coding` / `ark-coding`
|
|
@@ -1951,10 +2120,19 @@ registerCustomProviders([{
|
|
|
1951
2120
|
baseUrl: 'https://api.example.com/v1',
|
|
1952
2121
|
apiKeyEnv: 'MY_GATEWAY_KEY',
|
|
1953
2122
|
model: 'gpt-4-mini',
|
|
2123
|
+
maxOutputTokensField: 'max_completion_tokens',
|
|
1954
2124
|
verifyStrategy: 'minimal-message', // optional override
|
|
1955
2125
|
}]);
|
|
1956
2126
|
```
|
|
1957
2127
|
|
|
2128
|
+
For an OpenAI-compatible endpoint that follows DeepSeek Chat Completions, set
|
|
2129
|
+
`maxOutputTokensField: 'max_tokens'`. The default is
|
|
2130
|
+
`max_completion_tokens`; an object entry in `models[]` may override the
|
|
2131
|
+
provider-level value for a mixed gateway. DeepSeek V4 custom configurations
|
|
2132
|
+
should likewise use the model-specific `deepseek-v4-flash-openai` or
|
|
2133
|
+
`deepseek-v4-pro-openai` reasoning preset. Both built-in DeepSeek V4 routes are
|
|
2134
|
+
text-only.
|
|
2135
|
+
|
|
1958
2136
|
The validator rejects illegal combinations:
|
|
1959
2137
|
- `protocol: 'openai'` + `verifyStrategy: 'count-tokens'` → throws (OpenAI protocol has no count_tokens endpoint).
|
|
1960
2138
|
|
|
@@ -2385,6 +2563,7 @@ The important creation options are:
|
|
|
2385
2563
|
| `isolation` | `'inline'` | Embedded-only. `'worker'` creates a private Runtime Worker; daemon rejects any explicit isolation because it is already process-isolated. |
|
|
2386
2564
|
| `worker.resourceLimits` | unset | Optional V8 heap/stack limits; requires `isolation: 'worker'`. |
|
|
2387
2565
|
| `worker.shutdownTimeoutMs` | `2000` | Grace before the parent terminates the Runtime Worker. |
|
|
2566
|
+
| `worker.configuredA2A` | `false` | Explicitly lets the Worker owner load and reconcile `<homeDir>/.kodax/integrations/a2a.json`; installs the full list/describe/spawn/task plane inside the Worker. |
|
|
2388
2567
|
| `requirements.hardDispose` | `false` | Rejects inline and daemon forms; prevents an accidental weaker ownership form. |
|
|
2389
2568
|
| `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`. |
|
|
2390
2569
|
| `profile` | `'default'` | Daemon uniqueness and runtime configuration namespace. |
|
|
@@ -2472,6 +2651,15 @@ SDK auto-start allows `daemonStartupTimeoutMs` (default 60 seconds) and
|
|
|
2472
2651
|
concurrent test/desktop startup without weakening PID, endpoint, token, or
|
|
2473
2652
|
runtime-identity validation.
|
|
2474
2653
|
|
|
2654
|
+
On Windows, current child cleanup verifies observed process identities and
|
|
2655
|
+
reports indeterminate outcomes instead of falling back to bare-PID success.
|
|
2656
|
+
That is not Job Object-grade containment: if an intermediate process exits
|
|
2657
|
+
between snapshots, an already-running descendant can become unobservable.
|
|
2658
|
+
Until Issue 256 is resolved (scheduled for v0.7.84) with spawn-time Job Object
|
|
2659
|
+
assignment and a host-issued Worker owner lease, embedders must not treat
|
|
2660
|
+
Runtime/Worker close or executor cleanup as proof that every descendant has
|
|
2661
|
+
terminated.
|
|
2662
|
+
|
|
2475
2663
|
`homeDir` and `KODAX_HOME` deliberately name different levels. Runtime SDK and
|
|
2476
2664
|
CLI daemon `--home` accept the **base directory that contains `.kodax`**;
|
|
2477
2665
|
lower-level `KODAX_HOME` points at the **data directory itself** and need not be
|
|
@@ -2493,7 +2681,9 @@ const runtime = await createKodaXRuntime({
|
|
|
2493
2681
|
worker: {
|
|
2494
2682
|
resourceLimits: { maxOldGenerationSizeMb: 1024 },
|
|
2495
2683
|
shutdownTimeoutMs: 2000,
|
|
2684
|
+
configuredA2A: true,
|
|
2496
2685
|
},
|
|
2686
|
+
requirements: { externalAgents: true },
|
|
2497
2687
|
});
|
|
2498
2688
|
|
|
2499
2689
|
try {
|
|
@@ -2515,6 +2705,12 @@ Worker `resourceLimits` bound parts of the V8 heap only. They do not cover every
|
|
|
2515
2705
|
kind of native/external memory and do not make Node code safe to treat as
|
|
2516
2706
|
untrusted. Worker isolation is a fault boundary, not a security sandbox.
|
|
2517
2707
|
|
|
2708
|
+
Configured A2A is loaded by the Worker owner, not serialized from a parent
|
|
2709
|
+
factory closure. `worker.configuredA2A` is therefore an explicit opt-in. When
|
|
2710
|
+
enabled, the Worker reconciles the same user document as the CLI/daemon path,
|
|
2711
|
+
advertises `externalAgents`, and backs `listDispatchable`, `describe`,
|
|
2712
|
+
`preflight`, and external Actor dispatch with one real executor plane.
|
|
2713
|
+
|
|
2518
2714
|
Callers that cannot accept a silent fallback can require the capability:
|
|
2519
2715
|
|
|
2520
2716
|
```ts
|
|
@@ -2543,6 +2739,33 @@ The same method name deliberately has deployment-specific ownership effects:
|
|
|
2543
2739
|
automatically retry or replay an in-flight run after a Worker/daemon owner dies,
|
|
2544
2740
|
because provider and tool side effects may already have happened.
|
|
2545
2741
|
|
|
2742
|
+
`runtime.shutdown` and `stopForInline()` responses mean the fenced stop was
|
|
2743
|
+
accepted; host-close logging and owner-lock release are also intermediate
|
|
2744
|
+
progress boundaries. A successful `kodax daemon stop` / `restart` additionally
|
|
2745
|
+
waits for the original daemon PID to disappear and verifies a shutdown-success
|
|
2746
|
+
outcome bound to that exact Runtime ID and PID. Before that process exits, the
|
|
2747
|
+
serve host completes bounded A2A/extension, LSP, managed-child-tree, and tracing
|
|
2748
|
+
cleanup; an unverified current-owner child tree, cleanup timeout, failed outcome,
|
|
2749
|
+
or missing outcome makes CLI stop fail instead of reporting a false success.
|
|
2750
|
+
The stop client also owns an outer watchdog that starts when the request is
|
|
2751
|
+
accepted. On Windows it captures the daemon's creation identity before
|
|
2752
|
+
requesting shutdown and can therefore reclaim that exact process tree if
|
|
2753
|
+
Runtime close never settles or synchronous code blocks the daemon event loop.
|
|
2754
|
+
Node 20 exposes only cached-PID signaling on POSIX, so KodaX fails closed there
|
|
2755
|
+
instead of risking a reused PID/PGID; the result is `cleanup_unverified` and the
|
|
2756
|
+
process is left to an external lifecycle manager until Issue 269 supplies a
|
|
2757
|
+
retained native handle/supervisor. A forced exit without a matching success
|
|
2758
|
+
outcome is never success. After the original PID exits, the client re-reads the profile; JSON
|
|
2759
|
+
results use `replacementRunning: true` and return the current state/health when
|
|
2760
|
+
a replacement daemon has already acquired the owner lock. Such a result also
|
|
2761
|
+
uses `stopped: false` and `reason: 'replacement_running'`, so existing callers
|
|
2762
|
+
that gate cleanup on `stopped` remain safe. Callers must not clean or rebuild a
|
|
2763
|
+
profile while that flag is present or health is non-missing.
|
|
2764
|
+
The administrative connection also verifies that `initialize.identity` still
|
|
2765
|
+
matches the Runtime ID/profile observed before connecting; if an old owner exits
|
|
2766
|
+
and a replacement binds the same endpoint first, the stale stop command fails
|
|
2767
|
+
without sending `daemon.stop` to the replacement.
|
|
2768
|
+
|
|
2546
2769
|
### Daemon ownership and state
|
|
2547
2770
|
|
|
2548
2771
|
Daemon ownership is scoped by `homeDir + profile`.
|
|
@@ -2591,7 +2814,9 @@ Operational guidance:
|
|
|
2591
2814
|
configurations;
|
|
2592
2815
|
- test harnesses that auto-start a process daemon must send authenticated
|
|
2593
2816
|
`runtime.shutdown` (or run `kodax daemon stop --home <dir> --profile <name>`)
|
|
2594
|
-
before deleting their temporary home;
|
|
2817
|
+
before deleting their temporary home; use the CLI command when the caller
|
|
2818
|
+
needs a completed process-exit boundary, because `runtime.close()` only
|
|
2819
|
+
detaches and the low-level shutdown response is acceptance, not completion;
|
|
2595
2820
|
- KodaX's own Vitest harness also supplies an internal worker-PID marker so a
|
|
2596
2821
|
forcibly terminated worker cannot strand its test daemon; this is a test-only
|
|
2597
2822
|
fallback, not a public SDK option or a production idle-shutdown policy;
|
|
@@ -2779,11 +3004,11 @@ await runtime.mcp.upsertServer('filesystem', {
|
|
|
2779
3004
|
});
|
|
2780
3005
|
await runtime.mcp.reloadServers();
|
|
2781
3006
|
|
|
2782
|
-
const commands = await runtime.catalog.commands(process.cwd());
|
|
2783
|
-
const skills = await runtime.catalog.skills({ projectRoot: process.cwd() });
|
|
2784
|
-
```
|
|
2785
|
-
|
|
2786
|
-
This is the intended path for KodaX Space, IDE adapters, and settings UIs:
|
|
3007
|
+
const commands = await runtime.catalog.commands(process.cwd());
|
|
3008
|
+
const skills = await runtime.catalog.skills({ projectRoot: process.cwd() });
|
|
3009
|
+
```
|
|
3010
|
+
|
|
3011
|
+
This is the intended path for KodaX Space, IDE adapters, and settings UIs:
|
|
2787
3012
|
session defaults go through `sessions.updateSettings()`, one-turn overrides go
|
|
2788
3013
|
through `runs.start({ options })`, and daemon/profile config goes through
|
|
2789
3014
|
`config`, `catalog`, and `mcp`.
|
|
@@ -2803,19 +3028,19 @@ engine:
|
|
|
2803
3028
|
Hosts that set `capabilities.contextDiagnostics: true` can read:
|
|
2804
3029
|
|
|
2805
3030
|
```ts
|
|
2806
|
-
const budget = await runtime.diagnostics.latestContextBudget({ sessionId });
|
|
2807
|
-
const exposure = await runtime.diagnostics.latestToolExposure({ sessionId });
|
|
2808
|
-
const cache = await runtime.diagnostics.latestProviderCacheDiagnostic({ sessionId });
|
|
2809
|
-
```
|
|
2810
|
-
|
|
2811
|
-
Pass `{ sessionId, contextKind: 'child', agentId }` to query one logical child
|
|
2812
|
-
even though its physical transcript uses an isolated Session. Diagnostic
|
|
2813
|
-
payloads carry `contextId` and, for children, `parentContextId`; reconnecting
|
|
2814
|
-
hosts can use the same latest APIs instead of fabricating identity fields.
|
|
2815
|
-
These diagnostics are designed for status panels and debugging. Budget
|
|
2816
|
-
snapshots contain counts, and cache diagnostics contain hashes plus
|
|
2817
|
-
Provider-reported usage; they do not contain raw prompt or sensitive tool
|
|
2818
|
-
input/output.
|
|
3031
|
+
const budget = await runtime.diagnostics.latestContextBudget({ sessionId });
|
|
3032
|
+
const exposure = await runtime.diagnostics.latestToolExposure({ sessionId });
|
|
3033
|
+
const cache = await runtime.diagnostics.latestProviderCacheDiagnostic({ sessionId });
|
|
3034
|
+
```
|
|
3035
|
+
|
|
3036
|
+
Pass `{ sessionId, contextKind: 'child', agentId }` to query one logical child
|
|
3037
|
+
even though its physical transcript uses an isolated Session. Diagnostic
|
|
3038
|
+
payloads carry `contextId` and, for children, `parentContextId`; reconnecting
|
|
3039
|
+
hosts can use the same latest APIs instead of fabricating identity fields.
|
|
3040
|
+
These diagnostics are designed for status panels and debugging. Budget
|
|
3041
|
+
snapshots contain counts, and cache diagnostics contain hashes plus
|
|
3042
|
+
Provider-reported usage; they do not contain raw prompt or sensitive tool
|
|
3043
|
+
input/output.
|
|
2819
3044
|
|
|
2820
3045
|
### Protocol schema and versioning
|
|
2821
3046
|
|
|
@@ -2834,7 +3059,11 @@ import {
|
|
|
2834
3059
|
The schema is additive within this patch line. Removing or changing required
|
|
2835
3060
|
fields requires a protocol version bump.
|
|
2836
3061
|
|
|
2837
|
-
###
|
|
3062
|
+
### v0.7.69 Runtime verification record
|
|
3063
|
+
|
|
3064
|
+
This subsection is the historical verification record for the original shared
|
|
3065
|
+
Runtime delivery. Current 0.7.80 release gates and evidence live in
|
|
3066
|
+
[`docs/release.md`](release.md#v0780-release-preparation).
|
|
2838
3067
|
|
|
2839
3068
|
The v0.7.69 release validation covers the runtime migration, the Worker
|
|
2840
3069
|
isolation follow-ups delivered ahead of their original v0.7.71/v0.7.72
|
|
@@ -2912,7 +3141,7 @@ daemon DTO boundary. Install factories where the Runtime owner executes:
|
|
|
2912
3141
|
| Private in-process owner | `createKodaXRuntime({ mode: 'embedded', isolation: 'inline', externalAgents })` |
|
|
2913
3142
|
| New locally hosted daemon owner | `createKodaXRuntime({ mode: 'daemon', profile: '<unique>', externalAgents })` |
|
|
2914
3143
|
| Existing daemon | Configure its owner, then attach with `connectKodaXRuntime({ requirements: { externalAgents: true } })`; a client cannot inject factories. |
|
|
2915
|
-
| Runtime Worker |
|
|
3144
|
+
| Runtime Worker | Use `worker: { configuredA2A: true }` for the built-in configured A2A plane. Custom factories must still be installed by a custom Worker owner; passing function-valued `externalAgents` from the parent is rejected. |
|
|
2916
3145
|
|
|
2917
3146
|
When `mode: 'daemon'` and `externalAgents` are supplied, the caller must win a
|
|
2918
3147
|
new in-process daemon lease. KodaX rejects an already-running profile instead of
|
|
@@ -3230,7 +3459,7 @@ workflow's structured findings, verification results, and quality gates.
|
|
|
3230
3459
|
|
|
3231
3460
|
---
|
|
3232
3461
|
|
|
3233
|
-
## 21. Experimental governed memory — `/experimental-memory` (FEATURE_260 + FEATURE_275, v0.7.68–v0.7.77)
|
|
3462
|
+
## 21. Experimental governed memory — `/experimental-memory` (FEATURE_260 + FEATURE_275, v0.7.68–v0.7.77)
|
|
3234
3463
|
|
|
3235
3464
|
KodaX has one durable memory plane: the F228 Memory Control Plane. FEATURE_260
|
|
3236
3465
|
adds a thin, opt-in agent/session API over that plane; it does not add a second
|
|
@@ -3291,53 +3520,53 @@ await session.complete({
|
|
|
3291
3520
|
await session.close();
|
|
3292
3521
|
```
|
|
3293
3522
|
|
|
3294
|
-
`recall()` is synchronous and exact. `query()` is deliberate and read-only; one
|
|
3295
|
-
distinct query is admitted per decision epoch, and the result is bounded to at
|
|
3296
|
-
most three prompt-safe hints and 512 estimated tokens. `undefined` means there
|
|
3297
|
-
is no governed reminder to inject.
|
|
3298
|
-
|
|
3299
|
-
### Sparse foreground intervention (FEATURE_275, v0.7.77)
|
|
3300
|
-
|
|
3301
|
-
`MemorySession.intervene()` replaces the old timing-ineffective semantic
|
|
3302
|
-
prefetch. It is awaited only after `tool_failure`, `verification_failure`, or a
|
|
3303
|
-
durably committed `context_compacted` event, then supplies at most three
|
|
3304
|
-
prompt-safe, low-authority evidence items to the next Action-LLM request. The
|
|
3305
|
-
candidate set is rebuilt from current objective/open todos, recent governed
|
|
3306
|
-
observations, and a fresh F228 pack. Exact selection is deterministic; stale,
|
|
3307
|
-
unknown, malformed, timed-out, cancelled, or failed semantic results are
|
|
3308
|
-
discarded without blocking the coding run.
|
|
3309
|
-
|
|
3310
|
-
Top-level coding runs wire the deterministic path automatically and make zero
|
|
3311
|
-
selector calls by default. An inline host that deliberately wants semantic
|
|
3312
|
-
selection can provide the coding-owned forced-tool runner:
|
|
3313
|
-
|
|
3314
|
-
```ts
|
|
3315
|
-
import {
|
|
3316
|
-
createCodingMemoryInterventionRunner,
|
|
3317
|
-
runKodaX,
|
|
3318
|
-
} from '@kodax-ai/kodax/coding';
|
|
3319
|
-
import { resolveProvider } from '@kodax-ai/kodax/llm';
|
|
3320
|
-
|
|
3321
|
-
const memoryRecallRunner = createCodingMemoryInterventionRunner({
|
|
3322
|
-
provider: resolveProvider('zhipu-coding'),
|
|
3323
|
-
model: 'glm-5.2',
|
|
3324
|
-
});
|
|
3325
|
-
|
|
3326
|
-
await runKodaX(
|
|
3327
|
-
{
|
|
3328
|
-
provider: 'zhipu-coding',
|
|
3329
|
-
model: 'glm-5.2',
|
|
3330
|
-
memoryRecallRunner,
|
|
3331
|
-
},
|
|
3332
|
-
'Finish the repository migration and verify it.',
|
|
3333
|
-
);
|
|
3334
|
-
```
|
|
3335
|
-
|
|
3336
|
-
`memoryRecallRunner` is a process-local function binding. Worker and daemon DTO
|
|
3337
|
-
options reject it instead of silently dropping it; configure the binding
|
|
3338
|
-
inside the Runtime owner or keep this run inline. The selector can return only
|
|
3339
|
-
exact IDs from the closed offered set and is capped at three calls per memory
|
|
3340
|
-
Session.
|
|
3523
|
+
`recall()` is synchronous and exact. `query()` is deliberate and read-only; one
|
|
3524
|
+
distinct query is admitted per decision epoch, and the result is bounded to at
|
|
3525
|
+
most three prompt-safe hints and 512 estimated tokens. `undefined` means there
|
|
3526
|
+
is no governed reminder to inject.
|
|
3527
|
+
|
|
3528
|
+
### Sparse foreground intervention (FEATURE_275, v0.7.77)
|
|
3529
|
+
|
|
3530
|
+
`MemorySession.intervene()` replaces the old timing-ineffective semantic
|
|
3531
|
+
prefetch. It is awaited only after `tool_failure`, `verification_failure`, or a
|
|
3532
|
+
durably committed `context_compacted` event, then supplies at most three
|
|
3533
|
+
prompt-safe, low-authority evidence items to the next Action-LLM request. The
|
|
3534
|
+
candidate set is rebuilt from current objective/open todos, recent governed
|
|
3535
|
+
observations, and a fresh F228 pack. Exact selection is deterministic; stale,
|
|
3536
|
+
unknown, malformed, timed-out, cancelled, or failed semantic results are
|
|
3537
|
+
discarded without blocking the coding run.
|
|
3538
|
+
|
|
3539
|
+
Top-level coding runs wire the deterministic path automatically and make zero
|
|
3540
|
+
selector calls by default. An inline host that deliberately wants semantic
|
|
3541
|
+
selection can provide the coding-owned forced-tool runner:
|
|
3542
|
+
|
|
3543
|
+
```ts
|
|
3544
|
+
import {
|
|
3545
|
+
createCodingMemoryInterventionRunner,
|
|
3546
|
+
runKodaX,
|
|
3547
|
+
} from '@kodax-ai/kodax/coding';
|
|
3548
|
+
import { resolveProvider } from '@kodax-ai/kodax/llm';
|
|
3549
|
+
|
|
3550
|
+
const memoryRecallRunner = createCodingMemoryInterventionRunner({
|
|
3551
|
+
provider: resolveProvider('zhipu-coding'),
|
|
3552
|
+
model: 'glm-5.2',
|
|
3553
|
+
});
|
|
3554
|
+
|
|
3555
|
+
await runKodaX(
|
|
3556
|
+
{
|
|
3557
|
+
provider: 'zhipu-coding',
|
|
3558
|
+
model: 'glm-5.2',
|
|
3559
|
+
memoryRecallRunner,
|
|
3560
|
+
},
|
|
3561
|
+
'Finish the repository migration and verify it.',
|
|
3562
|
+
);
|
|
3563
|
+
```
|
|
3564
|
+
|
|
3565
|
+
`memoryRecallRunner` is a process-local function binding. Worker and daemon DTO
|
|
3566
|
+
options reject it instead of silently dropping it; configure the binding
|
|
3567
|
+
inside the Runtime owner or keep this run inline. The selector can return only
|
|
3568
|
+
exact IDs from the closed offered set and is capped at three calls per memory
|
|
3569
|
+
Session.
|
|
3341
3570
|
|
|
3342
3571
|
### Evidence, tracing, and persistence boundaries
|
|
3343
3572
|
|
|
@@ -3347,11 +3576,11 @@ Session.
|
|
|
3347
3576
|
material. `rewind()` removes observations after a sequence boundary.
|
|
3348
3577
|
- `complete()` can emit an Outcome Digest through `persistOutcomeDigest` and run
|
|
3349
3578
|
bounded episode review through `reviewEpisode`; cancellation creates neither.
|
|
3350
|
-
- `onTrace` receives policy-versioned `MemoryDecisionReceipt` metadata that links
|
|
3351
|
-
offered `candidateIds`, validated `selectedCandidateIds`, exposed
|
|
3352
|
-
`injectedEvidenceRefs`, triggers, and later outcome influence. Receipts are
|
|
3353
|
-
trace-only and contain no hidden reasoning; exposure is not proof of
|
|
3354
|
-
causality.
|
|
3579
|
+
- `onTrace` receives policy-versioned `MemoryDecisionReceipt` metadata that links
|
|
3580
|
+
offered `candidateIds`, validated `selectedCandidateIds`, exposed
|
|
3581
|
+
`injectedEvidenceRefs`, triggers, and later outcome influence. Receipts are
|
|
3582
|
+
trace-only and contain no hidden reasoning; exposure is not proof of
|
|
3583
|
+
causality.
|
|
3355
3584
|
- Durable memory mutation remains owned by F228's
|
|
3356
3585
|
proposal/preview/fingerprint/apply flow. `/memory` is the CLI governance
|
|
3357
3586
|
surface; direct file/shell writes to managed memory roots are denied.
|
|
@@ -3502,6 +3731,23 @@ kodax a2a test reviewer
|
|
|
3502
3731
|
kodax a2a call reviewer "Review this document"
|
|
3503
3732
|
```
|
|
3504
3733
|
|
|
3734
|
+
Private-address access and plaintext HTTP beyond exact loopback are independent
|
|
3735
|
+
persisted permissions. Both default to false; private HTTP requires both:
|
|
3736
|
+
|
|
3737
|
+
```bash
|
|
3738
|
+
kodax a2a add intranet http://10.20.30.40/.well-known/agent-card.json \
|
|
3739
|
+
--allow-private --allow-insecure-http --effect read
|
|
3740
|
+
```
|
|
3741
|
+
|
|
3742
|
+
Private HTTPS requires only `--allow-private`. Public HTTP requires only
|
|
3743
|
+
`--allow-insecure-http`, but TLS remains the recommended deployment. The flags
|
|
3744
|
+
are persisted under the Agent's `network` block and are honored by Card
|
|
3745
|
+
discovery, interface execution, Runtime registration, and task execution.
|
|
3746
|
+
Private-address permission also applies to HTTPS OAuth endpoints, but OAuth
|
|
3747
|
+
token endpoints deliberately retain the stricter HTTPS-or-exact-loopback
|
|
3748
|
+
protocol rule; Agent-transport plaintext authorization does not weaken OAuth.
|
|
3749
|
+
`a2a test` and `a2a call` also accept the flags as one-shot overrides.
|
|
3750
|
+
|
|
3505
3751
|
The no-code OAuth path stores only the environment-variable name for the client
|
|
3506
3752
|
secret. It can be staged disabled and hot-activated later:
|
|
3507
3753
|
|
|
@@ -3524,8 +3770,9 @@ Embedded CLI Runtimes and the user-owned daemon automatically reconcile these
|
|
|
3524
3770
|
entries as `external:<name>`. Discovery/update failure retains that entry's
|
|
3525
3771
|
last-known-good registration; another entry can still update. The environment
|
|
3526
3772
|
broker resolves `credentialEnv` only at call time. Automatic Runtime
|
|
3527
|
-
registration accepts public HTTPS and exact loopback targets;
|
|
3528
|
-
|
|
3773
|
+
registration accepts public HTTPS and exact loopback targets; private-address
|
|
3774
|
+
and non-loopback plaintext access require their independent persisted operator
|
|
3775
|
+
permissions.
|
|
3529
3776
|
|
|
3530
3777
|
`enabled` is desired state in `a2a.json`, not a fabricated cross-process live
|
|
3531
3778
|
flag. `a2a list` reports configured entries and that desired state. The owning
|
|
@@ -3699,16 +3946,16 @@ const server = createKodaXA2AServer({
|
|
|
3699
3946
|
},
|
|
3700
3947
|
});
|
|
3701
3948
|
|
|
3702
|
-
// Development only: the built-in listener refuses non-loopback hosts.
|
|
3703
|
-
const localBaseUrl = await server.listen({ hostname: '127.0.0.1', port: 0 });
|
|
3704
|
-
```
|
|
3705
|
-
|
|
3706
|
-
The listener also refuses an explicit port blocked by WHATWG Fetch clients.
|
|
3707
|
-
With `port: 0`, it retries ephemeral allocation rather than returning a URL
|
|
3708
|
-
that Fetch would reject before connecting.
|
|
3709
|
-
|
|
3710
|
-
Production hosts route `GET /.well-known/agent-card.json` and canonical
|
|
3711
|
-
JSON-RPC `POST /a2a` to `server.handle(request)` behind their own TLS
|
|
3949
|
+
// Development only: the built-in listener refuses non-loopback hosts.
|
|
3950
|
+
const localBaseUrl = await server.listen({ hostname: '127.0.0.1', port: 0 });
|
|
3951
|
+
```
|
|
3952
|
+
|
|
3953
|
+
The listener also refuses an explicit port blocked by WHATWG Fetch clients.
|
|
3954
|
+
With `port: 0`, it retries ephemeral allocation rather than returning a URL
|
|
3955
|
+
that Fetch would reject before connecting.
|
|
3956
|
+
|
|
3957
|
+
Production hosts route `GET /.well-known/agent-card.json` and canonical
|
|
3958
|
+
JSON-RPC `POST /a2a` to `server.handle(request)` behind their own TLS
|
|
3712
3959
|
terminator. `POST /` remains an accepted compatibility alias. `listen()` waits for durable recovery before
|
|
3713
3960
|
it resolves. A host that wires `handle()` directly may explicitly await
|
|
3714
3961
|
`server.whenReady()` before it starts accepting traffic; `handle()` also waits
|
|
@@ -3792,14 +4039,14 @@ SDK does not relaunch the GUI or silently fall back to an inline Runtime.
|
|
|
3792
4039
|
```ts
|
|
3793
4040
|
import { connectKodaXRuntime } from '@kodax-ai/kodax/runtime';
|
|
3794
4041
|
|
|
3795
|
-
const runtime = await connectKodaXRuntime({
|
|
3796
|
-
profile: 'coder',
|
|
3797
|
-
autoStart: true,
|
|
3798
|
-
// Opt in only when this product remains the visible owner of the daemon.
|
|
3799
|
-
// If the product crashes, the daemon stops after its final client is gone
|
|
3800
|
-
// and governed work becomes idle.
|
|
3801
|
-
daemonOrphanExitMs: 30_000,
|
|
3802
|
-
homeDir: coderRuntimeBaseDir, // owns <coderRuntimeBaseDir>/.kodax
|
|
4042
|
+
const runtime = await connectKodaXRuntime({
|
|
4043
|
+
profile: 'coder',
|
|
4044
|
+
autoStart: true,
|
|
4045
|
+
// Opt in only when this product remains the visible owner of the daemon.
|
|
4046
|
+
// If the product crashes, the daemon stops after its final client is gone
|
|
4047
|
+
// and governed work becomes idle.
|
|
4048
|
+
daemonOrphanExitMs: 30_000,
|
|
4049
|
+
homeDir: coderRuntimeBaseDir, // owns <coderRuntimeBaseDir>/.kodax
|
|
3803
4050
|
clientInfo: {
|
|
3804
4051
|
name: 'kodax-space',
|
|
3805
4052
|
version: '0.1.32',
|
|
@@ -3831,10 +4078,11 @@ const runtime = await connectKodaXRuntime({
|
|
|
3831
4078
|
sharedSessionSettings: 1,
|
|
3832
4079
|
durableRecoveryQueries: 1,
|
|
3833
4080
|
daemonManagement: 1,
|
|
3834
|
-
|
|
3835
|
-
|
|
3836
|
-
}
|
|
3837
|
-
|
|
4081
|
+
runtimeEventCoalescing: 1,
|
|
4082
|
+
runtimeAutoModeGuardrail: 4,
|
|
4083
|
+
},
|
|
4084
|
+
});
|
|
4085
|
+
```
|
|
3838
4086
|
|
|
3839
4087
|
Requirements are server facts, not authorization requests. Check
|
|
3840
4088
|
`runtime.grantedScopes` before enabling controls. Missing capabilities or
|
|
@@ -3844,35 +4092,54 @@ Coder. Products that depend on same-Run delivery should require
|
|
|
3844
4092
|
(for example, SA execution) still return `unsupported_capability`; do not
|
|
3845
4093
|
silently substitute `delivery:'after_turn'` unless that is the user's intent.
|
|
3846
4094
|
|
|
3847
|
-
The SDK requires `runtimeAutoModeGuardrail:4` automatically for ordinary
|
|
3848
|
-
`autoStart: true`. Supplying `daemonOrphanExitMs` additionally requires the
|
|
3849
|
-
dedicated `daemonOrphanExit:1` capability and passes the option only when
|
|
3850
|
-
spawning a new daemon. It does not silently reinterpret an already-running
|
|
3851
|
-
persistent daemon: the SDK uses the normal fenced capability-upgrade path and
|
|
3852
|
-
replaces it only when preflight proves that doing so is safe. After the daemon
|
|
3853
|
-
has observed a logical client, final-client detach arms the requested grace
|
|
3854
|
-
period; a new client cancels it, and active/queued runs, Workflow, Agent turns,
|
|
3855
|
-
pending permission/user input, or other governed work defer exit until
|
|
3856
|
-
preflight is idle. Omit the option for CLI-style daemons that are intentionally
|
|
3857
|
-
persistent.
|
|
3858
|
-
|
|
3859
|
-
An embedder can inspect `KODAX_RUNTIME_SDK_CAPABILITIES.daemonOrphanExit`
|
|
3860
|
-
before calling an auto-start API. This prevents an older SDK from spawning a
|
|
3861
|
-
persistent daemon and only then discovering that it cannot honor the requested
|
|
3862
|
-
lifecycle policy. The connected daemon capability remains the authoritative
|
|
3863
|
-
check that the current host actually enabled the policy.
|
|
3864
|
-
|
|
3865
|
-
|
|
3866
|
-
|
|
3867
|
-
|
|
3868
|
-
|
|
3869
|
-
|
|
3870
|
-
|
|
3871
|
-
|
|
3872
|
-
|
|
3873
|
-
|
|
3874
|
-
|
|
3875
|
-
|
|
4095
|
+
The SDK requires `runtimeAutoModeGuardrail:4` automatically for ordinary
|
|
4096
|
+
`autoStart: true`. Supplying `daemonOrphanExitMs` additionally requires the
|
|
4097
|
+
dedicated `daemonOrphanExit:1` capability and passes the option only when
|
|
4098
|
+
spawning a new daemon. It does not silently reinterpret an already-running
|
|
4099
|
+
persistent daemon: the SDK uses the normal fenced capability-upgrade path and
|
|
4100
|
+
replaces it only when preflight proves that doing so is safe. After the daemon
|
|
4101
|
+
has observed a logical client, final-client detach arms the requested grace
|
|
4102
|
+
period; a new client cancels it, and active/queued runs, Workflow, Agent turns,
|
|
4103
|
+
pending permission/user input, or other governed work defer exit until
|
|
4104
|
+
preflight is idle. Omit the option for CLI-style daemons that are intentionally
|
|
4105
|
+
persistent.
|
|
4106
|
+
|
|
4107
|
+
An embedder can inspect `KODAX_RUNTIME_SDK_CAPABILITIES.daemonOrphanExit`
|
|
4108
|
+
before calling an auto-start API. This prevents an older SDK from spawning a
|
|
4109
|
+
persistent daemon and only then discovering that it cannot honor the requested
|
|
4110
|
+
lifecycle policy. The connected daemon capability remains the authoritative
|
|
4111
|
+
check that the current host actually enabled the policy.
|
|
4112
|
+
|
|
4113
|
+
`KODAX_RUNTIME_SDK_CAPABILITIES.runtimeEventCoalescing` performs the same
|
|
4114
|
+
local-SDK preflight for bounded source-level text/reasoning coalescing. Requiring
|
|
4115
|
+
`runtimeEventCoalescing:1` lets auto-start replace only an idle legacy daemon;
|
|
4116
|
+
a busy or otherwise unsafe owner produces the normal capability-upgrade error.
|
|
4117
|
+
The capability changes event allocation/persistence pressure, not reconstructed
|
|
4118
|
+
stream content: flush boundaries remain explicit and an accumulated merge
|
|
4119
|
+
never exceeds 8 KiB.
|
|
4120
|
+
|
|
4121
|
+
Observation boundaries such as `events.subscribe()`, `events.replay()`, and
|
|
4122
|
+
Session status projection flush pending events before answering, so they can
|
|
4123
|
+
surface a durable-persistence failure instead of returning a stale waterline.
|
|
4124
|
+
A determinate append failure retains one bounded batch for a later explicit
|
|
4125
|
+
retry. If both append and rollback fail, commit state is unknowable: the error
|
|
4126
|
+
is intentionally sticky and the Runtime must be closed/recreated rather than
|
|
4127
|
+
guessing or replaying the batch. Latest-only progress coalescing preserves its
|
|
4128
|
+
required first sample plus the most recent sample; discarded intermediate
|
|
4129
|
+
snapshots are not lifecycle events, and surviving samples retain their latest
|
|
4130
|
+
emission order.
|
|
4131
|
+
|
|
4132
|
+
When a healthy profile daemon is too old, the SDK first requires
|
|
4133
|
+
`daemonManagement:1`, takes a revision/owner-policy fenced preflight, and
|
|
4134
|
+
replaces it only when no active or queued run, Workflow, Agent turn, pending
|
|
4135
|
+
permission/user input, or other logical client exists. A busy or still-older
|
|
4136
|
+
daemon is never stopped: the connection rejects with
|
|
4137
|
+
`RuntimeDaemonCapabilityUpgradeError`, whose `recoverable` and
|
|
4138
|
+
`restartRequired` fields are `true` and whose optional `preflight` explains the
|
|
4139
|
+
blockers. Attach-only connections never mutate daemon ownership and must request
|
|
4140
|
+
the exact capabilities they depend on. Capability requirements are minimum
|
|
4141
|
+
versions: v4 satisfies v1-v3, v3 satisfies v1/v2, v2 satisfies v1, and an older
|
|
4142
|
+
daemon never satisfies a newer requirement.
|
|
3876
4143
|
|
|
3877
4144
|
The `coderFeatureMatrix` capability reports daemon availability for managed
|
|
3878
4145
|
runs, transcript/session operations, Todo projection, managed tasks, Workflow,
|
|
@@ -3891,11 +4158,116 @@ or Host Tool leases; only its hash participates in daemon-owned bridge state.
|
|
|
3891
4158
|
Keep all three values in Electron Main. Mutually distrusting processes running
|
|
3892
4159
|
as the same OS account remain outside this release's threat model.
|
|
3893
4160
|
|
|
4161
|
+
### Query authoritative Session and Run lifecycle
|
|
4162
|
+
|
|
4163
|
+
Use `runtime.runs.get(runId)` for the exact Run and
|
|
4164
|
+
`runtime.sessions.status(sessionId)` for the one current Session projection.
|
|
4165
|
+
Do not infer completion from assistant text:
|
|
4166
|
+
|
|
4167
|
+
```ts
|
|
4168
|
+
const run = await runtime.runs.get(runId);
|
|
4169
|
+
const sessionStatus = await runtime.sessions.status(sessionId);
|
|
4170
|
+
|
|
4171
|
+
switch (run.phase) {
|
|
4172
|
+
case 'running':
|
|
4173
|
+
case 'waiting_agent':
|
|
4174
|
+
case 'recovering':
|
|
4175
|
+
case 'waiting_permission':
|
|
4176
|
+
case 'waiting_user_input':
|
|
4177
|
+
renderActive(run.phase);
|
|
4178
|
+
break;
|
|
4179
|
+
case 'unknown':
|
|
4180
|
+
renderUnconfirmedExecution(run.error);
|
|
4181
|
+
break;
|
|
4182
|
+
case 'completed':
|
|
4183
|
+
case 'cancelled':
|
|
4184
|
+
case 'interrupted':
|
|
4185
|
+
case 'failed':
|
|
4186
|
+
renderTerminal(run.terminal);
|
|
4187
|
+
break;
|
|
4188
|
+
}
|
|
4189
|
+
```
|
|
4190
|
+
|
|
4191
|
+
`phase` answers whether the Run is queued, active, terminal, or unconfirmed.
|
|
4192
|
+
Use `stage` for the finer executor location: `executing`,
|
|
4193
|
+
`waiting_agent`, `recovering`, managed phases such as `worker` or `verifying`,
|
|
4194
|
+
and `finalizing`. In particular, managed status `completed` first appears as
|
|
4195
|
+
`phase:'running', stage:'finalizing'`; it is not terminal until the outer
|
|
4196
|
+
executor settles. `stageChangedAt` timestamps that transition, and
|
|
4197
|
+
`activeSubtaskCount` is present only when the managed executor reported an
|
|
4198
|
+
authoritative count. If Runtime shutdown begins after an executor terminal
|
|
4199
|
+
callback was latched, that terminal fact is persisted synchronously before the
|
|
4200
|
+
owner-liveness endpoint is released. Result-handle resolution and queue drain
|
|
4201
|
+
still wait for the executor result or the deferred lost-result fallback.
|
|
4202
|
+
Destructive Session mutations remain fenced across the same settlement window.
|
|
4203
|
+
|
|
4204
|
+
Run ownership is private persistence metadata, not a public ordering field. A
|
|
4205
|
+
second Runtime may observe a live owner's status but cannot abort or mutate
|
|
4206
|
+
that Run. A definitely dead owner is recovered to a durable `interrupted`
|
|
4207
|
+
result. If liveness cannot be proven either way, the observer returns
|
|
4208
|
+
`phase: 'unknown'`; it does not report a successful stop and does not overwrite
|
|
4209
|
+
the original non-terminal record. The same rule applies to ownerless legacy
|
|
4210
|
+
non-terminal records. A durable terminal event is reconciled before this
|
|
4211
|
+
fallback; non-terminal evidence such as an input-delivery event may refine the
|
|
4212
|
+
read projection but does not prove that execution stopped. Persisted terminal
|
|
4213
|
+
states are monotonic.
|
|
4214
|
+
|
|
4215
|
+
`runtime.status.preflight()` treats `queued`, all active/waiting/recovery
|
|
4216
|
+
phases, and `unknown` as stop blockers. A host must never infer completion from
|
|
4217
|
+
reply text or convert `unknown` into success.
|
|
4218
|
+
|
|
4219
|
+
`runtime.runs.abort(runId)` requests Stop; it does not manufacture an
|
|
4220
|
+
acknowledgement. Queued work can become `cancelled` immediately. For an active
|
|
4221
|
+
executor, inspect `run.stop`: while termination is unconfirmed, the Run reports
|
|
4222
|
+
`phase/stage:'unknown'` and `stop.state/outcome:'unknown'`. A later executor
|
|
4223
|
+
result confirms the actual outcome, including `completed` when the executor
|
|
4224
|
+
ignored Stop and completed normally. Terminal callback order is latched before
|
|
4225
|
+
deferred result settlement: Stop cannot rewrite an earlier completion as
|
|
4226
|
+
interrupted, and late finalizer/recovery progress cannot revive an unconfirmed
|
|
4227
|
+
stopped Run. If the Runtime owner then dies, recovery resolves the pending Stop
|
|
4228
|
+
as `confirmed/interrupted`.
|
|
4229
|
+
|
|
4230
|
+
For support bundles, use the pure read-only compositor:
|
|
4231
|
+
|
|
4232
|
+
```ts
|
|
4233
|
+
import { captureRuntimeSessionDiagnostics } from '@kodax-ai/kodax/runtime';
|
|
4234
|
+
|
|
4235
|
+
const diagnostic = await captureRuntimeSessionDiagnostics(runtime, {
|
|
4236
|
+
sessionId,
|
|
4237
|
+
runId,
|
|
4238
|
+
timeoutMs: 10_000,
|
|
4239
|
+
signal: abortController.signal,
|
|
4240
|
+
});
|
|
4241
|
+
```
|
|
4242
|
+
|
|
4243
|
+
The schema-versioned result includes SDK/Runtime/daemon versions, Runtime and
|
|
4244
|
+
Session identity, observation cursor/transcript revision, Run/Turn identity,
|
|
4245
|
+
phase/stage and stage time, terminal fact/time, Run-owned active child count,
|
|
4246
|
+
Stop and chat-interrupt records, and structured errors. If the selected Run
|
|
4247
|
+
did not persist an authoritative child count, the result returns
|
|
4248
|
+
`activeSubtaskCount:null` and `activeSubtaskCountSource:'unknown'`; it never
|
|
4249
|
+
attributes a later Session-wide child sample to that Run. If a historical
|
|
4250
|
+
Session has no Run control record, it returns `controlRecord:'unknown'` with
|
|
4251
|
+
`run_control_unknown`; it never infers completion from transcript text.
|
|
4252
|
+
`owner_liveness_unconfirmed`, `owner_recovery_required`,
|
|
4253
|
+
`stop_outcome_unconfirmed`, `run_failed`, `run_status_unknown`, and
|
|
4254
|
+
`terminal_time_unknown` remain distinct. These are independent facts rather
|
|
4255
|
+
than an enum: for example, an unavailable owner and an unconfirmed Stop appear
|
|
4256
|
+
together in `errors`. `sdkVersion` is the calling SDK package version;
|
|
4257
|
+
`runtimeVersion` and `daemonVersion` identify the connected daemon in daemon
|
|
4258
|
+
mode, so a support bundle preserves version skew. One timeout/cancellation
|
|
4259
|
+
budget covers the transcript, settings, pending interactions, and owner
|
|
4260
|
+
liveness inspection. The helper uses the dedicated
|
|
4261
|
+
`sessions.diagnostics()` read boundary: it never runs status preflight,
|
|
4262
|
+
resumes, repairs, migrates, takes ownership, or evicts a retained transcript
|
|
4263
|
+
page boundary. Embedded and daemon facades use the same schema-validated
|
|
4264
|
+
`session.diagnostics` contract.
|
|
4265
|
+
|
|
3894
4266
|
### Join atomically and resync after disconnect
|
|
3895
4267
|
|
|
3896
4268
|
`sessions.observe()` installs the live subscription before taking the
|
|
3897
4269
|
snapshot. Its snapshot contains one authoritative `runtimeId`, cursor,
|
|
3898
|
-
`transcriptRevision`,
|
|
4270
|
+
`transcriptRevision`, bounded transcript slice, versioned settings, run/queue state,
|
|
3899
4271
|
queued continuation IDs/order/origin/safe previews, pending permission and
|
|
3900
4272
|
AskUser requests, and live assistant/thinking/tool/Todo/managed-task
|
|
3901
4273
|
projection. Run requirements include the current credential/Host Tool
|
|
@@ -3918,6 +4290,10 @@ async function openCoderSession(sessionId: string) {
|
|
|
3918
4290
|
observedRuntimeId = snapshot.runtimeId;
|
|
3919
4291
|
lastCursor = snapshot.cursor;
|
|
3920
4292
|
replaceSessionProjection(snapshot, { runtimeChanged });
|
|
4293
|
+
void observation.invalidated.then((reason) => {
|
|
4294
|
+
discardSessionProjection(reason);
|
|
4295
|
+
scheduleFreshObservation(sessionId);
|
|
4296
|
+
});
|
|
3921
4297
|
return observation;
|
|
3922
4298
|
}
|
|
3923
4299
|
```
|
|
@@ -3944,9 +4320,14 @@ On transport failure, Runtime change, expired history, or `resync_required`,
|
|
|
3944
4320
|
discard the local derived projection and call `sessions.observe()` again. Do
|
|
3945
4321
|
not merge a new snapshot into the old projection. The handshake buffer is
|
|
3946
4322
|
bounded; overflow fails explicitly instead of dropping events. A Runtime
|
|
3947
|
-
restart changes `runtimeId
|
|
3948
|
-
|
|
3949
|
-
|
|
4323
|
+
restart changes `runtimeId`. The observation's `invalidated` promise reports
|
|
4324
|
+
`event_overflow`, `event_order`, `runtime_changed`, or
|
|
4325
|
+
`transport_disconnected`; after it resolves, no state derived from that
|
|
4326
|
+
observation remains authoritative. Restart recovery interrupts only a Run
|
|
4327
|
+
whose owner is definitely gone; uncertain external execution is `unknown`.
|
|
4328
|
+
Timeout or cancellation removes the daemon request immediately. If a
|
|
4329
|
+
third-party transport ignores cancellation and later returns an observation,
|
|
4330
|
+
the client compensates by unsubscribing that late observation.
|
|
3950
4331
|
|
|
3951
4332
|
### Durable mutations, stable ordering, and settings CAS
|
|
3952
4333
|
|
|
@@ -4022,17 +4403,17 @@ complete ordered batch. Exact operation retries return the same `inputId`.
|
|
|
4022
4403
|
The accepted result's `runId` is the existing owning Run (equal to
|
|
4023
4404
|
`afterRunId`), not a newly created continuation.
|
|
4024
4405
|
|
|
4025
|
-
Interrupt admission closes when the Runner publishes its final completion or
|
|
4026
|
-
terminal error signal, or when the Run's supplied `abortSignal` aborts, even if
|
|
4027
|
-
the outer Run is still settling. Non-terminal observer diagnostics do not close
|
|
4028
|
-
the window. Same-Run lifecycle continuations also have a fixed internal
|
|
4029
|
-
allowance beyond the configured iteration ceiling; the Runner closes admission
|
|
4030
|
-
before its final absolute generation so repeated submissions cannot keep one
|
|
4031
|
-
Run alive indefinitely. The allowance never expands an admitted manifest's
|
|
4032
|
-
`maxIterations` governance cap. A submission after closure returns
|
|
4033
|
-
`accepted:false` with `reason:'interrupt_window_closed'` and is not queued. Keep
|
|
4034
|
-
the original input available for retry after the Run ends; do not silently
|
|
4035
|
-
change its delivery to `after_turn`. As a final race/recovery
|
|
4406
|
+
Interrupt admission closes when the Runner publishes its final completion or
|
|
4407
|
+
terminal error signal, or when the Run's supplied `abortSignal` aborts, even if
|
|
4408
|
+
the outer Run is still settling. Non-terminal observer diagnostics do not close
|
|
4409
|
+
the window. Same-Run lifecycle continuations also have a fixed internal
|
|
4410
|
+
allowance beyond the configured iteration ceiling; the Runner closes admission
|
|
4411
|
+
before its final absolute generation so repeated submissions cannot keep one
|
|
4412
|
+
Run alive indefinitely. The allowance never expands an admitted manifest's
|
|
4413
|
+
`maxIterations` governance cap. A submission after closure returns
|
|
4414
|
+
`accepted:false` with `reason:'interrupt_window_closed'` and is not queued. Keep
|
|
4415
|
+
the original input available for retry after the Run ends; do not silently
|
|
4416
|
+
change its delivery to `after_turn`. As a final race/recovery
|
|
4036
4417
|
guard, inspect terminal Run status: any `interruptInputs` entry whose state is
|
|
4037
4418
|
`terminal` was not delivered. Reconcile it by `inputId` and present a visible
|
|
4038
4419
|
non-delivery outcome rather than leaving a pending queue indicator.
|
|
@@ -4059,6 +4440,16 @@ query `runtime.operations.get({ operationId, journalEpoch })`; applied receipts
|
|
|
4059
4440
|
include the canonical result. Permission grants remain daemon-owned and
|
|
4060
4441
|
revisioned.
|
|
4061
4442
|
|
|
4443
|
+
Runtime startup restores all indexed active Runs and at most 200 recent
|
|
4444
|
+
terminal Runs from a bounded durable status index. `runs.get(runId)` remains an
|
|
4445
|
+
exact persisted lookup even when an older terminal Run is outside that recent
|
|
4446
|
+
window. The first start after upgrading an unindexed home may perform one full
|
|
4447
|
+
compatibility scan. A start after a crash, or a concurrent Runtime starting
|
|
4448
|
+
while a live writer has intentionally left the index dirty, may likewise do
|
|
4449
|
+
one authoritative reconciliation. Normal writers publish canonical
|
|
4450
|
+
`status.json` first and keep the derived index recovery-fenced across those
|
|
4451
|
+
crashes and concurrent Runtime instances.
|
|
4452
|
+
|
|
4062
4453
|
### AskUser and permission from any client
|
|
4063
4454
|
|
|
4064
4455
|
AskUser is no longer an in-process callback for daemon Coder runs. Any client
|
|
@@ -4102,11 +4493,11 @@ Write/Edit content may differ). Generic extension calls can receive only an
|
|
|
4102
4493
|
exact in-memory Session grant. Raw command/argv data is not stored in the
|
|
4103
4494
|
matcher; grants and audit contain only its fingerprint plus a bounded,
|
|
4104
4495
|
secret-redacted operator label. Clients must not keep separate persistent
|
|
4105
|
-
permission rule stores. Runtime capability `runtimeAutoModeGuardrail` v4
|
|
4106
|
-
advertises this opaque concrete-grant contract plus the intent-aligned
|
|
4107
|
-
retry/Accept-edits behavior that never changes the engine to rules. Embedded,
|
|
4108
|
-
Worker, and daemon hosts all report `fallbackPersistsEngine:false`; restart or
|
|
4109
|
-
upgrade an older daemon instead of falling back to a client-side alias.
|
|
4496
|
+
permission rule stores. Runtime capability `runtimeAutoModeGuardrail` v4
|
|
4497
|
+
advertises this opaque concrete-grant contract plus the intent-aligned
|
|
4498
|
+
retry/Accept-edits behavior that never changes the engine to rules. Embedded,
|
|
4499
|
+
Worker, and daemon hosts all report `fallbackPersistsEngine:false`; restart or
|
|
4500
|
+
upgrade an older daemon instead of falling back to a client-side alias.
|
|
4110
4501
|
|
|
4111
4502
|
### Broker a Space keychain credential
|
|
4112
4503
|
|
|
@@ -4330,7 +4721,7 @@ await runtime.sessions.updateSettings(session.id, {
|
|
|
4330
4721
|
permissionMode: 'auto',
|
|
4331
4722
|
autoModeEngine: 'llm',
|
|
4332
4723
|
autoModeClassifierModel: 'zhipu:glm-5.2', // optional; otherwise follow the run model
|
|
4333
|
-
autoModeTimeoutMs:
|
|
4724
|
+
autoModeTimeoutMs: 30_000, // positive safe integer, optional
|
|
4334
4725
|
autoModeSpeculativeWindowMs: 0, // non-negative safe integer, optional
|
|
4335
4726
|
executionCwd: projectDirectory,
|
|
4336
4727
|
});
|
|
@@ -4385,9 +4776,9 @@ engine/denial/breaker state per Session. It reuses bounded context-specific
|
|
|
4385
4776
|
guardrails across turns while provider/model, repository boundary, execution
|
|
4386
4777
|
directory, classifier model, and timeout remain the same. Updating one of
|
|
4387
4778
|
those inputs selects a new context guardrail by design without copying stale
|
|
4388
|
-
state from a queued turn. Active runs, queued runs, explicit settings updates,
|
|
4389
|
-
and explicit engine changes merge through the same Session mutation queue.
|
|
4390
|
-
Classifier infrastructure failures do not mutate the engine to rules.
|
|
4779
|
+
state from a queued turn. Active runs, queued runs, explicit settings updates,
|
|
4780
|
+
and explicit engine changes merge through the same Session mutation queue.
|
|
4781
|
+
Classifier infrastructure failures do not mutate the engine to rules.
|
|
4391
4782
|
|
|
4392
4783
|
### What an embedder should expect
|
|
4393
4784
|
|
|
@@ -4401,35 +4792,129 @@ Exactly modeled ordinary reads and workspace/system-temp mutations are admitted
|
|
|
4401
4792
|
before classifier latency, independent of sandbox readiness. Other calls are
|
|
4402
4793
|
reviewed against the latest genuine user request, bounded user-only intent
|
|
4403
4794
|
evidence, and exact operation facts. Consequently, an LLM/rules `allow` does
|
|
4404
|
-
not create a pending permission request just because a host installed a static
|
|
4405
|
-
approval hook. Classifier concerns, critical deterministic matches, and rules
|
|
4406
|
-
concerns use the existing shared
|
|
4407
|
-
`runtime.permissions` flow, so another authorized client may render and answer
|
|
4795
|
+
not create a pending permission request just because a host installed a static
|
|
4796
|
+
approval hook. Classifier concerns, critical deterministic matches, and rules
|
|
4797
|
+
concerns use the existing shared
|
|
4798
|
+
`runtime.permissions` flow, so another authorized client may render and answer
|
|
4408
4799
|
it. Hosts should subscribe to permission events to display such a request, but
|
|
4409
4800
|
must not treat a missing request as an error for a safe tool call.
|
|
4410
4801
|
|
|
4411
|
-
|
|
4412
|
-
|
|
4413
|
-
|
|
4414
|
-
|
|
4415
|
-
|
|
4416
|
-
|
|
4417
|
-
|
|
4418
|
-
|
|
4419
|
-
|
|
4420
|
-
|
|
4421
|
-
|
|
4422
|
-
|
|
4802
|
+
Direct guardrail embedders should supply `projectRoot` or `executionCwd` for
|
|
4803
|
+
path-bearing calls. If both are absent, KodaX keeps those targets unresolved
|
|
4804
|
+
instead of treating the host process cwd as the user's workspace.
|
|
4805
|
+
|
|
4806
|
+
On PowerShell, deterministic read admission includes independently validated
|
|
4807
|
+
sequential/pipeline stages such as `where.exe`, ordinary `rg` inspection,
|
|
4808
|
+
non-sensitive `$env:NAME` reads, and constrained `Where-Object` /
|
|
4809
|
+
`Select-Object` expressions. This is a structural allowlist, not a blanket
|
|
4810
|
+
"review task" exemption. The executable token must be an admitted bare command
|
|
4811
|
+
name; path-qualified executables, arbitrary `& script.cmd`, effectful `find` /
|
|
4812
|
+
`awk` / `sed` forms, script blocks, sensitive credential environment names,
|
|
4813
|
+
external ripgrep preprocessors, and file redirection continue through the LLM
|
|
4814
|
+
classifier. They reach user approval only when the classifier identifies a
|
|
4815
|
+
concrete hazard (or when its bounded retry and fallback policy requires it).
|
|
4816
|
+
Authenticated child constraints are checked before deterministic admission:
|
|
4817
|
+
for example, `Do not execute shell commands` keeps even a read-only shell call
|
|
4818
|
+
under review, while `Do not modify files` does not make an ordinary read ask.
|
|
4819
|
+
|
|
4820
|
+
The classifier deadline is 45 seconds for the first default attempt and 90
|
|
4821
|
+
seconds for its one retry. A configured `timeoutMs` overrides both attempts
|
|
4822
|
+
with the same explicit deadline. Each deadline includes connection setup,
|
|
4823
|
+
provider Retry-After/backoff, inference, and stream completion. A second
|
|
4824
|
+
timeout/provider/response-contract failure uses the Accept-edits safety
|
|
4825
|
+
boundary and never switches to Auto[rules]. Before the
|
|
4826
|
+
provider call it removes assistant prose/thinking and image paths, limits each
|
|
4827
|
+
tool result to 2 KiB and the serialized permission-relevant transcript to
|
|
4828
|
+
8 KiB, then enforces 16 KiB action and 32 KiB total-prompt ceilings plus a
|
|
4829
|
+
256-token first-attempt output cap. If that answer is truncated before a
|
|
4830
|
+
decision, the retry uses 1024 tokens instead of repeating the same impossible
|
|
4831
|
+
budget. An oversized action or prompt escalates without a provider call; it is
|
|
4832
|
+
never truncated into an automatic allow. These limits are owned by
|
|
4833
|
+
`classify()` itself, so custom callers cannot accidentally bypass the
|
|
4834
|
+
session-history boundary.
|
|
4835
|
+
|
|
4836
|
+
Deterministic read admission keeps ordinary project directories, structured
|
|
4837
|
+
`grep`/`glob`, ripgrep/GNU-grep filters, and other bounded search selectors on
|
|
4838
|
+
the read fast path. A wildcard remains unresolved only when it can reach a
|
|
4839
|
+
protected credential area or its path boundary cannot be established.
|
|
4840
|
+
Explicit PowerShell `-LiteralPath` remains literal. Indirect file lists,
|
|
4841
|
+
dynamic Git pathspecs, and expanded `git grep --no-index`/`--untracked` scopes
|
|
4842
|
+
remain LLM-reviewed when their targets cannot be bounded safely. Git
|
|
4843
|
+
line-log `-L` paths, patch-enabling short-option clusters, and merge-diff modes
|
|
4844
|
+
are resolved before deciding whether output is scoped. Exact-file reads and
|
|
4845
|
+
metadata-only output such as `git show --stat` remain deterministic. Exclusion
|
|
4846
|
+
selectors do not become input targets. On Windows, protected-name checks also
|
|
4847
|
+
cover trailing-dot/space aliases, alternate data streams, and canonicalized
|
|
4848
|
+
existing 8.3 names. Git-config
|
|
4849
|
+
regexp/URL reads account for accepted option abbreviations and effective
|
|
4850
|
+
boolean-option ordering; only complete anchored selectors over known
|
|
4851
|
+
non-secret metadata can take the deterministic fast path. Likewise, incomplete
|
|
4852
|
+
current-user content may still admit a proven read, but a complete current
|
|
4853
|
+
request that explicitly prohibits reading or shell execution routes the
|
|
4854
|
+
matching read-only operation through the classifier. Ordinary review and
|
|
4855
|
+
read-only requests remain deterministic. `currentUserContentTruncated:true` prevents a
|
|
4856
|
+
write, delete, move, copy, or unmodeled execution from relying on the compacted
|
|
4857
|
+
fragment alone. On classifier failure, Accept-edits fallback is available only
|
|
4858
|
+
when the full structured review would pass the same deterministic predicate;
|
|
4859
|
+
protected/unresolved targets and partial/risky reviews remain approval-bound.
|
|
4423
4860
|
|
|
4424
4861
|
`ClassifyDecision.diagnostics` and the lower-level
|
|
4425
4862
|
`SideQueryResult.diagnostics` expose provider, model, effective timeout,
|
|
4426
|
-
elapsed time, retry count/wait,
|
|
4427
|
-
|
|
4428
|
-
|
|
4429
|
-
|
|
4430
|
-
|
|
4431
|
-
|
|
4432
|
-
|
|
4863
|
+
elapsed time, retry count/wait, provider stop reason, response byte/text-block
|
|
4864
|
+
counts, and a coarse terminal phase without including the prompt, action,
|
|
4865
|
+
messages, or response text. `pre_output` means no non-empty text delta was
|
|
4866
|
+
observed; `streaming` means output began before termination. `firstOutputMs`
|
|
4867
|
+
and `streamMs` are present only when the provider adapter emits a text delta.
|
|
4868
|
+
Each classifier attempt may additionally expose `observedProtocol`
|
|
4869
|
+
(`structured_v2`, `legacy_v1`, or `unknown`) and a bounded
|
|
4870
|
+
`parseFailureCode` when no valid unambiguous decision exists. When exactly one
|
|
4871
|
+
valid `decision=allow|ask` (or legacy `block=no|yes`) is present, that decision
|
|
4872
|
+
is authoritative. Missing, malformed, or contradictory `hazard` / `reason`
|
|
4873
|
+
fields and surrounding-format defects are reported as bounded
|
|
4874
|
+
`outputWarnings`; they do not change the decision, consume the retry, increment
|
|
4875
|
+
the circuit breaker, or create an approval request after `allow`. Runtime
|
|
4876
|
+
permission requests copy these fields into `autoModeDiagnostics`, allowing a
|
|
4877
|
+
host to distinguish an LLM confirmation decision (`source:
|
|
4878
|
+
classifier_confirm`) from provider/decision-contract failure (`source:
|
|
4879
|
+
classifier_failure`) without retaining model output. Missing or invalid
|
|
4880
|
+
decision values and ambiguous duplicate/mixed decisions remain contract
|
|
4881
|
+
failures and receive the same single bounded retry. The current
|
|
4882
|
+
provider API cannot honestly separate DNS/connect, TLS, provider queueing, and
|
|
4883
|
+
inference, so embedders must not infer those stages from `pre_output`.
|
|
4884
|
+
Because raw classifier text is deliberately not retained, these diagnostics
|
|
4885
|
+
cannot retroactively prove the shape of an older response. A future
|
|
4886
|
+
`observedProtocol:legacy_v1` value is evidence for that attempt; compatibility
|
|
4887
|
+
with `legacy_v1` alone is not proof that a prior provider returned it.
|
|
4888
|
+
`outputWarnings` explain the accepted attempt; they are not a second verdict.
|
|
4889
|
+
Under the LLM engine, a broken/non-string tool projection and an unavailable or
|
|
4890
|
+
faulty direct-read analyzer are recoverable metadata faults: KodaX records a
|
|
4891
|
+
warning, builds a bounded credential-redacted fallback projection, and still
|
|
4892
|
+
asks the classifier. They do not directly create a user approval request.
|
|
4893
|
+
Extension/provider exception bodies are omitted entirely from Auto-mode logs
|
|
4894
|
+
and approval reasons; only the stable failure stage and exception category are
|
|
4895
|
+
retained. All Auto-mode host warning messages are additionally
|
|
4896
|
+
credential-redacted, normalized to one line, and capped at 768 characters.
|
|
4897
|
+
Logging is best-effort observation: an embedder logger that throws cannot alter
|
|
4898
|
+
the permission decision or interrupt the documented fallback.
|
|
4899
|
+
For source compatibility, the public `ClassifierDecision` allow branch keeps
|
|
4900
|
+
`hazard?: 'none'`. If a model returns `decision=allow` with another hazard, the
|
|
4901
|
+
allow remains authoritative, `decision_hazard_conflict` appears in
|
|
4902
|
+
`outputWarnings`, and the contradictory hazard value is not placed in that
|
|
4903
|
+
legacy field.
|
|
4904
|
+
Auto[LLM] is allow-by-default automatic review. An operational classifier is
|
|
4905
|
+
instructed to return `ask` only for one of two evidence-based classes: a
|
|
4906
|
+
concrete read from a known key/token/credential store or a mutation to KodaX
|
|
4907
|
+
authorization controls; or direct system destruction/resource exhaustion that
|
|
4908
|
+
can destabilize the OS or unrelated software. Ordinary project mutations, Git
|
|
4909
|
+
stash and other Git writes, and normal global dependency install/uninstall/
|
|
4910
|
+
reinstall do not require per-command root authorization. Syntax complexity,
|
|
4911
|
+
incomplete analysis, general uncertainty, and command category are not ask
|
|
4912
|
+
reasons. Static signals, including historical catastrophic-pattern matches,
|
|
4913
|
+
are classifier facts rather than a second Auto[LLM] verdict; explicit
|
|
4914
|
+
Auto[Rules] retains its legacy deterministic gate. A host must honor a valid
|
|
4915
|
+
classifier `allow` without manufacturing another approval. Infrastructure or
|
|
4916
|
+
decision-contract failure still uses the bounded retry and documented
|
|
4917
|
+
Accept-edits fallback; only exhaustion beyond that fallback reaches approval.
|
|
4433
4918
|
|
|
4434
4919
|
The permission event's `inputPreview` is a display-safe diagnostic projection:
|
|
4435
4920
|
it is bounded, credential-redacted, valid JSON, and includes the effective
|
|
@@ -4439,21 +4924,21 @@ session repository safety boundary, whereas relative operands resolve from the
|
|
|
4439
4924
|
validated `executionCwd`. In particular, quoted Python/JavaScript/regexp source
|
|
4440
4925
|
inside a shell command is not a path operand.
|
|
4441
4926
|
|
|
4442
|
-
Every Runtime permission request has a deadline. If the host does not answer,
|
|
4443
|
-
the current operation is not executed and the guardrail returns a stable
|
|
4444
|
-
`approval_timeout` result telling the main model to try a safer, narrower, or
|
|
4445
|
-
reversible approach, or stop and wait for explicit user approval. A timeout is
|
|
4446
|
-
not serialized as an ordinary user rejection.
|
|
4447
|
-
|
|
4448
|
-
The user-level `.kodax` directory is a credential/configuration boundary, not
|
|
4449
|
-
an ordinary project path. Direct shell mutations, output redirects, and
|
|
4450
|
-
recognized nested-shell payloads whose target is provably beneath that
|
|
4451
|
-
directory are identified before LLM classification and
|
|
4452
|
-
|
|
4453
|
-
Windows case-insensitive. KodaX deliberately does not scan arbitrary
|
|
4454
|
-
quoted language source for path-looking substrings: doing so would turn Python,
|
|
4455
|
-
JavaScript, YAML, and regular expressions into false Tier-0 matches. Trusted
|
|
4456
|
-
configuration changes should use the KodaX config CLI or SDK configuration API.
|
|
4927
|
+
Every Runtime permission request has a deadline. If the host does not answer,
|
|
4928
|
+
the current operation is not executed and the guardrail returns a stable
|
|
4929
|
+
`approval_timeout` result telling the main model to try a safer, narrower, or
|
|
4930
|
+
reversible approach, or stop and wait for explicit user approval. A timeout is
|
|
4931
|
+
not serialized as an ordinary user rejection.
|
|
4932
|
+
|
|
4933
|
+
The user-level `.kodax` directory is a credential/configuration boundary, not
|
|
4934
|
+
an ordinary project path. Direct shell mutations, output redirects, and
|
|
4935
|
+
recognized nested-shell payloads whose target is provably beneath that
|
|
4936
|
+
directory are identified before LLM classification and supplied as precise
|
|
4937
|
+
facts rather than permanently policy-blocked. The check is segment-safe and
|
|
4938
|
+
Windows case-insensitive. KodaX deliberately does not scan arbitrary
|
|
4939
|
+
quoted language source for path-looking substrings: doing so would turn Python,
|
|
4940
|
+
JavaScript, YAML, and regular expressions into false Tier-0 matches. Trusted
|
|
4941
|
+
configuration changes should use the KodaX config CLI or SDK configuration API.
|
|
4457
4942
|
|
|
4458
4943
|
### 0.7.x source compatibility
|
|
4459
4944
|
|
|
@@ -4613,8 +5098,12 @@ while (page) {
|
|
|
4613
5098
|
|
|
4614
5099
|
`transcriptEntryChunk()` returns lossless `base64-json` chunks. Concatenate the
|
|
4615
5100
|
decoded bytes and parse JSON only after `hasMore` becomes false. Page and entry
|
|
4616
|
-
cursors are opaque and revision-bound
|
|
4617
|
-
|
|
5101
|
+
cursors are opaque and revision-bound. Runtime retains a bounded immutable
|
|
5102
|
+
snapshot for an in-progress cursor, so new appends do not create duplicates,
|
|
5103
|
+
gaps, or an endless moving boundary. Once that snapshot is no longer retained,
|
|
5104
|
+
the next read returns `resync_required`; restart from a fresh observation.
|
|
5105
|
+
All transcript/history methods accept `{ timeoutMs, signal }` as their final
|
|
5106
|
+
read options in embedded and daemon mode. The shared daemon's
|
|
4618
5107
|
legacy `session.transcript` method rejects payloads above 512 KiB and names the
|
|
4619
5108
|
page/chunk methods rather than attempting a frame near the 8 MiB ceiling.
|
|
4620
5109
|
|
|
@@ -4737,394 +5226,455 @@ boundary, but they do not replace product-level validation in the packaged host.
|
|
|
4737
5226
|
KodaX Space should install the exact v0.7.75 tarball and complete
|
|
4738
5227
|
[`ISSUE_205_v0.7.75_REGRESSION_GUIDE.md`](test-guides/ISSUE_205_v0.7.75_REGRESSION_GUIDE.md)
|
|
4739
5228
|
on Windows 10 and Windows 11 as a non-blocking product validation follow-up.
|
|
4740
|
-
This follow-up does not gate SDK packaging, tagging, or publication.
|
|
4741
|
-
|
|
4742
|
-
---
|
|
4743
|
-
|
|
4744
|
-
## 28. Host-configurable Shell Execution Contract (v0.7.77)
|
|
4745
|
-
|
|
4746
|
-
The shared daemon is long-lived, so its startup `process.env.PATH` is not a
|
|
4747
|
-
reliable description of every project's toolchain. A host can opt a Session
|
|
4748
|
-
into a serializable shell contract:
|
|
4749
|
-
|
|
4750
|
-
```ts
|
|
4751
|
-
await runtime.sessions.updateSettings(session.id, {
|
|
4752
|
-
executionCwd: projectDirectory,
|
|
4753
|
-
shellExecution: {
|
|
4754
|
-
version: 1,
|
|
4755
|
-
shell: {
|
|
4756
|
-
kind: 'pwsh', // pwsh | powershell | cmd | bash | zsh
|
|
4757
|
-
profile: 'default',
|
|
4758
|
-
},
|
|
4759
|
-
environment: {
|
|
4760
|
-
inherit: 'filtered',
|
|
4761
|
-
// Trusted host code, not model input. Use it for a directory-aware
|
|
4762
|
-
// activation command when the shell profile does not switch on cwd.
|
|
4763
|
-
setup: 'fnm use --silent-if-unchanged',
|
|
4764
|
-
windowsPath: 'registry',
|
|
4765
|
-
},
|
|
4766
|
-
cache: {
|
|
4767
|
-
ttlMs: 30_000,
|
|
4768
|
-
refreshToken: 'toolchain-revision-1',
|
|
4769
|
-
},
|
|
4770
|
-
probeTimeoutMs: 10_000,
|
|
4771
|
-
},
|
|
4772
|
-
});
|
|
4773
|
-
```
|
|
4774
|
-
|
|
4775
|
-
An individual Run can supply `options.context.shellExecution`; a concrete Run
|
|
4776
|
-
contract overrides the Session setting. An omitted value or explicit
|
|
4777
|
-
`undefined` does not erase the Session setting. Use a `null` Session patch to
|
|
4778
|
-
remove it.
|
|
4779
|
-
|
|
4780
|
-
The contract supports an optional absolute `shell.executable` (for example Git
|
|
4781
|
-
Bash) and bounded fixed `shell.args`. Command/file/persistence/profile/server
|
|
4782
|
-
and working-directory control flags are rejected from those fixed arguments.
|
|
4783
|
-
`environment.set` is for non-secret host variables. `denyPatterns` can remove
|
|
4784
|
-
additional names but cannot weaken the built-in Provider credential deny set.
|
|
4785
|
-
`inherit: "none"` retains only the OS variables required to start the selected
|
|
4786
|
-
shell. On Windows, `windowsPath: "registry"` re-reads current Machine/User
|
|
4787
|
-
environment values instead of reusing the daemon's startup PATH.
|
|
4788
|
-
|
|
4789
|
-
|
|
4790
|
-
|
|
4791
|
-
|
|
4792
|
-
|
|
4793
|
-
|
|
4794
|
-
|
|
4795
|
-
|
|
4796
|
-
|
|
4797
|
-
|
|
4798
|
-
|
|
4799
|
-
|
|
4800
|
-
|
|
4801
|
-
|
|
4802
|
-
|
|
4803
|
-
|
|
4804
|
-
|
|
4805
|
-
|
|
4806
|
-
|
|
4807
|
-
|
|
4808
|
-
|
|
4809
|
-
|
|
4810
|
-
|
|
4811
|
-
|
|
4812
|
-
|
|
4813
|
-
|
|
4814
|
-
|
|
4815
|
-
|
|
4816
|
-
|
|
4817
|
-
|
|
4818
|
-
|
|
4819
|
-
|
|
4820
|
-
|
|
4821
|
-
|
|
4822
|
-
|
|
4823
|
-
|
|
4824
|
-
|
|
4825
|
-
|
|
4826
|
-
|
|
4827
|
-
|
|
4828
|
-
|
|
4829
|
-
|
|
4830
|
-
|
|
4831
|
-
|
|
4832
|
-
|
|
4833
|
-
|
|
4834
|
-
|
|
4835
|
-
|
|
4836
|
-
|
|
4837
|
-
|
|
4838
|
-
|
|
4839
|
-
|
|
4840
|
-
|
|
4841
|
-
|
|
4842
|
-
|
|
4843
|
-
|
|
4844
|
-
|
|
4845
|
-
|
|
4846
|
-
|
|
4847
|
-
|
|
4848
|
-
|
|
4849
|
-
|
|
4850
|
-
|
|
4851
|
-
|
|
4852
|
-
|
|
4853
|
-
|
|
4854
|
-
|
|
4855
|
-
|
|
4856
|
-
|
|
4857
|
-
|
|
4858
|
-
|
|
4859
|
-
|
|
4860
|
-
|
|
4861
|
-
|
|
4862
|
-
|
|
4863
|
-
|
|
4864
|
-
|
|
4865
|
-
|
|
4866
|
-
|
|
4867
|
-
|
|
4868
|
-
|
|
4869
|
-
|
|
4870
|
-
|
|
4871
|
-
|
|
4872
|
-
|
|
4873
|
-
|
|
4874
|
-
|
|
4875
|
-
|
|
4876
|
-
|
|
4877
|
-
|
|
4878
|
-
|
|
4879
|
-
|
|
4880
|
-
|
|
4881
|
-
|
|
4882
|
-
|
|
4883
|
-
|
|
4884
|
-
|
|
4885
|
-
|
|
4886
|
-
|
|
4887
|
-
|
|
4888
|
-
|
|
4889
|
-
|
|
4890
|
-
|
|
4891
|
-
|
|
4892
|
-
|
|
4893
|
-
|
|
4894
|
-
|
|
4895
|
-
|
|
4896
|
-
|
|
4897
|
-
|
|
4898
|
-
|
|
4899
|
-
|
|
4900
|
-
|
|
4901
|
-
|
|
4902
|
-
|
|
4903
|
-
|
|
4904
|
-
|
|
4905
|
-
|
|
4906
|
-
|
|
4907
|
-
|
|
4908
|
-
|
|
4909
|
-
|
|
4910
|
-
|
|
4911
|
-
|
|
4912
|
-
|
|
4913
|
-
|
|
4914
|
-
|
|
4915
|
-
|
|
4916
|
-
|
|
4917
|
-
|
|
4918
|
-
|
|
4919
|
-
|
|
4920
|
-
|
|
4921
|
-
|
|
4922
|
-
|
|
4923
|
-
|
|
4924
|
-
|
|
4925
|
-
|
|
4926
|
-
|
|
4927
|
-
|
|
4928
|
-
|
|
4929
|
-
|
|
4930
|
-
|
|
4931
|
-
|
|
4932
|
-
|
|
4933
|
-
|
|
4934
|
-
|
|
4935
|
-
|
|
4936
|
-
|
|
4937
|
-
|
|
4938
|
-
|
|
4939
|
-
|
|
4940
|
-
|
|
4941
|
-
|
|
4942
|
-
|
|
4943
|
-
|
|
4944
|
-
|
|
4945
|
-
|
|
4946
|
-
|
|
4947
|
-
|
|
4948
|
-
|
|
4949
|
-
|
|
4950
|
-
|
|
4951
|
-
|
|
4952
|
-
|
|
4953
|
-
|
|
4954
|
-
|
|
4955
|
-
|
|
4956
|
-
|
|
4957
|
-
|
|
4958
|
-
|
|
4959
|
-
|
|
4960
|
-
|
|
4961
|
-
|
|
4962
|
-
|
|
4963
|
-
|
|
4964
|
-
|
|
4965
|
-
|
|
4966
|
-
|
|
4967
|
-
|
|
4968
|
-
|
|
4969
|
-
|
|
4970
|
-
|
|
4971
|
-
|
|
4972
|
-
|
|
4973
|
-
|
|
4974
|
-
|
|
4975
|
-
|
|
4976
|
-
|
|
4977
|
-
|
|
4978
|
-
|
|
4979
|
-
|
|
4980
|
-
|
|
4981
|
-
|
|
4982
|
-
|
|
4983
|
-
|
|
4984
|
-
|
|
4985
|
-
|
|
4986
|
-
|
|
4987
|
-
|
|
4988
|
-
|
|
4989
|
-
|
|
4990
|
-
|
|
4991
|
-
|
|
4992
|
-
|
|
4993
|
-
|
|
4994
|
-
|
|
4995
|
-
|
|
4996
|
-
|
|
4997
|
-
|
|
4998
|
-
|
|
4999
|
-
|
|
5000
|
-
|
|
5001
|
-
|
|
5002
|
-
|
|
5003
|
-
|
|
5004
|
-
|
|
5005
|
-
|
|
5006
|
-
|
|
5007
|
-
|
|
5008
|
-
|
|
5009
|
-
|
|
5010
|
-
|
|
5011
|
-
|
|
5012
|
-
|
|
5013
|
-
|
|
5014
|
-
|
|
5015
|
-
|
|
5016
|
-
|
|
5017
|
-
|
|
5018
|
-
|
|
5019
|
-
|
|
5020
|
-
|
|
5021
|
-
|
|
5022
|
-
|
|
5023
|
-
|
|
5024
|
-
|
|
5025
|
-
|
|
5026
|
-
|
|
5027
|
-
|
|
5028
|
-
|
|
5029
|
-
|
|
5030
|
-
|
|
5031
|
-
|
|
5032
|
-
|
|
5033
|
-
|
|
5034
|
-
|
|
5035
|
-
|
|
5036
|
-
|
|
5037
|
-
|
|
5038
|
-
|
|
5039
|
-
|
|
5040
|
-
|
|
5041
|
-
|
|
5042
|
-
|
|
5043
|
-
|
|
5044
|
-
|
|
5045
|
-
|
|
5046
|
-
|
|
5047
|
-
|
|
5048
|
-
|
|
5049
|
-
|
|
5050
|
-
|
|
5051
|
-
|
|
5052
|
-
|
|
5053
|
-
|
|
5054
|
-
|
|
5055
|
-
|
|
5056
|
-
|
|
5057
|
-
|
|
5058
|
-
|
|
5059
|
-
|
|
5060
|
-
|
|
5061
|
-
|
|
5062
|
-
|
|
5063
|
-
|
|
5064
|
-
|
|
5065
|
-
|
|
5066
|
-
|
|
5067
|
-
|
|
5068
|
-
|
|
5069
|
-
|
|
5070
|
-
|
|
5071
|
-
|
|
5072
|
-
|
|
5073
|
-
|
|
5074
|
-
|
|
5075
|
-
|
|
5076
|
-
|
|
5077
|
-
|
|
5078
|
-
|
|
5079
|
-
|
|
5080
|
-
|
|
5081
|
-
|
|
5082
|
-
|
|
5083
|
-
|
|
5084
|
-
|
|
5085
|
-
|
|
5086
|
-
|
|
5087
|
-
|
|
5088
|
-
|
|
5089
|
-
|
|
5090
|
-
|
|
5091
|
-
|
|
5092
|
-
|
|
5093
|
-
|
|
5094
|
-
|
|
5095
|
-
|
|
5096
|
-
|
|
5097
|
-
|
|
5098
|
-
|
|
5099
|
-
|
|
5100
|
-
|
|
5101
|
-
|
|
5102
|
-
|
|
5103
|
-
|
|
5104
|
-
|
|
5105
|
-
|
|
5106
|
-
|
|
5107
|
-
```
|
|
5108
|
-
|
|
5109
|
-
|
|
5110
|
-
|
|
5111
|
-
|
|
5112
|
-
|
|
5113
|
-
|
|
5114
|
-
`/
|
|
5115
|
-
|
|
5116
|
-
|
|
5117
|
-
|
|
5118
|
-
---
|
|
5119
|
-
|
|
5120
|
-
##
|
|
5121
|
-
|
|
5122
|
-
|
|
5229
|
+
This follow-up does not gate SDK packaging, tagging, or publication.
|
|
5230
|
+
|
|
5231
|
+
---
|
|
5232
|
+
|
|
5233
|
+
## 28. Host-configurable Shell Execution Contract (v0.7.77)
|
|
5234
|
+
|
|
5235
|
+
The shared daemon is long-lived, so its startup `process.env.PATH` is not a
|
|
5236
|
+
reliable description of every project's toolchain. A host can opt a Session
|
|
5237
|
+
into a serializable shell contract:
|
|
5238
|
+
|
|
5239
|
+
```ts
|
|
5240
|
+
await runtime.sessions.updateSettings(session.id, {
|
|
5241
|
+
executionCwd: projectDirectory,
|
|
5242
|
+
shellExecution: {
|
|
5243
|
+
version: 1,
|
|
5244
|
+
shell: {
|
|
5245
|
+
kind: 'pwsh', // pwsh | powershell | cmd | bash | zsh
|
|
5246
|
+
profile: 'default',
|
|
5247
|
+
},
|
|
5248
|
+
environment: {
|
|
5249
|
+
inherit: 'filtered',
|
|
5250
|
+
// Trusted host code, not model input. Use it for a directory-aware
|
|
5251
|
+
// activation command when the shell profile does not switch on cwd.
|
|
5252
|
+
setup: 'fnm use --silent-if-unchanged',
|
|
5253
|
+
windowsPath: 'registry',
|
|
5254
|
+
},
|
|
5255
|
+
cache: {
|
|
5256
|
+
ttlMs: 30_000,
|
|
5257
|
+
refreshToken: 'toolchain-revision-1',
|
|
5258
|
+
},
|
|
5259
|
+
probeTimeoutMs: 10_000,
|
|
5260
|
+
},
|
|
5261
|
+
});
|
|
5262
|
+
```
|
|
5263
|
+
|
|
5264
|
+
An individual Run can supply `options.context.shellExecution`; a concrete Run
|
|
5265
|
+
contract overrides the Session setting. An omitted value or explicit
|
|
5266
|
+
`undefined` does not erase the Session setting. Use a `null` Session patch to
|
|
5267
|
+
remove it.
|
|
5268
|
+
|
|
5269
|
+
The contract supports an optional absolute `shell.executable` (for example Git
|
|
5270
|
+
Bash) and bounded fixed `shell.args`. Command/file/persistence/profile/server
|
|
5271
|
+
and working-directory control flags are rejected from those fixed arguments.
|
|
5272
|
+
`environment.set` is for non-secret host variables. `denyPatterns` can remove
|
|
5273
|
+
additional names but cannot weaken the built-in Provider credential deny set.
|
|
5274
|
+
`inherit: "none"` retains only the OS variables required to start the selected
|
|
5275
|
+
shell. On Windows, `windowsPath: "registry"` re-reads current Machine/User
|
|
5276
|
+
environment values instead of reusing the daemon's startup PATH.
|
|
5277
|
+
|
|
5278
|
+
The CLI config and SDK expose the same small command-target setting for commands
|
|
5279
|
+
that intentionally need host credentials:
|
|
5280
|
+
|
|
5281
|
+
```json
|
|
5282
|
+
{
|
|
5283
|
+
"sandbox": {
|
|
5284
|
+
"envPass": ["GH_TOKEN", "GITHUB_TOKEN"]
|
|
5285
|
+
}
|
|
5286
|
+
}
|
|
5287
|
+
```
|
|
5288
|
+
|
|
5289
|
+
SDK callers pass the same shape per Run, without mutating `process.env` config:
|
|
5290
|
+
|
|
5291
|
+
```ts
|
|
5292
|
+
const handle = await runtime.runs.start({
|
|
5293
|
+
sessionId: session.id,
|
|
5294
|
+
prompt: 'Inspect the authenticated GitHub repository.',
|
|
5295
|
+
options: {
|
|
5296
|
+
sandbox: { envPass: ['GH_TOKEN', 'GITHUB_TOKEN'] },
|
|
5297
|
+
},
|
|
5298
|
+
});
|
|
5299
|
+
```
|
|
5300
|
+
|
|
5301
|
+
Direct `runKodaX()` / `startKodaX()` callers use the same
|
|
5302
|
+
`KodaXOptions.sandbox` field. The option is Run-scoped, overrides the process-
|
|
5303
|
+
level fallback even when `envPass` is empty, and is inherited by native child
|
|
5304
|
+
Agents, Workflow children, and deterministic evaluators. Concurrent SDK Runs
|
|
5305
|
+
can therefore use different lists without changing global configuration.
|
|
5306
|
+
|
|
5307
|
+
`sandbox.envPass` defaults to empty and stores exact names, never values. It
|
|
5308
|
+
does not expose credentials to shell profile/setup resolution; the current
|
|
5309
|
+
host values are restored only into the final command environment, which then
|
|
5310
|
+
flows to ASRT or the ordinary fallback path. Windows matching is
|
|
5311
|
+
case-insensitive; POSIX matching is case-sensitive. `NODE_OPTIONS`, `BASH_ENV`,
|
|
5312
|
+
`RIPGREP_CONFIG_PATH`, and imported Bash functions remain blocked even if
|
|
5313
|
+
named. The CLI projects its user config to the Run option through
|
|
5314
|
+
`KODAX_SANDBOX_ENV_PASS`; that environment variable remains a CLI/backward-
|
|
5315
|
+
compatibility fallback rather than the SDK API. Worker and daemon transports
|
|
5316
|
+
carry names only. Values are read from the command-execution host environment;
|
|
5317
|
+
an auto-started daemon inherits that environment, while an attached persistent
|
|
5318
|
+
daemon must already have the variables and must restart after they change.
|
|
5319
|
+
|
|
5320
|
+
Resolution is two-stage and uses the effective cwd:
|
|
5321
|
+
|
|
5322
|
+
1. sanitize the bootstrap environment, including credentials for built-in,
|
|
5323
|
+
custom, active, inactive, and stacked runtime Providers;
|
|
5324
|
+
2. start the selected shell, load the requested profile/setup, capture a
|
|
5325
|
+
random-framed environment, validate and sanitize it again;
|
|
5326
|
+
3. execute the actual command through that same explicit interpreter.
|
|
5327
|
+
|
|
5328
|
+
The cache is in-memory and isolated by normalized contract, canonical cwd,
|
|
5329
|
+
Session scratch identity, credential deny names, and refresh generation. TTL
|
|
5330
|
+
is bounded to ten minutes; zero disables caching. Daemon restart clears it.
|
|
5331
|
+
`clearShellExecutionEnvironmentCache()` is available for an in-process owner
|
|
5332
|
+
that needs immediate global invalidation.
|
|
5333
|
+
|
|
5334
|
+
Native children, nested Actor turns, Workflow child paths, and deterministic
|
|
5335
|
+
build/test/lint evaluators inherit the effective contract. Runtime exact-command
|
|
5336
|
+
permission grants bind the interpreter family and contract fingerprint, so a
|
|
5337
|
+
grant created under cmd cannot silently authorize the same command after a
|
|
5338
|
+
switch to PowerShell or Bash.
|
|
5339
|
+
|
|
5340
|
+
Configured-shell failures are visible and fail closed: KodaX does not reinterpret
|
|
5341
|
+
the command through another shell. When `shellExecution` is absent, KodaX keeps
|
|
5342
|
+
the pre-v0.7.77 platform-shell interpreter behavior for compatibility; the
|
|
5343
|
+
credential filter and explicit `sandbox.envPass` final-target restoration still
|
|
5344
|
+
apply. See
|
|
5345
|
+
[`ISSUE_214_v0.7.77_REGRESSION_GUIDE.md`](test-guides/ISSUE_214_v0.7.77_REGRESSION_GUIDE.md)
|
|
5346
|
+
for cross-project, cache, cancellation, credential, and Windows argv checks.
|
|
5347
|
+
|
|
5348
|
+
---
|
|
5349
|
+
|
|
5350
|
+
## 29. Evidence-gated background Skill learning (FEATURE_263, v0.7.78)
|
|
5351
|
+
|
|
5352
|
+
F263 completes the existing Learning Center rather than introducing a second
|
|
5353
|
+
queue or client-owned Skill store. Episode review runs after durable foreground
|
|
5354
|
+
completion and stays off the active Run's latency path. A correction, failure,
|
|
5355
|
+
or verifier result is Memory evidence first; it does not itself authorize a
|
|
5356
|
+
Skill mutation.
|
|
5357
|
+
|
|
5358
|
+
A low-risk declarative Skill can enter automatic project-scoped testing only
|
|
5359
|
+
after an explicit preserve-as-Skill request with verified terminal evidence,
|
|
5360
|
+
or repeated independent root episodes plus independent verified artifact
|
|
5361
|
+
evidence. The owner writes an immutable revision and its canonical capability
|
|
5362
|
+
record before discovery can expose it. Formal/builtin/plugin/human Skills keep
|
|
5363
|
+
precedence and cannot be shadowed. Protected/formal changes, user-global
|
|
5364
|
+
promotion, and Extension authoring remain explicit user actions.
|
|
5365
|
+
|
|
5366
|
+
Hosts that require this behavior should negotiate both Runtime capabilities:
|
|
5367
|
+
|
|
5368
|
+
```ts
|
|
5369
|
+
const runtime = await createKodaXRuntime({
|
|
5370
|
+
mode: 'daemon',
|
|
5371
|
+
requirements: {
|
|
5372
|
+
learningCenter: 1,
|
|
5373
|
+
skillLearningLoop: 1,
|
|
5374
|
+
},
|
|
5375
|
+
clientInfo: {
|
|
5376
|
+
name: 'my-host',
|
|
5377
|
+
instanceId: stableClientId,
|
|
5378
|
+
instanceSecret: keychainSecret,
|
|
5379
|
+
},
|
|
5380
|
+
});
|
|
5381
|
+
|
|
5382
|
+
const snapshot = await runtime.learning.getSnapshot();
|
|
5383
|
+
const page = await runtime.learning.list({ limit: 50 });
|
|
5384
|
+
|
|
5385
|
+
for await (const event of runtime.learning.subscribe({
|
|
5386
|
+
afterRevision: snapshot.revision,
|
|
5387
|
+
})) {
|
|
5388
|
+
renderLearningEvent(event);
|
|
5389
|
+
}
|
|
5390
|
+
```
|
|
5391
|
+
|
|
5392
|
+
`loadFullTranscript()` remains the nullable compatibility API.
|
|
5393
|
+
`readFullTranscript()` is the strict host/audit API: it reads main and sidecars
|
|
5394
|
+
under one Session boundary without migration, execution recovery, takeover, or
|
|
5395
|
+
repair. It reports `data_corrupt`, `version_incompatible`, `read_timeout`, and
|
|
5396
|
+
`read_cancelled` instead of turning those cases into an empty/null history.
|
|
5397
|
+
`readConversationHistory()` uses that same read-only boundary, then derives the
|
|
5398
|
+
ordinary conversation projection without changing Session or Run state.
|
|
5399
|
+
|
|
5400
|
+
For an old or incompatible Session that cannot safely resume, use the
|
|
5401
|
+
top-level `exportSessionBundle(id, options?)`. It returns the exact main,
|
|
5402
|
+
`.islands.jsonl`, and legacy `.archive.jsonl` bytes plus hashes and
|
|
5403
|
+
compatibility diagnostics. Export is read-only and fails closed on ambiguous
|
|
5404
|
+
duplicate main files; it does not choose a Session, migrate it, or claim that
|
|
5405
|
+
the task is resumable. Decode each file's `contentBase64` for the canonical
|
|
5406
|
+
lossless bytes; `content` is only a UTF-8 compatibility preview. `byteLength`
|
|
5407
|
+
and `sha256` are computed from the original bytes.
|
|
5408
|
+
|
|
5409
|
+
`runtime.learning` is the authoritative host surface:
|
|
5410
|
+
|
|
5411
|
+
| Need | API |
|
|
5412
|
+
|---|---|
|
|
5413
|
+
| Render inventory or one exact record | `list()` / `get()` |
|
|
5414
|
+
| Render client-specific badges | `getSnapshot()` |
|
|
5415
|
+
| Replay or follow durable lifecycle events | `events()` / `subscribe()` |
|
|
5416
|
+
| Clear or defer only this client's notice | `acknowledge()` / `snooze()` |
|
|
5417
|
+
| Explicitly control a learned revision | `reject()` / `disable()` / `rollback()` / `review()` / `trust()` |
|
|
5418
|
+
| Promote to the user scope | `promote(nameOrSlugOrId, 'user')` |
|
|
5419
|
+
|
|
5420
|
+
Do not scan learned files and infer activation from their presence. Discovery
|
|
5421
|
+
requires the canonical record, matching project identity, lifecycle,
|
|
5422
|
+
fingerprint, regular-file checks, formal-name policy, and exact revision.
|
|
5423
|
+
Testing admission permits one concurrent root binding and at most three
|
|
5424
|
+
exact-revision invocations. Promotion requires independently verified success;
|
|
5425
|
+
failed or inconclusive canaries return to Ready/attention. A Run retains the
|
|
5426
|
+
revision it captured at admission, so rollback or replacement affects future
|
|
5427
|
+
bindings without mutating an in-flight prompt.
|
|
5428
|
+
|
|
5429
|
+
Learning Center notification state is client-specific, but capability
|
|
5430
|
+
lifecycle and project canary state are owner-global. Renderer code should
|
|
5431
|
+
receive sanitized records/events through host IPC; it should not receive
|
|
5432
|
+
daemon credentials or mutate files directly. Inline, Worker, and daemon
|
|
5433
|
+
facades expose the same learning methods. A host missing `skillLearningLoop:1`
|
|
5434
|
+
may still support the older Ready/manual Learning Center surface, but must not
|
|
5435
|
+
claim the complete F263 project-canary contract.
|
|
5436
|
+
|
|
5437
|
+
---
|
|
5438
|
+
|
|
5439
|
+
## 30. Standalone sandbox SDK (v0.7.78)
|
|
5440
|
+
|
|
5441
|
+
ASRT containment is a public SDK capability, not an Auto[LLM]-only
|
|
5442
|
+
implementation detail. Import the dedicated subpath when a host needs to
|
|
5443
|
+
sandbox its own commands or scripts:
|
|
5444
|
+
|
|
5445
|
+
```ts
|
|
5446
|
+
import {
|
|
5447
|
+
activateKodaXSandbox,
|
|
5448
|
+
doctorKodaXSandbox,
|
|
5449
|
+
getKodaXSandboxCapability,
|
|
5450
|
+
getKodaXSandboxSetupGuidance,
|
|
5451
|
+
runKodaXSandboxed,
|
|
5452
|
+
} from '@kodax-ai/kodax/sandbox';
|
|
5453
|
+
|
|
5454
|
+
const capability = getKodaXSandboxCapability();
|
|
5455
|
+
const doctor = await doctorKodaXSandbox({ refresh: true });
|
|
5456
|
+
|
|
5457
|
+
if (!doctor.ready) {
|
|
5458
|
+
showSandboxInfo(getKodaXSandboxSetupGuidance(doctor));
|
|
5459
|
+
// Call only from an explicit setup/onboarding action. On Windows this may
|
|
5460
|
+
// display UAC; ordinary SDK calls never invoke it automatically.
|
|
5461
|
+
const activation = await activateKodaXSandbox();
|
|
5462
|
+
if (activation.status !== 'ready') {
|
|
5463
|
+
showSandboxInfo(activation.guidance);
|
|
5464
|
+
}
|
|
5465
|
+
}
|
|
5466
|
+
```
|
|
5467
|
+
|
|
5468
|
+
Platform behavior:
|
|
5469
|
+
|
|
5470
|
+
- Windows uses the pinned ASRT restricted-user/WFP setup. The parent terminal
|
|
5471
|
+
does not need to be elevated; the one-time installer requests UAC itself.
|
|
5472
|
+
- macOS uses Seatbelt through `sandbox-exec` and requires ripgrep. Guide users
|
|
5473
|
+
to `brew install ripgrep` when doctor reports it missing.
|
|
5474
|
+
- Linux uses bubblewrap and requires `bubblewrap`, `socat`, and `ripgrep`.
|
|
5475
|
+
Present the emitted `apt`/`dnf`/`pacman` guidance; do not run `sudo` or a
|
|
5476
|
+
package manager silently.
|
|
5477
|
+
|
|
5478
|
+
Do not call `activateKodaXSandbox()` during ordinary Runtime startup, tool
|
|
5479
|
+
execution, or a background permission check. KodaX's own first-run/setup UI
|
|
5480
|
+
checks once; a declined UAC prompt or missing dependency is reported there and
|
|
5481
|
+
is not repeatedly surfaced until the user runs setup again.
|
|
5482
|
+
|
|
5483
|
+
### Run a host-owned command with an explicit policy
|
|
5484
|
+
|
|
5485
|
+
```ts
|
|
5486
|
+
import os from 'node:os';
|
|
5487
|
+
import path from 'node:path';
|
|
5488
|
+
import { runKodaXSandboxed } from '@kodax-ai/kodax/sandbox';
|
|
5489
|
+
|
|
5490
|
+
const result = await runKodaXSandboxed({
|
|
5491
|
+
command: process.execPath,
|
|
5492
|
+
args: ['scripts/generate-report.mjs'],
|
|
5493
|
+
cwd: projectDirectory,
|
|
5494
|
+
filesystem: {
|
|
5495
|
+
allowRead: [projectDirectory, process.execPath],
|
|
5496
|
+
allowWrite: [projectDirectory, os.tmpdir()],
|
|
5497
|
+
denyRead: [path.join(os.homedir(), '.ssh')],
|
|
5498
|
+
denyWrite: [path.join(projectDirectory, '.git', 'config')],
|
|
5499
|
+
},
|
|
5500
|
+
network: {
|
|
5501
|
+
mode: 'allowlist',
|
|
5502
|
+
origins: ['https://api.example.com'],
|
|
5503
|
+
},
|
|
5504
|
+
// false by default: start with KodaX's minimal execution environment.
|
|
5505
|
+
inheritEnvironment: false,
|
|
5506
|
+
env: { REPORT_FORMAT: 'pdf' },
|
|
5507
|
+
timeoutMs: 120_000,
|
|
5508
|
+
maxOutputBytes: 2 * 1024 * 1024,
|
|
5509
|
+
});
|
|
5510
|
+
|
|
5511
|
+
if (result.status === 'unavailable') {
|
|
5512
|
+
// The command was NOT run. Decide explicitly whether your product should
|
|
5513
|
+
// wait for setup, reject the operation, or use its own non-sandbox path.
|
|
5514
|
+
showSandboxInfo(getKodaXSandboxSetupGuidance(result.doctor));
|
|
5515
|
+
} else if (result.exitCode !== 0) {
|
|
5516
|
+
throw new Error(result.stderr || `sandboxed command exited ${result.exitCode}`);
|
|
5517
|
+
}
|
|
5518
|
+
```
|
|
5519
|
+
|
|
5520
|
+
The generic executor supports network `allow`, `deny`, and exact HTTP(S)
|
|
5521
|
+
origin `allowlist` modes plus filesystem policy roots, environment inheritance,
|
|
5522
|
+
timeout, cancellation, and bounded output. ASRT permits ordinary reads by
|
|
5523
|
+
default: `denyRead` removes access and a more specific `allowRead` carves access
|
|
5524
|
+
back. `allowWrite` defines the writable roots, while `denyWrite` removes
|
|
5525
|
+
subtrees and always takes precedence over `allowWrite`. The HTTP(S) `origins`
|
|
5526
|
+
are normalized to the hostname/port pair enforced by ASRT's network proxy. It
|
|
5527
|
+
never silently runs without containment: sandbox unavailability is the typed
|
|
5528
|
+
`{ status: 'unavailable', sandboxed: false, doctor }` result.
|
|
5529
|
+
|
|
5530
|
+
KodaX's own local workspace-shell policy supplies a stricter `denyRead` set
|
|
5531
|
+
than the generic SDK default: common home credential locations, sensitive
|
|
5532
|
+
private-key/environment filenames, and the complete resolved agent home are
|
|
5533
|
+
denied. Home-local executable search paths nested below those roots are not
|
|
5534
|
+
re-granted. This policy belongs to KodaX's command adapter; a standalone SDK
|
|
5535
|
+
host must declare the sensitive paths required by its own threat boundary.
|
|
5536
|
+
|
|
5537
|
+
`command` and `args` remain separate process arguments. On Windows KodaX uses
|
|
5538
|
+
an encoded bootstrap followed by `shell: false`, so `%VAR%`, `&`, embedded
|
|
5539
|
+
quotes, and spaces are not expanded or re-parsed by the host shell. The
|
|
5540
|
+
explicit environment policy is overlaid into ASRT's fresh restricted-user
|
|
5541
|
+
environment; ASRT-owned proxy, CA, and Git safety variables retain precedence.
|
|
5542
|
+
`timeoutMs` covers the complete broker lifecycle, including ASRT
|
|
5543
|
+
initialization, the command, and cleanup. Windows ACL initialization on a cold
|
|
5544
|
+
path can take tens of seconds, so do not reuse a classifier-scale 20–30 second
|
|
5545
|
+
deadline unless that early cancellation is intentional.
|
|
5546
|
+
|
|
5547
|
+
This is intentionally different from KodaX's local permission fallback. When
|
|
5548
|
+
ASRT is unavailable, Auto[LLM] still makes the same deterministic/LLM/user
|
|
5549
|
+
permission decision and an admitted local shell may use the ordinary execution
|
|
5550
|
+
path; only OS containment is absent. The same local fallback applies when
|
|
5551
|
+
ASRT preparation or backend initialization fails before the target process
|
|
5552
|
+
starts. KodaX never retries after the target has started, so a sandbox fault
|
|
5553
|
+
cannot duplicate command side effects. Remote A2A admitted Skill scripts
|
|
5554
|
+
retain their stronger isolation contract and do not fall back to an
|
|
5555
|
+
unsandboxed script. Embedded and daemon Runtime capability metadata expose
|
|
5556
|
+
`sandboxRuntime` with the platform backend, ASRT version, supported control
|
|
5557
|
+
dimensions, elevation behavior, and fallback semantics.
|
|
5558
|
+
|
|
5559
|
+
Local workspace commands reuse one long-lived ASRT session per canonical
|
|
5560
|
+
workspace. Session-level ACL/WFP initialization is warmed once; the owner
|
|
5561
|
+
coordinates short wrap/cleanup RPCs without locking across the target process
|
|
5562
|
+
lifetime, so concurrent and background targets do not block later preparation.
|
|
5563
|
+
Per-command target attestation and fallback remain independent. Session reset
|
|
5564
|
+
happens only after an idle drain or host-process shutdown, never on the
|
|
5565
|
+
interactive command critical path. A cold first command may still wait for
|
|
5566
|
+
platform initialization, but later commands must not repeatedly pay that
|
|
5567
|
+
setup/reset cost. Abort signals and the command deadline cover that prepare
|
|
5568
|
+
wait; a cancelled/timed-out prepare never starts the target or changes to the
|
|
5569
|
+
ordinary fallback path. Explicit
|
|
5570
|
+
Windows system-temp operations that ASRT cannot safely ACL-manage are not
|
|
5571
|
+
selected for containment and keep the already-approved normal execution path.
|
|
5572
|
+
|
|
5573
|
+
An unhealthy session fails the current prepare immediately so the host can use
|
|
5574
|
+
its normal permission fallback. Cleanup continues out of band. On Windows the
|
|
5575
|
+
owner first closes its command input and allows up to 130 seconds for ASRT
|
|
5576
|
+
0.0.65's two serial ACL cleanup helpers to finish; only then may process-tree
|
|
5577
|
+
termination be forced. The failed workspace session is not replaced until
|
|
5578
|
+
that bounded cleanup settles, preventing a replacement from racing stale ACL
|
|
5579
|
+
recovery. macOS and Linux use the same EOF-first sequence with a shorter
|
|
5580
|
+
bounded termination grace.
|
|
5581
|
+
|
|
5582
|
+
Runtime event streams may also contain one terminal `tool.sandbox` observation
|
|
5583
|
+
associated with the tool ID. `applied` is emitted only after the in-sandbox
|
|
5584
|
+
bootstrap confirms that the real target process reached Node's `spawn` event;
|
|
5585
|
+
wrapper startup alone is not sufficient. Before that handshake, a local
|
|
5586
|
+
backend failure may fall back to normal execution. After it, KodaX never
|
|
5587
|
+
restarts the target:
|
|
5588
|
+
|
|
5589
|
+
```ts
|
|
5590
|
+
runtime.events.subscribe({ sessionId: session.id, type: 'tool.sandbox' }, (event) => {
|
|
5591
|
+
if (event.type !== 'tool.sandbox') return;
|
|
5592
|
+
const { observation } = event.payload.update;
|
|
5593
|
+
// observation.state: 'applied' | 'fallback' | 'not_selected'
|
|
5594
|
+
diagnostics.recordSandboxRoute(observation);
|
|
5595
|
+
});
|
|
5596
|
+
```
|
|
5597
|
+
|
|
5598
|
+
This event is optional diagnostics, not conversation content. It is never added
|
|
5599
|
+
to model-visible messages. Default consumer UX should not render it in startup
|
|
5600
|
+
output, command cards, notifications, or conversation history; expose it only
|
|
5601
|
+
in an explicit advanced diagnostics view. KodaX's own Ink REPL does not
|
|
5602
|
+
subscribe to the event and refreshes human-readable status only when the user
|
|
5603
|
+
runs `/sandbox`. Explicit JSON output and SDK subscriptions retain the
|
|
5604
|
+
structured event for professional diagnostics. `/sandbox` is read-only and
|
|
5605
|
+
never activates the backend or requests elevation.
|
|
5606
|
+
|
|
5607
|
+
---
|
|
5608
|
+
|
|
5609
|
+
## Learned Skill promotion reference (v0.7.78)
|
|
5610
|
+
|
|
5611
|
+
Promotion is the explicit transfer of one immutable, reviewed `ready` or
|
|
5612
|
+
`active_learned` Skill revision into the formal user catalog. It is not the
|
|
5613
|
+
evidence-driven `testing -> active_learned` canary transition. The public named
|
|
5614
|
+
service type is exported from the Runtime SDK:
|
|
5615
|
+
|
|
5616
|
+
```ts
|
|
5617
|
+
import {
|
|
5618
|
+
createKodaXRuntime,
|
|
5619
|
+
type RuntimeLearningService,
|
|
5620
|
+
} from '@kodax-ai/kodax/runtime';
|
|
5621
|
+
|
|
5622
|
+
const runtime = await createKodaXRuntime({
|
|
5623
|
+
requirements: {
|
|
5624
|
+
learningCenter: 1,
|
|
5625
|
+
skillLearningLoop: 1,
|
|
5626
|
+
},
|
|
5627
|
+
});
|
|
5628
|
+
|
|
5629
|
+
const learning: RuntimeLearningService = runtime.learning;
|
|
5630
|
+
const record = await learning.get('normalize-release-notes');
|
|
5631
|
+
|
|
5632
|
+
try {
|
|
5633
|
+
await learning.promote(record.capabilityId, 'user');
|
|
5634
|
+
} finally {
|
|
5635
|
+
await runtime.close();
|
|
5636
|
+
}
|
|
5637
|
+
```
|
|
5638
|
+
|
|
5639
|
+
`name`, `slug`, and exact `capabilityId` are accepted. Exact IDs are preferred
|
|
5640
|
+
when multiple projects expose the same display name or slug. `'user'` is the
|
|
5641
|
+
only supported scope. Daemon clients need the server-issued
|
|
5642
|
+
`learning:control` scope; advertising a client capability does not grant it.
|
|
5643
|
+
Inline, Worker, and daemon facades carry the same v2 learned-record shape and
|
|
5644
|
+
promotion method.
|
|
5645
|
+
|
|
5646
|
+
The Runtime verifies the source is a regular non-symlink file inside the exact
|
|
5647
|
+
project Learned Area and that its content still matches the recorded
|
|
5648
|
+
fingerprint. It then creates the configured user Skill destination—normally
|
|
5649
|
+
`~/.kodax/skills/<slug>/SKILL.md`—with atomic exclusive-publish semantics.
|
|
5650
|
+
Existing identical content is idempotent; the final path appears only after the
|
|
5651
|
+
complete temporary file is synced. Different formal content returns an
|
|
5652
|
+
`action_failed` error and is never overwritten. On success the canonical
|
|
5653
|
+
project record changes to `promoted_user`.
|
|
5654
|
+
|
|
5655
|
+
Terminal users can inspect the same contract with:
|
|
5656
|
+
|
|
5657
|
+
```text
|
|
5658
|
+
/learn promote --help
|
|
5659
|
+
/learn help promote
|
|
5660
|
+
/help learn promote
|
|
5661
|
+
```
|
|
5662
|
+
|
|
5663
|
+
The canonical command is
|
|
5664
|
+
`/learn promote <name|slug|capability-id> --scope user`; omitting the scope is a
|
|
5665
|
+
backward-compatible shorthand for the same user scope. Unknown, duplicate, or
|
|
5666
|
+
unsupported options fail before the Runtime mutation.
|
|
5667
|
+
|
|
5668
|
+
---
|
|
5669
|
+
|
|
5670
|
+
## See also
|
|
5671
|
+
|
|
5672
|
+
- [README.md](../README.md) — end-user CLI quick start
|
|
5123
5673
|
- [docs/ADR.md ADR-024](ADR.md#adr-024-npm-发布物正名-kodax-aikodax--sdk-subpath-exports-形式化-v0739) — SDK subpath architecture rationale
|
|
5124
5674
|
- [docs/ADR.md ADR-032](ADR.md#adr-032-sdk-embedder-surface-closure-feature_186-v0742) — FEATURE_186 design record (all 8 phases)
|
|
5125
5675
|
- [docs/ADR.md ADR-057](ADR.md#adr-057-large-compaction-is-an-always-on-context-scoped-full-coverage-transaction) — v0.7.74 compaction and exact-history ownership
|
|
5126
5676
|
- [docs/ADR.md ADR-058](ADR.md#adr-058-model-agent-wait-is-mailbox-control-not-event-telemetry) — mailbox control versus Actor telemetry
|
|
5127
5677
|
- [docs/features/v0.7.42.md FEATURE_186](features/v0.7.42.md#feature_186-sdk-embedder-surface-closure--kodax-space-gap-list--mcp-popout) — gap-by-gap landing matrix
|
|
5128
|
-
- [docs/features/v0.7.74.md](features/v0.7.74.md) — v0.7.74 release-candidate design and verification record
|
|
5129
|
-
- [docs/features/v0.7.75.md](features/v0.7.75.md) — v0.7.75 Windows GUI and Sidecar/Runtime stabilization candidate
|
|
5130
|
-
- [docs/features/v0.7.77.md](features/v0.7.77.md) — v0.7.77 adaptive-quality, governed-memory, and release-hardening record
|
|
5678
|
+
- [docs/features/v0.7.74.md](features/v0.7.74.md) — v0.7.74 release-candidate design and verification record
|
|
5679
|
+
- [docs/features/v0.7.75.md](features/v0.7.75.md) — v0.7.75 Windows GUI and Sidecar/Runtime stabilization candidate
|
|
5680
|
+
- [docs/features/v0.7.77.md](features/v0.7.77.md) — v0.7.77 adaptive-quality, governed-memory, and release-hardening record
|