chainlesschain 0.166.132 → 0.166.135
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/README.md +44 -18
- package/package.json +1 -1
- package/src/assets/linux-subreaper/linux-arm64/manifest.json +1 -1
- package/src/assets/linux-subreaper/linux-x64/manifest.json +1 -1
- package/src/data/changelog.json +26 -0
- package/src/gateways/ws/llm-chat-protocol.js +1 -1
- package/src/gateways/ws/personal-data-hub-protocol.js +27 -11
- package/src/gateways/ws/session-core-protocol.js +22 -17
- package/src/gateways/ws/session-protocol.js +19 -8
- package/src/gateways/ws/ws-agent-handler.js +30 -0
- package/src/lib/background-launch-profile.js +37 -1
- package/src/lib/chat-core.js +40 -35
- package/src/lib/coding-task-guidance.js +10 -9
- package/src/lib/context-memory-kernel/message-adapter.js +5 -1
- package/src/lib/evolution/agent-model-projection.js +0 -1
- package/src/lib/evolution/governed-hub-llm.js +5 -1
- package/src/lib/evolution/governed-hub-resolver.js +15 -6
- package/src/lib/evolution/governed-hub-skill.js +2 -0
- package/src/lib/evolution/optional-ui-model-turn.js +13 -2
- package/src/lib/llm-config-defaults.js +17 -5
- package/src/lib/llm-connection-config.js +10 -2
- package/src/lib/llm-connection-probe.js +5 -2
- package/src/lib/llm-connection-resolution.js +7 -4
- package/src/lib/llm-providers.js +24 -11
- package/src/lib/openai-responses.js +2 -1
- package/src/lib/personal-data-hub-wiring.js +69 -36
- package/src/lib/provider-protocol.js +35 -3
- package/src/lib/provider-stream.js +6 -4
- package/src/lib/runnable-provider.js +4 -0
- package/src/lib/ws-chat-handler.js +22 -9
- package/src/runtime/agent-core.js +182 -34
- package/src/runtime/headless-runner.js +3 -3
package/README.md
CHANGED
|
@@ -2,11 +2,33 @@
|
|
|
2
2
|
|
|
3
3
|
Command-line interface for installing, configuring, and managing [ChainlessChain](https://www.chainlesschain.com), including the coding agent, governed tools, sessions, memory, and IDE integrations.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## CLI 0.166.135
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
This release recovers genuine OpenAI Responses JSON disconnects before accepted output, bounds stalled requests, and settles completed streaming responses without waiting for socket EOF. It preserves empty tool allowlists across resume and rejects malformed saved permission, sandbox and budget settings. HTTP/provider failures, cancellation, usage persistence and incomplete tool-call replay protections remain authoritative.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Install with `npm i -g chainlesschain@0.166.135 --registry https://registry.npmjs.org`, restart the chat host, then run `cc --version` and `cc doctor`. Paired IDE versions are Open VSX **0.37.172** and JetBrains **0.4.189**, recommending CLI **0.166.135**; Kernel stays **0.1.11**. Check the [release guide](https://docs.chainlesschain.com/chainlesschain/agent-platform-release.html) for channel publication status. Microsoft Marketplace and PC/mobile releases are outside this release scope.
|
|
10
|
+
|
|
11
|
+
The preceding OpenAI native cohort accepted **39/45** tasks. Runtime regression tests and this release do not change that historical result or establish formal paid/native production admission.
|
|
12
|
+
|
|
13
|
+
## CLI 0.166.133 — verified preceding release
|
|
14
|
+
|
|
15
|
+
CLI **0.166.133** is published on npm. It preserves required empty OpenAI Responses reasoning summaries, applies the self-evolution switch to all model-owning `cc ui` entry points, and supports explicitly configured OpenAI-compatible local/LAN platforms through `custom`, with optional credentials and unchanged endpoint/model aliases. It also exposes canonical allowed mutation paths in provider tool schemas while retaining execution-time scope and file-identity checks. Controlled regressions and release gates do not establish live-platform task acceptance.
|
|
16
|
+
|
|
17
|
+
Install with `npm i -g chainlesschain@0.166.133 --registry https://registry.npmjs.org`, restart the chat host, then run `cc --version` and `cc doctor`. Companion Open VSX **0.37.171** / JetBrains **0.4.188** are public and recommend this CLI; their original payloads match the official artifacts. Kernel stays **0.1.11**. Exact source `2eb0c5b1e048509362f9d980f94acee55c2ffa9f` passed Linux, Windows and macOS CLI CI / CLI Strict Sandbox. [OIDC publication](https://github.com/chainlesschain/chainlesschain/actions/runs/41073612529) and [independent readback](https://github.com/chainlesschain/chainlesschain/actions/runs/41075409835) verified immutable package bytes and signed provenance. Consult the [current release guide](https://docs.chainlesschain.com/chainlesschain/agent-platform-release.html) for IDE channel availability. Microsoft Marketplace and PC/mobile releases are outside this release scope.
|
|
18
|
+
|
|
19
|
+
## CLI 0.166.132 — verified public baseline before this source revision
|
|
20
|
+
|
|
21
|
+
Install: `npm i -g chainlesschain@0.166.132 --registry https://registry.npmjs.org`. Public Open VSX **0.37.170** and approved JetBrains **0.4.187** recommend this CLI; Kernel **0.1.11** is verified upstream. Restart the chat host, then run `cc --version` and `cc doctor`.
|
|
22
|
+
|
|
23
|
+
This release stops tool execution on truncated responses and permits one replan when complete usage is recorded and budget remains. Recovery preserves the original task, including through role combination, asynchronous recall and readContext continuation. Filtered or incomplete terminal responses and missing task context stop with a clear failure. No independent IDE UI change is included.
|
|
24
|
+
|
|
25
|
+
Release source: [772fcdbda85bcf91df95bfced5b476dcf4c74614](https://github.com/chainlesschain/chainlesschain/commit/772fcdbda85bcf91df95bfced5b476dcf4c74614). Exact-source [validation gates](https://github.com/chainlesschain/chainlesschain/blob/0d57ebfea919ae941a36a1f11a9ee10964722120/docs/research/cli/evidence/release132-public/gates.json), [OIDC publication run](https://github.com/chainlesschain/chainlesschain/actions/runs/40985169005), [official artifact](https://github.com/chainlesschain/chainlesschain/actions/runs/40985169005/artifacts/12946249806), [independent readback run](https://github.com/chainlesschain/chainlesschain/actions/runs/40986625203), and [public CLI readback](https://github.com/chainlesschain/chainlesschain/blob/0d57ebfea919ae941a36a1f11a9ee10964722120/docs/research/cli/evidence/release132-public/cli.json) identify this public package. [Three-package publication evidence](https://github.com/chainlesschain/chainlesschain/blob/0d57ebfea919ae941a36a1f11a9ee10964722120/docs/research/cli/evidence/release132-public/publication.json).
|
|
26
|
+
|
|
27
|
+
At historical source `abbe13ad3658ff9e2e100989a4cfefc409d9261c`, the combined local suite actually ran **456 tests across 18 files + 10 real CLI tests across 3 files**, with no skips. Subsequent fixture and native-preflight repairs compared the selected runtime components byte for byte; release source `772fcdbda85bcf91df95bfced5b476dcf4c74614` did **not** repeat that combined local suite. [Component scope evidence](https://github.com/chainlesschain/chainlesschain/blob/0d57ebfea919ae941a36a1f11a9ee10964722120/docs/research/cli/evidence/release132-native-preflight-2026-10-30/review.json) and the release source's [cloud gates](https://github.com/chainlesschain/chainlesschain/blob/0d57ebfea919ae941a36a1f11a9ee10964722120/docs/research/cli/evidence/release132-public/gates.json) retain this distinction. Failed source `40753f179a3dc18760bd64a754b28a46ba718f57` and the earlier superseded `7f8ef64b4bd8b429f97d2e121c440143ed7ddf3b` remain historical; their successful checks do not transfer to this release.
|
|
28
|
+
|
|
29
|
+
Automatic acceptance remains **34/45**, with **11** unfinished; **3 assisted deliveries** are recorded separately and are not added to that total. The original broker **403** and Memory cold SLO remain unresolved. Publication adds no task acceptance or paid/native admission: **NOT_PAID_READY / NOT_ADMITTED / HOLD** remain. Microsoft Marketplace remains deferred. PC/mobile **v5.0.3.143** remain independent.
|
|
30
|
+
|
|
31
|
+
## CLI 0.166.131 — previous public release (2026-10-29)
|
|
10
32
|
|
|
11
33
|
Install: `npm i -g chainlesschain@0.166.131 --registry https://registry.npmjs.org`. Kernel **0.1.11** was published first. Restart the chat host, then run `cc --version` and `cc doctor`. Public Open VSX **0.37.169** / JetBrains **0.4.186** both recommend CLI **0.166.131**; their downloaded packages match the official CI payloads. Microsoft Marketplace is deferred.
|
|
12
34
|
|
|
@@ -64,7 +86,7 @@ Ordinary Agent, `ask`, `chat` and `cc ui` use self-evolution only when enabled.
|
|
|
64
86
|
|
|
65
87
|
Run `cc config set evolution.enabled true` to opt in, or `cc config set evolution.enabled false` to switch off. `cc evolution deployment enable` initializes the local signed runtime when needed; `cc evolution deployment disable` turns it off and retains deployment data. `cc evolution deployment status --json` reports the effective state. An explicit `false` also overrides inherited deployment environment variables; explicitly supplied external/managed deployments remain opt-ins when this setting is absent.
|
|
66
88
|
|
|
67
|
-
The **Skill 自进化配置** page in `cc ui`, VS Code and JetBrains shares this setting. Quick Ask
|
|
89
|
+
The **Skill 自进化配置** page in `cc ui`, VS Code and JetBrains shares this setting. Since CLI **0.166.133**, `cc ui` Chat, Agent, Quick Ask, streaming and Personal Data Hub model requests re-read it for each new request, including existing UI sessions. Stop and restart already running terminal or IDE Agent sessions to apply changes there. Switching off leaves ordinary chat, tool permissions and sandbox policy available. Enabled deployments still enforce their signatures and evidence validation; automatic candidate promotion remains `HOLD`. RRSI evaluation modules are part of the CLI evolution code, but this switch does not itself start a full RRSI campaign or publish learned candidates.
|
|
68
90
|
|
|
69
91
|
Install this release with `npm i -g chainlesschain@0.166.127 --registry https://registry.npmjs.org`. Paired public IDE versions are Open VSX / VS Code **0.37.165** and JetBrains **0.4.182**. Their exact-source release gates and public archive readback passed; Microsoft Marketplace remains unpublished. Child package pins are unchanged. The preceding **0.166.126** JSON recovery-summary repair addresses host-truncated retained context; the same error code can also report other invalid original evidence. Exact-source Linux, Windows and macOS CLI CI and Strict Sandbox passed; GitHub Actions OIDC publication and public package readback are complete. Mobile and PC release [`v5.0.3.142`](https://github.com/chainlesschain/chainlesschain/releases/tag/v5.0.3.142) is public from frozen source `c9881a2b710cf49f0ad3d63673e0998ecc608514`; automatic promotion remains `HOLD`.
|
|
70
92
|
|
|
@@ -239,7 +261,7 @@ Explicit Linux durable permissions require administrator provisioning. Cross-pro
|
|
|
239
261
|
Install the CLI version published by this release:
|
|
240
262
|
|
|
241
263
|
```bash
|
|
242
|
-
npm install -g chainlesschain@0.166.
|
|
264
|
+
npm install -g chainlesschain@0.166.132 --registry https://registry.npmjs.org
|
|
243
265
|
chainlesschain setup
|
|
244
266
|
```
|
|
245
267
|
|
|
@@ -2412,19 +2434,23 @@ Configuration is stored at `~/.chainlesschain/config.json`. The CLI creates and
|
|
|
2412
2434
|
|
|
2413
2435
|
### Supported LLM Providers
|
|
2414
2436
|
|
|
2415
|
-
| Provider | Default Model | API Key Required
|
|
2416
|
-
| -------------------------- | ---------------------- |
|
|
2417
|
-
| Ollama (Local) | qwen2.5:7b | No
|
|
2418
|
-
| OpenAI | gpt-4o | Yes
|
|
2419
|
-
| Anthropic | claude-opus-4-6 | Yes
|
|
2420
|
-
| DeepSeek | deepseek-chat | Yes
|
|
2421
|
-
| DashScope (Alibaba) | qwen-turbo | Yes
|
|
2422
|
-
| Google Gemini | gemini-2.0-flash | Yes
|
|
2423
|
-
| Mistral AI | mistral-large-latest | Yes
|
|
2424
|
-
| Volcengine (火山引擎/豆包) | doubao-seed-1-6-251015 | Yes
|
|
2425
|
-
| Kimi (月之暗面) | moonshot-v1-auto | Yes
|
|
2426
|
-
| MiniMax (海螺AI) | MiniMax-Text-01 | Yes
|
|
2427
|
-
| Custom
|
|
2437
|
+
| Provider | Default Model | API Key Required |
|
|
2438
|
+
| -------------------------- | ---------------------- | ------------------ |
|
|
2439
|
+
| Ollama (Local) | qwen2.5:7b | No |
|
|
2440
|
+
| OpenAI | gpt-4o | Yes |
|
|
2441
|
+
| Anthropic | claude-opus-4-6 | Yes |
|
|
2442
|
+
| DeepSeek | deepseek-chat | Yes |
|
|
2443
|
+
| DashScope (Alibaba) | qwen-turbo | Yes |
|
|
2444
|
+
| Google Gemini | gemini-2.0-flash | Yes |
|
|
2445
|
+
| Mistral AI | mistral-large-latest | Yes |
|
|
2446
|
+
| Volcengine (火山引擎/豆包) | doubao-seed-1-6-251015 | Yes |
|
|
2447
|
+
| Kimi (月之暗面) | moonshot-v1-auto | Yes |
|
|
2448
|
+
| MiniMax (海螺AI) | MiniMax-Text-01 | Yes |
|
|
2449
|
+
| Custom (OpenAI-compatible) | Set explicitly | Platform-dependent |
|
|
2450
|
+
|
|
2451
|
+
Use `provider: "custom"` for local or LAN model platforms exposing OpenAI-compatible Chat Completions, including LM Studio, vLLM, Xinference, LocalAI, and model-routing gateways. Set the platform's API base URL (for example `http://localhost:1234/v1` or a LAN gateway's documented base path) and exact model name. Supply its API key when required; keyless endpoints are also supported. Chat, Agent, streaming, and connection tests keep that endpoint and model instead of selecting Ollama or a cloud provider.
|
|
2452
|
+
|
|
2453
|
+
Self-evolution is optional. With `evolution.enabled: false`, `cc ui` Chat, Agent, Quick Ask, streaming, and Personal Data Hub analysis do not require an Evolution deployment certificate. Existing sessions read the setting again on subsequent requests. Enabled deployments still require valid authentication.
|
|
2428
2454
|
|
|
2429
2455
|
## File Structure
|
|
2430
2456
|
|
package/package.json
CHANGED
package/src/data/changelog.json
CHANGED
|
@@ -1,6 +1,32 @@
|
|
|
1
1
|
{
|
|
2
2
|
"source": "CHANGELOG.md",
|
|
3
3
|
"releases": [
|
|
4
|
+
{
|
|
5
|
+
"productVersion": null,
|
|
6
|
+
"cliVersion": "0.166.135",
|
|
7
|
+
"cliVersions": ["0.166.135"],
|
|
8
|
+
"date": "2026-10-31",
|
|
9
|
+
"title": "",
|
|
10
|
+
"sections": [
|
|
11
|
+
{
|
|
12
|
+
"heading": "Fixed - cc CLI 0.166.135: Responses disconnect recovery and launch policy preservation (candidate, 2026-10-31)",
|
|
13
|
+
"body": "- Recover genuine pre-output OpenAI Responses JSON disconnects with the existing bounded retry policy, distinct physical request IDs and synchronous usage settlement. Bound stalled headers and JSON reads, preserve HTTP/provider verdicts and caller cancellation, and keep workflow effects and incomplete tool streams non-replayable.\n- Settle completed Responses events without waiting for socket EOF. A hung reader cancellation no longer blocks timeout settlement; already queued transport aborts and user cancellation remain authoritative.\n- Select newly generated context handoffs before ordinary conversation so a full conversation partition cannot evict its summary. Retain the same input budget, data-only trust and oversized-summary rejection.\n- Preserve an explicit empty tool allowlist through saved launch argv and the CLI parser. Reject malformed persisted permission modes, sandbox modes and budgets instead of silently restoring weaker defaults.\n- Strengthen generic regression guidance for delegated lifecycle ownership, race controls, persisted history projections and a final response within the iteration budget. Add same-handle metadata-race and approval-cancellation controls without changing historical model candidates.\n- Give independent IDE cleanup a separate bounded deadline after the model task ends. The previous `verify-26` trace actually contains `error_max_turns` at 20/20; cleanup had obscured that result with an expired-deadline error. Historical OpenAI acceptance remains 39/45 and is not reclassified by these fixes.\n- Prepare companion Open VSX 0.37.172 and JetBrains 0.4.189 for CLI 0.166.135. Required child npm packages retain their published versions; publication must follow the exact-source multi-platform gates and upstream readback."
|
|
14
|
+
}
|
|
15
|
+
]
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"productVersion": null,
|
|
19
|
+
"cliVersion": "0.166.133",
|
|
20
|
+
"cliVersions": ["0.166.133"],
|
|
21
|
+
"date": "2026-10-30",
|
|
22
|
+
"title": "",
|
|
23
|
+
"sections": [
|
|
24
|
+
{
|
|
25
|
+
"heading": "Fixed - cc CLI 0.166.133: scoped mutation schemas and model platform compatibility (candidate, 2026-10-30)",
|
|
26
|
+
"body": "- Expose canonical allowed mutation paths in provider tool schemas for scoped write/edit calls. Explain that absolute paths returned by read tools are not valid mutation arguments in that run; execution-time permission, scope and file-identity checks remain authoritative.\n- Preserve the required `summary` array on Responses reasoning items, including empty summaries and replayed older sessions, so tool continuations do not fail with `Missing required parameter: input[n].summary`.\n- Apply the current self-evolution setting to ordinary `cc ui` Chat, Agent, streaming and Personal Data Hub analysis, skills and resolver requests. Disabled requests do not require a deployment certificate; enabled deployments retain authentication checks. Existing UI sessions re-read the setting for subsequent requests.\n- Support explicitly configured `custom` OpenAI-compatible local and LAN platforms across Chat, Agent, streaming and connection probes. Keep the platform's API base path and model alias, allow platform-specific or absent credentials, and do not relabel localhost endpoints as Ollama or fall back to an unrelated cloud account.\n- Validate the model-platform and self-evolution fixes with over 400 focused regression tests, including the actual Chat-session creation path and disabled resolver model/embedding transports. Platform compatibility uses controlled test endpoints; the reported user's live platform was not contacted.\n- Validate the generic schema contract: the new provider-wire suite passed **10/10 tests**; the combined related suites passed **34 tests**, with **1 existing Windows skip**. Local test results do not establish new real-model task success or change the historical **34/45** automatic acceptance total.\n- Prepare candidate pairing Open VSX **0.37.171** / JetBrains **0.4.188** for CLI **0.166.133**. Kernel **0.1.11** and unchanged child packages retain their versions; no independent IDE UI or client release is included.\n- Release requirements: verified public child packages, exact-source multi-OS release gates, CLI OIDC publication and public readback before IDE publication and readback. Consult versioned publication evidence for actual availability. Microsoft Marketplace remains deferred; **0.166.132 / 0.37.170 / 0.4.187** is the preceding verified public pairing."
|
|
27
|
+
}
|
|
28
|
+
]
|
|
29
|
+
},
|
|
4
30
|
{
|
|
5
31
|
"productVersion": null,
|
|
6
32
|
"cliVersion": "0.166.132",
|
|
@@ -121,7 +121,7 @@ export async function handleLlmChat(server, id, ws, message) {
|
|
|
121
121
|
return;
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
-
if (creds.provider
|
|
124
|
+
if (!["ollama", "custom"].includes(creds.provider) && !creds.apiKey) {
|
|
125
125
|
const envHint = BUILT_IN_PROVIDERS[creds.provider]?.apiKeyEnv;
|
|
126
126
|
send({
|
|
127
127
|
type: `${topic}.result`,
|
|
@@ -25,6 +25,7 @@ import {
|
|
|
25
25
|
import { importPdh } from "../../lib/pdh-load-error.js";
|
|
26
26
|
import { getAIChatWizard } from "../../lib/personal-data-hub-aichat-wizard.js";
|
|
27
27
|
import { runDedicatedBatchCollectors } from "../../lib/pdh-dedicated-batch-collectors.js";
|
|
28
|
+
import { resolveServerEvolutionFactory } from "../../lib/evolution/optional-ui-model-turn.js";
|
|
28
29
|
import {
|
|
29
30
|
existsSync,
|
|
30
31
|
unlinkSync,
|
|
@@ -150,8 +151,9 @@ export async function _tryAdbAutoPullInputPath(hub, name, options) {
|
|
|
150
151
|
return options || {};
|
|
151
152
|
}
|
|
152
153
|
try {
|
|
153
|
-
const { createHostAdbBridge } =
|
|
154
|
-
|
|
154
|
+
const { createHostAdbBridge } = await import(
|
|
155
|
+
"../../lib/host-adb-bridge.js"
|
|
156
|
+
);
|
|
155
157
|
const bridge = createHostAdbBridge();
|
|
156
158
|
const content = await bridge.invoke("snapshot.read", {
|
|
157
159
|
fileName: `${name}.json`,
|
|
@@ -192,9 +194,15 @@ async function withHub(fn, loadHub = getHub) {
|
|
|
192
194
|
}
|
|
193
195
|
}
|
|
194
196
|
|
|
195
|
-
function governedHubLoader(
|
|
197
|
+
async function governedHubLoader(server) {
|
|
198
|
+
const factory = await resolveServerEvolutionFactory(server);
|
|
196
199
|
if (typeof factory === "function")
|
|
197
200
|
return () => getGovernedAnalysisHub(factory);
|
|
201
|
+
if (
|
|
202
|
+
factory === null &&
|
|
203
|
+
typeof server?.resolveEvolutionCompositionFactory === "function"
|
|
204
|
+
)
|
|
205
|
+
return () => getGovernedAnalysisHub(null, { evolutionDisabled: true });
|
|
198
206
|
const error = new Error(
|
|
199
207
|
"Personal Data Hub model egress requires an authenticated evolution composition factory",
|
|
200
208
|
);
|
|
@@ -215,11 +223,13 @@ function validateEntityReference(entityType, entityId) {
|
|
|
215
223
|
|
|
216
224
|
export const PERSONAL_DATA_HUB_HANDLERS = {
|
|
217
225
|
"personal-data-hub.ask": async (msg, context = {}) => {
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
226
|
+
return withHub(
|
|
227
|
+
async (hub) => {
|
|
228
|
+
if (!hub.engine) throw new Error("Analysis engine unavailable");
|
|
229
|
+
return await hub.engine.ask(msg.question, msg.options || {});
|
|
230
|
+
},
|
|
231
|
+
await governedHubLoader(context.server),
|
|
232
|
+
);
|
|
223
233
|
},
|
|
224
234
|
|
|
225
235
|
// Path Y: prompt context only, no LLM call. Lets web-shell / mobile host
|
|
@@ -668,7 +678,14 @@ export const PERSONAL_DATA_HUB_HANDLERS = {
|
|
|
668
678
|
"personal-data-hub.resolver-drain": async (msg, context = {}) =>
|
|
669
679
|
withHub(async (hub) => {
|
|
670
680
|
if (!hub.entityResolver) throw new Error("EntityResolver not wired");
|
|
671
|
-
const factory = context.server
|
|
681
|
+
const factory = await resolveServerEvolutionFactory(context.server);
|
|
682
|
+
if (
|
|
683
|
+
factory === null &&
|
|
684
|
+
typeof context.server?.resolveEvolutionCompositionFactory === "function"
|
|
685
|
+
)
|
|
686
|
+
return await hub.drainResolver({ limit: msg.limit || 50 }, null, {
|
|
687
|
+
evolutionDisabled: true,
|
|
688
|
+
});
|
|
672
689
|
return await hub.drainResolver({ limit: msg.limit || 50 }, factory);
|
|
673
690
|
}),
|
|
674
691
|
|
|
@@ -691,10 +708,9 @@ export const PERSONAL_DATA_HUB_HANDLERS = {
|
|
|
691
708
|
withHub((hub) => hub.analysisSkillNames || []),
|
|
692
709
|
|
|
693
710
|
"personal-data-hub.run-skill": async (msg, context = {}) => {
|
|
694
|
-
const factory = context.server?.evolutionCompositionFactory;
|
|
695
711
|
return withHub(
|
|
696
712
|
async (hub) => await hub.runSkill(msg.name, msg.options || {}),
|
|
697
|
-
governedHubLoader(
|
|
713
|
+
await governedHubLoader(context.server),
|
|
698
714
|
);
|
|
699
715
|
},
|
|
700
716
|
|
|
@@ -19,12 +19,14 @@ function fail(code, message) {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
async function canonicalMemoryService(scopeKey) {
|
|
22
|
-
const { resolveCliContextMemoryCutover } =
|
|
23
|
-
|
|
22
|
+
const { resolveCliContextMemoryCutover } = await import(
|
|
23
|
+
"../../lib/context-memory-kernel/authority.js"
|
|
24
|
+
);
|
|
24
25
|
const decision = resolveCliContextMemoryCutover({ scopeKey });
|
|
25
26
|
if (!decision.canonical) return null;
|
|
26
|
-
const { createCliCanonicalMemoryService } =
|
|
27
|
-
|
|
27
|
+
const { createCliCanonicalMemoryService } = await import(
|
|
28
|
+
"../../lib/context-memory-kernel/memory-service.js"
|
|
29
|
+
);
|
|
28
30
|
return createCliCanonicalMemoryService();
|
|
29
31
|
}
|
|
30
32
|
|
|
@@ -104,8 +106,9 @@ async function sessionsEnd(message) {
|
|
|
104
106
|
let consolidated = null;
|
|
105
107
|
if (message.consolidate) {
|
|
106
108
|
try {
|
|
107
|
-
const { consolidateJsonlSession } =
|
|
108
|
-
|
|
109
|
+
const { consolidateJsonlSession } = await import(
|
|
110
|
+
"../../lib/session-consolidator.js"
|
|
111
|
+
);
|
|
109
112
|
consolidated = await consolidateJsonlSession(message.sessionId, {
|
|
110
113
|
scope: message.scope || "session",
|
|
111
114
|
scopeId: message.scopeId || null,
|
|
@@ -249,8 +252,9 @@ async function memoryDelete(message) {
|
|
|
249
252
|
async function memoryConsolidate(message) {
|
|
250
253
|
if (!message.sessionId) return fail("BAD_REQUEST", "sessionId required");
|
|
251
254
|
try {
|
|
252
|
-
const { consolidateJsonlSession } =
|
|
253
|
-
|
|
255
|
+
const { consolidateJsonlSession } = await import(
|
|
256
|
+
"../../lib/session-consolidator.js"
|
|
257
|
+
);
|
|
254
258
|
const result = await consolidateJsonlSession(message.sessionId, {
|
|
255
259
|
scope: message.scope || "session",
|
|
256
260
|
scopeId: message.scopeId || null,
|
|
@@ -321,16 +325,17 @@ async function streamRun(message, sender, signal, context) {
|
|
|
321
325
|
const { buildProviderSource } = await import("../../lib/provider-stream.js");
|
|
322
326
|
const { createStreamRouter } = await loadSingletons();
|
|
323
327
|
const router = createStreamRouter();
|
|
324
|
-
const {
|
|
325
|
-
|
|
326
|
-
const turn = await prepareGovernedModelTurn(
|
|
327
|
-
context?.server?.evolutionCompositionFactory ?? null,
|
|
328
|
-
{
|
|
329
|
-
mode: "ws-stream",
|
|
330
|
-
messages: [{ role: "user", content: message.prompt }],
|
|
331
|
-
signal,
|
|
332
|
-
},
|
|
328
|
+
const { governModelTokenSource } = await import(
|
|
329
|
+
"../../lib/evolution/governed-model-turn.js"
|
|
333
330
|
);
|
|
331
|
+
const { prepareServerUiModelTurn } = await import(
|
|
332
|
+
"../../lib/evolution/optional-ui-model-turn.js"
|
|
333
|
+
);
|
|
334
|
+
const turn = await prepareServerUiModelTurn(context?.server, {
|
|
335
|
+
mode: "ws-stream",
|
|
336
|
+
messages: [{ role: "user", content: message.prompt }],
|
|
337
|
+
signal,
|
|
338
|
+
});
|
|
334
339
|
const source = governModelTokenSource(
|
|
335
340
|
buildProviderSource(provider, {
|
|
336
341
|
model: message.model,
|
|
@@ -31,6 +31,7 @@ import {
|
|
|
31
31
|
} from "../../lib/session-budget-production-root.js";
|
|
32
32
|
import { mergePricing } from "../../lib/llm-pricing.js";
|
|
33
33
|
import { ensureCanonicalSessionTranscript } from "../../lib/session-transcript-migration.js";
|
|
34
|
+
import { prepareServerUiModelTurn } from "../../lib/evolution/optional-ui-model-turn.js";
|
|
34
35
|
|
|
35
36
|
const PAYLOAD_DIGEST = /^sha256:[0-9a-f]{64}$/;
|
|
36
37
|
const RAW_AUTHORITY_HASH = /^[0-9a-f]{64}$/;
|
|
@@ -505,8 +506,9 @@ function sameSessionBudgetConfig(left, right) {
|
|
|
505
506
|
|
|
506
507
|
async function _isPhase5EnvelopesEnabled() {
|
|
507
508
|
try {
|
|
508
|
-
const { getBetaFlags } =
|
|
509
|
-
|
|
509
|
+
const { getBetaFlags } = await import(
|
|
510
|
+
"../../lib/session-core-singletons.js"
|
|
511
|
+
);
|
|
510
512
|
const flags = await getBetaFlags();
|
|
511
513
|
return flags.isEnabled(PHASE5_ENVELOPE_FLAG);
|
|
512
514
|
} catch (_e) {
|
|
@@ -532,8 +534,9 @@ async function ensureSessionHandler(
|
|
|
532
534
|
throw error;
|
|
533
535
|
}
|
|
534
536
|
|
|
535
|
-
const { WebSocketInteractionAdapter } =
|
|
536
|
-
|
|
537
|
+
const { WebSocketInteractionAdapter } = await import(
|
|
538
|
+
"../../lib/interaction-adapter.js"
|
|
539
|
+
);
|
|
537
540
|
const enablePhase5Envelopes = await _isPhase5EnvelopesEnabled();
|
|
538
541
|
const stateSnapshot =
|
|
539
542
|
typeof server.sessionManager.getSessionStateSnapshot === "function"
|
|
@@ -605,6 +608,8 @@ async function ensureSessionHandler(
|
|
|
605
608
|
session,
|
|
606
609
|
interaction: session.interaction,
|
|
607
610
|
evolutionCompositionFactory: server.evolutionCompositionFactory ?? null,
|
|
611
|
+
prepareUiModelTurn: (context) =>
|
|
612
|
+
prepareServerUiModelTurn(server, context),
|
|
608
613
|
});
|
|
609
614
|
} else {
|
|
610
615
|
const { WSAgentHandler } = await import("./ws-agent-handler.js");
|
|
@@ -661,6 +666,10 @@ async function ensureSessionHandler(
|
|
|
661
666
|
sessionHostLease,
|
|
662
667
|
sessionBudgetRoot,
|
|
663
668
|
evolutionCompositionFactory: server.evolutionCompositionFactory,
|
|
669
|
+
resolveEvolutionCompositionFactory:
|
|
670
|
+
typeof server.resolveEvolutionCompositionFactory === "function"
|
|
671
|
+
? () => server.resolveEvolutionCompositionFactory()
|
|
672
|
+
: null,
|
|
664
673
|
skillOutcomeIndex: server.skillOutcomeIndex,
|
|
665
674
|
skillVectorAuthority: server.skillVectorAuthority,
|
|
666
675
|
...server.runtimeAdmissionDependencies,
|
|
@@ -1733,8 +1742,9 @@ export async function handleSubAgentList(server, id, ws, message) {
|
|
|
1733
1742
|
const sessionId = message?.sessionId || null;
|
|
1734
1743
|
|
|
1735
1744
|
try {
|
|
1736
|
-
const { SubAgentRegistry } =
|
|
1737
|
-
|
|
1745
|
+
const { SubAgentRegistry } = await import(
|
|
1746
|
+
"../../lib/sub-agent-registry.js"
|
|
1747
|
+
);
|
|
1738
1748
|
const registry = SubAgentRegistry.getInstance();
|
|
1739
1749
|
|
|
1740
1750
|
let payload;
|
|
@@ -1796,8 +1806,9 @@ export async function handleSubAgentGet(server, id, ws, message) {
|
|
|
1796
1806
|
}
|
|
1797
1807
|
|
|
1798
1808
|
try {
|
|
1799
|
-
const { SubAgentRegistry } =
|
|
1800
|
-
|
|
1809
|
+
const { SubAgentRegistry } = await import(
|
|
1810
|
+
"../../lib/sub-agent-registry.js"
|
|
1811
|
+
);
|
|
1801
1812
|
const snapshot = SubAgentRegistry.getInstance().getById(subAgentId);
|
|
1802
1813
|
|
|
1803
1814
|
if (!snapshot) {
|
|
@@ -183,6 +183,7 @@ export class WSAgentHandler {
|
|
|
183
183
|
sessionBudgetRoot = null,
|
|
184
184
|
hostResourceBudget = null,
|
|
185
185
|
evolutionCompositionFactory = null,
|
|
186
|
+
resolveEvolutionCompositionFactory = null,
|
|
186
187
|
skillOutcomeIndex = null,
|
|
187
188
|
skillVectorAuthority = null,
|
|
188
189
|
skillRuntimeAdmission = null,
|
|
@@ -265,6 +266,15 @@ export class WSAgentHandler {
|
|
|
265
266
|
throw new TypeError("evolutionCompositionFactory must be a function");
|
|
266
267
|
}
|
|
267
268
|
this._evolutionCompositionFactory = evolutionCompositionFactory;
|
|
269
|
+
if (
|
|
270
|
+
resolveEvolutionCompositionFactory !== null &&
|
|
271
|
+
typeof resolveEvolutionCompositionFactory !== "function"
|
|
272
|
+
)
|
|
273
|
+
throw new TypeError(
|
|
274
|
+
"resolveEvolutionCompositionFactory must be a function",
|
|
275
|
+
);
|
|
276
|
+
this._resolveEvolutionCompositionFactory =
|
|
277
|
+
resolveEvolutionCompositionFactory;
|
|
268
278
|
this._skillOutcomeIndex =
|
|
269
279
|
skillOutcomeIndex === null
|
|
270
280
|
? null
|
|
@@ -337,6 +347,14 @@ export class WSAgentHandler {
|
|
|
337
347
|
: Object.freeze(hostSystemPrefix);
|
|
338
348
|
}
|
|
339
349
|
|
|
350
|
+
async _refreshEvolutionFactory() {
|
|
351
|
+
if (this._resolveEvolutionCompositionFactory === null) return;
|
|
352
|
+
const current = await this._resolveEvolutionCompositionFactory();
|
|
353
|
+
if (current !== null && typeof current !== "function")
|
|
354
|
+
throw new TypeError("Invalid evolution composition factory");
|
|
355
|
+
this._evolutionCompositionFactory = current;
|
|
356
|
+
}
|
|
357
|
+
|
|
340
358
|
async _prepareEvolutionTurn(requestId) {
|
|
341
359
|
if (!this._evolutionCompositionFactory) return null;
|
|
342
360
|
const sessionId = String(this.session.id);
|
|
@@ -876,6 +894,7 @@ export class WSAgentHandler {
|
|
|
876
894
|
let result;
|
|
877
895
|
try {
|
|
878
896
|
this._sessionHostLease?.assert?.();
|
|
897
|
+
|
|
879
898
|
result = await this._compactContextMemory(
|
|
880
899
|
session.messages,
|
|
881
900
|
{
|
|
@@ -1402,6 +1421,11 @@ export class WSAgentHandler {
|
|
|
1402
1421
|
const { session } = this;
|
|
1403
1422
|
this.assertSessionBudgetAdmission("WebSocket turn");
|
|
1404
1423
|
this._sessionHostLease?.assert?.();
|
|
1424
|
+
// A resident UI must observe a changed setting before each new turn.
|
|
1425
|
+
if (this._resolveEvolutionCompositionFactory !== null) {
|
|
1426
|
+
await this._refreshEvolutionFactory();
|
|
1427
|
+
}
|
|
1428
|
+
turnSignal.throwIfAborted();
|
|
1405
1429
|
|
|
1406
1430
|
if (session.canonicalJsonlSession === true) {
|
|
1407
1431
|
const inputDigest =
|
|
@@ -1676,6 +1700,7 @@ export class WSAgentHandler {
|
|
|
1676
1700
|
}
|
|
1677
1701
|
|
|
1678
1702
|
const executeAgentTurn = async () => {
|
|
1703
|
+
turnSignal.throwIfAborted();
|
|
1679
1704
|
for await (const event of this._agentLoop(
|
|
1680
1705
|
session.messages,
|
|
1681
1706
|
loopOptions,
|
|
@@ -2282,6 +2307,11 @@ export class WSAgentHandler {
|
|
|
2282
2307
|
try {
|
|
2283
2308
|
this.assertSessionBudgetAdmission("WebSocket compaction");
|
|
2284
2309
|
this._sessionHostLease?.assert?.();
|
|
2310
|
+
if (this._resolveEvolutionCompositionFactory !== null) {
|
|
2311
|
+
await this._refreshEvolutionFactory();
|
|
2312
|
+
}
|
|
2313
|
+
this._sessionBudgetSignal?.throwIfAborted();
|
|
2314
|
+
this._sessionHostLease?.assert?.();
|
|
2285
2315
|
compactionEvolution = await this._prepareEvolutionTurn(
|
|
2286
2316
|
`compact:${requestId || randomUUID()}`,
|
|
2287
2317
|
);
|
|
@@ -666,6 +666,41 @@ export function normalizeBackgroundLaunchProfile(input) {
|
|
|
666
666
|
`Unsupported background launch profile version: ${input?.version ?? "missing"}`,
|
|
667
667
|
);
|
|
668
668
|
}
|
|
669
|
+
const rejectField = (field) => {
|
|
670
|
+
const error = new Error(
|
|
671
|
+
`Invalid background launch profile field: ${field}`,
|
|
672
|
+
);
|
|
673
|
+
error.code = "BACKGROUND_LAUNCH_PROFILE_INVALID";
|
|
674
|
+
throw error;
|
|
675
|
+
};
|
|
676
|
+
// A persisted policy must not silently fall back to a weaker default.
|
|
677
|
+
if (
|
|
678
|
+
input.permission?.mode != null &&
|
|
679
|
+
!PERMISSION_MODES.has(input.permission.mode)
|
|
680
|
+
)
|
|
681
|
+
rejectField("permission.mode");
|
|
682
|
+
if (input.sandbox?.mode != null && !SANDBOX_MODES.has(input.sandbox.mode))
|
|
683
|
+
rejectField("sandbox.mode");
|
|
684
|
+
for (const field of [
|
|
685
|
+
"maxTurns",
|
|
686
|
+
"maxCostUsd",
|
|
687
|
+
"thinkingTokens",
|
|
688
|
+
"maxRewakes",
|
|
689
|
+
"maxOuterTurns",
|
|
690
|
+
"goalMaxTokens",
|
|
691
|
+
"goalMaxCostUsd",
|
|
692
|
+
"goalMaxTimeMs",
|
|
693
|
+
]) {
|
|
694
|
+
const value = input.budget?.[field];
|
|
695
|
+
if (value == null || value === "") continue;
|
|
696
|
+
const number = Number(value);
|
|
697
|
+
if (
|
|
698
|
+
!Number.isFinite(number) ||
|
|
699
|
+
number <= 0 ||
|
|
700
|
+
(!field.toLowerCase().includes("cost") && !Number.isSafeInteger(number))
|
|
701
|
+
)
|
|
702
|
+
rejectField(`budget.${field}`);
|
|
703
|
+
}
|
|
669
704
|
const cwd = canonicalPath(input.workspace?.cwd || process.cwd());
|
|
670
705
|
const safeBase = sanitizeBackgroundBaseUrl(input.llm?.baseUrl);
|
|
671
706
|
const permissionMode = PERMISSION_MODES.has(input.permission?.mode)
|
|
@@ -966,7 +1001,8 @@ export function buildArgvFromBackgroundLaunchProfile(profile) {
|
|
|
966
1001
|
pushValue(argv, "--fallback-model", model);
|
|
967
1002
|
}
|
|
968
1003
|
if (p.tools.allowed !== null) {
|
|
969
|
-
|
|
1004
|
+
// Empty means no tools; omitting this option would allow every tool.
|
|
1005
|
+
argv.push("--allowed-tools", p.tools.allowed.join(","));
|
|
970
1006
|
}
|
|
971
1007
|
pushValue(argv, "--disallowed-tools", p.tools.disallowed.join(","));
|
|
972
1008
|
pushValue(argv, "--permission-mode", p.permission.mode);
|