@veris-ai/daytona-opencode 0.1.1 → 0.2.0-rc.1
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/.opencode/plugin/daytona/index.d.ts +11 -0
- package/.opencode/plugin/daytona/index.js +2 -0
- package/.opencode/plugin/daytona/index.js.map +1 -1
- package/.opencode/plugin/daytona/index.ts +2 -0
- package/.opencode/plugin/daytona/plugins/custom-tools.d.ts +9 -0
- package/.opencode/plugin/daytona/plugins/system-transform.js +8 -0
- package/.opencode/plugin/daytona/plugins/system-transform.js.map +1 -1
- package/.opencode/plugin/daytona/plugins/system-transform.ts +8 -0
- package/.opencode/plugin/daytona/plugins/veris-config.d.ts +21 -0
- package/.opencode/plugin/daytona/plugins/veris-config.js +76 -0
- package/.opencode/plugin/daytona/plugins/veris-config.js.map +1 -0
- package/.opencode/plugin/daytona/plugins/veris-config.ts +81 -0
- package/.opencode/plugin/daytona/tools/veris-receipt.d.ts +1 -1
- package/.opencode/plugin/daytona/tools/veris-receipt.js +1 -1
- package/.opencode/plugin/daytona/tools/veris-receipt.ts +1 -1
- package/.opencode/plugin/daytona/tools/veris-twin.d.ts +28 -0
- package/.opencode/plugin/daytona/tools/veris-twin.js +63 -0
- package/.opencode/plugin/daytona/tools/veris-twin.js.map +1 -0
- package/.opencode/plugin/daytona/tools/veris-twin.ts +87 -0
- package/.opencode/plugin/daytona/tools.d.ts +9 -0
- package/.opencode/plugin/daytona/tools.js +2 -0
- package/.opencode/plugin/daytona/tools.js.map +1 -1
- package/.opencode/plugin/daytona/tools.ts +2 -0
- package/README.md +54 -4
- package/package.json +6 -4
|
@@ -3,8 +3,10 @@
|
|
|
3
3
|
* SPDX-License-Identifier: Apache-2.0
|
|
4
4
|
*/
|
|
5
5
|
import type { PluginInput } from '@opencode-ai/plugin';
|
|
6
|
+
import { verisConfig } from './plugins/veris-config';
|
|
6
7
|
export type { EventSessionDeleted, LogLevel, SandboxInfo, SessionInfo, ProjectSessionData } from './core/types';
|
|
7
8
|
declare function daytonaPlugin(ctx: PluginInput): Promise<{
|
|
9
|
+
config: typeof verisConfig;
|
|
8
10
|
tool: {
|
|
9
11
|
bash: {
|
|
10
12
|
description: string;
|
|
@@ -117,6 +119,15 @@ declare function daytonaPlugin(ctx: PluginInput): Promise<{
|
|
|
117
119
|
service?: string;
|
|
118
120
|
}, ctx: import("@opencode-ai/plugin").ToolContext): Promise<string>;
|
|
119
121
|
};
|
|
122
|
+
verisTwin: {
|
|
123
|
+
description: string;
|
|
124
|
+
args: {
|
|
125
|
+
service: import("zod").ZodOptional<import("zod").ZodString>;
|
|
126
|
+
};
|
|
127
|
+
execute(args: {
|
|
128
|
+
service?: string;
|
|
129
|
+
}, ctx: import("@opencode-ai/plugin").ToolContext): Promise<string>;
|
|
130
|
+
};
|
|
120
131
|
};
|
|
121
132
|
event: (args: any) => Promise<void>;
|
|
122
133
|
'experimental.chat.system.transform': (input: import("./core/types").ExperimentalChatSystemTransformInput, output: import("./core/types").ExperimentalChatSystemTransformOutput) => Promise<void>;
|
|
@@ -32,6 +32,7 @@ import { toast } from './core/toast';
|
|
|
32
32
|
import { customTools } from './plugins/custom-tools';
|
|
33
33
|
import { eventHandlers } from './plugins/session-events';
|
|
34
34
|
import { systemPromptTransform } from './plugins/system-transform';
|
|
35
|
+
import { verisConfig } from './plugins/veris-config';
|
|
35
36
|
const xdgDataDir = xdgData ?? join(homedir(), '.local', 'share');
|
|
36
37
|
const LOG_FILE = join(xdgDataDir, 'opencode', 'log', 'daytona.log');
|
|
37
38
|
const STORAGE_DIR = join(xdgDataDir, 'opencode', 'storage', 'daytona');
|
|
@@ -41,6 +42,7 @@ const sessionManager = new DaytonaSessionManager(process.env.DAYTONA_API_KEY ||
|
|
|
41
42
|
async function daytonaPlugin(ctx) {
|
|
42
43
|
toast.initialize(ctx.client?.tui);
|
|
43
44
|
return {
|
|
45
|
+
config: verisConfig,
|
|
44
46
|
tool: await customTools(ctx, sessionManager),
|
|
45
47
|
event: await eventHandlers(ctx, sessionManager, REPO_PATH),
|
|
46
48
|
'experimental.chat.system.transform': await systemPromptTransform(ctx, REPO_PATH),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAC3B,OAAO,EAAE,OAAO,EAAE,MAAM,IAAI,CAAA;AAC5B,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAA;AAErC,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AACtD,OAAO,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAA;AAC9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAA;AAC7D,OAAO,EAAE,KAAK,EAAE,MAAM,cAAc,CAAA;AACpC,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAA;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAA;AACxD,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAA;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAC3B,OAAO,EAAE,OAAO,EAAE,MAAM,IAAI,CAAA;AAC5B,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAA;AAErC,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AACtD,OAAO,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAA;AAC9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAA;AAC7D,OAAO,EAAE,KAAK,EAAE,MAAM,cAAc,CAAA;AACpC,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAA;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAA;AACxD,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAA;AAClE,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAA;AAIpD,MAAM,UAAU,GAAG,OAAO,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAA;AAChE,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,EAAE,UAAU,EAAE,KAAK,EAAE,aAAa,CAAC,CAAA;AACnE,MAAM,WAAW,GAAG,IAAI,CAAC,UAAU,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,CAAC,CAAA;AACtE,MAAM,SAAS,GAAG,uBAAuB,CAAA;AAEzC,cAAc,CAAC,QAAQ,CAAC,CAAA;AACxB,MAAM,cAAc,GAAG,IAAI,qBAAqB,CAC9C,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,EAAE,EACjC,WAAW,EACX,SAAS,EACT,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAC7B,CAAA;AAED,KAAK,UAAU,aAAa,CAAC,GAAgB;IAC3C,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IACjC,OAAO;QACL,MAAM,EAAE,WAAW;QACnB,IAAI,EAAE,MAAM,WAAW,CAAC,GAAG,EAAE,cAAc,CAAC;QAC5C,KAAK,EAAE,MAAM,aAAa,CAAC,GAAG,EAAE,cAAc,EAAE,SAAS,CAAC;QAC1D,oCAAoC,EAAE,MAAM,qBAAqB,CAAC,GAAG,EAAE,SAAS,CAAC;QACjF,mFAAmF;QACnF,iFAAiF;QACjF,iFAAiF;QACjF,iFAAiF;QACjF,OAAO,EAAE,KAAK,IAAI,EAAE;YAClB,MAAM,OAAO,GAAG,MAAM,iBAAiB,CAAC,sBAAsB,CAAC,MAAM,CAAC,CAAA;YACtE,IAAI,CAAC,OAAO,EAAE,CAAC;gBACb,MAAM,CAAC,IAAI,CAAC,iFAAiF,CAAC,CAAA;YAChG,CAAC;QACH,CAAC;KACF,CAAA;AACH,CAAC;AAED,eAAe,aAAa,CAAA"}
|
|
@@ -35,6 +35,7 @@ import { toast } from './core/toast'
|
|
|
35
35
|
import { customTools } from './plugins/custom-tools'
|
|
36
36
|
import { eventHandlers } from './plugins/session-events'
|
|
37
37
|
import { systemPromptTransform } from './plugins/system-transform'
|
|
38
|
+
import { verisConfig } from './plugins/veris-config'
|
|
38
39
|
|
|
39
40
|
export type { EventSessionDeleted, LogLevel, SandboxInfo, SessionInfo, ProjectSessionData } from './core/types'
|
|
40
41
|
|
|
@@ -54,6 +55,7 @@ const sessionManager = new DaytonaSessionManager(
|
|
|
54
55
|
async function daytonaPlugin(ctx: PluginInput) {
|
|
55
56
|
toast.initialize(ctx.client?.tui)
|
|
56
57
|
return {
|
|
58
|
+
config: verisConfig,
|
|
57
59
|
tool: await customTools(ctx, sessionManager),
|
|
58
60
|
event: await eventHandlers(ctx, sessionManager, REPO_PATH),
|
|
59
61
|
'experimental.chat.system.transform': await systemPromptTransform(ctx, REPO_PATH),
|
|
@@ -119,4 +119,13 @@ export declare function customTools(ctx: PluginInput, sessionManager: DaytonaSes
|
|
|
119
119
|
service?: string;
|
|
120
120
|
}, ctx: import("@opencode-ai/plugin").ToolContext): Promise<string>;
|
|
121
121
|
};
|
|
122
|
+
verisTwin: {
|
|
123
|
+
description: string;
|
|
124
|
+
args: {
|
|
125
|
+
service: import("zod").ZodOptional<import("zod").ZodString>;
|
|
126
|
+
};
|
|
127
|
+
execute(args: {
|
|
128
|
+
service?: string;
|
|
129
|
+
}, ctx: import("@opencode-ai/plugin").ToolContext): Promise<string>;
|
|
130
|
+
};
|
|
122
131
|
}>;
|
|
@@ -32,6 +32,14 @@ export async function systemPromptTransform(ctx, repoPath) {
|
|
|
32
32
|
'read what the twin actually received. If the receipt is empty, the change does not',
|
|
33
33
|
'work yet — report that, do not report success. Cite the receipt when you claim an',
|
|
34
34
|
'integration works.',
|
|
35
|
+
'',
|
|
36
|
+
'The twin already exists and this plugin owns its lifecycle: it was created with the',
|
|
37
|
+
'sandbox and is deleted with it. Do NOT create or delete Veris sandboxes. Run the',
|
|
38
|
+
'verisTwin tool to get its id (every Veris MCP tool takes one) and to read a',
|
|
39
|
+
"service's manual before writing code against it. Nothing here runs under",
|
|
40
|
+
'`veris-proxy` — egress is already intercepted at the sandbox boundary, so any',
|
|
41
|
+
'instruction to start a proxy or provision a sandbox is describing a different setup',
|
|
42
|
+
'than this one.',
|
|
35
43
|
].join('\n'));
|
|
36
44
|
};
|
|
37
45
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"system-transform.js","sourceRoot":"","sources":["system-transform.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,GAAgB,EAAE,QAAgB;IAC5E,OAAO,KAAK,EAAE,KAA2C,EAAE,MAA6C,EAAE,EAAE;QAC1G,MAAM,CAAC,MAAM,CAAC,IAAI,CAChB;YACE,gCAAgC;YAChC,oDAAoD;YACpD,8CAA8C,QAAQ,GAAG;YACzD,2CAA2C;YAC3C,gHAAgH;YAChH,+FAA+F;YAC/F,yFAAyF;YACzF,wGAAwG;YACxG,EAAE;YACF,eAAe;YACf,qFAAqF;YACrF,qFAAqF;YACrF,iFAAiF;YACjF,mFAAmF;YACnF,iFAAiF;YACjF,+EAA+E;YAC/E,EAAE;YACF,oFAAoF;YACpF,mFAAmF;YACnF,oFAAoF;YACpF,oFAAoF;YACpF,oFAAoF;YACpF,mFAAmF;YACnF,oBAAoB;
|
|
1
|
+
{"version":3,"file":"system-transform.js","sourceRoot":"","sources":["system-transform.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,GAAgB,EAAE,QAAgB;IAC5E,OAAO,KAAK,EAAE,KAA2C,EAAE,MAA6C,EAAE,EAAE;QAC1G,MAAM,CAAC,MAAM,CAAC,IAAI,CAChB;YACE,gCAAgC;YAChC,oDAAoD;YACpD,8CAA8C,QAAQ,GAAG;YACzD,2CAA2C;YAC3C,gHAAgH;YAChH,+FAA+F;YAC/F,yFAAyF;YACzF,wGAAwG;YACxG,EAAE;YACF,eAAe;YACf,qFAAqF;YACrF,qFAAqF;YACrF,iFAAiF;YACjF,mFAAmF;YACnF,iFAAiF;YACjF,+EAA+E;YAC/E,EAAE;YACF,oFAAoF;YACpF,mFAAmF;YACnF,oFAAoF;YACpF,oFAAoF;YACpF,oFAAoF;YACpF,mFAAmF;YACnF,oBAAoB;YACpB,EAAE;YACF,qFAAqF;YACrF,kFAAkF;YAClF,6EAA6E;YAC7E,0EAA0E;YAC1E,+EAA+E;YAC/E,qFAAqF;YACrF,gBAAgB;SACjB,CAAC,IAAI,CAAC,IAAI,CAAC,CACb,CAAA;IACH,CAAC,CAAA;AACH,CAAC"}
|
|
@@ -37,6 +37,14 @@ export async function systemPromptTransform(ctx: PluginInput, repoPath: string)
|
|
|
37
37
|
'read what the twin actually received. If the receipt is empty, the change does not',
|
|
38
38
|
'work yet — report that, do not report success. Cite the receipt when you claim an',
|
|
39
39
|
'integration works.',
|
|
40
|
+
'',
|
|
41
|
+
'The twin already exists and this plugin owns its lifecycle: it was created with the',
|
|
42
|
+
'sandbox and is deleted with it. Do NOT create or delete Veris sandboxes. Run the',
|
|
43
|
+
'verisTwin tool to get its id (every Veris MCP tool takes one) and to read a',
|
|
44
|
+
"service's manual before writing code against it. Nothing here runs under",
|
|
45
|
+
'`veris-proxy` — egress is already intercepted at the sandbox boundary, so any',
|
|
46
|
+
'instruction to start a proxy or provision a sandbox is describing a different setup',
|
|
47
|
+
'than this one.',
|
|
40
48
|
].join('\n'),
|
|
41
49
|
)
|
|
42
50
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright Veris AI
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*
|
|
5
|
+
* Wires the Veris MCP server into the session, and gates the two of its tools
|
|
6
|
+
* that are always wrong here.
|
|
7
|
+
*
|
|
8
|
+
* The MCP is the shared home for twin LIFECYCLE (get, promote, reset). Keeping
|
|
9
|
+
* it there rather than reimplementing those as plugin tools is what stops a
|
|
10
|
+
* second engine plugin from having to grow the same surface again: the only
|
|
11
|
+
* thing to copy is this file.
|
|
12
|
+
*
|
|
13
|
+
* Everything is `??=`, so a user's own opencode.json always wins and someone who
|
|
14
|
+
* already has `opencode-veris-sim` installed globally gets no double
|
|
15
|
+
* registration. The whole body is wrapped: a failed registration should surface
|
|
16
|
+
* as a missing MCP server, never as OpenCode failing to start.
|
|
17
|
+
*/
|
|
18
|
+
import type { Hooks } from '@opencode-ai/plugin';
|
|
19
|
+
type OpencodeConfig = Parameters<NonNullable<Hooks['config']>>[0];
|
|
20
|
+
export declare function verisConfig(cfg: OpencodeConfig): Promise<void>;
|
|
21
|
+
export {};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright Veris AI
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*
|
|
5
|
+
* Wires the Veris MCP server into the session, and gates the two of its tools
|
|
6
|
+
* that are always wrong here.
|
|
7
|
+
*
|
|
8
|
+
* The MCP is the shared home for twin LIFECYCLE (get, promote, reset). Keeping
|
|
9
|
+
* it there rather than reimplementing those as plugin tools is what stops a
|
|
10
|
+
* second engine plugin from having to grow the same surface again: the only
|
|
11
|
+
* thing to copy is this file.
|
|
12
|
+
*
|
|
13
|
+
* Everything is `??=`, so a user's own opencode.json always wins and someone who
|
|
14
|
+
* already has `opencode-veris-sim` installed globally gets no double
|
|
15
|
+
* registration. The whole body is wrapped: a failed registration should surface
|
|
16
|
+
* as a missing MCP server, never as OpenCode failing to start.
|
|
17
|
+
*/
|
|
18
|
+
import { logger } from '../core/logger';
|
|
19
|
+
/**
|
|
20
|
+
* Defaults for the MCP's destructive tools. The plugin creates and owns this
|
|
21
|
+
* session's twin, which is what makes two of these never the right call:
|
|
22
|
+
*
|
|
23
|
+
* create_sandbox makes a twin the plugin does not know about. Traffic still
|
|
24
|
+
* goes to the plugin's twin and verisReceipt still reads the
|
|
25
|
+
* plugin's twin, so the agent would seed the orphan and then
|
|
26
|
+
* report success — the exact failure the receipt exists to
|
|
27
|
+
* catch, reintroduced one layer up.
|
|
28
|
+
* delete_sandbox destroys the running session's twin. The egress credential
|
|
29
|
+
* dies with it and every later call fails obscurely.
|
|
30
|
+
* promote_sandbox legitimate, but it freezes and scrubs the sandbox AND
|
|
31
|
+
* rewrites what every future run in the environment starts
|
|
32
|
+
* from. That is a human's decision.
|
|
33
|
+
* reset_sandbox legitimate, but it clears /veris/requests, which is the
|
|
34
|
+
* receipt.
|
|
35
|
+
*/
|
|
36
|
+
const TOOL_PERMISSIONS = {
|
|
37
|
+
veris_create_sandbox: 'deny',
|
|
38
|
+
veris_delete_sandbox: 'deny',
|
|
39
|
+
veris_promote_sandbox: 'ask',
|
|
40
|
+
veris_reset_sandbox: 'ask',
|
|
41
|
+
};
|
|
42
|
+
const DEFAULT_API_BASE = 'https://svc.api.veris.ai';
|
|
43
|
+
export async function verisConfig(cfg) {
|
|
44
|
+
try {
|
|
45
|
+
// No key, no registration. Registering with an empty header instead would
|
|
46
|
+
// give a server that fails every call and reads as a Veris outage; the
|
|
47
|
+
// session manager already reports the missing credential properly.
|
|
48
|
+
const apiKey = process.env.VERIS_API_KEY;
|
|
49
|
+
if (!apiKey)
|
|
50
|
+
return;
|
|
51
|
+
cfg.mcp ??= {};
|
|
52
|
+
cfg.mcp.veris ??= {
|
|
53
|
+
type: 'remote',
|
|
54
|
+
url: `${process.env.VERIS_API_BASE || DEFAULT_API_BASE}/mcp`,
|
|
55
|
+
headers: { 'X-API-Key': apiKey },
|
|
56
|
+
// The Veris MCP authenticates by header. Without this, OAuth
|
|
57
|
+
// auto-detection can intercept the connection and prompt for a login
|
|
58
|
+
// that does not exist.
|
|
59
|
+
oauth: false,
|
|
60
|
+
};
|
|
61
|
+
// `permission` may legally be a bare 'ask' | 'allow' | 'deny' that applies
|
|
62
|
+
// to everything; only the object form takes per-tool keys. The bundled SDK
|
|
63
|
+
// types predate both that string form and the arbitrary-tool-name index
|
|
64
|
+
// signature the running config schema documents, hence the assertion.
|
|
65
|
+
if (typeof cfg.permission === 'string')
|
|
66
|
+
return;
|
|
67
|
+
const permission = (cfg.permission ??= {});
|
|
68
|
+
for (const [tool, action] of Object.entries(TOOL_PERMISSIONS)) {
|
|
69
|
+
permission[tool] ??= action;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
catch (err) {
|
|
73
|
+
logger.warn(`[veris] MCP registration skipped: ${err instanceof Error ? err.message : String(err)}`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=veris-config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"veris-config.js","sourceRoot":"","sources":["veris-config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAGH,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAA;AAIvC;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,gBAAgB,GAAmC;IACvD,oBAAoB,EAAE,MAAM;IAC5B,oBAAoB,EAAE,MAAM;IAC5B,qBAAqB,EAAE,KAAK;IAC5B,mBAAmB,EAAE,KAAK;CAC3B,CAAA;AAED,MAAM,gBAAgB,GAAG,0BAA0B,CAAA;AAEnD,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,GAAmB;IACnD,IAAI,CAAC;QACH,0EAA0E;QAC1E,uEAAuE;QACvE,mEAAmE;QACnE,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,aAAa,CAAA;QACxC,IAAI,CAAC,MAAM;YAAE,OAAM;QAEnB,GAAG,CAAC,GAAG,KAAK,EAAE,CAAA;QACd,GAAG,CAAC,GAAG,CAAC,KAAK,KAAK;YAChB,IAAI,EAAE,QAAQ;YACd,GAAG,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,gBAAgB,MAAM;YAC5D,OAAO,EAAE,EAAE,WAAW,EAAE,MAAM,EAAE;YAChC,6DAA6D;YAC7D,qEAAqE;YACrE,uBAAuB;YACvB,KAAK,EAAE,KAAK;SACb,CAAA;QAED,2EAA2E;QAC3E,2EAA2E;QAC3E,wEAAwE;QACxE,sEAAsE;QACtE,IAAI,OAAO,GAAG,CAAC,UAAU,KAAK,QAAQ;YAAE,OAAM;QAC9C,MAAM,UAAU,GAAG,CAAC,GAAG,CAAC,UAAU,KAAK,EAAE,CAA4B,CAAA;QACrE,KAAK,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,gBAAgB,CAAC,EAAE,CAAC;YAC9D,UAAU,CAAC,IAAI,CAAC,KAAK,MAAM,CAAA;QAC7B,CAAC;IACH,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,CAAC,IAAI,CAAC,qCAAqC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;IACtG,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright Veris AI
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*
|
|
5
|
+
* Wires the Veris MCP server into the session, and gates the two of its tools
|
|
6
|
+
* that are always wrong here.
|
|
7
|
+
*
|
|
8
|
+
* The MCP is the shared home for twin LIFECYCLE (get, promote, reset). Keeping
|
|
9
|
+
* it there rather than reimplementing those as plugin tools is what stops a
|
|
10
|
+
* second engine plugin from having to grow the same surface again: the only
|
|
11
|
+
* thing to copy is this file.
|
|
12
|
+
*
|
|
13
|
+
* Everything is `??=`, so a user's own opencode.json always wins and someone who
|
|
14
|
+
* already has `opencode-veris-sim` installed globally gets no double
|
|
15
|
+
* registration. The whole body is wrapped: a failed registration should surface
|
|
16
|
+
* as a missing MCP server, never as OpenCode failing to start.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import type { Hooks } from '@opencode-ai/plugin'
|
|
20
|
+
import { logger } from '../core/logger'
|
|
21
|
+
|
|
22
|
+
type OpencodeConfig = Parameters<NonNullable<Hooks['config']>>[0]
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Defaults for the MCP's destructive tools. The plugin creates and owns this
|
|
26
|
+
* session's twin, which is what makes two of these never the right call:
|
|
27
|
+
*
|
|
28
|
+
* create_sandbox makes a twin the plugin does not know about. Traffic still
|
|
29
|
+
* goes to the plugin's twin and verisReceipt still reads the
|
|
30
|
+
* plugin's twin, so the agent would seed the orphan and then
|
|
31
|
+
* report success — the exact failure the receipt exists to
|
|
32
|
+
* catch, reintroduced one layer up.
|
|
33
|
+
* delete_sandbox destroys the running session's twin. The egress credential
|
|
34
|
+
* dies with it and every later call fails obscurely.
|
|
35
|
+
* promote_sandbox legitimate, but it freezes and scrubs the sandbox AND
|
|
36
|
+
* rewrites what every future run in the environment starts
|
|
37
|
+
* from. That is a human's decision.
|
|
38
|
+
* reset_sandbox legitimate, but it clears /veris/requests, which is the
|
|
39
|
+
* receipt.
|
|
40
|
+
*/
|
|
41
|
+
const TOOL_PERMISSIONS: Record<string, 'ask' | 'deny'> = {
|
|
42
|
+
veris_create_sandbox: 'deny',
|
|
43
|
+
veris_delete_sandbox: 'deny',
|
|
44
|
+
veris_promote_sandbox: 'ask',
|
|
45
|
+
veris_reset_sandbox: 'ask',
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const DEFAULT_API_BASE = 'https://svc.api.veris.ai'
|
|
49
|
+
|
|
50
|
+
export async function verisConfig(cfg: OpencodeConfig): Promise<void> {
|
|
51
|
+
try {
|
|
52
|
+
// No key, no registration. Registering with an empty header instead would
|
|
53
|
+
// give a server that fails every call and reads as a Veris outage; the
|
|
54
|
+
// session manager already reports the missing credential properly.
|
|
55
|
+
const apiKey = process.env.VERIS_API_KEY
|
|
56
|
+
if (!apiKey) return
|
|
57
|
+
|
|
58
|
+
cfg.mcp ??= {}
|
|
59
|
+
cfg.mcp.veris ??= {
|
|
60
|
+
type: 'remote',
|
|
61
|
+
url: `${process.env.VERIS_API_BASE || DEFAULT_API_BASE}/mcp`,
|
|
62
|
+
headers: { 'X-API-Key': apiKey },
|
|
63
|
+
// The Veris MCP authenticates by header. Without this, OAuth
|
|
64
|
+
// auto-detection can intercept the connection and prompt for a login
|
|
65
|
+
// that does not exist.
|
|
66
|
+
oauth: false,
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// `permission` may legally be a bare 'ask' | 'allow' | 'deny' that applies
|
|
70
|
+
// to everything; only the object form takes per-tool keys. The bundled SDK
|
|
71
|
+
// types predate both that string form and the arbitrary-tool-name index
|
|
72
|
+
// signature the running config schema documents, hence the assertion.
|
|
73
|
+
if (typeof cfg.permission === 'string') return
|
|
74
|
+
const permission = (cfg.permission ??= {}) as Record<string, unknown>
|
|
75
|
+
for (const [tool, action] of Object.entries(TOOL_PERMISSIONS)) {
|
|
76
|
+
permission[tool] ??= action
|
|
77
|
+
}
|
|
78
|
+
} catch (err) {
|
|
79
|
+
logger.warn(`[veris] MCP registration skipped: ${err instanceof Error ? err.message : String(err)}`)
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Copyright Veris AI
|
|
3
3
|
* SPDX-License-Identifier: Apache-2.0
|
|
4
4
|
*
|
|
5
|
-
* The
|
|
5
|
+
* The tool this fork exists for.
|
|
6
6
|
*
|
|
7
7
|
* Every other tool tells the agent what its own code did. This one tells it
|
|
8
8
|
* what the VENDOR received — read from the twin, not from the sandbox — which
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Copyright Veris AI
|
|
3
3
|
* SPDX-License-Identifier: Apache-2.0
|
|
4
4
|
*
|
|
5
|
-
* The
|
|
5
|
+
* The tool this fork exists for.
|
|
6
6
|
*
|
|
7
7
|
* Every other tool tells the agent what its own code did. This one tells it
|
|
8
8
|
* what the VENDOR received — read from the twin, not from the sandbox — which
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Copyright Veris AI
|
|
3
3
|
* SPDX-License-Identifier: Apache-2.0
|
|
4
4
|
*
|
|
5
|
-
* The
|
|
5
|
+
* The tool this fork exists for.
|
|
6
6
|
*
|
|
7
7
|
* Every other tool tells the agent what its own code did. This one tells it
|
|
8
8
|
* what the VENDOR received — read from the twin, not from the sandbox — which
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright Veris AI
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*
|
|
5
|
+
* Where the agent finds out which twin is its own.
|
|
6
|
+
*
|
|
7
|
+
* Every tool on the Veris MCP takes a `sandbox_id`, and the twin's id is not
|
|
8
|
+
* something the agent can discover from inside the sandbox — it is held by the
|
|
9
|
+
* session manager on the host, keyed by session. That mapping is the one thing
|
|
10
|
+
* an MCP server cannot know, which is why this is a plugin tool and the
|
|
11
|
+
* lifecycle operations are not.
|
|
12
|
+
*
|
|
13
|
+
* It is also the reachable way in to a service's manual, which is the document
|
|
14
|
+
* Veris expects you to read before designing anything against a twin.
|
|
15
|
+
*/
|
|
16
|
+
import { z } from 'zod';
|
|
17
|
+
import type { PluginInput } from '@opencode-ai/plugin';
|
|
18
|
+
import type { ToolContext } from '@opencode-ai/plugin/tool';
|
|
19
|
+
import type { DaytonaSessionManager } from '../core/session-manager';
|
|
20
|
+
export declare const verisTwinTool: (sessionManager: DaytonaSessionManager, projectId: string, worktree: string, pluginCtx: PluginInput) => {
|
|
21
|
+
description: string;
|
|
22
|
+
args: {
|
|
23
|
+
service: z.ZodOptional<z.ZodString>;
|
|
24
|
+
};
|
|
25
|
+
execute(args: {
|
|
26
|
+
service?: string;
|
|
27
|
+
}, ctx: ToolContext): Promise<string>;
|
|
28
|
+
};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright Veris AI
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*
|
|
5
|
+
* Where the agent finds out which twin is its own.
|
|
6
|
+
*
|
|
7
|
+
* Every tool on the Veris MCP takes a `sandbox_id`, and the twin's id is not
|
|
8
|
+
* something the agent can discover from inside the sandbox — it is held by the
|
|
9
|
+
* session manager on the host, keyed by session. That mapping is the one thing
|
|
10
|
+
* an MCP server cannot know, which is why this is a plugin tool and the
|
|
11
|
+
* lifecycle operations are not.
|
|
12
|
+
*
|
|
13
|
+
* It is also the reachable way in to a service's manual, which is the document
|
|
14
|
+
* Veris expects you to read before designing anything against a twin.
|
|
15
|
+
*/
|
|
16
|
+
import { z } from 'zod';
|
|
17
|
+
import { isVerisSandbox } from '@veris-ai/daytona';
|
|
18
|
+
export const verisTwinTool = (sessionManager, projectId, worktree, pluginCtx) => ({
|
|
19
|
+
description: "Identify this session's Veris twin: its id, and the vendor services it answers for. " +
|
|
20
|
+
'Call it before any Veris MCP tool, which all take a sandbox_id — this is where that id ' +
|
|
21
|
+
'comes from, and creating a new Veris sandbox instead would give you one nothing else ' +
|
|
22
|
+
"in the session is using. Pass a service name to read that service's manual: what the " +
|
|
23
|
+
'twin models and how its data is shaped, which is worth reading before writing code ' +
|
|
24
|
+
'against it.',
|
|
25
|
+
args: {
|
|
26
|
+
service: z
|
|
27
|
+
.string()
|
|
28
|
+
.optional()
|
|
29
|
+
.describe("Service name (e.g. \"stripe\") to read its manual. Omit to list the twin's services."),
|
|
30
|
+
},
|
|
31
|
+
async execute(args, ctx) {
|
|
32
|
+
const sandbox = await sessionManager.getSandbox(ctx.sessionID, projectId, worktree, pluginCtx);
|
|
33
|
+
// Without this OpenCode renders the call as "verisTwin Unknown".
|
|
34
|
+
const title = (t) => ctx.metadata({ title: t });
|
|
35
|
+
if (!isVerisSandbox(sandbox)) {
|
|
36
|
+
title('no twin attached');
|
|
37
|
+
return ('No Veris twin is attached to this sandbox.\n' +
|
|
38
|
+
'Set VERIS_API_KEY and VERIS_ENVIRONMENT_ID and start a new session to get one.');
|
|
39
|
+
}
|
|
40
|
+
const twinId = sandbox.verisSandboxId;
|
|
41
|
+
if (args.service) {
|
|
42
|
+
const manual = await sandbox.veris.manual(args.service);
|
|
43
|
+
title(`${args.service} manual`);
|
|
44
|
+
return `Manual for '${args.service}' (twin ${twinId}):\n\n${manual}`;
|
|
45
|
+
}
|
|
46
|
+
const services = await sandbox.veris.services();
|
|
47
|
+
title(`twin ${twinId} — ${services.length} service(s)`);
|
|
48
|
+
if (services.length === 0) {
|
|
49
|
+
return (`Veris twin ${twinId} answers for no services.\n\n` +
|
|
50
|
+
'Its environment has none configured, so no vendor call from this sandbox will be ' +
|
|
51
|
+
'intercepted. Check VERIS_ENVIRONMENT_ID.');
|
|
52
|
+
}
|
|
53
|
+
const rows = services
|
|
54
|
+
.map((s) => ` ${s.name} (${s.status}) -> ${s.url}`)
|
|
55
|
+
.join('\n');
|
|
56
|
+
return (`Veris twin ${twinId}\n\n` +
|
|
57
|
+
`${services.length} service(s):\n${rows}\n\n` +
|
|
58
|
+
'Pass a service name to this tool to read its manual. Use the twin id above as ' +
|
|
59
|
+
"sandbox_id for Veris MCP tools; do not create a new Veris sandbox — this session's " +
|
|
60
|
+
'twin is created and deleted for you.');
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
//# sourceMappingURL=veris-twin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"veris-twin.js","sourceRoot":"","sources":["veris-twin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAGvB,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAGlD,MAAM,CAAC,MAAM,aAAa,GAAG,CAC3B,cAAqC,EACrC,SAAiB,EACjB,QAAgB,EAChB,SAAsB,EACtB,EAAE,CAAC,CAAC;IACJ,WAAW,EACT,sFAAsF;QACtF,yFAAyF;QACzF,uFAAuF;QACvF,uFAAuF;QACvF,qFAAqF;QACrF,aAAa;IACf,IAAI,EAAE;QACJ,OAAO,EAAE,CAAC;aACP,MAAM,EAAE;aACR,QAAQ,EAAE;aACV,QAAQ,CAAC,sFAAsF,CAAC;KACpG;IACD,KAAK,CAAC,OAAO,CAAC,IAA0B,EAAE,GAAgB;QACxD,MAAM,OAAO,GAAG,MAAM,cAAc,CAAC,UAAU,CAAC,GAAG,CAAC,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAA;QAE9F,iEAAiE;QACjE,MAAM,KAAK,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAA;QAEvD,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,EAAE,CAAC;YAC7B,KAAK,CAAC,kBAAkB,CAAC,CAAA;YACzB,OAAO,CACL,8CAA8C;gBAC9C,gFAAgF,CACjF,CAAA;QACH,CAAC;QAED,MAAM,MAAM,GAAG,OAAO,CAAC,cAAc,CAAA;QAErC,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACjB,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;YACvD,KAAK,CAAC,GAAG,IAAI,CAAC,OAAO,SAAS,CAAC,CAAA;YAC/B,OAAO,eAAe,IAAI,CAAC,OAAO,WAAW,MAAM,SAAS,MAAM,EAAE,CAAA;QACtE,CAAC;QAED,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAA;QAC/C,KAAK,CAAC,QAAQ,MAAM,MAAM,QAAQ,CAAC,MAAM,aAAa,CAAC,CAAA;QAEvD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC1B,OAAO,CACL,cAAc,MAAM,+BAA+B;gBACnD,mFAAmF;gBACnF,0CAA0C,CAC3C,CAAA;QACH,CAAC;QAED,MAAM,IAAI,GAAG,QAAQ;aAClB,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,MAAM,QAAQ,CAAC,CAAC,GAAG,EAAE,CAAC;aACnD,IAAI,CAAC,IAAI,CAAC,CAAA;QAEb,OAAO,CACL,cAAc,MAAM,MAAM;YAC1B,GAAG,QAAQ,CAAC,MAAM,iBAAiB,IAAI,MAAM;YAC7C,gFAAgF;YAChF,qFAAqF;YACrF,sCAAsC,CACvC,CAAA;IACH,CAAC;CACF,CAAC,CAAA"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright Veris AI
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*
|
|
5
|
+
* Where the agent finds out which twin is its own.
|
|
6
|
+
*
|
|
7
|
+
* Every tool on the Veris MCP takes a `sandbox_id`, and the twin's id is not
|
|
8
|
+
* something the agent can discover from inside the sandbox — it is held by the
|
|
9
|
+
* session manager on the host, keyed by session. That mapping is the one thing
|
|
10
|
+
* an MCP server cannot know, which is why this is a plugin tool and the
|
|
11
|
+
* lifecycle operations are not.
|
|
12
|
+
*
|
|
13
|
+
* It is also the reachable way in to a service's manual, which is the document
|
|
14
|
+
* Veris expects you to read before designing anything against a twin.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { z } from 'zod'
|
|
18
|
+
import type { PluginInput } from '@opencode-ai/plugin'
|
|
19
|
+
import type { ToolContext } from '@opencode-ai/plugin/tool'
|
|
20
|
+
import { isVerisSandbox } from '@veris-ai/daytona'
|
|
21
|
+
import type { DaytonaSessionManager } from '../core/session-manager'
|
|
22
|
+
|
|
23
|
+
export const verisTwinTool = (
|
|
24
|
+
sessionManager: DaytonaSessionManager,
|
|
25
|
+
projectId: string,
|
|
26
|
+
worktree: string,
|
|
27
|
+
pluginCtx: PluginInput,
|
|
28
|
+
) => ({
|
|
29
|
+
description:
|
|
30
|
+
"Identify this session's Veris twin: its id, and the vendor services it answers for. " +
|
|
31
|
+
'Call it before any Veris MCP tool, which all take a sandbox_id — this is where that id ' +
|
|
32
|
+
'comes from, and creating a new Veris sandbox instead would give you one nothing else ' +
|
|
33
|
+
"in the session is using. Pass a service name to read that service's manual: what the " +
|
|
34
|
+
'twin models and how its data is shaped, which is worth reading before writing code ' +
|
|
35
|
+
'against it.',
|
|
36
|
+
args: {
|
|
37
|
+
service: z
|
|
38
|
+
.string()
|
|
39
|
+
.optional()
|
|
40
|
+
.describe("Service name (e.g. \"stripe\") to read its manual. Omit to list the twin's services."),
|
|
41
|
+
},
|
|
42
|
+
async execute(args: { service?: string }, ctx: ToolContext) {
|
|
43
|
+
const sandbox = await sessionManager.getSandbox(ctx.sessionID, projectId, worktree, pluginCtx)
|
|
44
|
+
|
|
45
|
+
// Without this OpenCode renders the call as "verisTwin Unknown".
|
|
46
|
+
const title = (t: string) => ctx.metadata({ title: t })
|
|
47
|
+
|
|
48
|
+
if (!isVerisSandbox(sandbox)) {
|
|
49
|
+
title('no twin attached')
|
|
50
|
+
return (
|
|
51
|
+
'No Veris twin is attached to this sandbox.\n' +
|
|
52
|
+
'Set VERIS_API_KEY and VERIS_ENVIRONMENT_ID and start a new session to get one.'
|
|
53
|
+
)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const twinId = sandbox.verisSandboxId
|
|
57
|
+
|
|
58
|
+
if (args.service) {
|
|
59
|
+
const manual = await sandbox.veris.manual(args.service)
|
|
60
|
+
title(`${args.service} manual`)
|
|
61
|
+
return `Manual for '${args.service}' (twin ${twinId}):\n\n${manual}`
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const services = await sandbox.veris.services()
|
|
65
|
+
title(`twin ${twinId} — ${services.length} service(s)`)
|
|
66
|
+
|
|
67
|
+
if (services.length === 0) {
|
|
68
|
+
return (
|
|
69
|
+
`Veris twin ${twinId} answers for no services.\n\n` +
|
|
70
|
+
'Its environment has none configured, so no vendor call from this sandbox will be ' +
|
|
71
|
+
'intercepted. Check VERIS_ENVIRONMENT_ID.'
|
|
72
|
+
)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const rows = services
|
|
76
|
+
.map((s) => ` ${s.name} (${s.status}) -> ${s.url}`)
|
|
77
|
+
.join('\n')
|
|
78
|
+
|
|
79
|
+
return (
|
|
80
|
+
`Veris twin ${twinId}\n\n` +
|
|
81
|
+
`${services.length} service(s):\n${rows}\n\n` +
|
|
82
|
+
'Pass a service name to this tool to read its manual. Use the twin id above as ' +
|
|
83
|
+
"sandbox_id for Veris MCP tools; do not create a new Veris sandbox — this session's " +
|
|
84
|
+
'twin is created and deleted for you.'
|
|
85
|
+
)
|
|
86
|
+
},
|
|
87
|
+
})
|
|
@@ -116,4 +116,13 @@ export declare function createDaytonaTools(sessionManager: DaytonaSessionManager
|
|
|
116
116
|
service?: string;
|
|
117
117
|
}, ctx: import("@opencode-ai/plugin").ToolContext): Promise<string>;
|
|
118
118
|
};
|
|
119
|
+
verisTwin: {
|
|
120
|
+
description: string;
|
|
121
|
+
args: {
|
|
122
|
+
service: import("zod").ZodOptional<import("zod").ZodString>;
|
|
123
|
+
};
|
|
124
|
+
execute(args: {
|
|
125
|
+
service?: string;
|
|
126
|
+
}, ctx: import("@opencode-ai/plugin").ToolContext): Promise<string>;
|
|
127
|
+
};
|
|
119
128
|
};
|
|
@@ -16,6 +16,7 @@ import { grepTool } from './tools/grep';
|
|
|
16
16
|
import { getPreviewURLTool } from './tools/get-preview-url';
|
|
17
17
|
import { gitSyncTool } from './tools/git-sync';
|
|
18
18
|
import { verisReceiptTool } from './tools/veris-receipt';
|
|
19
|
+
import { verisTwinTool } from './tools/veris-twin';
|
|
19
20
|
export function createDaytonaTools(sessionManager, projectId, worktree, pluginCtx) {
|
|
20
21
|
const repoPath = sessionManager.repoPath;
|
|
21
22
|
return {
|
|
@@ -30,6 +31,7 @@ export function createDaytonaTools(sessionManager, projectId, worktree, pluginCt
|
|
|
30
31
|
getPreviewURL: getPreviewURLTool(sessionManager, projectId, worktree, pluginCtx),
|
|
31
32
|
gitSync: gitSyncTool(sessionManager, projectId, worktree, pluginCtx),
|
|
32
33
|
verisReceipt: verisReceiptTool(sessionManager, projectId, worktree, pluginCtx),
|
|
34
|
+
verisTwin: verisTwinTool(sessionManager, projectId, worktree, pluginCtx),
|
|
33
35
|
};
|
|
34
36
|
}
|
|
35
37
|
//# sourceMappingURL=tools.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tools.js","sourceRoot":"","sources":["tools.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;GAEG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AACvC,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AACvC,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAA;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AACjD,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AACnC,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AACvC,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AACvC,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAA;AAC3D,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAC9C,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;
|
|
1
|
+
{"version":3,"file":"tools.js","sourceRoot":"","sources":["tools.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;GAEG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AACvC,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AACvC,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAA;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AACjD,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AACnC,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AACvC,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AACvC,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAA;AAC3D,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAC9C,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAKlD,MAAM,UAAU,kBAAkB,CAChC,cAAqC,EACrC,SAAiB,EACjB,QAAgB,EAChB,SAAsB;IAEtB,MAAM,QAAQ,GAAG,cAAc,CAAC,QAAQ,CAAA;IACxC,OAAO;QACL,IAAI,EAAE,QAAQ,CAAC,cAAc,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,CAAC;QACxE,IAAI,EAAE,QAAQ,CAAC,cAAc,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,CAAC;QAC9D,KAAK,EAAE,SAAS,CAAC,cAAc,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,CAAC;QAChE,IAAI,EAAE,QAAQ,CAAC,cAAc,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,CAAC;QAC9D,SAAS,EAAE,aAAa,CAAC,cAAc,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,CAAC;QACxE,EAAE,EAAE,MAAM,CAAC,cAAc,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,CAAC;QAC1D,IAAI,EAAE,QAAQ,CAAC,cAAc,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,CAAC;QAC9D,IAAI,EAAE,QAAQ,CAAC,cAAc,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,CAAC;QAC9D,aAAa,EAAE,iBAAiB,CAAC,cAAc,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,CAAC;QAChF,OAAO,EAAE,WAAW,CAAC,cAAc,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,CAAC;QACpE,YAAY,EAAE,gBAAgB,CAAC,cAAc,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,CAAC;QAC9E,SAAS,EAAE,aAAa,CAAC,cAAc,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,CAAC;KACzE,CAAA;AACH,CAAC"}
|
|
@@ -18,6 +18,7 @@ import { grepTool } from './tools/grep'
|
|
|
18
18
|
import { getPreviewURLTool } from './tools/get-preview-url'
|
|
19
19
|
import { gitSyncTool } from './tools/git-sync'
|
|
20
20
|
import { verisReceiptTool } from './tools/veris-receipt'
|
|
21
|
+
import { verisTwinTool } from './tools/veris-twin'
|
|
21
22
|
|
|
22
23
|
import type { DaytonaSessionManager } from './core/session-manager'
|
|
23
24
|
import type { PluginInput } from '@opencode-ai/plugin'
|
|
@@ -41,5 +42,6 @@ export function createDaytonaTools(
|
|
|
41
42
|
getPreviewURL: getPreviewURLTool(sessionManager, projectId, worktree, pluginCtx),
|
|
42
43
|
gitSync: gitSyncTool(sessionManager, projectId, worktree, pluginCtx),
|
|
43
44
|
verisReceipt: verisReceiptTool(sessionManager, projectId, worktree, pluginCtx),
|
|
45
|
+
verisTwin: verisTwinTool(sessionManager, projectId, worktree, pluginCtx),
|
|
44
46
|
}
|
|
45
47
|
}
|
package/README.md
CHANGED
|
@@ -25,7 +25,7 @@ Then `opencode`. No image to build, no network configuration, nothing to start.
|
|
|
25
25
|
|
|
26
26
|
The ten sandbox-backed tools from [`@daytona/opencode`](https://www.npmjs.com/package/@daytona/opencode)
|
|
27
27
|
— bash, read, write, edit, multiedit, ls, glob, grep, getPreviewURL, gitSync —
|
|
28
|
-
plus
|
|
28
|
+
plus two, and the Veris MCP server.
|
|
29
29
|
|
|
30
30
|
**`verisReceipt`** reports what the twin *received*.
|
|
31
31
|
|
|
@@ -52,6 +52,41 @@ it. Do not report this change as working.
|
|
|
52
52
|
The system prompt tells the agent to check it before claiming an integration
|
|
53
53
|
works. You can also just ask for it.
|
|
54
54
|
|
|
55
|
+
**`verisTwin`** names the twin and what it answers for — and with a service
|
|
56
|
+
argument, returns that service's manual: what it models and how its data is
|
|
57
|
+
shaped.
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
Veris twin sbx_a1b2c3
|
|
61
|
+
|
|
62
|
+
1 service(s):
|
|
63
|
+
stripe (ready) -> https://gw.api.veris.ai/stripe
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### The Veris MCP
|
|
67
|
+
|
|
68
|
+
The plugin also registers Veris's MCP server, so the agent can manage the twin's
|
|
69
|
+
lifecycle — read the environment, promote a sandbox to the environment's
|
|
70
|
+
baseline, reset it. Nothing to configure: it uses the `VERIS_API_KEY` you
|
|
71
|
+
already set, and is skipped entirely if that is unset.
|
|
72
|
+
|
|
73
|
+
Two of its tools are denied by default, because this plugin creates and owns the
|
|
74
|
+
session's twin:
|
|
75
|
+
|
|
76
|
+
| tool | default | why |
|
|
77
|
+
|---|---|---|
|
|
78
|
+
| `create_sandbox` | `deny` | makes a twin nothing else in the session uses — the agent would seed it and report success while traffic and receipts went elsewhere |
|
|
79
|
+
| `delete_sandbox` | `deny` | destroys the running session's twin |
|
|
80
|
+
| `promote_sandbox` | `ask` | rewrites what every future run in the environment starts from |
|
|
81
|
+
| `reset_sandbox` | `ask` | clears the request log the receipt is read from |
|
|
82
|
+
|
|
83
|
+
Set them yourself in `opencode.json` and your values win; the plugin only fills
|
|
84
|
+
in what you have not.
|
|
85
|
+
|
|
86
|
+
Every one of these calls is made from your machine, never from inside the
|
|
87
|
+
sandbox. The twin's control plane is deliberately unreachable from the sandbox:
|
|
88
|
+
code that could reach `/veris/reset` could erase its own receipt.
|
|
89
|
+
|
|
55
90
|
## Why the sandbox cannot reach the real vendor
|
|
56
91
|
|
|
57
92
|
The sandbox is created with a `domainAllowList` — the vendor hostnames the twin
|
|
@@ -67,11 +102,26 @@ if you want the same thing without an agent.
|
|
|
67
102
|
## Relationship to `@daytona/opencode`
|
|
68
103
|
|
|
69
104
|
This is a fork of `@daytona/opencode` 0.192.0 (Apache-2.0, Copyright Daytona
|
|
70
|
-
Platforms Inc.), and a deliberately small one: one changed import,
|
|
71
|
-
|
|
72
|
-
coordinates are set. The ten inherited tools, the git-sync flow and the session
|
|
105
|
+
Platforms Inc.), and a deliberately small one: one changed import, two added
|
|
106
|
+
tools, a config hook, two paragraphs in the system prompt, and a check that the
|
|
107
|
+
Veris coordinates are set. The ten inherited tools, the git-sync flow and the session
|
|
73
108
|
bookkeeping are untouched, so upstream changes stay easy to take.
|
|
74
109
|
|
|
110
|
+
## Using the veris-sim skills alongside
|
|
111
|
+
|
|
112
|
+
[`opencode-veris-sim`](https://www.npmjs.com/package/opencode-veris-sim) carries
|
|
113
|
+
Veris's reference material on twins — schemas, seeding, faults, webhooks — and
|
|
114
|
+
composes with this plugin:
|
|
115
|
+
|
|
116
|
+
```sh
|
|
117
|
+
opencode plugin opencode-veris-sim -g
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Its `/veris-sim:setup|build|fix` commands describe a different setup: your code
|
|
121
|
+
running on your own machine under `veris-proxy`. In a session backed by this
|
|
122
|
+
plugin the sandbox is the boundary and there is no proxy to start, so those
|
|
123
|
+
commands' steps do not apply. The reference material does.
|
|
124
|
+
|
|
75
125
|
## Limitations
|
|
76
126
|
|
|
77
127
|
- **Requires a Veris control plane that serves an HTTP CONNECT gateway.**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@veris-ai/daytona-opencode",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0-rc.1",
|
|
4
4
|
"description": "Run every OpenCode session in a Daytona sandbox whose vendor API calls are answered by a Veris twin. One line in opencode.json: unmodified code, real hostnames, and a receipt of what the vendor actually received.",
|
|
5
5
|
"main": "./.opencode/plugin/index.js",
|
|
6
6
|
"types": "./.opencode/plugin/index.d.ts",
|
|
@@ -33,10 +33,11 @@
|
|
|
33
33
|
"scripts": {
|
|
34
34
|
"build": "tsc",
|
|
35
35
|
"typecheck": "tsc --noEmit",
|
|
36
|
-
"prepublishOnly": "npm run build"
|
|
36
|
+
"prepublishOnly": "npm run build",
|
|
37
|
+
"test": "vitest run"
|
|
37
38
|
},
|
|
38
39
|
"dependencies": {
|
|
39
|
-
"@veris-ai/daytona": "^0.
|
|
40
|
+
"@veris-ai/daytona": "^0.2.0-rc.1",
|
|
40
41
|
"@daytona/sdk": "^0.204.1",
|
|
41
42
|
"@opencode-ai/plugin": "1.1.59",
|
|
42
43
|
"xdg-basedir": "^5.1.0",
|
|
@@ -44,7 +45,8 @@
|
|
|
44
45
|
},
|
|
45
46
|
"devDependencies": {
|
|
46
47
|
"@types/node": "^22",
|
|
47
|
-
"typescript": "^5.6.0"
|
|
48
|
+
"typescript": "^5.6.0",
|
|
49
|
+
"vitest": "^3.2.7"
|
|
48
50
|
},
|
|
49
51
|
"publishConfig": {
|
|
50
52
|
"access": "public"
|