@gendigital/sage-opencode 0.9.0 → 0.11.0

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 (49) hide show
  1. package/README.md +60 -71
  2. package/dist/bundled-dirs.d.ts +1 -1
  3. package/dist/bundled-dirs.d.ts.map +1 -1
  4. package/dist/bundled-dirs.js +1 -1
  5. package/dist/bundled-dirs.js.map +1 -1
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +83 -18
  8. package/dist/index.js.map +1 -1
  9. package/dist/mcp-server.cjs +31776 -0
  10. package/dist/mcp-server.cjs.map +7 -0
  11. package/dist/plugin-discovery.js +1 -1
  12. package/dist/plugin-discovery.js.map +1 -1
  13. package/dist/startup-scan.d.ts.map +1 -1
  14. package/dist/startup-scan.js +3 -2
  15. package/dist/startup-scan.js.map +1 -1
  16. package/dist/tool-handler.d.ts +1 -1
  17. package/dist/tool-handler.d.ts.map +1 -1
  18. package/dist/tool-handler.js +61 -6
  19. package/dist/tool-handler.js.map +1 -1
  20. package/package.json +6 -4
  21. package/resources/threats/agent-layer.yaml +186 -0
  22. package/resources/threats/commands.yaml +24 -53
  23. package/resources/threats/credentials.yaml +12 -20
  24. package/resources/threats/dummy.yaml +22 -33
  25. package/resources/threats/files.yaml +34 -53
  26. package/resources/threats/mac-commands.yaml +20 -47
  27. package/resources/threats/mac-credentials.yaml +4 -12
  28. package/resources/threats/mac-files.yaml +5 -15
  29. package/resources/threats/mac-obfuscation.yaml +6 -13
  30. package/resources/threats/mac-persistence.yaml +11 -20
  31. package/resources/threats/mac-supply-chain.yaml +5 -8
  32. package/resources/threats/mitre.yaml +105 -167
  33. package/resources/threats/obfuscation.yaml +7 -15
  34. package/resources/threats/persistence.yaml +10 -17
  35. package/resources/threats/prompt-injection.yaml +94 -44
  36. package/resources/threats/self-defense.yaml +8 -12
  37. package/resources/threats/supply_chain.yaml +1 -2
  38. package/resources/threats/urls.yaml +9 -14
  39. package/resources/threats/win-commands.yaml +46 -106
  40. package/resources/threats/win-credentials.yaml +15 -25
  41. package/resources/threats/win-files.yaml +10 -20
  42. package/resources/threats/win-obfuscation.yaml +21 -37
  43. package/resources/threats/win-persistence.yaml +8 -16
  44. package/resources/threats/win-supply-chain.yaml +2 -3
  45. package/dist/logger-adaptor.d.ts +0 -15
  46. package/dist/logger-adaptor.d.ts.map +0 -1
  47. package/dist/logger-adaptor.js +0 -32
  48. package/dist/logger-adaptor.js.map +0 -1
  49. /package/resources/{allowlists → trusted-domains}/trusted-installer-domains.yaml +0 -0
package/README.md CHANGED
@@ -1,102 +1,91 @@
1
- # Sage for OpenCode
1
+ # Sage — Safety for Agents
2
2
 
3
- Sage integrates with OpenCode as a plugin and evaluates tool calls before they run.
3
+ <p align="center">
4
+ <img src="https://raw.githubusercontent.com/gendigitalinc/sage/main/images/logo-shaded.png" alt="Sage" width="250">
5
+ </p>
4
6
 
5
- ## What it protects
7
+ <p align="center">
8
+ Protect your AI coding agent from dangerous commands, malicious URLs, and harmful file operations.
9
+ </p>
6
10
 
7
- - `bash` commands
8
- - `webfetch` URLs
9
- - File operations (`read`, `write`, `edit`, `ls`, `glob`, `grep`)
11
+ ---
10
12
 
11
- Unmapped tools pass through unchanged.
13
+ <p align="center">
14
+ <img src="https://raw.githubusercontent.com/gendigitalinc/sage/main/images/block-opencode-allow.gif" alt="Sage blocking a dangerous command in OpenCode" width="700">
15
+ </p>
12
16
 
