@xopcai/xopc 0.0.106 → 0.0.108
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/browser-ext/manifest.json +1 -1
- package/dist/extensions/dashscope/xopc.extension.json +7 -2
- package/dist/extensions/fal/xopc.extension.json +7 -2
- package/dist/extensions/feishu/src/adapters/gateway-setup.js +22 -26
- package/dist/extensions/feishu/src/adapters/gateway-setup.js.map +1 -1
- package/dist/extensions/feishu/xopc.extension.json +458 -26
- package/dist/extensions/google/xopc.extension.json +7 -2
- package/dist/extensions/minimax/xopc.extension.json +8 -2
- package/dist/extensions/openai/xopc.extension.json +7 -2
- package/dist/extensions/telegram/xopc.extension.json +10 -2
- package/dist/extensions/weixin/xopc.extension.json +57 -14
- package/dist/gateway/static/root/apple-touch-icon.png +0 -0
- package/dist/gateway/static/root/assets/agents-56kCGHdV.js +78 -0
- package/dist/gateway/static/root/assets/apps-page-Dd88J47r.js +1 -0
- package/dist/gateway/static/root/assets/channels-settings-Ckyvnvml.js +1 -0
- package/dist/gateway/static/root/assets/cron-page-DKo2ZdRA.js +1 -0
- package/dist/gateway/static/root/assets/{dist-OM9qGfm5.js → dist-dmxWdtPG.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-debug-page-D-WHrr_w.js → extension-debug-page-iGpG-vpp.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-CCgjSoKn.js → extension-page-I7-3uHiD.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-settings-page-BJH5a-Gn.js → extension-settings-page-BisnHG7r.js} +1 -1
- package/dist/gateway/static/root/assets/{fetch-CkvRO30i.js → fetch-CFdKBnE6.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-startup-retry-BkKyRDrF.js → gateway-startup-retry-DdngdObI.js} +1 -1
- package/dist/gateway/static/root/assets/{heartbeat-config-api-CVyP6pFj.js → heartbeat-config-api-tbWAFPN8.js} +1 -1
- package/dist/gateway/static/root/assets/index-DI5_GsZt.css +1 -0
- package/dist/gateway/static/root/assets/index-slwUkhxH.js +4845 -0
- package/dist/gateway/static/root/assets/{logs-page-R6Vv_Ja6.js → logs-page-CCZiXrWu.js} +2 -2
- package/dist/gateway/static/root/assets/{note-detail-page-BUDarLhh.js → note-detail-page-BYbw2GLl.js} +4 -4
- package/dist/gateway/static/root/assets/{note-time-Cc2-T21g.js → note-time-Cya72Lte.js} +1 -1
- package/dist/gateway/static/root/assets/notes-page-DrAO7VRK.js +1 -0
- package/dist/gateway/static/root/assets/{schema-form-CrlOmEZt.js → schema-form-a6UJCK55.js} +1 -1
- package/dist/gateway/static/root/assets/sessions-page-DNU3Ymvz.js +1 -0
- package/dist/gateway/static/root/assets/{settings-advanced-gate-BBAQxxew.js → settings-advanced-gate-CyEJYTbw.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-form-section-DjPD-ErC.js → settings-form-section-DpHo2ecA.js} +1 -1
- package/dist/gateway/static/root/assets/settings-page-YRwe4q56.js +3 -0
- package/dist/gateway/static/root/assets/{share-preview-page-BjvPht3V.js → share-preview-page-B7rf4QB4.js} +1 -1
- package/dist/gateway/static/root/assets/skills-page-we1NAt7u.js +2 -0
- package/dist/gateway/static/root/assets/{theme-store-DYgVdRB4.js → theme-store-BZozwAeK.js} +1 -1
- package/dist/gateway/static/root/assets/url-DQRqHuL-.js +3 -0
- package/dist/gateway/static/root/assets/voice-api-key-field-Btagks_B.js +1 -0
- package/dist/gateway/static/root/assets/{workflow-page.utils-D4sqR0yX.js → workflow-page.utils-DvzJKs6W.js} +1 -1
- package/dist/gateway/static/root/assets/workflows-page-BgOAgCOG.js +18 -0
- package/dist/gateway/static/root/favicon-16.png +0 -0
- package/dist/gateway/static/root/favicon-32.png +0 -0
- package/dist/gateway/static/root/favicon.ico +0 -0
- package/dist/gateway/static/root/favicon.svg +18 -0
- package/dist/gateway/static/root/index.html +11 -10
- package/dist/package.js +1 -1
- package/dist/src/agent/skills/marketplace/adapters/store/store-api-client.d.ts +4 -0
- package/dist/src/agent/skills/marketplace/adapters/store/store-api-client.js +4 -2
- package/dist/src/agent/skills/marketplace/adapters/store/store-api-client.js.map +1 -1
- package/dist/src/agent/starter-agents.d.ts +19 -0
- package/dist/src/agent/starter-agents.js +190 -0
- package/dist/src/agent/starter-agents.js.map +1 -0
- package/dist/src/agent/workflow/runtime.js +49 -15
- package/dist/src/agent/workflow/runtime.js.map +1 -1
- package/dist/src/agent/workflow/types.d.ts +11 -0
- package/dist/src/channels/catalog/channel-catalog-service.js +13 -0
- package/dist/src/channels/catalog/channel-catalog-service.js.map +1 -1
- package/dist/src/channels/plugins/types.adapters.d.ts +2 -0
- package/dist/src/cli/commands/extension-create.d.ts +2 -0
- package/dist/src/cli/commands/extension-create.js +165 -0
- package/dist/src/cli/commands/extension-create.js.map +1 -0
- package/dist/src/cli/commands/extension-dev.js +0 -2
- package/dist/src/cli/commands/extension-dev.js.map +1 -1
- package/dist/src/cli/commands/extension-marketplace.js +104 -80
- package/dist/src/cli/commands/extension-marketplace.js.map +1 -1
- package/dist/src/cli/commands/extension-pack.d.ts +13 -0
- package/dist/src/cli/commands/extension-pack.js +159 -91
- package/dist/src/cli/commands/extension-pack.js.map +1 -1
- package/dist/src/cli/commands/extension.d.ts +2 -0
- package/dist/src/cli/commands/extension.js +375 -3
- package/dist/src/cli/commands/extension.js.map +1 -1
- package/dist/src/cli/commands/gateway/run-foreground.js +1 -2
- package/dist/src/cli/commands/gateway/run-foreground.js.map +1 -1
- package/dist/src/cli/commands/onboard.js +0 -2
- package/dist/src/cli/commands/onboard.js.map +1 -1
- package/dist/src/cli/commands/setup.js +0 -2
- package/dist/src/cli/commands/setup.js.map +1 -1
- package/dist/src/cli/utils/init-workspace-core.js +9 -6
- package/dist/src/cli/utils/init-workspace-core.js.map +1 -1
- package/dist/src/config/paths.d.ts +2 -2
- package/dist/src/config/paths.js +10 -6
- package/dist/src/config/paths.js.map +1 -1
- package/dist/src/config/schema.d.ts +2 -0
- package/dist/src/config/schema.js +3 -1
- package/dist/src/config/schema.js.map +1 -1
- package/dist/src/extensions/api.d.ts +4 -2
- package/dist/src/extensions/api.js +31 -12
- package/dist/src/extensions/api.js.map +1 -1
- package/dist/src/extensions/discover-extensions.js +1 -0
- package/dist/src/extensions/discover-extensions.js.map +1 -1
- package/dist/src/extensions/install.d.ts +1 -0
- package/dist/src/extensions/install.js +51 -8
- package/dist/src/extensions/install.js.map +1 -1
- package/dist/src/extensions/loader.d.ts +0 -4
- package/dist/src/extensions/loader.js +54 -87
- package/dist/src/extensions/loader.js.map +1 -1
- package/dist/src/extensions/lockfile.d.ts +9 -1
- package/dist/src/extensions/lockfile.js.map +1 -1
- package/dist/src/extensions/normalize-manifest.js +20 -4
- package/dist/src/extensions/normalize-manifest.js.map +1 -1
- package/dist/src/extensions/package-contract.d.ts +12 -0
- package/dist/src/extensions/package-contract.js +59 -0
- package/dist/src/extensions/package-contract.js.map +1 -0
- package/dist/src/extensions/sdk/core.d.ts +1 -1
- package/dist/src/extensions/sdk/index.d.ts +1 -1
- package/dist/src/extensions/sdk/index.js.map +1 -1
- package/dist/src/extensions/types/manifest.d.ts +15 -0
- package/dist/src/gateway/agents-admin.d.ts +0 -8
- package/dist/src/gateway/agents-admin.js +1 -26
- package/dist/src/gateway/agents-admin.js.map +1 -1
- package/dist/src/gateway/hono/routes/agents.js +1 -55
- package/dist/src/gateway/hono/routes/agents.js.map +1 -1
- package/dist/src/gateway/hono/routes/channels.js +10 -1
- package/dist/src/gateway/hono/routes/channels.js.map +1 -1
- package/dist/src/gateway/hono/routes/workflows.js +130 -0
- package/dist/src/gateway/hono/routes/workflows.js.map +1 -1
- package/dist/src/gateway/service.d.ts +2 -0
- package/dist/src/gateway/service.js +44 -0
- package/dist/src/gateway/service.js.map +1 -1
- package/dist/src/notes/agent-context.d.ts +12 -4
- package/dist/src/notes/agent-context.js.map +1 -1
- package/dist/src/tui/backends/embedded-backend.js +0 -7
- package/dist/src/tui/backends/embedded-backend.js.map +1 -1
- package/dist/src/tui/backends/gateway-sse-backend.js +0 -7
- package/dist/src/tui/backends/gateway-sse-backend.js.map +1 -1
- package/dist/src/tui/tui-backend.d.ts +0 -1
- package/dist/src/tui/tui-commands.js +1 -1
- package/dist/src/tui/tui-commands.js.map +1 -1
- package/dist/src/tui/tui-picker-overlay.js +1 -1
- package/dist/src/tui/tui-picker-overlay.js.map +1 -1
- package/dist/src/workflows/domain/definition-utils.d.ts +1 -0
- package/dist/src/workflows/domain/definition-utils.js +32 -2
- package/dist/src/workflows/domain/definition-utils.js.map +1 -1
- package/dist/src/workflows/domain/definition.d.ts +2 -0
- package/dist/src/workflows/domain/event.d.ts +2 -1
- package/dist/src/workflows/domain/index.d.ts +1 -0
- package/dist/src/workflows/domain/index.js +3 -2
- package/dist/src/workflows/domain/run.d.ts +27 -1
- package/dist/src/workflows/domain/run.js.map +1 -1
- package/dist/src/workflows/domain/schema-validation.d.ts +8 -0
- package/dist/src/workflows/domain/schema-validation.js +28 -0
- package/dist/src/workflows/domain/schema-validation.js.map +1 -0
- package/dist/src/workflows/draft/index.d.ts +3 -0
- package/dist/src/workflows/draft/index.js +3 -0
- package/dist/src/workflows/draft/workflow-draft-prompt.d.ts +23 -0
- package/dist/src/workflows/draft/workflow-draft-prompt.js +98 -0
- package/dist/src/workflows/draft/workflow-draft-prompt.js.map +1 -0
- package/dist/src/workflows/draft/workflow-draft-service.d.ts +12 -0
- package/dist/src/workflows/draft/workflow-draft-service.js +131 -0
- package/dist/src/workflows/draft/workflow-draft-service.js.map +1 -0
- package/dist/src/workflows/draft/workflow-draft-validator.d.ts +4 -0
- package/dist/src/workflows/draft/workflow-draft-validator.js +133 -0
- package/dist/src/workflows/draft/workflow-draft-validator.js.map +1 -0
- package/dist/src/workflows/draft/workflow-draft.types.d.ts +44 -0
- package/dist/src/workflows/draft/workflow-draft.types.js +1 -0
- package/dist/src/workflows/engine/projector.js +1 -0
- package/dist/src/workflows/engine/projector.js.map +1 -1
- package/dist/src/workflows/engine/workflow-engine.d.ts +18 -0
- package/dist/src/workflows/engine/workflow-engine.js +320 -11
- package/dist/src/workflows/engine/workflow-engine.js.map +1 -1
- package/dist/src/workflows/index.js +4 -3
- package/dist/src/workflows/service/workflow-run-service.d.ts +14 -3
- package/dist/src/workflows/service/workflow-run-service.js +227 -6
- package/dist/src/workflows/service/workflow-run-service.js.map +1 -1
- package/dist/src/workflows/service/workflow-run-service.types.d.ts +7 -2
- package/package.json +4 -2
- package/dist/gateway/static/root/assets/agents-3eP0vqFO.js +0 -396
- package/dist/gateway/static/root/assets/apps-page-CZ2ZKrko.js +0 -1
- package/dist/gateway/static/root/assets/channels-settings-BbETa93K.js +0 -1
- package/dist/gateway/static/root/assets/cron-page-DCtUkHMu.js +0 -1
- package/dist/gateway/static/root/assets/index-BPYKW7FB.css +0 -1
- package/dist/gateway/static/root/assets/index-CEZxETzw.js +0 -4845
- package/dist/gateway/static/root/assets/notes-page-EAHl16bq.js +0 -1
- package/dist/gateway/static/root/assets/sessions-page-Ce_xuuSB.js +0 -1
- package/dist/gateway/static/root/assets/settings-page-KhQ9QgH0.js +0 -3
- package/dist/gateway/static/root/assets/skills-page-C0laq7l1.js +0 -2
- package/dist/gateway/static/root/assets/url-DPEStU4M.js +0 -3
- package/dist/gateway/static/root/assets/voice-api-key-field-DeGckWkl.js +0 -1
- package/dist/gateway/static/root/assets/workflows-page-DFjM95g5.js +0 -27
- package/dist/src/agent/builtin-agent-ids.d.ts +0 -2
- package/dist/src/agent/builtin-agent-ids.js +0 -17
- package/dist/src/agent/builtin-agent-ids.js.map +0 -1
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { WorkflowDefinitionManifest } from '../domain/definition.js';
|
|
2
|
+
import type { WorkflowDefinitionValidationResult } from '../domain/validation.js';
|
|
3
|
+
export type WorkflowDraftMode = 'create' | 'improve';
|
|
4
|
+
export interface WorkflowDraftConstraints {
|
|
5
|
+
allowedTools?: string[];
|
|
6
|
+
allowNetwork?: boolean;
|
|
7
|
+
fileSystem?: 'none' | 'read' | 'write';
|
|
8
|
+
maxPhases?: number;
|
|
9
|
+
maxSubagents?: number;
|
|
10
|
+
outputFormat?: 'report' | 'json' | 'actions';
|
|
11
|
+
}
|
|
12
|
+
export interface CreateWorkflowDraftRequest {
|
|
13
|
+
prompt: string;
|
|
14
|
+
agentId: string;
|
|
15
|
+
language?: 'en' | 'zh';
|
|
16
|
+
mode?: WorkflowDraftMode;
|
|
17
|
+
existingScript?: string;
|
|
18
|
+
constraints?: WorkflowDraftConstraints;
|
|
19
|
+
}
|
|
20
|
+
export interface WorkflowDraftLintIssue {
|
|
21
|
+
severity: 'error' | 'warning';
|
|
22
|
+
code: 'missing_input_schema' | 'missing_output_schema' | 'unsafe_permission' | 'too_many_agents' | 'unknown_tool' | 'weak_phase_names' | 'unbounded_parallelism';
|
|
23
|
+
message: string;
|
|
24
|
+
}
|
|
25
|
+
export interface GeneratedWorkflowDraft {
|
|
26
|
+
name: string;
|
|
27
|
+
script: string;
|
|
28
|
+
manifest: WorkflowDefinitionManifest;
|
|
29
|
+
explanation: string;
|
|
30
|
+
assumptions: string[];
|
|
31
|
+
risks: string[];
|
|
32
|
+
}
|
|
33
|
+
export interface WorkflowDraftResponse extends GeneratedWorkflowDraft {
|
|
34
|
+
draftId: string;
|
|
35
|
+
repairAttempts: number;
|
|
36
|
+
permissionsSummary: string[];
|
|
37
|
+
validation: WorkflowDefinitionValidationResult;
|
|
38
|
+
lint: WorkflowDraftLintIssue[];
|
|
39
|
+
suggestedInputs?: Array<{
|
|
40
|
+
key: string;
|
|
41
|
+
label: string;
|
|
42
|
+
example: string;
|
|
43
|
+
}>;
|
|
44
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"projector.js","names":[],"sources":["../../../../src/workflows/engine/projector.ts"],"sourcesContent":["import type { WorkflowEventEnvelope } from '../domain/event.js';\nimport type {\n WorkflowAgentStatus,\n WorkflowAgentView,\n WorkflowLogEntry,\n WorkflowPhaseStatus,\n WorkflowPhaseView,\n WorkflowRun,\n WorkflowRunView,\n WorkflowTimelineItem,\n} from '../domain/run.js';\nimport { isTerminalWorkflowRunStatus } from '../domain/run.js';\nimport type { WorkflowArtifactRef } from '../domain/result.js';\n\nfunction terminalRunStatus(status: WorkflowRun['status']): boolean {\n return isTerminalWorkflowRunStatus(status);\n}\n\nfunction phaseStatusAfterAgentStatus(agentStatuses: WorkflowAgentStatus[]): WorkflowPhaseStatus {\n if (agentStatuses.some((status) => status === 'running')) {\n return 'running';\n }\n if (agentStatuses.some((status) => status === 'error')) {\n return 'failed';\n }\n if (agentStatuses.length > 0 && agentStatuses.every((status) => status === 'done' || status === 'skipped')) {\n return 'completed';\n }\n return 'pending';\n}\n\nfunction buildTimelineItem(event: WorkflowEventEnvelope): WorkflowTimelineItem {\n return {\n sequence: event.sequence,\n type: event.type,\n title: event.type.replaceAll('_', ' '),\n createdAtMs: event.createdAtMs,\n };\n}\n\nexport function projectWorkflowRunView(events: WorkflowEventEnvelope[]): WorkflowRunView | null {\n if (events.length === 0) {\n return null;\n }\n\n const orderedEvents = [...events].sort((left, right) => left.sequence - right.sequence);\n const firstEvent = orderedEvents[0];\n if (firstEvent.type !== 'run_queued') {\n return null;\n }\n\n const firstPayload = firstEvent.payload as { run?: WorkflowRun };\n if (!firstPayload.run) {\n return null;\n }\n\n const run: WorkflowRun = {\n ...firstPayload.run,\n metrics: { ...firstPayload.run.metrics },\n };\n const phaseIdToPhase = new Map<string, WorkflowPhaseView>();\n const agentIdToAgent = new Map<string, WorkflowAgentView>();\n const logs: WorkflowLogEntry[] = [];\n const artifacts: WorkflowArtifactRef[] = [];\n const timeline: WorkflowTimelineItem[] = [];\n\n for (const event of orderedEvents) {\n timeline.push(buildTimelineItem(event));\n\n switch (event.type) {\n case 'run_started': {\n const payload = event.payload as { startedAtMs: number };\n run.status = 'running';\n run.startedAtMs = payload.startedAtMs;\n break;\n }\n case 'phase_started': {\n const payload = event.payload as { phaseId: string; title: string };\n phaseIdToPhase.set(payload.phaseId, {\n id: payload.phaseId,\n title: payload.title,\n status: 'running',\n startedAtMs: event.createdAtMs,\n agentIds: phaseIdToPhase.get(payload.phaseId)?.agentIds ?? [],\n });\n break;\n }\n case 'phase_completed': {\n const payload = event.payload as { phaseId: string };\n const existing = phaseIdToPhase.get(payload.phaseId);\n if (existing) {\n phaseIdToPhase.set(payload.phaseId, {\n ...existing,\n status: 'completed',\n completedAtMs: event.createdAtMs,\n });\n }\n break;\n }\n case 'agent_queued': {\n const payload = event.payload as { agentId: string; label: string; phaseId?: string; prompt?: string; sessionKey: string };\n agentIdToAgent.set(payload.agentId, {\n id: payload.agentId,\n label: payload.label,\n phaseId: payload.phaseId,\n status: 'queued',\n prompt: payload.prompt,\n sessionKey: payload.sessionKey,\n transcriptMessageCount: 0,\n steps: [],\n });\n if (payload.phaseId) {\n const existingPhase = phaseIdToPhase.get(payload.phaseId);\n if (existingPhase && !existingPhase.agentIds.includes(payload.agentId)) {\n phaseIdToPhase.set(payload.phaseId, {\n ...existingPhase,\n agentIds: [...existingPhase.agentIds, payload.agentId],\n });\n }\n }\n break;\n }\n case 'agent_started': {\n const payload = event.payload as { agentId: string };\n const existing = agentIdToAgent.get(payload.agentId);\n if (existing) {\n agentIdToAgent.set(payload.agentId, {\n ...existing,\n status: 'running',\n startedAtMs: event.createdAtMs,\n });\n }\n break;\n }\n case 'agent_step_started': {\n const payload = event.payload as {\n agentId: string;\n stepId: string;\n label: string;\n kind: 'tool' | 'llm' | 'thinking';\n toolName?: string;\n detail?: string;\n };\n const existing = agentIdToAgent.get(payload.agentId);\n if (existing) {\n agentIdToAgent.set(payload.agentId, {\n ...existing,\n currentStep: payload.label,\n transcriptMessageCount: existing.transcriptMessageCount + 1,\n steps: [\n ...existing.steps,\n {\n id: payload.stepId,\n label: payload.label,\n kind: payload.kind,\n toolName: payload.toolName,\n detail: payload.detail,\n status: 'running',\n startedAtMs: event.createdAtMs,\n },\n ],\n });\n }\n break;\n }\n case 'agent_step_completed': {\n const payload = event.payload as {\n agentId: string;\n stepId: string;\n status: 'done' | 'error';\n resultPreview?: string;\n error?: string;\n };\n const existing = agentIdToAgent.get(payload.agentId);\n if (existing) {\n agentIdToAgent.set(payload.agentId, {\n ...existing,\n steps: existing.steps.map((step) =>\n step.id === payload.stepId\n ? {\n ...step,\n status: payload.status,\n resultPreview: payload.resultPreview,\n error: payload.error,\n completedAtMs: event.createdAtMs,\n }\n : step,\n ),\n });\n }\n break;\n }\n case 'agent_completed': {\n const payload = event.payload as {\n agentId: string;\n status: WorkflowAgentStatus;\n resultPreview?: string;\n error?: string;\n };\n const existing = agentIdToAgent.get(payload.agentId);\n if (existing) {\n agentIdToAgent.set(payload.agentId, {\n ...existing,\n status: payload.status,\n currentStep: undefined,\n resultPreview: payload.resultPreview,\n error: payload.error,\n transcriptMessageCount: existing.transcriptMessageCount + 1,\n completedAtMs: event.createdAtMs,\n });\n }\n break;\n }\n case 'log_appended': {\n const payload = event.payload as { message: string };\n logs.push({ sequence: event.sequence, message: payload.message, createdAtMs: event.createdAtMs });\n break;\n }\n case 'artifact_created': {\n const payload = event.payload as { artifact: WorkflowArtifactRef };\n artifacts.push(payload.artifact);\n run.metrics.artifactCount = artifacts.length;\n break;\n }\n case 'run_completed': {\n const payload = event.payload as { result: WorkflowRun['result'] };\n run.status = 'succeeded';\n run.result = payload.result;\n run.completedAtMs = event.createdAtMs;\n break;\n }\n case 'run_failed': {\n const payload = event.payload as { error: WorkflowRun['error'] };\n run.status = payload.error?.code === 'timeout' ? 'timeout' : 'failed';\n run.error = payload.error;\n run.completedAtMs = event.createdAtMs;\n break;\n }\n case 'run_cancelled': {\n run.status = 'cancelled';\n run.completedAtMs = event.createdAtMs;\n for (const [agentId, agent] of agentIdToAgent) {\n if (agent.status !== 'queued' && agent.status !== 'running') {\n continue;\n }\n agentIdToAgent.set(agentId, {\n ...agent,\n status: 'skipped',\n currentStep: undefined,\n completedAtMs: event.createdAtMs,\n steps: agent.steps.map((step) =>\n step.status === 'running'\n ? { ...step, status: 'error', completedAtMs: event.createdAtMs }\n : step,\n ),\n });\n }\n break;\n }\n case 'run_queued':\n default:\n break;\n }\n }\n\n const agents = [...agentIdToAgent.values()];\n run.metrics.agentCount = agents.length;\n run.metrics.doneAgentCount = agents.filter((agent) => agent.status === 'done').length;\n run.metrics.errorAgentCount = agents.filter((agent) => agent.status === 'error').length;\n run.metrics.skippedAgentCount = agents.filter((agent) => agent.status === 'skipped').length;\n run.metrics.artifactCount = artifacts.length;\n if (run.startedAtMs && run.completedAtMs) {\n run.metrics.durationMs = run.completedAtMs - run.startedAtMs;\n }\n\n const phases = [...phaseIdToPhase.values()].map((phase) => {\n if (phase.status === 'completed' || phase.status === 'failed') {\n return phase;\n }\n const phaseAgents = phase.agentIds\n .map((agentId) => agentIdToAgent.get(agentId)?.status)\n .filter((status): status is WorkflowAgentStatus => Boolean(status));\n return { ...phase, status: phaseStatusAfterAgentStatus(phaseAgents) };\n });\n\n return {\n run,\n phases,\n agents,\n logs,\n artifacts,\n timeline,\n controls: {\n canCancel: !terminalRunStatus(run.status),\n canRetry: terminalRunStatus(run.status),\n canArchive: terminalRunStatus(run.status),\n },\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"projector.js","names":[],"sources":["../../../../src/workflows/engine/projector.ts"],"sourcesContent":["import type { WorkflowEventEnvelope } from '../domain/event.js';\nimport type {\n WorkflowAgentStatus,\n WorkflowAgentView,\n WorkflowLogEntry,\n WorkflowPhaseStatus,\n WorkflowPhaseView,\n WorkflowRun,\n WorkflowRunView,\n WorkflowTimelineItem,\n} from '../domain/run.js';\nimport { isTerminalWorkflowRunStatus } from '../domain/run.js';\nimport type { WorkflowArtifactRef } from '../domain/result.js';\n\nfunction terminalRunStatus(status: WorkflowRun['status']): boolean {\n return isTerminalWorkflowRunStatus(status);\n}\n\nfunction phaseStatusAfterAgentStatus(agentStatuses: WorkflowAgentStatus[]): WorkflowPhaseStatus {\n if (agentStatuses.some((status) => status === 'running')) {\n return 'running';\n }\n if (agentStatuses.some((status) => status === 'error')) {\n return 'failed';\n }\n if (agentStatuses.length > 0 && agentStatuses.every((status) => status === 'done' || status === 'skipped')) {\n return 'completed';\n }\n return 'pending';\n}\n\nfunction buildTimelineItem(event: WorkflowEventEnvelope): WorkflowTimelineItem {\n return {\n sequence: event.sequence,\n type: event.type,\n title: event.type.replaceAll('_', ' '),\n createdAtMs: event.createdAtMs,\n };\n}\n\nexport function projectWorkflowRunView(events: WorkflowEventEnvelope[]): WorkflowRunView | null {\n if (events.length === 0) {\n return null;\n }\n\n const orderedEvents = [...events].sort((left, right) => left.sequence - right.sequence);\n const firstEvent = orderedEvents[0];\n if (firstEvent.type !== 'run_queued') {\n return null;\n }\n\n const firstPayload = firstEvent.payload as { run?: WorkflowRun };\n if (!firstPayload.run) {\n return null;\n }\n\n const run: WorkflowRun = {\n ...firstPayload.run,\n metrics: { ...firstPayload.run.metrics },\n };\n const phaseIdToPhase = new Map<string, WorkflowPhaseView>();\n const agentIdToAgent = new Map<string, WorkflowAgentView>();\n const logs: WorkflowLogEntry[] = [];\n const artifacts: WorkflowArtifactRef[] = [];\n const timeline: WorkflowTimelineItem[] = [];\n\n for (const event of orderedEvents) {\n timeline.push(buildTimelineItem(event));\n\n switch (event.type) {\n case 'run_started': {\n const payload = event.payload as { startedAtMs: number };\n run.status = 'running';\n run.startedAtMs = payload.startedAtMs;\n break;\n }\n case 'phase_started': {\n const payload = event.payload as { phaseId: string; title: string };\n phaseIdToPhase.set(payload.phaseId, {\n id: payload.phaseId,\n title: payload.title,\n status: 'running',\n startedAtMs: event.createdAtMs,\n agentIds: phaseIdToPhase.get(payload.phaseId)?.agentIds ?? [],\n });\n break;\n }\n case 'phase_completed': {\n const payload = event.payload as { phaseId: string };\n const existing = phaseIdToPhase.get(payload.phaseId);\n if (existing) {\n phaseIdToPhase.set(payload.phaseId, {\n ...existing,\n status: 'completed',\n completedAtMs: event.createdAtMs,\n });\n }\n break;\n }\n case 'agent_queued': {\n const payload = event.payload as {\n agentId: string;\n label: string;\n phaseId?: string;\n prompt?: string;\n sessionKey: string;\n invocation?: WorkflowAgentView['invocation'];\n };\n agentIdToAgent.set(payload.agentId, {\n id: payload.agentId,\n label: payload.label,\n phaseId: payload.phaseId,\n status: 'queued',\n prompt: payload.prompt,\n invocation: payload.invocation,\n sessionKey: payload.sessionKey,\n transcriptMessageCount: 0,\n steps: [],\n });\n if (payload.phaseId) {\n const existingPhase = phaseIdToPhase.get(payload.phaseId);\n if (existingPhase && !existingPhase.agentIds.includes(payload.agentId)) {\n phaseIdToPhase.set(payload.phaseId, {\n ...existingPhase,\n agentIds: [...existingPhase.agentIds, payload.agentId],\n });\n }\n }\n break;\n }\n case 'agent_started': {\n const payload = event.payload as { agentId: string };\n const existing = agentIdToAgent.get(payload.agentId);\n if (existing) {\n agentIdToAgent.set(payload.agentId, {\n ...existing,\n status: 'running',\n startedAtMs: event.createdAtMs,\n });\n }\n break;\n }\n case 'agent_step_started': {\n const payload = event.payload as {\n agentId: string;\n stepId: string;\n label: string;\n kind: 'tool' | 'llm' | 'thinking';\n toolName?: string;\n detail?: string;\n };\n const existing = agentIdToAgent.get(payload.agentId);\n if (existing) {\n agentIdToAgent.set(payload.agentId, {\n ...existing,\n currentStep: payload.label,\n transcriptMessageCount: existing.transcriptMessageCount + 1,\n steps: [\n ...existing.steps,\n {\n id: payload.stepId,\n label: payload.label,\n kind: payload.kind,\n toolName: payload.toolName,\n detail: payload.detail,\n status: 'running',\n startedAtMs: event.createdAtMs,\n },\n ],\n });\n }\n break;\n }\n case 'agent_step_completed': {\n const payload = event.payload as {\n agentId: string;\n stepId: string;\n status: 'done' | 'error';\n resultPreview?: string;\n error?: string;\n };\n const existing = agentIdToAgent.get(payload.agentId);\n if (existing) {\n agentIdToAgent.set(payload.agentId, {\n ...existing,\n steps: existing.steps.map((step) =>\n step.id === payload.stepId\n ? {\n ...step,\n status: payload.status,\n resultPreview: payload.resultPreview,\n error: payload.error,\n completedAtMs: event.createdAtMs,\n }\n : step,\n ),\n });\n }\n break;\n }\n case 'agent_completed': {\n const payload = event.payload as {\n agentId: string;\n status: WorkflowAgentStatus;\n resultPreview?: string;\n error?: string;\n };\n const existing = agentIdToAgent.get(payload.agentId);\n if (existing) {\n agentIdToAgent.set(payload.agentId, {\n ...existing,\n status: payload.status,\n currentStep: undefined,\n resultPreview: payload.resultPreview,\n error: payload.error,\n transcriptMessageCount: existing.transcriptMessageCount + 1,\n completedAtMs: event.createdAtMs,\n });\n }\n break;\n }\n case 'log_appended': {\n const payload = event.payload as { message: string };\n logs.push({ sequence: event.sequence, message: payload.message, createdAtMs: event.createdAtMs });\n break;\n }\n case 'artifact_created': {\n const payload = event.payload as { artifact: WorkflowArtifactRef };\n artifacts.push(payload.artifact);\n run.metrics.artifactCount = artifacts.length;\n break;\n }\n case 'run_completed': {\n const payload = event.payload as { result: WorkflowRun['result'] };\n run.status = 'succeeded';\n run.result = payload.result;\n run.completedAtMs = event.createdAtMs;\n break;\n }\n case 'run_failed': {\n const payload = event.payload as { error: WorkflowRun['error'] };\n run.status = payload.error?.code === 'timeout' ? 'timeout' : 'failed';\n run.error = payload.error;\n run.completedAtMs = event.createdAtMs;\n break;\n }\n case 'run_cancelled': {\n run.status = 'cancelled';\n run.completedAtMs = event.createdAtMs;\n for (const [agentId, agent] of agentIdToAgent) {\n if (agent.status !== 'queued' && agent.status !== 'running') {\n continue;\n }\n agentIdToAgent.set(agentId, {\n ...agent,\n status: 'skipped',\n currentStep: undefined,\n completedAtMs: event.createdAtMs,\n steps: agent.steps.map((step) =>\n step.status === 'running'\n ? { ...step, status: 'error', completedAtMs: event.createdAtMs }\n : step,\n ),\n });\n }\n break;\n }\n case 'run_queued':\n default:\n break;\n }\n }\n\n const agents = [...agentIdToAgent.values()];\n run.metrics.agentCount = agents.length;\n run.metrics.doneAgentCount = agents.filter((agent) => agent.status === 'done').length;\n run.metrics.errorAgentCount = agents.filter((agent) => agent.status === 'error').length;\n run.metrics.skippedAgentCount = agents.filter((agent) => agent.status === 'skipped').length;\n run.metrics.artifactCount = artifacts.length;\n if (run.startedAtMs && run.completedAtMs) {\n run.metrics.durationMs = run.completedAtMs - run.startedAtMs;\n }\n\n const phases = [...phaseIdToPhase.values()].map((phase) => {\n if (phase.status === 'completed' || phase.status === 'failed') {\n return phase;\n }\n const phaseAgents = phase.agentIds\n .map((agentId) => agentIdToAgent.get(agentId)?.status)\n .filter((status): status is WorkflowAgentStatus => Boolean(status));\n return { ...phase, status: phaseStatusAfterAgentStatus(phaseAgents) };\n });\n\n return {\n run,\n phases,\n agents,\n logs,\n artifacts,\n timeline,\n controls: {\n canCancel: !terminalRunStatus(run.status),\n canRetry: terminalRunStatus(run.status),\n canArchive: terminalRunStatus(run.status),\n },\n };\n}\n"],"mappings":"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"}
|
|
@@ -6,6 +6,7 @@ import { WorkflowRunStore } from '../store/run-store.js';
|
|
|
6
6
|
import type { WorkflowRuntime } from '../runtime/workflow-runtime-port.js';
|
|
7
7
|
import type { Api, Model } from '@earendil-works/pi-ai';
|
|
8
8
|
import type { WorkflowScriptSubagentRunner } from '../runtime/script-runtime.js';
|
|
9
|
+
import type { WorkflowAgentInvocationSnapshot } from '../domain/index.js';
|
|
9
10
|
export interface WorkflowEngineOptions {
|
|
10
11
|
cwd: string;
|
|
11
12
|
eventStore: WorkflowEventStore;
|
|
@@ -59,10 +60,27 @@ export interface StartWorkflowRunOptions {
|
|
|
59
60
|
concurrency?: number;
|
|
60
61
|
maxSubagents?: number;
|
|
61
62
|
tokenBudget?: number | null;
|
|
63
|
+
timeoutSec?: number;
|
|
64
|
+
}
|
|
65
|
+
export interface WorkflowReplayAgentTarget {
|
|
66
|
+
agentId: string;
|
|
67
|
+
label: string;
|
|
68
|
+
phaseId?: string;
|
|
69
|
+
phaseTitle?: string;
|
|
70
|
+
prompt: string;
|
|
71
|
+
invocation?: WorkflowAgentInvocationSnapshot;
|
|
72
|
+
}
|
|
73
|
+
export interface StartWorkflowReplayRunOptions extends StartWorkflowRunOptions {
|
|
74
|
+
sourceRunId: string;
|
|
75
|
+
replayScope: 'failed_agents' | 'failed_phases';
|
|
76
|
+
targets: WorkflowReplayAgentTarget[];
|
|
62
77
|
}
|
|
63
78
|
export declare class WorkflowEngine {
|
|
64
79
|
private readonly options;
|
|
65
80
|
constructor(options: WorkflowEngineOptions);
|
|
66
81
|
startRun(definition: WorkflowDefinition, options: StartWorkflowRunOptions): Promise<WorkflowRunView>;
|
|
82
|
+
startReplayRun(definition: WorkflowDefinition, options: StartWorkflowReplayRunOptions): Promise<WorkflowRunView>;
|
|
83
|
+
private runReplayTargets;
|
|
84
|
+
private runReplayTarget;
|
|
67
85
|
private callHooks;
|
|
68
86
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { workflowStepLabel } from "../../agent/workflow/step-labels.js";
|
|
2
|
+
import { validateWorkflowJsonSchema } from "../domain/schema-validation.js";
|
|
2
3
|
import { createScriptWorkflowRuntime } from "../runtime/script-workflow-runtime.js";
|
|
3
4
|
import { randomUUID } from "node:crypto";
|
|
4
5
|
//#region src/workflows/engine/workflow-engine.ts
|
|
@@ -53,7 +54,7 @@ var WorkflowEngine = class {
|
|
|
53
54
|
}));
|
|
54
55
|
await appendEvent("run_started", { startedAtMs: Date.now() });
|
|
55
56
|
try {
|
|
56
|
-
const runtimeResult = await (this.options.runtime ?? createScriptWorkflowRuntime()).run(definition.runtime.source, {
|
|
57
|
+
const runtimeResult = await withWorkflowTimeout((this.options.runtime ?? createScriptWorkflowRuntime()).run(definition.runtime.source, {
|
|
57
58
|
runner: this.options.runner,
|
|
58
59
|
resolveModelId: this.options.resolveModelId
|
|
59
60
|
}, {
|
|
@@ -95,7 +96,8 @@ var WorkflowEngine = class {
|
|
|
95
96
|
label: event.label,
|
|
96
97
|
phaseId,
|
|
97
98
|
prompt: event.prompt,
|
|
98
|
-
sessionKey: subagentSessionKey
|
|
99
|
+
sessionKey: subagentSessionKey,
|
|
100
|
+
invocation: event.invocation
|
|
99
101
|
});
|
|
100
102
|
},
|
|
101
103
|
onAgentStart: (event) => {
|
|
@@ -143,9 +145,10 @@ var WorkflowEngine = class {
|
|
|
143
145
|
onProgress: (event) => recorder.onProgress(event)
|
|
144
146
|
};
|
|
145
147
|
}
|
|
146
|
-
});
|
|
148
|
+
}), options.timeoutSec ?? definition.defaults.timeoutSec);
|
|
147
149
|
await eventQueue;
|
|
148
150
|
if (currentPhaseId) await appendEvent("phase_completed", { phaseId: currentPhaseId });
|
|
151
|
+
assertWorkflowOutput(definition, runtimeResult.result);
|
|
149
152
|
await appendEvent("run_completed", { result: toWorkflowResultEnvelope(runtimeResult.result) });
|
|
150
153
|
await this.callHooks((hook) => hook.afterRun?.({
|
|
151
154
|
runId,
|
|
@@ -153,7 +156,7 @@ var WorkflowEngine = class {
|
|
|
153
156
|
}));
|
|
154
157
|
} catch (err) {
|
|
155
158
|
await eventQueue;
|
|
156
|
-
const error = toWorkflowRunError(err, options.signal?.aborted === true);
|
|
159
|
+
const error = toWorkflowRunError(err, options.signal?.aborted === true, options.signal?.reason);
|
|
157
160
|
if (error.code === "cancelled") {
|
|
158
161
|
await appendEvent("run_cancelled", { reason: error.message });
|
|
159
162
|
await this.callHooks((hook) => hook.afterRun?.({
|
|
@@ -172,12 +175,233 @@ var WorkflowEngine = class {
|
|
|
172
175
|
if (!view) throw new Error(`workflow run view was not created for ${runId}`);
|
|
173
176
|
return view;
|
|
174
177
|
}
|
|
178
|
+
async startReplayRun(definition, options) {
|
|
179
|
+
const runId = options.runId ?? randomUUID();
|
|
180
|
+
const createdAtMs = Date.now();
|
|
181
|
+
let eventQueue = Promise.resolve();
|
|
182
|
+
const progressRecorders = /* @__PURE__ */ new Map();
|
|
183
|
+
const run = {
|
|
184
|
+
id: runId,
|
|
185
|
+
definitionId: definition.id,
|
|
186
|
+
definitionVersion: definition.version,
|
|
187
|
+
title: `${definition.title} replay`,
|
|
188
|
+
goal: options.goal ?? definition.description,
|
|
189
|
+
input: options.input ?? {},
|
|
190
|
+
status: "queued",
|
|
191
|
+
source: options.source,
|
|
192
|
+
metadata: options.metadata,
|
|
193
|
+
metrics: {
|
|
194
|
+
agentCount: 0,
|
|
195
|
+
doneAgentCount: 0,
|
|
196
|
+
errorAgentCount: 0,
|
|
197
|
+
skippedAgentCount: 0,
|
|
198
|
+
artifactCount: 0
|
|
199
|
+
},
|
|
200
|
+
createdAtMs
|
|
201
|
+
};
|
|
202
|
+
const appendEvent = (type, payload, createdAtMsOverride) => {
|
|
203
|
+
eventQueue = eventQueue.then(() => this.options.eventStore.append({
|
|
204
|
+
runId,
|
|
205
|
+
type,
|
|
206
|
+
payload,
|
|
207
|
+
createdAtMs: createdAtMsOverride
|
|
208
|
+
})).then(async (event) => {
|
|
209
|
+
this.options.onEventAppended?.(event);
|
|
210
|
+
const view = await this.options.runStore.rebuildRunView(runId);
|
|
211
|
+
if (view) this.options.onRunViewUpdated?.(view);
|
|
212
|
+
});
|
|
213
|
+
return eventQueue;
|
|
214
|
+
};
|
|
215
|
+
await appendEvent("run_queued", { run }, createdAtMs);
|
|
216
|
+
await this.callHooks((hook) => hook.beforeRun?.({
|
|
217
|
+
definition,
|
|
218
|
+
run
|
|
219
|
+
}));
|
|
220
|
+
await appendEvent("run_started", { startedAtMs: Date.now() });
|
|
221
|
+
try {
|
|
222
|
+
const result = await withWorkflowTimeout(this.runReplayTargets({
|
|
223
|
+
definition,
|
|
224
|
+
runId,
|
|
225
|
+
targets: options.targets,
|
|
226
|
+
signal: options.signal,
|
|
227
|
+
appendEvent,
|
|
228
|
+
progressRecorders
|
|
229
|
+
}), options.timeoutSec ?? definition.defaults.timeoutSec);
|
|
230
|
+
await eventQueue;
|
|
231
|
+
if (result.errors.length > 0) {
|
|
232
|
+
await appendEvent("run_failed", { error: {
|
|
233
|
+
code: "runtime_error",
|
|
234
|
+
message: `Replay completed with ${result.errors.length} failed target${result.errors.length === 1 ? "" : "s"}.`,
|
|
235
|
+
detail: result.errors.join("\n"),
|
|
236
|
+
recoverable: true
|
|
237
|
+
} });
|
|
238
|
+
await this.callHooks((hook) => hook.afterRun?.({
|
|
239
|
+
runId,
|
|
240
|
+
status: "failed"
|
|
241
|
+
}));
|
|
242
|
+
} else {
|
|
243
|
+
await appendEvent("run_completed", { result: buildReplayResultEnvelope(options, result.results) });
|
|
244
|
+
await this.callHooks((hook) => hook.afterRun?.({
|
|
245
|
+
runId,
|
|
246
|
+
status: "succeeded"
|
|
247
|
+
}));
|
|
248
|
+
}
|
|
249
|
+
} catch (err) {
|
|
250
|
+
await eventQueue;
|
|
251
|
+
const error = toWorkflowRunError(err, options.signal?.aborted === true, options.signal?.reason);
|
|
252
|
+
if (error.code === "cancelled") {
|
|
253
|
+
await appendEvent("run_cancelled", { reason: error.message });
|
|
254
|
+
await this.callHooks((hook) => hook.afterRun?.({
|
|
255
|
+
runId,
|
|
256
|
+
status: "cancelled"
|
|
257
|
+
}));
|
|
258
|
+
} else {
|
|
259
|
+
await appendEvent("run_failed", { error });
|
|
260
|
+
await this.callHooks((hook) => hook.afterRun?.({
|
|
261
|
+
runId,
|
|
262
|
+
status: "failed"
|
|
263
|
+
}));
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
const view = await this.options.runStore.readRunView(runId);
|
|
267
|
+
if (!view) throw new Error(`workflow replay run view was not created for ${runId}`);
|
|
268
|
+
return view;
|
|
269
|
+
}
|
|
270
|
+
async runReplayTargets(params) {
|
|
271
|
+
const results = [];
|
|
272
|
+
const errors = [];
|
|
273
|
+
const targetsByPhase = groupReplayTargetsByPhase(params.targets);
|
|
274
|
+
for (const group of targetsByPhase) {
|
|
275
|
+
throwIfSignalAborted(params.signal);
|
|
276
|
+
if (group.phaseId && group.phaseTitle) await params.appendEvent("phase_started", {
|
|
277
|
+
phaseId: group.phaseId,
|
|
278
|
+
title: group.phaseTitle
|
|
279
|
+
});
|
|
280
|
+
const phaseResults = await Promise.all(group.targets.map((target) => this.runReplayTarget({
|
|
281
|
+
...params,
|
|
282
|
+
target
|
|
283
|
+
})));
|
|
284
|
+
results.push(...phaseResults);
|
|
285
|
+
errors.push(...phaseResults.filter((item) => item.status === "error").map((item) => `${item.label}: ${item.error ?? "failed"}`));
|
|
286
|
+
if (group.phaseId && phaseResults.every((item) => item.status === "done")) await params.appendEvent("phase_completed", { phaseId: group.phaseId });
|
|
287
|
+
}
|
|
288
|
+
return {
|
|
289
|
+
results,
|
|
290
|
+
errors
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
async runReplayTarget(params) {
|
|
294
|
+
const { target } = params;
|
|
295
|
+
const invocation = normalizeReplayInvocation(target);
|
|
296
|
+
const sessionKey = this.options.subagentSessionKeyFactory?.({
|
|
297
|
+
runId: params.runId,
|
|
298
|
+
agentId: target.agentId
|
|
299
|
+
}) ?? defaultSubagentSessionKey(params.runId, target.agentId);
|
|
300
|
+
await this.callHooks((hook) => hook.beforeAgent?.({
|
|
301
|
+
runId: params.runId,
|
|
302
|
+
agentId: target.agentId,
|
|
303
|
+
label: invocation.label,
|
|
304
|
+
phaseId: target.phaseId,
|
|
305
|
+
prompt: invocation.prompt
|
|
306
|
+
}));
|
|
307
|
+
await params.appendEvent("agent_queued", {
|
|
308
|
+
agentId: target.agentId,
|
|
309
|
+
label: invocation.label,
|
|
310
|
+
phaseId: target.phaseId,
|
|
311
|
+
prompt: invocation.prompt,
|
|
312
|
+
sessionKey,
|
|
313
|
+
invocation
|
|
314
|
+
});
|
|
315
|
+
await params.appendEvent("agent_started", { agentId: target.agentId });
|
|
316
|
+
const recorder = new AgentProgressRecorder({
|
|
317
|
+
agentId: target.agentId,
|
|
318
|
+
appendEvent: params.appendEvent
|
|
319
|
+
});
|
|
320
|
+
params.progressRecorders.set(target.agentId, recorder);
|
|
321
|
+
try {
|
|
322
|
+
throwIfSignalAborted(params.signal);
|
|
323
|
+
const model = resolveReplayModel(invocation, this.options.resolveModelId);
|
|
324
|
+
const result = await this.options.runner.run(invocation.prompt, {
|
|
325
|
+
label: invocation.label,
|
|
326
|
+
schema: invocation.schema,
|
|
327
|
+
allowedToolNames: invocation.toolset,
|
|
328
|
+
maxIterations: invocation.maxIterations,
|
|
329
|
+
phase: invocation.phase ?? target.phaseTitle,
|
|
330
|
+
signal: params.signal,
|
|
331
|
+
model,
|
|
332
|
+
sessionKey,
|
|
333
|
+
sessionMetadata: {
|
|
334
|
+
parentSessionKey: this.options.parentSessionKey,
|
|
335
|
+
workflowRunId: params.runId,
|
|
336
|
+
workflowDefinitionId: params.definition.id,
|
|
337
|
+
workflowAgentId: target.agentId,
|
|
338
|
+
workflowAgentLabel: invocation.label
|
|
339
|
+
},
|
|
340
|
+
onProgress: (event) => recorder.onProgress(event)
|
|
341
|
+
});
|
|
342
|
+
throwIfSignalAborted(params.signal);
|
|
343
|
+
const status = result === null ? "error" : "done";
|
|
344
|
+
const resultPreview = previewWorkflowValue(result);
|
|
345
|
+
recorder.completeOpenSteps(status === "done" ? "done" : "error");
|
|
346
|
+
await params.appendEvent("agent_completed", {
|
|
347
|
+
agentId: target.agentId,
|
|
348
|
+
status,
|
|
349
|
+
resultPreview,
|
|
350
|
+
error: status === "error" ? "Replay target failed" : void 0
|
|
351
|
+
});
|
|
352
|
+
await this.callHooks((hook) => hook.afterAgent?.({
|
|
353
|
+
runId: params.runId,
|
|
354
|
+
agentId: target.agentId,
|
|
355
|
+
status,
|
|
356
|
+
resultPreview
|
|
357
|
+
}));
|
|
358
|
+
return {
|
|
359
|
+
agentId: target.agentId,
|
|
360
|
+
label: invocation.label,
|
|
361
|
+
phaseId: target.phaseId,
|
|
362
|
+
status,
|
|
363
|
+
resultPreview
|
|
364
|
+
};
|
|
365
|
+
} catch (err) {
|
|
366
|
+
if (params.signal?.aborted) throw err;
|
|
367
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
368
|
+
recorder.completeOpenSteps("error");
|
|
369
|
+
await params.appendEvent("agent_completed", {
|
|
370
|
+
agentId: target.agentId,
|
|
371
|
+
status: "error",
|
|
372
|
+
error: message
|
|
373
|
+
});
|
|
374
|
+
await this.callHooks((hook) => hook.afterAgent?.({
|
|
375
|
+
runId: params.runId,
|
|
376
|
+
agentId: target.agentId,
|
|
377
|
+
status: "error"
|
|
378
|
+
}));
|
|
379
|
+
return {
|
|
380
|
+
agentId: target.agentId,
|
|
381
|
+
label: invocation.label,
|
|
382
|
+
phaseId: target.phaseId,
|
|
383
|
+
status: "error",
|
|
384
|
+
error: message
|
|
385
|
+
};
|
|
386
|
+
} finally {
|
|
387
|
+
params.progressRecorders.delete(target.agentId);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
175
390
|
async callHooks(call) {
|
|
176
391
|
for (const hook of this.options.hooks ?? []) try {
|
|
177
392
|
await call(hook);
|
|
178
393
|
} catch {}
|
|
179
394
|
}
|
|
180
395
|
};
|
|
396
|
+
var WorkflowEngineRunError = class extends Error {
|
|
397
|
+
constructor(code, message, recoverable, detail) {
|
|
398
|
+
super(message);
|
|
399
|
+
this.code = code;
|
|
400
|
+
this.recoverable = recoverable;
|
|
401
|
+
this.detail = detail;
|
|
402
|
+
this.name = "WorkflowEngineRunError";
|
|
403
|
+
}
|
|
404
|
+
};
|
|
181
405
|
var AgentProgressRecorder = class {
|
|
182
406
|
activeToolStepIds = /* @__PURE__ */ new Map();
|
|
183
407
|
activeIterationStepId = null;
|
|
@@ -301,6 +525,80 @@ function buildPhaseTitleToId(definition) {
|
|
|
301
525
|
function normalizePhaseId(title) {
|
|
302
526
|
return title.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "phase";
|
|
303
527
|
}
|
|
528
|
+
function withWorkflowTimeout(promise, timeoutSec) {
|
|
529
|
+
if (typeof timeoutSec !== "number" || !Number.isFinite(timeoutSec) || timeoutSec <= 0) return promise;
|
|
530
|
+
let timeout;
|
|
531
|
+
const timeoutPromise = new Promise((_resolve, reject) => {
|
|
532
|
+
timeout = setTimeout(() => {
|
|
533
|
+
reject(/* @__PURE__ */ new Error(`workflow timed out after ${timeoutSec}s`));
|
|
534
|
+
}, timeoutSec * 1e3);
|
|
535
|
+
});
|
|
536
|
+
return Promise.race([promise, timeoutPromise]).finally(() => {
|
|
537
|
+
if (timeout) clearTimeout(timeout);
|
|
538
|
+
});
|
|
539
|
+
}
|
|
540
|
+
function throwIfSignalAborted(signal) {
|
|
541
|
+
if (!signal?.aborted) return;
|
|
542
|
+
const reason = signal.reason;
|
|
543
|
+
if (reason instanceof Error) throw reason;
|
|
544
|
+
if (reason) throw new Error(String(reason));
|
|
545
|
+
throw new Error("workflow aborted");
|
|
546
|
+
}
|
|
547
|
+
function groupReplayTargetsByPhase(targets) {
|
|
548
|
+
const groups = [];
|
|
549
|
+
for (const target of targets) {
|
|
550
|
+
const key = target.phaseId ?? "";
|
|
551
|
+
let group = groups.find((item) => item.key === key);
|
|
552
|
+
if (!group) {
|
|
553
|
+
group = {
|
|
554
|
+
key,
|
|
555
|
+
phaseId: target.phaseId,
|
|
556
|
+
phaseTitle: target.phaseTitle,
|
|
557
|
+
targets: []
|
|
558
|
+
};
|
|
559
|
+
groups.push(group);
|
|
560
|
+
}
|
|
561
|
+
group.targets.push(target);
|
|
562
|
+
}
|
|
563
|
+
return groups.map(({ key: _key, ...group }) => group);
|
|
564
|
+
}
|
|
565
|
+
function buildReplayResultEnvelope(options, results) {
|
|
566
|
+
return {
|
|
567
|
+
summary: `Replay completed for ${results.filter((item) => item.status === "done").length}/${results.length} target${results.length === 1 ? "" : "s"}.`,
|
|
568
|
+
sections: [{
|
|
569
|
+
kind: "json",
|
|
570
|
+
title: "Replay targets",
|
|
571
|
+
value: {
|
|
572
|
+
sourceRunId: options.sourceRunId,
|
|
573
|
+
scope: options.replayScope,
|
|
574
|
+
targets: results
|
|
575
|
+
}
|
|
576
|
+
}],
|
|
577
|
+
structuredOutput: { replay: {
|
|
578
|
+
sourceRunId: options.sourceRunId,
|
|
579
|
+
scope: options.replayScope,
|
|
580
|
+
targets: results
|
|
581
|
+
} }
|
|
582
|
+
};
|
|
583
|
+
}
|
|
584
|
+
function normalizeReplayInvocation(target) {
|
|
585
|
+
return {
|
|
586
|
+
prompt: target.invocation?.prompt ?? target.prompt,
|
|
587
|
+
label: target.invocation?.label ?? target.label,
|
|
588
|
+
phase: target.invocation?.phase ?? target.phaseTitle,
|
|
589
|
+
modelRef: target.invocation?.modelRef,
|
|
590
|
+
resolvedModelRef: target.invocation?.resolvedModelRef,
|
|
591
|
+
schema: target.invocation?.schema,
|
|
592
|
+
toolset: target.invocation?.toolset ? [...target.invocation.toolset] : void 0,
|
|
593
|
+
maxIterations: target.invocation?.maxIterations
|
|
594
|
+
};
|
|
595
|
+
}
|
|
596
|
+
function resolveReplayModel(invocation, resolveModelId) {
|
|
597
|
+
const modelRef = invocation.resolvedModelRef ?? invocation.modelRef;
|
|
598
|
+
if (!modelRef) return void 0;
|
|
599
|
+
if (!resolveModelId) throw new Error(`workflow replay missing resolveModelId; cannot resolve model '${modelRef}'`);
|
|
600
|
+
return resolveModelId(modelRef);
|
|
601
|
+
}
|
|
304
602
|
function formatRuntimeAgentId(id) {
|
|
305
603
|
return `agent-${id}`;
|
|
306
604
|
}
|
|
@@ -346,23 +644,34 @@ function toWorkflowResultEnvelope(value) {
|
|
|
346
644
|
raw: value
|
|
347
645
|
};
|
|
348
646
|
}
|
|
647
|
+
function assertWorkflowOutput(definition, value) {
|
|
648
|
+
const validation = validateWorkflowJsonSchema(definition.outputSchema, value);
|
|
649
|
+
if (validation.ok) return;
|
|
650
|
+
throw new WorkflowEngineRunError("result_validation_failed", validation.message ?? "Workflow result did not match output schema", false, JSON.stringify(validation.errors ?? []));
|
|
651
|
+
}
|
|
349
652
|
function isWorkflowResultEnvelope(value) {
|
|
350
653
|
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
351
654
|
const record = value;
|
|
352
655
|
return typeof record.summary === "string" && Array.isArray(record.sections);
|
|
353
656
|
}
|
|
354
|
-
function toWorkflowRunError(err, wasAborted) {
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
recoverable:
|
|
657
|
+
function toWorkflowRunError(err, wasAborted, abortReason) {
|
|
658
|
+
if (err instanceof WorkflowEngineRunError) return {
|
|
659
|
+
code: err.code,
|
|
660
|
+
message: err.message,
|
|
661
|
+
detail: err.detail,
|
|
662
|
+
recoverable: err.recoverable
|
|
360
663
|
};
|
|
361
|
-
|
|
664
|
+
const message = (abortReason instanceof Error ? abortReason.message : abortReason ? String(abortReason) : "") || (err instanceof Error ? err.message : String(err));
|
|
665
|
+
if (/timeout|timed out/i.test(message)) return {
|
|
362
666
|
code: "timeout",
|
|
363
667
|
message,
|
|
364
668
|
recoverable: true
|
|
365
669
|
};
|
|
670
|
+
if (wasAborted || /aborted|cancelled/i.test(message)) return {
|
|
671
|
+
code: "cancelled",
|
|
672
|
+
message: message || "Workflow run cancelled",
|
|
673
|
+
recoverable: true
|
|
674
|
+
};
|
|
366
675
|
if (/quota/i.test(message)) return {
|
|
367
676
|
code: "agent_quota_exceeded",
|
|
368
677
|
message,
|