@pixelbyte-software/pixcode 1.42.0 → 1.42.2
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/dist/assets/{index-CkOamyD3.js → index-CMeiCqQf.js} +182 -182
- package/dist/index.html +1 -1
- package/dist-server/server/modules/orchestration/workflows/context-packet.js +89 -0
- package/dist-server/server/modules/orchestration/workflows/context-packet.js.map +1 -0
- package/dist-server/server/modules/orchestration/workflows/workflow-fallback-policy.js +114 -0
- package/dist-server/server/modules/orchestration/workflows/workflow-fallback-policy.js.map +1 -0
- package/dist-server/server/modules/orchestration/workflows/workflow-replay.js +177 -0
- package/dist-server/server/modules/orchestration/workflows/workflow-replay.js.map +1 -0
- package/dist-server/server/modules/orchestration/workflows/workflow-runner.js +58 -7
- package/dist-server/server/modules/orchestration/workflows/workflow-runner.js.map +1 -1
- package/dist-server/server/modules/orchestration/workflows/workflow-trace.js +95 -0
- package/dist-server/server/modules/orchestration/workflows/workflow-trace.js.map +1 -1
- package/dist-server/server/modules/orchestration/workflows/workflow.routes.js +88 -0
- package/dist-server/server/modules/orchestration/workflows/workflow.routes.js.map +1 -1
- package/package.json +1 -1
- package/scripts/smoke/context-packet.mjs +43 -0
- package/scripts/smoke/workflow-fallback-replay.mjs +56 -0
- package/server/modules/orchestration/workflows/context-packet.ts +186 -0
- package/server/modules/orchestration/workflows/workflow-fallback-policy.ts +161 -0
- package/server/modules/orchestration/workflows/workflow-replay.ts +254 -0
- package/server/modules/orchestration/workflows/workflow-runner.ts +119 -6
- package/server/modules/orchestration/workflows/workflow-trace.ts +98 -0
- package/server/modules/orchestration/workflows/workflow.routes.ts +107 -0
- package/server/modules/orchestration/workflows/workflow.types.ts +7 -0
package/dist/index.html
CHANGED
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
|
|
36
36
|
<!-- Prevent zoom on iOS -->
|
|
37
37
|
<meta name="format-detection" content="telephone=no" />
|
|
38
|
-
<script type="module" crossorigin src="/assets/index-
|
|
38
|
+
<script type="module" crossorigin src="/assets/index-CMeiCqQf.js"></script>
|
|
39
39
|
<link rel="modulepreload" crossorigin href="/assets/vendor-react-D7WwDXvu.js">
|
|
40
40
|
<link rel="modulepreload" crossorigin href="/assets/vendor-codemirror-CzYAOTxS.js">
|
|
41
41
|
<link rel="modulepreload" crossorigin href="/assets/vendor-xterm-CJZjLICi.js">
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
export const PIXCODE_CONTEXT_PROTOCOL = 'pixcode.context.v1';
|
|
2
|
+
export const MAX_CONTEXT_PACKET_TEXT_CHARS = 16_000;
|
|
3
|
+
function compactContextText(text) {
|
|
4
|
+
const originalChars = text.length;
|
|
5
|
+
if (originalChars <= MAX_CONTEXT_PACKET_TEXT_CHARS) {
|
|
6
|
+
return {
|
|
7
|
+
text,
|
|
8
|
+
originalChars,
|
|
9
|
+
compactedChars: originalChars,
|
|
10
|
+
omittedChars: 0,
|
|
11
|
+
wasCompacted: false,
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
const edge = Math.floor(MAX_CONTEXT_PACKET_TEXT_CHARS / 2);
|
|
15
|
+
const omittedChars = originalChars - MAX_CONTEXT_PACKET_TEXT_CHARS;
|
|
16
|
+
const compacted = [
|
|
17
|
+
text.slice(0, edge),
|
|
18
|
+
`\n\n[...${omittedChars} characters omitted from upstream context packet...]\n\n`,
|
|
19
|
+
text.slice(-edge),
|
|
20
|
+
].join('');
|
|
21
|
+
return {
|
|
22
|
+
text: compacted,
|
|
23
|
+
originalChars,
|
|
24
|
+
compactedChars: compacted.length,
|
|
25
|
+
omittedChars,
|
|
26
|
+
wasCompacted: true,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
export function buildWorkflowContextPacket({ run, node, workspaceTarget, inputContext, inputNodeIds, }) {
|
|
30
|
+
const compacted = compactContextText(inputContext);
|
|
31
|
+
return {
|
|
32
|
+
protocol: PIXCODE_CONTEXT_PROTOCOL,
|
|
33
|
+
originalUserRequest: run.input?.trim() || '(No original user request was provided.)',
|
|
34
|
+
project: {
|
|
35
|
+
kind: workspaceTarget.kind,
|
|
36
|
+
label: workspaceTarget.label,
|
|
37
|
+
selectedProjectName: workspaceTarget.selectedProjectName,
|
|
38
|
+
},
|
|
39
|
+
task: {
|
|
40
|
+
workflowId: run.workflowId,
|
|
41
|
+
workflowRunId: run.id,
|
|
42
|
+
nodeId: node.id,
|
|
43
|
+
stage: node.stage,
|
|
44
|
+
assignment: node.assignment,
|
|
45
|
+
stepInstructions: node.prompt,
|
|
46
|
+
},
|
|
47
|
+
constraints: {
|
|
48
|
+
adapterId: node.adapterId,
|
|
49
|
+
agentLabel: node.agentLabel,
|
|
50
|
+
model: node.model,
|
|
51
|
+
permissionMode: node.permissionMode,
|
|
52
|
+
isolation: node.isolation,
|
|
53
|
+
toolsSettings: node.toolsSettings,
|
|
54
|
+
},
|
|
55
|
+
upstreamArtifacts: compacted.text
|
|
56
|
+
? [{
|
|
57
|
+
type: 'upstream-context',
|
|
58
|
+
text: compacted.text,
|
|
59
|
+
sourceNodeIds: inputNodeIds,
|
|
60
|
+
}]
|
|
61
|
+
: [],
|
|
62
|
+
runState: {
|
|
63
|
+
status: run.status,
|
|
64
|
+
startedAt: run.startedAt,
|
|
65
|
+
nodeCount: run.nodeRuns.length,
|
|
66
|
+
completedNodeIds: run.nodeRuns
|
|
67
|
+
.filter((nodeRun) => nodeRun.status === 'completed')
|
|
68
|
+
.map((nodeRun) => nodeRun.nodeId),
|
|
69
|
+
runningNodeIds: run.nodeRuns
|
|
70
|
+
.filter((nodeRun) => nodeRun.status === 'running')
|
|
71
|
+
.map((nodeRun) => nodeRun.nodeId),
|
|
72
|
+
},
|
|
73
|
+
compaction: {
|
|
74
|
+
maxChars: MAX_CONTEXT_PACKET_TEXT_CHARS,
|
|
75
|
+
originalChars: compacted.originalChars,
|
|
76
|
+
compactedChars: compacted.compactedChars,
|
|
77
|
+
omittedChars: compacted.omittedChars,
|
|
78
|
+
wasCompacted: compacted.wasCompacted,
|
|
79
|
+
},
|
|
80
|
+
createdAt: new Date().toISOString(),
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
export function formatContextPacketForPrompt(packet) {
|
|
84
|
+
return [
|
|
85
|
+
`Pixcode standardized init context packet (${PIXCODE_CONTEXT_PROTOCOL}):`,
|
|
86
|
+
JSON.stringify(packet, null, 2),
|
|
87
|
+
].join('\n');
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=context-packet.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context-packet.js","sourceRoot":"","sources":["../../../../../server/modules/orchestration/workflows/context-packet.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
export const PIXCODE_FALLBACK_POLICY_PROTOCOL = 'pixcode.fallback-policy.v1';
|
|
2
|
+
const DEFAULT_TRIGGERS = [
|
|
3
|
+
'provider_failure',
|
|
4
|
+
'timeout',
|
|
5
|
+
'tool_failure',
|
|
6
|
+
'invalid_output',
|
|
7
|
+
];
|
|
8
|
+
export const DEFAULT_WORKFLOW_FALLBACK_POLICY = {
|
|
9
|
+
protocol: PIXCODE_FALLBACK_POLICY_PROTOCOL,
|
|
10
|
+
enabled: true,
|
|
11
|
+
triggers: DEFAULT_TRIGGERS,
|
|
12
|
+
maxFallbacksPerRun: 3,
|
|
13
|
+
requireDifferentAgent: true,
|
|
14
|
+
};
|
|
15
|
+
function readRecord(value) {
|
|
16
|
+
return value && typeof value === 'object' ? value : undefined;
|
|
17
|
+
}
|
|
18
|
+
function readBoolean(value) {
|
|
19
|
+
return typeof value === 'boolean' ? value : undefined;
|
|
20
|
+
}
|
|
21
|
+
function readNumber(value) {
|
|
22
|
+
return typeof value === 'number' && Number.isFinite(value) ? value : undefined;
|
|
23
|
+
}
|
|
24
|
+
function isWorkflowFallbackTrigger(value) {
|
|
25
|
+
return value === 'provider_failure'
|
|
26
|
+
|| value === 'timeout'
|
|
27
|
+
|| value === 'tool_failure'
|
|
28
|
+
|| value === 'invalid_output';
|
|
29
|
+
}
|
|
30
|
+
function readFallbackTriggers(value) {
|
|
31
|
+
if (!Array.isArray(value))
|
|
32
|
+
return undefined;
|
|
33
|
+
const triggers = value.filter(isWorkflowFallbackTrigger);
|
|
34
|
+
return triggers.length > 0 ? [...new Set(triggers)] : undefined;
|
|
35
|
+
}
|
|
36
|
+
export function readWorkflowFallbackPolicy(metadata) {
|
|
37
|
+
const settings = readRecord(metadata?.settings) ?? {};
|
|
38
|
+
const configured = readRecord(settings.fallbackPolicy) ?? {};
|
|
39
|
+
const maxFallbacksPerRun = readNumber(configured.maxFallbacksPerRun);
|
|
40
|
+
return {
|
|
41
|
+
protocol: PIXCODE_FALLBACK_POLICY_PROTOCOL,
|
|
42
|
+
enabled: readBoolean(configured.enabled) ?? DEFAULT_WORKFLOW_FALLBACK_POLICY.enabled,
|
|
43
|
+
triggers: readFallbackTriggers(configured.triggers) ?? DEFAULT_WORKFLOW_FALLBACK_POLICY.triggers,
|
|
44
|
+
maxFallbacksPerRun: maxFallbacksPerRun === undefined
|
|
45
|
+
? DEFAULT_WORKFLOW_FALLBACK_POLICY.maxFallbacksPerRun
|
|
46
|
+
: Math.max(0, Math.min(8, Math.round(maxFallbacksPerRun))),
|
|
47
|
+
requireDifferentAgent: readBoolean(configured.requireDifferentAgent)
|
|
48
|
+
?? DEFAULT_WORKFLOW_FALLBACK_POLICY.requireDifferentAgent,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
export function classifyWorkflowFailure(reason, explicitTrigger) {
|
|
52
|
+
if (explicitTrigger)
|
|
53
|
+
return explicitTrigger;
|
|
54
|
+
const text = reason.toLocaleLowerCase('en');
|
|
55
|
+
if (/timed out|timeout|deadline/u.test(text))
|
|
56
|
+
return 'timeout';
|
|
57
|
+
if (/invalid (handoff|output|artifact|json|schema)|parse|protocol/u.test(text))
|
|
58
|
+
return 'invalid_output';
|
|
59
|
+
if (/tool|command|shell|exit code|permission|file write|write failed|network|fetch|curl|wget|gh |npm |git /u
|
|
60
|
+
.test(text)) {
|
|
61
|
+
return 'tool_failure';
|
|
62
|
+
}
|
|
63
|
+
return 'provider_failure';
|
|
64
|
+
}
|
|
65
|
+
function fallbackEventCount(run) {
|
|
66
|
+
return Array.isArray(run.metadata?.fallbackEvents) ? run.metadata.fallbackEvents.length : 0;
|
|
67
|
+
}
|
|
68
|
+
export function resolveWorkflowFallbackDecision({ run, node, reason, trigger, fallbackAgentInstanceId, }) {
|
|
69
|
+
const fallbackTrigger = classifyWorkflowFailure(reason, trigger);
|
|
70
|
+
const policy = readWorkflowFallbackPolicy(run.metadata);
|
|
71
|
+
if (!policy.enabled) {
|
|
72
|
+
return {
|
|
73
|
+
shouldFallback: false,
|
|
74
|
+
trigger: fallbackTrigger,
|
|
75
|
+
reason,
|
|
76
|
+
policy,
|
|
77
|
+
skippedReason: 'Fallback policy is disabled.',
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
if (!policy.triggers.includes(fallbackTrigger)) {
|
|
81
|
+
return {
|
|
82
|
+
shouldFallback: false,
|
|
83
|
+
trigger: fallbackTrigger,
|
|
84
|
+
reason,
|
|
85
|
+
policy,
|
|
86
|
+
skippedReason: `Fallback trigger ${fallbackTrigger} is not enabled.`,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
if (fallbackEventCount(run) >= policy.maxFallbacksPerRun) {
|
|
90
|
+
return {
|
|
91
|
+
shouldFallback: false,
|
|
92
|
+
trigger: fallbackTrigger,
|
|
93
|
+
reason,
|
|
94
|
+
policy,
|
|
95
|
+
skippedReason: `Fallback limit ${policy.maxFallbacksPerRun} reached.`,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
if (policy.requireDifferentAgent && fallbackAgentInstanceId && fallbackAgentInstanceId === node.agentInstanceId) {
|
|
99
|
+
return {
|
|
100
|
+
shouldFallback: false,
|
|
101
|
+
trigger: fallbackTrigger,
|
|
102
|
+
reason,
|
|
103
|
+
policy,
|
|
104
|
+
skippedReason: 'Fallback agent must be different from the failed agent.',
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
return {
|
|
108
|
+
shouldFallback: true,
|
|
109
|
+
trigger: fallbackTrigger,
|
|
110
|
+
reason,
|
|
111
|
+
policy,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=workflow-fallback-policy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workflow-fallback-policy.js","sourceRoot":"","sources":["../../../../../server/modules/orchestration/workflows/workflow-fallback-policy.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { redactTraceText } from '../../../modules/orchestration/workflows/workflow-trace.js';
|
|
2
|
+
export const PIXCODE_REPLAY_PROTOCOL = 'pixcode.workflow-replay.v1';
|
|
3
|
+
function safeNodeId(value) {
|
|
4
|
+
return value.replace(/[^a-zA-Z0-9_]+/g, '_').slice(0, 48) || 'node';
|
|
5
|
+
}
|
|
6
|
+
function readRecord(value) {
|
|
7
|
+
return value && typeof value === 'object' ? value : undefined;
|
|
8
|
+
}
|
|
9
|
+
function visibleNodes(run) {
|
|
10
|
+
return run.nodeRuns.filter((node) => !node.internal);
|
|
11
|
+
}
|
|
12
|
+
function defaultReplayNode(run) {
|
|
13
|
+
return visibleNodes(run).find((node) => node.status === 'failed')
|
|
14
|
+
?? [...visibleNodes(run)].reverse().find((node) => node.status !== 'skipped')
|
|
15
|
+
?? visibleNodes(run)[0];
|
|
16
|
+
}
|
|
17
|
+
function selectReplayNodes(run, scope, fromNodeId) {
|
|
18
|
+
const nodes = visibleNodes(run);
|
|
19
|
+
if (scope === 'run')
|
|
20
|
+
return nodes;
|
|
21
|
+
const requested = fromNodeId
|
|
22
|
+
? nodes.find((node) => node.nodeId === fromNodeId)
|
|
23
|
+
: defaultReplayNode(run);
|
|
24
|
+
return requested ? [requested] : [];
|
|
25
|
+
}
|
|
26
|
+
function compact(value, run, maxLength = 1_200) {
|
|
27
|
+
return redactTraceText(value, run, maxLength);
|
|
28
|
+
}
|
|
29
|
+
function nodeTraceSummary(run, node) {
|
|
30
|
+
const artifactTypes = (node.artifacts ?? []).map((artifact) => artifact.type).filter(Boolean);
|
|
31
|
+
return [
|
|
32
|
+
`Step: ${node.agentLabel || node.nodeId}`,
|
|
33
|
+
`Node id: ${node.nodeId}`,
|
|
34
|
+
`Status: ${node.status}`,
|
|
35
|
+
node.stage ? `Stage: ${node.stage}` : undefined,
|
|
36
|
+
node.adapterId ? `Adapter: ${node.adapterId}` : undefined,
|
|
37
|
+
node.model ? `Model: ${node.model}` : undefined,
|
|
38
|
+
node.error ? `Error: ${compact(node.error, run, 800)}` : undefined,
|
|
39
|
+
artifactTypes.length > 0 ? `Artifacts: ${artifactTypes.join(', ')}` : undefined,
|
|
40
|
+
node.outputText ? `Output excerpt:\n${compact(node.outputText, run)}` : undefined,
|
|
41
|
+
].filter(Boolean).join('\n');
|
|
42
|
+
}
|
|
43
|
+
function replayTraceSummary(run, nodes) {
|
|
44
|
+
return nodes.map((node) => nodeTraceSummary(run, node)).join('\n\n---\n\n');
|
|
45
|
+
}
|
|
46
|
+
function pushOperation(operations, kind, nodeId, summary) {
|
|
47
|
+
if (operations.some((operation) => operation.kind === kind && operation.nodeId === nodeId && operation.summary === summary)) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
operations.push({ kind, nodeId, summary });
|
|
51
|
+
}
|
|
52
|
+
function detectReplayOperations(run, nodes) {
|
|
53
|
+
const operations = [];
|
|
54
|
+
for (const node of nodes) {
|
|
55
|
+
for (const artifact of node.artifacts ?? []) {
|
|
56
|
+
if (artifact.type === 'file-diff') {
|
|
57
|
+
pushOperation(operations, 'file-write', node.nodeId, 'Prior step produced a file diff artifact.');
|
|
58
|
+
}
|
|
59
|
+
if (artifact.type === 'command-output') {
|
|
60
|
+
pushOperation(operations, 'shell', node.nodeId, 'Prior step produced command output.');
|
|
61
|
+
}
|
|
62
|
+
const text = [artifact.text, artifact.data ? JSON.stringify(artifact.data) : undefined]
|
|
63
|
+
.filter(Boolean)
|
|
64
|
+
.join('\n')
|
|
65
|
+
.toLocaleLowerCase('en');
|
|
66
|
+
if (/https?:\/\/|curl |wget |gh |npm publish|npm install|git push|ssh /u.test(text)) {
|
|
67
|
+
pushOperation(operations, 'network', node.nodeId, 'Prior artifact references a network-capable operation.');
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
const text = [node.outputText, node.error, node.promptPreview].filter(Boolean).join('\n').toLocaleLowerCase('en');
|
|
71
|
+
if (/apply_patch|write file|file write|modified files|changed files/u.test(text)) {
|
|
72
|
+
pushOperation(operations, 'file-write', node.nodeId, 'Prior step text references file-write activity.');
|
|
73
|
+
}
|
|
74
|
+
if (/shell|command|terminal|npm run|node |python |php |go test|cargo |make |exit code/u.test(text)) {
|
|
75
|
+
pushOperation(operations, 'shell', node.nodeId, 'Prior step text references shell execution.');
|
|
76
|
+
}
|
|
77
|
+
if (/https?:\/\/|curl |wget |gh |npm publish|npm install|git push|ssh |network/u.test(text)) {
|
|
78
|
+
pushOperation(operations, 'network', node.nodeId, 'Prior step text references a network-capable operation.');
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return operations;
|
|
82
|
+
}
|
|
83
|
+
function replayNodeFromRunNode(node, index, previousReplayNodeId, traceSummary, limitations, requiresApproval) {
|
|
84
|
+
const replayNodeId = `replay_${index + 1}_${safeNodeId(node.nodeId)}`;
|
|
85
|
+
return {
|
|
86
|
+
id: replayNodeId,
|
|
87
|
+
adapterId: node.adapterId || 'claude-code',
|
|
88
|
+
agentInstanceId: node.agentInstanceId,
|
|
89
|
+
agentLabel: node.agentLabel ? `${node.agentLabel} Replay` : 'Replay agent',
|
|
90
|
+
assignment: node.assignment ? `Replay: ${node.assignment}` : `Replay source node ${node.nodeId}`,
|
|
91
|
+
stage: node.stage ? `replay_${node.stage}` : 'replay',
|
|
92
|
+
model: node.model,
|
|
93
|
+
permissionMode: node.permissionMode === 'bypassPermissions' ? 'default' : node.permissionMode,
|
|
94
|
+
timeoutMs: node.timeoutMs,
|
|
95
|
+
inputs: previousReplayNodeId ? [previousReplayNodeId] : [],
|
|
96
|
+
output: 'both',
|
|
97
|
+
onFail: 'abort',
|
|
98
|
+
prompt: [
|
|
99
|
+
'This is a Pixcode workflow replay run.',
|
|
100
|
+
`Replay protocol: ${PIXCODE_REPLAY_PROTOCOL}`,
|
|
101
|
+
`Source node: ${node.nodeId}`,
|
|
102
|
+
requiresApproval
|
|
103
|
+
? 'Replay safety review found prior shell, network, or file-write activity. Do not repeat any such action unless the current CLI permission flow asks for and receives user approval.'
|
|
104
|
+
: 'Replay safety review did not find prior shell, network, or file-write artifacts, but still avoid destructive actions unless they are required and approved.',
|
|
105
|
+
'Use the trace summary to continue from the failure or inspect the run. Do not expose secrets, local-only paths, raw tool protocol, or irrelevant logs.',
|
|
106
|
+
`Known limitations:\n- ${limitations.join('\n- ')}`,
|
|
107
|
+
`Trace summary:\n${traceSummary}`,
|
|
108
|
+
`Original step prompt:\n${node.promptPreview || '(No source prompt was stored.)'}`,
|
|
109
|
+
].join('\n\n'),
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
export function buildWorkflowReplayPlan(run, options = {}) {
|
|
113
|
+
const scope = options.scope ?? 'node';
|
|
114
|
+
const nodes = selectReplayNodes(run, scope, options.fromNodeId);
|
|
115
|
+
if (nodes.length === 0) {
|
|
116
|
+
throw new Error('No replayable workflow steps were found.');
|
|
117
|
+
}
|
|
118
|
+
const limitations = [
|
|
119
|
+
'Replay uses stored run traces, prompt previews, messages, and artifacts; it cannot reproduce hidden provider state.',
|
|
120
|
+
'Replay reconstructs selected steps as a new workflow run instead of mutating the source run.',
|
|
121
|
+
'Shell, network, and file-write actions stay under the current CLI permission flow and require explicit replay approval when detected.',
|
|
122
|
+
];
|
|
123
|
+
const destructiveOperations = detectReplayOperations(run, nodes);
|
|
124
|
+
const requiresApproval = destructiveOperations.length > 0;
|
|
125
|
+
const traceSummary = replayTraceSummary(run, nodes);
|
|
126
|
+
const replayNodes = nodes.reduce((accumulator, node, index) => {
|
|
127
|
+
const previousReplayNodeId = accumulator[accumulator.length - 1]?.id;
|
|
128
|
+
accumulator.push(replayNodeFromRunNode(node, index, scope === 'run' ? previousReplayNodeId : undefined, traceSummary, limitations, requiresApproval));
|
|
129
|
+
return accumulator;
|
|
130
|
+
}, []);
|
|
131
|
+
const settings = readRecord(run.metadata?.settings) ?? {};
|
|
132
|
+
const replayMetadata = {
|
|
133
|
+
protocol: PIXCODE_REPLAY_PROTOCOL,
|
|
134
|
+
sourceRunId: run.id,
|
|
135
|
+
sourceWorkflowId: run.workflowId,
|
|
136
|
+
scope,
|
|
137
|
+
fromNodeId: options.fromNodeId,
|
|
138
|
+
selectedNodeIds: nodes.map((node) => node.nodeId),
|
|
139
|
+
requiresApproval,
|
|
140
|
+
destructiveOperations,
|
|
141
|
+
limitations,
|
|
142
|
+
createdAt: Date.now(),
|
|
143
|
+
};
|
|
144
|
+
return {
|
|
145
|
+
protocol: PIXCODE_REPLAY_PROTOCOL,
|
|
146
|
+
sourceRunId: run.id,
|
|
147
|
+
sourceWorkflowId: run.workflowId,
|
|
148
|
+
scope,
|
|
149
|
+
fromNodeId: options.fromNodeId,
|
|
150
|
+
selectedNodeIds: nodes.map((node) => node.nodeId),
|
|
151
|
+
requiresApproval,
|
|
152
|
+
approvalReasons: destructiveOperations.map((operation) => `${operation.kind}${operation.nodeId ? ` in ${operation.nodeId}` : ''}: ${operation.summary}`),
|
|
153
|
+
destructiveOperations,
|
|
154
|
+
limitations,
|
|
155
|
+
input: [
|
|
156
|
+
`Replay ${scope === 'run' ? 'full workflow run' : 'workflow step'} from source run ${run.id}.`,
|
|
157
|
+
run.input ? `Original request:\n${compact(run.input, run, 2_000)}` : undefined,
|
|
158
|
+
].filter(Boolean).join('\n\n'),
|
|
159
|
+
workflow: {
|
|
160
|
+
id: `${run.workflowId}_replay`,
|
|
161
|
+
name: `Replay ${run.workflowId}`,
|
|
162
|
+
description: 'Replay generated from stored Pixcode workflow trace data.',
|
|
163
|
+
trigger: 'manual',
|
|
164
|
+
nodes: replayNodes,
|
|
165
|
+
},
|
|
166
|
+
metadata: {
|
|
167
|
+
...run.metadata,
|
|
168
|
+
workflowName: `Replay: ${String(run.metadata?.workflowName ?? run.workflowId)}`,
|
|
169
|
+
replay: replayMetadata,
|
|
170
|
+
settings: {
|
|
171
|
+
...settings,
|
|
172
|
+
replayMode: true,
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
//# sourceMappingURL=workflow-replay.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workflow-replay.js","sourceRoot":"","sources":["../../../../../server/modules/orchestration/workflows/workflow-replay.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import crypto from 'node:crypto';
|
|
2
2
|
import { PIXCODE_HANDOFF_PROTOCOL, formatHandoffArtifactForContext, handoffArtifactToWorkflowArtifact, parseHandoffArtifact, } from '../../../modules/orchestration/workflows/handoff-artifact.js';
|
|
3
|
+
import { buildWorkflowContextPacket, formatContextPacketForPrompt, } from '../../../modules/orchestration/workflows/context-packet.js';
|
|
4
|
+
import { classifyWorkflowFailure, resolveWorkflowFallbackDecision, } from '../../../modules/orchestration/workflows/workflow-fallback-policy.js';
|
|
3
5
|
import { resolveWorkflowWorkspace, workspaceContextPrompt, workspaceTargetMetadata, } from '../../../modules/orchestration/workflows/workspace-target.js';
|
|
4
6
|
import { workflowStore } from '../../../modules/orchestration/workflows/workflow-store.js';
|
|
5
7
|
// @ts-ignore — plain-JS service
|
|
@@ -924,6 +926,8 @@ function nodeRunFromNode(node) {
|
|
|
924
926
|
timeoutMs: node.timeoutMs,
|
|
925
927
|
stage: node.stage,
|
|
926
928
|
internal: node.internal,
|
|
929
|
+
fallbackTrigger: node.fallbackTrigger,
|
|
930
|
+
fallbackSourceNodeId: node.fallbackSourceNodeId,
|
|
927
931
|
status: 'queued',
|
|
928
932
|
};
|
|
929
933
|
}
|
|
@@ -1059,7 +1063,7 @@ class WorkflowRunner {
|
|
|
1059
1063
|
}
|
|
1060
1064
|
return readAgentAssignments(run.metadata).find((agent) => agent.instanceId === fallbackAgentInstanceId);
|
|
1061
1065
|
}
|
|
1062
|
-
createFallbackNode(node, fallbackAgent, reason) {
|
|
1066
|
+
createFallbackNode(node, fallbackAgent, reason, fallbackTrigger) {
|
|
1063
1067
|
const fallbackSuffix = safeNodeId(fallbackAgent.instanceId, 'fallback');
|
|
1064
1068
|
return {
|
|
1065
1069
|
...node,
|
|
@@ -1072,9 +1076,12 @@ class WorkflowRunner {
|
|
|
1072
1076
|
model: fallbackAgent.model,
|
|
1073
1077
|
permissionMode: fallbackAgent.permissionMode,
|
|
1074
1078
|
toolsSettings: fallbackAgent.toolsSettings,
|
|
1079
|
+
fallbackTrigger,
|
|
1080
|
+
fallbackSourceNodeId: node.id,
|
|
1075
1081
|
prompt: [
|
|
1076
1082
|
'The previous CLI agent failed on this orchestration step.',
|
|
1077
1083
|
`Failed step: ${node.agentLabel || node.id}`,
|
|
1084
|
+
`Fallback trigger: ${fallbackTrigger}`,
|
|
1078
1085
|
`Failure: ${reason}`,
|
|
1079
1086
|
'Take over the same assignment as the backup CLI. Use the original goal and upstream context.',
|
|
1080
1087
|
'Do not repeat unrelated work; complete the failed step and report what you did.',
|
|
@@ -1083,9 +1090,41 @@ class WorkflowRunner {
|
|
|
1083
1090
|
onFail: 'continue',
|
|
1084
1091
|
};
|
|
1085
1092
|
}
|
|
1086
|
-
|
|
1093
|
+
recordFallbackSkipped(run, node, reason, fallbackTrigger, skippedReason) {
|
|
1094
|
+
const fallbackSkippedEvents = Array.isArray(run.metadata?.fallbackSkippedEvents)
|
|
1095
|
+
? run.metadata.fallbackSkippedEvents
|
|
1096
|
+
: [];
|
|
1097
|
+
run.metadata = {
|
|
1098
|
+
...run.metadata,
|
|
1099
|
+
fallbackSkippedEvents: [
|
|
1100
|
+
...fallbackSkippedEvents,
|
|
1101
|
+
{
|
|
1102
|
+
nodeId: node.id,
|
|
1103
|
+
trigger: fallbackTrigger,
|
|
1104
|
+
reason,
|
|
1105
|
+
skippedReason,
|
|
1106
|
+
createdAt: Date.now(),
|
|
1107
|
+
},
|
|
1108
|
+
],
|
|
1109
|
+
};
|
|
1110
|
+
workflowStore.setRun(run);
|
|
1111
|
+
}
|
|
1112
|
+
async runFallbackAfterFailure(node, workflow, run, outputs, started, completed, reason, trigger) {
|
|
1113
|
+
const fallbackTrigger = classifyWorkflowFailure(reason, trigger);
|
|
1087
1114
|
const fallbackAgent = this.fallbackAgentFor(run, node);
|
|
1088
1115
|
if (!fallbackAgent) {
|
|
1116
|
+
this.recordFallbackSkipped(run, node, reason, fallbackTrigger, 'No fallback agent is configured for this run.');
|
|
1117
|
+
return false;
|
|
1118
|
+
}
|
|
1119
|
+
const decision = resolveWorkflowFallbackDecision({
|
|
1120
|
+
run,
|
|
1121
|
+
node,
|
|
1122
|
+
reason,
|
|
1123
|
+
trigger: fallbackTrigger,
|
|
1124
|
+
fallbackAgentInstanceId: fallbackAgent.instanceId,
|
|
1125
|
+
});
|
|
1126
|
+
if (!decision.shouldFallback) {
|
|
1127
|
+
this.recordFallbackSkipped(run, node, reason, decision.trigger, decision.skippedReason ?? 'Fallback policy skipped this failure.');
|
|
1089
1128
|
return false;
|
|
1090
1129
|
}
|
|
1091
1130
|
if (workflow.nodes.length + 1 > 64) {
|
|
@@ -1096,7 +1135,7 @@ class WorkflowRunner {
|
|
|
1096
1135
|
workflowStore.setRun(run);
|
|
1097
1136
|
return false;
|
|
1098
1137
|
}
|
|
1099
|
-
let fallbackNode = this.createFallbackNode(node, fallbackAgent, reason);
|
|
1138
|
+
let fallbackNode = this.createFallbackNode(node, fallbackAgent, reason, decision.trigger);
|
|
1100
1139
|
let collision = 1;
|
|
1101
1140
|
while (workflow.nodes.some((candidate) => candidate.id === fallbackNode.id)) {
|
|
1102
1141
|
collision += 1;
|
|
@@ -1130,6 +1169,8 @@ class WorkflowRunner {
|
|
|
1130
1169
|
nodeId: node.id,
|
|
1131
1170
|
fallbackNodeId: fallbackNode.id,
|
|
1132
1171
|
fallbackAgentInstanceId: fallbackAgent.instanceId,
|
|
1172
|
+
trigger: decision.trigger,
|
|
1173
|
+
policy: decision.policy,
|
|
1133
1174
|
reason,
|
|
1134
1175
|
startedAt: Date.now(),
|
|
1135
1176
|
},
|
|
@@ -1317,9 +1358,19 @@ class WorkflowRunner {
|
|
|
1317
1358
|
workflowStore.setRun(run);
|
|
1318
1359
|
const inputContext = node.inputs.map((input) => outputs.get(input)).filter(Boolean).join('\n\n');
|
|
1319
1360
|
const workspaceTarget = resolveWorkflowWorkspace(run.metadata);
|
|
1361
|
+
const contextPacket = buildWorkflowContextPacket({
|
|
1362
|
+
run,
|
|
1363
|
+
node,
|
|
1364
|
+
workspaceTarget,
|
|
1365
|
+
inputContext,
|
|
1366
|
+
inputNodeIds: node.inputs,
|
|
1367
|
+
});
|
|
1368
|
+
nodeRun.contextPacket = contextPacket;
|
|
1369
|
+
workflowStore.setRun(run);
|
|
1320
1370
|
const prompt = [
|
|
1321
1371
|
'Original user request (primary task; answer this directly even if the workspace is empty):',
|
|
1322
1372
|
run.input?.trim() || '(No original user request was provided.)',
|
|
1373
|
+
formatContextPacketForPrompt(contextPacket),
|
|
1323
1374
|
inputContext
|
|
1324
1375
|
? `Upstream workflow context from prior agents:\n${inputContext}`
|
|
1325
1376
|
: '',
|
|
@@ -1401,7 +1452,7 @@ class WorkflowRunner {
|
|
|
1401
1452
|
workflowStore.setRun(run);
|
|
1402
1453
|
return;
|
|
1403
1454
|
}
|
|
1404
|
-
if (await this.runFallbackAfterFailure(node, workflow, run, outputs, started, completed, nodeRun.error)) {
|
|
1455
|
+
if (await this.runFallbackAfterFailure(node, workflow, run, outputs, started, completed, nodeRun.error, 'provider_failure')) {
|
|
1405
1456
|
return;
|
|
1406
1457
|
}
|
|
1407
1458
|
if (node.onFail === 'continue') {
|
|
@@ -1446,7 +1497,7 @@ class WorkflowRunner {
|
|
|
1446
1497
|
workflowStore.setRun(run);
|
|
1447
1498
|
return;
|
|
1448
1499
|
}
|
|
1449
|
-
if (await this.runFallbackAfterFailure(node, workflow, run, outputs, started, completed, nodeRun.error)) {
|
|
1500
|
+
if (await this.runFallbackAfterFailure(node, workflow, run, outputs, started, completed, nodeRun.error, 'timeout')) {
|
|
1450
1501
|
return;
|
|
1451
1502
|
}
|
|
1452
1503
|
if (node.onFail === 'continue') {
|
|
@@ -1480,7 +1531,7 @@ class WorkflowRunner {
|
|
|
1480
1531
|
nodeRun.status = 'failed';
|
|
1481
1532
|
nodeRun.error = visibleHandoffError;
|
|
1482
1533
|
workflowStore.setRun(run);
|
|
1483
|
-
if (await this.runFallbackAfterFailure(node, workflow, run, outputs, started, completed, visibleHandoffError)) {
|
|
1534
|
+
if (await this.runFallbackAfterFailure(node, workflow, run, outputs, started, completed, visibleHandoffError, 'invalid_output')) {
|
|
1484
1535
|
return;
|
|
1485
1536
|
}
|
|
1486
1537
|
if (node.onFail === 'continue') {
|
|
@@ -1516,7 +1567,7 @@ class WorkflowRunner {
|
|
|
1516
1567
|
workflowStore.setRun(run);
|
|
1517
1568
|
return;
|
|
1518
1569
|
}
|
|
1519
|
-
if (await this.runFallbackAfterFailure(node, workflow, run, outputs, started, completed, nodeRun.error)) {
|
|
1570
|
+
if (await this.runFallbackAfterFailure(node, workflow, run, outputs, started, completed, nodeRun.error, classifyWorkflowFailure(`${nodeRun.error}\n${nodeRun.outputText ?? ''}`))) {
|
|
1520
1571
|
return;
|
|
1521
1572
|
}
|
|
1522
1573
|
if (node.onFail === 'continue') {
|