@pixelbyte-software/pixcode 1.51.1 → 1.51.3
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/CODE_OF_CONDUCT.md +41 -41
- package/CONTRIBUTING.md +155 -155
- package/LICENSE +718 -718
- package/README.de.md +169 -169
- package/README.ja.md +167 -167
- package/README.ko.md +167 -167
- package/README.md +419 -419
- package/README.ru.md +169 -169
- package/README.tr.md +298 -298
- package/README.zh-CN.md +167 -167
- package/SECURITY.md +46 -46
- package/dist/api-automation.html +110 -110
- package/dist/api-docs.html +548 -548
- package/dist/assets/{index-DARIZgoD.js → index-17CwxHSZ.js} +185 -185
- package/dist/assets/index-B9N-gfOQ.css +32 -0
- package/dist/clear-cache.html +85 -85
- package/dist/convert-icons.md +52 -52
- package/dist/docs.html +308 -308
- package/dist/favicon.svg +8 -8
- package/dist/features.html +133 -133
- package/dist/generate-icons.js +48 -48
- package/dist/humans.txt +15 -15
- package/dist/icons/codex-white.svg +3 -3
- package/dist/icons/codex.svg +3 -3
- package/dist/icons/cursor-white.svg +11 -11
- package/dist/icons/icon-128x128.svg +9 -9
- package/dist/icons/icon-144x144.svg +9 -9
- package/dist/icons/icon-152x152.svg +9 -9
- package/dist/icons/icon-192x192.svg +9 -9
- package/dist/icons/icon-384x384.svg +9 -9
- package/dist/icons/icon-512x512.svg +9 -9
- package/dist/icons/icon-72x72.svg +9 -9
- package/dist/icons/icon-96x96.svg +9 -9
- package/dist/icons/icon-template.svg +9 -9
- package/dist/icons/qwen-logo.svg +14 -14
- package/dist/index.html +59 -59
- package/dist/landing.html +268 -268
- package/dist/llms-full.txt +119 -119
- package/dist/llms.txt +53 -53
- package/dist/logo.svg +12 -12
- package/dist/manifest.json +60 -60
- package/dist/openapi.yaml +1696 -1696
- package/dist/orchestration.html +125 -125
- package/dist/robots.txt +4 -4
- package/dist/site.css +692 -692
- package/dist/sitemap.xml +51 -51
- package/dist/sw.js +132 -132
- package/dist-server/server/cli.js +96 -96
- package/dist-server/server/daemon/manager.js +33 -33
- package/dist-server/server/daemon-manager.js +64 -64
- package/dist-server/server/index.js +125 -4
- package/dist-server/server/index.js.map +1 -1
- package/dist-server/server/modules/orchestration/a2a/adapters/json-event.adapter.js +84 -0
- package/dist-server/server/modules/orchestration/a2a/adapters/json-event.adapter.js.map +1 -0
- package/dist-server/server/modules/orchestration/a2a/adapters/json-event.adapter.test.js +43 -0
- package/dist-server/server/modules/orchestration/a2a/adapters/json-event.adapter.test.js.map +1 -0
- package/dist-server/server/modules/orchestration/hermes/hermes.routes.js +55 -1
- package/dist-server/server/modules/orchestration/hermes/hermes.routes.js.map +1 -1
- package/dist-server/server/modules/orchestration/index.js +1 -0
- package/dist-server/server/modules/orchestration/index.js.map +1 -1
- package/dist-server/server/routes/commands.js +25 -25
- package/dist-server/server/routes/git.js +17 -17
- package/dist-server/server/routes/live-view.js +46 -46
- package/dist-server/server/services/hermes-gateway.js +310 -0
- package/dist-server/server/services/hermes-gateway.js.map +1 -1
- package/dist-server/server/services/public-api-manifest.js +59 -51
- package/dist-server/server/services/public-api-manifest.js.map +1 -1
- package/package.json +222 -222
- package/scripts/fix-node-pty.js +67 -67
- package/scripts/github/create-v1.38-issues.mjs +351 -351
- package/scripts/github/create-vscode-workbench-issues.mjs +121 -121
- package/scripts/hermes/configure-pixcode-mcp.mjs +165 -163
- package/scripts/hermes/pixcode-mcp-server.mjs +1009 -958
- package/scripts/smoke/changes-panel-layout.mjs +48 -48
- package/scripts/smoke/chat-composer-fixed-layout.mjs +55 -55
- package/scripts/smoke/chat-message-timeline-order.mjs +41 -41
- package/scripts/smoke/chat-realtime-hydration.mjs +44 -44
- package/scripts/smoke/chat-session-provider-pools.mjs +35 -35
- package/scripts/smoke/chat-session-state.mjs +19 -19
- package/scripts/smoke/code-editor-theme.mjs +55 -55
- package/scripts/smoke/code-editor-vscode-engine.mjs +91 -91
- package/scripts/smoke/command-center-agent-writes.mjs +79 -79
- package/scripts/smoke/command-center-non-git.mjs +46 -46
- package/scripts/smoke/context-packet.mjs +43 -43
- package/scripts/smoke/control-room-ux-redesign.mjs +91 -91
- package/scripts/smoke/daemon-entrypoint.mjs +20 -20
- package/scripts/smoke/default-landing-routing.mjs +33 -33
- package/scripts/smoke/desktop-native-notifications.mjs +30 -30
- package/scripts/smoke/desktop-tray-icon.mjs +33 -33
- package/scripts/smoke/discord-release-workflow.mjs +24 -24
- package/scripts/smoke/git-install-update.mjs +255 -255
- package/scripts/smoke/handoff-artifact-protocol.mjs +50 -50
- package/scripts/smoke/hermes-api-install.mjs +56 -56
- package/scripts/smoke/hermes-gateway-persistence.mjs +104 -104
- package/scripts/smoke/hermes-mcp-pixcode-roundtrip.mjs +426 -367
- package/scripts/smoke/hermes-rest-chat-api.mjs +162 -162
- package/scripts/smoke/hermes-rest-chat-live.mjs +45 -45
- package/scripts/smoke/hermes-rest-codex-launch.mjs +209 -209
- package/scripts/smoke/hermes-rest-gateway.mjs +79 -70
- package/scripts/smoke/hermes-rest-live.mjs +42 -42
- package/scripts/smoke/hermes-roundtrip.mjs +167 -167
- package/scripts/smoke/hermes-settings-commands.mjs +349 -346
- package/scripts/smoke/hermes-smoke-launcher-guard.mjs +34 -34
- package/scripts/smoke/live-view-diagnostics.mjs +53 -53
- package/scripts/smoke/live-view-environment.mjs +92 -92
- package/scripts/smoke/live-view-integration.mjs +450 -450
- package/scripts/smoke/mac-desktop-runtime.mjs +37 -37
- package/scripts/smoke/mobile-tunnel-guidance.mjs +29 -29
- package/scripts/smoke/model-registry.mjs +36 -36
- package/scripts/smoke/multi-project-ui.mjs +45 -45
- package/scripts/smoke/multi-worker-slots.mjs +42 -42
- package/scripts/smoke/notification-center.mjs +87 -87
- package/scripts/smoke/notification-inapp-preference.mjs +23 -23
- package/scripts/smoke/notification-taxonomy.mjs +58 -58
- package/scripts/smoke/orchestration-api.mjs +172 -172
- package/scripts/smoke/orchestration-execution-dashboard.mjs +33 -33
- package/scripts/smoke/orchestration-live-run.mjs +176 -176
- package/scripts/smoke/orchestration-mobile-scroll.mjs +29 -29
- package/scripts/smoke/orchestration-model-sync.mjs +30 -30
- package/scripts/smoke/orchestration-permission-fallback.mjs +34 -34
- package/scripts/smoke/orchestration-runtime-guards.mjs +48 -48
- package/scripts/smoke/orchestration-user-facing-output.mjs +25 -25
- package/scripts/smoke/permission-policy.mjs +50 -50
- package/scripts/smoke/pixcode-workbench-1-48.mjs +167 -164
- package/scripts/smoke/provider-models-opencode-live.mjs +66 -66
- package/scripts/smoke/provider-rest-api.mjs +124 -124
- package/scripts/smoke/provider-selection-status.mjs +52 -52
- package/scripts/smoke/run-state-refresh.mjs +52 -52
- package/scripts/smoke/runtime-manager.mjs +99 -99
- package/scripts/smoke/shell-manual-disconnect.mjs +30 -30
- package/scripts/smoke/side-panel-editor-layout.mjs +34 -34
- package/scripts/smoke/static-root-routing.mjs +21 -21
- package/scripts/smoke/strict-handoff-compact.mjs +60 -60
- package/scripts/smoke/taskmaster-config.mjs +24 -24
- package/scripts/smoke/taskmaster-execution-telegram.mjs +3 -3
- package/scripts/smoke/taskmaster-onboarding.mjs +3 -3
- package/scripts/smoke/taskmaster-run-graph.mjs +3 -3
- package/scripts/smoke/telegram-control.mjs +242 -242
- package/scripts/smoke/tunnel-persistence.mjs +56 -56
- package/scripts/smoke/update-issue-progress.mjs +69 -69
- package/scripts/smoke/update-ux.mjs +55 -55
- package/scripts/smoke/v138-completion.mjs +132 -132
- package/scripts/smoke/v138-desktop-release-hardening.mjs +69 -69
- package/scripts/smoke/v138-diagnostics.mjs +63 -63
- package/scripts/smoke/v138-issue-planner.mjs +33 -33
- package/scripts/smoke/v143-remote-control.mjs +76 -76
- package/scripts/smoke/v144-production-loop.mjs +47 -47
- package/scripts/smoke/v145-platformization.mjs +46 -46
- package/scripts/smoke/v146-control-room-ui.mjs +150 -150
- package/scripts/smoke/version-modal-autoshow.mjs +29 -29
- package/scripts/smoke/vscode-workbench-layout.mjs +63 -63
- package/scripts/smoke/vscode-workbench-polish.mjs +461 -436
- package/scripts/smoke/workflow-fallback-replay.mjs +56 -56
- package/scripts/smoke/workflow-templates.mjs +43 -43
- package/scripts/smoke/workflow-trace-timeline.mjs +46 -46
- package/scripts/update-git-install.mjs +293 -293
- package/server/claude-sdk.js +920 -920
- package/server/cli.js +1039 -1039
- package/server/constants/config.js +4 -4
- package/server/cursor-cli.js +344 -344
- package/server/daemon/manager.js +563 -563
- package/server/daemon-manager.js +964 -964
- package/server/database/db.js +921 -921
- package/server/database/json-store.js +197 -197
- package/server/gemini-cli.js +550 -550
- package/server/gemini-response-handler.js +79 -79
- package/server/index.js +131 -3
- package/server/load-env.js +35 -35
- package/server/middleware/auth.js +175 -175
- package/server/modules/orchestration/a2a/adapter-registry.ts +108 -108
- package/server/modules/orchestration/a2a/adapters/abstract-a2a.adapter.ts +63 -63
- package/server/modules/orchestration/a2a/adapters/claude-code.adapter.ts +286 -286
- package/server/modules/orchestration/a2a/adapters/codex.adapter.ts +244 -244
- package/server/modules/orchestration/a2a/adapters/cursor.adapter.ts +249 -249
- package/server/modules/orchestration/a2a/adapters/gemini.adapter.ts +248 -248
- package/server/modules/orchestration/a2a/adapters/json-event.adapter.test.ts +60 -0
- package/server/modules/orchestration/a2a/adapters/json-event.adapter.ts +101 -0
- package/server/modules/orchestration/a2a/adapters/opencode.adapter.ts +248 -248
- package/server/modules/orchestration/a2a/adapters/qwen.adapter.ts +248 -248
- package/server/modules/orchestration/a2a/agent-card.ts +55 -55
- package/server/modules/orchestration/a2a/routes.ts +590 -590
- package/server/modules/orchestration/a2a/task-store.ts +178 -178
- package/server/modules/orchestration/a2a/types.ts +126 -126
- package/server/modules/orchestration/a2a/validator.ts +113 -113
- package/server/modules/orchestration/hermes/hermes.routes.ts +642 -583
- package/server/modules/orchestration/index.ts +101 -100
- package/server/modules/orchestration/preview/port-watcher.ts +112 -112
- package/server/modules/orchestration/preview/preview-proxy.ts +60 -60
- package/server/modules/orchestration/preview/types.ts +19 -19
- package/server/modules/orchestration/security/permission-policy.ts +401 -401
- package/server/modules/orchestration/tasks/orchestration-task-store.ts +41 -41
- package/server/modules/orchestration/tasks/orchestration-task.routes.ts +64 -64
- package/server/modules/orchestration/tasks/orchestration-task.service.ts +209 -209
- package/server/modules/orchestration/tasks/orchestration-task.types.ts +40 -40
- package/server/modules/orchestration/tasks/task-run-graph.ts +155 -155
- package/server/modules/orchestration/workflows/approval-queue.ts +106 -106
- package/server/modules/orchestration/workflows/built-in-workflows.ts +127 -127
- package/server/modules/orchestration/workflows/context-packet.ts +186 -186
- package/server/modules/orchestration/workflows/handoff-artifact.ts +175 -175
- package/server/modules/orchestration/workflows/workflow-fallback-policy.ts +161 -161
- package/server/modules/orchestration/workflows/workflow-replay.ts +254 -254
- package/server/modules/orchestration/workflows/workflow-runner.ts +2070 -2070
- package/server/modules/orchestration/workflows/workflow-store.ts +97 -97
- package/server/modules/orchestration/workflows/workflow-templates.ts +272 -272
- package/server/modules/orchestration/workflows/workflow-trace.ts +424 -424
- package/server/modules/orchestration/workflows/workflow.routes.ts +586 -586
- package/server/modules/orchestration/workflows/workflow.types.ts +111 -111
- package/server/modules/orchestration/workflows/workspace-target.ts +122 -122
- package/server/modules/orchestration/workspace/docker-workspace.ts +136 -136
- package/server/modules/orchestration/workspace/path-safety.ts +55 -55
- package/server/modules/orchestration/workspace/types.ts +52 -52
- package/server/modules/orchestration/workspace/workspace-manager.ts +102 -102
- package/server/modules/orchestration/workspace/worktree-workspace.ts +126 -126
- package/server/modules/providers/index.ts +2 -2
- package/server/modules/providers/list/claude/claude-auth.provider.ts +146 -146
- package/server/modules/providers/list/claude/claude-mcp.provider.ts +135 -135
- package/server/modules/providers/list/claude/claude-sessions.provider.ts +306 -306
- package/server/modules/providers/list/claude/claude.provider.ts +15 -15
- package/server/modules/providers/list/codex/codex-auth.provider.ts +117 -117
- package/server/modules/providers/list/codex/codex-mcp.provider.ts +135 -135
- package/server/modules/providers/list/codex/codex-sessions.provider.ts +319 -319
- package/server/modules/providers/list/codex/codex.provider.ts +15 -15
- package/server/modules/providers/list/cursor/cursor-auth.provider.ts +147 -147
- package/server/modules/providers/list/cursor/cursor-mcp.provider.ts +108 -108
- package/server/modules/providers/list/cursor/cursor-sessions.provider.ts +421 -421
- package/server/modules/providers/list/cursor/cursor.provider.ts +15 -15
- package/server/modules/providers/list/gemini/gemini-auth.provider.ts +173 -173
- package/server/modules/providers/list/gemini/gemini-mcp.provider.ts +110 -110
- package/server/modules/providers/list/gemini/gemini-sessions.provider.ts +227 -227
- package/server/modules/providers/list/gemini/gemini.provider.ts +15 -15
- package/server/modules/providers/list/opencode/opencode-auth.provider.ts +131 -131
- package/server/modules/providers/list/opencode/opencode-mcp.provider.ts +126 -126
- package/server/modules/providers/list/opencode/opencode-sessions.provider.ts +286 -286
- package/server/modules/providers/list/opencode/opencode.provider.ts +29 -29
- package/server/modules/providers/list/qwen/qwen-auth.provider.ts +146 -146
- package/server/modules/providers/list/qwen/qwen-mcp.provider.ts +114 -114
- package/server/modules/providers/list/qwen/qwen-sessions.provider.ts +265 -265
- package/server/modules/providers/list/qwen/qwen.provider.ts +21 -21
- package/server/modules/providers/provider.registry.ts +40 -40
- package/server/modules/providers/provider.routes.ts +944 -944
- package/server/modules/providers/services/mcp.service.ts +86 -86
- package/server/modules/providers/services/provider-auth.service.ts +26 -26
- package/server/modules/providers/services/sessions.service.ts +45 -45
- package/server/modules/providers/shared/base/abstract.provider.ts +20 -20
- package/server/modules/providers/shared/mcp/mcp.provider.ts +151 -151
- package/server/modules/providers/shared/provider-configs.ts +142 -142
- package/server/modules/providers/tests/mcp.test.ts +293 -293
- package/server/openai-codex.js +462 -462
- package/server/opencode-cli.js +491 -491
- package/server/opencode-response-handler.js +111 -111
- package/server/projects.js +3008 -3008
- package/server/qwen-code-cli.js +410 -410
- package/server/qwen-response-handler.js +73 -73
- package/server/routes/agent.js +1435 -1435
- package/server/routes/auth.js +159 -159
- package/server/routes/codex.js +20 -20
- package/server/routes/commands.js +570 -570
- package/server/routes/cursor.js +61 -61
- package/server/routes/diagnostics.js +41 -41
- package/server/routes/gemini.js +25 -25
- package/server/routes/git.js +1650 -1650
- package/server/routes/live-view.js +411 -411
- package/server/routes/mcp-utils.js +13 -13
- package/server/routes/messages.js +62 -62
- package/server/routes/network.js +125 -125
- package/server/routes/platformization.js +212 -212
- package/server/routes/plugins.js +320 -320
- package/server/routes/production-agent-loop.js +90 -90
- package/server/routes/projects.js +917 -917
- package/server/routes/public-api.js +34 -34
- package/server/routes/qwen.js +27 -27
- package/server/routes/remote.js +55 -55
- package/server/routes/settings.js +321 -321
- package/server/routes/telegram.js +140 -140
- package/server/routes/user.js +125 -125
- package/server/routes/webhooks.js +63 -63
- package/server/services/control-room.js +102 -102
- package/server/services/diagnostics.js +165 -165
- package/server/services/external-access.js +375 -375
- package/server/services/hermes-gateway.js +1562 -1247
- package/server/services/hermes-install-jobs.js +729 -729
- package/server/services/install-jobs.js +715 -715
- package/server/services/live-view.js +956 -956
- package/server/services/managed-runtimes.js +493 -493
- package/server/services/model-registry.js +144 -144
- package/server/services/notification-orchestrator.js +365 -365
- package/server/services/notification-taxonomy.js +204 -204
- package/server/services/platformization.js +815 -815
- package/server/services/production-agent-loop.js +248 -248
- package/server/services/provider-cli-versions.js +149 -149
- package/server/services/provider-credentials.js +189 -189
- package/server/services/provider-models.js +396 -396
- package/server/services/public-api-manifest.js +190 -182
- package/server/services/remote-connection.js +127 -127
- package/server/services/runtime-manager.js +323 -323
- package/server/services/startup-update.js +234 -234
- package/server/services/telegram/bot.js +331 -331
- package/server/services/telegram/control-center.js +979 -979
- package/server/services/telegram/telegram-http-client.js +151 -151
- package/server/services/telegram/translations.js +340 -340
- package/server/services/vapid-keys.js +36 -36
- package/server/services/webhooks.js +216 -216
- package/server/sessionManager.js +225 -225
- package/server/shared/interfaces.ts +54 -54
- package/server/shared/types.ts +172 -172
- package/server/shared/utils.ts +193 -193
- package/server/tsconfig.json +36 -36
- package/server/utils/colors.js +21 -21
- package/server/utils/commandParser.js +305 -305
- package/server/utils/frontmatter.js +18 -18
- package/server/utils/gitConfig.js +34 -34
- package/server/utils/plugin-loader.js +457 -457
- package/server/utils/plugin-process-manager.js +185 -185
- package/server/utils/port-access.js +209 -209
- package/server/utils/runtime-paths.js +37 -37
- package/server/utils/url-detection.js +71 -71
- package/server/vite-daemon.js +79 -79
- package/shared/modelConstants.js +161 -161
- package/shared/networkHosts.js +22 -22
- package/dist/assets/index-DMz0zv6T.css +0 -32
|
@@ -1,254 +1,254 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
Workflow,
|
|
3
|
-
WorkflowNode,
|
|
4
|
-
WorkflowNodeRun,
|
|
5
|
-
WorkflowRun,
|
|
6
|
-
} from '@/modules/orchestration/workflows/workflow.types.js';
|
|
7
|
-
import { redactTraceText } from '@/modules/orchestration/workflows/workflow-trace.js';
|
|
8
|
-
|
|
9
|
-
export const PIXCODE_REPLAY_PROTOCOL = 'pixcode.workflow-replay.v1';
|
|
10
|
-
|
|
11
|
-
export type WorkflowReplayScope = 'run' | 'node';
|
|
12
|
-
export type WorkflowReplaySafetyKind = 'file-write' | 'shell' | 'network';
|
|
13
|
-
|
|
14
|
-
export interface WorkflowReplayOperation {
|
|
15
|
-
kind: WorkflowReplaySafetyKind;
|
|
16
|
-
nodeId?: string;
|
|
17
|
-
summary: string;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export interface WorkflowReplayPlan {
|
|
21
|
-
protocol: typeof PIXCODE_REPLAY_PROTOCOL;
|
|
22
|
-
sourceRunId: string;
|
|
23
|
-
sourceWorkflowId: string;
|
|
24
|
-
scope: WorkflowReplayScope;
|
|
25
|
-
fromNodeId?: string;
|
|
26
|
-
selectedNodeIds: string[];
|
|
27
|
-
requiresApproval: boolean;
|
|
28
|
-
approvalReasons: string[];
|
|
29
|
-
destructiveOperations: WorkflowReplayOperation[];
|
|
30
|
-
limitations: string[];
|
|
31
|
-
input: string;
|
|
32
|
-
workflow: Workflow;
|
|
33
|
-
metadata: Record<string, unknown>;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function safeNodeId(value: string): string {
|
|
37
|
-
return value.replace(/[^a-zA-Z0-9_]+/g, '_').slice(0, 48) || 'node';
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function readRecord(value: unknown): Record<string, unknown> | undefined {
|
|
41
|
-
return value && typeof value === 'object' ? value as Record<string, unknown> : undefined;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function visibleNodes(run: WorkflowRun): WorkflowNodeRun[] {
|
|
45
|
-
return run.nodeRuns.filter((node) => !node.internal);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function defaultReplayNode(run: WorkflowRun): WorkflowNodeRun | undefined {
|
|
49
|
-
return visibleNodes(run).find((node) => node.status === 'failed')
|
|
50
|
-
?? [...visibleNodes(run)].reverse().find((node) => node.status !== 'skipped')
|
|
51
|
-
?? visibleNodes(run)[0];
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
function selectReplayNodes(run: WorkflowRun, scope: WorkflowReplayScope, fromNodeId?: string): WorkflowNodeRun[] {
|
|
55
|
-
const nodes = visibleNodes(run);
|
|
56
|
-
if (scope === 'run') return nodes;
|
|
57
|
-
|
|
58
|
-
const requested = fromNodeId
|
|
59
|
-
? nodes.find((node) => node.nodeId === fromNodeId)
|
|
60
|
-
: defaultReplayNode(run);
|
|
61
|
-
return requested ? [requested] : [];
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
function compact(value: string | undefined, run: WorkflowRun, maxLength = 1_200): string | undefined {
|
|
65
|
-
return redactTraceText(value, run, maxLength);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
function nodeTraceSummary(run: WorkflowRun, node: WorkflowNodeRun): string {
|
|
69
|
-
const artifactTypes = (node.artifacts ?? []).map((artifact) => artifact.type).filter(Boolean);
|
|
70
|
-
return [
|
|
71
|
-
`Step: ${node.agentLabel || node.nodeId}`,
|
|
72
|
-
`Node id: ${node.nodeId}`,
|
|
73
|
-
`Status: ${node.status}`,
|
|
74
|
-
node.stage ? `Stage: ${node.stage}` : undefined,
|
|
75
|
-
node.adapterId ? `Adapter: ${node.adapterId}` : undefined,
|
|
76
|
-
node.model ? `Model: ${node.model}` : undefined,
|
|
77
|
-
node.error ? `Error: ${compact(node.error, run, 800)}` : undefined,
|
|
78
|
-
artifactTypes.length > 0 ? `Artifacts: ${artifactTypes.join(', ')}` : undefined,
|
|
79
|
-
node.outputText ? `Output excerpt:\n${compact(node.outputText, run)}` : undefined,
|
|
80
|
-
].filter(Boolean).join('\n');
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
function replayTraceSummary(run: WorkflowRun, nodes: WorkflowNodeRun[]): string {
|
|
84
|
-
return nodes.map((node) => nodeTraceSummary(run, node)).join('\n\n---\n\n');
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
function pushOperation(
|
|
88
|
-
operations: WorkflowReplayOperation[],
|
|
89
|
-
kind: WorkflowReplaySafetyKind,
|
|
90
|
-
nodeId: string | undefined,
|
|
91
|
-
summary: string,
|
|
92
|
-
): void {
|
|
93
|
-
if (operations.some((operation) =>
|
|
94
|
-
operation.kind === kind && operation.nodeId === nodeId && operation.summary === summary,
|
|
95
|
-
)) {
|
|
96
|
-
return;
|
|
97
|
-
}
|
|
98
|
-
operations.push({ kind, nodeId, summary });
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
function detectReplayOperations(run: WorkflowRun, nodes: WorkflowNodeRun[]): WorkflowReplayOperation[] {
|
|
102
|
-
const operations: WorkflowReplayOperation[] = [];
|
|
103
|
-
|
|
104
|
-
for (const node of nodes) {
|
|
105
|
-
for (const artifact of node.artifacts ?? []) {
|
|
106
|
-
if (artifact.type === 'file-diff') {
|
|
107
|
-
pushOperation(operations, 'file-write', node.nodeId, 'Prior step produced a file diff artifact.');
|
|
108
|
-
}
|
|
109
|
-
if (artifact.type === 'command-output') {
|
|
110
|
-
pushOperation(operations, 'shell', node.nodeId, 'Prior step produced command output.');
|
|
111
|
-
}
|
|
112
|
-
const text = [artifact.text, artifact.data ? JSON.stringify(artifact.data) : undefined]
|
|
113
|
-
.filter(Boolean)
|
|
114
|
-
.join('\n')
|
|
115
|
-
.toLocaleLowerCase('en');
|
|
116
|
-
if (/https?:\/\/|curl |wget |gh |npm publish|npm install|git push|ssh /u.test(text)) {
|
|
117
|
-
pushOperation(operations, 'network', node.nodeId, 'Prior artifact references a network-capable operation.');
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
const text = [node.outputText, node.error, node.promptPreview].filter(Boolean).join('\n').toLocaleLowerCase('en');
|
|
122
|
-
if (/apply_patch|write file|file write|modified files|changed files/u.test(text)) {
|
|
123
|
-
pushOperation(operations, 'file-write', node.nodeId, 'Prior step text references file-write activity.');
|
|
124
|
-
}
|
|
125
|
-
if (/shell|command|terminal|npm run|node |python |php |go test|cargo |make |exit code/u.test(text)) {
|
|
126
|
-
pushOperation(operations, 'shell', node.nodeId, 'Prior step text references shell execution.');
|
|
127
|
-
}
|
|
128
|
-
if (/https?:\/\/|curl |wget |gh |npm publish|npm install|git push|ssh |network/u.test(text)) {
|
|
129
|
-
pushOperation(operations, 'network', node.nodeId, 'Prior step text references a network-capable operation.');
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
return operations;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
function replayNodeFromRunNode(
|
|
137
|
-
node: WorkflowNodeRun,
|
|
138
|
-
index: number,
|
|
139
|
-
previousReplayNodeId: string | undefined,
|
|
140
|
-
traceSummary: string,
|
|
141
|
-
limitations: string[],
|
|
142
|
-
requiresApproval: boolean,
|
|
143
|
-
): WorkflowNode {
|
|
144
|
-
const replayNodeId = `replay_${index + 1}_${safeNodeId(node.nodeId)}`;
|
|
145
|
-
return {
|
|
146
|
-
id: replayNodeId,
|
|
147
|
-
adapterId: node.adapterId || 'claude-code',
|
|
148
|
-
agentInstanceId: node.agentInstanceId,
|
|
149
|
-
agentLabel: node.agentLabel ? `${node.agentLabel} Replay` : 'Replay agent',
|
|
150
|
-
assignment: node.assignment ? `Replay: ${node.assignment}` : `Replay source node ${node.nodeId}`,
|
|
151
|
-
stage: node.stage ? `replay_${node.stage}` : 'replay',
|
|
152
|
-
model: node.model,
|
|
153
|
-
permissionMode: node.permissionMode === 'bypassPermissions' ? 'default' : node.permissionMode,
|
|
154
|
-
timeoutMs: node.timeoutMs,
|
|
155
|
-
inputs: previousReplayNodeId ? [previousReplayNodeId] : [],
|
|
156
|
-
output: 'both',
|
|
157
|
-
onFail: 'abort',
|
|
158
|
-
prompt: [
|
|
159
|
-
'This is a Pixcode workflow replay run.',
|
|
160
|
-
`Replay protocol: ${PIXCODE_REPLAY_PROTOCOL}`,
|
|
161
|
-
`Source node: ${node.nodeId}`,
|
|
162
|
-
requiresApproval
|
|
163
|
-
? '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.'
|
|
164
|
-
: 'Replay safety review did not find prior shell, network, or file-write artifacts, but still avoid destructive actions unless they are required and approved.',
|
|
165
|
-
'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.',
|
|
166
|
-
`Known limitations:\n- ${limitations.join('\n- ')}`,
|
|
167
|
-
`Trace summary:\n${traceSummary}`,
|
|
168
|
-
`Original step prompt:\n${node.promptPreview || '(No source prompt was stored.)'}`,
|
|
169
|
-
].join('\n\n'),
|
|
170
|
-
};
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
export function buildWorkflowReplayPlan(
|
|
174
|
-
run: WorkflowRun,
|
|
175
|
-
options: {
|
|
176
|
-
scope?: WorkflowReplayScope;
|
|
177
|
-
fromNodeId?: string;
|
|
178
|
-
} = {},
|
|
179
|
-
): WorkflowReplayPlan {
|
|
180
|
-
const scope = options.scope ?? 'node';
|
|
181
|
-
const nodes = selectReplayNodes(run, scope, options.fromNodeId);
|
|
182
|
-
if (nodes.length === 0) {
|
|
183
|
-
throw new Error('No replayable workflow steps were found.');
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
const limitations = [
|
|
187
|
-
'Replay uses stored run traces, prompt previews, messages, and artifacts; it cannot reproduce hidden provider state.',
|
|
188
|
-
'Replay reconstructs selected steps as a new workflow run instead of mutating the source run.',
|
|
189
|
-
'Shell, network, and file-write actions stay under the current CLI permission flow and require explicit replay approval when detected.',
|
|
190
|
-
];
|
|
191
|
-
const destructiveOperations = detectReplayOperations(run, nodes);
|
|
192
|
-
const requiresApproval = destructiveOperations.length > 0;
|
|
193
|
-
const traceSummary = replayTraceSummary(run, nodes);
|
|
194
|
-
const replayNodes = nodes.reduce<WorkflowNode[]>((accumulator, node, index) => {
|
|
195
|
-
const previousReplayNodeId = accumulator[accumulator.length - 1]?.id;
|
|
196
|
-
accumulator.push(replayNodeFromRunNode(
|
|
197
|
-
node,
|
|
198
|
-
index,
|
|
199
|
-
scope === 'run' ? previousReplayNodeId : undefined,
|
|
200
|
-
traceSummary,
|
|
201
|
-
limitations,
|
|
202
|
-
requiresApproval,
|
|
203
|
-
));
|
|
204
|
-
return accumulator;
|
|
205
|
-
}, []);
|
|
206
|
-
const settings = readRecord(run.metadata?.settings) ?? {};
|
|
207
|
-
const replayMetadata = {
|
|
208
|
-
protocol: PIXCODE_REPLAY_PROTOCOL,
|
|
209
|
-
sourceRunId: run.id,
|
|
210
|
-
sourceWorkflowId: run.workflowId,
|
|
211
|
-
scope,
|
|
212
|
-
fromNodeId: options.fromNodeId,
|
|
213
|
-
selectedNodeIds: nodes.map((node) => node.nodeId),
|
|
214
|
-
requiresApproval,
|
|
215
|
-
destructiveOperations,
|
|
216
|
-
limitations,
|
|
217
|
-
createdAt: Date.now(),
|
|
218
|
-
};
|
|
219
|
-
|
|
220
|
-
return {
|
|
221
|
-
protocol: PIXCODE_REPLAY_PROTOCOL,
|
|
222
|
-
sourceRunId: run.id,
|
|
223
|
-
sourceWorkflowId: run.workflowId,
|
|
224
|
-
scope,
|
|
225
|
-
fromNodeId: options.fromNodeId,
|
|
226
|
-
selectedNodeIds: nodes.map((node) => node.nodeId),
|
|
227
|
-
requiresApproval,
|
|
228
|
-
approvalReasons: destructiveOperations.map((operation) =>
|
|
229
|
-
`${operation.kind}${operation.nodeId ? ` in ${operation.nodeId}` : ''}: ${operation.summary}`,
|
|
230
|
-
),
|
|
231
|
-
destructiveOperations,
|
|
232
|
-
limitations,
|
|
233
|
-
input: [
|
|
234
|
-
`Replay ${scope === 'run' ? 'full workflow run' : 'workflow step'} from source run ${run.id}.`,
|
|
235
|
-
run.input ? `Original request:\n${compact(run.input, run, 2_000)}` : undefined,
|
|
236
|
-
].filter(Boolean).join('\n\n'),
|
|
237
|
-
workflow: {
|
|
238
|
-
id: `${run.workflowId}_replay`,
|
|
239
|
-
name: `Replay ${run.workflowId}`,
|
|
240
|
-
description: 'Replay generated from stored Pixcode workflow trace data.',
|
|
241
|
-
trigger: 'manual',
|
|
242
|
-
nodes: replayNodes,
|
|
243
|
-
},
|
|
244
|
-
metadata: {
|
|
245
|
-
...run.metadata,
|
|
246
|
-
workflowName: `Replay: ${String(run.metadata?.workflowName ?? run.workflowId)}`,
|
|
247
|
-
replay: replayMetadata,
|
|
248
|
-
settings: {
|
|
249
|
-
...settings,
|
|
250
|
-
replayMode: true,
|
|
251
|
-
},
|
|
252
|
-
},
|
|
253
|
-
};
|
|
254
|
-
}
|
|
1
|
+
import type {
|
|
2
|
+
Workflow,
|
|
3
|
+
WorkflowNode,
|
|
4
|
+
WorkflowNodeRun,
|
|
5
|
+
WorkflowRun,
|
|
6
|
+
} from '@/modules/orchestration/workflows/workflow.types.js';
|
|
7
|
+
import { redactTraceText } from '@/modules/orchestration/workflows/workflow-trace.js';
|
|
8
|
+
|
|
9
|
+
export const PIXCODE_REPLAY_PROTOCOL = 'pixcode.workflow-replay.v1';
|
|
10
|
+
|
|
11
|
+
export type WorkflowReplayScope = 'run' | 'node';
|
|
12
|
+
export type WorkflowReplaySafetyKind = 'file-write' | 'shell' | 'network';
|
|
13
|
+
|
|
14
|
+
export interface WorkflowReplayOperation {
|
|
15
|
+
kind: WorkflowReplaySafetyKind;
|
|
16
|
+
nodeId?: string;
|
|
17
|
+
summary: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface WorkflowReplayPlan {
|
|
21
|
+
protocol: typeof PIXCODE_REPLAY_PROTOCOL;
|
|
22
|
+
sourceRunId: string;
|
|
23
|
+
sourceWorkflowId: string;
|
|
24
|
+
scope: WorkflowReplayScope;
|
|
25
|
+
fromNodeId?: string;
|
|
26
|
+
selectedNodeIds: string[];
|
|
27
|
+
requiresApproval: boolean;
|
|
28
|
+
approvalReasons: string[];
|
|
29
|
+
destructiveOperations: WorkflowReplayOperation[];
|
|
30
|
+
limitations: string[];
|
|
31
|
+
input: string;
|
|
32
|
+
workflow: Workflow;
|
|
33
|
+
metadata: Record<string, unknown>;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function safeNodeId(value: string): string {
|
|
37
|
+
return value.replace(/[^a-zA-Z0-9_]+/g, '_').slice(0, 48) || 'node';
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function readRecord(value: unknown): Record<string, unknown> | undefined {
|
|
41
|
+
return value && typeof value === 'object' ? value as Record<string, unknown> : undefined;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function visibleNodes(run: WorkflowRun): WorkflowNodeRun[] {
|
|
45
|
+
return run.nodeRuns.filter((node) => !node.internal);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function defaultReplayNode(run: WorkflowRun): WorkflowNodeRun | undefined {
|
|
49
|
+
return visibleNodes(run).find((node) => node.status === 'failed')
|
|
50
|
+
?? [...visibleNodes(run)].reverse().find((node) => node.status !== 'skipped')
|
|
51
|
+
?? visibleNodes(run)[0];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function selectReplayNodes(run: WorkflowRun, scope: WorkflowReplayScope, fromNodeId?: string): WorkflowNodeRun[] {
|
|
55
|
+
const nodes = visibleNodes(run);
|
|
56
|
+
if (scope === 'run') return nodes;
|
|
57
|
+
|
|
58
|
+
const requested = fromNodeId
|
|
59
|
+
? nodes.find((node) => node.nodeId === fromNodeId)
|
|
60
|
+
: defaultReplayNode(run);
|
|
61
|
+
return requested ? [requested] : [];
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function compact(value: string | undefined, run: WorkflowRun, maxLength = 1_200): string | undefined {
|
|
65
|
+
return redactTraceText(value, run, maxLength);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function nodeTraceSummary(run: WorkflowRun, node: WorkflowNodeRun): string {
|
|
69
|
+
const artifactTypes = (node.artifacts ?? []).map((artifact) => artifact.type).filter(Boolean);
|
|
70
|
+
return [
|
|
71
|
+
`Step: ${node.agentLabel || node.nodeId}`,
|
|
72
|
+
`Node id: ${node.nodeId}`,
|
|
73
|
+
`Status: ${node.status}`,
|
|
74
|
+
node.stage ? `Stage: ${node.stage}` : undefined,
|
|
75
|
+
node.adapterId ? `Adapter: ${node.adapterId}` : undefined,
|
|
76
|
+
node.model ? `Model: ${node.model}` : undefined,
|
|
77
|
+
node.error ? `Error: ${compact(node.error, run, 800)}` : undefined,
|
|
78
|
+
artifactTypes.length > 0 ? `Artifacts: ${artifactTypes.join(', ')}` : undefined,
|
|
79
|
+
node.outputText ? `Output excerpt:\n${compact(node.outputText, run)}` : undefined,
|
|
80
|
+
].filter(Boolean).join('\n');
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function replayTraceSummary(run: WorkflowRun, nodes: WorkflowNodeRun[]): string {
|
|
84
|
+
return nodes.map((node) => nodeTraceSummary(run, node)).join('\n\n---\n\n');
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function pushOperation(
|
|
88
|
+
operations: WorkflowReplayOperation[],
|
|
89
|
+
kind: WorkflowReplaySafetyKind,
|
|
90
|
+
nodeId: string | undefined,
|
|
91
|
+
summary: string,
|
|
92
|
+
): void {
|
|
93
|
+
if (operations.some((operation) =>
|
|
94
|
+
operation.kind === kind && operation.nodeId === nodeId && operation.summary === summary,
|
|
95
|
+
)) {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
operations.push({ kind, nodeId, summary });
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function detectReplayOperations(run: WorkflowRun, nodes: WorkflowNodeRun[]): WorkflowReplayOperation[] {
|
|
102
|
+
const operations: WorkflowReplayOperation[] = [];
|
|
103
|
+
|
|
104
|
+
for (const node of nodes) {
|
|
105
|
+
for (const artifact of node.artifacts ?? []) {
|
|
106
|
+
if (artifact.type === 'file-diff') {
|
|
107
|
+
pushOperation(operations, 'file-write', node.nodeId, 'Prior step produced a file diff artifact.');
|
|
108
|
+
}
|
|
109
|
+
if (artifact.type === 'command-output') {
|
|
110
|
+
pushOperation(operations, 'shell', node.nodeId, 'Prior step produced command output.');
|
|
111
|
+
}
|
|
112
|
+
const text = [artifact.text, artifact.data ? JSON.stringify(artifact.data) : undefined]
|
|
113
|
+
.filter(Boolean)
|
|
114
|
+
.join('\n')
|
|
115
|
+
.toLocaleLowerCase('en');
|
|
116
|
+
if (/https?:\/\/|curl |wget |gh |npm publish|npm install|git push|ssh /u.test(text)) {
|
|
117
|
+
pushOperation(operations, 'network', node.nodeId, 'Prior artifact references a network-capable operation.');
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const text = [node.outputText, node.error, node.promptPreview].filter(Boolean).join('\n').toLocaleLowerCase('en');
|
|
122
|
+
if (/apply_patch|write file|file write|modified files|changed files/u.test(text)) {
|
|
123
|
+
pushOperation(operations, 'file-write', node.nodeId, 'Prior step text references file-write activity.');
|
|
124
|
+
}
|
|
125
|
+
if (/shell|command|terminal|npm run|node |python |php |go test|cargo |make |exit code/u.test(text)) {
|
|
126
|
+
pushOperation(operations, 'shell', node.nodeId, 'Prior step text references shell execution.');
|
|
127
|
+
}
|
|
128
|
+
if (/https?:\/\/|curl |wget |gh |npm publish|npm install|git push|ssh |network/u.test(text)) {
|
|
129
|
+
pushOperation(operations, 'network', node.nodeId, 'Prior step text references a network-capable operation.');
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return operations;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function replayNodeFromRunNode(
|
|
137
|
+
node: WorkflowNodeRun,
|
|
138
|
+
index: number,
|
|
139
|
+
previousReplayNodeId: string | undefined,
|
|
140
|
+
traceSummary: string,
|
|
141
|
+
limitations: string[],
|
|
142
|
+
requiresApproval: boolean,
|
|
143
|
+
): WorkflowNode {
|
|
144
|
+
const replayNodeId = `replay_${index + 1}_${safeNodeId(node.nodeId)}`;
|
|
145
|
+
return {
|
|
146
|
+
id: replayNodeId,
|
|
147
|
+
adapterId: node.adapterId || 'claude-code',
|
|
148
|
+
agentInstanceId: node.agentInstanceId,
|
|
149
|
+
agentLabel: node.agentLabel ? `${node.agentLabel} Replay` : 'Replay agent',
|
|
150
|
+
assignment: node.assignment ? `Replay: ${node.assignment}` : `Replay source node ${node.nodeId}`,
|
|
151
|
+
stage: node.stage ? `replay_${node.stage}` : 'replay',
|
|
152
|
+
model: node.model,
|
|
153
|
+
permissionMode: node.permissionMode === 'bypassPermissions' ? 'default' : node.permissionMode,
|
|
154
|
+
timeoutMs: node.timeoutMs,
|
|
155
|
+
inputs: previousReplayNodeId ? [previousReplayNodeId] : [],
|
|
156
|
+
output: 'both',
|
|
157
|
+
onFail: 'abort',
|
|
158
|
+
prompt: [
|
|
159
|
+
'This is a Pixcode workflow replay run.',
|
|
160
|
+
`Replay protocol: ${PIXCODE_REPLAY_PROTOCOL}`,
|
|
161
|
+
`Source node: ${node.nodeId}`,
|
|
162
|
+
requiresApproval
|
|
163
|
+
? '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.'
|
|
164
|
+
: 'Replay safety review did not find prior shell, network, or file-write artifacts, but still avoid destructive actions unless they are required and approved.',
|
|
165
|
+
'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.',
|
|
166
|
+
`Known limitations:\n- ${limitations.join('\n- ')}`,
|
|
167
|
+
`Trace summary:\n${traceSummary}`,
|
|
168
|
+
`Original step prompt:\n${node.promptPreview || '(No source prompt was stored.)'}`,
|
|
169
|
+
].join('\n\n'),
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export function buildWorkflowReplayPlan(
|
|
174
|
+
run: WorkflowRun,
|
|
175
|
+
options: {
|
|
176
|
+
scope?: WorkflowReplayScope;
|
|
177
|
+
fromNodeId?: string;
|
|
178
|
+
} = {},
|
|
179
|
+
): WorkflowReplayPlan {
|
|
180
|
+
const scope = options.scope ?? 'node';
|
|
181
|
+
const nodes = selectReplayNodes(run, scope, options.fromNodeId);
|
|
182
|
+
if (nodes.length === 0) {
|
|
183
|
+
throw new Error('No replayable workflow steps were found.');
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const limitations = [
|
|
187
|
+
'Replay uses stored run traces, prompt previews, messages, and artifacts; it cannot reproduce hidden provider state.',
|
|
188
|
+
'Replay reconstructs selected steps as a new workflow run instead of mutating the source run.',
|
|
189
|
+
'Shell, network, and file-write actions stay under the current CLI permission flow and require explicit replay approval when detected.',
|
|
190
|
+
];
|
|
191
|
+
const destructiveOperations = detectReplayOperations(run, nodes);
|
|
192
|
+
const requiresApproval = destructiveOperations.length > 0;
|
|
193
|
+
const traceSummary = replayTraceSummary(run, nodes);
|
|
194
|
+
const replayNodes = nodes.reduce<WorkflowNode[]>((accumulator, node, index) => {
|
|
195
|
+
const previousReplayNodeId = accumulator[accumulator.length - 1]?.id;
|
|
196
|
+
accumulator.push(replayNodeFromRunNode(
|
|
197
|
+
node,
|
|
198
|
+
index,
|
|
199
|
+
scope === 'run' ? previousReplayNodeId : undefined,
|
|
200
|
+
traceSummary,
|
|
201
|
+
limitations,
|
|
202
|
+
requiresApproval,
|
|
203
|
+
));
|
|
204
|
+
return accumulator;
|
|
205
|
+
}, []);
|
|
206
|
+
const settings = readRecord(run.metadata?.settings) ?? {};
|
|
207
|
+
const replayMetadata = {
|
|
208
|
+
protocol: PIXCODE_REPLAY_PROTOCOL,
|
|
209
|
+
sourceRunId: run.id,
|
|
210
|
+
sourceWorkflowId: run.workflowId,
|
|
211
|
+
scope,
|
|
212
|
+
fromNodeId: options.fromNodeId,
|
|
213
|
+
selectedNodeIds: nodes.map((node) => node.nodeId),
|
|
214
|
+
requiresApproval,
|
|
215
|
+
destructiveOperations,
|
|
216
|
+
limitations,
|
|
217
|
+
createdAt: Date.now(),
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
return {
|
|
221
|
+
protocol: PIXCODE_REPLAY_PROTOCOL,
|
|
222
|
+
sourceRunId: run.id,
|
|
223
|
+
sourceWorkflowId: run.workflowId,
|
|
224
|
+
scope,
|
|
225
|
+
fromNodeId: options.fromNodeId,
|
|
226
|
+
selectedNodeIds: nodes.map((node) => node.nodeId),
|
|
227
|
+
requiresApproval,
|
|
228
|
+
approvalReasons: destructiveOperations.map((operation) =>
|
|
229
|
+
`${operation.kind}${operation.nodeId ? ` in ${operation.nodeId}` : ''}: ${operation.summary}`,
|
|
230
|
+
),
|
|
231
|
+
destructiveOperations,
|
|
232
|
+
limitations,
|
|
233
|
+
input: [
|
|
234
|
+
`Replay ${scope === 'run' ? 'full workflow run' : 'workflow step'} from source run ${run.id}.`,
|
|
235
|
+
run.input ? `Original request:\n${compact(run.input, run, 2_000)}` : undefined,
|
|
236
|
+
].filter(Boolean).join('\n\n'),
|
|
237
|
+
workflow: {
|
|
238
|
+
id: `${run.workflowId}_replay`,
|
|
239
|
+
name: `Replay ${run.workflowId}`,
|
|
240
|
+
description: 'Replay generated from stored Pixcode workflow trace data.',
|
|
241
|
+
trigger: 'manual',
|
|
242
|
+
nodes: replayNodes,
|
|
243
|
+
},
|
|
244
|
+
metadata: {
|
|
245
|
+
...run.metadata,
|
|
246
|
+
workflowName: `Replay: ${String(run.metadata?.workflowName ?? run.workflowId)}`,
|
|
247
|
+
replay: replayMetadata,
|
|
248
|
+
settings: {
|
|
249
|
+
...settings,
|
|
250
|
+
replayMode: true,
|
|
251
|
+
},
|
|
252
|
+
},
|
|
253
|
+
};
|
|
254
|
+
}
|