@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.
- package/README.md +60 -71
- package/dist/bundled-dirs.d.ts +1 -1
- package/dist/bundled-dirs.d.ts.map +1 -1
- package/dist/bundled-dirs.js +1 -1
- package/dist/bundled-dirs.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +83 -18
- package/dist/index.js.map +1 -1
- package/dist/mcp-server.cjs +31776 -0
- package/dist/mcp-server.cjs.map +7 -0
- package/dist/plugin-discovery.js +1 -1
- package/dist/plugin-discovery.js.map +1 -1
- package/dist/startup-scan.d.ts.map +1 -1
- package/dist/startup-scan.js +3 -2
- package/dist/startup-scan.js.map +1 -1
- package/dist/tool-handler.d.ts +1 -1
- package/dist/tool-handler.d.ts.map +1 -1
- package/dist/tool-handler.js +61 -6
- package/dist/tool-handler.js.map +1 -1
- package/package.json +6 -4
- package/resources/threats/agent-layer.yaml +186 -0
- package/resources/threats/commands.yaml +24 -53
- package/resources/threats/credentials.yaml +12 -20
- package/resources/threats/dummy.yaml +22 -33
- package/resources/threats/files.yaml +34 -53
- package/resources/threats/mac-commands.yaml +20 -47
- package/resources/threats/mac-credentials.yaml +4 -12
- package/resources/threats/mac-files.yaml +5 -15
- package/resources/threats/mac-obfuscation.yaml +6 -13
- package/resources/threats/mac-persistence.yaml +11 -20
- package/resources/threats/mac-supply-chain.yaml +5 -8
- package/resources/threats/mitre.yaml +105 -167
- package/resources/threats/obfuscation.yaml +7 -15
- package/resources/threats/persistence.yaml +10 -17
- package/resources/threats/prompt-injection.yaml +94 -44
- package/resources/threats/self-defense.yaml +8 -12
- package/resources/threats/supply_chain.yaml +1 -2
- package/resources/threats/urls.yaml +9 -14
- package/resources/threats/win-commands.yaml +46 -106
- package/resources/threats/win-credentials.yaml +15 -25
- package/resources/threats/win-files.yaml +10 -20
- package/resources/threats/win-obfuscation.yaml +21 -37
- package/resources/threats/win-persistence.yaml +8 -16
- package/resources/threats/win-supply-chain.yaml +2 -3
- package/dist/logger-adaptor.d.ts +0 -15
- package/dist/logger-adaptor.d.ts.map +0 -1
- package/dist/logger-adaptor.js +0 -32
- package/dist/logger-adaptor.js.map +0 -1
- /package/resources/{allowlists → trusted-domains}/trusted-installer-domains.yaml +0 -0
package/README.md
CHANGED
|
@@ -1,102 +1,91 @@
|
|
|
1
|
-
# Sage for
|
|
1
|
+
# Sage — Safety for Agents
|
|
2
2
|
|
|
3
|
-
|
|
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
|
-
|
|
7
|
+
<p align="center">
|
|
8
|
+
Protect your AI coding agent from dangerous commands, malicious URLs, and harmful file operations.
|
|
9
|
+
</p>
|
|
6
10
|
|
|
7
|
-
|
|
8
|
-
- `webfetch` URLs
|
|
9
|
-
- File operations (`read`, `write`, `edit`, `ls`, `glob`, `grep`)
|
|
11
|
+
---
|
|
10
12
|
|
|
11
|
-
|
|
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
|
-
##
|
|
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
|
-
|
|
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
|
-
|
|
35
|
-
- **ask** verdicts: blocked with an explicit `sage_approve` action ID
|
|
36
|
-
- **allow** verdicts: pass through
|
|
22
|
+
## What it protects against
|
|
37
23
|
|
|
38
|
-
|
|
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
|
-
|
|
31
|
+
## Install
|
|
41
32
|
|
|
42
|
-
|
|
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
|
-
```
|
|
47
|
-
|
|
35
|
+
```json
|
|
36
|
+
{
|
|
37
|
+
"plugin": ["@gendigital/sage-opencode"]
|
|
38
|
+
}
|
|
48
39
|
```
|
|
49
40
|
|
|
50
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
47
|
+
Sage hooks into OpenCode's plugin system:
|
|
63
48
|
|
|
64
|
-
- **
|
|
65
|
-
- **
|
|
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
|
-
|
|
55
|
+
## How it works
|
|
68
56
|
|
|
69
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
71
|
+
## Configuration
|
|
87
72
|
|
|
88
|
-
|
|
73
|
+
Sage works out of the box with no configuration. To customize behavior, edit `~/.sage/config.json`:
|
|
89
74
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
75
|
+
```json
|
|
76
|
+
{
|
|
77
|
+
"sensitivity": "balanced",
|
|
78
|
+
"url_check": { "enabled": true },
|
|
79
|
+
"heuristics_enabled": true
|
|
80
|
+
}
|
|
94
81
|
```
|
|
95
82
|
|
|
96
|
-
|
|
83
|
+
See [Configuration](https://github.com/gendigitalinc/sage/blob/main/docs/user-guide.md#configuration) for all options.
|
|
97
84
|
|
|
98
|
-
##
|
|
85
|
+
## Links
|
|
99
86
|
|
|
100
|
-
|
|
101
|
-
|
|
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)
|
package/dist/bundled-dirs.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ export declare function getBundledDataDirs(): {
|
|
|
6
6
|
pluginDir: string;
|
|
7
7
|
packageRoot: string;
|
|
8
8
|
threatsDir: string;
|
|
9
|
-
|
|
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,
|
|
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"}
|
package/dist/bundled-dirs.js
CHANGED
|
@@ -12,7 +12,7 @@ export function getBundledDataDirs() {
|
|
|
12
12
|
pluginDir,
|
|
13
13
|
packageRoot,
|
|
14
14
|
threatsDir: join(packageRoot, "resources", "threats"),
|
|
15
|
-
|
|
15
|
+
trustedDomainsDir: join(packageRoot, "resources", "trusted-domains"),
|
|
16
16
|
};
|
|
17
17
|
}
|
|
18
18
|
export function getSageVersion() {
|
package/dist/bundled-dirs.js.map
CHANGED
|
@@ -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,
|
|
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"}
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;
|
|
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 {
|
|
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
|
|
14
|
-
const
|
|
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
|
|
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(
|
|
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.
|
|
72
|
-
|
|
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(
|
|
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: `
|
|
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}
|
|
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,
|
|
117
|
-
|
|
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":"AAAA;;;GAGG;AAEH,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EACN,aAAa,EACb,aAAa,EACb,uBAAuB,EACvB,uBAAuB,EACvB,+BAA+B,EAC/B,2BAA2B,EAC3B,wBAAwB,EACxB,UAAU,EACV,eAAe,GACf,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EAAE,IAAI,EAAE,MAAM,0BAA0B,CAAC;AAChD,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AAC7D,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAEvD,MAAM,kCAAkC,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,YAAY;AAEtE,MAAM,CAAC,MAAM,UAAU,GAAW,KAAK,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,EAAE,EAAE;IACjE,MAAM,MAAM,GAAG,MAAM,UAAU,EAAE,CAAC;IAClC,MAAM,iBAAiB,GAAG,uBAAuB,CAAC,MAAM,CAAC,mBAAmB,EAAE,UAAU,CAAC,CAAC;IAC1F,MAAM,MAAM,GAAG,iBAAiB,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC;IACxD,MAAM,UAAU,GAAG,iBAAiB,CAAC,YAAY,CAAC,cAAc,CAAC,CAAC;IAClE,MAAM,UAAU,GAAG,iBAAiB,CAAC,YAAY,CAAC,cAAc,CAAC,CAAC;IAClE,MAAM,QAAQ,GAAG,eAAe,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;IAC3D,MAAM,cAAc,GAAG,2BAA2B,CACjD,MAAM,wBAAwB,CAAC,SAAS,EAAE,MAAM,CAAC,EACjD,QAAQ,CACR,CAAC;IACF,IAAI,cAAc,EAAE,CAAC;QACpB,MAAM,CAAC,GAAG;aACR,SAAS,CAAC;YACV,IAAI,EAAE,EAAE,KAAK,EAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,IAAI,EAAE;SAC3F,CAAC;aACD,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IACnB,CAAC;IACD,MAAM,kBAAkB,GAAG,MAAM,uBAAuB,EAAE,CAAC;IAC3D,IAAI,kBAAkB,CAAC,MAAM,EAAE,CAAC;QAC/B,MAAM,CAAC,GAAG;aACR,SAAS,CAAC;YACV,IAAI,EAAE;gBACL,KAAK,EAAE,QAAQ,CAAC,IAAI;gBACpB,OAAO,EAAE,+BAA+B,CAAC,kBAAkB,CAAC,UAAU,EAAE,QAAQ,CAAC;gBACjF,OAAO,EAAE,SAAS;gBAClB,QAAQ,EAAE,IAAI;aACd;SACD,CAAC;aACD,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IACnB,CAAC;IACD,MAAM,EAAE,UAAU,EAAE,iBAAiB,EAAE,GAAG,kBAAkB,EAAE,CAAC;IAC/D,MAAM,aAAa,GAAG,IAAI,aAAa,EAAE,CAAC;IAE1C,2DAA2D;IAC3D,MAAM,wBAAwB,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC3D,mFAAmF;IACnF,MAAM,eAAe,GAAG,IAAI,GAAG,EAAU,CAAC;IAE1C,yDAAyD;IACzD,MAAM,QAAQ,GAAG,WAAW,CAAC,GAAG,EAAE;QACjC,aAAa,CAAC,OAAO,EAAE,CAAC;IACzB,CAAC,EAAE,kCAAkC,CAAC,CAAC;IACvC,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAC;IAEnB,MAAM,YAAY,GAAG,kBAAkB,CACtC,UAAU,EACV,aAAa,EACb,UAAU,EACV,iBAAiB,EACjB;QACC,SAAS,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,EAAE;YAC3B,MAAM,CAAC,GAAG;iBACR,SAAS,CAAC;gBACV,IAAI,EAAE,EAAE,KAAK,EAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE;aACrE,CAAC;iBACD,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QACnB,CAAC;KACD,EACD,QAAQ,CACR,CAAC;IAEF,OAAO;QACN;;;;;WAKG;QACH,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;YACrB,MAAM,GAAG,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;YACpD,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,gBAAgB,CAAC,CAAC;YAC/C,qFAAqF;YACrF,MAAM,GAAG,GAAI,GAAW,CAAC,GAA0C,CAAC;YACpE,IAAI,GAAG,EAAE,IAAI;gBAAE,OAAO;YACtB,kGAAkG;YACjG,GAAW,CAAC,GAAG,GAAG;gBAClB,GAAG,GAAG;gBACN,IAAI,EAAE;oBACL,IAAI,EAAE,OAAO;oBACb,OAAO,EAAE,CAAC,OAAO,CAAC,QAAQ,EAAE,UAAU,CAAC;oBACvC,WAAW,EAAE,EAAE,UAAU,EAAE,GAAG,EAAE,kBAAkB,EAAE,UAAU,EAAE;oBAChE,OAAO,EAAE,IAAI;iBACb;aACD,CAAC;QACH,CAAC;QAED,qBAAqB,EAAE,YAAY,CAAC,qBAAqB,CAAC;QAE1D;;;;;;WAMG;QACH,sCAAsC,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE;YAChE,IAAI,wBAAwB,CAAC,IAAI,KAAK,CAAC;gBAAE,OAAO;YAEhD,MAAM,WAAW,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC;YAC1E,MAAM,OAAO,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC;YAC/B,IAAI,CAAC,OAAO;gBAAE,OAAO;YAErB,MAAM,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC;YACzC,MAAM,QAAQ,GAAG,wBAAwB,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;YACzD,IAAI,CAAC,QAAQ;gBAAE,OAAO;YAEtB,MAAM,QAAQ,GAAG;gBAChB,EAAE,EAAE,MAAM,CAAC,UAAU,EAAE;gBACvB,SAAS;gBACT,SAAS,EAAE,OAAO,CAAC,IAAI,CAAC,EAAE;gBAC1B,IAAI,EAAE,MAAe;gBACrB,IAAI,EAAE;oBACL,mBAAmB;oBACnB,QAAQ;oBACR,EAAE;oBACF,gDAAgD;oBAChD,oBAAoB;iBACpB,CAAC,IAAI,CAAC,IAAI,CAAC;gBACZ,SAAS,EAAE,IAAI;aACf,CAAC;YACF,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YAE7B,MAAM,CAAC,KAAK,CAAC,oDAAoD,EAAE;gBAClE,SAAS;gBACT,cAAc,EAAE,QAAQ,CAAC,MAAM;aAC/B,CAAC,CAAC;YACH,wBAAwB,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QAC5C,CAAC;QAED,mEAAmE;QACnE,uEAAuE;QACvE,KAAK,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE;YAC1B,IAAI,KAAK,CAAC,IAAI,KAAK,iBAAiB;gBAAE,OAAO;YAE7C,mFAAmF;YACnF,MAAM,KAAK,GAAI,KAAa,CAAC,UAAU,CAAC;YACxC,MAAM,SAAS,GAAG,KAAK,EAAE,SAAS,IAAI,KAAK,EAAE,IAAI,EAAE,EAAE,IAAI,SAAS,CAAC;YACnE,IAAI,SAAS,KAAK,SAAS,IAAI,eAAe,CAAC,GAAG,CAAC,SAAS,CAAC;gBAAE,OAAO;YAEtE,IAAI,CAAC;gBACJ,MAAM,CAAC,KAAK,CAAC,GAAG,QAAQ,CAAC,IAAI,yBAAyB,EAAE,EAAE,SAAS,EAAE,CAAC,CAAC;gBACvE,MAAM,WAAW,GAAG,wBAAwB,CAC3C,UAAU,EACV,SAAS,EACT,CAAC,GAAG,EAAE,EAAE;oBACP,wBAAwB,CAAC,GAAG,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;gBAC9C,CAAC,EACD,QAAQ,CACR,CAAC;gBACF,MAAM,WAAW,EAAE,CAAC;gBACpB,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;oBAC7B,eAAe,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;gBAChC,CAAC;YACF,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBAChB,MAAM,CAAC,KAAK,CAAC,GAAG,QAAQ,CAAC,IAAI,kCAAkC,EAAE;oBAChE,SAAS;oBACT,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC;iBACpB,CAAC,CAAC;YACJ,CAAC;QACF,CAAC;QAED,IAAI,EAAE;YACL,YAAY,EAAE,IAAI,CAAC;gBAClB,WAAW,EAAE,YAAY,QAAQ,CAAC,IAAI,8FAA8F,QAAQ,CAAC,IAAI,uDAAuD;gBACxM,IAAI,EAAE;oBACL,QAAQ,EAAE,IAAI,CAAC,MAAM;yBACnB,MAAM,EAAE;yBACR,QAAQ,CAAC,kBAAkB,QAAQ,CAAC,IAAI,kBAAkB,CAAC;iBAC7D;gBACD,KAAK,CAAC,OAAO,CAAC,IAA0B,EAAE,OAAO;oBAChD,MAAM,OAAO,GAAG,aAAa,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;oBACxD,IAAI,CAAC,OAAO,EAAE,CAAC;wBACd,OAAO,cAAc,QAAQ,CAAC,IAAI,qCAAqC,CAAC;oBACzE,CAAC;oBAED,IAAI,CAAC;wBACJ,+EAA+E;wBAC/E,yGAAyG;wBACzG,MAAM,OAAO,CAAC,GAAG,CAAC;4BACjB,UAAU,EAAE,WAAW;4BACvB,QAAQ,EAAE,OAAO,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,KAAK,EAAE,CAAC;4BAChE,MAAM,EAAE,EAAE;4BACV,QAAQ,EAAE,EAAE;yBACZ,CAAC,CAAC;oBACJ,CAAC;oBAAC,OAAO,KAAK,EAAE,CAAC;wBAChB,MAAM,CAAC,KAAK,CAAC,GAAG,QAAQ,CAAC,IAAI,0BAA0B,EAAE;4BACxD,QAAQ,EAAE,IAAI,CAAC,QAAQ;4BACvB,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC;yBACpB,CAAC,CAAC;wBACH,aAAa,CAAC,aAAa,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;wBAC3C,MAAM,CAAC,KAAK,CAAC,4BAA4B,EAAE,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;wBACxE,OAAO,mBAAmB,CAAC;oBAC5B,CAAC;oBACD,MAAM,CAAC,IAAI,CAAC,4BAA4B,EAAE,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;oBAEvE,OAAO,aAAa,CAAC,aAAa,EAAE,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;gBAC9D,CAAC;aACD,CAAC;SACF;KACD,CAAC;AACH,CAAC,CAAC;AAEF,eAAe,UAAU,CAAC"}
|