@yeaft/webchat-agent 1.0.144 → 1.0.145
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/package.json
CHANGED
package/yeaft/engine.js
CHANGED
|
@@ -1008,12 +1008,13 @@ export class Engine {
|
|
|
1008
1008
|
* @param {object} [args.vpPersona]
|
|
1009
1009
|
* @param {object} [args.activeScope] — DESIGN-PROMPT §3 ④ structured scope summary
|
|
1010
1010
|
* @param {string} [args.sessionAnnouncement]
|
|
1011
|
+
* @param {string} [args.workCenterInstructions] — frozen Agent-level Work Center policy
|
|
1011
1012
|
* @param {string} [args.projectDoc] — resolved CLAUDE.md / AGENTS.md text (already truncated)
|
|
1012
1013
|
* @param {object} [args.taskCtx] — legacy task-context sub-block (optional)
|
|
1013
1014
|
* @param {string} [args.explicitSkillName] — leading /skill:<name> command, if present
|
|
1014
1015
|
* @returns {string}
|
|
1015
1016
|
*/
|
|
1016
|
-
#buildSystemPrompt({ prompt, memoryInjection, vpPersona, activeScope, sessionAnnouncement, projectDoc, taskCtx, activeTasks, explicitSkillName } = {}) {
|
|
1017
|
+
#buildSystemPrompt({ prompt, memoryInjection, vpPersona, activeScope, sessionAnnouncement, workCenterInstructions, projectDoc, taskCtx, activeTasks, explicitSkillName } = {}) {
|
|
1017
1018
|
// Get relevant skill content if SkillManager is wired. A leading
|
|
1018
1019
|
// /skill:<name> is explicit, not relevance matching: load that skill by
|
|
1019
1020
|
// name or inject a visible prompt warning when the command is unknown.
|
|
@@ -1040,6 +1041,7 @@ export class Engine {
|
|
|
1040
1041
|
vpPersona,
|
|
1041
1042
|
activeScope,
|
|
1042
1043
|
sessionAnnouncement,
|
|
1044
|
+
workCenterInstructions,
|
|
1043
1045
|
projectDoc,
|
|
1044
1046
|
runtimePlatform: getRuntimePlatformInfo(),
|
|
1045
1047
|
taskCtx,
|
|
@@ -1647,7 +1649,7 @@ export class Engine {
|
|
|
1647
1649
|
* string-prompt shape (no regression for existing callers).
|
|
1648
1650
|
* @yields {EngineEvent}
|
|
1649
1651
|
*/
|
|
1650
|
-
async *query({ prompt, promptParts = null, messages = [], signal, userEffort = null, scenario = 'chat', vpPersona, router, senderVpId, inboundEnvelope, taskId, taskMembers, sessionId, sessionMembers, sessionTopics = null, vpPlan, sessionAnnouncement, workDir, userAlreadyPersisted = false, getCurrentTodos = null, setCurrentTodos = null, threadId = MAIN_THREAD_ID, vpTurnId = null, drainPendingUserMessages = null, collabToolPolicy = null } = {}) {
|
|
1652
|
+
async *query({ prompt, promptParts = null, messages = [], signal, userEffort = null, scenario = 'chat', vpPersona, router, senderVpId, inboundEnvelope, taskId, taskMembers, sessionId, sessionMembers, sessionTopics = null, vpPlan, sessionAnnouncement, workCenterInstructions, workDir, userAlreadyPersisted = false, getCurrentTodos = null, setCurrentTodos = null, threadId = MAIN_THREAD_ID, vpTurnId = null, drainPendingUserMessages = null, collabToolPolicy = null } = {}) {
|
|
1651
1653
|
if (!prompt || typeof prompt !== 'string' || !prompt.trim()) {
|
|
1652
1654
|
yield {
|
|
1653
1655
|
type: 'error',
|
|
@@ -1715,7 +1717,7 @@ export class Engine {
|
|
|
1715
1717
|
|
|
1716
1718
|
try {
|
|
1717
1719
|
this.#currentThreadId = threadId || MAIN_THREAD_ID;
|
|
1718
|
-
yield* this.#runQuery({ prompt: effectivePrompt, promptParts: effectivePromptParts, messages, signal: runSignal, userEffort: effectiveUserEffort, scenario, vpPersona, router, senderVpId, inboundEnvelope, taskId, taskMembers, sessionId, sessionMembers, sessionTopics, vpPlan, sessionAnnouncement, workDir, userAlreadyPersisted, getCurrentTodos, setCurrentTodos, threadId: this.#currentThreadId, vpTurnId, drainPendingUserMessages, collabToolPolicy: effectiveCollabToolPolicy, explicitSkillName: parsedSkill.skillName });
|
|
1720
|
+
yield* this.#runQuery({ prompt: effectivePrompt, promptParts: effectivePromptParts, messages, signal: runSignal, userEffort: effectiveUserEffort, scenario, vpPersona, router, senderVpId, inboundEnvelope, taskId, taskMembers, sessionId, sessionMembers, sessionTopics, vpPlan, sessionAnnouncement, workCenterInstructions, workDir, userAlreadyPersisted, getCurrentTodos, setCurrentTodos, threadId: this.#currentThreadId, vpTurnId, drainPendingUserMessages, collabToolPolicy: effectiveCollabToolPolicy, explicitSkillName: parsedSkill.skillName });
|
|
1719
1721
|
} finally {
|
|
1720
1722
|
if (signal) {
|
|
1721
1723
|
try { signal.removeEventListener('abort', onExternalAbort); } catch { /* ignore */ }
|
|
@@ -1759,7 +1761,7 @@ export class Engine {
|
|
|
1759
1761
|
* in a try/finally without indenting the whole loop.
|
|
1760
1762
|
* @private
|
|
1761
1763
|
*/
|
|
1762
|
-
async *#runQuery({ prompt, promptParts = null, messages, signal, userEffort = null, scenario = 'chat', vpPersona, router, senderVpId, inboundEnvelope, taskId, taskMembers, sessionId, sessionMembers, sessionTopics = null, vpPlan, sessionAnnouncement, workDir, userAlreadyPersisted = false, getCurrentTodos = null, setCurrentTodos = null, threadId = MAIN_THREAD_ID, vpTurnId = null, drainPendingUserMessages = null, collabToolPolicy = null, explicitSkillName = null }) {
|
|
1764
|
+
async *#runQuery({ prompt, promptParts = null, messages, signal, userEffort = null, scenario = 'chat', vpPersona, router, senderVpId, inboundEnvelope, taskId, taskMembers, sessionId, sessionMembers, sessionTopics = null, vpPlan, sessionAnnouncement, workCenterInstructions, workDir, userAlreadyPersisted = false, getCurrentTodos = null, setCurrentTodos = null, threadId = MAIN_THREAD_ID, vpTurnId = null, drainPendingUserMessages = null, collabToolPolicy = null, explicitSkillName = null }) {
|
|
1763
1765
|
|
|
1764
1766
|
const effectiveCollabToolPolicy = collabToolPolicy === COLLAB_TOOL_POLICY.SINGLE_VP || collabToolPolicy === COLLAB_TOOL_POLICY.MULTI_VP
|
|
1765
1767
|
? collabToolPolicy
|
|
@@ -1896,6 +1898,7 @@ export class Engine {
|
|
|
1896
1898
|
vpPersona,
|
|
1897
1899
|
activeScope,
|
|
1898
1900
|
sessionAnnouncement,
|
|
1901
|
+
workCenterInstructions,
|
|
1899
1902
|
projectDoc,
|
|
1900
1903
|
activeTasks,
|
|
1901
1904
|
explicitSkillName,
|
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
|
+
workCenterInstructionsHeader: '[Work Center Agent Instructions]',
|
|
208
|
+
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.',
|
|
207
209
|
// Project-doc (CLAUDE.md / AGENTS.md) header + one-liner intro. Both
|
|
208
210
|
// filenames are recognized: CLAUDE.md is this project's convention,
|
|
209
211
|
// AGENTS.md is the cross-tool convention (Codex / OpenAI Codex CLI).
|
|
@@ -224,6 +226,8 @@ const PROMPTS = {
|
|
|
224
226
|
activeScopeEnvelopeLabel: '转交消息',
|
|
225
227
|
multiVpRoutingHeader: '## multi_vp_routing',
|
|
226
228
|
sessionAnnouncementHeader: '[会话公告]',
|
|
229
|
+
workCenterInstructionsHeader: '[Work Center Agent 指令]',
|
|
230
|
+
workCenterInstructionsIntro: '这些 Agent 级指令作用于当前 Work Item 的每个 Action。除非与系统/工具安全规则、权威项目文档或 Work Item 契约冲突,否则必须遵循。',
|
|
227
231
|
// 项目文档块:CLAUDE.md / AGENTS.md(与 Codex 通用命名兼容)。
|
|
228
232
|
projectDocHeader: '[项目文档]',
|
|
229
233
|
projectDocIntro:
|
|
@@ -293,6 +297,7 @@ export function normalizePromptLanguage(language) {
|
|
|
293
297
|
* activeScope?: object,
|
|
294
298
|
* vpPersona?: object,
|
|
295
299
|
* sessionAnnouncement?: string,
|
|
300
|
+
* workCenterInstructions?: string,
|
|
296
301
|
* projectDoc?: string,
|
|
297
302
|
* }} params
|
|
298
303
|
* @returns {string}
|
|
@@ -306,6 +311,7 @@ export function buildSystemPrompt({
|
|
|
306
311
|
activeScope,
|
|
307
312
|
vpPersona,
|
|
308
313
|
sessionAnnouncement = '',
|
|
314
|
+
workCenterInstructions = '',
|
|
309
315
|
projectDoc = '',
|
|
310
316
|
runtimePlatform,
|
|
311
317
|
activeTasks = '',
|
|
@@ -360,6 +366,13 @@ export function buildSystemPrompt({
|
|
|
360
366
|
parts.push(`${lang.sessionAnnouncementHeader || '[Session Announcement]'}\n${annText}`);
|
|
361
367
|
}
|
|
362
368
|
|
|
369
|
+
const workCenterText = (typeof workCenterInstructions === 'string') ? workCenterInstructions.trim() : '';
|
|
370
|
+
if (workCenterText) {
|
|
371
|
+
const header = lang.workCenterInstructionsHeader || '[Work Center Agent Instructions]';
|
|
372
|
+
const intro = lang.workCenterInstructionsIntro || '';
|
|
373
|
+
parts.push(`${header}\n${intro ? `${intro}\n\n` : ''}${workCenterText}`);
|
|
374
|
+
}
|
|
375
|
+
|
|
363
376
|
// ─── 2. Date Metadata ──────────────────────────────────
|
|
364
377
|
parts.push(lang.date(new Date().toISOString().split('T')[0]));
|
|
365
378
|
|
|
@@ -14,6 +14,11 @@ const CAPABILITY_TERMS = Object.freeze({
|
|
|
14
14
|
review: ['review', 'reviewer', 'refactor', 'architecture', 'code-smells', 'readability', 'maintainability'],
|
|
15
15
|
deliver: ['deliver', 'release', 'ship', 'git', 'engineering', 'systems', 'execution'],
|
|
16
16
|
research: ['research', 'science', 'analysis', 'evidence', 'investigation'],
|
|
17
|
+
design: ['design', 'architecture', 'architect', 'systems', 'product', 'ux'],
|
|
18
|
+
diagnose: ['diagnose', 'debug', 'root-cause', 'reliability', 'investigation', 'systems'],
|
|
19
|
+
migrate: ['migrate', 'migration', 'database', 'compatibility', 'data', 'systems'],
|
|
20
|
+
document: ['document', 'documentation', 'writer', 'writing', 'communication'],
|
|
21
|
+
operate: ['operate', 'operations', 'release', 'deployment', 'reliability', 'systems'],
|
|
17
22
|
write: ['write', 'writer', 'writing', 'documentation', 'editor', 'communication'],
|
|
18
23
|
});
|
|
19
24
|
|
|
@@ -34,7 +39,7 @@ function capabilityScore(vp, capability) {
|
|
|
34
39
|
const needle = String(capability || '').trim().toLowerCase();
|
|
35
40
|
if (!needle) return 0;
|
|
36
41
|
const text = vpSearchText(vp);
|
|
37
|
-
const terms = CAPABILITY_TERMS[needle]
|
|
42
|
+
const terms = Object.hasOwn(CAPABILITY_TERMS, needle) ? CAPABILITY_TERMS[needle] : [needle];
|
|
38
43
|
let score = 0;
|
|
39
44
|
for (const term of terms) {
|
|
40
45
|
if (text.includes(term)) score += term === needle ? 6 : 2;
|
|
@@ -277,7 +277,7 @@ function completionContract(action, workItem) {
|
|
|
277
277
|
? ',\n "contractPatch": { "goal": "optional refined goal", "acceptanceCriteria": ["optional refined criterion"] }'
|
|
278
278
|
: '';
|
|
279
279
|
const planField = action.type === 'triage' && workItem?.workflowSnapshot?.planningMode === 'ai'
|
|
280
|
-
? ',\n "plan": { "workItemType": "
|
|
280
|
+
? ',\n "plan": { "workItemType": "specific-lowercase-slug", "actions": [{ "id": "stable-id", "name": "User-facing name", "type": "extensible-lowercase-slug (built-ins include research|design|diagnose|implement|migrate|test|review|document|operate|deliver|write|custom)", "capability": "specific executor capability", "objective": "independently executable and verifiable Action objective", "separateFromActionTypes": ["optional prior Action type"], "changesRequestedActionId": "for review: optional earlier editable Action id; omit to use nearest", "maxAttempts": 2 }] }'
|
|
281
281
|
: '';
|
|
282
282
|
return `\n\nYou are executing one Work Center Action. Before the terminal JSON, write a concise user-facing response describing what you did and the result. Do not include raw tool output or secrets. End your response with exactly one JSON object, preferably in a json code fence:\n{
|
|
283
283
|
"outcome": "completed|waiting|retryable|failed",
|
|
@@ -452,6 +452,7 @@ export class WorkItemRunner {
|
|
|
452
452
|
signal,
|
|
453
453
|
scenario: 'work-item',
|
|
454
454
|
vpPersona: personaFor(vp),
|
|
455
|
+
workCenterInstructions: workItem?.workflowSnapshot?.globalInstructions || '',
|
|
455
456
|
workDir,
|
|
456
457
|
userAlreadyPersisted: true,
|
|
457
458
|
collabToolPolicy: 'single-vp',
|
|
@@ -1,17 +1,37 @@
|
|
|
1
|
-
const
|
|
1
|
+
export const BUILT_IN_ACTION_TYPES = Object.freeze([
|
|
2
|
+
'triage',
|
|
3
|
+
'research',
|
|
4
|
+
'design',
|
|
5
|
+
'diagnose',
|
|
6
|
+
'implement',
|
|
7
|
+
'migrate',
|
|
8
|
+
'test',
|
|
9
|
+
'review',
|
|
10
|
+
'document',
|
|
11
|
+
'operate',
|
|
12
|
+
'deliver',
|
|
13
|
+
'write',
|
|
14
|
+
'custom',
|
|
15
|
+
]);
|
|
16
|
+
const STAGE_TYPES = new Set(BUILT_IN_ACTION_TYPES);
|
|
2
17
|
const ASSIGNMENT_MODES = new Set(['auto', 'pool', 'fixed']);
|
|
3
18
|
const MODEL_MODES = new Set(['inherit', 'primary', 'fast', 'specific']);
|
|
4
19
|
const MODEL_EFFORTS = new Set(['minimal', 'low', 'medium', 'high', 'xhigh', 'max']);
|
|
5
20
|
|
|
6
21
|
const DEFAULT_STAGE_INSTRUCTIONS = Object.freeze({
|
|
7
|
-
triage: '
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
22
|
+
triage: 'Turn the request into an executable contract. Inspect relevant repository facts before deciding the flow. Classify the WorkItem, identify constraints, risks, dependencies, and missing acceptance criteria, then plan only the Actions needed for this task. Do not implement. If the goal or acceptance criteria must change, submit a contractPatch and explain why.',
|
|
23
|
+
research: 'Answer the Action objective with verifiable evidence. Search the repository and, when needed, authoritative external sources. Separate observed facts from inference, record unresolved uncertainty, and produce a concise conclusion that a later Action can use.',
|
|
24
|
+
design: 'Design the smallest maintainable solution that satisfies the WorkItem contract. Inspect existing architecture and conventions, define data flow and boundaries, consider failure and compatibility cases, and leave concrete implementation guidance. Do not add abstraction without a demonstrated need.',
|
|
25
|
+
diagnose: 'Reproduce or otherwise establish the reported behavior, trace it to a root cause, and distinguish evidence from hypotheses. Identify the smallest safe correction and the regression tests that would prove it. Do not stop at the visible symptom.',
|
|
26
|
+
implement: 'Implement the Action objective in the supplied work directory using the smallest correct diff. Follow repository conventions, preserve compatibility, handle relevant boundary conditions, and add or update focused tests. Run the checks needed to prove the change. Use prior Action and review findings; do not approve your own work.',
|
|
27
|
+
migrate: 'Execute the required migration without losing existing semantics or data. Define compatibility and rollback boundaries, make the operation repeatable or safely fenced, validate representative legacy data, and provide evidence for both upgraded and already-current states.',
|
|
28
|
+
test: 'Verify the current result against every applicable acceptance criterion. Run focused tests first, then the broader checks justified by the risk. Cover failure, compatibility, and boundary cases, report exact reproducible evidence, and do not hide skipped or inconclusive checks.',
|
|
29
|
+
review: 'Review the implementation and evidence independently against the WorkItem contract and repository rules. Prioritize correctness, security, data loss, compatibility, and missing tests over style preferences. Return approved or changes_requested with concrete, actionable findings and evidence.',
|
|
30
|
+
document: 'Update the user or maintainer documentation required by the Action objective. Keep terminology and examples consistent with actual behavior, cover compatibility or operational consequences, and verify links, commands, and bilingual requirements where applicable.',
|
|
31
|
+
operate: 'Perform the operational change with explicit preconditions, safety fences, observability, and rollback handling. Verify the live or simulated postcondition from authoritative state, avoid destructive shortcuts, and record the exact evidence needed for handoff.',
|
|
32
|
+
deliver: 'Deliver only an approved result using the repository release policy. Recheck the reviewed commit and remote state, run required final verification on the immutable delivery tree, publish only the requested artifacts, and report commit, tag, deployment, and residual-risk evidence.',
|
|
33
|
+
write: 'Produce the requested written deliverable for its intended audience. Use the available evidence, preserve required terminology and structure, avoid unsupported claims, and verify the result against every acceptance criterion.',
|
|
34
|
+
custom: 'Complete the Action objective using repository facts and the WorkItem contract. State the approach, handle relevant risks and boundary conditions, produce the requested artifact or change, verify the result, and return concrete evidence plus any residual uncertainty.',
|
|
15
35
|
});
|
|
16
36
|
|
|
17
37
|
const DEFAULT_SOFTWARE_CHANGE_STAGES = Object.freeze([
|
|
@@ -94,6 +114,14 @@ export function defaultWorkCenterStageInstructions() {
|
|
|
94
114
|
return { ...DEFAULT_STAGE_INSTRUCTIONS };
|
|
95
115
|
}
|
|
96
116
|
|
|
117
|
+
function normalizeGlobalInstructions(value) {
|
|
118
|
+
return typeof value === 'string' ? value.trim().slice(0, 20_000) : '';
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function generatedActionType(value) {
|
|
122
|
+
return cleanId(value, 'custom').slice(0, 64);
|
|
123
|
+
}
|
|
124
|
+
|
|
97
125
|
export function normalizeWorkflowDefinition(value, index = 0) {
|
|
98
126
|
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
99
127
|
throw new Error('Work Center workflow must be an object');
|
|
@@ -103,10 +131,13 @@ export function normalizeWorkflowDefinition(value, index = 0) {
|
|
|
103
131
|
if (!Array.isArray(value.stages) || value.stages.length === 0) {
|
|
104
132
|
throw new Error(`Work Center workflow "${id}" requires at least one stage`);
|
|
105
133
|
}
|
|
134
|
+
const planningMode = value.planningMode === 'ai' ? 'ai' : 'static';
|
|
106
135
|
const seen = new Set();
|
|
107
136
|
const stages = value.stages.map((rawStage, stageIndex) => {
|
|
108
137
|
const source = rawStage && typeof rawStage === 'object' ? rawStage : {};
|
|
109
|
-
const type =
|
|
138
|
+
const type = planningMode === 'ai'
|
|
139
|
+
? generatedActionType(source.type)
|
|
140
|
+
: (STAGE_TYPES.has(source.type) ? source.type : 'custom');
|
|
110
141
|
const stageId = cleanId(source.id, `${type}-${stageIndex + 1}`);
|
|
111
142
|
if (seen.has(stageId)) throw new Error(`Duplicate Work Center stage id: ${stageId}`);
|
|
112
143
|
seen.add(stageId);
|
|
@@ -137,7 +168,6 @@ export function normalizeWorkflowDefinition(value, index = 0) {
|
|
|
137
168
|
throw new Error(`Review stage "${stage.id}" must return to an earlier editable stage`);
|
|
138
169
|
}
|
|
139
170
|
}
|
|
140
|
-
const planningMode = value.planningMode === 'ai' ? 'ai' : 'static';
|
|
141
171
|
return {
|
|
142
172
|
version: 1,
|
|
143
173
|
id,
|
|
@@ -146,6 +176,7 @@ export function normalizeWorkflowDefinition(value, index = 0) {
|
|
|
146
176
|
workItemType: typeof value.workItemType === 'string' && value.workItemType.trim()
|
|
147
177
|
? cleanId(value.workItemType, 'general')
|
|
148
178
|
: null,
|
|
179
|
+
globalInstructions: normalizeGlobalInstructions(value.globalInstructions),
|
|
149
180
|
modelPolicy: normalizeModelPolicy(value.modelPolicy),
|
|
150
181
|
actionInstructions: normalizeActionInstructions(value.actionInstructions),
|
|
151
182
|
stages,
|
|
@@ -169,6 +200,7 @@ export function defaultWorkCenterSettings() {
|
|
|
169
200
|
defaultWorkflowId: 'software-change',
|
|
170
201
|
startImmediately: true,
|
|
171
202
|
defaultWorkDir: '',
|
|
203
|
+
globalInstructions: '',
|
|
172
204
|
modelPolicy: { mode: 'inherit', model: null, effort: null },
|
|
173
205
|
actionInstructions: normalizeActionInstructions(),
|
|
174
206
|
workflows: [normalizeWorkflowDefinition({
|
|
@@ -204,6 +236,7 @@ export function normalizeWorkCenterSettings(value) {
|
|
|
204
236
|
defaultWorkflowId,
|
|
205
237
|
startImmediately: source.startImmediately !== false,
|
|
206
238
|
defaultWorkDir: typeof source.defaultWorkDir === 'string' ? source.defaultWorkDir.trim() : '',
|
|
239
|
+
globalInstructions: normalizeGlobalInstructions(source.globalInstructions),
|
|
207
240
|
modelPolicy: normalizeModelPolicy(source.modelPolicy || migratedModelPolicy),
|
|
208
241
|
actionInstructions: normalizeActionInstructions(source.actionInstructions || migratedInstructions),
|
|
209
242
|
workflows,
|
|
@@ -212,11 +245,12 @@ export function normalizeWorkCenterSettings(value) {
|
|
|
212
245
|
|
|
213
246
|
export function resolvePlanningWorkflowSnapshot(settings) {
|
|
214
247
|
const normalized = normalizeWorkCenterSettings(settings);
|
|
215
|
-
const triageInstruction = `${normalized.actionInstructions.triage}\n\
|
|
248
|
+
const triageInstruction = `${normalized.actionInstructions.triage}\n\nChoose a specific workItemType and the smallest reliable sequence of 1 to 8 Actions for this WorkItem. Action types are extensible lowercase slugs: use a built-in type when its reusable policy fits, or define a precise domain type when it does not. For every Action, provide an objective that is independently executable and verifiable, plus the capability needed to select the right VP. Add research, design, diagnosis, migration, documentation, operations, testing, independent review, or delivery only when the task requires them. Do not copy a generic workflow.`;
|
|
216
249
|
return normalizeWorkflowDefinition({
|
|
217
250
|
id: 'ai-planned',
|
|
218
251
|
name: 'AI planned',
|
|
219
252
|
planningMode: 'ai',
|
|
253
|
+
globalInstructions: normalized.globalInstructions,
|
|
220
254
|
modelPolicy: normalized.modelPolicy,
|
|
221
255
|
actionInstructions: normalized.actionInstructions,
|
|
222
256
|
stages: [{
|
|
@@ -266,24 +300,32 @@ export function applyGeneratedPlan(workItem, rawPlan) {
|
|
|
266
300
|
if (!rawPlan || typeof rawPlan !== 'object' || Array.isArray(rawPlan)) {
|
|
267
301
|
throw new Error('AI-planned triage requires a structured plan');
|
|
268
302
|
}
|
|
269
|
-
|
|
303
|
+
if (typeof rawPlan.workItemType !== 'string' || !rawPlan.workItemType.trim()) {
|
|
304
|
+
throw new Error('AI-planned triage requires a specific workItemType');
|
|
305
|
+
}
|
|
306
|
+
const workItemType = cleanId(rawPlan.workItemType, '').slice(0, 64);
|
|
307
|
+
if (!workItemType) throw new Error('AI-planned triage requires a valid workItemType');
|
|
270
308
|
if (!Array.isArray(rawPlan.actions) || rawPlan.actions.length < 1 || rawPlan.actions.length > 8) {
|
|
271
309
|
throw new Error('AI-planned triage requires between 1 and 8 Actions');
|
|
272
310
|
}
|
|
273
311
|
const seen = new Set(['triage']);
|
|
274
312
|
const generated = rawPlan.actions.map((rawAction, index) => {
|
|
275
313
|
const input = rawAction && typeof rawAction === 'object' && !Array.isArray(rawAction) ? rawAction : {};
|
|
276
|
-
const type =
|
|
314
|
+
const type = generatedActionType(input.type);
|
|
315
|
+
if (type === 'triage') throw new Error('AI-planned Actions cannot add another triage Action');
|
|
277
316
|
const id = cleanId(input.id, `${type}-${index + 1}`);
|
|
278
317
|
if (seen.has(id)) throw new Error(`Duplicate AI-planned Action id: ${id}`);
|
|
279
318
|
seen.add(id);
|
|
280
319
|
const objective = typeof input.objective === 'string' ? input.objective.trim().slice(0, 2_000) : '';
|
|
281
320
|
if (!objective) throw new Error(`AI-planned Action "${id}" requires an objective`);
|
|
321
|
+
const actionInstruction = Object.hasOwn(source.actionInstructions, type)
|
|
322
|
+
? source.actionInstructions[type]
|
|
323
|
+
: source.actionInstructions.custom;
|
|
282
324
|
const stage = {
|
|
283
325
|
id,
|
|
284
326
|
name: String(input.name || id).trim().slice(0, 120) || id,
|
|
285
327
|
type,
|
|
286
|
-
instruction: `${
|
|
328
|
+
instruction: `${actionInstruction}\n\nAction type: ${type}\nAction objective for this WorkItem:\n${objective}`,
|
|
287
329
|
assignmentPolicy: {
|
|
288
330
|
mode: 'auto',
|
|
289
331
|
capability: cleanId(input.capability, type),
|