@pinta-ai/pinta-copilot 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/CHANGELOG.md +59 -0
  2. package/LICENSE +136 -0
  3. package/README.md +116 -0
  4. package/dist/core/config.d.ts +15 -0
  5. package/dist/core/config.js +19 -0
  6. package/dist/core/config.js.map +1 -0
  7. package/dist/core/env-bridge.d.ts +25 -0
  8. package/dist/core/env-bridge.js +43 -0
  9. package/dist/core/env-bridge.js.map +1 -0
  10. package/dist/core/guard.d.ts +14 -0
  11. package/dist/core/guard.js +48 -0
  12. package/dist/core/guard.js.map +1 -0
  13. package/dist/core/otlp.d.ts +55 -0
  14. package/dist/core/otlp.js +184 -0
  15. package/dist/core/otlp.js.map +1 -0
  16. package/dist/core/redact.d.ts +57 -0
  17. package/dist/core/redact.js +149 -0
  18. package/dist/core/redact.js.map +1 -0
  19. package/dist/core/retry-queue.d.ts +26 -0
  20. package/dist/core/retry-queue.js +127 -0
  21. package/dist/core/retry-queue.js.map +1 -0
  22. package/dist/core/surface.d.ts +21 -0
  23. package/dist/core/surface.js +13 -0
  24. package/dist/core/surface.js.map +1 -0
  25. package/dist/core/trace.d.ts +20 -0
  26. package/dist/core/trace.js +80 -0
  27. package/dist/core/trace.js.map +1 -0
  28. package/dist/core/transport.d.ts +18 -0
  29. package/dist/core/transport.js +127 -0
  30. package/dist/core/transport.js.map +1 -0
  31. package/dist/core/types.d.ts +49 -0
  32. package/dist/core/types.js +124 -0
  33. package/dist/core/types.js.map +1 -0
  34. package/dist/env-file.d.ts +4 -0
  35. package/dist/env-file.js +69 -0
  36. package/dist/env-file.js.map +1 -0
  37. package/dist/index.d.ts +1 -0
  38. package/dist/index.js +71 -0
  39. package/dist/index.js.map +1 -0
  40. package/dist/tools/doctor.d.ts +1 -0
  41. package/dist/tools/doctor.js +81 -0
  42. package/dist/tools/doctor.js.map +1 -0
  43. package/dist/tools/install-hooks.d.ts +1 -0
  44. package/dist/tools/install-hooks.js +101 -0
  45. package/dist/tools/install-hooks.js.map +1 -0
  46. package/package.json +38 -0
