@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.
@@ -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 workItemMessages = Array.isArray(workItem.messages) && workItem.messages.length > 0
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,