@bpmsoftwaresolutions/ai-engine-client 1.1.87 → 1.1.88

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,6 +1,67 @@
1
+ import { isPlainObject } from '../utils/text.js';
2
+ import { buildVerificationError } from '../utils/verified-mutations.js';
3
+
1
4
  export function createImplementationArtifactsDomain(client) {
2
5
  return {
3
6
  getArtifactManifest: (implementationItemId) => client._request(`/api/governed-implementation/items/${implementationItemId}/artifact-manifest`),
4
7
  getDecisionPacket: (implementationItemId) => client._request(`/api/governed-implementation/items/${implementationItemId}/decision-packet`),
8
+ verifyImplementationItemArtifacts: async (implementationItemId, { requiredArtifacts = [] } = {}) => {
9
+ const requiredKinds = Array.isArray(requiredArtifacts) ? requiredArtifacts : [];
10
+ const verifications = [];
11
+ const artifactReaders = {
12
+ artifact_manifest: async () => client.getArtifactManifest(implementationItemId),
13
+ decision_packet: async () => client.getDecisionPacket(implementationItemId),
14
+ };
15
+
16
+ for (const kind of requiredKinds) {
17
+ const reader = artifactReaders[kind];
18
+ if (typeof reader !== 'function') {
19
+ throw new Error(`Unsupported required artifact: ${kind}`);
20
+ }
21
+ const payload = await reader();
22
+ if (payload?.source_truth !== 'sql') {
23
+ throw buildVerificationError(`Artifact ${kind} did not declare SQL as source_truth.`, {
24
+ artifact_kind: kind,
25
+ verified_current_state: payload ?? null,
26
+ });
27
+ }
28
+ if (payload?.item_id !== implementationItemId) {
29
+ throw buildVerificationError(`Artifact ${kind} returned item_id ${payload?.item_id ?? 'unknown'} instead of ${implementationItemId}.`, {
30
+ artifact_kind: kind,
31
+ verified_current_state: payload ?? null,
32
+ });
33
+ }
34
+ const dataKey = kind === 'artifact_manifest' ? 'artifact_manifest' : 'decision_packet';
35
+ if (!isPlainObject(payload?.[dataKey])) {
36
+ throw buildVerificationError(`Artifact ${kind} did not include required ${dataKey} data.`, {
37
+ artifact_kind: kind,
38
+ verified_current_state: payload ?? null,
39
+ });
40
+ }
41
+ verifications.push({
42
+ artifact_kind: kind,
43
+ http_status: 200,
44
+ source_truth: payload.source_truth,
45
+ item_id: payload.item_id,
46
+ verified_current_state: payload[dataKey],
47
+ });
48
+ }
49
+
50
+ return {
51
+ itemId: implementationItemId,
52
+ requiredArtifacts: requiredKinds,
53
+ verified: true,
54
+ mutationVerificationEvidence: {
55
+ mutation_name: 'verifyImplementationItemArtifacts',
56
+ evidence_label: `implementation-item-artifacts:${implementationItemId}`,
57
+ mutation_attempted: false,
58
+ authoritative_read_performed: true,
59
+ post_condition_verified: true,
60
+ expected_state: { required_artifacts: requiredKinds },
61
+ verified_current_state: verifications,
62
+ },
63
+ artifacts: verifications,
64
+ };
65
+ },
5
66
  };
6
67
  }
