vibe-coding-master 0.7.43 → 0.7.45

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 (54) hide show
  1. package/README.md +64 -39
  2. package/dist/backend/adapters/claude-adapter.js +2 -2
  3. package/dist/backend/adapters/codex-bridge-adapter.js +213 -0
  4. package/dist/backend/api/app-settings-routes.js +6 -6
  5. package/dist/backend/api/artifact-routes.js +3 -0
  6. package/dist/backend/api/harness-routes.js +16 -0
  7. package/dist/backend/api/task-routes.js +1 -0
  8. package/dist/backend/api/workflow-control-routes.js +12 -0
  9. package/dist/backend/cli/install-vcm-harness.js +21 -0
  10. package/dist/backend/server.js +11 -9
  11. package/dist/backend/services/app-settings-service.js +11 -11
  12. package/dist/backend/services/artifact-service.js +7 -30
  13. package/dist/backend/services/auto-memory-service.js +640 -12
  14. package/dist/backend/services/claude-hook-service.js +80 -2
  15. package/dist/backend/services/{ccr-integration-service.js → codex-bridge-integration-service.js} +71 -71
  16. package/dist/backend/services/gate-review-service.js +173 -74
  17. package/dist/backend/services/harness-feedback-service.js +76 -78
  18. package/dist/backend/services/harness-service.js +27 -3
  19. package/dist/backend/services/runtime-coordinator-service.js +2 -1
  20. package/dist/backend/services/session-service.js +16 -16
  21. package/dist/backend/services/status-service.js +1 -0
  22. package/dist/backend/services/translation-worker-service.js +19 -4
  23. package/dist/backend/services/usage-analytics-service.js +3 -3
  24. package/dist/backend/services/workflow-control-service.js +96 -12
  25. package/dist/backend/templates/handoff.js +44 -2
  26. package/dist/backend/templates/harness/architect-agent.js +13 -7
  27. package/dist/backend/templates/harness/architect-scaffold-worker-agent.js +1 -1
  28. package/dist/backend/templates/harness/check-scaffold-ledger.js +234 -10
  29. package/dist/backend/templates/harness/claude-root.js +3 -2
  30. package/dist/backend/templates/harness/coder-agent.js +8 -0
  31. package/dist/backend/templates/harness/gate-review.js +144 -49
  32. package/dist/backend/templates/harness/harness-engineer-agent.js +25 -10
  33. package/dist/backend/templates/harness/project-manager-agent.js +16 -10
  34. package/dist/backend/templates/harness/resolve-durable-doc-assignment.js +60 -0
  35. package/dist/backend/templates/harness/tester-agent.js +13 -0
  36. package/dist/backend/templates/harness/vcm-ask-user-skill.js +82 -0
  37. package/dist/backend/templates/harness/vcm-code-navigation-skill.js +7 -5
  38. package/dist/backend/templates/harness/vcm-task-state-skill.js +2 -2
  39. package/dist/backend/templates/harness/vcm-workflow-review-skill.js +1 -1
  40. package/dist/shared/types/session.js +28 -22
  41. package/dist/shared/types/workflow.js +1 -0
  42. package/dist/shared/validation/artifact-check.js +3 -3
  43. package/dist/shared/validation/artifact-contract.js +1 -1
  44. package/dist/shared/validation/artifact-registry.js +16 -0
  45. package/dist-frontend/assets/index-DDmygnV6.css +32 -0
  46. package/dist-frontend/assets/{index-VW9tYPP5.js → index-nAV6toi8.js} +47 -47
  47. package/dist-frontend/index.html +2 -2
  48. package/package.json +1 -1
  49. package/scripts/claude-plugins/vcm-lsp-bridge/.claude-plugin/plugin.json +21 -6
  50. package/scripts/{ccr-api-key-helper.mjs → codex-bridge-api-key-helper.mjs} +1 -1
  51. package/scripts/harness-tools/vcm-artifact +1 -2
  52. package/scripts/harness-tools/vcm-bash-guard +1 -1
  53. package/dist/backend/adapters/ccr-gateway-adapter.js +0 -172
  54. package/dist-frontend/assets/index-B0d4Z6ny.css +0 -32
package/README.md CHANGED
@@ -190,6 +190,12 @@ restart, but it does not infer transitions or choose the next role. Current
190
190
  artifacts, Gate Review state, Round/Turn state, and the role rules remain
191
191
  authoritative.
192
192
 
193
+ Whenever Project Manager asks the user a question, it first uses
194
+ `vcm-ask-user`. VCM records the wait and cancels any pending role dispatch in
195
+ one state update. Stop Hook then ends the PM turn without route scanning. Only
196
+ a new direct user message clears the wait, and the next role dispatch requires
197
+ a fresh Workflow Review.
198
+
193
199
  Typical flow:
194
200
 