13
- ## Install
14
-
15
- Clone the repo, build the OpenCode package, then point OpenCode to this package path:
16
-
17
- ```bash
18
- git clone https://github.com/gendigitalinc/sage
19
- cd sage
20
- pnpm install
21
- pnpm --filter @gendigital/sage-opencode run build
22
- ```
23
-
24
- Global config (`~/.config/opencode/opencode.json`):
17
+ ## What is Sage?
25
18
 
26
- ```json
27
- {
28
- "plugin": ["/absolute/path/to/sage/packages/opencode"]
29
- }
30
- ```
19
+ Sage is a security layer for OpenCode. It intercepts tool calls — bash commands, URL fetches, file operations — and checks them for threats before they execute. If something looks dangerous, Sage blocks it or asks you to approve via OpenCode's native approval dialog.
31
20
 
32
- ## Behavior
33
21
 
34
- - **deny** verdicts: blocked immediately
35
- - **ask** verdicts: blocked with an explicit `sage_approve` action ID
36
- - **allow** verdicts: pass through
22
+ ## What it protects against
37
23
 
38
- ## Approval Flow
24
+ - **Malicious URLs** — phishing, malware, and scam sites detected via cloud reputation
25
+ - **Dangerous commands** — reverse shells, pipe-to-curl, credential theft, data exfiltration
26
+ - **Prompt injection** — heuristics + a fine-tuned ML model detect injected instructions in fetched content
27
+ - **Suspicious file operations** — writes to sensitive paths, credential files, system configs
28
+ - **Supply-chain attacks** — malicious or typosquatted npm/PyPI packages
29
+ - **Compromised plugins** — automatic scanning of installed plugins at session start
39
30
 
40
- OpenCode does not expose a plugin-spawned permission dialog, so Sage uses tool-based approval:
31
+ ## Install
41
32
 
42
- 1. Sage blocks an `ask` verdict and returns an `actionId`
43
- 2. Ask the user for explicit confirmation in chat
44
- 3. If approved, call:
33
+ Add the plugin to your OpenCode config (`~/.config/opencode/opencode.json`):
45
34
 
