@eddacraft/kindling-adapter-opencode 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 (55) hide show
  1. package/LICENSE +190 -0
  2. package/README.md +192 -0
  3. package/dist/index.d.ts +7 -0
  4. package/dist/index.d.ts.map +1 -0
  5. package/dist/index.js +7 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/opencode/commands/export.d.ts +67 -0
  8. package/dist/opencode/commands/export.d.ts.map +1 -0
  9. package/dist/opencode/commands/export.js +76 -0
  10. package/dist/opencode/commands/export.js.map +1 -0
  11. package/dist/opencode/commands/forget.d.ts +50 -0
  12. package/dist/opencode/commands/forget.d.ts.map +1 -0
  13. package/dist/opencode/commands/forget.js +63 -0
  14. package/dist/opencode/commands/forget.js.map +1 -0
  15. package/dist/opencode/commands/index.d.ts +11 -0
  16. package/dist/opencode/commands/index.d.ts.map +1 -0
  17. package/dist/opencode/commands/index.js +11 -0
  18. package/dist/opencode/commands/index.js.map +1 -0
  19. package/dist/opencode/commands/pin.d.ts +64 -0
  20. package/dist/opencode/commands/pin.d.ts.map +1 -0
  21. package/dist/opencode/commands/pin.js +74 -0
  22. package/dist/opencode/commands/pin.js.map +1 -0
  23. package/dist/opencode/commands/search.d.ts +43 -0
  24. package/dist/opencode/commands/search.d.ts.map +1 -0
  25. package/dist/opencode/commands/search.js +90 -0
  26. package/dist/opencode/commands/search.js.map +1 -0
  27. package/dist/opencode/commands/status.d.ts +65 -0
  28. package/dist/opencode/commands/status.d.ts.map +1 -0
  29. package/dist/opencode/commands/status.js +64 -0
  30. package/dist/opencode/commands/status.js.map +1 -0
  31. package/dist/opencode/events.d.ts +95 -0
  32. package/dist/opencode/events.d.ts.map +1 -0
  33. package/dist/opencode/events.js +19 -0
  34. package/dist/opencode/events.js.map +1 -0
  35. package/dist/opencode/filter.d.ts +88 -0
  36. package/dist/opencode/filter.d.ts.map +1 -0
  37. package/dist/opencode/filter.js +150 -0
  38. package/dist/opencode/filter.js.map +1 -0
  39. package/dist/opencode/index.d.ts +10 -0
  40. package/dist/opencode/index.d.ts.map +1 -0
  41. package/dist/opencode/index.js +10 -0
  42. package/dist/opencode/index.js.map +1 -0
  43. package/dist/opencode/mapping.d.ts +37 -0
  44. package/dist/opencode/mapping.d.ts.map +1 -0
  45. package/dist/opencode/mapping.js +160 -0
  46. package/dist/opencode/mapping.js.map +1 -0
  47. package/dist/opencode/provenance.d.ts +27 -0
  48. package/dist/opencode/provenance.d.ts.map +1 -0
  49. package/dist/opencode/provenance.js +105 -0
  50. package/dist/opencode/provenance.js.map +1 -0
  51. package/dist/opencode/session.d.ts +108 -0
  52. package/dist/opencode/session.d.ts.map +1 -0
  53. package/dist/opencode/session.js +194 -0
  54. package/dist/opencode/session.js.map +1 -0
  55. package/package.json +57 -0
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Content filtering and safety utilities
3
+ *
4
+ * Provides filters to prevent accidental secret capture and reduce noise:
5
+ * - Content truncation for large outputs
6
+ * - Pattern-based secret detection
7
+ * - File path filtering
8
+ */
9
+ /**
10
+ * Maximum content length before truncation (characters)
11
+ */
12
+ export declare const MAX_CONTENT_LENGTH = 50000;
13
+ /**
14
+ * Options for content filtering
15
+ */
16
+ export interface FilterOptions {
17
+ /**
18
+ * Maximum content length before truncation
19
+ */
20
+ maxLength?: number;
21
+ /**
22
+ * Whether to detect and mask secrets
23
+ */
24
+ maskSecrets?: boolean;
25
+ /**
26
+ * Whether to append truncation notice
27
+ */
28
+ showTruncationNotice?: boolean;
29
+ }
30
+ /**
31
+ * Truncate content if it exceeds max length
32
+ *
33
+ * @param content - Content to truncate
34
+ * @param options - Filter options
35
+ * @returns Truncated content with optional notice
36
+ */
37
+ export declare function truncateContent(content: string, options?: FilterOptions): string;
38
+ /**
39
+ * Detect if content contains potential secrets
40
+ *
41
+ * @param content - Content to check
42
+ * @returns True if content likely contains secrets
43
+ */
44
+ export declare function containsSecrets(content: string): boolean;
45
+ /**
46
+ * Mask potential secrets in content
47
+ *
48
+ * Replaces patterns that look like secrets with [REDACTED]
49
+ *
50
+ * @param content - Content to mask
51
+ * @returns Content with secrets masked
52
+ */
53
+ export declare function maskSecrets(content: string): string;
54
+ /**
55
+ * Filter content with all safety rules
56
+ *
57
+ * Applies truncation and optional secret masking
58
+ *
59
+ * @param content - Content to filter
60
+ * @param options - Filter options
61
+ * @returns Filtered content
62
+ */
63
+ export declare function filterContent(content: string, options?: FilterOptions): string;
64
+ /**
65
+ * Check if a file path should be excluded from capture
66
+ *
67
+ * @param path - File path to check
68
+ * @returns True if path should be excluded
69
+ */
70
+ export declare function isExcludedPath(path: string): boolean;
71
+ /**
72
+ * Filter tool result fields
73
+ *
74
+ * For certain tools, only capture specific fields to reduce noise
75
+ *
76
+ * @param toolName - Name of the tool
77
+ * @param result - Tool result object
78
+ * @returns Filtered result
79
+ */
80
+ export declare function filterToolResult(_toolName: string, result: unknown): unknown;
81
+ /**
82
+ * Create a redaction reason string
83
+ *
84
+ * @param reason - Reason for redaction
85
+ * @returns Formatted redaction message
86
+ */
87
+ export declare function createRedactionReason(reason: string): string;
88
+ //# sourceMappingURL=filter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filter.d.ts","sourceRoot":"","sources":["../../src/opencode/filter.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH;;GAEG;AACH,eAAO,MAAM,kBAAkB,QAAQ,CAAC;AAgCxC;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;OAEG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IAEtB;;OAEG;IACH,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,aAAkB,GAC1B,MAAM,CAkBR;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAKxD;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAiBnD;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,aAAkB,GAC1B,MAAM,CAcR;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEpD;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAC9B,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,OAAO,GACd,OAAO,CAMT;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAE5D"}
@@ -0,0 +1,150 @@
1
+ /**
2
+ * Content filtering and safety utilities
3
+ *
4
+ * Provides filters to prevent accidental secret capture and reduce noise:
5
+ * - Content truncation for large outputs
6
+ * - Pattern-based secret detection
7
+ * - File path filtering
8
+ */
9
+ /**
10
+ * Maximum content length before truncation (characters)
11
+ */
12
+ export const MAX_CONTENT_LENGTH = 50000; // 50KB
13
+ /**
14
+ * Common secret patterns to detect in content
15
+ */
16
+ const SECRET_PATTERNS = [
17
+ // API keys and tokens
18
+ /['"]?(?:api[-_]?key|apikey|token|secret|password|passwd|pwd)['"]?\s*[:=]\s*['"]?([^\s'"]+)['"]?/gi,
19
+ // AWS keys
20
+ /(?:AWS|aws)[-_]?(?:SECRET|secret)[-_]?(?:ACCESS|access)[-_]?(?:KEY|key)\s*[:=]\s*['"]?([A-Za-z0-9/+=]{40})['"]?/g,
21
+ // Generic API tokens (long alphanumeric strings with mixed chars that look like tokens)
22
+ // Must have at least one digit AND one letter AND be 32+ chars
23
+ /\b(?=.*[0-9])(?=.*[A-Za-z])[A-Za-z0-9]{32,}\b/g,
24
+ // Bearer tokens
25
+ /Bearer\s+([A-Za-z0-9\-._~+/]+=*)/gi,
26
+ // Basic auth
27
+ /Basic\s+([A-Za-z0-9+/]+=*)/gi,
28
+ ];
29
+ /**
30
+ * File paths that should be excluded from capture
31
+ */
32
+ const EXCLUDED_PATHS = [
33
+ /node_modules/,
34
+ /\.git\//,
35
+ /\.env$/,
36
+ /\.pem$/,
37
+ /\.key$/,
38
+ /credentials/i,
39
+ /secrets/i,
40
+ ];
41
+ /**
42
+ * Truncate content if it exceeds max length
43
+ *
44
+ * @param content - Content to truncate
45
+ * @param options - Filter options
46
+ * @returns Truncated content with optional notice
47
+ */
48
+ export function truncateContent(content, options = {}) {
49
+ const { maxLength = MAX_CONTENT_LENGTH, showTruncationNotice = true, } = options;
50
+ if (content.length <= maxLength) {
51
+ return content;
52
+ }
53
+ const truncated = content.substring(0, maxLength);
54
+ if (showTruncationNotice) {
55
+ const remaining = content.length - maxLength;
56
+ return `${truncated}\n\n[Truncated ${remaining} characters]`;
57
+ }
58
+ return truncated;
59
+ }
60
+ /**
61
+ * Detect if content contains potential secrets
62
+ *
63
+ * @param content - Content to check
64
+ * @returns True if content likely contains secrets
65
+ */
66
+ export function containsSecrets(content) {
67
+ return SECRET_PATTERNS.some(pattern => {
68
+ pattern.lastIndex = 0; // Reset regex state
69
+ return pattern.test(content);
70
+ });
71
+ }
72
+ /**
73
+ * Mask potential secrets in content
74
+ *
75
+ * Replaces patterns that look like secrets with [REDACTED]
76
+ *
77
+ * @param content - Content to mask
78
+ * @returns Content with secrets masked
79
+ */
80
+ export function maskSecrets(content) {
81
+ let masked = content;
82
+ for (const pattern of SECRET_PATTERNS) {
83
+ pattern.lastIndex = 0; // Reset regex state
84
+ masked = masked.replace(pattern, (match) => {
85
+ // Keep the key name but mask the value
86
+ if (match.includes(':') || match.includes('=')) {
87
+ const parts = match.split(/[:=]/);
88
+ return `${parts[0]}=[REDACTED]`;
89
+ }
90
+ // For bearer/basic tokens and long strings, just redact
91
+ return '[REDACTED]';
92
+ });
93
+ }
94
+ return masked;
95
+ }
96
+ /**
97
+ * Filter content with all safety rules
98
+ *
99
+ * Applies truncation and optional secret masking
100
+ *
101
+ * @param content - Content to filter
102
+ * @param options - Filter options
103
+ * @returns Filtered content
104
+ */
105
+ export function filterContent(content, options = {}) {
106
+ const { maskSecrets: shouldMask = false } = options;
107
+ let filtered = content;
108
+ // Apply secret masking if enabled
109
+ if (shouldMask && containsSecrets(filtered)) {
110
+ filtered = maskSecrets(filtered);
111
+ }
112
+ // Apply truncation
113
+ filtered = truncateContent(filtered, options);
114
+ return filtered;
115
+ }
116
+ /**
117
+ * Check if a file path should be excluded from capture
118
+ *
119
+ * @param path - File path to check
120
+ * @returns True if path should be excluded
121
+ */
122
+ export function isExcludedPath(path) {
123
+ return EXCLUDED_PATHS.some(pattern => pattern.test(path));
124
+ }
125
+ /**
126
+ * Filter tool result fields
127
+ *
128
+ * For certain tools, only capture specific fields to reduce noise
129
+ *
130
+ * @param toolName - Name of the tool
131
+ * @param result - Tool result object
132
+ * @returns Filtered result
133
+ */
134
+ export function filterToolResult(_toolName, result) {
135
+ // For now, return as-is
136
+ // Future: implement allowlists for specific tools
137
+ // e.g., for 'read_file', only capture first N lines
138
+ // for 'list_dir', limit number of entries
139
+ return result;
140
+ }
141
+ /**
142
+ * Create a redaction reason string
143
+ *
144
+ * @param reason - Reason for redaction
145
+ * @returns Formatted redaction message
146
+ */
147
+ export function createRedactionReason(reason) {
148
+ return `[Content redacted: ${reason}]`;
149
+ }
150
+ //# sourceMappingURL=filter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filter.js","sourceRoot":"","sources":["../../src/opencode/filter.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH;;GAEG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,KAAK,CAAC,CAAC,OAAO;AAEhD;;GAEG;AACH,MAAM,eAAe,GAAG;IACtB,sBAAsB;IACtB,mGAAmG;IACnG,WAAW;IACX,kHAAkH;IAClH,wFAAwF;IACxF,+DAA+D;IAC/D,gDAAgD;IAChD,gBAAgB;IAChB,oCAAoC;IACpC,aAAa;IACb,8BAA8B;CAC/B,CAAC;AAEF;;GAEG;AACH,MAAM,cAAc,GAAG;IACrB,cAAc;IACd,SAAS;IACT,QAAQ;IACR,QAAQ;IACR,QAAQ;IACR,cAAc;IACd,UAAU;CACX,CAAC;AAsBF;;;;;;GAMG;AACH,MAAM,UAAU,eAAe,CAC7B,OAAe,EACf,UAAyB,EAAE;IAE3B,MAAM,EACJ,SAAS,GAAG,kBAAkB,EAC9B,oBAAoB,GAAG,IAAI,GAC5B,GAAG,OAAO,CAAC;IAEZ,IAAI,OAAO,CAAC,MAAM,IAAI,SAAS,EAAE,CAAC;QAChC,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;IAElD,IAAI,oBAAoB,EAAE,CAAC;QACzB,MAAM,SAAS,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;QAC7C,OAAO,GAAG,SAAS,kBAAkB,SAAS,cAAc,CAAC;IAC/D,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAAC,OAAe;IAC7C,OAAO,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE;QACpC,OAAO,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC,oBAAoB;QAC3C,OAAO,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC/B,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,WAAW,CAAC,OAAe;IACzC,IAAI,MAAM,GAAG,OAAO,CAAC;IAErB,KAAK,MAAM,OAAO,IAAI,eAAe,EAAE,CAAC;QACtC,OAAO,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC,oBAAoB;QAC3C,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;YACzC,uCAAuC;YACvC,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;gBAC/C,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;gBAClC,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,aAAa,CAAC;YAClC,CAAC;YACD,wDAAwD;YACxD,OAAO,YAAY,CAAC;QACtB,CAAC,CAAC,CAAC;IACL,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,aAAa,CAC3B,OAAe,EACf,UAAyB,EAAE;IAE3B,MAAM,EAAE,WAAW,EAAE,UAAU,GAAG,KAAK,EAAE,GAAG,OAAO,CAAC;IAEpD,IAAI,QAAQ,GAAG,OAAO,CAAC;IAEvB,kCAAkC;IAClC,IAAI,UAAU,IAAI,eAAe,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC5C,QAAQ,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC;IACnC,CAAC;IAED,mBAAmB;IACnB,QAAQ,GAAG,eAAe,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAE9C,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,OAAO,cAAc,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;AAC5D,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,gBAAgB,CAC9B,SAAiB,EACjB,MAAe;IAEf,wBAAwB;IACxB,kDAAkD;IAClD,oDAAoD;IACpD,0CAA0C;IAC1C,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,qBAAqB,CAAC,MAAc;IAClD,OAAO,sBAAsB,MAAM,GAAG,CAAC;AACzC,CAAC"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * OpenCode adapter - Event mapping and ingestion
3
+ */
4
+ export * from './events.js';
5
+ export * from './mapping.js';
6
+ export * from './provenance.js';
7
+ export * from './session.js';
8
+ export * from './filter.js';
9
+ export * from './commands/index.js';
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/opencode/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAC5B,cAAc,qBAAqB,CAAC"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * OpenCode adapter - Event mapping and ingestion
3
+ */
4
+ export * from './events.js';
5
+ export * from './mapping.js';
6
+ export * from './provenance.js';
7
+ export * from './session.js';
8
+ export * from './filter.js';
9
+ export * from './commands/index.js';
10
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/opencode/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAC5B,cAAc,qBAAqB,CAAC"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Event to observation mapping
3
+ *
4
+ * Maps OpenCode events to Kindling observations with provenance.
5
+ */
6
+ import type { ObservationKind, ObservationInput } from '@eddacraft/kindling-core';
7
+ import type { OpenCodeEvent } from './events.js';
8
+ /**
9
+ * Mapping from OpenCode event types to Kindling observation kinds
10
+ */
11
+ export declare const EVENT_TO_KIND_MAP: Record<string, ObservationKind>;
12
+ /**
13
+ * Result of event mapping
14
+ */
15
+ export interface MapEventResult {
16
+ /** Successfully mapped observation */
17
+ observation?: ObservationInput;
18
+ /** Error if mapping failed */
19
+ error?: string;
20
+ /** Whether this event should be skipped */
21
+ skip?: boolean;
22
+ }
23
+ /**
24
+ * Map an OpenCode event to a Kindling observation
25
+ *
26
+ * @param event - OpenCode event to map
27
+ * @returns Mapped observation or error
28
+ */
29
+ export declare function mapEvent(event: OpenCodeEvent): MapEventResult;
30
+ /**
31
+ * Batch map multiple events
32
+ *
33
+ * @param events - Events to map
34
+ * @returns Array of mapped observations (skipped events excluded)
35
+ */
36
+ export declare function mapEvents(events: OpenCodeEvent[]): ObservationInput[];
37
+ //# sourceMappingURL=mapping.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mapping.d.ts","sourceRoot":"","sources":["../../src/opencode/mapping.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAClF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AASjD;;GAEG;AACH,eAAO,MAAM,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAMpD,CAAC;AAEX;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,sCAAsC;IACtC,WAAW,CAAC,EAAE,gBAAgB,CAAC;IAC/B,8BAA8B;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2CAA2C;IAC3C,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,aAAa,GAAG,cAAc,CA2C7D;AA2GD;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,aAAa,EAAE,GAAG,gBAAgB,EAAE,CAerE"}
@@ -0,0 +1,160 @@
1
+ /**
2
+ * Event to observation mapping
3
+ *
4
+ * Maps OpenCode events to Kindling observations with provenance.
5
+ */
6
+ import { extractToolCallProvenance, extractCommandProvenance, extractFileDiffProvenance, extractErrorProvenance, extractMessageProvenance, } from './provenance.js';
7
+ /**
8
+ * Mapping from OpenCode event types to Kindling observation kinds
9
+ */
10
+ export const EVENT_TO_KIND_MAP = {
11
+ tool_call: 'tool_call',
12
+ command: 'command',
13
+ file_change: 'file_diff',
14
+ error: 'error',
15
+ message: 'message',
16
+ };
17
+ /**
18
+ * Map an OpenCode event to a Kindling observation
19
+ *
20
+ * @param event - OpenCode event to map
21
+ * @returns Mapped observation or error
22
+ */
23
+ export function mapEvent(event) {
24
+ // Skip session lifecycle events (handled separately)
25
+ if (event.type === 'session_start' || event.type === 'session_end') {
26
+ return { skip: true };
27
+ }
28
+ // Get observation kind
29
+ const kind = EVENT_TO_KIND_MAP[event.type];
30
+ if (!kind) {
31
+ return {
32
+ error: `Unknown event type: ${event.type}`,
33
+ };
34
+ }
35
+ // Extract content
36
+ const content = extractContent(event);
37
+ if (!content) {
38
+ return {
39
+ error: `Could not extract content from event type: ${event.type}`,
40
+ };
41
+ }
42
+ // Extract provenance
43
+ const provenance = extractProvenance(event);
44
+ // Build scope IDs
45
+ const scopeIds = {
46
+ sessionId: event.sessionId,
47
+ };
48
+ if (event.repoId) {
49
+ scopeIds.repoId = event.repoId;
50
+ }
51
+ // Return observation input
52
+ return {
53
+ observation: {
54
+ kind,
55
+ content,
56
+ provenance,
57
+ scopeIds,
58
+ },
59
+ };
60
+ }
61
+ /**
62
+ * Extract content string from event
63
+ */
64
+ function extractContent(event) {
65
+ switch (event.type) {
66
+ case 'tool_call':
67
+ return formatToolCallContent(event);
68
+ case 'command':
69
+ return formatCommandContent(event);
70
+ case 'file_change':
71
+ return formatFileChangeContent(event);
72
+ case 'error':
73
+ return event.message;
74
+ case 'message':
75
+ return event.content;
76
+ default:
77
+ return null;
78
+ }
79
+ }
80
+ /**
81
+ * Format tool call as human-readable content
82
+ */
83
+ function formatToolCallContent(event) {
84
+ const parts = [`Tool: ${event.toolName}`];
85
+ if (event.result) {
86
+ const resultStr = typeof event.result === 'string' ? event.result : JSON.stringify(event.result, null, 2);
87
+ parts.push(resultStr);
88
+ }
89
+ if (event.error) {
90
+ parts.push(`Error: ${event.error}`);
91
+ }
92
+ return parts.join('\n\n');
93
+ }
94
+ /**
95
+ * Format command as human-readable content
96
+ */
97
+ function formatCommandContent(event) {
98
+ const parts = [`$ ${event.command}`];
99
+ if (event.stdout) {
100
+ parts.push(event.stdout);
101
+ }
102
+ if (event.stderr) {
103
+ parts.push(`stderr: ${event.stderr}`);
104
+ }
105
+ parts.push(`Exit code: ${event.exitCode}`);
106
+ return parts.join('\n\n');
107
+ }
108
+ /**
109
+ * Format file change as human-readable content
110
+ */
111
+ function formatFileChangeContent(event) {
112
+ const parts = [`Modified files:\n${event.paths.map((p) => ` ${p}`).join('\n')}`];
113
+ if (event.additions !== undefined || event.deletions !== undefined) {
114
+ parts.push(`+${event.additions || 0} -${event.deletions || 0}`);
115
+ }
116
+ if (event.diff) {
117
+ parts.push(event.diff);
118
+ }
119
+ return parts.join('\n\n');
120
+ }
121
+ /**
122
+ * Extract provenance from event
123
+ */
124
+ function extractProvenance(event) {
125
+ switch (event.type) {
126
+ case 'tool_call':
127
+ return extractToolCallProvenance(event);
128
+ case 'command':
129
+ return extractCommandProvenance(event);
130
+ case 'file_change':
131
+ return extractFileDiffProvenance(event);
132
+ case 'error':
133
+ return extractErrorProvenance(event);
134
+ case 'message':
135
+ return extractMessageProvenance(event);
136
+ default:
137
+ return {};
138
+ }
139
+ }
140
+ /**
141
+ * Batch map multiple events
142
+ *
143
+ * @param events - Events to map
144
+ * @returns Array of mapped observations (skipped events excluded)
145
+ */
146
+ export function mapEvents(events) {
147
+ const observations = [];
148
+ for (const event of events) {
149
+ const result = mapEvent(event);
150
+ if (result.observation) {
151
+ observations.push(result.observation);
152
+ }
153
+ else if (result.error) {
154
+ console.warn(`Failed to map event: ${result.error}`, event);
155
+ }
156
+ // Skip events are silently ignored
157
+ }
158
+ return observations;
159
+ }
160
+ //# sourceMappingURL=mapping.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mapping.js","sourceRoot":"","sources":["../../src/opencode/mapping.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Provenance extraction for OpenCode events
3
+ *
4
+ * Extracts structured metadata from events for queryability and explainability.
5
+ */
6
+ import type { ToolCallEvent, CommandEvent, FileChangeEvent, ErrorEvent, MessageEvent } from './events.js';
7
+ /**
8
+ * Extract provenance from tool call event
9
+ */
10
+ export declare function extractToolCallProvenance(event: ToolCallEvent): Record<string, unknown>;
11
+ /**
12
+ * Extract provenance from command event
13
+ */
14
+ export declare function extractCommandProvenance(event: CommandEvent): Record<string, unknown>;
15
+ /**
16
+ * Extract provenance from file change event
17
+ */
18
+ export declare function extractFileDiffProvenance(event: FileChangeEvent): Record<string, unknown>;
19
+ /**
20
+ * Extract provenance from error event
21
+ */
22
+ export declare function extractErrorProvenance(event: ErrorEvent): Record<string, unknown>;
23
+ /**
24
+ * Extract provenance from message event
25
+ */
26
+ export declare function extractMessageProvenance(event: MessageEvent): Record<string, unknown>;
27
+ //# sourceMappingURL=provenance.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provenance.d.ts","sourceRoot":"","sources":["../../src/opencode/provenance.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EACV,aAAa,EACb,YAAY,EACZ,eAAe,EACf,UAAU,EACV,YAAY,EACb,MAAM,aAAa,CAAC;AAErB;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAQvF;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAQrF;AAED;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAOzF;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAMjF;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAMrF"}
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Provenance extraction for OpenCode events
3
+ *
4
+ * Extracts structured metadata from events for queryability and explainability.
5
+ */
6
+ /**
7
+ * Extract provenance from tool call event
8
+ */
9
+ export function extractToolCallProvenance(event) {
10
+ return {
11
+ toolName: event.toolName,
12
+ // Sanitize args to remove sensitive data
13
+ args: sanitizeArgs(event.args),
14
+ duration_ms: event.duration_ms,
15
+ hasError: !!event.error,
16
+ };
17
+ }
18
+ /**
19
+ * Extract provenance from command event
20
+ */
21
+ export function extractCommandProvenance(event) {
22
+ return {
23
+ // Only store the command name, not full args (may contain secrets)
24
+ cmd: extractCommandName(event.command),
25
+ exitCode: event.exitCode,
26
+ cwd: event.cwd,
27
+ hasStderr: !!event.stderr,
28
+ };
29
+ }
30
+ /**
31
+ * Extract provenance from file change event
32
+ */
33
+ export function extractFileDiffProvenance(event) {
34
+ return {
35
+ paths: event.paths,
36
+ additions: event.additions,
37
+ deletions: event.deletions,
38
+ fileCount: event.paths.length,
39
+ };
40
+ }
41
+ /**
42
+ * Extract provenance from error event
43
+ */
44
+ export function extractErrorProvenance(event) {
45
+ return {
46
+ source: event.source,
47
+ // Truncate stack trace to avoid excessive storage
48
+ stackPreview: event.stack?.substring(0, 200),
49
+ };
50
+ }
51
+ /**
52
+ * Extract provenance from message event
53
+ */
54
+ export function extractMessageProvenance(event) {
55
+ return {
56
+ role: event.role,
57
+ model: event.model,
58
+ length: event.content.length,
59
+ };
60
+ }
61
+ /**
62
+ * Sanitize tool arguments to remove sensitive data
63
+ */
64
+ function sanitizeArgs(args) {
65
+ const sanitized = {};
66
+ for (const [key, value] of Object.entries(args)) {
67
+ // Skip known sensitive fields
68
+ if (isSensitiveField(key)) {
69
+ sanitized[key] = '[REDACTED]';
70
+ continue;
71
+ }
72
+ // Truncate long string values
73
+ if (typeof value === 'string' && value.length > 100) {
74
+ sanitized[key] = value.substring(0, 100) + '...';
75
+ }
76
+ else {
77
+ sanitized[key] = value;
78
+ }
79
+ }
80
+ return sanitized;
81
+ }
82
+ /**
83
+ * Check if a field name suggests sensitive data
84
+ */
85
+ function isSensitiveField(fieldName) {
86
+ const lowerName = fieldName.toLowerCase();
87
+ const sensitivePatterns = [
88
+ 'password',
89
+ 'token',
90
+ 'secret',
91
+ 'key',
92
+ 'auth',
93
+ 'credential',
94
+ ];
95
+ return sensitivePatterns.some(pattern => lowerName.includes(pattern));
96
+ }
97
+ /**
98
+ * Extract just the command name from a full command string
99
+ */
100
+ function extractCommandName(command) {
101
+ // Get first word (command name) without args
102
+ const parts = command.trim().split(/\s+/);
103
+ return parts[0] || command;
104
+ }
105
+ //# sourceMappingURL=provenance.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provenance.js","sourceRoot":"","sources":["../../src/opencode/provenance.ts"],"names":[],"mappings":"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"}