dsh-memory-palace 1.8.0-alpha.1 → 1.8.0-alpha.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -0
- package/lib/client.js +2 -2
- package/lib/common/logger.mjs +150 -0
- package/lib/common/planmode.mjs +32 -0
- package/lib/distill.mjs +25 -24
- package/lib/hybrid/subagent.mjs +446 -419
- package/lib/hybrid/tools.mjs +3 -1
- package/lib/index.js +118 -48
- package/lib/projection.mjs +5 -4
- package/lib/tools.mjs +4 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -124,6 +124,13 @@ dsh plugin --profile web remove dsh-memory-palace
|
|
|
124
124
|
## 版本历史
|
|
125
125
|
|
|
126
126
|
- **1.8.0**
|
|
127
|
+
- **1.8.0-alpha.4**
|
|
128
|
+
- 💥移除 stderr log 输出
|
|
129
|
+
- 💪新增不同 profiles 场景下 log文件 *详情见 [DEVELOPMENT.md](./DEVELOPMENT.md#日志落盘)*
|
|
130
|
+
- **1.8.0-alpha.2**
|
|
131
|
+
- 🐛修复多会话并发导致写侧串台 @sunligh91
|
|
132
|
+
- 🐛删除会话分桶后遗留的三个全局状态死字段
|
|
133
|
+
- 🐛修复plan 模式禁写记忆串台导致其他会话也禁写记忆
|
|
127
134
|
- **1.8.0-alpha.1**
|
|
128
135
|
- 💥移除插件模式与智能模式,记忆写入统一为混合模式
|
|
129
136
|
- 💥配置项 memoryMode / autoCaptureErrors / dailyLogRetentionDays 删除(旧值静默失效)
|
package/lib/client.js
CHANGED
|
@@ -69,7 +69,7 @@ window.__ModuleLoader__.load({
|
|
|
69
69
|
notifyProjectDone: "项目记忆蒸馏完成",
|
|
70
70
|
notifyFail: "记忆蒸馏失败",
|
|
71
71
|
distillDebugLogLabel: "蒸馏调试日志",
|
|
72
|
-
distillDebugLogHint: "
|
|
72
|
+
distillDebugLogHint: "开启后把本插件全部诊断日志落盘到 profile 目录下的 .memory-palace/logs/<会话 id>/(不再输出控制台)。info.log 收失败/跳过留痕与子代理终态台账;debug.log 收详单,配合 distillLogLevel=debug 时另含 LLM 原始响应。关闭时零输出,仅排障用。",
|
|
73
73
|
dev: "开发",
|
|
74
74
|
devDesc: "蒸馏调试日志与超时",
|
|
75
75
|
distillTimeoutLabel: "蒸馏超时(秒)",
|
|
@@ -143,7 +143,7 @@ window.__ModuleLoader__.load({
|
|
|
143
143
|
notifyProjectDone: "Project memory distilled",
|
|
144
144
|
notifyFail: "Memory distillation failed",
|
|
145
145
|
distillDebugLogLabel: "Distill debug log",
|
|
146
|
-
distillDebugLogHint: "When on,
|
|
146
|
+
distillDebugLogHint: "When on, writes all plugin diagnostics to .memory-palace/logs/<session-id>/ under the profile directory (no console output). info.log holds failure/skip traces and the subagent ledger; debug.log holds verbose detail plus the raw LLM response when distillLogLevel=debug. Off means zero output; troubleshooting only.",
|
|
147
147
|
dev: "Developer",
|
|
148
148
|
devDesc: "Distill debug log and timeout",
|
|
149
149
|
distillTimeoutLabel: "Distill timeout (s)",
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
// dsh-memory-palace v1.8.0-alpha.4:统一日志落盘模块。
|
|
2
|
+
//
|
|
3
|
+
// 背景:插件原先所有诊断输出走 console.error → dsh 服务端 stderr。但 Desktop 下 stderr 只进
|
|
4
|
+
// host 进程内存(apps/desktop/src/host-process.ts:204 存末 64KB),仅崩溃时随报告落盘,正常运行
|
|
5
|
+
// 时完全不可见;而插件其余落盘点只有 Markdown 记忆文件 —— 于是子代理的失败/跳过、蒸馏链路的
|
|
6
|
+
// 跳过原因全都无痕可查。
|
|
7
|
+
//
|
|
8
|
+
// 落点(与 dsh 原生 @deepseek-ai/dsh-plugin-manager 同构):
|
|
9
|
+
// <profileContext.dir>/.memory-palace/logs/<session-id>/<level>.log
|
|
10
|
+
// 其中 profileContext.dir = $DSH_HOME/profiles/<profile>。取径依据:宿主 CLI 与 Desktop 都经
|
|
11
|
+
// 同一个 runProfile 启动(apps/cli/src/profile-boot.ts:298 provide、apps/desktop-host/src/index.ts:25
|
|
12
|
+
// runProfile({profile:'desktop'})),故 ctx.get('profileContext').dir 两端都可用。
|
|
13
|
+
//
|
|
14
|
+
// 门控:统一受 distillDebugLog 控制 —— false(默认)时零输出(既不写终端也不写文件)。
|
|
15
|
+
// 分级:按【行级别】分流 —— warn/info → info.log;debug → debug.log。
|
|
16
|
+
//
|
|
17
|
+
// 失败安全:调用方都位于 turn 结算 / pre-step 钩子栈上,抛错会污染整个 turn(agent/pre-step 抛错
|
|
18
|
+
// 毁 turn 是已知宿主契约),故所有 fs 错误一律吞掉;写入经独立 promise 链串行(不与记忆落盘锁
|
|
19
|
+
// 共用 —— 日志 IO 不得拖慢记忆写入,且二者语义无关)。
|
|
20
|
+
import { appendFile, mkdir, stat } from "node:fs/promises";
|
|
21
|
+
import { dirname, join } from "node:path";
|
|
22
|
+
|
|
23
|
+
/** 单文件大小上限(字节);超出后停止追加(不删旧内容)。 */
|
|
24
|
+
const MAX_FILE_BYTES = 1024 * 1024;
|
|
25
|
+
/**
|
|
26
|
+
* 拿不到会话身份时的兜底目录名。
|
|
27
|
+
* ⚠️ 必须与 index.mjs 既有的 `__nosession__` 约定一致(`sid || "__nosession__"`),
|
|
28
|
+
* 否则会出现两套"无身份"目录并存,排查时难以判断哪份是哪条路径落的。
|
|
29
|
+
*/
|
|
30
|
+
const NO_SESSION = "__nosession__";
|
|
31
|
+
|
|
32
|
+
/** 本地时间戳(禁用 toISOString:UTC 会跨日错位,见 AGENTS.md §⑤)。 */
|
|
33
|
+
function stamp() {
|
|
34
|
+
const d = new Date();
|
|
35
|
+
const p = (n, w = 2) => String(n).padStart(w, "0");
|
|
36
|
+
return (
|
|
37
|
+
`${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())} ` +
|
|
38
|
+
`${p(d.getHours())}:${p(d.getMinutes())}:${p(d.getSeconds())}.${p(d.getMilliseconds(), 3)}`
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** JSON 化额外字段;循环引用等异常一律降级为字符串(日志失败不得影响主流程)。 */
|
|
43
|
+
function safeJson(value) {
|
|
44
|
+
try {
|
|
45
|
+
return JSON.stringify(value);
|
|
46
|
+
} catch {
|
|
47
|
+
return String(value);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* @param {{ ctx: object, getConfig: () => object }} deps
|
|
53
|
+
* @returns {{ for: (sessionId?: string, scope?: string) => object }}
|
|
54
|
+
*/
|
|
55
|
+
export function createLogger({ ctx, getConfig }) {
|
|
56
|
+
let chain = Promise.resolve(); // 独立写链(全局串行)
|
|
57
|
+
const capped = new Set(); // 已达大小上限的文件(停止追加)
|
|
58
|
+
const prepared = new Set(); // 已确保存在的目录
|
|
59
|
+
|
|
60
|
+
// 落盘根 = profile 目录 + 私有尾目录。profileContext 缺失(测试 mock / 非 dsh 启动的宿主)时
|
|
61
|
+
// 返回 null → 放弃落盘,绝不猜路径(process.cwd() 在 CLI 下是用户目录;import.meta.url 在
|
|
62
|
+
// 本地软链安装下解析到开发仓库路径,二者都不可靠)。
|
|
63
|
+
function logRoot() {
|
|
64
|
+
const pc = typeof ctx?.get === "function" ? ctx.get("profileContext") : undefined;
|
|
65
|
+
const dir = pc?.dir;
|
|
66
|
+
return typeof dir === "string" && dir ? join(dir, ".memory-palace", "logs") : null;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function fileOf(root, sessionId, level) {
|
|
70
|
+
const box = typeof sessionId === "string" && sessionId ? sessionId : NO_SESSION;
|
|
71
|
+
return join(root, box, level === "debug" ? "debug.log" : "info.log");
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async function put(file, text) {
|
|
75
|
+
if (capped.has(file)) return;
|
|
76
|
+
const dir = dirname(file);
|
|
77
|
+
if (!prepared.has(dir)) {
|
|
78
|
+
// 0700/0600:与原生 plugin-manager 同款收紧(日志含 cwd / sessionId / 路径等本机信息)。
|
|
79
|
+
// Windows 下 mode 不生效但不报错(plugin-manager 同样如此);Linux/macOS 上生效。
|
|
80
|
+
await mkdir(dir, { recursive: true, mode: 0o700 });
|
|
81
|
+
prepared.add(dir);
|
|
82
|
+
}
|
|
83
|
+
const info = await stat(file).catch(() => null);
|
|
84
|
+
if (info !== null && info.size >= MAX_FILE_BYTES) {
|
|
85
|
+
capped.add(file);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
// mode 仅在文件首次创建时生效(已存在则忽略),故对新老文件都安全。
|
|
89
|
+
await appendFile(file, text, { encoding: "utf8", mode: 0o600 });
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** 入链写入(fire-and-forget):调用点同步返回,异常全部吞掉。 */
|
|
93
|
+
function enqueue(file, text) {
|
|
94
|
+
chain = chain.then(() => put(file, text)).catch(() => {});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** 门控 + 取径 + 入链;任一环节不可用即静默返回。 */
|
|
98
|
+
function emit(sessionId, level, scope, why, extra) {
|
|
99
|
+
let cfg = null;
|
|
100
|
+
try {
|
|
101
|
+
cfg = getConfig();
|
|
102
|
+
} catch {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
if (!cfg?.distillDebugLog) return;
|
|
106
|
+
const root = logRoot();
|
|
107
|
+
if (!root) return;
|
|
108
|
+
const tail = extra === undefined ? "" : ` ${safeJson(extra)}`;
|
|
109
|
+
enqueue(fileOf(root, sessionId, level), `${stamp()} [${level}] ${scope} · ${why}${tail}\n`);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return {
|
|
113
|
+
/** 等待已入链的写入全部落盘(测试用;生产侧 fire-and-forget,无需等待)。 */
|
|
114
|
+
flush: () => chain,
|
|
115
|
+
/**
|
|
116
|
+
* 绑定会话与模块名,返回该作用域的日志器。
|
|
117
|
+
* @param {string|undefined} sessionId 会话 id(缺省落 __nosession__/)
|
|
118
|
+
* @param {string} scope 模块名(subagent / distill / projection / plugin)
|
|
119
|
+
*/
|
|
120
|
+
for(sessionId, scope) {
|
|
121
|
+
const name = scope || "plugin";
|
|
122
|
+
return {
|
|
123
|
+
/** 失败/跳过留痕(warn 级 → info.log)。 */
|
|
124
|
+
dbgFail: (why, extra) => emit(sessionId, "warn", name, why, extra),
|
|
125
|
+
/** 详单(debug 级 → debug.log)。 */
|
|
126
|
+
dbg: (why, extra) => emit(sessionId, "debug", name, why, extra),
|
|
127
|
+
/** 信息行(info 级 → info.log),如子代理终态台账。 */
|
|
128
|
+
info: (why, extra) => emit(sessionId, "info", name, why, extra),
|
|
129
|
+
/** LLM 原始响应等文本块(debug 级 → debug.log);调用点须已过双门控。 */
|
|
130
|
+
raw(text) {
|
|
131
|
+
let cfg = null;
|
|
132
|
+
try {
|
|
133
|
+
cfg = getConfig();
|
|
134
|
+
} catch {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
if (!cfg?.distillDebugLog) return;
|
|
138
|
+
const root = logRoot();
|
|
139
|
+
if (!root) return;
|
|
140
|
+
enqueue(
|
|
141
|
+
fileOf(root, sessionId, "debug"),
|
|
142
|
+
`${stamp()} [debug] ${name} · llm raw text begin >>>\n` +
|
|
143
|
+
`--------------------------->\n${text}\n<----------------------------\n` +
|
|
144
|
+
`${stamp()} [debug] ${name} · llm raw text end <<<\n`,
|
|
145
|
+
);
|
|
146
|
+
},
|
|
147
|
+
};
|
|
148
|
+
},
|
|
149
|
+
};
|
|
150
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// plan 模式判定(按会话)—— 单一判定入口,供 section 注入 / 结算闸门 / 写工具 deny 共用。
|
|
2
|
+
//
|
|
3
|
+
// 宿主事实:plan 模式是**会话级**状态,不是应用级开关 ——
|
|
4
|
+
// - 状态以事件 append 到单个会话:`session.append('plan/mode', { active })`
|
|
5
|
+
// (packages/plan/plan-mode/src/index.ts:434、:450)
|
|
6
|
+
// - plan-mode 插件自身也按 session 保存待提交意图与已记录状态:
|
|
7
|
+
// `this.pendingIntents.set(session, …)` / `this.loggedActive(session)`(同文件 :425、:434)
|
|
8
|
+
// - 状态由该会话的事件流折叠:`if (event.type === 'plan/mode') …`(同文件 :154)
|
|
9
|
+
//
|
|
10
|
+
// 故本插件不得用进程级全局承载它:A 会话进 plan 会波及 B 会话 —— B 的记忆不落盘、
|
|
11
|
+
// B 的 system prompt 被注入 plan 提示、B 的写工具被 deny(回归测试 tests/test-planmode-crosstalk.mjs)。
|
|
12
|
+
//
|
|
13
|
+
// 判定顺序:
|
|
14
|
+
// ① 拿得到会话身份(session.id)→ **只看该会话的记录**:无记录 = 该会话从未进 plan = false。
|
|
15
|
+
// 🔴 此处**不得**回落到全局值——那等于用别的会话的 plan 状态来判定本会话,正是本次修的串台。
|
|
16
|
+
// ② 拿不到会话身份(宿主某些调用形态的 exec 不带 agent.session,如 tools/pre-execute 的载荷)
|
|
17
|
+
// → 回落全局 `state.planModeActive`(= 最近一次 plan/mode 事件的值;fail-safe:偏向拦写,
|
|
18
|
+
// 绝不会写错项目)。
|
|
19
|
+
//
|
|
20
|
+
// ⚠️ 写成**纯函数**而非 state 上的方法:多处单测用裸 mock state 构造
|
|
21
|
+
// `{ planModeActive: … }`(tests/test-session-cwd.mjs、tests/test-load.mjs),
|
|
22
|
+
// 若挂成 `state.isPlanMode()` 这些 mock 会直接 TypeError。纯函数 + 兜底读全局,两条路径都兼容。
|
|
23
|
+
export function planModeOf(state, session) {
|
|
24
|
+
const sid = session?.id;
|
|
25
|
+
if (sid) {
|
|
26
|
+
const map = state?.planModeBySession;
|
|
27
|
+
// 生产路径:有会话身份 + 有分桶表 → 只看该会话(无记录即 false,绝不看全局)。
|
|
28
|
+
// 单测的裸 mock state 没有 planModeBySession → 落到下面的全局兜底,保持旧行为兼容。
|
|
29
|
+
if (map && typeof map.get === "function") return !!map.get(sid);
|
|
30
|
+
}
|
|
31
|
+
return !!state?.planModeActive;
|
|
32
|
+
}
|
package/lib/distill.mjs
CHANGED
|
@@ -13,14 +13,18 @@ import { runWithRetry, RETRY_CONSTANTS } from "./common/retry.mjs";
|
|
|
13
13
|
/**
|
|
14
14
|
* @param {{ ctx: object, getConfig: () => object, paths: object, records: object }} deps
|
|
15
15
|
*/
|
|
16
|
-
export function createDistill({ ctx, getConfig, paths, records }) {
|
|
16
|
+
export function createDistill({ ctx, getConfig, paths, records, logger }) {
|
|
17
17
|
const cfg = () => getConfig();
|
|
18
18
|
|
|
19
|
-
//
|
|
20
|
-
//
|
|
21
|
-
//
|
|
22
|
-
|
|
23
|
-
|
|
19
|
+
// 日志(v1.8.0-alpha.4):原 stderr 输出改为经统一 logger 落盘
|
|
20
|
+
// (`<profileDir>/.memory-palace/logs/<sessionId>/{info,debug}.log`,见 common/logger.mjs)。
|
|
21
|
+
// 按【会话】取作用域日志器——工厂级闭包会让并发会话的日志落到同一目录;logger 未注入时降级为 no-op。
|
|
22
|
+
// dbgFail=失败/跳过留痕(warn 级 → info.log);dbg=详单(debug 级 → debug.log);
|
|
23
|
+
// 两者统一受 distillDebugLog 门控:关闭时(默认)本插件零输出(既不写终端也不写文件)。
|
|
24
|
+
// 隐私红线:只打计数/字符数/错误 message+stack 首行等元数据,绝不打印对话或 MEMORY.md 文本
|
|
25
|
+
// (唯一例外:distillLogLevel=debug 时的 LLM 原始响应,经 raw() 显式落 debug.log)。
|
|
26
|
+
const noop = () => {};
|
|
27
|
+
const logFor = (session) => (typeof logger?.for === "function" ? logger.for(session?.id, "distill") : null);
|
|
24
28
|
|
|
25
29
|
// 模型解析(v1.2.3 修复):summaryModel 存的是注册表 models[].id 原样(client 下拉 value = m.id),
|
|
26
30
|
// pi-ai getModel(provider, id) 按 model.id 全等匹配(pi-ai models.js: getModels(provider).find(m => m.id === id)),
|
|
@@ -46,6 +50,9 @@ export function createDistill({ ctx, getConfig, paths, records }) {
|
|
|
46
50
|
return null;
|
|
47
51
|
}
|
|
48
52
|
async function resolveModel(summaryModel, session) {
|
|
53
|
+
const log = logFor(session);
|
|
54
|
+
const dbg = log ? log.dbg : noop;
|
|
55
|
+
const dbgFail = log ? log.dbgFail : noop;
|
|
49
56
|
const sm = (summaryModel || "").trim();
|
|
50
57
|
if (sm) {
|
|
51
58
|
const hit = await findModelInRegistry(sm);
|
|
@@ -97,6 +104,9 @@ export function createDistill({ ctx, getConfig, paths, records }) {
|
|
|
97
104
|
// v1.8.0:唯一调用方 = 按钮「蒸馏会话」(全量 fromSeq=0);原 smart 模式自动摘要链路已删除。
|
|
98
105
|
// 返回 { ok, summary, durableCount };失败 { ok:false }(调用方各自决定降级策略)。
|
|
99
106
|
async function distillSessionCore(session, dirs, fromSeq, opts) {
|
|
107
|
+
const log = logFor(session);
|
|
108
|
+
const dbg = log ? log.dbg : noop;
|
|
109
|
+
const dbgFail = log ? log.dbgFail : noop;
|
|
100
110
|
const allowDelete = !!(opts && opts.allowDelete);
|
|
101
111
|
// 回喂存量记忆(v1.4.0 特性3):feedbackEnabled 开启即回喂项目级 + 用户级 MEMORY.md 全文
|
|
102
112
|
// (逐行编号、无截断)。delete 由 allowDelete 决定(手动按钮传 true)。
|
|
@@ -185,17 +195,11 @@ export function createDistill({ ctx, getConfig, paths, records }) {
|
|
|
185
195
|
const { finish, text, chunks, deltaChars, tFirstChunk } = result;
|
|
186
196
|
dbg("session finish", { kind: finish?.kind, attempts: result.attempts });
|
|
187
197
|
dbg("session stream done", { chunks, deltaChars, firstChunkMs: tFirstChunk });
|
|
188
|
-
// 本地调试(受 distillDebugLog + distillLogLevel==="debug"
|
|
189
|
-
//
|
|
190
|
-
if (cfg().
|
|
191
|
-
console.error("[memory-palace][debug] session llm raw text begin >>>");
|
|
192
|
-
console.error("--------------------------->");
|
|
193
|
-
console.error(text);
|
|
194
|
-
console.error("<----------------------------");
|
|
195
|
-
console.error("[memory-palace][debug] session llm raw text end <<<");
|
|
196
|
-
}
|
|
198
|
+
// 本地调试(受 distillDebugLog + distillLogLevel==="debug" 双门控):LLM 原始响应写入 debug.log,
|
|
199
|
+
// 用分隔符整段包裹,便于一眼框住起止、不与其它 dbg 行混淆。
|
|
200
|
+
if (cfg().distillLogLevel === "debug") log?.raw(text);
|
|
197
201
|
if (finish && finish.kind === "max-tokens") {
|
|
198
|
-
|
|
202
|
+
dbgFail("session llm finish max-tokens (attempting partial JSON)", {});
|
|
199
203
|
}
|
|
200
204
|
if (!text) {
|
|
201
205
|
dbgFail("empty llm text");
|
|
@@ -279,6 +283,9 @@ export function createDistill({ ctx, getConfig, paths, records }) {
|
|
|
279
283
|
// 流程:读主目标 MEMORY.md → DISTILL_PROMPT 蒸馏 → 写 memory-cover.md(同目录,保证 rename 原子)
|
|
280
284
|
// → 完整性检查 → 备份 MEMORY.md.{时间戳} → rename 覆盖 → cover 随 rename 消失。
|
|
281
285
|
async function distillProjectMemory(cwd, session) {
|
|
286
|
+
const log = logFor(session);
|
|
287
|
+
const dbg = log ? log.dbg : noop;
|
|
288
|
+
const dbgFail = log ? log.dbgFail : noop;
|
|
282
289
|
const c = cfg();
|
|
283
290
|
if (!c.enabled) return { ok: false, message: "memory-palace 已停用。" };
|
|
284
291
|
const dirs = paths.writeDirs(cwd);
|
|
@@ -338,14 +345,8 @@ export function createDistill({ ctx, getConfig, paths, records }) {
|
|
|
338
345
|
return { ok: false, message: `蒸馏${kindZh}(重试 ${e?.attempts || 0} 次后仍失败):${cls.message || ""}` };
|
|
339
346
|
}
|
|
340
347
|
const { finish, text, chunks, deltaChars, gotFirstChunk, tFirstChunk } = result;
|
|
341
|
-
// 本地调试(受 distillDebugLog + distillLogLevel==="debug"
|
|
342
|
-
if (cfg().
|
|
343
|
-
console.error("[memory-palace][debug] project llm raw text begin >>>");
|
|
344
|
-
console.error("--------------------------->");
|
|
345
|
-
console.error(text);
|
|
346
|
-
console.error("<----------------------------");
|
|
347
|
-
console.error("[memory-palace][debug] project llm raw text end <<<");
|
|
348
|
-
}
|
|
348
|
+
// 本地调试(受 distillDebugLog + distillLogLevel==="debug" 双门控,与会话蒸馏路径一致):原始响应写入 debug.log。
|
|
349
|
+
if (cfg().distillLogLevel === "debug") log?.raw(text);
|
|
349
350
|
if (finish && finish.kind === "max-tokens") {
|
|
350
351
|
dbg("project finish", { kind: "max-tokens", attempts: result.attempts });
|
|
351
352
|
} else {
|