@defend-tech/opencode-optima 0.1.36 → 0.1.37

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -9616,6 +9616,45 @@ function formatClickUpWebhookPrompt({ eventType, taskId, payload, branch = "", w
9616
9616
  deliveryEvidencePath ? `Final merge-trackable evidence must be written under ${deliveryEvidencePath}; use .optima only as local mirror/staging.` : null
9617
9617
  ].filter((part) => part !== null).join("\n");
9618
9618
  }
9619
+ function clickUpNoAbandonmentRuleText() {
9620
+ return "No-abandonment rule: PM must keep working, or before stopping must post a ClickUp comment, hand off, remove PM as assignee, and assign the next owner.";
9621
+ }
9622
+ function buildClickUpStartupResumeComment({ taskId, result = {} } = {}) {
9623
+ const contextParts = [
9624
+ result.branch ? `- Branch: ${result.branch}` : null,
9625
+ result.worktree ? `- Worktree: ${result.worktree}` : null,
9626
+ result.deliveryEvidencePath ? `- Delivery evidence path: ${result.deliveryEvidencePath}` : null,
9627
+ result.evidencePath ? `- Local evidence path: ${result.evidencePath}` : null
9628
+ ].filter(Boolean);
9629
+ return [
9630
+ `Startup reconciliation resumed ClickUp task ${taskId}.`,
9631
+ `Route result: ${result.action || "unknown"}${result.ok === false ? " (failed)" : ""}.`,
9632
+ result.sessionId ? `Session id: ${result.sessionId}.` : "Session id: unavailable.",
9633
+ contextParts.length ? contextParts.join("\n") : "Worktree/branch/delivery evidence path: unavailable.",
9634
+ clickUpNoAbandonmentRuleText()
9635
+ ].join("\n");
9636
+ }
9637
+ function buildClickUpStartupBlockerComment({ taskId, error } = {}) {
9638
+ const summary = error?.message || String(error || "unknown error");
9639
+ return [
9640
+ `Startup reconciliation blocker for ClickUp task ${taskId}: ${summary}`,
9641
+ "Optima skipped this task for this startup pass and continued with the next PM-assigned task.",
9642
+ clickUpNoAbandonmentRuleText()
9643
+ ].join("\n");
9644
+ }
9645
+ async function postClickUpStartupComment({ clickupClient, worktree, taskId, comment, type } = {}) {
9646
+ if (typeof clickupClient?.postTaskComment !== "function") {
9647
+ appendClickUpWebhookLocalLog(worktree, { type: "startup_reconciliation_comment_unavailable", taskId, commentType: type });
9648
+ return { ok: false, skipped: true, reason: "post_task_comment_unavailable" };
9649
+ }
9650
+ try {
9651
+ await clickupClient.postTaskComment({ taskId, comment });
9652
+ return { ok: true };
9653
+ } catch (error) {
9654
+ appendClickUpWebhookLocalLog(worktree, { type: "startup_reconciliation_comment_failed", taskId, commentType: type, message: error.message });
9655
+ return { ok: false, error: error.message };
9656
+ }
9657
+ }
9619
9658
  function appendClickUpWebhookLocalLog(worktree, entry) {
9620
9659
  const logPath = clickUpWebhookLogPath(worktree);
9621
9660
  fs2.mkdirSync(path2.dirname(logPath), { recursive: true });
@@ -9887,19 +9926,19 @@ async function routeClickUpWebhookEventUnlocked({ payload, config, state = {}, w
9887
9926
  await clickupClient.updateTaskMetadata({ taskId, fieldId: config.routing.metadataFieldId, value: nextMetadata });
9888
9927
  const { [taskId]: _completedPending, ...remainingPending } = stateToPersist.pendingSessions || {};
9889
9928
  stateToPersist = { ...stateToPersist, pendingSessions: remainingPending };
9890
- return finish({ ok: true, action: "created_session", taskId, sessionId: pendingSessionId, eventKey });
9929
+ return finish({ ok: true, action: "created_session", taskId, sessionId: pendingSessionId, eventKey, branch: taskRoute.branch, worktree: taskRoute.worktree, deliveryEvidencePath, evidencePath: routingMetadata.evidence_path });
9891
9930
  }
9892
9931
  const sessionId = String(existingSessionId);
9893
9932
  if (await sessionExists(openCodeClient, sessionId)) {
9894
9933
  if (typeof clickupClient?.updateTaskMetadata === "function") await clickupClient.updateTaskMetadata({ taskId, fieldId: config.routing.metadataFieldId, value: metadataWithRouting });
9895
9934
  await sendSessionEvent(openCodeClient, { sessionId, agent: config.routing.targetAgent, text: prompt, directory: taskRoute.worktree, opencodeBaseUrl: config.opencode?.baseUrl });
9896
- return finish({ ok: true, action: "sent_to_existing_session", taskId, sessionId, eventKey });
9935
+ return finish({ ok: true, action: "sent_to_existing_session", taskId, sessionId, eventKey, branch: taskRoute.branch, worktree: taskRoute.worktree, deliveryEvidencePath, evidencePath: routingMetadata.evidence_path });
9897
9936
  }
9898
9937
  const at = now().toISOString();
9899
9938
  const incidentComment = `Optima webhook could not route this ClickUp event because OpenCode session ${sessionId} is missing on host ${host} at ${at}. No replacement session was created automatically.`;
9900
9939
  appendClickUpWebhookLocalLog(worktree, { type: "missing_session", taskId, sessionId, host, at });
9901
9940
  await clickupClient.postTaskComment({ taskId, comment: incidentComment });
9902
- return finish({ ok: true, action: "missing_session_reported", taskId, sessionId, eventKey });
9941
+ return finish({ ok: true, action: "missing_session_reported", taskId, sessionId, eventKey, branch: taskRoute.branch, worktree: taskRoute.worktree, deliveryEvidencePath, evidencePath: routingMetadata.evidence_path });
9903
9942
  }
9904
9943
  async function routeClickUpWebhookEvent(options = {}) {
9905
9944
  const taskId = clickUpTaskIdFromPayload(options.payload || {});
@@ -9955,10 +9994,26 @@ async function reconcileClickUpStartup({ config, state = {}, worktree = process.
9955
9994
  saveState: persistState,
9956
9995
  now
9957
9996
  });
9958
- if (result?.ok && result.action !== "ignored") routed.assigned += 1;
9997
+ if (result?.ok && result.action !== "ignored") {
9998
+ routed.assigned += 1;
9999
+ await postClickUpStartupComment({
10000
+ clickupClient,
10001
+ worktree,
10002
+ taskId,
10003
+ type: "resume",
10004
+ comment: buildClickUpStartupResumeComment({ taskId, result })
10005
+ });
10006
+ }
9959
10007
  } catch (error) {
9960
10008
  routed.errors += 1;
9961
10009
  appendClickUpWebhookLocalLog(worktree, { type: "startup_reconciliation_task_failed", taskId, message: error.message });
10010
+ await postClickUpStartupComment({
10011
+ clickupClient,
10012
+ worktree,
10013
+ taskId,
10014
+ type: "blocker",
10015
+ comment: buildClickUpStartupBlockerComment({ taskId, error })
10016
+ });
9962
10017
  }
9963
10018
  if (!lastWebhookMs || !clickupClient?.getTaskComments) continue;
9964
10019
  try {
@@ -9623,6 +9623,45 @@ function formatClickUpWebhookPrompt({ eventType, taskId, payload, branch = "", w
9623
9623
  deliveryEvidencePath ? `Final merge-trackable evidence must be written under ${deliveryEvidencePath}; use .optima only as local mirror/staging.` : null
9624
9624
  ].filter((part) => part !== null).join("\n");
9625
9625
  }
9626
+ function clickUpNoAbandonmentRuleText() {
9627
+ return "No-abandonment rule: PM must keep working, or before stopping must post a ClickUp comment, hand off, remove PM as assignee, and assign the next owner.";
9628
+ }
9629
+ function buildClickUpStartupResumeComment({ taskId, result = {} } = {}) {
9630
+ const contextParts = [
9631
+ result.branch ? `- Branch: ${result.branch}` : null,
9632
+ result.worktree ? `- Worktree: ${result.worktree}` : null,
9633
+ result.deliveryEvidencePath ? `- Delivery evidence path: ${result.deliveryEvidencePath}` : null,
9634
+ result.evidencePath ? `- Local evidence path: ${result.evidencePath}` : null
9635
+ ].filter(Boolean);
9636
+ return [
9637
+ `Startup reconciliation resumed ClickUp task ${taskId}.`,
9638
+ `Route result: ${result.action || "unknown"}${result.ok === false ? " (failed)" : ""}.`,
9639
+ result.sessionId ? `Session id: ${result.sessionId}.` : "Session id: unavailable.",
9640
+ contextParts.length ? contextParts.join("\n") : "Worktree/branch/delivery evidence path: unavailable.",
9641
+ clickUpNoAbandonmentRuleText()
9642
+ ].join("\n");
9643
+ }
9644
+ function buildClickUpStartupBlockerComment({ taskId, error } = {}) {
9645
+ const summary = error?.message || String(error || "unknown error");
9646
+ return [
9647
+ `Startup reconciliation blocker for ClickUp task ${taskId}: ${summary}`,
9648
+ "Optima skipped this task for this startup pass and continued with the next PM-assigned task.",
9649
+ clickUpNoAbandonmentRuleText()
9650
+ ].join("\n");
9651
+ }
9652
+ async function postClickUpStartupComment({ clickupClient, worktree, taskId, comment, type } = {}) {
9653
+ if (typeof clickupClient?.postTaskComment !== "function") {
9654
+ appendClickUpWebhookLocalLog(worktree, { type: "startup_reconciliation_comment_unavailable", taskId, commentType: type });
9655
+ return { ok: false, skipped: true, reason: "post_task_comment_unavailable" };
9656
+ }
9657
+ try {
9658
+ await clickupClient.postTaskComment({ taskId, comment });
9659
+ return { ok: true };
9660
+ } catch (error) {
9661
+ appendClickUpWebhookLocalLog(worktree, { type: "startup_reconciliation_comment_failed", taskId, commentType: type, message: error.message });
9662
+ return { ok: false, error: error.message };
9663
+ }
9664
+ }
9626
9665
  function appendClickUpWebhookLocalLog(worktree, entry) {
9627
9666
  const logPath = clickUpWebhookLogPath(worktree);
9628
9667
  fs2.mkdirSync(path2.dirname(logPath), { recursive: true });
@@ -9894,19 +9933,19 @@ async function routeClickUpWebhookEventUnlocked({ payload, config, state = {}, w
9894
9933
  await clickupClient.updateTaskMetadata({ taskId, fieldId: config.routing.metadataFieldId, value: nextMetadata });
9895
9934
  const { [taskId]: _completedPending, ...remainingPending } = stateToPersist.pendingSessions || {};
9896
9935
  stateToPersist = { ...stateToPersist, pendingSessions: remainingPending };
9897
- return finish({ ok: true, action: "created_session", taskId, sessionId: pendingSessionId, eventKey });
9936
+ return finish({ ok: true, action: "created_session", taskId, sessionId: pendingSessionId, eventKey, branch: taskRoute.branch, worktree: taskRoute.worktree, deliveryEvidencePath, evidencePath: routingMetadata.evidence_path });
9898
9937
  }
9899
9938
  const sessionId = String(existingSessionId);
9900
9939
  if (await sessionExists(openCodeClient, sessionId)) {
9901
9940
  if (typeof clickupClient?.updateTaskMetadata === "function") await clickupClient.updateTaskMetadata({ taskId, fieldId: config.routing.metadataFieldId, value: metadataWithRouting });
9902
9941
  await sendSessionEvent(openCodeClient, { sessionId, agent: config.routing.targetAgent, text: prompt, directory: taskRoute.worktree, opencodeBaseUrl: config.opencode?.baseUrl });
9903
- return finish({ ok: true, action: "sent_to_existing_session", taskId, sessionId, eventKey });
9942
+ return finish({ ok: true, action: "sent_to_existing_session", taskId, sessionId, eventKey, branch: taskRoute.branch, worktree: taskRoute.worktree, deliveryEvidencePath, evidencePath: routingMetadata.evidence_path });
9904
9943
  }
9905
9944
  const at = now().toISOString();
9906
9945
  const incidentComment = `Optima webhook could not route this ClickUp event because OpenCode session ${sessionId} is missing on host ${host} at ${at}. No replacement session was created automatically.`;
9907
9946
  appendClickUpWebhookLocalLog(worktree, { type: "missing_session", taskId, sessionId, host, at });
9908
9947
  await clickupClient.postTaskComment({ taskId, comment: incidentComment });
9909
- return finish({ ok: true, action: "missing_session_reported", taskId, sessionId, eventKey });
9948
+ return finish({ ok: true, action: "missing_session_reported", taskId, sessionId, eventKey, branch: taskRoute.branch, worktree: taskRoute.worktree, deliveryEvidencePath, evidencePath: routingMetadata.evidence_path });
9910
9949
  }
9911
9950
  async function routeClickUpWebhookEvent(options = {}) {
9912
9951
  const taskId = clickUpTaskIdFromPayload(options.payload || {});
@@ -9962,10 +10001,26 @@ async function reconcileClickUpStartup({ config, state = {}, worktree = process.
9962
10001
  saveState: persistState,
9963
10002
  now
9964
10003
  });
9965
- if (result?.ok && result.action !== "ignored") routed.assigned += 1;
10004
+ if (result?.ok && result.action !== "ignored") {
10005
+ routed.assigned += 1;
10006
+ await postClickUpStartupComment({
10007
+ clickupClient,
10008
+ worktree,
10009
+ taskId,
10010
+ type: "resume",
10011
+ comment: buildClickUpStartupResumeComment({ taskId, result })
10012
+ });
10013
+ }
9966
10014
  } catch (error) {
9967
10015
  routed.errors += 1;
9968
10016
  appendClickUpWebhookLocalLog(worktree, { type: "startup_reconciliation_task_failed", taskId, message: error.message });
10017
+ await postClickUpStartupComment({
10018
+ clickupClient,
10019
+ worktree,
10020
+ taskId,
10021
+ type: "blocker",
10022
+ comment: buildClickUpStartupBlockerComment({ taskId, error })
10023
+ });
9969
10024
  }
9970
10025
  if (!lastWebhookMs || !clickupClient?.getTaskComments) continue;
9971
10026
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@defend-tech/opencode-optima",
3
- "version": "0.1.36",
3
+ "version": "0.1.37",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+ssh://git@github.com/defend-tech/opencode-optima.git"