@@ -0,0 +1,41 @@
1
+ import { executeVerifiedMutation } from '../utils/verified-mutations.js';
2
+
3
+ export function createImplementationChecksDomain(client) {
4
+ return {
5
+ getImplementationItemAcceptanceChecks: (implementationItemId) => client._request(`/api/governed-implementation/items/${implementationItemId}/acceptance-checks`),
6
+ updateAcceptanceCheckStatus: (implementationItemId, acceptanceCheckId, body) => {
7
+ client._assertExecutionEligibility({
8
+ ...(body || {}),
9
+ claimed_item_id: body?.claimed_item_id || implementationItemId,
10
+ requested_mutation_surface: body?.requested_mutation_surface || 'implementation_acceptance_check',
11
+ verification_required: true,
12
+ });
13
+ return client._request(
14
+ `/api/governed-implementation/items/${implementationItemId}/acceptance-checks/${acceptanceCheckId}/status`,
15
+ { method: 'PATCH', body }
16
+ );
17
+ },
18
+ updateAcceptanceCheckStatusVerified: (implementationItemId, acceptanceCheckId, status, body = {}) => executeVerifiedMutation({
19
+ mutationName: 'updateAcceptanceCheckStatus',
20
+ evidenceLabel: `acceptance-check-status:${implementationItemId}:${acceptanceCheckId}`,
21
+ mutationFn: () => client.updateAcceptanceCheckStatus(implementationItemId, acceptanceCheckId, { ...body, status }),
22
+ verificationFn: async () => client.getImplementationItemAcceptanceChecks(implementationItemId),
23
+ expectedState: (verificationResult) => {
24
+ const checks = Array.isArray(verificationResult?.acceptance_checks) ? verificationResult.acceptance_checks : [];
25
+ const targetCheck = checks.find((check) => check?.acceptance_check_id === acceptanceCheckId) || null;
26
+ if (!targetCheck) {
27
+ return {
28
+ ok: false,
29
+ message: `Acceptance check ${acceptanceCheckId} was not returned by the authoritative read.`,
30
+ verifiedCurrentState: verificationResult,
31
+ };
32
+ }
33
+ return {
34
+ ok: targetCheck.status === status,
35
+ message: `Acceptance check ${acceptanceCheckId} status remained ${targetCheck.status ?? 'unknown'} after mutation attempt.`,
36
+ verifiedCurrentState: targetCheck,
37
+ };
38
+ },
39
+ }),
40
+ };
41
+ }
@@ -1,94 +1,9 @@
1
+ import { cleanText, isPlainObject } from '../utils/text.js';
2
+ import { buildVerificationError, executeVerifiedMutation } from '../utils/verified-mutations.js';
3
+
1
4
  export function createImplementationItemsDomain(client) {
2
5
  return {
3
- createImplementationTask: async (implementationItemId, {
4
- title,
5
- implementationPacketId,
6
- description,
7
- priority,
8
- assignedTo,
9
- assignedBy,
10
- dueAt,
11
- sortOrder,
12
- notes,
13
- createdBy,
14
- parentTaskId,
15
- claimId,
16
- claimProjectId,
17
- projectId,
18
- claimedItemId,
19
- workflowRunId,
20
- agentSessionId,
21
- allowedMutationSurfaces,
22
- acknowledgedReminders,
23
- executionIntent,
24
- executionType,
25
- executionPurpose,
26
- } = {}) => {
27
- const normalizedClaimId = String(claimId || '').trim() || null;
28
- const normalizedClaimProjectId = String(claimProjectId || projectId || '').trim() || null;
29
- const normalizedClaimedItemId = String(claimedItemId || implementationItemId || '').trim() || null;
30
- const normalizedWorkflowRunId = String(workflowRunId || '').trim() || null;
31
- const normalizedAgentSessionId = String(agentSessionId || '').trim() || null;
32
- const normalizedAllowedMutationSurfaces = Array.isArray(allowedMutationSurfaces) && allowedMutationSurfaces.length > 0
33
- ? allowedMutationSurfaces
34
- : ['project_roadmap_task'];
35
- const normalizedAcknowledgedReminders = Array.isArray(acknowledgedReminders)
36
- ? acknowledgedReminders
37
- : undefined;
38
- const normalizedExecutionIntent = (() => {
39
- if (executionIntent && typeof executionIntent === 'object') {
40
- const execution_type = String(executionIntent.execution_type || executionIntent.executionType || '').trim();
41
- const execution_purpose = String(executionIntent.execution_purpose || executionIntent.executionPurpose || '').trim();
42
- if (execution_type || execution_purpose) {
43
- return {
44
- ...(execution_type ? { execution_type } : {}),
45
- ...(execution_purpose ? { execution_purpose } : {}),
46
- };
47
- }
48
- }
49
- const normalizedExecutionType = String(executionType || '').trim() || 'implementation_task';
50
- const normalizedExecutionPurpose = String(executionPurpose || title || '').trim() || 'implementation task';
51
- return {
52
- execution_type: normalizedExecutionType,
53
- execution_purpose: normalizedExecutionPurpose,
54
- };
55
- })();
56
- await client._assertExecutionEligibility({
57
- claim_id: normalizedClaimId,
58
- claim_project_id: normalizedClaimProjectId,
59
- claimed_item_id: normalizedClaimedItemId,
60
- workflow_run_id: normalizedWorkflowRunId,
61
- agent_session_id: normalizedAgentSessionId,
62
- allowed_mutation_surfaces: normalizedAllowedMutationSurfaces,
63
- acknowledged_reminders: normalizedAcknowledgedReminders || [],
64
- requested_mutation_surface: 'project_roadmap_task',
65
- verification_required: true,
66
- });
67
- return client._request(`/api/operator/implementation-items/${implementationItemId}/tasks`, {
68
- method: 'POST',
69
- body: {
70
- title,
71
- implementation_packet_id: implementationPacketId,
72
- description,
73
- priority,
74
- assigned_to: assignedTo,
75
- assigned_by: assignedBy,
76
- due_at: dueAt,
77
- sort_order: sortOrder,
78
- notes,
79
- created_by: createdBy,
80
- parent_task_id: parentTaskId,
81
- claim_id: normalizedClaimId,
82
- claim_project_id: normalizedClaimProjectId,
83
- claimed_item_id: normalizedClaimedItemId,
84
- workflow_run_id: normalizedWorkflowRunId,
85
- agent_session_id: normalizedAgentSessionId,
86
- allowed_mutation_surfaces: normalizedAllowedMutationSurfaces,
87
- acknowledged_reminders: normalizedAcknowledgedReminders,
88
- execution_intent: normalizedExecutionIntent,
89
- },
90
- });
91
- },
6
+ createImplementationTask: (implementationItemId, request) => client.implementationTasks.createImplementationTask(implementationItemId, request),
92
7
  listImplementationTasks: (implementationItemId) => client._request(`/api/operator/implementation-items/${implementationItemId}/tasks`),
93
8
  listImplementationSubtasks: (taskId) => client._request(`/api/operator/implementation-item-tasks/${taskId}/subtasks`),
94
9
  updateImplementationTask: (taskId, updates) => client._request(`/api/operator/implementation-item-tasks/${taskId}`, {
@@ -103,5 +18,83 @@ export function createImplementationItemsDomain(client) {
103
18
  method: 'POST',
104
19
  body: { completed_by: request.completedBy },
105
20
  }),
21
+ updateImplementationItemStatus: async (implementationItemId, body) => {
22
+ await client._assertExecutionEligibility({
23
+ ...(body || {}),
24
+ claimed_item_id: body?.claimed_item_id || implementationItemId,
25
+ requested_mutation_surface: body?.requested_mutation_surface || 'implementation_packet_item',
26
+ verification_required: true,
27
+ });
28
+ const result = await client._request(`/api/governed-implementation/items/${implementationItemId}/status`, {
29
+ method: 'PATCH', body,
30
+ });
31
+ const expectedStatus = cleanText(body?.status);
32
+ const authoritativeItem = isPlainObject(result?.implementation_item) ? result.implementation_item : null;
33
+ const actualStatus = cleanText(authoritativeItem?.status);
34
+ if (expectedStatus && !authoritativeItem) {
35
+ throw buildVerificationError(
36
+ `updateImplementationItemStatus did not return authoritative item state for ${implementationItemId}.`,
37
+ {
38
+ mutation_name: 'updateImplementationItemStatus',
39
+ mutation_attempted: true,
40
+ authoritative_read_performed: false,
41
+ post_condition_verified: false,
42
+ expected_state: { status: expectedStatus },
43
+ mutation_result: result ?? null,
44
+ },
45
+ );
46
+ }
47
+ if (expectedStatus && actualStatus !== expectedStatus) {
48
+ throw buildVerificationError(
49
+ `updateImplementationItemStatus returned ${actualStatus ?? 'unknown'} instead of ${expectedStatus}.`,
50
+ {
51
+ mutation_name: 'updateImplementationItemStatus',
52
+ mutation_attempted: true,
53
+ authoritative_read_performed: true,
54
+ post_condition_verified: false,
55
+ expected_state: { status: expectedStatus },
56
+ verified_current_state: authoritativeItem ?? result ?? null,
57
+ mutation_result: result ?? null,
58
+ },
59
+ );
60
+ }
61
+ return result;
62
+ },
63
+ updateImplementationItemStatusVerified: (implementationItemId, status, body = {}) => executeVerifiedMutation({
64
+ mutationName: 'updateImplementationItemStatus',
65
+ evidenceLabel: `implementation-item-status:${implementationItemId}`,
66
+ mutationFn: () => client.updateImplementationItemStatus(implementationItemId, { ...body, status }),
67
+ verificationFn: async (mutationResult) => {
68
+ const workflowId = cleanText(body.workflowId)
69
+ || cleanText(body.workflow_id)
70
+ || cleanText(mutationResult?.workflow_id);
71
+ if (!workflowId) {
72
+ throw new Error('workflowId is required to verify implementation item status.');
73
+ }
74
+ const roadmap = await client.getWorkflowImplementationRoadmap(workflowId);
75
+ const items = Array.isArray(roadmap?.items) ? roadmap.items : [];
76
+ const matchedItem = items.find((item) => item?.implementation_item_id === implementationItemId) || null;
77
+ return {
78
+ workflow_id: workflowId,
79
+ item: matchedItem,
80
+ roadmap,
81
+ };
82
+ },
83
+ expectedState: (verificationResult) => {
84
+ const item = verificationResult?.item || null;
85
+ if (!item) {
86
+ return {
87
+ ok: false,
88
+ message: `Implementation item ${implementationItemId} was not present in the authoritative roadmap read.`,
89
+ verifiedCurrentState: verificationResult,
90
+ };
91
+ }
92
+ return {
93
+ ok: item.status === status,
94
+ message: `Implementation item ${implementationItemId} status remained ${item.status ?? 'unknown'} after mutation attempt.`,
95
+ verifiedCurrentState: item,
96
+ };
97
+ },
98
+ }),
106
99
  };
107
100
  }
@@ -1,3 +1,36 @@
1
+ import { cleanList, cleanText, isPlainObject } from '../utils/text.js';
2
+
3
+ function looksLikeUuid(value) {
4
+ const text = cleanText(value);
5
+ return Boolean(text && /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(text));
6
+ }
7
+
8
+ function countRoadmapProjectionLines(markdown) {
9
+ const lines = [];
10
+ let inSummary = false;
11
+ for (const line of String(markdown || '').split(/\r?\n/)) {
12
+ if (line.startsWith('## Roadmap Summary')) {
13
+ inSummary = true;
14
+ continue;
15
+ }
16
+ if (inSummary && line.startsWith('## ')) {
17
+ break;
18
+ }
19
+ if (inSummary && line.startsWith('- ')) {
20
+ lines.push(line);
21
+ }
22
+ }
23
+ const wanted = [
24
+ 'Total Items',
25
+ 'Total Tasks',
26
+ 'Total Subtasks',
27
+ 'Total Acceptance Checks',
28
+ 'Open Acceptance Checks',
29
+ 'Completion Percentage',
30
+ ];
31
+ return lines.filter((line) => wanted.some((label) => line.startsWith(`- ${label}:`)));
32
+ }
33
+
1
34
  export function createImplementationPacketsDomain(client) {
2
35
  return {
3
36
  importImplementationPacket: (body) => client._request('/api/governed-implementation/packets/import', { method: 'POST', body }),
@@ -11,5 +44,145 @@ export function createImplementationPacketsDomain(client) {
11
44
  }),
12
45
  getWorkflowImplementationRoadmap: (workflowId) => client._request(`/api/governed-implementation/workflows/${workflowId}/roadmap`),
13
46
  getWorkflowResumeContext: (workflowId) => client._request(`/api/governed-implementation/workflows/${workflowId}/resume-context`),
47
+ importImplementationPacketAndMaterializeRoadmap: async (packetPayload, {
48
+ importedBy,
49
+ requestedBy,
50
+ createdBy,
51
+ workflowId,
52
+ bindingRole = 'active',
53
+ assignedTo,
54
+ notes,
55
+ createAcceptanceSubtasks = true,
56
+ } = {}) => {
57
+ if (!isPlainObject(packetPayload)) {
58
+ throw new Error('packetPayload must be a JSON object.');
59
+ }
60
+
61
+ const normalizedImportedBy = cleanText(importedBy) || cleanText(requestedBy) || client.actorId;
62
+ const normalizedRequestedBy = cleanText(requestedBy) || normalizedImportedBy;
63
+ const normalizedCreatedBy = cleanText(createdBy) || normalizedRequestedBy;
64
+
65
+ const importedPacket = await client.importImplementationPacket({
66
+ ...packetPayload,
67
+ imported_by: normalizedImportedBy,
68
+ });
69
+ const packetId = cleanText(importedPacket?.packet_id || importedPacket?.packetId || importedPacket?.implementation_packet_key || importedPacket?.implementationPacketKey || packetPayload.packetId || packetPayload.packet_id);
70
+ const implementationPacketId = cleanText(importedPacket?.implementation_packet_id || importedPacket?.implementationPacketId);
71
+ if (!implementationPacketId) {
72
+ throw new Error('Imported implementation packet response must include implementation_packet_id.');
73
+ }
74
+ if (!looksLikeUuid(implementationPacketId)) {
75
+ throw new Error('Imported implementation packet response returned a non-UUID implementation_packet_id.');
76
+ }
77
+ const projectReference = client._resolveImplementationPacketProjectReference(packetPayload);
78
+ if (!projectReference) {
79
+ throw new Error('Could not resolve a project reference from the packet payload.');
80
+ }
81
+ const projectPayload = await client.getProject(projectReference);
82
+ const projectSummary = isPlainObject(projectPayload?.summary) ? projectPayload.summary : {};
83
+ const projectId = cleanText(projectSummary.project_id);
84
+ if (!projectId) {
85
+ throw new Error('Project id could not be resolved.');
86
+ }
87
+
88
+ const workflowReference = client._resolveImplementationPacketWorkflowReference(packetPayload, {
89
+ projectSummary,
90
+ workflowIdOverride: cleanText(workflowId),
91
+ });
92
+ if (!workflowReference) {
93
+ throw new Error('Could not resolve a workflow reference from the packet payload or project summary.');
94
+ }
95
+ const resolvedWorkflowId = await client._resolveImplementationPacketWorkflowId(workflowReference);
96
+ const workflowSlug = cleanText(projectSummary.workflow_slug);
97
+
98
+ const binding = await client.bindImplementationPacketToWorkflow(resolvedWorkflowId, {
99
+ packet_id: packetId,
100
+ binding_role: cleanText(bindingRole) || 'active',
101
+ created_by: normalizedCreatedBy,
102
+ notes: cleanText(notes),
103
+ });
104
+
105
+ const taskSurface = await client.ensureProjectRoadmapTaskSurface(projectId, {
106
+ requestedBy: normalizedRequestedBy,
107
+ assignedTo,
108
+ createAcceptanceSubtasks,
109
+ });
110
+
111
+ const roadmapReport = await client.getProjectImplementationRoadmapReport(projectId);
112
+ const roadmapMarkdown = await client.downloadProjectImplementationRoadmapReportMarkdown(projectId);
113
+ const phases = Array.isArray(packetPayload.phases) ? packetPayload.phases.filter(isPlainObject) : [];
114
+ const roadmapItems = phases.flatMap((phase) => (
115
+ Array.isArray(phase.items) ? phase.items.filter(isPlainObject) : []
116
+ ));
117
+ const taskRows = Array.isArray(taskSurface?.task_surfaces) ? taskSurface.task_surfaces.filter(isPlainObject) : [];
118
+ const subtaskRows = taskRows.filter((task) => cleanText(task.parent_task_id) !== null);
119
+ const acceptanceCheckCount = roadmapItems.reduce(
120
+ (total, item) => total + (Array.isArray(item.acceptanceChecks) ? item.acceptanceChecks.filter(Boolean).length : 0),
121
+ 0,
122
+ );
123
+
124
+ return {
125
+ status: 'materialized',
126
+ project_id: projectId,
127
+ project_slug: cleanText(projectSummary.project_slug),
128
+ workflow_id: resolvedWorkflowId,
129
+ workflow_slug: workflowSlug,
130
+ implementation_packet_id: implementationPacketId,
131
+ implementation_packet_key: packetId,
132
+ binding,
133
+ task_surface: taskSurface,
134
+ roadmap_projection: {
135
+ report: roadmapReport,
136
+ markdown: roadmapMarkdown,
137
+ count_lines: countRoadmapProjectionLines(roadmapMarkdown),
138
+ phase_count: phases.length,
139
+ roadmap_item_count: roadmapItems.length,
140
+ task_count: taskRows.length,
141
+ subtask_count: subtaskRows.length,
142
+ acceptance_check_count: acceptanceCheckCount,
143
+ },
144
+ };
145
+ },
146
+ _resolveImplementationPacketProjectReference: (packetPayload) => cleanText(packetPayload.projectId)
147
+ || cleanText(packetPayload.project_id)
148
+ || cleanText(packetPayload.projectSlug)
149
+ || cleanText(packetPayload.project_slug)
150
+ || cleanText(packetPayload?.scope?.projectId)
151
+ || cleanText(packetPayload?.scope?.project_id)
152
+ || cleanText(packetPayload?.scope?.projectSlug)
153
+ || cleanText(packetPayload?.scope?.project_slug)
154
+ || cleanText(packetPayload?.system?.slug),
155
+ _resolveImplementationPacketWorkflowReference: (packetPayload, { projectSummary, workflowIdOverride } = {}) => {
156
+ if (workflowIdOverride) return workflowIdOverride;
157
+ return cleanText(packetPayload.workflowId)
158
+ || cleanText(packetPayload.workflow_id)
159
+ || cleanText(packetPayload.workflowSlug)
160
+ || cleanText(packetPayload.workflow_slug)
161
+ || cleanText(packetPayload?.scope?.workflowId)
162
+ || cleanText(packetPayload?.scope?.workflow_id)
163
+ || cleanText(packetPayload?.scope?.workflowSlug)
164
+ || cleanText(packetPayload?.scope?.workflow_slug)
165
+ || cleanText(projectSummary?.workflow_id)
166
+ || cleanText(projectSummary?.workflow_slug);
167
+ },
168
+ _resolveImplementationPacketWorkflowId: async (workflowReference) => {
169
+ const normalized = cleanText(workflowReference);
170
+ if (!normalized) {
171
+ throw new Error('workflow reference is required.');
172
+ }
173
+ const workflow = await client.getWorkflow(normalized);
174
+ if (workflow) {
175
+ return cleanText(workflow.workflow_id) || cleanText(workflow.workflowId) || normalized;
176
+ }
177
+ const workflowList = await client.listWorkflows();
178
+ const workflows = Array.isArray(workflowList) ? workflowList : workflowList?.workflows;
179
+ const matches = Array.isArray(workflows)
180
+ ? workflows.filter((item) => cleanText(item?.slug) === normalized)
181
+ : [];
182
+ if (matches.length === 1) {
183
+ return cleanText(matches[0].workflow_id) || cleanText(matches[0].workflowId) || normalized;
184
+ }
185
+ throw new Error(`Workflow not found: ${normalized}`);
186
+ },
14
187
  };
15
188
  }
