opencode-agentlens 0.1.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 +88 -0
- package/dist/index.cjs +363 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +20 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.js +343 -0
- package/dist/index.js.map +1 -0
- package/package.json +57 -0
package/README.md
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# opencode-agentlens
|
|
2
|
+
|
|
3
|
+
OpenCode plugin for AgentLens — trace your coding agent's decisions, tool calls, and sessions.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/opencode-agentlens)
|
|
6
|
+
[](https://github.com/repi/agentlens/blob/main/LICENSE)
|
|
7
|
+
|
|
8
|
+
## Requirements
|
|
9
|
+
|
|
10
|
+
- OpenCode >= 1.1.0
|
|
11
|
+
|
|
12
|
+
## Install
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install opencode-agentlens
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Configuration
|
|
19
|
+
|
|
20
|
+
### Environment Variables
|
|
21
|
+
|
|
22
|
+
| Variable | Required | Default | Description |
|
|
23
|
+
|---|---|---|---|
|
|
24
|
+
| `AGENTLENS_API_KEY` | Yes | — | Your AgentLens API key. |
|
|
25
|
+
| `AGENTLENS_ENDPOINT` | No | AgentLens cloud | API endpoint URL. |
|
|
26
|
+
| `AGENTLENS_ENABLED` | No | `true` | Set to `false` to disable tracing. |
|
|
27
|
+
| `AGENTLENS_CAPTURE_CONTENT` | No | `true` | Capture message and tool output content. |
|
|
28
|
+
| `AGENTLENS_MAX_OUTPUT_LENGTH` | No | `10000` | Max characters to capture per output. |
|
|
29
|
+
| `AGENTLENS_FLUSH_INTERVAL` | No | `5000` | Flush interval in milliseconds. |
|
|
30
|
+
| `AGENTLENS_BATCH_SIZE` | No | `100` | Max items per batch before auto-flush. |
|
|
31
|
+
|
|
32
|
+
### OpenCode Setup
|
|
33
|
+
|
|
34
|
+
Add the plugin to your OpenCode configuration at `~/.config/opencode/opencode.json`:
|
|
35
|
+
|
|
36
|
+
```json
|
|
37
|
+
{
|
|
38
|
+
"plugins": [
|
|
39
|
+
{
|
|
40
|
+
"name": "agentlens",
|
|
41
|
+
"module": "opencode-agentlens"
|
|
42
|
+
}
|
|
43
|
+
]
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Set your API key:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
export AGENTLENS_API_KEY="your-api-key"
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
The plugin activates automatically when OpenCode starts. No code changes required.
|
|
54
|
+
|
|
55
|
+
## What Gets Captured
|
|
56
|
+
|
|
57
|
+
The plugin hooks into OpenCode's event system and records:
|
|
58
|
+
|
|
59
|
+
- **Sessions** — Full session lifecycle from start to finish, including duration and metadata.
|
|
60
|
+
- **Tool calls** — Every tool invocation with input arguments and output results (e.g., file reads, shell commands, code edits).
|
|
61
|
+
- **LLM calls** — Chat messages sent to and received from the model, including token usage.
|
|
62
|
+
- **Permission flows** — When the agent requests permission and whether it was granted or denied.
|
|
63
|
+
- **File edits** — File paths and change summaries produced by the agent.
|
|
64
|
+
|
|
65
|
+
All data is sent to your AgentLens instance where you can inspect traces, replay sessions, and analyze agent behavior.
|
|
66
|
+
|
|
67
|
+
## How It Works
|
|
68
|
+
|
|
69
|
+
The plugin registers handlers for OpenCode's event hooks:
|
|
70
|
+
|
|
71
|
+
| Event | What is recorded |
|
|
72
|
+
|---|---|
|
|
73
|
+
| Session start/end | Trace lifecycle, session metadata |
|
|
74
|
+
| `tool.execute.before` | Tool name, input arguments |
|
|
75
|
+
| `tool.execute.after` | Tool output, duration, success/failure |
|
|
76
|
+
| `chat.message` | LLM responses and assistant messages |
|
|
77
|
+
| `chat.params` | Model parameters and prompt configuration |
|
|
78
|
+
| `permission.ask` | Permission requests and user decisions |
|
|
79
|
+
|
|
80
|
+
Each OpenCode session maps to a single AgentLens trace. Tool calls and LLM interactions become spans within that trace.
|
|
81
|
+
|
|
82
|
+
## Documentation
|
|
83
|
+
|
|
84
|
+
Full documentation: [agentlens.vectry.tech/docs/opencode-plugin](https://agentlens.vectry.tech/docs/opencode-plugin)
|
|
85
|
+
|
|
86
|
+
## License
|
|
87
|
+
|
|
88
|
+
MIT
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
AgentLensPlugin: () => plugin,
|
|
24
|
+
default: () => index_default,
|
|
25
|
+
loadConfig: () => loadConfig
|
|
26
|
+
});
|
|
27
|
+
module.exports = __toCommonJS(index_exports);
|
|
28
|
+
var import_agentlens_sdk2 = require("agentlens-sdk");
|
|
29
|
+
|
|
30
|
+
// src/config.ts
|
|
31
|
+
function loadConfig() {
|
|
32
|
+
const apiKey = process.env["AGENTLENS_API_KEY"] ?? "";
|
|
33
|
+
if (!apiKey) {
|
|
34
|
+
console.warn(
|
|
35
|
+
"[agentlens] AGENTLENS_API_KEY not set \u2014 plugin will be disabled"
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
apiKey,
|
|
40
|
+
endpoint: process.env["AGENTLENS_ENDPOINT"] ?? "https://agentlens.vectry.tech",
|
|
41
|
+
enabled: (process.env["AGENTLENS_ENABLED"] ?? "true") === "true",
|
|
42
|
+
captureContent: (process.env["AGENTLENS_CAPTURE_CONTENT"] ?? "false") === "true",
|
|
43
|
+
maxOutputLength: parseInt(
|
|
44
|
+
process.env["AGENTLENS_MAX_OUTPUT_LENGTH"] ?? "2000",
|
|
45
|
+
10
|
|
46
|
+
),
|
|
47
|
+
flushInterval: parseInt(
|
|
48
|
+
process.env["AGENTLENS_FLUSH_INTERVAL"] ?? "5000",
|
|
49
|
+
10
|
|
50
|
+
),
|
|
51
|
+
maxBatchSize: parseInt(process.env["AGENTLENS_BATCH_SIZE"] ?? "10", 10)
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// src/state.ts
|
|
56
|
+
var import_agentlens_sdk = require("agentlens-sdk");
|
|
57
|
+
|
|
58
|
+
// src/utils.ts
|
|
59
|
+
function truncate(str, maxLength) {
|
|
60
|
+
if (str.length <= maxLength) return str;
|
|
61
|
+
return str.slice(0, maxLength) + "... [truncated]";
|
|
62
|
+
}
|
|
63
|
+
function extractToolMetadata(tool, args) {
|
|
64
|
+
const a = args;
|
|
65
|
+
if (!a || typeof a !== "object") return {};
|
|
66
|
+
switch (tool) {
|
|
67
|
+
case "read":
|
|
68
|
+
case "mcp_read":
|
|
69
|
+
return { filePath: a["filePath"] };
|
|
70
|
+
case "write":
|
|
71
|
+
case "mcp_write":
|
|
72
|
+
return { filePath: a["filePath"] };
|
|
73
|
+
case "edit":
|
|
74
|
+
case "mcp_edit":
|
|
75
|
+
return { filePath: a["filePath"] };
|
|
76
|
+
case "bash":
|
|
77
|
+
case "mcp_bash":
|
|
78
|
+
return {
|
|
79
|
+
command: truncate(String(a["command"] ?? ""), 200)
|
|
80
|
+
};
|
|
81
|
+
case "glob":
|
|
82
|
+
case "mcp_glob":
|
|
83
|
+
return { pattern: a["pattern"] };
|
|
84
|
+
case "grep":
|
|
85
|
+
case "mcp_grep":
|
|
86
|
+
return { pattern: a["pattern"], path: a["path"] };
|
|
87
|
+
case "task":
|
|
88
|
+
case "mcp_task":
|
|
89
|
+
return {
|
|
90
|
+
category: a["category"],
|
|
91
|
+
description: a["description"]
|
|
92
|
+
};
|
|
93
|
+
default:
|
|
94
|
+
return {};
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
function safeJsonValue(value) {
|
|
98
|
+
if (value === null || value === void 0) return null;
|
|
99
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
100
|
+
return value;
|
|
101
|
+
}
|
|
102
|
+
if (Array.isArray(value)) {
|
|
103
|
+
return value.map((v) => safeJsonValue(v));
|
|
104
|
+
}
|
|
105
|
+
if (typeof value === "object") {
|
|
106
|
+
const result = {};
|
|
107
|
+
for (const [k, v] of Object.entries(value)) {
|
|
108
|
+
result[k] = safeJsonValue(v);
|
|
109
|
+
}
|
|
110
|
+
return result;
|
|
111
|
+
}
|
|
112
|
+
return String(value);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// src/state.ts
|
|
116
|
+
var SessionState = class {
|
|
117
|
+
traces = /* @__PURE__ */ new Map();
|
|
118
|
+
toolCalls = /* @__PURE__ */ new Map();
|
|
119
|
+
rootSpans = /* @__PURE__ */ new Map();
|
|
120
|
+
startSession(sessionId, metadata) {
|
|
121
|
+
const trace = new import_agentlens_sdk.TraceBuilder("opencode-session", {
|
|
122
|
+
sessionId,
|
|
123
|
+
tags: ["opencode", "coding-agent"],
|
|
124
|
+
metadata: metadata ? safeJsonValue(metadata) : void 0
|
|
125
|
+
});
|
|
126
|
+
const rootSpanId = trace.addSpan({
|
|
127
|
+
name: "session",
|
|
128
|
+
type: import_agentlens_sdk.SpanType.AGENT,
|
|
129
|
+
startedAt: (0, import_agentlens_sdk.nowISO)(),
|
|
130
|
+
metadata: metadata ? safeJsonValue(metadata) : void 0
|
|
131
|
+
});
|
|
132
|
+
this.traces.set(sessionId, trace);
|
|
133
|
+
this.rootSpans.set(sessionId, rootSpanId);
|
|
134
|
+
return trace;
|
|
135
|
+
}
|
|
136
|
+
getTrace(sessionId) {
|
|
137
|
+
return this.traces.get(sessionId);
|
|
138
|
+
}
|
|
139
|
+
endSession(sessionId, status) {
|
|
140
|
+
const trace = this.traces.get(sessionId);
|
|
141
|
+
if (!trace) return;
|
|
142
|
+
const rootSpanId = this.rootSpans.get(sessionId);
|
|
143
|
+
if (rootSpanId) {
|
|
144
|
+
trace.addSpan({
|
|
145
|
+
id: rootSpanId,
|
|
146
|
+
name: "session",
|
|
147
|
+
type: import_agentlens_sdk.SpanType.AGENT,
|
|
148
|
+
status: status === "ERROR" ? import_agentlens_sdk.SpanStatus.ERROR : import_agentlens_sdk.SpanStatus.COMPLETED,
|
|
149
|
+
endedAt: (0, import_agentlens_sdk.nowISO)()
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
trace.end({ status: status ?? "COMPLETED" });
|
|
153
|
+
this.traces.delete(sessionId);
|
|
154
|
+
this.rootSpans.delete(sessionId);
|
|
155
|
+
}
|
|
156
|
+
startToolCall(callID, tool, args, sessionID) {
|
|
157
|
+
this.toolCalls.set(callID, {
|
|
158
|
+
startTime: Date.now(),
|
|
159
|
+
tool,
|
|
160
|
+
args,
|
|
161
|
+
sessionID
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
endToolCall(callID, output, title, metadata) {
|
|
165
|
+
const call = this.toolCalls.get(callID);
|
|
166
|
+
if (!call) return;
|
|
167
|
+
this.toolCalls.delete(callID);
|
|
168
|
+
const trace = this.traces.get(call.sessionID);
|
|
169
|
+
if (!trace) return;
|
|
170
|
+
const durationMs = Date.now() - call.startTime;
|
|
171
|
+
const rootSpanId = this.rootSpans.get(call.sessionID);
|
|
172
|
+
const toolMeta = extractToolMetadata(call.tool, call.args);
|
|
173
|
+
trace.addSpan({
|
|
174
|
+
name: title,
|
|
175
|
+
type: import_agentlens_sdk.SpanType.TOOL_CALL,
|
|
176
|
+
parentSpanId: rootSpanId,
|
|
177
|
+
input: safeJsonValue(call.args),
|
|
178
|
+
output,
|
|
179
|
+
durationMs,
|
|
180
|
+
status: import_agentlens_sdk.SpanStatus.COMPLETED,
|
|
181
|
+
startedAt: new Date(call.startTime).toISOString(),
|
|
182
|
+
endedAt: (0, import_agentlens_sdk.nowISO)(),
|
|
183
|
+
metadata: safeJsonValue({ ...toolMeta, rawMetadata: metadata })
|
|
184
|
+
});
|
|
185
|
+
trace.addDecision({
|
|
186
|
+
type: import_agentlens_sdk.DecisionType.TOOL_SELECTION,
|
|
187
|
+
chosen: call.tool,
|
|
188
|
+
alternatives: [],
|
|
189
|
+
reasoning: title,
|
|
190
|
+
durationMs,
|
|
191
|
+
parentSpanId: rootSpanId
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
recordLLMCall(sessionId, options) {
|
|
195
|
+
const trace = this.traces.get(sessionId);
|
|
196
|
+
if (!trace) return;
|
|
197
|
+
const rootSpanId = this.rootSpans.get(sessionId);
|
|
198
|
+
const agentName = options.agent ?? "assistant";
|
|
199
|
+
const modelName = options.model?.modelID ?? "unknown";
|
|
200
|
+
trace.addSpan({
|
|
201
|
+
name: `${agentName} \u2192 ${modelName}`,
|
|
202
|
+
type: import_agentlens_sdk.SpanType.LLM_CALL,
|
|
203
|
+
parentSpanId: rootSpanId,
|
|
204
|
+
status: import_agentlens_sdk.SpanStatus.COMPLETED,
|
|
205
|
+
startedAt: (0, import_agentlens_sdk.nowISO)(),
|
|
206
|
+
endedAt: (0, import_agentlens_sdk.nowISO)(),
|
|
207
|
+
metadata: safeJsonValue({
|
|
208
|
+
provider: options.model?.providerID,
|
|
209
|
+
model: options.model?.modelID,
|
|
210
|
+
agent: options.agent,
|
|
211
|
+
messageID: options.messageID
|
|
212
|
+
})
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
recordPermission(sessionId, permission, status) {
|
|
216
|
+
const trace = this.traces.get(sessionId);
|
|
217
|
+
if (!trace) return;
|
|
218
|
+
const rootSpanId = this.rootSpans.get(sessionId);
|
|
219
|
+
const p = permission;
|
|
220
|
+
const title = p?.["title"] ?? "permission";
|
|
221
|
+
const permType = p?.["type"] ?? "unknown";
|
|
222
|
+
trace.addDecision({
|
|
223
|
+
type: import_agentlens_sdk.DecisionType.ESCALATION,
|
|
224
|
+
chosen: safeJsonValue({ action: status }),
|
|
225
|
+
alternatives: [
|
|
226
|
+
"allow",
|
|
227
|
+
"deny",
|
|
228
|
+
"ask"
|
|
229
|
+
],
|
|
230
|
+
reasoning: `${permType}: ${title}`,
|
|
231
|
+
parentSpanId: rootSpanId
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
getRootSpanId(sessionId) {
|
|
235
|
+
return this.rootSpans.get(sessionId);
|
|
236
|
+
}
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
// src/index.ts
|
|
240
|
+
var plugin = async ({ project, directory, worktree }) => {
|
|
241
|
+
const config = loadConfig();
|
|
242
|
+
if (!config.enabled || !config.apiKey) {
|
|
243
|
+
console.log("[agentlens] Plugin disabled \u2014 missing AGENTLENS_API_KEY");
|
|
244
|
+
return {};
|
|
245
|
+
}
|
|
246
|
+
(0, import_agentlens_sdk2.init)({
|
|
247
|
+
apiKey: config.apiKey,
|
|
248
|
+
endpoint: config.endpoint,
|
|
249
|
+
flushInterval: config.flushInterval,
|
|
250
|
+
maxBatchSize: config.maxBatchSize
|
|
251
|
+
});
|
|
252
|
+
const state = new SessionState();
|
|
253
|
+
return {
|
|
254
|
+
event: async ({ event }) => {
|
|
255
|
+
const type = event.type;
|
|
256
|
+
const props = event.properties;
|
|
257
|
+
if (type === "session.created" && props?.["id"]) {
|
|
258
|
+
state.startSession(String(props["id"]), {
|
|
259
|
+
project: project.id,
|
|
260
|
+
directory,
|
|
261
|
+
worktree
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
if (type === "session.idle") {
|
|
265
|
+
const sessionId = props?.["sessionID"] ?? props?.["id"];
|
|
266
|
+
if (sessionId) await (0, import_agentlens_sdk2.flush)();
|
|
267
|
+
}
|
|
268
|
+
if (type === "session.error") {
|
|
269
|
+
const sessionId = String(props?.["sessionID"] ?? props?.["id"] ?? "");
|
|
270
|
+
if (sessionId) {
|
|
271
|
+
const trace = state.getTrace(sessionId);
|
|
272
|
+
if (trace) {
|
|
273
|
+
trace.addEvent({
|
|
274
|
+
type: import_agentlens_sdk2.EventType.ERROR,
|
|
275
|
+
name: String(props?.["error"] ?? "session error"),
|
|
276
|
+
metadata: safeJsonValue(props)
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
if (type === "session.deleted") {
|
|
282
|
+
const sessionId = String(props?.["sessionID"] ?? props?.["id"] ?? "");
|
|
283
|
+
if (sessionId) state.endSession(sessionId);
|
|
284
|
+
}
|
|
285
|
+
if (type === "session.diff") {
|
|
286
|
+
const sessionId = String(props?.["sessionID"] ?? props?.["id"] ?? "");
|
|
287
|
+
if (sessionId) {
|
|
288
|
+
const trace = state.getTrace(sessionId);
|
|
289
|
+
if (trace) {
|
|
290
|
+
trace.setMetadata({
|
|
291
|
+
diff: truncate(String(props?.["diff"] ?? ""), 5e3)
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
if (type === "file.edited") {
|
|
297
|
+
const sessionId = String(props?.["sessionID"] ?? props?.["id"] ?? "");
|
|
298
|
+
const trace = sessionId ? state.getTrace(sessionId) : void 0;
|
|
299
|
+
if (trace) {
|
|
300
|
+
trace.addEvent({
|
|
301
|
+
type: import_agentlens_sdk2.EventType.CUSTOM,
|
|
302
|
+
name: "file.edited",
|
|
303
|
+
metadata: safeJsonValue({
|
|
304
|
+
filePath: props?.["filePath"]
|
|
305
|
+
})
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
},
|
|
310
|
+
"tool.execute.before": async (input, output) => {
|
|
311
|
+
state.startToolCall(
|
|
312
|
+
input.callID,
|
|
313
|
+
input.tool,
|
|
314
|
+
output.args,
|
|
315
|
+
input.sessionID
|
|
316
|
+
);
|
|
317
|
+
},
|
|
318
|
+
"tool.execute.after": async (input, output) => {
|
|
319
|
+
state.endToolCall(
|
|
320
|
+
input.callID,
|
|
321
|
+
truncate(output.output ?? "", config.maxOutputLength),
|
|
322
|
+
output.title ?? input.tool,
|
|
323
|
+
output.metadata
|
|
324
|
+
);
|
|
325
|
+
},
|
|
326
|
+
"chat.message": async (input) => {
|
|
327
|
+
if (input.model) {
|
|
328
|
+
state.recordLLMCall(input.sessionID, {
|
|
329
|
+
model: input.model,
|
|
330
|
+
agent: input.agent,
|
|
331
|
+
messageID: input.messageID
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
},
|
|
335
|
+
"chat.params": async (input, output) => {
|
|
336
|
+
const trace = state.getTrace(input.sessionID);
|
|
337
|
+
if (trace) {
|
|
338
|
+
trace.addEvent({
|
|
339
|
+
type: import_agentlens_sdk2.EventType.CUSTOM,
|
|
340
|
+
name: "chat.params",
|
|
341
|
+
metadata: safeJsonValue({
|
|
342
|
+
agent: input.agent,
|
|
343
|
+
model: input.model.id,
|
|
344
|
+
provider: input.provider.info.id,
|
|
345
|
+
temperature: output.temperature,
|
|
346
|
+
topP: output.topP,
|
|
347
|
+
topK: output.topK
|
|
348
|
+
})
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
},
|
|
352
|
+
"permission.ask": async (input, output) => {
|
|
353
|
+
state.recordPermission(input.sessionID, input, output.status);
|
|
354
|
+
}
|
|
355
|
+
};
|
|
356
|
+
};
|
|
357
|
+
var index_default = plugin;
|
|
358
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
359
|
+
0 && (module.exports = {
|
|
360
|
+
AgentLensPlugin,
|
|
361
|
+
loadConfig
|
|
362
|
+
});
|
|
363
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/config.ts","../src/state.ts","../src/utils.ts"],"sourcesContent":["import type { Plugin } from \"@opencode-ai/plugin\";\nimport type { JsonValue } from \"agentlens-sdk\";\nimport { init, flush, EventType as EventTypeValues } from \"agentlens-sdk\";\nimport { loadConfig } from \"./config.js\";\nimport { SessionState } from \"./state.js\";\nimport { truncate, safeJsonValue } from \"./utils.js\";\n\nconst plugin: Plugin = async ({ project, directory, worktree }) => {\n const config = loadConfig();\n\n if (!config.enabled || !config.apiKey) {\n console.log(\"[agentlens] Plugin disabled — missing AGENTLENS_API_KEY\");\n return {};\n }\n\n init({\n apiKey: config.apiKey,\n endpoint: config.endpoint,\n flushInterval: config.flushInterval,\n maxBatchSize: config.maxBatchSize,\n });\n\n const state = new SessionState();\n\n return {\n event: async ({ event }) => {\n const type = event.type;\n const props = (event as Record<string, unknown>).properties as\n | Record<string, unknown>\n | undefined;\n\n if (type === \"session.created\" && props?.[\"id\"]) {\n state.startSession(String(props[\"id\"]), {\n project: project.id,\n directory,\n worktree,\n });\n }\n\n if (type === \"session.idle\") {\n const sessionId = props?.[\"sessionID\"] ?? props?.[\"id\"];\n if (sessionId) await flush();\n }\n\n if (type === \"session.error\") {\n const sessionId = String(props?.[\"sessionID\"] ?? props?.[\"id\"] ?? \"\");\n if (sessionId) {\n const trace = state.getTrace(sessionId);\n if (trace) {\n trace.addEvent({\n type: EventTypeValues.ERROR,\n name: String(props?.[\"error\"] ?? \"session error\"),\n metadata: safeJsonValue(props) as JsonValue,\n });\n }\n }\n }\n\n if (type === \"session.deleted\") {\n const sessionId = String(props?.[\"sessionID\"] ?? props?.[\"id\"] ?? \"\");\n if (sessionId) state.endSession(sessionId);\n }\n\n if (type === \"session.diff\") {\n const sessionId = String(props?.[\"sessionID\"] ?? props?.[\"id\"] ?? \"\");\n if (sessionId) {\n const trace = state.getTrace(sessionId);\n if (trace) {\n trace.setMetadata({\n diff: truncate(String(props?.[\"diff\"] ?? \"\"), 5000),\n });\n }\n }\n }\n\n if (type === \"file.edited\") {\n const sessionId = String(props?.[\"sessionID\"] ?? props?.[\"id\"] ?? \"\");\n const trace = sessionId ? state.getTrace(sessionId) : undefined;\n if (trace) {\n trace.addEvent({\n type: EventTypeValues.CUSTOM,\n name: \"file.edited\",\n metadata: safeJsonValue({\n filePath: props?.[\"filePath\"],\n }) as JsonValue,\n });\n }\n }\n },\n\n \"tool.execute.before\": async (input, output) => {\n state.startToolCall(\n input.callID,\n input.tool,\n output.args as unknown,\n input.sessionID,\n );\n },\n\n \"tool.execute.after\": async (input, output) => {\n state.endToolCall(\n input.callID,\n truncate(output.output ?? \"\", config.maxOutputLength),\n output.title ?? input.tool,\n output.metadata as unknown,\n );\n },\n\n \"chat.message\": async (input) => {\n if (input.model) {\n state.recordLLMCall(input.sessionID, {\n model: input.model,\n agent: input.agent,\n messageID: input.messageID,\n });\n }\n },\n\n \"chat.params\": async (input, output) => {\n const trace = state.getTrace(input.sessionID);\n if (trace) {\n trace.addEvent({\n type: EventTypeValues.CUSTOM,\n name: \"chat.params\",\n metadata: safeJsonValue({\n agent: input.agent,\n model: input.model.id,\n provider: input.provider.info.id,\n temperature: output.temperature,\n topP: output.topP,\n topK: output.topK,\n }) as JsonValue,\n });\n }\n },\n\n \"permission.ask\": async (input, output) => {\n state.recordPermission(input.sessionID, input, output.status);\n },\n };\n};\n\nexport default plugin;\nexport { plugin as AgentLensPlugin };\nexport type { PluginConfig } from \"./config.js\";\nexport { loadConfig } from \"./config.js\";\n","export interface PluginConfig {\n apiKey: string;\n endpoint: string;\n enabled: boolean;\n /** Opt-in: capture full message content in traces */\n captureContent: boolean;\n /** Maximum characters for tool output before truncation */\n maxOutputLength: number;\n /** Milliseconds between automatic flushes */\n flushInterval: number;\n /** Maximum traces per batch */\n maxBatchSize: number;\n}\n\nexport function loadConfig(): PluginConfig {\n const apiKey = process.env[\"AGENTLENS_API_KEY\"] ?? \"\";\n\n if (!apiKey) {\n console.warn(\n \"[agentlens] AGENTLENS_API_KEY not set — plugin will be disabled\",\n );\n }\n\n return {\n apiKey,\n endpoint:\n process.env[\"AGENTLENS_ENDPOINT\"] ?? \"https://agentlens.vectry.tech\",\n enabled: (process.env[\"AGENTLENS_ENABLED\"] ?? \"true\") === \"true\",\n captureContent:\n (process.env[\"AGENTLENS_CAPTURE_CONTENT\"] ?? \"false\") === \"true\",\n maxOutputLength: parseInt(\n process.env[\"AGENTLENS_MAX_OUTPUT_LENGTH\"] ?? \"2000\",\n 10,\n ),\n flushInterval: parseInt(\n process.env[\"AGENTLENS_FLUSH_INTERVAL\"] ?? \"5000\",\n 10,\n ),\n maxBatchSize: parseInt(process.env[\"AGENTLENS_BATCH_SIZE\"] ?? \"10\", 10),\n };\n}\n","import {\n TraceBuilder,\n SpanType,\n SpanStatus,\n DecisionType,\n nowISO,\n} from \"agentlens-sdk\";\nimport type { JsonValue, TraceStatus } from \"agentlens-sdk\";\nimport { extractToolMetadata, safeJsonValue } from \"./utils.js\";\n\ninterface ToolCallState {\n startTime: number;\n tool: string;\n args: unknown;\n sessionID: string;\n}\n\nexport class SessionState {\n private traces = new Map<string, TraceBuilder>();\n private toolCalls = new Map<string, ToolCallState>();\n private rootSpans = new Map<string, string>();\n\n startSession(\n sessionId: string,\n metadata?: Record<string, unknown>,\n ): TraceBuilder {\n const trace = new TraceBuilder(\"opencode-session\", {\n sessionId,\n tags: [\"opencode\", \"coding-agent\"],\n metadata: metadata ? (safeJsonValue(metadata) as JsonValue) : undefined,\n });\n\n const rootSpanId = trace.addSpan({\n name: \"session\",\n type: SpanType.AGENT,\n startedAt: nowISO(),\n metadata: metadata ? (safeJsonValue(metadata) as JsonValue) : undefined,\n });\n\n this.traces.set(sessionId, trace);\n this.rootSpans.set(sessionId, rootSpanId);\n return trace;\n }\n\n getTrace(sessionId: string): TraceBuilder | undefined {\n return this.traces.get(sessionId);\n }\n\n endSession(sessionId: string, status?: TraceStatus): void {\n const trace = this.traces.get(sessionId);\n if (!trace) return;\n\n const rootSpanId = this.rootSpans.get(sessionId);\n if (rootSpanId) {\n trace.addSpan({\n id: rootSpanId,\n name: \"session\",\n type: SpanType.AGENT,\n status: status === \"ERROR\" ? SpanStatus.ERROR : SpanStatus.COMPLETED,\n endedAt: nowISO(),\n });\n }\n\n trace.end({ status: status ?? \"COMPLETED\" });\n this.traces.delete(sessionId);\n this.rootSpans.delete(sessionId);\n }\n\n startToolCall(\n callID: string,\n tool: string,\n args: unknown,\n sessionID: string,\n ): void {\n this.toolCalls.set(callID, {\n startTime: Date.now(),\n tool,\n args,\n sessionID,\n });\n }\n\n endToolCall(\n callID: string,\n output: string,\n title: string,\n metadata: unknown,\n ): void {\n const call = this.toolCalls.get(callID);\n if (!call) return;\n this.toolCalls.delete(callID);\n\n const trace = this.traces.get(call.sessionID);\n if (!trace) return;\n\n const durationMs = Date.now() - call.startTime;\n const rootSpanId = this.rootSpans.get(call.sessionID);\n const toolMeta = extractToolMetadata(call.tool, call.args);\n\n trace.addSpan({\n name: title,\n type: SpanType.TOOL_CALL,\n parentSpanId: rootSpanId,\n input: safeJsonValue(call.args),\n output: output as JsonValue,\n durationMs,\n status: SpanStatus.COMPLETED,\n startedAt: new Date(call.startTime).toISOString(),\n endedAt: nowISO(),\n metadata: safeJsonValue({ ...toolMeta, rawMetadata: metadata }),\n });\n\n trace.addDecision({\n type: DecisionType.TOOL_SELECTION,\n chosen: call.tool as JsonValue,\n alternatives: [],\n reasoning: title,\n durationMs,\n parentSpanId: rootSpanId,\n });\n }\n\n recordLLMCall(\n sessionId: string,\n options: {\n model?: { providerID: string; modelID: string };\n agent?: string;\n messageID?: string;\n },\n ): void {\n const trace = this.traces.get(sessionId);\n if (!trace) return;\n\n const rootSpanId = this.rootSpans.get(sessionId);\n const agentName = options.agent ?? \"assistant\";\n const modelName = options.model?.modelID ?? \"unknown\";\n\n trace.addSpan({\n name: `${agentName} → ${modelName}`,\n type: SpanType.LLM_CALL,\n parentSpanId: rootSpanId,\n status: SpanStatus.COMPLETED,\n startedAt: nowISO(),\n endedAt: nowISO(),\n metadata: safeJsonValue({\n provider: options.model?.providerID,\n model: options.model?.modelID,\n agent: options.agent,\n messageID: options.messageID,\n }),\n });\n }\n\n recordPermission(\n sessionId: string,\n permission: unknown,\n status: string,\n ): void {\n const trace = this.traces.get(sessionId);\n if (!trace) return;\n\n const rootSpanId = this.rootSpans.get(sessionId);\n const p = permission as Record<string, unknown> | null;\n const title = (p?.[\"title\"] as string) ?? \"permission\";\n const permType = (p?.[\"type\"] as string) ?? \"unknown\";\n\n trace.addDecision({\n type: DecisionType.ESCALATION,\n chosen: safeJsonValue({ action: status }),\n alternatives: [\n \"allow\" as JsonValue,\n \"deny\" as JsonValue,\n \"ask\" as JsonValue,\n ],\n reasoning: `${permType}: ${title}`,\n parentSpanId: rootSpanId,\n });\n }\n\n getRootSpanId(sessionId: string): string | undefined {\n return this.rootSpans.get(sessionId);\n }\n}\n","import type { JsonValue } from \"agentlens-sdk\";\n\nexport function truncate(str: string, maxLength: number): string {\n if (str.length <= maxLength) return str;\n return str.slice(0, maxLength) + \"... [truncated]\";\n}\n\nexport function extractToolMetadata(\n tool: string,\n args: unknown,\n): Record<string, unknown> {\n const a = args as Record<string, unknown> | null | undefined;\n if (!a || typeof a !== \"object\") return {};\n\n switch (tool) {\n case \"read\":\n case \"mcp_read\":\n return { filePath: a[\"filePath\"] };\n\n case \"write\":\n case \"mcp_write\":\n return { filePath: a[\"filePath\"] };\n\n case \"edit\":\n case \"mcp_edit\":\n return { filePath: a[\"filePath\"] };\n\n case \"bash\":\n case \"mcp_bash\":\n return {\n command: truncate(String(a[\"command\"] ?? \"\"), 200),\n };\n\n case \"glob\":\n case \"mcp_glob\":\n return { pattern: a[\"pattern\"] };\n\n case \"grep\":\n case \"mcp_grep\":\n return { pattern: a[\"pattern\"], path: a[\"path\"] };\n\n case \"task\":\n case \"mcp_task\":\n return {\n category: a[\"category\"],\n description: a[\"description\"],\n };\n\n default:\n return {};\n }\n}\n\nconst MODEL_COSTS: Record<string, { input: number; output: number }> = {\n \"gpt-5.2\": { input: 1.75, output: 14 },\n \"gpt-5.1\": { input: 1.25, output: 10 },\n \"gpt-5\": { input: 1.25, output: 10 },\n \"gpt-5-mini\": { input: 0.25, output: 2 },\n \"gpt-5-nano\": { input: 0.05, output: 0.4 },\n \"gpt-4.1\": { input: 2, output: 8 },\n \"gpt-4.1-mini\": { input: 0.4, output: 1.6 },\n \"gpt-4.1-nano\": { input: 0.1, output: 0.4 },\n \"o3\": { input: 2, output: 8 },\n \"o3-mini\": { input: 1.1, output: 4.4 },\n \"o4-mini\": { input: 1.1, output: 4.4 },\n \"o1\": { input: 15, output: 60 },\n \"gpt-4o\": { input: 2.5, output: 10 },\n \"gpt-4o-mini\": { input: 0.15, output: 0.6 },\n \"gpt-4-turbo\": { input: 10, output: 30 },\n \"gpt-4\": { input: 30, output: 60 },\n \"claude-opus-4-6\": { input: 5, output: 25 },\n \"claude-opus-4-20250514\": { input: 15, output: 75 },\n \"claude-sonnet-4-20250514\": { input: 3, output: 15 },\n \"claude-4.5-opus\": { input: 5, output: 25 },\n \"claude-4.5-sonnet\": { input: 3, output: 15 },\n \"claude-4.5-haiku\": { input: 1, output: 5 },\n \"claude-3-5-sonnet\": { input: 3, output: 15 },\n \"claude-3-5-haiku\": { input: 0.8, output: 4 },\n \"claude-3-opus\": { input: 15, output: 75 },\n \"claude-3-haiku\": { input: 0.25, output: 1.25 },\n};\n\nexport function getModelCost(\n modelId: string,\n): { input: number; output: number } | undefined {\n const direct = MODEL_COSTS[modelId];\n if (direct) return direct;\n\n for (const [key, cost] of Object.entries(MODEL_COSTS)) {\n if (modelId.includes(key)) return cost;\n }\n return undefined;\n}\n\n/** Coerce arbitrary values into SDK-compatible `JsonValue`, stringifying unknowns. */\nexport function safeJsonValue(value: unknown): JsonValue {\n if (value === null || value === undefined) return null;\n if (\n typeof value === \"string\" ||\n typeof value === \"number\" ||\n typeof value === \"boolean\"\n ) {\n return value;\n }\n if (Array.isArray(value)) {\n return value.map((v) => safeJsonValue(v));\n }\n if (typeof value === \"object\") {\n const result: Record<string, JsonValue> = {};\n for (const [k, v] of Object.entries(value as Record<string, unknown>)) {\n result[k] = safeJsonValue(v);\n }\n return result;\n }\n return String(value);\n}\n"],"mappings":"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","names":["import_agentlens_sdk","EventTypeValues"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Plugin } from '@opencode-ai/plugin';
|
|
2
|
+
|
|
3
|
+
interface PluginConfig {
|
|
4
|
+
apiKey: string;
|
|
5
|
+
endpoint: string;
|
|
6
|
+
enabled: boolean;
|
|
7
|
+
/** Opt-in: capture full message content in traces */
|
|
8
|
+
captureContent: boolean;
|
|
9
|
+
/** Maximum characters for tool output before truncation */
|
|
10
|
+
maxOutputLength: number;
|
|
11
|
+
/** Milliseconds between automatic flushes */
|
|
12
|
+
flushInterval: number;
|
|
13
|
+
/** Maximum traces per batch */
|
|
14
|
+
maxBatchSize: number;
|
|
15
|
+
}
|
|
16
|
+
declare function loadConfig(): PluginConfig;
|
|
17
|
+
|
|
18
|
+
declare const plugin: Plugin;
|
|
19
|
+
|
|
20
|
+
export { plugin as AgentLensPlugin, type PluginConfig, plugin as default, loadConfig };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Plugin } from '@opencode-ai/plugin';
|
|
2
|
+
|
|
3
|
+
interface PluginConfig {
|
|
4
|
+
apiKey: string;
|
|
5
|
+
endpoint: string;
|
|
6
|
+
enabled: boolean;
|
|
7
|
+
/** Opt-in: capture full message content in traces */
|
|
8
|
+
captureContent: boolean;
|
|
9
|
+
/** Maximum characters for tool output before truncation */
|
|
10
|
+
maxOutputLength: number;
|
|
11
|
+
/** Milliseconds between automatic flushes */
|
|
12
|
+
flushInterval: number;
|
|
13
|
+
/** Maximum traces per batch */
|
|
14
|
+
maxBatchSize: number;
|
|
15
|
+
}
|
|
16
|
+
declare function loadConfig(): PluginConfig;
|
|
17
|
+
|
|
18
|
+
declare const plugin: Plugin;
|
|
19
|
+
|
|
20
|
+
export { plugin as AgentLensPlugin, type PluginConfig, plugin as default, loadConfig };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import { init, flush, EventType as EventTypeValues } from "agentlens-sdk";
|
|
3
|
+
|
|
4
|
+
// src/config.ts
|
|
5
|
+
function loadConfig() {
|
|
6
|
+
const apiKey = process.env["AGENTLENS_API_KEY"] ?? "";
|
|
7
|
+
if (!apiKey) {
|
|
8
|
+
console.warn(
|
|
9
|
+
"[agentlens] AGENTLENS_API_KEY not set \u2014 plugin will be disabled"
|
|
10
|
+
);
|
|
11
|
+
}
|
|
12
|
+
return {
|
|
13
|
+
apiKey,
|
|
14
|
+
endpoint: process.env["AGENTLENS_ENDPOINT"] ?? "https://agentlens.vectry.tech",
|
|
15
|
+
enabled: (process.env["AGENTLENS_ENABLED"] ?? "true") === "true",
|
|
16
|
+
captureContent: (process.env["AGENTLENS_CAPTURE_CONTENT"] ?? "false") === "true",
|
|
17
|
+
maxOutputLength: parseInt(
|
|
18
|
+
process.env["AGENTLENS_MAX_OUTPUT_LENGTH"] ?? "2000",
|
|
19
|
+
10
|
|
20
|
+
),
|
|
21
|
+
flushInterval: parseInt(
|
|
22
|
+
process.env["AGENTLENS_FLUSH_INTERVAL"] ?? "5000",
|
|
23
|
+
10
|
|
24
|
+
),
|
|
25
|
+
maxBatchSize: parseInt(process.env["AGENTLENS_BATCH_SIZE"] ?? "10", 10)
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// src/state.ts
|
|
30
|
+
import {
|
|
31
|
+
TraceBuilder,
|
|
32
|
+
SpanType,
|
|
33
|
+
SpanStatus,
|
|
34
|
+
DecisionType,
|
|
35
|
+
nowISO
|
|
36
|
+
} from "agentlens-sdk";
|
|
37
|
+
|
|
38
|
+
// src/utils.ts
|
|
39
|
+
function truncate(str, maxLength) {
|
|
40
|
+
if (str.length <= maxLength) return str;
|
|
41
|
+
return str.slice(0, maxLength) + "... [truncated]";
|
|
42
|
+
}
|
|
43
|
+
function extractToolMetadata(tool, args) {
|
|
44
|
+
const a = args;
|
|
45
|
+
if (!a || typeof a !== "object") return {};
|
|
46
|
+
switch (tool) {
|
|
47
|
+
case "read":
|
|
48
|
+
case "mcp_read":
|
|
49
|
+
return { filePath: a["filePath"] };
|
|
50
|
+
case "write":
|
|
51
|
+
case "mcp_write":
|
|
52
|
+
return { filePath: a["filePath"] };
|
|
53
|
+
case "edit":
|
|
54
|
+
case "mcp_edit":
|
|
55
|
+
return { filePath: a["filePath"] };
|
|
56
|
+
case "bash":
|
|
57
|
+
case "mcp_bash":
|
|
58
|
+
return {
|
|
59
|
+
command: truncate(String(a["command"] ?? ""), 200)
|
|
60
|
+
};
|
|
61
|
+
case "glob":
|
|
62
|
+
case "mcp_glob":
|
|
63
|
+
return { pattern: a["pattern"] };
|
|
64
|
+
case "grep":
|
|
65
|
+
case "mcp_grep":
|
|
66
|
+
return { pattern: a["pattern"], path: a["path"] };
|
|
67
|
+
case "task":
|
|
68
|
+
case "mcp_task":
|
|
69
|
+
return {
|
|
70
|
+
category: a["category"],
|
|
71
|
+
description: a["description"]
|
|
72
|
+
};
|
|
73
|
+
default:
|
|
74
|
+
return {};
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
function safeJsonValue(value) {
|
|
78
|
+
if (value === null || value === void 0) return null;
|
|
79
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
80
|
+
return value;
|
|
81
|
+
}
|
|
82
|
+
if (Array.isArray(value)) {
|
|
83
|
+
return value.map((v) => safeJsonValue(v));
|
|
84
|
+
}
|
|
85
|
+
if (typeof value === "object") {
|
|
86
|
+
const result = {};
|
|
87
|
+
for (const [k, v] of Object.entries(value)) {
|
|
88
|
+
result[k] = safeJsonValue(v);
|
|
89
|
+
}
|
|
90
|
+
return result;
|
|
91
|
+
}
|
|
92
|
+
return String(value);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// src/state.ts
|
|
96
|
+
var SessionState = class {
|
|
97
|
+
traces = /* @__PURE__ */ new Map();
|
|
98
|
+
toolCalls = /* @__PURE__ */ new Map();
|
|
99
|
+
rootSpans = /* @__PURE__ */ new Map();
|
|
100
|
+
startSession(sessionId, metadata) {
|
|
101
|
+
const trace = new TraceBuilder("opencode-session", {
|
|
102
|
+
sessionId,
|
|
103
|
+
tags: ["opencode", "coding-agent"],
|
|
104
|
+
metadata: metadata ? safeJsonValue(metadata) : void 0
|
|
105
|
+
});
|
|
106
|
+
const rootSpanId = trace.addSpan({
|
|
107
|
+
name: "session",
|
|
108
|
+
type: SpanType.AGENT,
|
|
109
|
+
startedAt: nowISO(),
|
|
110
|
+
metadata: metadata ? safeJsonValue(metadata) : void 0
|
|
111
|
+
});
|
|
112
|
+
this.traces.set(sessionId, trace);
|
|
113
|
+
this.rootSpans.set(sessionId, rootSpanId);
|
|
114
|
+
return trace;
|
|
115
|
+
}
|
|
116
|
+
getTrace(sessionId) {
|
|
117
|
+
return this.traces.get(sessionId);
|
|
118
|
+
}
|
|
119
|
+
endSession(sessionId, status) {
|
|
120
|
+
const trace = this.traces.get(sessionId);
|
|
121
|
+
if (!trace) return;
|
|
122
|
+
const rootSpanId = this.rootSpans.get(sessionId);
|
|
123
|
+
if (rootSpanId) {
|
|
124
|
+
trace.addSpan({
|
|
125
|
+
id: rootSpanId,
|
|
126
|
+
name: "session",
|
|
127
|
+
type: SpanType.AGENT,
|
|
128
|
+
status: status === "ERROR" ? SpanStatus.ERROR : SpanStatus.COMPLETED,
|
|
129
|
+
endedAt: nowISO()
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
trace.end({ status: status ?? "COMPLETED" });
|
|
133
|
+
this.traces.delete(sessionId);
|
|
134
|
+
this.rootSpans.delete(sessionId);
|
|
135
|
+
}
|
|
136
|
+
startToolCall(callID, tool, args, sessionID) {
|
|
137
|
+
this.toolCalls.set(callID, {
|
|
138
|
+
startTime: Date.now(),
|
|
139
|
+
tool,
|
|
140
|
+
args,
|
|
141
|
+
sessionID
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
endToolCall(callID, output, title, metadata) {
|
|
145
|
+
const call = this.toolCalls.get(callID);
|
|
146
|
+
if (!call) return;
|
|
147
|
+
this.toolCalls.delete(callID);
|
|
148
|
+
const trace = this.traces.get(call.sessionID);
|
|
149
|
+
if (!trace) return;
|
|
150
|
+
const durationMs = Date.now() - call.startTime;
|
|
151
|
+
const rootSpanId = this.rootSpans.get(call.sessionID);
|
|
152
|
+
const toolMeta = extractToolMetadata(call.tool, call.args);
|
|
153
|
+
trace.addSpan({
|
|
154
|
+
name: title,
|
|
155
|
+
type: SpanType.TOOL_CALL,
|
|
156
|
+
parentSpanId: rootSpanId,
|
|
157
|
+
input: safeJsonValue(call.args),
|
|
158
|
+
output,
|
|
159
|
+
durationMs,
|
|
160
|
+
status: SpanStatus.COMPLETED,
|
|
161
|
+
startedAt: new Date(call.startTime).toISOString(),
|
|
162
|
+
endedAt: nowISO(),
|
|
163
|
+
metadata: safeJsonValue({ ...toolMeta, rawMetadata: metadata })
|
|
164
|
+
});
|
|
165
|
+
trace.addDecision({
|
|
166
|
+
type: DecisionType.TOOL_SELECTION,
|
|
167
|
+
chosen: call.tool,
|
|
168
|
+
alternatives: [],
|
|
169
|
+
reasoning: title,
|
|
170
|
+
durationMs,
|
|
171
|
+
parentSpanId: rootSpanId
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
recordLLMCall(sessionId, options) {
|
|
175
|
+
const trace = this.traces.get(sessionId);
|
|
176
|
+
if (!trace) return;
|
|
177
|
+
const rootSpanId = this.rootSpans.get(sessionId);
|
|
178
|
+
const agentName = options.agent ?? "assistant";
|
|
179
|
+
const modelName = options.model?.modelID ?? "unknown";
|
|
180
|
+
trace.addSpan({
|
|
181
|
+
name: `${agentName} \u2192 ${modelName}`,
|
|
182
|
+
type: SpanType.LLM_CALL,
|
|
183
|
+
parentSpanId: rootSpanId,
|
|
184
|
+
status: SpanStatus.COMPLETED,
|
|
185
|
+
startedAt: nowISO(),
|
|
186
|
+
endedAt: nowISO(),
|
|
187
|
+
metadata: safeJsonValue({
|
|
188
|
+
provider: options.model?.providerID,
|
|
189
|
+
model: options.model?.modelID,
|
|
190
|
+
agent: options.agent,
|
|
191
|
+
messageID: options.messageID
|
|
192
|
+
})
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
recordPermission(sessionId, permission, status) {
|
|
196
|
+
const trace = this.traces.get(sessionId);
|
|
197
|
+
if (!trace) return;
|
|
198
|
+
const rootSpanId = this.rootSpans.get(sessionId);
|
|
199
|
+
const p = permission;
|
|
200
|
+
const title = p?.["title"] ?? "permission";
|
|
201
|
+
const permType = p?.["type"] ?? "unknown";
|
|
202
|
+
trace.addDecision({
|
|
203
|
+
type: DecisionType.ESCALATION,
|
|
204
|
+
chosen: safeJsonValue({ action: status }),
|
|
205
|
+
alternatives: [
|
|
206
|
+
"allow",
|
|
207
|
+
"deny",
|
|
208
|
+
"ask"
|
|
209
|
+
],
|
|
210
|
+
reasoning: `${permType}: ${title}`,
|
|
211
|
+
parentSpanId: rootSpanId
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
getRootSpanId(sessionId) {
|
|
215
|
+
return this.rootSpans.get(sessionId);
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
// src/index.ts
|
|
220
|
+
var plugin = async ({ project, directory, worktree }) => {
|
|
221
|
+
const config = loadConfig();
|
|
222
|
+
if (!config.enabled || !config.apiKey) {
|
|
223
|
+
console.log("[agentlens] Plugin disabled \u2014 missing AGENTLENS_API_KEY");
|
|
224
|
+
return {};
|
|
225
|
+
}
|
|
226
|
+
init({
|
|
227
|
+
apiKey: config.apiKey,
|
|
228
|
+
endpoint: config.endpoint,
|
|
229
|
+
flushInterval: config.flushInterval,
|
|
230
|
+
maxBatchSize: config.maxBatchSize
|
|
231
|
+
});
|
|
232
|
+
const state = new SessionState();
|
|
233
|
+
return {
|
|
234
|
+
event: async ({ event }) => {
|
|
235
|
+
const type = event.type;
|
|
236
|
+
const props = event.properties;
|
|
237
|
+
if (type === "session.created" && props?.["id"]) {
|
|
238
|
+
state.startSession(String(props["id"]), {
|
|
239
|
+
project: project.id,
|
|
240
|
+
directory,
|
|
241
|
+
worktree
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
if (type === "session.idle") {
|
|
245
|
+
const sessionId = props?.["sessionID"] ?? props?.["id"];
|
|
246
|
+
if (sessionId) await flush();
|
|
247
|
+
}
|
|
248
|
+
if (type === "session.error") {
|
|
249
|
+
const sessionId = String(props?.["sessionID"] ?? props?.["id"] ?? "");
|
|
250
|
+
if (sessionId) {
|
|
251
|
+
const trace = state.getTrace(sessionId);
|
|
252
|
+
if (trace) {
|
|
253
|
+
trace.addEvent({
|
|
254
|
+
type: EventTypeValues.ERROR,
|
|
255
|
+
name: String(props?.["error"] ?? "session error"),
|
|
256
|
+
metadata: safeJsonValue(props)
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
if (type === "session.deleted") {
|
|
262
|
+
const sessionId = String(props?.["sessionID"] ?? props?.["id"] ?? "");
|
|
263
|
+
if (sessionId) state.endSession(sessionId);
|
|
264
|
+
}
|
|
265
|
+
if (type === "session.diff") {
|
|
266
|
+
const sessionId = String(props?.["sessionID"] ?? props?.["id"] ?? "");
|
|
267
|
+
if (sessionId) {
|
|
268
|
+
const trace = state.getTrace(sessionId);
|
|
269
|
+
if (trace) {
|
|
270
|
+
trace.setMetadata({
|
|
271
|
+
diff: truncate(String(props?.["diff"] ?? ""), 5e3)
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
if (type === "file.edited") {
|
|
277
|
+
const sessionId = String(props?.["sessionID"] ?? props?.["id"] ?? "");
|
|
278
|
+
const trace = sessionId ? state.getTrace(sessionId) : void 0;
|
|
279
|
+
if (trace) {
|
|
280
|
+
trace.addEvent({
|
|
281
|
+
type: EventTypeValues.CUSTOM,
|
|
282
|
+
name: "file.edited",
|
|
283
|
+
metadata: safeJsonValue({
|
|
284
|
+
filePath: props?.["filePath"]
|
|
285
|
+
})
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
},
|
|
290
|
+
"tool.execute.before": async (input, output) => {
|
|
291
|
+
state.startToolCall(
|
|
292
|
+
input.callID,
|
|
293
|
+
input.tool,
|
|
294
|
+
output.args,
|
|
295
|
+
input.sessionID
|
|
296
|
+
);
|
|
297
|
+
},
|
|
298
|
+
"tool.execute.after": async (input, output) => {
|
|
299
|
+
state.endToolCall(
|
|
300
|
+
input.callID,
|
|
301
|
+
truncate(output.output ?? "", config.maxOutputLength),
|
|
302
|
+
output.title ?? input.tool,
|
|
303
|
+
output.metadata
|
|
304
|
+
);
|
|
305
|
+
},
|
|
306
|
+
"chat.message": async (input) => {
|
|
307
|
+
if (input.model) {
|
|
308
|
+
state.recordLLMCall(input.sessionID, {
|
|
309
|
+
model: input.model,
|
|
310
|
+
agent: input.agent,
|
|
311
|
+
messageID: input.messageID
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
},
|
|
315
|
+
"chat.params": async (input, output) => {
|
|
316
|
+
const trace = state.getTrace(input.sessionID);
|
|
317
|
+
if (trace) {
|
|
318
|
+
trace.addEvent({
|
|
319
|
+
type: EventTypeValues.CUSTOM,
|
|
320
|
+
name: "chat.params",
|
|
321
|
+
metadata: safeJsonValue({
|
|
322
|
+
agent: input.agent,
|
|
323
|
+
model: input.model.id,
|
|
324
|
+
provider: input.provider.info.id,
|
|
325
|
+
temperature: output.temperature,
|
|
326
|
+
topP: output.topP,
|
|
327
|
+
topK: output.topK
|
|
328
|
+
})
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
},
|
|
332
|
+
"permission.ask": async (input, output) => {
|
|
333
|
+
state.recordPermission(input.sessionID, input, output.status);
|
|
334
|
+
}
|
|
335
|
+
};
|
|
336
|
+
};
|
|
337
|
+
var index_default = plugin;
|
|
338
|
+
export {
|
|
339
|
+
plugin as AgentLensPlugin,
|
|
340
|
+
index_default as default,
|
|
341
|
+
loadConfig
|
|
342
|
+
};
|
|
343
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/config.ts","../src/state.ts","../src/utils.ts"],"sourcesContent":["import type { Plugin } from \"@opencode-ai/plugin\";\nimport type { JsonValue } from \"agentlens-sdk\";\nimport { init, flush, EventType as EventTypeValues } from \"agentlens-sdk\";\nimport { loadConfig } from \"./config.js\";\nimport { SessionState } from \"./state.js\";\nimport { truncate, safeJsonValue } from \"./utils.js\";\n\nconst plugin: Plugin = async ({ project, directory, worktree }) => {\n const config = loadConfig();\n\n if (!config.enabled || !config.apiKey) {\n console.log(\"[agentlens] Plugin disabled — missing AGENTLENS_API_KEY\");\n return {};\n }\n\n init({\n apiKey: config.apiKey,\n endpoint: config.endpoint,\n flushInterval: config.flushInterval,\n maxBatchSize: config.maxBatchSize,\n });\n\n const state = new SessionState();\n\n return {\n event: async ({ event }) => {\n const type = event.type;\n const props = (event as Record<string, unknown>).properties as\n | Record<string, unknown>\n | undefined;\n\n if (type === \"session.created\" && props?.[\"id\"]) {\n state.startSession(String(props[\"id\"]), {\n project: project.id,\n directory,\n worktree,\n });\n }\n\n if (type === \"session.idle\") {\n const sessionId = props?.[\"sessionID\"] ?? props?.[\"id\"];\n if (sessionId) await flush();\n }\n\n if (type === \"session.error\") {\n const sessionId = String(props?.[\"sessionID\"] ?? props?.[\"id\"] ?? \"\");\n if (sessionId) {\n const trace = state.getTrace(sessionId);\n if (trace) {\n trace.addEvent({\n type: EventTypeValues.ERROR,\n name: String(props?.[\"error\"] ?? \"session error\"),\n metadata: safeJsonValue(props) as JsonValue,\n });\n }\n }\n }\n\n if (type === \"session.deleted\") {\n const sessionId = String(props?.[\"sessionID\"] ?? props?.[\"id\"] ?? \"\");\n if (sessionId) state.endSession(sessionId);\n }\n\n if (type === \"session.diff\") {\n const sessionId = String(props?.[\"sessionID\"] ?? props?.[\"id\"] ?? \"\");\n if (sessionId) {\n const trace = state.getTrace(sessionId);\n if (trace) {\n trace.setMetadata({\n diff: truncate(String(props?.[\"diff\"] ?? \"\"), 5000),\n });\n }\n }\n }\n\n if (type === \"file.edited\") {\n const sessionId = String(props?.[\"sessionID\"] ?? props?.[\"id\"] ?? \"\");\n const trace = sessionId ? state.getTrace(sessionId) : undefined;\n if (trace) {\n trace.addEvent({\n type: EventTypeValues.CUSTOM,\n name: \"file.edited\",\n metadata: safeJsonValue({\n filePath: props?.[\"filePath\"],\n }) as JsonValue,\n });\n }\n }\n },\n\n \"tool.execute.before\": async (input, output) => {\n state.startToolCall(\n input.callID,\n input.tool,\n output.args as unknown,\n input.sessionID,\n );\n },\n\n \"tool.execute.after\": async (input, output) => {\n state.endToolCall(\n input.callID,\n truncate(output.output ?? \"\", config.maxOutputLength),\n output.title ?? input.tool,\n output.metadata as unknown,\n );\n },\n\n \"chat.message\": async (input) => {\n if (input.model) {\n state.recordLLMCall(input.sessionID, {\n model: input.model,\n agent: input.agent,\n messageID: input.messageID,\n });\n }\n },\n\n \"chat.params\": async (input, output) => {\n const trace = state.getTrace(input.sessionID);\n if (trace) {\n trace.addEvent({\n type: EventTypeValues.CUSTOM,\n name: \"chat.params\",\n metadata: safeJsonValue({\n agent: input.agent,\n model: input.model.id,\n provider: input.provider.info.id,\n temperature: output.temperature,\n topP: output.topP,\n topK: output.topK,\n }) as JsonValue,\n });\n }\n },\n\n \"permission.ask\": async (input, output) => {\n state.recordPermission(input.sessionID, input, output.status);\n },\n };\n};\n\nexport default plugin;\nexport { plugin as AgentLensPlugin };\nexport type { PluginConfig } from \"./config.js\";\nexport { loadConfig } from \"./config.js\";\n","export interface PluginConfig {\n apiKey: string;\n endpoint: string;\n enabled: boolean;\n /** Opt-in: capture full message content in traces */\n captureContent: boolean;\n /** Maximum characters for tool output before truncation */\n maxOutputLength: number;\n /** Milliseconds between automatic flushes */\n flushInterval: number;\n /** Maximum traces per batch */\n maxBatchSize: number;\n}\n\nexport function loadConfig(): PluginConfig {\n const apiKey = process.env[\"AGENTLENS_API_KEY\"] ?? \"\";\n\n if (!apiKey) {\n console.warn(\n \"[agentlens] AGENTLENS_API_KEY not set — plugin will be disabled\",\n );\n }\n\n return {\n apiKey,\n endpoint:\n process.env[\"AGENTLENS_ENDPOINT\"] ?? \"https://agentlens.vectry.tech\",\n enabled: (process.env[\"AGENTLENS_ENABLED\"] ?? \"true\") === \"true\",\n captureContent:\n (process.env[\"AGENTLENS_CAPTURE_CONTENT\"] ?? \"false\") === \"true\",\n maxOutputLength: parseInt(\n process.env[\"AGENTLENS_MAX_OUTPUT_LENGTH\"] ?? \"2000\",\n 10,\n ),\n flushInterval: parseInt(\n process.env[\"AGENTLENS_FLUSH_INTERVAL\"] ?? \"5000\",\n 10,\n ),\n maxBatchSize: parseInt(process.env[\"AGENTLENS_BATCH_SIZE\"] ?? \"10\", 10),\n };\n}\n","import {\n TraceBuilder,\n SpanType,\n SpanStatus,\n DecisionType,\n nowISO,\n} from \"agentlens-sdk\";\nimport type { JsonValue, TraceStatus } from \"agentlens-sdk\";\nimport { extractToolMetadata, safeJsonValue } from \"./utils.js\";\n\ninterface ToolCallState {\n startTime: number;\n tool: string;\n args: unknown;\n sessionID: string;\n}\n\nexport class SessionState {\n private traces = new Map<string, TraceBuilder>();\n private toolCalls = new Map<string, ToolCallState>();\n private rootSpans = new Map<string, string>();\n\n startSession(\n sessionId: string,\n metadata?: Record<string, unknown>,\n ): TraceBuilder {\n const trace = new TraceBuilder(\"opencode-session\", {\n sessionId,\n tags: [\"opencode\", \"coding-agent\"],\n metadata: metadata ? (safeJsonValue(metadata) as JsonValue) : undefined,\n });\n\n const rootSpanId = trace.addSpan({\n name: \"session\",\n type: SpanType.AGENT,\n startedAt: nowISO(),\n metadata: metadata ? (safeJsonValue(metadata) as JsonValue) : undefined,\n });\n\n this.traces.set(sessionId, trace);\n this.rootSpans.set(sessionId, rootSpanId);\n return trace;\n }\n\n getTrace(sessionId: string): TraceBuilder | undefined {\n return this.traces.get(sessionId);\n }\n\n endSession(sessionId: string, status?: TraceStatus): void {\n const trace = this.traces.get(sessionId);\n if (!trace) return;\n\n const rootSpanId = this.rootSpans.get(sessionId);\n if (rootSpanId) {\n trace.addSpan({\n id: rootSpanId,\n name: \"session\",\n type: SpanType.AGENT,\n status: status === \"ERROR\" ? SpanStatus.ERROR : SpanStatus.COMPLETED,\n endedAt: nowISO(),\n });\n }\n\n trace.end({ status: status ?? \"COMPLETED\" });\n this.traces.delete(sessionId);\n this.rootSpans.delete(sessionId);\n }\n\n startToolCall(\n callID: string,\n tool: string,\n args: unknown,\n sessionID: string,\n ): void {\n this.toolCalls.set(callID, {\n startTime: Date.now(),\n tool,\n args,\n sessionID,\n });\n }\n\n endToolCall(\n callID: string,\n output: string,\n title: string,\n metadata: unknown,\n ): void {\n const call = this.toolCalls.get(callID);\n if (!call) return;\n this.toolCalls.delete(callID);\n\n const trace = this.traces.get(call.sessionID);\n if (!trace) return;\n\n const durationMs = Date.now() - call.startTime;\n const rootSpanId = this.rootSpans.get(call.sessionID);\n const toolMeta = extractToolMetadata(call.tool, call.args);\n\n trace.addSpan({\n name: title,\n type: SpanType.TOOL_CALL,\n parentSpanId: rootSpanId,\n input: safeJsonValue(call.args),\n output: output as JsonValue,\n durationMs,\n status: SpanStatus.COMPLETED,\n startedAt: new Date(call.startTime).toISOString(),\n endedAt: nowISO(),\n metadata: safeJsonValue({ ...toolMeta, rawMetadata: metadata }),\n });\n\n trace.addDecision({\n type: DecisionType.TOOL_SELECTION,\n chosen: call.tool as JsonValue,\n alternatives: [],\n reasoning: title,\n durationMs,\n parentSpanId: rootSpanId,\n });\n }\n\n recordLLMCall(\n sessionId: string,\n options: {\n model?: { providerID: string; modelID: string };\n agent?: string;\n messageID?: string;\n },\n ): void {\n const trace = this.traces.get(sessionId);\n if (!trace) return;\n\n const rootSpanId = this.rootSpans.get(sessionId);\n const agentName = options.agent ?? \"assistant\";\n const modelName = options.model?.modelID ?? \"unknown\";\n\n trace.addSpan({\n name: `${agentName} → ${modelName}`,\n type: SpanType.LLM_CALL,\n parentSpanId: rootSpanId,\n status: SpanStatus.COMPLETED,\n startedAt: nowISO(),\n endedAt: nowISO(),\n metadata: safeJsonValue({\n provider: options.model?.providerID,\n model: options.model?.modelID,\n agent: options.agent,\n messageID: options.messageID,\n }),\n });\n }\n\n recordPermission(\n sessionId: string,\n permission: unknown,\n status: string,\n ): void {\n const trace = this.traces.get(sessionId);\n if (!trace) return;\n\n const rootSpanId = this.rootSpans.get(sessionId);\n const p = permission as Record<string, unknown> | null;\n const title = (p?.[\"title\"] as string) ?? \"permission\";\n const permType = (p?.[\"type\"] as string) ?? \"unknown\";\n\n trace.addDecision({\n type: DecisionType.ESCALATION,\n chosen: safeJsonValue({ action: status }),\n alternatives: [\n \"allow\" as JsonValue,\n \"deny\" as JsonValue,\n \"ask\" as JsonValue,\n ],\n reasoning: `${permType}: ${title}`,\n parentSpanId: rootSpanId,\n });\n }\n\n getRootSpanId(sessionId: string): string | undefined {\n return this.rootSpans.get(sessionId);\n }\n}\n","import type { JsonValue } from \"agentlens-sdk\";\n\nexport function truncate(str: string, maxLength: number): string {\n if (str.length <= maxLength) return str;\n return str.slice(0, maxLength) + \"... [truncated]\";\n}\n\nexport function extractToolMetadata(\n tool: string,\n args: unknown,\n): Record<string, unknown> {\n const a = args as Record<string, unknown> | null | undefined;\n if (!a || typeof a !== \"object\") return {};\n\n switch (tool) {\n case \"read\":\n case \"mcp_read\":\n return { filePath: a[\"filePath\"] };\n\n case \"write\":\n case \"mcp_write\":\n return { filePath: a[\"filePath\"] };\n\n case \"edit\":\n case \"mcp_edit\":\n return { filePath: a[\"filePath\"] };\n\n case \"bash\":\n case \"mcp_bash\":\n return {\n command: truncate(String(a[\"command\"] ?? \"\"), 200),\n };\n\n case \"glob\":\n case \"mcp_glob\":\n return { pattern: a[\"pattern\"] };\n\n case \"grep\":\n case \"mcp_grep\":\n return { pattern: a[\"pattern\"], path: a[\"path\"] };\n\n case \"task\":\n case \"mcp_task\":\n return {\n category: a[\"category\"],\n description: a[\"description\"],\n };\n\n default:\n return {};\n }\n}\n\nconst MODEL_COSTS: Record<string, { input: number; output: number }> = {\n \"gpt-5.2\": { input: 1.75, output: 14 },\n \"gpt-5.1\": { input: 1.25, output: 10 },\n \"gpt-5\": { input: 1.25, output: 10 },\n \"gpt-5-mini\": { input: 0.25, output: 2 },\n \"gpt-5-nano\": { input: 0.05, output: 0.4 },\n \"gpt-4.1\": { input: 2, output: 8 },\n \"gpt-4.1-mini\": { input: 0.4, output: 1.6 },\n \"gpt-4.1-nano\": { input: 0.1, output: 0.4 },\n \"o3\": { input: 2, output: 8 },\n \"o3-mini\": { input: 1.1, output: 4.4 },\n \"o4-mini\": { input: 1.1, output: 4.4 },\n \"o1\": { input: 15, output: 60 },\n \"gpt-4o\": { input: 2.5, output: 10 },\n \"gpt-4o-mini\": { input: 0.15, output: 0.6 },\n \"gpt-4-turbo\": { input: 10, output: 30 },\n \"gpt-4\": { input: 30, output: 60 },\n \"claude-opus-4-6\": { input: 5, output: 25 },\n \"claude-opus-4-20250514\": { input: 15, output: 75 },\n \"claude-sonnet-4-20250514\": { input: 3, output: 15 },\n \"claude-4.5-opus\": { input: 5, output: 25 },\n \"claude-4.5-sonnet\": { input: 3, output: 15 },\n \"claude-4.5-haiku\": { input: 1, output: 5 },\n \"claude-3-5-sonnet\": { input: 3, output: 15 },\n \"claude-3-5-haiku\": { input: 0.8, output: 4 },\n \"claude-3-opus\": { input: 15, output: 75 },\n \"claude-3-haiku\": { input: 0.25, output: 1.25 },\n};\n\nexport function getModelCost(\n modelId: string,\n): { input: number; output: number } | undefined {\n const direct = MODEL_COSTS[modelId];\n if (direct) return direct;\n\n for (const [key, cost] of Object.entries(MODEL_COSTS)) {\n if (modelId.includes(key)) return cost;\n }\n return undefined;\n}\n\n/** Coerce arbitrary values into SDK-compatible `JsonValue`, stringifying unknowns. */\nexport function safeJsonValue(value: unknown): JsonValue {\n if (value === null || value === undefined) return null;\n if (\n typeof value === \"string\" ||\n typeof value === \"number\" ||\n typeof value === \"boolean\"\n ) {\n return value;\n }\n if (Array.isArray(value)) {\n return value.map((v) => safeJsonValue(v));\n }\n if (typeof value === \"object\") {\n const result: Record<string, JsonValue> = {};\n for (const [k, v] of Object.entries(value as Record<string, unknown>)) {\n result[k] = safeJsonValue(v);\n }\n return result;\n }\n return String(value);\n}\n"],"mappings":"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","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "opencode-agentlens",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "OpenCode plugin for AgentLens — trace your coding agent's decisions, tool calls, and sessions",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"default": "./dist/index.js"
|
|
14
|
+
},
|
|
15
|
+
"require": {
|
|
16
|
+
"types": "./dist/index.d.cts",
|
|
17
|
+
"default": "./dist/index.cjs"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"files": ["dist"],
|
|
22
|
+
"scripts": {
|
|
23
|
+
"build": "tsup",
|
|
24
|
+
"dev": "tsup --watch",
|
|
25
|
+
"typecheck": "tsc --noEmit",
|
|
26
|
+
"clean": "rm -rf dist"
|
|
27
|
+
},
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"agentlens-sdk": "*"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@opencode-ai/plugin": "^1.1.53",
|
|
33
|
+
"tsup": "^8.3.0",
|
|
34
|
+
"typescript": "^5.7.0"
|
|
35
|
+
},
|
|
36
|
+
"peerDependencies": {
|
|
37
|
+
"@opencode-ai/plugin": ">=1.1.0"
|
|
38
|
+
},
|
|
39
|
+
"keywords": [
|
|
40
|
+
"opencode",
|
|
41
|
+
"agentlens",
|
|
42
|
+
"observability",
|
|
43
|
+
"tracing",
|
|
44
|
+
"coding-agent"
|
|
45
|
+
],
|
|
46
|
+
"license": "MIT",
|
|
47
|
+
"author": "Vectry <hunter@repi.fun>",
|
|
48
|
+
"repository": {
|
|
49
|
+
"type": "git",
|
|
50
|
+
"url": "https://gitea.repi.fun/repi/agentlens",
|
|
51
|
+
"directory": "packages/opencode-plugin"
|
|
52
|
+
},
|
|
53
|
+
"homepage": "https://agentlens.vectry.tech",
|
|
54
|
+
"bugs": {
|
|
55
|
+
"url": "https://gitea.repi.fun/repi/agentlens/issues"
|
|
56
|
+
}
|
|
57
|
+
}
|