@pinta-ai/pinta-opencode 0.9.0 → 0.10.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/CHANGELOG.md +20 -0
- package/README.md +54 -0
- package/dist/index.mjs +313 -41
- package/dist/plugin.js +19 -19
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.10.0
|
|
4
|
+
|
|
5
|
+
- Emit exact scalar `opencode.model` with source/provider evidence from verified
|
|
6
|
+
host model shapes. Preserve original nested fields and conflicting top-level
|
|
7
|
+
model values; omit blanks, placeholders and every JSON-prefixed ID, including
|
|
8
|
+
malformed/truncated strings.
|
|
9
|
+
- Correlate tool models through the exact session + callID -> messageID join.
|
|
10
|
+
Request evidence is scoped to its user message and agent, not reused as a
|
|
11
|
+
session default. Bounded caches handle switches, subagents, concurrent
|
|
12
|
+
sessions, out-of-order updates, stale state and end/removal events without
|
|
13
|
+
borrowing an unrelated model.
|
|
14
|
+
- Report assistant metadata as host selection, not proof of actual provider
|
|
15
|
+
response identity. Span counts, guard behavior and redaction remain unchanged;
|
|
16
|
+
model resolution adds no file scans, host commands or network requests.
|
|
17
|
+
- Pinta Manager 0.1.11 or later remains the guard-payload compatibility floor,
|
|
18
|
+
with `@pinta-ai/core` still at `^0.8.0` and no new required envelope fields or
|
|
19
|
+
additional desktop upgrade.
|
|
20
|
+
|
|
21
|
+
Refs PTA-524.
|
|
22
|
+
|
|
3
23
|
## 0.9.0
|
|
4
24
|
|
|
5
25
|
- The guard is asked about the span, not about a summary of it. Until now the
|
package/README.md
CHANGED
|
@@ -13,6 +13,7 @@ opencode fires plugin hooks around every built-in **and** MCP tool. This adapter
|
|
|
13
13
|
| Hook | Adapter does | Verified payload |
|
|
14
14
|
|---|---|---|
|
|
15
15
|
| `chat.message` | start a new trace (turn boundary) | `{ sessionID, agent, model, messageID, variant }` |
|
|
16
|
+
| `chat.params` | remember exact-message request model evidence (no extra span) | `{ sessionID, agent, model, message }` |
|
|
16
17
|
| `event` | lifecycle span (Bronze flatten) + flush on `session.idle` | `{ event: { id, type, properties } }`, every event carries `properties.sessionID` |
|
|
17
18
|
| `tool.execute.before` | query guard → **`throw` on DENY** + emit span | input `{ tool, sessionID, callID }`, output `{ args }` (full tool args, mutable) |
|
|
18
19
|
| `tool.execute.after` | tool-result span (incl. exit code) | output `{ title, output, metadata{ output, exit, truncated, … } }` |
|
|
@@ -91,6 +92,59 @@ Guard is **fail-open** (no endpoint / `PINTA_GUARD_DISABLED=1` / non-200 / timeo
|
|
|
91
92
|
|
|
92
93
|
Tool spans are built from `tool.execute.before/after` (richer: args, output, exit) rather than the event bus; `event` covers lifecycle and turn boundaries.
|
|
93
94
|
|
|
95
|
+
### Model evidence and limits
|
|
96
|
+
|
|
97
|
+
`opencode.model` is the exact scalar ID, never a serialized model object or an
|
|
98
|
+
`unknown` default. `opencode.model_source` records its provenance;
|
|
99
|
+
`opencode.provider` carries an explicitly supplied `providerID`.
|
|
100
|
+
|
|
101
|
+
| Evidence | Source / scope |
|
|
102
|
+
| --- | --- |
|
|
103
|
+
| `message.updated.info.modelID` (assistant) | `reported:message.updated.info.modelID`; exact session + assistant message |
|
|
104
|
+
| `message.updated.info.model.{modelID,id}` (user) | `requested:message.updated.info.model.…` |
|
|
105
|
+
| `chat.message` output message model, then input model | `requested:chat.message.…`; fallback only for that **user message and agent**, never its subagents |
|
|
106
|
+
| `chat.params.model.{modelID,id}` | `requested:chat.params.model.…`; same user-message + agent constraint |
|
|
107
|
+
| `session.updated.info.model` / `session.next.model.switched.model` | `selected:<event>.<field>` on that event **only**, not a default for tools |
|
|
108
|
+
| Explicit tool or other event `model` / `modelID` | `reported:tool.…` / `reported:event.…` |
|
|
109
|
+
|
|
110
|
+
The [plugin callbacks](https://github.com/anomalyco/opencode/blob/v1.15.3/packages/plugin/src/index.ts)
|
|
111
|
+
and [message/part schemas](https://github.com/anomalyco/opencode/blob/v1.15.3/packages/opencode/src/session/message-v2.ts)
|
|
112
|
+
agree with this repository's `phase1-e2e-events.jsonl`. OpenCode constructs an
|
|
113
|
+
assistant's `modelID` from the selected model **before** the provider replies
|
|
114
|
+
([prompt implementation](https://github.com/anomalyco/opencode/blob/v1.15.3/packages/opencode/src/session/prompt.ts)).
|
|
115
|
+
It is therefore *reported selection*, **not proof of the actual routed
|
|
116
|
+
response model** (notably for routers and fallback providers).
|
|
117
|
+
|
|
118
|
+
Tool hooks carry only `sessionID` + `callID`. They receive a model only when a
|
|
119
|
+
`message.part.updated` tool part joins that exact call to a message with model
|
|
120
|
+
evidence in the same session. Correlated `opencode.message_id` and
|
|
121
|
+
`opencode.agent` accompany it when available. No latest-message/session default,
|
|
122
|
+
parent-message model, or subtask's requested model is borrowed. Out-of-order
|
|
123
|
+
messages can enrich a later after-span, not retroactively invent a before-span.
|
|
124
|
+
Conflicting identities/models, missing joins and expired evidence are omitted.
|
|
125
|
+
The before model is pinned to its call; switches do not relabel in-flight tools.
|
|
126
|
+
Message-part updates/deltas likewise use only their own message's evidence.
|
|
127
|
+
Consumed calls cannot be reopened by delayed part events. Idle (`session.idle`
|
|
128
|
+
or `session.status` with `status.type=idle`), deleted and errored sessions clear
|
|
129
|
+
their state; a new chat/session start reopens them.
|
|
130
|
+
|
|
131
|
+
Memory is per plugin instance, capped globally at **1,024 message entries,
|
|
132
|
+
512 request entries, 2,048 call entries, 128 ended-session markers**. Model,
|
|
133
|
+
request and call entries expire **15 minutes after their last update**.
|
|
134
|
+
Identifiers over 1,024 characters are not cached or treated as model IDs.
|
|
135
|
+
Normal resolution uses fixed field checks and O(1)
|
|
136
|
+
map lookups; end/removal cleanup examines at most those bounded maps. **No
|
|
137
|
+
added file reads, host commands, network requests, timers or spans.**
|
|
138
|
+
|
|
139
|
+
Blank/non-string/placeholder IDs (`unknown`, `undefined`, `null`, `n/a`, `none`,
|
|
140
|
+
`-`, `auto`, `default`) are omitted. Every trimmed ID beginning with `{` or `[`
|
|
141
|
+
is also omitted, including malformed/truncated JSON-like strings without a
|
|
142
|
+
closing brace. Objects/conflicting top-level model fields
|
|
143
|
+
remain in `opencode.model_raw`; raw nested `info` / `part` payloads stay intact.
|
|
144
|
+
Existing redaction, tool guards, event counts and errors are unchanged. A host
|
|
145
|
+
that never exposes a model (or its exact call/message relationship) stays
|
|
146
|
+
unattributed rather than being assigned an unrelated model.
|
|
147
|
+
|
|
94
148
|
## Architecture
|
|
95
149
|
|
|
96
150
|
```
|
package/dist/index.mjs
CHANGED
|
@@ -16,9 +16,9 @@ var __commonJS = (cb, mod) => function __require2() {
|
|
|
16
16
|
};
|
|
17
17
|
var __copyProps = (to, from, except, desc) => {
|
|
18
18
|
if (from && typeof from === "object" || typeof from === "function") {
|
|
19
|
-
for (let
|
|
20
|
-
if (!__hasOwnProp.call(to,
|
|
21
|
-
__defProp(to,
|
|
19
|
+
for (let key2 of __getOwnPropNames(from))
|
|
20
|
+
if (!__hasOwnProp.call(to, key2) && key2 !== except)
|
|
21
|
+
__defProp(to, key2, { get: () => from[key2], enumerable: !(desc = __getOwnPropDesc(from, key2)) || desc.enumerable });
|
|
22
22
|
}
|
|
23
23
|
return to;
|
|
24
24
|
};
|
|
@@ -216,19 +216,19 @@ var require_otlp = __commonJS({
|
|
|
216
216
|
return name.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/([A-Z])([A-Z][a-z])/g, "$1_$2").toLowerCase();
|
|
217
217
|
}
|
|
218
218
|
var EMPTY_SET = /* @__PURE__ */ new Set();
|
|
219
|
-
function maybeRedactString(
|
|
219
|
+
function maybeRedactString(key2, raw, policy) {
|
|
220
220
|
const truncated = (0, redact_js_1.truncate)(raw);
|
|
221
|
-
if ((policy.skipRedactKeys ?? EMPTY_SET).has(
|
|
221
|
+
if ((policy.skipRedactKeys ?? EMPTY_SET).has(key2))
|
|
222
222
|
return truncated;
|
|
223
|
-
const context = (policy.bashContextKeys ?? EMPTY_SET).has(
|
|
223
|
+
const context = (policy.bashContextKeys ?? EMPTY_SET).has(key2) ? "bash" : void 0;
|
|
224
224
|
return (0, redact_js_1.redact)(truncated, { context });
|
|
225
225
|
}
|
|
226
|
-
function toOtlpValue(
|
|
226
|
+
function toOtlpValue(key2, v, policy = {}) {
|
|
227
227
|
if (v === null || v === void 0)
|
|
228
228
|
return null;
|
|
229
229
|
switch (typeof v) {
|
|
230
230
|
case "string":
|
|
231
|
-
return { stringValue: maybeRedactString(
|
|
231
|
+
return { stringValue: maybeRedactString(key2, v, policy) };
|
|
232
232
|
case "boolean":
|
|
233
233
|
return { boolValue: v };
|
|
234
234
|
case "number":
|
|
@@ -237,22 +237,22 @@ var require_otlp = __commonJS({
|
|
|
237
237
|
return { doubleValue: v };
|
|
238
238
|
case "object":
|
|
239
239
|
try {
|
|
240
|
-
return { stringValue: maybeRedactString(
|
|
240
|
+
return { stringValue: maybeRedactString(key2, JSON.stringify(v), policy) };
|
|
241
241
|
} catch {
|
|
242
|
-
return { stringValue: maybeRedactString(
|
|
242
|
+
return { stringValue: maybeRedactString(key2, String(v), policy) };
|
|
243
243
|
}
|
|
244
244
|
default:
|
|
245
|
-
return { stringValue: maybeRedactString(
|
|
245
|
+
return { stringValue: maybeRedactString(key2, String(v), policy) };
|
|
246
246
|
}
|
|
247
247
|
}
|
|
248
|
-
function attrsFromRecord2(
|
|
248
|
+
function attrsFromRecord2(record2, prefix, policy = {}) {
|
|
249
249
|
const out = [];
|
|
250
|
-
for (const [k, v] of Object.entries(
|
|
251
|
-
const
|
|
252
|
-
const value = toOtlpValue(
|
|
250
|
+
for (const [k, v] of Object.entries(record2)) {
|
|
251
|
+
const key2 = `${prefix}.${k}`;
|
|
252
|
+
const value = toOtlpValue(key2, v, policy);
|
|
253
253
|
if (value === null)
|
|
254
254
|
continue;
|
|
255
|
-
out.push({ key, value });
|
|
255
|
+
out.push({ key: key2, value });
|
|
256
256
|
}
|
|
257
257
|
return out;
|
|
258
258
|
}
|
|
@@ -924,19 +924,19 @@ var require_session_trace = __commonJS({
|
|
|
924
924
|
}
|
|
925
925
|
}
|
|
926
926
|
newTrace(sessionId) {
|
|
927
|
-
const
|
|
927
|
+
const key2 = sessionId || "default";
|
|
928
928
|
const traceId = (0, trace_js_1.generateUlid)();
|
|
929
929
|
const map = this.read();
|
|
930
|
-
map[
|
|
930
|
+
map[key2] = traceId;
|
|
931
931
|
this.write(map);
|
|
932
932
|
return traceId;
|
|
933
933
|
}
|
|
934
934
|
currentTrace(sessionId) {
|
|
935
|
-
const
|
|
936
|
-
const existing = this.read()[
|
|
935
|
+
const key2 = sessionId || "default";
|
|
936
|
+
const existing = this.read()[key2];
|
|
937
937
|
if (existing)
|
|
938
938
|
return existing;
|
|
939
|
-
return this.newTrace(
|
|
939
|
+
return this.newTrace(key2);
|
|
940
940
|
}
|
|
941
941
|
};
|
|
942
942
|
exports.DiskSessionTraceManager = DiskSessionTraceManager;
|
|
@@ -947,9 +947,9 @@ var require_session_trace = __commonJS({
|
|
|
947
947
|
this.maxSessions = opts.maxSessions ?? DEFAULT_MAX_SESSIONS;
|
|
948
948
|
}
|
|
949
949
|
newTrace(sessionId) {
|
|
950
|
-
const
|
|
950
|
+
const key2 = sessionId || "default";
|
|
951
951
|
const traceId = (0, trace_js_1.generateUlid)();
|
|
952
|
-
this.map.set(
|
|
952
|
+
this.map.set(key2, traceId);
|
|
953
953
|
if (this.map.size > this.maxSessions) {
|
|
954
954
|
const oldest = this.map.keys().next().value;
|
|
955
955
|
if (oldest !== void 0)
|
|
@@ -958,8 +958,8 @@ var require_session_trace = __commonJS({
|
|
|
958
958
|
return traceId;
|
|
959
959
|
}
|
|
960
960
|
currentTrace(sessionId) {
|
|
961
|
-
const
|
|
962
|
-
return this.map.get(
|
|
961
|
+
const key2 = sessionId || "default";
|
|
962
|
+
return this.map.get(key2) ?? this.newTrace(key2);
|
|
963
963
|
}
|
|
964
964
|
};
|
|
965
965
|
exports.MemorySessionTraceManager = MemorySessionTraceManager2;
|
|
@@ -994,13 +994,13 @@ var require_env_file = __commonJS({
|
|
|
994
994
|
const idx = line.indexOf("=");
|
|
995
995
|
if (idx < 0)
|
|
996
996
|
continue;
|
|
997
|
-
const
|
|
997
|
+
const key2 = line.slice(0, idx).trim();
|
|
998
998
|
let value = line.slice(idx + 1).trim();
|
|
999
999
|
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
1000
1000
|
value = value.slice(1, -1);
|
|
1001
1001
|
}
|
|
1002
|
-
if (
|
|
1003
|
-
out[
|
|
1002
|
+
if (key2)
|
|
1003
|
+
out[key2] = value;
|
|
1004
1004
|
}
|
|
1005
1005
|
return out;
|
|
1006
1006
|
}
|
|
@@ -1012,9 +1012,9 @@ var require_env_file = __commonJS({
|
|
|
1012
1012
|
return;
|
|
1013
1013
|
}
|
|
1014
1014
|
const parsed = parseEnvFile2(content);
|
|
1015
|
-
for (const [
|
|
1016
|
-
if (process.env[
|
|
1017
|
-
process.env[
|
|
1015
|
+
for (const [key2, value] of Object.entries(parsed)) {
|
|
1016
|
+
if (process.env[key2] === void 0) {
|
|
1017
|
+
process.env[key2] = value;
|
|
1018
1018
|
}
|
|
1019
1019
|
}
|
|
1020
1020
|
}
|
|
@@ -1242,7 +1242,7 @@ var import_core8 = __toESM(require_dist(), 1);
|
|
|
1242
1242
|
var import_core5 = __toESM(require_dist(), 1);
|
|
1243
1243
|
|
|
1244
1244
|
// src/core/version.ts
|
|
1245
|
-
var ADAPTER_VERSION = "0.
|
|
1245
|
+
var ADAPTER_VERSION = "0.10.0";
|
|
1246
1246
|
|
|
1247
1247
|
// src/core/guard.ts
|
|
1248
1248
|
var DEFAULT_TIMEOUT_MS = 50;
|
|
@@ -1327,6 +1327,258 @@ function buildOtlpPayload(args) {
|
|
|
1327
1327
|
});
|
|
1328
1328
|
}
|
|
1329
1329
|
|
|
1330
|
+
// src/model.ts
|
|
1331
|
+
var MODEL_CACHE_LIMITS = {
|
|
1332
|
+
messages: 1024,
|
|
1333
|
+
requests: 512,
|
|
1334
|
+
calls: 2048,
|
|
1335
|
+
endedSessions: 128,
|
|
1336
|
+
ttlMs: 15 * 60 * 1e3
|
|
1337
|
+
};
|
|
1338
|
+
var PLACEHOLDERS = /* @__PURE__ */ new Set([
|
|
1339
|
+
"unknown",
|
|
1340
|
+
"undefined",
|
|
1341
|
+
"null",
|
|
1342
|
+
"n/a",
|
|
1343
|
+
"none",
|
|
1344
|
+
"-",
|
|
1345
|
+
"auto",
|
|
1346
|
+
"default"
|
|
1347
|
+
]);
|
|
1348
|
+
function identifier(value) {
|
|
1349
|
+
if (typeof value !== "string") return void 0;
|
|
1350
|
+
const text = value.trim();
|
|
1351
|
+
return text && text.length <= 1024 ? text : void 0;
|
|
1352
|
+
}
|
|
1353
|
+
function modelId(value) {
|
|
1354
|
+
const id = identifier(value);
|
|
1355
|
+
if (!id || id.startsWith("{") || id.startsWith("[") || PLACEHOLDERS.has(id.toLowerCase())) return void 0;
|
|
1356
|
+
return id;
|
|
1357
|
+
}
|
|
1358
|
+
function record(value) {
|
|
1359
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
1360
|
+
}
|
|
1361
|
+
function fromModel(value, source, provider) {
|
|
1362
|
+
const obj = record(value);
|
|
1363
|
+
const model = obj ? modelId(obj.modelID) ?? modelId(obj.id) : modelId(value);
|
|
1364
|
+
if (!model) return void 0;
|
|
1365
|
+
return {
|
|
1366
|
+
model,
|
|
1367
|
+
source: obj ? `${source}.${modelId(obj.modelID) ? "modelID" : "id"}` : source,
|
|
1368
|
+
provider: modelId(obj?.providerID ?? provider)
|
|
1369
|
+
};
|
|
1370
|
+
}
|
|
1371
|
+
function key(...parts) {
|
|
1372
|
+
return JSON.stringify(parts);
|
|
1373
|
+
}
|
|
1374
|
+
function agreed(...values) {
|
|
1375
|
+
const ids = values.map(identifier).filter((id) => id !== void 0);
|
|
1376
|
+
return ids.every((id) => id === ids[0]) ? ids[0] : void 0;
|
|
1377
|
+
}
|
|
1378
|
+
function eventSessionID(type, props) {
|
|
1379
|
+
const info = record(props.info);
|
|
1380
|
+
const part = record(props.part);
|
|
1381
|
+
return typeof type === "string" && type.startsWith("session.") ? agreed(props.sessionID, info?.id) : agreed(props.sessionID, info?.sessionID, part?.sessionID);
|
|
1382
|
+
}
|
|
1383
|
+
var BoundedCache = class {
|
|
1384
|
+
constructor(limit, now) {
|
|
1385
|
+
this.limit = limit;
|
|
1386
|
+
this.now = now;
|
|
1387
|
+
}
|
|
1388
|
+
entries = /* @__PURE__ */ new Map();
|
|
1389
|
+
get(id) {
|
|
1390
|
+
const entry = this.entries.get(id);
|
|
1391
|
+
if (entry && entry.expires > this.now()) return entry.value;
|
|
1392
|
+
this.entries.delete(id);
|
|
1393
|
+
return void 0;
|
|
1394
|
+
}
|
|
1395
|
+
set(id, value) {
|
|
1396
|
+
this.entries.delete(id);
|
|
1397
|
+
this.entries.set(id, { value, expires: this.now() + MODEL_CACHE_LIMITS.ttlMs });
|
|
1398
|
+
if (this.entries.size > this.limit) this.entries.delete(this.entries.keys().next().value);
|
|
1399
|
+
}
|
|
1400
|
+
removeWhere(predicate) {
|
|
1401
|
+
for (const [id, entry] of this.entries) {
|
|
1402
|
+
if (predicate(entry.value)) this.entries.delete(id);
|
|
1403
|
+
}
|
|
1404
|
+
}
|
|
1405
|
+
clearSession(sessionID) {
|
|
1406
|
+
this.removeWhere((entry) => entry.sessionID === sessionID);
|
|
1407
|
+
}
|
|
1408
|
+
};
|
|
1409
|
+
var ModelTracker = class {
|
|
1410
|
+
constructor(now = Date.now) {
|
|
1411
|
+
this.now = now;
|
|
1412
|
+
this.messages = new BoundedCache(MODEL_CACHE_LIMITS.messages, now);
|
|
1413
|
+
this.requests = new BoundedCache(MODEL_CACHE_LIMITS.requests, now);
|
|
1414
|
+
this.calls = new BoundedCache(MODEL_CACHE_LIMITS.calls, now);
|
|
1415
|
+
}
|
|
1416
|
+
messages;
|
|
1417
|
+
requests;
|
|
1418
|
+
calls;
|
|
1419
|
+
ended = /* @__PURE__ */ new Map();
|
|
1420
|
+
chatMessage(input, output) {
|
|
1421
|
+
const sessionID = agreed(input.sessionID, output?.message?.sessionID);
|
|
1422
|
+
if (!sessionID) return;
|
|
1423
|
+
this.ended.delete(sessionID);
|
|
1424
|
+
const messageID = agreed(input.messageID, output?.message?.id);
|
|
1425
|
+
if (!messageID) return;
|
|
1426
|
+
const agent = identifier(output?.message?.agent ?? input.agent);
|
|
1427
|
+
const supplied = output?.message?.model !== void 0;
|
|
1428
|
+
this.requests.set(key(sessionID, messageID, agent ?? ""), {
|
|
1429
|
+
sessionID,
|
|
1430
|
+
messageID,
|
|
1431
|
+
evidence: fromModel(
|
|
1432
|
+
supplied ? output.message?.model : input.model,
|
|
1433
|
+
supplied ? "requested:chat.message.message.model" : "requested:chat.message.model"
|
|
1434
|
+
)
|
|
1435
|
+
});
|
|
1436
|
+
}
|
|
1437
|
+
chatParams(input) {
|
|
1438
|
+
const sessionID = agreed(input.sessionID, input.message?.sessionID);
|
|
1439
|
+
const messageID = identifier(input.message?.id);
|
|
1440
|
+
const agent = identifier(input.agent);
|
|
1441
|
+
if (!sessionID || !messageID || !agent || this.ended.has(sessionID)) return;
|
|
1442
|
+
this.requests.set(key(sessionID, messageID, agent), {
|
|
1443
|
+
sessionID,
|
|
1444
|
+
messageID,
|
|
1445
|
+
evidence: fromModel(input.model, "requested:chat.params.model")
|
|
1446
|
+
});
|
|
1447
|
+
}
|
|
1448
|
+
fresh(timestamp) {
|
|
1449
|
+
return timestamp === void 0 || typeof timestamp === "number" && Number.isFinite(timestamp) && timestamp >= this.now() - MODEL_CACHE_LIMITS.ttlMs;
|
|
1450
|
+
}
|
|
1451
|
+
clearSession(sessionID) {
|
|
1452
|
+
this.messages.clearSession(sessionID);
|
|
1453
|
+
this.requests.clearSession(sessionID);
|
|
1454
|
+
this.calls.clearSession(sessionID);
|
|
1455
|
+
}
|
|
1456
|
+
event(type, props) {
|
|
1457
|
+
const sessionID = eventSessionID(type, props);
|
|
1458
|
+
const info = record(props.info);
|
|
1459
|
+
const part = record(props.part);
|
|
1460
|
+
const ended = ["session.idle", "session.deleted", "session.error"].includes(type ?? "") || type === "session.status" && record(props.status)?.type === "idle";
|
|
1461
|
+
if (sessionID && ended) {
|
|
1462
|
+
this.clearSession(sessionID);
|
|
1463
|
+
this.ended.delete(sessionID);
|
|
1464
|
+
this.ended.set(sessionID, true);
|
|
1465
|
+
if (this.ended.size > MODEL_CACHE_LIMITS.endedSessions) this.ended.delete(this.ended.keys().next().value);
|
|
1466
|
+
} else if (sessionID && type === "session.created") {
|
|
1467
|
+
this.clearSession(sessionID);
|
|
1468
|
+
this.ended.delete(sessionID);
|
|
1469
|
+
}
|
|
1470
|
+
if (type === "message.updated" && info) {
|
|
1471
|
+
if (!agreed(props.sessionID, info.sessionID) && (props.sessionID || info.sessionID)) return void 0;
|
|
1472
|
+
const messageID = identifier(info.id);
|
|
1473
|
+
const agent = identifier(info.agent);
|
|
1474
|
+
const role = info.role;
|
|
1475
|
+
let evidence = role === "assistant" ? fromModel(modelId(info.modelID), "reported:message.updated.info.modelID", info.providerID) ?? fromModel(info.model, "reported:message.updated.info.model") : role === "user" ? fromModel(info.model, "requested:message.updated.info.model") : void 0;
|
|
1476
|
+
if (!evidence && role === "user" && !("model" in info) && sessionID && messageID) {
|
|
1477
|
+
evidence = this.requests.get(key(sessionID, messageID, agent ?? ""))?.evidence;
|
|
1478
|
+
}
|
|
1479
|
+
if (role === "user" && "model" in info && !evidence && sessionID && messageID) {
|
|
1480
|
+
this.requests.set(key(sessionID, messageID, agent ?? ""), { sessionID, messageID });
|
|
1481
|
+
}
|
|
1482
|
+
if (evidence) evidence = { ...evidence, agent, messageID };
|
|
1483
|
+
if (sessionID && messageID && !this.ended.has(sessionID) && this.fresh(record(info.time)?.created)) {
|
|
1484
|
+
const old = this.messages.get(key(sessionID, messageID));
|
|
1485
|
+
const ambiguous = old?.ambiguous || Boolean(role === "assistant" && old?.evidence && evidence && (old.evidence.model !== evidence.model || old.evidence.provider !== evidence.provider || old.evidence.agent !== evidence.agent));
|
|
1486
|
+
this.messages.set(key(sessionID, messageID), {
|
|
1487
|
+
sessionID,
|
|
1488
|
+
messageID,
|
|
1489
|
+
evidence: ambiguous ? void 0 : evidence,
|
|
1490
|
+
ambiguous
|
|
1491
|
+
});
|
|
1492
|
+
}
|
|
1493
|
+
return evidence;
|
|
1494
|
+
}
|
|
1495
|
+
if (sessionID && type === "message.removed") {
|
|
1496
|
+
const messageID = identifier(props.messageID);
|
|
1497
|
+
this.messages.removeWhere((entry) => entry.sessionID === sessionID && entry.messageID === messageID);
|
|
1498
|
+
this.requests.removeWhere((entry) => entry.sessionID === sessionID && entry.messageID === messageID);
|
|
1499
|
+
this.calls.removeWhere((entry) => entry.sessionID === sessionID && entry.messageID === messageID);
|
|
1500
|
+
}
|
|
1501
|
+
if (sessionID && type === "message.part.removed") {
|
|
1502
|
+
const partID = identifier(props.partID);
|
|
1503
|
+
if (partID) this.calls.removeWhere((entry) => entry.sessionID === sessionID && entry.partID === partID);
|
|
1504
|
+
}
|
|
1505
|
+
if (type === "message.part.delta") {
|
|
1506
|
+
const messageID = identifier(props.messageID);
|
|
1507
|
+
return sessionID && messageID && !this.ended.has(sessionID) ? this.messages.get(key(sessionID, messageID))?.evidence : void 0;
|
|
1508
|
+
}
|
|
1509
|
+
if (type === "message.part.updated" && part) {
|
|
1510
|
+
if (!sessionID || this.ended.has(sessionID) || !this.fresh(props.time)) return void 0;
|
|
1511
|
+
const messageID = identifier(part.messageID);
|
|
1512
|
+
if (!messageID) return void 0;
|
|
1513
|
+
const callID = identifier(part.callID);
|
|
1514
|
+
if (part.type === "tool" && callID) {
|
|
1515
|
+
const callKey = key(sessionID, callID);
|
|
1516
|
+
const old = this.calls.get(callKey);
|
|
1517
|
+
if (!old?.closed) {
|
|
1518
|
+
const tool = identifier(part.tool);
|
|
1519
|
+
this.calls.set(callKey, {
|
|
1520
|
+
sessionID,
|
|
1521
|
+
messageID,
|
|
1522
|
+
partID: identifier(part.id),
|
|
1523
|
+
tool,
|
|
1524
|
+
pinned: old?.pinned,
|
|
1525
|
+
ambiguous: old?.ambiguous || Boolean(old && (old.messageID !== messageID || old.tool !== tool))
|
|
1526
|
+
});
|
|
1527
|
+
}
|
|
1528
|
+
}
|
|
1529
|
+
return this.messages.get(key(sessionID, messageID))?.evidence;
|
|
1530
|
+
}
|
|
1531
|
+
if (typeof type === "string" && type.startsWith("session.")) {
|
|
1532
|
+
return fromModel(info?.model, `selected:${type}.info.model`) ?? fromModel(props.model, `selected:${type}.model`);
|
|
1533
|
+
}
|
|
1534
|
+
return fromModel(props.model, "reported:event.model") ?? fromModel(modelId(props.modelID), "reported:event.modelID", props.providerID);
|
|
1535
|
+
}
|
|
1536
|
+
tool(input, after) {
|
|
1537
|
+
const direct = fromModel(modelId(input.modelID), "reported:tool.modelID", input.providerID) ?? fromModel(input.model, "reported:tool.model", input.providerID);
|
|
1538
|
+
const sessionID = identifier(input.sessionID);
|
|
1539
|
+
const callID = identifier(input.callID);
|
|
1540
|
+
if (!sessionID || !callID) return direct;
|
|
1541
|
+
const callKey = key(sessionID, callID);
|
|
1542
|
+
const call = this.calls.get(callKey);
|
|
1543
|
+
let evidence = direct;
|
|
1544
|
+
if (!("model" in input) && !("modelID" in input) && call && !call.closed && !call.ambiguous && !this.ended.has(sessionID) && (!call.tool || call.tool === input.tool)) {
|
|
1545
|
+
const message = this.messages.get(key(sessionID, call.messageID));
|
|
1546
|
+
if (!message?.ambiguous) evidence = call.pinned ?? message?.evidence;
|
|
1547
|
+
if (input.agent && evidence?.agent && input.agent !== evidence.agent || input.messageID && input.messageID !== call.messageID) evidence = void 0;
|
|
1548
|
+
}
|
|
1549
|
+
if (call && !call.closed) {
|
|
1550
|
+
this.calls.set(callKey, {
|
|
1551
|
+
...call,
|
|
1552
|
+
pinned: after ? void 0 : evidence,
|
|
1553
|
+
closed: after
|
|
1554
|
+
});
|
|
1555
|
+
}
|
|
1556
|
+
return evidence;
|
|
1557
|
+
}
|
|
1558
|
+
beforeTool(input) {
|
|
1559
|
+
return this.tool(input, false);
|
|
1560
|
+
}
|
|
1561
|
+
afterTool(input) {
|
|
1562
|
+
return this.tool(input, true);
|
|
1563
|
+
}
|
|
1564
|
+
};
|
|
1565
|
+
function modelFields(fields, evidence) {
|
|
1566
|
+
const { model: rawModel, model_source: rawSource, ...out } = fields;
|
|
1567
|
+
if ("model" in fields && rawModel !== evidence?.model) out.model_raw = rawModel;
|
|
1568
|
+
if ("model_source" in fields) out.model_source_raw = rawSource;
|
|
1569
|
+
if (evidence) {
|
|
1570
|
+
out.model = evidence.model;
|
|
1571
|
+
out.model_source = evidence.source;
|
|
1572
|
+
if (evidence.provider) {
|
|
1573
|
+
if ("provider" in out && out.provider !== evidence.provider) out.provider_raw = out.provider;
|
|
1574
|
+
out.provider = evidence.provider;
|
|
1575
|
+
}
|
|
1576
|
+
if (out.agent === void 0) out.agent = evidence.agent;
|
|
1577
|
+
if (out.message_id === void 0) out.message_id = evidence.messageID;
|
|
1578
|
+
}
|
|
1579
|
+
return out;
|
|
1580
|
+
}
|
|
1581
|
+
|
|
1330
1582
|
// src/telemetry.ts
|
|
1331
1583
|
var Telemetry = class {
|
|
1332
1584
|
constructor(transport, trace, config) {
|
|
@@ -1334,6 +1586,7 @@ var Telemetry = class {
|
|
|
1334
1586
|
this.trace = trace;
|
|
1335
1587
|
this.config = config;
|
|
1336
1588
|
}
|
|
1589
|
+
models = new ModelTracker();
|
|
1337
1590
|
build(name, sessionId, fields) {
|
|
1338
1591
|
const traceId = this.trace.currentTrace(sessionId);
|
|
1339
1592
|
return buildOtlpPayload({ name, traceId, fields, serviceVersion: this.config.serviceVersion });
|
|
@@ -1345,17 +1598,24 @@ var Telemetry = class {
|
|
|
1345
1598
|
async emit(name, sessionId, fields) {
|
|
1346
1599
|
await this.send(this.build(name, sessionId, fields));
|
|
1347
1600
|
}
|
|
1601
|
+
chatMessage(input, output) {
|
|
1602
|
+
this.models.chatMessage(input, output);
|
|
1603
|
+
}
|
|
1604
|
+
chatParams(input) {
|
|
1605
|
+
this.models.chatParams(input);
|
|
1606
|
+
}
|
|
1348
1607
|
/** Lifecycle span from the `event` hook. Flushes the retry buffer on turn-END. */
|
|
1349
1608
|
async lifecycle(ev) {
|
|
1350
|
-
const props = ev.properties ?? {};
|
|
1351
|
-
const sessionId =
|
|
1352
|
-
|
|
1609
|
+
const props = record(ev.properties) ?? {};
|
|
1610
|
+
const sessionId = eventSessionID(ev.type, props);
|
|
1611
|
+
const model = this.models.event(ev.type, props);
|
|
1612
|
+
await this.emit(`opencode.event.${ev.type ?? "unknown"}`, sessionId, modelFields({
|
|
1353
1613
|
hook: "event",
|
|
1354
1614
|
event_type: ev.type,
|
|
1355
1615
|
session_id: sessionId,
|
|
1356
1616
|
cwd: process.cwd(),
|
|
1357
1617
|
...props
|
|
1358
|
-
});
|
|
1618
|
+
}, model));
|
|
1359
1619
|
if (ev.type === "session.idle") await this.transport.flush();
|
|
1360
1620
|
}
|
|
1361
1621
|
/**
|
|
@@ -1368,7 +1628,10 @@ var Telemetry = class {
|
|
|
1368
1628
|
return this.build(
|
|
1369
1629
|
"opencode.tool.before",
|
|
1370
1630
|
input.sessionID,
|
|
1371
|
-
|
|
1631
|
+
modelFields(
|
|
1632
|
+
{ ...toolIdentity("tool.execute.before", input), tool_input: args },
|
|
1633
|
+
this.models.beforeTool(input)
|
|
1634
|
+
)
|
|
1372
1635
|
);
|
|
1373
1636
|
}
|
|
1374
1637
|
/** Tool span from `tool.execute.before`, carrying an already-known guard decision. */
|
|
@@ -1378,13 +1641,13 @@ var Telemetry = class {
|
|
|
1378
1641
|
/** Tool result span from `tool.execute.after`, incl. exit code / truncation. */
|
|
1379
1642
|
async toolAfter(input, output) {
|
|
1380
1643
|
const meta = output.metadata ?? {};
|
|
1381
|
-
await this.emit("opencode.tool.after", input.sessionID, {
|
|
1644
|
+
await this.emit("opencode.tool.after", input.sessionID, modelFields({
|
|
1382
1645
|
...toolIdentity("tool.execute.after", input),
|
|
1383
1646
|
title: output.title,
|
|
1384
1647
|
tool_response: output.output,
|
|
1385
1648
|
exit: meta.exit,
|
|
1386
1649
|
truncated: meta.truncated
|
|
1387
|
-
});
|
|
1650
|
+
}, this.models.afterTool(input)));
|
|
1388
1651
|
}
|
|
1389
1652
|
};
|
|
1390
1653
|
function toolIdentity(hook, input) {
|
|
@@ -1393,7 +1656,12 @@ function toolIdentity(hook, input) {
|
|
|
1393
1656
|
tool_name: input.tool,
|
|
1394
1657
|
session_id: input.sessionID,
|
|
1395
1658
|
tool_use_id: input.callID,
|
|
1396
|
-
cwd: process.cwd()
|
|
1659
|
+
cwd: process.cwd(),
|
|
1660
|
+
...input.model !== void 0 ? { model: input.model } : {},
|
|
1661
|
+
...input.modelID !== void 0 ? { modelID: input.modelID } : {},
|
|
1662
|
+
...input.providerID !== void 0 ? { providerID: input.providerID } : {},
|
|
1663
|
+
...input.agent !== void 0 ? { agent: input.agent } : {},
|
|
1664
|
+
...input.messageID !== void 0 ? { message_id: input.messageID } : {}
|
|
1397
1665
|
};
|
|
1398
1666
|
}
|
|
1399
1667
|
|
|
@@ -1433,8 +1701,12 @@ var PintaOpencode = async (_input, options) => {
|
|
|
1433
1701
|
}
|
|
1434
1702
|
return {
|
|
1435
1703
|
// turn-START → rotate a new trace for this session.
|
|
1436
|
-
"chat.message": failOpen("chat.message", async (input) => {
|
|
1704
|
+
"chat.message": failOpen("chat.message", async (input, output) => {
|
|
1437
1705
|
trace.newTrace(input?.sessionID);
|
|
1706
|
+
telemetry.chatMessage(input, output);
|
|
1707
|
+
}),
|
|
1708
|
+
"chat.params": failOpen("chat.params", async (input, _output) => {
|
|
1709
|
+
telemetry.chatParams(input);
|
|
1438
1710
|
}),
|
|
1439
1711
|
// lifecycle telemetry; flushes the retry buffer on session.idle (turn-END).
|
|
1440
1712
|
event: failOpen("event", async (input) => {
|
package/dist/plugin.js
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
import { createRequire as __pintaCreateRequire } from 'module'; const require = __pintaCreateRequire(import.meta.url);
|
|
2
|
-
var
|
|
3
|
-
`;
|
|
4
|
-
`)}}readAll(){try{let e=
|
|
5
|
-
`))if(n.trim())try{let
|
|
2
|
+
var _t=Object.create;var Pe=Object.defineProperty;var vt=Object.getOwnPropertyDescriptor;var Tt=Object.getOwnPropertyNames;var Ot=Object.getPrototypeOf,Pt=Object.prototype.hasOwnProperty;var y=(r=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(r,{get:(e,t)=>(typeof require<"u"?require:e)[t]}):r)(function(r){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+r+'" is not supported')});var P=(r,e)=>()=>(e||r((e={exports:{}}).exports,e),e.exports);var St=(r,e,t,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of Tt(e))!Pt.call(r,s)&&s!==t&&Pe(r,s,{get:()=>e[s],enumerable:!(n=vt(e,s))||n.enumerable});return r};var b=(r,e,t)=>(t=r!=null?_t(Ot(r)):{},St(e||!r||!r.__esModule?Pe(t,"default",{value:r,enumerable:!0}):t,r));var ee=P(h=>{"use strict";Object.defineProperty(h,"__esModule",{value:!0});h.PATTERNS=h.MAX_BYTES=void 0;h.truncateTo=Se;h.truncate=Et;h.collectMatches=Ee;h.resolveOverlaps=be;h.applyMatches=Ie;h.redact=bt;h.MAX_BYTES=102400;function Se(r,e){let t=Buffer.from(r,"utf-8");return t.length<=e?r:`${t.subarray(0,Math.max(0,e)).toString("utf-8")}\u2026[TRUNCATED:${t.length}]`}function Et(r){return Se(r,h.MAX_BYTES)}h.PATTERNS=[{type:"aws_access_key",regex:/AKIA[0-9A-Z]{16}/g},{type:"aws_secret_key",regex:/(?:aws[_-]?secret(?:[_-]?(?:access)?[_-]?key)?)\s*[:=]\s*["']?([A-Za-z0-9/+=]{40})(?![A-Za-z0-9/+=])/gi,captureGroup:1},{type:"gcp_service_account",regex:/\{[\s\S]{0,200}?"type"\s*:\s*"service_account"[\s\S]*?\}/g},{type:"github_token",regex:/gh[pousr]_[A-Za-z0-9]{36,}/g},{type:"gitlab_token",regex:/glpat-[A-Za-z0-9_-]{20}/g},{type:"slack_token",regex:/xox[abrsp]-[0-9A-Za-z-]{10,}/g},{type:"openai_key",regex:/sk-(?:proj-)?[A-Za-z0-9_-]{40,}/g},{type:"anthropic_key",regex:/sk-ant-[A-Za-z0-9_-]{50,}/g},{type:"stripe_key",regex:/(?:sk|rk|pk)_(?:live|test)_[A-Za-z0-9]{20,}/g},{type:"jwt",regex:/eyJ[A-Za-z0-9_-]{10,}\.eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}/g},{type:"private_key_block",regex:/-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/g},{type:"bearer_token",regex:/bearer\s+([A-Za-z0-9._~+/=-]{12,})/gi,captureGroup:1},{type:"basic_auth",regex:/basic\s+([A-Za-z0-9+/=]{12,})/gi,captureGroup:1},{type:"db_url_password",regex:/\b(?:postgres|postgresql|mysql|mariadb|mongodb(?:\+srv)?|redis):\/\/[^:\s/]+:([^@\s]+)@/gi,captureGroup:1},{type:"cli_password_flag",regex:/(?:--password|--pass|--pwd)[=\s]([^\s'"]+)/g,captureGroup:1},{type:"cli_password_short",regex:/\s-p([^\s'"]+)/g,captureGroup:1,requireContext:"bash"},{type:"env_var_secret",regex:/^(?:export\s+)?([A-Z][A-Z0-9_]*(?:KEY|SECRET|TOKEN|PASSWORD|PASSWD|PWD|API_KEY)[A-Z0-9_]*)\s*=\s*["']?([^\s"'\n]+)/gm,captureGroup:2}];function Ee(r,e){let t=[];for(let n of h.PATTERNS){if(n.requireContext&&n.requireContext!==e.context)continue;let s=new RegExp(n.regex.source,n.regex.flags),i;for(;(i=s.exec(r))!==null;){let c=n.captureGroup??0,o=i[c];if(o===void 0){i.index===s.lastIndex&&s.lastIndex++;continue}let u=i.index,d=i.index+i[0].length,l=u+i[0].indexOf(o),O=l+o.length;t.push({start:u,end:d,replaceStart:l,replaceEnd:O,type:n.type}),i.index===s.lastIndex&&s.lastIndex++}}return t}function be(r){let e=[...r].sort((s,i)=>s.start!==i.start?s.start-i.start:i.end-i.start-(s.end-s.start)),t=[],n=-1;for(let s of e)s.start<n||(t.push(s),n=s.end);return t}function Ie(r,e){let t=[...e].sort((s,i)=>i.replaceStart-s.replaceStart),n=r;for(let s of t)n=n.slice(0,s.replaceStart)+`[REDACTED:${s.type}]`+n.slice(s.replaceEnd);return n}function bt(r,e={}){if(r.length===0)return r;let t=Ee(r,e);if(t.length===0)return r;let n=be(t);return Ie(r,n)}});var re=P(p=>{"use strict";var It=p&&p.__importDefault||function(r){return r&&r.__esModule?r:{default:r}};Object.defineProperty(p,"__esModule",{value:!0});p.MAX_SPAN_BYTES=void 0;p.ulidToTraceId=Ae;p.newSpanId=ke;p.snakeCase=wt;p.toOtlpValue=Re;p.attrsFromRecord=At;p.guardAttrs=xe;p.clientAttrs=je;p.enforceSpanBudget=Ne;p.buildPayload=kt;p.attachGuard=Ce;p.mergeBatch=Rt;var Dt=It(y("crypto")),te=ee(),Mt="0123456789ABCDEFGHJKMNPQRSTVWXYZ";function Ae(r){if(r.length!==26)throw new Error(`ulidToTraceId: expected 26 chars, got ${r.length}`);let e=0n;for(let n of r){let s=Mt.indexOf(n);if(s<0)throw new Error(`ulidToTraceId: invalid Crockford char "${n}"`);e=e<<5n|BigInt(s)}let t=(1n<<128n)-1n;return e&=t,e.toString(16).padStart(32,"0")}function ke(){return Dt.default.randomBytes(8).toString("hex")}function wt(r){return r.replace(/([a-z0-9])([A-Z])/g,"$1_$2").replace(/([A-Z])([A-Z][a-z])/g,"$1_$2").toLowerCase()}var De=new Set;function V(r,e,t){let n=(0,te.truncate)(e);if((t.skipRedactKeys??De).has(r))return n;let s=(t.bashContextKeys??De).has(r)?"bash":void 0;return(0,te.redact)(n,{context:s})}function Re(r,e,t={}){if(e==null)return null;switch(typeof e){case"string":return{stringValue:V(r,e,t)};case"boolean":return{boolValue:e};case"number":return Number.isInteger(e)?{intValue:e}:{doubleValue:e};case"object":try{return{stringValue:V(r,JSON.stringify(e),t)}}catch{return{stringValue:V(r,String(e),t)}}default:return{stringValue:V(r,String(e),t)}}}function At(r,e,t={}){let n=[];for(let[s,i]of Object.entries(r)){let c=`${e}.${s}`,o=Re(c,i,t);o!==null&&n.push({key:c,value:o})}return n}function xe(r){let e=[{key:"pinta.guard.decision",value:{stringValue:r.decision.toLowerCase()}},{key:"pinta.guard.duration_ms",value:{intValue:r.durationMs}}];return r.reason&&e.push({key:"pinta.guard.matched_rule",value:{stringValue:r.reason}}),r.failOpenReason&&e.push({key:"pinta.guard.fail_open_reason",value:{stringValue:r.failOpenReason}}),e}function je(r){return[{key:"pinta.client.rtt_ms",value:Number.isInteger(r.rttMs)?{intValue:r.rttMs}:{doubleValue:r.rttMs}},{key:"pinta.client.op",value:{stringValue:r.op}}]}p.MAX_SPAN_BYTES=800*1024;function Me(r){let e=JSON.stringify(r);return e===void 0?0:Buffer.byteLength(e,"utf-8")}var we=1024;function Ne(r,e=p.MAX_SPAN_BYTES){let t=Me(r);for(let n=0;n<100&&t>e;n++){let s=null,i=0;for(let u of r.attributes){if(!("stringValue"in u.value))continue;let d=Buffer.byteLength(u.value.stringValue,"utf-8");d>i&&(i=d,s=u.value)}if(s===null||i<=we)break;let c=t-e,o=Math.max(we,i-c-64);s.stringValue=(0,te.truncateTo)(s.stringValue,o),t=Me(r)}return r}function kt(r){let e=r.now??Date.now(),t=(BigInt(e)*1000000n).toString(),n=[...r.attributes],s={traceId:Ae(r.traceId),spanId:ke(),name:r.spanName,kind:r.spanKind??1,startTimeUnixNano:t,endTimeUnixNano:t,attributes:n};Ne(s);let i={resourceSpans:[{resource:{attributes:r.resource},scopeSpans:[{scope:r.scope,spans:[s]}]}]};return Ce(i,r.guard,r.clientTiming)}function Ce(r,e,t){let n=r.resourceSpans[0]?.scopeSpans[0]?.spans[0];if(!n)return r;e&&n.attributes.push(...xe(e));let s=t??(e?.clientRttMs!==void 0?{op:"guard",rttMs:e.clientRttMs}:null);return s&&n.attributes.push(...je(s)),r}function Rt(r){let e=[];for(let t of r)e.push(...t.resourceSpans);return{resourceSpans:e}}});var qe=P(R=>{"use strict";Object.defineProperty(R,"__esModule",{value:!0});R.GUARD_STATUS_REFUSED=void 0;R.evaluateGuard=Ct;var xt=1e4,jt="pinta-core";function G(r,e){return{decision:"ALLOW",reason:null,userMessage:null,durationMs:r,clientRttMs:r,failOpenReason:e}}var Nt=new Set(["ALLOW","DENY","REVIEW"]);R.GUARD_STATUS_REFUSED=410;async function Ct(r,e,t={}){if(!e||t.disabled)return null;let n=t.timeoutMs??xt,s=Date.now(),i=new AbortController,c=setTimeout(()=>i.abort(),n);try{let o=await fetch(e,{method:"POST",headers:{"content-type":"application/json","user-agent":t.userAgent??jt,"x-pinta-relay-token":t.token??"",...t.agentType?{"x-pinta-agent-type":t.agentType}:{}},body:JSON.stringify(r),signal:i.signal});if(o.status===R.GUARD_STATUS_REFUSED)return G(Date.now()-s,"refused");if(o.status!==200)return G(Date.now()-s,"error");let u=await o.json(),d=u?.durationMs===void 0||typeof u.durationMs=="number"&&Number.isFinite(u.durationMs);if(!u||!Nt.has(u.decision)||!d)return G(Date.now()-s,"error");let l=Date.now()-s;return{decision:u.decision,reason:u.reason??null,userMessage:u.userMessage??null,durationMs:u.durationMs??l,clientRttMs:l}}catch(o){let u=o.name,d=u==="AbortError"||u==="TimeoutError"?"timeout":"error";return G(Date.now()-s,d)}finally{clearTimeout(c)}}});var L=P(q=>{"use strict";var qt=q&&q.__importDefault||function(r){return r&&r.__esModule?r:{default:r}};Object.defineProperty(q,"__esModule",{value:!0});q.atomicWriteFileSync=Bt;var Be=qt(y("fs"));function Bt(r,e){let t=`${r}.${process.pid}.tmp`;Be.default.writeFileSync(t,e),Be.default.renameSync(t,r)}});var oe=P(I=>{"use strict";var Fe=I&&I.__importDefault||function(r){return r&&r.__esModule?r:{default:r}};Object.defineProperty(I,"__esModule",{value:!0});I.MemoryRetryQueue=I.DiskRetryQueue=void 0;var m=Fe(y("fs")),U=Fe(y("path")),Ft=L(),X=1e3,$t=50,Vt=5,ne=class{filePath;lockPath;logPrefix;constructor(e,t){this.filePath=U.default.join(e,"failed-spans.jsonl"),this.lockPath=this.filePath+".lock",this.logPrefix=t}enqueue(e){try{m.default.mkdirSync(U.default.dirname(this.filePath),{recursive:!0});let t=JSON.stringify({savedAt:new Date().toISOString(),payload:e})+`
|
|
3
|
+
`;m.default.appendFileSync(this.filePath,t),this.trim()}catch(t){process.stderr.write(`[${this.logPrefix}] retry-queue enqueue failed: ${t}
|
|
4
|
+
`)}}readAll(){try{let e=m.default.readFileSync(this.filePath,"utf-8"),t=[];for(let n of e.split(`
|
|
5
|
+
`))if(n.trim())try{let s=JSON.parse(n);Array.isArray(s?.payload?.resourceSpans)&&t.push(s)}catch{}return t}catch{return[]}}rewrite(e){try{if(e.length===0){m.default.existsSync(this.filePath)&&m.default.unlinkSync(this.filePath);return}m.default.mkdirSync(U.default.dirname(this.filePath),{recursive:!0}),(0,Ft.atomicWriteFileSync)(this.filePath,e.map(t=>JSON.stringify(t)).join(`
|
|
6
6
|
`)+`
|
|
7
|
-
`)}catch(
|
|
8
|
-
`)}}tryAcquireLock(){let e=Date.now();try{
|
|
9
|
-
`),!1}for(;Date.now()-e
|
|
10
|
-
`),!1;try{let i=
|
|
11
|
-
`),this.rewrite(e.slice(
|
|
12
|
-
`),!1}return!0}catch(i){return process.stderr.write(`[${
|
|
13
|
-
`),!1}finally{clearTimeout(
|
|
14
|
-
`);let i=[].concat(...n),
|
|
15
|
-
`)}finally{this.queue.release()}}dropIfOversized(e){let
|
|
16
|
-
`),!0)}};
|
|
17
|
-
`);return}await
|
|
18
|
-
`);let i=[].concat(...n),
|
|
19
|
-
`)}}};
|
|
20
|
-
`)){let n=r.trim();if(!n||n.startsWith("#"))continue;let o=n.indexOf("=");if(o<0)continue;let i=n.slice(0,o).trim(),u=n.slice(o+1).trim();(u.startsWith('"')&&u.endsWith('"')||u.startsWith("'")&&u.endsWith("'"))&&(u=u.slice(1,-1)),i&&(e[i]=u)}return e}function $t(t){let e;try{e=Bt.default.readFileSync(t,"utf-8")}catch{return}let r=Ge(e);for(let[n,o]of Object.entries(r))process.env[n]===void 0&&(process.env[n]=o)}});var T=m(s=>{"use strict";Object.defineProperty(s,"__esModule",{value:!0});s.loadEnvFile=s.parseEnvFile=s.envFilePath=s.MemorySessionTraceManager=s.DiskSessionTraceManager=s.generateUlid=s.TraceManager=s.MAX_POST_BYTES=s.parseHeadersEnv=s.envOptionsResolver=s.postOtlp=s.MemoryTransport=s.DiskTransport=s.MemoryRetryQueue=s.DiskRetryQueue=s.GUARD_STATUS_REFUSED=s.evaluateGuard=s.MAX_SPAN_BYTES=s.enforceSpanBudget=s.mergeBatch=s.attachGuard=s.buildPayload=s.clientAttrs=s.guardAttrs=s.attrsFromRecord=s.toOtlpValue=s.snakeCase=s.newSpanId=s.ulidToTraceId=s.MAX_BYTES=s.PATTERNS=s.applyMatches=s.resolveOverlaps=s.collectMatches=s.truncateTo=s.truncate=s.redact=void 0;var v=V();Object.defineProperty(s,"redact",{enumerable:!0,get:function(){return v.redact}});Object.defineProperty(s,"truncate",{enumerable:!0,get:function(){return v.truncate}});Object.defineProperty(s,"truncateTo",{enumerable:!0,get:function(){return v.truncateTo}});Object.defineProperty(s,"collectMatches",{enumerable:!0,get:function(){return v.collectMatches}});Object.defineProperty(s,"resolveOverlaps",{enumerable:!0,get:function(){return v.resolveOverlaps}});Object.defineProperty(s,"applyMatches",{enumerable:!0,get:function(){return v.applyMatches}});Object.defineProperty(s,"PATTERNS",{enumerable:!0,get:function(){return v.PATTERNS}});Object.defineProperty(s,"MAX_BYTES",{enumerable:!0,get:function(){return v.MAX_BYTES}});var y=U();Object.defineProperty(s,"ulidToTraceId",{enumerable:!0,get:function(){return y.ulidToTraceId}});Object.defineProperty(s,"newSpanId",{enumerable:!0,get:function(){return y.newSpanId}});Object.defineProperty(s,"snakeCase",{enumerable:!0,get:function(){return y.snakeCase}});Object.defineProperty(s,"toOtlpValue",{enumerable:!0,get:function(){return y.toOtlpValue}});Object.defineProperty(s,"attrsFromRecord",{enumerable:!0,get:function(){return y.attrsFromRecord}});Object.defineProperty(s,"guardAttrs",{enumerable:!0,get:function(){return y.guardAttrs}});Object.defineProperty(s,"clientAttrs",{enumerable:!0,get:function(){return y.clientAttrs}});Object.defineProperty(s,"buildPayload",{enumerable:!0,get:function(){return y.buildPayload}});Object.defineProperty(s,"attachGuard",{enumerable:!0,get:function(){return y.attachGuard}});Object.defineProperty(s,"mergeBatch",{enumerable:!0,get:function(){return y.mergeBatch}});Object.defineProperty(s,"enforceSpanBudget",{enumerable:!0,get:function(){return y.enforceSpanBudget}});Object.defineProperty(s,"MAX_SPAN_BYTES",{enumerable:!0,get:function(){return y.MAX_SPAN_BYTES}});var Le=Ee();Object.defineProperty(s,"evaluateGuard",{enumerable:!0,get:function(){return Le.evaluateGuard}});Object.defineProperty(s,"GUARD_STATUS_REFUSED",{enumerable:!0,get:function(){return Le.GUARD_STATUS_REFUSED}});var Ue=z();Object.defineProperty(s,"DiskRetryQueue",{enumerable:!0,get:function(){return Ue.DiskRetryQueue}});Object.defineProperty(s,"MemoryRetryQueue",{enumerable:!0,get:function(){return Ue.MemoryRetryQueue}});var k=xe();Object.defineProperty(s,"DiskTransport",{enumerable:!0,get:function(){return k.DiskTransport}});Object.defineProperty(s,"MemoryTransport",{enumerable:!0,get:function(){return k.MemoryTransport}});Object.defineProperty(s,"postOtlp",{enumerable:!0,get:function(){return k.postOtlp}});Object.defineProperty(s,"envOptionsResolver",{enumerable:!0,get:function(){return k.envOptionsResolver}});Object.defineProperty(s,"parseHeadersEnv",{enumerable:!0,get:function(){return k.parseHeadersEnv}});Object.defineProperty(s,"MAX_POST_BYTES",{enumerable:!0,get:function(){return k.MAX_POST_BYTES}});var Xe=Q();Object.defineProperty(s,"TraceManager",{enumerable:!0,get:function(){return Xe.TraceManager}});Object.defineProperty(s,"generateUlid",{enumerable:!0,get:function(){return Xe.generateUlid}});var Ye=$e();Object.defineProperty(s,"DiskSessionTraceManager",{enumerable:!0,get:function(){return Ye.DiskSessionTraceManager}});Object.defineProperty(s,"MemorySessionTraceManager",{enumerable:!0,get:function(){return Ye.MemorySessionTraceManager}});var ne=Ve();Object.defineProperty(s,"envFilePath",{enumerable:!0,get:function(){return ne.envFilePath}});Object.defineProperty(s,"parseEnvFile",{enumerable:!0,get:function(){return ne.parseEnvFile}});Object.defineProperty(s,"loadEnvFile",{enumerable:!0,get:function(){return ne.loadEnvFile}})});var Ke=O(T(),1);import Gt from"node:fs";import oe from"node:path";var D=O(T(),1);function Ct(){return(0,D.envFilePath)(".config/opencode","pinta-opencode.env","OPENCODE_CONFIG_DIR")}function ze(t=Ct()){(0,D.loadEnvFile)(t)}function se(...t){return t.find(e=>e)||void 0}function Vt(t){let e=se(t.endpoint,process.env.PINTA_OPENCODE_ENDPOINT,process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT);if(e)return e.replace(/\/+$/,"");let r=process.env.OTEL_EXPORTER_OTLP_ENDPOINT;if(r)return r.replace(/\/+$/,"")+"/v1/traces"}var Lt="opencode-ai";function Ut(){let t=oe.dirname(process.execPath);for(let e=0;e<5;e++){try{let n=JSON.parse(Gt.readFileSync(oe.join(t,"package.json"),"utf8"));if(n.name===Lt&&typeof n.version=="string"&&n.version)return n.version}catch{}let r=oe.dirname(t);if(r===t)break;t=r}}function Xt(){let t=process.env.OPENCODE_VERSION?.trim();return t||Ut()}function Ze(t={}){ze();let e=se(t.token,process.env.PINTA_OPENCODE_TOKEN),r=(0,Ke.parseHeadersEnv)(t.headers??process.env.PINTA_OPENCODE_HEADERS??process.env.OTEL_EXPORTER_OTLP_HEADERS);e&&!Object.keys(r).some(o=>o.toLowerCase()==="x-pinta-relay-token")&&(r["x-pinta-relay-token"]=e);let n=t.guardTimeoutMs??(Number(process.env.PINTA_OPENCODE_GUARD_TIMEOUT_MS)||50);return{endpoint:Vt(t),headers:r,guardEndpoint:se(t.guard,process.env.PINTA_OPENCODE_GUARD),relayToken:e,guardTimeoutMs:n,guardDisabled:process.env.PINTA_OPENCODE_GUARD_DISABLED==="1",serviceVersion:Xt()}}var We=O(T(),1),F=class extends We.MemoryTransport{constructor(e){super({logPrefix:"pinta-opencode",resolveOptions:()=>e.endpoint?{endpoint:e.endpoint,headers:e.headers}:null})}};var ie=O(T(),1);var nt=O(T(),1);var Je=O(T(),1);var $="0.9.0";var Yt=50,zt=`pinta-opencode/${$}`;function He(t,e,r={}){return(0,Je.evaluateGuard)(t,e,{timeoutMs:r.timeoutMs??Yt,token:r.token,disabled:r.disabled,userAgent:zt})}var rt=O(T(),1);import ae from"os";var w=O(T(),1);var C;function Kt(){if(C===void 0)try{C=ae.userInfo().username}catch{C=process.env.USER??process.env.LOGNAME??(typeof process.getuid=="function"?String(process.getuid()):"unknown")}return C}var Qe=$,Zt=new Set(["opencode.hook","opencode.event_type","opencode.tool_name","opencode.session_id","opencode.sessionID","opencode.tool_use_id","opencode.cwd","opencode.agent","opencode.model","opencode.exit","opencode.truncated","opencode.title"]),Wt=new Set(["opencode.tool_input","opencode.tool_response"]),Jt={skipRedactKeys:Zt,bashContextKeys:Wt};function Ht(t){let e=[{key:"ingest.type",value:{stringValue:"opencode"}}];return e.push(...(0,w.attrsFromRecord)(t,"opencode",Jt)),e}function Qt(t){return[{key:"service.name",value:{stringValue:"opencode"}},...t?[{key:"service.version",value:{stringValue:t}}]:[],{key:"telemetry.sdk.name",value:{stringValue:"pinta-opencode"}},{key:"telemetry.sdk.language",value:{stringValue:"nodejs"}},{key:"telemetry.sdk.version",value:{stringValue:Qe}},{key:"process.pid",value:{intValue:process.pid}},{key:"process.owner",value:{stringValue:Kt()}},{key:"host.name",value:{stringValue:ae.hostname()}},{key:"host.arch",value:{stringValue:ae.arch()}}]}function et(t){return(0,w.buildPayload)({traceId:t.traceId,spanName:t.name,attributes:Ht(t.fields),resource:Qt(t.serviceVersion),scope:{name:"pinta-opencode",version:Qe},now:t.now})}var G=class{constructor(e,r,n){this.transport=e;this.trace=r;this.config=n}build(e,r,n){let o=this.trace.currentTrace(r);return et({name:e,traceId:o,fields:n,serviceVersion:this.config.serviceVersion})}async send(e){await this.transport.send(e)}async emit(e,r,n){await this.send(this.build(e,r,n))}async lifecycle(e){let r=e.properties??{},n=typeof r.sessionID=="string"?r.sessionID:void 0;await this.emit(`opencode.event.${e.type??"unknown"}`,n,{hook:"event",event_type:e.type,session_id:n,cwd:process.cwd(),...r}),e.type==="session.idle"&&await this.transport.flush()}toolBeforePayload(e,r){return this.build("opencode.tool.before",e.sessionID,{...tt("tool.execute.before",e),tool_input:r})}async toolBefore(e,r,n){await this.send((0,rt.attachGuard)(this.toolBeforePayload(e,r),n))}async toolAfter(e,r){let n=r.metadata??{};await this.emit("opencode.tool.after",e.sessionID,{...tt("tool.execute.after",e),title:r.title,tool_response:r.output,exit:n.exit,truncated:n.truncated})}};function tt(t,e){return{hook:t,tool_name:e.tool,session_id:e.sessionID,tool_use_id:e.callID,cwd:process.cwd()}}function ot(t,e){process.stderr.write(`[pinta-opencode] ${t}: ${e?.message??String(e)}
|
|
21
|
-
`)}function
|
|
7
|
+
`)}catch(t){process.stderr.write(`[${this.logPrefix}] retry-queue rewrite failed: ${t}
|
|
8
|
+
`)}}tryAcquireLock(){let e=Date.now();try{m.default.mkdirSync(U.default.dirname(this.lockPath),{recursive:!0})}catch(t){return process.stderr.write(`[${this.logPrefix}] retry-queue lock mkdir failed: ${t}
|
|
9
|
+
`),!1}for(;Date.now()-e<$t;)try{let t=m.default.openSync(this.lockPath,"wx");return m.default.writeSync(t,String(process.pid)),m.default.closeSync(t),!0}catch(t){if(t?.code!=="EEXIST")return process.stderr.write(`[${this.logPrefix}] retry-queue lock open failed: ${t}
|
|
10
|
+
`),!1;try{let i=m.default.statSync(this.lockPath);if(Date.now()-i.mtimeMs>3e4){m.default.unlinkSync(this.lockPath);continue}}catch{}let n=Vt,s=Date.now()+n;for(;Date.now()<s;);}return!1}release(){try{m.default.unlinkSync(this.lockPath)}catch{}}trim(){let e=this.readAll();if(e.length<=X)return;let t=e.length-X;process.stderr.write(`[${this.logPrefix}] retry-queue full, dropping ${t} oldest entries
|
|
11
|
+
`),this.rewrite(e.slice(t))}};I.DiskRetryQueue=ne;var se=class{entries=[];enqueue(e){this.entries.push(e),this.entries.length>X&&this.entries.splice(0,this.entries.length-X)}drain(){let e=this.entries;return this.entries=[],e}get size(){return this.entries.length}};I.MemoryRetryQueue=se});var Xe=P(f=>{"use strict";Object.defineProperty(f,"__esModule",{value:!0});f.MemoryTransport=f.DiskTransport=f.MAX_POST_BYTES=void 0;f.parseHeadersEnv=Le;f.envOptionsResolver=Ue;f.postOtlp=B;var $e=oe(),Ve=re(),Gt=5e3;f.MAX_POST_BYTES=900*1024;function Y(r){let e=JSON.stringify(r);return e===void 0?0:Buffer.byteLength(e,"utf-8")}function Ge(r,e,t){let n=[],s=[],i=[],c=0;for(let o of r){let u=e(o);if(u>t){s.push(o);continue}i.length>0&&c+u>t&&(n.push(i),i=[],c=0),i.push(o),c+=u}return i.length>0&&n.push(i),{chunks:n,oversize:s}}function Le(r){if(!r)return{};if(typeof r=="object")return{...r};let e={};for(let t of r.split(",")){let[n,...s]=t.split("=");n&&s.length>0&&(e[n.trim()]=s.join("=").trim())}return e}function Ue(){let r=process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT,e=process.env.OTEL_EXPORTER_OTLP_ENDPOINT,t;return r?t=r.replace(/\/+$/,""):e&&(t=e.replace(/\/+$/,"")+"/v1/traces"),t?{endpoint:t,headers:Le(process.env.OTEL_EXPORTER_OTLP_HEADERS)}:null}async function B(r,e,t){let n=new AbortController,s=setTimeout(()=>n.abort(),Gt);try{let i=await fetch(e.endpoint,{method:"POST",headers:{"Content-Type":"application/json",...e.headers},body:JSON.stringify(r),signal:n.signal});if(!i.ok){let c="";try{c=(await i.text()).slice(0,200)}catch{}let o=i.status===401||i.status===403?" \u2014 check OTEL_EXPORTER_OTLP_HEADERS (relay token)":i.status===404?" \u2014 check OTEL_EXPORTER_OTLP_TRACES_ENDPOINT path":i.status>=500?" \u2014 collector may be down":"";return process.stderr.write(`[${t}] OTLP POST ${i.status} ${e.endpoint}${o}${c?` body=${c}`:""}
|
|
12
|
+
`),!1}return!0}catch(i){return process.stderr.write(`[${t}] OTLP POST failed: ${i.message??String(i)}
|
|
13
|
+
`),!1}finally{clearTimeout(s)}}var ie=class{queue;logPrefix;resolveOptions;constructor(e){this.queue=new $e.DiskRetryQueue(e.pluginData,e.logPrefix),this.logPrefix=e.logPrefix,this.resolveOptions=e.resolveOptions??Ue}async send(e){let t=this.resolveOptions();if(!t||this.dropIfOversized(e))return;await B(e,t,this.logPrefix)||this.queue.enqueue(e)}async flush(){let e=this.resolveOptions();if(e&&this.queue.tryAcquireLock())try{let t=this.queue.readAll();if(t.length===0)return;let{chunks:n,oversize:s}=Ge(t,o=>Y(o.payload),f.MAX_POST_BYTES);s.length>0&&process.stderr.write(`[${this.logPrefix}] dropping ${s.length} oversized retry ${s.length===1?"entry":"entries"} (each > ${f.MAX_POST_BYTES} bytes \u2014 the backend caps the request body, so they can never be delivered)
|
|
14
|
+
`);let i=[].concat(...n),c=0;for(let o of n){if(!await B((0,Ve.mergeBatch)(o.map(d=>d.payload)),e,this.logPrefix))break;c+=o.length}this.queue.rewrite(i.slice(c))}catch(t){process.stderr.write(`[${this.logPrefix}] flush failed: ${t?.message??String(t)}
|
|
15
|
+
`)}finally{this.queue.release()}}dropIfOversized(e){let t=Y(e);return t<=f.MAX_POST_BYTES?!1:(process.stderr.write(`[${this.logPrefix}] dropping oversized span payload (${t} > ${f.MAX_POST_BYTES} bytes) \u2014 undeliverable, not queued
|
|
16
|
+
`),!0)}};f.DiskTransport=ie;var ae=class{queue=new $e.MemoryRetryQueue;logPrefix;resolveOptions;constructor(e){this.logPrefix=e.logPrefix,this.resolveOptions=e.resolveOptions}async send(e){let t=this.resolveOptions();if(!t)return;if(Y(e)>f.MAX_POST_BYTES){process.stderr.write(`[${this.logPrefix}] dropping oversized span payload (> ${f.MAX_POST_BYTES} bytes) \u2014 undeliverable, not buffered
|
|
17
|
+
`);return}await B(e,t,this.logPrefix)||this.queue.enqueue(e)}async flush(){let e=this.resolveOptions();if(!e)return;let t=this.queue.drain();if(t.length===0)return;let{chunks:n,oversize:s}=Ge(t,Y,f.MAX_POST_BYTES);s.length>0&&process.stderr.write(`[${this.logPrefix}] dropping ${s.length} oversized buffered ${s.length===1?"payload":"payloads"} (each > ${f.MAX_POST_BYTES} bytes \u2014 undeliverable)
|
|
18
|
+
`);let i=[].concat(...n),c=0;for(let o of n){if(!await B((0,Ve.mergeBatch)(o),e,this.logPrefix))break;c+=o.length}for(let o of i.slice(c))this.queue.enqueue(o)}};f.MemoryTransport=ae});var le=P(A=>{"use strict";var de=A&&A.__importDefault||function(r){return r&&r.__esModule?r:{default:r}};Object.defineProperty(A,"__esModule",{value:!0});A.TraceManager=void 0;A.generateUlid=ze;var Ye=de(y("fs")),Lt=de(y("path")),Ut=de(y("crypto")),Xt=L(),ue="0123456789ABCDEFGHJKMNPQRSTVWXYZ";function ze(){let r=Date.now(),e="",t=r;for(let i=0;i<10;i++)e=ue[t&31]+e,t=Math.floor(t/32);let n=Ut.default.randomBytes(10),s="";for(let i=0;i<10;i++)s+=ue[n[i]&31];for(;s.length<16;)s+=ue[0];return e+s}var ce=class{tracePath;constructor(e){this.tracePath=e}newTrace(){let e=ze();return this.save(e),e}currentTrace(){try{let e=Ye.default.readFileSync(this.tracePath,"utf-8"),{traceId:t}=JSON.parse(e);if(t)return t}catch{}return this.newTrace()}save(e){try{Ye.default.mkdirSync(Lt.default.dirname(this.tracePath),{recursive:!0}),(0,Xt.atomicWriteFileSync)(this.tracePath,JSON.stringify({traceId:e}))}catch(t){process.stderr.write(`[pinta-core] trace persistence failed: ${t}
|
|
19
|
+
`)}}};A.TraceManager=ce});var He=P(D=>{"use strict";var Ke=D&&D.__importDefault||function(r){return r&&r.__esModule?r:{default:r}};Object.defineProperty(D,"__esModule",{value:!0});D.MemorySessionTraceManager=D.DiskSessionTraceManager=void 0;var We=Ke(y("fs")),Yt=Ke(y("path")),Ze=le(),zt=L(),Je=200,fe=class{tracePath;maxSessions;constructor(e,t={}){this.tracePath=e,this.maxSessions=t.maxSessions??Je}read(){try{let e=JSON.parse(We.default.readFileSync(this.tracePath,"utf-8"));if(e&&typeof e=="object"&&!("traceId"in e))return e}catch{}return{}}write(e){try{We.default.mkdirSync(Yt.default.dirname(this.tracePath),{recursive:!0});let t=Object.entries(e),n=t.length>this.maxSessions?Object.fromEntries(t.slice(-this.maxSessions)):e;(0,zt.atomicWriteFileSync)(this.tracePath,JSON.stringify(n))}catch{}}newTrace(e){let t=e||"default",n=(0,Ze.generateUlid)(),s=this.read();return s[t]=n,this.write(s),n}currentTrace(e){let t=e||"default",n=this.read()[t];return n||this.newTrace(t)}};D.DiskSessionTraceManager=fe;var pe=class{map=new Map;maxSessions;constructor(e={}){this.maxSessions=e.maxSessions??Je}newTrace(e){let t=e||"default",n=(0,Ze.generateUlid)();if(this.map.set(t,n),this.map.size>this.maxSessions){let s=this.map.keys().next().value;s!==void 0&&this.map.delete(s)}return n}currentTrace(e){let t=e||"default";return this.map.get(t)??this.newTrace(t)}};D.MemorySessionTraceManager=pe});var tt=P(k=>{"use strict";var ge=k&&k.__importDefault||function(r){return r&&r.__esModule?r:{default:r}};Object.defineProperty(k,"__esModule",{value:!0});k.envFilePath=Zt;k.parseEnvFile=et;k.loadEnvFile=Jt;var Wt=ge(y("node:fs")),Kt=ge(y("node:os")),Qe=ge(y("node:path"));function Zt(r,e,t){let n=t?process.env[t]:void 0,s=n&&n.length>0?n:Qe.default.join(Kt.default.homedir(),r);return Qe.default.join(s,e)}function et(r){let e={};for(let t of r.split(`
|
|
20
|
+
`)){let n=t.trim();if(!n||n.startsWith("#"))continue;let s=n.indexOf("=");if(s<0)continue;let i=n.slice(0,s).trim(),c=n.slice(s+1).trim();(c.startsWith('"')&&c.endsWith('"')||c.startsWith("'")&&c.endsWith("'"))&&(c=c.slice(1,-1)),i&&(e[i]=c)}return e}function Jt(r){let e;try{e=Wt.default.readFileSync(r,"utf-8")}catch{return}let t=et(e);for(let[n,s]of Object.entries(t))process.env[n]===void 0&&(process.env[n]=s)}});var S=P(a=>{"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.loadEnvFile=a.parseEnvFile=a.envFilePath=a.MemorySessionTraceManager=a.DiskSessionTraceManager=a.generateUlid=a.TraceManager=a.MAX_POST_BYTES=a.parseHeadersEnv=a.envOptionsResolver=a.postOtlp=a.MemoryTransport=a.DiskTransport=a.MemoryRetryQueue=a.DiskRetryQueue=a.GUARD_STATUS_REFUSED=a.evaluateGuard=a.MAX_SPAN_BYTES=a.enforceSpanBudget=a.mergeBatch=a.attachGuard=a.buildPayload=a.clientAttrs=a.guardAttrs=a.attrsFromRecord=a.toOtlpValue=a.snakeCase=a.newSpanId=a.ulidToTraceId=a.MAX_BYTES=a.PATTERNS=a.applyMatches=a.resolveOverlaps=a.collectMatches=a.truncateTo=a.truncate=a.redact=void 0;var M=ee();Object.defineProperty(a,"redact",{enumerable:!0,get:function(){return M.redact}});Object.defineProperty(a,"truncate",{enumerable:!0,get:function(){return M.truncate}});Object.defineProperty(a,"truncateTo",{enumerable:!0,get:function(){return M.truncateTo}});Object.defineProperty(a,"collectMatches",{enumerable:!0,get:function(){return M.collectMatches}});Object.defineProperty(a,"resolveOverlaps",{enumerable:!0,get:function(){return M.resolveOverlaps}});Object.defineProperty(a,"applyMatches",{enumerable:!0,get:function(){return M.applyMatches}});Object.defineProperty(a,"PATTERNS",{enumerable:!0,get:function(){return M.PATTERNS}});Object.defineProperty(a,"MAX_BYTES",{enumerable:!0,get:function(){return M.MAX_BYTES}});var _=re();Object.defineProperty(a,"ulidToTraceId",{enumerable:!0,get:function(){return _.ulidToTraceId}});Object.defineProperty(a,"newSpanId",{enumerable:!0,get:function(){return _.newSpanId}});Object.defineProperty(a,"snakeCase",{enumerable:!0,get:function(){return _.snakeCase}});Object.defineProperty(a,"toOtlpValue",{enumerable:!0,get:function(){return _.toOtlpValue}});Object.defineProperty(a,"attrsFromRecord",{enumerable:!0,get:function(){return _.attrsFromRecord}});Object.defineProperty(a,"guardAttrs",{enumerable:!0,get:function(){return _.guardAttrs}});Object.defineProperty(a,"clientAttrs",{enumerable:!0,get:function(){return _.clientAttrs}});Object.defineProperty(a,"buildPayload",{enumerable:!0,get:function(){return _.buildPayload}});Object.defineProperty(a,"attachGuard",{enumerable:!0,get:function(){return _.attachGuard}});Object.defineProperty(a,"mergeBatch",{enumerable:!0,get:function(){return _.mergeBatch}});Object.defineProperty(a,"enforceSpanBudget",{enumerable:!0,get:function(){return _.enforceSpanBudget}});Object.defineProperty(a,"MAX_SPAN_BYTES",{enumerable:!0,get:function(){return _.MAX_SPAN_BYTES}});var rt=qe();Object.defineProperty(a,"evaluateGuard",{enumerable:!0,get:function(){return rt.evaluateGuard}});Object.defineProperty(a,"GUARD_STATUS_REFUSED",{enumerable:!0,get:function(){return rt.GUARD_STATUS_REFUSED}});var nt=oe();Object.defineProperty(a,"DiskRetryQueue",{enumerable:!0,get:function(){return nt.DiskRetryQueue}});Object.defineProperty(a,"MemoryRetryQueue",{enumerable:!0,get:function(){return nt.MemoryRetryQueue}});var x=Xe();Object.defineProperty(a,"DiskTransport",{enumerable:!0,get:function(){return x.DiskTransport}});Object.defineProperty(a,"MemoryTransport",{enumerable:!0,get:function(){return x.MemoryTransport}});Object.defineProperty(a,"postOtlp",{enumerable:!0,get:function(){return x.postOtlp}});Object.defineProperty(a,"envOptionsResolver",{enumerable:!0,get:function(){return x.envOptionsResolver}});Object.defineProperty(a,"parseHeadersEnv",{enumerable:!0,get:function(){return x.parseHeadersEnv}});Object.defineProperty(a,"MAX_POST_BYTES",{enumerable:!0,get:function(){return x.MAX_POST_BYTES}});var st=le();Object.defineProperty(a,"TraceManager",{enumerable:!0,get:function(){return st.TraceManager}});Object.defineProperty(a,"generateUlid",{enumerable:!0,get:function(){return st.generateUlid}});var ot=He();Object.defineProperty(a,"DiskSessionTraceManager",{enumerable:!0,get:function(){return ot.DiskSessionTraceManager}});Object.defineProperty(a,"MemorySessionTraceManager",{enumerable:!0,get:function(){return ot.MemorySessionTraceManager}});var he=tt();Object.defineProperty(a,"envFilePath",{enumerable:!0,get:function(){return he.envFilePath}});Object.defineProperty(a,"parseEnvFile",{enumerable:!0,get:function(){return he.parseEnvFile}});Object.defineProperty(a,"loadEnvFile",{enumerable:!0,get:function(){return he.loadEnvFile}})});var at=b(S(),1);import Qt from"node:fs";import me from"node:path";var F=b(S(),1);function Ht(){return(0,F.envFilePath)(".config/opencode","pinta-opencode.env","OPENCODE_CONFIG_DIR")}function it(r=Ht()){(0,F.loadEnvFile)(r)}function ye(...r){return r.find(e=>e)||void 0}function er(r){let e=ye(r.endpoint,process.env.PINTA_OPENCODE_ENDPOINT,process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT);if(e)return e.replace(/\/+$/,"");let t=process.env.OTEL_EXPORTER_OTLP_ENDPOINT;if(t)return t.replace(/\/+$/,"")+"/v1/traces"}var tr="opencode-ai";function rr(){let r=me.dirname(process.execPath);for(let e=0;e<5;e++){try{let n=JSON.parse(Qt.readFileSync(me.join(r,"package.json"),"utf8"));if(n.name===tr&&typeof n.version=="string"&&n.version)return n.version}catch{}let t=me.dirname(r);if(t===r)break;r=t}}function nr(){let r=process.env.OPENCODE_VERSION?.trim();return r||rr()}function ut(r={}){it();let e=ye(r.token,process.env.PINTA_OPENCODE_TOKEN),t=(0,at.parseHeadersEnv)(r.headers??process.env.PINTA_OPENCODE_HEADERS??process.env.OTEL_EXPORTER_OTLP_HEADERS);e&&!Object.keys(t).some(s=>s.toLowerCase()==="x-pinta-relay-token")&&(t["x-pinta-relay-token"]=e);let n=r.guardTimeoutMs??(Number(process.env.PINTA_OPENCODE_GUARD_TIMEOUT_MS)||50);return{endpoint:er(r),headers:t,guardEndpoint:ye(r.guard,process.env.PINTA_OPENCODE_GUARD),relayToken:e,guardTimeoutMs:n,guardDisabled:process.env.PINTA_OPENCODE_GUARD_DISABLED==="1",serviceVersion:nr()}}var ct=b(S(),1),z=class extends ct.MemoryTransport{constructor(e){super({logPrefix:"pinta-opencode",resolveOptions:()=>e.endpoint?{endpoint:e.endpoint,headers:e.headers}:null})}};var _e=b(S(),1);var mt=b(S(),1);var dt=b(S(),1);var W="0.10.0";var sr=50,or=`pinta-opencode/${W}`;function lt(r,e,t={}){return(0,dt.evaluateGuard)(r,e,{timeoutMs:t.timeoutMs??sr,token:t.token,disabled:t.disabled,userAgent:or})}var ht=b(S(),1);import ve from"os";var j=b(S(),1);var K;function ir(){if(K===void 0)try{K=ve.userInfo().username}catch{K=process.env.USER??process.env.LOGNAME??(typeof process.getuid=="function"?String(process.getuid()):"unknown")}return K}var ft=W,ar=new Set(["opencode.hook","opencode.event_type","opencode.tool_name","opencode.session_id","opencode.sessionID","opencode.tool_use_id","opencode.cwd","opencode.agent","opencode.model","opencode.exit","opencode.truncated","opencode.title"]),ur=new Set(["opencode.tool_input","opencode.tool_response"]),cr={skipRedactKeys:ar,bashContextKeys:ur};function dr(r){let e=[{key:"ingest.type",value:{stringValue:"opencode"}}];return e.push(...(0,j.attrsFromRecord)(r,"opencode",cr)),e}function lr(r){return[{key:"service.name",value:{stringValue:"opencode"}},...r?[{key:"service.version",value:{stringValue:r}}]:[],{key:"telemetry.sdk.name",value:{stringValue:"pinta-opencode"}},{key:"telemetry.sdk.language",value:{stringValue:"nodejs"}},{key:"telemetry.sdk.version",value:{stringValue:ft}},{key:"process.pid",value:{intValue:process.pid}},{key:"process.owner",value:{stringValue:ir()}},{key:"host.name",value:{stringValue:ve.hostname()}},{key:"host.arch",value:{stringValue:ve.arch()}}]}function pt(r){return(0,j.buildPayload)({traceId:r.traceId,spanName:r.name,attributes:dr(r.fields),resource:lr(r.serviceVersion),scope:{name:"pinta-opencode",version:ft},now:r.now})}var N={messages:1024,requests:512,calls:2048,endedSessions:128,ttlMs:9e5},fr=new Set(["unknown","undefined","null","n/a","none","-","auto","default"]);function g(r){if(typeof r!="string")return;let e=r.trim();return e&&e.length<=1024?e:void 0}function w(r){let e=g(r);if(!(!e||e.startsWith("{")||e.startsWith("[")||fr.has(e.toLowerCase())))return e}function E(r){return r!==null&&typeof r=="object"&&!Array.isArray(r)?r:void 0}function v(r,e,t){let n=E(r),s=n?w(n.modelID)??w(n.id):w(r);if(s)return{model:s,source:n?`${e}.${w(n.modelID)?"modelID":"id"}`:e,provider:w(n?.providerID??t)}}function T(...r){return JSON.stringify(r)}function C(...r){let e=r.map(g).filter(t=>t!==void 0);return e.every(t=>t===e[0])?e[0]:void 0}function Te(r,e){let t=E(e.info),n=E(e.part);return typeof r=="string"&&r.startsWith("session.")?C(e.sessionID,t?.id):C(e.sessionID,t?.sessionID,n?.sessionID)}var $=class{constructor(e,t){this.limit=e;this.now=t}entries=new Map;get(e){let t=this.entries.get(e);if(t&&t.expires>this.now())return t.value;this.entries.delete(e)}set(e,t){this.entries.delete(e),this.entries.set(e,{value:t,expires:this.now()+N.ttlMs}),this.entries.size>this.limit&&this.entries.delete(this.entries.keys().next().value)}removeWhere(e){for(let[t,n]of this.entries)e(n.value)&&this.entries.delete(t)}clearSession(e){this.removeWhere(t=>t.sessionID===e)}},Z=class{constructor(e=Date.now){this.now=e;this.messages=new $(N.messages,e),this.requests=new $(N.requests,e),this.calls=new $(N.calls,e)}messages;requests;calls;ended=new Map;chatMessage(e,t){let n=C(e.sessionID,t?.message?.sessionID);if(!n)return;this.ended.delete(n);let s=C(e.messageID,t?.message?.id);if(!s)return;let i=g(t?.message?.agent??e.agent),c=t?.message?.model!==void 0;this.requests.set(T(n,s,i??""),{sessionID:n,messageID:s,evidence:v(c?t.message?.model:e.model,c?"requested:chat.message.message.model":"requested:chat.message.model")})}chatParams(e){let t=C(e.sessionID,e.message?.sessionID),n=g(e.message?.id),s=g(e.agent);!t||!n||!s||this.ended.has(t)||this.requests.set(T(t,n,s),{sessionID:t,messageID:n,evidence:v(e.model,"requested:chat.params.model")})}fresh(e){return e===void 0||typeof e=="number"&&Number.isFinite(e)&&e>=this.now()-N.ttlMs}clearSession(e){this.messages.clearSession(e),this.requests.clearSession(e),this.calls.clearSession(e)}event(e,t){let n=Te(e,t),s=E(t.info),i=E(t.part),c=["session.idle","session.deleted","session.error"].includes(e??"")||e==="session.status"&&E(t.status)?.type==="idle";if(n&&c?(this.clearSession(n),this.ended.delete(n),this.ended.set(n,!0),this.ended.size>N.endedSessions&&this.ended.delete(this.ended.keys().next().value)):n&&e==="session.created"&&(this.clearSession(n),this.ended.delete(n)),e==="message.updated"&&s){if(!C(t.sessionID,s.sessionID)&&(t.sessionID||s.sessionID))return;let o=g(s.id),u=g(s.agent),d=s.role,l=d==="assistant"?v(w(s.modelID),"reported:message.updated.info.modelID",s.providerID)??v(s.model,"reported:message.updated.info.model"):d==="user"?v(s.model,"requested:message.updated.info.model"):void 0;if(!l&&d==="user"&&!("model"in s)&&n&&o&&(l=this.requests.get(T(n,o,u??""))?.evidence),d==="user"&&"model"in s&&!l&&n&&o&&this.requests.set(T(n,o,u??""),{sessionID:n,messageID:o}),l&&(l={...l,agent:u,messageID:o}),n&&o&&!this.ended.has(n)&&this.fresh(E(s.time)?.created)){let O=this.messages.get(T(n,o)),Oe=O?.ambiguous||!!(d==="assistant"&&O?.evidence&&l&&(O.evidence.model!==l.model||O.evidence.provider!==l.provider||O.evidence.agent!==l.agent));this.messages.set(T(n,o),{sessionID:n,messageID:o,evidence:Oe?void 0:l,ambiguous:Oe})}return l}if(n&&e==="message.removed"){let o=g(t.messageID);this.messages.removeWhere(u=>u.sessionID===n&&u.messageID===o),this.requests.removeWhere(u=>u.sessionID===n&&u.messageID===o),this.calls.removeWhere(u=>u.sessionID===n&&u.messageID===o)}if(n&&e==="message.part.removed"){let o=g(t.partID);o&&this.calls.removeWhere(u=>u.sessionID===n&&u.partID===o)}if(e==="message.part.delta"){let o=g(t.messageID);return n&&o&&!this.ended.has(n)?this.messages.get(T(n,o))?.evidence:void 0}if(e==="message.part.updated"&&i){if(!n||this.ended.has(n)||!this.fresh(t.time))return;let o=g(i.messageID);if(!o)return;let u=g(i.callID);if(i.type==="tool"&&u){let d=T(n,u),l=this.calls.get(d);if(!l?.closed){let O=g(i.tool);this.calls.set(d,{sessionID:n,messageID:o,partID:g(i.id),tool:O,pinned:l?.pinned,ambiguous:l?.ambiguous||!!(l&&(l.messageID!==o||l.tool!==O))})}}return this.messages.get(T(n,o))?.evidence}return typeof e=="string"&&e.startsWith("session.")?v(s?.model,`selected:${e}.info.model`)??v(t.model,`selected:${e}.model`):v(t.model,"reported:event.model")??v(w(t.modelID),"reported:event.modelID",t.providerID)}tool(e,t){let n=v(w(e.modelID),"reported:tool.modelID",e.providerID)??v(e.model,"reported:tool.model",e.providerID),s=g(e.sessionID),i=g(e.callID);if(!s||!i)return n;let c=T(s,i),o=this.calls.get(c),u=n;if(!("model"in e)&&!("modelID"in e)&&o&&!o.closed&&!o.ambiguous&&!this.ended.has(s)&&(!o.tool||o.tool===e.tool)){let d=this.messages.get(T(s,o.messageID));d?.ambiguous||(u=o.pinned??d?.evidence),(e.agent&&u?.agent&&e.agent!==u.agent||e.messageID&&e.messageID!==o.messageID)&&(u=void 0)}return o&&!o.closed&&this.calls.set(c,{...o,pinned:t?void 0:u,closed:t}),u}beforeTool(e){return this.tool(e,!1)}afterTool(e){return this.tool(e,!0)}};function J(r,e){let{model:t,model_source:n,...s}=r;return"model"in r&&t!==e?.model&&(s.model_raw=t),"model_source"in r&&(s.model_source_raw=n),e&&(s.model=e.model,s.model_source=e.source,e.provider&&("provider"in s&&s.provider!==e.provider&&(s.provider_raw=s.provider),s.provider=e.provider),s.agent===void 0&&(s.agent=e.agent),s.message_id===void 0&&(s.message_id=e.messageID)),s}var H=class{constructor(e,t,n){this.transport=e;this.trace=t;this.config=n}models=new Z;build(e,t,n){let s=this.trace.currentTrace(t);return pt({name:e,traceId:s,fields:n,serviceVersion:this.config.serviceVersion})}async send(e){await this.transport.send(e)}async emit(e,t,n){await this.send(this.build(e,t,n))}chatMessage(e,t){this.models.chatMessage(e,t)}chatParams(e){this.models.chatParams(e)}async lifecycle(e){let t=E(e.properties)??{},n=Te(e.type,t),s=this.models.event(e.type,t);await this.emit(`opencode.event.${e.type??"unknown"}`,n,J({hook:"event",event_type:e.type,session_id:n,cwd:process.cwd(),...t},s)),e.type==="session.idle"&&await this.transport.flush()}toolBeforePayload(e,t){return this.build("opencode.tool.before",e.sessionID,J({...gt("tool.execute.before",e),tool_input:t},this.models.beforeTool(e)))}async toolBefore(e,t,n){await this.send((0,ht.attachGuard)(this.toolBeforePayload(e,t),n))}async toolAfter(e,t){let n=t.metadata??{};await this.emit("opencode.tool.after",e.sessionID,J({...gt("tool.execute.after",e),title:t.title,tool_response:t.output,exit:n.exit,truncated:n.truncated},this.models.afterTool(e)))}};function gt(r,e){return{hook:r,tool_name:e.tool,session_id:e.sessionID,tool_use_id:e.callID,cwd:process.cwd(),...e.model!==void 0?{model:e.model}:{},...e.modelID!==void 0?{modelID:e.modelID}:{},...e.providerID!==void 0?{providerID:e.providerID}:{},...e.agent!==void 0?{agent:e.agent}:{},...e.messageID!==void 0?{message_id:e.messageID}:{}}}function yt(r,e){process.stderr.write(`[pinta-opencode] ${r}: ${e?.message??String(e)}
|
|
21
|
+
`)}function Q(r,e){return async(...t)=>{try{await e(...t)}catch(n){yt(r,n)}}}var pr=async(r,e)=>{let t=ut(e??{}),n=new z({endpoint:t.endpoint,headers:t.headers}),s=new _e.MemorySessionTraceManager,i=new H(n,s,t);async function c(o,u){let d=null;try{let l=i.toolBeforePayload(o,u);d=await lt(l,t.guardEndpoint,{timeoutMs:t.guardTimeoutMs,token:t.relayToken,disabled:t.guardDisabled}),await i.send((0,mt.attachGuard)(l,d))}catch(l){yt("tool.execute.before",l)}return d}return{"chat.message":Q("chat.message",async(o,u)=>{s.newTrace(o?.sessionID),i.chatMessage(o,u)}),"chat.params":Q("chat.params",async(o,u)=>{i.chatParams(o)}),event:Q("event",async o=>{o?.event&&await i.lifecycle(o.event)}),"tool.execute.before":async(o,u)=>{let d=await c(o,u?.args);if(d?.decision==="DENY")throw new Error(d.userMessage??d.reason??"guard_deny")},"tool.execute.after":Q("tool.execute.after",(o,u)=>i.toolAfter(o,u))}},zr=pr;export{pr as PintaOpencode,zr as default};
|