@pinta-ai/pinta-opencode 0.8.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,47 @@
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
+
23
+ ## 0.9.0
24
+
25
+ - The guard is asked about the span, not about a summary of it. Until now the
26
+ `PreToolUse` gate assembled a five-field `GuardInput` (tool name, input, working
27
+ directory, hook, session) beside the span that carried the same facts under
28
+ `opencode.*`, and the two were free to drift — `cwd` (PTA-176) and the hook name
29
+ (PTA-207) were on the span and missing from the summary. Now the span is built
30
+ first, `POST /guard/evaluate` receives that OTLP payload unwrapped, the manager
31
+ projects it through the same AgentEvent assembly the backend stores it with,
32
+ and the verdict is attached to the same object before it is relayed — the span
33
+ the manager judged is the span the backend stores, `spanId` included.
34
+ Requires Pinta Manager 0.1.11 or later; an older manager answers `400` and the
35
+ gate fails open (`pinta.guard.fail_open_reason: error`), a newer manager that
36
+ refuses a body answers `410`, recorded as `refused`.
37
+ - `@pinta-ai/core` `^0.6.0` → `^0.8.0` (`evaluateGuard(payload)`, `attachGuard`). `Telemetry.toolBeforePayload` builds the
38
+ span the guard is asked about; `Telemetry.send` relays it once the verdict is
39
+ attached.
40
+
41
+ ## 0.8.0
42
+
43
+ - Version derived from one literal (`src/core/version.ts`); the build fails on drift.
44
+
3
45
  ## 0.7.0
4
46
 
5
47
  - Telemetry now names each tool span by the opencode hook it arrived on
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, … } }` |
@@ -63,14 +64,17 @@ Telemetry and governance are independent — endpoint only, guard only, both, or
63
64
 
64
65
  ## Guard (allow / deny + reason)
65
66
 
66
- On `tool.execute.before` the adapter POSTs to the guard endpoint (cc/codex/copilot contract — backend unchanged):
67
+ On `tool.execute.before` the adapter POSTs the span it is about to relay to the guard endpoint (cc/codex/copilot contract — `@pinta-ai/core` ≥ 0.8.0):
67
68
 
68
69
  ```
69
70
  POST {guard} header: x-pinta-relay-token: {PINTA_RELAY_TOKEN}
70
- body: { "input": { "spanId", "toolName", "toolInput", "rawTextFields": { "toolInput" } } }
71
+ body: the OTLP payload itself — { "resourceSpans": [ … one span, opencode.* attributes … ] }
71
72
  200: { "decision": "ALLOW"|"DENY"|"REVIEW", "reason", "userMessage?", "durationMs?" }
