@defend-tech/opencode-optima 0.1.25 → 0.1.26
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/README.md +1 -1
- package/{agents → assets/agents}/codemap.yml +1 -1
- package/{policies → assets/policies}/README.md +2 -2
- package/{policies → assets/policies}/codemap.yml +1 -1
- package/dist/index.js +86 -5
- package/dist/sanitize_cli.js +86 -5
- package/docs/setup/CONFIGURATION.md +1 -1
- package/package.json +2 -3
- /package/{agents → assets/agents}/business_analyst.md +0 -0
- /package/{agents → assets/agents}/developer.md +0 -0
- /package/{agents → assets/agents}/ops_product_manager.md +0 -0
- /package/{agents → assets/agents}/product_manager.md +0 -0
- /package/{agents → assets/agents}/qa_engineer.md +0 -0
- /package/{agents → assets/agents}/tech_lead.md +0 -0
- /package/{agents → assets/agents}/technical_architect.md +0 -0
- /package/{agents → assets/agents}/ui_ux_designer.md +0 -0
- /package/{agents → assets/agents}/workflow_product_manager.md +0 -0
- /package/{agents → assets/agents}/workflow_runner.md +0 -0
- /package/{policies → assets/policies}/definition-of-done.md +0 -0
- /package/{policies → assets/policies}/definition-of-ready.md +0 -0
- /package/{policies → assets/policies}/development-guidelines.md +0 -0
- /package/{policies → assets/policies}/documentation-guidelines.md +0 -0
- /package/{policies → assets/policies}/git-commit-messaging.md +0 -0
- /package/{policies → assets/policies}/product-guidelines.md +0 -0
- /package/{policies → assets/policies}/testing-guidelines.md +0 -0
- /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,13 +1,13 @@
|
|
|
1
1
|
# Optima Bundled Policies
|
|
2
2
|
|
|
3
|
-
This package
|
|
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
|
|
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
|
|
7904
|
-
var
|
|
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
|
};
|
package/dist/sanitize_cli.js
CHANGED
|
@@ -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
|
|
7911
|
-
var
|
|
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.
|
|
3
|
+
"version": "0.1.26",
|
|
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
|
-
"
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|