shennian 0.2.65 → 0.2.67
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/src/agent-env.js +2 -0
- package/dist/src/agents/command-spec.js +5 -0
- package/dist/src/agents/config-status.d.ts +5 -4
- package/dist/src/agents/config-status.js +30 -9
- package/dist/src/agents/manager.js +11 -1
- package/dist/src/agents/pi-context.d.ts +1 -1
- package/dist/src/agents/pi-context.js +4 -3
- package/dist/src/agents/pi.d.ts +1 -0
- package/dist/src/agents/pi.js +34 -5
- package/dist/src/commands/daemon.d.ts +7 -0
- package/dist/src/commands/daemon.js +28 -20
- package/dist/src/commands/external-attachments.d.ts +9 -0
- package/dist/src/commands/external-attachments.js +52 -0
- package/dist/src/commands/external.js +4 -52
- package/dist/src/commands/manager.js +49 -6
- package/dist/src/commands/tools.d.ts +2 -0
- package/dist/src/commands/tools.js +34 -0
- package/dist/src/commands/upgrade.js +1 -1
- package/dist/src/fs/boundary.js +7 -2
- package/dist/src/index.js +2 -0
- package/dist/src/manager/prompt.d.ts +1 -1
- package/dist/src/manager/prompt.js +10 -4
- package/dist/src/manager/registry.d.ts +4 -0
- package/dist/src/manager/registry.js +2 -0
- package/dist/src/manager/runtime.d.ts +8 -1
- package/dist/src/manager/runtime.js +35 -8
- package/dist/src/session/handlers/agent-config.js +3 -3
- package/dist/src/session/handlers/chat.js +40 -14
- package/dist/src/session/handlers/fs.d.ts +1 -0
- package/dist/src/session/handlers/fs.js +76 -2
- package/dist/src/session/manager.js +4 -1
- package/dist/src/session/queue.js +18 -2
- package/dist/src/session/remote-attachments.d.ts +15 -0
- package/dist/src/session/remote-attachments.js +72 -0
- package/dist/src/tools/markdown-to-pdf.d.ts +20 -0
- package/dist/src/tools/markdown-to-pdf.js +303 -0
- package/dist/src/upgrade/engine.js +5 -5
- package/package.json +1 -1
|
@@ -1,30 +1,9 @@
|
|
|
1
1
|
// @arch docs/features/wecom-managed-channel.md
|
|
2
2
|
// @test src/__tests__/external-command.test.ts
|
|
3
3
|
import fs from 'node:fs';
|
|
4
|
-
import path from 'node:path';
|
|
5
4
|
import chalk from 'chalk';
|
|
6
5
|
import { resolveShennianPath } from '../config/index.js';
|
|
7
|
-
|
|
8
|
-
'.jpg': 'image/jpeg',
|
|
9
|
-
'.jpeg': 'image/jpeg',
|
|
10
|
-
'.png': 'image/png',
|
|
11
|
-
'.gif': 'image/gif',
|
|
12
|
-
'.webp': 'image/webp',
|
|
13
|
-
'.mp4': 'video/mp4',
|
|
14
|
-
'.mov': 'video/quicktime',
|
|
15
|
-
'.pdf': 'application/pdf',
|
|
16
|
-
'.txt': 'text/plain',
|
|
17
|
-
'.md': 'text/markdown',
|
|
18
|
-
'.csv': 'text/csv',
|
|
19
|
-
'.doc': 'application/msword',
|
|
20
|
-
'.docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
21
|
-
'.xls': 'application/vnd.ms-excel',
|
|
22
|
-
'.xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
23
|
-
'.ppt': 'application/vnd.ms-powerpoint',
|
|
24
|
-
'.pptx': 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
|
25
|
-
'.zip': 'application/zip',
|
|
26
|
-
};
|
|
27
|
-
const MAX_EXTERNAL_ATTACHMENT_BYTES = Number(process.env.SHENNIAN_EXTERNAL_ATTACHMENT_MAX_BYTES || 50 * 1024 * 1024);
|
|
6
|
+
import { readExternalAttachment } from './external-attachments.js';
|
|
28
7
|
function loadManagerIpcFromRuntimeFile() {
|
|
29
8
|
try {
|
|
30
9
|
const parsed = JSON.parse(fs.readFileSync(resolveShennianPath('runtime', 'manager-ipc.json'), 'utf-8'));
|
|
@@ -50,33 +29,6 @@ function requireExternalContext(explicitSessionId) {
|
|
|
50
29
|
}
|
|
51
30
|
return { url, token, sessionId };
|
|
52
31
|
}
|
|
53
|
-
function inferMimeType(filePath, kind) {
|
|
54
|
-
const ext = path.extname(filePath).toLowerCase();
|
|
55
|
-
if (MIME_BY_EXT[ext])
|
|
56
|
-
return MIME_BY_EXT[ext];
|
|
57
|
-
if (kind === 'image')
|
|
58
|
-
return 'image/jpeg';
|
|
59
|
-
if (kind === 'video')
|
|
60
|
-
return 'video/mp4';
|
|
61
|
-
return 'application/octet-stream';
|
|
62
|
-
}
|
|
63
|
-
function readAttachment(filePath, kind) {
|
|
64
|
-
const absolutePath = path.resolve(filePath);
|
|
65
|
-
const stat = fs.statSync(absolutePath);
|
|
66
|
-
if (!stat.isFile())
|
|
67
|
-
throw new Error(`Attachment is not a file: ${absolutePath}`);
|
|
68
|
-
if (stat.size > MAX_EXTERNAL_ATTACHMENT_BYTES) {
|
|
69
|
-
throw new Error(`Attachment is too large: ${stat.size} bytes. Max: ${MAX_EXTERNAL_ATTACHMENT_BYTES} bytes.`);
|
|
70
|
-
}
|
|
71
|
-
const buffer = fs.readFileSync(absolutePath);
|
|
72
|
-
return {
|
|
73
|
-
kind,
|
|
74
|
-
name: path.basename(absolutePath),
|
|
75
|
-
mimeType: inferMimeType(absolutePath, kind),
|
|
76
|
-
size: buffer.byteLength,
|
|
77
|
-
dataBase64: buffer.toString('base64'),
|
|
78
|
-
};
|
|
79
|
-
}
|
|
80
32
|
async function sendExternal(input) {
|
|
81
33
|
const ctx = requireExternalContext(input.sessionId);
|
|
82
34
|
const response = await fetch(`${ctx.url}/external/reply`, {
|
|
@@ -120,7 +72,7 @@ export function registerExternalCommand(program) {
|
|
|
120
72
|
.action(async (opts) => {
|
|
121
73
|
await sendExternal({
|
|
122
74
|
text: opts.caption,
|
|
123
|
-
attachment:
|
|
75
|
+
attachment: readExternalAttachment(opts.path, 'image'),
|
|
124
76
|
idempotencyKey: opts.idempotencyKey,
|
|
125
77
|
sessionId: opts.sessionId,
|
|
126
78
|
});
|
|
@@ -135,7 +87,7 @@ export function registerExternalCommand(program) {
|
|
|
135
87
|
.action(async (opts) => {
|
|
136
88
|
await sendExternal({
|
|
137
89
|
text: opts.caption,
|
|
138
|
-
attachment:
|
|
90
|
+
attachment: readExternalAttachment(opts.path, 'video'),
|
|
139
91
|
idempotencyKey: opts.idempotencyKey,
|
|
140
92
|
sessionId: opts.sessionId,
|
|
141
93
|
});
|
|
@@ -150,7 +102,7 @@ export function registerExternalCommand(program) {
|
|
|
150
102
|
.action(async (opts) => {
|
|
151
103
|
await sendExternal({
|
|
152
104
|
text: opts.caption,
|
|
153
|
-
attachment:
|
|
105
|
+
attachment: readExternalAttachment(opts.path, 'file'),
|
|
154
106
|
idempotencyKey: opts.idempotencyKey,
|
|
155
107
|
sessionId: opts.sessionId,
|
|
156
108
|
});
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
// @arch docs/features/manager-agent.md
|
|
2
2
|
// @test src/__tests__/manager-runtime.test.ts
|
|
3
|
+
// @test src/__tests__/manager-command.test.ts
|
|
3
4
|
import fs from 'node:fs';
|
|
4
5
|
import chalk from 'chalk';
|
|
6
|
+
import { readExternalAttachment } from './external-attachments.js';
|
|
5
7
|
function requireManagerContext() {
|
|
6
8
|
const url = process.env.SHENNIAN_MANAGER_IPC_URL;
|
|
7
9
|
const token = process.env.SHENNIAN_MANAGER_IPC_TOKEN;
|
|
@@ -58,7 +60,7 @@ export function registerManagerCommand(program) {
|
|
|
58
60
|
sessions
|
|
59
61
|
.command('start')
|
|
60
62
|
.description('Start a worker session in the same project')
|
|
61
|
-
.requiredOption('--agent <agent>', 'Worker agent type, e.g. codex or
|
|
63
|
+
.requiredOption('--agent <agent>', 'Worker agent type, e.g. codex, claude, gemini, cursor, opencode, pi, or custom:<name>')
|
|
62
64
|
.requiredOption('--workdir <path>', 'Worker workdir; must match Manager workdir')
|
|
63
65
|
.option('--model <model>', 'Worker model id')
|
|
64
66
|
.option('--message <text>', 'Worker prompt')
|
|
@@ -134,14 +136,16 @@ export function registerManagerCommand(program) {
|
|
|
134
136
|
});
|
|
135
137
|
console.log('ok');
|
|
136
138
|
});
|
|
139
|
+
const stopWorker = async (opts) => {
|
|
140
|
+
await ipc('/sessions/stop', { sessionId: opts.sessionId });
|
|
141
|
+
console.log('ok');
|
|
142
|
+
};
|
|
137
143
|
sessions
|
|
138
144
|
.command('stop')
|
|
139
|
-
.
|
|
145
|
+
.aliases(['terminate', 'kill'])
|
|
146
|
+
.description('Stop or terminate a running managed worker')
|
|
140
147
|
.requiredOption('--session-id <id>', 'Worker session id')
|
|
141
|
-
.action(
|
|
142
|
-
await ipc('/sessions/stop', { sessionId: opts.sessionId });
|
|
143
|
-
console.log('ok');
|
|
144
|
-
});
|
|
148
|
+
.action(stopWorker);
|
|
145
149
|
sessions
|
|
146
150
|
.command('read')
|
|
147
151
|
.description('Read a managed worker transcript')
|
|
@@ -175,6 +179,45 @@ export function registerManagerCommand(program) {
|
|
|
175
179
|
.requiredOption('--text <text>', 'Message text')
|
|
176
180
|
.option('--idempotency-key <key>', 'Idempotency key')
|
|
177
181
|
.action(sendExternal);
|
|
182
|
+
external
|
|
183
|
+
.command('send-image')
|
|
184
|
+
.description('Send an image file to the Manager-bound external channel')
|
|
185
|
+
.requiredOption('--path <path>', 'Image file path')
|
|
186
|
+
.option('--caption <text>', 'Optional text to send before the image')
|
|
187
|
+
.option('--idempotency-key <key>', 'Idempotency key')
|
|
188
|
+
.action(async (opts) => {
|
|
189
|
+
await sendExternal({
|
|
190
|
+
text: opts.caption,
|
|
191
|
+
attachment: readExternalAttachment(opts.path, 'image'),
|
|
192
|
+
idempotencyKey: opts.idempotencyKey,
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
external
|
|
196
|
+
.command('send-video')
|
|
197
|
+
.description('Send a video file to the Manager-bound external channel')
|
|
198
|
+
.requiredOption('--path <path>', 'Video file path')
|
|
199
|
+
.option('--caption <text>', 'Optional text to send before the video')
|
|
200
|
+
.option('--idempotency-key <key>', 'Idempotency key')
|
|
201
|
+
.action(async (opts) => {
|
|
202
|
+
await sendExternal({
|
|
203
|
+
text: opts.caption,
|
|
204
|
+
attachment: readExternalAttachment(opts.path, 'video'),
|
|
205
|
+
idempotencyKey: opts.idempotencyKey,
|
|
206
|
+
});
|
|
207
|
+
});
|
|
208
|
+
external
|
|
209
|
+
.command('send-file')
|
|
210
|
+
.description('Send a file to the Manager-bound external channel')
|
|
211
|
+
.requiredOption('--path <path>', 'File path')
|
|
212
|
+
.option('--caption <text>', 'Optional text to send before the file')
|
|
213
|
+
.option('--idempotency-key <key>', 'Idempotency key')
|
|
214
|
+
.action(async (opts) => {
|
|
215
|
+
await sendExternal({
|
|
216
|
+
text: opts.caption,
|
|
217
|
+
attachment: readExternalAttachment(opts.path, 'file'),
|
|
218
|
+
idempotencyKey: opts.idempotencyKey,
|
|
219
|
+
});
|
|
220
|
+
});
|
|
178
221
|
external
|
|
179
222
|
.command('reply')
|
|
180
223
|
.description('Send an explicit reply to an external channel')
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// @arch docs/features/markdown-pdf-export.md
|
|
2
|
+
// @test src/__tests__/markdown-to-pdf.test.ts
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import { convertMarkdownToPdf, defaultPdfOutputPath } from '../tools/markdown-to-pdf.js';
|
|
5
|
+
export function registerToolsCommand(program) {
|
|
6
|
+
const tools = program.command('tools').description('Local deterministic Shennian tools');
|
|
7
|
+
tools
|
|
8
|
+
.command('markdown-to-pdf')
|
|
9
|
+
.alias('md-to-pdf')
|
|
10
|
+
.description('Convert a local Markdown file to PDF on this machine')
|
|
11
|
+
.argument('<input>', 'Markdown file path (.md or .mdx)')
|
|
12
|
+
.option('-o, --output <path>', 'PDF output path; defaults to the same folder and basename')
|
|
13
|
+
.option('--title <title>', 'PDF document title')
|
|
14
|
+
.option('--keep-html', 'Keep the intermediate HTML file next to the PDF')
|
|
15
|
+
.option('--chrome <path>', 'Chrome/Edge/Chromium executable path')
|
|
16
|
+
.action(async (input, opts) => {
|
|
17
|
+
try {
|
|
18
|
+
const outputPath = opts.output || defaultPdfOutputPath(input);
|
|
19
|
+
const result = await convertMarkdownToPdf(input, {
|
|
20
|
+
outputPath,
|
|
21
|
+
title: opts.title,
|
|
22
|
+
keepHtml: opts.keepHtml,
|
|
23
|
+
chromePath: opts.chrome,
|
|
24
|
+
});
|
|
25
|
+
console.log(chalk.green('✓ PDF exported'));
|
|
26
|
+
console.log(result.outputPath);
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
console.error(chalk.red('✗ Markdown to PDF failed'));
|
|
30
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
}
|
|
@@ -31,7 +31,7 @@ export function restartCurrentDaemonAfterUpgrade(deps = {}) {
|
|
|
31
31
|
}
|
|
32
32
|
catch {
|
|
33
33
|
try {
|
|
34
|
-
execSyncFn('schtasks /run /tn "ShennianAgent"', { stdio: 'pipe' });
|
|
34
|
+
execSyncFn('schtasks /run /tn "ShennianAgent"', { stdio: 'pipe', windowsHide: true });
|
|
35
35
|
}
|
|
36
36
|
catch {
|
|
37
37
|
// Fall through to process exit; manual start/status still works.
|
package/dist/src/fs/boundary.js
CHANGED
|
@@ -15,6 +15,9 @@ function hasWindowsDriveRelativePrefix(input) {
|
|
|
15
15
|
function normalizeWindowsAbsolutePath(input) {
|
|
16
16
|
return input.replace(/^[/\\]([A-Za-z]:[\\/].*)$/, '$1');
|
|
17
17
|
}
|
|
18
|
+
function isHomeRelativePath(input) {
|
|
19
|
+
return input === '~' || input.startsWith('~/') || input.startsWith('~\\');
|
|
20
|
+
}
|
|
18
21
|
function pathFlavor(input) {
|
|
19
22
|
return isWindowsAbsolutePath(normalizeWindowsAbsolutePath(input)) ? 'win32' : 'posix';
|
|
20
23
|
}
|
|
@@ -52,7 +55,7 @@ export function resolveSessionWorkDir(input) {
|
|
|
52
55
|
const normalizedInput = normalizeWindowsAbsolutePath(input);
|
|
53
56
|
const flavor = pathFlavor(normalizedInput);
|
|
54
57
|
const api = pathApi(flavor);
|
|
55
|
-
if (normalizedInput
|
|
58
|
+
if (isHomeRelativePath(normalizedInput)) {
|
|
56
59
|
return path.join(os.homedir(), normalizedInput.slice(1).replace(/^[/\\]+/, ''));
|
|
57
60
|
}
|
|
58
61
|
if (process.platform === 'win32') {
|
|
@@ -84,7 +87,9 @@ export function createAuthorizedFsRoot(rawRoot) {
|
|
|
84
87
|
}
|
|
85
88
|
export function resolveAuthorizedPath(rawPath, root) {
|
|
86
89
|
const input = String(rawPath || '').trim() || root.normalized;
|
|
87
|
-
const normalizedInput =
|
|
90
|
+
const normalizedInput = isHomeRelativePath(input)
|
|
91
|
+
? resolveSessionWorkDir(input)
|
|
92
|
+
: normalizeWindowsAbsolutePath(input);
|
|
88
93
|
const api = pathApi(root.flavor);
|
|
89
94
|
if (hasWindowsDriveRelativePrefix(normalizedInput)) {
|
|
90
95
|
return { ok: false, error: `Access denied: ${rawPath}` };
|
package/dist/src/index.js
CHANGED
|
@@ -13,6 +13,7 @@ import { registerAgentCommand } from './commands/agent.js';
|
|
|
13
13
|
import { registerManagerCommand } from './commands/manager.js';
|
|
14
14
|
import { registerExternalCommand } from './commands/external.js';
|
|
15
15
|
import { registerUpgradeCommand } from './commands/upgrade.js';
|
|
16
|
+
import { registerToolsCommand } from './commands/tools.js';
|
|
16
17
|
import { SessionManager } from './session/manager.js';
|
|
17
18
|
import { SERVERS, regionToUrl, urlToRegion } from './region.js';
|
|
18
19
|
import { getCurrentVersion, handleStartupCrashCheck, checkForUpdate, isUpgradeVersionInCooldown, recordUpgradeFailure, } from './upgrade/engine.js';
|
|
@@ -327,6 +328,7 @@ registerAgentCommand(program);
|
|
|
327
328
|
registerManagerCommand(program);
|
|
328
329
|
registerExternalCommand(program);
|
|
329
330
|
registerUpgradeCommand(program);
|
|
331
|
+
registerToolsCommand(program);
|
|
330
332
|
program.parse();
|
|
331
333
|
// ─── Auto-upgrade helper ──────────────────────────────────────────────────────
|
|
332
334
|
async function scheduleAutoUpgrade(client, policy, currentVersion) {
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const MANAGER_SYSTEM_PROMPT = "\u4F60\u662F\u9879\u76EE\u7ECF\u7406\uFF0C\u662F\u5F53\u524D\u9879\u76EE\u7684\u7BA1\u7406\u8005\u3002\n\n\u4F60\u7684\u804C\u8D23\uFF1A\n- \u7406\u89E3\u7528\u6237\u76EE\u6807\u3002\n- \u62C6\u89E3\u4EFB\u52A1\u3002\n- \u521B\u5EFA\u3001\u6307\u6D3E\u3001\u89C2\u5BDF\u548C\u505C\u6B62\u540C\u4E00\u9879\u76EE\u76EE\u5F55\u4E0B\u7684 worker Agent session\u3002\n- \u6C47\u603B worker \u7ED3\u679C\u3002\n- \u5224\u65AD\u662F\u5426\u9700\u8981\u7EE7\u7EED\u7B49\u5F85\u3001\u8C03\u6574\u5B89\u6392\u3001\u8BE2\u95EE\u7528\u6237\u6216\u9A8C\u6536\u3002\n- \u5728\u9879\u76EE .shennian/ \u76EE\u5F55\u4E0B\u7EF4\u62A4\u5FC5\u8981\u7684\u8BA1\u5212\u3001\u8BB0\u5F55\u548C\u9879\u76EE\u8BB0\u5FC6\u3002\n\n\u4F60\u7684\u8FB9\u754C\uFF1A\n- \u4E0D\u8981\u628A\u81EA\u5DF1\u5F53\u4F5C\u4E3B\u8981\u6267\u884C\u8005\u3002\n- \u4E0D\u8981\u76F4\u63A5\u7F16\u8F91\u4E1A\u52A1\u4EE3\u7801\uFF0C\u9664\u975E\u7528\u6237\u660E\u786E\u8981\u6C42\u4F60\u4EB2\u81EA\u6267\u884C\u3002\n- \u53EF\u4EE5\u8BFB\u53D6\u6587\u4EF6\u3001\u641C\u7D22\u9879\u76EE\u548C\u68C0\u67E5\u4E0A\u4E0B\u6587\uFF0C\u4EE5\u4FBF\u505A\u5224\u65AD\u3002\n- \u9700\u8981\u4FEE\u6539\u4EE3\u7801\u3001\u8FD0\u884C\u6D4B\u8BD5\u3001\u8C03\u7814\u65B9\u6848\u65F6\uFF0C\u4F18\u5148\u521B\u5EFA\u6216\u6307\u6D3E worker\u3002\n- \u6BCF\u6B21\u6536\u5230\u65B0\u4EFB\u52A1\u3001\u8865\u5145\u8981\u6C42\u3001\u7EA0\u504F\u6216\u5916\u90E8\u6D88\u606F\u65F6\uFF0C\u5148\u7528 sessions list \u67E5\u770B\u5F53\u524D\u540C\u9879\u76EE worker\uFF1B\u5982\u679C\u53EF\u80FD\u76F8\u5173\uFF0C\u518D\u7528 sessions read \u8BFB\u53D6\u5FC5\u8981\u6458\u8981\u540E\u5224\u65AD\u662F\u5426\u590D\u7528\u3002\n- \u5982\u679C\u5DF2\u6709 worker \u6B63\u5728\u5904\u7406\u540C\u4E00\u76EE\u6807\u3001\u540C\u4E00\u529F\u80FD\u533A\u3001\u540C\u4E00\u6587\u4EF6\u8303\u56F4\u6216\u540C\u4E00\u95EE\u9898\u94FE\u8DEF\uFF0C\u4F18\u5148\u7528 sessions send \u628A\u65B0\u8981\u6C42\u53D1\u7ED9\u8FD9\u4E2A worker\uFF1B\u5373\u4F7F worker \u6B63\u5FD9\u4E5F\u53EF\u4EE5\u53D1\u9001\uFF0C\u9ED8\u8BA4\u4F1A\u8FDB\u5165\u672C\u673A\u961F\u5217\uFF0C\u4E0D\u8981\u56E0\u4E3A\u5B83\u5FD9\u5C31\u65B0\u5EFA worker\u3002\n- \u53EA\u6709\u6CA1\u6709\u76F8\u5173 worker\u3001\u73B0\u6709 worker \u5DF2\u660E\u663E\u4E0D\u9002\u5408\u7EE7\u7EED\u63A8\u8FDB\u3001\u6216\u4EFB\u52A1\u9700\u8981\u5E76\u884C\u62C6\u5206\u7ED9\u4E0D\u540C\u4E13\u957F\u65F6\uFF0C\u624D\u521B\u5EFA\u65B0\u7684 worker\u3002\n- \u521B\u5EFA\u6216\u6307\u6D3E worker \u540E\uFF0C\u9664\u975E\u7528\u6237\u660E\u786E\u8981\u6C42\u4F60\u5F53\u573A\u7EE7\u7EED\u8C03\u5EA6\uFF0C\u5426\u5219\u56DE\u590D\u7528\u6237\u5DF2\u5B89\u6392\u5E76\u7ED3\u675F\u5F53\u524D turn\uFF1B\u4E0D\u8981\u4E3B\u52A8\u8F6E\u8BE2 worker \u72B6\u6001\uFF0C\u795E\u5FF5\u4F1A\u5728 worker \u7EC8\u6001\u6216\u5065\u5EB7\u6458\u8981\u5230\u6765\u65F6\u91CD\u65B0\u5524\u9192\u4F60\u3002\n- sessions read \u8FD4\u56DE\u7684\u662F\u7ED9\u7BA1\u7406\u8005\u770B\u7684\u7B80\u6D01\u8FDB\u5C55\u3001\u5DE5\u5177\u6458\u8981\u548C\u6700\u7EC8\u7ED3\u679C\uFF0C\u4E0D\u662F\u539F\u59CB\u6D41\u5F0F token\uFF1B\u4E0D\u8981\u8981\u6C42\u8BFB\u53D6\u6216\u8F6C\u8FF0\u5B8C\u6574\u6D41\u5F0F\u65E5\u5FD7\u3002\n- \u53EA\u80FD\u7BA1\u7406\u4E0E\u4F60\u5904\u4E8E\u540C\u4E00\u53F0\u673A\u5668\u3001\u540C\u4E00\u9879\u76EE\u76EE\u5F55\u7684\u4F1A\u8BDD\uFF1B\u4E0D\u8981\u8DE8\u673A\u5668\u6216\u8DE8\u9879\u76EE\u8C03\u5EA6\u3002\n- \u4E0D\u8981\u65E0\u9650\u5FAA\u73AF\uFF1B\u6CA1\u6709\u660E\u786E\u4E0B\u4E00\u6B65\u65F6\u8BE2\u95EE\u7528\u6237\u6216\u7ED3\u675F\u5F53\u524D turn \u7B49\u5F85\u7CFB\u7EDF\u4E8B\u4EF6\u3002\n- \u4E0D\u8981\u81EA\u5DF1\u8BBE\u7F6E\u5B9A\u65F6\u5524\u9192\uFF1B\u795E\u5FF5\u4F1A\u5728\u7528\u6237\u6D88\u606F\u3001worker \u7EC8\u6001\u6216 worker \u957F\u8FD0\u884C\u5065\u5EB7\u6458\u8981\u5230\u6765\u65F6\u5524\u9192\u4F60\u3002\n- \u5916\u90E8\u6D88\u606F\u901A\u9053\u4E8B\u4EF6\u4F1A\u50CF\u666E\u901A\u7528\u6237\u6D88\u606F\u4E00\u6837\u9001\u8FBE\uFF0C\u683C\u5F0F\u7C7B\u4F3C\u201C\u5916\u90E8\u6D88\u606F / \u53D1\u9001\u4EBA\u201D\u540E\u8DDF\u6D88\u606F\u5185\u5BB9\uFF0C\u53EF\u80FD\u662F\u5408\u5E76\u6D88\u606F\uFF0C\u4E5F\u53EF\u80FD\u5305\u542B\u56FE\u7247\u3001\u89C6\u9891\u6216\u6587\u4EF6 URL\u3002\n- \u5BF9\u5916\u4F60\u662F\u5F53\u524D\u9879\u76EE\u7684\u9879\u76EE\u7ECF\u7406\uFF0C\u4E0D\u8981\u81EA\u79F0\u795E\u5FF5\u3001Manager Agent \u6216 worker\uFF0C\u4E5F\u4E0D\u8981\u89E3\u91CA\u5185\u90E8\u8C03\u5EA6\u673A\u5236\uFF1B\u53EA\u5728\u9700\u8981\u65F6\u7528\u201C\u6211\u8FD9\u8FB9/\u6211\u4EEC\u8FD9\u8FB9\u201D\u6C9F\u901A\u3002\n- \u5BF9\u5916\u56DE\u590D\u5FC5\u987B\u50CF\u771F\u4EBA\u804A\u5929\uFF1A\u77ED\u56DE\u590D\u4E00\u6761\u53D1\u5B8C\uFF1B\u5185\u5BB9\u8F83\u591A\u65F6\u6309\u81EA\u7136\u6BB5\u62C6\u6210 2-4 \u6761\u8FDE\u7EED\u6D88\u606F\uFF0C\u6BCF\u6761\u53EA\u8BB2\u4E00\u4E2A\u5B8C\u6574\u4E3B\u9898\u3002\n- \u907F\u514D\u628A\u8D85\u8FC7 300-500 \u5B57\u7684\u5185\u5BB9\u585E\u8FDB\u5355\u6761\u6D88\u606F\uFF1B\u4E0D\u8981\u4F7F\u7528 Markdown\u3001\u7F16\u53F7\u5217\u8868\u3001\u9879\u76EE\u7B26\u53F7\u6216\u5B57\u9762 \\n\u3002\n- \u5916\u90E8\u6D88\u606F\u4E0E\u5F53\u524D\u9879\u76EE\u65E0\u5173\u65F6\u53EF\u4EE5\u5FFD\u7565\uFF1B\u9700\u8981\u8F83\u957F\u5904\u7406\u65F6\uFF0C\u5148\u7B80\u77ED\u56DE\u590D\u201C\u6536\u5230\uFF0C\u6211\u5148\u5904\u7406/\u5B89\u6392\u4E00\u4E0B\u201D\uFF0C\u518D\u521B\u5EFA\u6216\u6307\u6D3E worker\u3002\n- \u5411\u5916\u90E8\u7FA4\u53D1\
|
|
1
|
+
export declare const MANAGER_SYSTEM_PROMPT = "\u4F60\u662F\u9879\u76EE\u7ECF\u7406\uFF0C\u662F\u5F53\u524D\u9879\u76EE\u7684\u7BA1\u7406\u8005\u3002\n\n\u4F60\u7684\u804C\u8D23\uFF1A\n- \u7406\u89E3\u7528\u6237\u76EE\u6807\u3002\n- \u62C6\u89E3\u4EFB\u52A1\u3002\n- \u521B\u5EFA\u3001\u6307\u6D3E\u3001\u89C2\u5BDF\u548C\u505C\u6B62\u540C\u4E00\u9879\u76EE\u76EE\u5F55\u4E0B\u7684 worker Agent session\u3002\n- \u6C47\u603B worker \u7ED3\u679C\u3002\n- \u5224\u65AD\u662F\u5426\u9700\u8981\u7EE7\u7EED\u7B49\u5F85\u3001\u8C03\u6574\u5B89\u6392\u3001\u8BE2\u95EE\u7528\u6237\u6216\u9A8C\u6536\u3002\n- \u5728\u9879\u76EE .shennian/ \u76EE\u5F55\u4E0B\u7EF4\u62A4\u5FC5\u8981\u7684\u8BA1\u5212\u3001\u8BB0\u5F55\u548C\u9879\u76EE\u8BB0\u5FC6\u3002\n\n\u4F60\u7684\u8FB9\u754C\uFF1A\n- \u4E0D\u8981\u628A\u81EA\u5DF1\u5F53\u4F5C\u4E3B\u8981\u6267\u884C\u8005\u3002\n- \u4E0D\u8981\u76F4\u63A5\u7F16\u8F91\u4E1A\u52A1\u4EE3\u7801\uFF0C\u9664\u975E\u7528\u6237\u660E\u786E\u8981\u6C42\u4F60\u4EB2\u81EA\u6267\u884C\u3002\n- \u53EF\u4EE5\u8BFB\u53D6\u6587\u4EF6\u3001\u641C\u7D22\u9879\u76EE\u548C\u68C0\u67E5\u4E0A\u4E0B\u6587\uFF0C\u4EE5\u4FBF\u505A\u5224\u65AD\u3002\n- \u9700\u8981\u4FEE\u6539\u4EE3\u7801\u3001\u8FD0\u884C\u6D4B\u8BD5\u3001\u8C03\u7814\u65B9\u6848\u65F6\uFF0C\u4F18\u5148\u521B\u5EFA\u6216\u6307\u6D3E worker\u3002\n- \u6BCF\u6B21\u6536\u5230\u65B0\u4EFB\u52A1\u3001\u8865\u5145\u8981\u6C42\u3001\u7EA0\u504F\u6216\u5916\u90E8\u6D88\u606F\u65F6\uFF0C\u5148\u7528 sessions list \u67E5\u770B\u5F53\u524D\u540C\u9879\u76EE worker\uFF1B\u5982\u679C\u53EF\u80FD\u76F8\u5173\uFF0C\u518D\u7528 sessions read \u8BFB\u53D6\u5FC5\u8981\u6458\u8981\u540E\u5224\u65AD\u662F\u5426\u590D\u7528\u3002\n- \u5982\u679C\u5DF2\u6709 worker \u6B63\u5728\u5904\u7406\u540C\u4E00\u76EE\u6807\u3001\u540C\u4E00\u529F\u80FD\u533A\u3001\u540C\u4E00\u6587\u4EF6\u8303\u56F4\u6216\u540C\u4E00\u95EE\u9898\u94FE\u8DEF\uFF0C\u4F18\u5148\u7528 sessions send \u628A\u65B0\u8981\u6C42\u53D1\u7ED9\u8FD9\u4E2A worker\uFF1B\u5373\u4F7F worker \u6B63\u5FD9\u4E5F\u53EF\u4EE5\u53D1\u9001\uFF0C\u9ED8\u8BA4\u4F1A\u8FDB\u5165\u672C\u673A\u961F\u5217\uFF0C\u4E0D\u8981\u56E0\u4E3A\u5B83\u5FD9\u5C31\u65B0\u5EFA worker\u3002\n- \u53EA\u6709\u6CA1\u6709\u76F8\u5173 worker\u3001\u73B0\u6709 worker \u5DF2\u660E\u663E\u4E0D\u9002\u5408\u7EE7\u7EED\u63A8\u8FDB\u3001\u6216\u4EFB\u52A1\u9700\u8981\u5E76\u884C\u62C6\u5206\u7ED9\u4E0D\u540C\u4E13\u957F\u65F6\uFF0C\u624D\u521B\u5EFA\u65B0\u7684 worker\u3002\n- \u521B\u5EFA\u6216\u6307\u6D3E worker \u540E\uFF0C\u9664\u975E\u7528\u6237\u660E\u786E\u8981\u6C42\u4F60\u5F53\u573A\u7EE7\u7EED\u8C03\u5EA6\uFF0C\u5426\u5219\u56DE\u590D\u7528\u6237\u5DF2\u5B89\u6392\u5E76\u7ED3\u675F\u5F53\u524D turn\uFF1B\u4E0D\u8981\u4E3B\u52A8\u8F6E\u8BE2 worker \u72B6\u6001\uFF0C\u795E\u5FF5\u4F1A\u5728 worker \u7EC8\u6001\u6216\u5065\u5EB7\u6458\u8981\u5230\u6765\u65F6\u91CD\u65B0\u5524\u9192\u4F60\u3002\n- sessions read \u8FD4\u56DE\u7684\u662F\u7ED9\u7BA1\u7406\u8005\u770B\u7684\u7B80\u6D01\u8FDB\u5C55\u3001\u5DE5\u5177\u6458\u8981\u548C\u6700\u7EC8\u7ED3\u679C\uFF0C\u4E0D\u662F\u539F\u59CB\u6D41\u5F0F token\uFF1B\u4E0D\u8981\u8981\u6C42\u8BFB\u53D6\u6216\u8F6C\u8FF0\u5B8C\u6574\u6D41\u5F0F\u65E5\u5FD7\u3002\n- \u53EA\u80FD\u7BA1\u7406\u4E0E\u4F60\u5904\u4E8E\u540C\u4E00\u53F0\u673A\u5668\u3001\u540C\u4E00\u9879\u76EE\u76EE\u5F55\u7684\u4F1A\u8BDD\uFF1B\u4E0D\u8981\u8DE8\u673A\u5668\u6216\u8DE8\u9879\u76EE\u8C03\u5EA6\u3002\n- \u4E0D\u8981\u65E0\u9650\u5FAA\u73AF\uFF1B\u6CA1\u6709\u660E\u786E\u4E0B\u4E00\u6B65\u65F6\u8BE2\u95EE\u7528\u6237\u6216\u7ED3\u675F\u5F53\u524D turn \u7B49\u5F85\u7CFB\u7EDF\u4E8B\u4EF6\u3002\n- \u4E0D\u8981\u81EA\u5DF1\u8BBE\u7F6E\u5B9A\u65F6\u5524\u9192\uFF1B\u795E\u5FF5\u4F1A\u5728\u7528\u6237\u6D88\u606F\u3001worker \u7EC8\u6001\u6216 worker \u957F\u8FD0\u884C\u5065\u5EB7\u6458\u8981\u5230\u6765\u65F6\u5524\u9192\u4F60\u3002\n- \u5916\u90E8\u6D88\u606F\u901A\u9053\u4E8B\u4EF6\u4F1A\u50CF\u666E\u901A\u7528\u6237\u6D88\u606F\u4E00\u6837\u9001\u8FBE\uFF0C\u683C\u5F0F\u7C7B\u4F3C\u201C\u5916\u90E8\u6D88\u606F / \u53D1\u9001\u4EBA\u201D\u540E\u8DDF\u6D88\u606F\u5185\u5BB9\uFF0C\u53EF\u80FD\u662F\u5408\u5E76\u6D88\u606F\uFF0C\u4E5F\u53EF\u80FD\u5305\u542B\u56FE\u7247\u3001\u89C6\u9891\u6216\u6587\u4EF6 URL\u3002\n- \u5BF9\u5916\u4F60\u662F\u5F53\u524D\u9879\u76EE\u7684\u9879\u76EE\u7ECF\u7406\uFF0C\u4E0D\u8981\u81EA\u79F0\u795E\u5FF5\u3001Manager Agent \u6216 worker\uFF0C\u4E5F\u4E0D\u8981\u89E3\u91CA\u5185\u90E8\u8C03\u5EA6\u673A\u5236\uFF1B\u53EA\u5728\u9700\u8981\u65F6\u7528\u201C\u6211\u8FD9\u8FB9/\u6211\u4EEC\u8FD9\u8FB9\u201D\u6C9F\u901A\u3002\n- \u5BF9\u5916\u56DE\u590D\u5FC5\u987B\u50CF\u771F\u4EBA\u804A\u5929\uFF1A\u77ED\u56DE\u590D\u4E00\u6761\u53D1\u5B8C\uFF1B\u5185\u5BB9\u8F83\u591A\u65F6\u6309\u81EA\u7136\u6BB5\u62C6\u6210 2-4 \u6761\u8FDE\u7EED\u6D88\u606F\uFF0C\u6BCF\u6761\u53EA\u8BB2\u4E00\u4E2A\u5B8C\u6574\u4E3B\u9898\u3002\n- \u907F\u514D\u628A\u8D85\u8FC7 300-500 \u5B57\u7684\u5185\u5BB9\u585E\u8FDB\u5355\u6761\u6D88\u606F\uFF1B\u4E0D\u8981\u4F7F\u7528 Markdown\u3001\u7F16\u53F7\u5217\u8868\u3001\u9879\u76EE\u7B26\u53F7\u6216\u5B57\u9762 \\n\u3002\n- \u5916\u90E8\u6D88\u606F\u4E0E\u5F53\u524D\u9879\u76EE\u65E0\u5173\u65F6\u53EF\u4EE5\u5FFD\u7565\uFF1B\u9700\u8981\u8F83\u957F\u5904\u7406\u65F6\uFF0C\u5148\u7B80\u77ED\u56DE\u590D\u201C\u6536\u5230\uFF0C\u6211\u5148\u5904\u7406/\u5B89\u6392\u4E00\u4E0B\u201D\uFF0C\u518D\u521B\u5EFA\u6216\u6307\u6D3E worker\u3002\n- \u5411\u5916\u90E8\u7FA4\u53D1\u6587\u5B57\u4E00\u5F8B\u8C03\u7528 shennian manager external send --text \"<\u6D88\u606F\u5185\u5BB9>\"\n- \u5411\u5916\u90E8\u7FA4\u53D1\u56FE\u7247\u8C03\u7528 shennian manager external send-image --path \"<\u56FE\u7247\u7EDD\u5BF9\u8DEF\u5F84>\" --caption \"<\u53EF\u9009\u8BF4\u660E>\"\n- \u5411\u5916\u90E8\u7FA4\u53D1\u89C6\u9891\u8C03\u7528 shennian manager external send-video --path \"<\u89C6\u9891\u7EDD\u5BF9\u8DEF\u5F84>\" --caption \"<\u53EF\u9009\u8BF4\u660E>\"\n- \u5411\u5916\u90E8\u7FA4\u53D1\u6587\u4EF6\u8C03\u7528 shennian manager external send-file --path \"<\u6587\u4EF6\u7EDD\u5BF9\u8DEF\u5F84>\" --caption \"<\u53EF\u9009\u8BF4\u660E>\"\n- \u4E0D\u8981\u628A\u6240\u6709\u7EC6\u8282\u585E\u8FDB\u5BF9\u8BDD\u4E0A\u4E0B\u6587\uFF1B\u9700\u8981\u957F\u671F\u4FDD\u5B58\u7684\u4FE1\u606F\u5199\u5230\u9879\u76EE .shennian/ \u4E0B\u3002\n\n\u9700\u8981\u7BA1\u7406 worker \u6216\u5916\u90E8\u901A\u9053\u65F6\uFF0C\u4F7F\u7528\u672C\u5730\u547D\u4EE4\uFF1A\n- shennian manager sessions list --json\n- shennian manager sessions start --agent <codex|claude|gemini|cursor|opencode|pi|custom:name> --workdir <path> --message <text>\n- shennian manager sessions send --session-id <id> --message <text>\n- shennian manager sessions send --session-id <id> --message <text> --direct\n- shennian manager sessions queue list --session-id <id> --json\n- shennian manager sessions queue edit --session-id <id> --message-id <queueMessageId> --message <text>\n- shennian manager sessions queue delete --session-id <id> --message-id <queueMessageId>\n- shennian manager sessions stop --session-id <id>\uFF08\u7EC8\u6B62\u6B63\u5728\u8FD0\u884C\u7684 worker\uFF1B\u4E5F\u53EF\u7528 terminate/kill \u522B\u540D\uFF09\n- shennian manager sessions read --session-id <id> --limit 200 --json\n- shennian manager memory path\n- shennian manager external send --text <text>\n- shennian manager external send-image --path <path> --caption <text>\n- shennian manager external send-video --path <path> --caption <text>\n- shennian manager external send-file --path <path> --caption <text>\n\n\u53EF\u521B\u5EFA\u7684 worker Agent \u5305\u62EC Codex\u3001Claude Code\u3001Gemini\u3001Cursor\u3001opencode\u3001Nian\uFF0C\u4EE5\u53CA\u672C\u673A custom agent\uFF08custom:<name>\uFF09\u3002\u9ED8\u8BA4\u7528 sessions send \u6392\u961F\u53D1\u9001 worker \u6D88\u606F\uFF1Aworker \u6B63\u5FD9\u65F6\u6D88\u606F\u4F1A\u5728\u672C\u673A daemon \u961F\u5217\u91CC\u7B49\u5F85\uFF0Cworker \u7A7A\u95F2\u65F6\u81EA\u52A8\u6267\u884C\u3002\u961F\u5217\u91CC\u7684\u672A\u6267\u884C\u6D88\u606F\u53EF\u4EE5 list/edit/delete\uFF1B\u5DF2\u7ECF\u5F00\u59CB\u6267\u884C\u7684\u6D88\u606F\u4E0D\u80FD\u7F16\u8F91\u6216\u5220\u9664\uFF0C\u53EA\u80FD stop \u540E\u91CD\u65B0\u53D1\u9001\u3002\u53EA\u6709\u660E\u786E\u9700\u8981\u6253\u65AD\u987A\u5E8F\u65F6\u624D\u4F7F\u7528 --direct\u3002\n\n\u8FD9\u4E9B\u547D\u4EE4\u5DF2\u7ECF\u7531\u795E\u5FF5\u6CE8\u5165\u5F53\u524D Manager \u8EAB\u4EFD\u548C\u540C\u9879\u76EE\u6743\u9650\u8FB9\u754C\u3002\u4E0D\u8981\u5C1D\u8BD5\u4F2A\u9020 Manager session id\u3002";
|
|
2
2
|
export declare function buildManagerPrompt(userText: string): string;
|
|
@@ -28,23 +28,29 @@ export const MANAGER_SYSTEM_PROMPT = `你是项目经理,是当前项目的管
|
|
|
28
28
|
- 对外回复必须像真人聊天:短回复一条发完;内容较多时按自然段拆成 2-4 条连续消息,每条只讲一个完整主题。
|
|
29
29
|
- 避免把超过 300-500 字的内容塞进单条消息;不要使用 Markdown、编号列表、项目符号或字面 \\n。
|
|
30
30
|
- 外部消息与当前项目无关时可以忽略;需要较长处理时,先简短回复“收到,我先处理/安排一下”,再创建或指派 worker。
|
|
31
|
-
-
|
|
31
|
+
- 向外部群发文字一律调用 shennian manager external send --text "<消息内容>"
|
|
32
|
+
- 向外部群发图片调用 shennian manager external send-image --path "<图片绝对路径>" --caption "<可选说明>"
|
|
33
|
+
- 向外部群发视频调用 shennian manager external send-video --path "<视频绝对路径>" --caption "<可选说明>"
|
|
34
|
+
- 向外部群发文件调用 shennian manager external send-file --path "<文件绝对路径>" --caption "<可选说明>"
|
|
32
35
|
- 不要把所有细节塞进对话上下文;需要长期保存的信息写到项目 .shennian/ 下。
|
|
33
36
|
|
|
34
37
|
需要管理 worker 或外部通道时,使用本地命令:
|
|
35
38
|
- shennian manager sessions list --json
|
|
36
|
-
- shennian manager sessions start --agent codex --workdir <path> --message <text>
|
|
39
|
+
- shennian manager sessions start --agent <codex|claude|gemini|cursor|opencode|pi|custom:name> --workdir <path> --message <text>
|
|
37
40
|
- shennian manager sessions send --session-id <id> --message <text>
|
|
38
41
|
- shennian manager sessions send --session-id <id> --message <text> --direct
|
|
39
42
|
- shennian manager sessions queue list --session-id <id> --json
|
|
40
43
|
- shennian manager sessions queue edit --session-id <id> --message-id <queueMessageId> --message <text>
|
|
41
44
|
- shennian manager sessions queue delete --session-id <id> --message-id <queueMessageId>
|
|
42
|
-
- shennian manager sessions stop --session-id <id
|
|
45
|
+
- shennian manager sessions stop --session-id <id>(终止正在运行的 worker;也可用 terminate/kill 别名)
|
|
43
46
|
- shennian manager sessions read --session-id <id> --limit 200 --json
|
|
44
47
|
- shennian manager memory path
|
|
45
48
|
- shennian manager external send --text <text>
|
|
49
|
+
- shennian manager external send-image --path <path> --caption <text>
|
|
50
|
+
- shennian manager external send-video --path <path> --caption <text>
|
|
51
|
+
- shennian manager external send-file --path <path> --caption <text>
|
|
46
52
|
|
|
47
|
-
|
|
53
|
+
可创建的 worker Agent 包括 Codex、Claude Code、Gemini、Cursor、opencode、Nian,以及本机 custom agent(custom:<name>)。默认用 sessions send 排队发送 worker 消息:worker 正忙时消息会在本机 daemon 队列里等待,worker 空闲时自动执行。队列里的未执行消息可以 list/edit/delete;已经开始执行的消息不能编辑或删除,只能 stop 后重新发送。只有明确需要打断顺序时才使用 --direct。
|
|
48
54
|
|
|
49
55
|
这些命令已经由神念注入当前 Manager 身份和同项目权限边界。不要尝试伪造 Manager session id。`;
|
|
50
56
|
export function buildManagerPrompt(userText) {
|
|
@@ -28,6 +28,8 @@ export type ManagerRecord = {
|
|
|
28
28
|
workDir: string;
|
|
29
29
|
machineId?: string | null;
|
|
30
30
|
modelId: 'codex' | 'claude';
|
|
31
|
+
defaultWorkerAgentType?: AgentType | null;
|
|
32
|
+
defaultWorkerModelId?: string | null;
|
|
31
33
|
status: ManagerStatus;
|
|
32
34
|
managedWorkerSessionIds: string[];
|
|
33
35
|
attachedExternalChannels: string[];
|
|
@@ -59,6 +61,8 @@ export declare class ManagerRegistry {
|
|
|
59
61
|
workDir: string;
|
|
60
62
|
machineId?: string | null;
|
|
61
63
|
modelId: 'codex' | 'claude';
|
|
64
|
+
defaultWorkerAgentType?: AgentType | null;
|
|
65
|
+
defaultWorkerModelId?: string | null;
|
|
62
66
|
status?: ManagerStatus;
|
|
63
67
|
}): ManagerRecord;
|
|
64
68
|
getManager(sessionId: string): ManagerRecord | undefined;
|
|
@@ -42,6 +42,8 @@ export class ManagerRegistry {
|
|
|
42
42
|
workDir: input.workDir,
|
|
43
43
|
machineId: input.machineId ?? existing?.machineId ?? null,
|
|
44
44
|
modelId: input.modelId,
|
|
45
|
+
defaultWorkerAgentType: input.defaultWorkerAgentType ?? existing?.defaultWorkerAgentType ?? null,
|
|
46
|
+
defaultWorkerModelId: input.defaultWorkerModelId ?? existing?.defaultWorkerModelId ?? null,
|
|
45
47
|
status: input.status ?? existing?.status ?? 'idle',
|
|
46
48
|
managedWorkerSessionIds: existing?.managedWorkerSessionIds ?? [],
|
|
47
49
|
attachedExternalChannels: existing?.attachedExternalChannels ?? [],
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AgentEvent, AgentAdapter } from '../agents/adapter.js';
|
|
2
|
-
import { type ReqFrame } from '@shennian/wire';
|
|
2
|
+
import { type AgentType, type ReqFrame } from '@shennian/wire';
|
|
3
3
|
import { ManagerRegistry } from './registry.js';
|
|
4
4
|
import type { SessionManagerRuntime } from '../session/types.js';
|
|
5
5
|
import { ChannelRuntime } from '../channels/runtime.js';
|
|
@@ -33,8 +33,15 @@ export declare class ManagerRuntimeService {
|
|
|
33
33
|
agentSessionId?: string | null;
|
|
34
34
|
workDir: string;
|
|
35
35
|
modelId: 'codex' | 'claude';
|
|
36
|
+
defaultWorkerAgentType?: AgentType | null;
|
|
37
|
+
defaultWorkerModelId?: string | null;
|
|
36
38
|
status?: 'idle' | 'running' | 'interrupting';
|
|
37
39
|
}): void;
|
|
40
|
+
setManagerWorkerDefaults(sessionId: string, agentType?: AgentType | null, modelId?: string | null): void;
|
|
41
|
+
getManagerWorkerDefaults(sessionId: string): {
|
|
42
|
+
agentType: AgentType | null;
|
|
43
|
+
modelId: string | null;
|
|
44
|
+
};
|
|
38
45
|
noteManagerAgentSession(sessionId: string, agentSessionId: string | null, workDir: string, modelId: 'codex' | 'claude'): void;
|
|
39
46
|
noteAgentEvent(sessionId: string, event: AgentEvent): void;
|
|
40
47
|
private findWorker;
|
|
@@ -5,7 +5,7 @@ import { randomBytes, randomUUID } from 'node:crypto';
|
|
|
5
5
|
import fs from 'node:fs';
|
|
6
6
|
import os from 'node:os';
|
|
7
7
|
import path from 'node:path';
|
|
8
|
-
import { extractPayloadText, isToolPayload } from '@shennian/wire';
|
|
8
|
+
import { AVAILABLE_BUILTIN_AGENT_TYPES, extractPayloadText, isToolPayload, } from '@shennian/wire';
|
|
9
9
|
import { ManagerRegistry } from './registry.js';
|
|
10
10
|
import { readMessages } from '../session/store.js';
|
|
11
11
|
import { ChannelRuntime } from '../channels/runtime.js';
|
|
@@ -29,6 +29,13 @@ function runIdFromMessageId(id) {
|
|
|
29
29
|
const match = /^agent-(.+)-\d+$/.exec(id);
|
|
30
30
|
return match?.[1] ?? null;
|
|
31
31
|
}
|
|
32
|
+
function isManagerWorkerAgentType(agentType) {
|
|
33
|
+
if (agentType === 'manager')
|
|
34
|
+
return false;
|
|
35
|
+
if (agentType.startsWith('custom:'))
|
|
36
|
+
return true;
|
|
37
|
+
return AVAILABLE_BUILTIN_AGENT_TYPES.includes(agentType);
|
|
38
|
+
}
|
|
32
39
|
function seqFromMessageId(id) {
|
|
33
40
|
const match = /^agent-.+-(\d+)$/.exec(id);
|
|
34
41
|
if (!match)
|
|
@@ -253,6 +260,23 @@ export class ManagerRuntimeService {
|
|
|
253
260
|
machineId: process.env.SHENNIAN_MACHINE_ID ?? null,
|
|
254
261
|
});
|
|
255
262
|
}
|
|
263
|
+
setManagerWorkerDefaults(sessionId, agentType, modelId) {
|
|
264
|
+
const manager = this.registry.getManager(sessionId);
|
|
265
|
+
if (!manager)
|
|
266
|
+
return;
|
|
267
|
+
this.registry.upsertManager({
|
|
268
|
+
...manager,
|
|
269
|
+
defaultWorkerAgentType: agentType ?? null,
|
|
270
|
+
defaultWorkerModelId: modelId ?? null,
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
getManagerWorkerDefaults(sessionId) {
|
|
274
|
+
const manager = this.registry.getManager(sessionId);
|
|
275
|
+
return {
|
|
276
|
+
agentType: manager?.defaultWorkerAgentType ?? null,
|
|
277
|
+
modelId: manager?.defaultWorkerModelId ?? null,
|
|
278
|
+
};
|
|
279
|
+
}
|
|
256
280
|
noteManagerAgentSession(sessionId, agentSessionId, workDir, modelId) {
|
|
257
281
|
this.registry.upsertManager({
|
|
258
282
|
sessionId,
|
|
@@ -393,9 +417,10 @@ export class ManagerRuntimeService {
|
|
|
393
417
|
if (url.pathname === '/sessions/start') {
|
|
394
418
|
if (!manager)
|
|
395
419
|
throw new Error('Manager runtime is not registered');
|
|
396
|
-
const agentType = String(body.agentType || body.agent || 'codex');
|
|
397
|
-
if (agentType
|
|
398
|
-
throw new Error(
|
|
420
|
+
const agentType = String(body.agentType || body.agent || manager.defaultWorkerAgentType || 'codex');
|
|
421
|
+
if (!isManagerWorkerAgentType(agentType)) {
|
|
422
|
+
throw new Error(`Unsupported manager worker agent: ${agentType}`);
|
|
423
|
+
}
|
|
399
424
|
const workDir = normalizeWorkDir(String(body.workDir || manager.workDir));
|
|
400
425
|
if (workDir !== manager.workDir)
|
|
401
426
|
throw new Error('Manager can only start workers in the same workDir');
|
|
@@ -408,7 +433,8 @@ export class ManagerRuntimeService {
|
|
|
408
433
|
workDir,
|
|
409
434
|
summary: message.slice(0, 120),
|
|
410
435
|
});
|
|
411
|
-
|
|
436
|
+
const workerModelId = String(body.modelId || (agentType === manager.defaultWorkerAgentType ? manager.defaultWorkerModelId ?? '' : ''));
|
|
437
|
+
await this.dispatchChatSend(worker.sessionId, agentType, workDir, message, null, workerModelId);
|
|
412
438
|
json(res, 200, { ok: true, session: worker });
|
|
413
439
|
return;
|
|
414
440
|
}
|
|
@@ -419,11 +445,12 @@ export class ManagerRuntimeService {
|
|
|
419
445
|
const worker = this.registry.getWorkerForManager(managerSessionId, sessionId);
|
|
420
446
|
if (!worker)
|
|
421
447
|
throw new Error('Worker not found in this manager scope');
|
|
448
|
+
const workerModelId = String(body.modelId || (worker.agentType === manager?.defaultWorkerAgentType ? manager.defaultWorkerModelId ?? '' : ''));
|
|
422
449
|
if (enqueue) {
|
|
423
|
-
await this.dispatchChatEnqueue(worker.sessionId, worker.agentType, worker.workDir, message, worker.agentSessionId ?? null,
|
|
450
|
+
await this.dispatchChatEnqueue(worker.sessionId, worker.agentType, worker.workDir, message, worker.agentSessionId ?? null, workerModelId);
|
|
424
451
|
}
|
|
425
452
|
else {
|
|
426
|
-
await this.dispatchChatSend(worker.sessionId, worker.agentType, worker.workDir, message, worker.agentSessionId ?? null,
|
|
453
|
+
await this.dispatchChatSend(worker.sessionId, worker.agentType, worker.workDir, message, worker.agentSessionId ?? null, workerModelId);
|
|
427
454
|
}
|
|
428
455
|
json(res, 200, { ok: true });
|
|
429
456
|
return;
|
|
@@ -472,7 +499,7 @@ export class ManagerRuntimeService {
|
|
|
472
499
|
json(res, 200, { ok: true, queue: this.opts.getRuntime().chatQueue?.getSnapshot(sessionId) });
|
|
473
500
|
return;
|
|
474
501
|
}
|
|
475
|
-
if (url.pathname === '/sessions/stop') {
|
|
502
|
+
if (url.pathname === '/sessions/stop' || url.pathname === '/sessions/terminate') {
|
|
476
503
|
const sessionId = String(body.sessionId || '');
|
|
477
504
|
const worker = this.registry.getWorkerForManager(managerSessionId, sessionId);
|
|
478
505
|
if (!worker)
|
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
import { buildManagedAgentEnv, deleteManagedAgentProviderConfig, getAgentConfigSummary, upsertManagedAgentProviderConfig, } from '../../agents/config-status.js';
|
|
4
4
|
import { handleAgentsRefresh } from './agents.js';
|
|
5
5
|
function normalizeAgent(value) {
|
|
6
|
-
if (value === 'codex' || value === 'claude')
|
|
6
|
+
if (value === 'codex' || value === 'claude' || value === 'pi')
|
|
7
7
|
return value;
|
|
8
|
-
throw new Error('Only Codex
|
|
8
|
+
throw new Error('Only Codex, Claude Code, and Nian provider config are supported');
|
|
9
9
|
}
|
|
10
10
|
export async function handleAgentConfigGet(runtime, req) {
|
|
11
11
|
const agent = normalizeAgent(req.params.agent);
|
|
@@ -65,7 +65,7 @@ async function broadcastAgents(runtime) {
|
|
|
65
65
|
export function getManagedEnvForAgent(agentType) {
|
|
66
66
|
// Re-export point kept here for session handlers to avoid reaching into storage internals.
|
|
67
67
|
// The implementation lives in agents/config-status.ts.
|
|
68
|
-
return agentType === 'codex' || agentType === 'claude'
|
|
68
|
+
return agentType === 'codex' || agentType === 'claude' || agentType === 'pi'
|
|
69
69
|
? buildManagedAgentEnv(agentType)
|
|
70
70
|
: {};
|
|
71
71
|
}
|