@fkqfkq123/opencode-autopilot 0.1.0

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.
Files changed (107) hide show
  1. package/README.md +462 -0
  2. package/README.zh-CN.md +464 -0
  3. package/dist/packages/adapters/opencode/src/opencode-session-client.d.ts +188 -0
  4. package/dist/packages/adapters/opencode/src/opencode-session-client.js +382 -0
  5. package/dist/packages/core/src/artifacts/artifact-evaluator.d.ts +17 -0
  6. package/dist/packages/core/src/artifacts/artifact-evaluator.js +1 -0
  7. package/dist/packages/core/src/artifacts/artifact.d.ts +7 -0
  8. package/dist/packages/core/src/artifacts/artifact.js +1 -0
  9. package/dist/packages/core/src/human-actions/human-action-record.d.ts +10 -0
  10. package/dist/packages/core/src/human-actions/human-action-record.js +1 -0
  11. package/dist/packages/core/src/human-actions/human-action.d.ts +13 -0
  12. package/dist/packages/core/src/human-actions/human-action.js +1 -0
  13. package/dist/packages/core/src/human-actions/question.d.ts +8 -0
  14. package/dist/packages/core/src/human-actions/question.js +1 -0
  15. package/dist/packages/core/src/state/phase.d.ts +2 -0
  16. package/dist/packages/core/src/state/phase.js +1 -0
  17. package/dist/packages/core/src/state/workflow-runtime-state.d.ts +14 -0
  18. package/dist/packages/core/src/state/workflow-runtime-state.js +1 -0
  19. package/dist/packages/core/src/state/workflow-state.d.ts +13 -0
  20. package/dist/packages/core/src/state/workflow-state.js +1 -0
  21. package/dist/packages/core/src/transitions/default-phase-transition.d.ts +5 -0
  22. package/dist/packages/core/src/transitions/default-phase-transition.js +195 -0
  23. package/dist/packages/core/src/transitions/phase-transition.d.ts +22 -0
  24. package/dist/packages/core/src/transitions/phase-transition.js +1 -0
  25. package/dist/packages/core/src/transitions/transition-action.d.ts +20 -0
  26. package/dist/packages/core/src/transitions/transition-action.js +1 -0
  27. package/dist/packages/runtime/src/artifacts/file-system-artifact-evaluator.d.ts +36 -0
  28. package/dist/packages/runtime/src/artifacts/file-system-artifact-evaluator.js +1213 -0
  29. package/dist/packages/runtime/src/attach/attach-service.d.ts +15 -0
  30. package/dist/packages/runtime/src/attach/attach-service.js +31 -0
  31. package/dist/packages/runtime/src/bootstrap/create-harness.d.ts +33 -0
  32. package/dist/packages/runtime/src/bootstrap/create-harness.js +79 -0
  33. package/dist/packages/runtime/src/bootstrap/initialize-workflow.d.ts +8 -0
  34. package/dist/packages/runtime/src/bootstrap/initialize-workflow.js +33 -0
  35. package/dist/packages/runtime/src/commands/create-opencode-workflow-commands.d.ts +12 -0
  36. package/dist/packages/runtime/src/commands/create-opencode-workflow-commands.js +24 -0
  37. package/dist/packages/runtime/src/commands/default-workflow-command-runner.d.ts +4 -0
  38. package/dist/packages/runtime/src/commands/default-workflow-command-runner.js +343 -0
  39. package/dist/packages/runtime/src/commands/opencode-plugin-command-adapter.d.ts +20 -0
  40. package/dist/packages/runtime/src/commands/opencode-plugin-command-adapter.js +22 -0
  41. package/dist/packages/runtime/src/commands/workflow-command-runner.d.ts +19 -0
  42. package/dist/packages/runtime/src/commands/workflow-command-runner.js +1 -0
  43. package/dist/packages/runtime/src/commands/workflow-open-request.d.ts +10 -0
  44. package/dist/packages/runtime/src/commands/workflow-open-request.js +220 -0
  45. package/dist/packages/runtime/src/config/skill-registry.d.ts +15 -0
  46. package/dist/packages/runtime/src/config/skill-registry.js +108 -0
  47. package/dist/packages/runtime/src/config/workflow-config.d.ts +17 -0
  48. package/dist/packages/runtime/src/config/workflow-config.js +51 -0
  49. package/dist/packages/runtime/src/diagnostics/workflow-diagnostics-format.d.ts +4 -0
  50. package/dist/packages/runtime/src/diagnostics/workflow-diagnostics-format.js +70 -0
  51. package/dist/packages/runtime/src/diagnostics/workflow-doctor.d.ts +23 -0
  52. package/dist/packages/runtime/src/diagnostics/workflow-doctor.js +120 -0
  53. package/dist/packages/runtime/src/engine/default-workflow-engine.d.ts +9 -0
  54. package/dist/packages/runtime/src/engine/default-workflow-engine.js +337 -0
  55. package/dist/packages/runtime/src/engine/workflow-engine.d.ts +28 -0
  56. package/dist/packages/runtime/src/engine/workflow-engine.js +1 -0
  57. package/dist/packages/runtime/src/events/file-system-workflow-event-store.d.ts +8 -0
  58. package/dist/packages/runtime/src/events/file-system-workflow-event-store.js +28 -0
  59. package/dist/packages/runtime/src/events/workflow-event-store.d.ts +10 -0
  60. package/dist/packages/runtime/src/events/workflow-event-store.js +1 -0
  61. package/dist/packages/runtime/src/index.d.ts +4 -0
  62. package/dist/packages/runtime/src/index.js +4 -0
  63. package/dist/packages/runtime/src/install/workflow-installer.d.ts +15 -0
  64. package/dist/packages/runtime/src/install/workflow-installer.js +111 -0
  65. package/dist/packages/runtime/src/plugin/workflow-plugin-entry.d.ts +167 -0
  66. package/dist/packages/runtime/src/plugin/workflow-plugin-entry.js +340 -0
  67. package/dist/packages/runtime/src/presentation/human-action-renderer.d.ts +13 -0
  68. package/dist/packages/runtime/src/presentation/human-action-renderer.js +161 -0
  69. package/dist/packages/runtime/src/presentation/watch-renderer.d.ts +12 -0
  70. package/dist/packages/runtime/src/presentation/watch-renderer.js +17 -0
  71. package/dist/packages/runtime/src/recovery/basic-recovery-classifier.d.ts +4 -0
  72. package/dist/packages/runtime/src/recovery/basic-recovery-classifier.js +12 -0
  73. package/dist/packages/runtime/src/recovery/recovery-classifier.d.ts +4 -0
  74. package/dist/packages/runtime/src/recovery/recovery-classifier.js +1 -0
  75. package/dist/packages/runtime/src/scheduling/immediate-tick-scheduler.d.ts +9 -0
  76. package/dist/packages/runtime/src/scheduling/immediate-tick-scheduler.js +28 -0
  77. package/dist/packages/runtime/src/scheduling/tick-scheduler.d.ts +3 -0
  78. package/dist/packages/runtime/src/scheduling/tick-scheduler.js +1 -0
  79. package/dist/packages/runtime/src/sessions/file-system-session-coordinator.d.ts +19 -0
  80. package/dist/packages/runtime/src/sessions/file-system-session-coordinator.js +132 -0
  81. package/dist/packages/runtime/src/sessions/session-activity-monitor.d.ts +22 -0
  82. package/dist/packages/runtime/src/sessions/session-activity-monitor.js +112 -0
  83. package/dist/packages/runtime/src/sessions/session-coordinator.d.ts +24 -0
  84. package/dist/packages/runtime/src/sessions/session-coordinator.js +1 -0
  85. package/dist/packages/runtime/src/shared/json-file.d.ts +2 -0
  86. package/dist/packages/runtime/src/shared/json-file.js +19 -0
  87. package/dist/packages/runtime/src/state/file-system-human-action-store.d.ts +15 -0
  88. package/dist/packages/runtime/src/state/file-system-human-action-store.js +69 -0
  89. package/dist/packages/runtime/src/state/file-system-workflow-state-store.d.ts +15 -0
  90. package/dist/packages/runtime/src/state/file-system-workflow-state-store.js +59 -0
  91. package/dist/packages/runtime/src/state/human-action-service.d.ts +21 -0
  92. package/dist/packages/runtime/src/state/human-action-service.js +80 -0
  93. package/dist/packages/runtime/src/state/human-action-store.d.ts +9 -0
  94. package/dist/packages/runtime/src/state/human-action-store.js +1 -0
  95. package/dist/packages/runtime/src/state/workflow-state-store.d.ts +11 -0
  96. package/dist/packages/runtime/src/state/workflow-state-store.js +1 -0
  97. package/dist/packages/runtime/src/subtasks/noop-subtask-tracker.d.ts +4 -0
  98. package/dist/packages/runtime/src/subtasks/noop-subtask-tracker.js +5 -0
  99. package/dist/packages/runtime/src/subtasks/subtask-tracker.d.ts +3 -0
  100. package/dist/packages/runtime/src/subtasks/subtask-tracker.js +1 -0
  101. package/dist/packages/runtime/src/workspace/workflow-workspace.d.ts +31 -0
  102. package/dist/packages/runtime/src/workspace/workflow-workspace.js +43 -0
  103. package/dist/plugin.d.ts +1 -0
  104. package/dist/plugin.js +1 -0
  105. package/dist/src/cli.d.ts +1 -0
  106. package/dist/src/cli.js +175 -0
  107. package/package.json +56 -0
