vibe64 0.1.21 → 0.1.22

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 (143) hide show
  1. package/dist/assets/ProjectTypeGate-B3NHteXC.js +1 -0
  2. package/dist/assets/ProjectTypeGate-fJcYj6sR.css +1 -0
  3. package/dist/assets/{SetupReadinessGate-DkkorR2Y.js → SetupReadinessGate-2twHjoYZ.js} +1 -1
  4. package/dist/assets/{ShellLayout-BcKS2HG9.js → ShellLayout-BjAbXfzf.js} +1 -1
  5. package/dist/assets/{VTabs-BJ8MKoig.js → VTabs-BLGGdbms.js} +1 -1
  6. package/dist/assets/{error-CwT53qTu.js → error-DvUPciuh.js} +1 -1
  7. package/dist/assets/history-DfM4ewyV.js +1 -0
  8. package/dist/assets/home-sh4v0vz7.js +1 -0
  9. package/dist/assets/index-C4JbNnml.css +1 -0
  10. package/dist/assets/{index-BJxDQvyK.js → index-CdFWokfE.js} +1 -1
  11. package/dist/assets/index-D4oqjV9n.js +132 -0
  12. package/dist/assets/{index-CY-wDjNv.js → index-DqoYKexs.js} +15 -15
  13. package/dist/assets/{placement-7FCYadh2.js → placement-QfRaYTFY.js} +1 -1
  14. package/dist/assets/{setup-BzzdD38G.js → setup-B-WOti3H.js} +2 -2
  15. package/dist/assets/{streamEvents-Ba9kHw-1.js → streamEvents-CIqegoLa.js} +1 -1
  16. package/dist/assets/{studioGateApi-hkZdLr1Y.js → studioGateApi-CFtez6lH.js} +1 -1
  17. package/dist/assets/{target-scripts-8hczu84t.js → target-scripts-CI-nAISO.js} +1 -1
  18. package/dist/assets/useStudioTerminal-I8tuBymw.js +1 -0
  19. package/dist/assets/vibe64SessionPanelModel-DLdexNxC.js +1 -0
  20. package/dist/index.html +1 -1
  21. package/node_modules/@local/setup-doctor-core/src/server/doctorToolchain.js +4 -0
  22. package/node_modules/@local/studio-terminal-core/src/server/gitGithubTransport.js +33 -0
  23. package/node_modules/@local/studio-terminal-core/src/server/index.js +1 -0
  24. package/node_modules/@local/studio-terminal-core/src/server/terminalSessions.js +51 -1
  25. package/node_modules/@local/vibe64-adapters/src/server/adapter.js +4 -0
  26. package/node_modules/@local/vibe64-adapters/src/server/adapters/laravel/adapter.js +5 -0
  27. package/node_modules/@local/vibe64-adapters/src/server/adapters/laravel/databaseRuntime.js +50 -0
  28. package/node_modules/@local/vibe64-adapters/src/server/configStore.js +22 -1
  29. package/node_modules/@local/vibe64-adapters/src/server/systemPrompts/agent_conversation.txt +0 -2
  30. package/node_modules/@local/vibe64-adapters/src/server/systemPrompts/draft_issue.txt +18 -0
  31. package/node_modules/@local/vibe64-adapters/src/server/systemPrompts/make_plan.txt +14 -2
  32. package/node_modules/@local/vibe64-adapters/src/server/systemPrompts/prepare_for_merge.txt +2 -0
  33. package/node_modules/@local/vibe64-adapters/src/server/systemPrompts/resolve_pull_request.txt +1 -0
  34. package/node_modules/@local/vibe64-adapters/src/server/systemPrompts/run_deep_ui_check.txt +7 -3
  35. package/node_modules/@local/vibe64-adapters/src/server/workflowCommandTerminal/commitPush.js +4 -22
  36. package/node_modules/@local/vibe64-adapters/src/server/workflowCommandTerminal/factMetadata.js +21 -20
  37. package/node_modules/@local/vibe64-adapters/src/server/workflowCommandTerminal/issuePr.js +27 -4
  38. package/node_modules/@local/vibe64-adapters/src/server/workflowCommandTerminal/mergeSync.js +65 -8
  39. package/node_modules/@local/vibe64-adapters/src/server/workflowCommandTerminal/worktreeDependencies.js +12 -25
  40. package/node_modules/@local/vibe64-adapters/src/server/workflowSessionActions.js +77 -20
  41. package/node_modules/@local/vibe64-core/src/server/terminalWebSocketRoutes.js +14 -5
  42. package/node_modules/@local/vibe64-project/src/server/actions.js +21 -0
  43. package/node_modules/@local/vibe64-project/src/server/registerRoutes.js +6 -0
  44. package/node_modules/@local/vibe64-project/src/server/service.js +125 -8
  45. package/node_modules/@local/vibe64-runtime/src/server/coreProjectTools.js +135 -0
  46. package/node_modules/@local/vibe64-runtime/src/server/index.js +4 -0
  47. package/node_modules/@local/vibe64-runtime/src/server/projectToolRegistry.js +434 -0
  48. package/node_modules/@local/vibe64-runtime/src/server/runtime.js +40 -16
  49. package/node_modules/@local/vibe64-runtime/src/server/sessionWorktreeState.js +25 -0
  50. package/node_modules/@local/vibe64-runtime/src/server/terminalFailureFixRequest.js +112 -0
  51. package/node_modules/@local/vibe64-runtime/src/server/workflowArtifacts.js +2 -2
  52. package/node_modules/@local/vibe64-runtime/src/server/workflowDefinitionBuilders.js +1 -0
  53. package/node_modules/@local/vibe64-runtime/src/server/workflowDefinitionComposers.js +192 -0
  54. package/node_modules/@local/vibe64-runtime/src/server/workflowMachine.js +5 -0
  55. package/node_modules/@local/vibe64-runtime/src/server/workflowModules/coreCoding.js +1309 -419
  56. package/node_modules/@local/vibe64-runtime/src/server/workflowModules/coreLifecycle.js +540 -358
  57. package/node_modules/@local/vibe64-runtime/src/server/workflowModules/coreMaintenance.js +5 -26
  58. package/node_modules/@local/vibe64-runtime/src/server/workflowPresentation.js +153 -26
  59. package/node_modules/@local/vibe64-runtime/src/server/workflowStepMachineHelpers.js +63 -10
  60. package/node_modules/@local/vibe64-runtime/src/server/workflowStepMachines.js +63 -2
  61. package/node_modules/@local/vibe64-sessions/src/server/inputSchemas.js +5 -0
  62. package/node_modules/@local/vibe64-sessions/src/server/service.js +8 -9
  63. package/node_modules/@local/vibe64-terminals/src/server/actions.js +65 -0
  64. package/node_modules/@local/vibe64-terminals/src/server/codexTerminal.js +608 -23
  65. package/node_modules/@local/vibe64-terminals/src/server/commandTerminal.js +261 -4
  66. package/node_modules/@local/vibe64-terminals/src/server/fixCodexJobs.js +420 -0
  67. package/node_modules/@local/vibe64-terminals/src/server/inputSchemas.js +240 -0
  68. package/node_modules/@local/vibe64-terminals/src/server/launchTargetTerminal.js +2 -0
  69. package/node_modules/@local/vibe64-terminals/src/server/registerRoutes.js +226 -0
  70. package/node_modules/@local/vibe64-terminals/src/server/service.js +140 -1
  71. package/node_modules/@local/vibe64-terminals/src/server/targetToolchainTerminal.js +1 -1
  72. package/node_modules/@local/vibe64-terminals/src/server/terminalShared.js +34 -0
  73. package/node_modules/node-pty/build/Makefile +369 -0
  74. package/node_modules/node-pty/build/Release/pty.node +0 -0
  75. package/node_modules/node-pty/build/binding.Makefile +6 -0
  76. package/node_modules/node-pty/build/config.gypi +440 -0
  77. package/node_modules/node-pty/build/pty.target.mk +166 -0
  78. package/node_modules/node-pty/node-addon-api/node_addon_api.Makefile +6 -0
  79. package/node_modules/node-pty/node-addon-api/node_addon_api.target.mk +104 -0
  80. package/node_modules/node-pty/node-addon-api/node_addon_api_except.target.mk +108 -0
  81. package/node_modules/node-pty/node-addon-api/node_addon_api_maybe.target.mk +104 -0
  82. package/node_modules/which/CHANGELOG.md +166 -0
  83. package/package.json +2 -2
  84. package/packages/setup-doctor-core/src/server/doctorToolchain.js +4 -0
  85. package/packages/studio-terminal-core/src/server/gitGithubTransport.js +33 -0
  86. package/packages/studio-terminal-core/src/server/index.js +1 -0
  87. package/packages/studio-terminal-core/src/server/terminalSessions.js +51 -1
  88. package/packages/vibe64-adapters/src/server/adapter.js +4 -0
  89. package/packages/vibe64-adapters/src/server/adapters/laravel/adapter.js +5 -0
  90. package/packages/vibe64-adapters/src/server/adapters/laravel/databaseRuntime.js +50 -0
  91. package/packages/vibe64-adapters/src/server/configStore.js +22 -1
  92. package/packages/vibe64-adapters/src/server/systemPrompts/agent_conversation.txt +0 -2
  93. package/packages/vibe64-adapters/src/server/systemPrompts/draft_issue.txt +18 -0
  94. package/packages/vibe64-adapters/src/server/systemPrompts/make_plan.txt +14 -2
  95. package/packages/vibe64-adapters/src/server/systemPrompts/prepare_for_merge.txt +2 -0
  96. package/packages/vibe64-adapters/src/server/systemPrompts/resolve_pull_request.txt +1 -0
  97. package/packages/vibe64-adapters/src/server/systemPrompts/run_deep_ui_check.txt +7 -3
  98. package/packages/vibe64-adapters/src/server/workflowCommandTerminal/commitPush.js +4 -22
  99. package/packages/vibe64-adapters/src/server/workflowCommandTerminal/factMetadata.js +21 -20
  100. package/packages/vibe64-adapters/src/server/workflowCommandTerminal/issuePr.js +27 -4
  101. package/packages/vibe64-adapters/src/server/workflowCommandTerminal/mergeSync.js +65 -8
  102. package/packages/vibe64-adapters/src/server/workflowCommandTerminal/worktreeDependencies.js +12 -25
  103. package/packages/vibe64-adapters/src/server/workflowSessionActions.js +77 -20
  104. package/packages/vibe64-core/src/server/terminalWebSocketRoutes.js +14 -5
  105. package/packages/vibe64-project/src/server/actions.js +21 -0
  106. package/packages/vibe64-project/src/server/registerRoutes.js +6 -0
  107. package/packages/vibe64-project/src/server/service.js +125 -8
  108. package/packages/vibe64-runtime/src/server/coreProjectTools.js +135 -0
  109. package/packages/vibe64-runtime/src/server/index.js +4 -0
  110. package/packages/vibe64-runtime/src/server/projectToolRegistry.js +434 -0
  111. package/packages/vibe64-runtime/src/server/runtime.js +40 -16
  112. package/packages/vibe64-runtime/src/server/sessionWorktreeState.js +25 -0
  113. package/packages/vibe64-runtime/src/server/terminalFailureFixRequest.js +112 -0
  114. package/packages/vibe64-runtime/src/server/workflowArtifacts.js +2 -2
  115. package/packages/vibe64-runtime/src/server/workflowDefinitionBuilders.js +1 -0
  116. package/packages/vibe64-runtime/src/server/workflowDefinitionComposers.js +192 -0
  117. package/packages/vibe64-runtime/src/server/workflowMachine.js +5 -0
  118. package/packages/vibe64-runtime/src/server/workflowModules/coreCoding.js +1309 -419
  119. package/packages/vibe64-runtime/src/server/workflowModules/coreLifecycle.js +540 -358
  120. package/packages/vibe64-runtime/src/server/workflowModules/coreMaintenance.js +5 -26
  121. package/packages/vibe64-runtime/src/server/workflowPresentation.js +153 -26
  122. package/packages/vibe64-runtime/src/server/workflowStepMachineHelpers.js +63 -10
  123. package/packages/vibe64-runtime/src/server/workflowStepMachines.js +63 -2
  124. package/packages/vibe64-sessions/src/server/inputSchemas.js +5 -0
  125. package/packages/vibe64-sessions/src/server/service.js +8 -9
  126. package/packages/vibe64-terminals/src/server/actions.js +65 -0
  127. package/packages/vibe64-terminals/src/server/codexTerminal.js +608 -23
  128. package/packages/vibe64-terminals/src/server/commandTerminal.js +261 -4
  129. package/packages/vibe64-terminals/src/server/fixCodexJobs.js +420 -0
  130. package/packages/vibe64-terminals/src/server/inputSchemas.js +240 -0
  131. package/packages/vibe64-terminals/src/server/launchTargetTerminal.js +2 -0
  132. package/packages/vibe64-terminals/src/server/registerRoutes.js +226 -0
  133. package/packages/vibe64-terminals/src/server/service.js +140 -1
  134. package/packages/vibe64-terminals/src/server/targetToolchainTerminal.js +1 -1
  135. package/packages/vibe64-terminals/src/server/terminalShared.js +34 -0
  136. package/dist/assets/ProjectTypeGate-DStq4W4X.js +0 -1
  137. package/dist/assets/ProjectTypeGate-Dh2ycSoM.css +0 -1
  138. package/dist/assets/history-HQ_XliX2.js +0 -1
  139. package/dist/assets/home-B89EbUmA.js +0 -1
  140. package/dist/assets/index-16P0GeGq.js +0 -134
  141. package/dist/assets/index-eMhOXhf_.css +0 -1
  142. package/dist/assets/useStudioTerminal-D9TNb50w.js +0 -1
  143. package/dist/assets/vibe64SessionPanelModel-DvY_IbW3.js +0 -1
@@ -16,16 +16,23 @@ import {
16
16
  REPORT_ARTIFACT
17
17
  } from "../workflowArtifacts.js";
18
18
  import {
19
- buildAgentConversationActionDefinition,
20
- buildAgentConversationStepDefinition
19
+ buildAgentConversationActionDefinition
21
20
  } from "../workflowDefinitionBuilders.js";
21
+ import {
22
+ defineWorkflow,
23
+ workflowGroup,
24
+ workflowWhen
25
+ } from "../workflowDefinitionComposers.js";
22
26
  import {
23
27
  coreLifecycleWorkflowIntentHandlers
24
28
  } from "./coreLifecycle.js";
