@defend-tech/opencode-optima 0.1.59 → 0.1.61

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.
@@ -51,7 +51,9 @@ You are Workflow_Product_Manager, Optima's ClickUp-first delivery orchestrator.
51
51
 
52
52
  - Principal workspace stays on `dev`; never use `main` for delivery and never push directly to `main`.
53
53
  - Do not implement, plan, or write ClickUp task mirrors in the principal workspace. Use task-specific worktrees/branches.
54
- - To plan a ClickUp task, first create or reuse that task's branch/worktree with `optima_clickup_start_task`, then do Definition planning and local `.optima` mirror writes inside that task worktree.
54
+ - To plan a ClickUp task, first create or reuse that task's branch/worktree with `optima_clickup_start_task`; webhook-created worktrees must be provisioned or registered through the configured OpenChamber Git API (`clickup.openchamber.base_url` `/api/git/worktrees`) before local `.optima` mirror writes.
55
+ - Use `clickup.openchamber.base_url` only for OpenChamber Git worktree API calls; use `clickup.opencode.base_url` for OpenCode workspace/project sync, visibility checks, and session/prompt delivery.
56
+ - If OpenChamber cannot create or verify the required branch/worktree (especially subtask start-from-parent semantics), fail closed with a ClickUp blocker comment instead of silently using raw `git worktree add`.
55
57
  - Do not update the principal `dev` workspace `.optima/tasks/current.md` for ClickUp task planning.
56
58
  - Unrelated active tasks in `.optima/tasks/current.md` must not block planning; move to/create the correct task worktree instead.
57
59
  - Parent setup pulls remote once; after parent branch creation, subtasks can trust the parent local branch without continuous remote polling.
package/dist/index.js CHANGED
@@ -8527,6 +8527,7 @@ var CLICKUP_WEBHOOK_REQUEST_TIMEOUT_MS = 1e4;
8527
8527
  var CLICKUP_WEBHOOK_STARTUP_TASK_LIMIT = 50;
8528
8528
  var CLICKUP_WEBHOOK_STARTUP_COMMENT_LIMIT = 100;
8529
8529
  var CLICKUP_WEBHOOK_STARTUP_RECONCILIATION_DELAY_MS = 3e4;
8530
+ var CLICKUP_WORKTREE_FAILURE_COMMENT_DEDUPE_MS = 10 * 60 * 1e3;
8530
8531
  var CLICKUP_WEBHOOK_LOG_LEVELS = /* @__PURE__ */ new Set(["error", "info", "verbose"]);
8531
8532
  var CLICKUP_WEBHOOK_REDACTED = "[REDACTED]";
8532
8533
  var DISCUSSION_BACKFILL_FETCH_LIMIT = 100;
@@ -9224,6 +9225,239 @@ function addClickUpWorktreeForBranch({ baseWorktree, branch, worktreePath, start
9224
9225
  runGitFn(baseWorktree, ["worktree", "add", "-b", branch, worktreePath, startPoint]);
9225
9226
  }
9226
9227
  }