195
201
  ```text
@@ -237,18 +243,18 @@ Open `Usage Analytics` in the sidebar `Task` section to inspect native Claude
237
243
  Code usage for the active task. The report shows task totals and breakdowns by
238
244
  role and model for input, output, cache-read, and cache-creation tokens plus
239
245
  estimated USD cost. It combines every restart and resumed Claude session for
240
- all seven roles. CCR/GPT usage is excluded.
246
+ all seven roles. Codex Bridge usage is excluded.
241
247
 
242
248
  VCM retains only aggregate task data in
243
249
  `<task-worktree>/.ai/vcm/telemetry/usage.json`. The file is temporary runtime
244
250
  state and is removed with the task worktree when the task is closed.
245
251
 
246
- ### GPT Through Claude Code Router
252
+ ### GPT Through Codex Bridge
247
253
 
248
- VCM can launch its normal Claude Code sessions with `GPT-5.6 Sol (CCR)` through
249
- a host-running [Claude Code Router](https://github.com/musistudio/claude-code-router).
250
- CCR must already be installed, authenticated, configured, and running on the
251
- host. VCM does not manage the CCR process.
254
+ VCM can launch its normal Claude Code sessions with models provided by a
255
+ host-running Codex Bridge. The Bridge must already be installed and running,
256
+ and Codex must be logged in on the host. VCM does not manage the Bridge process
257
+ or Codex login.
252
258
 
253
259
  VCM automatically checks the local-host and DevContainer endpoints:
254
260
 
@@ -257,36 +263,34 @@ http://127.0.0.1:3456
257
263
  http://host.docker.internal:3456
258
264
  ```
259
265
 
260
- Configure CCR to listen on port `3456` with an API key. When VCM runs in a
261
- DevContainer, make the second endpoint reachable from the container. In the VCM
262
- `Settings` section:
266
+ Configure Codex Bridge to listen on port `3456`. When VCM runs in a
267
+ DevContainer, start it with a container-reachable bind address such as
268
+ `codex-bridge serve --host 0.0.0.0 --port 3456`. In the VCM `Settings` section:
263
269
 
264
- 1. enter and save the CCR API key;
265
- 2. enable `CCR GPT models`;
270
+ 1. enter and save the Codex Bridge API key;
271
+ 2. enable `Codex Bridge models`;
266
272
  3. confirm the status is `available`;
267
- 4. select `GPT-5.6 Sol (CCR)` in any Session model control.
273
+ 4. select any discovered Codex Bridge model in a Session model control.
268
274
 
269
275
  The key is stored in global VCM state (`~/.vcm/settings.json`) with owner-only
270
- permissions and is never returned by the settings API. It is used for CCR
271
- checks, model discovery, and the GPT-only `apiKeyHelper`. GPT sessions receive a
276
+ permissions and is never returned by the settings API. It is used for Bridge
277
+ checks, model discovery, and the Bridge-only `apiKeyHelper`. Bridge sessions receive a
272
278
  child-only `--settings` override and use the isolated Claude configuration root
273
- `~/.vcm/claude/ccr`. VCM never edits `~/.claude/settings.json`. Native Claude
279
+ `~/.vcm/claude/codex-bridge`. VCM never edits `~/.claude/settings.json`. Native Claude
274
280
  sessions keep their normal configuration and account authentication; VCM only
275
- removes inherited environment variables that clearly point at the local CCR
276
- gateway from the native child process. Configure CCR without enabling its
277
- global Claude Code or Claude App takeover if those clients should remain on
278
- Anthropic.
281
+ removes inherited environment variables that clearly point at the local Bridge
282
+ from the native child process.
279
283
 
280
- CCR/GPT child processes use a hard context limit and auto-compaction window of
284
+ Codex Bridge child processes use a hard context limit and auto-compaction window of
281
285
  `258400` tokens. VCM sets Claude Code's proactive compaction threshold to `90%`
282
- (about `232560` tokens), leaving headroom before the gateway limit. Native
286
+ (about `232560` tokens), leaving headroom before the Codex API limit. Native
283
287
  Claude sessions receive no VCM-owned context override.
284
288
 
285
289
  Resume keeps the provider recorded by the existing Session. Use Restart when
286
- switching between a native Claude model and `GPT-5.6 Sol (CCR)`. If CCR is
287
- disabled, unreachable, rejects the key, or does not expose
288
- `Codex API/gpt-5.6-sol`, VCM blocks the new Start, Resume, or Restart and does
289
- not fall back to another model.
290
+ switching between native Claude and Codex Bridge. If the Bridge is disabled,
291
+ unreachable, rejects the key, has unavailable Codex credentials, or no longer
292
+ exposes the selected model, VCM blocks the new Start, Resume, or Restart and
293
+ does not fall back to another model.
290
294
 
291
295
  ## Launch Template