@@ -1,10 +1,129 @@
1
1
  export function createImplementationTasksDomain(client) {
2
2
  return {
3
- createImplementationTask: (implementationItemId, request) => client.createImplementationTask(implementationItemId, request),
4
- listImplementationTasks: (implementationItemId) => client.listImplementationTasks(implementationItemId),
5
- listImplementationSubtasks: (taskId) => client.listImplementationSubtasks(taskId),
6
- updateImplementationTask: (taskId, updates) => client.updateImplementationTask(taskId, updates),
7
- assignImplementationTask: (taskId, request) => client.assignImplementationTask(taskId, request),
8
- completeImplementationTask: (taskId, request) => client.completeImplementationTask(taskId, request),
3
+ createImplementationTask: (implementationItemId, request) => createImplementationTask(client, implementationItemId, request),
4
+ listImplementationTasks: (implementationItemId) => listImplementationTasks(client, implementationItemId),
5
+ listImplementationSubtasks: (taskId) => listImplementationSubtasks(client, taskId),
6
+ updateImplementationTask: (taskId, updates) => updateImplementationTask(client, taskId, updates),
7
+ assignImplementationTask: (taskId, request) => assignImplementationTask(client, taskId, request),
8
+ completeImplementationTask: (taskId, request) => completeImplementationTask(client, taskId, request),
9
9
  };
10
10
  }
