@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 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 key of __getOwnPropNames(from))
20
- if (!__hasOwnProp.call(to, key) && key !== except)
21
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
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(key, raw, policy) {
219
+ function maybeRedactString(key2, raw, policy) {
220
220
  const truncated = (0, redact_js_1.truncate)(raw);
221
- if ((policy.skipRedactKeys ?? EMPTY_SET).has(key))
221
+ if ((policy.skipRedactKeys ?? EMPTY_SET).has(key2))
222
222
  return truncated;
223
- const context = (policy.bashContextKeys ?? EMPTY_SET).has(key) ? "bash" : void 0;
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(key, v, policy = {}) {
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(key, v, policy) };
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(key, JSON.stringify(v), policy) };
240
+ return { stringValue: maybeRedactString(key2, JSON.stringify(v), policy) };
241
241
  } catch {
242
- return { stringValue: maybeRedactString(key, String(v), policy) };
242
+ return { stringValue: maybeRedactString(key2, String(v), policy) };
243
243
  }
244
244
  default:
245
- return { stringValue: maybeRedactString(key, String(v), policy) };
245
+ return { stringValue: maybeRedactString(key2, String(v), policy) };
246
246
  }
247
247
  }
248
- function attrsFromRecord2(record, prefix, policy = {}) {
248
+ function attrsFromRecord2(record2, prefix, policy = {}) {
249
249
  const out = [];
250
- for (const [k, v] of Object.entries(record)) {
251
- const key = `${prefix}.${k}`;
252
- const value = toOtlpValue(key, v, policy);
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 key = sessionId || "default";
927
+ const key2 = sessionId || "default";
928
928
  const traceId = (0, trace_js_1.generateUlid)();
929
929
  const map = this.read();
930
- map[key] = traceId;
930
+ map[key2] = traceId;
931
931
  this.write(map);
932
932
  return traceId;
933
933
  }
934
934
  currentTrace(sessionId) {
935
- const key = sessionId || "default";
936
- const existing = this.read()[key];
935
+ const key2 = sessionId || "default";
936
+ const existing = this.read()[key2];
937
937
  if (existing)
938
938
  return existing;
939
- return this.newTrace(key);
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 key = sessionId || "default";
950
+ const key2 = sessionId || "default";
951
951
  const traceId = (0, trace_js_1.generateUlid)();
952
- this.map.set(key, traceId);
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 key = sessionId || "default";
962
- return this.map.get(key) ?? this.newTrace(key);
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 key = line.slice(0, idx).trim();
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 (key)
1003
- out[key] = value;
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 [key, value] of Object.entries(parsed)) {
1016
- if (process.env[key] === void 0) {
1017
- process.env[key] = value;
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.9.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 = typeof props.sessionID === "string" ? props.sessionID : void 0;
1352
- await this.emit(`opencode.event.${ev.type ?? "unknown"}`, sessionId, {
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
- { ...toolIdentity("tool.execute.before", input), tool_input: args }
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 it=Object.create;var ce=Object.defineProperty;var at=Object.getOwnPropertyDescriptor;var ut=Object.getOwnPropertyNames;var ct=Object.getPrototypeOf,lt=Object.prototype.hasOwnProperty;var h=(t=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(t,{get:(e,r)=>(typeof require<"u"?require:e)[r]}):t)(function(t){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+t+'" is not supported')});var m=(t,e)=>()=>(e||t((e={exports:{}}).exports,e),e.exports);var dt=(t,e,r,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of ut(e))!lt.call(t,o)&&o!==r&&ce(t,o,{get:()=>e[o],enumerable:!(n=at(e,o))||n.enumerable});return t};var O=(t,e,r)=>(r=t!=null?it(ct(t)):{},dt(e||!t||!t.__esModule?ce(r,"default",{value:t,enumerable:!0}):r,t));var V=m(f=>{"use strict";Object.defineProperty(f,"__esModule",{value:!0});f.PATTERNS=f.MAX_BYTES=void 0;f.truncateTo=le;f.truncate=pt;f.collectMatches=de;f.resolveOverlaps=pe;f.applyMatches=fe;f.redact=ft;f.MAX_BYTES=102400;function le(t,e){let r=Buffer.from(t,"utf-8");return r.length<=e?t:`${r.subarray(0,Math.max(0,e)).toString("utf-8")}\u2026[TRUNCATED:${r.length}]`}function pt(t){return le(t,f.MAX_BYTES)}f.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 de(t,e){let r=[];for(let n of f.PATTERNS){if(n.requireContext&&n.requireContext!==e.context)continue;let o=new RegExp(n.regex.source,n.regex.flags),i;for(;(i=o.exec(t))!==null;){let u=n.captureGroup??0,a=i[u];if(a===void 0){i.index===o.lastIndex&&o.lastIndex++;continue}let c=i.index,d=i.index+i[0].length,_=c+i[0].indexOf(a),st=_+a.length;r.push({start:c,end:d,replaceStart:_,replaceEnd:st,type:n.type}),i.index===o.lastIndex&&o.lastIndex++}}return r}function pe(t){let e=[...t].sort((o,i)=>o.start!==i.start?o.start-i.start:i.end-i.start-(o.end-o.start)),r=[],n=-1;for(let o of e)o.start<n||(r.push(o),n=o.end);return r}function fe(t,e){let r=[...e].sort((o,i)=>i.replaceStart-o.replaceStart),n=t;for(let o of r)n=n.slice(0,o.replaceStart)+`[REDACTED:${o.type}]`+n.slice(o.replaceEnd);return n}function ft(t,e={}){if(t.length===0)return t;let r=de(t,e);if(r.length===0)return t;let n=pe(r);return fe(t,n)}});var U=m(p=>{"use strict";var gt=p&&p.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(p,"__esModule",{value:!0});p.MAX_SPAN_BYTES=void 0;p.ulidToTraceId=_e;p.newSpanId=me;p.snakeCase=_t;p.toOtlpValue=Te;p.attrsFromRecord=mt;p.guardAttrs=Oe;p.clientAttrs=Pe;p.enforceSpanBudget=Se;p.buildPayload=Tt;p.attachGuard=ve;p.mergeBatch=Ot;var ht=gt(h("crypto")),L=V(),yt="0123456789ABCDEFGHJKMNPQRSTVWXYZ";function _e(t){if(t.length!==26)throw new Error(`ulidToTraceId: expected 26 chars, got ${t.length}`);let e=0n;for(let n of t){let o=yt.indexOf(n);if(o<0)throw new Error(`ulidToTraceId: invalid Crockford char "${n}"`);e=e<<5n|BigInt(o)}let r=(1n<<128n)-1n;return e&=r,e.toString(16).padStart(32,"0")}function me(){return ht.default.randomBytes(8).toString("hex")}function _t(t){return t.replace(/([a-z0-9])([A-Z])/g,"$1_$2").replace(/([A-Z])([A-Z][a-z])/g,"$1_$2").toLowerCase()}var ge=new Set;function x(t,e,r){let n=(0,L.truncate)(e);if((r.skipRedactKeys??ge).has(t))return n;let o=(r.bashContextKeys??ge).has(t)?"bash":void 0;return(0,L.redact)(n,{context:o})}function Te(t,e,r={}){if(e==null)return null;switch(typeof e){case"string":return{stringValue:x(t,e,r)};case"boolean":return{boolValue:e};case"number":return Number.isInteger(e)?{intValue:e}:{doubleValue:e};case"object":try{return{stringValue:x(t,JSON.stringify(e),r)}}catch{return{stringValue:x(t,String(e),r)}}default:return{stringValue:x(t,String(e),r)}}}function mt(t,e,r={}){let n=[];for(let[o,i]of Object.entries(t)){let u=`${e}.${o}`,a=Te(u,i,r);a!==null&&n.push({key:u,value:a})}return n}function Oe(t){let e=[{key:"pinta.guard.decision",value:{stringValue:t.decision.toLowerCase()}},{key:"pinta.guard.duration_ms",value:{intValue:t.durationMs}}];return t.reason&&e.push({key:"pinta.guard.matched_rule",value:{stringValue:t.reason}}),t.failOpenReason&&e.push({key:"pinta.guard.fail_open_reason",value:{stringValue:t.failOpenReason}}),e}function Pe(t){return[{key:"pinta.client.rtt_ms",value:Number.isInteger(t.rttMs)?{intValue:t.rttMs}:{doubleValue:t.rttMs}},{key:"pinta.client.op",value:{stringValue:t.op}}]}p.MAX_SPAN_BYTES=800*1024;function he(t){let e=JSON.stringify(t);return e===void 0?0:Buffer.byteLength(e,"utf-8")}var ye=1024;function Se(t,e=p.MAX_SPAN_BYTES){let r=he(t);for(let n=0;n<100&&r>e;n++){let o=null,i=0;for(let c of t.attributes){if(!("stringValue"in c.value))continue;let d=Buffer.byteLength(c.value.stringValue,"utf-8");d>i&&(i=d,o=c.value)}if(o===null||i<=ye)break;let u=r-e,a=Math.max(ye,i-u-64);o.stringValue=(0,L.truncateTo)(o.stringValue,a),r=he(t)}return t}function Tt(t){let e=t.now??Date.now(),r=(BigInt(e)*1000000n).toString(),n=[...t.attributes],o={traceId:_e(t.traceId),spanId:me(),name:t.spanName,kind:t.spanKind??1,startTimeUnixNano:r,endTimeUnixNano:r,attributes:n};Se(o);let i={resourceSpans:[{resource:{attributes:t.resource},scopeSpans:[{scope:t.scope,spans:[o]}]}]};return ve(i,t.guard,t.clientTiming)}function ve(t,e,r){let n=t.resourceSpans[0]?.scopeSpans[0]?.spans[0];if(!n)return t;e&&n.attributes.push(...Oe(e));let o=r??(e?.clientRttMs!==void 0?{op:"guard",rttMs:e.clientRttMs}:null);return o&&n.attributes.push(...Pe(o)),t}function Ot(t){let e=[];for(let r of t)e.push(...r.resourceSpans);return{resourceSpans:e}}});var Ee=m(A=>{"use strict";Object.defineProperty(A,"__esModule",{value:!0});A.GUARD_STATUS_REFUSED=void 0;A.evaluateGuard=Et;var Pt=1e4,St="pinta-core";function I(t,e){return{decision:"ALLOW",reason:null,userMessage:null,durationMs:t,clientRttMs:t,failOpenReason:e}}var vt=new Set(["ALLOW","DENY","REVIEW"]);A.GUARD_STATUS_REFUSED=410;async function Et(t,e,r={}){if(!e||r.disabled)return null;let n=r.timeoutMs??Pt,o=Date.now(),i=new AbortController,u=setTimeout(()=>i.abort(),n);try{let a=await fetch(e,{method:"POST",headers:{"content-type":"application/json","user-agent":r.userAgent??St,"x-pinta-relay-token":r.token??"",...r.agentType?{"x-pinta-agent-type":r.agentType}:{}},body:JSON.stringify(t),signal:i.signal});if(a.status===A.GUARD_STATUS_REFUSED)return I(Date.now()-o,"refused");if(a.status!==200)return I(Date.now()-o,"error");let c=await a.json(),d=c?.durationMs===void 0||typeof c.durationMs=="number"&&Number.isFinite(c.durationMs);if(!c||!vt.has(c.decision)||!d)return I(Date.now()-o,"error");let _=Date.now()-o;return{decision:c.decision,reason:c.reason??null,userMessage:c.userMessage??null,durationMs:c.durationMs??_,clientRttMs:_}}catch(a){let c=a.name,d=c==="AbortError"||c==="TimeoutError"?"timeout":"error";return I(Date.now()-o,d)}finally{clearTimeout(u)}}});var j=m(M=>{"use strict";var bt=M&&M.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(M,"__esModule",{value:!0});M.atomicWriteFileSync=At;var be=bt(h("fs"));function At(t,e){let r=`${t}.${process.pid}.tmp`;be.default.writeFileSync(r,e),be.default.renameSync(r,t)}});var z=m(P=>{"use strict";var Ae=P&&P.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(P,"__esModule",{value:!0});P.MemoryRetryQueue=P.DiskRetryQueue=void 0;var g=Ae(h("fs")),N=Ae(h("path")),kt=j(),B=1e3,wt=50,Mt=5,X=class{filePath;lockPath;logPrefix;constructor(e,r){this.filePath=N.default.join(e,"failed-spans.jsonl"),this.lockPath=this.filePath+".lock",this.logPrefix=r}enqueue(e){try{g.default.mkdirSync(N.default.dirname(this.filePath),{recursive:!0});let r=JSON.stringify({savedAt:new Date().toISOString(),payload:e})+`
3
- `;g.default.appendFileSync(this.filePath,r),this.trim()}catch(r){process.stderr.write(`[${this.logPrefix}] retry-queue enqueue failed: ${r}
4
- `)}}readAll(){try{let e=g.default.readFileSync(this.filePath,"utf-8"),r=[];for(let n of e.split(`
5
- `))if(n.trim())try{let o=JSON.parse(n);Array.isArray(o?.payload?.resourceSpans)&&r.push(o)}catch{}return r}catch{return[]}}rewrite(e){try{if(e.length===0){g.default.existsSync(this.filePath)&&g.default.unlinkSync(this.filePath);return}g.default.mkdirSync(N.default.dirname(this.filePath),{recursive:!0}),(0,kt.atomicWriteFileSync)(this.filePath,e.map(r=>JSON.stringify(r)).join(`
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(r){process.stderr.write(`[${this.logPrefix}] retry-queue rewrite failed: ${r}
8
- `)}}tryAcquireLock(){let e=Date.now();try{g.default.mkdirSync(N.default.dirname(this.lockPath),{recursive:!0})}catch(r){return process.stderr.write(`[${this.logPrefix}] retry-queue lock mkdir failed: ${r}
9
- `),!1}for(;Date.now()-e<wt;)try{let r=g.default.openSync(this.lockPath,"wx");return g.default.writeSync(r,String(process.pid)),g.default.closeSync(r),!0}catch(r){if(r?.code!=="EEXIST")return process.stderr.write(`[${this.logPrefix}] retry-queue lock open failed: ${r}
10
- `),!1;try{let i=g.default.statSync(this.lockPath);if(Date.now()-i.mtimeMs>3e4){g.default.unlinkSync(this.lockPath);continue}}catch{}let n=Mt,o=Date.now()+n;for(;Date.now()<o;);}return!1}release(){try{g.default.unlinkSync(this.lockPath)}catch{}}trim(){let e=this.readAll();if(e.length<=B)return;let r=e.length-B;process.stderr.write(`[${this.logPrefix}] retry-queue full, dropping ${r} oldest entries
11
- `),this.rewrite(e.slice(r))}};P.DiskRetryQueue=X;var Y=class{entries=[];enqueue(e){this.entries.push(e),this.entries.length>B&&this.entries.splice(0,this.entries.length-B)}drain(){let e=this.entries;return this.entries=[],e}get size(){return this.entries.length}};P.MemoryRetryQueue=Y});var xe=m(l=>{"use strict";Object.defineProperty(l,"__esModule",{value:!0});l.MemoryTransport=l.DiskTransport=l.MAX_POST_BYTES=void 0;l.parseHeadersEnv=Re;l.envOptionsResolver=De;l.postOtlp=R;var ke=z(),we=U(),Rt=5e3;l.MAX_POST_BYTES=900*1024;function q(t){let e=JSON.stringify(t);return e===void 0?0:Buffer.byteLength(e,"utf-8")}function Me(t,e,r){let n=[],o=[],i=[],u=0;for(let a of t){let c=e(a);if(c>r){o.push(a);continue}i.length>0&&u+c>r&&(n.push(i),i=[],u=0),i.push(a),u+=c}return i.length>0&&n.push(i),{chunks:n,oversize:o}}function Re(t){if(!t)return{};if(typeof t=="object")return{...t};let e={};for(let r of t.split(",")){let[n,...o]=r.split("=");n&&o.length>0&&(e[n.trim()]=o.join("=").trim())}return e}function De(){let t=process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT,e=process.env.OTEL_EXPORTER_OTLP_ENDPOINT,r;return t?r=t.replace(/\/+$/,""):e&&(r=e.replace(/\/+$/,"")+"/v1/traces"),r?{endpoint:r,headers:Re(process.env.OTEL_EXPORTER_OTLP_HEADERS)}:null}async function R(t,e,r){let n=new AbortController,o=setTimeout(()=>n.abort(),Rt);try{let i=await fetch(e.endpoint,{method:"POST",headers:{"Content-Type":"application/json",...e.headers},body:JSON.stringify(t),signal:n.signal});if(!i.ok){let u="";try{u=(await i.text()).slice(0,200)}catch{}let a=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(`[${r}] OTLP POST ${i.status} ${e.endpoint}${a}${u?` body=${u}`:""}
12
- `),!1}return!0}catch(i){return process.stderr.write(`[${r}] OTLP POST failed: ${i.message??String(i)}
13
- `),!1}finally{clearTimeout(o)}}var K=class{queue;logPrefix;resolveOptions;constructor(e){this.queue=new ke.DiskRetryQueue(e.pluginData,e.logPrefix),this.logPrefix=e.logPrefix,this.resolveOptions=e.resolveOptions??De}async send(e){let r=this.resolveOptions();if(!r||this.dropIfOversized(e))return;await R(e,r,this.logPrefix)||this.queue.enqueue(e)}async flush(){let e=this.resolveOptions();if(e&&this.queue.tryAcquireLock())try{let r=this.queue.readAll();if(r.length===0)return;let{chunks:n,oversize:o}=Me(r,a=>q(a.payload),l.MAX_POST_BYTES);o.length>0&&process.stderr.write(`[${this.logPrefix}] dropping ${o.length} oversized retry ${o.length===1?"entry":"entries"} (each > ${l.MAX_POST_BYTES} bytes \u2014 the backend caps the request body, so they can never be delivered)
14
- `);let i=[].concat(...n),u=0;for(let a of n){if(!await R((0,we.mergeBatch)(a.map(d=>d.payload)),e,this.logPrefix))break;u+=a.length}this.queue.rewrite(i.slice(u))}catch(r){process.stderr.write(`[${this.logPrefix}] flush failed: ${r?.message??String(r)}
15
- `)}finally{this.queue.release()}}dropIfOversized(e){let r=q(e);return r<=l.MAX_POST_BYTES?!1:(process.stderr.write(`[${this.logPrefix}] dropping oversized span payload (${r} > ${l.MAX_POST_BYTES} bytes) \u2014 undeliverable, not queued
16
- `),!0)}};l.DiskTransport=K;var Z=class{queue=new ke.MemoryRetryQueue;logPrefix;resolveOptions;constructor(e){this.logPrefix=e.logPrefix,this.resolveOptions=e.resolveOptions}async send(e){let r=this.resolveOptions();if(!r)return;if(q(e)>l.MAX_POST_BYTES){process.stderr.write(`[${this.logPrefix}] dropping oversized span payload (> ${l.MAX_POST_BYTES} bytes) \u2014 undeliverable, not buffered
17
- `);return}await R(e,r,this.logPrefix)||this.queue.enqueue(e)}async flush(){let e=this.resolveOptions();if(!e)return;let r=this.queue.drain();if(r.length===0)return;let{chunks:n,oversize:o}=Me(r,q,l.MAX_POST_BYTES);o.length>0&&process.stderr.write(`[${this.logPrefix}] dropping ${o.length} oversized buffered ${o.length===1?"payload":"payloads"} (each > ${l.MAX_POST_BYTES} bytes \u2014 undeliverable)
18
- `);let i=[].concat(...n),u=0;for(let a of n){if(!await R((0,we.mergeBatch)(a),e,this.logPrefix))break;u+=a.length}for(let a of i.slice(u))this.queue.enqueue(a)}};l.MemoryTransport=Z});var Q=m(E=>{"use strict";var H=E&&E.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(E,"__esModule",{value:!0});E.TraceManager=void 0;E.generateUlid=je;var Ie=H(h("fs")),Dt=H(h("path")),xt=H(h("crypto")),It=j(),W="0123456789ABCDEFGHJKMNPQRSTVWXYZ";function je(){let t=Date.now(),e="",r=t;for(let i=0;i<10;i++)e=W[r&31]+e,r=Math.floor(r/32);let n=xt.default.randomBytes(10),o="";for(let i=0;i<10;i++)o+=W[n[i]&31];for(;o.length<16;)o+=W[0];return e+o}var J=class{tracePath;constructor(e){this.tracePath=e}newTrace(){let e=je();return this.save(e),e}currentTrace(){try{let e=Ie.default.readFileSync(this.tracePath,"utf-8"),{traceId:r}=JSON.parse(e);if(r)return r}catch{}return this.newTrace()}save(e){try{Ie.default.mkdirSync(Dt.default.dirname(this.tracePath),{recursive:!0}),(0,It.atomicWriteFileSync)(this.tracePath,JSON.stringify({traceId:e}))}catch(r){process.stderr.write(`[pinta-core] trace persistence failed: ${r}
19
- `)}}};E.TraceManager=J});var $e=m(S=>{"use strict";var Be=S&&S.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(S,"__esModule",{value:!0});S.MemorySessionTraceManager=S.DiskSessionTraceManager=void 0;var Ne=Be(h("fs")),jt=Be(h("path")),qe=Q(),Nt=j(),Fe=200,ee=class{tracePath;maxSessions;constructor(e,r={}){this.tracePath=e,this.maxSessions=r.maxSessions??Fe}read(){try{let e=JSON.parse(Ne.default.readFileSync(this.tracePath,"utf-8"));if(e&&typeof e=="object"&&!("traceId"in e))return e}catch{}return{}}write(e){try{Ne.default.mkdirSync(jt.default.dirname(this.tracePath),{recursive:!0});let r=Object.entries(e),n=r.length>this.maxSessions?Object.fromEntries(r.slice(-this.maxSessions)):e;(0,Nt.atomicWriteFileSync)(this.tracePath,JSON.stringify(n))}catch{}}newTrace(e){let r=e||"default",n=(0,qe.generateUlid)(),o=this.read();return o[r]=n,this.write(o),n}currentTrace(e){let r=e||"default",n=this.read()[r];return n||this.newTrace(r)}};S.DiskSessionTraceManager=ee;var te=class{map=new Map;maxSessions;constructor(e={}){this.maxSessions=e.maxSessions??Fe}newTrace(e){let r=e||"default",n=(0,qe.generateUlid)();if(this.map.set(r,n),this.map.size>this.maxSessions){let o=this.map.keys().next().value;o!==void 0&&this.map.delete(o)}return n}currentTrace(e){let r=e||"default";return this.map.get(r)??this.newTrace(r)}};S.MemorySessionTraceManager=te});var Ve=m(b=>{"use strict";var re=b&&b.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(b,"__esModule",{value:!0});b.envFilePath=Ft;b.parseEnvFile=Ge;b.loadEnvFile=$t;var Bt=re(h("node:fs")),qt=re(h("node:os")),Ce=re(h("node:path"));function Ft(t,e,r){let n=r?process.env[r]:void 0,o=n&&n.length>0?n:Ce.default.join(qt.default.homedir(),t);return Ce.default.join(o,e)}function Ge(t){let e={};for(let r of t.split(`
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 ue(t,e){return async(...r)=>{try{await e(...r)}catch(n){ot(t,n)}}}var er=async(t,e)=>{let r=Ze(e??{}),n=new F({endpoint:r.endpoint,headers:r.headers}),o=new ie.MemorySessionTraceManager,i=new G(n,o,r);async function u(a,c){let d=null;try{let _=i.toolBeforePayload(a,c);d=await He(_,r.guardEndpoint,{timeoutMs:r.guardTimeoutMs,token:r.relayToken,disabled:r.guardDisabled}),await i.send((0,nt.attachGuard)(_,d))}catch(_){ot("tool.execute.before",_)}return d}return{"chat.message":ue("chat.message",async a=>{o.newTrace(a?.sessionID)}),event:ue("event",async a=>{a?.event&&await i.lifecycle(a.event)}),"tool.execute.before":async(a,c)=>{let d=await u(a,c?.args);if(d?.decision==="DENY")throw new Error(d.userMessage??d.reason??"guard_deny")},"tool.execute.after":ue("tool.execute.after",(a,c)=>i.toolAfter(a,c))}},xr=er;export{er as PintaOpencode,xr as default};
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};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pinta-ai/pinta-opencode",
3
- "version": "0.9.0",
3
+ "version": "0.10.0",
4
4
  "description": "OTLP forwarder + guard for opencode (in-process plugin)",
5
5
  "type": "module",
6
6
  "main": "dist/plugin.js",