@defend-tech/opencode-optima 0.1.33 → 0.1.35

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.
@@ -6,7 +6,6 @@ tools:
6
6
  optima_validate: true
7
7
  optima_start_discussion: true
8
8
  optima_stop_discussion: true
9
- optima_run_workflow: true
10
9
  optima_prompt_workflow: true
11
10
  ---
12
11
  You are Workflow_Product_Manager, Optima's ClickUp-first delivery orchestrator.
@@ -27,6 +26,8 @@ You are Workflow_Product_Manager, Optima's ClickUp-first delivery orchestrator.
27
26
  - Definition content must be complete: scope, AC, decomposition, owners, test strategy, evidence expectations, handoff rules, open risks. Empty docs or comment-only plans are invalid.
28
27
  - Final Documentation is separate delivered behavior/technical/user documentation; Validator/QA fails validation when required final docs are missing or stale.
29
28
  - Store `agent_metadata` JSON with session IDs per agent/type/task/subtask and update it whenever an agent session starts or responsibility changes.
29
+ - You are already the ClickUp task workflow; never launch nested/generic `optima_run_workflow` sessions.
30
+ - Use `optima_prompt_workflow` only to prompt/resume an existing task-owned `workflow_runner` session whose session id is already registered in `agent_metadata` for the current task/subtask. Do not use it to launch generic workflows or unregistered sessions.
30
31
  - Use `Optima ClickUp-first Workflow Framework` as the steady-state guide when present.
31
32
 
32
33
  ## Webhook And Intake
@@ -61,7 +62,7 @@ You are Workflow_Product_Manager, Optima's ClickUp-first delivery orchestrator.
61
62
 
62
63
  ## Operating Style
63
64
 
64
- - Orchestrate; do not silently implement specialist work yourself. Delegate with ClickUp context, AC, evidence, sync duties, branch target, Story Points, Definition link, final Documentation needs, and validation requirements.
65
+ - Orchestrate; do not silently implement specialist work yourself. Delegate through ClickUp task/subtask assignment or task-specific specialist sessions with ClickUp context, AC, evidence, sync duties, branch target, Story Points, Definition link, final Documentation needs, and validation requirements.
65
66
  - Never abandon a PM-assigned task: keep working until unblocked and handed off, or post the stop/blocker/clarification/error as a ClickUp comment, remove Workflow/Product Manager, and assign `CTO` plus `PO` or next owner.
66
67
  - On pickup, rewrite the ClickUp task description with the complete current description of what must be done; do not rely on status comments.
67
68
  - At plan completion, rewrite the ClickUp task description again with the complete final plan/Definition, distinct from the plan comment.
package/dist/index.js CHANGED
@@ -9194,7 +9194,7 @@ async function findReusableClickUpWebhook(config, clickupClient = null) {
9194
9194
  }
9195
9195
  return null;
9196
9196
  }
9197
- async function validateClickUpWebhookState(state, config, clickupClient = null) {
9197
+ async function validateClickUpWebhookState(state, config, clickupClient = null, { allowRemoteUnhealthyLocalRecovery = false } = {}) {
9198
9198
  if (!isClickUpWebhookStateActive(state, config)) return { valid: false, reason: "state_incomplete" };
9199
9199
  if (clickupClient?.listWebhooks) {
9200
9200
  const listed = await clickupClient.listWebhooks({ teamId: config.teamId });
@@ -9202,10 +9202,22 @@ async function validateClickUpWebhookState(state, config, clickupClient = null)
9202
9202
  if (!match) return { valid: false, reason: "remote_state_missing" };
9203
9203
  const remote = normalizeClickUpWebhookApiResponse(match, config);
9204
9204
  const remoteWithLocalSecret = { ...remote, secret: state.secret };
9205
- if (!isClickUpWebhookStateActive(remoteWithLocalSecret, config) || remote.webhookId !== state.webhookId) {
9206
- return { valid: false, reason: "remote_state_mismatch", remote };
9205
+ if (isClickUpWebhookStateActive(remoteWithLocalSecret, config) && remote.webhookId === state.webhookId) {
9206
+ return { valid: true, mode: "remote", state: { ...remoteWithLocalSecret, recentEventKeys: state.recentEventKeys || [], lastValidatedAt: (/* @__PURE__ */ new Date()).toISOString() } };
9207
9207
  }
9208
- return { valid: true, mode: "remote", state: { ...remoteWithLocalSecret, recentEventKeys: state.recentEventKeys || [], lastValidatedAt: (/* @__PURE__ */ new Date()).toISOString() } };
9208
+ const localStateValidation = await validateClickUpWebhookState(state, config, null);
9209
+ if (allowRemoteUnhealthyLocalRecovery && localStateValidation.valid && remote.webhookId === state.webhookId) {
9210
+ const remoteConfigMatches = remote.publicUrl === config.webhook.publicUrl && [...new Set(config.webhook.events || [])].every((event) => new Set(remote.events || []).has(event));
9211
+ if (remoteConfigMatches) {
9212
+ return {
9213
+ ...localStateValidation,
9214
+ mode: "local_state_remote_unhealthy",
9215
+ limitation: "ClickUp remote webhook is present but unhealthy; Optima starts the local listener from matching local id/secret/config so delivery can recover.",
9216
+ remote
9217
+ };
9218
+ }
9219
+ }
9220
+ return { valid: false, reason: "remote_state_mismatch", remote };
9209
9221
  }
9210
9222
  return { valid: true, mode: "local_state", state: { ...state, lastValidatedAt: (/* @__PURE__ */ new Date()).toISOString() }, limitation: "ClickUp API validation unavailable; Optima trusts ignored local runtime state only after id/secret/config checks." };
9211
9223
  }
@@ -9215,7 +9227,7 @@ async function ensureClickUpWebhookSubscription({ validation, worktree, clickupC
9215
9227
  }
9216
9228
  const { config } = validation;
9217
9229
  const existing = readClickUpWebhookState(worktree, config);
9218
- const existingValidation = await validateClickUpWebhookState(existing, config, clickupClient);
9230
+ const existingValidation = await validateClickUpWebhookState(existing, config, clickupClient, { allowRemoteUnhealthyLocalRecovery: true });
9219
9231
  if (existingValidation.valid) {
9220
9232
  const state = writeClickUpWebhookState(worktree, { ...existingValidation.state, recentEventKeys: existing.recentEventKeys || [] }, config);
9221
9233
  clickUpWebhookLifecycleLog(worktree, { type: "remote_webhook_reused", webhookId: state.webhookId, mode: existingValidation.mode });
@@ -9758,12 +9770,18 @@ async function routeClickUpWebhookEventUnlocked({ payload, config, state = {}, w
9758
9770
  if (!taskId) return { ok: false, action: "error", reason: "missing_task_id" };
9759
9771
  let task = payload.task || (clickupClient?.getTask ? await clickupClient.getTask(taskId) : null);
9760
9772
  if (!task) return { ok: false, action: "error", reason: "task_unavailable", taskId };
9773
+ if (clickupClient?.getTask) {
9774
+ const latestTask = await clickupClient.getTask(taskId);
9775
+ if (latestTask) task = latestTask;
9776
+ }
9761
9777
  const isCommentEvent = eventType === "taskCommentPosted" || eventType === "taskCommentUpdated";
9762
9778
  if (isCommentEvent) {
9763
9779
  const comment = clickUpCommentFromPayload(payload);
9764
9780
  const authorId = clickUpCommentAuthorId(comment);
9765
9781
  if (authorId && authorId === config.routing.ignoredCommentAuthorId) return finish({ ok: true, action: "ignored", reason: "self_authored_comment", taskId });
9766
- if (!clickUpCommentMentionsProductManager(comment, config.routing)) return finish({ ok: true, action: "ignored", reason: "missing_product_manager_mention", taskId });
9782
+ const mentionsProductManager = clickUpCommentMentionsProductManager(comment, config.routing);
9783
+ const assignedToProductManager = isClickUpTaskAssignedToProductManager(task, config.routing.productManagerAssigneeId);
9784
+ if (!mentionsProductManager && !assignedToProductManager) return finish({ ok: true, action: "ignored", reason: "missing_product_manager_mention_or_assignment", taskId });
9767
9785
  commentLedgerKey = clickUpCommentLedgerKey({ taskId, eventType, payload });
9768
9786
  try {
9769
9787
  if (isClickUpCommentVersionProcessed({ ledgerPath: commentLedgerPath, key: commentLedgerKey, worktree })) {
@@ -9775,10 +9793,6 @@ async function routeClickUpWebhookEventUnlocked({ payload, config, state = {}, w
9775
9793
  }
9776
9794
  if (!isCommentEvent && !isClickUpTaskAssignedToProductManager(task, config.routing.productManagerAssigneeId)) return finish({ ok: true, action: "ignored", reason: "not_assigned_to_product_manager", taskId });
9777
9795
  if (isClickUpTaskTerminal(task, payload, config.routing.ignoredStatuses)) return finish({ ok: true, action: "ignored", reason: "terminal_status", taskId });
9778
- if (clickupClient?.getTask) {
9779
- const latestTask = await clickupClient.getTask(taskId);
9780
- if (latestTask) task = latestTask;
9781
- }
9782
9796
  const sessionTitle = formatClickUpSessionTitle({ taskId, payloadTask: payload.task, task });
9783
9797
  const existingMetadata = clickUpTaskAgentMetadata(task, config.routing.metadataFieldId);
9784
9798
  const metadata = normalizeAgentMetadataJson(existingMetadata);
@@ -9201,7 +9201,7 @@ async function findReusableClickUpWebhook(config, clickupClient = null) {
9201
9201
  }
9202
9202
  return null;
9203
9203
  }
9204
- async function validateClickUpWebhookState(state, config, clickupClient = null) {
9204
+ async function validateClickUpWebhookState(state, config, clickupClient = null, { allowRemoteUnhealthyLocalRecovery = false } = {}) {
9205
9205
  if (!isClickUpWebhookStateActive(state, config)) return { valid: false, reason: "state_incomplete" };
9206
9206
  if (clickupClient?.listWebhooks) {
9207
9207
  const listed = await clickupClient.listWebhooks({ teamId: config.teamId });
@@ -9209,10 +9209,22 @@ async function validateClickUpWebhookState(state, config, clickupClient = null)
9209
9209
  if (!match) return { valid: false, reason: "remote_state_missing" };
9210
9210
  const remote = normalizeClickUpWebhookApiResponse(match, config);
9211
9211
  const remoteWithLocalSecret = { ...remote, secret: state.secret };
9212
- if (!isClickUpWebhookStateActive(remoteWithLocalSecret, config) || remote.webhookId !== state.webhookId) {
9213
- return { valid: false, reason: "remote_state_mismatch", remote };
9212
+ if (isClickUpWebhookStateActive(remoteWithLocalSecret, config) && remote.webhookId === state.webhookId) {
9213
+ return { valid: true, mode: "remote", state: { ...remoteWithLocalSecret, recentEventKeys: state.recentEventKeys || [], lastValidatedAt: (/* @__PURE__ */ new Date()).toISOString() } };
9214
9214
  }
9215
- return { valid: true, mode: "remote", state: { ...remoteWithLocalSecret, recentEventKeys: state.recentEventKeys || [], lastValidatedAt: (/* @__PURE__ */ new Date()).toISOString() } };
9215
+ const localStateValidation = await validateClickUpWebhookState(state, config, null);
9216
+ if (allowRemoteUnhealthyLocalRecovery && localStateValidation.valid && remote.webhookId === state.webhookId) {
9217
+ const remoteConfigMatches = remote.publicUrl === config.webhook.publicUrl && [...new Set(config.webhook.events || [])].every((event) => new Set(remote.events || []).has(event));
9218
+ if (remoteConfigMatches) {
9219
+ return {
9220
+ ...localStateValidation,
9221
+ mode: "local_state_remote_unhealthy",
9222
+ limitation: "ClickUp remote webhook is present but unhealthy; Optima starts the local listener from matching local id/secret/config so delivery can recover.",
9223
+ remote
9224
+ };
9225
+ }
9226
+ }
9227
+ return { valid: false, reason: "remote_state_mismatch", remote };
9216
9228
  }
9217
9229
  return { valid: true, mode: "local_state", state: { ...state, lastValidatedAt: (/* @__PURE__ */ new Date()).toISOString() }, limitation: "ClickUp API validation unavailable; Optima trusts ignored local runtime state only after id/secret/config checks." };
9218
9230
  }
@@ -9222,7 +9234,7 @@ async function ensureClickUpWebhookSubscription({ validation, worktree, clickupC
9222
9234
  }
9223
9235
  const { config } = validation;
9224
9236
  const existing = readClickUpWebhookState(worktree, config);
9225
- const existingValidation = await validateClickUpWebhookState(existing, config, clickupClient);
9237
+ const existingValidation = await validateClickUpWebhookState(existing, config, clickupClient, { allowRemoteUnhealthyLocalRecovery: true });
9226
9238
  if (existingValidation.valid) {
9227
9239
  const state = writeClickUpWebhookState(worktree, { ...existingValidation.state, recentEventKeys: existing.recentEventKeys || [] }, config);
9228
9240
  clickUpWebhookLifecycleLog(worktree, { type: "remote_webhook_reused", webhookId: state.webhookId, mode: existingValidation.mode });
@@ -9765,12 +9777,18 @@ async function routeClickUpWebhookEventUnlocked({ payload, config, state = {}, w
9765
9777
  if (!taskId) return { ok: false, action: "error", reason: "missing_task_id" };
9766
9778
  let task = payload.task || (clickupClient?.getTask ? await clickupClient.getTask(taskId) : null);
9767
9779
  if (!task) return { ok: false, action: "error", reason: "task_unavailable", taskId };
9780
+ if (clickupClient?.getTask) {
9781
+ const latestTask = await clickupClient.getTask(taskId);
9782
+ if (latestTask) task = latestTask;
9783
+ }
9768
9784
  const isCommentEvent = eventType === "taskCommentPosted" || eventType === "taskCommentUpdated";
9769
9785
  if (isCommentEvent) {
9770
9786
  const comment = clickUpCommentFromPayload(payload);
9771
9787
  const authorId = clickUpCommentAuthorId(comment);
9772
9788
  if (authorId && authorId === config.routing.ignoredCommentAuthorId) return finish({ ok: true, action: "ignored", reason: "self_authored_comment", taskId });
9773
- if (!clickUpCommentMentionsProductManager(comment, config.routing)) return finish({ ok: true, action: "ignored", reason: "missing_product_manager_mention", taskId });
9789
+ const mentionsProductManager = clickUpCommentMentionsProductManager(comment, config.routing);
9790
+ const assignedToProductManager = isClickUpTaskAssignedToProductManager(task, config.routing.productManagerAssigneeId);
9791
+ if (!mentionsProductManager && !assignedToProductManager) return finish({ ok: true, action: "ignored", reason: "missing_product_manager_mention_or_assignment", taskId });
9774
9792
  commentLedgerKey = clickUpCommentLedgerKey({ taskId, eventType, payload });
9775
9793
  try {
9776
9794
  if (isClickUpCommentVersionProcessed({ ledgerPath: commentLedgerPath, key: commentLedgerKey, worktree })) {
@@ -9782,10 +9800,6 @@ async function routeClickUpWebhookEventUnlocked({ payload, config, state = {}, w
9782
9800
  }
9783
9801
  if (!isCommentEvent && !isClickUpTaskAssignedToProductManager(task, config.routing.productManagerAssigneeId)) return finish({ ok: true, action: "ignored", reason: "not_assigned_to_product_manager", taskId });
9784
9802
  if (isClickUpTaskTerminal(task, payload, config.routing.ignoredStatuses)) return finish({ ok: true, action: "ignored", reason: "terminal_status", taskId });
9785
- if (clickupClient?.getTask) {
9786
- const latestTask = await clickupClient.getTask(taskId);
9787
- if (latestTask) task = latestTask;
9788
- }
9789
9803
  const sessionTitle = formatClickUpSessionTitle({ taskId, payloadTask: payload.task, task });
9790
9804
  const existingMetadata = clickUpTaskAgentMetadata(task, config.routing.metadataFieldId);
9791
9805
  const metadata = normalizeAgentMetadataJson(existingMetadata);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@defend-tech/opencode-optima",
3
- "version": "0.1.33",
3
+ "version": "0.1.35",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+ssh://git@github.com/defend-tech/opencode-optima.git"