73
+ 410: the manager no longer accepts this body shape → recorded as fail-open `refused`
72
74
  ```
73
75
 
76
+ The verdict is then attached to that same span (`pinta.guard.*`) before it is sent, so the span the guard judged is the span the collector stores.
77
+
74
78
  A `DENY` becomes `throw new Error(userMessage ?? reason ?? "guard_deny")`. Verified effect: **only that tool is blocked** (`tool.execute.after` does not fire), the reason shows as `✗ … failed` + `Error: <reason>` to the model/TUI, and the session stays alive. `ALLOW`/`REVIEW` pass through.
75
79
 
76
80
  Guard is **fail-open** (no endpoint / `PINTA_GUARD_DISABLED=1` / non-200 / timeout / error → allow), so it never breaks a session. The 50ms inline call does not block tool execution.
@@ -88,6 +92,59 @@ Guard is **fail-open** (no endpoint / `PINTA_GUARD_DISABLED=1` / non-200 / timeo
88
92
 
89
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.
90
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
+
91
148
  ## Architecture
92
149
 
93
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
  };
@@ -189,6 +189,7 @@ var require_otlp = __commonJS({
189
189
  exports.clientAttrs = clientAttrs;
190
190
  exports.enforceSpanBudget = enforceSpanBudget;
191
191
  exports.buildPayload = buildPayload2;
192
+ exports.attachGuard = attachGuard3;
192
193
  exports.mergeBatch = mergeBatch2;
193
194
  var crypto_1 = __importDefault(__require("crypto"));
194
195
  var redact_js_1 = require_redact();
@@ -215,19 +216,19 @@ var require_otlp = __commonJS({
215
216
  return name.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/([A-Z])([A-Z][a-z])/g, "$1_$2").toLowerCase();
216
217
  }
217
218
  var EMPTY_SET = /* @__PURE__ */ new Set();
218
- function maybeRedactString(key, raw, policy) {
219
+ function maybeRedactString(key2, raw, policy) {
219
220
  const truncated = (0, redact_js_1.truncate)(raw);
220
- if ((policy.skipRedactKeys ?? EMPTY_SET).has(key))
221
+ if ((policy.skipRedactKeys ?? EMPTY_SET).has(key2))
221
222
  return truncated;
222
- const context = (policy.bashContextKeys ?? EMPTY_SET).has(key) ? "bash" : void 0;
223
+ const context = (policy.bashContextKeys ?? EMPTY_SET).has(key2) ? "bash" : void 0;
223
224
  return (0, redact_js_1.redact)(truncated, { context });
224
225
  }
225
- function toOtlpValue(key, v, policy = {}) {
226
+ function toOtlpValue(key2, v, policy = {}) {
226
227
  if (v === null || v === void 0)
227
228
  return null;
228
229
  switch (typeof v) {
229
230
  case "string":
230
- return { stringValue: maybeRedactString(key, v, policy) };
231
+ return { stringValue: maybeRedactString(key2, v, policy) };
231
232
  case "boolean":
232
233
  return { boolValue: v };
233
234
  case "number":
@@ -236,22 +237,22 @@ var require_otlp = __commonJS({
236
237
  return { doubleValue: v };
237
238
  case "object":
238
239
  try {
239
- return { stringValue: maybeRedactString(key, JSON.stringify(v), policy) };
240
+ return { stringValue: maybeRedactString(key2, JSON.stringify(v), policy) };
240
241
  } catch {
241
- return { stringValue: maybeRedactString(key, String(v), policy) };
242
+ return { stringValue: maybeRedactString(key2, String(v), policy) };
242
243
  }
243
244
  default:
244
- return { stringValue: maybeRedactString(key, String(v), policy) };
245
+ return { stringValue: maybeRedactString(key2, String(v), policy) };
245
246
  }
246
247
  }
247
- function attrsFromRecord2(record, prefix, policy = {}) {
248
+ function attrsFromRecord2(record2, prefix, policy = {}) {
248
249
  const out = [];
249
- for (const [k, v] of Object.entries(record)) {
250
- const key = `${prefix}.${k}`;
251
- 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);
252
253
  if (value === null)
253
254
  continue;
254
- out.push({ key, value });
255
+ out.push({ key: key2, value });
255
256
  }
256
257
  return out;
257
258
  }
@@ -310,11 +311,6 @@ var require_otlp = __commonJS({
310
311
  const ts = args.now ?? Date.now();
311
312
  const tsNano = (BigInt(ts) * 1000000n).toString();
312
313
  const attrs = [...args.attributes];
313
- if (args.guard)
314
- attrs.push(...guardAttrs(args.guard));
315
- const timing = args.clientTiming ?? (args.guard?.clientRttMs !== void 0 ? { op: "guard", rttMs: args.guard.clientRttMs } : null);
316
- if (timing)
317
- attrs.push(...clientAttrs(timing));
318
314
  const span = {
319
315
  traceId: ulidToTraceId2(args.traceId),
320
316
  spanId: newSpanId(),
@@ -325,7 +321,7 @@ var require_otlp = __commonJS({
325
321
  attributes: attrs
326
322
  };
327
323
  enforceSpanBudget(span);
328
- return {
324
+ const payload = {
329
325
  resourceSpans: [
330
326
  {
331
327
  resource: { attributes: args.resource },
@@ -333,6 +329,18 @@ var require_otlp = __commonJS({
333
329
  }
334
330
  ]
335
331
  };
332
+ return attachGuard3(payload, args.guard, args.clientTiming);
333
+ }
334
+ function attachGuard3(payload, guard, clientTiming) {
335
+ const span = payload.resourceSpans[0]?.scopeSpans[0]?.spans[0];
336
+ if (!span)
337
+ return payload;
338
+ if (guard)
339
+ span.attributes.push(...guardAttrs(guard));
340
+ const timing = clientTiming ?? (guard?.clientRttMs !== void 0 ? { op: "guard", rttMs: guard.clientRttMs } : null);
341
+ if (timing)
342
+ span.attributes.push(...clientAttrs(timing));
343
+ return payload;
336
344
  }
337
345
  function mergeBatch2(payloads) {
338
346
  const out = [];
@@ -348,6 +356,7 @@ var require_guard = __commonJS({
348
356
  "node_modules/@pinta-ai/core/dist/guard.js"(exports) {
349
357
  "use strict";
350
358
  Object.defineProperty(exports, "__esModule", { value: true });
359
+ exports.GUARD_STATUS_REFUSED = void 0;
351
360
  exports.evaluateGuard = evaluateGuard2;
352
361
  var DEFAULT_TIMEOUT_MS2 = 1e4;
353
362
  var DEFAULT_UA = "pinta-core";
@@ -362,7 +371,8 @@ var require_guard = __commonJS({
362
371
  };
363
372
  }
364
373
  var VALID_DECISIONS = /* @__PURE__ */ new Set(["ALLOW", "DENY", "REVIEW"]);
365
- async function evaluateGuard2(input, endpoint, opts = {}) {
374
+ exports.GUARD_STATUS_REFUSED = 410;
375
+ async function evaluateGuard2(payload, endpoint, opts = {}) {
366
376
  if (!endpoint)
367
377
  return null;
368
378
  if (opts.disabled)
@@ -377,11 +387,18 @@ var require_guard = __commonJS({
377
387
  headers: {
378
388
  "content-type": "application/json",
379
389
  "user-agent": opts.userAgent ?? DEFAULT_UA,
380
- "x-pinta-relay-token": opts.token ?? ""
390
+ "x-pinta-relay-token": opts.token ?? "",
391
+ // Spread rather than defaulted: an empty string is not "unknown" to the
392
+ // manager, it is a claim that fails validation, and sending one would
393
+ // make every adapter that never sets this look like it tried.
394
+ ...opts.agentType ? { "x-pinta-agent-type": opts.agentType } : {}
381
395
  },
382
- body: JSON.stringify({ input }),
396
+ body: JSON.stringify(payload),
383
397
  signal: controller.signal
384
398
  });
399
+ if (res.status === exports.GUARD_STATUS_REFUSED) {
400
+ return failOpen2(Date.now() - start, "refused");
401
+ }
385
402
  if (res.status !== 200) {
386
403
  return failOpen2(Date.now() - start, "error");
387
404
  }
@@ -907,19 +924,19 @@ var require_session_trace = __commonJS({
907
924
  }
908
925
  }
909
926
  newTrace(sessionId) {
910
- const key = sessionId || "default";
927
+ const key2 = sessionId || "default";
911
928
  const traceId = (0, trace_js_1.generateUlid)();
912
929
  const map = this.read();
913
- map[key] = traceId;
930
+ map[key2] = traceId;
914
931
  this.write(map);
915
932
  return traceId;
916
933
  }
917
934
  currentTrace(sessionId) {
918
- const key = sessionId || "default";
919
- const existing = this.read()[key];
935
+ const key2 = sessionId || "default";
936
+ const existing = this.read()[key2];
920
937
  if (existing)
921
938
  return existing;
922
- return this.newTrace(key);
939
+ return this.newTrace(key2);
923
940
  }
924
941
  };
925
942
  exports.DiskSessionTraceManager = DiskSessionTraceManager;
@@ -930,9 +947,9 @@ var require_session_trace = __commonJS({
930
947
  this.maxSessions = opts.maxSessions ?? DEFAULT_MAX_SESSIONS;
931
948
  }
932
949
  newTrace(sessionId) {
933
- const key = sessionId || "default";
950
+ const key2 = sessionId || "default";
934
951
  const traceId = (0, trace_js_1.generateUlid)();
935
- this.map.set(key, traceId);
952
+ this.map.set(key2, traceId);
936
953
  if (this.map.size > this.maxSessions) {
937
954
  const oldest = this.map.keys().next().value;
938
955
  if (oldest !== void 0)
@@ -941,8 +958,8 @@ var require_session_trace = __commonJS({
941
958
  return traceId;
942
959
  }
943
960
  currentTrace(sessionId) {
944
- const key = sessionId || "default";
945
- return this.map.get(key) ?? this.newTrace(key);
961
+ const key2 = sessionId || "default";
962
+ return this.map.get(key2) ?? this.newTrace(key2);
946
963
  }
947
964
  };
948
965
  exports.MemorySessionTraceManager = MemorySessionTraceManager2;
@@ -977,13 +994,13 @@ var require_env_file = __commonJS({
977
994
  const idx = line.indexOf("=");
978
995
  if (idx < 0)
979
996
  continue;
980
- const key = line.slice(0, idx).trim();
997
+ const key2 = line.slice(0, idx).trim();
981
998
  let value = line.slice(idx + 1).trim();
982
999
  if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
983
1000
  value = value.slice(1, -1);
984
1001
  }
985
- if (key)
986
- out[key] = value;
1002
+ if (key2)
1003
+ out[key2] = value;
987
1004
  }
988
1005
  return out;
989
1006
  }
@@ -995,9 +1012,9 @@ var require_env_file = __commonJS({
995
1012
  return;
996
1013
  }
997
1014
  const parsed = parseEnvFile2(content);
998
- for (const [key, value] of Object.entries(parsed)) {
999
- if (process.env[key] === void 0) {
1000
- 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;
1001
1018
  }
1002
1019
  }
1003
1020
  }
@@ -1009,7 +1026,7 @@ var require_dist = __commonJS({
1009
1026
  "node_modules/@pinta-ai/core/dist/index.js"(exports) {
1010
1027
  "use strict";
1011
1028
  Object.defineProperty(exports, "__esModule", { value: true });
1012
- exports.loadEnvFile = exports.parseEnvFile = exports.envFilePath = exports.MemorySessionTraceManager = exports.DiskSessionTraceManager = exports.generateUlid = exports.TraceManager = exports.MAX_POST_BYTES = exports.parseHeadersEnv = exports.envOptionsResolver = exports.postOtlp = exports.MemoryTransport = exports.DiskTransport = exports.MemoryRetryQueue = exports.DiskRetryQueue = exports.evaluateGuard = exports.MAX_SPAN_BYTES = exports.enforceSpanBudget = exports.mergeBatch = exports.buildPayload = exports.clientAttrs = exports.guardAttrs = exports.attrsFromRecord = exports.toOtlpValue = exports.snakeCase = exports.newSpanId = exports.ulidToTraceId = exports.MAX_BYTES = exports.PATTERNS = exports.applyMatches = exports.resolveOverlaps = exports.collectMatches = exports.truncateTo = exports.truncate = exports.redact = void 0;
1029
+ exports.loadEnvFile = exports.parseEnvFile = exports.envFilePath = exports.MemorySessionTraceManager = exports.DiskSessionTraceManager = exports.generateUlid = exports.TraceManager = exports.MAX_POST_BYTES = exports.parseHeadersEnv = exports.envOptionsResolver = exports.postOtlp = exports.MemoryTransport = exports.DiskTransport = exports.MemoryRetryQueue = exports.DiskRetryQueue = exports.GUARD_STATUS_REFUSED = exports.evaluateGuard = exports.MAX_SPAN_BYTES = exports.enforceSpanBudget = exports.mergeBatch = exports.attachGuard = exports.buildPayload = exports.clientAttrs = exports.guardAttrs = exports.attrsFromRecord = exports.toOtlpValue = exports.snakeCase = exports.newSpanId = exports.ulidToTraceId = exports.MAX_BYTES = exports.PATTERNS = exports.applyMatches = exports.resolveOverlaps = exports.collectMatches = exports.truncateTo = exports.truncate = exports.redact = void 0;
1013
1030
  var redact_js_1 = require_redact();
1014
1031
  Object.defineProperty(exports, "redact", { enumerable: true, get: function() {
1015
1032
  return redact_js_1.redact;
@@ -1060,6 +1077,9 @@ var require_dist = __commonJS({
1060
1077
  Object.defineProperty(exports, "buildPayload", { enumerable: true, get: function() {
1061
1078
  return otlp_js_1.buildPayload;
1062
1079
  } });
1080
+ Object.defineProperty(exports, "attachGuard", { enumerable: true, get: function() {
1081
+ return otlp_js_1.attachGuard;
1082
+ } });
1063
1083
  Object.defineProperty(exports, "mergeBatch", { enumerable: true, get: function() {
1064
1084
  return otlp_js_1.mergeBatch;
1065
1085
  } });
@@ -1073,6 +1093,9 @@ var require_dist = __commonJS({
1073
1093
  Object.defineProperty(exports, "evaluateGuard", { enumerable: true, get: function() {
1074
1094
  return guard_js_1.evaluateGuard;
1075
1095
  } });
1096
+ Object.defineProperty(exports, "GUARD_STATUS_REFUSED", { enumerable: true, get: function() {
1097
+ return guard_js_1.GUARD_STATUS_REFUSED;
1098
+ } });
1076
1099
  var retry_queue_js_1 = require_retry_queue();
1077
1100
  Object.defineProperty(exports, "DiskRetryQueue", { enumerable: true, get: function() {
1078
1101
  return retry_queue_js_1.DiskRetryQueue;
@@ -1212,12 +1235,20 @@ var Transport = class extends import_core3.MemoryTransport {
1212
1235
  // src/core/trace.ts
1213
1236
  var import_core4 = __toESM(require_dist(), 1);
1214
1237
 
1238
+ // src/plugin.ts
1239
+ var import_core8 = __toESM(require_dist(), 1);
1240
+
1215
1241
  // src/core/guard.ts
1216
1242
  var import_core5 = __toESM(require_dist(), 1);
1243
+
1244
+ // src/core/version.ts
1245
+ var ADAPTER_VERSION = "0.10.0";
1246
+
1247
+ // src/core/guard.ts
1217
1248
  var DEFAULT_TIMEOUT_MS = 50;
1218
- var GUARD_UA = "pinta-opencode/0.7.0";
1219
- function evaluateGuard(input, endpoint, opts = {}) {
1220
- return (0, import_core5.evaluateGuard)(input, endpoint, {
1249
+ var GUARD_UA = `pinta-opencode/${ADAPTER_VERSION}`;
1250
+ function evaluateGuard(payload, endpoint, opts = {}) {
1251
+ return (0, import_core5.evaluateGuard)(payload, endpoint, {
1221
1252
  timeoutMs: opts.timeoutMs ?? DEFAULT_TIMEOUT_MS,
1222
1253
  token: opts.token,
1223
1254
  disabled: opts.disabled,
@@ -1225,9 +1256,12 @@ function evaluateGuard(input, endpoint, opts = {}) {
1225
1256
  });
1226
1257
  }
1227
1258
 
1259
+ // src/telemetry.ts
1260
+ var import_core7 = __toESM(require_dist(), 1);
1261
+
1228
1262
  // src/core/otlp.ts
1229
- var import_core6 = __toESM(require_dist(), 1);
1230
1263
  import os from "os";
1264
+ var import_core6 = __toESM(require_dist(), 1);
1231
1265
  var cachedProcessOwner;
1232
1266
  function processOwner() {
1233
1267
  if (cachedProcessOwner === void 0) {
@@ -1239,7 +1273,7 @@ function processOwner() {
1239
1273
  }
1240
1274
  return cachedProcessOwner;
1241
1275
  }
1242
- var SDK_VERSION = "0.7.0";
1276
+ var SDK_VERSION = ADAPTER_VERSION;
1243
1277
  var SKIP_REDACT_KEYS = /* @__PURE__ */ new Set([
1244
1278
  "opencode.hook",
1245
1279
  "opencode.event_type",
@@ -1289,11 +1323,262 @@ function buildOtlpPayload(args) {
1289
1323
  attributes: flattenFields(args.fields),
1290
1324
  resource: resourceAttrs(args.serviceVersion),
1291
1325
  scope: { name: "pinta-opencode", version: SDK_VERSION },
1292
- now: args.now,
1293
- guard: args.guard
1326
+ now: args.now
1294
1327
  });
1295
1328
  }
1296
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
+
1297
1582
  // src/telemetry.ts
1298
1583
  var Telemetry = class {
1299
1584
  constructor(transport, trace, config) {
@@ -1301,43 +1586,68 @@ var Telemetry = class {
1301
1586
  this.trace = trace;
1302
1587
  this.config = config;
1303
1588
  }
1304
- async emit(name, sessionId, fields, guard) {
1589
+ models = new ModelTracker();
1590
+ build(name, sessionId, fields) {
1305
1591
  const traceId = this.trace.currentTrace(sessionId);
1306
- const payload = buildOtlpPayload({ name, traceId, fields, guard, serviceVersion: this.config.serviceVersion });
1592
+ return buildOtlpPayload({ name, traceId, fields, serviceVersion: this.config.serviceVersion });
1593
+ }
1594
+ /** Send a payload built by one of the `*Payload` methods (best-effort by the transport's contract). */
1595
+ async send(payload) {
1307
1596
  await this.transport.send(payload);
1308
1597
  }
1598
+ async emit(name, sessionId, fields) {
1599
+ await this.send(this.build(name, sessionId, fields));
1600
+ }
1601
+ chatMessage(input, output) {
1602
+ this.models.chatMessage(input, output);
1603
+ }
1604
+ chatParams(input) {
1605
+ this.models.chatParams(input);
1606
+ }
1309
1607
  /** Lifecycle span from the `event` hook. Flushes the retry buffer on turn-END. */
1310
1608
  async lifecycle(ev) {
1311
- const props = ev.properties ?? {};
1312
- const sessionId = typeof props.sessionID === "string" ? props.sessionID : void 0;
1313
- 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({
1314
1613
  hook: "event",
1315
1614
  event_type: ev.type,
1316
1615
  session_id: sessionId,
1317
1616
  cwd: process.cwd(),
1318
1617
  ...props
1319
- });
1618
+ }, model));
1320
1619
  if (ev.type === "session.idle") await this.transport.flush();
1321
1620
  }
1322
- /** Tool span from `tool.execute.before`, carrying the guard decision. */
1323
- async toolBefore(input, args, guard) {
1324
- await this.emit(
1621
+ /**
1622
+ * Tool span from `tool.execute.before`, before the guard has been asked. The
1623
+ * plugin asks the guard about this payload, attaches the verdict with
1624
+ * `attachGuard`, and sends the same object — so the span the manager judged
1625
+ * is the span the backend stores.
1626
+ */
1627
+ toolBeforePayload(input, args) {
1628
+ return this.build(
1325
1629
  "opencode.tool.before",
1326
1630
  input.sessionID,
1327
- { ...toolIdentity("tool.execute.before", input), tool_input: args },
1328
- guard
1631
+ modelFields(
1632
+ { ...toolIdentity("tool.execute.before", input), tool_input: args },
1633
+ this.models.beforeTool(input)
1634
+ )
1329
1635
  );
1330
1636
  }
1637
+ /** Tool span from `tool.execute.before`, carrying an already-known guard decision. */
1638
+ async toolBefore(input, args, guard) {
1639
+ await this.send((0, import_core7.attachGuard)(this.toolBeforePayload(input, args), guard));
1640
+ }
1331
1641
  /** Tool result span from `tool.execute.after`, incl. exit code / truncation. */
1332
1642
  async toolAfter(input, output) {
1333
1643
  const meta = output.metadata ?? {};
1334
- await this.emit("opencode.tool.after", input.sessionID, {
1644
+ await this.emit("opencode.tool.after", input.sessionID, modelFields({
1335
1645
  ...toolIdentity("tool.execute.after", input),
1336
1646
  title: output.title,
1337
1647
  tool_response: output.output,
1338
1648
  exit: meta.exit,
1339
1649
  truncated: meta.truncated
1340
- });
1650
+ }, this.models.afterTool(input)));
1341
1651
  }
1342
1652
  };
1343
1653
  function toolIdentity(hook, input) {
@@ -1346,7 +1656,12 @@ function toolIdentity(hook, input) {
1346
1656
  tool_name: input.tool,
1347
1657
  session_id: input.sessionID,
1348
1658
  tool_use_id: input.callID,
1349
- 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 } : {}
1350
1665
  };
1351
1666
  }
1352
1667
 
@@ -1372,19 +1687,13 @@ var PintaOpencode = async (_input, options) => {
1372
1687
  async function guardAndTrace(input, args) {
1373
1688
  let guard = null;
1374
1689
  try {
1690
+ const payload = telemetry.toolBeforePayload(input, args);
1375
1691
  guard = await evaluateGuard(
1376
- {
1377
- spanId: input.sessionID,
1378
- toolName: input.tool,
1379
- method: "tool.execute.before",
1380
- cwd: process.cwd(),
1381
- toolInput: args,
1382
- rawTextFields: { toolInput: safeStringify(args) }
1383
- },
1692
+ payload,
1384
1693
  config.guardEndpoint,
1385
1694
  { timeoutMs: config.guardTimeoutMs, token: config.relayToken, disabled: config.guardDisabled }
1386
1695
  );
1387
- await telemetry.toolBefore(input, args, guard);
1696
+ await telemetry.send((0, import_core8.attachGuard)(payload, guard));
1388
1697
  } catch (err) {
1389
1698
  warn("tool.execute.before", err);
1390
1699
  }
@@ -1392,8 +1701,12 @@ var PintaOpencode = async (_input, options) => {
1392
1701
  }
1393
1702
  return {
1394
1703
  // turn-START → rotate a new trace for this session.
1395
- "chat.message": failOpen("chat.message", async (input) => {
1704
+ "chat.message": failOpen("chat.message", async (input, output) => {
1396
1705
  trace.newTrace(input?.sessionID);
1706
+ telemetry.chatMessage(input, output);
1707
+ }),
1708
+ "chat.params": failOpen("chat.params", async (input, _output) => {
1709
+ telemetry.chatParams(input);
1397
1710
  }),
1398
1711
  // lifecycle telemetry; flushes the retry buffer on session.idle (turn-END).
1399
1712
  event: failOpen("event", async (input) => {
@@ -1414,13 +1727,6 @@ var PintaOpencode = async (_input, options) => {
1414
1727
  )
1415
1728
  };
1416
1729
  };
1417
- function safeStringify(v) {
1418
- try {
1419
- return typeof v === "string" ? v : JSON.stringify(v) ?? "";
1420
- } catch {
1421
- return String(v);
1422
- }
1423
- }
1424
1730
  var plugin_default = PintaOpencode;
1425
1731
 
1426
1732
  // src/lifecycle/scanner.ts
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 tt=Object.create;var ue=Object.defineProperty;var rt=Object.getOwnPropertyDescriptor;var nt=Object.getOwnPropertyNames;var ot=Object.getPrototypeOf,st=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 _=(t,e)=>()=>(e||t((e={exports:{}}).exports,e),e.exports);var it=(t,e,r,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of nt(e))!st.call(t,o)&&o!==r&&ue(t,o,{get:()=>e[o],enumerable:!(n=rt(e,o))||n.enumerable});return t};var E=(t,e,r)=>(r=t!=null?tt(ot(t)):{},it(e||!t||!t.__esModule?ue(r,"default",{value:t,enumerable:!0}):r,t));var F=_(p=>{"use strict";Object.defineProperty(p,"__esModule",{value:!0});p.PATTERNS=p.MAX_BYTES=void 0;p.truncateTo=ce;p.truncate=at;p.collectMatches=le;p.resolveOverlaps=de;p.applyMatches=fe;p.redact=ut;p.MAX_BYTES=102400;function ce(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 at(t){return ce(t,p.MAX_BYTES)}p.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 le(t,e){let r=[];for(let n of p.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,P=c+i[0].indexOf(a),et=P+a.length;r.push({start:c,end:d,replaceStart:P,replaceEnd:et,type:n.type}),i.index===o.lastIndex&&o.lastIndex++}}return r}function de(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 ut(t,e={}){if(t.length===0)return t;let r=le(t,e);if(r.length===0)return t;let n=de(r);return fe(t,n)}});var V=_(f=>{"use strict";var ct=f&&f.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(f,"__esModule",{value:!0});f.MAX_SPAN_BYTES=void 0;f.ulidToTraceId=ye;f.newSpanId=_e;f.snakeCase=ft;f.toOtlpValue=me;f.attrsFromRecord=pt;f.guardAttrs=Oe;f.clientAttrs=Te;f.enforceSpanBudget=Pe;f.buildPayload=gt;f.mergeBatch=ht;var lt=ct(h("crypto")),$=F(),dt="0123456789ABCDEFGHJKMNPQRSTVWXYZ";function ye(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=dt.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 _e(){return lt.default.randomBytes(8).toString("hex")}function ft(t){return t.replace(/([a-z0-9])([A-Z])/g,"$1_$2").replace(/([A-Z])([A-Z][a-z])/g,"$1_$2").toLowerCase()}var pe=new Set;function R(t,e,r){let n=(0,$.truncate)(e);if((r.skipRedactKeys??pe).has(t))return n;let o=(r.bashContextKeys??pe).has(t)?"bash":void 0;return(0,$.redact)(n,{context:o})}function me(t,e,r={}){if(e==null)return null;switch(typeof e){case"string":return{stringValue:R(t,e,r)};case"boolean":return{boolValue:e};case"number":return Number.isInteger(e)?{intValue:e}:{doubleValue:e};case"object":try{return{stringValue:R(t,JSON.stringify(e),r)}}catch{return{stringValue:R(t,String(e),r)}}default:return{stringValue:R(t,String(e),r)}}}function pt(t,e,r={}){let n=[];for(let[o,i]of Object.entries(t)){let u=`${e}.${o}`,a=me(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 Te(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}}]}f.MAX_SPAN_BYTES=800*1024;function ge(t){let e=JSON.stringify(t);return e===void 0?0:Buffer.byteLength(e,"utf-8")}var he=1024;function Pe(t,e=f.MAX_SPAN_BYTES){let r=ge(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<=he)break;let u=r-e,a=Math.max(he,i-u-64);o.stringValue=(0,$.truncateTo)(o.stringValue,a),r=ge(t)}return t}function gt(t){let e=t.now??Date.now(),r=(BigInt(e)*1000000n).toString(),n=[...t.attributes];t.guard&&n.push(...Oe(t.guard));let o=t.clientTiming??(t.guard?.clientRttMs!==void 0?{op:"guard",rttMs:t.guard.clientRttMs}:null);o&&n.push(...Te(o));let i={traceId:ye(t.traceId),spanId:_e(),name:t.spanName,kind:t.spanKind??1,startTimeUnixNano:r,endTimeUnixNano:r,attributes:n};return Pe(i),{resourceSpans:[{resource:{attributes:t.resource},scopeSpans:[{scope:t.scope,spans:[i]}]}]}}function ht(t){let e=[];for(let r of t)e.push(...r.resourceSpans);return{resourceSpans:e}}});var ve=_(G=>{"use strict";Object.defineProperty(G,"__esModule",{value:!0});G.evaluateGuard=Ot;var yt=1e4,_t="pinta-core";function L(t,e){return{decision:"ALLOW",reason:null,userMessage:null,durationMs:t,clientRttMs:t,failOpenReason:e}}var mt=new Set(["ALLOW","DENY","REVIEW"]);async function Ot(t,e,r={}){if(!e||r.disabled)return null;let n=r.timeoutMs??yt,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??_t,"x-pinta-relay-token":r.token??""},body:JSON.stringify({input:t}),signal:i.signal});if(a.status!==200)return L(Date.now()-o,"error");let c=await a.json(),d=c?.durationMs===void 0||typeof c.durationMs=="number"&&Number.isFinite(c.durationMs);if(!c||!mt.has(c.decision)||!d)return L(Date.now()-o,"error");let P=Date.now()-o;return{decision:c.decision,reason:c.reason??null,userMessage:c.userMessage??null,durationMs:c.durationMs??P,clientRttMs:P}}catch(a){let c=a.name,d=c==="AbortError"||c==="TimeoutError"?"timeout":"error";return L(Date.now()-o,d)}finally{clearTimeout(u)}}});var I=_(w=>{"use strict";var Tt=w&&w.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(w,"__esModule",{value:!0});w.atomicWriteFileSync=Pt;var Se=Tt(h("fs"));function Pt(t,e){let r=`${t}.${process.pid}.tmp`;Se.default.writeFileSync(r,e),Se.default.renameSync(r,t)}});var z=_(m=>{"use strict";var be=m&&m.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(m,"__esModule",{value:!0});m.MemoryRetryQueue=m.DiskRetryQueue=void 0;var g=be(h("fs")),D=be(h("path")),vt=I(),j=1e3,St=50,bt=5,X=class{filePath;lockPath;logPrefix;constructor(e,r){this.filePath=D.default.join(e,"failed-spans.jsonl"),this.lockPath=this.filePath+".lock",this.logPrefix=r}enqueue(e){try{g.default.mkdirSync(D.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(D.default.dirname(this.filePath),{recursive:!0}),(0,vt.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(D.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<St;)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=bt,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<=j)return;let r=e.length-j;process.stderr.write(`[${this.logPrefix}] retry-queue full, dropping ${r} oldest entries
11
- `),this.rewrite(e.slice(r))}};m.DiskRetryQueue=X;var Y=class{entries=[];enqueue(e){this.entries.push(e),this.entries.length>j&&this.entries.splice(0,this.entries.length-j)}drain(){let e=this.entries;return this.entries=[],e}get size(){return this.entries.length}};m.MemoryRetryQueue=Y});var xe=_(l=>{"use strict";Object.defineProperty(l,"__esModule",{value:!0});l.MemoryTransport=l.DiskTransport=l.MAX_POST_BYTES=void 0;l.parseHeadersEnv=we;l.envOptionsResolver=Me;l.postOtlp=M;var Ee=z(),Ae=V(),Et=5e3;l.MAX_POST_BYTES=900*1024;function N(t){let e=JSON.stringify(t);return e===void 0?0:Buffer.byteLength(e,"utf-8")}function ke(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 we(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 Me(){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:we(process.env.OTEL_EXPORTER_OTLP_HEADERS)}:null}async function M(t,e,r){let n=new AbortController,o=setTimeout(()=>n.abort(),Et);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 U=class{queue;logPrefix;resolveOptions;constructor(e){this.queue=new Ee.DiskRetryQueue(e.pluginData,e.logPrefix),this.logPrefix=e.logPrefix,this.resolveOptions=e.resolveOptions??Me}async send(e){let r=this.resolveOptions();if(!r||this.dropIfOversized(e))return;await M(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}=ke(r,a=>N(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 M((0,Ae.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=N(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=U;var K=class{queue=new Ee.MemoryRetryQueue;logPrefix;resolveOptions;constructor(e){this.logPrefix=e.logPrefix,this.resolveOptions=e.resolveOptions}async send(e){let r=this.resolveOptions();if(!r)return;if(N(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 M(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}=ke(r,N,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 M((0,Ae.mergeBatch)(a),e,this.logPrefix))break;u+=a.length}for(let a of i.slice(u))this.queue.enqueue(a)}};l.MemoryTransport=K});var H=_(v=>{"use strict";var W=v&&v.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(v,"__esModule",{value:!0});v.TraceManager=void 0;v.generateUlid=Ie;var Re=W(h("fs")),At=W(h("path")),kt=W(h("crypto")),wt=I(),Z="0123456789ABCDEFGHJKMNPQRSTVWXYZ";function Ie(){let t=Date.now(),e="",r=t;for(let i=0;i<10;i++)e=Z[r&31]+e,r=Math.floor(r/32);let n=kt.default.randomBytes(10),o="";for(let i=0;i<10;i++)o+=Z[n[i]&31];for(;o.length<16;)o+=Z[0];return e+o}var J=class{tracePath;constructor(e){this.tracePath=e}newTrace(){let e=Ie();return this.save(e),e}currentTrace(){try{let e=Re.default.readFileSync(this.tracePath,"utf-8"),{traceId:r}=JSON.parse(e);if(r)return r}catch{}return this.newTrace()}save(e){try{Re.default.mkdirSync(At.default.dirname(this.tracePath),{recursive:!0}),(0,wt.atomicWriteFileSync)(this.tracePath,JSON.stringify({traceId:e}))}catch(r){process.stderr.write(`[pinta-core] trace persistence failed: ${r}
19
- `)}}};v.TraceManager=J});var qe=_(O=>{"use strict";var je=O&&O.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(O,"__esModule",{value:!0});O.MemorySessionTraceManager=O.DiskSessionTraceManager=void 0;var De=je(h("fs")),Mt=je(h("path")),Ne=H(),xt=I(),Be=200,Q=class{tracePath;maxSessions;constructor(e,r={}){this.tracePath=e,this.maxSessions=r.maxSessions??Be}read(){try{let e=JSON.parse(De.default.readFileSync(this.tracePath,"utf-8"));if(e&&typeof e=="object"&&!("traceId"in e))return e}catch{}return{}}write(e){try{De.default.mkdirSync(Mt.default.dirname(this.tracePath),{recursive:!0});let r=Object.entries(e),n=r.length>this.maxSessions?Object.fromEntries(r.slice(-this.maxSessions)):e;(0,xt.atomicWriteFileSync)(this.tracePath,JSON.stringify(n))}catch{}}newTrace(e){let r=e||"default",n=(0,Ne.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)}};O.DiskSessionTraceManager=Q;var ee=class{map=new Map;maxSessions;constructor(e={}){this.maxSessions=e.maxSessions??Be}newTrace(e){let r=e||"default",n=(0,Ne.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)}};O.MemorySessionTraceManager=ee});var $e=_(S=>{"use strict";var te=S&&S.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(S,"__esModule",{value:!0});S.envFilePath=Dt;S.parseEnvFile=Fe;S.loadEnvFile=jt;var Rt=te(h("node:fs")),It=te(h("node:os")),Ce=te(h("node:path"));function Dt(t,e,r){let n=r?process.env[r]:void 0,o=n&&n.length>0?n:Ce.default.join(It.default.homedir(),t);return Ce.default.join(o,e)}function Fe(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 jt(t){let e;try{e=Rt.default.readFileSync(t,"utf-8")}catch{return}let r=Fe(e);for(let[n,o]of Object.entries(r))process.env[n]===void 0&&(process.env[n]=o)}});var b=_(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.evaluateGuard=s.MAX_SPAN_BYTES=s.enforceSpanBudget=s.mergeBatch=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 T=F();Object.defineProperty(s,"redact",{enumerable:!0,get:function(){return T.redact}});Object.defineProperty(s,"truncate",{enumerable:!0,get:function(){return T.truncate}});Object.defineProperty(s,"truncateTo",{enumerable:!0,get:function(){return T.truncateTo}});Object.defineProperty(s,"collectMatches",{enumerable:!0,get:function(){return T.collectMatches}});Object.defineProperty(s,"resolveOverlaps",{enumerable:!0,get:function(){return T.resolveOverlaps}});Object.defineProperty(s,"applyMatches",{enumerable:!0,get:function(){return T.applyMatches}});Object.defineProperty(s,"PATTERNS",{enumerable:!0,get:function(){return T.PATTERNS}});Object.defineProperty(s,"MAX_BYTES",{enumerable:!0,get:function(){return T.MAX_BYTES}});var y=V();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,"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 Nt=ve();Object.defineProperty(s,"evaluateGuard",{enumerable:!0,get:function(){return Nt.evaluateGuard}});var Ve=z();Object.defineProperty(s,"DiskRetryQueue",{enumerable:!0,get:function(){return Ve.DiskRetryQueue}});Object.defineProperty(s,"MemoryRetryQueue",{enumerable:!0,get:function(){return Ve.MemoryRetryQueue}});var A=xe();Object.defineProperty(s,"DiskTransport",{enumerable:!0,get:function(){return A.DiskTransport}});Object.defineProperty(s,"MemoryTransport",{enumerable:!0,get:function(){return A.MemoryTransport}});Object.defineProperty(s,"postOtlp",{enumerable:!0,get:function(){return A.postOtlp}});Object.defineProperty(s,"envOptionsResolver",{enumerable:!0,get:function(){return A.envOptionsResolver}});Object.defineProperty(s,"parseHeadersEnv",{enumerable:!0,get:function(){return A.parseHeadersEnv}});Object.defineProperty(s,"MAX_POST_BYTES",{enumerable:!0,get:function(){return A.MAX_POST_BYTES}});var Le=H();Object.defineProperty(s,"TraceManager",{enumerable:!0,get:function(){return Le.TraceManager}});Object.defineProperty(s,"generateUlid",{enumerable:!0,get:function(){return Le.generateUlid}});var Ge=qe();Object.defineProperty(s,"DiskSessionTraceManager",{enumerable:!0,get:function(){return Ge.DiskSessionTraceManager}});Object.defineProperty(s,"MemorySessionTraceManager",{enumerable:!0,get:function(){return Ge.MemorySessionTraceManager}});var re=$e();Object.defineProperty(s,"envFilePath",{enumerable:!0,get:function(){return re.envFilePath}});Object.defineProperty(s,"parseEnvFile",{enumerable:!0,get:function(){return re.parseEnvFile}});Object.defineProperty(s,"loadEnvFile",{enumerable:!0,get:function(){return re.loadEnvFile}})});var Ye=E(b(),1);import qt from"node:fs";import ne from"node:path";var x=E(b(),1);function Bt(){return(0,x.envFilePath)(".config/opencode","pinta-opencode.env","OPENCODE_CONFIG_DIR")}function Xe(t=Bt()){(0,x.loadEnvFile)(t)}function oe(...t){return t.find(e=>e)||void 0}function Ct(t){let e=oe(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 Ft="opencode-ai";function $t(){let t=ne.dirname(process.execPath);for(let e=0;e<5;e++){try{let n=JSON.parse(qt.readFileSync(ne.join(t,"package.json"),"utf8"));if(n.name===Ft&&typeof n.version=="string"&&n.version)return n.version}catch{}let r=ne.dirname(t);if(r===t)break;t=r}}function Vt(){let t=process.env.OPENCODE_VERSION?.trim();return t||$t()}function ze(t={}){Xe();let e=oe(t.token,process.env.PINTA_OPENCODE_TOKEN),r=(0,Ye.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:Ct(t),headers:r,guardEndpoint:oe(t.guard,process.env.PINTA_OPENCODE_GUARD),relayToken:e,guardTimeoutMs:n,guardDisabled:process.env.PINTA_OPENCODE_GUARD_DISABLED==="1",serviceVersion:Vt()}}var Ue=E(b(),1),B=class extends Ue.MemoryTransport{constructor(e){super({logPrefix:"pinta-opencode",resolveOptions:()=>e.endpoint?{endpoint:e.endpoint,headers:e.headers}:null})}};var se=E(b(),1);var Ke=E(b(),1),Lt=50,Gt="pinta-opencode/0.7.0";function Ze(t,e,r={}){return(0,Ke.evaluateGuard)(t,e,{timeoutMs:r.timeoutMs??Lt,token:r.token,disabled:r.disabled,userAgent:Gt})}var k=E(b(),1);import ie from"os";var q;function Xt(){if(q===void 0)try{q=ie.userInfo().username}catch{q=process.env.USER??process.env.LOGNAME??(typeof process.getuid=="function"?String(process.getuid()):"unknown")}return q}var Je="0.7.0",Yt=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"]),zt=new Set(["opencode.tool_input","opencode.tool_response"]),Ut={skipRedactKeys:Yt,bashContextKeys:zt};function Kt(t){let e=[{key:"ingest.type",value:{stringValue:"opencode"}}];return e.push(...(0,k.attrsFromRecord)(t,"opencode",Ut)),e}function Zt(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:Je}},{key:"process.pid",value:{intValue:process.pid}},{key:"process.owner",value:{stringValue:Xt()}},{key:"host.name",value:{stringValue:ie.hostname()}},{key:"host.arch",value:{stringValue:ie.arch()}}]}function We(t){return(0,k.buildPayload)({traceId:t.traceId,spanName:t.name,attributes:Kt(t.fields),resource:Zt(t.serviceVersion),scope:{name:"pinta-opencode",version:Je},now:t.now,guard:t.guard})}var C=class{constructor(e,r,n){this.transport=e;this.trace=r;this.config=n}async emit(e,r,n,o){let i=this.trace.currentTrace(r),u=We({name:e,traceId:i,fields:n,guard:o,serviceVersion:this.config.serviceVersion});await this.transport.send(u)}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()}async toolBefore(e,r,n){await this.emit("opencode.tool.before",e.sessionID,{...He("tool.execute.before",e),tool_input:r},n)}async toolAfter(e,r){let n=r.metadata??{};await this.emit("opencode.tool.after",e.sessionID,{...He("tool.execute.after",e),title:r.title,tool_response:r.output,exit:n.exit,truncated:n.truncated})}};function He(t,e){return{hook:t,tool_name:e.tool,session_id:e.sessionID,tool_use_id:e.callID,cwd:process.cwd()}}function Qe(t,e){process.stderr.write(`[pinta-opencode] ${t}: ${e?.message??String(e)}
21
- `)}function ae(t,e){return async(...r)=>{try{await e(...r)}catch(n){Qe(t,n)}}}var Jt=async(t,e)=>{let r=ze(e??{}),n=new B({endpoint:r.endpoint,headers:r.headers}),o=new se.MemorySessionTraceManager,i=new C(n,o,r);async function u(a,c){let d=null;try{d=await Ze({spanId:a.sessionID,toolName:a.tool,method:"tool.execute.before",cwd:process.cwd(),toolInput:c,rawTextFields:{toolInput:Wt(c)}},r.guardEndpoint,{timeoutMs:r.guardTimeoutMs,token:r.relayToken,disabled:r.guardDisabled}),await i.toolBefore(a,c,d)}catch(P){Qe("tool.execute.before",P)}return d}return{"chat.message":ae("chat.message",async a=>{o.newTrace(a?.sessionID)}),event:ae("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":ae("tool.execute.after",(a,c)=>i.toolAfter(a,c))}};function Wt(t){try{return typeof t=="string"?t:JSON.stringify(t)??""}catch{return String(t)}}var Ar=Jt;export{Jt as PintaOpencode,Ar 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.8.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",
@@ -36,7 +36,7 @@
36
36
  "prepublishOnly": "npm run build"
37
37
  },
38
38
  "devDependencies": {
39
- "@pinta-ai/core": "^0.6.0",
39
+ "@pinta-ai/core": "^0.8.0",
40
40
  "@types/node": "^22.0.0",
41
41
  "esbuild": "0.24.2",
42
42
  "typescript": "^5.7.0",