@yeaft/webchat-agent 1.0.201 → 1.0.202
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.
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":"1.0.
|
|
1
|
+
{"version":"1.0.202"}
|
package/package.json
CHANGED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
function normalizeCriteria(value) {
|
|
2
|
+
if (!Array.isArray(value)) return null;
|
|
3
|
+
return value.map(item => String(item).trim()).filter(Boolean);
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export function normalizeContractPatch(value) {
|
|
7
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) return null;
|
|
8
|
+
const patch = {};
|
|
9
|
+
if (typeof value.goal === 'string' && value.goal.trim()) patch.goal = value.goal.trim();
|
|
10
|
+
if (Object.hasOwn(value, 'acceptanceCriteria')) {
|
|
11
|
+
const criteria = normalizeCriteria(value.acceptanceCriteria);
|
|
12
|
+
if (!criteria) throw new Error('contractPatch.acceptanceCriteria must be an array');
|
|
13
|
+
patch.acceptanceCriteria = criteria;
|
|
14
|
+
}
|
|
15
|
+
return Object.keys(patch).length > 0 ? patch : null;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function normalizeAcceptanceChecks(value, criteria) {
|
|
19
|
+
if (!Array.isArray(value) || value.length !== criteria.length) return null;
|
|
20
|
+
const checks = value.map((raw, index) => {
|
|
21
|
+
if (!raw || typeof raw !== 'object' || Array.isArray(raw)) return null;
|
|
22
|
+
const criterion = typeof raw.criterion === 'string' ? raw.criterion.trim() : '';
|
|
23
|
+
const status = ['passed', 'deferred', 'not_applicable'].includes(raw.status) ? raw.status : '';
|
|
24
|
+
const evidence = typeof raw.evidence === 'string' ? raw.evidence.trim().slice(0, 1_000) : '';
|
|
25
|
+
if (criterion !== criteria[index] || !status || !evidence) return null;
|
|
26
|
+
return { criterion, status, evidence };
|
|
27
|
+
});
|
|
28
|
+
return checks.every(Boolean) ? checks : null;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function validateCompletedResult(result, action, workItem) {
|
|
32
|
+
if (result.outcome !== 'completed') return;
|
|
33
|
+
if (result.evidence.length === 0) {
|
|
34
|
+
result.outcome = 'failed';
|
|
35
|
+
result.error = 'Completed Action requires at least one concrete evidence item';
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
const criteria = result.contractPatch?.acceptanceCriteria
|
|
39
|
+
?? (Array.isArray(workItem.acceptanceCriteria) ? workItem.acceptanceCriteria : []);
|
|
40
|
+
const checks = normalizeAcceptanceChecks(result.acceptanceChecks, criteria);
|
|
41
|
+
if (!checks) {
|
|
42
|
+
result.outcome = 'failed';
|
|
43
|
+
result.error = 'Completed Action requires one ordered acceptance check with evidence for every acceptance criterion';
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const mustVerify = action.type === 'test'
|
|
47
|
+
|| action.type === 'deliver'
|
|
48
|
+
|| (action.type === 'review' && result.reviewDecision === 'approved');
|
|
49
|
+
if (mustVerify && checks.some(check => check.status !== 'passed')) {
|
|
50
|
+
result.outcome = 'failed';
|
|
51
|
+
result.error = `${action.type} Action requires every acceptance check to pass`;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -9,60 +9,7 @@ import {
|
|
|
9
9
|
import { renderSessionContextSnapshot } from './session-context.js';
|
|
10
10
|
import { normalizeEvidence } from './evidence.js';
|
|
11
11
|
import { applyAdditivePlanProposal } from './plan-mutation.js';
|
|
12
|
-
|
|
13
|
-
function normalizeCriteria(value) {
|
|
14
|
-
if (!Array.isArray(value)) return null;
|
|
15
|
-
return value.map(item => String(item).trim()).filter(Boolean);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
function normalizeContractPatch(value) {
|
|
19
|
-
if (!value || typeof value !== 'object' || Array.isArray(value)) return null;
|
|
20
|
-
const patch = {};
|
|
21
|
-
if (typeof value.goal === 'string' && value.goal.trim()) patch.goal = value.goal.trim();
|
|
22
|
-
if (Object.prototype.hasOwnProperty.call(value, 'acceptanceCriteria')) {
|
|
23
|
-
const criteria = normalizeCriteria(value.acceptanceCriteria);
|
|
24
|
-
if (!criteria) throw new Error('contractPatch.acceptanceCriteria must be an array');
|
|
25
|
-
patch.acceptanceCriteria = criteria;
|
|
26
|
-
}
|
|
27
|
-
return Object.keys(patch).length > 0 ? patch : null;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function normalizeAcceptanceChecks(value, criteria) {
|
|
31
|
-
if (!Array.isArray(value) || value.length !== criteria.length) return null;
|
|
32
|
-
const checks = value.map((raw, index) => {
|
|
33
|
-
if (!raw || typeof raw !== 'object' || Array.isArray(raw)) return null;
|
|
34
|
-
const criterion = typeof raw.criterion === 'string' ? raw.criterion.trim() : '';
|
|
35
|
-
const status = ['passed', 'deferred', 'not_applicable'].includes(raw.status) ? raw.status : '';
|
|
36
|
-
const evidence = typeof raw.evidence === 'string' ? raw.evidence.trim().slice(0, 1_000) : '';
|
|
37
|
-
if (criterion !== criteria[index] || !status || !evidence) return null;
|
|
38
|
-
return { criterion, status, evidence };
|
|
39
|
-
});
|
|
40
|
-
return checks.every(Boolean) ? checks : null;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function validateCompletedResult(result, action, workItem) {
|
|
44
|
-
if (result.outcome !== 'completed') return;
|
|
45
|
-
if (result.evidence.length === 0) {
|
|
46
|
-
result.outcome = 'failed';
|
|
47
|
-
result.error = 'Completed Action requires at least one concrete evidence item';
|
|
48
|
-
return;
|
|
49
|
-
}
|
|
50
|
-
const criteria = result.contractPatch?.acceptanceCriteria
|
|
51
|
-
?? (Array.isArray(workItem.acceptanceCriteria) ? workItem.acceptanceCriteria : []);
|
|
52
|
-
const checks = normalizeAcceptanceChecks(result.acceptanceChecks, criteria);
|
|
53
|
-
if (!checks) {
|
|
54
|
-
result.outcome = 'failed';
|
|
55
|
-
result.error = 'Completed Action requires one ordered acceptance check with evidence for every acceptance criterion';
|
|
56
|
-
return;
|
|
57
|
-
}
|
|
58
|
-
const mustVerify = action.type === 'test'
|
|
59
|
-
|| action.type === 'deliver'
|
|
60
|
-
|| (action.type === 'review' && result.reviewDecision === 'approved');
|
|
61
|
-
if (mustVerify && checks.some(check => check.status !== 'passed')) {
|
|
62
|
-
result.outcome = 'failed';
|
|
63
|
-
result.error = `${action.type} Action requires every acceptance check to pass`;
|
|
64
|
-
}
|
|
65
|
-
}
|
|
12
|
+
import { normalizeContractPatch, validateCompletedResult } from './completion-contract.js';
|
|
66
13
|
|
|
67
14
|
function normalizeTerminalResult(result, action) {
|
|
68
15
|
if (!result || !RUN_OUTCOMES.includes(result.outcome)) {
|
|
@@ -31,7 +31,9 @@ import { loadMCPConfig } from '../config.js';
|
|
|
31
31
|
import { MCPManager } from '../mcp.js';
|
|
32
32
|
import { buildMcpFlattenedTools } from '../tools/mcp-tools.js';
|
|
33
33
|
import { recallWorkspaceSessionContext } from './workspace-context.js';
|
|
34
|
-
import { BUILT_IN_ACTION_TYPES } from './workflow.js';
|
|
34
|
+
import { applyGeneratedPlan, BUILT_IN_ACTION_TYPES } from './workflow.js';
|
|
35
|
+
import { normalizeContractPatch, validateCompletedResult } from './completion-contract.js';
|
|
36
|
+
import { normalizeEvidence } from './evidence.js';
|
|
35
37
|
import {
|
|
36
38
|
MAINLINE_CONTEXT_HARD_LIMIT_BYTES,
|
|
37
39
|
buildMainlineContextSnapshot,
|
|
@@ -300,14 +302,20 @@ export function planningVpCatalog(vps) {
|
|
|
300
302
|
}));
|
|
301
303
|
}
|
|
302
304
|
|
|
303
|
-
export function createSubmitWorkItemPlanTool({
|
|
305
|
+
export function createSubmitWorkItemPlanTool({
|
|
306
|
+
vps,
|
|
307
|
+
workItem,
|
|
308
|
+
collector,
|
|
309
|
+
isRunActive,
|
|
310
|
+
reservedStageIds = [],
|
|
311
|
+
}) {
|
|
304
312
|
const vpCatalog = planningVpCatalog(vps);
|
|
305
313
|
const vpIds = vpCatalog.map(vp => vp.id);
|
|
306
314
|
const actionTypes = BUILT_IN_ACTION_TYPES.filter(type => type !== 'triage');
|
|
307
315
|
const catalogDescription = `Action types: ${actionTypes.join(', ')}. Available VPs: ${vpCatalog.map(vp => `${vp.id} (${vp.role || vp.area || 'VP'}; ${vp.traits.join(', ') || 'no traits'})`).join('; ')}.`;
|
|
308
316
|
return defineTool({
|
|
309
317
|
name: 'SubmitWorkItemPlan',
|
|
310
|
-
description: `Submit the complete initial WorkItem contract and executable Action DAG. Every Action must describe this WorkItem's concrete objective, repository-aware approach, and verifiable expected outcome; never copy generic Action-type text. The reference workflow catalog does not replace this Action list. This tool
|
|
318
|
+
description: `Submit the complete initial WorkItem contract and executable Action DAG. Every Action must describe this WorkItem's concrete objective, repository-aware approach, and verifiable expected outcome; never copy generic Action-type text. The reference workflow catalog does not replace this Action list. If any Action uses isolated-write workspace mode, the plan must contain exactly one integrate Action in integrate workspace mode; that Action must depend directly on every isolated-write Action, and all later Actions must consume those writes through it. This tool validates the proposal immediately so you can correct an invalid graph in the same triage loop; Work Center persists only a valid proposal in the current Run finalization transaction. ${catalogDescription}`,
|
|
311
319
|
parameters: {
|
|
312
320
|
type: 'object',
|
|
313
321
|
additionalProperties: false,
|
|
@@ -331,6 +339,28 @@ export function createSubmitWorkItemPlanTool({ vps, collector, isRunActive }) {
|
|
|
331
339
|
async execute(input, ctx = {}) {
|
|
332
340
|
if (!isRunActive()) throw new Error('Work Center Run is no longer active');
|
|
333
341
|
if (collector.value) throw new Error('WorkItem plan was already submitted for this Run');
|
|
342
|
+
const contractPatch = normalizeContractPatch(input.contractPatch);
|
|
343
|
+
const proposedResult = {
|
|
344
|
+
outcome: 'completed',
|
|
345
|
+
evidence: normalizeEvidence(input.evidence),
|
|
346
|
+
contractPatch,
|
|
347
|
+
acceptanceChecks: input.acceptanceChecks,
|
|
348
|
+
};
|
|
349
|
+
validateCompletedResult(proposedResult, { type: 'triage' }, workItem);
|
|
350
|
+
if (proposedResult.outcome !== 'completed') throw new Error(proposedResult.error);
|
|
351
|
+
const effectiveWorkItem = contractPatch ? {
|
|
352
|
+
...workItem,
|
|
353
|
+
goal: contractPatch.goal ?? workItem.goal,
|
|
354
|
+
acceptanceCriteria: contractPatch.acceptanceCriteria ?? workItem.acceptanceCriteria,
|
|
355
|
+
} : workItem;
|
|
356
|
+
applyGeneratedPlan(effectiveWorkItem, {
|
|
357
|
+
workItemType: input.workItemType,
|
|
358
|
+
actions: input.actions,
|
|
359
|
+
}, {
|
|
360
|
+
availableVpIds: vpIds,
|
|
361
|
+
reservedStageIds,
|
|
362
|
+
});
|
|
363
|
+
if (!isRunActive()) throw new Error('Work Center Run is no longer active');
|
|
334
364
|
collector.value = structuredClone(input);
|
|
335
365
|
ctx.requestEndTurn?.({ kind: 'work_item_plan_submitted' });
|
|
336
366
|
return JSON.stringify({ submitted: true, actionCount: input.actions.length });
|
|
@@ -860,7 +890,13 @@ export class WorkItemRunner {
|
|
|
860
890
|
&& workItem?.workflowSnapshot?.planningMode === 'ai';
|
|
861
891
|
const runTools = [];
|
|
862
892
|
if (planToolEnabled) runTools.push(createSubmitWorkItemPlanTool({
|
|
863
|
-
vps: this.registry.listVps(),
|
|
893
|
+
vps: this.registry.listVps(),
|
|
894
|
+
workItem,
|
|
895
|
+
collector: planCollector,
|
|
896
|
+
isRunActive,
|
|
897
|
+
reservedStageIds: executionAction.stageId?.startsWith('replan-')
|
|
898
|
+
? this.store.getWorkItemDetail(workItem.id).actions.map(item => item.stageId)
|
|
899
|
+
: [],
|
|
864
900
|
}));
|
|
865
901
|
if (!planToolEnabled && workItem?.workflowSnapshot?.executionMode === 'graph') {
|
|
866
902
|
runTools.push(createProposeWorkItemActionsTool({
|
|
@@ -373,7 +373,7 @@ export function resolvePlanningWorkflowSnapshot(settings, requestedWorkItemType
|
|
|
373
373
|
const typeInstruction = requestedType
|
|
374
374
|
? `The user explicitly selected workItemType "${requestedType}". Keep that exact type.`
|
|
375
375
|
: '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. Split independent work into separate Actions and declare dependsOnActionIds. Use workspaceMode read for analysis, isolated-write for independent Git changes, integrate for an integrate Action that combines isolated-write dependencies, and shared for serial side effects. Non-Git or dirty workspaces are serialized automatically. 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.`;
|
|
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. Split independent work into separate Actions and declare dependsOnActionIds. Use workspaceMode read for analysis, 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. 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
377
|
return normalizeWorkflowDefinition({
|
|
378
378
|
id: 'ai-planned',
|
|
379
379
|
name: 'AI planned',
|