@defend-tech/opencode-optima 0.1.25 → 0.1.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. package/README.md +1 -1
  2. package/{agents → assets/agents}/codemap.yml +1 -1
  3. package/assets/codemap.yml +19 -0
  4. package/{policies → assets/policies}/README.md +2 -2
  5. package/{policies → assets/policies}/codemap.yml +1 -1
  6. package/dist/index.js +86 -5
  7. package/dist/sanitize_cli.js +86 -5
  8. package/docs/setup/CONFIGURATION.md +1 -1
  9. package/package.json +2 -3
  10. /package/{agents → assets/agents}/business_analyst.md +0 -0
  11. /package/{agents → assets/agents}/developer.md +0 -0
  12. /package/{agents → assets/agents}/ops_product_manager.md +0 -0
  13. /package/{agents → assets/agents}/product_manager.md +0 -0
  14. /package/{agents → assets/agents}/qa_engineer.md +0 -0
  15. /package/{agents → assets/agents}/tech_lead.md +0 -0
  16. /package/{agents → assets/agents}/technical_architect.md +0 -0
  17. /package/{agents → assets/agents}/ui_ux_designer.md +0 -0
  18. /package/{agents → assets/agents}/workflow_product_manager.md +0 -0
  19. /package/{agents → assets/agents}/workflow_runner.md +0 -0
  20. /package/{policies → assets/policies}/definition-of-done.md +0 -0
  21. /package/{policies → assets/policies}/definition-of-ready.md +0 -0
  22. /package/{policies → assets/policies}/development-guidelines.md +0 -0
  23. /package/{policies → assets/policies}/documentation-guidelines.md +0 -0
  24. /package/{policies → assets/policies}/git-commit-messaging.md +0 -0
  25. /package/{policies → assets/policies}/product-guidelines.md +0 -0
  26. /package/{policies → assets/policies}/testing-guidelines.md +0 -0
  27. /package/{policies → assets/policies}/ui-ux-guidelines.md +0 -0
package/README.md CHANGED
@@ -25,7 +25,7 @@ PMA will guide the repository setup flow and, when needed, initialize Optima ins
25
25
 
26
26
  During setup, PMA can initialize the repository and create `.optima/.config/optima.yaml`. Optima reads this file for repository-local defaults, feature flags, policy extraction settings, and per-agent config overrides.
27
27
 
28
- Repository-local policy overrides live in `.optima/policies/`. If a policy file is not present there, Optima falls back to the bundled plugin default automatically.
28
+ Repository-local policy overrides live in `.optima/policies/`. If a policy file is not present there, Optima falls back to the bundled plugin default from package `assets/policies/` automatically.
29
29
 
30
30
  Repository-local full agent definitions can live in `.optima/agents/`. Use this folder to override a bundled agent's base prompt or define a brand new custom repository agent.
31
31
 
@@ -1,5 +1,5 @@
1
1
  scope: module
2
- parent: ../.optima/codemap.yml
2
+ parent: ../../.optima/codemap.yml
3
3
  sources_of_truth:
4
4
  - path: product_manager.md
5
5
  - path: workflow_runner.md
@@ -0,0 +1,19 @@
1
+ scope: module
2
+ name: assets
3
+ parent: ../.optima/codemap.yml
4
+ purpose: Package-owned bundled runtime assets for opencode-optima.
5
+ code_roots:
6
+ - agents/
7
+ - policies/
8
+ modules:
9
+ - path: agents
10
+ summary: Bundled default agent prompts used by the plugin package.
11
+ - path: policies
12
+ summary: Bundled default policy markdown included by agent prompts.
13
+ entrypoints: []
14
+ sources_of_truth:
15
+ - path: agents
16
+ summary: Source of bundled agent definitions published with the npm package.
17
+ - path: policies
18
+ summary: Source of bundled policy defaults published with the npm package.
19
+ commands: {}
@@ -1,13 +1,13 @@
1
1
  # Optima Bundled Policies
2
2
 
3
- This package-root directory contains Optima's bundled fallback policy assets. Target repositories should put repo-local policy overrides in `.optima/policies/`, not in this package-root `policies/` directory.
3
+ This package asset directory contains Optima's bundled fallback policy assets. Target repositories should put repo-local policy overrides in `.optima/policies/`, not in a package-root `policies/` directory.
4
4
 
5
5
  ## How Policy Resolution Works
6
6
 
7
7
  For any `<include:policy:<file>.md>` include, Optima resolves policy files in this order:
8
8
 
9
9
  1. `.optima/policies/<file>.md`
10
- 2. bundled plugin default `policies/<file>.md`
10
+ 2. bundled plugin default from `assets/policies/<file>.md`
11
11
 
