@bolloon/bolloon-agent 0.3.41 → 0.3.43
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/pi-sdk.js +9 -0
- package/dist/agents/skill-writer.js +70 -14
- package/dist/cli/ink-app.js +4 -2
- package/dist/cli/mention-data.js +5 -4
- package/dist/index.js +382 -24
- package/package.json +1 -1
package/dist/agents/pi-sdk.js
CHANGED
|
@@ -167,6 +167,15 @@ export class PiAgentSession {
|
|
|
167
167
|
}
|
|
168
168
|
})();
|
|
169
169
|
}
|
|
170
|
+
/** 2026-08-08: 公开可用工具列表 (name + description + 参数名) 供 /tools 显示 */
|
|
171
|
+
getToolList() {
|
|
172
|
+
const out = [];
|
|
173
|
+
for (const tool of this.allowedTools()) {
|
|
174
|
+
const paramNames = tool.parameters ? Object.keys(tool.parameters) : [];
|
|
175
|
+
out.push({ name: tool.name, description: tool.description || '', parameters: paramNames });
|
|
176
|
+
}
|
|
177
|
+
return out;
|
|
178
|
+
}
|
|
170
179
|
/**
|
|
171
180
|
* Judgment 注入门临时结果: 在 prompt / promptStream / promptWithPivotLoop 入口算一次, 拼到本轮 systemPrompt 末尾
|
|
172
181
|
* 每次调用都会重置 (避免上一轮遗留)
|
|
@@ -146,8 +146,26 @@ export async function writeSkillCandidate(c) {
|
|
|
146
146
|
const dir = getCandidateDir();
|
|
147
147
|
await fs.mkdir(dir, { recursive: true });
|
|
148
148
|
const safeName = sanitizeSkillName(c.name);
|
|
149
|
-
|
|
150
|
-
|
|
149
|
+
// 2026-08-08: 有 signature 的候选用固定文件名 (合并更新同一个), 无 signature 才带时间戳
|
|
150
|
+
const file = c.signature
|
|
151
|
+
? path.join(dir, `${safeName}.json`)
|
|
152
|
+
: path.join(dir, `${safeName}-${Date.now()}.json`);
|
|
153
|
+
// 追加式合并: 若同 signature 已存在, 累积 runs + 追加 body
|
|
154
|
+
let runs = c.runs ?? 1;
|
|
155
|
+
let body = c.body;
|
|
156
|
+
try {
|
|
157
|
+
const prev = JSON.parse(await fs.readFile(file, 'utf-8'));
|
|
158
|
+
if (prev && prev.runs)
|
|
159
|
+
runs = prev.runs + 1;
|
|
160
|
+
if (prev && prev.body && body !== prev.body && c.signature) {
|
|
161
|
+
// 同一 signature 重复运行 → 追加一条经验 (去重, 避免 body 膨胀)
|
|
162
|
+
const line = `- ${new Date().toISOString().slice(0, 16)} ${c.source}: ${c.description}`;
|
|
163
|
+
body = `${prev.body}\n${line}`;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
catch { /* 新文件 */ }
|
|
167
|
+
const merged = { ...c, runs, body, timestamp: c.timestamp || new Date().toISOString() };
|
|
168
|
+
await fs.writeFile(file, JSON.stringify(merged, null, 2), 'utf-8');
|
|
151
169
|
return file;
|
|
152
170
|
}
|
|
153
171
|
export async function listSkillCandidates(home = os.homedir()) {
|
|
@@ -165,12 +183,37 @@ export async function listSkillCandidates(home = os.homedir()) {
|
|
|
165
183
|
const raw = await fs.readFile(path.join(dir, f), 'utf-8');
|
|
166
184
|
const c = JSON.parse(raw);
|
|
167
185
|
if (c.name && c.body)
|
|
168
|
-
out.push(c);
|
|
186
|
+
out.push({ ...c, file: path.join(dir, f) });
|
|
169
187
|
}
|
|
170
188
|
catch { /* 坏文件跳过 */ }
|
|
171
189
|
}
|
|
172
190
|
return out;
|
|
173
191
|
}
|
|
192
|
+
/** 按名字删除所有同名候选文件 (名可能与文件名前缀不完全一致) */
|
|
193
|
+
async function removeCandidateFiles(name, home) {
|
|
194
|
+
const safe = sanitizeSkillName(name);
|
|
195
|
+
const dir = getCandidateDir(home);
|
|
196
|
+
let files;
|
|
197
|
+
try {
|
|
198
|
+
files = await fs.readdir(dir);
|
|
199
|
+
}
|
|
200
|
+
catch {
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
for (const f of files) {
|
|
204
|
+
if (!f.endsWith('.json'))
|
|
205
|
+
continue;
|
|
206
|
+
try {
|
|
207
|
+
const c = JSON.parse(await fs.readFile(path.join(dir, f), 'utf-8'));
|
|
208
|
+
if (sanitizeSkillName(c.name) === safe)
|
|
209
|
+
await fs.rm(path.join(dir, f), { force: true });
|
|
210
|
+
}
|
|
211
|
+
catch {
|
|
212
|
+
if (f.startsWith(safe))
|
|
213
|
+
await fs.rm(path.join(dir, f), { force: true });
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
174
217
|
/** 把候选转正为正式 skill (可选: 转正后删除候选文件) */
|
|
175
218
|
export async function promoteCandidate(name, opts = {}, home = os.homedir()) {
|
|
176
219
|
const candidates = await listSkillCandidates(home);
|
|
@@ -179,18 +222,25 @@ export async function promoteCandidate(name, opts = {}, home = os.homedir()) {
|
|
|
179
222
|
return { ok: false, path: '', error: `候选 '${name}' 不存在` };
|
|
180
223
|
const r = await createSkill(c.name, c.description, c.body, opts);
|
|
181
224
|
if (r.ok) {
|
|
182
|
-
//
|
|
183
|
-
|
|
184
|
-
const dir = getCandidateDir(home);
|
|
185
|
-
for (const f of (await fs.readdir(dir))) {
|
|
186
|
-
if (f.startsWith(sanitizeSkillName(c.name) + '-'))
|
|
187
|
-
await fs.rm(path.join(dir, f), { force: true });
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
catch { /* 清理失败不阻塞 */ }
|
|
225
|
+
// 清理已转正的候选文件: 同 name 的所有候选
|
|
226
|
+
await removeCandidateFiles(c.name, home);
|
|
191
227
|
}
|
|
192
228
|
return r;
|
|
193
229
|
}
|
|
230
|
+
/**
|
|
231
|
+
* 从一轮成功的工具调用生成稳定签名 — 同一套工具序列 (有序去重, 最多 4 个) 视为同一经验.
|
|
232
|
+
* 用于跨运行合并: 第二次跑同样的工具 → 更新同一个候选, 而不是新建一个.
|
|
233
|
+
*/
|
|
234
|
+
export function toolSignature(okSteps) {
|
|
235
|
+
const seq = [];
|
|
236
|
+
for (const s of (okSteps || [])) {
|
|
237
|
+
if (s.name && !seq.includes(s.name))
|
|
238
|
+
seq.push(s.name);
|
|
239
|
+
if (seq.length >= 4)
|
|
240
|
+
break;
|
|
241
|
+
}
|
|
242
|
+
return seq.join('_');
|
|
243
|
+
}
|
|
194
244
|
export async function writeRunEndSkillCandidates(steps, source, minOk = 2) {
|
|
195
245
|
const okSteps = (steps || []).filter((s) => s.status === 'ok' && s.name && s.name !== 'system' && s.name !== '?');
|
|
196
246
|
if (okSteps.length < minOk) {
|
|
@@ -200,13 +250,19 @@ export async function writeRunEndSkillCandidates(steps, source, minOk = 2) {
|
|
|
200
250
|
const body = `## 背景\n本轮对话连续成功调用了 ${okSteps.length} 个工具: ${toolNames}.\n\n` +
|
|
201
251
|
`## 流程\n${okSteps.map((s) => `1. 调用 ${s.name}${s.output ? ': ' + String(s.output).slice(0, 120) : ''}`).join('\n')}\n\n` +
|
|
202
252
|
`## 注意事项\n- 工具名以 list_skills / get_operation_logs 的实际注册名为准\n- 沉淀为正式 skill 前请人工确认流程可复用\n`;
|
|
203
|
-
|
|
253
|
+
// 2026-08-08: 稳定签名 + 固定文件名 → 同一套工具反复跑时合并更新到同一个候选 (runs++)
|
|
254
|
+
const signature = toolSignature(okSteps);
|
|
255
|
+
const candName = `auto-${signature}`;
|
|
256
|
+
const existing = (await listSkillCandidates()).find((x) => x.signature === signature || sanitizeSkillName(x.name) === sanitizeSkillName(candName));
|
|
204
257
|
const file = await writeSkillCandidate({
|
|
205
258
|
name: candName,
|
|
206
259
|
description: `自动候选: ${okSteps.length} 个工具连续成功 (${toolNames})`,
|
|
207
260
|
body,
|
|
208
261
|
source,
|
|
209
262
|
timestamp: new Date().toISOString(),
|
|
263
|
+
signature,
|
|
210
264
|
});
|
|
211
|
-
|
|
265
|
+
const merged = !!existing;
|
|
266
|
+
const runs = (existing?.runs ?? 0) + 1;
|
|
267
|
+
return { wrote: true, file, count: okSteps.length, names: toolNames, merged, runs };
|
|
212
268
|
}
|
package/dist/cli/ink-app.js
CHANGED
|
@@ -30,14 +30,16 @@ const Messages = ({ msgs }) => (_jsx(Box, { flexDirection: "column", flexGrow: 1
|
|
|
30
30
|
}) }));
|
|
31
31
|
const MentionPopup = ({ title, items, sel, width, loading }) => {
|
|
32
32
|
const MAX_ROWS = 8;
|
|
33
|
-
|
|
33
|
+
// 2026-08-08: 滑动窗口 — 选中项始终可见 (原实现 fix 屏幕顶部, sel 超窗口时无高亮行)
|
|
34
|
+
const offset = Math.max(0, Math.min(sel - Math.floor(MAX_ROWS / 2), Math.max(0, items.length - MAX_ROWS)));
|
|
35
|
+
const shown = items.slice(offset, offset + MAX_ROWS);
|
|
34
36
|
const innerW = Math.max(width - 2, 10);
|
|
35
37
|
return (_jsxs(Box, { flexDirection: "column", width: width, children: [_jsx(Text, { color: "cyan", bold: true, children: `╭─ ${title} ${'─'.repeat(Math.max(2, innerW - dispWidth(title) - 4))}╮` }), loading && items.length === 0 ? (_jsx(Text, { color: "dim", children: "\u2502 \u626B\u63CF\u4E2D..." })) : !loading && items.length === 0 ? (_jsx(Text, { color: "dim", children: "\u2502 \u65E0\u5339\u914D" })) : (shown.map((it, i) => {
|
|
36
38
|
const active = i === sel;
|
|
37
39
|
const label = it.kind === 'file' ? it.label : `${it.kind === 'skill' ? '⚡' : it.kind === 'plugin' ? '🔌' : ''}${it.label}`;
|
|
38
40
|
const hint = it.hint ? `${it.hint}` : it.kind === 'file' ? '文件' : '';
|
|
39
41
|
return (_jsxs(Box, { width: innerW, children: [_jsx(Text, { color: active ? 'black' : undefined, backgroundColor: active ? 'cyan' : undefined, children: `${active ? '❯ ' : ' '}${label}` }), _jsx(Text, { color: active ? 'black' : 'dim', backgroundColor: active ? 'cyan' : undefined, dimColor: !active, children: ` ${hint}` })] }, `${it.kind}:${it.label}`));
|
|
40
|
-
})), items.length > MAX_ROWS && (_jsxs(Text, { color: "dim", children: ["\u2502 \u8FD8\u6709 ", items.length -
|
|
42
|
+
})), items.length > MAX_ROWS && (_jsxs(Text, { color: "dim", children: ["\u2502 ", offset + 1, "-", offset + shown.length, "/", items.length, " \u00B7 \u8FD8\u6709 ", items.length - (offset + shown.length), " \u9879..."] })), _jsx(Text, { color: "cyan", children: `╰${'─'.repeat(innerW)}╯` })] }));
|
|
41
43
|
};
|
|
42
44
|
const InkApp = ({ onPrompt, initialStatus, getStatusUpdate, terminalW, terminalH }) => {
|
|
43
45
|
const [input, setInput] = useState('');
|
package/dist/cli/mention-data.js
CHANGED
|
@@ -23,7 +23,7 @@ const CLI_COMMANDS = [
|
|
|
23
23
|
{ kind: 'command', label: 'add_friend', hint: '添加好友 <64位hex公钥>', insert: 'add_friend' },
|
|
24
24
|
// 2026-08-06: 系统命令组
|
|
25
25
|
{ kind: 'command', label: 'model', hint: '模型供应商选择器 (↑↓ 选择)', insert: 'model' },
|
|
26
|
-
{ kind: 'command', label: 'login', hint: '
|
|
26
|
+
{ kind: 'command', label: 'login', hint: '登录 GitHub + Google 账号 (骨架)', insert: 'login' },
|
|
27
27
|
{ kind: 'command', label: 'logout', hint: '查看当前供应商', insert: 'logout' },
|
|
28
28
|
{ kind: 'command', label: 'now', hint: '当前状态总览', insert: 'now' },
|
|
29
29
|
{ kind: 'command', label: 'session', hint: '当前会话信息', insert: 'session' },
|
|
@@ -44,14 +44,15 @@ const CLI_COMMANDS = [
|
|
|
44
44
|
{ kind: 'command', label: 'insight', hint: '洞察 (08-Insights)', insert: 'insight' },
|
|
45
45
|
{ kind: 'command', label: 'wiki', hint: 'wiki 状态', insert: 'wiki' },
|
|
46
46
|
{ kind: 'command', label: 'dream', hint: '随机灵感', insert: 'dream' },
|
|
47
|
+
{ kind: 'command', label: 'new agent', hint: '创建新智能体 channel', insert: 'new agent' },
|
|
48
|
+
{ kind: 'command', label: 'new session', hint: '创建新会话', insert: 'new session' },
|
|
49
|
+
{ kind: 'command', label: 'plan', hint: '循环计划 (创建/查看)', insert: 'plan' },
|
|
50
|
+
{ kind: 'command', label: 'todo', hint: '勾选步骤 (循环过程)', insert: 'todo' },
|
|
47
51
|
];
|
|
48
52
|
/** Web 端斜杠命令 (server /message 路由 → LLM 工具) */
|
|
49
53
|
const WEB_COMMANDS = [
|
|
50
|
-
{ kind: 'command', label: 'plan', hint: '创建计划', insert: 'plan' },
|
|
51
|
-
{ kind: 'command', label: 'todo', hint: '勾选步骤', insert: 'todo' },
|
|
52
54
|
{ kind: 'command', label: 'review', hint: '审查计划', insert: 'review' },
|
|
53
55
|
{ kind: 'command', label: 'task', hint: '创建任务', insert: 'task' },
|
|
54
|
-
{ kind: 'command', label: 'goal', hint: '暂停目标', insert: 'goal' },
|
|
55
56
|
{ kind: 'command', label: 'skill', hint: '沉淀技能', insert: 'skill' },
|
|
56
57
|
{ kind: 'command', label: 'add-friend', hint: '添加好友 (智能体工具)', insert: 'add-friend' },
|
|
57
58
|
];
|
package/dist/index.js
CHANGED
|
@@ -586,6 +586,98 @@ async function processInput(input, comm) {
|
|
|
586
586
|
}
|
|
587
587
|
return;
|
|
588
588
|
}
|
|
589
|
+
// /new agent <名字> — 创建新智能体 channel (2026-08-08)
|
|
590
|
+
if (trimmed.toLowerCase().startsWith('/new agent')) {
|
|
591
|
+
const q = trimmed.slice('/new agent'.length).trim();
|
|
592
|
+
if (!q) {
|
|
593
|
+
appendLine(`${C_DIM}用法: /new agent <名字> — 新建一个智能体 channel 并切换过去${RESET}`);
|
|
594
|
+
return;
|
|
595
|
+
}
|
|
596
|
+
try {
|
|
597
|
+
const [name, ...rest] = q.split(/\s+/);
|
|
598
|
+
const personaHint = rest.join(' ').trim();
|
|
599
|
+
const { getIdentityStore } = await import('./agents/agent-identity-store.js');
|
|
600
|
+
const store = getIdentityStore();
|
|
601
|
+
await store.load();
|
|
602
|
+
const { readFile, writeFile, mkdir } = await import('fs/promises');
|
|
603
|
+
const { join } = await import('path');
|
|
604
|
+
const home = process.env.HOME || '/tmp';
|
|
605
|
+
const channelsPath = join(home, '.bolloon', 'sessions', 'channels.json');
|
|
606
|
+
let channels = [];
|
|
607
|
+
try {
|
|
608
|
+
const parsed = JSON.parse(await readFile(channelsPath, 'utf-8'));
|
|
609
|
+
channels = Array.isArray(parsed) ? parsed : parsed?.channels || [];
|
|
610
|
+
}
|
|
611
|
+
catch { /* 首次无文件 */ }
|
|
612
|
+
const dupName = channels.find((c) => c.name === name.trim());
|
|
613
|
+
if (dupName) {
|
|
614
|
+
appendLine(`${C_ERROR}同名智能体已存在: '${dupName.name}' (id=${dupName.id})${RESET}`);
|
|
615
|
+
return;
|
|
616
|
+
}
|
|
617
|
+
const id = `ch_${Date.now()}_${Math.random().toString(36).substring(2, 8)}`;
|
|
618
|
+
const ch = {
|
|
619
|
+
id,
|
|
620
|
+
name: name.trim(),
|
|
621
|
+
agentId: `agent-${name.trim().toLowerCase().replace(/\s+/g, '-')}`,
|
|
622
|
+
createdAt: new Date().toISOString(),
|
|
623
|
+
updatedAt: new Date().toISOString(),
|
|
624
|
+
currentSessionId: 'default',
|
|
625
|
+
};
|
|
626
|
+
if (personaHint)
|
|
627
|
+
ch.persona = { name: name.trim(), description: personaHint };
|
|
628
|
+
channels.push(ch);
|
|
629
|
+
await mkdir(join(home, '.bolloon', 'sessions'), { recursive: true });
|
|
630
|
+
await writeFile(channelsPath, JSON.stringify(channels, null, 2), 'utf-8');
|
|
631
|
+
await store.setActive(id);
|
|
632
|
+
cliAgentName = name.trim();
|
|
633
|
+
cliActiveChannelId = id;
|
|
634
|
+
inkSetStatus(getStatus());
|
|
635
|
+
appendLine(`${C_OK}✓ 已创建智能体 channel: ${name.trim()}${RESET} (${C_DIM}${id}${RESET})${personaHint ? `\n ${C_DIM}persona: ${personaHint}${RESET}` : ''}`);
|
|
636
|
+
}
|
|
637
|
+
catch (e) {
|
|
638
|
+
appendLine(`${C_ERROR}/new agent 失败: ${String(e.message || e).slice(0, 200)}${RESET}`);
|
|
639
|
+
}
|
|
640
|
+
return;
|
|
641
|
+
}
|
|
642
|
+
// /new session — 当前 channel 开新会话 (2026-08-08)
|
|
643
|
+
if (trimmed.toLowerCase() === '/new session') {
|
|
644
|
+
try {
|
|
645
|
+
const { readFile, writeFile } = await import('fs/promises');
|
|
646
|
+
const { join } = await import('path');
|
|
647
|
+
const home = process.env.HOME || '/tmp';
|
|
648
|
+
const channelsPath = join(home, '.bolloon', 'sessions', 'channels.json');
|
|
649
|
+
const newSessionId = `sess_${Date.now()}`;
|
|
650
|
+
let saved = false;
|
|
651
|
+
try {
|
|
652
|
+
const parsed = JSON.parse(await readFile(channelsPath, 'utf-8'));
|
|
653
|
+
const channels = Array.isArray(parsed) ? parsed : parsed?.channels || [];
|
|
654
|
+
for (const c of channels) {
|
|
655
|
+
if (cliActiveChannelId && c.id === cliActiveChannelId) {
|
|
656
|
+
c.currentSessionId = newSessionId;
|
|
657
|
+
saved = true;
|
|
658
|
+
}
|
|
659
|
+
else if (!cliActiveChannelId && c.id === channels[0]?.id) {
|
|
660
|
+
c.currentSessionId = newSessionId;
|
|
661
|
+
saved = true;
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
await writeFile(channelsPath, JSON.stringify(Array.isArray(parsed) ? channels : { ...parsed, channels }, null, 2), 'utf-8');
|
|
665
|
+
}
|
|
666
|
+
catch { /* 无 channels.json → 仅提示 */ }
|
|
667
|
+
// 重置 agent 消息历史 (新会话空窗口)
|
|
668
|
+
try {
|
|
669
|
+
const a = await getAgent();
|
|
670
|
+
if (a && a.messageHistory)
|
|
671
|
+
a.messageHistory = [];
|
|
672
|
+
}
|
|
673
|
+
catch { /* 非致命 */ }
|
|
674
|
+
appendLine(`${C_OK}✓ 已新建会话${RESET} session=${C_DIM}${newSessionId}${RESET}${saved ? ` (channel: ${cliActiveChannelId || 'default'})` : ''}`);
|
|
675
|
+
}
|
|
676
|
+
catch (e) {
|
|
677
|
+
appendLine(`${C_ERROR}/new session 失败: ${String(e.message || e).slice(0, 200)}${RESET}`);
|
|
678
|
+
}
|
|
679
|
+
return;
|
|
680
|
+
}
|
|
589
681
|
// /queue — 切换队列模式
|
|
590
682
|
if (trimmed.toLowerCase() === '/queue') {
|
|
591
683
|
queueMode = !queueMode;
|
|
@@ -615,8 +707,8 @@ async function processInput(input, comm) {
|
|
|
615
707
|
}
|
|
616
708
|
// ==================== 2026-08-06: 系统命令组 (/model /now /ipfs /memory ...) ====================
|
|
617
709
|
const cmd = trimmed.toLowerCase();
|
|
618
|
-
// /model
|
|
619
|
-
if (cmd === '/model'
|
|
710
|
+
// /model — 模型供应商选择器 (ink 交互渲染, 复用 MentionPopup)
|
|
711
|
+
if (cmd === '/model') {
|
|
620
712
|
try {
|
|
621
713
|
const { llmConfigStore, PROVIDER_INFO } = await import('./llm/config-store.js');
|
|
622
714
|
await llmConfigStore.initialize();
|
|
@@ -644,6 +736,52 @@ async function processInput(input, comm) {
|
|
|
644
736
|
}
|
|
645
737
|
return;
|
|
646
738
|
}
|
|
739
|
+
// /login — GitHub / Google 账号登录骨架 (2026-08-08, 无真实 OAuth, 先做选择 + 记录)
|
|
740
|
+
if (cmd === '/login') {
|
|
741
|
+
try {
|
|
742
|
+
const { readFile, writeFile, mkdir } = await import('fs/promises');
|
|
743
|
+
const { join } = await import('path');
|
|
744
|
+
const home = process.env.HOME || '/tmp';
|
|
745
|
+
const accPath = join(home, '.bolloon', 'accounts.json');
|
|
746
|
+
let accs = [];
|
|
747
|
+
try {
|
|
748
|
+
const parsed = JSON.parse(await readFile(accPath, 'utf-8'));
|
|
749
|
+
accs = Array.isArray(parsed) ? parsed : [];
|
|
750
|
+
}
|
|
751
|
+
catch { /* 无 */ }
|
|
752
|
+
const gh = accs.filter((a) => a.provider === 'github');
|
|
753
|
+
const gg = accs.filter((a) => a.provider === 'google');
|
|
754
|
+
const items = [
|
|
755
|
+
{ kind: 'command', label: 'GitHub', hint: gh.length ? `已登录 ${gh.length} 个账号` : '未登录', insert: 'GitHub' },
|
|
756
|
+
{ kind: 'command', label: 'Google', hint: gg.length ? `已登录 ${gg.length} 个账号` : '未登录', insert: 'Google' },
|
|
757
|
+
];
|
|
758
|
+
globalThis.__inkOpenPicker?.(items, '登录账号 (骨架) · 选择服务 · Esc 取消', async (it) => {
|
|
759
|
+
const provider = it.label.toLowerCase();
|
|
760
|
+
try {
|
|
761
|
+
const existing = accs.find((a) => a.provider === provider);
|
|
762
|
+
if (existing) {
|
|
763
|
+
appendLine(`${C_OK}✓ ${it.label}: 已登录${RESET} 账号=${existing.username || existing.email || '?'} (${existing.loggedAt || ''})`);
|
|
764
|
+
appendLine(` ${C_DIM}token: ${existing.token ? '已保存' : '无'} (未做真实 OAuth, 仅骨架)${RESET}`);
|
|
765
|
+
return;
|
|
766
|
+
}
|
|
767
|
+
// 骨架: 记录一个占位账号 (真实 OAuth 后续接入, 在此扩展)
|
|
768
|
+
const entry = { provider, username: `user-${provider}`, email: '', token: '', loggedAt: new Date().toISOString(), skeleton: true };
|
|
769
|
+
accs.push(entry);
|
|
770
|
+
await mkdir(join(home, '.bolloon'), { recursive: true });
|
|
771
|
+
await writeFile(accPath, JSON.stringify(accs, null, 2), 'utf-8');
|
|
772
|
+
appendLine(`${C_OK}✓ ${it.label} 登录骨架已记录 (未做真实 OAuth)${RESET}`);
|
|
773
|
+
appendLine(` ${C_DIM}后续接入: 这里会打开浏览器授权并交换 token${RESET}`);
|
|
774
|
+
}
|
|
775
|
+
catch (e) {
|
|
776
|
+
appendLine(`${C_ERROR}✗ /login 失败: ${String(e.message || e).slice(0, 150)}${RESET}`);
|
|
777
|
+
}
|
|
778
|
+
});
|
|
779
|
+
}
|
|
780
|
+
catch (e) {
|
|
781
|
+
appendLine(`${C_ERROR}/login 失败: ${String(e.message || e).slice(0, 150)}${RESET}`);
|
|
782
|
+
}
|
|
783
|
+
return;
|
|
784
|
+
}
|
|
647
785
|
// /logout — 显示当前供应商 (减法: 登出 = 查看当前, 切换走 /model)
|
|
648
786
|
if (cmd === '/logout') {
|
|
649
787
|
try {
|
|
@@ -673,17 +811,22 @@ async function processInput(input, comm) {
|
|
|
673
811
|
catch { /* 静默 */ }
|
|
674
812
|
return;
|
|
675
813
|
}
|
|
676
|
-
// /tools — 可用工具列表
|
|
814
|
+
// /tools — 可用工具列表 (2026-08-08: 读 getToolList, 显示名 + 参数)
|
|
677
815
|
if (cmd === '/tools') {
|
|
678
816
|
try {
|
|
679
817
|
const a = await getAgent();
|
|
680
|
-
const
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
818
|
+
const list = a.getToolList?.() ?? [];
|
|
819
|
+
appendLine(`${C_ACCENT}可用工具 (${list.length}):${RESET}`);
|
|
820
|
+
if (list.length === 0) {
|
|
821
|
+
appendLine(` ${C_DIM}无 (agent 未初始化工具列表)${RESET}`);
|
|
822
|
+
}
|
|
823
|
+
for (const t of list.slice(0, 40)) {
|
|
824
|
+
const params = Array.isArray(t.parameters) && t.parameters.length > 0 ? `(${t.parameters.join(',')})` : '';
|
|
825
|
+
const desc = t.description ? ` ${C_DIM}${String(t.description).split('\n')[0].slice(0, 40)}${RESET}` : '';
|
|
826
|
+
appendLine(` ${C_DIM}·${RESET} ${t.name}${params}${desc}`);
|
|
827
|
+
}
|
|
828
|
+
if (list.length > 40)
|
|
829
|
+
appendLine(` ${C_DIM}... 共 ${list.length} 个${RESET}`);
|
|
687
830
|
}
|
|
688
831
|
catch { /* 静默 */ }
|
|
689
832
|
return;
|
|
@@ -754,14 +897,14 @@ async function processInput(input, comm) {
|
|
|
754
897
|
catch { /* 静默 */ }
|
|
755
898
|
return;
|
|
756
899
|
}
|
|
757
|
-
// /goal —
|
|
900
|
+
// /goal — 进行中的目标/计划; /goal <文本> 设定新目标并触发循环 (2026-08-08)
|
|
758
901
|
if (cmd === '/goal') {
|
|
759
902
|
try {
|
|
760
|
-
const { listActivePlans
|
|
903
|
+
const { listActivePlans } = await import('./agents/plan-store.js');
|
|
761
904
|
const plans = await listActivePlans();
|
|
762
905
|
appendLine(`${C_ACCENT}目标 (${plans.length} 个进行中):${RESET}`);
|
|
763
906
|
if (plans.length === 0) {
|
|
764
|
-
appendLine(` ${C_DIM}无进行中计划 — 可用 /plan 创建${RESET}`);
|
|
907
|
+
appendLine(` ${C_DIM}无进行中计划 — 可用 /goal <目标> 设定 或 /plan 创建${RESET}`);
|
|
765
908
|
}
|
|
766
909
|
for (const p of plans.slice(0, 5)) {
|
|
767
910
|
appendLine(` ${C_ACCENT}●${RESET} ${p.goal || p.planId} ${C_DIM}[${p.status || 'active'}]${RESET}`);
|
|
@@ -774,6 +917,102 @@ async function processInput(input, comm) {
|
|
|
774
917
|
catch { /* 静默 */ }
|
|
775
918
|
return;
|
|
776
919
|
}
|
|
920
|
+
if (cmd.startsWith('/goal ')) {
|
|
921
|
+
const q = trimmed.slice('/goal '.length).trim();
|
|
922
|
+
try {
|
|
923
|
+
const { createPlan } = await import('./agents/plan-store.js');
|
|
924
|
+
const r = await createPlan({ goal: q, steps: [q], createdBy: 'user', originChannel: cliActiveChannelId || 'cli' });
|
|
925
|
+
if (!r.ok || !r.plan) {
|
|
926
|
+
appendLine(`${C_ERROR}/goal 设定失败: ${r.error || '未知'}${RESET}`);
|
|
927
|
+
return;
|
|
928
|
+
}
|
|
929
|
+
appendLine(`${C_OK}✓ 目标已设定: ${C_ACCENT}${q}${RESET} (${C_DIM}plan ${r.plan.planId}${RESET})`);
|
|
930
|
+
// 触发自我改进循环 (沙箱分支, 输出供用户审)
|
|
931
|
+
const { runSelfImproveLoop } = await import('./agents/pi-sdk-session-factory.js');
|
|
932
|
+
const loop = await runSelfImproveLoop(q).catch(() => ({ success: false, error: '未启动' }));
|
|
933
|
+
if (loop.success)
|
|
934
|
+
appendLine(` ${C_DIM}${loop.output}${RESET}`);
|
|
935
|
+
else
|
|
936
|
+
appendLine(` ${C_WARN}⚠ 循环未启动: ${loop.error}${RESET}`);
|
|
937
|
+
}
|
|
938
|
+
catch (e) {
|
|
939
|
+
appendLine(`${C_ERROR}/goal 失败: ${String(e.message || e).slice(0, 200)}${RESET}`);
|
|
940
|
+
}
|
|
941
|
+
return;
|
|
942
|
+
}
|
|
943
|
+
// /plan — 循环过程工具: 创建/查看计划 (2026-08-08)
|
|
944
|
+
// /plan <目标> :: <步骤1> | <步骤2> ... 创建
|
|
945
|
+
// /plan 查看进行中
|
|
946
|
+
if (cmd.startsWith('/plan ')) {
|
|
947
|
+
const q = trimmed.slice('/plan '.length).trim();
|
|
948
|
+
const [goalText, ...rest] = q.split('::');
|
|
949
|
+
const stepsFlat = rest.length > 0 ? rest[0] : '';
|
|
950
|
+
const steps = stepsFlat ? stepsFlat.split(/\s*[||]\s*/).map(s => s.trim()).filter(Boolean) : [goalText].filter(Boolean);
|
|
951
|
+
try {
|
|
952
|
+
const { createPlan } = await import('./agents/plan-store.js');
|
|
953
|
+
const r = await createPlan({ goal: goalText || q, steps: steps.length ? steps : [goalText], createdBy: 'user', originChannel: cliActiveChannelId || 'cli' });
|
|
954
|
+
if (!r.ok || !r.plan) {
|
|
955
|
+
appendLine(`${C_ERROR}/plan 创建失败: ${r.error || '未知'}${RESET}`);
|
|
956
|
+
return;
|
|
957
|
+
}
|
|
958
|
+
appendLine(`${C_OK}✓ 计划已创建: ${C_ACCENT}${r.plan.goal}${RESET} (${C_DIM}${r.plan.planId} · ${r.plan.steps.length} 步${RESET})`);
|
|
959
|
+
for (const s of r.plan.steps)
|
|
960
|
+
appendLine(` ${C_DIM}· ${s.description}${RESET}`);
|
|
961
|
+
}
|
|
962
|
+
catch (e) {
|
|
963
|
+
appendLine(`${C_ERROR}/plan 失败: ${String(e.message || e).slice(0, 200)}${RESET}`);
|
|
964
|
+
}
|
|
965
|
+
return;
|
|
966
|
+
}
|
|
967
|
+
// /todo — 循环过程工具: 查看/勾选步骤 (2026-08-08)
|
|
968
|
+
if (cmd === '/todo') {
|
|
969
|
+
try {
|
|
970
|
+
const { listActivePlans } = await import('./agents/plan-store.js');
|
|
971
|
+
const plans = await listActivePlans();
|
|
972
|
+
if (plans.length === 0) {
|
|
973
|
+
appendLine(`${C_DIM}无进行中计划 — 可用 /plan <目标> 创建${RESET}`);
|
|
974
|
+
return;
|
|
975
|
+
}
|
|
976
|
+
for (const p of plans.slice(0, 3)) {
|
|
977
|
+
appendLine(`${C_ACCENT}● ${p.goal}${RESET} ${C_DIM}[${p.status || 'active'}]${RESET}`);
|
|
978
|
+
const steps = Array.isArray(p.steps) ? p.steps : [];
|
|
979
|
+
for (let i = 0; i < steps.length; i++) {
|
|
980
|
+
const s = steps[i];
|
|
981
|
+
const done = s.status === 'done' || s.done;
|
|
982
|
+
appendLine(` ${done ? '✓' : '○'} ${i + 1}. ${s.description || ''}${done ? '' : ` ${C_DIM}/todo ${p.planId} ${i + 1} 勾选${RESET}`}`);
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
catch { /* 静默 */ }
|
|
987
|
+
return;
|
|
988
|
+
}
|
|
989
|
+
if (/^\/todo\s+\S+\s+\d+/.test(trimmed.toLowerCase())) {
|
|
990
|
+
const parts = trimmed.split(/\s+/);
|
|
991
|
+
const planId = parts[1];
|
|
992
|
+
const idx = parseInt(parts.slice(2).join(' ').trim(), 10) - 1 || 0;
|
|
993
|
+
try {
|
|
994
|
+
const { loadPlan, updatePlan } = await import('./agents/plan-store.js');
|
|
995
|
+
const plan = await loadPlan(planId);
|
|
996
|
+
if (!plan) {
|
|
997
|
+
appendLine(`${C_ERROR}/todo 失败: plan '${planId}' 不存在${RESET}`);
|
|
998
|
+
return;
|
|
999
|
+
}
|
|
1000
|
+
const step = plan.steps[idx];
|
|
1001
|
+
if (!step) {
|
|
1002
|
+
appendLine(`${C_ERROR}/todo 失败: 无第 ${idx + 1} 步${RESET}`);
|
|
1003
|
+
return;
|
|
1004
|
+
}
|
|
1005
|
+
await updatePlan(planId, { stepId: step.id, status: 'done' });
|
|
1006
|
+
const done = plan.steps.filter(s => s.status === 'done' || s.done).length + 1;
|
|
1007
|
+
const total = plan.steps.length;
|
|
1008
|
+
const allDone = done >= total;
|
|
1009
|
+
appendLine(`${C_OK}✓ 勾选 ${idx + 1}. ${step.description}${RESET} (${done}/${total})${allDone ? `\n ${C_ACCENT}🎯 循环达到完成标准, 可以 review 结束: /review ${planId}${RESET}` : ''}`);
|
|
1010
|
+
}
|
|
1011
|
+
catch (e) {
|
|
1012
|
+
appendLine(`${C_ERROR}/todo 失败: ${String(e.message || e).slice(0, 150)}${RESET}`);
|
|
1013
|
+
}
|
|
1014
|
+
return;
|
|
1015
|
+
}
|
|
777
1016
|
// /skill — 技能候选 (skill-writer 落盘)
|
|
778
1017
|
if (cmd === '/skill') {
|
|
779
1018
|
try {
|
|
@@ -899,14 +1138,15 @@ async function processInput(input, comm) {
|
|
|
899
1138
|
catch { /* 静默 */ }
|
|
900
1139
|
return;
|
|
901
1140
|
}
|
|
902
|
-
// /email —
|
|
1141
|
+
// /email — 邮件配置管理; /email <host:port:user:from> 设置 / /email clear 清除 (2026-08-08)
|
|
903
1142
|
if (cmd === '/email') {
|
|
904
1143
|
try {
|
|
905
|
-
const { readFile } = await import('fs/promises');
|
|
1144
|
+
const { readFile, writeFile, mkdir } = await import('fs/promises');
|
|
906
1145
|
const { join } = await import('path');
|
|
1146
|
+
const p = join(process.env.HOME || '/tmp', '.bolloon', 'smtp.json');
|
|
907
1147
|
let cfg = null;
|
|
908
1148
|
try {
|
|
909
|
-
cfg = JSON.parse(await readFile(
|
|
1149
|
+
cfg = JSON.parse(await readFile(p, 'utf-8'));
|
|
910
1150
|
}
|
|
911
1151
|
catch { /* 无 */ }
|
|
912
1152
|
appendLine(`${C_ACCENT}邮件 (SMTP):${RESET}`);
|
|
@@ -917,11 +1157,59 @@ async function processInput(input, comm) {
|
|
|
917
1157
|
appendLine(` ${C_DIM}host:${RESET} ${cfg.host || 'smtp.qq.com'}`);
|
|
918
1158
|
appendLine(` ${C_DIM}发件人:${RESET} ${cfg.from || cfg.user || '—'}`);
|
|
919
1159
|
}
|
|
1160
|
+
appendLine(` ${C_DIM}用法: /email <host:port:user:from> 设置 · /email clear 清除 · /email pass <授权码> 设密码${RESET}`);
|
|
920
1161
|
}
|
|
921
1162
|
catch { /* 静默 */ }
|
|
922
1163
|
return;
|
|
923
1164
|
}
|
|
924
|
-
|
|
1165
|
+
if (cmd === '/email clear') {
|
|
1166
|
+
try {
|
|
1167
|
+
const { writeFile, mkdir } = await import('fs/promises');
|
|
1168
|
+
const { join } = await import('path');
|
|
1169
|
+
const p = join(process.env.HOME || '/tmp', '.bolloon', 'smtp.json');
|
|
1170
|
+
await mkdir(join(process.env.HOME || '/tmp', '.bolloon'), { recursive: true });
|
|
1171
|
+
await writeFile(p, '{}', 'utf-8');
|
|
1172
|
+
appendLine(`${C_OK}✓ smtp 配置已清除${RESET}`);
|
|
1173
|
+
}
|
|
1174
|
+
catch {
|
|
1175
|
+
appendLine(`${C_ERROR}/email clear 失败${RESET}`);
|
|
1176
|
+
}
|
|
1177
|
+
return;
|
|
1178
|
+
}
|
|
1179
|
+
if (cmd.startsWith('/email ')) {
|
|
1180
|
+
const q = trimmed.slice('/email '.length).trim();
|
|
1181
|
+
try {
|
|
1182
|
+
const { writeFile, mkdir } = await import('fs/promises');
|
|
1183
|
+
const { join } = await import('path');
|
|
1184
|
+
const p = join(process.env.HOME || '/tmp', '.bolloon', 'smtp.json');
|
|
1185
|
+
await mkdir(join(process.env.HOME || '/tmp', '.bolloon'), { recursive: true });
|
|
1186
|
+
let cfg = {};
|
|
1187
|
+
try {
|
|
1188
|
+
cfg = JSON.parse(await import('fs/promises').then(m => m.readFile(p, 'utf-8')));
|
|
1189
|
+
}
|
|
1190
|
+
catch { /* 无 */ }
|
|
1191
|
+
if (cmd.startsWith('/email pass')) {
|
|
1192
|
+
cfg.pass = q;
|
|
1193
|
+
await writeFile(p, JSON.stringify(cfg, null, 2), 'utf-8');
|
|
1194
|
+
appendLine(`${C_OK}✓ SMTP 授权码已保存${RESET}`);
|
|
1195
|
+
return;
|
|
1196
|
+
}
|
|
1197
|
+
const parts = q.split(':');
|
|
1198
|
+
if (parts.length >= 3) {
|
|
1199
|
+
cfg = { host: parts[0], port: parseInt(parts[1], 10) || 465, user: parts[2], from: parts[3] || parts[2], pass: cfg.pass };
|
|
1200
|
+
await writeFile(p, JSON.stringify(cfg, null, 2), 'utf-8');
|
|
1201
|
+
appendLine(`${C_OK}✓ SMTP 已设置: ${cfg.host}:${cfg.port} (发件人 ${cfg.from})${RESET}`);
|
|
1202
|
+
}
|
|
1203
|
+
else {
|
|
1204
|
+
appendLine(`${C_DIM}格式不对: /email <host:port:user:from> 或 /email clear 或 /email pass <授权码>${RESET}`);
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
1207
|
+
catch (e) {
|
|
1208
|
+
appendLine(`${C_ERROR}/email 设置失败: ${String(e.message || e).slice(0, 150)}${RESET}`);
|
|
1209
|
+
}
|
|
1210
|
+
return;
|
|
1211
|
+
}
|
|
1212
|
+
// /loop — 当前循环状态; /loop <目标> <完成标准> 设目标+标准并启动循环 (2026-08-08)
|
|
925
1213
|
if (cmd === '/loop') {
|
|
926
1214
|
try {
|
|
927
1215
|
const a = await getAgent();
|
|
@@ -937,10 +1225,42 @@ async function processInput(input, comm) {
|
|
|
937
1225
|
appendLine(`${C_ACCENT}Loop 状态:${RESET}`);
|
|
938
1226
|
appendLine(` ${C_DIM}消息:${RESET} ${h.length} 条 (窗口 15, ${Math.max(0, h.length - 15)} 条早期压缩)`);
|
|
939
1227
|
appendLine(` ${C_DIM}token:${RESET} ${(tokens / 1000).toFixed(1)}k / 1M (${((tokens / 1_000_000) * 100).toFixed(2)}%)`);
|
|
1228
|
+
appendLine(` ${C_DIM}用法: /loop <目标> (| <完成标准>) — 设目标并循环, 达到标准自动结束${RESET}`);
|
|
940
1229
|
}
|
|
941
1230
|
catch { /* 静默 */ }
|
|
942
1231
|
return;
|
|
943
1232
|
}
|
|
1233
|
+
if (cmd.startsWith('/loop ')) {
|
|
1234
|
+
const q = trimmed.slice('/loop '.length).trim();
|
|
1235
|
+
const [goalText, criteriaText] = q.split(/\s*[||]\s*/).map(s => s.trim());
|
|
1236
|
+
try {
|
|
1237
|
+
const { createPlan } = await import('./agents/plan-store.js');
|
|
1238
|
+
const criterion = criteriaText ? `达到标准: ${criteriaText}` : '';
|
|
1239
|
+
const r = await createPlan({
|
|
1240
|
+
goal: goalText || q,
|
|
1241
|
+
steps: [goalText || q, criterion].filter(Boolean),
|
|
1242
|
+
createdBy: 'user',
|
|
1243
|
+
originChannel: cliActiveChannelId || 'cli',
|
|
1244
|
+
});
|
|
1245
|
+
if (!r.ok || !r.plan) {
|
|
1246
|
+
appendLine(`${C_ERROR}/loop 启动失败: ${r.error || '未知'}${RESET}`);
|
|
1247
|
+
return;
|
|
1248
|
+
}
|
|
1249
|
+
appendLine(`${C_OK}✓ 循环已启动: ${C_ACCENT}${goalText}${RESET}${criteriaText ? `\n ${C_DIM}完成标准: ${criteriaText}${RESET}` : ''} (${C_DIM}plan ${r.plan.planId}${RESET})`);
|
|
1250
|
+
appendLine(` ${C_DIM}当标准达成时用 /todo 勾选最后一步 / 或 review 后自动结束循环${RESET}`);
|
|
1251
|
+
// 启动自我改进循环
|
|
1252
|
+
const { runSelfImproveLoop } = await import('./agents/pi-sdk-session-factory.js');
|
|
1253
|
+
const loop = await runSelfImproveLoop(goalText).catch(() => ({ success: false, error: '未启动' }));
|
|
1254
|
+
if (loop.success)
|
|
1255
|
+
appendLine(` ${C_DIM}${loop.output}${RESET}`);
|
|
1256
|
+
else
|
|
1257
|
+
appendLine(` ${C_WARN}⚠ 循环未启动: ${loop.error}${RESET}`);
|
|
1258
|
+
}
|
|
1259
|
+
catch (e) {
|
|
1260
|
+
appendLine(`${C_ERROR}/loop 失败: ${String(e.message || e).slice(0, 200)}${RESET}`);
|
|
1261
|
+
}
|
|
1262
|
+
return;
|
|
1263
|
+
}
|
|
944
1264
|
// /judgement — 判断力列表
|
|
945
1265
|
if (cmd === '/judgement' || cmd === '/judgments') {
|
|
946
1266
|
try {
|
|
@@ -992,7 +1312,7 @@ async function processInput(input, comm) {
|
|
|
992
1312
|
catch { /* 静默 */ }
|
|
993
1313
|
return;
|
|
994
1314
|
}
|
|
995
|
-
// /dream —
|
|
1315
|
+
// /dream — 随机灵感; /dream <主题> 把用户主题落盘到梦想文档并触发循环 (2026-08-08)
|
|
996
1316
|
if (cmd === '/dream') {
|
|
997
1317
|
try {
|
|
998
1318
|
const { readContextAssets, readAssetBody } = await import('./bootstrap/context-os.js');
|
|
@@ -1014,25 +1334,63 @@ async function processInput(input, comm) {
|
|
|
1014
1334
|
const pick = pool[Math.floor(Math.random() * pool.length)];
|
|
1015
1335
|
appendLine(`${C_DIM}🌙 ${pick}${RESET}`);
|
|
1016
1336
|
}
|
|
1337
|
+
appendLine(` ${C_DIM}用法: /dream <主题> — 把主题写入梦想文档并启动循环${RESET}`);
|
|
1017
1338
|
}
|
|
1018
1339
|
catch { /* 静默 */ }
|
|
1019
1340
|
return;
|
|
1020
1341
|
}
|
|
1342
|
+
if (cmd.startsWith('/dream ')) {
|
|
1343
|
+
const topic = trimmed.slice('/dream '.length).trim();
|
|
1344
|
+
try {
|
|
1345
|
+
const { writeFile, mkdir } = await import('fs/promises');
|
|
1346
|
+
const { join } = await import('path');
|
|
1347
|
+
const home = process.env.HOME || '/tmp';
|
|
1348
|
+
const dreamDir = join(home, '.bolloon', 'dreams');
|
|
1349
|
+
await mkdir(dreamDir, { recursive: true });
|
|
1350
|
+
// 梦想文档路径: 用户名 + 主题 → 文件名 (用户信息集成进路径)
|
|
1351
|
+
const userTag = (cliAgentName || 'user').toLowerCase().replace(/[^a-z0-9]+/g, '-');
|
|
1352
|
+
const safeTopic = topic.toLowerCase().replace(/[^a-z0-9\u4e00-\u9fa5]+/g, '-').slice(0, 40);
|
|
1353
|
+
const dreamPath = join(dreamDir, `${new Date().toISOString().slice(0, 10)}-${userTag}-${safeTopic}.md`);
|
|
1354
|
+
const doc = `# 🌙 Dream: ${topic}\n\ndate: ${new Date().toISOString()}\nuser: ${cliAgentName || 'user'}\nchannel: ${cliActiveChannelId || 'cli'}\n\n> 自动生成于 /dream, 触发循环去探索这个主题。\n`;
|
|
1355
|
+
await writeFile(dreamPath, doc, 'utf-8');
|
|
1356
|
+
appendLine(`${C_OK}✓ 梦想文档已写入: ${C_DIM}${dreamPath}${RESET}`);
|
|
1357
|
+
// 触发循环
|
|
1358
|
+
const { createPlan } = await import('./agents/plan-store.js');
|
|
1359
|
+
const r = await createPlan({ goal: `探索主题: ${topic}`, steps: [`研读 ${topic}`, '产出洞察'], createdBy: 'user', originChannel: cliActiveChannelId || 'cli' });
|
|
1360
|
+
if (r.ok)
|
|
1361
|
+
appendLine(` ${C_DIM}循环已关联 plan ${r.plan?.planId}${RESET}`);
|
|
1362
|
+
const { runSelfImproveLoop } = await import('./agents/pi-sdk-session-factory.js');
|
|
1363
|
+
const loop = await runSelfImproveLoop(`探索主题: ${topic}`).catch(() => ({ success: false, error: '未启动' }));
|
|
1364
|
+
if (loop.success)
|
|
1365
|
+
appendLine(` ${C_DIM}${loop.output}${RESET}`);
|
|
1366
|
+
else
|
|
1367
|
+
appendLine(` ${C_WARN}⚠ 循环未启动: ${loop.error}${RESET}`);
|
|
1368
|
+
}
|
|
1369
|
+
catch (e) {
|
|
1370
|
+
appendLine(`${C_ERROR}/dream 失败: ${String(e.message || e).slice(0, 200)}${RESET}`);
|
|
1371
|
+
}
|
|
1372
|
+
return;
|
|
1373
|
+
}
|
|
1021
1374
|
if (trimmed.toLowerCase() === '/help' || trimmed === 'help') {
|
|
1022
1375
|
appendLine(`${C_DIM}命令:${RESET}`);
|
|
1023
1376
|
appendLine(` ${C_ACCENT}!<cmd>${RESET} 执行终端命令 ${C_DIM}如 !ls -la${RESET}`);
|
|
1024
1377
|
appendLine(` ${C_ACCENT}/queue${RESET} 切换队列模式 ${C_DIM}输入排队, 当前结束后自动执行${RESET}`);
|
|
1025
1378
|
appendLine(` ${C_ACCENT}/dequeue${RESET} 出队一条`);
|
|
1026
1379
|
appendLine(` ${C_ACCENT}/channel [名字|id|序号]${RESET} 切换当前智能体 ${C_DIM}无参列出所有; 支持名字/ID/序号三种解析${RESET}`);
|
|
1027
|
-
appendLine(` ${C_ACCENT}/model${RESET}
|
|
1380
|
+
appendLine(` ${C_ACCENT}/model${RESET} 模型供应商选择器 ${C_DIM}↑↓ 选择 · Enter 确认 · Esc 取消${RESET}`);
|
|
1381
|
+
appendLine(` ${C_ACCENT}/login${RESET} 登录 GitHub/Google 账号 (骨架) ${C_DIM}暂无真实 OAuth${RESET}`);
|
|
1028
1382
|
appendLine(` ${C_ACCENT}/logout${RESET} 查看当前供应商`);
|
|
1383
|
+
appendLine(` ${C_ACCENT}/new agent${RESET} 创建新智能体 channel ${C_DIM}/new agent <名字>${RESET}`);
|
|
1384
|
+
appendLine(` ${C_ACCENT}/new session${RESET} 开新会话 ${C_DIM}清空当前 channel 消息窗口${RESET}`);
|
|
1029
1385
|
appendLine(` ${C_ACCENT}/now${RESET} 当前状态总览 ${C_DIM}智能体/运行时间/上下文 tokens/消息数${RESET}`);
|
|
1030
1386
|
appendLine(` ${C_ACCENT}/session${RESET} 当前会话信息 ${C_DIM}channel/agent/消息窗口${RESET}`);
|
|
1031
|
-
appendLine(` ${C_ACCENT}/loop${RESET}
|
|
1387
|
+
appendLine(` ${C_ACCENT}/loop${RESET} 循环状态/启动 ${C_DIM}/loop <目标> (| <完成标准>)${RESET}`);
|
|
1032
1388
|
appendLine(` ${C_ACCENT}/memory${RESET} 记忆摘要 ${C_DIM}memory-compressor 落盘摘要${RESET}`);
|
|
1033
1389
|
appendLine(` ${C_ACCENT}/resume${RESET} 恢复上下文 ${C_DIM}最近记忆 + 进行中计划${RESET}`);
|
|
1034
|
-
appendLine(` ${C_ACCENT}/goal${RESET}
|
|
1035
|
-
appendLine(` ${C_ACCENT}/
|
|
1390
|
+
appendLine(` ${C_ACCENT}/goal${RESET} 查看/设定目标 ${C_DIM}/goal 查看 · /goal <目标> 设定+循环${RESET}`);
|
|
1391
|
+
appendLine(` ${C_ACCENT}/plan${RESET} 创建计划 ${C_DIM}/plan <目标> :: <步骤1>|<步骤2>${RESET}`);
|
|
1392
|
+
appendLine(` ${C_ACCENT}/todo${RESET} 查看/勾选循环步骤 ${C_DIM}/todo <planId> <序号>${RESET}`);
|
|
1393
|
+
appendLine(` ${C_ACCENT}/tools${RESET} 可用工具列表 (名/参数/简介)`);
|
|
1036
1394
|
appendLine(` ${C_ACCENT}/skill${RESET} 技能候选 ${C_DIM}skill-writer 沉淀候选${RESET}`);
|
|
1037
1395
|
appendLine(` ${C_ACCENT}/mcp${RESET} MCP 服务器列表`);
|
|
1038
1396
|
appendLine(` ${C_ACCENT}/agent${RESET} 当前智能体身份`);
|
|
@@ -1040,11 +1398,11 @@ async function processInput(input, comm) {
|
|
|
1040
1398
|
appendLine(` ${C_ACCENT}/ipfs${RESET} Kubo 状态 ${C_DIM}节点/peers/pins${RESET}`);
|
|
1041
1399
|
appendLine(` ${C_ACCENT}/ipns${RESET} IPNS keys + resolve`);
|
|
1042
1400
|
appendLine(` ${C_ACCENT}/wallet${RESET} 钱包状态`);
|
|
1043
|
-
appendLine(` ${C_ACCENT}/email${RESET}
|
|
1401
|
+
appendLine(` ${C_ACCENT}/email${RESET} 邮件配置管理 ${C_DIM}/email 查看 · <host:port:user:from> 设置 · clear 清除${RESET}`);
|
|
1044
1402
|
appendLine(` ${C_ACCENT}/judgement${RESET} 判断力列表`);
|
|
1045
1403
|
appendLine(` ${C_ACCENT}/insight${RESET} Context OS 洞察 (08-Insights)`);
|
|
1046
1404
|
appendLine(` ${C_ACCENT}/wiki${RESET} wiki 状态`);
|
|
1047
|
-
appendLine(` ${C_ACCENT}/dream${RESET}
|
|
1405
|
+
appendLine(` ${C_ACCENT}/dream${RESET} 随机灵感 ${C_DIM}/dream <主题> 写入梦想文档并触发循环${RESET}`);
|
|
1048
1406
|
appendLine(` ${C_ACCENT}@名字${RESET} @ 命中智能体 ${C_DIM}弹出窗选择后发送给智能体${RESET}`);
|
|
1049
1407
|
appendLine(` ${C_ACCENT}/名字${RESET} / 命中命令/技能/插件 ${C_DIM}输入 / 自动弹出${RESET}`);
|
|
1050
1408
|
appendLine(` ${C_ACCENT}#路径${RESET} # 命中文件 ${C_DIM}输入 # 自动弹出文件列表${RESET}`);
|