@yeaft/webchat-agent 1.0.308 → 1.0.310
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/connection/message-router.js +5 -1
- package/local-runtime/server/db/connection.js +13 -2
- package/local-runtime/server/db/yeaft-project-db.js +26 -1
- package/local-runtime/server/handlers/client-conversation.js +32 -1
- package/local-runtime/version.json +1 -1
- package/local-runtime/web/app.bundle.js +243 -177
- package/local-runtime/web/app.bundle.js.gz +0 -0
- package/local-runtime/web/index.html +2 -2
- package/local-runtime/web/style.bundle.css +1 -1
- package/local-runtime/web/style.bundle.css.gz +0 -0
- package/package.json +1 -1
- package/yeaft/engine.js +12 -4
- package/yeaft/projects/store.js +18 -0
- package/yeaft/prompts.js +13 -0
- package/yeaft/sub-agent/prompt-queue.js +22 -0
- package/yeaft/sub-agent/runner.js +32 -8
- package/yeaft/tools/send-message.js +5 -2
- package/yeaft/web-bridge.js +37 -3
|
Binary file
|
package/package.json
CHANGED
package/yeaft/engine.js
CHANGED
|
@@ -1147,13 +1147,14 @@ export class Engine {
|
|
|
1147
1147
|
* @param {object} [args.vpPersona]
|
|
1148
1148
|
* @param {object} [args.activeScope] — DESIGN-PROMPT §3 ④ structured scope summary
|
|
1149
1149
|
* @param {string} [args.sessionAnnouncement]
|
|
1150
|
+
* @param {string} [args.projectInstruction] — server-managed instruction shared by Project Sessions
|
|
1150
1151
|
* @param {string} [args.workCenterInstructions] — frozen Agent-level Work Center policy
|
|
1151
1152
|
* @param {string} [args.projectDoc] — resolved CLAUDE.md / AGENTS.md text (already truncated)
|
|
1152
1153
|
* @param {object} [args.taskCtx] — legacy task-context sub-block (optional)
|
|
1153
1154
|
* @param {string} [args.explicitSkillName] — leading /skill:<name> command, if present
|
|
1154
1155
|
* @returns {string}
|
|
1155
1156
|
*/
|
|
1156
|
-
#buildSystemPrompt({ prompt, memoryInjection, vpPersona, activeScope, sessionAnnouncement, workCenterInstructions, projectDoc, taskCtx, activeTasks, explicitSkillName, resolvedSkillContent = null } = {}) {
|
|
1157
|
+
#buildSystemPrompt({ prompt, memoryInjection, vpPersona, activeScope, sessionAnnouncement, projectInstruction, workCenterInstructions, projectDoc, taskCtx, activeTasks, explicitSkillName, resolvedSkillContent = null } = {}) {
|
|
1157
1158
|
// Skill selection is normally resolved once by #runQuery so the prompt and
|
|
1158
1159
|
// emitted protocol events describe the exact same skills. Keep the local
|
|
1159
1160
|
// fallback for internal callers that do not need selection events.
|
|
@@ -1180,6 +1181,7 @@ export class Engine {
|
|
|
1180
1181
|
vpPersona,
|
|
1181
1182
|
activeScope,
|
|
1182
1183
|
sessionAnnouncement,
|
|
1184
|
+
projectInstruction,
|
|
1183
1185
|
workCenterInstructions,
|
|
1184
1186
|
projectDoc,
|
|
1185
1187
|
runtimePlatform: getRuntimePlatformInfo(),
|
|
@@ -1351,6 +1353,7 @@ export class Engine {
|
|
|
1351
1353
|
adapter: this.#adapter,
|
|
1352
1354
|
trace: this.#trace,
|
|
1353
1355
|
config: this.#config,
|
|
1356
|
+
memoryIndex: this.#memoryIndex,
|
|
1354
1357
|
parentToolRegistry: this.#toolRegistry,
|
|
1355
1358
|
skillManager: this.#skillManager,
|
|
1356
1359
|
mcpManager: this.#mcpManager,
|
|
@@ -1363,6 +1366,9 @@ export class Engine {
|
|
|
1363
1366
|
projectSessionIds: Array.isArray(vpCtx?.projectSessionIds)
|
|
1364
1367
|
? vpCtx.projectSessionIds.slice()
|
|
1365
1368
|
: [],
|
|
1369
|
+
projectInstruction: typeof vpCtx?.projectInstruction === 'string'
|
|
1370
|
+
? vpCtx.projectInstruction
|
|
1371
|
+
: '',
|
|
1366
1372
|
parentThreadId: vpCtx?.threadId || this.#currentThreadId || MAIN_THREAD_ID,
|
|
1367
1373
|
onEvent: this.#subAgentEventSink || null,
|
|
1368
1374
|
language: this.#config?.language || 'en',
|
|
@@ -1859,7 +1865,7 @@ export class Engine {
|
|
|
1859
1865
|
* string-prompt shape (no regression for existing callers).
|
|
1860
1866
|
* @yields {EngineEvent}
|
|
1861
1867
|
*/
|
|
1862
|
-
async *query({ prompt, promptParts = null, messages = [], signal, userEffort = null, scenario = 'chat', vpPersona, router, senderVpId, inboundEnvelope, taskId, taskMembers, sessionId, sessionMembers, projectSessionIds = null, sessionTopics = null, vpPlan, sessionAnnouncement, workCenterInstructions, workDir, userAlreadyPersisted = false, getCurrentTodos = null, setCurrentTodos = null, askUser = null, threadId = MAIN_THREAD_ID, vpTurnId = null, drainPendingUserMessages = null, prepareProviderRequest = null, startProviderRequest = null, finishProviderRequest = null, failProviderRequest = null, closePendingUserInput = null, collabToolPolicy = null } = {}) {
|
|
1868
|
+
async *query({ prompt, promptParts = null, messages = [], signal, userEffort = null, scenario = 'chat', vpPersona, router, senderVpId, inboundEnvelope, taskId, taskMembers, sessionId, sessionMembers, projectSessionIds = null, projectInstruction = '', sessionTopics = null, vpPlan, sessionAnnouncement, workCenterInstructions, workDir, userAlreadyPersisted = false, getCurrentTodos = null, setCurrentTodos = null, askUser = null, threadId = MAIN_THREAD_ID, vpTurnId = null, drainPendingUserMessages = null, prepareProviderRequest = null, startProviderRequest = null, finishProviderRequest = null, failProviderRequest = null, closePendingUserInput = null, collabToolPolicy = null } = {}) {
|
|
1863
1869
|
if (!prompt || typeof prompt !== 'string' || !prompt.trim()) {
|
|
1864
1870
|
yield {
|
|
1865
1871
|
type: 'error',
|
|
@@ -1936,7 +1942,7 @@ export class Engine {
|
|
|
1936
1942
|
};
|
|
1937
1943
|
try {
|
|
1938
1944
|
this.#currentThreadId = threadId || MAIN_THREAD_ID;
|
|
1939
|
-
yield* this.#runQuery({ prompt: effectivePrompt, promptParts: effectivePromptParts, messages, signal: runSignal, userEffort: effectiveUserEffort, scenario, vpPersona, router, senderVpId, inboundEnvelope, taskId, taskMembers, sessionId, sessionMembers, projectSessionIds, sessionTopics, vpPlan, sessionAnnouncement, workCenterInstructions, workDir, userAlreadyPersisted, getCurrentTodos, setCurrentTodos, askUser, threadId: this.#currentThreadId, vpTurnId, drainPendingUserMessages, prepareProviderRequest, startProviderRequest, finishProviderRequest, failProviderRequest, closePendingUserInput, collabToolPolicy: effectiveCollabToolPolicy, explicitSkillName: parsedSkill.skillName, retryLifecycle });
|
|
1945
|
+
yield* this.#runQuery({ prompt: effectivePrompt, promptParts: effectivePromptParts, messages, signal: runSignal, userEffort: effectiveUserEffort, scenario, vpPersona, router, senderVpId, inboundEnvelope, taskId, taskMembers, sessionId, sessionMembers, projectSessionIds, projectInstruction, sessionTopics, vpPlan, sessionAnnouncement, workCenterInstructions, workDir, userAlreadyPersisted, getCurrentTodos, setCurrentTodos, askUser, threadId: this.#currentThreadId, vpTurnId, drainPendingUserMessages, prepareProviderRequest, startProviderRequest, finishProviderRequest, failProviderRequest, closePendingUserInput, collabToolPolicy: effectiveCollabToolPolicy, explicitSkillName: parsedSkill.skillName, retryLifecycle });
|
|
1940
1946
|
} finally {
|
|
1941
1947
|
// Closing the async generator at a visible retry boundary means the
|
|
1942
1948
|
// continuation never reached a provider. Keep it out of history and
|
|
@@ -1984,7 +1990,7 @@ export class Engine {
|
|
|
1984
1990
|
* in a try/finally without indenting the whole loop.
|
|
1985
1991
|
* @private
|
|
1986
1992
|
*/
|
|
1987
|
-
async *#runQuery({ prompt, promptParts = null, messages, signal, userEffort = null, scenario = 'chat', vpPersona, router, senderVpId, inboundEnvelope, taskId, taskMembers, sessionId, sessionMembers, projectSessionIds = null, sessionTopics = null, vpPlan, sessionAnnouncement, workCenterInstructions, workDir, userAlreadyPersisted = false, getCurrentTodos = null, setCurrentTodos = null, askUser = null, threadId = MAIN_THREAD_ID, vpTurnId = null, drainPendingUserMessages = null, prepareProviderRequest = null, startProviderRequest = null, finishProviderRequest = null, failProviderRequest = null, closePendingUserInput = null, collabToolPolicy = null, explicitSkillName = null, retryLifecycle }) {
|
|
1993
|
+
async *#runQuery({ prompt, promptParts = null, messages, signal, userEffort = null, scenario = 'chat', vpPersona, router, senderVpId, inboundEnvelope, taskId, taskMembers, sessionId, sessionMembers, projectSessionIds = null, projectInstruction = '', sessionTopics = null, vpPlan, sessionAnnouncement, workCenterInstructions, workDir, userAlreadyPersisted = false, getCurrentTodos = null, setCurrentTodos = null, askUser = null, threadId = MAIN_THREAD_ID, vpTurnId = null, drainPendingUserMessages = null, prepareProviderRequest = null, startProviderRequest = null, finishProviderRequest = null, failProviderRequest = null, closePendingUserInput = null, collabToolPolicy = null, explicitSkillName = null, retryLifecycle }) {
|
|
1988
1994
|
|
|
1989
1995
|
const effectiveCollabToolPolicy = collabToolPolicy === COLLAB_TOOL_POLICY.SINGLE_VP || collabToolPolicy === COLLAB_TOOL_POLICY.MULTI_VP
|
|
1990
1996
|
? collabToolPolicy
|
|
@@ -2182,6 +2188,7 @@ export class Engine {
|
|
|
2182
2188
|
vpPersona,
|
|
2183
2189
|
activeScope,
|
|
2184
2190
|
sessionAnnouncement,
|
|
2191
|
+
projectInstruction,
|
|
2185
2192
|
workCenterInstructions,
|
|
2186
2193
|
projectDoc,
|
|
2187
2194
|
activeTasks,
|
|
@@ -3582,6 +3589,7 @@ export class Engine {
|
|
|
3582
3589
|
senderVpId,
|
|
3583
3590
|
sessionId: runtimeSessionId,
|
|
3584
3591
|
projectSessionIds,
|
|
3592
|
+
projectInstruction,
|
|
3585
3593
|
threadId: runtimeThreadId,
|
|
3586
3594
|
inboundEnvelope,
|
|
3587
3595
|
taskId,
|
package/yeaft/projects/store.js
CHANGED
|
@@ -37,6 +37,7 @@ function normalizeProject(row, index = 0) {
|
|
|
37
37
|
return {
|
|
38
38
|
id: row.id,
|
|
39
39
|
name,
|
|
40
|
+
instruction: typeof row.instruction === 'string' ? row.instruction.trim() : '',
|
|
40
41
|
sessionIds: normalizeSessionIds(row.sessionIds),
|
|
41
42
|
sortOrder: Number.isFinite(row.sortOrder) ? row.sortOrder : index,
|
|
42
43
|
createdAt: typeof row.createdAt === 'string' ? row.createdAt : '',
|
|
@@ -92,6 +93,7 @@ export function createProject(yeaftDir, name) {
|
|
|
92
93
|
const project = {
|
|
93
94
|
id: `project-${randomUUID().slice(0, 8)}`,
|
|
94
95
|
name: requireName(name),
|
|
96
|
+
instruction: '',
|
|
95
97
|
sessionIds: [],
|
|
96
98
|
sortOrder: projects.length,
|
|
97
99
|
createdAt: now,
|
|
@@ -110,6 +112,22 @@ export function renameProject(yeaftDir, projectId, name) {
|
|
|
110
112
|
return project;
|
|
111
113
|
}
|
|
112
114
|
|
|
115
|
+
export function updateProjectInstruction(yeaftDir, projectId, instruction) {
|
|
116
|
+
if (typeof instruction !== 'string') {
|
|
117
|
+
throw new ProjectStoreError('invalid_instruction', 'Project instruction must be a string');
|
|
118
|
+
}
|
|
119
|
+
const nextInstruction = instruction.trim();
|
|
120
|
+
if (nextInstruction.length > 20_000) {
|
|
121
|
+
throw new ProjectStoreError('instruction_too_long', 'Project instruction must not exceed 20000 characters');
|
|
122
|
+
}
|
|
123
|
+
const projects = loadProjects(yeaftDir);
|
|
124
|
+
const project = requireProject(projects, projectId);
|
|
125
|
+
project.instruction = nextInstruction;
|
|
126
|
+
project.updatedAt = new Date().toISOString();
|
|
127
|
+
saveProjects(yeaftDir, projects);
|
|
128
|
+
return project;
|
|
129
|
+
}
|
|
130
|
+
|
|
113
131
|
export function deleteProject(yeaftDir, projectId) {
|
|
114
132
|
const projects = loadProjects(yeaftDir);
|
|
115
133
|
requireProject(projects, projectId);
|
package/yeaft/prompts.js
CHANGED
|
@@ -204,6 +204,8 @@ const PROMPTS = {
|
|
|
204
204
|
activeScopeEnvelopeLabel: 'Handoff',
|
|
205
205
|
multiVpRoutingHeader: '## multi_vp_routing',
|
|
206
206
|
sessionAnnouncementHeader: '[Session Announcement]',
|
|
207
|
+
projectInstructionHeader: '[Project Instruction]',
|
|
208
|
+
projectInstructionIntro: 'This server-managed instruction applies to every Session in the current Project.',
|
|
207
209
|
workCenterInstructionsHeader: '[Work Center Agent Instructions]',
|
|
208
210
|
workCenterInstructionsIntro: 'These Agent-level instructions apply to every Action in this WorkItem. Follow them unless they conflict with system/tool safety rules, the authoritative project document, or the WorkItem contract.',
|
|
209
211
|
// Project-doc (CLAUDE.md / AGENTS.md) header + one-liner intro. Both
|
|
@@ -226,6 +228,8 @@ const PROMPTS = {
|
|
|
226
228
|
activeScopeEnvelopeLabel: '转交消息',
|
|
227
229
|
multiVpRoutingHeader: '## multi_vp_routing',
|
|
228
230
|
sessionAnnouncementHeader: '[会话公告]',
|
|
231
|
+
projectInstructionHeader: '[Project 指令]',
|
|
232
|
+
projectInstructionIntro: '这是服务器管理的 Project 级指令,适用于当前 Project 内的所有 Session。',
|
|
229
233
|
workCenterInstructionsHeader: '[Work Center Agent 指令]',
|
|
230
234
|
workCenterInstructionsIntro: '这些 Agent 级指令作用于当前 Work Item 的每个 Action。除非与系统/工具安全规则、权威项目文档或 Work Item 契约冲突,否则必须遵循。',
|
|
231
235
|
// 项目文档块:CLAUDE.md / AGENTS.md(与 Codex 通用命名兼容)。
|
|
@@ -297,6 +301,7 @@ export function normalizePromptLanguage(language) {
|
|
|
297
301
|
* activeScope?: object,
|
|
298
302
|
* vpPersona?: object,
|
|
299
303
|
* sessionAnnouncement?: string,
|
|
304
|
+
* projectInstruction?: string,
|
|
300
305
|
* workCenterInstructions?: string,
|
|
301
306
|
* projectDoc?: string,
|
|
302
307
|
* }} params
|
|
@@ -311,6 +316,7 @@ export function buildSystemPrompt({
|
|
|
311
316
|
activeScope,
|
|
312
317
|
vpPersona,
|
|
313
318
|
sessionAnnouncement = '',
|
|
319
|
+
projectInstruction = '',
|
|
314
320
|
workCenterInstructions = '',
|
|
315
321
|
projectDoc = '',
|
|
316
322
|
runtimePlatform,
|
|
@@ -357,6 +363,13 @@ export function buildSystemPrompt({
|
|
|
357
363
|
parts.push(`${docHeader}\n${introLine}${docText}`);
|
|
358
364
|
}
|
|
359
365
|
|
|
366
|
+
const projectInstructionText = typeof projectInstruction === 'string' ? projectInstruction.trim() : '';
|
|
367
|
+
if (projectInstructionText) {
|
|
368
|
+
const header = lang.projectInstructionHeader || '[Project Instruction]';
|
|
369
|
+
const intro = lang.projectInstructionIntro || '';
|
|
370
|
+
parts.push(`${header}\n${intro ? `${intro}\n\n` : ''}${projectInstructionText}`);
|
|
371
|
+
}
|
|
372
|
+
|
|
360
373
|
// ─── 1.5 Session Announcement (CLAUDE.md-style shared prefix) ───
|
|
361
374
|
// When a session has set an announcement, every VP in the session sees it
|
|
362
375
|
// near the top of the system prompt — before tools, memory, mode-specific
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Queue a sub-agent continuation with the Project context that is authoritative
|
|
3
|
+
* for the parent turn which submitted it. Keeping the snapshot on the queue
|
|
4
|
+
* entry preserves ordering when multiple continuations are submitted before
|
|
5
|
+
* the child consumes them. Empty values are an explicit Project-context clear.
|
|
6
|
+
*
|
|
7
|
+
* @param {object} agent
|
|
8
|
+
* @param {string} prompt
|
|
9
|
+
* @param {{ projectSessionIds?: string[], projectInstruction?: string }} projectContext
|
|
10
|
+
*/
|
|
11
|
+
export function enqueueSubAgentPrompt(agent, prompt, projectContext = {}) {
|
|
12
|
+
if (!Array.isArray(agent.pendingPrompts)) agent.pendingPrompts = [];
|
|
13
|
+
agent.pendingPrompts.push({
|
|
14
|
+
prompt,
|
|
15
|
+
projectSessionIds: Array.isArray(projectContext.projectSessionIds)
|
|
16
|
+
? projectContext.projectSessionIds.slice()
|
|
17
|
+
: [],
|
|
18
|
+
projectInstruction: typeof projectContext.projectInstruction === 'string'
|
|
19
|
+
? projectContext.projectInstruction
|
|
20
|
+
: '',
|
|
21
|
+
});
|
|
22
|
+
}
|
|
@@ -114,6 +114,7 @@ export function isRestrictedToolName(name) {
|
|
|
114
114
|
* trace: object,
|
|
115
115
|
* config: object,
|
|
116
116
|
* conversationStore?: object,
|
|
117
|
+
* memoryIndex?: object,
|
|
117
118
|
* memoryStore?: object,
|
|
118
119
|
* memoryShardStore?: object,
|
|
119
120
|
* parentToolRegistry?: ToolRegistry,
|
|
@@ -124,6 +125,7 @@ export function isRestrictedToolName(name) {
|
|
|
124
125
|
* parentVpId?: string,
|
|
125
126
|
* parentSessionId?: string|null,
|
|
126
127
|
* projectSessionIds?: string[],
|
|
128
|
+
* projectInstruction?: string,
|
|
127
129
|
* parentThreadId?: string|null,
|
|
128
130
|
* parentVpPersona?: object,
|
|
129
131
|
* toolStats?: object,
|
|
@@ -152,6 +154,7 @@ export function startSubAgent(agent, deps = {}) {
|
|
|
152
154
|
trace: deps.trace,
|
|
153
155
|
config: { ...deps.config, _readOnly: true },
|
|
154
156
|
conversationStore: null,
|
|
157
|
+
memoryIndex: deps.memoryIndex || null,
|
|
155
158
|
memoryStore: deps.memoryStore || null,
|
|
156
159
|
memoryShardStore: deps.memoryShardStore || null,
|
|
157
160
|
toolRegistry: childRegistry,
|
|
@@ -314,7 +317,29 @@ async function driveSubAgent(agent, subEngine, vpPersona, deps) {
|
|
|
314
317
|
|
|
315
318
|
const dequeueNextUserPrompt = () => {
|
|
316
319
|
if (!Array.isArray(agent.pendingPrompts)) agent.pendingPrompts = [];
|
|
317
|
-
|
|
320
|
+
const entry = agent.pendingPrompts.shift();
|
|
321
|
+
if (!entry) return null;
|
|
322
|
+
if (typeof entry === 'string') {
|
|
323
|
+
return {
|
|
324
|
+
prompt: entry,
|
|
325
|
+
projectSessionIds: Array.isArray(deps.projectSessionIds)
|
|
326
|
+
? deps.projectSessionIds.slice()
|
|
327
|
+
: [],
|
|
328
|
+
projectInstruction: typeof deps.projectInstruction === 'string'
|
|
329
|
+
? deps.projectInstruction
|
|
330
|
+
: '',
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
if (!entry || typeof entry !== 'object' || typeof entry.prompt !== 'string') return null;
|
|
334
|
+
return {
|
|
335
|
+
prompt: entry.prompt,
|
|
336
|
+
projectSessionIds: Array.isArray(entry.projectSessionIds)
|
|
337
|
+
? entry.projectSessionIds.slice()
|
|
338
|
+
: [],
|
|
339
|
+
projectInstruction: typeof entry.projectInstruction === 'string'
|
|
340
|
+
? entry.projectInstruction
|
|
341
|
+
: '',
|
|
342
|
+
};
|
|
318
343
|
};
|
|
319
344
|
|
|
320
345
|
try {
|
|
@@ -329,8 +354,8 @@ async function driveSubAgent(agent, subEngine, vpPersona, deps) {
|
|
|
329
354
|
emit({ type: 'sub_agent_status', status: STATUS.RUNNING });
|
|
330
355
|
|
|
331
356
|
while (!isTerminalAgentStatus(agent.status)) {
|
|
332
|
-
const
|
|
333
|
-
if (!
|
|
357
|
+
const queuedPrompt = dequeueNextUserPrompt();
|
|
358
|
+
if (!queuedPrompt) {
|
|
334
359
|
// No queued work — go idle and wait for PromptAgent / CloseAgent /
|
|
335
360
|
// watchdog.
|
|
336
361
|
agent.status = STATUS.IDLE;
|
|
@@ -375,15 +400,14 @@ async function driveSubAgent(agent, subEngine, vpPersona, deps) {
|
|
|
375
400
|
let turnUsageTokens = 0;
|
|
376
401
|
try {
|
|
377
402
|
const stream = subEngine.query({
|
|
378
|
-
prompt,
|
|
403
|
+
prompt: queuedPrompt.prompt,
|
|
379
404
|
messages: agent.engineMessages,
|
|
380
405
|
signal: agent.abortController?.signal,
|
|
381
406
|
scenario: 'chat',
|
|
382
407
|
vpPersona,
|
|
383
408
|
sessionId: agent.parentSessionId || deps.parentSessionId || null,
|
|
384
|
-
projectSessionIds:
|
|
385
|
-
|
|
386
|
-
: null,
|
|
409
|
+
projectSessionIds: queuedPrompt.projectSessionIds,
|
|
410
|
+
projectInstruction: queuedPrompt.projectInstruction,
|
|
387
411
|
});
|
|
388
412
|
for await (const evt of stream) {
|
|
389
413
|
// Liveness — update first so even listener throws don't lose
|
|
@@ -446,7 +470,7 @@ async function driveSubAgent(agent, subEngine, vpPersona, deps) {
|
|
|
446
470
|
|
|
447
471
|
// Persist the turn into the local message buffer so subsequent
|
|
448
472
|
// PromptAgent continuations see context.
|
|
449
|
-
agent.engineMessages.push({ role: 'user', content: prompt });
|
|
473
|
+
agent.engineMessages.push({ role: 'user', content: queuedPrompt.prompt });
|
|
450
474
|
if (assistantText) {
|
|
451
475
|
agent.engineMessages.push({ role: 'assistant', content: assistantText });
|
|
452
476
|
}
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import { defineTool } from './types.js';
|
|
9
9
|
import { agentBelongsToCaller, getAgentRegistry } from './agent.js';
|
|
10
|
+
import { enqueueSubAgentPrompt } from '../sub-agent/prompt-queue.js';
|
|
10
11
|
import { isTerminalAgentStatus, isPromptableAgentStatus, STATUS, describeAgentStatus } from '../sub-agent/status.js';
|
|
11
12
|
|
|
12
13
|
export default defineTool({
|
|
@@ -111,8 +112,10 @@ SpawnAgent -> (PromptAgent <-> WaitAgent)+ -> CloseAgent -> 最终回复给用
|
|
|
111
112
|
|
|
112
113
|
// Queue as a pending prompt the driver will pull. This wakes the
|
|
113
114
|
// driver out of its idle wait and starts a new turn.
|
|
114
|
-
|
|
115
|
-
|
|
115
|
+
enqueueSubAgentPrompt(agent, message, {
|
|
116
|
+
projectSessionIds: ctx?.parentEngineDeps?.projectSessionIds,
|
|
117
|
+
projectInstruction: ctx?.parentEngineDeps?.projectInstruction,
|
|
118
|
+
});
|
|
116
119
|
agent.messages.push({
|
|
117
120
|
role: 'user',
|
|
118
121
|
content: message,
|
package/yeaft/web-bridge.js
CHANGED
|
@@ -72,6 +72,7 @@ import {
|
|
|
72
72
|
moveSessionToProject,
|
|
73
73
|
removeSessionFromProjects,
|
|
74
74
|
renameProject,
|
|
75
|
+
updateProjectInstruction,
|
|
75
76
|
} from './projects/store.js';
|
|
76
77
|
import { readSummary as readScopeSummary } from './memory/store.js';
|
|
77
78
|
import { estimateTokens } from './dream/segment.js';
|
|
@@ -84,6 +85,7 @@ import { classifyThread as defaultClassifyThread, fallbackTitle } from './vp/thr
|
|
|
84
85
|
import { listMcpServers, upsertMcpServer, removeMcpServer } from './config-api.js';
|
|
85
86
|
import { buildMcpFlattenedTools } from './tools/mcp-tools.js';
|
|
86
87
|
import { getAgentRegistry, agentBelongsToScope } from './tools/agent.js';
|
|
88
|
+
import { enqueueSubAgentPrompt } from './sub-agent/prompt-queue.js';
|
|
87
89
|
import { isPromptableAgentStatus } from './sub-agent/status.js';
|
|
88
90
|
import { perfNowMs, recordAgentPerfTrace } from './perf-trace.js';
|
|
89
91
|
import { recordAgentSessionCreated, recordAgentTurn } from '../metrics.js';
|
|
@@ -602,6 +604,9 @@ function normalizeProjectContext(value, sessionId) {
|
|
|
602
604
|
projectName: typeof value.projectName === 'string' && value.projectName.trim()
|
|
603
605
|
? value.projectName.trim()
|
|
604
606
|
: null,
|
|
607
|
+
projectInstruction: typeof value.projectInstruction === 'string'
|
|
608
|
+
? value.projectInstruction.trim()
|
|
609
|
+
: '',
|
|
605
610
|
sessionIds: Array.from(new Set(value.sessionIds
|
|
606
611
|
.filter(id => typeof id === 'string' && id.trim())
|
|
607
612
|
.map(id => id.trim())
|
|
@@ -615,6 +620,7 @@ function legacyProjectContext(yeaftDir, sessionId) {
|
|
|
615
620
|
return normalizeProjectContext({
|
|
616
621
|
projectId: project.id,
|
|
617
622
|
projectName: project.name,
|
|
623
|
+
projectInstruction: project.instruction || '',
|
|
618
624
|
sessionIds: project.sessionIds,
|
|
619
625
|
}, sessionId);
|
|
620
626
|
}
|
|
@@ -3198,6 +3204,20 @@ export function handleYeaftListSessions(msg) {
|
|
|
3198
3204
|
}
|
|
3199
3205
|
}
|
|
3200
3206
|
|
|
3207
|
+
export function handleYeaftProjectContextSync(msg) {
|
|
3208
|
+
for (const row of Array.isArray(msg?.contexts) ? msg.contexts : []) {
|
|
3209
|
+
const sessionId = typeof row?.sessionId === 'string' ? row.sessionId.trim() : '';
|
|
3210
|
+
if (!sessionId) continue;
|
|
3211
|
+
const projectContext = normalizeProjectContext(row.projectContext, sessionId);
|
|
3212
|
+
projectContextBySession.set(sessionId, projectContext || {
|
|
3213
|
+
projectId: null,
|
|
3214
|
+
projectName: null,
|
|
3215
|
+
projectInstruction: '',
|
|
3216
|
+
sessionIds: [],
|
|
3217
|
+
});
|
|
3218
|
+
}
|
|
3219
|
+
}
|
|
3220
|
+
|
|
3201
3221
|
export function handleYeaftProjectMutation(msg) {
|
|
3202
3222
|
const requestId = msg && msg.requestId;
|
|
3203
3223
|
const op = msg && msg.op;
|
|
@@ -3206,7 +3226,9 @@ export function handleYeaftProjectMutation(msg) {
|
|
|
3206
3226
|
let result = null;
|
|
3207
3227
|
if (op === 'create') result = createProject(yeaftDir, msg.name);
|
|
3208
3228
|
else if (op === 'rename') result = renameProject(yeaftDir, msg.projectId, msg.name);
|
|
3209
|
-
else if (op === '
|
|
3229
|
+
else if (op === 'update_instruction') {
|
|
3230
|
+
result = updateProjectInstruction(yeaftDir, msg.projectId, msg.instruction);
|
|
3231
|
+
} else if (op === 'delete') result = deleteProject(yeaftDir, msg.projectId);
|
|
3210
3232
|
else if (op === 'move_session') {
|
|
3211
3233
|
if (!snapshotSessions(yeaftDir).some(row => row.id === msg.sessionId)) {
|
|
3212
3234
|
throw new ProjectStoreError('session_not_found', 'Session not found');
|
|
@@ -4568,6 +4590,7 @@ async function runYeaftSessionSend(msg) {
|
|
|
4568
4590
|
projectContextBySession.set(sessionId, inboundProjectContext || {
|
|
4569
4591
|
projectId: null,
|
|
4570
4592
|
projectName: null,
|
|
4593
|
+
projectInstruction: '',
|
|
4571
4594
|
sessionIds: [],
|
|
4572
4595
|
});
|
|
4573
4596
|
} else {
|
|
@@ -5250,6 +5273,7 @@ async function runVpTurn({ prompt, promptParts = null, sessionId, vpId, threadId
|
|
|
5250
5273
|
|| legacyProjectContext(ctx.CONFIG?.yeaftDir, sessionId);
|
|
5251
5274
|
const projectSessionIds = projectContext?.sessionIds || [];
|
|
5252
5275
|
queryOpts.projectSessionIds = projectSessionIds;
|
|
5276
|
+
queryOpts.projectInstruction = projectContext?.projectInstruction || '';
|
|
5253
5277
|
const projectSummaries = await sharedProjectContext(ctx.CONFIG?.yeaftDir, sessionId, {
|
|
5254
5278
|
sessionIds: projectSessionIds,
|
|
5255
5279
|
language: session?.config?.language,
|
|
@@ -6497,8 +6521,12 @@ export function handleYeaftSubAgentPrompt(msg) {
|
|
|
6497
6521
|
return;
|
|
6498
6522
|
}
|
|
6499
6523
|
|
|
6500
|
-
|
|
6501
|
-
|
|
6524
|
+
const projectContext = projectContextBySession.get(sessionId)
|
|
6525
|
+
|| legacyProjectContext(ctx.CONFIG?.yeaftDir, sessionId);
|
|
6526
|
+
enqueueSubAgentPrompt(agent, message, {
|
|
6527
|
+
projectSessionIds: projectContext?.sessionIds,
|
|
6528
|
+
projectInstruction: projectContext?.projectInstruction,
|
|
6529
|
+
});
|
|
6502
6530
|
if (!Array.isArray(agent.messages)) agent.messages = [];
|
|
6503
6531
|
agent.messages.push({ role: 'user', content: message, timestamp: Date.now() });
|
|
6504
6532
|
if (agent.status === 'idle' || agent.status === 'created') agent.status = 'running';
|
|
@@ -7385,6 +7413,12 @@ export const __testHooks = {
|
|
|
7385
7413
|
sharedProjectContext,
|
|
7386
7414
|
buildProjectSharedBlock,
|
|
7387
7415
|
normalizeProjectContext,
|
|
7416
|
+
handleProjectContextSyncForTest(msg) {
|
|
7417
|
+
handleYeaftProjectContextSync(msg);
|
|
7418
|
+
},
|
|
7419
|
+
projectContextForSessionForTest(sessionId) {
|
|
7420
|
+
return projectContextBySession.get(sessionId) || null;
|
|
7421
|
+
},
|
|
7388
7422
|
loadVisibleGroupHistoryPage,
|
|
7389
7423
|
projectVisibleHistoryChunkMessages,
|
|
7390
7424
|
persistInboundMessageOnceByMsgId,
|