12
12
  Files under `.optima/.config/generated/policies/` are reference copies only. They are not read directly at runtime.
13
13
 
@@ -1,5 +1,5 @@
1
1
  scope: module
2
- parent: ../codemap.yml
2
+ parent: ../../.optima/codemap.yml
3
3
  sources_of_truth:
4
4
  - path: README.md
5
5
  - path: definition-of-done.md
package/dist/index.js CHANGED
@@ -7900,8 +7900,9 @@ async function optima_validate_logic(worktree) {
7900
7900
 
7901
7901
  // src/index.js
7902
7902
  var PKG_ROOT = path2.resolve(path2.dirname(fileURLToPath(import.meta.url)), "..");
7903
- var BUNDLE_AGENTS_DIR = path2.join(PKG_ROOT, "agents");
7904
- var BUNDLE_POLICIES_DIR = path2.join(PKG_ROOT, "policies");
7903
+ var BUNDLE_ASSETS_DIR = path2.join(PKG_ROOT, "assets");
7904
+ var BUNDLE_AGENTS_DIR = path2.join(BUNDLE_ASSETS_DIR, "agents");
7905
+ var BUNDLE_POLICIES_DIR = path2.join(BUNDLE_ASSETS_DIR, "policies");
7905
7906
  var TEMPLATES_DIR = path2.join(PKG_ROOT, "templates");
7906
7907
  var HUMANS_REGISTRY_PATH = path2.join(PKG_ROOT, "docs", "core", "humans.md");
7907
7908
  var MANDATORY_AGENTS = /* @__PURE__ */ new Set(["product_manager", "business_analyst", "tech_lead"]);
@@ -7965,7 +7966,7 @@ var REPO_LOCAL_POLICIES_README = [
7965
7966
  "For any `<include:policy:<file>.md>` include, Optima resolves policy files in this order:",
7966
7967
  "",
7967
7968
  "1. `.optima/policies/<file>.md`",
7968
- "2. bundled plugin default `policies/<file>.md`",
7969
+ "2. bundled plugin default from package assets (`assets/policies/<file>.md`)",
7969
7970
  "",
7970
7971
  "Files under `.optima/.config/generated/policies/` are reference copies only. They are not read directly at runtime.",
7971
7972
  "",
@@ -8853,6 +8854,9 @@ function optimaRuntimeDir(worktree) {
8853
8854
  function clickUpWebhookStatePath(worktree) {
8854
8855
  return path2.join(optimaRuntimeDir(worktree), "clickup-webhook.json");
8855
8856
  }
8857
+ function clickUpCommentLedgerPath(worktree) {
8858
+ return path2.join(optimaRuntimeDir(worktree), "clickup-comment-ledger.jsonl");
8859
+ }
8856
8860
  function clickUpWebhookLogPath(worktree) {
8857
8861
  return path2.join(optimaRuntimeDir(worktree), "clickup-webhook.log.jsonl");
8858
8862
  }
@@ -9206,6 +9210,71 @@ function rememberClickUpWebhookEvent(state = {}, eventKey, limit = 200) {
9206
9210
  if (recent.includes(eventKey)) return { duplicate: true, state };
9207
9211
  return { duplicate: false, state: { ...state, recentEventKeys: [...recent, eventKey].slice(-limit) } };
9208
9212
  }
9213
+ function readClickUpCommentLedger(ledgerPath) {
9214
+ const processed = /* @__PURE__ */ new Set();
9215
+ if (!ledgerPath || !fs2.existsSync(ledgerPath)) return processed;
9216
+ try {
9217
+ const raw = fs2.readFileSync(ledgerPath, "utf8");
9218
+ for (const [index, line] of raw.split(/\r?\n/).entries()) {
9219
+ if (!line.trim()) continue;
9220
+ try {
9221
+ const entry = JSON.parse(line);
9222
+ if (entry?.key) processed.add(String(entry.key));
9223
+ } catch (error) {
9224
+ throw new Error(`malformed ledger row ${index + 1}: ${error.message}`);
9225
+ }
9226
+ }
9227
+ } catch (error) {
9228
+ throw new Error(`ClickUp comment ledger unavailable: ${error.message}`);
9229
+ }
9230
+ return processed;
9231
+ }
9232
+ function appendClickUpCommentLedgerEntry(ledgerPath, entry = {}) {
9233
+ if (!ledgerPath || !entry.key) return;
9234
+ fs2.mkdirSync(path2.dirname(ledgerPath), { recursive: true, mode: 448 });
9235
+ fs2.appendFileSync(ledgerPath, `${JSON.stringify(entry)}
9236
+ `, { encoding: "utf8", mode: 384 });
9237
+ try {
9238
+ fs2.chmodSync(ledgerPath, 384);
9239
+ } catch {
9240
+ }
9241
+ }
9242
+ function clickUpCommentLedgerKey({ taskId, eventType, payload }) {
9243
+ const history = Array.isArray(payload?.history_items) ? payload.history_items[0] : payload?.history_item;
9244
+ const comment = clickUpCommentFromPayload(payload);
9245
+ const commentId = String(comment?.id || payload?.comment_id || payload?.commentId || history?.comment_id || history?.commentId || "").trim();
9246
+ if (!commentId) return "";
9247
+ const explicitVersion = String(
9248
+ comment?.date_updated || comment?.dateUpdated || comment?.updated_at || comment?.updatedAt || comment?._version_vector || comment?.version_vector || comment?.versionVector || comment?.version || comment?.revision || comment?.modified_at || comment?.modifiedAt || ""
9249
+ ).trim();
9250
+ const contentVersion = crypto.createHash("sha256").update(JSON.stringify({ text: clickUpCommentText(comment), parts: Array.isArray(comment.comment) ? comment.comment : null })).digest("hex").slice(0, 16);
9251
+ return [String(taskId || "").trim(), "comment", commentId, explicitVersion || `sha256-${contentVersion}`].filter(Boolean).join(":");
9252
+ }
9253
+ function isClickUpCommentVersionProcessed({ ledgerPath, key, ledger = null, worktree = process.cwd() } = {}) {
9254
+ if (!key) return false;
9255
+ try {
9256
+ return (ledger || readClickUpCommentLedger(ledgerPath)).has(key);
9257
+ } catch (error) {
9258
+ try {
9259
+ appendClickUpWebhookLocalLog(worktree, { type: "comment_ledger_read_failed", ledgerPath, message: error.message });
9260
+ } catch {
9261
+ }
9262
+ throw error;
9263
+ }
9264
+ }
9265
+ function recordClickUpCommentVersionProcessed({ ledgerPath, key, taskId, eventType, payload, result, at = /* @__PURE__ */ new Date() } = {}) {
9266
+ if (!key) return;
9267
+ const comment = clickUpCommentFromPayload(payload);
9268
+ appendClickUpCommentLedgerEntry(ledgerPath, {
9269
+ key,
9270
+ taskId,
9271
+ eventType,
9272
+ commentId: comment?.id || payload?.comment_id || payload?.commentId || null,
9273
+ action: result?.action || null,
9274
+ sessionId: result?.sessionId || null,
9275
+ recordedAt: at.toISOString()
9276
+ });
9277
+ }
9209
9278
  function clickUpTaskIdFromPayload(payload = {}) {
9210
9279
  return String(payload.task_id || payload.taskId || payload.task?.id || payload.task?.task_id || "").trim();
9211
9280
  }
@@ -9561,17 +9630,21 @@ async function withClickUpTaskRouteLock(taskId, operation) {
9561
9630
  if (activeClickUpTaskRoutes.get(key) === current) activeClickUpTaskRoutes.delete(key);
9562
9631
  }
9563
9632
  }
9564
- async function routeClickUpWebhookEventUnlocked({ payload, config, state = {}, worktree = process.cwd(), clickupClient, openCodeClient, sessionExists = openCodeSessionExists, createSession = createOpenCodeSession, sendSessionEvent = sendOpenCodeSessionEvent, saveState = null, now = () => /* @__PURE__ */ new Date(), host = os.hostname() } = {}) {
9633
+ async function routeClickUpWebhookEventUnlocked({ payload, config, state = {}, worktree = process.cwd(), clickupClient, openCodeClient, sessionExists = openCodeSessionExists, createSession = createOpenCodeSession, sendSessionEvent = sendOpenCodeSessionEvent, saveState = null, now = () => /* @__PURE__ */ new Date(), host = os.hostname(), commentLedgerPath = clickUpCommentLedgerPath(worktree) } = {}) {
9565
9634
  const eventType = clickUpEventType(payload);
9566
9635
  const eventKey = clickUpWebhookEventKey(payload);
9567
9636
  const remembered = rememberClickUpWebhookEvent(state, eventKey);
9568
9637
  if (remembered.duplicate) return { ok: true, action: "ignored", reason: "duplicate", eventKey };
9569
9638
  let stateToPersist = remembered.state;
9639
+ let commentLedgerKey = "";
9570
9640
  const persistState = (nextState) => {
9571
9641
  stateToPersist = nextState;
9572
9642
  if (saveState) saveState(nextState);
9573
9643
  };
9574
9644
  const finish = (result) => {
9645
+ if (result?.ok && commentLedgerKey && ["created_session", "sent_to_existing_session", "missing_session_reported"].includes(result.action)) {
9646
+ recordClickUpCommentVersionProcessed({ ledgerPath: commentLedgerPath, key: commentLedgerKey, taskId: result.taskId, eventType, payload, result, at: now() });
9647
+ }
9575
9648
  if (result?.ok && saveState) saveState(stateToPersist);
9576
9649
  return result;
9577
9650
  };
@@ -9586,6 +9659,14 @@ async function routeClickUpWebhookEventUnlocked({ payload, config, state = {}, w
9586
9659
  const authorId = clickUpCommentAuthorId(comment);
9587
9660
  if (authorId && authorId === config.routing.ignoredCommentAuthorId) return finish({ ok: true, action: "ignored", reason: "self_authored_comment", taskId });
9588
9661
  if (!clickUpCommentMentionsProductManager(comment, config.routing)) return finish({ ok: true, action: "ignored", reason: "missing_product_manager_mention", taskId });
9662
+ commentLedgerKey = clickUpCommentLedgerKey({ taskId, eventType, payload });
9663
+ try {
9664
+ if (isClickUpCommentVersionProcessed({ ledgerPath: commentLedgerPath, key: commentLedgerKey, worktree })) {
9665
+ return finish({ ok: true, action: "ignored", reason: "comment_version_already_processed", taskId, eventKey, commentLedgerKey });
9666
+ }
9667
+ } catch (error) {
9668
+ return { ok: false, action: "error", reason: "comment_ledger_unavailable", taskId, eventKey, message: error.message };
9669
+ }
9589
9670
  }
9590
9671
  if (!isCommentEvent && !isClickUpTaskAssignedToProductManager(task, config.routing.productManagerAssigneeId)) return finish({ ok: true, action: "ignored", reason: "not_assigned_to_product_manager", taskId });
9591
9672
  if (isClickUpTaskTerminal(task, payload, config.routing.ignoredStatuses)) return finish({ ok: true, action: "ignored", reason: "terminal_status", taskId });
@@ -11397,7 +11478,7 @@ Follow-up: use optima_prompt_workflow with session_id '${sessionId}' to check in
11397
11478
  }
11398
11479
  };
11399
11480
  }
11400
- OptimaPlugin.__internals = { BUNDLE_AGENTS_DIR, CLICKUP_DEFINITION_DOC_PARENT, activeClickUpWebhookLifecycleRegistry, cleanupManagedClickUpWebhook, deleteClickUpWebhookBestEffort, BUNDLE_POLICIES_DIR, buildClickUpApplyPayloadResult, buildClickUpCreateSubtasksPayload, buildClickUpStartTaskPayload, buildClickUpSummaryPayload, buildClickUpTransitionPayload, buildOptimaAgents, clickUpCommentMentionsProductManager, clickUpWebhookAuditLogDir, clickUpWebhookAuditLogPath, createClickUpApiClient, ensureClickUpWebhookSubscription, handleClickUpWebhookRequest, isClickUpWebhookStateActive, normalizeClickUpWebhookConfig, normalizeClickUpWebhookLogLevel, normalizeOpenCodeBaseUrl, readClickUpWebhookState, resolveOptimaPluginOptions, resolveSecretReference, routeClickUpWebhookEvent, sendOpenCodeSessionEvent, sendOpenCodeSessionEventDirect, startClickUpWebhookListener, validateClickUpWebhookState, verifyClickUpSignature, writeClickUpWebhookState, decideClickUpStatusAction, deriveClickUpBranchName, deriveClickUpPendingSubtaskBranch, deriveClickUpPrTarget, deriveClickUpWorktree, determineClickUpMergeAuthority, ensureOptimaGitignoreRules, explicitSafeInputWorktree, finalApprovalAssignees, formatValidationResult, isIgnoredClickUpTaskType, isOptimaPluginPackageWorktree, isSafeWritableDirectory, loadHumansRegistry, mergeClickUpAgentMetadata, mergeClickUpSessionMetadata, migrateLegacyOptimaLayout, normalizeAgentMetadataJson, normalizeClickUpStatus, normalizeClickUpTaskType, normalizePromptResponseParts, normalizeWorkflowTaskPath, parseClickUpSubtasksMarkdown, parseHumansRegistry, parseMarkdownArtifact, parseMarkdownSections, preEstimateClickUpWork, readMarkdownArtifact, resolveHumanRoles, resolveSafeWorktree, safeWorktreeOrFailure, stripRawLogSections, validateMainWorkspaceBranchSafety, workflowFinalMessageFromPromptResponse };
11481
+ 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, createClickUpApiClient, ensureClickUpWebhookSubscription, handleClickUpWebhookRequest, isClickUpWebhookStateActive, normalizeClickUpWebhookConfig, normalizeClickUpWebhookLogLevel, normalizeOpenCodeBaseUrl, readClickUpCommentLedger, readClickUpWebhookState, recordClickUpCommentVersionProcessed, resolveOptimaPluginOptions, resolveSecretReference, routeClickUpWebhookEvent, sendOpenCodeSessionEvent, sendOpenCodeSessionEventDirect, startClickUpWebhookListener, validateClickUpWebhookState, verifyClickUpSignature, writeClickUpWebhookState, decideClickUpStatusAction, deriveClickUpBranchName, deriveClickUpPendingSubtaskBranch, deriveClickUpPrTarget, deriveClickUpWorktree, determineClickUpMergeAuthority, ensureOptimaGitignoreRules, explicitSafeInputWorktree, finalApprovalAssignees, formatValidationResult, isIgnoredClickUpTaskType, isOptimaPluginPackageWorktree, isSafeWritableDirectory, loadHumansRegistry, mergeClickUpAgentMetadata, mergeClickUpSessionMetadata, migrateLegacyOptimaLayout, normalizeAgentMetadataJson, normalizeClickUpStatus, normalizeClickUpTaskType, normalizePromptResponseParts, normalizeWorkflowTaskPath, parseClickUpSubtasksMarkdown, parseHumansRegistry, parseMarkdownArtifact, parseMarkdownSections, preEstimateClickUpWork, readMarkdownArtifact, resolveHumanRoles, resolveSafeWorktree, safeWorktreeOrFailure, stripRawLogSections, validateMainWorkspaceBranchSafety, workflowFinalMessageFromPromptResponse };
11401
11482
  export {
11402
11483
  OptimaPlugin as default
11403
11484
  };
@@ -7907,8 +7907,9 @@ async function optima_validate_logic(worktree) {
7907
7907
 
7908
7908
  // src/index.js
7909
7909
  var PKG_ROOT = path2.resolve(path2.dirname(fileURLToPath(import.meta.url)), "..");
7910
- var BUNDLE_AGENTS_DIR = path2.join(PKG_ROOT, "agents");
7911
- var BUNDLE_POLICIES_DIR = path2.join(PKG_ROOT, "policies");
7910
+ var BUNDLE_ASSETS_DIR = path2.join(PKG_ROOT, "assets");
7911
+ var BUNDLE_AGENTS_DIR = path2.join(BUNDLE_ASSETS_DIR, "agents");
7912
+ var BUNDLE_POLICIES_DIR = path2.join(BUNDLE_ASSETS_DIR, "policies");
7912
7913
  var TEMPLATES_DIR = path2.join(PKG_ROOT, "templates");
7913
7914
  var HUMANS_REGISTRY_PATH = path2.join(PKG_ROOT, "docs", "core", "humans.md");
7914
7915
  var MANDATORY_AGENTS = /* @__PURE__ */ new Set(["product_manager", "business_analyst", "tech_lead"]);
@@ -7972,7 +7973,7 @@ var REPO_LOCAL_POLICIES_README = [
7972
7973
  "For any `<include:policy:<file>.md>` include, Optima resolves policy files in this order:",
7973
7974
  "",
7974
7975
  "1. `.optima/policies/<file>.md`",
7975
- "2. bundled plugin default `policies/<file>.md`",
7976
+ "2. bundled plugin default from package assets (`assets/policies/<file>.md`)",
7976
7977
  "",
7977
7978
  "Files under `.optima/.config/generated/policies/` are reference copies only. They are not read directly at runtime.",
7978
7979
  "",
@@ -8860,6 +8861,9 @@ function optimaRuntimeDir(worktree) {
8860
8861
  function clickUpWebhookStatePath(worktree) {
8861
8862
  return path2.join(optimaRuntimeDir(worktree), "clickup-webhook.json");
8862
8863
  }
8864
+ function clickUpCommentLedgerPath(worktree) {
8865
+ return path2.join(optimaRuntimeDir(worktree), "clickup-comment-ledger.jsonl");
8866
+ }
8863
8867
  function clickUpWebhookLogPath(worktree) {
8864
8868
  return path2.join(optimaRuntimeDir(worktree), "clickup-webhook.log.jsonl");
8865
8869
  }
@@ -9213,6 +9217,71 @@ function rememberClickUpWebhookEvent(state = {}, eventKey, limit = 200) {
9213
9217
  if (recent.includes(eventKey)) return { duplicate: true, state };
9214
9218
  return { duplicate: false, state: { ...state, recentEventKeys: [...recent, eventKey].slice(-limit) } };
9215
9219
  }
9220
+ function readClickUpCommentLedger(ledgerPath) {
9221
+ const processed = /* @__PURE__ */ new Set();
9222
+ if (!ledgerPath || !fs2.existsSync(ledgerPath)) return processed;
9223
+ try {
9224
+ const raw = fs2.readFileSync(ledgerPath, "utf8");
9225
+ for (const [index, line] of raw.split(/\r?\n/).entries()) {
9226
+ if (!line.trim()) continue;
9227
+ try {
9228
+ const entry = JSON.parse(line);
9229
+ if (entry?.key) processed.add(String(entry.key));
9230
+ } catch (error) {
9231
+ throw new Error(`malformed ledger row ${index + 1}: ${error.message}`);
9232
+ }
9233
+ }
9234
+ } catch (error) {
9235
+ throw new Error(`ClickUp comment ledger unavailable: ${error.message}`);
9236
+ }
9237
+ return processed;
9238
+ }
9239
+ function appendClickUpCommentLedgerEntry(ledgerPath, entry = {}) {
9240
+ if (!ledgerPath || !entry.key) return;
9241
+ fs2.mkdirSync(path2.dirname(ledgerPath), { recursive: true, mode: 448 });
9242
+ fs2.appendFileSync(ledgerPath, `${JSON.stringify(entry)}
9243
+ `, { encoding: "utf8", mode: 384 });
9244
+ try {
9245
+ fs2.chmodSync(ledgerPath, 384);
9246
+ } catch {
9247
+ }
9248
+ }
9249
+ function clickUpCommentLedgerKey({ taskId, eventType, payload }) {
9250
+ const history = Array.isArray(payload?.history_items) ? payload.history_items[0] : payload?.history_item;
9251
+ const comment = clickUpCommentFromPayload(payload);
9252
+ const commentId = String(comment?.id || payload?.comment_id || payload?.commentId || history?.comment_id || history?.commentId || "").trim();
9253
+ if (!commentId) return "";
9254
+ const explicitVersion = String(
9255
+ comment?.date_updated || comment?.dateUpdated || comment?.updated_at || comment?.updatedAt || comment?._version_vector || comment?.version_vector || comment?.versionVector || comment?.version || comment?.revision || comment?.modified_at || comment?.modifiedAt || ""
9256
+ ).trim();
9257
+ const contentVersion = crypto.createHash("sha256").update(JSON.stringify({ text: clickUpCommentText(comment), parts: Array.isArray(comment.comment) ? comment.comment : null })).digest("hex").slice(0, 16);
9258
+ return [String(taskId || "").trim(), "comment", commentId, explicitVersion || `sha256-${contentVersion}`].filter(Boolean).join(":");
9259
+ }
9260
+ function isClickUpCommentVersionProcessed({ ledgerPath, key, ledger = null, worktree = process.cwd() } = {}) {
9261
+ if (!key) return false;
9262
+ try {
9263
+ return (ledger || readClickUpCommentLedger(ledgerPath)).has(key);
9264
+ } catch (error) {
9265
+ try {
9266
+ appendClickUpWebhookLocalLog(worktree, { type: "comment_ledger_read_failed", ledgerPath, message: error.message });
9267
+ } catch {
9268
+ }
9269
+ throw error;
9270
+ }
9271
+ }
9272
+ function recordClickUpCommentVersionProcessed({ ledgerPath, key, taskId, eventType, payload, result, at = /* @__PURE__ */ new Date() } = {}) {
9273
+ if (!key) return;
9274
+ const comment = clickUpCommentFromPayload(payload);
9275
+ appendClickUpCommentLedgerEntry(ledgerPath, {
9276
+ key,
9277
+ taskId,
9278
+ eventType,
9279
+ commentId: comment?.id || payload?.comment_id || payload?.commentId || null,
9280
+ action: result?.action || null,
9281
+ sessionId: result?.sessionId || null,
9282
+ recordedAt: at.toISOString()
9283
+ });
9284
+ }
9216
9285
  function clickUpTaskIdFromPayload(payload = {}) {
9217
9286
  return String(payload.task_id || payload.taskId || payload.task?.id || payload.task?.task_id || "").trim();
9218
9287
  }
@@ -9568,17 +9637,21 @@ async function withClickUpTaskRouteLock(taskId, operation) {
9568
9637
  if (activeClickUpTaskRoutes.get(key) === current) activeClickUpTaskRoutes.delete(key);
9569
9638
  }
9570
9639
  }
9571
- async function routeClickUpWebhookEventUnlocked({ payload, config, state = {}, worktree = process.cwd(), clickupClient, openCodeClient, sessionExists = openCodeSessionExists, createSession = createOpenCodeSession, sendSessionEvent = sendOpenCodeSessionEvent, saveState = null, now = () => /* @__PURE__ */ new Date(), host = os.hostname() } = {}) {
9640
+ async function routeClickUpWebhookEventUnlocked({ payload, config, state = {}, worktree = process.cwd(), clickupClient, openCodeClient, sessionExists = openCodeSessionExists, createSession = createOpenCodeSession, sendSessionEvent = sendOpenCodeSessionEvent, saveState = null, now = () => /* @__PURE__ */ new Date(), host = os.hostname(), commentLedgerPath = clickUpCommentLedgerPath(worktree) } = {}) {
9572
9641
  const eventType = clickUpEventType(payload);
9573
9642
  const eventKey = clickUpWebhookEventKey(payload);
9574
9643
  const remembered = rememberClickUpWebhookEvent(state, eventKey);
9575
9644
  if (remembered.duplicate) return { ok: true, action: "ignored", reason: "duplicate", eventKey };
9576
9645
  let stateToPersist = remembered.state;
9646
+ let commentLedgerKey = "";
9577
9647
  const persistState = (nextState) => {
9578
9648
  stateToPersist = nextState;
9579
9649
  if (saveState) saveState(nextState);
9580
9650
  };
9581
9651
  const finish = (result) => {
9652
+ if (result?.ok && commentLedgerKey && ["created_session", "sent_to_existing_session", "missing_session_reported"].includes(result.action)) {
9653
+ recordClickUpCommentVersionProcessed({ ledgerPath: commentLedgerPath, key: commentLedgerKey, taskId: result.taskId, eventType, payload, result, at: now() });
9654
+ }
9582
9655
  if (result?.ok && saveState) saveState(stateToPersist);
9583
9656
  return result;
9584
9657
  };
@@ -9593,6 +9666,14 @@ async function routeClickUpWebhookEventUnlocked({ payload, config, state = {}, w
9593
9666
  const authorId = clickUpCommentAuthorId(comment);
9594
9667
  if (authorId && authorId === config.routing.ignoredCommentAuthorId) return finish({ ok: true, action: "ignored", reason: "self_authored_comment", taskId });
9595
9668
  if (!clickUpCommentMentionsProductManager(comment, config.routing)) return finish({ ok: true, action: "ignored", reason: "missing_product_manager_mention", taskId });
9669
+ commentLedgerKey = clickUpCommentLedgerKey({ taskId, eventType, payload });
9670
+ try {
9671
+ if (isClickUpCommentVersionProcessed({ ledgerPath: commentLedgerPath, key: commentLedgerKey, worktree })) {
9672
+ return finish({ ok: true, action: "ignored", reason: "comment_version_already_processed", taskId, eventKey, commentLedgerKey });
9673
+ }
9674
+ } catch (error) {
9675
+ return { ok: false, action: "error", reason: "comment_ledger_unavailable", taskId, eventKey, message: error.message };
9676
+ }
9596
9677
  }
9597
9678
  if (!isCommentEvent && !isClickUpTaskAssignedToProductManager(task, config.routing.productManagerAssigneeId)) return finish({ ok: true, action: "ignored", reason: "not_assigned_to_product_manager", taskId });
9598
9679
  if (isClickUpTaskTerminal(task, payload, config.routing.ignoredStatuses)) return finish({ ok: true, action: "ignored", reason: "terminal_status", taskId });
@@ -11404,7 +11485,7 @@ Follow-up: use optima_prompt_workflow with session_id '${sessionId}' to check in
11404
11485
  }
11405
11486
  };
11406
11487
  }
11407
- OptimaPlugin.__internals = { BUNDLE_AGENTS_DIR, CLICKUP_DEFINITION_DOC_PARENT, activeClickUpWebhookLifecycleRegistry, cleanupManagedClickUpWebhook, deleteClickUpWebhookBestEffort, BUNDLE_POLICIES_DIR, buildClickUpApplyPayloadResult, buildClickUpCreateSubtasksPayload, buildClickUpStartTaskPayload, buildClickUpSummaryPayload, buildClickUpTransitionPayload, buildOptimaAgents, clickUpCommentMentionsProductManager, clickUpWebhookAuditLogDir, clickUpWebhookAuditLogPath, createClickUpApiClient, ensureClickUpWebhookSubscription, handleClickUpWebhookRequest, isClickUpWebhookStateActive, normalizeClickUpWebhookConfig, normalizeClickUpWebhookLogLevel, normalizeOpenCodeBaseUrl, readClickUpWebhookState, resolveOptimaPluginOptions, resolveSecretReference, routeClickUpWebhookEvent, sendOpenCodeSessionEvent, sendOpenCodeSessionEventDirect, startClickUpWebhookListener, validateClickUpWebhookState, verifyClickUpSignature, writeClickUpWebhookState, decideClickUpStatusAction, deriveClickUpBranchName, deriveClickUpPendingSubtaskBranch, deriveClickUpPrTarget, deriveClickUpWorktree, determineClickUpMergeAuthority, ensureOptimaGitignoreRules, explicitSafeInputWorktree, finalApprovalAssignees, formatValidationResult, isIgnoredClickUpTaskType, isOptimaPluginPackageWorktree, isSafeWritableDirectory, loadHumansRegistry, mergeClickUpAgentMetadata, mergeClickUpSessionMetadata, migrateLegacyOptimaLayout, normalizeAgentMetadataJson, normalizeClickUpStatus, normalizeClickUpTaskType, normalizePromptResponseParts, normalizeWorkflowTaskPath, parseClickUpSubtasksMarkdown, parseHumansRegistry, parseMarkdownArtifact, parseMarkdownSections, preEstimateClickUpWork, readMarkdownArtifact, resolveHumanRoles, resolveSafeWorktree, safeWorktreeOrFailure, stripRawLogSections, validateMainWorkspaceBranchSafety, workflowFinalMessageFromPromptResponse };
11488
+ 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, createClickUpApiClient, ensureClickUpWebhookSubscription, handleClickUpWebhookRequest, isClickUpWebhookStateActive, normalizeClickUpWebhookConfig, normalizeClickUpWebhookLogLevel, normalizeOpenCodeBaseUrl, readClickUpCommentLedger, readClickUpWebhookState, recordClickUpCommentVersionProcessed, resolveOptimaPluginOptions, resolveSecretReference, routeClickUpWebhookEvent, sendOpenCodeSessionEvent, sendOpenCodeSessionEventDirect, startClickUpWebhookListener, validateClickUpWebhookState, verifyClickUpSignature, writeClickUpWebhookState, decideClickUpStatusAction, deriveClickUpBranchName, deriveClickUpPendingSubtaskBranch, deriveClickUpPrTarget, deriveClickUpWorktree, determineClickUpMergeAuthority, ensureOptimaGitignoreRules, explicitSafeInputWorktree, finalApprovalAssignees, formatValidationResult, isIgnoredClickUpTaskType, isOptimaPluginPackageWorktree, isSafeWritableDirectory, loadHumansRegistry, mergeClickUpAgentMetadata, mergeClickUpSessionMetadata, migrateLegacyOptimaLayout, normalizeAgentMetadataJson, normalizeClickUpStatus, normalizeClickUpTaskType, normalizePromptResponseParts, normalizeWorkflowTaskPath, parseClickUpSubtasksMarkdown, parseHumansRegistry, parseMarkdownArtifact, parseMarkdownSections, preEstimateClickUpWork, readMarkdownArtifact, resolveHumanRoles, resolveSafeWorktree, safeWorktreeOrFailure, stripRawLogSections, validateMainWorkspaceBranchSafety, workflowFinalMessageFromPromptResponse };
11408
11489
 
11409
11490
  // src/sanitize_cli.js
11410
11491
  var { migrateLegacyOptimaLayout: migrateLegacyOptimaLayout2 } = OptimaPlugin.__internals;
@@ -90,7 +90,7 @@ policies:
90
90
  extract_defaults: all
91
91
  ```
92
92
 
93
- This writes the bundled default policy files to `.optima/.config/generated/policies/` for reference. Those generated files are not used directly at runtime. To customize one, copy it into `.optima/policies/` and edit the copy.
93
+ This writes the bundled default policy files from package `assets/policies/` to `.optima/.config/generated/policies/` for reference. Those generated files are not used directly at runtime. To customize one, copy it into `.optima/policies/` and edit the copy.
94
94
 
95
95
  ### Add repository-specific agent instructions
96
96
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@defend-tech/opencode-optima",
3
- "version": "0.1.25",
3
+ "version": "0.1.27",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+ssh://git@github.com/defend-tech/opencode-optima.git"
@@ -25,9 +25,8 @@
25
25
  },
26
26
  "files": [
27
27
  "dist",
28
- "agents",
28
+ "assets",
29
29
  "docs",
30
- "policies",
31
30
  "templates",
32
31
  "scripts/optima-sanitize-host.js",
33
32
  "Agents_Common.md",
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes