@bolloon/bolloon-agent 0.5.5 → 0.6.0
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/parse-tool-call.js +6 -1
- package/dist/agents/pi-sdk-session-factory.js +36 -3
- package/dist/agents/pi-sdk.js +349 -199
- package/dist/agents/run-context.js +20 -0
- package/dist/agents/runner-resolver.js +2 -1
- package/dist/electron-build/agents/parse-tool-call.js +6 -1
- package/dist/electron-build/agents/parse-tool-call.js.map +1 -1
- package/dist/electron-build/agents/pi-sdk-session-factory.js +36 -3
- package/dist/electron-build/agents/pi-sdk-session-factory.js.map +1 -1
- package/dist/electron-build/agents/pi-sdk-types.js.map +1 -1
- package/dist/electron-build/agents/pi-sdk.js +349 -199
- package/dist/electron-build/agents/pi-sdk.js.map +1 -1
- package/dist/electron-build/agents/run-context.js +24 -0
- package/dist/electron-build/agents/run-context.js.map +1 -0
- package/dist/electron-build/agents/runner-resolver.js +2 -1
- package/dist/electron-build/agents/runner-resolver.js.map +1 -1
- package/dist/electron-build/kernel/channel-actor.js +314 -0
- package/dist/electron-build/kernel/channel-actor.js.map +1 -0
- package/dist/index.js +24 -19
- package/dist/kernel/channel-actor.js +297 -0
- package/dist/kernel/control.js +95 -0
- package/dist/kernel/gate-scan.js +1036 -0
- package/dist/kernel/model-runtime.js +472 -0
- package/dist/kernel/plan-channel-actor.js +298 -0
- package/dist/kernel/plan-constraint.js +165 -0
- package/dist/kernel/plan-deletion.js +32 -0
- package/dist/kernel/plan-modelruntime.js +60 -0
- package/dist/kernel/plan-runcontext.js +163 -0
- package/dist/kernel/plan.js +153 -0
- package/dist/kernel/roster.js +242 -0
- package/dist/web/bolloon-web.json +3 -3
- package/dist/web/client.js +3 -2
- package/dist/web/routes-tasks.js +5 -1
- package/dist/web/server.js +58 -18
- package/dist/web/sw.js +1 -1
- package/dist/web/ui/message-renderer.js +473 -29
- package/dist/web/ui/step-timeline.js +13 -12
- package/package.json +1 -1
- package/scripts/build-web.ts +74 -1
- package/dist/constraint-runtime/_archive_helper.d.ts +0 -1
- package/dist/constraint-runtime/_archive_helper.js +0 -9
- package/dist/constraint-runtime/assistant/index.d.ts +0 -11
- package/dist/constraint-runtime/assistant/index.js +0 -12
- package/dist/constraint-runtime/bootstrap/index.d.ts +0 -11
- package/dist/constraint-runtime/bootstrap/index.js +0 -12
- package/dist/constraint-runtime/bridge/index.d.ts +0 -11
- package/dist/constraint-runtime/bridge/index.js +0 -12
- package/dist/constraint-runtime/buddy/index.d.ts +0 -11
- package/dist/constraint-runtime/buddy/index.js +0 -12
- package/dist/constraint-runtime/cli/index.d.ts +0 -11
- package/dist/constraint-runtime/cli/index.js +0 -12
- package/dist/constraint-runtime/components/index.d.ts +0 -11
- package/dist/constraint-runtime/components/index.js +0 -12
- package/dist/constraint-runtime/constants/index.d.ts +0 -11
- package/dist/constraint-runtime/constants/index.js +0 -12
- package/dist/constraint-runtime/coordinator/index.d.ts +0 -11
- package/dist/constraint-runtime/coordinator/index.js +0 -12
- package/dist/constraint-runtime/cost_hook.d.ts +0 -2
- package/dist/constraint-runtime/cost_hook.js +0 -4
- package/dist/constraint-runtime/entrypoints/index.d.ts +0 -11
- package/dist/constraint-runtime/entrypoints/index.js +0 -12
- package/dist/constraint-runtime/execution_registry.d.ts +0 -20
- package/dist/constraint-runtime/execution_registry.js +0 -44
- package/dist/constraint-runtime/hooks/index.d.ts +0 -11
- package/dist/constraint-runtime/hooks/index.js +0 -12
- package/dist/constraint-runtime/ink.d.ts +0 -1
- package/dist/constraint-runtime/ink.js +0 -4
- package/dist/constraint-runtime/keybindings/index.d.ts +0 -11
- package/dist/constraint-runtime/keybindings/index.js +0 -12
- package/dist/constraint-runtime/memdir/index.d.ts +0 -11
- package/dist/constraint-runtime/memdir/index.js +0 -12
- package/dist/constraint-runtime/migrations/index.d.ts +0 -11
- package/dist/constraint-runtime/migrations/index.js +0 -12
- package/dist/constraint-runtime/moreright/index.d.ts +0 -11
- package/dist/constraint-runtime/moreright/index.js +0 -12
- package/dist/constraint-runtime/native_ts/index.d.ts +0 -11
- package/dist/constraint-runtime/native_ts/index.js +0 -12
- package/dist/constraint-runtime/output_styles/index.d.ts +0 -11
- package/dist/constraint-runtime/output_styles/index.js +0 -12
- package/dist/constraint-runtime/plugins/index.d.ts +0 -11
- package/dist/constraint-runtime/plugins/index.js +0 -12
- package/dist/constraint-runtime/port_manifest.d.ts +0 -7
- package/dist/constraint-runtime/port_manifest.js +0 -11
- package/dist/constraint-runtime/query.d.ts +0 -6
- package/dist/constraint-runtime/query.js +0 -1
- package/dist/constraint-runtime/remote/index.d.ts +0 -11
- package/dist/constraint-runtime/remote/index.js +0 -12
- package/dist/constraint-runtime/schemas/index.d.ts +0 -11
- package/dist/constraint-runtime/schemas/index.js +0 -12
- package/dist/constraint-runtime/screens/index.d.ts +0 -11
- package/dist/constraint-runtime/screens/index.js +0 -12
- package/dist/constraint-runtime/server/index.d.ts +0 -11
- package/dist/constraint-runtime/server/index.js +0 -12
- package/dist/constraint-runtime/services/index.d.ts +0 -11
- package/dist/constraint-runtime/services/index.js +0 -12
- package/dist/constraint-runtime/state/index.d.ts +0 -11
- package/dist/constraint-runtime/state/index.js +0 -12
- package/dist/constraint-runtime/system_init.d.ts +0 -1
- package/dist/constraint-runtime/system_init.js +0 -20
- package/dist/constraint-runtime/types/index.d.ts +0 -11
- package/dist/constraint-runtime/types/index.js +0 -12
- package/dist/constraint-runtime/upstream_proxy/index.d.ts +0 -11
- package/dist/constraint-runtime/upstream_proxy/index.js +0 -12
- package/dist/constraint-runtime/utils/index.d.ts +0 -11
- package/dist/constraint-runtime/utils/index.js +0 -12
- package/dist/constraint-runtime/vim/index.d.ts +0 -11
- package/dist/constraint-runtime/vim/index.js +0 -12
- package/dist/constraint-runtime/voice/index.d.ts +0 -11
- package/dist/constraint-runtime/voice/index.js +0 -12
|
@@ -52,6 +52,8 @@ const fs = __importStar(require("fs/promises"));
|
|
|
52
52
|
const fsSync = __importStar(require("fs"));
|
|
53
53
|
const os = __importStar(require("os"));
|
|
54
54
|
const path = __importStar(require("path"));
|
|
55
|
+
const run_context_js_1 = require("./run-context.js");
|
|
56
|
+
const channel_actor_js_1 = require("../kernel/channel-actor.js");
|
|
55
57
|
const tool_path_args_js_1 = require("./tool-path-args.js");
|
|
56
58
|
const background_notices_js_1 = require("./background-notices.js");
|
|
57
59
|
const skill_ledger_js_1 = require("./skill-ledger.js");
|
|
@@ -260,7 +262,90 @@ class PiAgentSession {
|
|
|
260
262
|
sessionManager;
|
|
261
263
|
agentsManager;
|
|
262
264
|
socialHeartbeat = null;
|
|
263
|
-
|
|
265
|
+
/**
|
|
266
|
+
* **K5 第 4 步 (所有权转移)**: history 的**本体**。
|
|
267
|
+
* · 未绑定 actor ⇒ 本体在这里 (绑定前的暂存);
|
|
268
|
+
* · 绑定 actor 后 ⇒ 本体是 `actor.state.messageHistory`, 本地那份被**收养**并清空。
|
|
269
|
+
* 绑定由 `attachActor()` 做, 且**只在会话身份已知时**发生 (见 session factory) —— 没有身份就不归属。
|
|
270
|
+
*/
|
|
271
|
+
/**
|
|
272
|
+
* **K5 第 4 步 — history 写入的唯一漏斗 (push)**。
|
|
273
|
+
* 为什么必须同步: 调用点遍布 ReAct 循环 / 工具回灌 / 压缩, 而且**写完立刻读** (length / 索引 / slice),
|
|
274
|
+
* 改成 await 会改变同拍可见性。所以这里有意识地**不排队** —— 归属收敛 + 可数 (门能断言"零处直写"),
|
|
275
|
+
* 并发安全由入口投递进 mailbox 负责 (K5 第 5 步)。
|
|
276
|
+
*/
|
|
277
|
+
pushHistory(...msgs) {
|
|
278
|
+
for (const m of msgs) {
|
|
279
|
+
// 步骤⑧ 起**只有一条路径**: 写 actor (实例侧暂存字段已删除) —— 也彻底消除了"自递归"的可能
|
|
280
|
+
this.actor.appendMessageSync(m);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
/** **K5 第 4 步 — history 写入漏斗 (pop)** —— 同 pushHistory 的性质 */
|
|
284
|
+
popHistory() {
|
|
285
|
+
return this.actor.popMessageSync();
|
|
286
|
+
}
|
|
287
|
+
/** **K5 第 4 步 — history 写入漏斗 (整体替换)**: hydrate 回灌 / 压缩后的整块赋值都走这里 */
|
|
288
|
+
/**
|
|
289
|
+
* **整体替换**历史 —— 唯一漏斗之一, 步骤⑧ 起是**唯一**的"种历史"入口 (公开)。
|
|
290
|
+
* 公开的理由: 上层/测试要种一份历史时必须走这里; 直接给对象赋一个新数组会**换掉数组身份**
|
|
291
|
+
* (actor 里那份仍是空的) —— 那是静默丢数据 (实测: 删掉 setter 后 `session.messageHistory = [...]`
|
|
292
|
+
* 变成写在一个凭空出现的自有属性上, 测试于是看到 0 条)。
|
|
293
|
+
*/
|
|
294
|
+
replaceHistory(next) {
|
|
295
|
+
this.actor.replaceHistory(next);
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* **K5 步骤⑦ — 把一次性请求里的绑定落到位** (入口不再散着设字段: 只交一个请求)。
|
|
299
|
+
* 写入都走访问器 ⇒ 本体落进 `actor.state` (步骤③ 起就是这样); 没给的不覆盖 (与环境变量"只覆盖显式给出的"同秩)。
|
|
300
|
+
*/
|
|
301
|
+
applyExecutionRequest(req) {
|
|
302
|
+
if (req.channelId)
|
|
303
|
+
this.actor.state.channelId = req.channelId;
|
|
304
|
+
if (req.agentId)
|
|
305
|
+
this.actor.state.agentId = req.agentId;
|
|
306
|
+
if (req.goalId)
|
|
307
|
+
this.actor.state.goalBinding = req.goalId;
|
|
308
|
+
if (req.resumeRunId)
|
|
309
|
+
this.resumeRunId = req.resumeRunId;
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* **K5 步骤⑦ — Pi 的唯一执行入口**: 只接收一次性 `ExecutionRequest`。
|
|
313
|
+
* 有 `onStream` ⇒ 走 `promptStream` (流式); 否则走 `prompt`。绑定由 `applyExecutionRequest` 落位。
|
|
314
|
+
* 入口侧只需: `deliverThroughActor(session, () => session.runExecution(req))`。
|
|
315
|
+
*/
|
|
316
|
+
async runExecution(req) {
|
|
317
|
+
this.applyExecutionRequest(req);
|
|
318
|
+
if (req.onStream) {
|
|
319
|
+
return this.promptStream(req.input, req.onStream, req.signal, req.channelId);
|
|
320
|
+
}
|
|
321
|
+
return this.prompt(req.input, { signal: req.signal, channelId: req.channelId });
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* **K5 第 4 步**: 绑定 actor 并**收养**绑定前已存在的本地历史。
|
|
325
|
+
* 收养规则: actor 侧为空 且 本地非空 ⇒ 搬过去 (任何绑定时序都不丢历史); 收养后清空本地。
|
|
326
|
+
* 约束 (全量回归打出来的): 调用方必须用**会话身份**当注册键; 没有身份就**不要调**这个方法。
|
|
327
|
+
*/
|
|
328
|
+
attachActor(actor) {
|
|
329
|
+
const prev = this.actor;
|
|
330
|
+
this.actor = actor;
|
|
331
|
+
if (!prev || prev === actor)
|
|
332
|
+
return;
|
|
333
|
+
// **收养**: 把上一个 actor (通常是"出生时的私有 actor") 的状态搬进新家。
|
|
334
|
+
// 新家已有值时不覆盖 —— 同一会话身份以先到者为准 (禁止"后到的调用把先建的状态冲掉")。
|
|
335
|
+
const from = prev.state;
|
|
336
|
+
const to = actor.state;
|
|
337
|
+
if (from.messageHistory.length > 0 && to.messageHistory.length === 0) {
|
|
338
|
+
to.messageHistory.push(...from.messageHistory);
|
|
339
|
+
}
|
|
340
|
+
if (from.channelId && !to.channelId)
|
|
341
|
+
to.channelId = from.channelId;
|
|
342
|
+
if (from.agentId && !to.agentId)
|
|
343
|
+
to.agentId = from.agentId;
|
|
344
|
+
if (from.goalBinding && !to.goalBinding)
|
|
345
|
+
to.goalBinding = from.goalBinding;
|
|
346
|
+
if (from.activeRun && !to.activeRun)
|
|
347
|
+
to.activeRun = from.activeRun;
|
|
348
|
+
}
|
|
264
349
|
tools = new Map();
|
|
265
350
|
/** 2026-06-30: tool registry 模块 — 独立 alias resolve, 测试可消融. */
|
|
266
351
|
_toolRegistry = new tool_registry_js_1.ToolRegistry();
|
|
@@ -423,11 +508,29 @@ class PiAgentSession {
|
|
|
423
508
|
// 拼到 systemPrompt 末尾, 别再混进 user message
|
|
424
509
|
contextHintAddition = '';
|
|
425
510
|
/**
|
|
426
|
-
*
|
|
427
|
-
*
|
|
511
|
+
* **K2 收尾 (leo 2026-10-02 口径)**: **唯一一处**把"已有的恢复身份"播种进 RunContext。
|
|
512
|
+
*
|
|
513
|
+
* · `currentRunId` 属 **run-boundary**, 不是循环内部的可变 RunContext 状态 ⇒ 只允许这一次显式播种读取;
|
|
514
|
+
* · 冻结 `CURRENT_RUN_ID_SEED_READS = 1` (见 src/kernel/plan-runcontext.ts), 由门强制;
|
|
515
|
+
* · 只发生在**进入一次 Run 时**; 不得在循环中新增读取点; 不得成为新的写入口;
|
|
516
|
+
* · K5 Channel Actor 完成后改为 `createRunContext({ runId: request.resumeRunId })`, 届时删除 `currentRunId` 字段本体。
|
|
517
|
+
*/
|
|
518
|
+
seedRunContext(extra = {}) {
|
|
519
|
+
return (0, run_context_js_1.createRunContext)({ runId: this.actor.state.activeRun, ...extra });
|
|
520
|
+
}
|
|
521
|
+
/**
|
|
522
|
+
* **K5 迁移中**: 本会话所属 channel 的 Actor (由 session factory 在创建时绑定)。
|
|
523
|
+
* 现在只做**归属** —— 会话状态 (messageHistory / channelId / agentId / goalId) **仍然**在实例字段上,
|
|
524
|
+
* 逐项迁入 Actor 见 `src/kernel/plan-channel-actor.ts` 的 8 步。
|
|
525
|
+
*/
|
|
526
|
+
actor;
|
|
527
|
+
/**
|
|
528
|
+
* **K2 迁移中**: 一次 Run 的显式状态载体 (见 src/agents/run-context.ts)。
|
|
529
|
+
* 入口 (prompt / promptStream / promptWithPivotLoop) 用 createRunContext() 快照一次, 用完即清。
|
|
530
|
+
* **已外置**: `eventSink` (原 currentOnStream) · `abortSignal` (原 currentSignal)。
|
|
531
|
+
* 其余 6 个字段仍以本类实例字段为准, 逐格迁移。
|
|
428
532
|
*/
|
|
429
|
-
|
|
430
|
-
currentSignal = null;
|
|
533
|
+
runCtx = (0, run_context_js_1.createRunContext)();
|
|
431
534
|
/** Bootstrap SessionStart 拼的 system prompt 片段 (用完即清) */
|
|
432
535
|
bootstrapAddition = '';
|
|
433
536
|
/** 2026-08-12 (Task3 认知卸载): 工具选择与认知卸载指南 — 注入 system prompt, 降低"模型不触发 write/edit/read"率. */
|
|
@@ -441,12 +544,17 @@ class PiAgentSession {
|
|
|
441
544
|
- 每步只选一个最合适的工具, 先读后写, 写完验证 (跑 tsc/vitest 或读回文件).`;
|
|
442
545
|
/** 当前 prompt 开始时间 (供 Stop hook 计算 durationMs) */
|
|
443
546
|
promptStartTime = 0;
|
|
444
|
-
/**
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
547
|
+
/**
|
|
548
|
+
* **K5 步骤③ (会话绑定迁入 Actor)**: 三处绑定 (channelId / agentId / goalBinding) 的本体住进
|
|
549
|
+
* `actor.state`, 实例上只留**绑定前的暂存** (`_channelId` 等), 由 `attachActor()` 收养后清空。
|
|
550
|
+
* 用访问器而不是改调用点: 写入点 (prompt 入口 / Goal 绑定 / snapshot) 分散且语义各异, 逐个改风险大;
|
|
551
|
+
* 访问器让所有读写自动落到 actor (同一个值对象), 调用点零改动。
|
|
552
|
+
* 注意: 这三个字段的**访问数不变** (台账按 "self.currentX" 形态计数) —— 迁的是**所有权**, 不是删访问。
|
|
553
|
+
* 本注释刻意不写出那种带 `this.` 前缀的字面形态: 计数口径只剥 `//` 行注释, 块注释里的同形串会被算进去
|
|
554
|
+
* (曾因此把 currentChannelId 的计数虚增 1, 被门照出)。
|
|
555
|
+
*/
|
|
556
|
+
// **K5 步骤⑧ 批次4**: channelId 的访问器已删 —— 本体 `actor.state.channelId`, 直接影响本体。
|
|
557
|
+
// M2.2 intent 已外置到 runCtx.intent (K2); 拼 systemPrompt 时读 this.runCtx.intent
|
|
450
558
|
/** 2026-08-10: 本轮用户原始输入 (loop-review 任务动词兜底检测用) */
|
|
451
559
|
currentUserInput = '';
|
|
452
560
|
/**
|
|
@@ -454,7 +562,16 @@ class PiAgentSession {
|
|
|
454
562
|
* 之前只有"跑完才写"的轨迹 (trajectory-store, fire-and-forget, 崩了就没有), 没有跨重载可见的
|
|
455
563
|
* 运行事实, 也没有预算闸门/失速巡检; 这里补的就是那一层。
|
|
456
564
|
*/
|
|
457
|
-
|
|
565
|
+
/**
|
|
566
|
+
* **K5 步骤⑥ (Run 身份归属 Actor)**: 本体是 `actor.state.activeRun`。
|
|
567
|
+
* · 未绑定 actor ⇒ 住在这里 (`_runId`, 绑定前的暂存);
|
|
568
|
+
* · 绑定后 ⇒ 读写全落 actor (同一个值), 由 `attachActor()` 收养绑定前的值。
|
|
569
|
+
* 语义: "本会话当前活跃的 Run" —— 一个会话身份同时只有一个活跃 Run ✓ 与 Actor 的粒度一致。
|
|
570
|
+
* 注意: 访问数**不变** —— 迁的是**所有权**不是删访问 (与步骤③同理)。
|
|
571
|
+
* 本注释刻意不写出"带 self. 前缀 + 该字段名"的那种字面形态: K2 的计数只剥 `//` 行注释,
|
|
572
|
+
* 块注释里的同形串会被算进去 (同类踩过三次, 每次都被门照出)。
|
|
573
|
+
*/
|
|
574
|
+
// **K5 步骤⑧ 批次5 (收尾)**: currentRunId 访问器已删 —— Run 身份的本体是 `actor.state.activeRun`, 直接读写本体。
|
|
458
575
|
/** 上一次运行的 runId (收尾不清空; 见 getLastRunId) */
|
|
459
576
|
lastRunId = '';
|
|
460
577
|
runSurface = 'cli';
|
|
@@ -471,14 +588,14 @@ class PiAgentSession {
|
|
|
471
588
|
currentIntentHint = '';
|
|
472
589
|
/**
|
|
473
590
|
* 算 judgment 注入门: 失败静默, 不阻塞主对话
|
|
474
|
-
* 期间通过
|
|
591
|
+
* 期间通过 runCtx.eventSink 广播 phase 事件, 前端可显示 "正在检索判断力..." 状态
|
|
475
592
|
* 调用方负责用完即清 (judgmentGateAddition='')
|
|
476
593
|
*/
|
|
477
594
|
async computeJudgmentGate(input) {
|
|
478
595
|
const safePhase = (phase, extra = {}) => {
|
|
479
596
|
try {
|
|
480
|
-
if (this.
|
|
481
|
-
this.
|
|
597
|
+
if (this.runCtx.eventSink) {
|
|
598
|
+
this.runCtx.eventSink({ type: 'phase', phase, ...extra, content: '' });
|
|
482
599
|
}
|
|
483
600
|
}
|
|
484
601
|
catch { /* 静默 */ }
|
|
@@ -520,19 +637,23 @@ class PiAgentSession {
|
|
|
520
637
|
this.judgmentGateNegativeUsedIds = [];
|
|
521
638
|
}
|
|
522
639
|
constructor(config) {
|
|
640
|
+
// **K5 步骤⑧**: actor 从出生就在 —— 工厂通常在**构造前**就把身份 actor 注进来 (`config.actor`);
|
|
641
|
+
// 没有身份时用一份**私有 actor** (不注册 ⇒ 谁也拿不到, 隔离性优先)。
|
|
642
|
+
// ⇒ 实例侧不再需要"绑定前暂存"字段 (那些字段本步删除)。
|
|
643
|
+
this.actor = config.actor ?? (0, channel_actor_js_1.createPrivateActor)();
|
|
523
644
|
this.cwd = config.cwd;
|
|
524
645
|
this.peerId = config.peerId || 'local';
|
|
525
646
|
this.identity = config.identityDoc || this.createDefaultIdentity();
|
|
526
647
|
this.minimaxAvailable = this.checkMinimax();
|
|
527
648
|
// 2026-07-04: 透传 agentId (server.ts 通过 createAgentSession 选项注入)
|
|
528
|
-
this.
|
|
649
|
+
this.actor.state.agentId = config.agentId || '';
|
|
529
650
|
// 2026-10-01 **身份自愈** (用户实测: get_identity 返回 "DID: " 空 + 各 channel 名字串台):
|
|
530
651
|
// 工具上下文里的身份来自 this.identity, 而它可能是 config.identityDoc 与 session 实例身份的
|
|
531
652
|
// 合并结果 —— **空 did 会盖掉真 did** ✗; 名字也可能缺失或被别处覆盖。
|
|
532
653
|
// 规矩: 缺 did / 名字时, 按 **currentAgentId** 从真身份生成器补齐 (did:key + 落盘密钥),
|
|
533
654
|
// 名字优先取该 agent 自己的 persona.json。绝不回落到全局/用户身份名。
|
|
534
655
|
try {
|
|
535
|
-
if (this.
|
|
656
|
+
if (this.actor.state.agentId) {
|
|
536
657
|
const cur = this.identity || {};
|
|
537
658
|
const curDid = String(cur.did || '');
|
|
538
659
|
const badDid = !curDid || /^did:(local|pi):/i.test(curDid);
|
|
@@ -540,17 +661,17 @@ class PiAgentSession {
|
|
|
540
661
|
// 2026-10-01 加固: **did 与本人密钥不符** 也算坏 —— 用户实测 get_identity 返回一个
|
|
541
662
|
// 数据里根本不存在的 did (按 peerId 当 scope 新造的), 而它非空、非假值 ⇒ 上一版判据漏过 ✗。
|
|
542
663
|
// 规矩: 只要 currentAgentId 有值, 身份的 did 就必须等于该 agent 自己的密钥 did。
|
|
543
|
-
const mine = (0, agent_identity_js_1.loadOrCreateAgentIdentity)(this.
|
|
664
|
+
const mine = (0, agent_identity_js_1.loadOrCreateAgentIdentity)(this.actor.state.agentId);
|
|
544
665
|
const mismatch = !!mine?.did && !!curDid && curDid !== mine.did;
|
|
545
666
|
if (badDid || missingName || mismatch) {
|
|
546
667
|
if (badDid || mismatch) {
|
|
547
|
-
console.warn(`[identity] 身份纠正: ${this.
|
|
668
|
+
console.warn(`[identity] 身份纠正: ${this.actor.state.agentId} 的 did ${curDid ? curDid.slice(0, 26) : '(空)'} ⇒ ${mine.did.slice(0, 26)} (以该 agent 自己的密钥为准)`);
|
|
548
669
|
}
|
|
549
670
|
// 2026-10-01: 同样必须**原地改** —— 这段在 registerTools() **之后**跑, 工具上下文已经按引用
|
|
550
671
|
// 捕获了 this.identity; 换对象 ⇒ 工具看不到纠正结果 ✗ (同类 bug, 一并修)
|
|
551
672
|
Object.assign(this.identity, {
|
|
552
673
|
...(mine?.did ? { did: mine.did, publicKey: mine.publicKey || cur.publicKey } : {}),
|
|
553
|
-
...(missingName ? { name: (0, channel_identity_js_1.agentPersonaName)(this.
|
|
674
|
+
...(missingName ? { name: (0, channel_identity_js_1.agentPersonaName)(this.actor.state.agentId) || this.actor.state.agentId } : {}),
|
|
554
675
|
});
|
|
555
676
|
}
|
|
556
677
|
}
|
|
@@ -560,7 +681,7 @@ class PiAgentSession {
|
|
|
560
681
|
this._sessionStore = config.sessionStore ?? session_store_js_1.sessionStore;
|
|
561
682
|
this.constraintLayer = new constraint_layer_js_1.ConstraintLayer();
|
|
562
683
|
this.workflowEngine = new workflow_engine_js_1.WorkflowEngine(this.constraintLayer);
|
|
563
|
-
this.sessionManager = new pi_sdk_session_manager_js_2.PiSessionManager(this.identity.did, this.cwd, this.
|
|
684
|
+
this.sessionManager = new pi_sdk_session_manager_js_2.PiSessionManager(this.identity.did, this.cwd, this.actor.state.agentId);
|
|
564
685
|
this.agentsManager = new heartbeat_js_1.DiscoveredAgentsManager();
|
|
565
686
|
this.usePivotLoop = config.usePivotLoop ?? false;
|
|
566
687
|
this.pivotLoopConfig = config.pivotLoopConfig;
|
|
@@ -646,6 +767,18 @@ class PiAgentSession {
|
|
|
646
767
|
*/
|
|
647
768
|
async hydrateMessageHistory(sessionKey, maxMessages) {
|
|
648
769
|
try {
|
|
770
|
+
// **K5 第 4 步**: 实现体 (load → filter → 截断 → 替换) 已搬进 Actor —— 走邮箱 ⇒ 与 append/persist 串行。
|
|
771
|
+
// 业务侧只交两个**纯回调** (内核因此不必 import 业务模块); 未绑定 actor 的会话走下面原路径 (行为不变)。
|
|
772
|
+
if (this.actor) {
|
|
773
|
+
const n = await this.actor.hydrateHistory({
|
|
774
|
+
load: () => this._sessionStore.loadMessages(sessionKey),
|
|
775
|
+
filter: (loaded) => this._filterToMessage(loaded),
|
|
776
|
+
maxMessages,
|
|
777
|
+
});
|
|
778
|
+
if (n > 0)
|
|
779
|
+
console.log(`[PiAgent] 从 ${sessionKey} 回灌 ${n} 条历史 (经 Channel Actor)`);
|
|
780
|
+
return;
|
|
781
|
+
}
|
|
649
782
|
const loaded = await this._sessionStore.loadMessages(sessionKey);
|
|
650
783
|
if (!loaded) {
|
|
651
784
|
console.log(`[PiAgent] hydrate: 没有 ${sessionKey} 的历史`);
|
|
@@ -653,7 +786,7 @@ class PiAgentSession {
|
|
|
653
786
|
}
|
|
654
787
|
const hydrated = this._filterToMessage(loaded).slice(-maxMessages);
|
|
655
788
|
if (hydrated.length > 0) {
|
|
656
|
-
this.
|
|
789
|
+
this.replaceHistory(hydrated);
|
|
657
790
|
console.log(`[PiAgent] 从 ${sessionKey} 回灌 ${hydrated.length} 条历史`);
|
|
658
791
|
}
|
|
659
792
|
}
|
|
@@ -673,7 +806,10 @@ class PiAgentSession {
|
|
|
673
806
|
* 即可获得"重启 / 跨进程接续"的语义.
|
|
674
807
|
*/
|
|
675
808
|
async saveCurrentSession(key) {
|
|
676
|
-
|
|
809
|
+
// **K5 第 4 步**: 取数拍走 Actor 的快照 (走邮箱 ⇒ 与 append 串行, 不会抓到"边写边读"的半截状态);
|
|
810
|
+
// 未绑定 actor 的会话走原路径 (行为不变)。
|
|
811
|
+
const source = this.actor ? await this.actor.historySnapshot() : this.actor.state.messageHistory;
|
|
812
|
+
const persisted = source.map((m) => ({
|
|
677
813
|
role: m.role,
|
|
678
814
|
content: m.content,
|
|
679
815
|
toolCall: m.toolCall,
|
|
@@ -690,9 +826,9 @@ class PiAgentSession {
|
|
|
690
826
|
* 与 loadSessionKey (构造时读) 不同: 这个是 session 已建好后再读.
|
|
691
827
|
*/
|
|
692
828
|
async resumeSession(key, maxMessages = 30) {
|
|
693
|
-
const before = this.messageHistory.length;
|
|
829
|
+
const before = this.actor.state.messageHistory.length;
|
|
694
830
|
await this.hydrateMessageHistory(key, maxMessages);
|
|
695
|
-
return this.messageHistory.length - before;
|
|
831
|
+
return this.actor.state.messageHistory.length - before;
|
|
696
832
|
}
|
|
697
833
|
/**
|
|
698
834
|
* 2026-06-30: 读历史不修改 messageHistory.
|
|
@@ -864,7 +1000,7 @@ class PiAgentSession {
|
|
|
864
1000
|
const { CHANNELS_PATH } = await Promise.resolve().then(() => __importStar(require('../web/server-types.js')));
|
|
865
1001
|
const { loadChannels } = await Promise.resolve().then(() => __importStar(require('../web/server-storage.js')));
|
|
866
1002
|
const channels = await loadChannels();
|
|
867
|
-
const ch = channels.find((c) => c.id === this.
|
|
1003
|
+
const ch = channels.find((c) => c.id === this.actor.state.channelId);
|
|
868
1004
|
if (ch && ch.encryptedPrivateKey && ch.encryptedPrivateKeyIv && ch.walletAddress) {
|
|
869
1005
|
return {
|
|
870
1006
|
encryptedPrivateKey: ch.encryptedPrivateKey,
|
|
@@ -939,7 +1075,7 @@ class PiAgentSession {
|
|
|
939
1075
|
// 2026-10-01: 原先自造 `did:pi:<peerId>` —— **不是有效 DID** (仓里 server.ts 自己都把 did:pi:
|
|
940
1076
|
// 当"待升级"占位; 用户实测 get_identity 报 did:pi:ch_1785668060213)。
|
|
941
1077
|
// 改用仓里既有的真身份生成器 (agent-identity.loadOrCreateAgentIdentity, 同步, 产 did:key + 落盘密钥)。
|
|
942
|
-
const scope = this.
|
|
1078
|
+
const scope = this.actor.state.agentId || this.peerId || 'default';
|
|
943
1079
|
try {
|
|
944
1080
|
const real = (0, agent_identity_js_1.loadOrCreateAgentIdentity)(scope);
|
|
945
1081
|
return {
|
|
@@ -998,9 +1134,9 @@ class PiAgentSession {
|
|
|
998
1134
|
const did = await resolveUserDid();
|
|
999
1135
|
const model = this.llmConfig?.model || this.model || '';
|
|
1000
1136
|
return new TrajectoryRecorder({
|
|
1001
|
-
agentId: this.
|
|
1137
|
+
agentId: this.actor.state.agentId || 'default',
|
|
1002
1138
|
input,
|
|
1003
|
-
channelId: channelId || this.
|
|
1139
|
+
channelId: channelId || this.actor.state.channelId,
|
|
1004
1140
|
did: did || undefined,
|
|
1005
1141
|
model: typeof model === 'string' && model ? model : undefined,
|
|
1006
1142
|
});
|
|
@@ -1042,8 +1178,8 @@ class PiAgentSession {
|
|
|
1042
1178
|
if (gate !== 'ready') {
|
|
1043
1179
|
const why = `初始化未就绪 (${gate}, 当前阶段 ${state.stage})${state.lastError ? ` — ${state.lastError.message}` : ''}`;
|
|
1044
1180
|
const hint = (state.actions && state.actions[0]) || '运行 `bolloon setup` 完成初始化';
|
|
1045
|
-
this.
|
|
1046
|
-
this.
|
|
1181
|
+
this.pushHistory({ role: 'user', content: input });
|
|
1182
|
+
this.pushHistory({ role: 'assistant', content: `[初始化未就绪] ${why}\n${hint}` });
|
|
1047
1183
|
console.warn(`[PiAgent] 拒绝执行: ${why}`);
|
|
1048
1184
|
return `[初始化未就绪] ${why}\n${hint}`;
|
|
1049
1185
|
}
|
|
@@ -1051,13 +1187,13 @@ class PiAgentSession {
|
|
|
1051
1187
|
catch { /* 门禁自身异常 → 按"不能判定"处理会阻塞一切; 这里只在能读到状态时才拦 */ }
|
|
1052
1188
|
}
|
|
1053
1189
|
this.minimaxAvailable = this.checkMinimax();
|
|
1054
|
-
this.
|
|
1190
|
+
this.actor.state.channelId = options?.channelId ?? this.actor.state.channelId;
|
|
1055
1191
|
// 2026-08-08: 运行轨迹采集 (落盘 + OrbitDB, 失败静默) — 包裹 onStream 收集步骤事件
|
|
1056
1192
|
const trajRec = await this.createTrajectoryRecorder(input, options?.channelId);
|
|
1057
1193
|
if (trajRec && options?.onStream) {
|
|
1058
1194
|
options = { ...options, onStream: this.wrapTrajectoryStream(options.onStream, trajRec) };
|
|
1059
1195
|
}
|
|
1060
|
-
this.
|
|
1196
|
+
this.pushHistory({
|
|
1061
1197
|
role: 'user',
|
|
1062
1198
|
content: input
|
|
1063
1199
|
});
|
|
@@ -1067,23 +1203,23 @@ class PiAgentSession {
|
|
|
1067
1203
|
// 于是把"什么都没跑"当成一次正常执行 (这正是"agent 跑了但没记录"的老毛病在 fallback 路径的翻版)。
|
|
1068
1204
|
const response = await this.handleFallback(input);
|
|
1069
1205
|
try {
|
|
1070
|
-
let boundGoalId = this.
|
|
1206
|
+
let boundGoalId = this.actor.state.goalBinding;
|
|
1071
1207
|
if (!boundGoalId) {
|
|
1072
1208
|
const g = await (0, goal_store_js_1.createGoal)({
|
|
1073
1209
|
objective: this.currentUserInput || input.slice(0, 200),
|
|
1074
|
-
channelId: this.
|
|
1075
|
-
agentId: this.
|
|
1210
|
+
channelId: this.actor.state.channelId || undefined,
|
|
1211
|
+
agentId: this.actor.state.agentId || undefined,
|
|
1076
1212
|
createdBy: this.runSurface,
|
|
1077
1213
|
});
|
|
1078
1214
|
boundGoalId = g.goalId;
|
|
1079
1215
|
}
|
|
1080
|
-
this.
|
|
1216
|
+
this.actor.state.goalBinding = boundGoalId;
|
|
1081
1217
|
const rec = await (0, run_store_js_1.startRun)({
|
|
1082
1218
|
surface: this.runSurface,
|
|
1083
1219
|
goal: this.currentUserInput || input.slice(0, 200),
|
|
1084
1220
|
goalId: boundGoalId,
|
|
1085
|
-
channelId: this.
|
|
1086
|
-
agentId: this.
|
|
1221
|
+
channelId: this.actor.state.channelId || undefined,
|
|
1222
|
+
agentId: this.actor.state.agentId || undefined,
|
|
1087
1223
|
modelConfig: await this.runModelSnapshot(),
|
|
1088
1224
|
});
|
|
1089
1225
|
this.lastRunId = rec.runId;
|
|
@@ -1122,25 +1258,27 @@ class PiAgentSession {
|
|
|
1122
1258
|
catch (err) {
|
|
1123
1259
|
await (0, run_store_js_1.recordDegradation)({ kind: 'core', op: 'pi-sdk.fallbackRun', runId: this.lastRunId || '', message: String(err?.message || err).slice(0, 160) }).catch(() => { });
|
|
1124
1260
|
}
|
|
1125
|
-
this.
|
|
1261
|
+
this.pushHistory({ role: 'assistant', content: response });
|
|
1126
1262
|
this.reportUsageToContextManager();
|
|
1127
1263
|
this.finishTrajectory(trajRec, response);
|
|
1128
1264
|
return response;
|
|
1129
1265
|
}
|
|
1130
1266
|
// P0 注入门
|
|
1131
|
-
|
|
1132
|
-
|
|
1267
|
+
// K2: eventSink + abortSignal 已外置, 入口一次建好本轮 Context。
|
|
1268
|
+
// 其余字段**不在这里抄一份** —— 抄写会新增对旧实例字段的读, 违反方向判据
|
|
1269
|
+
// 「新层出现后旧写口调用点数只许不变或减少」。逐字段迁移时再各自搬进来。
|
|
1270
|
+
this.runCtx = this.seedRunContext({ eventSink: options?.onStream ?? null, abortSignal: options?.signal ?? null });
|
|
1133
1271
|
await this.computeJudgmentGate(input);
|
|
1134
1272
|
// M2.2 (2026-06-17): intent 分类 — prompt() 路径也跑 (跟 promptStream 对齐)
|
|
1135
1273
|
try {
|
|
1136
1274
|
const { classifyIntent, intentHint } = await Promise.resolve().then(() => __importStar(require('./intent-classifier.js')));
|
|
1137
|
-
this.
|
|
1138
|
-
this.currentIntentHint = intentHint(this.
|
|
1275
|
+
this.runCtx.intent = classifyIntent(input);
|
|
1276
|
+
this.currentIntentHint = intentHint(this.runCtx.intent);
|
|
1139
1277
|
this.currentUserInput = input;
|
|
1140
1278
|
}
|
|
1141
1279
|
catch (err) {
|
|
1142
1280
|
console.warn('[PiAgent] classifyIntent in prompt() failed:', err);
|
|
1143
|
-
this.
|
|
1281
|
+
this.runCtx.intent = 'chitchat';
|
|
1144
1282
|
this.currentIntentHint = '';
|
|
1145
1283
|
}
|
|
1146
1284
|
// P2: 解析当前 permission mode
|
|
@@ -1165,14 +1303,13 @@ class PiAgentSession {
|
|
|
1165
1303
|
(0, injection_gate_js_1.recordJudgmentUsage)(this.judgmentGateUsedIds, { userInput: input }).catch((err) => console.warn('[PiAgent] recordJudgmentUsage failed:', err));
|
|
1166
1304
|
}
|
|
1167
1305
|
this.clearJudgmentGate();
|
|
1168
|
-
this.
|
|
1169
|
-
this.currentOnStream = null;
|
|
1306
|
+
this.runCtx = (0, run_context_js_1.createRunContext)(); // K2: 用完即清 (换新 Context, 不继承残留);
|
|
1170
1307
|
this.reportUsageToContextManager();
|
|
1171
1308
|
}
|
|
1172
1309
|
}
|
|
1173
1310
|
try {
|
|
1174
1311
|
// 2026-06-16: runReActLoop 现在返回 { reply, aiFailed, aiFailureReason } — 这里只需 reply 字符串
|
|
1175
|
-
const loopResult = await this.runReActLoop(this.
|
|
1312
|
+
const loopResult = await this.runReActLoop(this.runCtx.eventSink ?? undefined, options?.signal);
|
|
1176
1313
|
// 2026-10-01: **本回合改过 TS ⇒ 收尾自动类型检查** (一轮一次) —— 复用已有的 tsc_check 工具
|
|
1177
1314
|
if ((0, code_write_gate_js_1.decideTypecheck)(this.tsTouchedThisTurn, this.typecheckRanThisTurn)) {
|
|
1178
1315
|
this.typecheckRanThisTurn = true;
|
|
@@ -1181,11 +1318,11 @@ class PiAgentSession {
|
|
|
1181
1318
|
if (tscTool?.execute) {
|
|
1182
1319
|
const r = await tscTool.execute({});
|
|
1183
1320
|
const line = (0, code_write_gate_js_1.formatTypecheckResult)(r?.success !== false && !/error TS\d+/.test(String(r?.output || '')), String(r?.output || ''));
|
|
1184
|
-
this.
|
|
1321
|
+
this.runCtx.eventSink?.({ type: 'status', content: `🔎 ${line}`, tool: 'system' });
|
|
1185
1322
|
}
|
|
1186
1323
|
}
|
|
1187
1324
|
catch (e) {
|
|
1188
|
-
this.
|
|
1325
|
+
this.runCtx.eventSink?.({ type: 'status', content: `🔎 类型检查没能跑起来: ${String(e?.message || e).slice(0, 100)} (改动已落盘, 记得自己跑一次)`, tool: 'system' });
|
|
1189
1326
|
}
|
|
1190
1327
|
this.tsTouchedThisTurn = [];
|
|
1191
1328
|
}
|
|
@@ -1217,7 +1354,7 @@ class PiAgentSession {
|
|
|
1217
1354
|
}
|
|
1218
1355
|
catch { /* 落日志失败不影响主流程 */ }
|
|
1219
1356
|
try {
|
|
1220
|
-
this.
|
|
1357
|
+
this.runCtx.eventSink?.({ type: 'status', content: `📚 复盘: ${m}`, tool: 'system' });
|
|
1221
1358
|
}
|
|
1222
1359
|
catch { /* 上屏失败不打断 */ }
|
|
1223
1360
|
};
|
|
@@ -1284,8 +1421,7 @@ class PiAgentSession {
|
|
|
1284
1421
|
(0, injection_gate_js_1.recordJudgmentUsage)(this.judgmentGateNegativeUsedIds, { userInput: input, polarity: 'negative' }).catch((err) => console.warn('[PiAgent] recordJudgmentUsage (negative) failed:', err));
|
|
1285
1422
|
}
|
|
1286
1423
|
this.clearJudgmentGate();
|
|
1287
|
-
this.
|
|
1288
|
-
this.currentOnStream = null;
|
|
1424
|
+
this.runCtx = (0, run_context_js_1.createRunContext)(); // K2: 用完即清 (换新 Context, 不继承残留);
|
|
1289
1425
|
this.reportUsageToContextManager();
|
|
1290
1426
|
}
|
|
1291
1427
|
}
|
|
@@ -1293,7 +1429,7 @@ class PiAgentSession {
|
|
|
1293
1429
|
console.log(`[PiAgent.promptStream] ENTRY, channelId=${channelId}, input chars=${input.length}`);
|
|
1294
1430
|
this.minimaxAvailable = this.checkMinimax();
|
|
1295
1431
|
console.log(`[PiAgent.promptStream] minimaxAvailable=${this.minimaxAvailable}`);
|
|
1296
|
-
this.
|
|
1432
|
+
this.actor.state.channelId = channelId ?? this.actor.state.channelId;
|
|
1297
1433
|
// 2026-08-08: 运行轨迹采集 (落盘 + OrbitDB, 失败静默) — 包裹 onStream 收集步骤事件
|
|
1298
1434
|
const trajRec = await this.createTrajectoryRecorder(input, channelId);
|
|
1299
1435
|
if (trajRec)
|
|
@@ -1306,7 +1442,7 @@ class PiAgentSession {
|
|
|
1306
1442
|
const userText = markerMatch ? markerMatch[1].trim() : input;
|
|
1307
1443
|
const contextHint = markerMatch ? input.replace(markerMatch[0], '').trim() : '';
|
|
1308
1444
|
console.log(`[PiAgent.promptStream] marker matched=${!!markerMatch}, userText chars=${userText.length}, contextHint chars=${contextHint.length}`);
|
|
1309
|
-
this.
|
|
1445
|
+
this.pushHistory({
|
|
1310
1446
|
role: 'user',
|
|
1311
1447
|
content: userText
|
|
1312
1448
|
});
|
|
@@ -1316,7 +1452,7 @@ class PiAgentSession {
|
|
|
1316
1452
|
// 让 agent 能"回忆起"之前 session 的记忆 (自动获取之前 session), 而非只靠启动时批量压缩.
|
|
1317
1453
|
try {
|
|
1318
1454
|
const { recallMemory } = await Promise.resolve().then(() => __importStar(require('./memory-recall.js')));
|
|
1319
|
-
const recalled = await recallMemory({ query: userText, agentId: this.
|
|
1455
|
+
const recalled = await recallMemory({ query: userText, agentId: this.actor.state.agentId || this.peerId || '' });
|
|
1320
1456
|
if (recalled) {
|
|
1321
1457
|
this.contextHintAddition = [this.contextHintAddition, recalled].filter(Boolean).join('\n\n');
|
|
1322
1458
|
}
|
|
@@ -1325,29 +1461,29 @@ class PiAgentSession {
|
|
|
1325
1461
|
onStream({ type: 'thinking', content: '🤔 开始思考...' });
|
|
1326
1462
|
if (!this.minimaxAvailable) {
|
|
1327
1463
|
const response = await this.handleFallback(userText);
|
|
1328
|
-
this.
|
|
1464
|
+
this.pushHistory({ role: 'assistant', content: response });
|
|
1329
1465
|
onStream({ type: 'done', content: '' });
|
|
1330
1466
|
this.reportUsageToContextManager();
|
|
1331
1467
|
this.finishTrajectory(trajRec, response);
|
|
1332
1468
|
return response;
|
|
1333
1469
|
}
|
|
1334
|
-
// P0 注入门: 缓存 onStream + signal, computeJudgmentGate 用
|
|
1335
|
-
|
|
1336
|
-
this.
|
|
1470
|
+
// P0 注入门: 缓存 onStream + signal, computeJudgmentGate 用 runCtx.eventSink 广播 phase
|
|
1471
|
+
// K2: eventSink + abortSignal 一起建进本轮 Context (只搬已外置的字段, 不抄未迁移的)
|
|
1472
|
+
this.runCtx = this.seedRunContext({ eventSink: onStream, abortSignal: signal ?? null });
|
|
1337
1473
|
await this.computeJudgmentGate(userText);
|
|
1338
1474
|
// M2.2 (2026-06-17): intent 分类 — 0 LLM 成本, 5 行 keyword 匹配
|
|
1339
1475
|
try {
|
|
1340
1476
|
const { classifyIntent, intentHint } = await Promise.resolve().then(() => __importStar(require('./intent-classifier.js')));
|
|
1341
|
-
this.
|
|
1342
|
-
this.currentIntentHint = intentHint(this.
|
|
1477
|
+
this.runCtx.intent = classifyIntent(userText);
|
|
1478
|
+
this.currentIntentHint = intentHint(this.runCtx.intent);
|
|
1343
1479
|
this.currentUserInput = userText;
|
|
1344
|
-
if (this.
|
|
1345
|
-
onStream({ type: 'phase', phase: 'intent_classified', detail: this.
|
|
1480
|
+
if (this.runCtx.intent !== 'chitchat') {
|
|
1481
|
+
onStream({ type: 'phase', phase: 'intent_classified', detail: this.runCtx.intent, content: '' });
|
|
1346
1482
|
}
|
|
1347
1483
|
}
|
|
1348
1484
|
catch (err) {
|
|
1349
1485
|
console.warn('[PiAgent] classifyIntent failed (non-fatal):', err);
|
|
1350
|
-
this.
|
|
1486
|
+
this.runCtx.intent = 'chitchat';
|
|
1351
1487
|
this.currentIntentHint = '';
|
|
1352
1488
|
}
|
|
1353
1489
|
// P1.1: 异步跑 Auto-Compact (LLM 摘要, 仅在 budget 超限时触发, 失败静默)
|
|
@@ -1364,8 +1500,8 @@ class PiAgentSession {
|
|
|
1364
1500
|
let bootstrapAddition = '';
|
|
1365
1501
|
try {
|
|
1366
1502
|
const ss = await (0, human_value_pipeline_js_1.onSessionStart)({
|
|
1367
|
-
channelId: this.
|
|
1368
|
-
agentId: this.
|
|
1503
|
+
channelId: this.actor.state.channelId || undefined,
|
|
1504
|
+
agentId: this.actor.state.agentId || undefined,
|
|
1369
1505
|
});
|
|
1370
1506
|
bootstrapAddition = ss.systemAddition || '';
|
|
1371
1507
|
}
|
|
@@ -1374,10 +1510,10 @@ class PiAgentSession {
|
|
|
1374
1510
|
}
|
|
1375
1511
|
// 2026-07-07 P1-B: 注入最近 5 条项目事件日志 (L2) — 让 LLM 知道项目状态/feature 变化
|
|
1376
1512
|
// 失败静默, append 到 bootstrapAddition 末尾 (超 800 字截断)
|
|
1377
|
-
if (this.
|
|
1513
|
+
if (this.actor.state.channelId) {
|
|
1378
1514
|
try {
|
|
1379
1515
|
const { getRecentEvents } = await Promise.resolve().then(() => __importStar(require('../bootstrap/event-log.js')));
|
|
1380
|
-
const events = await getRecentEvents(this.
|
|
1516
|
+
const events = await getRecentEvents(this.actor.state.channelId, 5);
|
|
1381
1517
|
if (events.length > 0) {
|
|
1382
1518
|
const eventBlock = [
|
|
1383
1519
|
'## 最近项目事件 (最近 5 条, 倒序)',
|
|
@@ -1392,7 +1528,7 @@ class PiAgentSession {
|
|
|
1392
1528
|
// 2026-07-07 P2-C: 注入项目当前状态 (L3) — 目标/约束/待办/已完成
|
|
1393
1529
|
try {
|
|
1394
1530
|
const { readState, formatStateForPrompt } = await Promise.resolve().then(() => __importStar(require('../bootstrap/project-state.js')));
|
|
1395
|
-
const state = await readState({ channelId: this.
|
|
1531
|
+
const state = await readState({ channelId: this.actor.state.channelId });
|
|
1396
1532
|
const stateText = formatStateForPrompt(state);
|
|
1397
1533
|
if (stateText) {
|
|
1398
1534
|
bootstrapAddition = (bootstrapAddition + '\n\n' + stateText).slice(-2500);
|
|
@@ -1404,7 +1540,7 @@ class PiAgentSession {
|
|
|
1404
1540
|
// 2026-07-07 P2-C: 向量检索 top-3 (L4) — 按当前 channelId + userText 找历史相关片段
|
|
1405
1541
|
try {
|
|
1406
1542
|
const { searchIndex } = await Promise.resolve().then(() => __importStar(require('../bootstrap/vector-index.js')));
|
|
1407
|
-
const indexName = `channel-${this.
|
|
1543
|
+
const indexName = `channel-${this.actor.state.channelId}`;
|
|
1408
1544
|
const results = await searchIndex({
|
|
1409
1545
|
indexName,
|
|
1410
1546
|
query: userText,
|
|
@@ -1465,13 +1601,12 @@ class PiAgentSession {
|
|
|
1465
1601
|
(0, monitor_gate_js_1.monitorAfterReply)(userText, pivotResult);
|
|
1466
1602
|
const stopStartTime = this.promptStartTime || Date.now();
|
|
1467
1603
|
(0, human_value_pipeline_js_1.onStop)({
|
|
1468
|
-
channelId: this.
|
|
1604
|
+
channelId: this.actor.state.channelId || 'unknown',
|
|
1469
1605
|
durationMs: Date.now() - stopStartTime,
|
|
1470
1606
|
usedJudgmentIds: [...this.judgmentGateUsedIds],
|
|
1471
1607
|
}).catch((err) => console.warn('[PiAgent] onStop failed:', err));
|
|
1472
1608
|
this.clearJudgmentGate();
|
|
1473
|
-
this.
|
|
1474
|
-
this.currentSignal = null;
|
|
1609
|
+
this.runCtx = (0, run_context_js_1.createRunContext)(); // K2: 用完即清 (换新 Context, 不继承残留);
|
|
1475
1610
|
this.bootstrapAddition = '';
|
|
1476
1611
|
this.contextHintAddition = '';
|
|
1477
1612
|
this.promptStartTime = 0;
|
|
@@ -1501,8 +1636,7 @@ class PiAgentSession {
|
|
|
1501
1636
|
}
|
|
1502
1637
|
catch (err) {
|
|
1503
1638
|
// abort 失败: 视作"已中断", 抛错让上层用 partial 兜底
|
|
1504
|
-
this.
|
|
1505
|
-
this.currentSignal = null;
|
|
1639
|
+
this.runCtx = (0, run_context_js_1.createRunContext)(); // K2: 用完即清 (换新 Context, 不继承残留);
|
|
1506
1640
|
throw err;
|
|
1507
1641
|
}
|
|
1508
1642
|
attempt++;
|
|
@@ -1540,8 +1674,8 @@ class PiAgentSession {
|
|
|
1540
1674
|
// 因为 messageHistory.push({role:'user'}) 在 promptStream 顶部已经做过, 重跑 runReActLoop 不会重复 push,
|
|
1541
1675
|
// 但 assistant 失败那条也别留 (留了会污染下一轮 LLM context).
|
|
1542
1676
|
// 简化: 重试前 pop 一次 assistant (如果最后一条是 assistant)
|
|
1543
|
-
if (this.messageHistory.length > 0 && this.messageHistory[this.messageHistory.length - 1].role === 'assistant') {
|
|
1544
|
-
this.
|
|
1677
|
+
if (this.actor.state.messageHistory.length > 0 && this.actor.state.messageHistory[this.actor.state.messageHistory.length - 1].role === 'assistant') {
|
|
1678
|
+
this.popHistory();
|
|
1545
1679
|
}
|
|
1546
1680
|
}
|
|
1547
1681
|
onStream({ type: 'done', content: '' });
|
|
@@ -1559,15 +1693,14 @@ class PiAgentSession {
|
|
|
1559
1693
|
// Bootstrap Stop hook: fire-and-forget 写本次 session 摘要
|
|
1560
1694
|
const stopStartTime = this.promptStartTime || Date.now();
|
|
1561
1695
|
(0, human_value_pipeline_js_1.onStop)({
|
|
1562
|
-
channelId: this.
|
|
1696
|
+
channelId: this.actor.state.channelId || 'unknown',
|
|
1563
1697
|
durationMs: Date.now() - stopStartTime,
|
|
1564
1698
|
usedJudgmentIds: [...this.judgmentGateUsedIds],
|
|
1565
1699
|
}).catch((err) => console.warn('[PiAgent] onStop failed:', err));
|
|
1566
1700
|
// 用完即清, 避免污染下一轮
|
|
1567
1701
|
this.clearJudgmentGate();
|
|
1568
|
-
this.
|
|
1702
|
+
this.runCtx = (0, run_context_js_1.createRunContext)(); // K2: 用完即清 (换新 Context, 不继承残留);
|
|
1569
1703
|
this.reportUsageToContextManager();
|
|
1570
|
-
this.currentSignal = null;
|
|
1571
1704
|
this.bootstrapAddition = '';
|
|
1572
1705
|
this.promptStartTime = 0;
|
|
1573
1706
|
// 2026-08-08: 轨迹收尾 — 落盘 + OrbitDB (失败静默, 不阻塞回复)
|
|
@@ -1575,7 +1708,7 @@ class PiAgentSession {
|
|
|
1575
1708
|
return result;
|
|
1576
1709
|
}
|
|
1577
1710
|
async promptWithPivotLoop(input, config, channelId) {
|
|
1578
|
-
this.
|
|
1711
|
+
this.actor.state.channelId = channelId ?? this.actor.state.channelId;
|
|
1579
1712
|
if (!this.minimaxAvailable) {
|
|
1580
1713
|
const response = await this.handleFallback(input);
|
|
1581
1714
|
return {
|
|
@@ -1607,8 +1740,8 @@ class PiAgentSession {
|
|
|
1607
1740
|
// M2.2 (2026-06-17): intent 分类 — pivot loop 也要拿到 hint
|
|
1608
1741
|
try {
|
|
1609
1742
|
const { classifyIntent, intentHint } = await Promise.resolve().then(() => __importStar(require('./intent-classifier.js')));
|
|
1610
|
-
this.
|
|
1611
|
-
this.currentIntentHint = intentHint(this.
|
|
1743
|
+
this.runCtx.intent = classifyIntent(input);
|
|
1744
|
+
this.currentIntentHint = intentHint(this.runCtx.intent);
|
|
1612
1745
|
this.currentUserInput = input;
|
|
1613
1746
|
}
|
|
1614
1747
|
catch (err) {
|
|
@@ -1655,21 +1788,21 @@ ${await this.renderActivePlansSection()}
|
|
|
1655
1788
|
- 工具调用前可以简短思考 (1-2 句话), 但**不要写长篇 thinking** (会撞 max_tokens)
|
|
1656
1789
|
- 工具调用后必须等结果, 不要在同一个回复里继续输出
|
|
1657
1790
|
- <final gen> 只在**真完成所有任务**时输出, 不要在工具调用前/中输出${this.judgmentGateAddition}${this.contextHintAddition}`;
|
|
1658
|
-
// 2026-06-15: 把
|
|
1791
|
+
// 2026-06-15: 把 runCtx.eventSink 传给 loop, 让 step-timeline 在 pivot 循环里也能 emit step_start/done
|
|
1659
1792
|
// 之前 loop.execute() 不接 streamCallback, 导致 step-timeline 只能看到老 runReActLoop 路径
|
|
1660
1793
|
// promptWithPivotLoop 路径 0 step events — UI 显示 timeline 但永远是空
|
|
1661
1794
|
// 2026-06-17: 透传 signal 让 abort 工作 — loop.execute() 当前不接 signal 参数,
|
|
1662
1795
|
// 所以 abort 行为通过 this.currentSignal 共享给 loop 内部读 (后续 M3.2 接 task plan 时一起加)
|
|
1663
1796
|
// 2026-07-06: pivot 内 token 累计到 ~70% 时回调 (workflow-pivot-loop.ts line 270+).
|
|
1664
|
-
// pivot 的 messageHistory 是 process-local, 不和 pi-sdk 的
|
|
1797
|
+
// pivot 的 messageHistory 是 process-local, 不和 pi-sdk 的 actor 的 messageHistory 同步.
|
|
1665
1798
|
// 真正折叠需要把 pi-sdk 历史灌回 pivot 的 history 数组 — 侵入较大.
|
|
1666
1799
|
// 当前 priority: 临时传空实现, 让 budget 公式放够 (workflow-pivot-loop.ts line 220)
|
|
1667
1800
|
// 不再撞预算. 这条路径留作技术债.
|
|
1668
1801
|
// 2026-07-17 Bug 1 修: 注入 messageHistory (hydrateMessageHistory 从 session JSON 回灌的) 到 system prompt
|
|
1669
|
-
// pivot loop execute() 内部自己维护 messageHistory, 跟 pi-sdk 的
|
|
1802
|
+
// pivot loop execute() 内部自己维护 messageHistory, 跟 pi-sdk 的 actor 的 messageHistory 隔离,
|
|
1670
1803
|
// 不注入的话 LLM 看不到历史对话, 每次都是新对话.
|
|
1671
1804
|
const historyLines = [];
|
|
1672
|
-
const historyToInject = this.messageHistory.slice(-20, -1);
|
|
1805
|
+
const historyToInject = this.actor.state.messageHistory.slice(-20, -1);
|
|
1673
1806
|
for (const m of historyToInject) {
|
|
1674
1807
|
const roleLabel = m.role === 'user' ? '用户' : m.role === 'assistant' ? '你' : m.role === 'tool' ? '工具结果' : m.role;
|
|
1675
1808
|
const text = (m.content || '').slice(0, 2000);
|
|
@@ -1682,9 +1815,9 @@ ${await this.renderActivePlansSection()}
|
|
|
1682
1815
|
const onCompact = async () => {
|
|
1683
1816
|
// no-op (best-effort hook for future pi-sdk/pivot history sync)
|
|
1684
1817
|
};
|
|
1685
|
-
const result = await loop.execute(input, llm, systemPrompt + historyBlock, this.
|
|
1818
|
+
const result = await loop.execute(input, llm, systemPrompt + historyBlock, this.runCtx.eventSink ?? undefined, this.runCtx.abortSignal ?? undefined, onCompact);
|
|
1686
1819
|
if (result.response) {
|
|
1687
|
-
this.
|
|
1820
|
+
this.pushHistory({ role: 'assistant', content: result.response });
|
|
1688
1821
|
}
|
|
1689
1822
|
// 回溯 + 清场
|
|
1690
1823
|
if (this.judgmentGateUsedIds.length > 0) {
|
|
@@ -1700,7 +1833,9 @@ ${await this.renderActivePlansSection()}
|
|
|
1700
1833
|
*/
|
|
1701
1834
|
_harness = null;
|
|
1702
1835
|
/** M2 绑定 GoalStore 后填真值; 在此之前为空 (事件里 goalId 字段已就位) */
|
|
1703
|
-
|
|
1836
|
+
// **K5 步骤③/⑧**: 会话的 Goal 默认绑定本体在 `actor.state.goalBinding` (访问器已删, 直接读本体)。
|
|
1837
|
+
// leo 口径不变: 这只是默认值; 每次执行开始必须把最终绑定写进 RunContext/Run 记录,
|
|
1838
|
+
// 运行中重绑必须走显式 Goal Binding 操作, 不许靠裸字段隐式生效。
|
|
1704
1839
|
/** 2026-09-16 (M2): 本次执行是"从 checkpoint 恢复"的 runId (非空 = 恢复模式, 不再新建 run) */
|
|
1705
1840
|
resumeRunId = '';
|
|
1706
1841
|
resumePlan = null;
|
|
@@ -1723,7 +1858,7 @@ ${await this.renderActivePlansSection()}
|
|
|
1723
1858
|
}
|
|
1724
1859
|
/** CLI / Web 注入"当前目标" (有 goalId 就在该 Goal 下执行) */
|
|
1725
1860
|
setGoalId(goalId) {
|
|
1726
|
-
this.
|
|
1861
|
+
this.actor.state.goalBinding = String(goalId || '');
|
|
1727
1862
|
}
|
|
1728
1863
|
/** 核心状态迁移的兜底: 失败 → 记降级 + 交给循环顶部的持久化硬闸 (不吞错) */
|
|
1729
1864
|
async safeSetRunStatus(runId, to) {
|
|
@@ -1742,13 +1877,13 @@ ${await this.renderActivePlansSection()}
|
|
|
1742
1877
|
* 这是"错误恢复闭环"的运行时接线点 (此前 recordRecovery / repeatedFailureCount 只是数据结构)。
|
|
1743
1878
|
*/
|
|
1744
1879
|
async wireToolFailure(tool, errorText, args) {
|
|
1745
|
-
if (!this.
|
|
1880
|
+
if (!this.actor.state.activeRun)
|
|
1746
1881
|
return;
|
|
1747
1882
|
const cls = (0, run_store_js_1.classifyError)(errorText);
|
|
1748
1883
|
const digest = (0, run_store_js_1.argsDigestOf)(args);
|
|
1749
1884
|
let repeats = 1;
|
|
1750
1885
|
try {
|
|
1751
|
-
const rec = await (0, run_store_js_1.readRun)(this.
|
|
1886
|
+
const rec = await (0, run_store_js_1.readRun)(this.actor.state.activeRun);
|
|
1752
1887
|
// recordStep 已经把**这次**失败记进去了, 所以"连续失败次数"直接就是当前值 (不再 +1)
|
|
1753
1888
|
const seen = rec ? (0, run_store_js_1.repeatedFailureCount)(rec, tool, digest) : 0;
|
|
1754
1889
|
repeats = Math.max(seen, 1);
|
|
@@ -1759,7 +1894,7 @@ ${await this.renderActivePlansSection()}
|
|
|
1759
1894
|
: cls === 'external_no_reply' ? 'pause'
|
|
1760
1895
|
: 'retry';
|
|
1761
1896
|
try {
|
|
1762
|
-
await (0, run_store_js_1.recordRecovery)(this.
|
|
1897
|
+
await (0, run_store_js_1.recordRecovery)(this.actor.state.activeRun, {
|
|
1763
1898
|
errorClass: cls,
|
|
1764
1899
|
message: `${tool}: ${errorText}`.slice(0, 200),
|
|
1765
1900
|
action,
|
|
@@ -1768,20 +1903,20 @@ ${await this.renderActivePlansSection()}
|
|
|
1768
1903
|
});
|
|
1769
1904
|
}
|
|
1770
1905
|
catch (err) {
|
|
1771
|
-
await (0, run_store_js_1.recordDegradation)({ kind: 'core', op: 'pi-sdk.recordRecovery', runId: this.
|
|
1906
|
+
await (0, run_store_js_1.recordDegradation)({ kind: 'core', op: 'pi-sdk.recordRecovery', runId: this.actor.state.activeRun, message: String(err?.message || err) }).catch(() => { });
|
|
1772
1907
|
}
|
|
1773
1908
|
// 外部无响应 → awaiting_external (不算失败; 等回话后回到 running)
|
|
1774
1909
|
if (cls === 'external_no_reply') {
|
|
1775
1910
|
this.awaitingExternal = true;
|
|
1776
|
-
await this.safeSetRunStatus(this.
|
|
1911
|
+
await this.safeSetRunStatus(this.actor.state.activeRun, 'awaiting_external');
|
|
1777
1912
|
// 2026-09-16 (2-C.4): 把"在等什么"写成持久化事实 (来源/关联/过期), 否则真实回包到了也不知道该唤醒谁。
|
|
1778
1913
|
try {
|
|
1779
|
-
if (this.
|
|
1914
|
+
if (this.actor.state.goalBinding) {
|
|
1780
1915
|
const { bindExternalWait, newContinuationId, defaultWaitExpiry } = await Promise.resolve().then(() => __importStar(require('./external-events.js')));
|
|
1781
1916
|
const isDelegate = /delegate/i.test(String(tool || ''));
|
|
1782
|
-
await bindExternalWait(this.
|
|
1783
|
-
requestId: this.pendingExternalRequestId || `${this.
|
|
1784
|
-
continuationId: newContinuationId(this.
|
|
1917
|
+
await bindExternalWait(this.actor.state.goalBinding, {
|
|
1918
|
+
requestId: this.pendingExternalRequestId || `${this.actor.state.activeRun}:${Date.now().toString(36)}`,
|
|
1919
|
+
continuationId: newContinuationId(this.actor.state.goalBinding),
|
|
1785
1920
|
expectedSource: isDelegate ? 'delegate' : 'p2p',
|
|
1786
1921
|
expectedEvent: 'result',
|
|
1787
1922
|
createdAt: new Date().toISOString(),
|
|
@@ -1791,31 +1926,31 @@ ${await this.renderActivePlansSection()}
|
|
|
1791
1926
|
}
|
|
1792
1927
|
}
|
|
1793
1928
|
catch (e) {
|
|
1794
|
-
await (0, run_store_js_1.recordDegradation)({ kind: 'core', op: 'pi-sdk.bindExternalWait', runId: this.
|
|
1929
|
+
await (0, run_store_js_1.recordDegradation)({ kind: 'core', op: 'pi-sdk.bindExternalWait', runId: this.actor.state.activeRun, message: String(e?.message || e) }).catch(() => { });
|
|
1795
1930
|
}
|
|
1796
1931
|
return;
|
|
1797
1932
|
}
|
|
1798
1933
|
// 鉴权类: 不重试, 直接交人
|
|
1799
1934
|
if (cls === 'auth') {
|
|
1800
1935
|
this.breakerReason = `鉴权类错误不重试 (${tool}): ${errorText.slice(0, 120)}`;
|
|
1801
|
-
await this.safeSetRunStatus(this.
|
|
1936
|
+
await this.safeSetRunStatus(this.actor.state.activeRun, 'needs_human');
|
|
1802
1937
|
return;
|
|
1803
1938
|
}
|
|
1804
1939
|
// 重复失败熔断: 同一工具 + 同一组参数连续失败达 3 次
|
|
1805
1940
|
if (repeats >= MAX_SAME_TOOL_FAILURES) {
|
|
1806
1941
|
this.breakerReason = `同一工具 ${tool} 连续失败 ${repeats} 次 (${cls}) → 熔断, 不再重试`;
|
|
1807
|
-
await this.safeSetRunStatus(this.
|
|
1942
|
+
await this.safeSetRunStatus(this.actor.state.activeRun, 'needs_human');
|
|
1808
1943
|
}
|
|
1809
1944
|
}
|
|
1810
1945
|
getRunId() {
|
|
1811
|
-
return this.
|
|
1946
|
+
return this.actor.state.activeRun;
|
|
1812
1947
|
}
|
|
1813
1948
|
/**
|
|
1814
1949
|
* 2026-09-16 (M2-B): **上一次**运行的 runId —— prompt 收尾时 `currentRunId` 会被清空,
|
|
1815
1950
|
* 而 Supervisor / 控制面要在运行结束后才知道"刚才跑的是哪条 run" (否则会拿旧 run 做决策)。
|
|
1816
1951
|
*/
|
|
1817
1952
|
getLastRunId() {
|
|
1818
|
-
return this.lastRunId || this.
|
|
1953
|
+
return this.lastRunId || this.actor.state.activeRun;
|
|
1819
1954
|
}
|
|
1820
1955
|
/**
|
|
1821
1956
|
* 2026-09-16 (M2): 从 checkpoint 恢复一次运行 —— **不是**重发原 prompt。
|
|
@@ -1862,7 +1997,7 @@ ${await this.renderActivePlansSection()}
|
|
|
1862
1997
|
}
|
|
1863
1998
|
this.resumeRunId = runId;
|
|
1864
1999
|
this.resumePlan = prep.plan;
|
|
1865
|
-
this.
|
|
2000
|
+
this.actor.state.goalBinding = prep.plan.goalId || this.actor.state.goalBinding;
|
|
1866
2001
|
try {
|
|
1867
2002
|
const reply = await this.prompt((0, run_store_js_1.buildResumeInstruction)(prep.plan), {});
|
|
1868
2003
|
return { ok: true, reply, ...(modelDrift ? { modelDrift } : {}), ...(modelApplied ? { modelApplied } : {}) };
|
|
@@ -1881,8 +2016,8 @@ ${await this.renderActivePlansSection()}
|
|
|
1881
2016
|
// pre-tool-validator 4 步链 (modeGate/blacklist/shell-guard/schema), 经 human-value-pipeline 包装
|
|
1882
2017
|
preToolUse: async (o) => (0, human_value_pipeline_js_1.onPreToolUse)({ tool: o.tool, args: o.args, permissionMode: o.permissionMode }),
|
|
1883
2018
|
// 事件写 Run: 观测级 (记账失败不改变已做出的决策)
|
|
1884
|
-
events: (e) => { if (this.
|
|
1885
|
-
void (0, run_store_js_1.recordHarnessEvent)(this.
|
|
2019
|
+
events: (e) => { if (this.actor.state.activeRun)
|
|
2020
|
+
void (0, run_store_js_1.recordHarnessEvent)(this.actor.state.activeRun, e); },
|
|
1886
2021
|
});
|
|
1887
2022
|
}
|
|
1888
2023
|
return this._harness;
|
|
@@ -1890,10 +2025,10 @@ ${await this.renderActivePlansSection()}
|
|
|
1890
2025
|
/** 每个生命周期事件都带上的运行身份 (runId / goalId / agentId / channelId / surface) */
|
|
1891
2026
|
harnessCtx() {
|
|
1892
2027
|
return {
|
|
1893
|
-
runId: this.
|
|
1894
|
-
goalId: this.
|
|
1895
|
-
agentId: this.
|
|
1896
|
-
channelId: this.
|
|
2028
|
+
runId: this.actor.state.activeRun || undefined,
|
|
2029
|
+
goalId: this.actor.state.goalBinding || undefined,
|
|
2030
|
+
agentId: this.actor.state.agentId || undefined,
|
|
2031
|
+
channelId: this.actor.state.channelId || undefined,
|
|
1897
2032
|
surface: this.runSurface,
|
|
1898
2033
|
};
|
|
1899
2034
|
}
|
|
@@ -1960,26 +2095,26 @@ ${await this.renderActivePlansSection()}
|
|
|
1960
2095
|
this.awaitingExternal = false;
|
|
1961
2096
|
if (this.resumeRunId) {
|
|
1962
2097
|
// ── 恢复模式: 复用原来的 runId, 不新建 run (历史保留) ──
|
|
1963
|
-
this.
|
|
2098
|
+
this.actor.state.activeRun = this.resumeRunId;
|
|
1964
2099
|
this.lastRunId = this.resumeRunId;
|
|
1965
2100
|
try {
|
|
1966
|
-
await (0, run_store_js_1.markRunRunning)(this.
|
|
2101
|
+
await (0, run_store_js_1.markRunRunning)(this.actor.state.activeRun);
|
|
1967
2102
|
}
|
|
1968
2103
|
catch (err) {
|
|
1969
2104
|
runPersistenceFailure = `恢复时状态迁移失败 (recovering → running): ${String(err?.message || err).slice(0, 180)}`;
|
|
1970
2105
|
}
|
|
1971
2106
|
const doneN = this.resumePlan?.completedSteps.length ?? 0;
|
|
1972
2107
|
const guards = this.resumePlan?.replayGuards.length ?? 0;
|
|
1973
|
-
onStream?.({ type: 'status', internal: true, content: `♻️ 从 checkpoint 恢复运行 ${this.
|
|
2108
|
+
onStream?.({ type: 'status', internal: true, content: `♻️ 从 checkpoint 恢复运行 ${this.actor.state.activeRun} (已完成 ${doneN} 步, 非幂等重放守卫 ${guards} 条)`, tool: 'harness' });
|
|
1974
2109
|
}
|
|
1975
2110
|
else {
|
|
1976
2111
|
// 2026-09-16 (M2): 目标绑定 —— 有 goalId 就在该 Goal 下执行; 没有就建 Goal 再建 Run。
|
|
1977
2112
|
// 延续规则 (确定性, 不靠猜): 该 channel/agent 上已有 open/active Goal, 且它的上一次执行**没收尾**
|
|
1978
2113
|
// (interrupted/stalled/needs_human/paused/awaiting_external/recovering) → 继续该 Goal; 否则新建。
|
|
1979
|
-
let boundGoalId = this.
|
|
2114
|
+
let boundGoalId = this.actor.state.goalBinding;
|
|
1980
2115
|
if (!boundGoalId) {
|
|
1981
2116
|
try {
|
|
1982
|
-
const active = await (0, goal_store_js_1.findActiveGoal)({ channelId: this.
|
|
2117
|
+
const active = await (0, goal_store_js_1.findActiveGoal)({ channelId: this.actor.state.channelId || undefined, agentId: this.actor.state.agentId || undefined });
|
|
1983
2118
|
if (active?.currentRunId) {
|
|
1984
2119
|
const prev = await (0, run_store_js_1.readRun)(active.currentRunId);
|
|
1985
2120
|
const unfinished = prev && ['interrupted', 'stalled', 'needs_human', 'paused', 'awaiting_external', 'recovering'].includes(prev.status);
|
|
@@ -1994,8 +2129,8 @@ ${await this.renderActivePlansSection()}
|
|
|
1994
2129
|
try {
|
|
1995
2130
|
const g = await (0, goal_store_js_1.createGoal)({
|
|
1996
2131
|
objective: this.currentUserInput || '(未记录目标)',
|
|
1997
|
-
channelId: this.
|
|
1998
|
-
agentId: this.
|
|
2132
|
+
channelId: this.actor.state.channelId || undefined,
|
|
2133
|
+
agentId: this.actor.state.agentId || undefined,
|
|
1999
2134
|
createdBy: this.runSurface,
|
|
2000
2135
|
});
|
|
2001
2136
|
boundGoalId = g.goalId;
|
|
@@ -2004,25 +2139,25 @@ ${await this.renderActivePlansSection()}
|
|
|
2004
2139
|
console.warn('[PiAgent] 创建 Goal 失败 (无目标也要有运行记录):', err?.message);
|
|
2005
2140
|
}
|
|
2006
2141
|
}
|
|
2007
|
-
this.
|
|
2142
|
+
this.actor.state.goalBinding = boundGoalId;
|
|
2008
2143
|
}
|
|
2009
2144
|
try {
|
|
2010
2145
|
const rec = await (0, run_store_js_1.startRun)({
|
|
2011
2146
|
surface: this.runSurface,
|
|
2012
2147
|
goal: this.currentUserInput || '(未记录目标)',
|
|
2013
2148
|
goalId: boundGoalId || undefined,
|
|
2014
|
-
channelId: this.
|
|
2015
|
-
agentId: this.
|
|
2149
|
+
channelId: this.actor.state.channelId || undefined,
|
|
2150
|
+
agentId: this.actor.state.agentId || undefined,
|
|
2016
2151
|
modelConfig: await this.runModelSnapshot(),
|
|
2017
2152
|
});
|
|
2018
|
-
this.
|
|
2153
|
+
this.actor.state.activeRun = rec.runId;
|
|
2019
2154
|
this.lastRunId = rec.runId;
|
|
2020
|
-
this.
|
|
2021
|
-
if (this.
|
|
2155
|
+
this.actor.state.goalBinding = rec.goalId || this.actor.state.goalBinding;
|
|
2156
|
+
if (this.actor.state.goalBinding) {
|
|
2022
2157
|
// Run → Goal 反查链: runId → goalId → objective / success criteria
|
|
2023
|
-
await (0, goal_store_js_1.attachRun)(this.
|
|
2158
|
+
await (0, goal_store_js_1.attachRun)(this.actor.state.goalBinding, rec.runId).catch((err) => console.warn('[PiAgent] attachRun 失败:', err?.message));
|
|
2024
2159
|
}
|
|
2025
|
-
onStream?.({ type: 'status', internal: true, content: `🧷 运行已登记 (run=${rec.runId}${this.
|
|
2160
|
+
onStream?.({ type: 'status', internal: true, content: `🧷 运行已登记 (run=${rec.runId}${this.actor.state.goalBinding ? `, goal=${this.actor.state.goalBinding}` : ''}, 预算 ${rec.budget.maxSteps} 步 / ${Math.round(rec.budget.deadlineMs / 60000)} 分钟)`, tool: 'harness' });
|
|
2026
2161
|
}
|
|
2027
2162
|
catch (err) {
|
|
2028
2163
|
// 核心写失败: 不再 warn 后继续 —— 没有运行记录就不执行 (strict 模式默认如此)
|
|
@@ -2052,18 +2187,18 @@ ${await this.renderActivePlansSection()}
|
|
|
2052
2187
|
break;
|
|
2053
2188
|
}
|
|
2054
2189
|
// 2026-09-16: 预算闸门 (持久化 harness 的约束面) —— 到点必须**如实**终止, 不许静默算完成
|
|
2055
|
-
if (this.
|
|
2190
|
+
if (this.actor.state.activeRun) {
|
|
2056
2191
|
try {
|
|
2057
|
-
const rec = await (0, run_store_js_1.readRun)(this.
|
|
2192
|
+
const rec = await (0, run_store_js_1.readRun)(this.actor.state.activeRun);
|
|
2058
2193
|
if (!rec)
|
|
2059
|
-
throw new Error(`运行记录读不到: ${this.
|
|
2194
|
+
throw new Error(`运行记录读不到: ${this.actor.state.activeRun}`);
|
|
2060
2195
|
// 2026-09-16 (M5): 外部控制面 (CLI /pause /abort, Web API) 改过状态 → 如实停在那儿。
|
|
2061
2196
|
// 不覆盖成 done/failed: 人按下暂停就是暂停, 人按下中止就是中止。
|
|
2062
2197
|
if (rec.status === 'paused' || rec.status === 'aborted') {
|
|
2063
2198
|
runExternallyPaused = rec.status === 'paused';
|
|
2064
2199
|
runExternallyAborted = rec.status === 'aborted';
|
|
2065
2200
|
runStopReason = `外部请求: ${rec.status}`;
|
|
2066
|
-
onStream?.({ type: 'error', content: `⏹️ 运行被外部${rec.status === 'paused' ? '暂停' : '中止'} (run=${this.
|
|
2201
|
+
onStream?.({ type: 'error', content: `⏹️ 运行被外部${rec.status === 'paused' ? '暂停' : '中止'} (run=${this.actor.state.activeRun})`, tool: 'harness' });
|
|
2067
2202
|
finalResponse = finalResponse || `(运行已${rec.status === 'paused' ? '暂停' : '中止'})`;
|
|
2068
2203
|
break;
|
|
2069
2204
|
}
|
|
@@ -2115,14 +2250,14 @@ ${await this.renderActivePlansSection()}
|
|
|
2115
2250
|
if (totalToolCallsThisLoop >= MAX_TOOL_CALLS_PER_LOOP) {
|
|
2116
2251
|
console.warn(`[PiAgent] 单轮工具调用已达 ${MAX_TOOL_CALLS_PER_LOOP}, 注入 hint 让 LLM 总结`);
|
|
2117
2252
|
onStream?.({ type: 'error', content: `⏹️ 工具调用已达上限 (${MAX_TOOL_CALLS_PER_LOOP}), 请基于已有结果回答`, tool: 'loop' });
|
|
2118
|
-
this.
|
|
2253
|
+
this.pushHistory({ role: 'system', content: `[注意] 你已连续调用 ${MAX_TOOL_CALLS_PER_LOOP} 次工具。请基于已有结果直接回答用户, 不要再次调用任何工具。在回答末尾加 <final gen> 标记结束。` });
|
|
2119
2254
|
totalToolCallsThisLoop = 0; // 重置计数器, 只防连续死循环
|
|
2120
2255
|
}
|
|
2121
2256
|
if (lastNTools.length >= MAX_IDEMPOTENT_TOOL && new Set(lastNTools).size === 1) {
|
|
2122
2257
|
const repeatedTool = lastNTools[0];
|
|
2123
2258
|
console.warn(`[PiAgent] 同工具 ${repeatedTool} 连续成功调 ${MAX_IDEMPOTENT_TOOL} 次, 注入 hint 让 LLM 总结`);
|
|
2124
2259
|
onStream?.({ type: 'error', content: `⏹️ 工具 ${repeatedTool} 重复调用 ${MAX_IDEMPOTENT_TOOL} 次, 请基于已有结果回答`, tool: 'loop' });
|
|
2125
|
-
this.
|
|
2260
|
+
this.pushHistory({ role: 'system', content: `[注意] 你已连续 ${MAX_IDEMPOTENT_TOOL} 次调用 ${repeatedTool}。请基于已有结果直接回答用户, 不要再次调用任何工具。在回答末尾加 <final gen> 标记结束。` });
|
|
2126
2261
|
lastNTools.length = 0; // 重置计数器
|
|
2127
2262
|
// 不 break — 让 LLM 在下一轮用已有信息回答
|
|
2128
2263
|
}
|
|
@@ -2325,7 +2460,7 @@ ${await this.renderActivePlansSection()}
|
|
|
2325
2460
|
break;
|
|
2326
2461
|
}
|
|
2327
2462
|
// 把错误当成 tool 结果 push 进 history, 这样下一轮 LLM 看到错误能调整
|
|
2328
|
-
this.
|
|
2463
|
+
this.pushHistory({
|
|
2329
2464
|
role: 'system',
|
|
2330
2465
|
content: `[Loop 错误恢复 ${totalErrors}/${this.MAX_TOTAL_ERRORS}] ${aiFailureReason}\n\n请基于上轮工具结果继续完成任务, 不要重复调用同一失败操作. 如果工具已成功执行, 请基于 result.output 给用户总结; 如果工具失败, 请换其他方式或重试.`
|
|
2331
2466
|
});
|
|
@@ -2398,7 +2533,7 @@ ${await this.renderActivePlansSection()}
|
|
|
2398
2533
|
}
|
|
2399
2534
|
if (toolCalls.length > 0) {
|
|
2400
2535
|
// 把原始 LLM 回复 push 进 history (仅一次)
|
|
2401
|
-
this.
|
|
2536
|
+
this.pushHistory({
|
|
2402
2537
|
role: 'assistant',
|
|
2403
2538
|
content: reply,
|
|
2404
2539
|
toolCalls: toolCalls.length > 1 ? toolCalls : [toolCalls[0]],
|
|
@@ -2428,12 +2563,12 @@ ${await this.renderActivePlansSection()}
|
|
|
2428
2563
|
consecutiveErrors++;
|
|
2429
2564
|
totalErrors++;
|
|
2430
2565
|
const errorResult = { success: false, error: `未知工具: ${toolCall.name}` };
|
|
2431
|
-
this.
|
|
2566
|
+
this.pushHistory({ role: 'tool', content: JSON.stringify(errorResult), toolResult: errorResult });
|
|
2432
2567
|
this.logToHarness(toolCall.name, toolCall.args, errorResult);
|
|
2433
2568
|
// 2026-07-28: 注入 Reflection 帮助 LLM 理解错误
|
|
2434
2569
|
const obs = (0, error_classifier_js_1.buildObservation)(toolCall.name, toolCall.args, errorResult);
|
|
2435
2570
|
const ref = (0, error_classifier_js_1.buildReflection)(toolCall.name, errorResult.error, totalErrors, lastFailedToolCount);
|
|
2436
|
-
this.
|
|
2571
|
+
this.pushHistory({ role: 'system', content: (0, error_classifier_js_1.formatObservationWithReflection)(obs, ref) });
|
|
2437
2572
|
if (onStream)
|
|
2438
2573
|
onStream({ type: 'status', content: `💡 Reflection: ${obs.summary}`, tool: 'system' });
|
|
2439
2574
|
console.warn(`[PiAgent] 未知工具: ${toolCall.name} (累计 ${totalErrors}/${this.MAX_TOTAL_ERRORS}),跳过并继续`);
|
|
@@ -2467,7 +2602,7 @@ ${await this.renderActivePlansSection()}
|
|
|
2467
2602
|
consecutiveErrors++;
|
|
2468
2603
|
totalErrors++;
|
|
2469
2604
|
const denyResultMsg = { success: false, error: `拒绝: [${toolDecision.rejectedBy || 'deny-pipeline'}] ${toolDecision.reason}` };
|
|
2470
|
-
this.
|
|
2605
|
+
this.pushHistory({ role: 'tool', content: JSON.stringify(denyResultMsg), toolResult: denyResultMsg });
|
|
2471
2606
|
this.logToHarness(toolCall.name, toolCall.args, denyResultMsg);
|
|
2472
2607
|
return;
|
|
2473
2608
|
}
|
|
@@ -2479,7 +2614,7 @@ ${await this.renderActivePlansSection()}
|
|
|
2479
2614
|
? `Harness gate 拒绝 (${toolDecision.rejectedBy}): ${toolDecision.reason || '未通过安全校验'}`
|
|
2480
2615
|
: `PreToolUse 拒绝: ${toolDecision.reason || '未通过安全校验'}`,
|
|
2481
2616
|
};
|
|
2482
|
-
this.
|
|
2617
|
+
this.pushHistory({ role: 'tool', content: JSON.stringify(deniedResult), toolResult: deniedResult });
|
|
2483
2618
|
this.logToHarness(toolCall.name, toolCall.args, deniedResult);
|
|
2484
2619
|
if (onStream) {
|
|
2485
2620
|
const gateName = isGateDeny ? `Harness ${toolDecision.rejectedBy}` : (isHarnessError ? '核心约束层' : 'PreToolUse');
|
|
@@ -2508,13 +2643,13 @@ ${await this.renderActivePlansSection()}
|
|
|
2508
2643
|
? `[注意] 连续 ${consecutiveErrors} 次工具调用因约束层失效被阻止. 请换其他工具或直接回答用户, 末尾加 <final gen>.`
|
|
2509
2644
|
: `[注意] 连续 ${consecutiveErrors} 次工具调用被系统拒绝. 请换其他工具或直接回答用户, 末尾加 <final gen>.`;
|
|
2510
2645
|
if (lastFailedToolCount >= MAX_SAME_TOOL_FAILURES) {
|
|
2511
|
-
this.
|
|
2646
|
+
this.pushHistory({ role: 'system', content: systemMaxMsg });
|
|
2512
2647
|
lastFailedTool = '';
|
|
2513
2648
|
lastFailedToolCount = 0;
|
|
2514
2649
|
consecutiveErrors = 0;
|
|
2515
2650
|
}
|
|
2516
2651
|
else if (consecutiveErrors >= MAX_CONSECUTIVE_ERRORS) {
|
|
2517
|
-
this.
|
|
2652
|
+
this.pushHistory({ role: 'system', content: systemConsecMsg });
|
|
2518
2653
|
consecutiveErrors = 0;
|
|
2519
2654
|
}
|
|
2520
2655
|
return;
|
|
@@ -2548,18 +2683,18 @@ ${await this.renderActivePlansSection()}
|
|
|
2548
2683
|
}
|
|
2549
2684
|
console.log(`[PiAgent] 工具 ${toolCall.name} 执行完成: success=${result.success} (${toolDurationMs}ms)`);
|
|
2550
2685
|
// 2026-09-16 (M3): 失败接线 —— 分类 + recovery 留痕 + 熔断 / 外部等待
|
|
2551
|
-
if (!result.success && this.
|
|
2686
|
+
if (!result.success && this.actor.state.activeRun && !replaySkip) {
|
|
2552
2687
|
await this.wireToolFailure(toolCall.name, String(result.error || ''), toolCall.args);
|
|
2553
2688
|
}
|
|
2554
|
-
else if (result.success && this.
|
|
2689
|
+
else if (result.success && this.actor.state.activeRun && this.awaitingExternal) {
|
|
2555
2690
|
// 外部回话了: awaiting_external → running (不是"恢复完成", 只是等待结束)
|
|
2556
2691
|
this.awaitingExternal = false;
|
|
2557
|
-
await this.safeSetRunStatus(this.
|
|
2692
|
+
await this.safeSetRunStatus(this.actor.state.activeRun, 'running');
|
|
2558
2693
|
}
|
|
2559
2694
|
// 2026-09-16: 持久化 run harness — 每步工具调用立即落盘 (崩在这里也能看到做到哪步)
|
|
2560
|
-
if (this.
|
|
2695
|
+
if (this.actor.state.activeRun) {
|
|
2561
2696
|
try {
|
|
2562
|
-
await (0, run_store_js_1.recordStep)(this.
|
|
2697
|
+
await (0, run_store_js_1.recordStep)(this.actor.state.activeRun, {
|
|
2563
2698
|
tool: toolCall.name,
|
|
2564
2699
|
ok: !!result.success,
|
|
2565
2700
|
ms: toolDurationMs,
|
|
@@ -2590,7 +2725,7 @@ ${await this.renderActivePlansSection()}
|
|
|
2590
2725
|
ok: !!result.success,
|
|
2591
2726
|
});
|
|
2592
2727
|
if (after.routeHint?.systemAddition) {
|
|
2593
|
-
this.
|
|
2728
|
+
this.pushHistory({ role: 'system', content: `[Harness Router Hint: ${after.routeHint.reason}]\n${after.routeHint.systemAddition}` });
|
|
2594
2729
|
}
|
|
2595
2730
|
if (after.outputBlocked) {
|
|
2596
2731
|
if (onStream) {
|
|
@@ -2623,14 +2758,14 @@ ${await this.renderActivePlansSection()}
|
|
|
2623
2758
|
}
|
|
2624
2759
|
}
|
|
2625
2760
|
catch { /* 观测失败绝不影响主路径 */ }
|
|
2626
|
-
this.
|
|
2761
|
+
this.pushHistory({ role: 'tool', content: JSON.stringify(result), toolResult: result, toolCallId: toolCall.id || `call_${Date.now()}_${Math.random().toString(36).slice(2, 8)}` });
|
|
2627
2762
|
// 2026-10-01 (用户: 「为什么这么慢」): 量到的事实 —— 每次 LLM 往返平均 3338ms,
|
|
2628
2763
|
// 而"一轮发多个工具"(toolCalls>1) 只占 12/63 ✗ ⇒ 回合时长 ≈ 往返次数 × 3.3 秒 ✓。
|
|
2629
2764
|
// 本回合**第一次**且**只发一个**工具时, 附一句带代价的提醒(不是口号 ✓), 只提一次免得刷屏。
|
|
2630
2765
|
if (toolCalls.length === 1 && this.successfulToolResults.length <= 1) { // 本回合头一次(免得每轮刷屏)
|
|
2631
2766
|
const __batchHint = (0, tool_loop_guard_js_1.batchHint)(toolCall.name, 1);
|
|
2632
2767
|
if (__batchHint)
|
|
2633
|
-
this.
|
|
2768
|
+
this.pushHistory({ role: 'system', content: __batchHint.trim() });
|
|
2634
2769
|
}
|
|
2635
2770
|
this.logToHarness(toolCall.name, toolCall.args, result);
|
|
2636
2771
|
// 2026-08-09: 记录到本轮行动日志 (循环进度 + final 前目标核查用)
|
|
@@ -2742,18 +2877,18 @@ ${await this.renderActivePlansSection()}
|
|
|
2742
2877
|
// 2026-07-28: 注入 Observation + Reflection 替代旧 hardcode 提示
|
|
2743
2878
|
const obs = (0, error_classifier_js_1.buildObservation)(toolCall.name, toolCall.args, { success: false, error: result.error });
|
|
2744
2879
|
const ref = (0, error_classifier_js_1.buildReflection)(toolCall.name, result.error, totalErrors, lastFailedToolCount);
|
|
2745
|
-
this.
|
|
2880
|
+
this.pushHistory({ role: 'system', content: (0, error_classifier_js_1.formatObservationWithReflection)(obs, ref) + SHELL_ESCAPE_HINT });
|
|
2746
2881
|
if (onStream)
|
|
2747
2882
|
onStream({ type: 'status', content: `💡 Reflection: ${obs.summary} → ${ref[0]?.action || '放弃'}`, tool: 'system' });
|
|
2748
2883
|
if (lastFailedToolCount >= MAX_SAME_TOOL_FAILURES) {
|
|
2749
|
-
this.
|
|
2884
|
+
this.pushHistory({ role: 'system', content: `[注意] 工具 ${toolCall.name} 在这个上下文中不可用 (连续 ${MAX_SAME_TOOL_FAILURES} 次失败: ${result.error}). 请不要再次调用它, 直接用你已知的信息回答用户, 并在回答开头标记 <final gen>.` });
|
|
2750
2885
|
lastFailedTool = '';
|
|
2751
2886
|
lastFailedToolCount = 0;
|
|
2752
2887
|
consecutiveErrors = 0;
|
|
2753
2888
|
return;
|
|
2754
2889
|
}
|
|
2755
2890
|
if (consecutiveErrors >= MAX_CONSECUTIVE_ERRORS) {
|
|
2756
|
-
this.
|
|
2891
|
+
this.pushHistory({ role: 'system', content: `[注意] 前面的工具调用连续失败。请尝试其他工具或换一种方式完成用户请求, 或用 <final gen> 给出最终回答.` });
|
|
2757
2892
|
consecutiveErrors = 0;
|
|
2758
2893
|
}
|
|
2759
2894
|
}
|
|
@@ -2762,11 +2897,11 @@ ${await this.renderActivePlansSection()}
|
|
|
2762
2897
|
consecutiveErrors++;
|
|
2763
2898
|
totalErrors++;
|
|
2764
2899
|
const errorResult = { success: false, error: String(execError) };
|
|
2765
|
-
this.
|
|
2900
|
+
this.pushHistory({ role: 'tool', content: JSON.stringify(errorResult), toolResult: errorResult });
|
|
2766
2901
|
this.logToHarness(toolCall.name, toolCall.args, errorResult);
|
|
2767
2902
|
const obs = (0, error_classifier_js_1.buildObservation)(toolCall.name, toolCall.args, errorResult);
|
|
2768
2903
|
const ref = (0, error_classifier_js_1.buildReflection)(toolCall.name, errorResult.error, totalErrors, lastFailedToolCount);
|
|
2769
|
-
this.
|
|
2904
|
+
this.pushHistory({ role: 'system', content: (0, error_classifier_js_1.formatObservationWithReflection)(obs, ref) + SHELL_ESCAPE_HINT });
|
|
2770
2905
|
if (onStream)
|
|
2771
2906
|
onStream({ type: 'status', content: `💡 Reflection: ${obs.summary}`, tool: 'system' });
|
|
2772
2907
|
console.error(`[PiAgent] 工具执行异常 (累计 ${totalErrors}/${this.MAX_TOTAL_ERRORS}): ${execError}`);
|
|
@@ -2777,7 +2912,7 @@ ${await this.renderActivePlansSection()}
|
|
|
2777
2912
|
}
|
|
2778
2913
|
else {
|
|
2779
2914
|
// LLM 返回的不是 tool call 格式
|
|
2780
|
-
this.
|
|
2915
|
+
this.pushHistory({
|
|
2781
2916
|
role: 'assistant',
|
|
2782
2917
|
content: reply,
|
|
2783
2918
|
reasoningContent: response?.reasoningContent,
|
|
@@ -2806,7 +2941,7 @@ ${await this.renderActivePlansSection()}
|
|
|
2806
2941
|
unreportedRetries++;
|
|
2807
2942
|
const unreported = this.successfulToolResults.length;
|
|
2808
2943
|
console.log(`[PiAgent] LLM 想 final_gen 但还有 ${unreported} 个工具结果未汇报 (${unreportedRetries}/${MAX_UNREPORTED_RETRIES}), push hint 让其继续`);
|
|
2809
|
-
this.
|
|
2944
|
+
this.pushHistory({
|
|
2810
2945
|
role: 'system',
|
|
2811
2946
|
content: `[dive-into stop condition] 你之前已成功执行了 ${unreported} 个工具, 但当前回复里没把它们的结果告诉用户. 请基于已有的工具结果 (在 history 里) 写一个完整总结回复给用户, 用 <final gen> 结尾. 不要再调工具.`
|
|
2812
2947
|
});
|
|
@@ -2819,7 +2954,7 @@ ${await this.renderActivePlansSection()}
|
|
|
2819
2954
|
// 反复提示仍未汇报超过上限 → 清空积压强制 final, 不再死循环
|
|
2820
2955
|
console.log(`[PiAgent] unreported 循环超限 (${unreportedRetries} 次), 清空积压强制 final`);
|
|
2821
2956
|
this.successfulToolResults = [];
|
|
2822
|
-
this.
|
|
2957
|
+
this.pushHistory({
|
|
2823
2958
|
role: 'system',
|
|
2824
2959
|
content: `[dive-into stop condition] 已多次提示汇报工具结果仍未完成 (超过 ${MAX_UNREPORTED_RETRIES} 次). 现在直接基于你已知的信息写最终回复给用户, 用 <final gen> 结尾, 不要再调任何工具.`
|
|
2825
2960
|
});
|
|
@@ -2831,7 +2966,7 @@ ${await this.renderActivePlansSection()}
|
|
|
2831
2966
|
// 2026-07-29: 质量门 — 即使 LLM 声称完成, 质量太低也继续
|
|
2832
2967
|
if (lastQualityScore < this.QUALITY_THRESHOLD && refineAttempts < this.MAX_REFINE_ATTEMPTS) {
|
|
2833
2968
|
console.log(`[PiAgent] final gen 质量 ${lastQualityScore.toFixed(2)} < ${this.QUALITY_THRESHOLD}, 注入 refine hint`);
|
|
2834
|
-
this.
|
|
2969
|
+
this.pushHistory({ role: 'system', content: `[质量检查] 你的回答质量评分为 ${(lastQualityScore * 10).toFixed(1)}/10, 低于 ${(this.QUALITY_THRESHOLD * 10).toFixed(1)}/10 阈值。请提供更完整、详细的回答, 包含工具调用获取到的具体信息, 末尾加 <final gen>。` });
|
|
2835
2970
|
refineAttempts++;
|
|
2836
2971
|
continue;
|
|
2837
2972
|
}
|
|
@@ -2846,13 +2981,13 @@ ${await this.renderActivePlansSection()}
|
|
|
2846
2981
|
userIntent: this.currentUserInput,
|
|
2847
2982
|
completedTools: Array.from(loopReviewCompletedTools),
|
|
2848
2983
|
actionLog: loopActionLog,
|
|
2849
|
-
runId: this.
|
|
2850
|
-
goalId: this.
|
|
2984
|
+
runId: this.actor.state.activeRun || undefined,
|
|
2985
|
+
goalId: this.actor.state.goalBinding || undefined,
|
|
2851
2986
|
}, loop_review_js_1.DEFAULT_MAX_REVIEWS);
|
|
2852
2987
|
if (reviewDecision.kind === 'continue-review') {
|
|
2853
2988
|
loopReviewCount++;
|
|
2854
2989
|
console.log(`[PiAgent] review ${loopReviewCount}/${loop_review_js_1.DEFAULT_MAX_REVIEWS}: LLM 想 final 但先对齐需求深挖一次`);
|
|
2855
|
-
this.
|
|
2990
|
+
this.pushHistory({ role: 'system', content: reviewDecision.hint });
|
|
2856
2991
|
if (onStream) {
|
|
2857
2992
|
onStream({ type: 'status', internal: true, content: `🔄 目标对齐 review ${loopReviewCount}/${loop_review_js_1.DEFAULT_MAX_REVIEWS}: 深挖续跑`, tool: 'system' });
|
|
2858
2993
|
}
|
|
@@ -2905,16 +3040,16 @@ ${await this.renderActivePlansSection()}
|
|
|
2905
3040
|
if (onStream) {
|
|
2906
3041
|
onStream({ type: 'status', internal: true, content: `✅ 处理完成,共 ${iteration - 1} 次循环`, tool: 'system' });
|
|
2907
3042
|
}
|
|
2908
|
-
this.
|
|
3043
|
+
this.pushHistory({ role: 'assistant', content: finalResponse });
|
|
2909
3044
|
// React Harness: 循环结束
|
|
2910
3045
|
// 2026-09-16 (Milestone 1-B): 会话收尾走唯一门面
|
|
2911
3046
|
await this.piHarness().sessionEnd(this.harnessCtx());
|
|
2912
3047
|
// 2026-09-16: 收尾落盘 — done / failed / aborted / needs_human 如实写回 (不留幽灵 running)
|
|
2913
|
-
if (this.
|
|
3048
|
+
if (this.actor.state.activeRun && !runExternallyPaused && !runExternallyAborted) {
|
|
2914
3049
|
try {
|
|
2915
3050
|
// 2026-09-16 (M4): 完成门 —— "模型说完成"不等于"系统确认完成"。
|
|
2916
3051
|
// 证据 = 成功步骤的事实摘要; 末尾还有失败步骤没被后续成功覆盖 → 不许 done。
|
|
2917
|
-
const recBefore = await (0, run_store_js_1.readRun)(this.
|
|
3052
|
+
const recBefore = await (0, run_store_js_1.readRun)(this.actor.state.activeRun).catch(() => null);
|
|
2918
3053
|
const steps = recBefore?.steps || [];
|
|
2919
3054
|
const evidence = steps.filter((s) => s.ok).map((s) => `${s.tool}: ${(s.summary || '').slice(0, 120)}`).slice(-10);
|
|
2920
3055
|
const lastStep = steps[steps.length - 1];
|
|
@@ -2935,7 +3070,7 @@ ${await this.renderActivePlansSection()}
|
|
|
2935
3070
|
: (trailingFailure || noEvidence)
|
|
2936
3071
|
? 'failed'
|
|
2937
3072
|
: 'done';
|
|
2938
|
-
await (0, run_store_js_1.finishRun)(this.
|
|
3073
|
+
await (0, run_store_js_1.finishRun)(this.actor.state.activeRun, {
|
|
2939
3074
|
status,
|
|
2940
3075
|
summary: finalResponse ? String(finalResponse).slice(0, 400) : undefined,
|
|
2941
3076
|
error: errText
|
|
@@ -2948,7 +3083,7 @@ ${await this.renderActivePlansSection()}
|
|
|
2948
3083
|
// 终态也写不下去: 这是最坏情况 —— 除了留痕, 没有别的自愈手段, 所以必须显眼
|
|
2949
3084
|
const message = `收尾落盘失败 (状态无法写回, 记录会停在 running): ${String(err?.message || err).slice(0, 200)}`;
|
|
2950
3085
|
console.error('[PiAgent] run-store finishRun 失败 (核心持久化):', message);
|
|
2951
|
-
await (0, run_store_js_1.recordDegradation)({ kind: 'core', op: 'pi-sdk.finishRun', runId: this.
|
|
3086
|
+
await (0, run_store_js_1.recordDegradation)({ kind: 'core', op: 'pi-sdk.finishRun', runId: this.actor.state.activeRun, message }).catch(() => { });
|
|
2952
3087
|
onStream?.({ type: 'error', content: `⚠️ ${message}`, tool: 'harness' });
|
|
2953
3088
|
}
|
|
2954
3089
|
// ★ 2026-09-25 (M0 接线冻结, 规则 ③ + ④): Run 结束**必过收尾漏斗**。
|
|
@@ -2960,14 +3095,14 @@ ${await this.renderActivePlansSection()}
|
|
|
2960
3095
|
//
|
|
2961
3096
|
// 现在: `closeRunOnce` (幂等) → 9 步收尾 → 产物落盘; 再由 Goal reducer 把"下一步是什么"
|
|
2962
3097
|
// 落进 Goal (没有 Supervisor 宿主时 Runner 也要留下 continuation, 否则下一次还是从零开始)。
|
|
2963
|
-
if (this.
|
|
3098
|
+
if (this.actor.state.goalBinding) {
|
|
2964
3099
|
try {
|
|
2965
3100
|
const wiring = await Promise.resolve().then(() => __importStar(require('./goal-flywheel-wiring.js')));
|
|
2966
3101
|
const { reduceGoalState } = await Promise.resolve().then(() => __importStar(require('./goal-state-reducer.js')));
|
|
2967
3102
|
const nowIso = new Date().toISOString();
|
|
2968
3103
|
const closed = await wiring.closeRunOnce({
|
|
2969
|
-
goalId: this.
|
|
2970
|
-
runId: this.
|
|
3104
|
+
goalId: this.actor.state.goalBinding,
|
|
3105
|
+
runId: this.actor.state.activeRun,
|
|
2971
3106
|
caller: 'runner',
|
|
2972
3107
|
now: nowIso,
|
|
2973
3108
|
finalReview: finalResponse ? String(finalResponse).slice(0, 2000) : '',
|
|
@@ -2978,7 +3113,7 @@ ${await this.renderActivePlansSection()}
|
|
|
2978
3113
|
else if (closed.outcome) {
|
|
2979
3114
|
const view = closed.outcome;
|
|
2980
3115
|
const applied = await reduceGoalState({
|
|
2981
|
-
goalId: this.
|
|
3116
|
+
goalId: this.actor.state.goalBinding,
|
|
2982
3117
|
intent: 'closure_outcome',
|
|
2983
3118
|
now: nowIso,
|
|
2984
3119
|
by: 'runner',
|
|
@@ -2995,7 +3130,7 @@ ${await this.renderActivePlansSection()}
|
|
|
2995
3130
|
onStream?.({
|
|
2996
3131
|
type: 'status', internal: true,
|
|
2997
3132
|
content: `${line}${applied.gateRejected ? ` [完成门拒绝: ${applied.gateRejected}]` : ''} `
|
|
2998
|
-
+ `(收尾 ${view.steps} 步 · memory ${view.memories} · skill 候选 ${view.candidates} · goal=${this.
|
|
3133
|
+
+ `(收尾 ${view.steps} 步 · memory ${view.memories} · skill 候选 ${view.candidates} · goal=${this.actor.state.goalBinding})`,
|
|
2999
3134
|
tool: 'harness',
|
|
3000
3135
|
});
|
|
3001
3136
|
}
|
|
@@ -3005,15 +3140,15 @@ ${await this.renderActivePlansSection()}
|
|
|
3005
3140
|
}
|
|
3006
3141
|
}
|
|
3007
3142
|
catch (err) {
|
|
3008
|
-
await (0, run_store_js_1.recordDegradation)({ kind: 'observational', op: 'pi-sdk.runClosure', runId: this.
|
|
3143
|
+
await (0, run_store_js_1.recordDegradation)({ kind: 'observational', op: 'pi-sdk.runClosure', runId: this.actor.state.activeRun, message: String(err?.message || err).slice(0, 160) }).catch(() => { });
|
|
3009
3144
|
}
|
|
3010
3145
|
}
|
|
3011
|
-
this.
|
|
3146
|
+
this.actor.state.activeRun = '';
|
|
3012
3147
|
}
|
|
3013
|
-
else if (this.
|
|
3148
|
+
else if (this.actor.state.activeRun) {
|
|
3014
3149
|
// 外部暂停/中止: 状态是人定的, 不覆盖 (paused 等 /resume; aborted 是终态)
|
|
3015
3150
|
onStream?.({ type: 'status', content: `⏹️ 运行状态保持为 ${runExternallyPaused ? 'paused' : 'aborted'} (由外部控制面决定)`, tool: 'harness' });
|
|
3016
|
-
this.
|
|
3151
|
+
this.actor.state.activeRun = '';
|
|
3017
3152
|
}
|
|
3018
3153
|
// 2026-06-16: 暴露 aiFailed 标志 — promptStream 据此决定是否自动重试整个 loop
|
|
3019
3154
|
return { reply: finalResponse, aiFailed, aiFailureReason: aiFailureReason || undefined };
|
|
@@ -3067,7 +3202,7 @@ ${await this.renderActivePlansSection()}
|
|
|
3067
3202
|
// 失败静默: 任何 stage 抛错 → 走老 slice(-10) 逻辑
|
|
3068
3203
|
//
|
|
3069
3204
|
// P1.2: 如果 maybeAutoCompact 算过 Context Collapse 投影, 用 this.projectedHistory (读时投影, 非破坏)
|
|
3070
|
-
const source = this.projectedHistory ?? this.messageHistory;
|
|
3205
|
+
const source = this.projectedHistory ?? this.actor.state.messageHistory;
|
|
3071
3206
|
const recentMessages = this.compressHistorySync(source).slice(-10);
|
|
3072
3207
|
return recentMessages.map(m => {
|
|
3073
3208
|
if (m.role === 'user')
|
|
@@ -3094,7 +3229,7 @@ ${await this.renderActivePlansSection()}
|
|
|
3094
3229
|
// 2026-08-06: 来源优先用 projectedHistory (Context Collapse 投影, 非破坏) —
|
|
3095
3230
|
// 与 buildContext 一致; 之前只让字符串路径用投影, messages 数组路径被跳过,
|
|
3096
3231
|
// 导致 LLM 实际看到的还是未压缩的历史.
|
|
3097
|
-
const source = this.projectedHistory ?? this.messageHistory;
|
|
3232
|
+
const source = this.projectedHistory ?? this.actor.state.messageHistory;
|
|
3098
3233
|
const WINDOW = 15;
|
|
3099
3234
|
const out = [];
|
|
3100
3235
|
// 2026-09-28 (前缀 KV 可命中): 带 CURRENT TURN 注入的条目要**原样回带**.
|
|
@@ -3173,11 +3308,11 @@ ${await this.renderActivePlansSection()}
|
|
|
3173
3308
|
/**
|
|
3174
3309
|
* 2026-09-28 (前缀 KV 可命中): 把 chat() 回带的当前轮写回自己的 messageHistory.
|
|
3175
3310
|
* 逻辑在模块级 `writeBackCurrentTurnInto` (纯函数, 门可以直接驱它断言);
|
|
3176
|
-
* 这里只负责接到 `
|
|
3311
|
+
* 这里只负责接到 `actor 的 messageHistory` 上, 并保证任何异常都静默 (写回失败不影响对话).
|
|
3177
3312
|
*/
|
|
3178
3313
|
writeBackCurrentTurn(wire) {
|
|
3179
3314
|
try {
|
|
3180
|
-
return writeBackCurrentTurnInto(this.messageHistory, wire);
|
|
3315
|
+
return writeBackCurrentTurnInto(this.actor.state.messageHistory, wire);
|
|
3181
3316
|
}
|
|
3182
3317
|
catch (err) {
|
|
3183
3318
|
console.warn('[PiAgent] writeBackCurrentTurn failed (silent):', err);
|
|
@@ -3191,7 +3326,7 @@ ${await this.renderActivePlansSection()}
|
|
|
3191
3326
|
estimateHistoryTokens() {
|
|
3192
3327
|
try {
|
|
3193
3328
|
const { estimateTokens } = _piRequire('../context-compaction/index.js');
|
|
3194
|
-
return estimateTokens(this.messageHistory);
|
|
3329
|
+
return estimateTokens(this.actor.state.messageHistory);
|
|
3195
3330
|
}
|
|
3196
3331
|
catch {
|
|
3197
3332
|
return 0;
|
|
@@ -3214,8 +3349,10 @@ ${await this.renderActivePlansSection()}
|
|
|
3214
3349
|
console.warn(`[PiAgent] reactive compaction pre-check (${estimated} tokens > 80% threshold)`);
|
|
3215
3350
|
onStream?.({ type: 'status', internal: true, content: '⚠️ reactive compaction 预检触发', tool: 'recovery' });
|
|
3216
3351
|
try {
|
|
3217
|
-
|
|
3218
|
-
|
|
3352
|
+
// 同步压缩: 取快照与替换在**同一拍相邻两行** (中间没有 await) ⇒ 不存在"变换期间被追加"的窗口。
|
|
3353
|
+
// 若哪天这里插入 await, 必须改成 `actor.rebaseHistory(…, snapshotLen)` (见 channel-actor.ts)。
|
|
3354
|
+
const compacted = this.compressHistorySync(this.actor.state.messageHistory);
|
|
3355
|
+
this.replaceHistory(compacted);
|
|
3219
3356
|
if (this.estimateHistoryTokens() > this.maxContextTokens() * 0.8) {
|
|
3220
3357
|
await this.maybeAutoCompact(onStream, signal);
|
|
3221
3358
|
}
|
|
@@ -3271,7 +3408,7 @@ ${await this.renderActivePlansSection()}
|
|
|
3271
3408
|
try {
|
|
3272
3409
|
const rc = String(response?.reasoningContent || '').trim();
|
|
3273
3410
|
if (rc)
|
|
3274
|
-
this.
|
|
3411
|
+
this.runCtx.eventSink?.({ type: 'reasoning', content: rc });
|
|
3275
3412
|
}
|
|
3276
3413
|
catch { /* 送不出去不影响主流程 */ } // 2026-06-30: 透传 toolCalls (OpenAI 协议 native) 给上层, 让 assistant message 能 emit 真 id
|
|
3277
3414
|
return { reply: response.reply || '', toolCalls: response.toolCalls, messages: response.messages };
|
|
@@ -3311,8 +3448,8 @@ ${await this.renderActivePlansSection()}
|
|
|
3311
3448
|
if (Array.isArray(contextOrMessages)) {
|
|
3312
3449
|
contextOrMessages = this.buildContext();
|
|
3313
3450
|
// 也清除最近一轮的 toolCalls, 防止再触发
|
|
3314
|
-
if (this.messageHistory.length > 1) {
|
|
3315
|
-
const last = this.messageHistory[this.messageHistory.length - 1];
|
|
3451
|
+
if (this.actor.state.messageHistory.length > 1) {
|
|
3452
|
+
const last = this.actor.state.messageHistory[this.actor.state.messageHistory.length - 1];
|
|
3316
3453
|
if (last.role === 'assistant' && last.toolCalls) {
|
|
3317
3454
|
delete last.toolCalls;
|
|
3318
3455
|
}
|
|
@@ -3375,7 +3512,7 @@ ${await this.renderActivePlansSection()}
|
|
|
3375
3512
|
* onStream 广播: 跟 computeJudgmentGate 风格一致 (phase 事件供 UI timeline 显示)
|
|
3376
3513
|
*/
|
|
3377
3514
|
async maybeAutoCompact(onStream, signal) {
|
|
3378
|
-
if (this.messageHistory.length < 10)
|
|
3515
|
+
if (this.actor.state.messageHistory.length < 10)
|
|
3379
3516
|
return; // 历史太短, 不值得压
|
|
3380
3517
|
onStream?.({ type: 'status', internal: true, content: '🗜️ 评估是否需要压缩上下文...', tool: 'compactor' });
|
|
3381
3518
|
// 注入 LLM (用 getMinimax().chat, 与 judgment 注入门 / ReAct 循环同一来源)
|
|
@@ -3394,14 +3531,17 @@ ${await this.renderActivePlansSection()}
|
|
|
3394
3531
|
const maxTokens = Math.max(4000, Math.round(cfg.maxTokens * cfg.compressionThreshold));
|
|
3395
3532
|
const beforeTokens = this.estimateHistoryTokens();
|
|
3396
3533
|
const { compactPipeline, isContextCollapseEnabled } = await Promise.resolve().then(() => __importStar(require('../context-compaction/index.js')));
|
|
3397
|
-
|
|
3534
|
+
// **K5 第 4 步**: 记下取快照时的长度 —— 压缩是 async, 从这一拍到 "落地" 之间是 await 窗口;
|
|
3535
|
+
// 期间 append 进来的消息必须由 `actor.rebaseHistory(…, snapshotLen)` 接回去, 否则会被整块替换丢掉。
|
|
3536
|
+
const snapshotLen = this.actor.state.messageHistory.length;
|
|
3537
|
+
const result = await compactPipeline(this.actor.state.messageHistory, {
|
|
3398
3538
|
maxTokens,
|
|
3399
3539
|
llmChat,
|
|
3400
3540
|
collapseLlmChat: llmChat, // P1.2: Context Collapse 投影也用同一 LLM
|
|
3401
|
-
cacheScope: this.
|
|
3541
|
+
cacheScope: this.actor.state.channelId || 'default',
|
|
3402
3542
|
});
|
|
3403
|
-
if (result.compacted && result.history.length < this.messageHistory.length) {
|
|
3404
|
-
const saved = this.messageHistory.length - result.history.length;
|
|
3543
|
+
if (result.compacted && result.history.length < this.actor.state.messageHistory.length) {
|
|
3544
|
+
const saved = this.actor.state.messageHistory.length - result.history.length;
|
|
3405
3545
|
const stagesApplied = result.stages.filter((s) => s.applied).map((s) => s.stage).join(' → ');
|
|
3406
3546
|
const afterTokens = this.estimateHistoryTokens();
|
|
3407
3547
|
const savedTokens = Math.max(0, beforeTokens - afterTokens);
|
|
@@ -3419,7 +3559,17 @@ ${await this.renderActivePlansSection()}
|
|
|
3419
3559
|
// messageHistory 不变 (非破坏)
|
|
3420
3560
|
}
|
|
3421
3561
|
else {
|
|
3422
|
-
|
|
3562
|
+
// 真破坏性更新 —— **K5 第 4 步**: 绑定了 actor 就交它落地 (rebase: 保住变换期间的追加);
|
|
3563
|
+
// 未绑定的会话走原路径 (行为不变)。
|
|
3564
|
+
if (this.actor) {
|
|
3565
|
+
const { keptTail } = await this.actor.rebaseHistory(result.history, snapshotLen);
|
|
3566
|
+
if (keptTail > 0) {
|
|
3567
|
+
console.warn(`[PiAgent] 压缩落地: 保住变换期间新追加的 ${keptTail} 条消息 (快照后到达)`);
|
|
3568
|
+
}
|
|
3569
|
+
}
|
|
3570
|
+
else {
|
|
3571
|
+
this.replaceHistory(result.history);
|
|
3572
|
+
}
|
|
3423
3573
|
this.projectedHistory = null;
|
|
3424
3574
|
}
|
|
3425
3575
|
// 2026-08-06: snapshot 记录 before/after + 摘要 (供恢复/调试/UI), 事件广播
|
|
@@ -3430,10 +3580,10 @@ ${await this.renderActivePlansSection()}
|
|
|
3430
3580
|
afterTokens,
|
|
3431
3581
|
summary: `压缩管道: ${summaryLine}; 节省 ${savedTokens} tokens / ${saved} 条消息`,
|
|
3432
3582
|
preservedMemory: [
|
|
3433
|
-
...this.messageHistory.filter(m => m.role === 'user').slice(-3).map(m => (m.content || '').slice(0, 80)),
|
|
3583
|
+
...this.actor.state.messageHistory.filter(m => m.role === 'user').slice(-3).map(m => (m.content || '').slice(0, 80)),
|
|
3434
3584
|
],
|
|
3435
|
-
agentId: this.
|
|
3436
|
-
channelId: this.
|
|
3585
|
+
agentId: this.actor.state.agentId,
|
|
3586
|
+
channelId: this.actor.state.channelId,
|
|
3437
3587
|
});
|
|
3438
3588
|
cm.markCompressComplete(snap);
|
|
3439
3589
|
}
|
|
@@ -3828,7 +3978,7 @@ ${this.extractOperationsFromRef(operationsRef)}
|
|
|
3828
3978
|
Object.assign(this.identity, updates);
|
|
3829
3979
|
}
|
|
3830
3980
|
setCurrentChannelId(channelId) {
|
|
3831
|
-
this.
|
|
3981
|
+
this.actor.state.channelId = channelId;
|
|
3832
3982
|
}
|
|
3833
3983
|
getSessionState() {
|
|
3834
3984
|
return this.sessionManager.getState();
|
|
@@ -3843,12 +3993,12 @@ ${this.extractOperationsFromRef(operationsRef)}
|
|
|
3843
3993
|
await this.sessionManager.savePersona(persona);
|
|
3844
3994
|
// 2026-10-01: **写透** —— 有身份文档的 agent 不套 persona.json, 只写 JSON 等于没改
|
|
3845
3995
|
// (用户报"每次让智能体改都是同一个") ⇒ 同步落进它自己的身份文档 (标记区内)
|
|
3846
|
-
if (this.
|
|
3996
|
+
if (this.actor.state.agentId) {
|
|
3847
3997
|
try {
|
|
3848
3998
|
const { applyPersonaToDocs } = await Promise.resolve().then(() => __importStar(require('../bootstrap/persona-init.js')));
|
|
3849
|
-
const wrote = await applyPersonaToDocs(this.
|
|
3999
|
+
const wrote = await applyPersonaToDocs(this.actor.state.agentId, persona);
|
|
3850
4000
|
if (wrote.length)
|
|
3851
|
-
console.warn(`[persona] 已把 ${this.
|
|
4001
|
+
console.warn(`[persona] 已把 ${this.actor.state.agentId} 的 persona 写进身份文档: ${wrote.join(' · ')}`);
|
|
3852
4002
|
}
|
|
3853
4003
|
catch { /* 非致命 */ }
|
|
3854
4004
|
}
|