@bolloon/bolloon-agent 0.6.5 → 0.6.6
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/dist/agents/chain/chain-settlement.js +0 -1
- package/dist/agents/chat-segmenter.js +4 -16
- package/dist/agents/contacts/human-directory.js +155 -0
- package/dist/agents/contacts/tools.js +79 -0
- package/dist/agents/experience-review.js +0 -3
- package/dist/agents/goal-flywheel/goal-change.js +3 -4
- package/dist/agents/goal-resume.js +0 -4
- package/dist/agents/intent-classifier.js +1 -1
- package/dist/agents/iteration-budget.js +12 -0
- package/dist/agents/knowledge-organizer.js +0 -2
- package/dist/agents/lesson-to-skill.js +0 -1
- package/dist/agents/loop-review.js +2 -2
- package/dist/agents/memory-store.js +359 -0
- package/dist/agents/opportunity-web-search.js +39 -0
- package/dist/agents/p2p-chat-tools.js +1 -2
- package/dist/agents/parse-tool-call.js +9 -22
- package/dist/agents/pi-harness.js +1 -2
- package/dist/agents/pi-sdk-tools.js +180 -5
- package/dist/agents/pi-sdk.js +143 -9
- package/dist/agents/process-runner.js +28 -9
- package/dist/agents/react-loop.js +9 -17
- package/dist/agents/runner-resolver.js +4 -3
- package/dist/agents/skill-loader.js +22 -15
- package/dist/agents/skills-manager.js +1 -25
- package/dist/agents/workflow-pivot-loop.js +152 -61
- package/dist/bootstrap/context-collector.js +2 -4
- package/dist/bootstrap/context-hierarchy.js +13 -1
- package/dist/cli/ink-app.js +44 -17
- package/dist/cli/intent-command.js +22 -12
- package/dist/cli/loading-tui.js +8 -2
- package/dist/cli/log-gate.js +40 -8
- package/dist/cli/mention-data.js +1 -0
- package/dist/cli/reply-hygiene.js +22 -0
- package/dist/cli/trace-line.js +61 -2
- package/dist/electron-build/agents/agent-gateway.js.map +1 -1
- package/dist/electron-build/agents/chain/chain-indexer.js.map +1 -1
- package/dist/electron-build/agents/chain/chain-settlement.js +0 -1
- package/dist/electron-build/agents/chain/chain-settlement.js.map +1 -1
- package/dist/electron-build/agents/contacts/chain.js.map +1 -1
- package/dist/electron-build/agents/contacts/human-directory.js +204 -0
- package/dist/electron-build/agents/contacts/human-directory.js.map +1 -0
- package/dist/electron-build/agents/contacts/policy.js.map +1 -1
- package/dist/electron-build/agents/contacts/tools.js +112 -0
- package/dist/electron-build/agents/contacts/tools.js.map +1 -1
- package/dist/electron-build/agents/experience-review.js +0 -3
- package/dist/electron-build/agents/experience-review.js.map +1 -1
- package/dist/electron-build/agents/external-events.js.map +1 -1
- package/dist/electron-build/agents/goal-flywheel/goal-change.js +3 -4
- package/dist/electron-build/agents/goal-flywheel/goal-change.js.map +1 -1
- package/dist/electron-build/agents/goal-flywheel-wiring.js.map +1 -1
- package/dist/electron-build/agents/goal-resume.js +0 -4
- package/dist/electron-build/agents/goal-resume.js.map +1 -1
- package/dist/electron-build/agents/intent-classifier.js +1 -1
- package/dist/electron-build/agents/lesson-to-skill.js +0 -1
- package/dist/electron-build/agents/lesson-to-skill.js.map +1 -1
- package/dist/electron-build/agents/loop-review.js +2 -2
- package/dist/electron-build/agents/loop-review.js.map +1 -1
- package/dist/electron-build/agents/memory-recall.js.map +1 -1
- package/dist/electron-build/agents/memory-store.js +404 -0
- package/dist/electron-build/agents/memory-store.js.map +1 -0
- package/dist/electron-build/agents/parse-tool-call.js +9 -22
- package/dist/electron-build/agents/parse-tool-call.js.map +1 -1
- package/dist/electron-build/agents/pi-harness.js +1 -2
- package/dist/electron-build/agents/pi-harness.js.map +1 -1
- package/dist/electron-build/agents/pi-sdk-tools.js +180 -5
- package/dist/electron-build/agents/pi-sdk-tools.js.map +1 -1
- package/dist/electron-build/agents/pi-sdk.js +143 -9
- package/dist/electron-build/agents/pi-sdk.js.map +1 -1
- package/dist/electron-build/agents/pre-tool-validator.js.map +1 -1
- package/dist/electron-build/agents/process-runner.js +28 -9
- package/dist/electron-build/agents/process-runner.js.map +1 -1
- package/dist/electron-build/agents/runner-resolver.js +4 -3
- package/dist/electron-build/agents/runner-resolver.js.map +1 -1
- package/dist/electron-build/agents/skill-loader.js +22 -16
- package/dist/electron-build/agents/skill-loader.js.map +1 -1
- package/dist/electron-build/agents/skill-supervisor-link.js.map +1 -1
- package/dist/electron-build/agents/skills-manager.js +0 -24
- package/dist/electron-build/agents/skills-manager.js.map +1 -1
- package/dist/electron-build/agents/task/task-runner.js.map +1 -1
- package/dist/electron-build/agents/workflow-engine.js.map +1 -1
- package/dist/electron-build/agents/workflow-pivot-loop.js +155 -62
- package/dist/electron-build/agents/workflow-pivot-loop.js.map +1 -1
- package/dist/electron-build/agents/x402/goal-run-bridge.js.map +1 -1
- package/dist/electron-build/bollharness-integration/gate-state-machine.js.map +1 -1
- package/dist/electron-build/bootstrap/context-collector.js +2 -4
- package/dist/electron-build/bootstrap/context-collector.js.map +1 -1
- package/dist/electron-build/bootstrap/context-hierarchy.js +13 -1
- package/dist/electron-build/bootstrap/context-hierarchy.js.map +1 -1
- package/dist/electron-build/electron-preload.js.map +1 -1
- package/dist/electron-build/external-engines/delegate.js.map +1 -1
- package/dist/electron-build/kernel/channel-actor.js +31 -2
- package/dist/electron-build/kernel/channel-actor.js.map +1 -1
- package/dist/electron-build/kernel/model-runtime.js +8 -1
- package/dist/electron-build/kernel/model-runtime.js.map +1 -1
- package/dist/electron-build/llm/config-store.js +20 -0
- package/dist/electron-build/llm/config-store.js.map +1 -1
- package/dist/electron-build/llm/embedding-adapter.js +75 -0
- package/dist/electron-build/llm/embedding-adapter.js.map +1 -0
- package/dist/electron-build/llm/pi-ai.js +96 -35
- package/dist/electron-build/llm/pi-ai.js.map +1 -1
- package/dist/electron-build/network/agent-network.js.map +1 -1
- package/dist/electron-build/orbitdb/memory-sync.js +76 -0
- package/dist/electron-build/orbitdb/memory-sync.js.map +1 -0
- package/dist/electron-build/pi-ecosystem-judgment/adaptive-scan.js.map +1 -1
- package/dist/electron-build/security/tool-gate.js +2 -0
- package/dist/electron-build/security/tool-gate.js.map +1 -1
- package/dist/electron-build/setup/setup-store.js +34 -6
- package/dist/electron-build/setup/setup-store.js.map +1 -1
- package/dist/electron-build/social/heartbeat.js +18 -5
- package/dist/electron-build/social/heartbeat.js.map +1 -1
- package/dist/electron-build/utils/update-manager.js.map +1 -1
- package/dist/index.js +250 -12
- package/dist/ios/bolloon-web.json +2 -2
- package/dist/ios/client.js +74 -35
- package/dist/ios/icons/icon-1400x1400.png +0 -0
- package/dist/ios/icons/icon-1400x1400.webp +0 -0
- package/dist/ios/index.html +106 -107
- package/dist/ios/mobile-agent.js +346 -45
- package/dist/ios/mobile-core.js +7303 -4590
- package/dist/ios/mobile-harness.js +1004 -0
- package/dist/ios/mobile-orbitdb.js +194 -0
- package/dist/ios/mobile-persona.js +80 -0
- package/dist/ios/mobile-privacy.js +2 -3
- package/dist/ios/mobile.css +286 -21
- package/dist/ios/mobile.html +106 -107
- package/dist/ios/mobile.js +300 -132
- package/dist/ios/routes-world.js +4 -1
- package/dist/ios/server.js +3 -1
- package/dist/ios/style.css +13 -5
- package/dist/ios/ui/message-renderer.js +4 -18
- package/dist/ios/ui/step-timeline.js +14 -1
- package/dist/kernel/channel-actor.js +30 -2
- package/dist/kernel/gate-scan.js +1 -1
- package/dist/kernel/index.js +33 -0
- package/dist/kernel/model-runtime.js +8 -1
- package/dist/kernel/plan.js +3 -3
- package/dist/kernel/roster.js +3 -3
- package/dist/llm/config-store.js +20 -0
- package/dist/llm/embedding-adapter.js +69 -0
- package/dist/llm/pi-ai.js +94 -34
- package/dist/orbitdb/memory-sync.js +70 -0
- package/dist/pi-ecosystem-judgment/adaptive-scan.js +1 -1
- package/dist/security/tool-gate.js +2 -0
- package/dist/setup/setup-store.js +34 -6
- package/dist/social/heartbeat.js +18 -5
- package/dist/web/bolloon-web.json +3 -3
- package/dist/web/client.js +74 -35
- package/dist/web/icons/icon-1400x1400.png +0 -0
- package/dist/web/icons/icon-1400x1400.webp +0 -0
- package/dist/web/index.html +37 -25
- package/dist/web/mobile-agent.js +351 -45
- package/dist/web/mobile-contacts.js +0 -4
- package/dist/web/mobile-core.js +7430 -4590
- package/dist/web/mobile-harness.js +1004 -0
- package/dist/web/mobile-orbitdb.js +194 -0
- package/dist/web/mobile-persona.js +80 -0
- package/dist/web/mobile-privacy.js +2 -3
- package/dist/web/mobile.css +286 -21
- package/dist/web/mobile.html +121 -107
- package/dist/web/mobile.js +409 -136
- package/dist/web/routes-contacts.js +94 -1
- package/dist/web/routes-world.js +4 -1
- package/dist/web/server.js +4 -2
- package/dist/web/style.css +13 -5
- package/dist/web/sw.js +1 -1
- package/dist/web/ui/message-renderer.js +4 -18
- package/dist/web/ui/step-timeline.js +14 -1
- package/package.json +3 -2
- package/scripts/build-web.ts +16 -1
package/dist/ios/style.css
CHANGED
|
@@ -126,13 +126,20 @@ body {
|
|
|
126
126
|
flex-shrink: 0;
|
|
127
127
|
transition: var(--transition);
|
|
128
128
|
transform: translateX(0);
|
|
129
|
-
|
|
129
|
+
/* 2026-10-06: 改为 fixed 覆盖 — sidebar 收起时绝对定位脱离流, relative 会被挤成 0 宽,
|
|
130
|
+
恢复条必须固定在屏幕左侧才能被看到/点到 */
|
|
131
|
+
position: fixed;
|
|
132
|
+
left: 0;
|
|
133
|
+
top: 0;
|
|
134
|
+
height: 100%;
|
|
130
135
|
z-index: 99;
|
|
131
136
|
}
|
|
132
137
|
|
|
133
138
|
.sidebar-collapsed.hidden {
|
|
134
139
|
transform: translateX(calc(-1 * var(--sidebar-collapsed)));
|
|
135
|
-
position:
|
|
140
|
+
position: fixed;
|
|
141
|
+
left: 0;
|
|
142
|
+
top: 0;
|
|
136
143
|
height: 100%;
|
|
137
144
|
}
|
|
138
145
|
|
|
@@ -1876,18 +1883,19 @@ body {
|
|
|
1876
1883
|
flex-shrink: 0;
|
|
1877
1884
|
}
|
|
1878
1885
|
.header-tab {
|
|
1879
|
-
padding:
|
|
1886
|
+
padding: 7px 22px;
|
|
1880
1887
|
background: transparent;
|
|
1881
1888
|
border: none;
|
|
1882
1889
|
border-radius: 6px;
|
|
1883
1890
|
color: var(--text-secondary);
|
|
1884
|
-
font-size:
|
|
1891
|
+
font-size: 13px;
|
|
1885
1892
|
font-weight: 600;
|
|
1886
1893
|
cursor: pointer;
|
|
1887
1894
|
transition: var(--transition);
|
|
1888
1895
|
display: flex;
|
|
1889
1896
|
align-items: center;
|
|
1890
|
-
gap:
|
|
1897
|
+
gap: 5px;
|
|
1898
|
+
white-space: nowrap;
|
|
1891
1899
|
}
|
|
1892
1900
|
.header-tab:hover {
|
|
1893
1901
|
color: var(--text-primary);
|
|
@@ -261,21 +261,10 @@ function segmentChatReply(reply, ctx) {
|
|
|
261
261
|
if (remaining.trim()) {
|
|
262
262
|
remaining = filterFillerText(remaining);
|
|
263
263
|
}
|
|
264
|
-
const
|
|
265
|
-
let beforeFinalText = remaining;
|
|
266
|
-
if (finalIdx >= 0) {
|
|
267
|
-
beforeFinalText = remaining.substring(0, finalIdx);
|
|
268
|
-
}
|
|
269
|
-
const textContent = beforeFinalText.trim();
|
|
264
|
+
const textContent = remaining.trim();
|
|
270
265
|
if (textContent) {
|
|
271
266
|
segments.push({ type: "text", content: textContent });
|
|
272
267
|
}
|
|
273
|
-
if (finalIdx >= 0) {
|
|
274
|
-
const afterFinal = remaining.substring(finalIdx + "<final gen>".length).trim();
|
|
275
|
-
if (afterFinal) {
|
|
276
|
-
segments.push({ type: "final", content: afterFinal });
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
268
|
return segments;
|
|
280
269
|
}
|
|
281
270
|
function extractAndPush(type, text, out, re) {
|
|
@@ -530,11 +519,7 @@ function addMessage(content, type, save = true, container, usedJudgmentIds = [],
|
|
|
530
519
|
div.className = `message message-${type}`;
|
|
531
520
|
let cleanContent = content;
|
|
532
521
|
if (type === "ai") {
|
|
533
|
-
cleanContent = cleanContent.replace(
|
|
534
|
-
const finalGenIdx = cleanContent.indexOf("<final gen>");
|
|
535
|
-
if (finalGenIdx >= 0) {
|
|
536
|
-
cleanContent = cleanContent.substring(0, finalGenIdx).trim();
|
|
537
|
-
}
|
|
522
|
+
cleanContent = cleanContent.replace(/ thinking[\s\S]*?<\/think>/g, "");
|
|
538
523
|
}
|
|
539
524
|
const knownToolNames = ctx && ctx.knownToolNames || /* @__PURE__ */ new Set();
|
|
540
525
|
const segments = segmentChatReply(cleanContent, { knownToolNames });
|
|
@@ -858,7 +843,8 @@ function handleStepEvent(data, ctx = { messagesEl: null, messagesContainers: /*
|
|
|
858
843
|
args: data.args,
|
|
859
844
|
success: data.success,
|
|
860
845
|
output: data.output,
|
|
861
|
-
error: data.error
|
|
846
|
+
error: data.error,
|
|
847
|
+
durationMs: data.durationMs
|
|
862
848
|
});
|
|
863
849
|
}
|
|
864
850
|
function flushStepEventBuffer(channelId, ctx) {
|
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
// src/web/ui/step-timeline.ts
|
|
2
2
|
var MAX_VISIBLE_NODES = 20;
|
|
3
3
|
var STORAGE_PREFIX = "bolloon.stepTimeline.expanded.";
|
|
4
|
+
function formatDuration(ms) {
|
|
5
|
+
if (!Number.isFinite(ms) || ms < 0) return "(耗时未知)";
|
|
6
|
+
if (ms < 1e3) return `${ms}ms`;
|
|
7
|
+
const s = ms / 1e3;
|
|
8
|
+
if (s < 60) return `${s.toFixed(1)}s`;
|
|
9
|
+
const m = Math.floor(s / 60);
|
|
10
|
+
const rem = Math.round(s % 60);
|
|
11
|
+
return `${m}m${rem}s`;
|
|
12
|
+
}
|
|
4
13
|
var stateMap = /* @__PURE__ */ new WeakMap();
|
|
5
14
|
function readExpanded(channelId) {
|
|
6
15
|
if (!channelId) return false;
|
|
@@ -121,7 +130,10 @@ function render(timelineEl) {
|
|
|
121
130
|
}
|
|
122
131
|
node.setAttribute("data-status", step.status);
|
|
123
132
|
const label = node.querySelector(".step-timeline-label");
|
|
124
|
-
if (label)
|
|
133
|
+
if (label) {
|
|
134
|
+
const dur = step.durationMs !== void 0 && step.status !== "active" ? ` · ${formatDuration(step.durationMs)}` : "";
|
|
135
|
+
label.textContent = step.name + dur;
|
|
136
|
+
}
|
|
125
137
|
const argsEl = node.querySelector(".step-timeline-args");
|
|
126
138
|
if (argsEl) {
|
|
127
139
|
const argStr = step.args && Object.keys(step.args).length > 0 ? JSON.stringify(step.args).slice(0, 60) : "";
|
|
@@ -265,6 +277,7 @@ function pushStepToTimeline(timelineEl, eventType, data) {
|
|
|
265
277
|
steps[targetIdx].status = data.success === false ? "error" : "done";
|
|
266
278
|
if (data.output) steps[targetIdx].output = data.output;
|
|
267
279
|
if (data.error) steps[targetIdx].error = data.error;
|
|
280
|
+
if (data.durationMs !== void 0) steps[targetIdx].durationMs = data.durationMs;
|
|
268
281
|
} else {
|
|
269
282
|
steps[targetIdx].status = "error";
|
|
270
283
|
if (data.error) steps[targetIdx].error = data.error;
|
|
@@ -23,6 +23,17 @@ import { AsyncLocalStorage } from 'node:async_hooks';
|
|
|
23
23
|
* ⇒ 用 ALS 记住"我在谁的上下文里", 同 actor 重入时**直跑**, 不同 actor 照常排队。
|
|
24
24
|
*/
|
|
25
25
|
const actorCtx = new AsyncLocalStorage();
|
|
26
|
+
/**
|
|
27
|
+
* 2026-10-13: 当前 mailbox 任务被注入的**取消信号**。
|
|
28
|
+
* `deliverThroughActor` 进入执行时调 `actor.beginCancellation()` 并把 signal 注入本 ALS,
|
|
29
|
+
* 业务侧 (CLI/Web) 在 run() 里经 `currentRequestSignal()` 取它传给模型/循环调用 ⇒
|
|
30
|
+
* Ctrl+C / 终止按钮走 `actor.abort()` / `abortActorsOfChannel()` 能真正打断回合。
|
|
31
|
+
* 零业务依赖: 只用 node:async_hooks, 不 import 任何业务模块。
|
|
32
|
+
*/
|
|
33
|
+
const requestSignalCtx = new AsyncLocalStorage();
|
|
34
|
+
export function currentRequestSignal() {
|
|
35
|
+
return requestSignalCtx.getStore() ?? undefined;
|
|
36
|
+
}
|
|
26
37
|
/** 当前是否正跑在某个 Actor 的 mailbox 任务内 (诊断用) */
|
|
27
38
|
export function currentActorContext() {
|
|
28
39
|
return actorCtx.getStore();
|
|
@@ -245,6 +256,18 @@ export function resetActors() {
|
|
|
245
256
|
channelQueues.clear();
|
|
246
257
|
privateActorsCreated = 0;
|
|
247
258
|
}
|
|
259
|
+
/**
|
|
260
|
+
* 2026-10-13: 在 mailbox 任务体内开新的取消位并把 signal 注入 ALS (见 deliverThroughActor)。
|
|
261
|
+
* 收尾时**只有本任务仍是取消位主人**才清掉 —— 嵌套/并行路径下防 abort 打到下一个任务。
|
|
262
|
+
*/
|
|
263
|
+
function runWithCancellation(actor, run) {
|
|
264
|
+
const ac = new AbortController();
|
|
265
|
+
actor.state.cancellation = ac;
|
|
266
|
+
return requestSignalCtx.run(ac.signal, () => Promise.resolve(run()).finally(() => {
|
|
267
|
+
if (actor.state.cancellation === ac)
|
|
268
|
+
actor.state.cancellation = null;
|
|
269
|
+
}));
|
|
270
|
+
}
|
|
248
271
|
export async function deliverThroughActor(
|
|
249
272
|
// 形参故意放宽成 `unknown`: 调用点的 receiver 类型五花八门 (AgentSession / 结构子集 / any),
|
|
250
273
|
// 写成 `{ actor?: ChannelActor }` 会触发 TS 的弱类型检查 ("no properties in common") 而误报。
|
|
@@ -264,10 +287,15 @@ holder, run, opts = {}) {
|
|
|
264
287
|
const q = getChannelQueue(ch);
|
|
265
288
|
if (channelCtx.getStore() === q)
|
|
266
289
|
return run();
|
|
267
|
-
return q.submit(() => channelCtx.run(q, () => actor.submit(() => actorCtx.run(actor, () => run
|
|
290
|
+
return q.submit(() => channelCtx.run(q, () => actor.submit(() => actorCtx.run(actor, () => runWithCancellation(actor, run)))));
|
|
268
291
|
}
|
|
269
292
|
}
|
|
270
|
-
|
|
293
|
+
// 2026-10-13 (leo: 「Ctrl+C 中断的落点必须是 kernel 的取消通道」):
|
|
294
|
+
// mailbox 任务体内开一个新的取消位 (state.cancellation) 并把 signal 注入 ALS ——
|
|
295
|
+
// 业务侧 run() 经 `currentRequestSignal()` 取到它 ⇒ Ctrl+C abort 能打断回合。
|
|
296
|
+
// **重入保护里直跑的分支不重新开位**: 嵌套的 inner run 复用同一个外层的 signal,
|
|
297
|
+
// 不会被内层 abort 清掉 (abort() 把 state.cancellation 置 null, 而 signal 经 ALS 走闭包引用)。
|
|
298
|
+
return actor.submit(() => actorCtx.run(actor, () => runWithCancellation(actor, run)));
|
|
271
299
|
}
|
|
272
300
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
273
301
|
// channel 级串行锁 (K5 步骤⑤) —— **能力先落地, 默认不启用**
|
package/dist/kernel/gate-scan.js
CHANGED
|
@@ -108,7 +108,7 @@ export function scanProhibition(files, p) {
|
|
|
108
108
|
const out = [];
|
|
109
109
|
const targets = p.targets ?? [];
|
|
110
110
|
const calls = p.writeCalls ?? [];
|
|
111
|
-
const callRe = calls.length ? new RegExp(
|
|
111
|
+
const callRe = calls.length ? new RegExp(`(?<![\\w.])(?<!async\\s)(${calls.join('|')})\\s*\\(`) : null;
|
|
112
112
|
for (const f of files) {
|
|
113
113
|
if (p.mode === 'import-edge') {
|
|
114
114
|
for (const { line, spec } of importSpecifiers(f.text)) {
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @bolloon/bolloon-kernel — Bolloon Native Macro-Kernel SDK 出口
|
|
3
|
+
*
|
|
4
|
+
* 进程内 Agent 内核机制层(统一调度 · 共享状态受控 · 端口注入 · 零业务依赖)。
|
|
5
|
+
*
|
|
6
|
+
* 设计页: docs/wiki/bolloon-native-macro-kernel.md
|
|
7
|
+
* 出口纪律:
|
|
8
|
+
* · 只导出「机制」—— 不导出任何业务判定(Kernel 判据: 不判"该不该继续"、不判"算不算完成")。
|
|
9
|
+
* · 只导出「运行时机制」—— gate-scan / roster / plan-* 是 Bolloon 仓库治理件(判据与台账),
|
|
10
|
+
* 面向本仓的门(kernel-boundary / kernel-authority / kernel-gate-scan 等测试)直接吃源码,
|
|
11
|
+
* 不进 SDK 出口。
|
|
12
|
+
* · 依赖面: 除 node: 内置外零依赖 —— 每个模块的 I/O 走端口注入,调用方给什么就用什么。
|
|
13
|
+
*
|
|
14
|
+
* 模块速览:
|
|
15
|
+
* channel-actor K5 ChannelActor + SerialMailbox(同 channel 串行、异 channel 并发、ALS 重入)
|
|
16
|
+
* model-runtime K6 ModelRuntime.acquire(snapshot) 只读租约(连接复用/超时/取消/退避/熔断)
|
|
17
|
+
* control 命令式控制面(pause / resume / abort / recover)
|
|
18
|
+
* run-lifecycle Run 生命周期写口(start-run / record-step / finish-run / save-checkpoint)
|
|
19
|
+
* session-lifecycle 会话规则(持久化映射 / 过滤水合 / 运行种子合并)
|
|
20
|
+
* run-resume 恢复编排(漂移上报 / 核对失败不阻塞 / 单次语义)
|
|
21
|
+
* turn-selfcheck 回合末自检编排(fail-closed: 被拒/门抛错 ⇒ 零执行且可见)
|
|
22
|
+
* code-write-gate 写代码工具判定(哪个写操作产出 TS · 收尾要不要跑 tsc)
|
|
23
|
+
* transport 通信端口(send / broadcast / peers,未注入即拒)
|
|
24
|
+
*/
|
|
25
|
+
export * from './channel-actor.js';
|
|
26
|
+
export * from './model-runtime.js';
|
|
27
|
+
export * from './control.js';
|
|
28
|
+
export * from './run-lifecycle.js';
|
|
29
|
+
export * from './session-lifecycle.js';
|
|
30
|
+
export * from './run-resume.js';
|
|
31
|
+
export * from './turn-selfcheck.js';
|
|
32
|
+
export * from './code-write-gate.js';
|
|
33
|
+
export * from './transport.js';
|
|
@@ -179,7 +179,14 @@ export class ModelRuntime {
|
|
|
179
179
|
call: async (req, opts) => {
|
|
180
180
|
if (released)
|
|
181
181
|
return { ok: false, error: '租约已归还' };
|
|
182
|
-
|
|
182
|
+
// 2026-10-13 (leo: 「多步执行的任务会返回空」根因链的②): 30s 硬超时与上下文长度脱钩 ——
|
|
183
|
+
// 多步任务工具多轮后 prompt 膨胀到 40KB+, 模型生成要更久, 30s 一到就被杀。
|
|
184
|
+
// 预算 = max(配置/默认, 上下文长度自适应): 每 1KB 上下文给 4s, 再加 10s 生成余量。
|
|
185
|
+
// 长上下文不被误杀; 短上下文仍是 30s 底线 (供应商真慢照样暴露, 不掩盖问题)。
|
|
186
|
+
const baseBudget = snapshotReadonly.timeoutMs ?? this.defaultTimeoutMs;
|
|
187
|
+
const ctxChars = String(req?.context ?? '').length + String(req?.systemPrompt ?? '').length;
|
|
188
|
+
const adaptive = Math.ceil(ctxChars / 1024) * 4_000 + 10_000;
|
|
189
|
+
const budget = Math.max(baseBudget, adaptive);
|
|
183
190
|
const external = opts?.signal;
|
|
184
191
|
if (external?.aborted) {
|
|
185
192
|
self.stats.aborts += 1;
|
package/dist/kernel/plan.js
CHANGED
|
@@ -144,6 +144,6 @@ export const DELETION_LEDGER = [
|
|
|
144
144
|
},
|
|
145
145
|
];
|
|
146
146
|
/** 第一批删除候选 = 产品码里 **0 入边引用** 且非入口形态 (机械派生, 不手写) */
|
|
147
|
-
export const DELETION_CANDIDATE_COUNT =
|
|
148
|
-
export const DELETION_CANDIDATE_SHA256 = '
|
|
149
|
-
export const DELETION_CANDIDATE_DIRS = { "agents": 5, "bollharness-integration": 1, "bollharness": 61, "cli": 1, "(根)": 1, "judgeness": 3, "llm": 1, "network": 3, "orbitdb": 2, "scripts": 1, "social": 2, "utils": 3, "web":
|
|
147
|
+
export const DELETION_CANDIDATE_COUNT = 95;
|
|
148
|
+
export const DELETION_CANDIDATE_SHA256 = 'f271d369c830b73833e195c62076a0869dc8c8ef7c9b3cffddd6ce5342f2cb3a';
|
|
149
|
+
export const DELETION_CANDIDATE_DIRS = { "agents": 5, "bollharness-integration": 1, "bollharness": 61, "cli": 1, "(根)": 1, "judgeness": 3, "llm": 1, "network": 3, "orbitdb": 2, "scripts": 1, "social": 2, "utils": 3, "web": 11 };
|
package/dist/kernel/roster.js
CHANGED
|
@@ -100,7 +100,7 @@ export const KERNEL_ALLOWED_IMPORT_PREFIXES = ['kernel/'];
|
|
|
100
100
|
* 判据是**双向相等**: 盘上多一个未登记文件 ⇒ 红; 名册有而盘上没有 ⇒ 红。
|
|
101
101
|
* (先例: SEAM_ROSTER 的「名册外无人越界」)
|
|
102
102
|
*/
|
|
103
|
-
export const KERNEL_FILES = ['kernel/channel-actor.ts', 'kernel/control.ts', 'kernel/run-lifecycle.ts', 'kernel/transport.ts', 'kernel/session-lifecycle.ts', 'kernel/code-write-gate.ts', 'kernel/turn-selfcheck.ts', 'kernel/run-resume.ts', 'kernel/gate-scan.ts', 'kernel/model-runtime.ts', 'kernel/plan-harness.ts', 'kernel/plan-modelruntime.ts', 'kernel/plan-constraint.ts', 'kernel/plan-channel-actor.ts', 'kernel/plan-deletion.ts', 'kernel/plan-runcontext.ts', 'kernel/plan-communication.ts', 'kernel/plan.ts', 'kernel/roster.ts'];
|
|
103
|
+
export const KERNEL_FILES = ['kernel/channel-actor.ts', 'kernel/control.ts', 'kernel/run-lifecycle.ts', 'kernel/transport.ts', 'kernel/session-lifecycle.ts', 'kernel/code-write-gate.ts', 'kernel/turn-selfcheck.ts', 'kernel/run-resume.ts', 'kernel/gate-scan.ts', 'kernel/model-runtime.ts', 'kernel/index.ts', 'kernel/plan-harness.ts', 'kernel/plan-modelruntime.ts', 'kernel/plan-constraint.ts', 'kernel/plan-channel-actor.ts', 'kernel/plan-deletion.ts', 'kernel/plan-runcontext.ts', 'kernel/plan-communication.ts', 'kernel/plan.ts', 'kernel/roster.ts'];
|
|
104
104
|
/** Goal 事实的写入口 (goal-store.ts 的导出中带副作用的那些) */
|
|
105
105
|
export const GOAL_WRITES = [
|
|
106
106
|
'createGoal',
|
|
@@ -230,9 +230,9 @@ export const AUTHORITY_DEBT_FROZEN_AT = 0; // 3 → 0 (K4 用内核控制面还
|
|
|
230
230
|
* 目的只有一个: **不许所有逻辑回流到 kernel.ts**。要加就得显式抬这个数字, 留下痕迹。
|
|
231
231
|
* 数值 = 当前 kernel 目录真实行数, 不留余量。
|
|
232
232
|
*/
|
|
233
|
-
export const KERNEL_LINE_BUDGET =
|
|
233
|
+
export const KERNEL_LINE_BUDGET = 3885; // 2026-10-12: **显式抬档 +33** (kernel SDK 独立 npm 包: 新增 `kernel/index.ts` 出口, 零业务 import 纯 `export *`) // 2026-10-02: 收尾自检规则 `kernel/code-write-gate.ts` 从 agents 侧整条搬入 (+49, 减法: agent 侧少一个模块) // 2026-10-02: 会话生命周期端口 `kernel/session-lifecycle.ts` (278 行) + `gate-scan` 播种锚点跟形状 (+4) —— **显式抬档 +282** (K10 余项; pi-sdk 本批 -18 行); // 2026-10-02 **显式抬档 +22** (K8 收尾: channel-actor 加 `abortActorsOfChannel` + 台账/门同步) // 2026-10-02 **显式抬档 +16** (K10 ④ 后半: transport.ts 加同步读入口 `transportPeersSync` + `peersSync` 端口) // 2026-10-02 **显式抬档 +145** (K10 ④: 新增内核模块 kernel/transport.ts —— 通信入口端口) // 2026-10-02 **显式抬档 +184** (K10 ①: 新增内核模块 kernel/run-lifecycle.ts —— 运行生命周期写口, 与 control.ts 的控制面分开; 预算 == 真实值) // 2026-10-02: +1 (K4-B: gate-scan 复位阈值注释) // 2026-10-02: +5 (K8 正刀: gate-scan 加"收口字段数 3→1"+ queue 反回归) // 2026-10-02: +54 (K8: gate-scan 加 K8 判据) // 2026-10-02: +2 (STAGE_STATUS 拆 K4-A/K4-B + K6/K7 如实修正)
|
|
234
234
|
/** 预算冻结值 (棘轮: 只许减; 想抬预算必须同时改上面那个数字 ⇒ 一次显式动作, diff 里看得见) */
|
|
235
|
-
export const KERNEL_LINE_BUDGET_FROZEN_AT =
|
|
235
|
+
export const KERNEL_LINE_BUDGET_FROZEN_AT = 3885; // 2026-10-12: 与 SDK index.ts 出口新增同步
|
|
236
236
|
/**
|
|
237
237
|
* K3b —— **台账数据**单独一档预算 (`src/kernel/plan.ts`)。
|
|
238
238
|
*
|
package/dist/llm/config-store.js
CHANGED
|
@@ -225,6 +225,26 @@ function getDefaultConfig() {
|
|
|
225
225
|
activeProvider = 'mimo';
|
|
226
226
|
else if (process.env.OLLAMA_BASE_URL)
|
|
227
227
|
activeProvider = 'ollama';
|
|
228
|
+
// ── 2026-10-07: 开箱即用 — 无任何私有 key 时落到内置公共网关 ──────────────
|
|
229
|
+
// 手机端/网页端下载安装后**不需要配置任何 API key** 就能直接对话:
|
|
230
|
+
// Cloudflare Worker 托管 (api.bolloon.cn) 转发到 GLM 上游, 公共通道每天 ≤500 次/人,
|
|
231
|
+
// 配了自己的 key 的机器不受限 (上面 env 检测先命中)。
|
|
232
|
+
const hasAnyKey = !!(process.env.OPENAI_API_KEY || process.env.ANTHROPIC_API_KEY
|
|
233
|
+
|| process.env.OPENROUTER_API_KEY || process.env.GEMINI_API_KEY || process.env.MINIMAX_API_KEY
|
|
234
|
+
|| process.env.DEEPSEEK_API_KEY || process.env.KIMI_API_KEY || process.env.MOONSHOT_API_KEY
|
|
235
|
+
|| process.env.GLM_API_KEY || process.env.ZHIPU_API_KEY || process.env.QWEN_API_KEY
|
|
236
|
+
|| process.env.DASHSCOPE_API_KEY || process.env.MIMO_API_KEY || process.env.OLLAMA_BASE_URL);
|
|
237
|
+
if (!hasAnyKey) {
|
|
238
|
+
envConfigs.glm = {
|
|
239
|
+
...DEFAULT_PROVIDER_CONFIGS.glm,
|
|
240
|
+
enabled: true,
|
|
241
|
+
baseUrl: 'https://api.bolloon.cn/v1', // CF Worker 公共网关 (免配置)
|
|
242
|
+
apiKey: 'bolloon-free', // 公共通道标识 (每天 ≤500 次/人)
|
|
243
|
+
model: 'glm-5.3',
|
|
244
|
+
maxTokens: 16384,
|
|
245
|
+
};
|
|
246
|
+
activeProvider = 'glm';
|
|
247
|
+
}
|
|
228
248
|
const providers = { ...DEFAULT_PROVIDER_CONFIGS };
|
|
229
249
|
for (const [provider, config] of Object.entries(envConfigs)) {
|
|
230
250
|
if (config) {
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/** none 实现: 无向量能力, 调用即抛 (由调用方按 provider 判走关键词回退) */
|
|
2
|
+
const noneAdapter = {
|
|
3
|
+
provider: 'none',
|
|
4
|
+
dim: 0,
|
|
5
|
+
async embed() {
|
|
6
|
+
throw new Error('embedding adapter 未配置 (provider=none); 调用方应先查 provider !== "none"');
|
|
7
|
+
},
|
|
8
|
+
async embedBatch(texts) {
|
|
9
|
+
throw new Error('embedding adapter 未配置 (provider=none); 调用方应先查 provider !== "none"');
|
|
10
|
+
},
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* 工厂: 从配置构造 adapter。
|
|
14
|
+
* 规则:
|
|
15
|
+
* - 无 provider / provider='none' → none adapter (关键词回退)
|
|
16
|
+
* - provider 有值但没有实现 → none + 说明 (不假装能用)
|
|
17
|
+
* - 新增供应商实现在此登记 (每实现一个, 加一个 case)
|
|
18
|
+
*/
|
|
19
|
+
export function createEmbeddingAdapter(cfg = {}) {
|
|
20
|
+
const provider = String(cfg.provider || 'none').toLowerCase();
|
|
21
|
+
switch (provider) {
|
|
22
|
+
case 'none':
|
|
23
|
+
case '':
|
|
24
|
+
return noneAdapter;
|
|
25
|
+
// 预留登记位: 供应商 key 就绪后在此补实现 (见文件头诚实边界)
|
|
26
|
+
// case 'glm': return createGlmAdapter(cfg);
|
|
27
|
+
// case 'minimax': return createMiniMaxAdapter(cfg);
|
|
28
|
+
// case 'openai': return createOpenAIAdapter(cfg);
|
|
29
|
+
default:
|
|
30
|
+
return noneAdapter;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/** 测试辅助: 造一个确定性伪 adapter (cosine 检索测试用, 不碰网络) */
|
|
34
|
+
export function createFakeAdapter(dim = 8, seedText = 'seed') {
|
|
35
|
+
// 确定性伪向量: 文本长度 + 字符和 → 稳定可复现 (仅测试用)
|
|
36
|
+
const vec = (t) => {
|
|
37
|
+
const out = [];
|
|
38
|
+
const s = String(t || '');
|
|
39
|
+
for (let i = 0; i < dim; i++) {
|
|
40
|
+
const v = (s.length * (i + 1) + s.charCodeAt(i % Math.max(1, s.length)) * 0.01) % 1;
|
|
41
|
+
out.push(Number(v.toFixed(6)));
|
|
42
|
+
}
|
|
43
|
+
// 归一化 (cosine 需要)
|
|
44
|
+
const norm = Math.sqrt(out.reduce((a, b) => a + b * b, 0)) || 1;
|
|
45
|
+
return out.map((x) => x / norm);
|
|
46
|
+
};
|
|
47
|
+
return {
|
|
48
|
+
provider: 'fake',
|
|
49
|
+
dim,
|
|
50
|
+
embed: async (t) => vec(t),
|
|
51
|
+
embedBatch: async (ts) => ts.map(vec),
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
/** cosine 相似度 (纯函数, 供检索用; 维度不一致 → 0) */
|
|
55
|
+
export function cosineSimilarity(a, b) {
|
|
56
|
+
if (!a || !b || a.length === 0 || a.length !== b.length)
|
|
57
|
+
return 0;
|
|
58
|
+
let dot = 0;
|
|
59
|
+
let na = 0;
|
|
60
|
+
let nb = 0;
|
|
61
|
+
for (let i = 0; i < a.length; i++) {
|
|
62
|
+
dot += a[i] * b[i];
|
|
63
|
+
na += a[i] * a[i];
|
|
64
|
+
nb += b[i] * b[i];
|
|
65
|
+
}
|
|
66
|
+
if (na === 0 || nb === 0)
|
|
67
|
+
return 0;
|
|
68
|
+
return dot / (Math.sqrt(na) * Math.sqrt(nb));
|
|
69
|
+
}
|
package/dist/llm/pi-ai.js
CHANGED
|
@@ -9,6 +9,42 @@ export const LLAMACPP_DEFAULT_BASE_URL = 'http://localhost:8080/v1';
|
|
|
9
9
|
export const LLAMACPP_DEFAULT_MODEL = 'local';
|
|
10
10
|
/** 唯一的重路径用途 */
|
|
11
11
|
export const MAIN_AGENT_PURPOSE = 'main-agent';
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
// 模型调用并发闸 (2026-10-15, leo: 「[AI 服务调用失败] ... 429 并发请求已达上限, 有错误也要修复」)
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
// 根因: CLI/Web/心跳/会话总结等多路会同时打同一个 provider, 上游按 API key 限并发
|
|
16
|
+
// (实测 zai/glm: `当前 API Key 的并发请求已达到上限` 429 rate_limit_error)。
|
|
17
|
+
// 修复: 在**全仓唯一模型调用咽喉** generateText 前按 `providerId|baseUrl` 分桶限流 —
|
|
18
|
+
// 同 key 同时最多 LLM_MAX_CONCURRENCY 个在途请求, 其余排队 (轮询, 可响应 abort)。
|
|
19
|
+
const llmConcurrencyBuckets = new Map();
|
|
20
|
+
export const LLM_MAX_CONCURRENCY = (() => {
|
|
21
|
+
const n = Number(process.env.BOLLOON_LLM_MAX_CONCURRENCY ?? '');
|
|
22
|
+
return Number.isFinite(n) && n >= 1 ? Math.min(n, 8) : 2;
|
|
23
|
+
})();
|
|
24
|
+
/** 包一个模型调用进并发闸: 同 key 超限则排队, abort 时尽快退出 (轮询 120ms, 最坏等一拍) */
|
|
25
|
+
export async function withLlmConcurrency(key, signal, fn) {
|
|
26
|
+
for (;;) {
|
|
27
|
+
if (signal?.aborted)
|
|
28
|
+
throw new Error(String(signal.reason || 'AbortError'));
|
|
29
|
+
const b = llmConcurrencyBuckets.get(key) ?? { active: 0, waiting: 0 };
|
|
30
|
+
if (b.active < LLM_MAX_CONCURRENCY) {
|
|
31
|
+
b.active++;
|
|
32
|
+
llmConcurrencyBuckets.set(key, b);
|
|
33
|
+
try {
|
|
34
|
+
return await fn();
|
|
35
|
+
}
|
|
36
|
+
finally {
|
|
37
|
+
b.active--;
|
|
38
|
+
if (b.active <= 0 && b.waiting <= 0)
|
|
39
|
+
llmConcurrencyBuckets.delete(key);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
b.waiting++;
|
|
43
|
+
llmConcurrencyBuckets.set(key, b);
|
|
44
|
+
await new Promise((r) => setTimeout(r, 120));
|
|
45
|
+
b.waiting--;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
12
48
|
/** 轻量前缀的 system 上限 (字符). 只对**自动装配部分 + 调用方自带短提示**做兜底截断, 正常调用远低于它 */
|
|
13
49
|
export const LIGHTWEIGHT_SYSTEM_MAX_CHARS = 2000;
|
|
14
50
|
/**
|
|
@@ -577,7 +613,9 @@ export class PiAIModel {
|
|
|
577
613
|
error = retryErr; // 记录最后一次错误, 循环继续重试 (backoff 递增)
|
|
578
614
|
}
|
|
579
615
|
}
|
|
580
|
-
|
|
616
|
+
// 2026-10-15: 单行标签形 (`[pi-ai]` 是 log-gate INTERNAL_CHATTER_TAG, 只落盘不上屏;
|
|
617
|
+
// `分类` 在 for 循环块级作用域内, errMsg 在下面才解析 —— 这里自包含一行, 不引块内变量)
|
|
618
|
+
console.error(`[pi-ai] chat 失败: ${String(error?.message ?? error).replace(/\n/g, ' ').slice(0, 300)}`);
|
|
581
619
|
// 2026-06-15: 真实 error 信息 + 明确告诉 LLM "这是 API 错, 不要 retry"
|
|
582
620
|
// 旧版: "抱歉,AI服务暂时不可用。" → LLM 看到 isTooShort=false(< 50 但 > 0),
|
|
583
621
|
// needsMoreWork 不会触发, 但 hasError 模式 (含 "error" / "失败") 会判定要继续修
|
|
@@ -655,29 +693,36 @@ export class PiAIModel {
|
|
|
655
693
|
finalMessages = [{ role: 'system', content: `可用工具:\n${toolDescriptions}` }, ...messages];
|
|
656
694
|
}
|
|
657
695
|
}
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
696
|
+
// 2026-10-15 (leo: 429 并发已达上限, 有错误也要修复): 全仓唯一模型调用咽喉 —
|
|
697
|
+
// 所有入口 (CLI/Web/MCP/技能/子 Agent/长期任务) 都经 generateText 出网,
|
|
698
|
+
// 在这里按 `providerId|baseUrl` 分桶限流, 同 key 同时最多 LLM_MAX_CONCURRENCY 个在途,
|
|
699
|
+
// 排队等待 (轮询 120ms, abort 时尽快退出) — 不再把同一 API key 的并发打爆。
|
|
700
|
+
const bucketKey = `${this.config.providerId ?? this.provider}|${this.getBaseUrl()}`;
|
|
701
|
+
return withLlmConcurrency(bucketKey, signal, async () => {
|
|
702
|
+
switch (this.provider) {
|
|
703
|
+
case 'openai':
|
|
704
|
+
case 'minimax':
|
|
705
|
+
case 'deepseek':
|
|
706
|
+
case 'kimi':
|
|
707
|
+
case 'glm':
|
|
708
|
+
case 'qwen':
|
|
709
|
+
case 'mimo':
|
|
710
|
+
case 'grok':
|
|
711
|
+
return this.callOpenAI(finalMessages, temperature, maxTokens, signal, openaiTools, onToken, purpose, source);
|
|
712
|
+
case 'anthropic':
|
|
713
|
+
return this.callAnthropic(finalMessages, temperature, maxTokens, signal);
|
|
714
|
+
case 'ollama':
|
|
715
|
+
return this.callOllama(finalMessages, temperature, signal);
|
|
716
|
+
case 'openrouter':
|
|
717
|
+
return this.callOpenRouter(finalMessages, temperature, maxTokens, signal);
|
|
718
|
+
case 'gemini':
|
|
719
|
+
return this.callGemini(finalMessages, temperature, maxTokens, signal);
|
|
720
|
+
case 'local':
|
|
721
|
+
return this.callLocal(finalMessages, temperature, signal);
|
|
722
|
+
default:
|
|
723
|
+
throw new Error(`Unsupported provider: ${this.provider}`);
|
|
724
|
+
}
|
|
725
|
+
});
|
|
681
726
|
}
|
|
682
727
|
getApiKey() {
|
|
683
728
|
// 2026-09-28: llama.cpp (本地 OpenAI 兼容服务) —— 声明 id 单独接 key, key 可空
|
|
@@ -1390,9 +1435,8 @@ export function initPiAI(config = {}) {
|
|
|
1390
1435
|
const isLlamacpp = declared === LLAMACPP_PROVIDER_ID;
|
|
1391
1436
|
const provider = isLlamacpp ? 'openai' : declared;
|
|
1392
1437
|
const providerId = config.providerId || (isLlamacpp ? LLAMACPP_PROVIDER_ID : undefined);
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
let baseUrl = config.baseUrl || (isLlamacpp ? (process.env.LLAMACPP_BASE_URL || LLAMACPP_DEFAULT_BASE_URL) : undefined);
|
|
1438
|
+
let model = config.model || undefined;
|
|
1439
|
+
let baseUrl = config.baseUrl || undefined;
|
|
1396
1440
|
console.log('[PiAIModel] Initializing with provider:', provider, 'model:', model);
|
|
1397
1441
|
// 如果没有提供 apiKey,从配置文件读取
|
|
1398
1442
|
let apiKey = config.apiKey;
|
|
@@ -1400,26 +1444,42 @@ export function initPiAI(config = {}) {
|
|
|
1400
1444
|
// 否则"换 key"这种真变化不会重建实例, 指纹就失去意义)
|
|
1401
1445
|
if (!apiKey && isLlamacpp)
|
|
1402
1446
|
apiKey = process.env.LLAMACPP_API_KEY || undefined;
|
|
1403
|
-
|
|
1447
|
+
// ── 2026-10-06 技术债重构: 配置文件对所有 provider 生效 (不只 llamacpp) ──
|
|
1448
|
+
// 用户是 200+ 模型 / 自适应 URL 的重度用户: 在配置里自定义 baseUrl/model 必须被尊重。
|
|
1449
|
+
// 之前只有 llamacpp 走这条, 其他 provider 一律被 getBaseUrl()/mapModel() 的
|
|
1450
|
+
// 内置默认 / env 覆盖掉 ⇒ 配置了自定义网关 URL 却打官方端点 (401 真实事故)。
|
|
1451
|
+
// 优先级: 显式 config > 配置文件 > env > 内置默认 (getBaseUrl/mapModel 天然如此,
|
|
1452
|
+
// 这里只把配置值塞进去, 让"配置文件"这一层对所有 provider 生效)。
|
|
1453
|
+
{
|
|
1404
1454
|
try {
|
|
1405
1455
|
const configPath = resolveConfigPath();
|
|
1406
1456
|
if (configPath) {
|
|
1407
1457
|
const configData = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
1408
|
-
const providerConfig = configData.providers[declared];
|
|
1409
|
-
if (providerConfig?.apiKey) {
|
|
1458
|
+
const providerConfig = configData.providers?.[declared];
|
|
1459
|
+
if (!apiKey && providerConfig?.apiKey) {
|
|
1410
1460
|
apiKey = providerConfig.apiKey;
|
|
1411
1461
|
console.log('[PiAIModel] Loaded apiKey from config for', declared);
|
|
1412
1462
|
}
|
|
1413
|
-
// 2026-
|
|
1414
|
-
// (
|
|
1415
|
-
if (
|
|
1463
|
+
// 2026-10-06: baseUrl / model 不再只给 llamacpp —— 所有 provider 都尊重配置文件
|
|
1464
|
+
// (显式 config 优先; 配置里的值只作为"未显式指定"时的兜底)
|
|
1465
|
+
if (!baseUrl && providerConfig?.baseUrl)
|
|
1416
1466
|
baseUrl = providerConfig.baseUrl;
|
|
1467
|
+
if (!model && providerConfig?.model)
|
|
1468
|
+
model = providerConfig.model;
|
|
1469
|
+
if (providerConfig?.baseUrl || providerConfig?.model) {
|
|
1470
|
+
console.log('[PiAIModel] Loaded baseUrl/model from config for', declared, baseUrl ? 'baseUrl=' + baseUrl : '', model ? 'model=' + model : '');
|
|
1471
|
+
}
|
|
1417
1472
|
}
|
|
1418
1473
|
}
|
|
1419
1474
|
catch (e) {
|
|
1420
|
-
console.log('[PiAIModel] Error reading
|
|
1475
|
+
console.log('[PiAIModel] Error reading config:', e);
|
|
1421
1476
|
}
|
|
1422
1477
|
}
|
|
1478
|
+
// 兜底: 配置文件也没给 → 回落 llamacpp env / detectModel 内置默认 (老行为不变)
|
|
1479
|
+
if (!model)
|
|
1480
|
+
model = isLlamacpp ? (process.env.LLAMACPP_MODEL || LLAMACPP_DEFAULT_MODEL) : detectModel(provider);
|
|
1481
|
+
if (!baseUrl)
|
|
1482
|
+
baseUrl = isLlamacpp ? (process.env.LLAMACPP_BASE_URL || LLAMACPP_DEFAULT_BASE_URL) : undefined;
|
|
1423
1483
|
// ── 2026-09-28: 按指纹幂等 ──────────────────────────────────────────────
|
|
1424
1484
|
// 同一份配置重复 init (CLI 每轮 / web 每次请求 / 脚本多入口) **不再重建实例、不再清装配缓存**.
|
|
1425
1485
|
// 之前每次 init 都清缓存 → 每轮重装 system → 前缀抖动 → 服务端前缀 KV 永远用不上.
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* memory-sync.ts — 记忆条目级 OrbitDB 复制 (P3, decentralized-memory-design §2.6)
|
|
3
|
+
*
|
|
4
|
+
* 在现有 CID 快照(整快照)之上加「条目级同步」:
|
|
5
|
+
* - 保存: MemoryEntry.id (sha256) 作为经内容寻址的 key
|
|
6
|
+
* - 存储: CIDDatabase type='memory', metadata 带 entryId + entryUpdatedAt
|
|
7
|
+
* - 复制: OrbitDB pubsub 自动 (既有机制)
|
|
8
|
+
* - 冲突: 同 entryId 冲突 → updatedAt 大者胜 (LWW), 平手比 content hash
|
|
9
|
+
*
|
|
10
|
+
* 诚实边界 (写死):
|
|
11
|
+
* - 条目级复制依赖 OrbitDB 网络就绪 (getCIDDatabase 初始化); 网络不可用 /
|
|
12
|
+
* 初始化失败 → 静默跳过 (本地记忆仍是权威, 复制是增强不是阻塞)
|
|
13
|
+
* - 本模块不建新 store, 复用既有 bolloon-cid-store (type='memory')
|
|
14
|
+
* - 跨机器真同步需双节点 (设计文档 §5 已记「跨机读群未验证」, P3 只保证
|
|
15
|
+
* 条目可被 CID 化 + 可恢复; 真双节点验证是 P3b)
|
|
16
|
+
*/
|
|
17
|
+
import { getCIDDatabase } from './cid-database.js';
|
|
18
|
+
/** 条目 → CID 记录的 metadata 常量键 */
|
|
19
|
+
const META_ENTRY_ID = 'entryId';
|
|
20
|
+
const META_ENTRY_UPDATED = 'entryUpdatedAt';
|
|
21
|
+
/** 从 CID 记录还原 MemoryEntry (metadata 无 entryId 的旧记录 → null) */
|
|
22
|
+
export function entryFromRecord(rec) {
|
|
23
|
+
if (!rec)
|
|
24
|
+
return null;
|
|
25
|
+
const meta = rec.metadata || {};
|
|
26
|
+
if (!meta[META_ENTRY_ID])
|
|
27
|
+
return null;
|
|
28
|
+
const e = rec.content;
|
|
29
|
+
if (!e || typeof e !== 'object' || !e.id || !e.agentId)
|
|
30
|
+
return null;
|
|
31
|
+
return e;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* 同步入口: 把一条 MemoryEntry 写入 OrbitDB (条目级)。
|
|
35
|
+
* 失败静默 (本地记忆不受影响), 返回 CID 或 null。
|
|
36
|
+
*/
|
|
37
|
+
export async function syncMemoryEntryToOrbit(entry, db = getCIDDatabase()) {
|
|
38
|
+
try {
|
|
39
|
+
const rec = await db.save({
|
|
40
|
+
agentId: entry.agentId,
|
|
41
|
+
type: 'memory',
|
|
42
|
+
content: entry,
|
|
43
|
+
metadata: {
|
|
44
|
+
[META_ENTRY_ID]: entry.id,
|
|
45
|
+
[META_ENTRY_UPDATED]: entry.updatedAt,
|
|
46
|
+
kind: 'memory-entry',
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
return rec.id;
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return null; // 网络/初始化失败静默 — 本地记忆仍权威
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* 拉取远端条目: 列出该 agent 的全部 memory type 记录, 还原成 MemoryEntry。
|
|
57
|
+
* 仅返回「本地没有 或 远端 updatedAt 更新」的条目 (LWW)。
|
|
58
|
+
* 不写本地 — 由调用方决定如何合并。
|
|
59
|
+
*/
|
|
60
|
+
export async function fetchRemoteEntries(agentId, db = getCIDDatabase()) {
|
|
61
|
+
try {
|
|
62
|
+
const recs = await db.list({ agentId, type: 'memory' });
|
|
63
|
+
return recs
|
|
64
|
+
.map((r) => entryFromRecord(r))
|
|
65
|
+
.filter((e) => e !== null);
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
return [];
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
import * as fs from 'fs/promises';
|
|
19
19
|
import * as os from 'os';
|
|
20
20
|
import * as path from 'path';
|
|
21
|
-
import { loadAllJudgments } from './human-value-store.js';
|
|
21
|
+
import { loadAllJudgments, } from './human-value-store.js';
|
|
22
22
|
// 用 getter 函数而非模块顶层 const: process.env.HOME 在测试 beforeAll 才设,
|
|
23
23
|
// 模块顶层求值时还是真 home. 运行时求值才能正确响应测试 fixture.
|
|
24
24
|
// 跨平台: 优先 process.env.HOME (测试可控), 然后 os.homedir() (Windows 上读 USERPROFILE).
|