25
29
  import { when } from "../workflowConditions.js";
26
30
  import {
31
+ LET_CODEX_DECIDE_INPUT,
27
32
  STEP_INPUT_KIND,
28
33
  STEP_STATUS,
34
+ actionCompleted,
35
+ assertAgentResultSource,
29
36
  artifactIsReady,
30
37
  artifactText,
31
38
  commandFailureInteraction,
@@ -45,6 +52,7 @@ import {
45
52
  readState,
46
53
  requireInputValue,
47
54
  unsupportedInputKind,
55
+ writePromptResponseArtifact,
48
56
  writeState
49
57
  } from "../workflowStepMachineHelpers.js";
50
58
 
@@ -52,27 +60,38 @@ const moduleId = "core.coding";
52
60
 
53
61
  const VIBE64_WORKFLOW_DEFINITION_IDS = deepFreeze({
54
62
  BIG_FEATURE: "big_feature",
55
- GENERAL_CODING: "general_coding",
56
63
  SEED_APPLICATION: "seed_application"
57
64
  });
58
65
  const DEFAULT_VIBE64_WORKFLOW_DEFINITION_ID = VIBE64_WORKFLOW_DEFINITION_IDS.BIG_FEATURE;
66
+ const CORE_CODING_WORKFLOW_GROUP_IDS = deepFreeze({
67
+ FINISH_OFF: "finish_off",
68
+ QA: "qa"
69
+ });
59
70
 
60
- const agentConversationStepId = "agent_conversation";
61
71
  const changesAcceptedStepId = "changes_accepted";
62
72
  const deepUiCheckRunStepId = "deep_ui_check_run";
63
73
  const implementationReviewedStepId = "implementation_reviewed";
64
- const issueSubmittedStepId = "issue_submitted";
65
- const planExecutedStepId = "plan_executed";
66
- const planMadeStepId = "plan_made";
67
- const projectKnowledgeUpdatedStepId = "project_knowledge_updated";
68
- const reportCreatedStepId = "report_created";
69
- const reviewRunStepId = "review_run";
74
+ const planAndExecuteStepId = "plan_and_execute";
75
+ const reportAndKnowledgeUpdatedStepId = "report_and_update_knowledge";
76
+ const reviewAndValidateStepId = "review_and_validate";
70
77
  const seedPlanExecutedStepId = "seed_plan_executed";
71
78
  const seedPlanMadeStepId = "seed_plan_made";
72
79
  const finalReviewConversationActionId = "final_review_conversation";
73
80
  const humanReviewConversationActionId = "human_review_conversation";
74
81
  const ISSUE_FILE_STEP_ID = "issue_file_created";
82
+ const draftIssueActionId = "draft_issue";
83
+ const rejectIssueDraftActionId = "reject_issue_draft";
75
84
  const SEED_APPLICATION_STEP_ID = "seed_application_defined";
85
+ const GITHUB_ISSUE_MODE_METADATA = "github_issue_mode";
86
+ const PLAN_READY_METADATA = "plan_ready";
87
+ const IMPLEMENTATION_DONE_METADATA = "implementation_done";
88
+ const PROJECT_KNOWLEDGE_UPDATED_METADATA = "project_knowledge_updated";
89
+ const REVIEW_DESLOP_COMPLETED_METADATA = "review_deslop_completed";
90
+ const GITHUB_ISSUE_MODES = deepFreeze({
91
+ CREATE: "create",
92
+ REUSE: "reuse",
93
+ SKIP: "skip"
94
+ });
76
95
 
77
96
  async function skipOptionalCheck(ctx = {}) {
78
97
  return ctx.forceAdvance("Skipped optional check.");
@@ -95,18 +114,51 @@ const finalReviewIntentHandlers = deepFreeze({
95
114
  const optionalCheckIntentHandlers = deepFreeze({
96
115
  skip_optional_check: skipOptionalCheck
97
116
  });
98
- const coreCodingSeedWorkflowIntentHandlers = deepFreeze({
99
- ...coreLifecycleWorkflowIntentHandlers,
100
- [changesAcceptedStepId]: finalReviewIntentHandlers
101
- });
102
- const coreCodingStandardWorkflowIntentHandlers = deepFreeze({
103
- ...coreCodingSeedWorkflowIntentHandlers,
104
- [deepUiCheckRunStepId]: optionalCheckIntentHandlers
105
- });
117
+
118
+ function finishOffWorkflowGroup({
119
+ recheckTo = "",
120
+ rejectTo = ""
121
+ } = {}) {
122
+ return workflowGroup({
123
+ id: CORE_CODING_WORKFLOW_GROUP_IDS.FINISH_OFF,
124
+ intentHandlers: {
125
+ ...coreLifecycleWorkflowIntentHandlers,
126
+ [changesAcceptedStepId]: finalReviewIntentHandlers
127
+ },
128
+ steps: [
129
+ {
130
+ recheckTo,
131
+ rejectTo,
132
+ stepId: changesAcceptedStepId
133
+ },
134
+ reportAndKnowledgeUpdatedStepId,
135
+ "changes_committed",
136
+ "create_and_merge_pull_request",
137
+ "session_finished"
138
+ ]
139
+ });
140
+ }
141
+
142
+ function qaWorkflowGroup({
143
+ humanReview = true
144
+ } = {}) {
145
+ return workflowGroup({
146
+ id: CORE_CODING_WORKFLOW_GROUP_IDS.QA,
147
+ intentHandlers: {
148
+ [deepUiCheckRunStepId]: optionalCheckIntentHandlers
149
+ },
150
+ steps: [
151
+ workflowWhen(humanReview, implementationReviewedStepId),
152
+ deepUiCheckRunStepId,
153
+ reviewAndValidateStepId
154
+ ]
155
+ });
156
+ }
106
157
 
107
158
  function createIssueOnGithubAction() {
108
159
  return {
109
160
  adapterCapability: "create_issue_on_gh",
161
+ auditMessage: "Issue draft accepted; creating GitHub issue.",
110
162
  disabledReason: "Create the issue file before submitting it to GitHub.",
111
163
  disabledWhen: [when.metadataExists("issue_url")],
112
164
  disabledWhenReason: "The GitHub issue already exists.",
@@ -115,6 +167,7 @@ function createIssueOnGithubAction() {
115
167
  icon: "github",
116
168
  id: "create_issue_on_gh",
117
169
  label: "Create issue on GH",
170
+ saveCurrentStepInputBeforeRun: true,
118
171
  type: "command"
119
172
  };
120
173
  }
@@ -142,63 +195,103 @@ const coreCodingStepDefinitionsById = deepFreeze({
142
195
  [ISSUE_FILE_STEP_ID]: {
143
196
  actions: [
144
197
  {
145
- adapterCapability: "use_existing_issue",
146
- disabledReason: "An existing issue is already selected.",
198
+ disabledReason: "Work details are already saved.",
199
+ disabledWhen: [
200
+ when.allArtifactsReady(ISSUE_TITLE_ARTIFACT, ISSUE_BODY_ARTIFACT, ISSUE_WORD_ARTIFACT)
201
+ ],
202
+ icon: "message-square-plus",
203
+ id: draftIssueActionId,
204
+ inputFields: [
205
+ {
206
+ kind: "textarea",
207
+ label: "What do you want Vibe64 to work on?",
208
+ name: "conversationRequest",
209
+ placeholder: "Describe the feature, bug, or change you want.",
210
+ requiredMessage: "Describe what you want Vibe64 to work on."
211
+ }
212
+ ],
213
+ label: "Describe work",
214
+ promptId: draftIssueActionId,
215
+ recordsConversationTurn: true,
216
+ type: "prompt"
217
+ },
218
+ {
219
+ disabledReason: "Draft an issue before requesting improvements.",
147
220
  disabledWhen: [when.metadataExists("issue_url")],
148
- icon: "github",
149
- id: "use_existing_issue",
221
+ disabledWhenReason: "An existing issue is already selected.",
222
+ enabledWhen: [when.allArtifactsReady(ISSUE_TITLE_ARTIFACT, ISSUE_BODY_ARTIFACT, ISSUE_WORD_ARTIFACT)],
223
+ enabledWhenReason: "Draft an issue before requesting improvements.",
224
+ icon: "rotate-ccw",
225
+ id: rejectIssueDraftActionId,
150
226
  inputFields: [
151
227
  {
152
- label: "Issue URL or number",
153
- name: "issueRef",
154
- placeholder: "123, #123, or https://github.com/org/repo/issues/123",
155
- requiredMessage: "Issue URL or number is required."
228
+ kind: "textarea",
229
+ label: "What should change?",
230
+ name: "feedback",
231
+ placeholder: "Tell Codex how to improve the saved issue draft.",
232
+ requiredMessage: "Explain what should change before sending the improvement request."
156
233
  }
157
234
  ],
158
- label: "Use existing issue",
159
- type: "adapter"
160
- }
235
+ label: "Send improvement request",
236
+ promptId: draftIssueActionId,
237
+ recordsConversationTurn: true,
238
+ type: "prompt"
239
+ },
240
+ createIssueOnGithubAction()
161
241
  ],
162
242
  autopilot: {
163
243
  kind: "issue_discussion",
164
244
  stop: true
165
245
  },
166
- description: "Define a new issue or select an existing GitHub issue.",
246
+ description: "Define the work and create a GitHub issue only when the starting point requires one.",
167
247
  id: ISSUE_FILE_STEP_ID,
168
- label: "Define or select issue",
248
+ label: "Define work",
169
249
  next: {
170
- disabledReason: "Discuss and finalise issue before continuing.",
171
- enabledWhen: [
172
- when.any(
173
- when.metadataExists("issue_url"),
174
- when.allArtifactsReady(ISSUE_TITLE_ARTIFACT, ISSUE_BODY_ARTIFACT, ISSUE_WORD_ARTIFACT)
175
- )
176
- ]
177
- },
178
- rewindCleanup: {
179
- actionResults: ["use_existing_issue"],
180
- artifacts: [ISSUE_TITLE_ARTIFACT, ISSUE_BODY_ARTIFACT, ISSUE_WORD_ARTIFACT],
181
- metadata: ["issue_url", "issue_number", "issue_title", "issue_source", ISSUE_WORD_ARTIFACT]
182
- }
183
- },
184
- [issueSubmittedStepId]: {
185
- actions: [
186
- createIssueOnGithubAction()
187
- ],
188
- autopilot: {
189
- actionId: "create_issue_on_gh",
190
- completeWhen: [when.metadataExists("issue_url")],
191
- label: "Edit and submit issue"
250
+ disabledReason: "Define the work before continuing.",
251
+ enabledWhen: [when.allArtifactsReady(ISSUE_TITLE_ARTIFACT, ISSUE_BODY_ARTIFACT, ISSUE_WORD_ARTIFACT)]
192
252
  },
193
- description: "Review the issue files and submit the GitHub issue.",
194
- id: issueSubmittedStepId,
195
- label: "Edit and submit issue",
196
- next: {
197
- enabledWhen: [when.metadataExists("issue_url")]
253
+ presentation: {
254
+ stop: {
255
+ intents: [
256
+ {
257
+ actionId: draftIssueActionId,
258
+ id: draftIssueActionId,
259
+ label: "Describe work",
260
+ style: "primary",
261
+ type: "action"
262
+ },
263
+ {
264
+ actionId: "create_issue_on_gh",
265
+ id: "create_issue_on_gh",
266
+ label: "Create issue on GH",
267
+ style: "secondary",
268
+ type: "action"
269
+ }
270
+ ],
271
+ screen: {
272
+ kind: "issue_source",
273
+ message: "Describe the work, review the saved details, and create a GitHub issue only when this session requires one.",
274
+ primaryIntentId: draftIssueActionId,
275
+ title: "Define work"
276
+ }
277
+ }
198
278
  },
199
279
  rewindCleanup: {
200
- actionResults: ["create_issue_on_gh"],
201
- metadata: ["issue_url", "issue_number", "issue_title"]
280
+ actionResults: [draftIssueActionId, rejectIssueDraftActionId, "create_issue_on_gh"],
281
+ artifacts: [ISSUE_TITLE_ARTIFACT, ISSUE_BODY_ARTIFACT, ISSUE_WORD_ARTIFACT],
282
+ metadata: [
283
+ "issue_url",
284
+ "issue_number",
285
+ "issue_title",
286
+ "issue_source",
287
+ "work_anchor_number",
288
+ "work_anchor_title",
289
+ "work_anchor_type",
290
+ "work_anchor_url",
291
+ ISSUE_WORD_ARTIFACT,
292
+ PLAN_READY_METADATA,
293
+ IMPLEMENTATION_DONE_METADATA
294
+ ]
202
295
  }
203
296
  },
204
297
  [seedPlanMadeStepId]: {
@@ -241,29 +334,22 @@ const coreCodingStepDefinitionsById = deepFreeze({
241
334
  actionResults: ["execute_seed_plan"]
242
335
  }
243
336
  },
244
- [planMadeStepId]: {
337
+ [planAndExecuteStepId]: {
245
338
  actions: [
246
339
  {
340
+ disabledWhen: [when.metadataExists(PLAN_READY_METADATA)],
341
+ disabledWhenReason: "The plan is already ready.",
247
342
  id: "make_plan",
248
- label: "Make plan",
343
+ label: "Make a plan",
249
344
  promptId: "make_plan",
250
345
  type: "prompt"
251
- }
252
- ],
253
- autopilot: {
254
- actionId: "make_plan",
255
- label: "Make plan"
256
- },
257
- description: "Ask Codex to create the implementation plan.",
258
- id: planMadeStepId,
259
- label: "Make plan",
260
- rewindCleanup: {
261
- actionResults: ["make_plan"]
262
- }
263
- },
264
- [planExecutedStepId]: {
265
- actions: [
346
+ },
266
347
  {
348
+ disabledReason: "Implementation is already complete.",
349
+ disabledWhen: [when.metadataExists(IMPLEMENTATION_DONE_METADATA)],
350
+ disabledWhenReason: "Implementation is already complete.",
351
+ enabledWhen: [when.metadataExists(PLAN_READY_METADATA)],
352
+ enabledWhenReason: "Make the plan before executing it.",
267
353
  id: "execute_plan",
268
354
  label: "Execute plan",
269
355
  promptId: "execute_plan",
@@ -271,14 +357,30 @@ const coreCodingStepDefinitionsById = deepFreeze({
271
357
  }
272
358
  ],
273
359
  autopilot: {
274
- actionId: "execute_plan",
275
- label: "Execute plan"
360
+ actionSequence: [
361
+ {
362
+ actionId: "make_plan",
363
+ completeWhen: [when.metadataExists(PLAN_READY_METADATA)],
364
+ label: "Make a plan"
365
+ },
366
+ {
367
+ actionId: "execute_plan",
368
+ completeWhen: [when.metadataExists(IMPLEMENTATION_DONE_METADATA)],
369
+ label: "Execute plan"
370
+ }
371
+ ],
372
+ label: "Plan and execute"
373
+ },
374
+ description: "Ask Codex to create the implementation plan, then execute it.",
375
+ id: planAndExecuteStepId,
376
+ label: "Plan and execute",
377
+ next: {
378
+ disabledReason: "Execute the plan before continuing.",
379
+ enabledWhen: [when.metadataExists(IMPLEMENTATION_DONE_METADATA)]
276
380
  },
277
- description: "Ask Codex to execute the plan.",
278
- id: planExecutedStepId,
279
- label: "Execute plan",
280
381
  rewindCleanup: {
281
- actionResults: ["execute_plan"]
382
+ actionResults: ["make_plan", "execute_plan"],
383
+ metadata: [PLAN_READY_METADATA, IMPLEMENTATION_DONE_METADATA]
282
384
  }
283
385
  },
284
386
  [implementationReviewedStepId]: {
@@ -297,7 +399,7 @@ const coreCodingStepDefinitionsById = deepFreeze({
297
399
  },
298
400
  description: "Try the implemented work and request small tweaks before slower review steps.",
299
401
  id: implementationReviewedStepId,
300
- label: "Human review",
402
+ label: "Initial human review",
301
403
  presentation: {
302
404
  stop: {
303
405
  intents: [
@@ -325,11 +427,13 @@ const coreCodingStepDefinitionsById = deepFreeze({
325
427
  type: "action"
326
428
  }
327
429
  ],
430
+ persistWhenComplete: true,
328
431
  screen: {
329
432
  kind: "review",
330
433
  message: "Try the work now. Ask Codex for small tweaks, or continue when it looks right.",
331
434
  sections: ["launch_controls", "report_preview", "response_preview"],
332
- title: "Human review",
435
+ title: "Initial human review",
436
+ primaryIntentId: "request_review_tweak",
333
437
  variant: "implementation"
334
438
  }
335
439
  }
@@ -339,32 +443,23 @@ const coreCodingStepDefinitionsById = deepFreeze({
339
443
  artifacts: [HUMAN_INPUT_RESPONSE_ARTIFACT]
340
444
  }
341
445
  },
342
- [agentConversationStepId]: buildAgentConversationStepDefinition({
343
- actionLabel: "Ask Codex for changes",
344
- description: "Ask Codex to make focused code changes while you inspect and steer the work.",
345
- id: agentConversationStepId,
346
- inputLabel: "What should Codex change?",
347
- inputPlaceholder: "Describe the code change, cleanup, bug fix, or follow-up request.",
348
- label: "Make changes",
349
- responseArtifact: HUMAN_INPUT_RESPONSE_ARTIFACT
350
- }),
351
446
  [deepUiCheckRunStepId]: {
352
447
  actions: [
353
448
  {
354
449
  id: "run_deep_ui_check",
355
- label: "Run deep UI check",
450
+ label: "Check user interface",
356
451
  promptId: "run_deep_ui_check",
357
452
  type: "prompt"
358
453
  }
359
454
  ],
360
455
  autopilot: {
361
456
  actionId: "run_deep_ui_check",
362
- label: "Run deep UI check",
457
+ label: "Check user interface",
363
458
  userDecision: true
364
459
  },
365
460
  description: "Run the deeper UI review when the target supports it.",
366
461
  id: deepUiCheckRunStepId,
367
- label: "Run deep UI check",
462
+ label: "Check user interface",
368
463
  presentation: {
369
464
  decision: {
370
465
  intents: [
@@ -392,24 +487,78 @@ const coreCodingStepDefinitionsById = deepFreeze({
392
487
  actionResults: ["run_deep_ui_check"]
393
488
  }
394
489
  },
395
- [reviewRunStepId]: {
490
+ [reviewAndValidateStepId]: {
396
491
  actions: [
397
492
  {
398
493
  id: "run_deslop",
399
494
  label: "Run deslop",
400
495
  promptId: "run_deslop",
401
496
  type: "prompt"
497
+ },
498
+ {
499
+ adapterCapability: "update_code_index",
500
+ enabledWhen: [when.metadataExists(REVIEW_DESLOP_COMPLETED_METADATA)],
501
+ enabledWhenReason: "Run review/deslop before updating the code index.",
502
+ icon: "sync",
503
+ id: "update_code_index",
504
+ label: "Update code index",
505
+ type: "command"
506
+ },
507
+ {
508
+ adapterCapability: "run_automated_checks",
509
+ enabledWhen: [
510
+ when.metadataExists(REVIEW_DESLOP_COMPLETED_METADATA),
511
+ when.metadataExists("code_index_updated")
512
+ ],
513
+ enabledWhenReason: "Update the code index before running automated checks.",
514
+ icon: "run",
515
+ id: "run_automated_checks",
516
+ label: "Run automated checks",
517
+ type: "command"
402
518
  }
403
519
  ],
404
520
  autopilot: {
405
- actionId: "run_deslop",
406
- label: "Run deslop"
521
+ actionSequence: [
522
+ {
523
+ actionId: "run_deslop",
524
+ completeWhen: [when.metadataExists(REVIEW_DESLOP_COMPLETED_METADATA)],
525
+ label: "Run review/deslop"
526
+ },
527
+ {
528
+ actionId: "update_code_index",
529
+ completeWhen: [when.metadataExists("code_index_updated")],
530
+ label: "Update code index"
531
+ },
532
+ {
533
+ actionId: "run_automated_checks",
534
+ completeWhen: [when.metadataExists("automated_checks_passed")],
535
+ label: "Run automated checks"
536
+ }
537
+ ],
538
+ label: "Review and validate"
539
+ },
540
+ description: "Run review/deslop, update the code index, and run automated checks.",
541
+ id: reviewAndValidateStepId,
542
+ label: "Review and validate",
543
+ next: {
544
+ disabledReason: "Run review/deslop, update the code index, and run automated checks successfully before continuing.",
545
+ enabledWhen: [
546
+ when.metadataExists(REVIEW_DESLOP_COMPLETED_METADATA),
547
+ when.metadataExists("code_index_updated"),
548
+ when.metadataExists("automated_checks_passed")
549
+ ]
407
550
  },
408
- description: "Run the review/deslop prompt.",
409
- id: reviewRunStepId,
410
- label: "Run review/deslop",
411
551
  rewindCleanup: {
412
- actionResults: ["run_deslop"]
552
+ actionResults: ["run_deslop", "update_code_index", "run_automated_checks"],
553
+ metadata: [
554
+ REVIEW_DESLOP_COMPLETED_METADATA,
555
+ "code_index_command_source",
556
+ "code_index_package_manager",
557
+ "code_index_path",
558
+ "code_index_updated",
559
+ "automated_checks_package_manager",
560
+ "automated_checks_passed"
561
+ ]
413
562
  }
414
563
  },
415
564
  [changesAcceptedStepId]: {
@@ -428,7 +577,7 @@ const coreCodingStepDefinitionsById = deepFreeze({
428
577
  },
429
578
  description: "Review the validated work before the report, commit, and pull request.",
430
579
  id: changesAcceptedStepId,
431
- label: "Final review",
580
+ label: "Final human review",
432
581
  presentation: {
433
582
  automation: {
434
583
  recheckAfterPrompt: {
@@ -479,11 +628,13 @@ const coreCodingStepDefinitionsById = deepFreeze({
479
628
  type: "reject"
480
629
  }
481
630
  ],
631
+ persistWhenComplete: true,
482
632
  screen: {
483
633
  kind: "review",
484
634
  message: "Review the validated work before Autopilot writes the report and commits.",
485
635
  sections: ["launch_controls", "report_preview", "response_preview"],
486
- title: "Final review",
636
+ title: "Final human review",
637
+ primaryIntentId: "request_review_tweak",
487
638
  variant: "final"
488
639
  }
489
640
  }
@@ -493,35 +644,17 @@ const coreCodingStepDefinitionsById = deepFreeze({
493
644
  metadata: ["autopilot_final_review_followup"]
494
645
  }
495
646
  },
496
- [reportCreatedStepId]: {
647
+ [reportAndKnowledgeUpdatedStepId]: {
497
648
  actions: [
498
649
  {
499
650
  id: "write_report",
500
651
  label: "Write report",
501
652
  promptId: "write_report",
502
653
  type: "prompt"
503
- }
504
- ],
505
- autopilot: {
506
- actionId: "write_report",
507
- completeWhen: [when.artifactReady(REPORT_ARTIFACT)],
508
- label: "Write report"
509
- },
510
- description: "Write the local report explaining what changed and why.",
511
- id: reportCreatedStepId,
512
- label: "Write report",
513
- next: {
514
- disabledReason: "Write the session report before updating project knowledge.",
515
- enabledWhen: [when.artifactReady(REPORT_ARTIFACT)]
516
- },
517
- rewindCleanup: {
518
- actionResults: ["write_report"],
519
- artifacts: [REPORT_ARTIFACT]
520
- }
521
- },
522
- [projectKnowledgeUpdatedStepId]: {
523
- actions: [
654
+ },
524
655
  {
656
+ enabledWhen: [when.artifactReady(REPORT_ARTIFACT)],
657
+ enabledWhenReason: "Write the session report before updating project knowledge.",
525
658
  id: "update_project_knowledge",
526
659
  label: "Update project knowledge",
527
660
  promptId: "update_project_knowledge",
@@ -529,26 +662,44 @@ const coreCodingStepDefinitionsById = deepFreeze({
529
662
  }
530
663
  ],
531
664
  autopilot: {
532
- actionId: "update_project_knowledge",
533
- label: "Update project knowledge"
665
+ actionSequence: [
666
+ {
667
+ actionId: "write_report",
668
+ completeWhen: [when.artifactReady(REPORT_ARTIFACT)],
669
+ label: "Write report"
670
+ },
671
+ {
672
+ actionId: "update_project_knowledge",
673
+ completeWhen: [when.metadataExists(PROJECT_KNOWLEDGE_UPDATED_METADATA)],
674
+ label: "Update project knowledge"
675
+ }
676
+ ],
677
+ label: "Write report and update project knowledge"
678
+ },
679
+ description: "Write the local report and update adapter-supported project knowledge.",
680
+ id: reportAndKnowledgeUpdatedStepId,
681
+ label: "Write report and update project knowledge",
682
+ next: {
683
+ disabledReason: "Write the report and update project knowledge before continuing.",
684
+ enabledWhen: [
685
+ when.artifactReady(REPORT_ARTIFACT),
686
+ when.metadataExists(PROJECT_KNOWLEDGE_UPDATED_METADATA)
687
+ ]
534
688
  },
535
- description: "Update adapter-supported project knowledge.",
536
- id: projectKnowledgeUpdatedStepId,
537
- label: "Update project knowledge",
538
689
  rewindCleanup: {
539
- actionResults: ["update_project_knowledge"]
690
+ actionResults: ["write_report", "update_project_knowledge"],
691
+ artifacts: [REPORT_ARTIFACT],
692
+ metadata: [PROJECT_KNOWLEDGE_UPDATED_METADATA]
540
693
  }
541
694
  }
542
695
  });
543
696
 
544
697
  const coreCodingWorkflowDefinitions = deepFreeze([
545
- {
698
+ defineWorkflow({
546
699
  description: "Create the initial application scaffold and local development foundation.",
547
700
  id: VIBE64_WORKFLOW_DEFINITION_IDS.SEED_APPLICATION,
548
- intentHandlers: coreCodingSeedWorkflowIntentHandlers,
549
701
  label: "Seed application",
550
- sessionWord: "seeding",
551
- steps: [
702
+ parts: [
552
703
  "session_created",
553
704
  "work_source_selected",
554
705
  "worktree_created",
@@ -556,85 +707,36 @@ const coreCodingWorkflowDefinitions = deepFreeze([
556
707
  seedPlanMadeStepId,
557
708
  seedPlanExecutedStepId,
558
709
  "dependencies_installed",
559
- "project_validated",
560
- {
710
+ reviewAndValidateStepId,
711
+ finishOffWorkflowGroup({
561
712
  rejectTo: seedPlanMadeStepId,
562
- recheckTo: "project_validated",
563
- stepId: changesAcceptedStepId
564
- },
565
- reportCreatedStepId,
566
- projectKnowledgeUpdatedStepId,
567
- "changes_committed",
568
- "create_pull_request",
569
- "pr_merged",
570
- "main_checkout_synced",
571
- "session_finished"
713
+ recheckTo: reviewAndValidateStepId
714
+ })
572
715
  ],
716
+ sessionWord: "seeding",
573
717
  userSelectable: false
574
- },
575
- {
576
- description: "Plan, implement, review, validate, commit, create a PR, and optionally merge.",
718
+ }),
719
+ defineWorkflow({
720
+ description: "Define, plan, implement, review, validate, commit, create a PR, and optionally merge.",
577
721
  id: VIBE64_WORKFLOW_DEFINITION_IDS.BIG_FEATURE,
578
- intentHandlers: coreCodingStandardWorkflowIntentHandlers,
579
- label: "Big feature",
580
- steps: [
722
+ label: "Make improvements",
723
+ parts: [
581
724
  "session_created",
582
725
  "work_source_selected",
583
726
  "worktree_created",
584
727
  "dependencies_installed",
585
728
  ISSUE_FILE_STEP_ID,
586
- issueSubmittedStepId,
587
- planMadeStepId,
588
- planExecutedStepId,
589
- implementationReviewedStepId,
590
- deepUiCheckRunStepId,
591
- reviewRunStepId,
592
- "project_validated",
593
- {
594
- rejectTo: planMadeStepId,
595
- recheckTo: reviewRunStepId,
596
- stepId: changesAcceptedStepId
597
- },
598
- reportCreatedStepId,
599
- projectKnowledgeUpdatedStepId,
600
- "changes_committed",
601
- "create_pull_request",
602
- "pr_merged",
603
- "main_checkout_synced",
604
- "session_finished"
605
- ],
606
- userSelectable: true
607
- },
608
- {
609
- description: "Make focused code changes with Codex, review, validate, commit, create a PR, and optionally merge.",
610
- id: VIBE64_WORKFLOW_DEFINITION_IDS.GENERAL_CODING,
611
- intentHandlers: coreCodingStandardWorkflowIntentHandlers,
612
- label: "General coding",
613
- sessionWord: "coding",
614
- steps: [
615
- "session_created",
616
- "work_source_selected",
617
- "worktree_created",
618
- "dependencies_installed",
619
- agentConversationStepId,
620
- deepUiCheckRunStepId,
621
- reviewRunStepId,
622
- "project_validated",
623
- {
624
- rejectTo: agentConversationStepId,
625
- recheckTo: reviewRunStepId,
626
- stepId: changesAcceptedStepId
627
- },
628
- reportCreatedStepId,
629
- projectKnowledgeUpdatedStepId,
630
- "changes_committed",
631
- "create_pull_request",
632
- "pr_merged",
633
- "main_checkout_synced",
634
- "session_finished"
729
+ planAndExecuteStepId,
730
+ qaWorkflowGroup({
731
+ humanReview: true
732
+ }),
733
+ finishOffWorkflowGroup({
734
+ rejectTo: planAndExecuteStepId,
735
+ recheckTo: reviewAndValidateStepId
736
+ })
635
737
  ],
636
738
  userSelectable: true
637
- }
739
+ })
638
740
  ]);
639
741
 
640
742
  function issueFilesAreReady(session = {}) {
@@ -645,6 +747,70 @@ function issueFilesAreReady(session = {}) {
645
747
  ].every((artifactName) => artifactIsReady(session, artifactName));
646
748
  }
647
749
 
750
+ function githubIssueMode(session = {}) {
751
+ return normalizeText(session.metadata?.[GITHUB_ISSUE_MODE_METADATA]);
752
+ }
753
+
754
+ function githubIssueShouldBeCreated(session = {}) {
755
+ return githubIssueMode(session) === GITHUB_ISSUE_MODES.CREATE;
756
+ }
757
+
758
+ function githubIssueShouldBeSkipped(session = {}) {
759
+ return githubIssueMode(session) === GITHUB_ISSUE_MODES.SKIP;
760
+ }
761
+
762
+ function githubIssueIsReused(session = {}) {
763
+ return githubIssueMode(session) === GITHUB_ISSUE_MODES.REUSE;
764
+ }
765
+
766
+ function disableActions(session = {}, reasonsById = {}) {
767
+ const reasons = Object.entries(reasonsById)
768
+ .filter(([, reason]) => normalizeText(reason));
769
+ if (reasons.length === 0) {
770
+ return Array.isArray(session.actions) ? session.actions : [];
771
+ }
772
+ return (Array.isArray(session.actions) ? session.actions : []).map((action) => {
773
+ const reasonEntry = reasons.find(([id]) => action.id === id);
774
+ if (!reasonEntry) {
775
+ return action;
776
+ }
777
+ return {
778
+ ...action,
779
+ disabledReason: reasonEntry[1],
780
+ enabled: false
781
+ };
782
+ });
783
+ }
784
+
785
+ function inputResponseText(input = {}) {
786
+ return normalizeText(input.text || input.fields?.response || input.fields?.conversationRequest);
787
+ }
788
+
789
+ function conversationSection(label = "", value = "") {
790
+ const normalizedValue = normalizeText(value);
791
+ return [
792
+ `${label}:`,
793
+ normalizedValue || "(empty)"
794
+ ].join("\n");
795
+ }
796
+
797
+ function issueDraftConversationMessage(context = {}, input = {}, {
798
+ proposed = false
799
+ } = {}) {
800
+ const fields = input?.fields || {};
801
+ const createGithubIssue = githubIssueShouldBeCreated(context.session);
802
+ const subject = createGithubIssue ? "issue draft" : "work description";
803
+ return [
804
+ `${proposed ? "Proposed" : "Saved"} ${subject}.`,
805
+ "",
806
+ conversationSection(createGithubIssue ? "Issue title" : "Work title", fields.title),
807
+ "",
808
+ conversationSection("Session label", fields.word),
809
+ "",
810
+ conversationSection(createGithubIssue ? "Issue body" : "Work description", fields.body)
811
+ ].join("\n");
812
+ }
813
+
648
814
  async function readIssueFieldValues(context = {}) {
649
815
  const [title, body, word] = await Promise.all([
650
816
  context.runtime.store.readArtifact(context.session.sessionId, ISSUE_TITLE_ARTIFACT),
@@ -659,28 +825,76 @@ async function readIssueFieldValues(context = {}) {
659
825
  }
660
826
 
661
827
  async function writeIssueFieldValues(context = {}, fields = {}) {
662
- const title = requireInputValue(fields.title, "Issue title is required.");
663
- const body = requireInputValue(fields.body, "Issue body is required.");
828
+ const title = requireInputValue(fields.title, "Work title is required.");
829
+ const body = requireInputValue(fields.body, "Work description is required.");
664
830
  const word = requireInputValue(fields.word, "Session label is required.");
831
+ const mode = githubIssueMode(context.session);
832
+ const preservesExistingPrAnchor = mode === GITHUB_ISSUE_MODES.SKIP &&
833
+ normalizeText(context.session.metadata?.work_source) === "existing_pr";
834
+ const staleMetadata = preservesExistingPrAnchor
835
+ ? ["issue_number", "issue_url"]
836
+ : ["issue_number", "issue_url", "work_anchor_number", "work_anchor_url"];
837
+ const metadata = {
838
+ issue_title: title,
839
+ ...(preservesExistingPrAnchor ? {} : { work_anchor_title: title }),
840
+ ...(mode === GITHUB_ISSUE_MODES.SKIP
841
+ ? {
842
+ issue_source: "none",
843
+ ...(preservesExistingPrAnchor ? {} : { work_anchor_type: "description" })
844
+ }
845
+ : {
846
+ issue_source: "draft",
847
+ work_anchor_type: "issue"
848
+ })
849
+ };
665
850
 
666
851
  await Promise.all([
667
852
  context.runtime.store.writeArtifact(context.session.sessionId, ISSUE_TITLE_ARTIFACT, artifactText(title)),
668
853
  context.runtime.store.writeArtifact(context.session.sessionId, ISSUE_BODY_ARTIFACT, artifactText(body)),
669
854
  context.runtime.store.writeArtifact(context.session.sessionId, ISSUE_WORD_ARTIFACT, artifactText(word)),
670
- context.runtime.store.writeMetadataValue(context.session.sessionId, "issue_title", title),
671
- context.runtime.store.writeIssueWordMetadata(context.session.sessionId, word)
855
+ ...Object.entries(metadata).map(([name, value]) => context.runtime.store.writeMetadataValue(
856
+ context.session.sessionId,
857
+ name,
858
+ value
859
+ )),
860
+ context.runtime.store.writeIssueWordMetadata(context.session.sessionId, word),
861
+ context.runtime.store.deleteMetadataValues(context.session.sessionId, staleMetadata)
672
862
  ]);
673
863
  }
674
864
 
675
- function issueInputInteraction(status = STEP_STATUS.WAITING_FOR_INPUT, values = {}) {
865
+ function issueInputInteraction(status = STEP_STATUS.WAITING_FOR_INPUT, values = {}, {
866
+ createGithubIssue = false
867
+ } = {}) {
868
+ const reviewIntents = status === STEP_STATUS.CONFIRM_FILES
869
+ ? [
870
+ {
871
+ id: "continue_step",
872
+ label: createGithubIssue ? "Create GitHub issue" : "Use this description",
873
+ auditMessage: createGithubIssue
874
+ ? "Issue draft accepted; creating GitHub issue."
875
+ : "Work description accepted.",
876
+ saveCurrentStepInputBeforeRun: true,
877
+ style: "primary",
878
+ type: createGithubIssue ? "action" : "continue",
879
+ ...(createGithubIssue ? { actionId: "create_issue_on_gh" } : {})
880
+ },
881
+ {
882
+ actionId: rejectIssueDraftActionId,
883
+ id: rejectIssueDraftActionId,
884
+ label: "Send improvement request",
885
+ style: "secondary",
886
+ type: "action"
887
+ }
888
+ ]
889
+ : [];
676
890
  return {
677
891
  fields: [
678
892
  {
679
893
  kind: "text",
680
- label: "Issue title",
894
+ label: createGithubIssue ? "Issue title" : "Work title",
681
895
  name: "title",
682
896
  required: true,
683
- requiredMessage: "Issue title is required.",
897
+ requiredMessage: createGithubIssue ? "Issue title is required." : "Work title is required.",
684
898
  value: values.title || ""
685
899
  },
686
900
  {
@@ -693,155 +907,402 @@ function issueInputInteraction(status = STEP_STATUS.WAITING_FOR_INPUT, values =
693
907
  },
694
908
  {
695
909
  kind: "textarea",
696
- label: "Issue body",
910
+ label: createGithubIssue ? "Issue body" : "Work description",
697
911
  name: "body",
698
912
  required: true,
699
- requiredMessage: "Issue body is required.",
913
+ requiredMessage: createGithubIssue ? "Issue body is required." : "Work description is required.",
700
914
  value: values.body || ""
701
915
  }
702
916
  ],
703
917
  kind: "confirm_files_run_command",
918
+ intents: reviewIntents,
704
919
  prompt: status === STEP_STATUS.CONFIRM_FILES
705
- ? "Review the issue details. Save changes here, or continue to create the GitHub issue."
706
- : "Discuss the requested change, then submit the issue title, session label, and issue body.",
920
+ ? (createGithubIssue
921
+ ? "Review the issue details, then create the GitHub issue."
922
+ : "Review the work details, then continue without creating a GitHub issue.")
923
+ : (createGithubIssue
924
+ ? "Discuss the requested change, then submit the issue title, session label, and issue body."
925
+ : "Discuss the requested change, then submit the work title, session label, and description."),
707
926
  submitKind: status === STEP_STATUS.CONFIRM_FILES
708
927
  ? STEP_INPUT_KIND.CONFIRM_FILES
709
928
  : STEP_INPUT_KIND.READY,
710
- submitLabel: status === STEP_STATUS.CONFIRM_FILES ? "Update issue" : "Save issue",
711
- title: "Define issue"
929
+ submitLabel: status === STEP_STATUS.CONFIRM_FILES ? "Save changes" : "Save details",
930
+ title: createGithubIssue ? "Define issue" : "Define work"
712
931
  };
713
932
  }
714
933
 
715
- const issueSubmittedMachine = {
716
- stepId: issueSubmittedStepId,
934
+ const issueFilePhase = Object.freeze({
935
+ CHOOSE_SOURCE: "choose_source",
936
+ CREATING_ISSUE: "creating_issue",
937
+ DRAFTING: "drafting",
938
+ EXISTING_SELECTED: "existing_selected",
939
+ REVIEW_DRAFT: "review_draft",
940
+ SKIPPED: "skipped"
941
+ });
942
+
943
+ function issueSkipMessage(session = {}) {
944
+ return normalizeText(session.metadata?.work_source) === "existing_pr"
945
+ ? "Skipped: existing PR selected as the work anchor; no GitHub issue is required."
946
+ : "No GitHub issue is required for this session.";
947
+ }
948
+
949
+ function issueSkipState(session = {}) {
950
+ return machineState(STEP_STATUS.DONE, {
951
+ message: issueSkipMessage(session),
952
+ phase: issueFilePhase.SKIPPED,
953
+ skipReason: issueSkipMessage(session)
954
+ });
955
+ }
956
+
957
+ function issueSourceSelectionState(details = {}) {
958
+ return machineState(STEP_STATUS.READY, {
959
+ phase: issueFilePhase.CHOOSE_SOURCE,
960
+ ...details
961
+ });
962
+ }
963
+
964
+ function issueDraftReviewState(details = {}) {
965
+ return machineState(STEP_STATUS.CONFIRM_FILES, {
966
+ phase: issueFilePhase.REVIEW_DRAFT,
967
+ ...details
968
+ });
969
+ }
970
+
971
+ function issueDraftPromptIsActive(state = {}) {
972
+ return [
973
+ STEP_STATUS.ATTEMPTING_EXECUTION,
974
+ STEP_STATUS.AWAITING_AGENT_RESULT,
975
+ STEP_STATUS.WAITING_FOR_INPUT
976
+ ].includes(state.status);
977
+ }
978
+
979
+ function workflowActionEnabled(session = {}, actionId = "") {
980
+ const normalizedActionId = normalizeText(actionId);
981
+ return Boolean((Array.isArray(session.actions) ? session.actions : [])
982
+ .find((action) => action.id === normalizedActionId)?.enabled);
983
+ }
984
+
985
+ function issueDraftResponseActionId(context = {}, state = {}) {
986
+ const stateActionId = normalizeText(state.promptActionId);
987
+ if (stateActionId) {
988
+ return stateActionId;
989
+ }
990
+ return workflowActionEnabled(context.session, rejectIssueDraftActionId)
991
+ ? rejectIssueDraftActionId
992
+ : draftIssueActionId;
993
+ }
994
+
995
+ async function submitIssueDraftAgentResult(context = {}, machine = {}, input = {}) {
996
+ assertAgentResultSource(context.session, input);
997
+ const state = await readState(context, machine);
998
+ const promptActionId = issueDraftResponseActionId(context, state);
999
+ switch (input.kind) {
1000
+ case STEP_INPUT_KIND.WAITING_FOR_INPUT:
1001
+ await writeState(context, machine, machineState(STEP_STATUS.WAITING_FOR_INPUT, {
1002
+ message: input.message,
1003
+ phase: issueFilePhase.DRAFTING,
1004
+ promptActionId,
1005
+ response: inputResponseText(input),
1006
+ source: input.source
1007
+ }));
1008
+ return;
1009
+
1010
+ case STEP_INPUT_KIND.CONFIRM_FILES:
1011
+ case STEP_INPUT_KIND.READY:
1012
+ await writeIssueFieldValues(context, input.fields);
1013
+ await writeState(context, machine, issueDraftReviewState({
1014
+ response: inputResponseText(input),
1015
+ source: input.source
1016
+ }));
1017
+ return;
1018
+
1019
+ default:
1020
+ throw unsupportedInputKind(input.kind, machine.stepId);
1021
+ }
1022
+ }
1023
+
1024
+ const issueFileMachine = {
1025
+ promptActionId: draftIssueActionId,
1026
+ stepId: ISSUE_FILE_STEP_ID,
717
1027
 
718
1028
  initialState(context = {}) {
719
- if (metadataExists(context.session, "issue_url")) {
720
- return machineState(STEP_STATUS.DONE);
1029
+ const filesReady = issueFilesAreReady(context.session);
1030
+ if (githubIssueShouldBeSkipped(context.session) && filesReady) {
1031
+ return issueSkipState(context.session);
721
1032
  }
722
- return issueFilesAreReady(context.session)
723
- ? machineState(STEP_STATUS.READY)
724
- : machineState(STEP_STATUS.WAITING_FOR_INPUT, {
725
- message: "Define and save the issue before creating it on GitHub."
726
- });
1033
+ if ((githubIssueIsReused(context.session) || githubIssueShouldBeCreated(context.session)) && filesReady && metadataExists(context.session, "issue_url")) {
1034
+ return machineState(STEP_STATUS.DONE, {
1035
+ phase: issueFilePhase.EXISTING_SELECTED
1036
+ });
1037
+ }
1038
+ return filesReady
1039
+ ? issueDraftReviewState()
1040
+ : issueSourceSelectionState();
727
1041
  },
728
1042
 
729
1043
  async view(context = {}) {
730
1044
  let state = await readState(context, this);
731
- if (metadataExists(context.session, "issue_url")) {
732
- state = machineState(STEP_STATUS.DONE);
733
- } else if (issueFilesAreReady(context.session) && state.status === STEP_STATUS.WAITING_FOR_INPUT && state.from !== STEP_STATUS.ATTEMPTING_EXECUTION) {
734
- state = machineState(STEP_STATUS.READY);
1045
+ const filesReady = issueFilesAreReady(context.session);
1046
+ const createGithubIssue = githubIssueShouldBeCreated(context.session);
1047
+ const skipGithubIssue = githubIssueShouldBeSkipped(context.session);
1048
+ if (skipGithubIssue && filesReady && state.status !== STEP_STATUS.CONFIRM_FILES && !issueDraftPromptIsActive(state)) {
1049
+ state = issueSkipState(context.session);
1050
+ } else if ((githubIssueIsReused(context.session) || createGithubIssue) && filesReady && metadataExists(context.session, "issue_url")) {
1051
+ state = machineState(STEP_STATUS.DONE, {
1052
+ phase: issueFilePhase.EXISTING_SELECTED
1053
+ });
1054
+ } else if (filesReady && state.status !== STEP_STATUS.CONFIRM_FILES && !issueDraftPromptIsActive(state)) {
1055
+ state = issueDraftReviewState({
1056
+ from: state.status
1057
+ });
1058
+ } else if (state.status === STEP_STATUS.DONE) {
1059
+ state = issueSourceSelectionState({
1060
+ from: STEP_STATUS.DONE,
1061
+ message: "Issue details are incomplete. Select the issue again or draft a new one."
1062
+ });
735
1063
  }
736
1064
 
737
1065
  switch (state.status) {
738
- case STEP_STATUS.DONE:
1066
+ case STEP_STATUS.READY:
739
1067
  return {
740
- actions: disableAction(context.session, "create_issue_on_gh", "The GitHub issue already exists."),
1068
+ actions: disableActions(context.session, {
1069
+ create_issue_on_gh: createGithubIssue
1070
+ ? "Save the issue details before creating the GitHub issue."
1071
+ : "This session continues without creating a GitHub issue.",
1072
+ [rejectIssueDraftActionId]: "Describe the work before requesting improvements."
1073
+ }),
741
1074
  next: nextForSession(context.session, {
742
- enabled: true
1075
+ disabledReason: "Describe the work before continuing."
743
1076
  }),
744
- stepMachine: publicState(this, state)
1077
+ stepMachine: publicState(this, {
1078
+ ...state,
1079
+ message: state.message || "Describe the work before continuing."
1080
+ })
745
1081
  };
746
1082
 
1083
+ case STEP_STATUS.ATTEMPTING_EXECUTION:
1084
+ return promptStepWaitingView(context, this, state, "Wait for Vibe64 to create the GitHub issue.");
1085
+
1086
+ case STEP_STATUS.AWAITING_AGENT_RESULT:
1087
+ return promptStepWaitingView(context, this, state, "Wait for Codex to draft the work details.");
1088
+
747
1089
  case STEP_STATUS.WAITING_FOR_INPUT:
1090
+ if (state.phase === issueFilePhase.CREATING_ISSUE) {
1091
+ return {
1092
+ actions: disableAction(context.session, "create_issue_on_gh", "Resolve the issue command before retrying."),
1093
+ interaction: commandFailureInteraction({
1094
+ prompt: state.message || "Could not create the GitHub issue. Explain what should happen, then retry.",
1095
+ title: "Issue command needs attention"
1096
+ }),
1097
+ next: nextForSession(context.session, {
1098
+ disabledReason: "Resolve the issue command before continuing."
1099
+ }),
1100
+ stepMachine: publicState(this, state)
1101
+ };
1102
+ }
1103
+ return promptStepWaitingForInputView(context, this, state, {
1104
+ actionId: issueDraftResponseActionId(context, state),
1105
+ prompt: state.message || "Codex needs more information before it can draft the issue.",
1106
+ skipInput: LET_CODEX_DECIDE_INPUT,
1107
+ title: "Describe the issue"
1108
+ });
1109
+
1110
+ case STEP_STATUS.CONFIRM_FILES: {
1111
+ const values = await readIssueFieldValues(context);
748
1112
  return {
749
- actions: disableAction(context.session, "create_issue_on_gh", "Resolve the issue command failure before retrying."),
750
- interaction: commandFailureInteraction({
751
- prompt: state.message || "The GitHub issue command failed. Explain what should happen, then retry the command.",
752
- title: "Issue command needs attention"
1113
+ actions: skipGithubIssue
1114
+ ? disableAction(context.session, "create_issue_on_gh", "This session continues without creating a GitHub issue.")
1115
+ : context.session.actions,
1116
+ interaction: issueInputInteraction(STEP_STATUS.CONFIRM_FILES, values, {
1117
+ createGithubIssue
753
1118
  }),
754
1119
  next: nextForSession(context.session, {
755
- disabledReason: "Resolve the GitHub issue command failure before continuing."
1120
+ disabledReason: createGithubIssue ? "Create the GitHub issue before continuing." : "",
1121
+ enabled: !createGithubIssue
756
1122
  }),
757
- stepMachine: publicState(this, state)
1123
+ stepMachine: publicState(this, {
1124
+ ...state,
1125
+ message: state.message || (createGithubIssue ? "Review the saved issue draft." : "Review the saved work description.")
1126
+ })
758
1127
  };
1128
+ }
759
1129
 
760
- case STEP_STATUS.READY:
761
- case STEP_STATUS.ATTEMPTING_EXECUTION:
762
- case STEP_STATUS.FAILED:
763
- default:
1130
+ case STEP_STATUS.DONE:
764
1131
  return {
1132
+ actions: disableActions(context.session, {
1133
+ create_issue_on_gh: state.skipReason || "The GitHub issue state is already resolved.",
1134
+ [draftIssueActionId]: state.skipReason || "Work details are already saved.",
1135
+ [rejectIssueDraftActionId]: state.skipReason || "Work details are already saved."
1136
+ }),
765
1137
  next: nextForSession(context.session, {
766
- disabledReason: "Create the GitHub issue before continuing."
1138
+ enabled: true
767
1139
  }),
768
- stepMachine: publicState(this, state)
1140
+ stepMachine: publicState(this, {
1141
+ ...state,
1142
+ message: state.message || "Work details are ready."
1143
+ })
769
1144
  };
770
- }
771
- },
772
1145
 
773
- async submitInput(context = {}) {
774
- const state = await readState(context, this);
775
- const input = normalizeMachineInput(context.input);
776
- switch (state.status) {
777
- case STEP_STATUS.WAITING_FOR_INPUT:
778
1146
  case STEP_STATUS.FAILED:
779
- if (input.kind === STEP_INPUT_KIND.CONSIDER_RESOLVED || input.kind === STEP_INPUT_KIND.USER_RESPONSE) {
780
- await writeState(context, this, machineState(STEP_STATUS.READY, {
781
- response: input.text || input.fields.response,
782
- source: input.source
783
- }));
784
- return;
785
- }
786
- throw unsupportedInputKind(input.kind, this.stepId);
1147
+ return {
1148
+ next: nextForSession(context.session, {
1149
+ disabledReason: "Resolve the work definition failure before continuing."
1150
+ }),
1151
+ stepMachine: publicState(this, {
1152
+ ...state,
1153
+ message: state.message || "Work definition failed."
1154
+ })
1155
+ };
787
1156
 
788
- case STEP_STATUS.READY:
789
- case STEP_STATUS.ATTEMPTING_EXECUTION:
790
- case STEP_STATUS.DONE:
791
1157
  default:
792
- throw vibe64Error("The GitHub issue step cannot accept input right now.", "vibe64_step_input_not_available");
1158
+ return {
1159
+ next: nextForSession(context.session, {
1160
+ disabledReason: "Describe the work before continuing."
1161
+ }),
1162
+ stepMachine: publicState(this, state)
1163
+ };
793
1164
  }
794
1165
  },
795
1166
 
796
1167
  async actionStarted(context = {}) {
797
- if (context.actionId !== "create_issue_on_gh") {
1168
+ if (context.actionId === draftIssueActionId) {
1169
+ await writeState(context, this, machineState(STEP_STATUS.AWAITING_AGENT_RESULT, {
1170
+ phase: issueFilePhase.DRAFTING,
1171
+ promptActionId: context.actionId
1172
+ }));
798
1173
  return;
799
1174
  }
800
1175
 
801
- const state = await readState(context, this);
802
- switch (state.status) {
803
- case STEP_STATUS.READY:
804
- case STEP_STATUS.FAILED:
805
- await writeState(context, this, machineState(STEP_STATUS.ATTEMPTING_EXECUTION));
806
- return;
1176
+ if (context.actionId === "create_issue_on_gh") {
1177
+ await writeState(context, this, machineState(STEP_STATUS.ATTEMPTING_EXECUTION, {
1178
+ phase: issueFilePhase.CREATING_ISSUE
1179
+ }));
1180
+ return;
1181
+ }
807
1182
 
808
- case STEP_STATUS.ATTEMPTING_EXECUTION:
809
- case STEP_STATUS.DONE:
810
- default:
811
- return;
1183
+ if (context.actionId === rejectIssueDraftActionId) {
1184
+ await writeState(context, this, machineState(STEP_STATUS.AWAITING_AGENT_RESULT, {
1185
+ phase: issueFilePhase.DRAFTING,
1186
+ promptActionId: context.actionId
1187
+ }));
812
1188
  }
813
1189
  },
814
1190
 
815
1191
  async actionFinished(context = {}) {
816
- if (context.actionId !== "create_issue_on_gh") {
1192
+ if (context.actionId === "create_issue_on_gh") {
1193
+ if (await commandSucceeded(context, "issue_url")) {
1194
+ await writeState(context, this, machineState(STEP_STATUS.DONE, {
1195
+ phase: issueFilePhase.EXISTING_SELECTED
1196
+ }));
1197
+ return;
1198
+ }
1199
+ await writeState(context, this, machineState(STEP_STATUS.WAITING_FOR_INPUT, {
1200
+ from: STEP_STATUS.ATTEMPTING_EXECUTION,
1201
+ message: normalizeText(context.actionResult?.message) || "Could not create the GitHub issue.",
1202
+ output: normalizeText(context.actionResult?.output),
1203
+ phase: issueFilePhase.CREATING_ISSUE
1204
+ }));
817
1205
  return;
818
1206
  }
1207
+ },
819
1208
 
1209
+ async submitInput(context = {}) {
820
1210
  const state = await readState(context, this);
1211
+ const input = normalizeMachineInput(context.input);
1212
+
821
1213
  switch (state.status) {
822
- case STEP_STATUS.ATTEMPTING_EXECUTION:
823
1214
  case STEP_STATUS.READY:
1215
+ if (input.kind === STEP_INPUT_KIND.CONFIRM_FILES || input.kind === STEP_INPUT_KIND.READY) {
1216
+ await writeIssueFieldValues(context, input.fields);
1217
+ await writeState(context, this, issueDraftReviewState({
1218
+ response: inputResponseText(input),
1219
+ source: input.source
1220
+ }));
1221
+ return;
1222
+ }
1223
+ throw unsupportedInputKind(input.kind, this.stepId);
1224
+
1225
+ case STEP_STATUS.AWAITING_AGENT_RESULT:
1226
+ await submitIssueDraftAgentResult(context, this, input);
1227
+ return;
1228
+
824
1229
  case STEP_STATUS.WAITING_FOR_INPUT:
1230
+ if (state.phase === issueFilePhase.CREATING_ISSUE && (input.kind === STEP_INPUT_KIND.CONSIDER_RESOLVED || input.kind === STEP_INPUT_KIND.USER_RESPONSE)) {
1231
+ await writeState(context, this, issueDraftReviewState({
1232
+ message: input.message,
1233
+ response: inputResponseText(input),
1234
+ source: input.source
1235
+ }));
1236
+ return;
1237
+ }
1238
+ if (input.source === "codex") {
1239
+ await submitIssueDraftAgentResult(context, this, input);
1240
+ return;
1241
+ }
1242
+ throw unsupportedInputKind(input.kind, this.stepId);
1243
+
1244
+ case STEP_STATUS.CONFIRM_FILES:
1245
+ if (input.kind === STEP_INPUT_KIND.CONFIRM_FILES || input.kind === STEP_INPUT_KIND.READY) {
1246
+ await writeIssueFieldValues(context, input.fields);
1247
+ await writeState(context, this, githubIssueShouldBeSkipped(context.session)
1248
+ ? issueSkipState(context.session)
1249
+ : issueDraftReviewState({
1250
+ response: inputResponseText(input),
1251
+ source: input.source
1252
+ }));
1253
+ return;
1254
+ }
1255
+ throw unsupportedInputKind(input.kind, this.stepId);
1256
+
825
1257
  case STEP_STATUS.FAILED:
826
- await writeState(context, this, await commandSucceeded(context, "issue_url")
827
- ? machineState(STEP_STATUS.DONE)
828
- : machineState(STEP_STATUS.WAITING_FOR_INPUT, {
829
- from: STEP_STATUS.ATTEMPTING_EXECUTION,
830
- message: normalizeText(context.actionResult?.message),
831
- output: normalizeText(context.actionResult?.output)
832
- }));
833
- return;
1258
+ if (input.kind === STEP_INPUT_KIND.CONSIDER_RESOLVED || input.kind === STEP_INPUT_KIND.USER_RESPONSE) {
1259
+ await writeState(context, this, issueSourceSelectionState({
1260
+ message: input.message,
1261
+ response: inputResponseText(input),
1262
+ source: input.source
1263
+ }));
1264
+ return;
1265
+ }
1266
+ throw unsupportedInputKind(input.kind, this.stepId);
834
1267
 
835
1268
  case STEP_STATUS.DONE:
836
1269
  default:
837
- return;
1270
+ throw vibe64Error("The issue step is already complete.", "vibe64_step_input_not_available");
1271
+ }
1272
+ },
1273
+
1274
+ inputCompletionMessage(context = {}) {
1275
+ const input = normalizeMachineInput(context.input);
1276
+ if (![STEP_INPUT_KIND.READY, STEP_INPUT_KIND.CONFIRM_FILES].includes(input.kind)) {
1277
+ return "";
838
1278
  }
1279
+ if (input.source === "codex") {
1280
+ return issueDraftConversationMessage(context, input, { proposed: true });
1281
+ }
1282
+ if (input.source === "ui") {
1283
+ return issueDraftConversationMessage(context, input);
1284
+ }
1285
+ return githubIssueShouldBeSkipped(context.session)
1286
+ ? "Work description saved."
1287
+ : "Issue draft submitted for review.";
1288
+ },
1289
+
1290
+ promptInstruction() {
1291
+ return currentStepHelperInstruction({
1292
+ doneFields: {
1293
+ body: "Markdown issue body describing the requested change, context, and acceptance criteria.",
1294
+ title: "Concise GitHub issue title.",
1295
+ word: "Short Vibe64 session label/word derived from the issue title."
1296
+ },
1297
+ doneMeaning: "You have enough information to propose a GitHub issue title, body, and Vibe64 session label for user review.",
1298
+ waitingForInputMeaning: "You need more information from the user before drafting the issue."
1299
+ });
839
1300
  }
840
1301
  };
841
1302
 
842
1303
  const makePlanMachine = {
843
1304
  promptActionId: "make_plan",
844
- stepId: planMadeStepId,
1305
+ stepId: planAndExecuteStepId,
845
1306
 
846
1307
  initialState() {
847
1308
  return machineState(STEP_STATUS.READY);
@@ -866,6 +1327,13 @@ const makePlanMachine = {
866
1327
  return handleStandardPromptInput(context, this);
867
1328
  },
868
1329
 
1330
+ inputCompletionMessage(context = {}) {
1331
+ const input = normalizeMachineInput(context.input);
1332
+ return input.kind === STEP_INPUT_KIND.READY
1333
+ ? "Plan submitted for review."
1334
+ : "";
1335
+ },
1336
+
869
1337
  async actionStarted(context = {}) {
870
1338
  return markPromptActionStarted(context, this, "make_plan");
871
1339
  },
@@ -887,6 +1355,13 @@ const seedPlanMadeMachine = {
887
1355
  return markPromptActionStarted(context, this, "make_seed_plan");
888
1356
  },
889
1357
 
1358
+ inputCompletionMessage(context = {}) {
1359
+ const input = normalizeMachineInput(context.input);
1360
+ return input.kind === STEP_INPUT_KIND.READY
1361
+ ? "Seed plan submitted for review."
1362
+ : "";
1363
+ },
1364
+
890
1365
  promptInstruction() {
891
1366
  return currentStepHelperInstruction({
892
1367
  doneMeaning: "The seed implementation plan has been written in the Codex response and is ready for execution.",
@@ -897,7 +1372,7 @@ const seedPlanMadeMachine = {
897
1372
 
898
1373
  const executePlanMachine = {
899
1374
  promptActionId: "execute_plan",
900
- stepId: planExecutedStepId,
1375
+ stepId: planAndExecuteStepId,
901
1376
 
902
1377
  initialState() {
903
1378
  return machineState(STEP_STATUS.READY);
@@ -922,6 +1397,13 @@ const executePlanMachine = {
922
1397
  return handleStandardPromptInput(context, this);
923
1398
  },
924
1399
 
1400
+ inputCompletionMessage(context = {}) {
1401
+ const input = normalizeMachineInput(context.input);
1402
+ return input.kind === STEP_INPUT_KIND.READY
1403
+ ? "Implementation submitted for review."
1404
+ : "";
1405
+ },
1406
+
925
1407
  async actionStarted(context = {}) {
926
1408
  return markPromptActionStarted(context, this, "execute_plan");
927
1409
  },
@@ -934,6 +1416,195 @@ const executePlanMachine = {
934
1416
  }
935
1417
  };
936
1418
 
1419
+ const planAndExecutePhase = Object.freeze({
1420
+ EXECUTING: "executing",
1421
+ PLANNING: "planning",
1422
+ PLAN_READY: "plan_ready"
1423
+ });
1424
+
1425
+ function planAndExecuteReadyState(details = {}) {
1426
+ return machineState(STEP_STATUS.READY, {
1427
+ phase: planAndExecutePhase.PLANNING,
1428
+ ...details
1429
+ });
1430
+ }
1431
+
1432
+ function planAndExecutePlanReadyState(details = {}) {
1433
+ return machineState(STEP_STATUS.READY, {
1434
+ phase: planAndExecutePhase.PLAN_READY,
1435
+ ...details
1436
+ });
1437
+ }
1438
+
1439
+ async function markPlanAndExecuteActionStarted(context = {}, machine = {}, {
1440
+ actionId = "",
1441
+ phase = ""
1442
+ } = {}) {
1443
+ if (context.actionId !== actionId) {
1444
+ return;
1445
+ }
1446
+ const state = await readState(context, machine);
1447
+ switch (state.status) {
1448
+ case STEP_STATUS.READY:
1449
+ case STEP_STATUS.FAILED:
1450
+ case STEP_STATUS.WAITING_FOR_INPUT:
1451
+ await writeState(context, machine, machineState(STEP_STATUS.AWAITING_AGENT_RESULT, {
1452
+ phase,
1453
+ response: state.response,
1454
+ source: state.source
1455
+ }));
1456
+ return;
1457
+ case STEP_STATUS.AWAITING_AGENT_RESULT:
1458
+ case STEP_STATUS.DONE:
1459
+ default:
1460
+ return;
1461
+ }
1462
+ }
1463
+
1464
+ const planAndExecuteMachine = {
1465
+ promptActionId: "make_plan",
1466
+ stepId: planAndExecuteStepId,
1467
+
1468
+ initialState(context = {}) {
1469
+ if (metadataExists(context.session, IMPLEMENTATION_DONE_METADATA)) {
1470
+ return machineState(STEP_STATUS.DONE, {
1471
+ phase: planAndExecutePhase.EXECUTING
1472
+ });
1473
+ }
1474
+ if (metadataExists(context.session, PLAN_READY_METADATA)) {
1475
+ return planAndExecutePlanReadyState();
1476
+ }
1477
+ return planAndExecuteReadyState();
1478
+ },
1479
+
1480
+ async view(context = {}) {
1481
+ let state = await readState(context, this);
1482
+ if (metadataExists(context.session, IMPLEMENTATION_DONE_METADATA)) {
1483
+ state = machineState(STEP_STATUS.DONE, {
1484
+ phase: planAndExecutePhase.EXECUTING
1485
+ });
1486
+ } else if (
1487
+ metadataExists(context.session, PLAN_READY_METADATA) &&
1488
+ ![STEP_STATUS.AWAITING_AGENT_RESULT, STEP_STATUS.WAITING_FOR_INPUT].includes(state.status)
1489
+ ) {
1490
+ state = planAndExecutePlanReadyState({
1491
+ from: state.status
1492
+ });
1493
+ }
1494
+
1495
+ switch (state.status) {
1496
+ case STEP_STATUS.DONE:
1497
+ return promptStepDoneView(context, this, state);
1498
+ case STEP_STATUS.WAITING_FOR_INPUT:
1499
+ return promptStepWaitingForInputView(context, this, state, {
1500
+ actionId: state.phase === planAndExecutePhase.EXECUTING ? "execute_plan" : "make_plan",
1501
+ prompt: state.message || "Codex needs more information before this step can continue.",
1502
+ skipInput: LET_CODEX_DECIDE_INPUT
1503
+ });
1504
+ case STEP_STATUS.READY:
1505
+ case STEP_STATUS.AWAITING_AGENT_RESULT:
1506
+ case STEP_STATUS.FAILED:
1507
+ default:
1508
+ return promptStepWaitingView(
1509
+ context,
1510
+ this,
1511
+ state,
1512
+ state.phase === planAndExecutePhase.PLAN_READY
1513
+ ? "Ask Codex to execute the plan before continuing."
1514
+ : "Ask Codex to make the plan before continuing."
1515
+ );
1516
+ }
1517
+ },
1518
+
1519
+ async submitInput(context = {}) {
1520
+ const state = await readState(context, this);
1521
+ const input = normalizeMachineInput(context.input);
1522
+ if (state.status === STEP_STATUS.AWAITING_AGENT_RESULT) {
1523
+ assertAgentResultSource(context.session, input);
1524
+ }
1525
+
1526
+ switch (state.status) {
1527
+ case STEP_STATUS.READY:
1528
+ case STEP_STATUS.AWAITING_AGENT_RESULT:
1529
+ case STEP_STATUS.WAITING_FOR_INPUT:
1530
+ case STEP_STATUS.FAILED:
1531
+ if (input.kind === STEP_INPUT_KIND.WAITING_FOR_INPUT) {
1532
+ await writeState(context, this, machineState(STEP_STATUS.WAITING_FOR_INPUT, {
1533
+ from: STEP_STATUS.AWAITING_AGENT_RESULT,
1534
+ message: input.message,
1535
+ phase: state.phase || planAndExecutePhase.PLANNING,
1536
+ source: input.source
1537
+ }));
1538
+ return;
1539
+ }
1540
+ if (input.kind === STEP_INPUT_KIND.USER_RESPONSE) {
1541
+ await writeState(context, this, machineState(STEP_STATUS.READY, {
1542
+ message: input.message,
1543
+ phase: state.phase || planAndExecutePhase.PLANNING,
1544
+ response: inputResponseText(input),
1545
+ source: input.source
1546
+ }));
1547
+ return;
1548
+ }
1549
+ if (input.kind === STEP_INPUT_KIND.READY || input.kind === STEP_INPUT_KIND.CONSIDER_RESOLVED) {
1550
+ if (state.phase === planAndExecutePhase.EXECUTING) {
1551
+ await context.runtime.store.writeMetadataValue(context.session.sessionId, IMPLEMENTATION_DONE_METADATA, "yes");
1552
+ await writeState(context, this, machineState(STEP_STATUS.DONE, {
1553
+ message: input.message,
1554
+ phase: planAndExecutePhase.EXECUTING,
1555
+ source: input.source
1556
+ }));
1557
+ return;
1558
+ }
1559
+ await context.runtime.store.writeMetadataValue(context.session.sessionId, PLAN_READY_METADATA, "yes");
1560
+ await writeState(context, this, planAndExecutePlanReadyState({
1561
+ message: input.message,
1562
+ source: input.source
1563
+ }));
1564
+ return;
1565
+ }
1566
+ throw unsupportedInputKind(input.kind, this.stepId);
1567
+
1568
+ case STEP_STATUS.DONE:
1569
+ default:
1570
+ throw vibe64Error("This step is already complete.", "vibe64_step_input_not_available");
1571
+ }
1572
+ },
1573
+
1574
+ inputCompletionMessage(context = {}) {
1575
+ const input = normalizeMachineInput(context.input);
1576
+ if (input.kind !== STEP_INPUT_KIND.READY) {
1577
+ return "";
1578
+ }
1579
+ return context.session.stepMachine?.phase === planAndExecutePhase.EXECUTING
1580
+ ? "Implementation submitted for review."
1581
+ : "Plan submitted for review.";
1582
+ },
1583
+
1584
+ async actionStarted(context = {}) {
1585
+ await markPlanAndExecuteActionStarted(context, this, {
1586
+ actionId: "make_plan",
1587
+ phase: planAndExecutePhase.PLANNING
1588
+ });
1589
+ await markPlanAndExecuteActionStarted(context, this, {
1590
+ actionId: "execute_plan",
1591
+ phase: planAndExecutePhase.EXECUTING
1592
+ });
1593
+ },
1594
+
1595
+ promptInstruction({ action = {} } = {}) {
1596
+ return normalizeText(action.id) === "execute_plan"
1597
+ ? currentStepHelperInstruction({
1598
+ doneMeaning: "The implementation work is complete enough to continue to review.",
1599
+ waitingForInputMeaning: "You cannot continue implementation without a user decision or missing project detail."
1600
+ })
1601
+ : currentStepHelperInstruction({
1602
+ doneMeaning: "The implementation plan has been written in the Codex response and is ready for execution.",
1603
+ waitingForInputMeaning: "You cannot make a useful plan without a user decision or clarification."
1604
+ });
1605
+ }
1606
+ };
1607
+
937
1608
  const seedPlanExecutedMachine = {
938
1609
  ...executePlanMachine,
939
1610
  promptActionId: "execute_seed_plan",
@@ -943,6 +1614,13 @@ const seedPlanExecutedMachine = {
943
1614
  return markPromptActionStarted(context, this, "execute_seed_plan");
944
1615
  },
945
1616
 
1617
+ inputCompletionMessage(context = {}) {
1618
+ const input = normalizeMachineInput(context.input);
1619
+ return input.kind === STEP_INPUT_KIND.READY
1620
+ ? "Seed implementation submitted for review."
1621
+ : "";
1622
+ },
1623
+
946
1624
  promptInstruction() {
947
1625
  return currentStepHelperInstruction({
948
1626
  doneMeaning: "The seed implementation work is complete enough to continue.",
@@ -960,6 +1638,13 @@ const deepUiCheckMachine = {
960
1638
  return markPromptActionStarted(context, this, "run_deep_ui_check");
961
1639
  },
962
1640
 
1641
+ inputCompletionMessage(context = {}) {
1642
+ const input = normalizeMachineInput(context.input);
1643
+ return input.kind === STEP_INPUT_KIND.READY
1644
+ ? "The user interface check is done."
1645
+ : "";
1646
+ },
1647
+
963
1648
  promptInstruction() {
964
1649
  return currentStepHelperInstruction({
965
1650
  doneMeaning: "The deep UI check has been completed or intentionally found no required fix.",
@@ -968,87 +1653,357 @@ const deepUiCheckMachine = {
968
1653
  }
969
1654
  };
970
1655
 
971
- const reviewRunMachine = {
972
- ...executePlanMachine,
1656
+ const reviewAndValidatePhase = Object.freeze({
1657
+ REVIEW: "review",
1658
+ VALIDATION: "validation"
1659
+ });
1660
+
1661
+ const reviewAndValidateCommandActionIds = Object.freeze(["update_code_index", "run_automated_checks"]);
1662
+
1663
+ function reviewAndValidateComplete(session = {}) {
1664
+ return metadataExists(session, REVIEW_DESLOP_COMPLETED_METADATA) &&
1665
+ metadataExists(session, "code_index_updated") &&
1666
+ metadataExists(session, "automated_checks_passed");
1667
+ }
1668
+
1669
+ const reviewAndValidateMachine = {
973
1670
  promptActionId: "run_deslop",
974
- stepId: reviewRunStepId,
1671
+ stepId: reviewAndValidateStepId,
975
1672
 
976
- async actionStarted(context = {}) {
977
- return markPromptActionStarted(context, this, "run_deslop");
1673
+ initialState(context = {}) {
1674
+ if (reviewAndValidateComplete(context.session)) {
1675
+ return machineState(STEP_STATUS.DONE);
1676
+ }
1677
+ return machineState(STEP_STATUS.READY, {
1678
+ phase: metadataExists(context.session, REVIEW_DESLOP_COMPLETED_METADATA)
1679
+ ? reviewAndValidatePhase.VALIDATION
1680
+ : reviewAndValidatePhase.REVIEW
1681
+ });
978
1682
  },
979
1683
 
980
- promptInstruction() {
981
- return currentStepHelperInstruction({
982
- doneMeaning: "The review/deslop loop has completed and only acceptable low-risk findings remain.",
983
- waitingForInputMeaning: "You cannot complete review/deslop without a user decision."
984
- });
985
- }
986
- };
1684
+ async view(context = {}) {
1685
+ let state = await readState(context, this);
1686
+ if (reviewAndValidateComplete(context.session)) {
1687
+ state = machineState(STEP_STATUS.DONE);
1688
+ } else if (
1689
+ metadataExists(context.session, REVIEW_DESLOP_COMPLETED_METADATA) &&
1690
+ ![STEP_STATUS.AWAITING_AGENT_RESULT, STEP_STATUS.ATTEMPTING_EXECUTION, STEP_STATUS.WAITING_FOR_INPUT].includes(state.status)
1691
+ ) {
1692
+ state = machineState(STEP_STATUS.READY, {
1693
+ phase: reviewAndValidatePhase.VALIDATION
1694
+ });
1695
+ }
987
1696
 
988
- const projectKnowledgeUpdatedMachine = {
989
- ...executePlanMachine,
990
- promptActionId: "update_project_knowledge",
991
- stepId: projectKnowledgeUpdatedStepId,
1697
+ switch (state.status) {
1698
+ case STEP_STATUS.DONE:
1699
+ return promptStepDoneView(context, this, state);
1700
+ case STEP_STATUS.WAITING_FOR_INPUT:
1701
+ return state.phase === reviewAndValidatePhase.VALIDATION
1702
+ ? {
1703
+ interaction: commandFailureInteraction({
1704
+ prompt: state.message || "The validation command failed. Explain what should happen, then retry validation.",
1705
+ title: state.title || "Validation needs attention"
1706
+ }),
1707
+ next: nextForSession(context.session, {
1708
+ disabledReason: "Run review/deslop, update the code index, and run automated checks successfully before continuing."
1709
+ }),
1710
+ stepMachine: publicState(this, state)
1711
+ }
1712
+ : promptStepWaitingForInputView(context, this, state, {
1713
+ actionId: "run_deslop",
1714
+ prompt: state.message || "Codex needs more information before review/deslop can continue.",
1715
+ skipInput: LET_CODEX_DECIDE_INPUT,
1716
+ title: "Review needs input"
1717
+ });
1718
+ case STEP_STATUS.READY:
1719
+ case STEP_STATUS.AWAITING_AGENT_RESULT:
1720
+ case STEP_STATUS.ATTEMPTING_EXECUTION:
1721
+ case STEP_STATUS.FAILED:
1722
+ default:
1723
+ return promptStepWaitingView(
1724
+ context,
1725
+ this,
1726
+ state,
1727
+ "Run review/deslop, update the code index, and run automated checks successfully before continuing."
1728
+ );
1729
+ }
1730
+ },
1731
+
1732
+ async submitInput(context = {}) {
1733
+ const state = await readState(context, this);
1734
+ const input = normalizeMachineInput(context.input);
1735
+ if (state.status === STEP_STATUS.AWAITING_AGENT_RESULT) {
1736
+ assertAgentResultSource(context.session, input);
1737
+ }
1738
+
1739
+ switch (state.status) {
1740
+ case STEP_STATUS.READY:
1741
+ case STEP_STATUS.AWAITING_AGENT_RESULT:
1742
+ case STEP_STATUS.WAITING_FOR_INPUT:
1743
+ case STEP_STATUS.FAILED:
1744
+ if (input.kind === STEP_INPUT_KIND.WAITING_FOR_INPUT) {
1745
+ await writeState(context, this, machineState(STEP_STATUS.WAITING_FOR_INPUT, {
1746
+ from: STEP_STATUS.AWAITING_AGENT_RESULT,
1747
+ message: input.message,
1748
+ phase: state.phase || reviewAndValidatePhase.REVIEW,
1749
+ source: input.source
1750
+ }));
1751
+ return;
1752
+ }
1753
+ if (input.kind === STEP_INPUT_KIND.USER_RESPONSE || input.kind === STEP_INPUT_KIND.CONSIDER_RESOLVED) {
1754
+ await writeState(context, this, machineState(STEP_STATUS.READY, {
1755
+ phase: state.phase || reviewAndValidatePhase.REVIEW,
1756
+ response: inputResponseText(input),
1757
+ source: input.source
1758
+ }));
1759
+ return;
1760
+ }
1761
+ if (input.kind === STEP_INPUT_KIND.READY) {
1762
+ await context.runtime.store.writeMetadataValue(
1763
+ context.session.sessionId,
1764
+ REVIEW_DESLOP_COMPLETED_METADATA,
1765
+ "yes"
1766
+ );
1767
+ await writeState(context, this, machineState(STEP_STATUS.READY, {
1768
+ message: input.message,
1769
+ phase: reviewAndValidatePhase.VALIDATION,
1770
+ source: input.source
1771
+ }));
1772
+ return;
1773
+ }
1774
+ throw unsupportedInputKind(input.kind, this.stepId);
1775
+
1776
+ case STEP_STATUS.DONE:
1777
+ case STEP_STATUS.ATTEMPTING_EXECUTION:
1778
+ default:
1779
+ throw vibe64Error("This step cannot accept input right now.", "vibe64_step_input_not_available");
1780
+ }
1781
+ },
1782
+
1783
+ inputCompletionMessage(context = {}) {
1784
+ const input = normalizeMachineInput(context.input);
1785
+ return input.kind === STEP_INPUT_KIND.READY
1786
+ ? "Review/deslop completed."
1787
+ : "";
1788
+ },
992
1789
 
993
1790
  async actionStarted(context = {}) {
994
- return markPromptActionStarted(context, this, "update_project_knowledge");
1791
+ if (context.actionId === "run_deslop") {
1792
+ return markPromptActionStarted(context, this, "run_deslop");
1793
+ }
1794
+ if (!reviewAndValidateCommandActionIds.includes(context.actionId)) {
1795
+ return;
1796
+ }
1797
+ const state = await readState(context, this);
1798
+ switch (state.status) {
1799
+ case STEP_STATUS.READY:
1800
+ case STEP_STATUS.FAILED:
1801
+ case STEP_STATUS.WAITING_FOR_INPUT:
1802
+ await writeState(context, this, machineState(STEP_STATUS.ATTEMPTING_EXECUTION, {
1803
+ actionId: context.actionId,
1804
+ phase: reviewAndValidatePhase.VALIDATION
1805
+ }));
1806
+ return;
1807
+
1808
+ case STEP_STATUS.ATTEMPTING_EXECUTION:
1809
+ case STEP_STATUS.DONE:
1810
+ default:
1811
+ return;
1812
+ }
1813
+ },
1814
+
1815
+ async actionFinished(context = {}) {
1816
+ if (!reviewAndValidateCommandActionIds.includes(context.actionId)) {
1817
+ return;
1818
+ }
1819
+ const state = await readState(context, this);
1820
+ switch (state.status) {
1821
+ case STEP_STATUS.ATTEMPTING_EXECUTION:
1822
+ case STEP_STATUS.READY:
1823
+ case STEP_STATUS.WAITING_FOR_INPUT:
1824
+ case STEP_STATUS.FAILED:
1825
+ if (reviewAndValidateComplete(await context.runtime.getSession(context.session.sessionId))) {
1826
+ await writeState(context, this, machineState(STEP_STATUS.DONE));
1827
+ return;
1828
+ }
1829
+ if (actionCompleted(context.actionResult)) {
1830
+ await writeState(context, this, machineState(STEP_STATUS.READY, {
1831
+ phase: reviewAndValidatePhase.VALIDATION
1832
+ }));
1833
+ return;
1834
+ }
1835
+ await writeState(context, this, machineState(STEP_STATUS.WAITING_FOR_INPUT, {
1836
+ from: STEP_STATUS.ATTEMPTING_EXECUTION,
1837
+ message: normalizeText(context.actionResult?.message),
1838
+ output: normalizeText(context.actionResult?.output),
1839
+ phase: reviewAndValidatePhase.VALIDATION,
1840
+ title: "Validation needs attention"
1841
+ }));
1842
+ return;
1843
+
1844
+ case STEP_STATUS.DONE:
1845
+ default:
1846
+ return;
1847
+ }
995
1848
  },
996
1849
 
997
1850
  promptInstruction() {
998
1851
  return currentStepHelperInstruction({
999
- doneMeaning: "Project knowledge has been updated or there is no adapter-supported project knowledge to update.",
1000
- waitingForInputMeaning: "You cannot update project knowledge without a user decision."
1852
+ doneMeaning: "The review/deslop loop has completed and only acceptable low-risk findings remain.",
1853
+ waitingForInputMeaning: "You cannot complete review/deslop without a user decision."
1001
1854
  });
1002
1855
  }
1003
1856
  };
1004
1857
 
1005
- const reportCreatedMachine = {
1858
+ const reportAndKnowledgePhase = Object.freeze({
1859
+ KNOWLEDGE: "knowledge",
1860
+ REPORT: "report"
1861
+ });
1862
+
1863
+ function reportAndKnowledgeComplete(session = {}) {
1864
+ return artifactIsReady(session, REPORT_ARTIFACT) &&
1865
+ metadataExists(session, PROJECT_KNOWLEDGE_UPDATED_METADATA);
1866
+ }
1867
+
1868
+ const reportAndKnowledgeUpdatedMachine = {
1006
1869
  promptActionId: "write_report",
1007
- stepId: reportCreatedStepId,
1870
+ stepId: reportAndKnowledgeUpdatedStepId,
1008
1871
 
1009
1872
  initialState(context = {}) {
1010
- return artifactIsReady(context.session, REPORT_ARTIFACT)
1011
- ? machineState(STEP_STATUS.DONE)
1012
- : machineState(STEP_STATUS.READY);
1873
+ if (reportAndKnowledgeComplete(context.session)) {
1874
+ return machineState(STEP_STATUS.DONE);
1875
+ }
1876
+ return machineState(STEP_STATUS.READY, {
1877
+ phase: artifactIsReady(context.session, REPORT_ARTIFACT)
1878
+ ? reportAndKnowledgePhase.KNOWLEDGE
1879
+ : reportAndKnowledgePhase.REPORT
1880
+ });
1013
1881
  },
1014
1882
 
1015
1883
  async view(context = {}) {
1016
1884
  let state = await readState(context, this);
1017
- if (artifactIsReady(context.session, REPORT_ARTIFACT)) {
1885
+ if (reportAndKnowledgeComplete(context.session)) {
1018
1886
  state = machineState(STEP_STATUS.DONE);
1887
+ } else if (
1888
+ artifactIsReady(context.session, REPORT_ARTIFACT) &&
1889
+ ![STEP_STATUS.AWAITING_AGENT_RESULT, STEP_STATUS.WAITING_FOR_INPUT].includes(state.status)
1890
+ ) {
1891
+ state = machineState(STEP_STATUS.READY, {
1892
+ phase: reportAndKnowledgePhase.KNOWLEDGE
1893
+ });
1019
1894
  }
1020
1895
 
1021
1896
  switch (state.status) {
1022
1897
  case STEP_STATUS.DONE:
1023
1898
  return promptStepDoneView(context, this, state);
1024
1899
  case STEP_STATUS.WAITING_FOR_INPUT:
1025
- return promptStepWaitingForInputView(context, this, state);
1900
+ return promptStepWaitingForInputView(context, this, state, {
1901
+ actionId: state.phase === reportAndKnowledgePhase.KNOWLEDGE ? "update_project_knowledge" : "write_report",
1902
+ prompt: state.message || "Codex needs more information before this step can continue.",
1903
+ skipInput: LET_CODEX_DECIDE_INPUT
1904
+ });
1026
1905
  case STEP_STATUS.READY:
1027
1906
  case STEP_STATUS.AWAITING_AGENT_RESULT:
1028
1907
  case STEP_STATUS.FAILED:
1029
1908
  default:
1030
- return promptStepWaitingView(context, this, state, "Write the session report before updating project knowledge.");
1909
+ return promptStepWaitingView(context, this, state, "Write the report and update project knowledge before continuing.");
1031
1910
  }
1032
1911
  },
1033
1912
 
1034
1913
  async submitInput(context = {}) {
1035
- return handleStandardPromptInput(context, this, {
1036
- responseArtifact: REPORT_ARTIFACT
1037
- });
1914
+ const state = await readState(context, this);
1915
+ const input = normalizeMachineInput(context.input);
1916
+ if (state.status === STEP_STATUS.AWAITING_AGENT_RESULT) {
1917
+ assertAgentResultSource(context.session, input);
1918
+ }
1919
+
1920
+ switch (state.status) {
1921
+ case STEP_STATUS.READY:
1922
+ case STEP_STATUS.AWAITING_AGENT_RESULT:
1923
+ case STEP_STATUS.WAITING_FOR_INPUT:
1924
+ case STEP_STATUS.FAILED:
1925
+ if (input.kind === STEP_INPUT_KIND.WAITING_FOR_INPUT) {
1926
+ await writeState(context, this, machineState(STEP_STATUS.WAITING_FOR_INPUT, {
1927
+ from: STEP_STATUS.AWAITING_AGENT_RESULT,
1928
+ message: input.message,
1929
+ phase: state.phase || reportAndKnowledgePhase.REPORT,
1930
+ source: input.source
1931
+ }));
1932
+ return;
1933
+ }
1934
+ if (input.kind !== STEP_INPUT_KIND.READY && input.kind !== STEP_INPUT_KIND.CONSIDER_RESOLVED) {
1935
+ throw unsupportedInputKind(input.kind, this.stepId);
1936
+ }
1937
+ if (state.phase === reportAndKnowledgePhase.KNOWLEDGE) {
1938
+ await context.runtime.store.writeMetadataValue(
1939
+ context.session.sessionId,
1940
+ PROJECT_KNOWLEDGE_UPDATED_METADATA,
1941
+ "yes"
1942
+ );
1943
+ await writeState(context, this, machineState(STEP_STATUS.DONE, {
1944
+ message: input.message,
1945
+ phase: reportAndKnowledgePhase.KNOWLEDGE,
1946
+ source: input.source
1947
+ }));
1948
+ return;
1949
+ }
1950
+ await writePromptResponseArtifact(context, REPORT_ARTIFACT, input.fields.response || input.text);
1951
+ await writeState(context, this, machineState(STEP_STATUS.READY, {
1952
+ message: input.message,
1953
+ phase: reportAndKnowledgePhase.KNOWLEDGE,
1954
+ source: input.source
1955
+ }));
1956
+ return;
1957
+
1958
+ case STEP_STATUS.DONE:
1959
+ default:
1960
+ throw vibe64Error("This step is already complete.", "vibe64_step_input_not_available");
1961
+ }
1962
+ },
1963
+
1964
+ inputCompletionMessage(context = {}) {
1965
+ const input = normalizeMachineInput(context.input);
1966
+ if (input.kind !== STEP_INPUT_KIND.READY) {
1967
+ return "";
1968
+ }
1969
+ return context.session.stepMachine?.phase === reportAndKnowledgePhase.KNOWLEDGE
1970
+ ? "Project knowledge update completed."
1971
+ : "Report submitted for review.";
1038
1972
  },
1039
1973
 
1040
1974
  async actionStarted(context = {}) {
1041
- return markPromptActionStarted(context, this, "write_report");
1975
+ const phaseByActionId = {
1976
+ update_project_knowledge: reportAndKnowledgePhase.KNOWLEDGE,
1977
+ write_report: reportAndKnowledgePhase.REPORT
1978
+ };
1979
+ const phase = phaseByActionId[context.actionId];
1980
+ if (!phase) {
1981
+ return;
1982
+ }
1983
+ const state = await readState(context, this);
1984
+ if (![STEP_STATUS.READY, STEP_STATUS.FAILED, STEP_STATUS.WAITING_FOR_INPUT].includes(state.status)) {
1985
+ return;
1986
+ }
1987
+ await writeState(context, this, machineState(STEP_STATUS.AWAITING_AGENT_RESULT, {
1988
+ phase,
1989
+ response: state.response,
1990
+ source: state.source
1991
+ }));
1042
1992
  },
1043
1993
 
1044
- promptInstruction() {
1045
- return currentStepHelperInstruction({
1046
- doneFields: {
1047
- response: "Markdown session report"
1048
- },
1049
- doneMeaning: "The report text is complete and should be saved by Studio as the session report.",
1050
- waitingForInputMeaning: "You cannot write the report without a user decision or missing context."
1051
- });
1994
+ promptInstruction({ action = {} } = {}) {
1995
+ return normalizeText(action.id) === "update_project_knowledge"
1996
+ ? currentStepHelperInstruction({
1997
+ doneMeaning: "Project knowledge has been updated or there is no adapter-supported project knowledge to update.",
1998
+ waitingForInputMeaning: "You cannot update project knowledge without a user decision."
1999
+ })
2000
+ : currentStepHelperInstruction({
2001
+ doneFields: {
2002
+ response: "Markdown session report"
2003
+ },
2004
+ doneMeaning: "The report text is complete and should be saved by Studio as the session report.",
2005
+ waitingForInputMeaning: "You cannot write the report without a user decision or missing context."
2006
+ });
1052
2007
  }
1053
2008
  };
1054
2009
 
@@ -1056,6 +2011,7 @@ const coreCodingSteps = Object.freeze(Object.values(Object.freeze({
1056
2011
  [SEED_APPLICATION_STEP_ID]: {
1057
2012
  config: {
1058
2013
  draftReady: issueFilesAreReady,
2014
+ completionMessage: "Seed issue draft submitted for review.",
1059
2015
  initialDetails: {
1060
2016
  doing: "discussion"
1061
2017
  },
@@ -1080,51 +2036,9 @@ const coreCodingSteps = Object.freeze(Object.values(Object.freeze({
1080
2036
  id: SEED_APPLICATION_STEP_ID
1081
2037
  },
1082
2038
  [ISSUE_FILE_STEP_ID]: {
1083
- config: {
1084
- command: {
1085
- actionId: "use_existing_issue",
1086
- doneMetadata: "issue_url",
1087
- failureState: (context = {}) => machineState(STEP_STATUS.FAILED, {
1088
- message: normalizeText(context.actionResult?.message)
1089
- }),
1090
- finishStatuses: [
1091
- STEP_STATUS.WAITING_FOR_INPUT,
1092
- STEP_STATUS.CONFIRM_FILES,
1093
- STEP_STATUS.FAILED
1094
- ],
1095
- markAttemptingOnStart: false
1096
- },
1097
- done: (session = {}) => metadataExists(session, "issue_url"),
1098
- draftReady: issueFilesAreReady,
1099
- initialDetails: {
1100
- doing: "discussion"
1101
- },
1102
- interaction: issueInputInteraction,
1103
- nextWhenDrafting: {
1104
- disabledReason: "Define and save the issue before continuing."
1105
- },
1106
- nextWhenWorking: {
1107
- disabledReason: "Define and save the issue before continuing."
1108
- },
1109
- onConfirmedActions: (context = {}) => disableAction(context.session, "use_existing_issue", "Issue details are already saved."),
1110
- onDoneActions: (context = {}) => disableAction(context.session, "use_existing_issue", "An existing issue is already selected."),
1111
- readValues: readIssueFieldValues,
1112
- saveValues: writeIssueFieldValues,
1113
- unsupportedDoneMessage: "The issue is already complete.",
1114
- waitingForInputState: (input = {}) => ({
1115
- doing: "discussion",
1116
- message: input.message
1117
- }),
1118
- waitingInteraction: () => issueInputInteraction(STEP_STATUS.WAITING_FOR_INPUT, {})
1119
- },
1120
2039
  definition: coreCodingStepDefinitionsById[ISSUE_FILE_STEP_ID],
1121
- factoryId: "editable_artifact_review",
1122
- id: ISSUE_FILE_STEP_ID
1123
- },
1124
- [issueSubmittedStepId]: {
1125
- definition: coreCodingStepDefinitionsById[issueSubmittedStepId],
1126
- id: issueSubmittedStepId,
1127
- machine: issueSubmittedMachine
2040
+ id: ISSUE_FILE_STEP_ID,
2041
+ machine: issueFileMachine
1128
2042
  },
1129
2043
  [seedPlanMadeStepId]: {
1130
2044
  definition: coreCodingStepDefinitionsById[seedPlanMadeStepId],
@@ -1136,18 +2050,14 @@ const coreCodingSteps = Object.freeze(Object.values(Object.freeze({
1136
2050
  id: seedPlanExecutedStepId,
1137
2051
  machine: seedPlanExecutedMachine
1138
2052
  },
1139
- [planMadeStepId]: {
1140
- definition: coreCodingStepDefinitionsById[planMadeStepId],
1141
- id: planMadeStepId,
1142
- machine: makePlanMachine
1143
- },
1144
- [planExecutedStepId]: {
1145
- definition: coreCodingStepDefinitionsById[planExecutedStepId],
1146
- id: planExecutedStepId,
1147
- machine: executePlanMachine
2053
+ [planAndExecuteStepId]: {
2054
+ definition: coreCodingStepDefinitionsById[planAndExecuteStepId],
2055
+ id: planAndExecuteStepId,
2056
+ machine: planAndExecuteMachine
1148
2057
  },
1149
2058
  [implementationReviewedStepId]: {
1150
2059
  config: {
2060
+ completionMessage: "Initial human review turn completed.",
1151
2061
  completionPolicy: {
1152
2062
  decidedBy: "ai",
1153
2063
  enoughWhen: "the requested focused tweak has either been made and focused checks run when practical, or you can clearly report that no code change is needed.",
@@ -1160,33 +2070,19 @@ const coreCodingSteps = Object.freeze(Object.values(Object.freeze({
1160
2070
  factoryId: "chat_with_ai",
1161
2071
  id: implementationReviewedStepId
1162
2072
  },
1163
- [agentConversationStepId]: {
1164
- config: {
1165
- completionPolicy: {
1166
- decidedBy: "user"
1167
- },
1168
- nextWhenIdle: (context = {}) => ({
1169
- disabledReason: "Ask Codex for changes before continuing.",
1170
- enabled: artifactIsReady(context.session, HUMAN_INPUT_RESPONSE_ARTIFACT)
1171
- }),
1172
- promptActionId: "agent_conversation"
1173
- },
1174
- definition: coreCodingStepDefinitionsById[agentConversationStepId],
1175
- factoryId: "chat_with_ai",
1176
- id: agentConversationStepId
1177
- },
1178
2073
  [deepUiCheckRunStepId]: {
1179
2074
  definition: coreCodingStepDefinitionsById[deepUiCheckRunStepId],
1180
2075
  id: deepUiCheckRunStepId,
1181
2076
  machine: deepUiCheckMachine
1182
2077
  },
1183
- [reviewRunStepId]: {
1184
- definition: coreCodingStepDefinitionsById[reviewRunStepId],
1185
- id: reviewRunStepId,
1186
- machine: reviewRunMachine
2078
+ [reviewAndValidateStepId]: {
2079
+ definition: coreCodingStepDefinitionsById[reviewAndValidateStepId],
2080
+ id: reviewAndValidateStepId,
2081
+ machine: reviewAndValidateMachine
1187
2082
  },
1188
2083
  [changesAcceptedStepId]: {
1189
2084
  config: {
2085
+ completionMessage: "Final human review turn completed.",
1190
2086
  completionPolicy: {
1191
2087
  decidedBy: "ai",
1192
2088
  enoughWhen: "the requested final tweak has either been made or you can clearly report the blocker; Vibe64 can then rerun review and validation.",
@@ -1199,15 +2095,10 @@ const coreCodingSteps = Object.freeze(Object.values(Object.freeze({
1199
2095
  factoryId: "chat_with_ai",
1200
2096
  id: changesAcceptedStepId
1201
2097
  },
1202
- [reportCreatedStepId]: {
1203
- definition: coreCodingStepDefinitionsById[reportCreatedStepId],
1204
- id: reportCreatedStepId,
1205
- machine: reportCreatedMachine
1206
- },
1207
- [projectKnowledgeUpdatedStepId]: {
1208
- definition: coreCodingStepDefinitionsById[projectKnowledgeUpdatedStepId],
1209
- id: projectKnowledgeUpdatedStepId,
1210
- machine: projectKnowledgeUpdatedMachine
2098
+ [reportAndKnowledgeUpdatedStepId]: {
2099
+ definition: coreCodingStepDefinitionsById[reportAndKnowledgeUpdatedStepId],
2100
+ id: reportAndKnowledgeUpdatedStepId,
2101
+ machine: reportAndKnowledgeUpdatedMachine
1211
2102
  }
1212
2103
  })));
1213
2104
 
@@ -1218,22 +2109,19 @@ const coreCodingWorkflowModule = Object.freeze({
1218
2109
  });
1219
2110
 
1220
2111
  const _testing = deepFreeze({
2112
+ groupIds: CORE_CODING_WORKFLOW_GROUP_IDS,
1221
2113
  moduleId,
1222
2114
  ownedStepIds: [
1223
2115
  SEED_APPLICATION_STEP_ID,
1224
2116
  ISSUE_FILE_STEP_ID,
1225
- issueSubmittedStepId,
1226
2117
  seedPlanMadeStepId,
1227
2118
  seedPlanExecutedStepId,
1228
- planMadeStepId,
1229
- planExecutedStepId,
2119
+ planAndExecuteStepId,
1230
2120
  implementationReviewedStepId,
1231
- agentConversationStepId,
1232
2121
  deepUiCheckRunStepId,
1233
- reviewRunStepId,
2122
+ reviewAndValidateStepId,
1234
2123
  changesAcceptedStepId,
1235
- reportCreatedStepId,
1236
- projectKnowledgeUpdatedStepId
2124
+ reportAndKnowledgeUpdatedStepId
1237
2125
  ],
1238
2126
  workflowDefinitionIds: VIBE64_WORKFLOW_DEFINITION_IDS
1239
2127
  });
@@ -1244,5 +2132,7 @@ export {
1244
2132
  ISSUE_FILE_STEP_ID,
1245
2133
  SEED_APPLICATION_STEP_ID,
1246
2134
  _testing,
1247
- coreCodingWorkflowModule
2135
+ coreCodingWorkflowModule,
2136
+ finishOffWorkflowGroup,
2137
+ qaWorkflowGroup
1248
2138
  };