11
+
12
+ export async function createImplementationTask(client, implementationItemId, {
13
+ title,
14
+ implementationPacketId,
15
+ description,
16
+ priority,
17
+ assignedTo,
18
+ assignedBy,
19
+ dueAt,
20
+ sortOrder,
21
+ notes,
22
+ createdBy,
23
+ parentTaskId,
24
+ claimId,
25
+ claimProjectId,
26
+ projectId,
27
+ claimedItemId,
28
+ workflowRunId,
29
+ agentSessionId,
30
+ allowedMutationSurfaces,
31
+ acknowledgedReminders,
32
+ executionIntent,
33
+ executionType,
34
+ executionPurpose,
35
+ } = {}) {
36
+ const normalizedClaimId = String(claimId || '').trim() || null;
37
+ const normalizedClaimProjectId = String(claimProjectId || projectId || '').trim() || null;
38
+ const normalizedClaimedItemId = String(claimedItemId || implementationItemId || '').trim() || null;
39
+ const normalizedWorkflowRunId = String(workflowRunId || '').trim() || null;
40
+ const normalizedAgentSessionId = String(agentSessionId || '').trim() || null;
41
+ const normalizedAllowedMutationSurfaces = Array.isArray(allowedMutationSurfaces) && allowedMutationSurfaces.length > 0
42
+ ? allowedMutationSurfaces
43
+ : ['project_roadmap_task'];
44
+ const normalizedAcknowledgedReminders = Array.isArray(acknowledgedReminders)
45
+ ? acknowledgedReminders
46
+ : undefined;
47
+ const normalizedExecutionIntent = (() => {
48
+ if (executionIntent && typeof executionIntent === 'object') {
49
+ const execution_type = String(executionIntent.execution_type || executionIntent.executionType || '').trim();
50
+ const execution_purpose = String(executionIntent.execution_purpose || executionIntent.executionPurpose || '').trim();
51
+ if (execution_type || execution_purpose) {
52
+ return {
53
+ ...(execution_type ? { execution_type } : {}),
54
+ ...(execution_purpose ? { execution_purpose } : {}),
55
+ };
56
+ }
57
+ }
58
+ const normalizedExecutionType = String(executionType || '').trim() || 'implementation_task';
59
+ const normalizedExecutionPurpose = String(executionPurpose || title || '').trim() || 'implementation task';
60
+ return {
61
+ execution_type: normalizedExecutionType,
62
+ execution_purpose: normalizedExecutionPurpose,
63
+ };
64
+ })();
65
+ await client._assertExecutionEligibility({
66
+ claim_id: normalizedClaimId,
67
+ claim_project_id: normalizedClaimProjectId,
68
+ claimed_item_id: normalizedClaimedItemId,
69
+ workflow_run_id: normalizedWorkflowRunId,
70
+ agent_session_id: normalizedAgentSessionId,
71
+ allowed_mutation_surfaces: normalizedAllowedMutationSurfaces,
72
+ acknowledged_reminders: normalizedAcknowledgedReminders || [],
73
+ requested_mutation_surface: 'project_roadmap_task',
74
+ verification_required: true,
75
+ });
76
+ return client._request(`/api/operator/implementation-items/${implementationItemId}/tasks`, {
77
+ method: 'POST',
78
+ body: {
79
+ title,
80
+ implementation_packet_id: implementationPacketId,
81
+ description,
82
+ priority,
83
+ assigned_to: assignedTo,
84
+ assigned_by: assignedBy,
85
+ due_at: dueAt,
86
+ sort_order: sortOrder,
87
+ notes,
88
+ created_by: createdBy,
89
+ parent_task_id: parentTaskId,
90
+ claim_id: normalizedClaimId,
91
+ claim_project_id: normalizedClaimProjectId,
92
+ claimed_item_id: normalizedClaimedItemId,
93
+ workflow_run_id: normalizedWorkflowRunId,
94
+ agent_session_id: normalizedAgentSessionId,
95
+ allowed_mutation_surfaces: normalizedAllowedMutationSurfaces,
96
+ acknowledged_reminders: normalizedAcknowledgedReminders,
97
+ execution_intent: normalizedExecutionIntent,
98
+ },
99
+ });
100
+ }
101
+
102
+ export function listImplementationTasks(client, implementationItemId) {
103
+ return client._request(`/api/operator/implementation-items/${implementationItemId}/tasks`);
104
+ }
105
+
106
+ export function listImplementationSubtasks(client, taskId) {
107
+ return client._request(`/api/operator/implementation-item-tasks/${taskId}/subtasks`);
108
+ }
109
+
110
+ export function updateImplementationTask(client, taskId, updates) {
111
+ return client._request(`/api/operator/implementation-item-tasks/${taskId}`, {
112
+ method: 'PATCH',
113
+ body: updates,
114
+ });
115
+ }
116
+
117
+ export function assignImplementationTask(client, taskId, request = {}) {
118
+ return client._request(`/api/operator/implementation-item-tasks/${taskId}/assign`, {
119
+ method: 'POST',
120
+ body: { assigned_to: request.assignedTo, assigned_by: request.assignedBy },
121
+ });
122
+ }
123
+
124
+ export function completeImplementationTask(client, taskId, request = {}) {
125
+ return client._request(`/api/operator/implementation-item-tasks/${taskId}/complete`, {
126
+ method: 'POST',
127
+ body: { completed_by: request.completedBy },
128
+ });
129
+ }