@@ -0,0 +1,80 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.TraceManager = void 0;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const crypto_1 = __importDefault(require("crypto"));
10
+ const CROCKFORD = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
11
+ function generateUlid() {
12
+ const now = Date.now();
13
+ let ts = "";
14
+ let t = now;
15
+ for (let i = 0; i < 10; i++) {
16
+ ts = CROCKFORD[t & 31] + ts;
17
+ t = Math.floor(t / 32);
18
+ }
19
+ const rand = crypto_1.default.randomBytes(10);
20
+ let r = "";
21
+ for (let i = 0; i < 10; i++)
22
+ r += CROCKFORD[rand[i] & 31];
23
+ while (r.length < 16)
24
+ r += CROCKFORD[0];
25
+ return ts + r;
26
+ }
27
+ /**
28
+ * Per-turn trace correlation, keyed by `session_id`.
29
+ *
30
+ * `UserPromptSubmit` starts a new ULID trace for its session; subsequent hooks
31
+ * in the same turn reuse it. Keying by session_id (not a single global file)
32
+ * is required because CLI and the VS Code extension can run concurrently and
33
+ * each emits its own session — verified that `session_id` is stable across a
34
+ * turn on both surfaces (H10). The store is a `{ [sessionId]: traceId }` map.
35
+ */
36
+ class TraceManager {
37
+ tracePath;
38
+ constructor(config) {
39
+ this.tracePath = config.tracePath;
40
+ }
41
+ readMap() {
42
+ try {
43
+ const data = JSON.parse(fs_1.default.readFileSync(this.tracePath, "utf-8"));
44
+ // Back-compat: a bare { traceId } file → treat as no sessions.
45
+ if (data && typeof data === "object" && !("traceId" in data)) {
46
+ return data;
47
+ }
48
+ }
49
+ catch {
50
+ // no/invalid file
51
+ }
52
+ return {};
53
+ }
54
+ writeMap(map) {
55
+ fs_1.default.mkdirSync(path_1.default.dirname(this.tracePath), { recursive: true });
56
+ // Cap stored sessions to avoid unbounded growth (keep most recent ~200).
57
+ const entries = Object.entries(map);
58
+ const capped = entries.length > 200 ? Object.fromEntries(entries.slice(-200)) : map;
59
+ fs_1.default.writeFileSync(this.tracePath, JSON.stringify(capped));
60
+ }
61
+ /** Start (and persist) a new trace for this session. */
62
+ newTrace(sessionId) {
63
+ const traceId = generateUlid();
64
+ const key = sessionId || "default";
65
+ const map = this.readMap();
66
+ map[key] = traceId;
67
+ this.writeMap(map);
68
+ return traceId;
69
+ }
70
+ /** Current trace for this session; create one if none exists yet. */
71
+ currentTrace(sessionId) {
72
+ const key = sessionId || "default";
73
+ const existing = this.readMap()[key];
74
+ if (existing)
75
+ return existing;
76
+ return this.newTrace(sessionId);
77
+ }
78
+ }
79
+ exports.TraceManager = TraceManager;
80
+ //# sourceMappingURL=trace.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trace.js","sourceRoot":"","sources":["../../src/core/trace.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ import type { OtlpPayload } from "./otlp.js";
2
+ import type { PintaConfig } from "./config.js";
3
+ export declare class Transport {
4
+ private queue;
5
+ constructor(config: PintaConfig);
6
+ /**
7
+ * POST a single payload. On any failure, enqueue it for the next hook to retry.
8
+ * Silent disable when no endpoint is configured.
9
+ */
10
+ send(payload: OtlpPayload): Promise<void>;
11
+ /**
12
+ * Best-effort drain. Acquires the lock, reads the queue, attempts a single
13
+ * batched POST. On failure, leaves the queue untouched.
14
+ * Silent disable when no endpoint is configured.
15
+ */
16
+ flush(): Promise<void>;
17
+ private post;
18
+ }
@@ -0,0 +1,127 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Transport = void 0;
4
+ const retry_queue_js_1 = require("./retry-queue.js");
5
+ const otlp_js_1 = require("./otlp.js");
6
+ const TIMEOUT_MS = 5000;
7
+ function parseHeadersEnv(raw) {
8
+ if (!raw)
9
+ return {};
10
+ const out = {};
11
+ for (const pair of raw.split(",")) {
12
+ const [k, ...rest] = pair.split("=");
13
+ if (k && rest.length > 0)
14
+ out[k.trim()] = rest.join("=").trim();
15
+ }
16
+ return out;
17
+ }
18
+ function getOptions() {
19
+ // Config is namespaced to COPILOT_PLUGIN_OPTION_* so it never collides with
20
+ // Copilot's own native OTel feature, which reads the standard
21
+ // OTEL_EXPORTER_OTLP_* vars. COPILOT_PLUGIN_OPTION_ENDPOINT is the full
22
+ // traces URL. OTEL_EXPORTER_OTLP_* are honored as a lower-priority fallback
23
+ // for OSS users who prefer the standard names. (ENDPOINT, without /v1/traces,
24
+ // is a base URL we append to.)
25
+ const fullEndpoint = process.env.COPILOT_PLUGIN_OPTION_ENDPOINT ||
26
+ process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT;
27
+ const baseEndpoint = process.env.OTEL_EXPORTER_OTLP_ENDPOINT;
28
+ let endpoint;
29
+ if (fullEndpoint) {
30
+ endpoint = fullEndpoint.replace(/\/+$/, "");
31
+ }
32
+ else if (baseEndpoint) {
33
+ endpoint = baseEndpoint.replace(/\/+$/, "") + "/v1/traces";
34
+ }
35
+ if (!endpoint)
36
+ return null;
37
+ return {
38
+ endpoint,
39
+ headers: parseHeadersEnv(process.env.COPILOT_PLUGIN_OPTION_HEADERS || process.env.OTEL_EXPORTER_OTLP_HEADERS),
40
+ };
41
+ }
42
+ class Transport {
43
+ queue;
44
+ constructor(config) {
45
+ this.queue = new retry_queue_js_1.RetryQueue(config.pluginData);
46
+ }
47
+ /**
48
+ * POST a single payload. On any failure, enqueue it for the next hook to retry.
49
+ * Silent disable when no endpoint is configured.
50
+ */
51
+ async send(payload) {
52
+ const opts = getOptions();
53
+ if (!opts)
54
+ return; // Silent disable when no endpoint configured
55
+ const ok = await this.post(payload, opts);
56
+ if (!ok)
57
+ this.queue.enqueue(payload);
58
+ }
59
+ /**
60
+ * Best-effort drain. Acquires the lock, reads the queue, attempts a single
61
+ * batched POST. On failure, leaves the queue untouched.
62
+ * Silent disable when no endpoint is configured.
63
+ */
64
+ async flush() {
65
+ const opts = getOptions();
66
+ if (!opts)
67
+ return;
68
+ if (!this.queue.tryAcquireLock())
69
+ return;
70
+ try {
71
+ const entries = this.queue.readAll();
72
+ if (entries.length === 0)
73
+ return;
74
+ const merged = (0, otlp_js_1.mergeBatch)(entries.map((e) => e.payload));
75
+ const ok = await this.post(merged, opts);
76
+ if (ok)
77
+ this.queue.rewrite([]);
78
+ }
79
+ finally {
80
+ this.queue.release();
81
+ }
82
+ }
83
+ async post(payload, opts) {
84
+ const ctrl = new AbortController();
85
+ const timer = setTimeout(() => ctrl.abort(), TIMEOUT_MS);
86
+ try {
87
+ // opts.endpoint is the full traces URL — no path append.
88
+ const res = await fetch(opts.endpoint, {
89
+ method: "POST",
90
+ headers: {
91
+ "Content-Type": "application/json",
92
+ ...opts.headers,
93
+ },
94
+ body: JSON.stringify(payload),
95
+ signal: ctrl.signal,
96
+ });
97
+ if (!res.ok) {
98
+ let body = "";
99
+ try {
100
+ body = (await res.text()).slice(0, 200);
101
+ }
102
+ catch {
103
+ /* ignore */
104
+ }
105
+ const hint = res.status === 401 || res.status === 403
106
+ ? " — check OTEL_EXPORTER_OTLP_HEADERS (relay token)"
107
+ : res.status === 404
108
+ ? " — check OTEL_EXPORTER_OTLP_TRACES_ENDPOINT path"
109
+ : res.status >= 500
110
+ ? " — collector may be down"
111
+ : "";
112
+ process.stderr.write(`[pinta-copilot] OTLP POST ${res.status} ${opts.endpoint}${hint}${body ? ` body=${body}` : ""}\n`);
113
+ return false;
114
+ }
115
+ return true;
116
+ }
117
+ catch (err) {
118
+ process.stderr.write(`[pinta-copilot] OTLP POST failed: ${err.message ?? String(err)}\n`);
119
+ return false;
120
+ }
121
+ finally {
122
+ clearTimeout(timer);
123
+ }
124
+ }
125
+ }
126
+ exports.Transport = Transport;
127
+ //# sourceMappingURL=transport.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transport.js","sourceRoot":"","sources":["../../src/core/transport.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,49 @@
1
+ export interface RawEvent {
2
+ [key: string]: unknown;
3
+ }
4
+ /**
5
+ * Resolve the hook event name. Copilot is inconsistent across surfaces/events:
6
+ * - snake `hook_event_name` (CLI/ext most events)
7
+ * - camel `hookEventName`
8
+ * - `hookName` (CLI permissionRequest)
9
+ * - NONE AT ALL — CLI `subagentStart` ships only camelCase agent fields with
10
+ * no event-name key (verified 2026-06-08, real adapter e2e).
11
+ *
12
+ * Final fallback: `PINTA_COPILOT_EVENT`, which `install-hooks` stamps into each
13
+ * hook entry's `env` block (Copilot passes hook `env` through to the process —
14
+ * H4). So we always know the event even when the payload omits it. The payload
15
+ * discriminator wins when present.
16
+ */
17
+ export declare function eventName(e: RawEvent): string | undefined;
18
+ /** session id — `session_id` (CLI/ext) or `sessionId` (permissionRequest camel). */
19
+ export declare function sessionId(e: RawEvent): string | undefined;
20
+ /** tool name — `tool_name` (snake) or `toolName` (camel/permissionRequest). */
21
+ export declare function toolName(e: RawEvent): string | undefined;
22
+ /** tool input — `tool_input` (snake) / `toolArgs` / `toolInput` (camel). */
23
+ export declare function toolInput(e: RawEvent): unknown;
24
+ export type EventKind = "SessionStart" | "SessionEnd" | "UserPromptSubmit" | "PreToolUse" | "PostToolUse" | "PostToolUseFailure" | "Stop" | "SubagentStart" | "SubagentStop" | "PreCompact" | "Notification" | "PermissionRequest" | "ErrorOccurred" | "Unknown";
25
+ export declare function classify(e: RawEvent): EventKind;
26
+ /** Guard runs on the two tool-gating events. PreToolUse fires on all surfaces;
27
+ * PermissionRequest is CLI-only (ext has no such event → entry is ignored). */
28
+ export declare function isGuardEvent(kind: EventKind): boolean;
29
+ export declare function isInternalTool(name: string | undefined): boolean;
30
+ /** preToolUse decision-control — honored by CLI, ext, and cloud. */
31
+ export interface PreToolUseDenyOutput {
32
+ hookSpecificOutput: {
33
+ hookEventName: "PreToolUse";
34
+ permissionDecision: "deny";
35
+ permissionDecisionReason: string;
36
+ };
37
+ }
38
+ /** permissionRequest decision-control — CLI permission service only. */
39
+ export interface PermissionRequestDenyOutput {
40
+ behavior: "deny";
41
+ message: string;
42
+ interrupt?: boolean;
43
+ }
44
+ /**
45
+ * Render the deny decision in the format the firing event expects, or null if
46
+ * the event isn't a gating event. preToolUse uses `permissionDecision`; the CLI
47
+ * permission service uses `behavior`/`message`.
48
+ */
49
+ export declare function formatDeny(kind: EventKind, reason: string): string | null;
@@ -0,0 +1,124 @@
1
+ "use strict";
2
+ // --- GitHub Copilot hook event types (CLI + VS Code extension + cloud) ---
3
+ //
4
+ // One stdin/stdout contract is shared by all three surfaces, but the payload
5
+ // SHAPE differs (verified 2026-06-08, BACKGROUND_RESEARCH §9.6/§9.7):
6
+ //
7
+ // • CLI : snake_case, discriminator `hook_event_name`; PostToolUse carries
8
+ // `tool_result` (structured); NO `tool_use_id`; `transcript_path`
9
+ // only on Stop; SessionStart has `initial_prompt`; Stop has
10
+ // `stop_reason`; subagent uses `agent_name`/`agent_display_name`.
11
+ // permissionRequest is a DIFFERENT schema: camelCase with
12
+ // discriminator `hookName` + `toolName`/`toolInput`/`permissionSuggestions`.
13
+ // • ext : snake_case, `hook_event_name`; Claude-Code-shaped — `tool_response`,
14
+ // `tool_use_id` present, `transcript_path` on every event, Stop has
15
+ // `stop_hook_active`, subagent uses `agent_id`/`agent_type`,
16
+ // SessionStart has `model`. Does NOT fire permissionRequest /
17
+ // SessionEnd / PostToolUseFailure / ErrorOccurred.
18
+ //
19
+ // Span building uses Bronze flattening (every top-level field → `copilot.<key>`),
20
+ // so we do NOT enumerate every field. We only normalize the handful used for
21
+ // routing / guard / trace, absorbing both casings + both discriminator keys.
22
+ Object.defineProperty(exports, "__esModule", { value: true });
23
+ exports.eventName = eventName;
24
+ exports.sessionId = sessionId;
25
+ exports.toolName = toolName;
26
+ exports.toolInput = toolInput;
27
+ exports.classify = classify;
28
+ exports.isGuardEvent = isGuardEvent;
29
+ exports.isInternalTool = isInternalTool;
30
+ exports.formatDeny = formatDeny;
31
+ function str(v) {
32
+ return typeof v === "string" ? v : undefined;
33
+ }
34
+ /**
35
+ * Resolve the hook event name. Copilot is inconsistent across surfaces/events:
36
+ * - snake `hook_event_name` (CLI/ext most events)
37
+ * - camel `hookEventName`
38
+ * - `hookName` (CLI permissionRequest)
39
+ * - NONE AT ALL — CLI `subagentStart` ships only camelCase agent fields with
40
+ * no event-name key (verified 2026-06-08, real adapter e2e).
41
+ *
42
+ * Final fallback: `PINTA_COPILOT_EVENT`, which `install-hooks` stamps into each
43
+ * hook entry's `env` block (Copilot passes hook `env` through to the process —
44
+ * H4). So we always know the event even when the payload omits it. The payload
45
+ * discriminator wins when present.
46
+ */
47
+ function eventName(e) {
48
+ return (str(e.hook_event_name) ??
49
+ str(e.hookEventName) ??
50
+ str(e.hookName) ??
51
+ (process.env.PINTA_COPILOT_EVENT || undefined));
52
+ }
53
+ /** session id — `session_id` (CLI/ext) or `sessionId` (permissionRequest camel). */
54
+ function sessionId(e) {
55
+ return str(e.session_id) ?? str(e.sessionId);
56
+ }
57
+ /** tool name — `tool_name` (snake) or `toolName` (camel/permissionRequest). */
58
+ function toolName(e) {
59
+ return str(e.tool_name) ?? str(e.toolName);
60
+ }
61
+ /** tool input — `tool_input` (snake) / `toolArgs` / `toolInput` (camel). */
62
+ function toolInput(e) {
63
+ return e.tool_input ?? e.toolArgs ?? e.toolInput;
64
+ }
65
+ const KIND_MAP = {
66
+ sessionstart: "SessionStart",
67
+ sessionend: "SessionEnd",
68
+ userpromptsubmit: "UserPromptSubmit",
69
+ userpromptsubmitted: "UserPromptSubmit",
70
+ pretooluse: "PreToolUse",
71
+ posttooluse: "PostToolUse",
72
+ posttoolusefailure: "PostToolUseFailure",
73
+ stop: "Stop",
74
+ agentstop: "Stop",
75
+ subagentstart: "SubagentStart",
76
+ subagentstop: "SubagentStop",
77
+ precompact: "PreCompact",
78
+ notification: "Notification",
79
+ permissionrequest: "PermissionRequest",
80
+ erroroccurred: "ErrorOccurred",
81
+ };
82
+ function classify(e) {
83
+ const n = eventName(e);
84
+ if (!n)
85
+ return "Unknown";
86
+ return KIND_MAP[n.toLowerCase()] ?? "Unknown";
87
+ }
88
+ /** Guard runs on the two tool-gating events. PreToolUse fires on all surfaces;
89
+ * PermissionRequest is CLI-only (ext has no such event → entry is ignored). */
90
+ function isGuardEvent(kind) {
91
+ return kind === "PreToolUse" || kind === "PermissionRequest";
92
+ }
93
+ /**
94
+ * Internal agent tools that are NOT subject to guard enforcement — they get
95
+ * telemetry only (decision: per user policy 2026-06-08). `report_intent` and
96
+ * `ask_user` are the agent's own control tools, not user-facing actions;
97
+ * denying them would brick the turn without security benefit (they also fire
98
+ * preToolUse, observed in §9.6). The span is still emitted; guard is skipped.
99
+ */
100
+ const INTERNAL_TOOLS = new Set(["report_intent", "ask_user"]);
101
+ function isInternalTool(name) {
102
+ return name !== undefined && INTERNAL_TOOLS.has(name);
103
+ }
104
+ /**
105
+ * Render the deny decision in the format the firing event expects, or null if
106
+ * the event isn't a gating event. preToolUse uses `permissionDecision`; the CLI
107
+ * permission service uses `behavior`/`message`.
108
+ */
109
+ function formatDeny(kind, reason) {
110
+ if (kind === "PreToolUse") {
111
+ return JSON.stringify({
112
+ hookSpecificOutput: {
113
+ hookEventName: "PreToolUse",
114
+ permissionDecision: "deny",
115
+ permissionDecisionReason: reason,
116
+ },
117
+ });
118
+ }
119
+ if (kind === "PermissionRequest") {
120
+ return JSON.stringify({ behavior: "deny", message: reason });
121
+ }
122
+ return null;
123
+ }
124
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export declare function envFilePath(): string;
2
+ export declare function parseEnvFile(content: string): Record<string, string>;
3
+ /** Load the env file (if present) and merge only-unset keys into process.env. */
4
+ export declare function loadEnvFile(filePath?: string): void;
@@ -0,0 +1,69 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.envFilePath = envFilePath;
7
+ exports.parseEnvFile = parseEnvFile;
8
+ exports.loadEnvFile = loadEnvFile;
9
+ /**
10
+ * Graceful env-file loader (D5).
11
+ *
12
+ * pinta-copilot reads its own config from `~/.copilot/pinta-copilot.env`
13
+ * (or `$COPILOT_HOME/pinta-copilot.env`) — a `KEY=VALUE` per line file written
14
+ * by `install-hooks`/`setup` (OSS) or Pinta Manager's sidecar enroll (managed).
15
+ *
16
+ * Resolution precedence (highest → lowest):
17
+ * 1. explicit process.env (incl. a hook `env` block, which Copilot passes
18
+ * through to the spawned hook — verified H4)
19
+ * 2. ~/.copilot/pinta-copilot.env ← this loader, unset keys only
20
+ * 3. legacy keys (handled elsewhere)
21
+ *
22
+ * Missing file is a silent no-op (config may come purely from process.env).
23
+ */
24
+ const node_fs_1 = __importDefault(require("node:fs"));
25
+ const node_os_1 = __importDefault(require("node:os"));
26
+ const node_path_1 = __importDefault(require("node:path"));
27
+ function copilotHome() {
28
+ return process.env.COPILOT_HOME || node_path_1.default.join(node_os_1.default.homedir(), ".copilot");
29
+ }
30
+ function envFilePath() {
31
+ return node_path_1.default.join(copilotHome(), "pinta-copilot.env");
32
+ }
33
+ function parseEnvFile(content) {
34
+ const out = {};
35
+ for (const raw of content.split("\n")) {
36
+ const line = raw.trim();
37
+ if (!line || line.startsWith("#"))
38
+ continue;
39
+ const idx = line.indexOf("=");
40
+ if (idx < 0)
41
+ continue;
42
+ const key = line.slice(0, idx).trim();
43
+ let value = line.slice(idx + 1).trim();
44
+ if ((value.startsWith('"') && value.endsWith('"')) ||
45
+ (value.startsWith("'") && value.endsWith("'"))) {
46
+ value = value.slice(1, -1);
47
+ }
48
+ if (key)
49
+ out[key] = value;
50
+ }
51
+ return out;
52
+ }
53
+ /** Load the env file (if present) and merge only-unset keys into process.env. */
54
+ function loadEnvFile(filePath = envFilePath()) {
55
+ let content;
56
+ try {
57
+ content = node_fs_1.default.readFileSync(filePath, "utf-8");
58
+ }
59
+ catch {
60
+ return; // missing/unreadable → no-op
61
+ }
62
+ const parsed = parseEnvFile(content);
63
+ for (const [key, value] of Object.entries(parsed)) {
64
+ if (process.env[key] === undefined) {
65
+ process.env[key] = value;
66
+ }
67
+ }
68
+ }
69
+ //# sourceMappingURL=env-file.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env-file.js","sourceRoot":"","sources":["../src/env-file.ts"],"names":[],"mappings":";;;;;AAuBA,kCAEC;AAED,oCAkBC;AAGD,kCAaC;AA7DD;;;;;;;;;;;;;;GAcG;AACH,sDAAyB;AACzB,sDAAyB;AACzB,0DAA6B;AAE7B,SAAS,WAAW;IAClB,OAAO,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,mBAAI,CAAC,IAAI,CAAC,iBAAE,CAAC,OAAO,EAAE,EAAE,UAAU,CAAC,CAAC;AACzE,CAAC;AAED,SAAgB,WAAW;IACzB,OAAO,mBAAI,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,mBAAmB,CAAC,CAAC;AACvD,CAAC;AAED,SAAgB,YAAY,CAAC,OAAe;IAC1C,MAAM,GAAG,GAA2B,EAAE,CAAC;IACvC,KAAK,MAAM,GAAG,IAAI,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACtC,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;QACxB,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,SAAS;QAC5C,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAC9B,IAAI,GAAG,GAAG,CAAC;YAAE,SAAS;QACtB,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;QACtC,IAAI,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QACvC,IACE,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;YAC9C,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,EAC9C,CAAC;YACD,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QAC7B,CAAC;QACD,IAAI,GAAG;YAAE,GAAG,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;IAC5B,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,iFAAiF;AACjF,SAAgB,WAAW,CAAC,WAAmB,WAAW,EAAE;IAC1D,IAAI,OAAe,CAAC;IACpB,IAAI,CAAC;QACH,OAAO,GAAG,iBAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAC/C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,CAAC,6BAA6B;IACvC,CAAC;IACD,MAAM,MAAM,GAAG,YAAY,CAAC,OAAO,CAAC,CAAC;IACrC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QAClD,IAAI,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,SAAS,EAAE,CAAC;YACnC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;QAC3B,CAAC;IACH,CAAC;AACH,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,71 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ // Load ~/.copilot/pinta-copilot.env BEFORE any other import that may read
4
+ // process.env. loadEnvFile only fills in unset keys, so explicit env / hook
5
+ // `env` blocks always win (D5).
6
+ const env_file_js_1 = require("./env-file.js");
7
+ (0, env_file_js_1.loadEnvFile)();
8
+ const config_js_1 = require("./core/config.js");
9
+ const surface_js_1 = require("./core/surface.js");
10
+ const types_js_1 = require("./core/types.js");
11
+ const transport_js_1 = require("./core/transport.js");
12
+ const trace_js_1 = require("./core/trace.js");
13
+ const otlp_js_1 = require("./core/otlp.js");
14
+ const guard_js_1 = require("./core/guard.js");
15
+ async function readStdin() {
16
+ const chunks = [];
17
+ for await (const chunk of process.stdin)
18
+ chunks.push(chunk);
19
+ return Buffer.concat(chunks).toString("utf-8");
20
+ }
21
+ async function main() {
22
+ // ⚠️ CLI preToolUse hooks are FAIL-CLOSED: a non-zero exit / crash / timeout
23
+ // DENIES the tool — and a crashing hook bricks the whole agent (report_intent
24
+ // / ask_user get blocked too, verified §9.6). Therefore this process MUST
25
+ // exit 0 on every path. All work is wrapped; the catch-all stays exit 0.
26
+ try {
27
+ const config = (0, config_js_1.loadConfig)();
28
+ const surface = (0, surface_js_1.detectSurface)();
29
+ const raw = await readStdin();
30
+ const event = JSON.parse(raw);
31
+ const kind = (0, types_js_1.classify)(event);
32
+ const sid = (0, types_js_1.sessionId)(event);
33
+ const transport = new transport_js_1.Transport(config);
34
+ await transport.flush(); // drain retry queue first
35
+ const trace = new trace_js_1.TraceManager(config);
36
+ // UserPromptSubmit starts a new per-turn trace; everything else reuses it.
37
+ const traceId = kind === "UserPromptSubmit" ? trace.newTrace(sid) : trace.currentTrace(sid);
38
+ // Guard runs on the two tool-gating events (PreToolUse: all surfaces;
39
+ // PermissionRequest: CLI-only). Internal agent tools (report_intent,
40
+ // ask_user) are telemetry-only — never guarded (would brick the turn).
41
+ const toolNm = (0, types_js_1.toolName)(event);
42
+ let guard = null;
43
+ if ((0, types_js_1.isGuardEvent)(kind) && !(0, types_js_1.isInternalTool)(toolNm)) {
44
+ const ti = (0, types_js_1.toolInput)(event);
45
+ guard = await (0, guard_js_1.evaluateGuard)({
46
+ spanId: sid ?? "unknown",
47
+ toolName: toolNm,
48
+ toolInput: ti,
49
+ rawTextFields: {
50
+ toolInput: typeof ti === "string" ? ti : JSON.stringify(ti ?? null),
51
+ },
52
+ }, process.env.PINTA_GUARD_ENDPOINT);
53
+ }
54
+ // Telemetry: one span per event (Bronze flattening, copilot.* prefix).
55
+ const payload = (0, otlp_js_1.buildOtlpPayload)({ event, traceId, surface, guard });
56
+ await transport.send(payload);
57
+ // Enforcement: emit a deny decision in the format the firing event expects.
58
+ if (guard?.decision === "DENY") {
59
+ const out = (0, types_js_1.formatDeny)(kind, guard.userMessage ?? guard.reason ?? "guard_deny");
60
+ if (out)
61
+ process.stdout.write(out + "\n");
62
+ }
63
+ }
64
+ catch (err) {
65
+ process.stderr.write(`[pinta-copilot] error: ${err}\n`);
66
+ // fail-open by design — never block a tool because the adapter crashed.
67
+ }
68
+ process.exit(0);
69
+ }
70
+ main();
71
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};