46
- ```ts
47
- sage_approve({ actionId: "...", approved: true })
35
+ ```json
36
+ {
37
+ "plugin": ["@gendigital/sage-opencode"]
38
+ }
48
39
  ```
49
40
 
50
- 4. Retry the original tool call
51
-
52
- ## Session Startup Scanning
53
-
54
- Sage automatically scans all installed OpenCode plugins when a new session starts.
41
+ Or [build from source](https://github.com/gendigitalinc/sage/blob/main/docs/user-guide.md#opencode).
55
42
 
56
- ### What's Scanned
43
+ > To verify it's working, ask your agent to run `echo __sage_test_deny_cmd_a75bf229__`. Sage should block this harmless canary command.
57
44
 
58
- - **NPM plugins**: Packages listed in `opencode.json` config (global + project)
59
- - **Local plugins**: Files in `~/.config/opencode/plugins/` (global)
60
- - **Project plugins**: Files in `.opencode/plugins/` (project-specific)
45
+ ## What Sage intercepts
61
46
 
62
- ### When Scanning Runs
47
+ Sage hooks into OpenCode's plugin system:
63
48
 
64
- - **Trigger**: Once per session on `session.updated` event (deduplicated by session ID)
65
- - **Performance**: Results are cached for fast subsequent sessions
49
+ - **bash** — shell commands
50
+ - **write / edit** — file modifications
51
+ - **read** — file reads (sensitive paths)
52
+ - **webfetch** — URL fetches and downloads
53
+ - **ls / glob / grep** — file system operations
66
54
 
67
- ### How Findings Are Shown
55
+ ## How it works
68
56
 
69
- 1. **System Prompt Injection**: Findings appear as first message in agent's context
70
- 2. **One-Shot**: Only injected once per session (not repeated)
71
- 3. **Agent Notification**: Agent sees threats and can inform user
72
- 4. **Console Logging**: Findings also logged to OpenCode console
57
+ When your agent makes a tool call, Sage evaluates it and returns a verdict:
73
58
 
74
- ### What Gets Scanned
59
+ | Verdict | What happens |
60
+ |---------|-------------|
61
+ | **Allow** | No threats detected — the action proceeds normally |
62
+ | **Ask** | Suspicious activity — OpenCode shows a native approval dialog |
63
+ | **Deny** | Threat detected — the action is blocked |
75
64
 
76
- - Plugin source code (JS/TS/PY files)
77
- - Package metadata (package.json)
78
- - Embedded URLs (checked against threat intelligence)
79
- - Suspicious patterns (credentials, obfuscation, persistence)
65
+ For `ask` verdicts, Sage blocks the tool call and the agent calls `sage_approve({ actionId })`, which surfaces OpenCode's native approval dialog. If you approve, the agent retries the original tool call.
80
66
 
81
- ### Self-Protection
67
+ Sage is designed to fail open: if anything goes wrong internally, the action proceeds. Your agent is never blocked due to a Sage error.
82
68
 
83
- - Sage excludes itself from scanning (`@gendigital/sage-opencode`)
84
- - Fail-open philosophy: Errors don't block OpenCode
69
+ > **Note:** In `paranoid` sensitivity mode, `ask` verdicts are promoted to `deny` on OpenCode. This prevents prompt-injection attacks from auto-approving flagged actions.
85
70
 
86
- ### Cache Location
71
+ ## Configuration
87
72
 
88
- Scan cache stored at `~/.sage/plugin_scan_cache.json` for performance.
73
+ Sage works out of the box with no configuration. To customize behavior, edit `~/.sage/config.json`:
89
74
 
90
- ## Build
91
-
92
- ```bash
93
- pnpm --filter @gendigital/sage-opencode run build
75
+ ```json
76
+ {
77
+ "sensitivity": "balanced",
78
+ "url_check": { "enabled": true },
79
+ "heuristics_enabled": true
80
+ }
94
81
  ```
95
82
 
