shennian 0.3.86 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -2
- package/dist/bin/shennian.js +1 -1
- package/dist/publish-build-manifest.json +32 -27
- package/dist/src/agents/adapter.js +1 -19
- package/dist/src/agents/claude-managed-permissions.js +1 -248
- package/dist/src/agents/claude.js +8 -393
- package/dist/src/agents/codex-managed-permissions.js +1 -294
- package/dist/src/agents/codex-managed-room-tools.js +1 -249
- package/dist/src/agents/codex-utils.js +7 -200
- package/dist/src/agents/codex.d.ts +7 -1
- package/dist/src/agents/codex.js +17 -1352
- package/dist/src/agents/config-status.js +1 -226
- package/dist/src/agents/cursor-managed-permissions.js +2 -299
- package/dist/src/agents/cursor.js +1 -314
- package/dist/src/agents/custom.js +4 -418
- package/dist/src/agents/external-channel-instructions.js +11 -94
- package/dist/src/agents/gemini.js +1 -180
- package/dist/src/agents/manager.js +18 -250
- package/dist/src/agents/model-registry/cache.js +1 -37
- package/dist/src/agents/model-registry/discovery.js +2 -188
- package/dist/src/agents/model-registry/parsers.js +4 -521
- package/dist/src/agents/model-registry/runner.js +1 -30
- package/dist/src/agents/model-registry/service.js +1 -115
- package/dist/src/agents/model-registry/types.js +1 -8
- package/dist/src/agents/model-registry.js +1 -19
- package/dist/src/agents/native-capabilities.js +1 -237
- package/dist/src/agents/openclaw.js +2 -293
- package/dist/src/agents/opencode-managed-permissions.js +2 -187
- package/dist/src/agents/opencode.js +1 -298
- package/dist/src/agents/platform-instructions.js +17 -46
- package/dist/src/agents/runtime-compatibility.js +1 -44
- package/dist/src/agents/workbuddy-managed-permissions.js +2 -329
- package/dist/src/agents/workbuddy.js +3 -444
- package/dist/src/commands/logout.d.ts +3 -0
- package/dist/src/commands/logout.js +1 -0
- package/dist/src/daemon-ipc/paths.js +1 -1
- package/dist/src/fs/boundary.js +1 -185
- package/dist/src/fs/handler.js +1 -130
- package/dist/src/fs/security.js +1 -32
- package/dist/src/fs/text-decoder.js +1 -110
- package/dist/src/index.js +3 -587
- package/dist/src/manager/prompt.js +29 -34
- package/dist/src/manager/registry.js +1 -317
- package/dist/src/manager/retired-external-channel-compat.js +1 -40
- package/dist/src/manager/runtime.js +23 -1514
- package/dist/src/native-fusion/codex-parser.d.ts +3 -1
- package/dist/src/native-fusion/codex-parser.js +4 -660
- package/dist/src/native-fusion/codex-user-input.js +5 -102
- package/dist/src/native-fusion/config.js +1 -5
- package/dist/src/native-fusion/file-watcher.js +1 -191
- package/dist/src/native-fusion/local-repository.js +2 -569
- package/dist/src/native-fusion/opencode-parser.js +3 -130
- package/dist/src/native-fusion/parser-common.d.ts +6 -1
- package/dist/src/native-fusion/parser-common.js +4 -4
- package/dist/src/native-fusion/parsers.d.ts +4 -2
- package/dist/src/native-fusion/parsers.js +3 -140
- package/dist/src/native-fusion/service.js +2 -739
- package/dist/src/native-fusion/session-header.js +2 -101
- package/dist/src/native-fusion/state.js +2 -104
- package/dist/src/native-fusion/types.js +1 -1
- package/dist/src/personal-sync/machine-authority.js +1 -34
- package/dist/src/room/activation-audit.js +2 -45
- package/dist/src/room/activation-policy.js +1 -30
- package/dist/src/room/activation-service.js +8 -408
- package/dist/src/room/invite-bootstrap.js +1 -156
- package/dist/src/room/managed-prompt-composer.d.ts +1 -1
- package/dist/src/room/managed-prompt-composer.js +17 -16
- package/dist/src/room/managed-room-binding.js +1 -167
- package/dist/src/room/managed-room-ipc.d.ts +4 -0
- package/dist/src/room/managed-room-ipc.js +2 -2
- package/dist/src/room/mirror-sync-service.d.ts +1 -0
- package/dist/src/room/mirror-sync-service.js +1 -183
- package/dist/src/room/room-binding-client.js +1 -151
- package/dist/src/session/archive-zip.js +1 -220
- package/dist/src/session/deletion-sync-state.js +2 -136
- package/dist/src/session/handlers/agent-capabilities.js +1 -14
- package/dist/src/session/handlers/agent-config.js +1 -150
- package/dist/src/session/handlers/agents.js +1 -55
- package/dist/src/session/handlers/chat.js +2 -1030
- package/dist/src/session/handlers/control.js +1 -61
- package/dist/src/session/handlers/fs.js +1 -594
- package/dist/src/session/handlers/message-status.js +1 -15
- package/dist/src/session/handlers/remote-access.js +1 -43
- package/dist/src/session/handlers/room-managed.js +1 -101
- package/dist/src/session/handlers/session-refresh.js +1 -35
- package/dist/src/session/handlers/title.js +1 -79
- package/dist/src/session/handlers/tool-detail.js +1 -149
- package/dist/src/session/handlers/voice-window.js +1 -62
- package/dist/src/session/history-subscriptions.js +1 -470
- package/dist/src/session/manager.d.ts +1 -0
- package/dist/src/session/manager.js +1 -658
- package/dist/src/session/native-cleanup-outbox.js +2 -287
- package/dist/src/session/native-session-lifecycle.js +6 -327
- package/dist/src/session/queue.d.ts +8 -0
- package/dist/src/session/queue.js +6 -6
- package/dist/src/upgrade/scheduler.js +1 -126
- package/node_modules/@shennian/wire/dist/agent-workspace.js +2 -2
- package/package.json +1 -1
|
@@ -1,180 +1 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { randomUUID } from 'node:crypto';
|
|
3
|
-
import { AgentAdapter, registerAgent } from './adapter.js';
|
|
4
|
-
import { resolveBuiltinCommand, spawnResolvedCommand } from './command-spec.js';
|
|
5
|
-
import { buildAgentProcessEnv } from '../agent-env.js';
|
|
6
|
-
import { buildInlineChannelPreamble } from './platform-instructions.js';
|
|
7
|
-
function num(v) {
|
|
8
|
-
return typeof v === 'number' && Number.isFinite(v) ? v : undefined;
|
|
9
|
-
}
|
|
10
|
-
function usageFromStats(stats) {
|
|
11
|
-
if (!stats)
|
|
12
|
-
return undefined;
|
|
13
|
-
const input = num(stats.input_tokens) ??
|
|
14
|
-
num(stats.prompt_tokens) ??
|
|
15
|
-
num(stats.inputTokens) ??
|
|
16
|
-
num(stats.promptTokenCount);
|
|
17
|
-
const output = num(stats.output_tokens) ??
|
|
18
|
-
num(stats.completion_tokens) ??
|
|
19
|
-
num(stats.outputTokens) ??
|
|
20
|
-
num(stats.candidatesTokenCount) ??
|
|
21
|
-
num(stats.responseTokenCount);
|
|
22
|
-
if (input === undefined && output === undefined)
|
|
23
|
-
return undefined;
|
|
24
|
-
return {
|
|
25
|
-
inputTokens: input ?? 0,
|
|
26
|
-
outputTokens: output ?? 0,
|
|
27
|
-
};
|
|
28
|
-
}
|
|
29
|
-
export class GeminiAdapter extends AgentAdapter {
|
|
30
|
-
type = 'gemini';
|
|
31
|
-
process = null;
|
|
32
|
-
agentSessionId = null;
|
|
33
|
-
workDir = null;
|
|
34
|
-
seq = 0;
|
|
35
|
-
runId = '';
|
|
36
|
-
externalChannel = null;
|
|
37
|
-
shennianSessionId = null;
|
|
38
|
-
extraEnv = {};
|
|
39
|
-
configure(options) {
|
|
40
|
-
this.shennianSessionId = options.sessionId ?? null;
|
|
41
|
-
this.externalChannel = options.externalChannel ?? null;
|
|
42
|
-
this.extraEnv = options.env ?? {};
|
|
43
|
-
}
|
|
44
|
-
async start(_sessionId, workDir, agentSessionId) {
|
|
45
|
-
this.workDir = workDir;
|
|
46
|
-
this.seq = 0;
|
|
47
|
-
if (agentSessionId)
|
|
48
|
-
this.agentSessionId = agentSessionId;
|
|
49
|
-
}
|
|
50
|
-
async send(text, modelId, _reasoningEffort, _attachments, agentSystemPrompt) {
|
|
51
|
-
await this.killProcess();
|
|
52
|
-
this.runId = randomUUID();
|
|
53
|
-
this.seq = 0;
|
|
54
|
-
const outgoing = `${buildInlineChannelPreamble(this.workDir ?? process.cwd(), this.externalChannel, this.shennianSessionId ?? undefined, agentSystemPrompt)}${text}`;
|
|
55
|
-
const args = ['-p', outgoing, '--output-format', 'stream-json'];
|
|
56
|
-
if (modelId) {
|
|
57
|
-
args.push('-m', modelId);
|
|
58
|
-
}
|
|
59
|
-
if (this.agentSessionId) {
|
|
60
|
-
args.push('--resume', this.agentSessionId);
|
|
61
|
-
}
|
|
62
|
-
this.spawnAndParse(args);
|
|
63
|
-
}
|
|
64
|
-
async resume(agentSessionId) {
|
|
65
|
-
await this.killProcess();
|
|
66
|
-
this.agentSessionId = agentSessionId;
|
|
67
|
-
this.runId = randomUUID();
|
|
68
|
-
this.seq = 0;
|
|
69
|
-
this.spawnAndParse(['--resume', agentSessionId, '--output-format', 'stream-json']);
|
|
70
|
-
}
|
|
71
|
-
async stop() {
|
|
72
|
-
await this.killProcess();
|
|
73
|
-
}
|
|
74
|
-
spawnAndParse(args) {
|
|
75
|
-
const spec = resolveBuiltinCommand('gemini');
|
|
76
|
-
if (!spec) {
|
|
77
|
-
this.emit('error', new Error('Command "gemini" not found. Is Gemini CLI installed?'));
|
|
78
|
-
return;
|
|
79
|
-
}
|
|
80
|
-
const proc = spawnResolvedCommand(spec, args, {
|
|
81
|
-
cwd: this.workDir ?? undefined,
|
|
82
|
-
stdio: ['ignore', 'pipe', 'pipe'],
|
|
83
|
-
env: buildAgentProcessEnv(this.extraEnv),
|
|
84
|
-
});
|
|
85
|
-
this.process = proc;
|
|
86
|
-
const rl = createInterface({ input: proc.stdout });
|
|
87
|
-
rl.on('line', (line) => {
|
|
88
|
-
if (!line.trim())
|
|
89
|
-
return;
|
|
90
|
-
try {
|
|
91
|
-
const obj = JSON.parse(line);
|
|
92
|
-
this.handleStreamEvent(obj);
|
|
93
|
-
}
|
|
94
|
-
catch {
|
|
95
|
-
this.emitEvent({ state: 'delta', text: line });
|
|
96
|
-
}
|
|
97
|
-
});
|
|
98
|
-
let stderrBuf = '';
|
|
99
|
-
proc.stderr?.on('data', (chunk) => {
|
|
100
|
-
stderrBuf += chunk.toString();
|
|
101
|
-
});
|
|
102
|
-
proc.on('close', (code) => {
|
|
103
|
-
if (this.process !== proc)
|
|
104
|
-
return;
|
|
105
|
-
this.process = null;
|
|
106
|
-
if (code !== 0 && code !== null) {
|
|
107
|
-
const msg = stderrBuf.trim() || `gemini exited with code ${code}`;
|
|
108
|
-
this.emitEvent({ state: 'error', message: msg });
|
|
109
|
-
}
|
|
110
|
-
});
|
|
111
|
-
proc.on('error', (err) => {
|
|
112
|
-
if (this.process !== proc)
|
|
113
|
-
return;
|
|
114
|
-
this.process = null;
|
|
115
|
-
if (err.code === 'ENOENT') {
|
|
116
|
-
this.emit('error', new Error('Command "gemini" not found. Is Gemini CLI installed?'));
|
|
117
|
-
}
|
|
118
|
-
else {
|
|
119
|
-
this.emit('error', err);
|
|
120
|
-
}
|
|
121
|
-
});
|
|
122
|
-
}
|
|
123
|
-
handleStreamEvent(obj) {
|
|
124
|
-
switch (obj.type) {
|
|
125
|
-
case 'init': {
|
|
126
|
-
if (obj.session_id) {
|
|
127
|
-
this.agentSessionId = obj.session_id;
|
|
128
|
-
}
|
|
129
|
-
break;
|
|
130
|
-
}
|
|
131
|
-
case 'message': {
|
|
132
|
-
if (obj.role === 'assistant' && obj.content) {
|
|
133
|
-
this.emitEvent({ state: 'delta', text: obj.content });
|
|
134
|
-
}
|
|
135
|
-
break;
|
|
136
|
-
}
|
|
137
|
-
case 'result': {
|
|
138
|
-
if (obj.status === 'success') {
|
|
139
|
-
this.emitEvent({
|
|
140
|
-
state: 'final',
|
|
141
|
-
agentSessionId: this.agentSessionId ?? undefined,
|
|
142
|
-
usage: usageFromStats(obj.stats),
|
|
143
|
-
});
|
|
144
|
-
}
|
|
145
|
-
else if (obj.status === 'error') {
|
|
146
|
-
const msg = (typeof obj.error === 'string' && obj.error) ||
|
|
147
|
-
(typeof obj.message === 'string' && obj.message) ||
|
|
148
|
-
'gemini result error';
|
|
149
|
-
this.emitEvent({ state: 'error', message: msg });
|
|
150
|
-
}
|
|
151
|
-
break;
|
|
152
|
-
}
|
|
153
|
-
default:
|
|
154
|
-
break;
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
emitEvent(partial) {
|
|
158
|
-
const event = {
|
|
159
|
-
...partial,
|
|
160
|
-
runId: this.runId,
|
|
161
|
-
seq: this.seq++,
|
|
162
|
-
};
|
|
163
|
-
this.emit('agentEvent', event);
|
|
164
|
-
}
|
|
165
|
-
async killProcess() {
|
|
166
|
-
const proc = this.process;
|
|
167
|
-
if (!proc)
|
|
168
|
-
return;
|
|
169
|
-
this.process = null;
|
|
170
|
-
proc.kill('SIGTERM');
|
|
171
|
-
await new Promise((resolve) => {
|
|
172
|
-
proc.on('close', resolve);
|
|
173
|
-
setTimeout(() => {
|
|
174
|
-
proc.kill('SIGKILL');
|
|
175
|
-
resolve();
|
|
176
|
-
}, 3000);
|
|
177
|
-
});
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
registerAgent('gemini', () => new GeminiAdapter());
|
|
1
|
+
import{createInterface as l}from"node:readline";import{randomUUID as u}from"node:crypto";import{AgentAdapter as d,registerAgent as h}from"./adapter.js";import{resolveBuiltinCommand as c,spawnResolvedCommand as p}from"./command-spec.js";import{buildAgentProcessEnv as f}from"../agent-env.js";import{buildInlineChannelPreamble as g}from"./platform-instructions.js";function r(t){return typeof t=="number"&&Number.isFinite(t)?t:void 0}function k(t){if(!t)return;const e=r(t.input_tokens)??r(t.prompt_tokens)??r(t.inputTokens)??r(t.promptTokenCount),n=r(t.output_tokens)??r(t.completion_tokens)??r(t.outputTokens)??r(t.candidatesTokenCount)??r(t.responseTokenCount);if(!(e===void 0&&n===void 0))return{inputTokens:e??0,outputTokens:n??0}}class I extends d{type="gemini";process=null;agentSessionId=null;workDir=null;seq=0;runId="";externalChannel=null;shennianSessionId=null;extraEnv={};configure(e){this.shennianSessionId=e.sessionId??null,this.externalChannel=e.externalChannel??null,this.extraEnv=e.env??{}}async start(e,n,i){this.workDir=n,this.seq=0,i&&(this.agentSessionId=i)}async send(e,n,i,m,a){await this.killProcess(),this.runId=u(),this.seq=0;const o=["-p",`${g(this.workDir??process.cwd(),this.externalChannel,this.shennianSessionId??void 0,a)}${e}`,"--output-format","stream-json"];n&&o.push("-m",n),this.agentSessionId&&o.push("--resume",this.agentSessionId),this.spawnAndParse(o)}async resume(e){await this.killProcess(),this.agentSessionId=e,this.runId=u(),this.seq=0,this.spawnAndParse(["--resume",e,"--output-format","stream-json"])}async stop(){await this.killProcess()}spawnAndParse(e){const n=c("gemini");if(!n){this.emit("error",new Error('Command "gemini" not found. Is Gemini CLI installed?'));return}const i=p(n,e,{cwd:this.workDir??void 0,stdio:["ignore","pipe","pipe"],env:f(this.extraEnv)});this.process=i,l({input:i.stdout}).on("line",s=>{if(s.trim())try{const o=JSON.parse(s);this.handleStreamEvent(o)}catch{this.emitEvent({state:"delta",text:s})}});let a="";i.stderr?.on("data",s=>{a+=s.toString()}),i.on("close",s=>{if(this.process===i&&(this.process=null,s!==0&&s!==null)){const o=a.trim()||`gemini exited with code ${s}`;this.emitEvent({state:"error",message:o})}}),i.on("error",s=>{this.process===i&&(this.process=null,s.code==="ENOENT"?this.emit("error",new Error('Command "gemini" not found. Is Gemini CLI installed?')):this.emit("error",s))})}handleStreamEvent(e){switch(e.type){case"init":{e.session_id&&(this.agentSessionId=e.session_id);break}case"message":{e.role==="assistant"&&e.content&&this.emitEvent({state:"delta",text:e.content});break}case"result":{if(e.status==="success")this.emitEvent({state:"final",agentSessionId:this.agentSessionId??void 0,usage:k(e.stats)});else if(e.status==="error"){const n=typeof e.error=="string"&&e.error||typeof e.message=="string"&&e.message||"gemini result error";this.emitEvent({state:"error",message:n})}break}default:break}}emitEvent(e){const n={...e,runId:this.runId,seq:this.seq++};this.emit("agentEvent",n)}async killProcess(){const e=this.process;e&&(this.process=null,e.kill("SIGTERM"),await new Promise(n=>{e.on("close",n),setTimeout(()=>{e.kill("SIGKILL"),n()},3e3)}))}}h("gemini",()=>new I);export{I as GeminiAdapter};
|
|
@@ -1,250 +1,18 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
try {
|
|
20
|
-
return fs.readFileSync(agentsPath, 'utf8').trim();
|
|
21
|
-
}
|
|
22
|
-
catch {
|
|
23
|
-
return '';
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
function managerInstructionsPath(workDir, sessionId) {
|
|
27
|
-
return path.join(workDir, '.shennian', 'runtime', 'manager', safeSessionDirName(sessionId), 'instructions.md');
|
|
28
|
-
}
|
|
29
|
-
function buildStableManagerInstructions(workDir, managerSessionId) {
|
|
30
|
-
const projectInstructions = readProjectAgentsMd(workDir);
|
|
31
|
-
const managerRuntime = getManagerRuntimeService();
|
|
32
|
-
const channelInstructions = managerRuntime?.getManagerExternalChannelSystemPrompt(managerSessionId) ?? '';
|
|
33
|
-
const workerDefaults = typeof managerRuntime?.getManagerWorkerDefaults === 'function'
|
|
34
|
-
? managerRuntime.getManagerWorkerDefaults(managerSessionId)
|
|
35
|
-
: undefined;
|
|
36
|
-
const workerDefaultInstructions = workerDefaults?.agentType
|
|
37
|
-
? `默认 worker Agent:${workerDefaults.agentType}${workerDefaults.modelId ? `\n默认 worker 模型:${workerDefaults.modelId}` : ''}。除非用户明确要求或任务明显需要其他 Agent/模型,创建 worker 时优先使用这个默认组合。`
|
|
38
|
-
: '';
|
|
39
|
-
const sections = [
|
|
40
|
-
'# Shennian Manager Instructions',
|
|
41
|
-
'This file is generated by Shennian for a Manager Agent substrate session. Do not edit it by hand.',
|
|
42
|
-
projectInstructions
|
|
43
|
-
? `## Project Instructions\n\n${projectInstructions}`
|
|
44
|
-
: '',
|
|
45
|
-
`## Manager Instructions\n\n${MANAGER_SYSTEM_PROMPT}`,
|
|
46
|
-
workerDefaultInstructions
|
|
47
|
-
? `## Worker Defaults\n\n${workerDefaultInstructions}`
|
|
48
|
-
: '',
|
|
49
|
-
channelInstructions
|
|
50
|
-
? `## External Message Channel Instructions\n\n${channelInstructions}`
|
|
51
|
-
: '',
|
|
52
|
-
PLATFORM_OUTPUT_INSTRUCTIONS,
|
|
53
|
-
].filter(Boolean);
|
|
54
|
-
return `${sections.join('\n\n')}\n`;
|
|
55
|
-
}
|
|
56
|
-
function ensureManagerInstructionsFile(workDir, sessionId) {
|
|
57
|
-
const filePath = managerInstructionsPath(workDir, sessionId);
|
|
58
|
-
const content = buildStableManagerInstructions(workDir, sessionId);
|
|
59
|
-
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
60
|
-
try {
|
|
61
|
-
if (fs.readFileSync(filePath, 'utf8') === content)
|
|
62
|
-
return filePath;
|
|
63
|
-
}
|
|
64
|
-
catch {
|
|
65
|
-
// Write the generated instruction file below.
|
|
66
|
-
}
|
|
67
|
-
fs.writeFileSync(filePath, content);
|
|
68
|
-
return filePath;
|
|
69
|
-
}
|
|
70
|
-
/** Decorates a real provider Adapter with Manager session capabilities. New
|
|
71
|
-
* sessions use this class and keep their real agentType/modelId. */
|
|
72
|
-
export class ManagerModeAdapter extends AgentAdapter {
|
|
73
|
-
inner;
|
|
74
|
-
substrate;
|
|
75
|
-
type;
|
|
76
|
-
sessionId = '';
|
|
77
|
-
workDir = '';
|
|
78
|
-
agentSessionId = null;
|
|
79
|
-
configured = {};
|
|
80
|
-
constructor(inner, substrate) {
|
|
81
|
-
super();
|
|
82
|
-
this.inner = inner;
|
|
83
|
-
this.substrate = substrate;
|
|
84
|
-
this.type = inner.type;
|
|
85
|
-
inner.on('agentEvent', (event) => {
|
|
86
|
-
if (event.agentSessionId)
|
|
87
|
-
this.agentSessionId = event.agentSessionId;
|
|
88
|
-
this.emit('agentEvent', { ...event, source: event.source ?? substrate });
|
|
89
|
-
});
|
|
90
|
-
inner.on('error', (error) => this.emit('error', error));
|
|
91
|
-
}
|
|
92
|
-
configure(options) {
|
|
93
|
-
this.configured = options;
|
|
94
|
-
if (options.sessionId) {
|
|
95
|
-
getManagerRuntimeService()?.setManagedAgentPolicy(options.sessionId, options.managedAgentPolicy ?? null);
|
|
96
|
-
}
|
|
97
|
-
this.applyEnvironment();
|
|
98
|
-
}
|
|
99
|
-
async start(sessionId, workDir, agentSessionId) {
|
|
100
|
-
this.sessionId = sessionId;
|
|
101
|
-
this.workDir = workDir;
|
|
102
|
-
this.agentSessionId = agentSessionId ?? null;
|
|
103
|
-
this.applyEnvironment();
|
|
104
|
-
await this.inner.start(sessionId, workDir, agentSessionId);
|
|
105
|
-
getManagerRuntimeService()?.registerManager({
|
|
106
|
-
sessionId, agentSessionId: this.agentSessionId, workDir, modelId: this.substrate,
|
|
107
|
-
agentType: this.type === 'claude' ? 'claude' : 'codex', status: 'idle',
|
|
108
|
-
});
|
|
109
|
-
getManagerRuntimeService()?.bindManagerAdapterEvents(sessionId, this.inner);
|
|
110
|
-
}
|
|
111
|
-
async send(text, modelId, reasoningEffort, attachments, agentSystemPrompt, context) {
|
|
112
|
-
getManagerRuntimeService()?.registerManager({
|
|
113
|
-
sessionId: this.sessionId, agentSessionId: this.agentSessionId, workDir: this.workDir,
|
|
114
|
-
modelId: this.substrate, agentType: this.type === 'claude' ? 'claude' : 'codex',
|
|
115
|
-
providerModelId: modelId ?? null, status: 'running',
|
|
116
|
-
});
|
|
117
|
-
await getManagerRuntimeService()?.ready();
|
|
118
|
-
this.applyEnvironment();
|
|
119
|
-
const managerInstructions = buildStableManagerInstructions(this.workDir, this.sessionId);
|
|
120
|
-
const combined = combineManagerInstructions(managerInstructions, agentSystemPrompt);
|
|
121
|
-
return this.inner.send(buildManagerPrompt(text), modelId, reasoningEffort, attachments, combined, context);
|
|
122
|
-
}
|
|
123
|
-
async resume(agentSessionId) {
|
|
124
|
-
this.agentSessionId = agentSessionId;
|
|
125
|
-
await this.inner.resume(agentSessionId);
|
|
126
|
-
}
|
|
127
|
-
async stop() {
|
|
128
|
-
getManagerRuntimeService()?.setManagedAgentPolicy(this.sessionId, null);
|
|
129
|
-
await this.inner.stop();
|
|
130
|
-
}
|
|
131
|
-
getStatus() { return this.inner.getStatus?.() ?? Promise.resolve({ active: false }); }
|
|
132
|
-
setTitle(agentSessionId, title, workDir) {
|
|
133
|
-
return this.inner.setTitle?.(agentSessionId, title, workDir) ?? Promise.resolve();
|
|
134
|
-
}
|
|
135
|
-
applyEnvironment() {
|
|
136
|
-
const managerEnv = this.sessionId
|
|
137
|
-
? getManagerRuntimeService()?.getInjectedEnv(this.sessionId, this.agentSessionId, this.workDir, this.substrate) ?? {}
|
|
138
|
-
: {};
|
|
139
|
-
this.inner.configure?.({
|
|
140
|
-
...this.configured,
|
|
141
|
-
sessionId: this.configured.sessionId ?? this.sessionId,
|
|
142
|
-
env: { ...(this.configured.env ?? {}), ...managerEnv },
|
|
143
|
-
managedAgentPolicy: this.configured.managedAgentPolicy ?? null,
|
|
144
|
-
});
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
export function combineManagerInstructions(managerInstructions, systemPrompt) {
|
|
148
|
-
const prompt = systemPrompt?.trim() ?? '';
|
|
149
|
-
if (!prompt)
|
|
150
|
-
return managerInstructions;
|
|
151
|
-
const roomMarkers = ['[智能体私有系统提示词]', '[智能体身份]'];
|
|
152
|
-
const index = roomMarkers
|
|
153
|
-
.map((marker) => prompt.indexOf(marker))
|
|
154
|
-
.filter((position) => position >= 0)
|
|
155
|
-
.sort((a, b) => a - b)[0] ?? -1;
|
|
156
|
-
if (prompt.startsWith('[神念受管会话规则]') && index >= 0) {
|
|
157
|
-
return `${prompt.slice(0, index)}[会话模式]\n${managerInstructions.trim()}\n\n${prompt.slice(index)}`;
|
|
158
|
-
}
|
|
159
|
-
return `${managerInstructions.trim()}\n\n${prompt}`;
|
|
160
|
-
}
|
|
161
|
-
export class ManagerAdapter extends AgentAdapter {
|
|
162
|
-
type = 'manager';
|
|
163
|
-
inner = null;
|
|
164
|
-
sessionId = '';
|
|
165
|
-
workDir = '';
|
|
166
|
-
agentSessionId = null;
|
|
167
|
-
modelId = 'codex';
|
|
168
|
-
async start(sessionId, workDir, agentSessionId) {
|
|
169
|
-
this.sessionId = sessionId;
|
|
170
|
-
this.workDir = workDir;
|
|
171
|
-
this.agentSessionId = agentSessionId ?? null;
|
|
172
|
-
getManagerRuntimeService()?.registerManager({
|
|
173
|
-
sessionId,
|
|
174
|
-
agentSessionId: this.agentSessionId,
|
|
175
|
-
workDir,
|
|
176
|
-
modelId: this.modelId,
|
|
177
|
-
agentType: 'manager',
|
|
178
|
-
status: 'idle',
|
|
179
|
-
});
|
|
180
|
-
}
|
|
181
|
-
async send(text, modelId) {
|
|
182
|
-
const nextModel = normalizeManagerModel(modelId);
|
|
183
|
-
if (!this.inner || nextModel !== this.modelId) {
|
|
184
|
-
await this.inner?.stop().catch(() => { });
|
|
185
|
-
this.inner?.removeAllListeners();
|
|
186
|
-
this.modelId = nextModel;
|
|
187
|
-
const instructionsFile = ensureManagerInstructionsFile(this.workDir, this.sessionId);
|
|
188
|
-
const systemPrompt = buildStableManagerInstructions(this.workDir, this.sessionId);
|
|
189
|
-
this.inner = nextModel === 'claude'
|
|
190
|
-
? new ClaudeAdapter({ systemPrompt, hidden: true })
|
|
191
|
-
: new CodexAdapter({ modelInstructionsFile: instructionsFile, hidden: true });
|
|
192
|
-
this.bindInner(this.inner);
|
|
193
|
-
await this.inner.start(this.sessionId, this.workDir, this.agentSessionId);
|
|
194
|
-
getManagerRuntimeService()?.bindManagerAdapterEvents(this.sessionId, this.inner);
|
|
195
|
-
}
|
|
196
|
-
getManagerRuntimeService()?.registerManager({
|
|
197
|
-
sessionId: this.sessionId,
|
|
198
|
-
agentSessionId: this.agentSessionId,
|
|
199
|
-
workDir: this.workDir,
|
|
200
|
-
modelId: this.modelId,
|
|
201
|
-
agentType: 'manager',
|
|
202
|
-
status: 'running',
|
|
203
|
-
});
|
|
204
|
-
await getManagerRuntimeService()?.ready();
|
|
205
|
-
await this.withManagerEnv(() => this.inner.send(buildManagerPrompt(text)));
|
|
206
|
-
}
|
|
207
|
-
async resume(agentSessionId) {
|
|
208
|
-
this.agentSessionId = agentSessionId;
|
|
209
|
-
if (this.inner)
|
|
210
|
-
await this.inner.resume(agentSessionId);
|
|
211
|
-
}
|
|
212
|
-
async stop() {
|
|
213
|
-
await this.inner?.stop();
|
|
214
|
-
}
|
|
215
|
-
bindInner(inner) {
|
|
216
|
-
inner.on('agentEvent', (event) => {
|
|
217
|
-
if (event.agentSessionId)
|
|
218
|
-
this.agentSessionId = event.agentSessionId;
|
|
219
|
-
this.emit('agentEvent', this.wrapEvent(event));
|
|
220
|
-
});
|
|
221
|
-
inner.on('error', (error) => this.emit('error', error));
|
|
222
|
-
}
|
|
223
|
-
wrapEvent(event) {
|
|
224
|
-
return {
|
|
225
|
-
...event,
|
|
226
|
-
source: event.source ?? this.modelId,
|
|
227
|
-
agentSessionId: event.agentSessionId ?? this.agentSessionId ?? undefined,
|
|
228
|
-
};
|
|
229
|
-
}
|
|
230
|
-
async withManagerEnv(work) {
|
|
231
|
-
const env = getManagerRuntimeService()?.getInjectedEnv(this.sessionId, this.agentSessionId, this.workDir, this.modelId) ?? {};
|
|
232
|
-
const previous = new Map();
|
|
233
|
-
for (const [key, value] of Object.entries(env)) {
|
|
234
|
-
previous.set(key, process.env[key]);
|
|
235
|
-
process.env[key] = value;
|
|
236
|
-
}
|
|
237
|
-
try {
|
|
238
|
-
return await work();
|
|
239
|
-
}
|
|
240
|
-
finally {
|
|
241
|
-
for (const [key, value] of previous) {
|
|
242
|
-
if (value === undefined)
|
|
243
|
-
delete process.env[key];
|
|
244
|
-
else
|
|
245
|
-
process.env[key] = value;
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
registerAgent('manager', () => new ManagerAdapter());
|
|
1
|
+
import{AgentAdapter as g,registerAgent as I}from"./adapter.js";import{CodexAdapter as p}from"./codex.js";import{ClaudeAdapter as f}from"./claude.js";import{MANAGER_SYSTEM_PROMPT as y,buildManagerPrompt as l}from"../manager/prompt.js";import{getManagerRuntimeService as a}from"../manager/runtime.js";import d from"node:fs";import c from"node:path";import{PLATFORM_OUTPUT_INSTRUCTIONS as S}from"./platform-instructions.js";function M(i){return i==="claude"?"claude":"codex"}function w(i){return i.replace(/[^a-zA-Z0-9_.-]/g,"_")}function v(i){const e=c.join(i,"AGENTS.md");try{return d.readFileSync(e,"utf8").trim()}catch{return""}}function A(i,e){return c.join(i,".shennian","runtime","manager",w(e),"instructions.md")}function u(i,e){const n=v(i),t=a(),r=t?.getManagerExternalChannelSystemPrompt(e)??"",s=typeof t?.getManagerWorkerDefaults=="function"?t.getManagerWorkerDefaults(e):void 0,o=s?.agentType?`\u9ED8\u8BA4 worker Agent\uFF1A${s.agentType}${s.modelId?`
|
|
2
|
+
\u9ED8\u8BA4 worker \u6A21\u578B\uFF1A${s.modelId}`:""}\u3002\u9664\u975E\u7528\u6237\u660E\u786E\u8981\u6C42\u6216\u4EFB\u52A1\u660E\u663E\u9700\u8981\u5176\u4ED6 Agent/\u6A21\u578B\uFF0C\u521B\u5EFA worker \u65F6\u4F18\u5148\u4F7F\u7528\u8FD9\u4E2A\u9ED8\u8BA4\u7EC4\u5408\u3002`:"";return`${["# Shennian Manager Instructions","This file is generated by Shennian for a Manager Agent substrate session. Do not edit it by hand.",n?`## Project Instructions
|
|
3
|
+
|
|
4
|
+
${n}`:"",`## Manager Instructions
|
|
5
|
+
|
|
6
|
+
${y}`,o?`## Worker Defaults
|
|
7
|
+
|
|
8
|
+
${o}`:"",r?`## External Message Channel Instructions
|
|
9
|
+
|
|
10
|
+
${r}`:"",S].filter(Boolean).join(`
|
|
11
|
+
|
|
12
|
+
`)}
|
|
13
|
+
`}function k(i,e){const n=A(i,e),t=u(i,e);d.mkdirSync(c.dirname(n),{recursive:!0});try{if(d.readFileSync(n,"utf8")===t)return n}catch{}return d.writeFileSync(n,t),n}class R extends g{inner;substrate;type;sessionId="";workDir="";agentSessionId=null;configured={};constructor(e,n){super(),this.inner=e,this.substrate=n,this.type=e.type,e.on("agentEvent",t=>{t.agentSessionId&&(this.agentSessionId=t.agentSessionId),this.emit("agentEvent",{...t,source:t.source??n})}),e.on("error",t=>this.emit("error",t))}configure(e){this.configured=e,e.sessionId&&a()?.setManagedAgentPolicy(e.sessionId,e.managedAgentPolicy??null),this.applyEnvironment()}async start(e,n,t){this.sessionId=e,this.workDir=n,this.agentSessionId=t??null,this.applyEnvironment(),await this.inner.start(e,n,t),a()?.registerManager({sessionId:e,agentSessionId:this.agentSessionId,workDir:n,modelId:this.substrate,agentType:this.type==="claude"?"claude":"codex",status:"idle"}),a()?.bindManagerAdapterEvents(e,this.inner)}async send(e,n,t,r,s,o){a()?.registerManager({sessionId:this.sessionId,agentSessionId:this.agentSessionId,workDir:this.workDir,modelId:this.substrate,agentType:this.type==="claude"?"claude":"codex",providerModelId:n??null,status:"running"}),await a()?.ready(),this.applyEnvironment();const h=u(this.workDir,this.sessionId),m=E(h,s);return this.inner.send(l(e),n,t,r,m,o)}async resume(e){this.agentSessionId=e,await this.inner.resume(e)}async stop(){a()?.setManagedAgentPolicy(this.sessionId,null),await this.inner.stop()}getStatus(){return this.inner.getStatus?.()??Promise.resolve({active:!1})}setTitle(e,n,t){return this.inner.setTitle?.(e,n,t)??Promise.resolve()}applyEnvironment(){const e=this.sessionId?a()?.getInjectedEnv(this.sessionId,this.agentSessionId,this.workDir,this.substrate)??{}:{};this.inner.configure?.({...this.configured,sessionId:this.configured.sessionId??this.sessionId,env:{...this.configured.env??{},...e},managedAgentPolicy:this.configured.managedAgentPolicy??null})}}function E(i,e){const n=e?.trim()??"";if(!n)return i;const r=["[\u667A\u80FD\u4F53\u79C1\u6709\u7CFB\u7EDF\u63D0\u793A\u8BCD]","[\u667A\u80FD\u4F53\u8EAB\u4EFD]"].map(s=>n.indexOf(s)).filter(s=>s>=0).sort((s,o)=>s-o)[0]??-1;return n.startsWith("[\u795E\u5FF5\u53D7\u7BA1\u4F1A\u8BDD\u89C4\u5219]")&&r>=0?`${n.slice(0,r)}[\u4F1A\u8BDD\u6A21\u5F0F]
|
|
14
|
+
${i.trim()}
|
|
15
|
+
|
|
16
|
+
${n.slice(r)}`:`${i.trim()}
|
|
17
|
+
|
|
18
|
+
${n}`}class P extends g{type="manager";inner=null;sessionId="";workDir="";agentSessionId=null;modelId="codex";async start(e,n,t){this.sessionId=e,this.workDir=n,this.agentSessionId=t??null,a()?.registerManager({sessionId:e,agentSessionId:this.agentSessionId,workDir:n,modelId:this.modelId,agentType:"manager",status:"idle"})}async send(e,n){const t=M(n);if(!this.inner||t!==this.modelId){await this.inner?.stop().catch(()=>{}),this.inner?.removeAllListeners(),this.modelId=t;const r=k(this.workDir,this.sessionId),s=u(this.workDir,this.sessionId);this.inner=t==="claude"?new f({systemPrompt:s,hidden:!0}):new p({modelInstructionsFile:r,hidden:!0}),this.bindInner(this.inner),await this.inner.start(this.sessionId,this.workDir,this.agentSessionId),a()?.bindManagerAdapterEvents(this.sessionId,this.inner)}a()?.registerManager({sessionId:this.sessionId,agentSessionId:this.agentSessionId,workDir:this.workDir,modelId:this.modelId,agentType:"manager",status:"running"}),await a()?.ready(),await this.withManagerEnv(()=>this.inner.send(l(e)))}async resume(e){this.agentSessionId=e,this.inner&&await this.inner.resume(e)}async stop(){await this.inner?.stop()}bindInner(e){e.on("agentEvent",n=>{n.agentSessionId&&(this.agentSessionId=n.agentSessionId),this.emit("agentEvent",this.wrapEvent(n))}),e.on("error",n=>this.emit("error",n))}wrapEvent(e){return{...e,source:e.source??this.modelId,agentSessionId:e.agentSessionId??this.agentSessionId??void 0}}async withManagerEnv(e){const n=a()?.getInjectedEnv(this.sessionId,this.agentSessionId,this.workDir,this.modelId)??{},t=new Map;for(const[r,s]of Object.entries(n))t.set(r,process.env[r]),process.env[r]=s;try{return await e()}finally{for(const[r,s]of t)s===void 0?delete process.env[r]:process.env[r]=s}}}I("manager",()=>new P);export{P as ManagerAdapter,R as ManagerModeAdapter,E as combineManagerInstructions};
|
|
@@ -1,37 +1 @@
|
|
|
1
|
-
|
|
2
|
-
// @test src/__tests__/model-switching.test.ts
|
|
3
|
-
import fs from 'node:fs';
|
|
4
|
-
import path from 'node:path';
|
|
5
|
-
import { CACHE_TTL_MS, CACHE_VERSION, MODEL_CACHE_PATH } from './types.js';
|
|
6
|
-
function ensureDirExists() {
|
|
7
|
-
fs.mkdirSync(path.dirname(MODEL_CACHE_PATH), { recursive: true });
|
|
8
|
-
}
|
|
9
|
-
export function readCache() {
|
|
10
|
-
try {
|
|
11
|
-
const parsed = JSON.parse(fs.readFileSync(MODEL_CACHE_PATH, 'utf-8'));
|
|
12
|
-
if (parsed.version === CACHE_VERSION && parsed.agents && typeof parsed.agents === 'object') {
|
|
13
|
-
return parsed;
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
catch {
|
|
17
|
-
// fall through
|
|
18
|
-
}
|
|
19
|
-
return { version: CACHE_VERSION, agents: {} };
|
|
20
|
-
}
|
|
21
|
-
export function writeCache(cache) {
|
|
22
|
-
try {
|
|
23
|
-
ensureDirExists();
|
|
24
|
-
fs.writeFileSync(MODEL_CACHE_PATH, JSON.stringify(cache, null, 2));
|
|
25
|
-
}
|
|
26
|
-
catch {
|
|
27
|
-
// Cache is best-effort. Discovery should still succeed when local
|
|
28
|
-
// filesystem writes are unavailable (e.g. restricted sandboxes).
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
export function isCacheFresh(entry, version) {
|
|
32
|
-
if (!entry)
|
|
33
|
-
return false;
|
|
34
|
-
if (entry.version !== version)
|
|
35
|
-
return false;
|
|
36
|
-
return Date.now() - entry.refreshedAt < CACHE_TTL_MS;
|
|
37
|
-
}
|
|
1
|
+
import e from"node:fs";import o from"node:path";import{CACHE_TTL_MS as s,CACHE_VERSION as n,MODEL_CACHE_PATH as t}from"./types.js";function a(){e.mkdirSync(o.dirname(t),{recursive:!0})}function p(){try{const r=JSON.parse(e.readFileSync(t,"utf-8"));if(r.version===n&&r.agents&&typeof r.agents=="object")return r}catch{}return{version:n,agents:{}}}function C(r){try{a(),e.writeFileSync(t,JSON.stringify(r,null,2))}catch{}}function h(r,i){return!r||r.version!==i?!1:Date.now()-r.refreshedAt<s}export{h as isCacheFresh,p as readCache,C as writeCache};
|