chainlesschain 0.166.131 → 0.166.133
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 +46 -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/chat-core.js +40 -35
- package/src/lib/context-memory-kernel/message-adapter.js +27 -6
- package/src/lib/context-memory-kernel/provider-context.js +48 -3
- 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 +201 -15
- package/src/runtime/message-roles.js +67 -3
- package/src/runtime/provider-stream-state.js +35 -1
package/README.md
CHANGED
|
@@ -2,11 +2,35 @@
|
|
|
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.133
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
This source tree targets package version **0.166.133**. 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. Over **400 focused regression tests** passed for the model-platform and self-evolution fixes; these controlled tests do not establish live-platform acceptance or public availability.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
When this version is available from npm, install it with `npm i -g chainlesschain@0.166.133 --registry https://registry.npmjs.org`. The companion version pair is Open VSX **0.37.171** / JetBrains **0.4.188**; the CLI is published before its companion extensions. Kernel stays **0.1.11**. Consult the [current release guide](https://docs.chainlesschain.com/chainlesschain/agent-platform-release.html) for verified channel availability. Source preparation alone does not establish publication or release-gate success.
|
|
10
|
+
|
|
11
|
+
## CLI 0.166.132 — verified public baseline before this source revision
|
|
12
|
+
|
|
13
|
+
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`.
|
|
14
|
+
|
|
15
|
+
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.
|
|
16
|
+
|
|
17
|
+
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).
|
|
18
|
+
|
|
19
|
+
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.
|
|
20
|
+
|
|
21
|
+
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.
|
|
22
|
+
|
|
23
|
+
## CLI 0.166.131 — previous public release (2026-10-29)
|
|
24
|
+
|
|
25
|
+
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.
|
|
26
|
+
|
|
27
|
+
This release includes explicit independent Goal `verify:` checks, Memory directory identity and cancellation fixes, MCP subtask and late-connection isolation, Skills download cancellation, rule refresh around compaction, temporary-worktree lifetime fixes and updated Terra capability/billing contracts. Exact-source three-OS CLI / Strict / Kernel checks passed. [OIDC publication](https://github.com/chainlesschain/chainlesschain/actions/runs/40862685540) and [independent public readback](https://github.com/chainlesschain/chainlesschain/actions/runs/40866417524) verified immutable package bytes and signed provenance. [Publication evidence](../../docs/research/cli/evidence/release131-public/publication.json).
|
|
28
|
+
|
|
29
|
+
Cumulative accepted tasks remain **34/45**, with **11** unfinished. Full Memory SLO, semantic quality and both IDE journeys remain separate acceptance work; the current native generation driver does not automatically use `verify:`. **NOT_PAID_READY / NOT_ADMITTED / HOLD** remain. PC/mobile retain their independent **v5.0.3.143** release. Older notes below retain their historical scope.
|
|
30
|
+
|
|
31
|
+
[.131 upgrade guide](https://docs.chainlesschain.com/chainlesschain/cli-ide-update-2026-10-29-131.html) · [Runtime design](https://design.chainlesschain.com/release-runtime-update-2026-10-29-131.html).
|
|
32
|
+
|
|
33
|
+
## CLI 0.166.130 — previous public release (2026-10-29)
|
|
10
34
|
|
|
11
35
|
Install the verified public package with `npm i -g chainlesschain@0.166.130 --registry https://registry.npmjs.org`. Public Open VSX **0.37.168** and approved JetBrains **0.4.185** both recommend `.130`; restart the chat host and run `cc --version` and `cc doctor`. Microsoft Marketplace remains unpublished; use Open VSX or its versioned VSIX.
|
|
12
36
|
|
|
@@ -54,7 +78,7 @@ Ordinary Agent, `ask`, `chat` and `cc ui` use self-evolution only when enabled.
|
|
|
54
78
|
|
|
55
79
|
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.
|
|
56
80
|
|
|
57
|
-
The **Skill 自进化配置** page in `cc ui`, VS Code and JetBrains shares this setting. Quick Ask
|
|
81
|
+
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.
|
|
58
82
|
|
|
59
83
|
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`.
|
|
60
84
|
|
|
@@ -229,7 +253,7 @@ Explicit Linux durable permissions require administrator provisioning. Cross-pro
|
|
|
229
253
|
Install the CLI version published by this release:
|
|
230
254
|
|
|
231
255
|
```bash
|
|
232
|
-
npm install -g chainlesschain@0.166.
|
|
256
|
+
npm install -g chainlesschain@0.166.132 --registry https://registry.npmjs.org
|
|
233
257
|
chainlesschain setup
|
|
234
258
|
```
|
|
235
259
|
|
|
@@ -2402,19 +2426,23 @@ Configuration is stored at `~/.chainlesschain/config.json`. The CLI creates and
|
|
|
2402
2426
|
|
|
2403
2427
|
### Supported LLM Providers
|
|
2404
2428
|
|
|
2405
|
-
| Provider | Default Model | API Key Required
|
|
2406
|
-
| -------------------------- | ---------------------- |
|
|
2407
|
-
| Ollama (Local) | qwen2.5:7b | No
|
|
2408
|
-
| OpenAI | gpt-4o | Yes
|
|
2409
|
-
| Anthropic | claude-opus-4-6 | Yes
|
|
2410
|
-
| DeepSeek | deepseek-chat | Yes
|
|
2411
|
-
| DashScope (Alibaba) | qwen-turbo | Yes
|
|
2412
|
-
| Google Gemini | gemini-2.0-flash | Yes
|
|
2413
|
-
| Mistral AI | mistral-large-latest | Yes
|
|
2414
|
-
| Volcengine (火山引擎/豆包) | doubao-seed-1-6-251015 | Yes
|
|
2415
|
-
| Kimi (月之暗面) | moonshot-v1-auto | Yes
|
|
2416
|
-
| MiniMax (海螺AI) | MiniMax-Text-01 | Yes
|
|
2417
|
-
| Custom
|
|
2429
|
+
| Provider | Default Model | API Key Required |
|
|
2430
|
+
| -------------------------- | ---------------------- | ------------------ |
|
|
2431
|
+
| Ollama (Local) | qwen2.5:7b | No |
|
|
2432
|
+
| OpenAI | gpt-4o | Yes |
|
|
2433
|
+
| Anthropic | claude-opus-4-6 | Yes |
|
|
2434
|
+
| DeepSeek | deepseek-chat | Yes |
|
|
2435
|
+
| DashScope (Alibaba) | qwen-turbo | Yes |
|
|
2436
|
+
| Google Gemini | gemini-2.0-flash | Yes |
|
|
2437
|
+
| Mistral AI | mistral-large-latest | Yes |
|
|
2438
|
+
| Volcengine (火山引擎/豆包) | doubao-seed-1-6-251015 | Yes |
|
|
2439
|
+
| Kimi (月之暗面) | moonshot-v1-auto | Yes |
|
|
2440
|
+
| MiniMax (海螺AI) | MiniMax-Text-01 | Yes |
|
|
2441
|
+
| Custom (OpenAI-compatible) | Set explicitly | Platform-dependent |
|
|
2442
|
+
|
|
2443
|
+
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.
|
|
2444
|
+
|
|
2445
|
+
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.
|
|
2418
2446
|
|
|
2419
2447
|
## File Structure
|
|
2420
2448
|
|
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.133",
|
|
7
|
+
"cliVersions": ["0.166.133"],
|
|
8
|
+
"date": "2026-10-30",
|
|
9
|
+
"title": "",
|
|
10
|
+
"sections": [
|
|
11
|
+
{
|
|
12
|
+
"heading": "Fixed - cc CLI 0.166.133: scoped mutation schemas and model platform compatibility (candidate, 2026-10-30)",
|
|
13
|
+
"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."
|
|
14
|
+
}
|
|
15
|
+
]
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"productVersion": null,
|
|
19
|
+
"cliVersion": "0.166.132",
|
|
20
|
+
"cliVersions": ["0.166.132"],
|
|
21
|
+
"date": "2026-10-30",
|
|
22
|
+
"title": "",
|
|
23
|
+
"sections": [
|
|
24
|
+
{
|
|
25
|
+
"heading": "Fixed - cc CLI 0.166.132: output-limit integrity and bounded recovery (candidate, 2026-10-30)",
|
|
26
|
+
"body": "- Preserve OpenAI-compatible chat-completions termination evidence in JSON and streaming responses. A response ending with `length` cannot execute any of its tool calls or become a successful final answer, even when its arguments happen to be valid JSON.\n- Settle complete physical-call usage before permitting at most one fresh planning iteration within the existing budget. Discard the truncated assistant/tool group; missing usage, incomplete streams, exhausted budgets and repeated truncation remain failures. Cancellation and final-synthesis limits still apply.\n- Reject `content_filter` and explicitly incomplete or ambiguous provider termination before executing tools or reporting completion. Filtered or ambiguous responses do not trigger a recovery request; conflicting SSE frames cannot clear an earlier filter.\n- Bind the current real user task across ordinary tool rounds, recovery, thinking-only turns and review, including canonical planning and recall. Resolve the owned task after provider projection, reject lost or ambiguous mappings, and retain the existing hermetic review contract. Recovery hints remain temporary system suffixes and do not replace the task.\n- Preserve legitimate resumed user-message merges through source mappings owned by the built-in role projector, while rejecting forged mapping fields and arbitrary rewrites. Recheck the task after asynchronous recall so changed content or moved messages cannot acquire a stale binding; keep authenticated history unchanged.\n- Move the existing user message after retained read context without duplicating it or discarding metadata. This preserves task identity after canonical compaction and independent CLI restart, retains historical user messages, and does not invent a fallback task when no user is present.\n- Add protocol and real canonical CLI process regressions for recovery, repeated truncation, missing usage, filtered output and ambiguous termination. Separately install three assisted pagination/history regression deliveries; their independent checks do not change the original 34/45 automatic task result.\n- Repair the VS Code README's current-public and unpublished-candidate sections against the existing Marketplace overview contract, retaining versioned artifacts and historical releases.\n- Pair the candidate with Open VSX 0.37.170 / JetBrains 0.4.187 after CLI publication is verified; Context/Memory Kernel remains 0.1.11. Exact-source full multi-OS gates, OIDC publication and public readback are required. Microsoft Marketplace remains deferred.\n- This change does not bypass the diagnostic broker's HTTP403 rejection or guarantee that a model will produce smaller writes. Real-model task retests follow verified publication of the new versions."
|
|
27
|
+
}
|
|
28
|
+
]
|
|
29
|
+
},
|
|
4
30
|
{
|
|
5
31
|
"productVersion": null,
|
|
6
32
|
"cliVersion": "0.166.131",
|
|
@@ -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
|
);
|
package/src/lib/chat-core.js
CHANGED
|
@@ -10,9 +10,13 @@ import { providerFetch } from "./formal-budget-provider-scope.js";
|
|
|
10
10
|
* - streamOllama / streamOpenAI — low-level streaming helpers
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
BUILT_IN_PROVIDERS,
|
|
15
|
+
resolveProviderDefinition,
|
|
16
|
+
} from "./llm-providers.js";
|
|
14
17
|
import { normalizeOllamaBaseUrl } from "./ollama-base-url.js";
|
|
15
18
|
import {
|
|
19
|
+
assertCustomProviderConnection,
|
|
16
20
|
geminiGenerateContentUrl,
|
|
17
21
|
geminiRequestBody,
|
|
18
22
|
geminiUsage,
|
|
@@ -262,22 +266,22 @@ export async function streamOpenAI(
|
|
|
262
266
|
response = await providerFetch(
|
|
263
267
|
`${normalizeProviderBaseUrl(baseUrl)}/chat/completions`,
|
|
264
268
|
{
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
269
|
+
method: "POST",
|
|
270
|
+
headers: {
|
|
271
|
+
"Content-Type": "application/json",
|
|
272
|
+
...(apiKey ? { Authorization: `Bearer ${apiKey}` } : {}),
|
|
273
|
+
},
|
|
274
|
+
body: JSON.stringify({
|
|
275
|
+
model,
|
|
276
|
+
messages,
|
|
277
|
+
stream: true,
|
|
278
|
+
// Opt-in token usage in the terminal chunk (OpenAI-compatible).
|
|
279
|
+
// Servers that don't understand it simply ignore it.
|
|
280
|
+
stream_options: { include_usage: true },
|
|
281
|
+
}),
|
|
282
|
+
signal: options.signal
|
|
283
|
+
? AbortSignal.any([guard.signal, options.signal])
|
|
284
|
+
: guard.signal,
|
|
281
285
|
},
|
|
282
286
|
);
|
|
283
287
|
} catch (e) {
|
|
@@ -410,22 +414,22 @@ export async function streamAnthropic(
|
|
|
410
414
|
response = await providerFetch(
|
|
411
415
|
`${normalizeProviderBaseUrl(baseUrl)}/messages`,
|
|
412
416
|
{
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
417
|
+
method: "POST",
|
|
418
|
+
headers: {
|
|
419
|
+
"Content-Type": "application/json",
|
|
420
|
+
"x-api-key": apiKey,
|
|
421
|
+
"anthropic-version": "2023-06-01",
|
|
422
|
+
},
|
|
423
|
+
body: JSON.stringify({
|
|
424
|
+
model,
|
|
425
|
+
max_tokens: 4096,
|
|
426
|
+
stream: true,
|
|
427
|
+
...(system ? { system } : {}),
|
|
428
|
+
messages: convo,
|
|
429
|
+
}),
|
|
430
|
+
signal: options.signal
|
|
431
|
+
? AbortSignal.any([guard.signal, options.signal])
|
|
432
|
+
: guard.signal,
|
|
429
433
|
},
|
|
430
434
|
);
|
|
431
435
|
} catch (e) {
|
|
@@ -666,6 +670,7 @@ export async function streamGemini(
|
|
|
666
670
|
* @param {object} options - provider, model, baseUrl, apiKey
|
|
667
671
|
*/
|
|
668
672
|
export async function* chatStream(messages, options) {
|
|
673
|
+
assertCustomProviderConnection(options.provider, options);
|
|
669
674
|
const { provider, model, baseUrl, apiKey, sessionId } = options;
|
|
670
675
|
|
|
671
676
|
// Token queue + waiter promise. onToken (called synchronously by each
|
|
@@ -765,14 +770,14 @@ export async function* chatStream(messages, options) {
|
|
|
765
770
|
options,
|
|
766
771
|
);
|
|
767
772
|
} else {
|
|
768
|
-
const providerDef =
|
|
773
|
+
const providerDef = resolveProviderDefinition(provider);
|
|
769
774
|
const url = normalizeProviderBaseUrl(
|
|
770
775
|
baseUrl || providerDef?.baseUrl || "https://api.openai.com/v1",
|
|
771
776
|
);
|
|
772
777
|
const key =
|
|
773
778
|
apiKey ||
|
|
774
779
|
(providerDef?.apiKeyEnv ? process.env[providerDef.apiKeyEnv] : null);
|
|
775
|
-
if (!key) {
|
|
780
|
+
if (!key && provider !== "custom") {
|
|
776
781
|
throw new Error(
|
|
777
782
|
`API key required for ${provider} (set ${providerDef?.apiKeyEnv || "API key"})`,
|
|
778
783
|
);
|