9228
+ function clickUpOpenChamberWorktreeName(branch = "") {
9229
+ return String(branch || "").trim().replace(/\//g, "-");
9230
+ }
9231
+ function openChamberUrl(baseUrl, pathname, query = {}) {
9232
+ const url = new URL(pathname, normalizeOpenCodeBaseUrl(baseUrl));
9233
+ for (const [key, value] of Object.entries(query)) {
9234
+ if (value !== void 0 && value !== null && String(value).trim()) url.searchParams.set(key, String(value));
9235
+ }
9236
+ return url.toString();
9237
+ }
9238
+ async function readOpenChamberJson(response, endpoint, serviceName = "OpenChamber") {
9239
+ const text = await response.text();
9240
+ let data = null;
9241
+ if (text.trim()) {
9242
+ try {
9243
+ data = JSON.parse(text);
9244
+ } catch {
9245
+ throw new Error(`${serviceName} ${endpoint} returned non-JSON response.`);
9246
+ }
9247
+ }
9248
+ if (!response.ok) {
9249
+ const message = data?.error?.message || data?.message || data?.data?.message || text.slice(0, 200) || `HTTP ${response.status}`;
9250
+ throw new Error(`${serviceName} ${endpoint} failed: ${response.status} ${message}`);
9251
+ }
9252
+ return data;
9253
+ }
9254
+ async function requestServiceJson({ baseUrl, serviceName = "OpenChamber", endpoint, method = "GET", directory, body, fetchImpl = globalThis.fetch } = {}) {
9255
+ if (typeof fetchImpl !== "function") throw new Error(`${serviceName} API calls require fetch.`);
9256
+ const response = await fetchImpl(openChamberUrl(baseUrl, endpoint, { directory }), {
9257
+ method,
9258
+ headers: body ? { "content-type": "application/json" } : void 0,
9259
+ body: body ? JSON.stringify(body) : void 0
9260
+ });
9261
+ return readOpenChamberJson(response, endpoint, serviceName);
9262
+ }
9263
+ async function requestOpenChamberJson(options = {}) {
9264
+ return requestServiceJson({ ...options, serviceName: "OpenChamber" });
9265
+ }
9266
+ async function requestOpenCodeJson(options = {}) {
9267
+ return requestServiceJson({ ...options, serviceName: "OpenCode" });
9268
+ }
9269
+ function normalizeOpenChamberCollection(value) {
9270
+ if (Array.isArray(value)) return value;
9271
+ if (!isPlainObject(value)) return [];
9272
+ for (const key of ["worktrees", "items", "data", "result", "results", "directories", "sandboxes"]) {
9273
+ const nested = value[key];
9274
+ if (Array.isArray(nested)) return nested;
9275
+ if (isPlainObject(nested)) {
9276
+ const normalized = normalizeOpenChamberCollection(nested);
9277
+ if (normalized.length > 0) return normalized;
9278
+ }
9279
+ }
9280
+ return [];
9281
+ }
9282
+ function openChamberEntryDirectory(entry) {
9283
+ if (typeof entry === "string") return entry;
9284
+ return String(entry?.directory || entry?.path || entry?.worktreePath || entry?.worktree_path || entry?.worktree?.path || entry?.worktree?.directory || "");
9285
+ }
9286
+ function openChamberEntryBranch(entry) {
9287
+ if (typeof entry === "string") return "";
9288
+ return String(entry?.branch || entry?.branchName || entry?.branch_name || entry?.worktree?.branch || "").replace(/^refs\/heads\//, "");
9289
+ }
9290
+ function openChamberListIncludesDirectory(list, directory) {
9291
+ const resolved = path5.resolve(directory);
9292
+ return normalizeOpenChamberCollection(list).some((entry) => {
9293
+ const entryDirectory = openChamberEntryDirectory(entry);
9294
+ return entryDirectory && path5.resolve(entryDirectory) === resolved;
9295
+ });
9296
+ }
9297
+ function openChamberListIncludesBranch(list, directory, branch) {
9298
+ const resolved = path5.resolve(directory);
9299
+ return normalizeOpenChamberCollection(list).some((entry) => {
9300
+ const entryDirectory = openChamberEntryDirectory(entry);
9301
+ if (!entryDirectory || path5.resolve(entryDirectory) !== resolved) return false;
9302
+ const entryBranch = openChamberEntryBranch(entry);
9303
+ return !entryBranch || entryBranch === branch;
9304
+ });
9305
+ }
9306
+ async function findOpenChamberProject({ opencodeBaseUrl, baseWorktree, fetchImpl = globalThis.fetch } = {}) {
9307
+ const projects = await requestOpenCodeJson({ baseUrl: opencodeBaseUrl, endpoint: "/project", directory: baseWorktree, fetchImpl });
9308
+ if (!Array.isArray(projects)) return null;
9309
+ const resolvedBase = path5.resolve(baseWorktree);
9310
+ return projects.find((project) => path5.resolve(String(project?.worktree || project?.path || "")) === resolvedBase) || null;
9311
+ }
9312
+ async function refreshOpenChamberProjectCopy({ opencodeBaseUrl, projectId, fetchImpl = globalThis.fetch } = {}) {
9313
+ if (!projectId) return { refreshed: false, reason: "project_id_unavailable" };
9314
+ await requestOpenCodeJson({ baseUrl: opencodeBaseUrl, endpoint: `/experimental/project/${encodeURIComponent(projectId)}/copy/refresh`, method: "POST", fetchImpl });
9315
+ return { refreshed: true, projectId };
9316
+ }
9317
+ async function listOpenChamberGitWorktrees({ openchamberBaseUrl, baseUrl, baseWorktree, fetchImpl = globalThis.fetch } = {}) {
9318
+ return requestOpenChamberJson({ baseUrl: openchamberBaseUrl || baseUrl, endpoint: "/api/git/worktrees", directory: baseWorktree, fetchImpl });
9319
+ }
9320
+ async function verifyOpenChamberGitWorktree({ openchamberBaseUrl, baseUrl, baseWorktree, worktreePath, branch, fetchImpl = globalThis.fetch } = {}) {
9321
+ const worktrees = await listOpenChamberGitWorktrees({ openchamberBaseUrl: openchamberBaseUrl || baseUrl, baseWorktree, fetchImpl });
9322
+ const verified = openChamberListIncludesBranch(worktrees, worktreePath, branch);
9323
+ if (!verified) {
9324
+ throw new Error(`OpenChamber Git worktree verification failed for ${worktreePath} on ${branch}.`);
9325
+ }
9326
+ return { worktree: true, branch: true };
9327
+ }
9328
+ async function syncOpenChamberWorktreeVisibility({ openchamberBaseUrl, opencodeBaseUrl, baseUrl, baseWorktree, worktreePath, branch, fetchImpl = globalThis.fetch } = {}) {
9329
+ const effectiveOpenChamberBaseUrl = openchamberBaseUrl || baseUrl;
9330
+ const effectiveOpenCodeBaseUrl = opencodeBaseUrl || baseUrl;
9331
+ const gitWorktree = await verifyOpenChamberGitWorktree({ openchamberBaseUrl: effectiveOpenChamberBaseUrl, baseWorktree, worktreePath, branch, fetchImpl });
9332
+ await requestOpenCodeJson({ baseUrl: effectiveOpenCodeBaseUrl, endpoint: "/experimental/workspace/sync-list", method: "POST", directory: baseWorktree, fetchImpl });
9333
+ const project = await findOpenChamberProject({ opencodeBaseUrl: effectiveOpenCodeBaseUrl, baseWorktree, fetchImpl });
9334
+ if (!project?.id) throw new Error("OpenCode project was not found after workspace sync; refusing to treat worktree as visible.");
9335
+ await refreshOpenChamberProjectCopy({ opencodeBaseUrl: effectiveOpenCodeBaseUrl, projectId: project.id, fetchImpl });
9336
+ const [worktrees, workspaces, directories] = await Promise.all([
9337
+ listOpenChamberGitWorktrees({ openchamberBaseUrl: effectiveOpenChamberBaseUrl, baseWorktree, fetchImpl }),
9338
+ requestOpenCodeJson({ baseUrl: effectiveOpenCodeBaseUrl, endpoint: "/experimental/workspace", directory: baseWorktree, fetchImpl }),
9339
+ requestOpenCodeJson({ baseUrl: effectiveOpenCodeBaseUrl, endpoint: `/project/${encodeURIComponent(project.id)}/directories`, directory: baseWorktree, fetchImpl })
9340
+ ]);
9341
+ const visibility = {
9342
+ worktree: openChamberListIncludesBranch(worktrees, worktreePath, branch),
9343
+ workspace: openChamberListIncludesBranch(workspaces, worktreePath, branch),
9344
+ projectDirectory: openChamberListIncludesDirectory(directories, worktreePath),
9345
+ gitWorktree,
9346
+ projectId: project.id
9347
+ };
9348
+ if (!visibility.worktree || !visibility.workspace || !visibility.projectDirectory) {
9349
+ throw new Error(`OpenCode visibility verification failed for ${worktreePath}: ${JSON.stringify(visibility)}`);
9350
+ }
9351
+ return visibility;
9352
+ }
9353
+ function assertOpenChamberClickUpWorktreePath({ baseWorktree, worktreePath } = {}) {
9354
+ if (!isClickUpDerivedWorktreeSibling(worktreePath, baseWorktree)) {
9355
+ throw new Error(`OpenChamber worktree path is outside the configured ClickUp sibling scope: ${worktreePath}`);
9356
+ }
9357
+ }
9358
+ async function createOpenChamberClickUpWorktree({ openchamberBaseUrl, baseUrl, baseWorktree, branch, worktreePath, startPoint, branchExists = false, fetchImpl = globalThis.fetch } = {}) {
9359
+ const effectiveOpenChamberBaseUrl = openchamberBaseUrl || baseUrl;
9360
+ const worktreeName = clickUpOpenChamberWorktreeName(branch);
9361
+ const body = { name: worktreeName, mode: branchExists ? "existing" : "new", worktreeName, branchName: branch, startRef: branchExists ? branch : startPoint };
9362
+ let created;
9363
+ try {
9364
+ created = await requestOpenChamberJson({ baseUrl: effectiveOpenChamberBaseUrl, endpoint: "/api/git/worktrees", method: "POST", directory: baseWorktree, body, fetchImpl });
9365
+ } catch (error) {
9366
+ throw new Error(`OpenChamber could not create ${branch} from ${branchExists ? branch : startPoint}; API may not support required branch/startRef semantics. Fail-closed: ${error.message}`);
9367
+ }
9368
+ const createdDirectory = openChamberEntryDirectory(created);
9369
+ const createdBranch = openChamberEntryBranch(created);
9370
+ if (!createdDirectory || !path5.isAbsolute(createdDirectory)) {
9371
+ throw new Error(`OpenChamber did not return an absolute worktree path for ${branch}.`);
9372
+ }
9373
+ if (createdBranch !== branch) {
9374
+ throw new Error(`OpenChamber created unexpected branch ${createdBranch || "<unknown>"}; expected ${branch}.`);
9375
+ }
9376
+ const verified = await verifyOpenChamberGitWorktree({ openchamberBaseUrl: effectiveOpenChamberBaseUrl, baseWorktree, worktreePath: createdDirectory, branch, fetchImpl });
9377
+ return { created, worktree: path5.resolve(createdDirectory), branch: createdBranch, verified };
9378
+ }
9379
+ async function registerOpenChamberClickUpWorktree({ openchamberBaseUrl, opencodeBaseUrl, baseUrl, baseWorktree, branch, worktreePath, fetchImpl = globalThis.fetch, source = "reuse" } = {}) {
9380
+ assertOpenChamberClickUpWorktreePath({ baseWorktree, worktreePath });
9381
+ const visibility = await syncOpenChamberWorktreeVisibility({ openchamberBaseUrl: openchamberBaseUrl || baseUrl, opencodeBaseUrl: opencodeBaseUrl || baseUrl, baseWorktree, worktreePath, branch, fetchImpl });
9382
+ return { branch, worktree: path5.resolve(worktreePath), reused: true, provider: "openchamber", openChamber: { source, visibility } };
9383
+ }
9384
+ async function ensureClickUpTaskWorktreeOpenChamber({ baseWorktree = "", taskId, taskType = "Tarea", parentTaskId = "", subtaskId = "", existingMetadata = {}, runGitFn = runGit, openchamberBaseUrl = "", opencodeBaseUrl = "", baseUrl = "", fetchImpl = globalThis.fetch, log = null } = {}) {
9385
+ const effectiveOpenChamberBaseUrl = openchamberBaseUrl || baseUrl;
9386
+ const effectiveOpenCodeBaseUrl = opencodeBaseUrl || baseUrl;
9387
+ const effectiveParent = parentTaskId || taskId;
9388
+ const isSubtask = isClickUpSubtaskRoute({ taskType, parentTaskId: effectiveParent, subtaskId, taskId });
9389
+ const parentBranch = isSubtask ? deriveClickUpBranchName({ taskType, parentTaskId: effectiveParent }) : "";
9390
+ const branch = deriveClickUpBranchName({ taskType, parentTaskId: effectiveParent, subtaskId, taskId });
9391
+ const prTarget = parentBranch || "dev";
9392
+ const existing = safeExistingClickUpWorktree({ metadata: existingMetadata, branch });
9393
+ if (existing) {
9394
+ const registered = await registerOpenChamberClickUpWorktree({ openchamberBaseUrl: effectiveOpenChamberBaseUrl, opencodeBaseUrl: effectiveOpenCodeBaseUrl, baseWorktree, branch, worktreePath: existing.worktree, fetchImpl, source: "metadata_reuse" });
9395
+ log?.({ type: "openchamber_worktree_registered", taskId, branch, worktree: registered.worktree, source: "metadata_reuse", visibility: registered.openChamber.visibility });
9396
+ return { ...registered, parentBranch: parentBranch || void 0, prTarget };
9397
+ }
9398
+ const worktreePath = deriveClickUpWorktree({ baseWorktree, taskId, taskType, parentTaskId: effectiveParent, subtaskId });
9399
+ if (fs5.existsSync(worktreePath)) {
9400
+ const registered = await registerOpenChamberClickUpWorktree({ openchamberBaseUrl: effectiveOpenChamberBaseUrl, opencodeBaseUrl: effectiveOpenCodeBaseUrl, baseWorktree, branch, worktreePath, fetchImpl, source: "existing_directory" });
9401
+ log?.({ type: "openchamber_worktree_registered", taskId, branch, worktree: registered.worktree, source: "existing_directory", visibility: registered.openChamber.visibility });
9402
+ return { ...registered, parentBranch: parentBranch || void 0, prTarget };
9403
+ }
9404
+ let parentBootstrap = null;
9405
+ if (isSubtask) {
9406
+ const parentWorktree = deriveClickUpWorktree({ baseWorktree, taskId: effectiveParent, taskType, parentTaskId: effectiveParent });
9407
+ if (fs5.existsSync(parentWorktree)) {
9408
+ const registeredParent = await registerOpenChamberClickUpWorktree({ openchamberBaseUrl: effectiveOpenChamberBaseUrl, opencodeBaseUrl: effectiveOpenCodeBaseUrl, baseWorktree, branch: parentBranch, worktreePath: parentWorktree, fetchImpl, source: "parent_existing_directory" });
9409
+ parentBootstrap = { branch: parentBranch, worktree: registeredParent.worktree, reused: true, provider: "openchamber", visibility: registeredParent.openChamber.visibility };
9410
+ } else {
9411
+ const parentStartPoint = resolveClickUpDevStartPoint(baseWorktree, runGitFn);
9412
+ const parentBranchExists = clickUpGitRefExists(baseWorktree, parentBranch, runGitFn);
9413
+ const createdParent = await createOpenChamberClickUpWorktree({ openchamberBaseUrl: effectiveOpenChamberBaseUrl, baseWorktree, branch: parentBranch, worktreePath: parentWorktree, startPoint: parentStartPoint, branchExists: parentBranchExists, fetchImpl });
9414
+ const parentVisibility = await syncOpenChamberWorktreeVisibility({ openchamberBaseUrl: effectiveOpenChamberBaseUrl, opencodeBaseUrl: effectiveOpenCodeBaseUrl, baseWorktree, worktreePath: createdParent.worktree, branch: parentBranch, fetchImpl });
9415
+ parentBootstrap = { branch: parentBranch, worktree: createdParent.worktree, startPoint: parentBranchExists ? parentBranch : parentStartPoint, reused: parentBranchExists, provider: "openchamber", visibility: parentVisibility };
9416
+ log?.({ type: "openchamber_worktree_created", taskId: effectiveParent, branch: parentBranch, worktree: parentBootstrap.worktree, startPoint: parentBootstrap.startPoint, visibility: parentVisibility });
9417
+ }
9418
+ }
9419
+ const startPoint = isSubtask ? parentBranch : resolveClickUpDevStartPoint(baseWorktree, runGitFn);
9420
+ const branchExists = clickUpGitRefExists(baseWorktree, branch, runGitFn);
9421
+ const created = await createOpenChamberClickUpWorktree({ openchamberBaseUrl: effectiveOpenChamberBaseUrl, baseWorktree, branch, worktreePath, startPoint, branchExists, fetchImpl });
9422
+ const visibility = await syncOpenChamberWorktreeVisibility({ openchamberBaseUrl: effectiveOpenChamberBaseUrl, opencodeBaseUrl: effectiveOpenCodeBaseUrl, baseWorktree, worktreePath: created.worktree, branch, fetchImpl });
9423
+ log?.({ type: "openchamber_worktree_created", taskId, branch, worktree: created.worktree, startPoint: branchExists ? branch : startPoint, visibility });
9424
+ return { branch, worktree: created.worktree, reused: false, startPoint, parentBranch: parentBranch || void 0, prTarget, parentBootstrap: parentBootstrap || void 0, provider: "openchamber", openChamber: { source: "created", visibility } };
9425
+ }
9426
+ async function ensureClickUpTaskWorktreeForWebhook({ opencodeBaseUrl = "", opencodeBaseUrlConfigured = false, openchamberBaseUrl = "", openchamberBaseUrlConfigured = false, clickupClient = null, webhookWorktree = process.cwd(), fetchImpl = globalThis.fetch, ...options } = {}) {
9427
+ void opencodeBaseUrlConfigured;
9428
+ if (!openchamberBaseUrlConfigured || !openchamberBaseUrl) return ensureClickUpTaskWorktree(options);
9429
+ try {
9430
+ return await ensureClickUpTaskWorktreeOpenChamber({ ...options, openchamberBaseUrl, opencodeBaseUrl, fetchImpl, log: (entry) => appendClickUpWebhookLocalLog(webhookWorktree, entry) });
9431
+ } catch (error) {
9432
+ const message = `OpenChamber worktree provisioning failed for ${options.taskId || "unknown task"}; raw git fallback is disabled to avoid invisible worktrees. ${error.message}`;
9433
+ appendClickUpWebhookLocalLog(webhookWorktree, { type: "openchamber_worktree_failed", taskId: options.taskId || null, message });
9434
+ if (typeof clickupClient?.postTaskComment === "function") {
9435
+ const taskId = options.taskId || "unknown task";
9436
+ const ledgerPath = clickUpCommentLedgerPath(webhookWorktree);
9437
+ const comment = `${message}
9438
+
9439
+ Optima did not run raw git worktree fallback. Please verify clickup.openchamber.base_url and OpenCode/OpenChamber endpoint configuration.`;
9440
+ let dedupe = { post: true, key: clickUpWorktreeFailureCommentKey({ taskId, message }) };
9441
+ try {
9442
+ dedupe = shouldPostClickUpWorktreeFailureComment({ ledgerPath, taskId, message });
9443
+ } catch (ledgerError) {
9444
+ appendClickUpWebhookLocalLog(webhookWorktree, { type: "openchamber_worktree_comment_dedupe_failed", taskId, message: ledgerError.message });
9445
+ }
9446
+ if (dedupe.post) {
9447
+ try {
9448
+ await clickupClient.postTaskComment({ taskId: options.taskId, comment });
9449
+ recordClickUpWorktreeFailureComment({ ledgerPath, taskId, message, posted: true });
9450
+ } catch (commentError) {
9451
+ appendClickUpWebhookLocalLog(webhookWorktree, { type: "openchamber_worktree_blocker_comment_failed", taskId: options.taskId || null, message: commentError.message });
9452
+ }
9453
+ } else {
9454
+ appendClickUpWebhookLocalLog(webhookWorktree, { type: "openchamber_worktree_blocker_comment_deduped", taskId, key: dedupe.key, reason: dedupe.reason });
9455
+ recordClickUpWorktreeFailureComment({ ledgerPath, taskId, message, posted: false, skippedReason: dedupe.reason });
9456
+ }
9457
+ }
9458
+ throw new Error(message);
9459
+ }
9460
+ }
9227
9461
  function ensureClickUpTaskWorktree({ baseWorktree = "", taskId, taskType = "Tarea", parentTaskId = "", subtaskId = "", existingMetadata = {}, runGitFn = runGit, allowNonGitFallback = false } = {}) {
9228
9462
  const effectiveParent = parentTaskId || taskId;
9229
9463
  const isSubtask = isClickUpSubtaskRoute({ taskType, parentTaskId: effectiveParent, subtaskId, taskId });
@@ -9585,6 +9819,11 @@ function normalizeClickUpWebhookConfig(rawClickUp = null, worktree = process.cwd
9585
9819
  const webhook = isPlainObject(raw.webhook) ? raw.webhook : {};
9586
9820
  const routing = isPlainObject(raw.routing) ? raw.routing : {};
9587
9821
  const opencode = isPlainObject(raw.opencode) ? raw.opencode : {};
9822
+ const openchamber = isPlainObject(raw.openchamber) ? raw.openchamber : {};
9823
+ const rawOpenCodeBaseUrl = opencode.base_url ?? opencode.baseUrl ?? raw.opencode_base_url ?? raw.opencodeBaseUrl;
9824
+ const rawOpenChamberBaseUrl = openchamber.base_url ?? openchamber.baseUrl ?? raw.openchamber_base_url ?? raw.openchamberBaseUrl;
9825
+ const openCodeBaseUrlConfigured = rawOpenCodeBaseUrl !== void 0 && rawOpenCodeBaseUrl !== null && String(rawOpenCodeBaseUrl).trim() !== "";
9826
+ const openChamberBaseUrlConfigured = rawOpenChamberBaseUrl !== void 0 && rawOpenChamberBaseUrl !== null && String(rawOpenChamberBaseUrl).trim() !== "";
9588
9827
  const location = isPlainObject(webhook.location) ? webhook.location : {};
9589
9828
  const events = Array.isArray(webhook.events) && webhook.events.length > 0 ? [...new Set(webhook.events.map((event) => String(event || "").trim()).filter(Boolean))] : [...CLICKUP_WEBHOOK_EVENTS];
9590
9829
  const ignoredStatuses = Array.isArray(routing.ignored_statuses) && routing.ignored_statuses.length > 0 ? routing.ignored_statuses : CLICKUP_WEBHOOK_TERMINAL_STATUSES;
@@ -9596,7 +9835,8 @@ function normalizeClickUpWebhookConfig(rawClickUp = null, worktree = process.cwd
9596
9835
  apiToken: String(raw.api_token || raw.apiToken || "").trim(),
9597
9836
  log: normalizeClickUpWebhookLogLevel(raw.log),
9598
9837
  opencode: {
9599
- baseUrl: normalizeOpenCodeBaseUrl(opencode.base_url || opencode.baseUrl || raw.opencode_base_url || raw.opencodeBaseUrl),
9838
+ baseUrl: normalizeOpenCodeBaseUrl(rawOpenCodeBaseUrl),
9839
+ baseUrlConfigured: openCodeBaseUrlConfigured,
9600
9840
  promptDelivery: ["http", "direct"].includes(String(opencode.prompt_delivery || opencode.promptDelivery || raw.prompt_delivery || raw.promptDelivery || "").trim().toLowerCase()) ? "http" : "sdk",
9601
9841
  acceptPromptAdmission: opencode.accept_prompt_admission === true || opencode.acceptPromptAdmission === true || raw.accept_prompt_admission === true || raw.acceptPromptAdmission === true,
9602
9842
  startupReconciliationDelayMs: normalizeNonNegativeInteger(
@@ -9604,6 +9844,10 @@ function normalizeClickUpWebhookConfig(rawClickUp = null, worktree = process.cwd
9604
9844
  CLICKUP_WEBHOOK_STARTUP_RECONCILIATION_DELAY_MS
9605
9845
  )
9606
9846
  },
9847
+ openchamber: {
9848
+ baseUrl: normalizeOpenCodeBaseUrl(rawOpenChamberBaseUrl, ""),
9849
+ baseUrlConfigured: openChamberBaseUrlConfigured
9850
+ },
9607
9851
  webhook: {
9608
9852
  publicUrl: String(webhook.public_url || webhook.publicUrl || "").trim(),
9609
9853
  bindHost: String(webhook.bind_host || webhook.bindHost || "127.0.0.1").trim(),
@@ -9645,6 +9889,13 @@ function normalizeClickUpWebhookConfig(rawClickUp = null, worktree = process.cwd
9645
9889
  errors.push("clickup.opencode.base_url must be a valid URL");
9646
9890
  }
9647
9891
  }
9892
+ if (config.openchamber.baseUrl) {
9893
+ try {
9894
+ new URL(config.openchamber.baseUrl);
9895
+ } catch {
9896
+ errors.push("clickup.openchamber.base_url must be a valid URL");
9897
+ }
9898
+ }
9648
9899
  const missingEvents = CLICKUP_WEBHOOK_EVENTS.filter((event) => !config.webhook.events.includes(event));
9649
9900
  if (missingEvents.length > 0) errors.push(`clickup.webhook.events missing: ${missingEvents.join(", ")}`);
9650
9901
  if (config.routing.targetAgent !== "workflow_product_manager") errors.push("clickup.routing.target_agent must be workflow_product_manager");
@@ -10001,23 +10252,29 @@ function rememberClickUpWebhookEvent(state = {}, eventKey, limit = 200) {
10001
10252
  if (recent.includes(eventKey)) return { duplicate: true, state };
10002
10253
  return { duplicate: false, state: { ...state, recentEventKeys: [...recent, eventKey].slice(-limit) } };
10003
10254
  }
10004
- function readClickUpCommentLedger(ledgerPath) {
10005
- const processed = /* @__PURE__ */ new Set();
10006
- if (!ledgerPath || !fs5.existsSync(ledgerPath)) return processed;
10255
+ function readClickUpCommentLedgerEntries(ledgerPath) {
10256
+ if (!ledgerPath || !fs5.existsSync(ledgerPath)) return [];
10007
10257
  try {
10008
10258
  const raw = fs5.readFileSync(ledgerPath, "utf8");
10259
+ const entries = [];
10009
10260
  for (const [index, line] of raw.split(/\r?\n/).entries()) {
10010
10261
  if (!line.trim()) continue;
10011
10262
  try {
10012
- const entry = JSON.parse(line);
10013
- if (entry?.key) processed.add(String(entry.key));
10263
+ entries.push(JSON.parse(line));
10014
10264
  } catch (error) {
10015
10265
  throw new Error(`malformed ledger row ${index + 1}: ${error.message}`);
10016
10266
  }
10017
10267
  }
10268
+ return entries;
10018
10269
  } catch (error) {
10019
10270
  throw new Error(`ClickUp comment ledger unavailable: ${error.message}`);
10020
10271
  }
10272
+ }
10273
+ function readClickUpCommentLedger(ledgerPath) {
10274
+ const processed = /* @__PURE__ */ new Set();
10275
+ for (const entry of readClickUpCommentLedgerEntries(ledgerPath)) {
10276
+ if (entry?.key) processed.add(String(entry.key));
10277
+ }
10021
10278
  return processed;
10022
10279
  }
10023
10280
  function appendClickUpCommentLedgerEntry(ledgerPath, entry = {}) {
@@ -10083,6 +10340,35 @@ function recordClickUpCommentVersionProcessed({ ledgerPath, key, taskId, eventTy
10083
10340
  recordedAt: at.toISOString()
10084
10341
  });
10085
10342
  }
10343
+ function clickUpWorktreeFailureCommentKey({ taskId, message } = {}) {
10344
+ const hash = crypto.createHash("sha256").update(String(message || "")).digest("hex").slice(0, 16);
10345
+ return [String(taskId || "unknown").trim() || "unknown", "worktree_failure", hash].join(":");
10346
+ }
10347
+ function shouldPostClickUpWorktreeFailureComment({ ledgerPath, taskId, message, now = /* @__PURE__ */ new Date(), windowMs = CLICKUP_WORKTREE_FAILURE_COMMENT_DEDUPE_MS } = {}) {
10348
+ const key = clickUpWorktreeFailureCommentKey({ taskId, message });
10349
+ const nowMs = now instanceof Date ? now.getTime() : new Date(now).getTime();
10350
+ for (const entry of readClickUpCommentLedgerEntries(ledgerPath).reverse()) {
10351
+ if (entry?.key !== key) continue;
10352
+ const postedAtMs = new Date(entry.postedAt || entry.recordedAt || entry.at || 0).getTime();
10353
+ if (Number.isFinite(postedAtMs) && Number.isFinite(nowMs) && nowMs - postedAtMs <= windowMs) {
10354
+ return { post: false, key, reason: "recent_duplicate" };
10355
+ }
10356
+ if (entry?.message === message) return { post: false, key, reason: "latest_equivalent" };
10357
+ }
10358
+ return { post: true, key };
10359
+ }
10360
+ function recordClickUpWorktreeFailureComment({ ledgerPath, taskId, message, posted = true, skippedReason = null, at = /* @__PURE__ */ new Date() } = {}) {
10361
+ appendClickUpCommentLedgerEntry(ledgerPath, {
10362
+ key: clickUpWorktreeFailureCommentKey({ taskId, message }),
10363
+ taskId,
10364
+ eventType: "worktree_failure",
10365
+ action: posted ? "worktree_failure_comment_posted" : "worktree_failure_comment_skipped",
10366
+ message,
10367
+ posted,
10368
+ skippedReason,
10369
+ postedAt: at.toISOString()
10370
+ });
10371
+ }
10086
10372
  function clickUpTaskIdFromPayload(payload = {}) {
10087
10373
  return String(payload.task_id || payload.taskId || payload.task?.id || payload.task?.task_id || "").trim();
10088
10374
  }
@@ -10934,7 +11220,7 @@ async function withClickUpTaskRouteLock(taskId, operation) {
10934
11220
  if (activeClickUpTaskRoutes.get(key) === current) activeClickUpTaskRoutes.delete(key);
10935
11221
  }
10936
11222
  }
10937
- async function routeClickUpWebhookEventUnlocked({ payload, config, state = {}, worktree = process.cwd(), clickupClient, openCodeClient, sessionExists = openCodeSessionExists, createSession = createOpenCodeSession, sendSessionEvent = sendOpenCodeSessionEvent, ensureTaskWorktree = ensureClickUpTaskWorktree, verifySessionEventDelivery = verifyOpenCodeSessionEventDelivery, saveState = null, now = () => /* @__PURE__ */ new Date(), host = os.hostname(), commentLedgerPath = clickUpCommentLedgerPath(worktree) } = {}) {
11223
+ async function routeClickUpWebhookEventUnlocked({ payload, config, state = {}, worktree = process.cwd(), clickupClient, openCodeClient, sessionExists = openCodeSessionExists, createSession = createOpenCodeSession, sendSessionEvent = sendOpenCodeSessionEvent, ensureTaskWorktree = ensureClickUpTaskWorktreeForWebhook, verifySessionEventDelivery = verifyOpenCodeSessionEventDelivery, saveState = null, now = () => /* @__PURE__ */ new Date(), host = os.hostname(), commentLedgerPath = clickUpCommentLedgerPath(worktree) } = {}) {
10938
11224
  const eventType = clickUpEventType(payload);
10939
11225
  const eventKey = clickUpWebhookEventKey(payload);
10940
11226
  const isStartupAssignmentReconciliation = payload?.startup_reconciliation === true && eventType === "taskAssigneeUpdated";
@@ -10990,7 +11276,7 @@ async function routeClickUpWebhookEventUnlocked({ payload, config, state = {}, w
10990
11276
  const subtaskId = parentTaskId && parentTaskId !== taskId ? taskId : "";
10991
11277
  let taskRoute;
10992
11278
  try {
10993
- taskRoute = ensureTaskWorktree({ baseWorktree: config.basePath, taskId, taskType, parentTaskId, subtaskId, existingMetadata: metadata, allowNonGitFallback: process.env.NODE_ENV === "test" || config.test === true });
11279
+ taskRoute = await ensureTaskWorktree({ baseWorktree: config.basePath, taskId, taskType, parentTaskId, subtaskId, existingMetadata: metadata, allowNonGitFallback: process.env.NODE_ENV === "test" || config.test === true, opencodeBaseUrl: config.opencode?.baseUrl, opencodeBaseUrlConfigured: config.opencode?.baseUrlConfigured === true, openchamberBaseUrl: config.openchamber?.baseUrl, openchamberBaseUrlConfigured: config.openchamber?.baseUrlConfigured === true, clickupClient, webhookWorktree: worktree });
10994
11280
  } catch (error) {
10995
11281
  const message = `Optima webhook could not create or reuse a task worktree for ${taskId}: ${error.message}`;
10996
11282
  appendClickUpWebhookLocalLog(worktree, { type: "task_worktree_failed", taskId, message });
@@ -13149,7 +13435,7 @@ Follow-up: use optima_prompt_workflow with session_id '${sessionId}' to check in
13149
13435
  }
13150
13436
  };
13151
13437
  }
13152
- OptimaPlugin.__internals = { BUNDLE_AGENTS_DIR, BUNDLE_ASSETS_DIR, CLICKUP_DEFINITION_DOC_PARENT, activeClickUpWebhookLifecycleRegistry, cleanupManagedClickUpWebhook, deleteClickUpWebhookBestEffort, BUNDLE_POLICIES_DIR, buildClickUpApplyPayloadResult, buildClickUpCreateSubtasksPayload, buildClickUpStartTaskPayload, buildClickUpSummaryPayload, buildClickUpTransitionPayload, buildOptimaAgents, clickUpCommentLedgerKey, clickUpCommentMentionsProductManager, clickUpWebhookAuditLogDir, clickUpWebhookAuditLogPath, compactPromptPath, createClickUpApiClient, createTestClickUpApiClient, createOpenCodeSession, createOpenCodeSessionControl, deliveryEvidencePathForClickUpTask, ensureClickUpTaskWorktree, ensureClickUpWebhookSubscription, handleClickUpWebhookRequest, isClickUpDerivedWorktreeSibling, isClickUpSubtaskRoute, isClickUpWebhookStateActive, isSameOrNestedPath, normalizeClickUpWebhookConfig, normalizeClickUpWebhookLogLevel, normalizeOpenCodeBaseUrl, openCodeSessionExists, promptOpenCodeSessionControl, probeOpenCodeSessionControl, readClickUpCommentLedger, readClickUpWebhookState, readOpenCodeSessionControl, readOpenCodeSessionMessages, reconcileClickUpStartup, scheduleClickUpStartupReconciliation, waitForOpenCodeReadiness, recordClickUpCommentVersionProcessed, resyncClickUpWebhookForSignatureDrift, resolveIncludeFile, resolveIncludes, resolveOptimaPluginOptions, resolveSecretReference, routeClickUpWebhookEvent, sendOpenCodeSessionEvent, sendOpenCodeSessionEventDirect, summarizeOpenCodeMessages, verifyOpenCodeSessionEventDelivery, stableClickUpCommentVersionMarker, startClickUpWebhookListener, validateClickUpWebhookState, verifyClickUpSignature, writeClickUpWebhookState, decideClickUpStatusAction, deriveClickUpBranchName, deriveClickUpPendingSubtaskBranch, deriveClickUpPrTarget, deriveClickUpWorktree, determineClickUpMergeAuthority, ensureOptimaGitignoreRules, explicitSafeInputWorktree, finalApprovalAssignees, formatRepairResult, formatValidationResult, isIgnoredClickUpTaskType, isOptimaPluginPackageWorktree, isSafeWritableDirectory, loadHumansRegistry, mergeClickUpAgentMetadata, mergeClickUpSessionMetadata, migrateLegacyOptimaLayout, normalizeAgentMetadataJson, normalizeClickUpStatus, normalizeClickUpTaskType, normalizePromptResponseParts, normalizeWorkflowTaskPath, optimaRepairDependencies, parseClickUpSubtasksMarkdown, parseHumansRegistry, parseMarkdownArtifact, parseMarkdownSections, planOptimaRepair, preEstimateClickUpWork, readMarkdownArtifact, resolveHumanRoles, resolveSafeWorktree, safeWorktreeOrFailure, stripRawLogSections, validateMainWorkspaceBranchSafety, workflowFinalMessageFromPromptResponse };
13438
+ OptimaPlugin.__internals = { BUNDLE_AGENTS_DIR, BUNDLE_ASSETS_DIR, CLICKUP_DEFINITION_DOC_PARENT, activeClickUpWebhookLifecycleRegistry, cleanupManagedClickUpWebhook, deleteClickUpWebhookBestEffort, BUNDLE_POLICIES_DIR, buildClickUpApplyPayloadResult, buildClickUpCreateSubtasksPayload, buildClickUpStartTaskPayload, buildClickUpSummaryPayload, buildClickUpTransitionPayload, buildOptimaAgents, clickUpCommentLedgerKey, clickUpCommentMentionsProductManager, clickUpWebhookAuditLogDir, clickUpWebhookAuditLogPath, compactPromptPath, createClickUpApiClient, createTestClickUpApiClient, createOpenCodeSession, createOpenCodeSessionControl, deliveryEvidencePathForClickUpTask, ensureClickUpTaskWorktree, ensureClickUpTaskWorktreeForWebhook, ensureClickUpTaskWorktreeOpenChamber, ensureClickUpWebhookSubscription, handleClickUpWebhookRequest, isClickUpDerivedWorktreeSibling, isClickUpSubtaskRoute, isClickUpWebhookStateActive, isSameOrNestedPath, normalizeClickUpWebhookConfig, normalizeClickUpWebhookLogLevel, normalizeOpenCodeBaseUrl, openCodeSessionExists, promptOpenCodeSessionControl, probeOpenCodeSessionControl, readClickUpCommentLedger, readClickUpWebhookState, readOpenCodeSessionControl, readOpenCodeSessionMessages, reconcileClickUpStartup, registerOpenChamberClickUpWorktree, scheduleClickUpStartupReconciliation, syncOpenChamberWorktreeVisibility, waitForOpenCodeReadiness, recordClickUpCommentVersionProcessed, resyncClickUpWebhookForSignatureDrift, resolveIncludeFile, resolveIncludes, resolveOptimaPluginOptions, resolveSecretReference, routeClickUpWebhookEvent, sendOpenCodeSessionEvent, sendOpenCodeSessionEventDirect, summarizeOpenCodeMessages, verifyOpenCodeSessionEventDelivery, stableClickUpCommentVersionMarker, startClickUpWebhookListener, validateClickUpWebhookState, verifyClickUpSignature, writeClickUpWebhookState, decideClickUpStatusAction, deriveClickUpBranchName, deriveClickUpPendingSubtaskBranch, deriveClickUpPrTarget, deriveClickUpWorktree, determineClickUpMergeAuthority, ensureOptimaGitignoreRules, explicitSafeInputWorktree, finalApprovalAssignees, formatRepairResult, formatValidationResult, isIgnoredClickUpTaskType, isOptimaPluginPackageWorktree, isSafeWritableDirectory, loadHumansRegistry, mergeClickUpAgentMetadata, mergeClickUpSessionMetadata, migrateLegacyOptimaLayout, normalizeAgentMetadataJson, normalizeClickUpStatus, normalizeClickUpTaskType, normalizePromptResponseParts, normalizeWorkflowTaskPath, optimaRepairDependencies, parseClickUpSubtasksMarkdown, parseHumansRegistry, parseMarkdownArtifact, parseMarkdownSections, planOptimaRepair, preEstimateClickUpWork, readMarkdownArtifact, resolveHumanRoles, resolveSafeWorktree, safeWorktreeOrFailure, stripRawLogSections, validateMainWorkspaceBranchSafety, workflowFinalMessageFromPromptResponse };
13153
13439
  export {
13154
13440
  OptimaPlugin as default
13155
13441
  };
@@ -8534,6 +8534,7 @@ var CLICKUP_WEBHOOK_REQUEST_TIMEOUT_MS = 1e4;
8534
8534
  var CLICKUP_WEBHOOK_STARTUP_TASK_LIMIT = 50;
8535
8535
  var CLICKUP_WEBHOOK_STARTUP_COMMENT_LIMIT = 100;
8536
8536
  var CLICKUP_WEBHOOK_STARTUP_RECONCILIATION_DELAY_MS = 3e4;
8537
+ var CLICKUP_WORKTREE_FAILURE_COMMENT_DEDUPE_MS = 10 * 60 * 1e3;
8537
8538
  var CLICKUP_WEBHOOK_LOG_LEVELS = /* @__PURE__ */ new Set(["error", "info", "verbose"]);
8538
8539
  var CLICKUP_WEBHOOK_REDACTED = "[REDACTED]";
8539
8540
  var DISCUSSION_BACKFILL_FETCH_LIMIT = 100;
@@ -9231,6 +9232,239 @@ function addClickUpWorktreeForBranch({ baseWorktree, branch, worktreePath, start
9231
9232
  runGitFn(baseWorktree, ["worktree", "add", "-b", branch, worktreePath, startPoint]);
9232
9233
  }
9233
9234
  }
9235
+ function clickUpOpenChamberWorktreeName(branch = "") {
9236
+ return String(branch || "").trim().replace(/\//g, "-");
9237
+ }
9238
+ function openChamberUrl(baseUrl, pathname, query = {}) {
9239
+ const url = new URL(pathname, normalizeOpenCodeBaseUrl(baseUrl));
9240
+ for (const [key, value] of Object.entries(query)) {
9241
+ if (value !== void 0 && value !== null && String(value).trim()) url.searchParams.set(key, String(value));
9242
+ }
9243
+ return url.toString();
9244
+ }
9245
+ async function readOpenChamberJson(response, endpoint, serviceName = "OpenChamber") {
9246
+ const text = await response.text();
9247
+ let data = null;
9248
+ if (text.trim()) {
9249
+ try {
9250
+ data = JSON.parse(text);
9251
+ } catch {
9252
+ throw new Error(`${serviceName} ${endpoint} returned non-JSON response.`);
9253
+ }
9254
+ }
9255
+ if (!response.ok) {
9256
+ const message = data?.error?.message || data?.message || data?.data?.message || text.slice(0, 200) || `HTTP ${response.status}`;
9257
+ throw new Error(`${serviceName} ${endpoint} failed: ${response.status} ${message}`);
9258
+ }
9259
+ return data;
9260
+ }
9261
+ async function requestServiceJson({ baseUrl, serviceName = "OpenChamber", endpoint, method = "GET", directory, body, fetchImpl = globalThis.fetch } = {}) {
9262
+ if (typeof fetchImpl !== "function") throw new Error(`${serviceName} API calls require fetch.`);
9263
+ const response = await fetchImpl(openChamberUrl(baseUrl, endpoint, { directory }), {
9264
+ method,
9265
+ headers: body ? { "content-type": "application/json" } : void 0,
9266
+ body: body ? JSON.stringify(body) : void 0
9267
+ });
9268
+ return readOpenChamberJson(response, endpoint, serviceName);
9269
+ }
9270
+ async function requestOpenChamberJson(options = {}) {
9271
+ return requestServiceJson({ ...options, serviceName: "OpenChamber" });
9272
+ }
9273
+ async function requestOpenCodeJson(options = {}) {
9274
+ return requestServiceJson({ ...options, serviceName: "OpenCode" });
9275
+ }
9276
+ function normalizeOpenChamberCollection(value) {
9277
+ if (Array.isArray(value)) return value;
9278
+ if (!isPlainObject(value)) return [];
9279
+ for (const key of ["worktrees", "items", "data", "result", "results", "directories", "sandboxes"]) {
9280
+ const nested = value[key];
9281
+ if (Array.isArray(nested)) return nested;
9282
+ if (isPlainObject(nested)) {
9283
+ const normalized = normalizeOpenChamberCollection(nested);
9284
+ if (normalized.length > 0) return normalized;
9285
+ }
9286
+ }
9287
+ return [];
9288
+ }
9289
+ function openChamberEntryDirectory(entry) {
9290
+ if (typeof entry === "string") return entry;
9291
+ return String(entry?.directory || entry?.path || entry?.worktreePath || entry?.worktree_path || entry?.worktree?.path || entry?.worktree?.directory || "");
9292
+ }
9293
+ function openChamberEntryBranch(entry) {
9294
+ if (typeof entry === "string") return "";
9295
+ return String(entry?.branch || entry?.branchName || entry?.branch_name || entry?.worktree?.branch || "").replace(/^refs\/heads\//, "");
9296
+ }
9297
+ function openChamberListIncludesDirectory(list, directory) {
9298
+ const resolved = path5.resolve(directory);
9299
+ return normalizeOpenChamberCollection(list).some((entry) => {
9300
+ const entryDirectory = openChamberEntryDirectory(entry);
9301
+ return entryDirectory && path5.resolve(entryDirectory) === resolved;
9302
+ });
9303
+ }
9304
+ function openChamberListIncludesBranch(list, directory, branch) {
9305
+ const resolved = path5.resolve(directory);
9306
+ return normalizeOpenChamberCollection(list).some((entry) => {
9307
+ const entryDirectory = openChamberEntryDirectory(entry);
9308
+ if (!entryDirectory || path5.resolve(entryDirectory) !== resolved) return false;
9309
+ const entryBranch = openChamberEntryBranch(entry);
9310
+ return !entryBranch || entryBranch === branch;
9311
+ });
9312
+ }
9313
+ async function findOpenChamberProject({ opencodeBaseUrl, baseWorktree, fetchImpl = globalThis.fetch } = {}) {
9314
+ const projects = await requestOpenCodeJson({ baseUrl: opencodeBaseUrl, endpoint: "/project", directory: baseWorktree, fetchImpl });
9315
+ if (!Array.isArray(projects)) return null;
9316
+ const resolvedBase = path5.resolve(baseWorktree);
9317
+ return projects.find((project) => path5.resolve(String(project?.worktree || project?.path || "")) === resolvedBase) || null;
9318
+ }
9319
+ async function refreshOpenChamberProjectCopy({ opencodeBaseUrl, projectId, fetchImpl = globalThis.fetch } = {}) {
9320
+ if (!projectId) return { refreshed: false, reason: "project_id_unavailable" };
9321
+ await requestOpenCodeJson({ baseUrl: opencodeBaseUrl, endpoint: `/experimental/project/${encodeURIComponent(projectId)}/copy/refresh`, method: "POST", fetchImpl });
9322
+ return { refreshed: true, projectId };
9323
+ }
9324
+ async function listOpenChamberGitWorktrees({ openchamberBaseUrl, baseUrl, baseWorktree, fetchImpl = globalThis.fetch } = {}) {
9325
+ return requestOpenChamberJson({ baseUrl: openchamberBaseUrl || baseUrl, endpoint: "/api/git/worktrees", directory: baseWorktree, fetchImpl });
9326
+ }
9327
+ async function verifyOpenChamberGitWorktree({ openchamberBaseUrl, baseUrl, baseWorktree, worktreePath, branch, fetchImpl = globalThis.fetch } = {}) {
9328
+ const worktrees = await listOpenChamberGitWorktrees({ openchamberBaseUrl: openchamberBaseUrl || baseUrl, baseWorktree, fetchImpl });
9329
+ const verified = openChamberListIncludesBranch(worktrees, worktreePath, branch);
9330
+ if (!verified) {
9331
+ throw new Error(`OpenChamber Git worktree verification failed for ${worktreePath} on ${branch}.`);
9332
+ }
9333
+ return { worktree: true, branch: true };
9334
+ }
9335
+ async function syncOpenChamberWorktreeVisibility({ openchamberBaseUrl, opencodeBaseUrl, baseUrl, baseWorktree, worktreePath, branch, fetchImpl = globalThis.fetch } = {}) {
9336
+ const effectiveOpenChamberBaseUrl = openchamberBaseUrl || baseUrl;
9337
+ const effectiveOpenCodeBaseUrl = opencodeBaseUrl || baseUrl;
9338
+ const gitWorktree = await verifyOpenChamberGitWorktree({ openchamberBaseUrl: effectiveOpenChamberBaseUrl, baseWorktree, worktreePath, branch, fetchImpl });
9339
+ await requestOpenCodeJson({ baseUrl: effectiveOpenCodeBaseUrl, endpoint: "/experimental/workspace/sync-list", method: "POST", directory: baseWorktree, fetchImpl });
9340
+ const project = await findOpenChamberProject({ opencodeBaseUrl: effectiveOpenCodeBaseUrl, baseWorktree, fetchImpl });
9341
+ if (!project?.id) throw new Error("OpenCode project was not found after workspace sync; refusing to treat worktree as visible.");
9342
+ await refreshOpenChamberProjectCopy({ opencodeBaseUrl: effectiveOpenCodeBaseUrl, projectId: project.id, fetchImpl });
9343
+ const [worktrees, workspaces, directories] = await Promise.all([
9344
+ listOpenChamberGitWorktrees({ openchamberBaseUrl: effectiveOpenChamberBaseUrl, baseWorktree, fetchImpl }),
9345
+ requestOpenCodeJson({ baseUrl: effectiveOpenCodeBaseUrl, endpoint: "/experimental/workspace", directory: baseWorktree, fetchImpl }),
9346
+ requestOpenCodeJson({ baseUrl: effectiveOpenCodeBaseUrl, endpoint: `/project/${encodeURIComponent(project.id)}/directories`, directory: baseWorktree, fetchImpl })
9347
+ ]);
9348
+ const visibility = {
9349
+ worktree: openChamberListIncludesBranch(worktrees, worktreePath, branch),
9350
+ workspace: openChamberListIncludesBranch(workspaces, worktreePath, branch),
9351
+ projectDirectory: openChamberListIncludesDirectory(directories, worktreePath),
9352
+ gitWorktree,
9353
+ projectId: project.id
9354
+ };
9355
+ if (!visibility.worktree || !visibility.workspace || !visibility.projectDirectory) {
9356
+ throw new Error(`OpenCode visibility verification failed for ${worktreePath}: ${JSON.stringify(visibility)}`);
9357
+ }
9358
+ return visibility;
9359
+ }
9360
+ function assertOpenChamberClickUpWorktreePath({ baseWorktree, worktreePath } = {}) {
9361
+ if (!isClickUpDerivedWorktreeSibling(worktreePath, baseWorktree)) {
9362
+ throw new Error(`OpenChamber worktree path is outside the configured ClickUp sibling scope: ${worktreePath}`);
9363
+ }
9364
+ }
9365
+ async function createOpenChamberClickUpWorktree({ openchamberBaseUrl, baseUrl, baseWorktree, branch, worktreePath, startPoint, branchExists = false, fetchImpl = globalThis.fetch } = {}) {
9366
+ const effectiveOpenChamberBaseUrl = openchamberBaseUrl || baseUrl;
9367
+ const worktreeName = clickUpOpenChamberWorktreeName(branch);
9368
+ const body = { name: worktreeName, mode: branchExists ? "existing" : "new", worktreeName, branchName: branch, startRef: branchExists ? branch : startPoint };
9369
+ let created;
9370
+ try {
9371
+ created = await requestOpenChamberJson({ baseUrl: effectiveOpenChamberBaseUrl, endpoint: "/api/git/worktrees", method: "POST", directory: baseWorktree, body, fetchImpl });
9372
+ } catch (error) {
9373
+ throw new Error(`OpenChamber could not create ${branch} from ${branchExists ? branch : startPoint}; API may not support required branch/startRef semantics. Fail-closed: ${error.message}`);
9374
+ }
9375
+ const createdDirectory = openChamberEntryDirectory(created);
9376
+ const createdBranch = openChamberEntryBranch(created);
9377
+ if (!createdDirectory || !path5.isAbsolute(createdDirectory)) {
9378
+ throw new Error(`OpenChamber did not return an absolute worktree path for ${branch}.`);
9379
+ }
9380
+ if (createdBranch !== branch) {
9381
+ throw new Error(`OpenChamber created unexpected branch ${createdBranch || "<unknown>"}; expected ${branch}.`);
9382
+ }
9383
+ const verified = await verifyOpenChamberGitWorktree({ openchamberBaseUrl: effectiveOpenChamberBaseUrl, baseWorktree, worktreePath: createdDirectory, branch, fetchImpl });
9384
+ return { created, worktree: path5.resolve(createdDirectory), branch: createdBranch, verified };
9385
+ }
9386
+ async function registerOpenChamberClickUpWorktree({ openchamberBaseUrl, opencodeBaseUrl, baseUrl, baseWorktree, branch, worktreePath, fetchImpl = globalThis.fetch, source = "reuse" } = {}) {
9387
+ assertOpenChamberClickUpWorktreePath({ baseWorktree, worktreePath });
9388
+ const visibility = await syncOpenChamberWorktreeVisibility({ openchamberBaseUrl: openchamberBaseUrl || baseUrl, opencodeBaseUrl: opencodeBaseUrl || baseUrl, baseWorktree, worktreePath, branch, fetchImpl });
9389
+ return { branch, worktree: path5.resolve(worktreePath), reused: true, provider: "openchamber", openChamber: { source, visibility } };
9390
+ }
9391
+ async function ensureClickUpTaskWorktreeOpenChamber({ baseWorktree = "", taskId, taskType = "Tarea", parentTaskId = "", subtaskId = "", existingMetadata = {}, runGitFn = runGit, openchamberBaseUrl = "", opencodeBaseUrl = "", baseUrl = "", fetchImpl = globalThis.fetch, log = null } = {}) {
9392
+ const effectiveOpenChamberBaseUrl = openchamberBaseUrl || baseUrl;
9393
+ const effectiveOpenCodeBaseUrl = opencodeBaseUrl || baseUrl;
9394
+ const effectiveParent = parentTaskId || taskId;
9395
+ const isSubtask = isClickUpSubtaskRoute({ taskType, parentTaskId: effectiveParent, subtaskId, taskId });
9396
+ const parentBranch = isSubtask ? deriveClickUpBranchName({ taskType, parentTaskId: effectiveParent }) : "";
9397
+ const branch = deriveClickUpBranchName({ taskType, parentTaskId: effectiveParent, subtaskId, taskId });
9398
+ const prTarget = parentBranch || "dev";
9399
+ const existing = safeExistingClickUpWorktree({ metadata: existingMetadata, branch });
9400
+ if (existing) {
9401
+ const registered = await registerOpenChamberClickUpWorktree({ openchamberBaseUrl: effectiveOpenChamberBaseUrl, opencodeBaseUrl: effectiveOpenCodeBaseUrl, baseWorktree, branch, worktreePath: existing.worktree, fetchImpl, source: "metadata_reuse" });
9402
+ log?.({ type: "openchamber_worktree_registered", taskId, branch, worktree: registered.worktree, source: "metadata_reuse", visibility: registered.openChamber.visibility });
9403
+ return { ...registered, parentBranch: parentBranch || void 0, prTarget };
9404
+ }
9405
+ const worktreePath = deriveClickUpWorktree({ baseWorktree, taskId, taskType, parentTaskId: effectiveParent, subtaskId });
9406
+ if (fs5.existsSync(worktreePath)) {
9407
+ const registered = await registerOpenChamberClickUpWorktree({ openchamberBaseUrl: effectiveOpenChamberBaseUrl, opencodeBaseUrl: effectiveOpenCodeBaseUrl, baseWorktree, branch, worktreePath, fetchImpl, source: "existing_directory" });
9408
+ log?.({ type: "openchamber_worktree_registered", taskId, branch, worktree: registered.worktree, source: "existing_directory", visibility: registered.openChamber.visibility });
9409
+ return { ...registered, parentBranch: parentBranch || void 0, prTarget };
9410
+ }
9411
+ let parentBootstrap = null;
9412
+ if (isSubtask) {
9413
+ const parentWorktree = deriveClickUpWorktree({ baseWorktree, taskId: effectiveParent, taskType, parentTaskId: effectiveParent });
9414
+ if (fs5.existsSync(parentWorktree)) {
9415
+ const registeredParent = await registerOpenChamberClickUpWorktree({ openchamberBaseUrl: effectiveOpenChamberBaseUrl, opencodeBaseUrl: effectiveOpenCodeBaseUrl, baseWorktree, branch: parentBranch, worktreePath: parentWorktree, fetchImpl, source: "parent_existing_directory" });
9416
+ parentBootstrap = { branch: parentBranch, worktree: registeredParent.worktree, reused: true, provider: "openchamber", visibility: registeredParent.openChamber.visibility };
9417
+ } else {
9418
+ const parentStartPoint = resolveClickUpDevStartPoint(baseWorktree, runGitFn);
9419
+ const parentBranchExists = clickUpGitRefExists(baseWorktree, parentBranch, runGitFn);
9420
+ const createdParent = await createOpenChamberClickUpWorktree({ openchamberBaseUrl: effectiveOpenChamberBaseUrl, baseWorktree, branch: parentBranch, worktreePath: parentWorktree, startPoint: parentStartPoint, branchExists: parentBranchExists, fetchImpl });
9421
+ const parentVisibility = await syncOpenChamberWorktreeVisibility({ openchamberBaseUrl: effectiveOpenChamberBaseUrl, opencodeBaseUrl: effectiveOpenCodeBaseUrl, baseWorktree, worktreePath: createdParent.worktree, branch: parentBranch, fetchImpl });
9422
+ parentBootstrap = { branch: parentBranch, worktree: createdParent.worktree, startPoint: parentBranchExists ? parentBranch : parentStartPoint, reused: parentBranchExists, provider: "openchamber", visibility: parentVisibility };
9423
+ log?.({ type: "openchamber_worktree_created", taskId: effectiveParent, branch: parentBranch, worktree: parentBootstrap.worktree, startPoint: parentBootstrap.startPoint, visibility: parentVisibility });
9424
+ }
9425
+ }
9426
+ const startPoint = isSubtask ? parentBranch : resolveClickUpDevStartPoint(baseWorktree, runGitFn);
9427
+ const branchExists = clickUpGitRefExists(baseWorktree, branch, runGitFn);
9428
+ const created = await createOpenChamberClickUpWorktree({ openchamberBaseUrl: effectiveOpenChamberBaseUrl, baseWorktree, branch, worktreePath, startPoint, branchExists, fetchImpl });
9429
+ const visibility = await syncOpenChamberWorktreeVisibility({ openchamberBaseUrl: effectiveOpenChamberBaseUrl, opencodeBaseUrl: effectiveOpenCodeBaseUrl, baseWorktree, worktreePath: created.worktree, branch, fetchImpl });
9430
+ log?.({ type: "openchamber_worktree_created", taskId, branch, worktree: created.worktree, startPoint: branchExists ? branch : startPoint, visibility });
9431
+ return { branch, worktree: created.worktree, reused: false, startPoint, parentBranch: parentBranch || void 0, prTarget, parentBootstrap: parentBootstrap || void 0, provider: "openchamber", openChamber: { source: "created", visibility } };
9432
+ }
9433
+ async function ensureClickUpTaskWorktreeForWebhook({ opencodeBaseUrl = "", opencodeBaseUrlConfigured = false, openchamberBaseUrl = "", openchamberBaseUrlConfigured = false, clickupClient = null, webhookWorktree = process.cwd(), fetchImpl = globalThis.fetch, ...options } = {}) {
9434
+ void opencodeBaseUrlConfigured;
9435
+ if (!openchamberBaseUrlConfigured || !openchamberBaseUrl) return ensureClickUpTaskWorktree(options);
9436
+ try {
9437
+ return await ensureClickUpTaskWorktreeOpenChamber({ ...options, openchamberBaseUrl, opencodeBaseUrl, fetchImpl, log: (entry) => appendClickUpWebhookLocalLog(webhookWorktree, entry) });
9438
+ } catch (error) {
9439
+ const message = `OpenChamber worktree provisioning failed for ${options.taskId || "unknown task"}; raw git fallback is disabled to avoid invisible worktrees. ${error.message}`;
9440
+ appendClickUpWebhookLocalLog(webhookWorktree, { type: "openchamber_worktree_failed", taskId: options.taskId || null, message });
9441
+ if (typeof clickupClient?.postTaskComment === "function") {
9442
+ const taskId = options.taskId || "unknown task";
9443
+ const ledgerPath = clickUpCommentLedgerPath(webhookWorktree);
9444
+ const comment = `${message}
9445
+
9446
+ Optima did not run raw git worktree fallback. Please verify clickup.openchamber.base_url and OpenCode/OpenChamber endpoint configuration.`;
9447
+ let dedupe = { post: true, key: clickUpWorktreeFailureCommentKey({ taskId, message }) };
9448
+ try {
9449
+ dedupe = shouldPostClickUpWorktreeFailureComment({ ledgerPath, taskId, message });
9450
+ } catch (ledgerError) {
9451
+ appendClickUpWebhookLocalLog(webhookWorktree, { type: "openchamber_worktree_comment_dedupe_failed", taskId, message: ledgerError.message });
9452
+ }
9453
+ if (dedupe.post) {
9454
+ try {
9455
+ await clickupClient.postTaskComment({ taskId: options.taskId, comment });
9456
+ recordClickUpWorktreeFailureComment({ ledgerPath, taskId, message, posted: true });
9457
+ } catch (commentError) {
9458
+ appendClickUpWebhookLocalLog(webhookWorktree, { type: "openchamber_worktree_blocker_comment_failed", taskId: options.taskId || null, message: commentError.message });
9459
+ }
9460
+ } else {
9461
+ appendClickUpWebhookLocalLog(webhookWorktree, { type: "openchamber_worktree_blocker_comment_deduped", taskId, key: dedupe.key, reason: dedupe.reason });
9462
+ recordClickUpWorktreeFailureComment({ ledgerPath, taskId, message, posted: false, skippedReason: dedupe.reason });
9463
+ }
9464
+ }
9465
+ throw new Error(message);
9466
+ }
9467
+ }
9234
9468
  function ensureClickUpTaskWorktree({ baseWorktree = "", taskId, taskType = "Tarea", parentTaskId = "", subtaskId = "", existingMetadata = {}, runGitFn = runGit, allowNonGitFallback = false } = {}) {
9235
9469
  const effectiveParent = parentTaskId || taskId;
9236
9470
  const isSubtask = isClickUpSubtaskRoute({ taskType, parentTaskId: effectiveParent, subtaskId, taskId });
@@ -9592,6 +9826,11 @@ function normalizeClickUpWebhookConfig(rawClickUp = null, worktree = process.cwd
9592
9826
  const webhook = isPlainObject(raw.webhook) ? raw.webhook : {};
9593
9827
  const routing = isPlainObject(raw.routing) ? raw.routing : {};
9594
9828
  const opencode = isPlainObject(raw.opencode) ? raw.opencode : {};
9829
+ const openchamber = isPlainObject(raw.openchamber) ? raw.openchamber : {};
9830
+ const rawOpenCodeBaseUrl = opencode.base_url ?? opencode.baseUrl ?? raw.opencode_base_url ?? raw.opencodeBaseUrl;
9831
+ const rawOpenChamberBaseUrl = openchamber.base_url ?? openchamber.baseUrl ?? raw.openchamber_base_url ?? raw.openchamberBaseUrl;
9832
+ const openCodeBaseUrlConfigured = rawOpenCodeBaseUrl !== void 0 && rawOpenCodeBaseUrl !== null && String(rawOpenCodeBaseUrl).trim() !== "";
9833
+ const openChamberBaseUrlConfigured = rawOpenChamberBaseUrl !== void 0 && rawOpenChamberBaseUrl !== null && String(rawOpenChamberBaseUrl).trim() !== "";
9595
9834
  const location = isPlainObject(webhook.location) ? webhook.location : {};
9596
9835
  const events = Array.isArray(webhook.events) && webhook.events.length > 0 ? [...new Set(webhook.events.map((event) => String(event || "").trim()).filter(Boolean))] : [...CLICKUP_WEBHOOK_EVENTS];
9597
9836
  const ignoredStatuses = Array.isArray(routing.ignored_statuses) && routing.ignored_statuses.length > 0 ? routing.ignored_statuses : CLICKUP_WEBHOOK_TERMINAL_STATUSES;
@@ -9603,7 +9842,8 @@ function normalizeClickUpWebhookConfig(rawClickUp = null, worktree = process.cwd
9603
9842
  apiToken: String(raw.api_token || raw.apiToken || "").trim(),
9604
9843
  log: normalizeClickUpWebhookLogLevel(raw.log),
9605
9844
  opencode: {
9606
- baseUrl: normalizeOpenCodeBaseUrl(opencode.base_url || opencode.baseUrl || raw.opencode_base_url || raw.opencodeBaseUrl),
9845
+ baseUrl: normalizeOpenCodeBaseUrl(rawOpenCodeBaseUrl),
9846
+ baseUrlConfigured: openCodeBaseUrlConfigured,
9607
9847
  promptDelivery: ["http", "direct"].includes(String(opencode.prompt_delivery || opencode.promptDelivery || raw.prompt_delivery || raw.promptDelivery || "").trim().toLowerCase()) ? "http" : "sdk",
9608
9848
  acceptPromptAdmission: opencode.accept_prompt_admission === true || opencode.acceptPromptAdmission === true || raw.accept_prompt_admission === true || raw.acceptPromptAdmission === true,
9609
9849
  startupReconciliationDelayMs: normalizeNonNegativeInteger(
@@ -9611,6 +9851,10 @@ function normalizeClickUpWebhookConfig(rawClickUp = null, worktree = process.cwd
9611
9851
  CLICKUP_WEBHOOK_STARTUP_RECONCILIATION_DELAY_MS
9612
9852
  )
9613
9853
  },
9854
+ openchamber: {
9855
+ baseUrl: normalizeOpenCodeBaseUrl(rawOpenChamberBaseUrl, ""),
9856
+ baseUrlConfigured: openChamberBaseUrlConfigured
9857
+ },
9614
9858
  webhook: {
9615
9859
  publicUrl: String(webhook.public_url || webhook.publicUrl || "").trim(),
9616
9860
  bindHost: String(webhook.bind_host || webhook.bindHost || "127.0.0.1").trim(),
@@ -9652,6 +9896,13 @@ function normalizeClickUpWebhookConfig(rawClickUp = null, worktree = process.cwd
9652
9896
  errors.push("clickup.opencode.base_url must be a valid URL");
9653
9897
  }
9654
9898
  }
9899
+ if (config.openchamber.baseUrl) {
9900
+ try {
9901
+ new URL(config.openchamber.baseUrl);
9902
+ } catch {
9903
+ errors.push("clickup.openchamber.base_url must be a valid URL");
9904
+ }
9905
+ }
9655
9906
  const missingEvents = CLICKUP_WEBHOOK_EVENTS.filter((event) => !config.webhook.events.includes(event));
9656
9907
  if (missingEvents.length > 0) errors.push(`clickup.webhook.events missing: ${missingEvents.join(", ")}`);
9657
9908
  if (config.routing.targetAgent !== "workflow_product_manager") errors.push("clickup.routing.target_agent must be workflow_product_manager");
@@ -10008,23 +10259,29 @@ function rememberClickUpWebhookEvent(state = {}, eventKey, limit = 200) {
10008
10259
  if (recent.includes(eventKey)) return { duplicate: true, state };
10009
10260
  return { duplicate: false, state: { ...state, recentEventKeys: [...recent, eventKey].slice(-limit) } };
10010
10261
  }
10011
- function readClickUpCommentLedger(ledgerPath) {
10012
- const processed = /* @__PURE__ */ new Set();
10013
- if (!ledgerPath || !fs5.existsSync(ledgerPath)) return processed;
10262
+ function readClickUpCommentLedgerEntries(ledgerPath) {
10263
+ if (!ledgerPath || !fs5.existsSync(ledgerPath)) return [];
10014
10264
  try {
10015
10265
  const raw = fs5.readFileSync(ledgerPath, "utf8");
10266
+ const entries = [];
10016
10267
  for (const [index, line] of raw.split(/\r?\n/).entries()) {
10017
10268
  if (!line.trim()) continue;
10018
10269
  try {
10019
- const entry = JSON.parse(line);
10020
- if (entry?.key) processed.add(String(entry.key));
10270
+ entries.push(JSON.parse(line));
10021
10271
  } catch (error) {
10022
10272
  throw new Error(`malformed ledger row ${index + 1}: ${error.message}`);
10023
10273
  }
10024
10274
  }
10275
+ return entries;
10025
10276
  } catch (error) {
10026
10277
  throw new Error(`ClickUp comment ledger unavailable: ${error.message}`);
10027
10278
  }
10279
+ }
10280
+ function readClickUpCommentLedger(ledgerPath) {
10281
+ const processed = /* @__PURE__ */ new Set();
10282
+ for (const entry of readClickUpCommentLedgerEntries(ledgerPath)) {
10283
+ if (entry?.key) processed.add(String(entry.key));
10284
+ }
10028
10285
  return processed;
10029
10286
  }
10030
10287
  function appendClickUpCommentLedgerEntry(ledgerPath, entry = {}) {
@@ -10090,6 +10347,35 @@ function recordClickUpCommentVersionProcessed({ ledgerPath, key, taskId, eventTy
10090
10347
  recordedAt: at.toISOString()
10091
10348
  });
10092
10349
  }
10350
+ function clickUpWorktreeFailureCommentKey({ taskId, message } = {}) {
10351
+ const hash = crypto.createHash("sha256").update(String(message || "")).digest("hex").slice(0, 16);
10352
+ return [String(taskId || "unknown").trim() || "unknown", "worktree_failure", hash].join(":");
10353
+ }
10354
+ function shouldPostClickUpWorktreeFailureComment({ ledgerPath, taskId, message, now = /* @__PURE__ */ new Date(), windowMs = CLICKUP_WORKTREE_FAILURE_COMMENT_DEDUPE_MS } = {}) {
10355
+ const key = clickUpWorktreeFailureCommentKey({ taskId, message });
10356
+ const nowMs = now instanceof Date ? now.getTime() : new Date(now).getTime();
10357
+ for (const entry of readClickUpCommentLedgerEntries(ledgerPath).reverse()) {
10358
+ if (entry?.key !== key) continue;
10359
+ const postedAtMs = new Date(entry.postedAt || entry.recordedAt || entry.at || 0).getTime();
10360
+ if (Number.isFinite(postedAtMs) && Number.isFinite(nowMs) && nowMs - postedAtMs <= windowMs) {
10361
+ return { post: false, key, reason: "recent_duplicate" };
10362
+ }
10363
+ if (entry?.message === message) return { post: false, key, reason: "latest_equivalent" };
10364
+ }
10365
+ return { post: true, key };
10366
+ }
10367
+ function recordClickUpWorktreeFailureComment({ ledgerPath, taskId, message, posted = true, skippedReason = null, at = /* @__PURE__ */ new Date() } = {}) {
10368
+ appendClickUpCommentLedgerEntry(ledgerPath, {
10369
+ key: clickUpWorktreeFailureCommentKey({ taskId, message }),
10370
+ taskId,
10371
+ eventType: "worktree_failure",
10372
+ action: posted ? "worktree_failure_comment_posted" : "worktree_failure_comment_skipped",
10373
+ message,
10374
+ posted,
10375
+ skippedReason,
10376
+ postedAt: at.toISOString()
10377
+ });
10378
+ }
10093
10379
  function clickUpTaskIdFromPayload(payload = {}) {
10094
10380
  return String(payload.task_id || payload.taskId || payload.task?.id || payload.task?.task_id || "").trim();
10095
10381
  }
@@ -10941,7 +11227,7 @@ async function withClickUpTaskRouteLock(taskId, operation) {
10941
11227
  if (activeClickUpTaskRoutes.get(key) === current) activeClickUpTaskRoutes.delete(key);
10942
11228
  }
10943
11229
  }
10944
- async function routeClickUpWebhookEventUnlocked({ payload, config, state = {}, worktree = process.cwd(), clickupClient, openCodeClient, sessionExists = openCodeSessionExists, createSession = createOpenCodeSession, sendSessionEvent = sendOpenCodeSessionEvent, ensureTaskWorktree = ensureClickUpTaskWorktree, verifySessionEventDelivery = verifyOpenCodeSessionEventDelivery, saveState = null, now = () => /* @__PURE__ */ new Date(), host = os.hostname(), commentLedgerPath = clickUpCommentLedgerPath(worktree) } = {}) {
11230
+ async function routeClickUpWebhookEventUnlocked({ payload, config, state = {}, worktree = process.cwd(), clickupClient, openCodeClient, sessionExists = openCodeSessionExists, createSession = createOpenCodeSession, sendSessionEvent = sendOpenCodeSessionEvent, ensureTaskWorktree = ensureClickUpTaskWorktreeForWebhook, verifySessionEventDelivery = verifyOpenCodeSessionEventDelivery, saveState = null, now = () => /* @__PURE__ */ new Date(), host = os.hostname(), commentLedgerPath = clickUpCommentLedgerPath(worktree) } = {}) {
10945
11231
  const eventType = clickUpEventType(payload);
10946
11232
  const eventKey = clickUpWebhookEventKey(payload);
10947
11233
  const isStartupAssignmentReconciliation = payload?.startup_reconciliation === true && eventType === "taskAssigneeUpdated";
@@ -10997,7 +11283,7 @@ async function routeClickUpWebhookEventUnlocked({ payload, config, state = {}, w
10997
11283
  const subtaskId = parentTaskId && parentTaskId !== taskId ? taskId : "";
10998
11284
  let taskRoute;
10999
11285
  try {
11000
- taskRoute = ensureTaskWorktree({ baseWorktree: config.basePath, taskId, taskType, parentTaskId, subtaskId, existingMetadata: metadata, allowNonGitFallback: process.env.NODE_ENV === "test" || config.test === true });
11286
+ taskRoute = await ensureTaskWorktree({ baseWorktree: config.basePath, taskId, taskType, parentTaskId, subtaskId, existingMetadata: metadata, allowNonGitFallback: process.env.NODE_ENV === "test" || config.test === true, opencodeBaseUrl: config.opencode?.baseUrl, opencodeBaseUrlConfigured: config.opencode?.baseUrlConfigured === true, openchamberBaseUrl: config.openchamber?.baseUrl, openchamberBaseUrlConfigured: config.openchamber?.baseUrlConfigured === true, clickupClient, webhookWorktree: worktree });
11001
11287
  } catch (error) {
11002
11288
  const message = `Optima webhook could not create or reuse a task worktree for ${taskId}: ${error.message}`;
11003
11289
  appendClickUpWebhookLocalLog(worktree, { type: "task_worktree_failed", taskId, message });
@@ -13156,7 +13442,7 @@ Follow-up: use optima_prompt_workflow with session_id '${sessionId}' to check in
13156
13442
  }
13157
13443
  };
13158
13444
  }
13159
- OptimaPlugin.__internals = { BUNDLE_AGENTS_DIR, BUNDLE_ASSETS_DIR, CLICKUP_DEFINITION_DOC_PARENT, activeClickUpWebhookLifecycleRegistry, cleanupManagedClickUpWebhook, deleteClickUpWebhookBestEffort, BUNDLE_POLICIES_DIR, buildClickUpApplyPayloadResult, buildClickUpCreateSubtasksPayload, buildClickUpStartTaskPayload, buildClickUpSummaryPayload, buildClickUpTransitionPayload, buildOptimaAgents, clickUpCommentLedgerKey, clickUpCommentMentionsProductManager, clickUpWebhookAuditLogDir, clickUpWebhookAuditLogPath, compactPromptPath, createClickUpApiClient, createTestClickUpApiClient, createOpenCodeSession, createOpenCodeSessionControl, deliveryEvidencePathForClickUpTask, ensureClickUpTaskWorktree, ensureClickUpWebhookSubscription, handleClickUpWebhookRequest, isClickUpDerivedWorktreeSibling, isClickUpSubtaskRoute, isClickUpWebhookStateActive, isSameOrNestedPath, normalizeClickUpWebhookConfig, normalizeClickUpWebhookLogLevel, normalizeOpenCodeBaseUrl, openCodeSessionExists, promptOpenCodeSessionControl, probeOpenCodeSessionControl, readClickUpCommentLedger, readClickUpWebhookState, readOpenCodeSessionControl, readOpenCodeSessionMessages, reconcileClickUpStartup, scheduleClickUpStartupReconciliation, waitForOpenCodeReadiness, recordClickUpCommentVersionProcessed, resyncClickUpWebhookForSignatureDrift, resolveIncludeFile, resolveIncludes, resolveOptimaPluginOptions, resolveSecretReference, routeClickUpWebhookEvent, sendOpenCodeSessionEvent, sendOpenCodeSessionEventDirect, summarizeOpenCodeMessages, verifyOpenCodeSessionEventDelivery, stableClickUpCommentVersionMarker, startClickUpWebhookListener, validateClickUpWebhookState, verifyClickUpSignature, writeClickUpWebhookState, decideClickUpStatusAction, deriveClickUpBranchName, deriveClickUpPendingSubtaskBranch, deriveClickUpPrTarget, deriveClickUpWorktree, determineClickUpMergeAuthority, ensureOptimaGitignoreRules, explicitSafeInputWorktree, finalApprovalAssignees, formatRepairResult, formatValidationResult, isIgnoredClickUpTaskType, isOptimaPluginPackageWorktree, isSafeWritableDirectory, loadHumansRegistry, mergeClickUpAgentMetadata, mergeClickUpSessionMetadata, migrateLegacyOptimaLayout, normalizeAgentMetadataJson, normalizeClickUpStatus, normalizeClickUpTaskType, normalizePromptResponseParts, normalizeWorkflowTaskPath, optimaRepairDependencies, parseClickUpSubtasksMarkdown, parseHumansRegistry, parseMarkdownArtifact, parseMarkdownSections, planOptimaRepair, preEstimateClickUpWork, readMarkdownArtifact, resolveHumanRoles, resolveSafeWorktree, safeWorktreeOrFailure, stripRawLogSections, validateMainWorkspaceBranchSafety, workflowFinalMessageFromPromptResponse };
13445
+ OptimaPlugin.__internals = { BUNDLE_AGENTS_DIR, BUNDLE_ASSETS_DIR, CLICKUP_DEFINITION_DOC_PARENT, activeClickUpWebhookLifecycleRegistry, cleanupManagedClickUpWebhook, deleteClickUpWebhookBestEffort, BUNDLE_POLICIES_DIR, buildClickUpApplyPayloadResult, buildClickUpCreateSubtasksPayload, buildClickUpStartTaskPayload, buildClickUpSummaryPayload, buildClickUpTransitionPayload, buildOptimaAgents, clickUpCommentLedgerKey, clickUpCommentMentionsProductManager, clickUpWebhookAuditLogDir, clickUpWebhookAuditLogPath, compactPromptPath, createClickUpApiClient, createTestClickUpApiClient, createOpenCodeSession, createOpenCodeSessionControl, deliveryEvidencePathForClickUpTask, ensureClickUpTaskWorktree, ensureClickUpTaskWorktreeForWebhook, ensureClickUpTaskWorktreeOpenChamber, ensureClickUpWebhookSubscription, handleClickUpWebhookRequest, isClickUpDerivedWorktreeSibling, isClickUpSubtaskRoute, isClickUpWebhookStateActive, isSameOrNestedPath, normalizeClickUpWebhookConfig, normalizeClickUpWebhookLogLevel, normalizeOpenCodeBaseUrl, openCodeSessionExists, promptOpenCodeSessionControl, probeOpenCodeSessionControl, readClickUpCommentLedger, readClickUpWebhookState, readOpenCodeSessionControl, readOpenCodeSessionMessages, reconcileClickUpStartup, registerOpenChamberClickUpWorktree, scheduleClickUpStartupReconciliation, syncOpenChamberWorktreeVisibility, waitForOpenCodeReadiness, recordClickUpCommentVersionProcessed, resyncClickUpWebhookForSignatureDrift, resolveIncludeFile, resolveIncludes, resolveOptimaPluginOptions, resolveSecretReference, routeClickUpWebhookEvent, sendOpenCodeSessionEvent, sendOpenCodeSessionEventDirect, summarizeOpenCodeMessages, verifyOpenCodeSessionEventDelivery, stableClickUpCommentVersionMarker, startClickUpWebhookListener, validateClickUpWebhookState, verifyClickUpSignature, writeClickUpWebhookState, decideClickUpStatusAction, deriveClickUpBranchName, deriveClickUpPendingSubtaskBranch, deriveClickUpPrTarget, deriveClickUpWorktree, determineClickUpMergeAuthority, ensureOptimaGitignoreRules, explicitSafeInputWorktree, finalApprovalAssignees, formatRepairResult, formatValidationResult, isIgnoredClickUpTaskType, isOptimaPluginPackageWorktree, isSafeWritableDirectory, loadHumansRegistry, mergeClickUpAgentMetadata, mergeClickUpSessionMetadata, migrateLegacyOptimaLayout, normalizeAgentMetadataJson, normalizeClickUpStatus, normalizeClickUpTaskType, normalizePromptResponseParts, normalizeWorkflowTaskPath, optimaRepairDependencies, parseClickUpSubtasksMarkdown, parseHumansRegistry, parseMarkdownArtifact, parseMarkdownSections, planOptimaRepair, preEstimateClickUpWork, readMarkdownArtifact, resolveHumanRoles, resolveSafeWorktree, safeWorktreeOrFailure, stripRawLogSections, validateMainWorkspaceBranchSafety, workflowFinalMessageFromPromptResponse };
13160
13446
 
13161
13447
  // src/sanitize_cli.js
13162
13448
  var { migrateLegacyOptimaLayout: migrateLegacyOptimaLayout2 } = OptimaPlugin.__internals;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@defend-tech/opencode-optima",
3
- "version": "0.1.59",
3
+ "version": "0.1.61",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+ssh://git@github.com/defend-tech/opencode-optima.git"