@yeaft/webchat-agent 1.0.246 → 1.0.248
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/local-runtime/version.json +1 -1
- package/local-runtime/web/app.bundle.js +59 -23
- 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/work-center/action-identity.js +23 -0
- package/yeaft/work-center/bridge.js +17 -2
- package/yeaft/work-center/completion-contract.js +25 -3
- package/yeaft/work-center/controller.js +52 -75
- package/yeaft/work-center/coordinator.js +323 -0
- package/yeaft/work-center/mainline-projection.js +86 -36
- package/yeaft/work-center/plan-mutation.js +99 -2
- package/yeaft/work-center/projection.js +23 -0
- package/yeaft/work-center/runner.js +1 -1
- package/yeaft/work-center/service.js +19 -5
- package/yeaft/work-center/store.js +1160 -147
- package/yeaft/work-center/workflow.js +75 -5
|
@@ -303,6 +303,7 @@ export function defaultWorkCenterSettings() {
|
|
|
303
303
|
defaultWorkDir: '',
|
|
304
304
|
globalInstructions: '',
|
|
305
305
|
modelPolicy: { mode: 'inherit', model: null, effort: null },
|
|
306
|
+
coordinatorModelPolicy: { mode: 'inherit', model: null, effort: 'high' },
|
|
306
307
|
actionModelPolicies: normalizeActionModelPolicies(),
|
|
307
308
|
actionInstructions: normalizeActionInstructions(),
|
|
308
309
|
workflows: [normalizeWorkflowDefinition({
|
|
@@ -341,6 +342,9 @@ export function normalizeWorkCenterSettings(value) {
|
|
|
341
342
|
defaultWorkDir: typeof source.defaultWorkDir === 'string' ? source.defaultWorkDir.trim() : '',
|
|
342
343
|
globalInstructions: normalizeGlobalInstructions(source.globalInstructions),
|
|
343
344
|
modelPolicy: normalizeModelPolicy(source.modelPolicy || migratedModelPolicy),
|
|
345
|
+
coordinatorModelPolicy: normalizeModelPolicy(
|
|
346
|
+
source.coordinatorModelPolicy || { ...(source.modelPolicy || migratedModelPolicy), effort: 'high' },
|
|
347
|
+
),
|
|
344
348
|
actionModelPolicies: normalizeActionModelPolicies(source.actionModelPolicies, source.modelPolicy || migratedModelPolicy),
|
|
345
349
|
actionInstructions: normalizeActionInstructions(source.actionInstructions || migratedInstructions),
|
|
346
350
|
workflows,
|
|
@@ -373,7 +377,7 @@ export function resolvePlanningWorkflowSnapshot(settings, requestedWorkItemType
|
|
|
373
377
|
const typeInstruction = requestedType
|
|
374
378
|
? `The user explicitly selected workItemType "${requestedType}". Keep that exact type.`
|
|
375
379
|
: 'Infer one specific workItemType from the contract.';
|
|
376
|
-
const triageInstruction = `${normalized.actionInstructions.triage}\n\n${typeInstruction}\nReference workflow catalog:\n${catalog || '(none)'}\nUse the catalog only to understand established task categories and sequencing patterns. Always submit the smallest reliable graph of 1 to 8 task-specific Actions; never omit Actions or copy template brief text. The scheduler can run up to ${normalized.maxConcurrentActions} Actions concurrently. Before submitting, compare each pair of Actions and add a dependency only when one consumes a concrete result or side effect of the other; ordering by narrative, phase name, or list position is not a dependency. Split independent analysis, verification, and repository changes into sibling Actions so the scheduler can use that concurrency. Use workspaceMode read only for Actions guaranteed not to mutate files, Git state, services, or external systems; use isolated-write for independent Git changes, integrate for an integrate Action that combines isolated-write dependencies, and shared for serial side effects. If any Action uses isolated-write, add exactly one Action with type integrate and workspaceMode integrate; it must depend directly on every isolated-write Action, and all later Actions must depend on the integration result rather than an isolated-write Action. Non-Git or dirty workspaces are serialized automatically; do not fake parallelism by marking a mutating Action as read. Every generated Action must state objective, approach, expectedOutcome, capability, dependencies, and workspaceMode. The objective, approach, and expectedOutcome must be specific to this WorkItem and that Action: describe the concrete work, the repository-aware execution method, and the verifiable result that will guide the executor. Generic Action-type boilerplate is invalid. Add only Actions required by this task. Do not copy a generic workflow.`;
|
|
380
|
+
const triageInstruction = `${normalized.actionInstructions.triage}\n\n${typeInstruction}\nReference workflow catalog:\n${catalog || '(none)'}\nUse the catalog only to understand established task categories and sequencing patterns. Always submit the smallest reliable graph of 1 to 8 task-specific Actions; never omit Actions or copy template brief text. Every graph must end in exactly one final acceptance gate: normally one deliver Action, or one terminal review when no delivery operation is required. The final gate must be the unique graph sink and every other Action must be its transitive dependency, so final acceptance cannot run before required evidence. The scheduler can run up to ${normalized.maxConcurrentActions} Actions concurrently. Before submitting, compare each pair of Actions and add a dependency only when one consumes a concrete result or side effect of the other; ordering by narrative, phase name, or list position is not a dependency. Split independent analysis, verification, and repository changes into sibling Actions so the scheduler can use that concurrency. Use workspaceMode read only for Actions guaranteed not to mutate files, Git state, services, or external systems; use isolated-write for independent Git changes, integrate for an integrate Action that combines isolated-write dependencies, and shared for serial side effects. If any Action uses isolated-write, add exactly one Action with type integrate and workspaceMode integrate; it must depend directly on every isolated-write Action, and all later Actions must depend on the integration result rather than an isolated-write Action. Non-Git or dirty workspaces are serialized automatically; do not fake parallelism by marking a mutating Action as read. Every generated Action must state objective, approach, expectedOutcome, capability, dependencies, and workspaceMode. The objective, approach, and expectedOutcome must be specific to this WorkItem and that Action: describe the concrete work, the repository-aware execution method, and the verifiable result that will guide the executor. Generic Action-type boilerplate is invalid. Add only Actions required by this task. Do not copy a generic workflow.`;
|
|
377
381
|
return normalizeWorkflowDefinition({
|
|
378
382
|
id: 'ai-planned',
|
|
379
383
|
name: 'AI planned',
|
|
@@ -426,6 +430,40 @@ export function resolveWorkflowSnapshot(settings, workflowId, stageOverrides = {
|
|
|
426
430
|
});
|
|
427
431
|
}
|
|
428
432
|
|
|
433
|
+
export function validateGeneratedCompletionGate(stages) {
|
|
434
|
+
const deliverStages = stages.filter(stage => stage.type === 'deliver');
|
|
435
|
+
if (deliverStages.length > 1) {
|
|
436
|
+
throw new Error('AI-planned graph requires exactly one final acceptance gate; multiple deliver Actions are not allowed');
|
|
437
|
+
}
|
|
438
|
+
const dependents = new Map(stages.map(stage => [stage.id, []]));
|
|
439
|
+
for (const stage of stages) {
|
|
440
|
+
for (const dependencyId of stage.dependsOnStageIds || []) {
|
|
441
|
+
dependents.get(dependencyId)?.push(stage.id);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
const sinks = stages.filter(stage => (dependents.get(stage.id) || []).length === 0);
|
|
445
|
+
const gate = deliverStages[0]
|
|
446
|
+
|| (sinks.length === 1 && sinks[0].type === 'review' ? sinks[0] : null);
|
|
447
|
+
if (!gate) {
|
|
448
|
+
throw new Error('AI-planned graph requires one final deliver Action or one terminal review Action');
|
|
449
|
+
}
|
|
450
|
+
if (sinks.length !== 1 || sinks[0].id !== gate.id) {
|
|
451
|
+
throw new Error(`AI-planned final acceptance gate "${gate.id}" must be the unique graph sink`);
|
|
452
|
+
}
|
|
453
|
+
const byId = new Map(stages.map(stage => [stage.id, stage]));
|
|
454
|
+
const ancestors = new Set();
|
|
455
|
+
const visit = stageId => {
|
|
456
|
+
if (ancestors.has(stageId)) return;
|
|
457
|
+
ancestors.add(stageId);
|
|
458
|
+
for (const dependencyId of byId.get(stageId)?.dependsOnStageIds || []) visit(dependencyId);
|
|
459
|
+
};
|
|
460
|
+
visit(gate.id);
|
|
461
|
+
const uncovered = stages.filter(stage => !ancestors.has(stage.id)).map(stage => stage.id);
|
|
462
|
+
if (uncovered.length > 0) {
|
|
463
|
+
throw new Error(`AI-planned final acceptance gate "${gate.id}" does not cover Actions: ${uncovered.join(', ')}`);
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
|
|
429
467
|
export function applyGeneratedPlan(workItem, rawPlan, options = {}) {
|
|
430
468
|
const source = workflowFrom(workItem);
|
|
431
469
|
const forceGraph = options.forceGraph !== false;
|
|
@@ -587,6 +625,7 @@ export function applyGeneratedPlan(workItem, rawPlan, options = {}) {
|
|
|
587
625
|
throw new Error(`AI-planned Action "${stage.id}" must consume isolated writes through integration`);
|
|
588
626
|
}
|
|
589
627
|
}
|
|
628
|
+
validateGeneratedCompletionGate(generated);
|
|
590
629
|
return normalizeWorkflowDefinition({
|
|
591
630
|
...source,
|
|
592
631
|
executionMode: forceGraph ? 'graph' : source.executionMode,
|
|
@@ -675,16 +714,47 @@ function renderContext(context = []) {
|
|
|
675
714
|
|
|
676
715
|
export function actionInstruction(stage, workItem, context = [], sessionContextBlock = renderSessionContextSnapshot(workItem?.sessionContext)) {
|
|
677
716
|
const criteria = (workItem.acceptanceCriteria || []).map(item => `- ${item}`).join('\n') || '- No explicit criteria';
|
|
678
|
-
const
|
|
679
|
-
? `\n\nWorkItem-level user messages (apply to every unfinished Action):\n${workItem.messages.map(message => `- ${message.text}`).join('\n')}`
|
|
680
|
-
: '';
|
|
681
|
-
const common = `WorkItem: ${workItem.title}\nGoal: ${workItem.goal}\nAcceptance criteria:\n${criteria}${sessionContextBlock}${workItemMessages}${renderContext(context)}`;
|
|
717
|
+
const common = `WorkItem: ${workItem.title}\nGoal: ${workItem.goal}\nAcceptance criteria:\n${criteria}${sessionContextBlock}${renderContext(context)}`;
|
|
682
718
|
const policy = stage.instruction || defaultWorkCenterStageInstruction(stage.type);
|
|
683
719
|
const brief = normalizeActionBrief(stage.brief || stage, stage.type);
|
|
684
720
|
const contract = `Action type: ${stage.type}\nWhat to do:\n${brief.objective}\n\nHow to do it:\n${brief.approach}\n\nExpected result:\n${brief.expectedOutcome}`;
|
|
685
721
|
return `${common}\n\n${policy}\n\n${contract}`;
|
|
686
722
|
}
|
|
687
723
|
|
|
724
|
+
export function withoutActionInputContext(context, preserveInputIds = []) {
|
|
725
|
+
const preserved = new Set((Array.isArray(preserveInputIds) ? preserveInputIds : []).filter(Boolean));
|
|
726
|
+
return (Array.isArray(context) ? context : []).filter(entry => (
|
|
727
|
+
entry?.type !== 'input' || (entry.inputId && preserved.has(entry.inputId))
|
|
728
|
+
));
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
export function canonicalActionInstruction(workItem, action, context = action?.context || []) {
|
|
732
|
+
const workflowSnapshot = workItem?.workflowSnapshot || null;
|
|
733
|
+
const workflowStage = (Array.isArray(workflowSnapshot?.stages) ? workflowSnapshot.stages : [])
|
|
734
|
+
.find(stage => stage?.id === action?.stageId)
|
|
735
|
+
|| (Array.isArray(workflowSnapshot?.stages) ? workflowSnapshot.stages : [])
|
|
736
|
+
.find(stage => stage?.type === action?.type)
|
|
737
|
+
|| null;
|
|
738
|
+
const actionInstructions = workflowSnapshot?.actionInstructions;
|
|
739
|
+
const policyInstruction = actionInstructions && Object.hasOwn(actionInstructions, action?.type)
|
|
740
|
+
? actionInstructions[action.type]
|
|
741
|
+
: actionInstructions?.custom;
|
|
742
|
+
const stage = {
|
|
743
|
+
...(workflowStage || {}),
|
|
744
|
+
id: action?.stageId || workflowStage?.id || action?.type,
|
|
745
|
+
type: action?.type || workflowStage?.type || 'custom',
|
|
746
|
+
instruction: workflowStage?.instruction || policyInstruction || '',
|
|
747
|
+
brief: action?.brief || workflowStage?.brief || workflowStage || null,
|
|
748
|
+
assignmentPolicy: action?.assignmentPolicy,
|
|
749
|
+
modelPolicy: action?.modelPolicy,
|
|
750
|
+
dependsOnStageIds: action?.dependsOnStageIds,
|
|
751
|
+
workspaceMode: action?.workspaceMode,
|
|
752
|
+
changesRequestedStageId: action?.changesRequestedStageId,
|
|
753
|
+
maxAttempts: action?.maxAttempts,
|
|
754
|
+
};
|
|
755
|
+
return actionInstruction(stage, workItem, context);
|
|
756
|
+
}
|
|
757
|
+
|
|
688
758
|
export function actionForStage(stage, workItem, context = []) {
|
|
689
759
|
return {
|
|
690
760
|
type: stage.type,
|