96
- This copies `threats/` and `allowlists/` into `packages/opencode/resources/`.
83
+ See [Configuration](https://github.com/gendigitalinc/sage/blob/main/docs/user-guide.md#configuration) for all options.
97
84
 
98
- ## Test
85
+ ## Links
99
86
 
100
- ```bash
101
- pnpm test -- packages/opencode/src/__tests__/integration.test.ts
102
- ```
87
+ - [User Guide](https://github.com/gendigitalinc/sage/blob/main/docs/user-guide.md) — verify install, handle alerts, manage false positives
88
+ - [Configuration](https://github.com/gendigitalinc/sage/blob/main/docs/user-guide.md#configuration) — all config options
89
+ - [Exceptions](https://github.com/gendigitalinc/sage/blob/main/docs/user-guide.md#exceptions) — pattern-based allow/deny rules
90
+ - [Privacy](https://github.com/gendigitalinc/sage/blob/main/docs/user-guide.md#privacy) — what data is sent, what stays local
91
+ - [GitHub](https://github.com/gendigitalinc/sage)
@@ -6,7 +6,7 @@ export declare function getBundledDataDirs(): {
6
6
  pluginDir: string;
7
7
  packageRoot: string;
8
8
  threatsDir: string;
9
- allowlistsDir: string;
9
+ trustedDomainsDir: string;
10
10
  };
11
11
  export declare function getSageVersion(): string;
12
12
  //# sourceMappingURL=bundled-dirs.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"bundled-dirs.d.ts","sourceRoot":"","sources":["../src/bundled-dirs.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,wBAAgB,kBAAkB,IAAI;IACrC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;CACtB,CASA;AAED,wBAAgB,cAAc,IAAI,MAAM,CASvC"}
1
+ {"version":3,"file":"bundled-dirs.d.ts","sourceRoot":"","sources":["../src/bundled-dirs.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,wBAAgB,kBAAkB,IAAI;IACrC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,iBAAiB,EAAE,MAAM,CAAC;CAC1B,CASA;AAED,wBAAgB,cAAc,IAAI,MAAM,CASvC"}
@@ -12,7 +12,7 @@ export function getBundledDataDirs() {
12
12
  pluginDir,
13
13
  packageRoot,
14
14
  threatsDir: join(packageRoot, "resources", "threats"),
15
- allowlistsDir: join(packageRoot, "resources", "allowlists"),
15
+ trustedDomainsDir: join(packageRoot, "resources", "trusted-domains"),
16
16
  };
17
17
  }
18
18
  export function getSageVersion() {
@@ -1 +1 @@
1
- {"version":3,"file":"bundled-dirs.js","sourceRoot":"","sources":["../src/bundled-dirs.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,MAAM,UAAU,kBAAkB;IAMjC,MAAM,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IAC1D,MAAM,WAAW,GAAG,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;IAC1C,OAAO;QACN,SAAS;QACT,WAAW;QACX,UAAU,EAAE,IAAI,CAAC,WAAW,EAAE,WAAW,EAAE,SAAS,CAAC;QACrD,aAAa,EAAE,IAAI,CAAC,WAAW,EAAE,WAAW,EAAE,YAAY,CAAC;KAC3D,CAAC;AACH,CAAC;AAED,MAAM,UAAU,cAAc;IAC7B,IAAI,CAAC;QACJ,MAAM,EAAE,WAAW,EAAE,GAAG,kBAAkB,EAAE,CAAC;QAC7C,MAAM,OAAO,GAAG,IAAI,CAAC,WAAW,EAAE,cAAc,CAAC,CAAC;QAClD,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,CAA4B,CAAC;QAClF,OAAQ,GAAG,CAAC,OAAkB,IAAI,OAAO,CAAC;IAC3C,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,OAAO,CAAC;IAChB,CAAC;AACF,CAAC"}
1
+ {"version":3,"file":"bundled-dirs.js","sourceRoot":"","sources":["../src/bundled-dirs.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,MAAM,UAAU,kBAAkB;IAMjC,MAAM,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IAC1D,MAAM,WAAW,GAAG,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;IAC1C,OAAO;QACN,SAAS;QACT,WAAW;QACX,UAAU,EAAE,IAAI,CAAC,WAAW,EAAE,WAAW,EAAE,SAAS,CAAC;QACrD,iBAAiB,EAAE,IAAI,CAAC,WAAW,EAAE,WAAW,EAAE,iBAAiB,CAAC;KACpE,CAAC;AACH,CAAC;AAED,MAAM,UAAU,cAAc;IAC7B,IAAI,CAAC;QACJ,MAAM,EAAE,WAAW,EAAE,GAAG,kBAAkB,EAAE,CAAC;QAC7C,MAAM,OAAO,GAAG,IAAI,CAAC,WAAW,EAAE,cAAc,CAAC,CAAC;QAClD,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,CAA4B,CAAC;QAClF,OAAQ,GAAG,CAAC,OAAkB,IAAI,OAAO,CAAC;IAC3C,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,OAAO,CAAC;IAChB,CAAC;AACF,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AASlD,eAAO,MAAM,UAAU,EAAE,MAqIxB,CAAC;AAEF,eAAe,UAAU,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAeH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAQlD,eAAO,MAAM,UAAU,EAAE,MAyMxB,CAAC;AAEF,eAAe,UAAU,CAAC"}
package/dist/index.js CHANGED
@@ -2,18 +2,43 @@
2
2
  * Sage OpenCode plugin.
3
3
  * Intercepts tool calls and uses @gendigital/sage-core to enforce security verdicts.
4
4
  */
5
- import { ApprovalStore, approveAction, loadConfig, resolveBranding } from "@gendigital/sage-core";
5
+ import { dirname, join } from "node:path";
6
+ import { fileURLToPath } from "node:url";
7
+ import { ApprovalStore, approveAction, checkAllowlistMigration, createOperationalLogger, formatAllowlistMigrationWarning, formatConfigurationWarnings, getConfigurationWarnings, loadConfig, resolveBranding, } from "@gendigital/sage-core";
6
8
  import { tool } from "@opencode-ai/plugin/tool";
7
9
  import { getBundledDataDirs } from "./bundled-dirs.js";
8
- import { OpencodeLogger } from "./logger-adaptor.js";
9
10
  import { createSessionScanHandler } from "./startup-scan.js";
10
11
  import { createToolHandlers } from "./tool-handler.js";
11
12
  const APPROVAL_STORE_CLEANUP_INTERVAL_MS = 5 * 60 * 1000; // 5 minutes
12
13
  export const SagePlugin = async ({ client, directory }) => {
13
- const logger = new OpencodeLogger(client);
14
- const config = await loadConfig(undefined, logger);
14
+ const config = await loadConfig();
15
+ const operationalLogger = createOperationalLogger(config.operational_logging, "opencode");
16
+ const logger = operationalLogger.forComponent("plugin");
17
+ const toolLogger = operationalLogger.forComponent("tool-handler");
18
+ const scanLogger = operationalLogger.forComponent("startup-scan");
15
19
  const branding = resolveBranding(config.brand_key, logger);
16
- const { threatsDir, allowlistsDir } = getBundledDataDirs();
20
+ const warningMessage = formatConfigurationWarnings(await getConfigurationWarnings(undefined, logger), branding);
21
+ if (warningMessage) {
22
+ client.tui
23
+ .showToast({
24
+ body: { title: branding.name, message: warningMessage, variant: "warning", duration: 5000 },
25
+ })
26
+ .catch(() => { });
27
+ }
28
+ const allowlistMigration = await checkAllowlistMigration();
29
+ if (allowlistMigration.needed) {
30
+ client.tui
31
+ .showToast({
32
+ body: {
33
+ title: branding.name,
34
+ message: formatAllowlistMigrationWarning(allowlistMigration.entryTypes, branding),
35
+ variant: "warning",
36
+ duration: 8000,
37
+ },
38
+ })
39
+ .catch(() => { });
40
+ }
41
+ const { threatsDir, trustedDomainsDir } = getBundledDataDirs();
17
42
  const approvalStore = new ApprovalStore();
18
43
  // State: track findings per session for one-shot injection
19
44
  const pendingFindingsBySession = new Map();
@@ -24,7 +49,7 @@ export const SagePlugin = async ({ client, directory }) => {
24
49
  approvalStore.cleanup();
25
50
  }, APPROVAL_STORE_CLEANUP_INTERVAL_MS);
26
51
  interval.unref?.();
27
- const toolHandlers = createToolHandlers(logger, approvalStore, threatsDir, allowlistsDir, {
52
+ const toolHandlers = createToolHandlers(toolLogger, approvalStore, threatsDir, trustedDomainsDir, {
28
53
  showToast: (msg, variant) => {
29
54
  client.tui
30
55
  .showToast({
@@ -34,6 +59,30 @@ export const SagePlugin = async ({ client, directory }) => {
34
59
  },
35
60
  }, branding);
36
61
  return {
62
+ /**
63
+ * Register the Sage MCP server so sage_report_false_positive /
64
+ * sage_list_audit_entries are available as agent tools. OpenCode runs
65
+ * config hooks before MCP init, so mutating cfg.mcp here is sufficient.
66
+ * ??= ensures a user-supplied override in opencode.json is never clobbered.
67
+ */
68
+ config: async (cfg) => {
69
+ const dir = dirname(fileURLToPath(import.meta.url));
70
+ const serverPath = join(dir, "mcp-server.cjs");
71
+ // biome-ignore lint/suspicious/noExplicitAny: OpenCode Config.mcp types not exported
72
+ const mcp = cfg.mcp;
73
+ if (mcp?.sage)
74
+ return;
75
+ // biome-ignore lint/suspicious/noExplicitAny: assigning McpLocalConfig without importing its type
76
+ cfg.mcp = {
77
+ ...mcp,
78
+ sage: {
79
+ type: "local",
80
+ command: [process.execPath, serverPath],
81
+ environment: { BUN_BE_BUN: "1", SAGE_AGENT_RUNTIME: "opencode" },
82
+ enabled: true,
83
+ },
84
+ };
85
+ },
37
86
  "tool.execute.before": toolHandlers["tool.execute.before"],
38
87
  /**
39
88
  * Inject plugin scan findings into first user message as <system-reminder>.
@@ -68,8 +117,9 @@ export const SagePlugin = async ({ client, directory }) => {
68
117
  synthetic: true,
69
118
  };
70
119
  message.parts.push(textPart);
71
- logger.info(`Injected sage plugin scan findings to user message`, {
72
- findings,
120
+ logger.debug(`Injected sage plugin scan findings to user message`, {
121
+ sessionID,
122
+ findingsLength: findings.length,
73
123
  });
74
124
  pendingFindingsBySession.delete(sessionID);
75
125
  },
@@ -85,7 +135,7 @@ export const SagePlugin = async ({ client, directory }) => {
85
135
  return;
86
136
  try {
87
137
  logger.debug(`${branding.name}: starting session scan`, { sessionID });
88
- const scanHandler = createSessionScanHandler(logger, directory, (msg) => {
138
+ const scanHandler = createSessionScanHandler(scanLogger, directory, (msg) => {
89
139
  pendingFindingsBySession.set(sessionID, msg);
90
140
  }, branding);
91
141
  await scanHandler();
@@ -101,23 +151,38 @@ export const SagePlugin = async ({ client, directory }) => {
101
151
  }
102
152
  },
103
153
  tool: {
104
- // TODO: After the following PR merged to support client V2 in Opencode Plugin, gitleaks:allow
105
- // use QuestionTools.ask to replace sage_approve tool
106
- // PR: https://github.com/anomalyco/opencode/pull/12046
107
- // Discussion: https://github.com/avast/sage/pull/21#discussion_r2873812399
108
154
  sage_approve: tool({
109
- description: `Approve or reject a ${branding.name}-flagged tool call. IMPORTANT: you MUST ask the user for explicit confirmation in the conversation BEFORE calling this tool. Never auto-approve - always present the flagged action and wait for the user to response.`,
155
+ description: `Review a ${branding.name}-flagged tool call. Shows a native approval dialog to the user. Call this immediately when ${branding.name} flags an action — do NOT ask the user in chat first.`,
110
156
  args: {
111
157
  actionId: tool.schema
112
158
  .string()
113
- .describe(`Action ID from ${branding.name} blocked message`),
114
- approved: tool.schema.boolean().describe("true to approve, false to reject"),
159
+ .describe(`Action ID from ${branding.name} flagged message`),
115
160
  },
116
- async execute(args, _context) {
117
- if (!args.approved) {
161
+ async execute(args, context) {
162
+ const pending = approvalStore.getPending(args.actionId);
163
+ if (!pending) {
164
+ return `No pending ${branding.name} approval found for this action ID.`;
165
+ }
166
+ try {
167
+ // "doom_loop" is the only permission OpenCode always gates with an ask dialog,
168
+ // bypassing wildcard `*: allow` defaults. If OpenCode changes this, the dialog silently stops appearing.
169
+ await context.ask({
170
+ permission: "doom_loop",
171
+ patterns: pending.artifacts.map((a) => `[${a.type}] ${a.value}`),
172
+ always: [],
173
+ metadata: {},
174
+ });
175
+ }
176
+ catch (error) {
177
+ logger.debug(`${branding.name}: approval dialog failed`, {
178
+ actionId: args.actionId,
179
+ error: String(error),
180
+ });
118
181
  approvalStore.deletePending(args.actionId);
182
+ logger.debug("OpenCode approval rejected", { actionId: args.actionId });
119
183
  return "Rejected by user.";
120
184
  }
185
+ logger.info("OpenCode approval accepted", { actionId: args.actionId });
121
186
  return approveAction(approvalStore, args.actionId, branding);
122
187
  },
123
188
  }),
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}