@@ -0,0 +1,15 @@
1
+ import type { WorkflowEventStore } from "../events/workflow-event-store";
2
+ import type { TickScheduler } from "../scheduling/tick-scheduler";
3
+ import type { SessionActivityMonitor } from "../sessions/session-activity-monitor";
4
+ import type { WorkflowStateStore } from "../state/workflow-state-store";
5
+ export interface AttachService {
6
+ attach(workflowId: string): Promise<void>;
7
+ }
8
+ export declare class DefaultAttachService implements AttachService {
9
+ private readonly stateStore;
10
+ private readonly sessionActivityMonitor;
11
+ private readonly tickScheduler;
12
+ private readonly eventStore;
13
+ constructor(stateStore: WorkflowStateStore, sessionActivityMonitor: SessionActivityMonitor, tickScheduler: TickScheduler, eventStore: WorkflowEventStore);
14
+ attach(workflowId: string): Promise<void>;
15
+ }
@@ -0,0 +1,31 @@
1
+ export class DefaultAttachService {
2
+ stateStore;
3
+ sessionActivityMonitor;
4
+ tickScheduler;
5
+ eventStore;
6
+ constructor(stateStore, sessionActivityMonitor, tickScheduler, eventStore) {
7
+ this.stateStore = stateStore;
8
+ this.sessionActivityMonitor = sessionActivityMonitor;
9
+ this.tickScheduler = tickScheduler;
10
+ this.eventStore = eventStore;
11
+ }
12
+ async attach(workflowId) {
13
+ const workflow = await this.stateStore.getWorkflow(workflowId);
14
+ if (!workflow) {
15
+ throw new Error(`Workflow not found: ${workflowId}`);
16
+ }
17
+ await this.sessionActivityMonitor.start(workflowId);
18
+ await this.eventStore.append({
19
+ workflowId,
20
+ type: "workflow.attached",
21
+ at: new Date().toISOString(),
22
+ payload: {
23
+ phase: workflow.phase,
24
+ status: workflow.status,
25
+ },
26
+ });
27
+ if (workflow.phase !== "done" && workflow.phase !== "blocked") {
28
+ await this.tickScheduler.requestTick(workflowId, "workflow attached");
29
+ }
30
+ }
31
+ }
@@ -0,0 +1,33 @@
1
+ import { type OpencodeSessionClient } from "../../../adapters/opencode/src/opencode-session-client";
2
+ import { FileSystemArtifactEvaluator } from "../artifacts/file-system-artifact-evaluator";
3
+ import { DefaultAttachService } from "../attach/attach-service";
4
+ import { DefaultWorkflowEngine } from "../engine/default-workflow-engine";
5
+ import { FileSystemWorkflowEventStore } from "../events/file-system-workflow-event-store";
6
+ import { ImmediateTickScheduler } from "../scheduling/immediate-tick-scheduler";
7
+ import { DefaultSessionActivityMonitor } from "../sessions/session-activity-monitor";
8
+ import { FileSystemSessionCoordinator } from "../sessions/file-system-session-coordinator";
9
+ import { FileSystemHumanActionStore } from "../state/file-system-human-action-store";
10
+ import { FileSystemWorkflowStateStore } from "../state/file-system-workflow-state-store";
11
+ import { DefaultHumanActionService } from "../state/human-action-service";
12
+ import { DefaultWorkflowWorkspace } from "../workspace/workflow-workspace";
13
+ export interface CreateHarnessOptions {
14
+ sessionClient?: OpencodeSessionClient;
15
+ opencodeBaseUrl?: string;
16
+ opencodePassword?: string;
17
+ }
18
+ export declare function createHarness(baseDir: string, options?: CreateHarnessOptions): Promise<{
19
+ stateStore: FileSystemWorkflowStateStore;
20
+ humanActionStore: FileSystemHumanActionStore;
21
+ artifactEvaluator: FileSystemArtifactEvaluator;
22
+ eventStore: FileSystemWorkflowEventStore;
23
+ sessionCoordinator: FileSystemSessionCoordinator;
24
+ tickScheduler: ImmediateTickScheduler;
25
+ engine: DefaultWorkflowEngine;
26
+ humanActionService: DefaultHumanActionService;
27
+ attachService: DefaultAttachService;
28
+ sessionActivityMonitor: DefaultSessionActivityMonitor;
29
+ sessionClient: OpencodeSessionClient;
30
+ workspace: DefaultWorkflowWorkspace;
31
+ resolvedConfig: import("../config/workflow-config").ResolvedWorkflowConfig;
32
+ skillRegistry: Map<string, string>;
33
+ }>;
@@ -0,0 +1,79 @@
1
+ import { mkdir } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ import { HttpOpencodeSessionClient, InMemoryOpencodeSessionClient, } from "../../../adapters/opencode/src/opencode-session-client";
4
+ import { DefaultPhaseTransition } from "../../../core/src/transitions/default-phase-transition";
5
+ import { FileSystemArtifactEvaluator } from "../artifacts/file-system-artifact-evaluator";
6
+ import { DefaultAttachService } from "../attach/attach-service";
7
+ import { buildSkillRegistryWithWarnings } from "../config/skill-registry";
8
+ import { resolveWorkflowConfig } from "../config/workflow-config";
9
+ import { DefaultWorkflowEngine } from "../engine/default-workflow-engine";
10
+ import { FileSystemWorkflowEventStore } from "../events/file-system-workflow-event-store";
11
+ import { BasicRecoveryClassifier } from "../recovery/basic-recovery-classifier";
12
+ import { ImmediateTickScheduler } from "../scheduling/immediate-tick-scheduler";
13
+ import { DefaultSessionActivityMonitor } from "../sessions/session-activity-monitor";
14
+ import { FileSystemSessionCoordinator } from "../sessions/file-system-session-coordinator";
15
+ import { FileSystemHumanActionStore } from "../state/file-system-human-action-store";
16
+ import { FileSystemWorkflowStateStore } from "../state/file-system-workflow-state-store";
17
+ import { DefaultHumanActionService } from "../state/human-action-service";
18
+ import { NoopSubtaskTracker } from "../subtasks/noop-subtask-tracker";
19
+ import { DefaultWorkflowWorkspace } from "../workspace/workflow-workspace";
20
+ export async function createHarness(baseDir, options = {}) {
21
+ await mkdir(join(baseDir, "workflows"), { recursive: true });
22
+ const workspace = new DefaultWorkflowWorkspace(baseDir);
23
+ const resolvedConfig = await resolveWorkflowConfig({
24
+ projectConfigFile: workspace.workflowConfigFile(),
25
+ });
26
+ const skillRegistryResult = await buildSkillRegistryWithWarnings(resolvedConfig.skillRoots);
27
+ const skillRegistry = skillRegistryResult.registry;
28
+ resolvedConfig.warnings.push(...skillRegistryResult.warnings);
29
+ const httpOptions = options.opencodeBaseUrl
30
+ ? {
31
+ baseUrl: options.opencodeBaseUrl,
32
+ ...(options.opencodePassword ? { password: options.opencodePassword } : {}),
33
+ }
34
+ : null;
35
+ const sessionClient = options.sessionClient
36
+ ?? (httpOptions
37
+ ? new HttpOpencodeSessionClient(httpOptions)
38
+ : new InMemoryOpencodeSessionClient());
39
+ const stateStore = new FileSystemWorkflowStateStore(workspace);
40
+ const humanActionStore = new FileSystemHumanActionStore(workspace);
41
+ const artifactEvaluator = new FileSystemArtifactEvaluator(workspace);
42
+ const eventStore = new FileSystemWorkflowEventStore(workspace);
43
+ const sessionCoordinator = new FileSystemSessionCoordinator(workspace, sessionClient);
44
+ const tickScheduler = new ImmediateTickScheduler();
45
+ const engine = new DefaultWorkflowEngine({
46
+ stateStore,
47
+ humanActionStore,
48
+ artifactEvaluator,
49
+ phaseTransition: new DefaultPhaseTransition(),
50
+ sessionCoordinator,
51
+ recoveryClassifier: new BasicRecoveryClassifier(),
52
+ subtaskTracker: new NoopSubtaskTracker(),
53
+ tickScheduler,
54
+ eventStore,
55
+ workspace,
56
+ resolvedConfig,
57
+ skillRegistry,
58
+ });
59
+ tickScheduler.setHandler((workflowId) => engine.tick(workflowId));
60
+ const sessionActivityMonitor = new DefaultSessionActivityMonitor(baseDir, stateStore, sessionCoordinator, tickScheduler);
61
+ const humanActionService = new DefaultHumanActionService(humanActionStore, stateStore, artifactEvaluator, tickScheduler, eventStore);
62
+ const attachService = new DefaultAttachService(stateStore, sessionActivityMonitor, tickScheduler, eventStore);
63
+ return {
64
+ stateStore,
65
+ humanActionStore,
66
+ artifactEvaluator,
67
+ eventStore,
68
+ sessionCoordinator,
69
+ tickScheduler,
70
+ engine,
71
+ humanActionService,
72
+ attachService,
73
+ sessionActivityMonitor,
74
+ sessionClient,
75
+ workspace,
76
+ resolvedConfig,
77
+ skillRegistry,
78
+ };
79
+ }
@@ -0,0 +1,8 @@
1
+ import type { FileSystemArtifactEvaluator } from "../artifacts/file-system-artifact-evaluator";
2
+ import type { WorkflowStateStore } from "../state/workflow-state-store";
3
+ export declare function initializeWorkflow(args: {
4
+ workflowId: string;
5
+ stateStore: WorkflowStateStore;
6
+ artifactEvaluator: FileSystemArtifactEvaluator;
7
+ userRequest?: string;
8
+ }): Promise<void>;
@@ -0,0 +1,33 @@
1
+ export async function initializeWorkflow(args) {
2
+ const { workflowId, stateStore, artifactEvaluator, userRequest } = args;
3
+ const existing = await stateStore.getWorkflow(workflowId);
4
+ if (existing) {
5
+ return;
6
+ }
7
+ const now = new Date().toISOString();
8
+ const workflow = {
9
+ workflowId,
10
+ phase: "spec_refinement",
11
+ status: "pending",
12
+ approved: false,
13
+ iteration: 0,
14
+ maxIterations: 3,
15
+ blockReason: null,
16
+ activeSessionId: null,
17
+ phaseEnteredAt: now,
18
+ updatedAt: now,
19
+ };
20
+ const runtime = {
21
+ workflowId,
22
+ preferredForegroundSessionId: null,
23
+ recoveryState: "idle",
24
+ waitingHumanActionId: null,
25
+ consecutiveFailures: 0,
26
+ refinementAttempts: 0,
27
+ refinementLastDispatchSummary: null,
28
+ refinementEscalationReason: null,
29
+ };
30
+ await stateStore.saveWorkflow(workflow);
31
+ await stateStore.saveRuntime(runtime);
32
+ await artifactEvaluator.ensureDefault(workflowId, userRequest);
33
+ }
@@ -0,0 +1,12 @@
1
+ import type { WorkflowPluginCommandAdapter } from "./opencode-plugin-command-adapter";
2
+ import type { WorkflowChannelCommand } from "./workflow-command-runner";
3
+ export interface WorkflowPluginCommandDefinition {
4
+ name: WorkflowChannelCommand;
5
+ description: string;
6
+ execute(args: {
7
+ workflowId: string;
8
+ payload?: string;
9
+ foregroundSessionId?: string;
10
+ }): Promise<string>;
11
+ }
12
+ export declare function createOpencodeWorkflowCommands(adapter: WorkflowPluginCommandAdapter): WorkflowPluginCommandDefinition[];
@@ -0,0 +1,24 @@
1
+ const COMMAND_DESCRIPTIONS = {
2
+ "workflow-open": "Open or initialize a workflow channel and attach to it.",
3
+ "workflow-attach": "Attach to an existing workflow channel.",
4
+ "workflow-status": "Render the current workflow status block.",
5
+ "workflow-answer": "Answer workflow clarification questions.",
6
+ "workflow-approve": "Approve the current workflow plan or decision.",
7
+ "workflow-resume": "Resume a blocked workflow.",
8
+ "workflow-back": "Leave the workflow channel without stopping the workflow.",
9
+ };
10
+ export function createOpencodeWorkflowCommands(adapter) {
11
+ return Object.keys(COMMAND_DESCRIPTIONS).map((name) => ({
12
+ name,
13
+ description: COMMAND_DESCRIPTIONS[name],
14
+ async execute(args) {
15
+ const response = await adapter.execute({
16
+ command: name,
17
+ workflowId: args.workflowId,
18
+ ...(args.payload !== undefined ? { payload: args.payload } : {}),
19
+ ...(args.foregroundSessionId ? { foregroundSessionId: args.foregroundSessionId } : {}),
20
+ });
21
+ return response.text;
22
+ },
23
+ }));
24
+ }
@@ -0,0 +1,4 @@
1
+ import type { WorkflowCommandResult, WorkflowCommandRunner } from "./workflow-command-runner";
2
+ export declare class DefaultWorkflowCommandRunner implements WorkflowCommandRunner {
3
+ run(args: Parameters<WorkflowCommandRunner["run"]>[0]): Promise<WorkflowCommandResult>;
4
+ }
@@ -0,0 +1,343 @@
1
+ import { initializeWorkflow } from "../bootstrap/initialize-workflow";
2
+ import { renderHumanActionBlock } from "../presentation/human-action-renderer";
3
+ import { buildWorkflowOpenRequest } from "./workflow-open-request";
4
+ import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
5
+ import { join } from "node:path";
6
+ function extractSection(content, heading) {
7
+ const start = content.indexOf(heading);
8
+ if (start === -1) {
9
+ return "";
10
+ }
11
+ const afterHeading = start + heading.length;
12
+ const nextHeading = content.slice(afterHeading).match(/\n##\s+/);
13
+ const end = nextHeading && typeof nextHeading.index === "number"
14
+ ? afterHeading + nextHeading.index
15
+ : content.length;
16
+ return content.slice(afterHeading, end).trim();
17
+ }
18
+ async function buildStatusEnhancements(args) {
19
+ const { harness, workflow, events } = args;
20
+ const evaluation = await harness.artifactEvaluator.evaluate(workflow);
21
+ const artifactContent = await readFile(harness.workspace.phaseArtifactFile(workflow.workflowId, workflow.phase), "utf8").catch(() => "");
22
+ const lines = [];
23
+ const relevantSession = await harness.sessionCoordinator.getRelevantSession(workflow.workflowId);
24
+ const storedSession = relevantSession.sessionId
25
+ ? await harness.sessionCoordinator.getStoredSession(workflow.workflowId, relevantSession.sessionId)
26
+ : null;
27
+ if (evaluation.summary) {
28
+ lines.push(`Phase summary: ${evaluation.summary}`);
29
+ }
30
+ if (storedSession?.sessionId) {
31
+ lines.push(`Worker session: ${storedSession.sessionId}`);
32
+ lines.push(`Worker session phase: ${storedSession.phase}`);
33
+ lines.push(`Worker session status: ${relevantSession.status}`);
34
+ lines.push(`Execution mode: ${storedSession.isForegroundPreferred ? "foreground session reuse" : "detached background workflow session"}`);
35
+ }
36
+ if (storedSession?.lastDispatchMode) {
37
+ lines.push(`Dispatch mode: ${storedSession.lastDispatchMode}`);
38
+ }
39
+ if (storedSession?.lastStatusBeforeDispatch) {
40
+ lines.push(`Status before dispatch: ${storedSession.lastStatusBeforeDispatch}`);
41
+ }
42
+ if (evaluation.missing.length > 0) {
43
+ lines.push(`Missing signals: ${evaluation.missing.join(" | ")}`);
44
+ }
45
+ if (workflow.phase === "develop") {
46
+ const changedFiles = extractSection(artifactContent, "## 修改文件");
47
+ const selfCheck = extractSection(artifactContent, "## 自检结果");
48
+ if (changedFiles) {
49
+ lines.push(`Code changes: ${changedFiles}`);
50
+ }
51
+ if (selfCheck) {
52
+ lines.push(`Self-check: ${selfCheck}`);
53
+ }
54
+ }
55
+ if (workflow.phase === "review") {
56
+ const scope = extractSection(artifactContent, "## 检查范围");
57
+ const issues = extractSection(artifactContent, "## 发现的问题");
58
+ const regression = extractSection(artifactContent, "## Regression 风险评估");
59
+ if (scope) {
60
+ lines.push(`Review scope: ${scope}`);
61
+ }
62
+ if (issues) {
63
+ lines.push(`Findings: ${issues}`);
64
+ }
65
+ if (regression) {
66
+ lines.push(`Regression risk: ${regression}`);
67
+ }
68
+ if (workflow.status === "in_progress" && evaluation.reportStatus === "unknown") {
69
+ lines.push("Waiting reason: review artifact has not produced a final pass/fail conclusion yet.");
70
+ }
71
+ }
72
+ if (workflow.phase === "test") {
73
+ const strategy = extractSection(artifactContent, "## 测试策略");
74
+ const failures = extractSection(artifactContent, "## 失败项");
75
+ const regression = extractSection(artifactContent, "## Regression 验证");
76
+ if (strategy) {
77
+ lines.push(`Test strategy: ${strategy}`);
78
+ }
79
+ if (failures) {
80
+ lines.push(`Failures: ${failures}`);
81
+ }
82
+ if (regression) {
83
+ lines.push(`Regression verification: ${regression}`);
84
+ }
85
+ if (workflow.status === "in_progress" && evaluation.reportStatus === "unknown") {
86
+ lines.push("Waiting reason: test artifact has not produced a final pass/fail conclusion yet.");
87
+ }
88
+ }
89
+ if (workflow.phase === "plan") {
90
+ const summary = extractSection(artifactContent, "## 需求摘要");
91
+ const impact = extractSection(artifactContent, "## 影响范围");
92
+ const coreFiles = extractSection(artifactContent, "## 核心修改文件");
93
+ const risk = extractSection(artifactContent, "## 风险评估");
94
+ if (summary) {
95
+ lines.push(`Plan summary: ${summary}`);
96
+ }
97
+ if (impact) {
98
+ lines.push(`Impact scope: ${impact}`);
99
+ }
100
+ if (coreFiles) {
101
+ lines.push(`Core files: ${coreFiles}`);
102
+ }
103
+ if (risk) {
104
+ lines.push(`Plan risks: ${risk}`);
105
+ }
106
+ if (workflow.status === "waiting_human") {
107
+ lines.push("Approval preview: review the plan summary, impacted scope, core files, and risks before approving.");
108
+ }
109
+ }
110
+ if (workflow.phase === "done" || workflow.status === "completed") {
111
+ lines.push("Workflow completed: all workflow phases reached terminal success state.");
112
+ if (evaluation.summary) {
113
+ lines.push(`Completion summary: ${evaluation.summary}`);
114
+ }
115
+ }
116
+ const lastPhaseChange = [...events].reverse().find((event) => event.type === "phase.changed");
117
+ if (lastPhaseChange?.payload?.from && lastPhaseChange.payload?.to) {
118
+ lines.push(`Last transition: ${String(lastPhaseChange.payload.from)} -> ${String(lastPhaseChange.payload.to)}`);
119
+ }
120
+ const recentEvents = events.slice(-3);
121
+ if (recentEvents.length > 0) {
122
+ lines.push(`Recent events: ${recentEvents.map((event) => event.type).join(" -> ")}`);
123
+ }
124
+ return lines;
125
+ }
126
+ function clarificationFilePath(harness, workflowId) {
127
+ return join(harness.workspace.workflowsRoot(), "..", "clarifications", `${workflowId}.json`);
128
+ }
129
+ async function renderClarificationIfAny(harness, workflowId) {
130
+ const pending = await readPendingClarification(harness, workflowId);
131
+ if (!pending) {
132
+ return null;
133
+ }
134
+ return {
135
+ ok: true,
136
+ output: [
137
+ pending.prompt,
138
+ ...pending.options,
139
+ ].join("\n"),
140
+ events: [],
141
+ };
142
+ }
143
+ async function savePendingClarification(harness, workflowId, payload) {
144
+ const filePath = clarificationFilePath(harness, workflowId);
145
+ await mkdir(join(harness.workspace.workflowsRoot(), "..", "clarifications"), { recursive: true });
146
+ await writeFile(filePath, `${JSON.stringify(payload, null, 2)}\n`, "utf8");
147
+ }
148
+ async function readPendingClarification(harness, workflowId) {
149
+ try {
150
+ return JSON.parse(await readFile(clarificationFilePath(harness, workflowId), "utf8"));
151
+ }
152
+ catch {
153
+ return null;
154
+ }
155
+ }
156
+ async function clearPendingClarification(harness, workflowId) {
157
+ try {
158
+ await rm(clarificationFilePath(harness, workflowId));
159
+ }
160
+ catch {
161
+ return;
162
+ }
163
+ }
164
+ function buildIntentPromptFromChoice(choice, rawPayload) {
165
+ const normalized = choice.trim();
166
+ if (normalized.includes("启动") || normalized.includes("workflow") || normalized.includes("开始")) {
167
+ return `请直接启动完整 workflow。
168
+ ${rawPayload}`;
169
+ }
170
+ if (normalized.includes("分析") || normalized.includes("提炼")) {
171
+ return `请先分析文档并提炼需求。
172
+ ${rawPayload}`;
173
+ }
174
+ if (normalized.includes("补全") || normalized.includes("完善")) {
175
+ return `请先补全文档缺失内容,再决定是否启动 workflow。
176
+ ${rawPayload}`;
177
+ }
178
+ if (normalized.includes("总结") || normalized.includes("查看")) {
179
+ return `请只查看并总结文档。
180
+ ${rawPayload}`;
181
+ }
182
+ if (normalized.startsWith("1")) {
183
+ return `请直接启动完整 workflow。
184
+ ${rawPayload}`;
185
+ }
186
+ if (normalized.startsWith("2")) {
187
+ return `请先分析文档并提炼需求。
188
+ ${rawPayload}`;
189
+ }
190
+ if (normalized.startsWith("3")) {
191
+ return `请先补全文档缺失内容,再决定是否启动 workflow。
192
+ ${rawPayload}`;
193
+ }
194
+ return `请只查看并总结文档。
195
+ ${rawPayload}`;
196
+ }
197
+ export class DefaultWorkflowCommandRunner {
198
+ async run(args) {
199
+ const { harness, command, workflowId, payload, foregroundSessionId } = args;
200
+ if (command === "workflow-open") {
201
+ const openRequest = await buildWorkflowOpenRequest(payload, process.cwd());
202
+ if (openRequest.needsClarification) {
203
+ await savePendingClarification(harness, workflowId, {
204
+ rawPayload: payload ?? "",
205
+ prompt: openRequest.clarificationQuestion ?? "我需要先确认你的意图。",
206
+ options: openRequest.clarificationOptions ?? [],
207
+ });
208
+ const outputLines = [openRequest.clarificationQuestion ?? "我需要先确认你的意图。"];
209
+ if (openRequest.clarificationOptions && openRequest.clarificationOptions.length > 0) {
210
+ outputLines.push(...openRequest.clarificationOptions);
211
+ }
212
+ return {
213
+ ok: true,
214
+ output: outputLines.join("\n"),
215
+ events: [],
216
+ };
217
+ }
218
+ await initializeWorkflow({
219
+ workflowId,
220
+ stateStore: harness.stateStore,
221
+ artifactEvaluator: harness.artifactEvaluator,
222
+ userRequest: openRequest.userRequest,
223
+ });
224
+ if (foregroundSessionId) {
225
+ await harness.stateStore.updateRuntime(workflowId, {
226
+ preferredForegroundSessionId: foregroundSessionId,
227
+ });
228
+ }
229
+ await harness.attachService.attach(workflowId);
230
+ }
231
+ else if (command === "workflow-attach") {
232
+ const clarification = await renderClarificationIfAny(harness, workflowId);
233
+ if (clarification) {
234
+ return clarification;
235
+ }
236
+ if (foregroundSessionId) {
237
+ await harness.stateStore.updateRuntime(workflowId, {
238
+ preferredForegroundSessionId: foregroundSessionId,
239
+ });
240
+ }
241
+ await harness.attachService.attach(workflowId);
242
+ }
243
+ else if (command === "workflow-answer") {
244
+ let answers = null;
245
+ if (payload) {
246
+ try {
247
+ const parsed = JSON.parse(payload);
248
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
249
+ answers = parsed;
250
+ }
251
+ }
252
+ catch {
253
+ answers = null;
254
+ }
255
+ }
256
+ if (answers) {
257
+ await harness.humanActionService.answer(workflowId, answers);
258
+ }
259
+ else {
260
+ const pending = await readPendingClarification(harness, workflowId);
261
+ if (!pending) {
262
+ throw new Error("workflow_answer received non-JSON payload but no pending clarification was found");
263
+ }
264
+ const intentPrompt = buildIntentPromptFromChoice(payload ?? "", pending.rawPayload);
265
+ await clearPendingClarification(harness, workflowId);
266
+ await initializeWorkflow({
267
+ workflowId,
268
+ stateStore: harness.stateStore,
269
+ artifactEvaluator: harness.artifactEvaluator,
270
+ userRequest: intentPrompt,
271
+ });
272
+ if (foregroundSessionId) {
273
+ await harness.stateStore.updateRuntime(workflowId, {
274
+ preferredForegroundSessionId: foregroundSessionId,
275
+ });
276
+ }
277
+ await harness.attachService.attach(workflowId);
278
+ }
279
+ }
280
+ else if (command === "workflow-approve") {
281
+ if (foregroundSessionId) {
282
+ await harness.stateStore.updateRuntime(workflowId, {
283
+ preferredForegroundSessionId: foregroundSessionId,
284
+ });
285
+ }
286
+ await harness.humanActionService.approve(workflowId);
287
+ await harness.attachService.attach(workflowId);
288
+ }
289
+ else if (command === "workflow-resume") {
290
+ if (foregroundSessionId) {
291
+ await harness.stateStore.updateRuntime(workflowId, {
292
+ preferredForegroundSessionId: foregroundSessionId,
293
+ });
294
+ }
295
+ await harness.humanActionService.resume(workflowId);
296
+ await harness.attachService.attach(workflowId);
297
+ }
298
+ else if (command === "workflow-back") {
299
+ await harness.sessionActivityMonitor.stop(workflowId);
300
+ const events = await harness.eventStore.list(workflowId);
301
+ return {
302
+ ok: true,
303
+ output: `Returned from workflow channel for ${workflowId}. Your workflow continues and can be re-attached later.`,
304
+ events,
305
+ };
306
+ }
307
+ const workflow = await harness.stateStore.getWorkflow(workflowId);
308
+ const runtime = await harness.stateStore.getRuntime(workflowId);
309
+ const humanAction = await harness.humanActionStore.getCurrent(workflowId);
310
+ const events = await harness.eventStore.list(workflowId);
311
+ const clarification = await readPendingClarification(harness, workflowId);
312
+ if (!workflow) {
313
+ if (clarification) {
314
+ return {
315
+ ok: true,
316
+ output: [clarification.prompt, ...clarification.options].join("\n"),
317
+ events: [],
318
+ };
319
+ }
320
+ throw new Error(`Workflow not found: ${workflowId}`);
321
+ }
322
+ const enhancementLines = await buildStatusEnhancements({
323
+ harness,
324
+ workflow,
325
+ events,
326
+ });
327
+ return {
328
+ ok: true,
329
+ output: [
330
+ renderHumanActionBlock({
331
+ workflow,
332
+ runtime,
333
+ humanAction,
334
+ clarification: clarification
335
+ ? { prompt: clarification.prompt, options: clarification.options }
336
+ : null,
337
+ phaseDetails: enhancementLines,
338
+ }),
339
+ ].join("\n"),
340
+ events,
341
+ };
342
+ }
343
+ }
@@ -0,0 +1,20 @@
1
+ import type { WorkflowChannelCommand, WorkflowCommandRunner } from "./workflow-command-runner";
2
+ export interface WorkflowPluginCommandRequest {
3
+ command: WorkflowChannelCommand;
4
+ workflowId: string;
5
+ payload?: string;
6
+ foregroundSessionId?: string;
7
+ }
8
+ export interface WorkflowPluginCommandResponse {
9
+ ok: boolean;
10
+ text: string;
11
+ }
12
+ export interface WorkflowPluginCommandAdapter {
13
+ execute(request: WorkflowPluginCommandRequest): Promise<WorkflowPluginCommandResponse>;
14
+ }
15
+ export declare class DefaultWorkflowPluginCommandAdapter implements WorkflowPluginCommandAdapter {
16
+ private readonly runner;
17
+ private readonly harnessFactory;
18
+ constructor(runner: WorkflowCommandRunner, harnessFactory: () => Promise<Awaited<ReturnType<typeof import("../bootstrap/create-harness").createHarness>>>);
19
+ execute(request: WorkflowPluginCommandRequest): Promise<WorkflowPluginCommandResponse>;
20
+ }
@@ -0,0 +1,22 @@
1
+ export class DefaultWorkflowPluginCommandAdapter {
2
+ runner;
3
+ harnessFactory;
4
+ constructor(runner, harnessFactory) {
5
+ this.runner = runner;
6
+ this.harnessFactory = harnessFactory;
7
+ }
8
+ async execute(request) {
9
+ const harness = await this.harnessFactory();
10
+ const result = await this.runner.run({
11
+ harness,
12
+ command: request.command,
13
+ workflowId: request.workflowId,
14
+ ...(request.payload !== undefined ? { payload: request.payload } : {}),
15
+ ...(request.foregroundSessionId ? { foregroundSessionId: request.foregroundSessionId } : {}),
16
+ });
17
+ return {
18
+ ok: result.ok,
19
+ text: result.output,
20
+ };
21
+ }
22
+ }
@@ -0,0 +1,19 @@
1
+ import type { createHarness } from "../bootstrap/create-harness";
2
+ import type { WorkflowEventRecord } from "../events/workflow-event-store";
3
+ export type WorkflowChannelCommand = "workflow-open" | "workflow-attach" | "workflow-status" | "workflow-answer" | "workflow-approve" | "workflow-resume" | "workflow-back";
4
+ export interface WorkflowCommandResult {
5
+ ok: boolean;
6
+ output: string;
7
+ events: WorkflowEventRecord[];
8
+ }
9
+ type Harness = Awaited<ReturnType<typeof createHarness>>;
10
+ export interface WorkflowCommandRunner {
11
+ run(args: {
12
+ harness: Harness;
13
+ command: WorkflowChannelCommand;
14
+ workflowId: string;
15
+ payload?: string;
16
+ foregroundSessionId?: string;
17
+ }): Promise<WorkflowCommandResult>;
18
+ }
19
+ export {};
@@ -0,0 +1,10 @@
1
+ export interface WorkflowOpenRequest {
2
+ userRequest: string;
3
+ prompt: string;
4
+ docPaths: string[];
5
+ projectContext?: string;
6
+ needsClarification: boolean;
7
+ clarificationQuestion?: string;
8
+ clarificationOptions?: string[];
9
+ }
10
+ export declare function buildWorkflowOpenRequest(payload: string | undefined, workspaceRoot: string): Promise<WorkflowOpenRequest>;