292
296
 
@@ -329,7 +333,8 @@ strict `workflow-progress.md` transition through `vcm-workflow-review`. VCM
329
333
  checks the confirmed dispatch history and current task artifacts, then grants
330
334
  one matching route. The target role's `UserPromptSubmit` consumes that approval
331
335
  and appends the confirmed transition. A rejected transition can be bypassed
332
- only by an exact one-time user authorization recorded through the VCM dialog.
336
+ only by the user's exact one-time authorization recorded in the next Workflow
337
+ Progress submission.
333
338
 
334
339
  If the flow stops, VCM always shows a blocking pause alert. `Pause alert sound`
335
340
  only controls the looping sound. Enabling Gateway turns that preference off once;
@@ -475,11 +480,14 @@ Use it to:
475
480
  appropriate
476
481
 
477
482
  VCM bundles the Claude Code LSP bridge and loads it only for Architect sessions,
478
- including CCR launches. The project environment must still
483
+ including Codex Bridge launches. The project environment must still
479
484
  provide the language server for each detected language: `rust-analyzer`,
480
485
  `typescript-language-server`, `pyright-langserver`, `gopls`, `clangd`, or
481
486
  `jdtls`. Architect preloads `vcm-code-navigation` and uses LSP for semantic
482
- relationships; other roles use generated context and ordinary source reads.
487
+ relationships. Accessors, backing fields, trait items, implementations, wrappers,
488
+ and aliases are queried as separate symbols; a bounded source-text fallback may
489
+ only locate candidates after the correct LSP query is demonstrably partial.
490
+ Other roles use generated context and ordinary source reads.
483
491
  Harness Studio reports
484
492
  whether the server executable and plugin can run; the role Session performs the
485
493
  real workspace indexing and semantic query retries. VCM uses Claude Code progress
@@ -498,10 +506,11 @@ files.
498
506
  `Auto memory` is the switch for the entire automated memory workflow. During
499
507
  Review Task Harness after Final Acceptance, Project Manager, Architect, Coder,
500
508
  Tester, and an enabled Reviewer submit proposals in sequence through
501
- `vcm-propose-memory`. Harness Engineer verifies and consolidates them before VCM
502
- records the result as part of the same Task Harness Retrospective. Workflow roles
503
- cannot edit active memory directly; Harness Engineer edits it only during the
504
- assigned Auto Memory Retrospective.
509
+ `vcm-propose-memory`. Harness Engineer verifies and consolidates them, commits
510
+ the reviewed memory, and writes the assigned machine-readable review result as
511
+ part of the same Task Harness Retrospective. Workflow roles cannot edit active
512
+ memory directly; Harness Engineer edits it only during the assigned Auto Memory
513
+ Retrospective.
505
514
 
506
515
  When Auto Memory is enabled, the planning Architect writes a provisional memory
507
516
  candidate before its post-planning Session restart. VCM snapshots that candidate
@@ -528,7 +537,10 @@ Final Acceptance
528
537
  -> Review pending Harness Feedback
529
538
  -> Review Auto Memory proposals, when Auto Memory is enabled
530
539
  -> Harness Engineer updates and commits memory, when Auto Memory is enabled
531
- -> VCM records the committed memory result
540
+ -> Harness Engineer writes review-result.json, when Auto Memory is enabled
541
+ -> VCM validates and records the committed memory result
542
+ -> VCM dispatches durable-document assignments one at a time
543
+ -> Complete Task Harness Retrospective after every assignment completes
532
544
  ```
533
545
 
534
546
  Memory proposal prompts sent to Project Manager, Architect, Coder, Tester, and
@@ -542,16 +554,29 @@ impact, and durable-document disposition, then evaluates every proposal item
542
554
  independently. Each Add or Update decision records why the memory is necessary,
543
555
  what happens if it is absent, whether it belongs in memory or a durable
544
556
  document, and the exact final memory content when retained. VCM validates that
545
- the commit changes only assigned memory host files and only their
546
- `<VCM-memory>` blocks; it does not parse or apply Harness Engineer's semantic
547
- decisions. Harness Engineer review and retrospective work remain tool role
548
- activity and do not participate in Round completion.
557
+ every existing entry and proposal has one structurally valid decision, every
558
+ move decision has one durable-document assignment, moved content has already
559
+ been removed from memory, and the commit changes only assigned memory host
560
+ files and only their `<VCM-memory>` blocks. VCM does not decide whether the
561
+ Harness Engineer's semantic judgment is correct. Harness Engineer review and
562
+ retrospective work remain tool role activity and do not participate in Round
563
+ completion.
564
+
565
+ For `move-to-durable-doc`, memory is removed in the Harness Engineer commit
566
+ before documentation work starts. VCM assigns architecture and known-issues
567
+ documents to Architect and testing documentation to Tester. For another target,
568
+ PM selects Architect, Coder, or Tester and asks the user only when ownership
569
+ cannot be determined. These assignments are backend-dispatched work rather than
570
+ a Docs-Only Flow. Each owner updates and commits the document, submits the exact
571
+ assignment ID in `docs-update-report.md`, and finishes before the next assignment
572
+ starts. Harness Studio shows assignment status and allows failed work to be
573
+ retried.
549
574
 
550
575
  When Auto Memory is disabled, Review Task Harness does not collect proposals or
551
576
  ask Harness Engineer to update memory. When enabled, both automatic and manual
552
577
  review requests collect proposals before starting the retrospective. A failed
553
- proposal collection or combined retrospective memory review must be retried
554
- from Harness Studio.
578
+ proposal collection, combined retrospective memory review, or durable-document
579
+ assignment must be retried from Harness Studio.
555
580
 
556
581
  ## Closing a Task
557
582
 
@@ -1,5 +1,5 @@
1
1
  import { roleRuntimeDisallowedTools, roleUsesLsp } from "../role-tool-policy.js";
2
- import { isCcrSessionModel } from "../../shared/types/session.js";
2
+ import { isCodexBridgeSessionModel } from "../../shared/types/session.js";
3
3
  import { VcmError } from "../errors.js";
4
4
  export function createClaudeAdapter(runner) {
5
5
  return {
@@ -33,7 +33,7 @@ export function createClaudeAdapter(runner) {
33
33
  if (claudeSessionId) {
34
34
  args.push(resume ? "--resume" : "--session-id", claudeSessionId);
35
35
  }
36
- if (!isCcrSessionModel(model)) {
36
+ if (!isCodexBridgeSessionModel(model)) {
37
37
  args.push("--model", model);
38
38
  }
39
39
  if (effort === "ultracode") {
@@ -0,0 +1,213 @@
1
+ import { CODEX_BRIDGE_BASE_URLS } from "../../shared/types/session.js";
2
+ const DEFAULT_TIMEOUT_MS = 3_000;
3
+ export function createCodexBridgeAdapter(deps = {}) {
4
+ const baseUrls = (deps.baseUrl ? [deps.baseUrl] : CODEX_BRIDGE_BASE_URLS)
5
+ .map((baseUrl) => baseUrl.replace(/\/+$/, ""));
6
+ const fetchImpl = deps.fetch ?? globalThis.fetch;
7
+ const timeoutMs = deps.timeoutMs ?? DEFAULT_TIMEOUT_MS;
8
+ return {
9
+ async probe(apiKey) {
10
+ const failures = [];
11
+ for (const baseUrl of baseUrls) {
12
+ const result = await probeEndpoint(fetchImpl, baseUrl, apiKey, timeoutMs);
13
+ if (result.baseUrl) {
14
+ return result;
15
+ }
16
+ failures.push(result);
17
+ }
18
+ return combineProbeFailures(baseUrls, failures);
19
+ }
20
+ };
21
+ }
22
+ async function probeEndpoint(fetchImpl, baseUrl, apiKey, timeoutMs) {
23
+ try {
24
+ const health = await requestJson(fetchImpl, `${baseUrl}/health`, timeoutMs);
25
+ if (!health.response.ok) {
26
+ return invalidResponse(`Codex Bridge health check at ${baseUrl} returned HTTP ${health.response.status}.`);
27
+ }
28
+ if (!isCodexBridgeHealth(health.payload)) {
29
+ return {
30
+ connectionState: "not-codex-bridge",
31
+ modelAvailable: false,
32
+ models: [],
33
+ error: `${baseUrl} did not identify as Codex Bridge.`
34
+ };
35
+ }
36
+ const headers = {
37
+ authorization: `Bearer ${apiKey}`,
38
+ "user-agent": "VibeCodingMaster"
39
+ };
40
+ const auth = await requestJson(fetchImpl, `${baseUrl}/auth/status`, timeoutMs, headers);
41
+ if (auth.response.status === 401 || auth.response.status === 403) {
42
+ return {
43
+ connectionState: "unauthorized",
44
+ modelAvailable: false,
45
+ models: [],
46
+ baseUrl,
47
+ error: "Codex Bridge rejected the configured API key."
48
+ };
49
+ }
50
+ if (!auth.response.ok) {
51
+ return invalidResponse(responseError("Codex Bridge auth status", baseUrl, auth.response, auth.payload), baseUrl);
52
+ }
53
+ const authState = readAuthState(auth.payload);
54
+ if (!authState) {
55
+ return invalidResponse("Codex Bridge returned an invalid /auth/status response.", baseUrl);
56
+ }
57
+ if (authState.state !== "ready") {
58
+ return {
59
+ connectionState: "codex-auth-unavailable",
60
+ modelAvailable: false,
61
+ models: [],
62
+ baseUrl,
63
+ error: authState.message
64
+ ?? `Codex authentication is ${authState.state}. Open Codex or run codex login, then retry.`
65
+ };
66
+ }
67
+ const modelsResponse = await requestJson(fetchImpl, `${baseUrl}/v1/models`, timeoutMs, headers);
68
+ if (!modelsResponse.response.ok) {
69
+ const code = readErrorCode(modelsResponse.payload);
70
+ const connectionState = code?.startsWith("CODEX_AUTH_")
71
+ ? "codex-auth-unavailable"
72
+ : modelsResponse.response.status === 401 || modelsResponse.response.status === 403
73
+ ? "unauthorized"
74
+ : "invalid-response";
75
+ return {
76
+ connectionState,
77
+ modelAvailable: false,
78
+ models: [],
79
+ baseUrl,
80
+ error: responseError("Codex Bridge model discovery", baseUrl, modelsResponse.response, modelsResponse.payload)
81
+ };
82
+ }
83
+ const models = readModelDescriptors(modelsResponse.payload);
84
+ if (!models) {
85
+ return invalidResponse("Codex Bridge returned an invalid /v1/models response.", baseUrl);
86
+ }
87
+ return {
88
+ connectionState: "available",
89
+ modelAvailable: models.length > 0,
90
+ models,
91
+ baseUrl,
92
+ ...(models.length > 0 ? {} : { error: "Codex Bridge did not expose any models." })
93
+ };
94
+ }
95
+ catch (error) {
96
+ const timedOut = isAbortError(error);
97
+ return {
98
+ connectionState: "unreachable",
99
+ modelAvailable: false,
100
+ models: [],
101
+ error: timedOut
102
+ ? `Codex Bridge connection timed out after ${timeoutMs} ms at ${baseUrl}.`
103
+ : `Codex Bridge could not be reached from the VCM backend at ${baseUrl}. Reason: ${errorMessage(error)}`
104
+ };
105
+ }
106
+ }
107
+ function combineProbeFailures(baseUrls, failures) {
108
+ const lastFailure = failures.at(-1);
109
+ return {
110
+ connectionState: lastFailure?.connectionState ?? "unreachable",
111
+ modelAvailable: false,
112
+ models: [],
113
+ error: `Codex Bridge was not found at ${baseUrls.join(" or ")}. ${failures
114
+ .map((failure) => failure.error)
115
+ .filter(Boolean)
116
+ .join(" ")}`.trim()
117
+ };
118
+ }
119
+ async function requestJson(fetchImpl, url, timeoutMs, headers = {}) {
120
+ const controller = new AbortController();
121
+ const timeout = setTimeout(() => controller.abort(), timeoutMs);
122
+ try {
123
+ const response = await fetchImpl(url, {
124
+ method: "GET",
125
+ headers: {
126
+ accept: "application/json",
127
+ ...headers
128
+ },
129
+ signal: controller.signal
130
+ });
131
+ let payload;
132
+ try {
133
+ payload = await response.json();
134
+ }
135
+ catch {
136
+ payload = undefined;
137
+ }
138
+ return { response, payload };
139
+ }
140
+ finally {
141
+ clearTimeout(timeout);
142
+ }
143
+ }
144
+ function isCodexBridgeHealth(value) {
145
+ return isObject(value)
146
+ && value.status === "ok"
147
+ && value.service === "codex-bridge";
148
+ }
149
+ function readAuthState(value) {
150
+ if (!isObject(value) || typeof value.state !== "string" || !value.state.trim()) {
151
+ return undefined;
152
+ }
153
+ return {
154
+ state: value.state.trim(),
155
+ ...(typeof value.message === "string" && value.message.trim()
156
+ ? { message: value.message.trim() }
157
+ : {})
158
+ };
159
+ }
160
+ function readModelDescriptors(value) {
161
+ if (!isObject(value) || !Array.isArray(value.data)) {
162
+ return undefined;
163
+ }
164
+ const models = [];
165
+ for (const item of value.data) {
166
+ if (!isObject(item) || typeof item.id !== "string" || !item.id.trim()) {
167
+ return undefined;
168
+ }
169
+ models.push({
170
+ id: item.id.trim(),
171
+ ...(typeof item.display_name === "string" && item.display_name.trim()
172
+ ? { displayName: item.display_name.trim() }
173
+ : {})
174
+ });
175
+ }
176
+ return models;
177
+ }
178
+ function responseError(operation, baseUrl, response, payload) {
179
+ const message = readErrorMessage(payload);
180
+ return `${operation} at ${baseUrl} returned HTTP ${response.status}.${message ? ` Reason: ${message}` : ""}`;
181
+ }
182
+ function readErrorCode(value) {
183
+ return isObject(value)
184
+ && isObject(value.error)
185
+ && typeof value.error.code === "string"
186
+ ? value.error.code
187
+ : undefined;
188
+ }
189
+ function readErrorMessage(value) {
190
+ return isObject(value)
191
+ && isObject(value.error)
192
+ && typeof value.error.message === "string"
193
+ ? value.error.message
194
+ : undefined;
195
+ }
196
+ function invalidResponse(error, baseUrl) {
197
+ return {
198
+ connectionState: "invalid-response",
199
+ modelAvailable: false,
200
+ models: [],
201
+ ...(baseUrl ? { baseUrl } : {}),
202
+ error
203
+ };
204
+ }
205
+ function isAbortError(error) {
206
+ return error instanceof Error && error.name === "AbortError";
207
+ }
208
+ function errorMessage(error) {
209
+ return error instanceof Error ? error.message : String(error);
210
+ }
211
+ function isObject(value) {
212
+ return typeof value === "object" && value !== null && !Array.isArray(value);
213
+ }
@@ -5,13 +5,13 @@ export function registerAppSettingsRoutes(app, deps) {
5
5
  app.put("/api/settings/preferences", async (request) => {
6
6
  return deps.appSettings.updatePreferences(request.body ?? {});
7
7
  });
8
- app.get("/api/settings/ccr", async () => {
9
- return deps.ccrIntegration.getStatus();
8
+ app.get("/api/settings/codex-bridge", async () => {
9
+ return deps.codexBridgeIntegration.getStatus();
10
10
  });
11
- app.put("/api/settings/ccr", async (request) => {
12
- return deps.ccrIntegration.updateSettings(request.body ?? {});
11
+ app.put("/api/settings/codex-bridge", async (request) => {
12
+ return deps.codexBridgeIntegration.updateSettings(request.body ?? {});
13
13
  });
14
- app.post("/api/settings/ccr/check", async () => {
15
- return deps.ccrIntegration.checkConnection();
14
+ app.post("/api/settings/codex-bridge/check", async () => {
15
+ return deps.codexBridgeIntegration.checkConnection();
16
16
  });
17
17
  }
@@ -144,6 +144,9 @@ function artifactNameToPath(paths, artifactName) {
144
144
  if (artifactName === "test-report.md") {
145
145
  return paths.testReportPath;
146
146
  }
147
+ if (artifactName === "docs-update-report.md") {
148
+ return paths.docsUpdateReportPath;
149
+ }
147
150
  if (artifactName === "docs-sync-report.md") {
148
151
  return paths.docsSyncReportPath;
149
152
  }
@@ -208,6 +208,22 @@ export function registerHarnessRoutes(app, deps) {
208
208
  const { project, task } = await requireHarnessTaskContext(deps, request.body?.taskSlug);
209
209
  return deps.autoMemoryService.retryFailedReview(project.repoRoot, task.worktreePath);
210
210
  });
211
+ app.post("/api/projects/harness/memory/assignments/retry", async (request) => {
212
+ const { project, task } = await requireHarnessTaskContext(deps, request.body?.taskSlug);
213
+ return deps.autoMemoryService.retryDurableDocAssignment(project.repoRoot, task.worktreePath, request.body?.assignmentId ?? "");
214
+ });
215
+ app.post("/api/projects/harness/memory/assignments/owner", async (request) => {
216
+ const { project, task } = await requireHarnessTaskContext(deps, request.body?.taskSlug);
217
+ const owner = request.body?.owner;
218
+ if (owner !== "architect" && owner !== "coder" && owner !== "tester") {
219
+ throw new VcmError({
220
+ code: "DURABLE_DOC_OWNER_INVALID",
221
+ message: "Durable-document owner must be architect, coder, or tester.",
222
+ statusCode: 400
223
+ });
224
+ }
225
+ return deps.autoMemoryService.resolveDurableDocAssignmentOwner(project.repoRoot, task.worktreePath, request.body?.assignmentId ?? "", owner);
226
+ });
211
227
  }
212
228
  async function persistHarnessEngineerDefaults(appSettings, session) {
213
229
  const defaults = createDefaultToolSessionDefaults()["harness-engineer"];
@@ -235,6 +235,7 @@ function degradedArtifactSummary(handoffDir) {
235
235
  architectDebugPath: `${handoffDir}/architect-debug.md`,
236
236
  architectureDiagnosisPath: `${handoffDir}/architecture-diagnosis.md`,
237
237
  testReportPath: `${handoffDir}/test-report.md`,
238
+ docsUpdateReportPath: `${handoffDir}/docs-update-report.md`,
238
239
  docsSyncReportPath: `${handoffDir}/docs-sync-report.md`,
239
240
  workflowProgressPath: `${handoffDir}/workflow-progress.md`,
240
241
  finalAcceptancePath: `${handoffDir}/final-acceptance.md`
@@ -5,6 +5,18 @@ export function registerWorkflowControlRoutes(app, deps) {
5
5
  const context = await getContext(deps, request.params.taskSlug);
6
6
  return deps.workflowControlService.getState(context);
7
7
  });
8
+ app.post("/api/tasks/:taskSlug/ask-user", async (request) => {
9
+ const context = await getContext(deps, request.params.taskSlug);
10
+ const question = typeof request.body?.question === "string" ? request.body.question.trim() : "";
11
+ if (!question) {
12
+ throw new VcmError({
13
+ code: "WORKFLOW_USER_QUESTION_REQUIRED",
14
+ message: "A non-empty user question is required.",
15
+ statusCode: 400
16
+ });
17
+ }
18
+ return deps.workflowControlService.requestUserInput(context, question);
19
+ });
8
20
  }
9
21
  async function getContext(deps, taskSlug) {
10
22
  const project = await deps.projectService.getCurrentProject();
@@ -28,10 +28,12 @@ import { renderVcmLongRunningValidationSkillRules } from "../templates/harness/v
28
28
  import { renderVcmProposeMemorySkillRules } from "../templates/harness/vcm-propose-memory-skill.js";
29
29
  import { renderVcmReportHarnessIssueSkillRules } from "../templates/harness/vcm-report-harness-issue-skill.js";
30
30
  import { renderVcmRouteMessageSkillRules } from "../templates/harness/vcm-route-message-skill.js";
31
+ import { renderAskUserTool, renderVcmAskUserSkillRules } from "../templates/harness/vcm-ask-user-skill.js";
31
32
  import { renderUpdateTaskStateTool, renderVcmTaskStateSkillRules } from "../templates/harness/vcm-task-state-skill.js";
32
33
  import { renderVcmWorkflowReviewSkillRules } from "../templates/harness/vcm-workflow-review-skill.js";
33
34
  import { renderCheckScaffoldLedgerTool } from "../templates/harness/check-scaffold-ledger.js";
34
35
  import { renderRequestArchitectRestartTool, renderRestartArchitectSkillRules } from "../templates/harness/restart-architect-skill.js";
36
+ import { renderResolveDurableDocAssignmentTool } from "../templates/harness/resolve-durable-doc-assignment.js";
35
37
  import { readVcmPackageVersion } from "../app-version.js";
36
38
  const CLI_DIR = path.dirname(fileURLToPath(import.meta.url));
37
39
  const APP_ROOT = path.resolve(CLI_DIR, "../../..");
@@ -318,6 +320,12 @@ const WHOLE_FILES = [
318
320
  mode: 0o644,
319
321
  content: renderSkillFile("VCM Route Message Skill", "vcm-route-message", "Use when project-manager dispatches a VCM role or when a VCM role reports a question, result, blocker, or finding back to project-manager.", renderVcmRouteMessageSkillRules())
320
322
  },
323
+ {
324
+ path: ".claude/skills/vcm-ask-user/SKILL.md",
325
+ category: "skill",
326
+ mode: 0o644,
327
+ content: renderSkillFile("VCM Ask User Skill", "vcm-ask-user", "Use whenever project-manager asks the user a question and must pause the workflow.", renderVcmAskUserSkillRules())
328
+ },
321
329
  {
322
330
  path: ".claude/skills/vcm-task-state/SKILL.md",
323
331
  category: "skill",
@@ -360,6 +368,12 @@ const WHOLE_FILES = [
360
368
  mode: 0o755,
361
369
  content: renderRequestGateReviewTool()
362
370
  },
371
+ {
372
+ path: ".ai/tools/vcm-ask-user",
373
+ category: "runtime-tool",
374
+ mode: 0o755,
375
+ content: renderAskUserTool()
376
+ },
363
377
  {
364
378
  path: ".ai/tools/update-task-state",
365
379
  category: "runtime-tool",
@@ -378,6 +392,12 @@ const WHOLE_FILES = [
378
392
  mode: 0o755,
379
393
  content: renderRequestArchitectRestartTool()
380
394
  },
395
+ {
396
+ path: ".ai/tools/resolve-durable-doc-assignment",
397
+ category: "runtime-tool",
398
+ mode: 0o755,
399
+ content: renderResolveDurableDocAssignmentTool()
400
+ },
381
401
  {
382
402
  path: ".ai/tools/run-long-check",
383
403
  category: "runtime-tool",
@@ -642,6 +662,7 @@ function fixedDirectories() {
642
662
  ".claude/skills/vcm-harness-bootstrap/",
643
663
  ".claude/skills/vcm-long-running-validation/",
644
664
  ".claude/skills/vcm-route-message/",
665
+ ".claude/skills/vcm-ask-user/",
645
666
  ".claude/skills/vcm-task-state/",
646
667
  ".claude/skills/vcm-workflow-review/",
647
668
  ".claude/skills/vcm-gate-review/",
@@ -4,13 +4,13 @@ import Fastify from "fastify";
4
4
  import fastifyStatic from "@fastify/static";
5
5
  import { createArtifactService } from "./services/artifact-service.js";
6
6
  import { createClaudeAdapter } from "./adapters/claude-adapter.js";
7
- import { createCcrGatewayAdapter } from "./adapters/ccr-gateway-adapter.js";
7
+ import { createCodexBridgeAdapter } from "./adapters/codex-bridge-adapter.js";
8
8
  import { createCommandRunner } from "./adapters/command-runner.js";
9
9
  import { createCommandDispatcher } from "./services/command-dispatcher.js";
10
10
  import { createClaudeHookService } from "./services/claude-hook-service.js";
11
11
  import { createGitAdapter } from "./adapters/git-adapter.js";
12
12
  import { createAppSettingsService } from "./services/app-settings-service.js";
13
- import { createCcrIntegrationService } from "./services/ccr-integration-service.js";
13
+ import { createCodexBridgeIntegrationService } from "./services/codex-bridge-integration-service.js";
14
14
  import { createAutoMemoryService } from "./services/auto-memory-service.js";
15
15
  import { createArchitectRestartService } from "./services/architect-restart-service.js";
16
16
  import { createRoleStallDetectorService } from "./services/role-stall-detector-service.js";
@@ -88,7 +88,7 @@ export async function createServer(deps, options = {}) {
88
88
  registerDiagnosticsRoutes(app, { diagnosticsService: deps.diagnosticsService });
89
89
  registerAppSettingsRoutes(app, {
90
90
  appSettings: deps.appSettings,
91
- ccrIntegration: deps.ccrIntegration
91
+ codexBridgeIntegration: deps.codexBridgeIntegration
92
92
  });
93
93
  registerClaudeHookRoutes(app, { claudeHookService: deps.claudeHookService });
94
94
  registerGateReviewRoutes(app, {
@@ -186,7 +186,7 @@ export async function createServer(deps, options = {}) {
186
186
  onManualInterrupt: (sessionId) => deps.terminalInterruptService.handleManualInterrupt(sessionId)
187
187
  });
188
188
  app.addHook("onReady", async () => {
189
- await deps.ccrIntegration.initialize();
189
+ await deps.codexBridgeIntegration.initialize();
190
190
  await cleanupRecentTranslationRuntime(deps);
191
191
  deps.terminalProcessExitService.start();
192
192
  deps.runtimeCoordinator.start();
@@ -235,9 +235,9 @@ export function createDefaultServerDeps(options = {}) {
235
235
  const git = createGitAdapter(runner);
236
236
  const claude = createClaudeAdapter(runner);
237
237
  const appSettings = createAppSettingsService({ fs });
238
- const ccrIntegration = createCcrIntegrationService({
238
+ const codexBridgeIntegration = createCodexBridgeIntegrationService({
239
239
  settings: appSettings,
240
- gateway: createCcrGatewayAdapter()
240
+ bridge: createCodexBridgeAdapter()
241
241
  });
242
242
  const runtime = createNodePtyTerminalRuntime({ fs });
243
243
  const registry = createSessionRegistry();
@@ -255,7 +255,7 @@ export function createDefaultServerDeps(options = {}) {
255
255
  projectService,
256
256
  taskService,
257
257
  taskWorkflowService,
258
- ccrIntegration,
258
+ codexBridgeIntegration,
259
259
  apiUrl: options.apiUrl
260
260
  });
261
261
  const harnessService = createHarnessService({
@@ -332,6 +332,7 @@ export function createDefaultServerDeps(options = {}) {
332
332
  appSettings,
333
333
  sessionService,
334
334
  roundService,
335
+ workflowControlService,
335
336
  onArchitecturePlanDisposition: ({ repoRoot, taskSlug, accepted }) => architectRestartService.recordArchitectureGateDisposition(repoRoot, taskSlug, accepted)
336
337
  });
337
338
  const translationWorkerService = createTranslationWorkerService({
@@ -416,7 +417,8 @@ export function createDefaultServerDeps(options = {}) {
416
417
  jobGuard: createJobGuardService(),
417
418
  translationWorkerService,
418
419
  architectRestartService,
419
- roleStallDetector
420
+ roleStallDetector,
421
+ workflowControlService
420
422
  });
421
423
  const runtimeCoordinator = createRuntimeCoordinatorService({
422
424
  appSettings,
@@ -456,7 +458,7 @@ export function createDefaultServerDeps(options = {}) {
456
458
  });
457
459
  return {
458
460
  appSettings,
459
- ccrIntegration,
461
+ codexBridgeIntegration,
460
462
  projectService,
461
463
  taskService,
462
464
  taskCloseService,