aisnitch 0.2.19 → 0.2.21
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/dist/cli/index.cjs +1246 -100
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.js +1222 -76
- package/dist/cli/index.js.map +1 -1
- package/dist/index.cjs +1808 -133
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1491 -4
- package/dist/index.d.ts +1491 -4
- package/dist/index.js +1736 -122
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -34,10 +34,12 @@ __export(index_exports, {
|
|
|
34
34
|
AISNITCH_EVENT_TYPES: () => AISNITCH_EVENT_TYPES,
|
|
35
35
|
AISNITCH_PACKAGE_NAME: () => AISNITCH_PACKAGE_NAME,
|
|
36
36
|
AISNITCH_VERSION: () => AISNITCH_VERSION,
|
|
37
|
+
AISnitchError: () => AISnitchError,
|
|
37
38
|
AISnitchEventSchema: () => AISnitchEventSchema,
|
|
38
39
|
AISnitchEventTypeSchema: () => AISnitchEventTypeSchema,
|
|
39
40
|
AUTO_UPDATE_MANAGERS: () => AUTO_UPDATE_MANAGERS,
|
|
40
41
|
AdapterConfigSchema: () => AdapterConfigSchema,
|
|
42
|
+
AdapterError: () => AdapterError,
|
|
41
43
|
AdapterRegistry: () => AdapterRegistry,
|
|
42
44
|
AiderAdapter: () => AiderAdapter,
|
|
43
45
|
App: () => App,
|
|
@@ -46,6 +48,8 @@ __export(index_exports, {
|
|
|
46
48
|
CESPCategorySchema: () => CESPCategorySchema,
|
|
47
49
|
CESP_CATEGORIES: () => CESP_CATEGORIES,
|
|
48
50
|
CESP_MAP: () => CESP_MAP,
|
|
51
|
+
CircuitBreaker: () => CircuitBreaker,
|
|
52
|
+
CircuitOpenError: () => CircuitOpenError,
|
|
49
53
|
ClaudeCodeAdapter: () => ClaudeCodeAdapter,
|
|
50
54
|
CodexAdapter: () => CodexAdapter,
|
|
51
55
|
ConfigSchema: () => ConfigSchema,
|
|
@@ -53,8 +57,10 @@ __export(index_exports, {
|
|
|
53
57
|
CopilotCLIAdapter: () => CopilotCLIAdapter,
|
|
54
58
|
CursorAdapter: () => CursorAdapter,
|
|
55
59
|
DEFAULT_CONFIG: () => DEFAULT_CONFIG,
|
|
60
|
+
DEFAULT_TIMEOUTS: () => DEFAULT_TIMEOUTS,
|
|
56
61
|
DEFAULT_TUI_FILTERS: () => DEFAULT_TUI_FILTERS,
|
|
57
62
|
DEFAULT_VISIBLE_EVENT_COUNT: () => DEFAULT_VISIBLE_EVENT_COUNT,
|
|
63
|
+
DefaultRetryOptions: () => DefaultRetryOptions,
|
|
58
64
|
DevinAdapter: () => DevinAdapter,
|
|
59
65
|
ERROR_TYPES: () => ERROR_TYPES,
|
|
60
66
|
EVENT_COLORS: () => EVENT_COLORS,
|
|
@@ -66,33 +72,51 @@ __export(index_exports, {
|
|
|
66
72
|
EventLine: () => EventLine,
|
|
67
73
|
EventStream: () => EventStream,
|
|
68
74
|
FilterBar: () => FilterBar,
|
|
75
|
+
FinalMessageSchema: () => FinalMessageSchema,
|
|
69
76
|
GeminiCLIAdapter: () => GeminiCLIAdapter,
|
|
70
77
|
GenericPTYSession: () => GenericPTYSession,
|
|
71
78
|
GlobalBadge: () => GlobalBadge,
|
|
72
79
|
GooseAdapter: () => GooseAdapter,
|
|
80
|
+
GracefulShutdownManager: () => GracefulShutdownManager,
|
|
73
81
|
HTTPReceiver: () => HTTPReceiver,
|
|
74
82
|
Header: () => Header,
|
|
75
83
|
HelpOverlay: () => HelpOverlay,
|
|
76
84
|
KiloAdapter: () => KiloAdapter,
|
|
77
85
|
LOG_LEVELS: () => LOG_LEVELS,
|
|
86
|
+
MAX_GENERIC_STRING_LENGTH: () => MAX_GENERIC_STRING_LENGTH,
|
|
87
|
+
MAX_LABEL_LENGTH: () => MAX_LABEL_LENGTH,
|
|
88
|
+
MAX_PATH_LENGTH: () => MAX_PATH_LENGTH,
|
|
89
|
+
MAX_PORT: () => MAX_PORT,
|
|
90
|
+
MIN_PORT: () => MIN_PORT,
|
|
78
91
|
ManagedDaemonApp: () => ManagedDaemonApp,
|
|
92
|
+
MessageContentSchema: () => MessageContentSchema,
|
|
93
|
+
NetworkError: () => NetworkError,
|
|
79
94
|
OpenClawAdapter: () => OpenClawAdapter,
|
|
80
95
|
OpenCodeAdapter: () => OpenCodeAdapter,
|
|
81
96
|
Panel: () => Panel,
|
|
82
97
|
PanelStack: () => PanelStack,
|
|
98
|
+
PiAdapter: () => PiAdapter,
|
|
83
99
|
Pipeline: () => Pipeline,
|
|
100
|
+
PipelineError: () => PipelineError,
|
|
84
101
|
RingBuffer: () => RingBuffer,
|
|
85
102
|
SESSION_STALE_AFTER_MS: () => SESSION_STALE_AFTER_MS,
|
|
103
|
+
SHARED_BREAKERS: () => SHARED_BREAKERS,
|
|
86
104
|
SessionPanel: () => SessionPanel,
|
|
87
105
|
StatusBar: () => StatusBar,
|
|
88
106
|
TOOL_COLORS: () => TOOL_COLORS,
|
|
89
107
|
TOOL_NAMES: () => TOOL_NAMES,
|
|
90
108
|
TUI_THEME: () => TUI_THEME,
|
|
91
109
|
TUI_VIEW_MODES: () => TUI_VIEW_MODES,
|
|
110
|
+
ThinkingContentSchema: () => ThinkingContentSchema,
|
|
111
|
+
TimeoutError: () => TimeoutError,
|
|
112
|
+
ToolCallNameSchema: () => ToolCallNameSchema,
|
|
92
113
|
ToolInputSchema: () => ToolInputSchema,
|
|
93
114
|
ToolNameSchema: () => ToolNameSchema,
|
|
115
|
+
ToolResultSchema: () => ToolResultSchema,
|
|
94
116
|
UDSServer: () => UDSServer,
|
|
117
|
+
ValidationError: () => ValidationError,
|
|
95
118
|
WSServer: () => WSServer,
|
|
119
|
+
ZedAdapter: () => ZedAdapter,
|
|
96
120
|
analyzeTerminalOutputChunk: () => analyzeTerminalOutputChunk,
|
|
97
121
|
appendEventToStream: () => appendEventToStream,
|
|
98
122
|
applyEventFilters: () => applyEventFilters,
|
|
@@ -106,6 +130,9 @@ __export(index_exports, {
|
|
|
106
130
|
deriveGlobalActivityStatus: () => deriveGlobalActivityStatus,
|
|
107
131
|
deriveSessions: () => deriveSessions,
|
|
108
132
|
ensureConfigDir: () => ensureConfigDir,
|
|
133
|
+
err: () => err,
|
|
134
|
+
fireAndForgetRetry: () => fireAndForgetRetry,
|
|
135
|
+
flatMap: () => flatMap,
|
|
109
136
|
formatEventDetail: () => formatEventDetail,
|
|
110
137
|
formatEventLine: () => formatEventLine,
|
|
111
138
|
formatEventTime: () => formatEventTime,
|
|
@@ -113,16 +140,42 @@ __export(index_exports, {
|
|
|
113
140
|
formatSessionLabelFromEvent: () => formatSessionLabelFromEvent,
|
|
114
141
|
formatSessionShortId: () => formatSessionShortId,
|
|
115
142
|
formatWelcomeLine: () => formatWelcomeLine,
|
|
143
|
+
fromPromise: () => fromPromise,
|
|
144
|
+
fromSync: () => fromSync,
|
|
116
145
|
getAISnitchHomePath: () => getAISnitchHomePath,
|
|
146
|
+
getArray: () => getArray,
|
|
147
|
+
getBoolean: () => getBoolean2,
|
|
117
148
|
getCESPCategory: () => getCESPCategory,
|
|
118
149
|
getConfigPath: () => getConfigPath,
|
|
150
|
+
getNumber: () => getNumber9,
|
|
151
|
+
getObject: () => getObject,
|
|
119
152
|
getPackageScaffoldInfo: () => getPackageScaffoldInfo,
|
|
120
153
|
getPendingFrozenEventCount: () => getPendingFrozenEventCount,
|
|
154
|
+
getPort: () => getPort,
|
|
155
|
+
getPositiveNumber: () => getPositiveNumber,
|
|
156
|
+
getSafeInteger: () => getSafeInteger,
|
|
157
|
+
getSeqnum: () => getSeqnum,
|
|
121
158
|
getSocketPath: () => getSocketPath,
|
|
159
|
+
getString: () => getString11,
|
|
160
|
+
getStringWithMaxLength: () => getStringWithMaxLength,
|
|
161
|
+
getTimeout: () => getTimeout,
|
|
122
162
|
getVisibleEventWindow: () => getVisibleEventWindow,
|
|
163
|
+
isAISnitchError: () => isAISnitchError,
|
|
164
|
+
isErr: () => isErr,
|
|
123
165
|
isGenericSessionId: () => isGenericSessionId,
|
|
166
|
+
isNotNull: () => isNotNull,
|
|
167
|
+
isOk: () => isOk,
|
|
168
|
+
isRecord: () => isRecord11,
|
|
169
|
+
isRetryableError: () => isRetryableError,
|
|
170
|
+
isTimeoutError: () => isTimeoutError,
|
|
171
|
+
isValidPathLength: () => isValidPathLength,
|
|
172
|
+
isValidPort: () => isValidPort,
|
|
173
|
+
isValidStringLength: () => isValidStringLength,
|
|
124
174
|
loadConfig: () => loadConfig,
|
|
125
175
|
logger: () => logger,
|
|
176
|
+
mapErr: () => mapErr,
|
|
177
|
+
mapOk: () => mapOk,
|
|
178
|
+
ok: () => ok,
|
|
126
179
|
parseAiderHistoryMarkdown: () => parseAiderHistoryMarkdown,
|
|
127
180
|
renderAttachedTui: () => renderAttachedTui,
|
|
128
181
|
renderForegroundTui: () => renderForegroundTui,
|
|
@@ -131,9 +184,17 @@ __export(index_exports, {
|
|
|
131
184
|
resolveSessionId: () => resolveSessionId,
|
|
132
185
|
saveConfig: () => saveConfig,
|
|
133
186
|
setLoggerLevel: () => setLoggerLevel,
|
|
187
|
+
shutdownInOrder: () => shutdownInOrder,
|
|
188
|
+
sleep: () => sleep,
|
|
189
|
+
timeoutWarning: () => timeoutWarning,
|
|
134
190
|
useEventStream: () => useEventStream,
|
|
135
191
|
useKeyBinds: () => useKeyBinds,
|
|
136
|
-
useSessions: () => useSessions
|
|
192
|
+
useSessions: () => useSessions,
|
|
193
|
+
withOverallShutdownTimeout: () => withOverallShutdownTimeout,
|
|
194
|
+
withRetry: () => withRetry,
|
|
195
|
+
withRetryOn: () => withRetryOn,
|
|
196
|
+
withShutdownTimeout: () => withShutdownTimeout,
|
|
197
|
+
withTimeout: () => withTimeout
|
|
137
198
|
});
|
|
138
199
|
module.exports = __toCommonJS(index_exports);
|
|
139
200
|
|
|
@@ -258,6 +319,399 @@ function sanitizeToken(value) {
|
|
|
258
319
|
var import_node_os = require("os");
|
|
259
320
|
var import_zod2 = require("zod");
|
|
260
321
|
|
|
322
|
+
// src/core/errors.ts
|
|
323
|
+
var AISnitchError = class _AISnitchError extends Error {
|
|
324
|
+
/**
|
|
325
|
+
* Machine-readable error code for programmatic handling.
|
|
326
|
+
* Format: `SUBCATEGORY_SPECIFIC_DETAIL` (uppercase with underscores).
|
|
327
|
+
*/
|
|
328
|
+
code;
|
|
329
|
+
/**
|
|
330
|
+
* Arbitrary context bag forwarded to the logger for structured debugging.
|
|
331
|
+
*/
|
|
332
|
+
context;
|
|
333
|
+
constructor(message, code, context) {
|
|
334
|
+
super(message);
|
|
335
|
+
this.name = "AISnitchError";
|
|
336
|
+
this.code = code;
|
|
337
|
+
this.context = context;
|
|
338
|
+
if (Error.captureStackTrace) {
|
|
339
|
+
Error.captureStackTrace(this, _AISnitchError);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
/**
|
|
343
|
+
* Full error chain for logging: `[name] code — message`.
|
|
344
|
+
*/
|
|
345
|
+
toString() {
|
|
346
|
+
return `${this.name} [${this.code}] \u2014 ${this.message}`;
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* JSON serialization friendly to pino serializers.
|
|
350
|
+
*/
|
|
351
|
+
toJSON() {
|
|
352
|
+
return {
|
|
353
|
+
name: this.name,
|
|
354
|
+
code: this.code,
|
|
355
|
+
message: this.message,
|
|
356
|
+
context: this.context,
|
|
357
|
+
stack: this.stack
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
};
|
|
361
|
+
var AdapterError = class _AdapterError extends AISnitchError {
|
|
362
|
+
constructor(message, code, context) {
|
|
363
|
+
super(message, code, context);
|
|
364
|
+
this.name = "AdapterError";
|
|
365
|
+
if (Error.captureStackTrace) {
|
|
366
|
+
Error.captureStackTrace(this, _AdapterError);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* Factory for adapter-specific errors with auto-generated codes.
|
|
371
|
+
*/
|
|
372
|
+
static withAutoCode(message, tool, context) {
|
|
373
|
+
const sanitizedTool = tool.replace(/[^a-z0-9]/gi, "_").toUpperCase();
|
|
374
|
+
const code = `ADAPTER_${sanitizedTool}_ERROR`;
|
|
375
|
+
return new _AdapterError(message, code, { tool, ...context });
|
|
376
|
+
}
|
|
377
|
+
};
|
|
378
|
+
var PipelineError = class _PipelineError extends AISnitchError {
|
|
379
|
+
constructor(message, code, context) {
|
|
380
|
+
super(message, code, context);
|
|
381
|
+
this.name = "PipelineError";
|
|
382
|
+
if (Error.captureStackTrace) {
|
|
383
|
+
Error.captureStackTrace(this, _PipelineError);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
};
|
|
387
|
+
var ValidationError = class _ValidationError extends AISnitchError {
|
|
388
|
+
constructor(message, code, context) {
|
|
389
|
+
super(message, code, context);
|
|
390
|
+
this.name = "ValidationError";
|
|
391
|
+
if (Error.captureStackTrace) {
|
|
392
|
+
Error.captureStackTrace(this, _ValidationError);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
};
|
|
396
|
+
var NetworkError = class _NetworkError extends AISnitchError {
|
|
397
|
+
constructor(message, code, context) {
|
|
398
|
+
super(message, code, context);
|
|
399
|
+
this.name = "NetworkError";
|
|
400
|
+
if (Error.captureStackTrace) {
|
|
401
|
+
Error.captureStackTrace(this, _NetworkError);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
};
|
|
405
|
+
var TimeoutError = class _TimeoutError extends AISnitchError {
|
|
406
|
+
constructor(message, code, context) {
|
|
407
|
+
super(message, code, context);
|
|
408
|
+
this.name = "TimeoutError";
|
|
409
|
+
if (Error.captureStackTrace) {
|
|
410
|
+
Error.captureStackTrace(this, _TimeoutError);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
};
|
|
414
|
+
function isAISnitchError(error) {
|
|
415
|
+
return error instanceof AISnitchError;
|
|
416
|
+
}
|
|
417
|
+
function isRetryableError(error) {
|
|
418
|
+
if (!isAISnitchError(error)) {
|
|
419
|
+
if (error instanceof Error) {
|
|
420
|
+
const code = error.code;
|
|
421
|
+
const retryableCodes = /* @__PURE__ */ new Set([
|
|
422
|
+
"ECONNREFUSED",
|
|
423
|
+
"ECONNRESET",
|
|
424
|
+
"ETIMEDOUT",
|
|
425
|
+
"ENOTFOUND",
|
|
426
|
+
"EHOSTUNREACH",
|
|
427
|
+
"EPIPE",
|
|
428
|
+
"EPERM"
|
|
429
|
+
// sometimes transient on macOS file locks
|
|
430
|
+
]);
|
|
431
|
+
if (typeof code === "string" && retryableCodes.has(code)) {
|
|
432
|
+
return true;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
return false;
|
|
436
|
+
}
|
|
437
|
+
const retryableCategories = /* @__PURE__ */ new Set(["TIMEOUT", "NETWORK"]);
|
|
438
|
+
for (const category of retryableCategories) {
|
|
439
|
+
if (error.code.startsWith(category)) {
|
|
440
|
+
return true;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
const retryablePatterns = [
|
|
444
|
+
/^ADAPTER_.*_(FILE_IO|NETWORK|PROCESS_DETECT)_ERROR$/,
|
|
445
|
+
/^PIPELINE_.*_(RETRY|RECONNECT)_ERROR$/
|
|
446
|
+
];
|
|
447
|
+
for (const pattern of retryablePatterns) {
|
|
448
|
+
if (pattern.test(error.code)) {
|
|
449
|
+
return true;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
return false;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
// src/core/circuit-breaker.ts
|
|
456
|
+
var CircuitOpenError = class _CircuitOpenError extends AISnitchError {
|
|
457
|
+
constructor(circuitId, state) {
|
|
458
|
+
super(
|
|
459
|
+
`Circuit "${circuitId}" is OPEN \u2014 operation rejected`,
|
|
460
|
+
"CIRCUIT_OPEN",
|
|
461
|
+
{ circuitId, failures: state.failures, lastFailureAt: state.lastFailureAt }
|
|
462
|
+
);
|
|
463
|
+
this.circuitId = circuitId;
|
|
464
|
+
this.state = state;
|
|
465
|
+
this.name = "CircuitOpenError";
|
|
466
|
+
if (Error.captureStackTrace) {
|
|
467
|
+
Error.captureStackTrace(this, _CircuitOpenError);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
toString() {
|
|
471
|
+
return `${this.name} [${this.code}] "${this.circuitId}" \u2014 failures=${this.state.failures}`;
|
|
472
|
+
}
|
|
473
|
+
};
|
|
474
|
+
var DEFAULT_OPTIONS = {
|
|
475
|
+
failureThreshold: 5,
|
|
476
|
+
halfOpenAfterMs: 3e4,
|
|
477
|
+
id: "unnamed",
|
|
478
|
+
resetOnSuccess: true,
|
|
479
|
+
shouldCountAsFailure: isRetryableError,
|
|
480
|
+
windowMs: 6e4
|
|
481
|
+
};
|
|
482
|
+
var CircuitBreaker = class {
|
|
483
|
+
failures = 0;
|
|
484
|
+
lastFailureAt = null;
|
|
485
|
+
state = "closed";
|
|
486
|
+
halfOpenTestStartedAt = null;
|
|
487
|
+
options;
|
|
488
|
+
constructor(options = {}) {
|
|
489
|
+
this.options = {
|
|
490
|
+
...DEFAULT_OPTIONS,
|
|
491
|
+
...options,
|
|
492
|
+
// Re-spread to ensure all fields have defaults
|
|
493
|
+
failureThreshold: options.failureThreshold ?? DEFAULT_OPTIONS.failureThreshold,
|
|
494
|
+
halfOpenAfterMs: options.halfOpenAfterMs ?? DEFAULT_OPTIONS.halfOpenAfterMs,
|
|
495
|
+
id: options.id ?? DEFAULT_OPTIONS.id,
|
|
496
|
+
resetOnSuccess: options.resetOnSuccess ?? DEFAULT_OPTIONS.resetOnSuccess,
|
|
497
|
+
shouldCountAsFailure: options.shouldCountAsFailure ?? DEFAULT_OPTIONS.shouldCountAsFailure,
|
|
498
|
+
windowMs: options.windowMs ?? DEFAULT_OPTIONS.windowMs
|
|
499
|
+
};
|
|
500
|
+
}
|
|
501
|
+
/**
|
|
502
|
+
* Executes an async operation through the circuit breaker.
|
|
503
|
+
*
|
|
504
|
+
* - If the circuit is CLOSED → runs `fn` and updates state based on result
|
|
505
|
+
* - If the circuit is HALF-OPEN → runs `fn` once to test recovery
|
|
506
|
+
* - If the circuit is OPEN → throws `CircuitOpenError` immediately (no call)
|
|
507
|
+
*
|
|
508
|
+
* @param fn - The async operation to protect
|
|
509
|
+
* @returns The result of `fn` if successful
|
|
510
|
+
* @throws CircuitOpenError if the circuit is OPEN
|
|
511
|
+
* @throws The error from `fn` if it throws (and `shouldCountAsFailure` returns true)
|
|
512
|
+
*/
|
|
513
|
+
async execute(fn) {
|
|
514
|
+
switch (this.state) {
|
|
515
|
+
case "closed":
|
|
516
|
+
return this.executeClosed(fn);
|
|
517
|
+
case "half-open":
|
|
518
|
+
return this.executeHalfOpen(fn);
|
|
519
|
+
case "open":
|
|
520
|
+
if (this.shouldTransitionToHalfOpen()) {
|
|
521
|
+
this.transitionToHalfOpen();
|
|
522
|
+
return this.executeHalfOpen(fn);
|
|
523
|
+
}
|
|
524
|
+
throw new CircuitOpenError(this.options.id, this.getState());
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
/**
|
|
528
|
+
* Returns the current observable circuit state.
|
|
529
|
+
*/
|
|
530
|
+
getState() {
|
|
531
|
+
return {
|
|
532
|
+
failures: this.failures,
|
|
533
|
+
lastFailureAt: this.lastFailureAt,
|
|
534
|
+
state: this.state
|
|
535
|
+
};
|
|
536
|
+
}
|
|
537
|
+
/**
|
|
538
|
+
* Forces the circuit to CLOSED (resets failure count and state).
|
|
539
|
+
* Useful for manual recovery after a known-fix or after a maintenance window.
|
|
540
|
+
*/
|
|
541
|
+
reset() {
|
|
542
|
+
this.failures = 0;
|
|
543
|
+
this.lastFailureAt = null;
|
|
544
|
+
this.state = "closed";
|
|
545
|
+
this.halfOpenTestStartedAt = null;
|
|
546
|
+
logger.debug({ circuitId: this.options.id }, "Circuit breaker manually reset");
|
|
547
|
+
}
|
|
548
|
+
/**
|
|
549
|
+
* Pre-warms the circuit by performing one test call in HALF-OPEN state.
|
|
550
|
+
* If the circuit is already HALF-OPEN, this does nothing.
|
|
551
|
+
* If the circuit is CLOSED, this does nothing.
|
|
552
|
+
*/
|
|
553
|
+
async preWarm(fn) {
|
|
554
|
+
if (this.state !== "open") {
|
|
555
|
+
return;
|
|
556
|
+
}
|
|
557
|
+
this.transitionToHalfOpen();
|
|
558
|
+
try {
|
|
559
|
+
await fn();
|
|
560
|
+
this.transitionToClosed();
|
|
561
|
+
} catch {
|
|
562
|
+
this.transitionToOpen();
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
566
|
+
// Private methods
|
|
567
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
568
|
+
async executeClosed(fn) {
|
|
569
|
+
try {
|
|
570
|
+
const result = await fn();
|
|
571
|
+
this.onSuccess();
|
|
572
|
+
return result;
|
|
573
|
+
} catch (error) {
|
|
574
|
+
this.onFailure(error);
|
|
575
|
+
throw error;
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
async executeHalfOpen(fn) {
|
|
579
|
+
this.halfOpenTestStartedAt = Date.now();
|
|
580
|
+
try {
|
|
581
|
+
const result = await fn();
|
|
582
|
+
this.transitionToClosed();
|
|
583
|
+
return result;
|
|
584
|
+
} catch (error) {
|
|
585
|
+
this.transitionToOpen();
|
|
586
|
+
throw error;
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
onSuccess() {
|
|
590
|
+
if (this.options.resetOnSuccess) {
|
|
591
|
+
this.failures = 0;
|
|
592
|
+
this.lastFailureAt = null;
|
|
593
|
+
} else {
|
|
594
|
+
this.failures = Math.max(0, this.failures - 1);
|
|
595
|
+
if (this.failures === 0) {
|
|
596
|
+
this.lastFailureAt = null;
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
logger.debug(
|
|
600
|
+
{
|
|
601
|
+
circuitId: this.options.id,
|
|
602
|
+
failures: this.failures
|
|
603
|
+
},
|
|
604
|
+
"Circuit breaker operation succeeded"
|
|
605
|
+
);
|
|
606
|
+
}
|
|
607
|
+
onFailure(error) {
|
|
608
|
+
if (!this.options.shouldCountAsFailure(error)) {
|
|
609
|
+
logger.debug(
|
|
610
|
+
{ circuitId: this.options.id, error },
|
|
611
|
+
"Circuit breaker operation failed but error is not counted as failure"
|
|
612
|
+
);
|
|
613
|
+
return;
|
|
614
|
+
}
|
|
615
|
+
this.failures += 1;
|
|
616
|
+
this.lastFailureAt = Date.now();
|
|
617
|
+
if (this.failures >= this.options.failureThreshold) {
|
|
618
|
+
this.transitionToOpen();
|
|
619
|
+
} else {
|
|
620
|
+
logger.debug(
|
|
621
|
+
{
|
|
622
|
+
circuitId: this.options.id,
|
|
623
|
+
failures: this.failures,
|
|
624
|
+
threshold: this.options.failureThreshold
|
|
625
|
+
},
|
|
626
|
+
"Circuit breaker recorded failure"
|
|
627
|
+
);
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
transitionToOpen() {
|
|
631
|
+
if (this.state === "open") {
|
|
632
|
+
return;
|
|
633
|
+
}
|
|
634
|
+
this.state = "open";
|
|
635
|
+
this.halfOpenTestStartedAt = null;
|
|
636
|
+
logger.warn(
|
|
637
|
+
{
|
|
638
|
+
circuitId: this.options.id,
|
|
639
|
+
failures: this.failures,
|
|
640
|
+
windowMs: this.options.windowMs
|
|
641
|
+
},
|
|
642
|
+
"\u{1F534} Circuit breaker OPEN \u2014 blocking operations"
|
|
643
|
+
);
|
|
644
|
+
}
|
|
645
|
+
transitionToHalfOpen() {
|
|
646
|
+
this.state = "half-open";
|
|
647
|
+
this.halfOpenTestStartedAt = Date.now();
|
|
648
|
+
logger.info(
|
|
649
|
+
{ circuitId: this.options.id },
|
|
650
|
+
"\u{1F7E1} Circuit breaker HALF-OPEN \u2014 testing recovery"
|
|
651
|
+
);
|
|
652
|
+
}
|
|
653
|
+
transitionToClosed() {
|
|
654
|
+
this.state = "closed";
|
|
655
|
+
this.failures = 0;
|
|
656
|
+
this.lastFailureAt = null;
|
|
657
|
+
this.halfOpenTestStartedAt = null;
|
|
658
|
+
logger.info(
|
|
659
|
+
{ circuitId: this.options.id },
|
|
660
|
+
"\u{1F7E2} Circuit breaker CLOSED \u2014 recovery successful"
|
|
661
|
+
);
|
|
662
|
+
}
|
|
663
|
+
shouldTransitionToHalfOpen() {
|
|
664
|
+
if (this.lastFailureAt === null) {
|
|
665
|
+
return true;
|
|
666
|
+
}
|
|
667
|
+
const elapsed = Date.now() - this.lastFailureAt;
|
|
668
|
+
return elapsed >= this.options.halfOpenAfterMs;
|
|
669
|
+
}
|
|
670
|
+
};
|
|
671
|
+
var SHARED_BREAKERS = Object.freeze({
|
|
672
|
+
/**
|
|
673
|
+
* Breaker for adapter event emission.
|
|
674
|
+
* Threshold: 5 failures in 60s → open for 30s → half-open test.
|
|
675
|
+
*/
|
|
676
|
+
adapterEmit: new CircuitBreaker({
|
|
677
|
+
id: "adapter.emit",
|
|
678
|
+
failureThreshold: 5,
|
|
679
|
+
halfOpenAfterMs: 3e4,
|
|
680
|
+
shouldCountAsFailure: isRetryableError,
|
|
681
|
+
windowMs: 6e4
|
|
682
|
+
}),
|
|
683
|
+
/**
|
|
684
|
+
* Breaker for file system operations (transcript reading, config loading).
|
|
685
|
+
* More tolerant: 10 failures in 60s → open for 30s.
|
|
686
|
+
*/
|
|
687
|
+
fileSystem: new CircuitBreaker({
|
|
688
|
+
id: "filesystem",
|
|
689
|
+
failureThreshold: 10,
|
|
690
|
+
halfOpenAfterMs: 3e4,
|
|
691
|
+
windowMs: 6e4
|
|
692
|
+
}),
|
|
693
|
+
/**
|
|
694
|
+
* Breaker for HTTP/HTTPS requests.
|
|
695
|
+
* Stricter: 3 failures in 30s → open for 15s.
|
|
696
|
+
*/
|
|
697
|
+
httpRequest: new CircuitBreaker({
|
|
698
|
+
id: "http-request",
|
|
699
|
+
failureThreshold: 3,
|
|
700
|
+
halfOpenAfterMs: 15e3,
|
|
701
|
+
windowMs: 3e4
|
|
702
|
+
}),
|
|
703
|
+
/**
|
|
704
|
+
* Breaker for process detection operations.
|
|
705
|
+
* Most tolerant: 20 failures in 60s → open for 10s.
|
|
706
|
+
*/
|
|
707
|
+
processDetection: new CircuitBreaker({
|
|
708
|
+
id: "process-detection",
|
|
709
|
+
failureThreshold: 20,
|
|
710
|
+
halfOpenAfterMs: 1e4,
|
|
711
|
+
windowMs: 6e4
|
|
712
|
+
})
|
|
713
|
+
});
|
|
714
|
+
|
|
261
715
|
// src/core/events/schema.ts
|
|
262
716
|
var import_uuid = require("uuid");
|
|
263
717
|
var import_zod = require("zod");
|
|
@@ -296,6 +750,8 @@ var TOOL_NAMES = [
|
|
|
296
750
|
"kiro",
|
|
297
751
|
"augment-code",
|
|
298
752
|
"mistral",
|
|
753
|
+
"zed",
|
|
754
|
+
"pi",
|
|
299
755
|
"unknown"
|
|
300
756
|
];
|
|
301
757
|
var ERROR_TYPES = [
|
|
@@ -337,47 +793,58 @@ function createUuidV7() {
|
|
|
337
793
|
return (0, import_uuid.v7)();
|
|
338
794
|
}
|
|
339
795
|
var ToolInputSchema = import_zod.z.strictObject({
|
|
340
|
-
filePath: import_zod.z.string().min(1).optional(),
|
|
341
|
-
command: import_zod.z.string().min(1).optional()
|
|
796
|
+
filePath: import_zod.z.string().min(1).max(4096).optional(),
|
|
797
|
+
command: import_zod.z.string().min(1).max(1e4).optional()
|
|
342
798
|
}).refine(
|
|
343
799
|
(value) => value.filePath !== void 0 || value.command !== void 0,
|
|
344
800
|
"toolInput must include filePath or command"
|
|
345
801
|
);
|
|
802
|
+
var ThinkingContentSchema = import_zod.z.string().max(1e5).describe("Raw thinking/reasoning content from the AI model");
|
|
803
|
+
var ToolCallNameSchema = import_zod.z.string().min(1).max(100).describe("Name of the tool being invoked (e.g., Edit, Bash, Grep)");
|
|
804
|
+
var FinalMessageSchema = import_zod.z.string().max(5e4).describe("End-of-run summary or completion message");
|
|
805
|
+
var ToolResultSchema = import_zod.z.string().max(1e4).describe("Tool execution result or output");
|
|
806
|
+
var MessageContentSchema = import_zod.z.string().max(1e5).describe("Raw text content from AI messages");
|
|
346
807
|
var ToolNameSchema = import_zod.z.enum(TOOL_NAMES);
|
|
347
808
|
var AISnitchEventTypeSchema = import_zod.z.enum(AISNITCH_EVENT_TYPES);
|
|
348
809
|
var ErrorTypeSchema = import_zod.z.enum(ERROR_TYPES);
|
|
349
810
|
var CESPCategorySchema = import_zod.z.enum(CESP_CATEGORIES);
|
|
350
811
|
var EventDataSchema = import_zod.z.strictObject({
|
|
351
812
|
state: AISnitchEventTypeSchema,
|
|
352
|
-
project: import_zod.z.string().min(1).optional(),
|
|
353
|
-
projectPath: import_zod.z.string().min(1).optional(),
|
|
813
|
+
project: import_zod.z.string().min(1).max(255).optional(),
|
|
814
|
+
projectPath: import_zod.z.string().min(1).max(4096).optional(),
|
|
354
815
|
duration: import_zod.z.number().int().min(0).optional(),
|
|
355
|
-
toolName: import_zod.z.string().min(1).optional(),
|
|
816
|
+
toolName: import_zod.z.string().min(1).max(100).optional(),
|
|
356
817
|
toolInput: ToolInputSchema.optional(),
|
|
357
|
-
activeFile: import_zod.z.string().min(1).optional(),
|
|
358
|
-
model: import_zod.z.string().min(1).optional(),
|
|
818
|
+
activeFile: import_zod.z.string().min(1).max(4096).optional(),
|
|
819
|
+
model: import_zod.z.string().min(1).max(200).optional(),
|
|
359
820
|
tokensUsed: import_zod.z.number().int().min(0).optional(),
|
|
360
|
-
errorMessage: import_zod.z.string().min(1).optional(),
|
|
821
|
+
errorMessage: import_zod.z.string().min(1).max(1e4).optional(),
|
|
361
822
|
errorType: ErrorTypeSchema.optional(),
|
|
362
823
|
raw: import_zod.z.record(import_zod.z.string(), import_zod.z.unknown()).optional(),
|
|
363
|
-
terminal: import_zod.z.string().min(1).optional(),
|
|
364
|
-
cwd: import_zod.z.string().min(1).optional(),
|
|
824
|
+
terminal: import_zod.z.string().min(1).max(100).optional(),
|
|
825
|
+
cwd: import_zod.z.string().min(1).max(4096).optional(),
|
|
365
826
|
pid: import_zod.z.number().int().positive().optional(),
|
|
366
|
-
instanceId: import_zod.z.string().min(1).optional(),
|
|
827
|
+
instanceId: import_zod.z.string().min(1).max(255).optional(),
|
|
367
828
|
instanceIndex: import_zod.z.number().int().min(1).optional(),
|
|
368
|
-
instanceTotal: import_zod.z.number().int().min(1).optional()
|
|
829
|
+
instanceTotal: import_zod.z.number().int().min(1).optional(),
|
|
830
|
+
// New fields for enhanced content capture
|
|
831
|
+
thinkingContent: ThinkingContentSchema.optional(),
|
|
832
|
+
toolCallName: ToolCallNameSchema.optional(),
|
|
833
|
+
finalMessage: FinalMessageSchema.optional(),
|
|
834
|
+
toolResult: ToolResultSchema.optional(),
|
|
835
|
+
messageContent: MessageContentSchema.optional()
|
|
369
836
|
});
|
|
370
837
|
var AISnitchEventSchema = import_zod.z.strictObject({
|
|
371
838
|
specversion: import_zod.z.literal("1.0"),
|
|
372
839
|
id: import_zod.z.string().refine(isUuidV7, "id must be a valid UUIDv7 string"),
|
|
373
|
-
source: import_zod.z.string().refine(
|
|
840
|
+
source: import_zod.z.string().max(2e3).refine(
|
|
374
841
|
isValidUriReference,
|
|
375
842
|
"source must be a valid non-empty CloudEvents URI-reference"
|
|
376
843
|
),
|
|
377
844
|
type: AISnitchEventTypeSchema,
|
|
378
845
|
time: ISO_TIMESTAMP_SCHEMA,
|
|
379
846
|
"aisnitch.tool": ToolNameSchema,
|
|
380
|
-
"aisnitch.sessionid": import_zod.z.string().min(1),
|
|
847
|
+
"aisnitch.sessionid": import_zod.z.string().min(1).max(500),
|
|
381
848
|
"aisnitch.seqnum": import_zod.z.number().int().min(1),
|
|
382
849
|
data: EventDataSchema
|
|
383
850
|
});
|
|
@@ -491,20 +958,29 @@ var BaseAdapter = class {
|
|
|
491
958
|
});
|
|
492
959
|
let published;
|
|
493
960
|
try {
|
|
494
|
-
published = await
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
961
|
+
published = await SHARED_BREAKERS.adapterEmit.execute(async () => {
|
|
962
|
+
return await this.publishEventImplementation(event, {
|
|
963
|
+
cwd: context.cwd,
|
|
964
|
+
env: context.env,
|
|
965
|
+
hookPayload: context.hookPayload,
|
|
966
|
+
pid: context.pid,
|
|
967
|
+
sessionId,
|
|
968
|
+
source: context.source,
|
|
969
|
+
transcriptPath: context.transcriptPath
|
|
970
|
+
});
|
|
502
971
|
});
|
|
503
972
|
} catch (error) {
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
973
|
+
if (error instanceof Error && error.name === "CircuitOpenError") {
|
|
974
|
+
logger.warn(
|
|
975
|
+
{ error, eventType: type, adapter: this.name },
|
|
976
|
+
"\u{1F4D6} Adapter emit blocked by open circuit \u2014 event dropped"
|
|
977
|
+
);
|
|
978
|
+
} else {
|
|
979
|
+
logger.error(
|
|
980
|
+
{ error, eventType: type, adapter: this.name, sessionId },
|
|
981
|
+
"\u{1F4D6} Failed to publish event \u2014 swallowing to prevent daemon crash"
|
|
982
|
+
);
|
|
983
|
+
}
|
|
508
984
|
published = false;
|
|
509
985
|
}
|
|
510
986
|
if (published) {
|
|
@@ -1518,7 +1994,11 @@ var ClaudeCodeAdapter = class extends BaseAdapter {
|
|
|
1518
1994
|
return;
|
|
1519
1995
|
}
|
|
1520
1996
|
case "SessionEnd": {
|
|
1521
|
-
|
|
1997
|
+
const finalMessage = extractFinalMessageFromPayload(payload);
|
|
1998
|
+
await this.emitStateChange("session.end", {
|
|
1999
|
+
...sharedData,
|
|
2000
|
+
finalMessage
|
|
2001
|
+
}, context);
|
|
1522
2002
|
return;
|
|
1523
2003
|
}
|
|
1524
2004
|
case "UserPromptSubmit":
|
|
@@ -1535,12 +2015,22 @@ var ClaudeCodeAdapter = class extends BaseAdapter {
|
|
|
1535
2015
|
return;
|
|
1536
2016
|
}
|
|
1537
2017
|
case "PreToolUse": {
|
|
1538
|
-
|
|
2018
|
+
const toolCallName = extractToolNameFromPayload(payload);
|
|
2019
|
+
await this.emitStateChange("agent.tool_call", {
|
|
2020
|
+
...sharedData,
|
|
2021
|
+
toolCallName
|
|
2022
|
+
}, context);
|
|
1539
2023
|
return;
|
|
1540
2024
|
}
|
|
1541
2025
|
case "PostToolUse": {
|
|
2026
|
+
const toolCallName = extractToolNameFromPayload(payload);
|
|
2027
|
+
const toolResult = extractToolResultFromPayload(payload);
|
|
1542
2028
|
const emittedType = isClaudeCodingTool(sharedData.toolName) ? "agent.coding" : "agent.tool_call";
|
|
1543
|
-
await this.emitStateChange(emittedType,
|
|
2029
|
+
await this.emitStateChange(emittedType, {
|
|
2030
|
+
...sharedData,
|
|
2031
|
+
toolCallName,
|
|
2032
|
+
toolResult
|
|
2033
|
+
}, context);
|
|
1544
2034
|
return;
|
|
1545
2035
|
}
|
|
1546
2036
|
case "PostToolUseFailure":
|
|
@@ -1742,21 +2232,50 @@ function extractClaudeTranscriptObservations(payload, transcriptPath) {
|
|
|
1742
2232
|
};
|
|
1743
2233
|
const observations = [];
|
|
1744
2234
|
if (contentParts.some((part) => part.type === "thinking")) {
|
|
2235
|
+
const thinkingParts = contentParts.filter((part) => part.type === "thinking");
|
|
2236
|
+
const thinkingText = thinkingParts.map((part) => {
|
|
2237
|
+
const text = part.text;
|
|
2238
|
+
return typeof text === "string" ? text : void 0;
|
|
2239
|
+
}).filter((text) => text !== void 0).join("\n");
|
|
1745
2240
|
observations.push({
|
|
1746
2241
|
context: sharedContext,
|
|
1747
|
-
data:
|
|
2242
|
+
data: {
|
|
2243
|
+
...sharedData,
|
|
2244
|
+
thinkingContent: thinkingText.length > 0 ? thinkingText : void 0
|
|
2245
|
+
},
|
|
1748
2246
|
type: "agent.thinking"
|
|
1749
2247
|
});
|
|
1750
2248
|
}
|
|
1751
2249
|
if (contentParts.some(
|
|
1752
2250
|
(part) => part.type === "text" && typeof part.text === "string" && part.text.trim().length > 0
|
|
1753
2251
|
)) {
|
|
2252
|
+
const messageTexts = contentParts.filter((part) => part.type === "text").map((part) => part.text).filter((text) => text.trim().length > 0);
|
|
2253
|
+
const messageContent = messageTexts.join("\n");
|
|
1754
2254
|
observations.push({
|
|
1755
2255
|
context: sharedContext,
|
|
1756
|
-
data:
|
|
2256
|
+
data: {
|
|
2257
|
+
...sharedData,
|
|
2258
|
+
messageContent: messageContent.length > 0 ? messageContent : void 0
|
|
2259
|
+
},
|
|
1757
2260
|
type: "agent.streaming"
|
|
1758
2261
|
});
|
|
1759
2262
|
}
|
|
2263
|
+
const toolUseParts = contentParts.filter(
|
|
2264
|
+
(part) => part.type === "tool_use" || part.type === "toolUse"
|
|
2265
|
+
);
|
|
2266
|
+
if (toolUseParts.length > 0) {
|
|
2267
|
+
const toolName = getString(toolUseParts[0], "name") ?? getString(toolUseParts[0], "tool");
|
|
2268
|
+
if (toolName) {
|
|
2269
|
+
observations.push({
|
|
2270
|
+
context: sharedContext,
|
|
2271
|
+
data: {
|
|
2272
|
+
...sharedData,
|
|
2273
|
+
toolCallName: toolName
|
|
2274
|
+
},
|
|
2275
|
+
type: "agent.tool_call"
|
|
2276
|
+
});
|
|
2277
|
+
}
|
|
2278
|
+
}
|
|
1760
2279
|
return observations;
|
|
1761
2280
|
}
|
|
1762
2281
|
function extractClaudeContentParts(payload) {
|
|
@@ -1879,6 +2398,51 @@ function getString(payload, key) {
|
|
|
1879
2398
|
const value = payload[key];
|
|
1880
2399
|
return typeof value === "string" && value.trim().length > 0 ? value : void 0;
|
|
1881
2400
|
}
|
|
2401
|
+
function extractToolNameFromPayload(payload) {
|
|
2402
|
+
const directToolName = getString(payload, "tool_name") ?? getString(payload, "toolName");
|
|
2403
|
+
if (directToolName) {
|
|
2404
|
+
return directToolName;
|
|
2405
|
+
}
|
|
2406
|
+
const toolUse = getRecord(payload.tool_use) ?? getRecord(payload.toolUse);
|
|
2407
|
+
if (toolUse) {
|
|
2408
|
+
return getString(toolUse, "name") ?? getString(toolUse, "tool");
|
|
2409
|
+
}
|
|
2410
|
+
const toolInput = getRecord(payload.tool_input) ?? getRecord(payload.toolInput);
|
|
2411
|
+
if (toolInput) {
|
|
2412
|
+
return getString(toolInput, "tool_name") ?? getString(toolInput, "type");
|
|
2413
|
+
}
|
|
2414
|
+
return void 0;
|
|
2415
|
+
}
|
|
2416
|
+
function extractToolResultFromPayload(payload) {
|
|
2417
|
+
const directResult = getString(payload, "result") ?? getString(payload, "output");
|
|
2418
|
+
if (directResult) {
|
|
2419
|
+
return directResult;
|
|
2420
|
+
}
|
|
2421
|
+
const toolResult = getRecord(payload.tool_result) ?? getRecord(payload.toolResult);
|
|
2422
|
+
if (toolResult) {
|
|
2423
|
+
return getString(toolResult, "content") ?? getString(toolResult, "output");
|
|
2424
|
+
}
|
|
2425
|
+
const errorField = getString(payload, "error") ?? getString(payload, "error_message");
|
|
2426
|
+
if (errorField) {
|
|
2427
|
+
return errorField;
|
|
2428
|
+
}
|
|
2429
|
+
return void 0;
|
|
2430
|
+
}
|
|
2431
|
+
function extractFinalMessageFromPayload(payload) {
|
|
2432
|
+
const directMessage = getString(payload, "final_message") ?? getString(payload, "finalMessage") ?? getString(payload, "summary") ?? getString(payload, "completion_message");
|
|
2433
|
+
if (directMessage) {
|
|
2434
|
+
return directMessage;
|
|
2435
|
+
}
|
|
2436
|
+
const result = getString(payload, "result") ?? getString(payload, "output") ?? getString(payload, "message");
|
|
2437
|
+
if (result) {
|
|
2438
|
+
return result;
|
|
2439
|
+
}
|
|
2440
|
+
const stats = getRecord(payload.stats);
|
|
2441
|
+
if (stats) {
|
|
2442
|
+
return getString(stats, "summary") ?? getString(stats, "completion_summary");
|
|
2443
|
+
}
|
|
2444
|
+
return void 0;
|
|
2445
|
+
}
|
|
1882
2446
|
|
|
1883
2447
|
// src/adapters/copilot-cli.ts
|
|
1884
2448
|
var import_node_child_process3 = require("child_process");
|
|
@@ -6852,7 +7416,11 @@ var OpenCodeAdapter = class extends BaseAdapter {
|
|
|
6852
7416
|
return;
|
|
6853
7417
|
}
|
|
6854
7418
|
case "session.deleted": {
|
|
6855
|
-
|
|
7419
|
+
const finalMessage = extractOpenCodeFinalMessage(payload);
|
|
7420
|
+
await this.emitStateChange("session.end", {
|
|
7421
|
+
...sharedData,
|
|
7422
|
+
finalMessage
|
|
7423
|
+
}, context);
|
|
6856
7424
|
return;
|
|
6857
7425
|
}
|
|
6858
7426
|
case "session.error": {
|
|
@@ -6877,12 +7445,22 @@ var OpenCodeAdapter = class extends BaseAdapter {
|
|
|
6877
7445
|
return;
|
|
6878
7446
|
}
|
|
6879
7447
|
case "tool.execute.before": {
|
|
6880
|
-
|
|
7448
|
+
const toolCallName = extractOpenCodeToolName(payload);
|
|
7449
|
+
await this.emitStateChange("agent.tool_call", {
|
|
7450
|
+
...sharedData,
|
|
7451
|
+
toolCallName
|
|
7452
|
+
}, context);
|
|
6881
7453
|
return;
|
|
6882
7454
|
}
|
|
6883
7455
|
case "tool.execute.after": {
|
|
7456
|
+
const toolCallName = extractOpenCodeToolName(payload);
|
|
7457
|
+
const toolResult = extractOpenCodeToolResult(payload);
|
|
6884
7458
|
const emittedType = isOpenCodeCodingTool(sharedData.toolName) ? "agent.coding" : "agent.tool_call";
|
|
6885
|
-
await this.emitStateChange(emittedType,
|
|
7459
|
+
await this.emitStateChange(emittedType, {
|
|
7460
|
+
...sharedData,
|
|
7461
|
+
toolCallName,
|
|
7462
|
+
toolResult
|
|
7463
|
+
}, context);
|
|
6886
7464
|
return;
|
|
6887
7465
|
}
|
|
6888
7466
|
default: {
|
|
@@ -7049,68 +7627,597 @@ function getString10(payload, key) {
|
|
|
7049
7627
|
const value = payload[key];
|
|
7050
7628
|
return typeof value === "string" && value.trim().length > 0 ? value : void 0;
|
|
7051
7629
|
}
|
|
7052
|
-
|
|
7053
|
-
|
|
7054
|
-
|
|
7055
|
-
|
|
7056
|
-
/**
|
|
7057
|
-
* Registers one built-in or community adapter instance.
|
|
7058
|
-
*/
|
|
7059
|
-
register(adapter) {
|
|
7060
|
-
if (this.adapters.has(adapter.name)) {
|
|
7061
|
-
throw new Error(`Adapter "${adapter.name}" is already registered.`);
|
|
7062
|
-
}
|
|
7063
|
-
this.adapters.set(adapter.name, adapter);
|
|
7630
|
+
function extractOpenCodeFinalMessage(payload) {
|
|
7631
|
+
const directMessage = getString10(payload, "final_message") ?? getString10(payload, "finalMessage") ?? getString10(payload, "summary") ?? getString10(payload, "completion_message");
|
|
7632
|
+
if (directMessage) {
|
|
7633
|
+
return directMessage;
|
|
7064
7634
|
}
|
|
7065
|
-
|
|
7066
|
-
|
|
7067
|
-
|
|
7068
|
-
get(toolName) {
|
|
7069
|
-
return this.adapters.get(toolName);
|
|
7635
|
+
const result = getString10(payload, "result") ?? getString10(payload, "output") ?? getString10(getRecord9(payload.properties), "result");
|
|
7636
|
+
if (result) {
|
|
7637
|
+
return result;
|
|
7070
7638
|
}
|
|
7071
|
-
|
|
7072
|
-
|
|
7073
|
-
|
|
7074
|
-
|
|
7075
|
-
|
|
7639
|
+
return void 0;
|
|
7640
|
+
}
|
|
7641
|
+
function extractOpenCodeToolResult(payload) {
|
|
7642
|
+
const directResult = getString10(payload, "result") ?? getString10(payload, "output") ?? getString10(payload, "toolResult");
|
|
7643
|
+
if (directResult) {
|
|
7644
|
+
return directResult;
|
|
7076
7645
|
}
|
|
7077
|
-
|
|
7078
|
-
|
|
7079
|
-
|
|
7080
|
-
getStatus() {
|
|
7081
|
-
return this.list().map((adapter) => adapter.getStatus());
|
|
7646
|
+
const toolResult = getRecord9(payload.tool_result) ?? getRecord9(payload.toolResult);
|
|
7647
|
+
if (toolResult) {
|
|
7648
|
+
return getString10(toolResult, "content") ?? getString10(toolResult, "output");
|
|
7082
7649
|
}
|
|
7083
|
-
|
|
7084
|
-
|
|
7085
|
-
|
|
7086
|
-
|
|
7087
|
-
|
|
7088
|
-
async startAll(config) {
|
|
7089
|
-
for (const adapter of this.list()) {
|
|
7090
|
-
if (config.adapters[adapter.name]?.enabled !== true) {
|
|
7091
|
-
continue;
|
|
7092
|
-
}
|
|
7093
|
-
try {
|
|
7094
|
-
await adapter.start();
|
|
7095
|
-
} catch (error) {
|
|
7096
|
-
logger.error(
|
|
7097
|
-
{ error, adapter: adapter.name },
|
|
7098
|
-
`\u{1F4D6} Failed to start adapter "${adapter.name}" \u2014 skipping`
|
|
7099
|
-
);
|
|
7100
|
-
}
|
|
7650
|
+
const props = getRecord9(payload.properties);
|
|
7651
|
+
if (props) {
|
|
7652
|
+
const nestedResult = getRecord9(props.tool_result) ?? getRecord9(props.toolResult);
|
|
7653
|
+
if (nestedResult) {
|
|
7654
|
+
return getString10(nestedResult, "content") ?? getString10(nestedResult, "output");
|
|
7101
7655
|
}
|
|
7102
7656
|
}
|
|
7103
|
-
|
|
7104
|
-
|
|
7105
|
-
|
|
7106
|
-
|
|
7107
|
-
|
|
7108
|
-
|
|
7109
|
-
|
|
7110
|
-
|
|
7111
|
-
|
|
7112
|
-
|
|
7113
|
-
|
|
7657
|
+
return void 0;
|
|
7658
|
+
}
|
|
7659
|
+
|
|
7660
|
+
// src/adapters/pi.ts
|
|
7661
|
+
var import_node_child_process12 = require("child_process");
|
|
7662
|
+
var import_node_path12 = require("path");
|
|
7663
|
+
var import_node_util12 = require("util");
|
|
7664
|
+
var execFileAsync = (0, import_node_util12.promisify)(import_node_child_process12.execFile);
|
|
7665
|
+
var PiAdapter = class extends BaseAdapter {
|
|
7666
|
+
displayName = "Pi (MiniMax)";
|
|
7667
|
+
name = "pi";
|
|
7668
|
+
strategies = [
|
|
7669
|
+
"process-detect",
|
|
7670
|
+
"api-client",
|
|
7671
|
+
"log-watch"
|
|
7672
|
+
];
|
|
7673
|
+
apiPort = 7890;
|
|
7674
|
+
logPath;
|
|
7675
|
+
poller = null;
|
|
7676
|
+
activePiSessions = /* @__PURE__ */ new Map();
|
|
7677
|
+
lastCheckedTime = 0;
|
|
7678
|
+
constructor(options) {
|
|
7679
|
+
super(options);
|
|
7680
|
+
this.logPath = (0, import_node_path12.join)(
|
|
7681
|
+
options.homeDirectory ?? process.env.HOME ?? "",
|
|
7682
|
+
".pi",
|
|
7683
|
+
"agent.log"
|
|
7684
|
+
);
|
|
7685
|
+
}
|
|
7686
|
+
start() {
|
|
7687
|
+
if (this.getStatus().running) {
|
|
7688
|
+
return Promise.resolve();
|
|
7689
|
+
}
|
|
7690
|
+
this.setRunning(true);
|
|
7691
|
+
this.startPolling();
|
|
7692
|
+
logger.info({ adapter: this.name }, "Pi adapter started");
|
|
7693
|
+
return Promise.resolve();
|
|
7694
|
+
}
|
|
7695
|
+
stop() {
|
|
7696
|
+
if (this.poller !== null) {
|
|
7697
|
+
clearInterval(this.poller);
|
|
7698
|
+
this.poller = null;
|
|
7699
|
+
}
|
|
7700
|
+
this.setRunning(false);
|
|
7701
|
+
logger.info({ adapter: this.name }, "Pi adapter stopped");
|
|
7702
|
+
return Promise.resolve();
|
|
7703
|
+
}
|
|
7704
|
+
async handleHook(payload) {
|
|
7705
|
+
const normalized = this.parseNormalizedHookPayload(payload);
|
|
7706
|
+
if (normalized === null) {
|
|
7707
|
+
return;
|
|
7708
|
+
}
|
|
7709
|
+
const context = {
|
|
7710
|
+
cwd: normalized.cwd,
|
|
7711
|
+
pid: normalized.pid,
|
|
7712
|
+
sessionId: normalized.sessionId,
|
|
7713
|
+
source: "pi-hook"
|
|
7714
|
+
};
|
|
7715
|
+
const eventType = this.mapEventType(normalized.type ?? "");
|
|
7716
|
+
const eventData = this.buildEventData(eventType, normalized);
|
|
7717
|
+
await this.emit(eventType, eventData, context);
|
|
7718
|
+
}
|
|
7719
|
+
startPolling() {
|
|
7720
|
+
this.poller = setInterval(() => {
|
|
7721
|
+
void this.pollPiActivity();
|
|
7722
|
+
}, 2e3);
|
|
7723
|
+
}
|
|
7724
|
+
async pollPiActivity() {
|
|
7725
|
+
const running = await this.detectPiInstance();
|
|
7726
|
+
if (!running) {
|
|
7727
|
+
for (const [sessionId, activity] of this.activePiSessions) {
|
|
7728
|
+
if (activity.state !== "idle") {
|
|
7729
|
+
activity.state = "idle";
|
|
7730
|
+
await this.emitIdle(sessionId);
|
|
7731
|
+
}
|
|
7732
|
+
}
|
|
7733
|
+
return;
|
|
7734
|
+
}
|
|
7735
|
+
try {
|
|
7736
|
+
const response = await fetch(
|
|
7737
|
+
`http://127.0.0.1:${this.apiPort}/api/status`,
|
|
7738
|
+
{
|
|
7739
|
+
signal: AbortSignal.timeout(500)
|
|
7740
|
+
}
|
|
7741
|
+
);
|
|
7742
|
+
if (response.ok) {
|
|
7743
|
+
const data = await response.json();
|
|
7744
|
+
await this.processPiApiResponse(data);
|
|
7745
|
+
}
|
|
7746
|
+
} catch {
|
|
7747
|
+
await this.checkMiniMaxApi();
|
|
7748
|
+
}
|
|
7749
|
+
}
|
|
7750
|
+
async detectPiInstance() {
|
|
7751
|
+
try {
|
|
7752
|
+
const result = await execFileAsync("pgrep", ["-l", "pi|minimax"]);
|
|
7753
|
+
if (result.stdout.includes("pi") || result.stdout.includes("minimax")) {
|
|
7754
|
+
return true;
|
|
7755
|
+
}
|
|
7756
|
+
} catch {
|
|
7757
|
+
}
|
|
7758
|
+
try {
|
|
7759
|
+
const response = await fetch(
|
|
7760
|
+
`http://127.0.0.1:${this.apiPort}/health`,
|
|
7761
|
+
{
|
|
7762
|
+
signal: AbortSignal.timeout(200)
|
|
7763
|
+
}
|
|
7764
|
+
);
|
|
7765
|
+
if (response.ok) {
|
|
7766
|
+
return true;
|
|
7767
|
+
}
|
|
7768
|
+
} catch {
|
|
7769
|
+
}
|
|
7770
|
+
return false;
|
|
7771
|
+
}
|
|
7772
|
+
async checkMiniMaxApi() {
|
|
7773
|
+
try {
|
|
7774
|
+
const response = await fetch("http://127.0.0.1:3000/api/agent/status", {
|
|
7775
|
+
signal: AbortSignal.timeout(500)
|
|
7776
|
+
});
|
|
7777
|
+
if (response.ok) {
|
|
7778
|
+
const data = await response.json();
|
|
7779
|
+
await this.processPiApiResponse(data);
|
|
7780
|
+
}
|
|
7781
|
+
} catch {
|
|
7782
|
+
}
|
|
7783
|
+
}
|
|
7784
|
+
async processPiApiResponse(data) {
|
|
7785
|
+
const rawSession = data.sessionId ?? data.project ?? "default";
|
|
7786
|
+
const sessionId = `pi:${rawSession.replace(/[^a-zA-Z0-9-_]/g, "-")}`;
|
|
7787
|
+
let activity = this.activePiSessions.get(sessionId);
|
|
7788
|
+
if (!activity) {
|
|
7789
|
+
activity = { sessionId, state: "idle" };
|
|
7790
|
+
this.activePiSessions.set(sessionId, activity);
|
|
7791
|
+
await this.emitSessionStart(sessionId, data);
|
|
7792
|
+
}
|
|
7793
|
+
const rawState = data.state ?? "idle";
|
|
7794
|
+
const state = rawState;
|
|
7795
|
+
if (state !== activity.state) {
|
|
7796
|
+
switch (state) {
|
|
7797
|
+
case "thinking": {
|
|
7798
|
+
const rawThinking = data.thinking;
|
|
7799
|
+
if (rawThinking) {
|
|
7800
|
+
await this.emitThinking(sessionId, rawThinking);
|
|
7801
|
+
}
|
|
7802
|
+
break;
|
|
7803
|
+
}
|
|
7804
|
+
case "tool": {
|
|
7805
|
+
const rawFilePath = data.filePath;
|
|
7806
|
+
const rawCommand = data.command;
|
|
7807
|
+
const rawToolName = data.toolName ?? "unknown";
|
|
7808
|
+
await this.emitToolCall(
|
|
7809
|
+
sessionId,
|
|
7810
|
+
{
|
|
7811
|
+
filePath: rawFilePath ?? "",
|
|
7812
|
+
command: rawCommand ?? ""
|
|
7813
|
+
},
|
|
7814
|
+
rawToolName
|
|
7815
|
+
);
|
|
7816
|
+
break;
|
|
7817
|
+
}
|
|
7818
|
+
case "output": {
|
|
7819
|
+
const rawOutput = data.output;
|
|
7820
|
+
if (rawOutput) {
|
|
7821
|
+
await this.emitOutput(sessionId, rawOutput);
|
|
7822
|
+
}
|
|
7823
|
+
break;
|
|
7824
|
+
}
|
|
7825
|
+
case "error": {
|
|
7826
|
+
const rawError = data.error ?? "Unknown error";
|
|
7827
|
+
await this.emitError(sessionId, rawError);
|
|
7828
|
+
break;
|
|
7829
|
+
}
|
|
7830
|
+
case "idle":
|
|
7831
|
+
await this.emitIdle(sessionId);
|
|
7832
|
+
break;
|
|
7833
|
+
}
|
|
7834
|
+
activity.state = state;
|
|
7835
|
+
}
|
|
7836
|
+
}
|
|
7837
|
+
async emitSessionStart(sessionId, data) {
|
|
7838
|
+
const rawProject = data.project ?? "pi-project";
|
|
7839
|
+
const rawModel = data.model ?? "minimax/moonshot";
|
|
7840
|
+
const eventData = {
|
|
7841
|
+
state: "session.start",
|
|
7842
|
+
project: rawProject,
|
|
7843
|
+
model: rawModel,
|
|
7844
|
+
raw: data
|
|
7845
|
+
};
|
|
7846
|
+
await this.emit("session.start", eventData, { sessionId });
|
|
7847
|
+
}
|
|
7848
|
+
async emitThinking(sessionId, content) {
|
|
7849
|
+
if (!content) return;
|
|
7850
|
+
const eventData = {
|
|
7851
|
+
state: "agent.thinking",
|
|
7852
|
+
thinkingContent: content
|
|
7853
|
+
};
|
|
7854
|
+
await this.emit("agent.thinking", eventData, { sessionId });
|
|
7855
|
+
}
|
|
7856
|
+
async emitToolCall(sessionId, toolInput, toolName) {
|
|
7857
|
+
const eventData = {
|
|
7858
|
+
state: "agent.tool_call",
|
|
7859
|
+
toolCallName: toolName,
|
|
7860
|
+
toolInput,
|
|
7861
|
+
activeFile: toolInput.filePath
|
|
7862
|
+
};
|
|
7863
|
+
await this.emit("agent.tool_call", eventData, { sessionId });
|
|
7864
|
+
}
|
|
7865
|
+
async emitOutput(sessionId, content) {
|
|
7866
|
+
if (!content) return;
|
|
7867
|
+
const eventData = {
|
|
7868
|
+
state: "agent.streaming",
|
|
7869
|
+
messageContent: content
|
|
7870
|
+
};
|
|
7871
|
+
await this.emit("agent.streaming", eventData, { sessionId });
|
|
7872
|
+
}
|
|
7873
|
+
async emitError(sessionId, errorMessage) {
|
|
7874
|
+
const eventData = {
|
|
7875
|
+
state: "agent.error",
|
|
7876
|
+
errorMessage,
|
|
7877
|
+
errorType: "api_error"
|
|
7878
|
+
};
|
|
7879
|
+
await this.emit("agent.error", eventData, { sessionId });
|
|
7880
|
+
}
|
|
7881
|
+
async emitIdle(sessionId) {
|
|
7882
|
+
const eventData = {
|
|
7883
|
+
state: "agent.idle"
|
|
7884
|
+
};
|
|
7885
|
+
await this.emit("agent.idle", eventData, { sessionId });
|
|
7886
|
+
}
|
|
7887
|
+
mapEventType(type) {
|
|
7888
|
+
const mapping = {
|
|
7889
|
+
"session.start": "session.start",
|
|
7890
|
+
"session.end": "session.end",
|
|
7891
|
+
"task.start": "task.start",
|
|
7892
|
+
"task.complete": "task.complete",
|
|
7893
|
+
thinking: "agent.thinking",
|
|
7894
|
+
tool: "agent.tool_call",
|
|
7895
|
+
coding: "agent.coding",
|
|
7896
|
+
output: "agent.streaming",
|
|
7897
|
+
message: "agent.streaming",
|
|
7898
|
+
ask: "agent.asking_user",
|
|
7899
|
+
error: "agent.error",
|
|
7900
|
+
idle: "agent.idle",
|
|
7901
|
+
compact: "agent.compact"
|
|
7902
|
+
};
|
|
7903
|
+
return mapping[type] ?? "agent.streaming";
|
|
7904
|
+
}
|
|
7905
|
+
buildEventData(eventType, payload) {
|
|
7906
|
+
const data = payload.data ?? {};
|
|
7907
|
+
return {
|
|
7908
|
+
state: eventType,
|
|
7909
|
+
project: data.project,
|
|
7910
|
+
activeFile: data.activeFile,
|
|
7911
|
+
model: data.model,
|
|
7912
|
+
toolInput: data.toolInput,
|
|
7913
|
+
toolCallName: data.toolCallName,
|
|
7914
|
+
thinkingContent: data.thinkingContent,
|
|
7915
|
+
messageContent: data.messageContent,
|
|
7916
|
+
finalMessage: data.finalMessage,
|
|
7917
|
+
toolResult: data.toolResult,
|
|
7918
|
+
errorMessage: data.errorMessage,
|
|
7919
|
+
errorType: data.errorType,
|
|
7920
|
+
raw: data.raw
|
|
7921
|
+
};
|
|
7922
|
+
}
|
|
7923
|
+
};
|
|
7924
|
+
|
|
7925
|
+
// src/adapters/zed.ts
|
|
7926
|
+
var import_promises11 = require("fs/promises");
|
|
7927
|
+
var import_node_path13 = require("path");
|
|
7928
|
+
var ZedAdapter = class extends BaseAdapter {
|
|
7929
|
+
displayName = "Zed AI";
|
|
7930
|
+
name = "zed";
|
|
7931
|
+
strategies = [
|
|
7932
|
+
"process-detect",
|
|
7933
|
+
"api-client"
|
|
7934
|
+
];
|
|
7935
|
+
logPaths;
|
|
7936
|
+
apiPort = 9876;
|
|
7937
|
+
pollIntervalMs;
|
|
7938
|
+
poller = null;
|
|
7939
|
+
lastEventTime = 0;
|
|
7940
|
+
activeZedSessions = /* @__PURE__ */ new Map();
|
|
7941
|
+
constructor(options) {
|
|
7942
|
+
super(options);
|
|
7943
|
+
this.pollIntervalMs = 2e3;
|
|
7944
|
+
this.logPaths = [
|
|
7945
|
+
(0, import_node_path13.join)(options.homeDirectory ?? process.env.HOME ?? "", ".config", "zed", "logs", "agent.log"),
|
|
7946
|
+
"/tmp/zed-agent.log"
|
|
7947
|
+
];
|
|
7948
|
+
}
|
|
7949
|
+
start() {
|
|
7950
|
+
if (this.getStatus().running) {
|
|
7951
|
+
return Promise.resolve();
|
|
7952
|
+
}
|
|
7953
|
+
this.setRunning(true);
|
|
7954
|
+
this.startPolling();
|
|
7955
|
+
logger.info({ adapter: this.name }, "Zed adapter started");
|
|
7956
|
+
return Promise.resolve();
|
|
7957
|
+
}
|
|
7958
|
+
stop() {
|
|
7959
|
+
if (this.poller !== null) {
|
|
7960
|
+
clearInterval(this.poller);
|
|
7961
|
+
this.poller = null;
|
|
7962
|
+
}
|
|
7963
|
+
this.setRunning(false);
|
|
7964
|
+
logger.info({ adapter: this.name }, "Zed adapter stopped");
|
|
7965
|
+
return Promise.resolve();
|
|
7966
|
+
}
|
|
7967
|
+
async handleHook(payload) {
|
|
7968
|
+
const normalized = this.parseNormalizedHookPayload(payload);
|
|
7969
|
+
if (normalized === null) {
|
|
7970
|
+
return;
|
|
7971
|
+
}
|
|
7972
|
+
const context = {
|
|
7973
|
+
cwd: normalized.cwd,
|
|
7974
|
+
pid: normalized.pid,
|
|
7975
|
+
sessionId: normalized.sessionId,
|
|
7976
|
+
source: "zed-hook"
|
|
7977
|
+
};
|
|
7978
|
+
const eventType = this.mapEventType(normalized.type ?? "");
|
|
7979
|
+
const eventData = this.buildEventData(eventType, normalized);
|
|
7980
|
+
await this.emit(eventType, eventData, context);
|
|
7981
|
+
}
|
|
7982
|
+
startPolling() {
|
|
7983
|
+
this.poller = setInterval(() => {
|
|
7984
|
+
void this.pollZedStatus();
|
|
7985
|
+
}, this.pollIntervalMs);
|
|
7986
|
+
}
|
|
7987
|
+
async pollZedStatus() {
|
|
7988
|
+
try {
|
|
7989
|
+
const response = await fetch(`http://127.0.0.1:${this.apiPort}/api/agent/status`, {
|
|
7990
|
+
signal: AbortSignal.timeout(1e3)
|
|
7991
|
+
});
|
|
7992
|
+
if (response.ok) {
|
|
7993
|
+
const data = await response.json();
|
|
7994
|
+
if (data.sessionId && typeof data.sessionId === "string") {
|
|
7995
|
+
const sessionId = `zed:${data.sessionId}`;
|
|
7996
|
+
if (!this.activeZedSessions.has(sessionId)) {
|
|
7997
|
+
this.activeZedSessions.set(sessionId, sessionId);
|
|
7998
|
+
await this.emitSessionStart(sessionId, data);
|
|
7999
|
+
}
|
|
8000
|
+
if (data.state === "thinking" && this.lastEventTime < Date.now() - 5e3) {
|
|
8001
|
+
const rawThinking = data.thinking;
|
|
8002
|
+
if (rawThinking) {
|
|
8003
|
+
await this.emitThinking(sessionId, rawThinking);
|
|
8004
|
+
}
|
|
8005
|
+
} else if (data.state === "tool" && data.toolName) {
|
|
8006
|
+
const rawFilePath = data.filePath;
|
|
8007
|
+
const rawCommand = data.command;
|
|
8008
|
+
const rawToolName = data.toolName ?? "unknown";
|
|
8009
|
+
await this.emitToolCall(
|
|
8010
|
+
sessionId,
|
|
8011
|
+
{
|
|
8012
|
+
filePath: rawFilePath ?? "",
|
|
8013
|
+
command: rawCommand ?? ""
|
|
8014
|
+
},
|
|
8015
|
+
rawToolName
|
|
8016
|
+
);
|
|
8017
|
+
} else if (data.state === "idle") {
|
|
8018
|
+
await this.emitIdle(sessionId);
|
|
8019
|
+
}
|
|
8020
|
+
}
|
|
8021
|
+
}
|
|
8022
|
+
} catch {
|
|
8023
|
+
await this.checkLogFiles();
|
|
8024
|
+
}
|
|
8025
|
+
}
|
|
8026
|
+
async checkLogFiles() {
|
|
8027
|
+
for (const logPath of this.logPaths) {
|
|
8028
|
+
try {
|
|
8029
|
+
const content = await (0, import_promises11.readFile)(logPath, "utf8");
|
|
8030
|
+
await this.parseLogContent(content);
|
|
8031
|
+
} catch {
|
|
8032
|
+
}
|
|
8033
|
+
}
|
|
8034
|
+
}
|
|
8035
|
+
async parseLogContent(content) {
|
|
8036
|
+
const lines = content.split("\n").filter((line) => line.trim());
|
|
8037
|
+
for (const line of lines) {
|
|
8038
|
+
if (this.lastEventTime > 0 && this.lastEventTime >= Date.now() - 2e3) {
|
|
8039
|
+
continue;
|
|
8040
|
+
}
|
|
8041
|
+
const event = this.extractZedEventFromLog(line);
|
|
8042
|
+
if (event) {
|
|
8043
|
+
await this.handleHook(event);
|
|
8044
|
+
this.lastEventTime = Date.now();
|
|
8045
|
+
}
|
|
8046
|
+
}
|
|
8047
|
+
}
|
|
8048
|
+
extractZedEventFromLog(line) {
|
|
8049
|
+
try {
|
|
8050
|
+
const parsed = JSON.parse(line);
|
|
8051
|
+
if (!parsed.type || typeof parsed.type !== "string") {
|
|
8052
|
+
return null;
|
|
8053
|
+
}
|
|
8054
|
+
const rawSessionId = parsed.sessionId;
|
|
8055
|
+
const rawWorkspace = parsed.workspace;
|
|
8056
|
+
const sessionId = rawSessionId ? rawSessionId : rawWorkspace ? `zed:${(0, import_node_path13.basename)(rawWorkspace)}` : `zed:${Date.now()}`;
|
|
8057
|
+
const rawCwd = parsed.cwd ?? rawWorkspace;
|
|
8058
|
+
return {
|
|
8059
|
+
type: parsed.type,
|
|
8060
|
+
sessionId,
|
|
8061
|
+
cwd: rawCwd,
|
|
8062
|
+
data: {
|
|
8063
|
+
project: parsed.project ?? rawWorkspace ? (0, import_node_path13.basename)(String(rawWorkspace)) : void 0,
|
|
8064
|
+
model: parsed.model,
|
|
8065
|
+
state: parsed.type,
|
|
8066
|
+
thinkingContent: parsed.thinking,
|
|
8067
|
+
toolCallName: parsed.toolName,
|
|
8068
|
+
toolInput: parsed.toolInput,
|
|
8069
|
+
messageContent: parsed.message ?? parsed.output,
|
|
8070
|
+
errorMessage: parsed.error,
|
|
8071
|
+
raw: parsed
|
|
8072
|
+
}
|
|
8073
|
+
};
|
|
8074
|
+
} catch {
|
|
8075
|
+
if (line.includes("[zed:agent]")) {
|
|
8076
|
+
const cleaned = line.replace(/^\[.*?\] \[.*?\] /, "");
|
|
8077
|
+
if (cleaned.includes("Thinking:")) {
|
|
8078
|
+
return { type: "thinking", thinkingContent: cleaned.replace("Thinking:", "").trim() };
|
|
8079
|
+
}
|
|
8080
|
+
if (cleaned.includes("Executing tool:")) {
|
|
8081
|
+
return { type: "tool", toolCallName: cleaned.replace("Executing tool:", "").trim() };
|
|
8082
|
+
}
|
|
8083
|
+
if (cleaned.includes("Error:")) {
|
|
8084
|
+
return { type: "error", errorMessage: cleaned.replace("Error:", "").trim() };
|
|
8085
|
+
}
|
|
8086
|
+
}
|
|
8087
|
+
return null;
|
|
8088
|
+
}
|
|
8089
|
+
}
|
|
8090
|
+
async emitSessionStart(sessionId, _data) {
|
|
8091
|
+
const eventData = {
|
|
8092
|
+
state: "session.start",
|
|
8093
|
+
project: sessionId.split(":")[1] ?? "unknown"
|
|
8094
|
+
};
|
|
8095
|
+
await this.emit("session.start", eventData, { sessionId });
|
|
8096
|
+
}
|
|
8097
|
+
async emitThinking(sessionId, content) {
|
|
8098
|
+
if (!content) return;
|
|
8099
|
+
const eventData = {
|
|
8100
|
+
state: "agent.thinking",
|
|
8101
|
+
thinkingContent: content
|
|
8102
|
+
};
|
|
8103
|
+
await this.emit("agent.thinking", eventData, { sessionId });
|
|
8104
|
+
this.lastEventTime = Date.now();
|
|
8105
|
+
}
|
|
8106
|
+
async emitToolCall(sessionId, toolInput, toolName) {
|
|
8107
|
+
const eventData = {
|
|
8108
|
+
state: "agent.tool_call",
|
|
8109
|
+
toolCallName: toolName,
|
|
8110
|
+
toolInput,
|
|
8111
|
+
activeFile: toolInput.filePath
|
|
8112
|
+
};
|
|
8113
|
+
await this.emit("agent.tool_call", eventData, { sessionId });
|
|
8114
|
+
this.lastEventTime = Date.now();
|
|
8115
|
+
}
|
|
8116
|
+
async emitIdle(sessionId) {
|
|
8117
|
+
const eventData = {
|
|
8118
|
+
state: "agent.idle"
|
|
8119
|
+
};
|
|
8120
|
+
await this.emit("agent.idle", eventData, { sessionId });
|
|
8121
|
+
}
|
|
8122
|
+
mapEventType(type) {
|
|
8123
|
+
const mapping = {
|
|
8124
|
+
"session.start": "session.start",
|
|
8125
|
+
"session.end": "session.end",
|
|
8126
|
+
"task.start": "task.start",
|
|
8127
|
+
"task.complete": "task.complete",
|
|
8128
|
+
thinking: "agent.thinking",
|
|
8129
|
+
tool: "agent.tool_call",
|
|
8130
|
+
coding: "agent.coding",
|
|
8131
|
+
output: "agent.streaming",
|
|
8132
|
+
message: "agent.streaming",
|
|
8133
|
+
ask: "agent.asking_user",
|
|
8134
|
+
error: "agent.error",
|
|
8135
|
+
idle: "agent.idle",
|
|
8136
|
+
compact: "agent.compact"
|
|
8137
|
+
};
|
|
8138
|
+
return mapping[type] ?? "agent.streaming";
|
|
8139
|
+
}
|
|
8140
|
+
buildEventData(eventType, payload) {
|
|
8141
|
+
const data = payload.data ?? {};
|
|
8142
|
+
return {
|
|
8143
|
+
state: eventType,
|
|
8144
|
+
project: data.project,
|
|
8145
|
+
activeFile: data.activeFile,
|
|
8146
|
+
model: data.model,
|
|
8147
|
+
toolInput: data.toolInput,
|
|
8148
|
+
toolCallName: data.toolCallName,
|
|
8149
|
+
thinkingContent: data.thinkingContent,
|
|
8150
|
+
messageContent: data.messageContent,
|
|
8151
|
+
finalMessage: data.finalMessage,
|
|
8152
|
+
toolResult: data.toolResult,
|
|
8153
|
+
errorMessage: data.errorMessage,
|
|
8154
|
+
errorType: data.errorType,
|
|
8155
|
+
raw: data.raw
|
|
8156
|
+
};
|
|
8157
|
+
}
|
|
8158
|
+
};
|
|
8159
|
+
|
|
8160
|
+
// src/adapters/registry.ts
|
|
8161
|
+
var AdapterRegistry = class {
|
|
8162
|
+
adapters = /* @__PURE__ */ new Map();
|
|
8163
|
+
/**
|
|
8164
|
+
* Registers one built-in or community adapter instance.
|
|
8165
|
+
*/
|
|
8166
|
+
register(adapter) {
|
|
8167
|
+
if (this.adapters.has(adapter.name)) {
|
|
8168
|
+
throw new Error(`Adapter "${adapter.name}" is already registered.`);
|
|
8169
|
+
}
|
|
8170
|
+
this.adapters.set(adapter.name, adapter);
|
|
8171
|
+
}
|
|
8172
|
+
/**
|
|
8173
|
+
* Returns one adapter instance by its tool name.
|
|
8174
|
+
*/
|
|
8175
|
+
get(toolName) {
|
|
8176
|
+
return this.adapters.get(toolName);
|
|
8177
|
+
}
|
|
8178
|
+
/**
|
|
8179
|
+
* Lists every registered adapter.
|
|
8180
|
+
*/
|
|
8181
|
+
list() {
|
|
8182
|
+
return [...this.adapters.values()];
|
|
8183
|
+
}
|
|
8184
|
+
/**
|
|
8185
|
+
* Returns one status snapshot per registered adapter.
|
|
8186
|
+
*/
|
|
8187
|
+
getStatus() {
|
|
8188
|
+
return this.list().map((adapter) => adapter.getStatus());
|
|
8189
|
+
}
|
|
8190
|
+
/**
|
|
8191
|
+
* Starts every adapter enabled in the current AISnitch config.
|
|
8192
|
+
* 📖 Each adapter is started independently — one failure does not prevent
|
|
8193
|
+
* the others from starting.
|
|
8194
|
+
*/
|
|
8195
|
+
async startAll(config) {
|
|
8196
|
+
for (const adapter of this.list()) {
|
|
8197
|
+
if (config.adapters[adapter.name]?.enabled !== true) {
|
|
8198
|
+
continue;
|
|
8199
|
+
}
|
|
8200
|
+
try {
|
|
8201
|
+
await adapter.start();
|
|
8202
|
+
} catch (error) {
|
|
8203
|
+
logger.error(
|
|
8204
|
+
{ error, adapter: adapter.name },
|
|
8205
|
+
`\u{1F4D6} Failed to start adapter "${adapter.name}" \u2014 skipping`
|
|
8206
|
+
);
|
|
8207
|
+
}
|
|
8208
|
+
}
|
|
8209
|
+
}
|
|
8210
|
+
/**
|
|
8211
|
+
* Stops every adapter in reverse registration order.
|
|
8212
|
+
* 📖 Each adapter is stopped independently — one failure does not prevent
|
|
8213
|
+
* the others from being stopped.
|
|
8214
|
+
*/
|
|
8215
|
+
async stopAll() {
|
|
8216
|
+
const adapters = this.list().reverse();
|
|
8217
|
+
for (const adapter of adapters) {
|
|
8218
|
+
try {
|
|
8219
|
+
await adapter.stop();
|
|
8220
|
+
} catch (error) {
|
|
7114
8221
|
logger.warn(
|
|
7115
8222
|
{ error, adapter: adapter.name },
|
|
7116
8223
|
`\u{1F4D6} Error stopping adapter "${adapter.name}" \u2014 continuing`
|
|
@@ -7121,16 +8228,16 @@ var AdapterRegistry = class {
|
|
|
7121
8228
|
};
|
|
7122
8229
|
|
|
7123
8230
|
// src/adapters/generic-pty.ts
|
|
7124
|
-
var
|
|
8231
|
+
var import_node_path15 = require("path");
|
|
7125
8232
|
var import_node_pty = require("@lydell/node-pty");
|
|
7126
8233
|
var import_strip_ansi = __toESM(require("strip-ansi"), 1);
|
|
7127
8234
|
|
|
7128
8235
|
// src/core/engine/context-detector.ts
|
|
7129
|
-
var
|
|
7130
|
-
var
|
|
7131
|
-
var
|
|
8236
|
+
var import_node_child_process13 = require("child_process");
|
|
8237
|
+
var import_node_path14 = require("path");
|
|
8238
|
+
var import_node_util13 = require("util");
|
|
7132
8239
|
var import_pid_cwd3 = __toESM(require("pid-cwd"), 1);
|
|
7133
|
-
var
|
|
8240
|
+
var execFile13 = (0, import_node_util13.promisify)(import_node_child_process13.execFile);
|
|
7134
8241
|
var TERM_PROGRAM_MAP = {
|
|
7135
8242
|
Apple_Terminal: "Terminal.app",
|
|
7136
8243
|
Hyper: "Hyper",
|
|
@@ -7173,9 +8280,11 @@ var TOOL_BINARY_MAP = {
|
|
|
7173
8280
|
"openhands": "openhands",
|
|
7174
8281
|
"openclaw": "openclaw",
|
|
7175
8282
|
"opencode": "opencode",
|
|
8283
|
+
"pi": "pi",
|
|
7176
8284
|
"qwen-code": "qwen",
|
|
7177
8285
|
"unknown": "unknown",
|
|
7178
|
-
"windsurf": "windsurf"
|
|
8286
|
+
"windsurf": "windsurf",
|
|
8287
|
+
"zed": "zed"
|
|
7179
8288
|
};
|
|
7180
8289
|
var ContextDetector = class {
|
|
7181
8290
|
cache = /* @__PURE__ */ new Map();
|
|
@@ -7241,7 +8350,7 @@ var ContextDetector = class {
|
|
|
7241
8350
|
return "unknown";
|
|
7242
8351
|
}
|
|
7243
8352
|
const nextPid = Number.parseInt(parentPidToken, 10);
|
|
7244
|
-
const normalizedProcessName = (0,
|
|
8353
|
+
const normalizedProcessName = (0, import_node_path14.basename)(commandText).replace(/\.app$/u, "");
|
|
7245
8354
|
const mappedTerminal = PROCESS_NAME_MAP[normalizedProcessName] ?? PROCESS_NAME_MAP[commandText.trim()];
|
|
7246
8355
|
if (mappedTerminal) {
|
|
7247
8356
|
return mappedTerminal;
|
|
@@ -7425,7 +8534,7 @@ var ContextDetector = class {
|
|
|
7425
8534
|
return cwd ?? void 0;
|
|
7426
8535
|
}
|
|
7427
8536
|
async defaultExecCommand(command, args, options = {}) {
|
|
7428
|
-
const result = await
|
|
8537
|
+
const result = await execFile13(command, [...args], {
|
|
7429
8538
|
encoding: "utf8",
|
|
7430
8539
|
timeout: options.timeoutMs ?? this.commandTimeoutMs,
|
|
7431
8540
|
maxBuffer: 1024 * 1024
|
|
@@ -7505,7 +8614,7 @@ var GenericPTYSession = class {
|
|
|
7505
8614
|
{
|
|
7506
8615
|
cwd: this.cwd,
|
|
7507
8616
|
pid: pty.pid,
|
|
7508
|
-
project: (0,
|
|
8617
|
+
project: (0, import_node_path15.basename)(this.cwd) || this.cwd,
|
|
7509
8618
|
projectPath: this.cwd,
|
|
7510
8619
|
raw: {
|
|
7511
8620
|
args: this.args,
|
|
@@ -7516,7 +8625,7 @@ var GenericPTYSession = class {
|
|
|
7516
8625
|
toolInput: {
|
|
7517
8626
|
command: this.commandLine
|
|
7518
8627
|
},
|
|
7519
|
-
toolName: (0,
|
|
8628
|
+
toolName: (0, import_node_path15.basename)(this.command) || this.command
|
|
7520
8629
|
}
|
|
7521
8630
|
);
|
|
7522
8631
|
await this.emitEvent(
|
|
@@ -7526,7 +8635,7 @@ var GenericPTYSession = class {
|
|
|
7526
8635
|
{
|
|
7527
8636
|
cwd: this.cwd,
|
|
7528
8637
|
pid: pty.pid,
|
|
7529
|
-
project: (0,
|
|
8638
|
+
project: (0, import_node_path15.basename)(this.cwd) || this.cwd,
|
|
7530
8639
|
projectPath: this.cwd,
|
|
7531
8640
|
raw: {
|
|
7532
8641
|
args: this.args,
|
|
@@ -7676,7 +8785,7 @@ var GenericPTYSession = class {
|
|
|
7676
8785
|
await this.emitEvent(pty.pid, sessionId, observation.type, {
|
|
7677
8786
|
cwd: this.cwd,
|
|
7678
8787
|
pid: pty.pid,
|
|
7679
|
-
project: (0,
|
|
8788
|
+
project: (0, import_node_path15.basename)(this.cwd) || this.cwd,
|
|
7680
8789
|
projectPath: this.cwd,
|
|
7681
8790
|
terminal,
|
|
7682
8791
|
...observation.data
|
|
@@ -7786,7 +8895,7 @@ function analyzeTerminalOutputChunk(input) {
|
|
|
7786
8895
|
} : {
|
|
7787
8896
|
command: input.commandLine
|
|
7788
8897
|
},
|
|
7789
|
-
toolName: activeFile ? "file-edit" : (0,
|
|
8898
|
+
toolName: activeFile ? "file-edit" : (0, import_node_path15.basename)(input.commandLine) || "shell"
|
|
7790
8899
|
},
|
|
7791
8900
|
fingerprint: createPtyFingerprint("agent.coding", normalizedText, activeFile),
|
|
7792
8901
|
type: "agent.coding"
|
|
@@ -7816,7 +8925,7 @@ function normalizePtyEnvironment(env) {
|
|
|
7816
8925
|
);
|
|
7817
8926
|
}
|
|
7818
8927
|
function inferWrappedToolName(command, args) {
|
|
7819
|
-
const commandBaseName = (0,
|
|
8928
|
+
const commandBaseName = (0, import_node_path15.basename)(command).toLowerCase();
|
|
7820
8929
|
const fullCommandLine = [commandBaseName, ...args].join(" ").toLowerCase();
|
|
7821
8930
|
const toolMatchers = [
|
|
7822
8931
|
["aider", /\baider\b/u],
|
|
@@ -7902,7 +9011,9 @@ function createDefaultAdapters(options) {
|
|
|
7902
9011
|
new KiloAdapter(options),
|
|
7903
9012
|
new CodexAdapter(options),
|
|
7904
9013
|
new OpenClawAdapter(options),
|
|
7905
|
-
new OpenCodeAdapter(options)
|
|
9014
|
+
new OpenCodeAdapter(options),
|
|
9015
|
+
new PiAdapter(options),
|
|
9016
|
+
new ZedAdapter(options)
|
|
7906
9017
|
];
|
|
7907
9018
|
}
|
|
7908
9019
|
|
|
@@ -7950,35 +9061,35 @@ var DEFAULT_CONFIG = {
|
|
|
7950
9061
|
};
|
|
7951
9062
|
|
|
7952
9063
|
// src/core/config/loader.ts
|
|
7953
|
-
var
|
|
9064
|
+
var import_promises12 = require("fs/promises");
|
|
7954
9065
|
var import_node_net = require("net");
|
|
7955
9066
|
var import_node_os2 = require("os");
|
|
7956
|
-
var
|
|
9067
|
+
var import_node_path16 = require("path");
|
|
7957
9068
|
function getAISnitchHomePath(options = {}) {
|
|
7958
9069
|
if (options.configPath && options.configPath.trim().length > 0) {
|
|
7959
|
-
return (0,
|
|
9070
|
+
return (0, import_node_path16.dirname)((0, import_node_path16.resolve)(options.configPath));
|
|
7960
9071
|
}
|
|
7961
9072
|
const configuredHome = options.env?.AISNITCH_HOME;
|
|
7962
9073
|
if (configuredHome && configuredHome.trim().length > 0) {
|
|
7963
|
-
return (0,
|
|
9074
|
+
return (0, import_node_path16.resolve)(configuredHome);
|
|
7964
9075
|
}
|
|
7965
|
-
return (0,
|
|
9076
|
+
return (0, import_node_path16.join)(options.homeDirectory ?? (0, import_node_os2.homedir)(), ".aisnitch");
|
|
7966
9077
|
}
|
|
7967
9078
|
function getConfigPath(options = {}) {
|
|
7968
9079
|
if (options.configPath && options.configPath.trim().length > 0) {
|
|
7969
|
-
return (0,
|
|
9080
|
+
return (0, import_node_path16.resolve)(options.configPath);
|
|
7970
9081
|
}
|
|
7971
|
-
return (0,
|
|
9082
|
+
return (0, import_node_path16.join)(getAISnitchHomePath(options), "config.json");
|
|
7972
9083
|
}
|
|
7973
9084
|
async function ensureConfigDir(options = {}) {
|
|
7974
9085
|
const directoryPath = getAISnitchHomePath(options);
|
|
7975
|
-
await (0,
|
|
9086
|
+
await (0, import_promises12.mkdir)(directoryPath, { recursive: true });
|
|
7976
9087
|
return directoryPath;
|
|
7977
9088
|
}
|
|
7978
9089
|
async function loadConfig(options = {}) {
|
|
7979
9090
|
const configPath = getConfigPath(options);
|
|
7980
9091
|
try {
|
|
7981
|
-
const rawConfig = await (0,
|
|
9092
|
+
const rawConfig = await (0, import_promises12.readFile)(configPath, "utf8");
|
|
7982
9093
|
const parsedJson = JSON.parse(rawConfig);
|
|
7983
9094
|
return ConfigSchema.parse(parsedJson);
|
|
7984
9095
|
} catch (error) {
|
|
@@ -7997,7 +9108,7 @@ async function saveConfig(config, options = {}) {
|
|
|
7997
9108
|
const validatedConfig = ConfigSchema.parse(config);
|
|
7998
9109
|
const configPath = getConfigPath(options);
|
|
7999
9110
|
await ensureConfigDir(options);
|
|
8000
|
-
await (0,
|
|
9111
|
+
await (0, import_promises12.writeFile)(
|
|
8001
9112
|
configPath,
|
|
8002
9113
|
`${JSON.stringify(validatedConfig, null, 2)}
|
|
8003
9114
|
`,
|
|
@@ -8241,7 +9352,7 @@ var import_ws = require("ws");
|
|
|
8241
9352
|
|
|
8242
9353
|
// src/package-info.ts
|
|
8243
9354
|
var AISNITCH_PACKAGE_NAME = "aisnitch";
|
|
8244
|
-
var AISNITCH_VERSION = "0.2.
|
|
9355
|
+
var AISNITCH_VERSION = "0.2.21";
|
|
8245
9356
|
var AISNITCH_DESCRIPTION = "Universal bridge for AI coding tool activity \u2014 capture, normalize, stream.";
|
|
8246
9357
|
function getPackageScaffoldInfo() {
|
|
8247
9358
|
return {
|
|
@@ -8621,7 +9732,7 @@ var HTTPReceiver = class {
|
|
|
8621
9732
|
};
|
|
8622
9733
|
|
|
8623
9734
|
// src/core/engine/uds-server.ts
|
|
8624
|
-
var
|
|
9735
|
+
var import_promises13 = require("fs/promises");
|
|
8625
9736
|
var import_node_fs = require("fs");
|
|
8626
9737
|
var import_node_net2 = require("net");
|
|
8627
9738
|
var import_node_readline = require("readline");
|
|
@@ -8680,7 +9791,7 @@ var UDSServer = class {
|
|
|
8680
9791
|
});
|
|
8681
9792
|
}
|
|
8682
9793
|
if (this.socketPath && process.platform !== "win32") {
|
|
8683
|
-
await (0,
|
|
9794
|
+
await (0, import_promises13.rm)(this.socketPath, { force: true });
|
|
8684
9795
|
}
|
|
8685
9796
|
this.socketPath = null;
|
|
8686
9797
|
logger.info("UDS server stopped");
|
|
@@ -8747,7 +9858,7 @@ var UDSServer = class {
|
|
|
8747
9858
|
return;
|
|
8748
9859
|
}
|
|
8749
9860
|
try {
|
|
8750
|
-
await (0,
|
|
9861
|
+
await (0, import_promises13.access)(socketPath, import_node_fs.constants.F_OK);
|
|
8751
9862
|
} catch (error) {
|
|
8752
9863
|
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
8753
9864
|
return;
|
|
@@ -8771,12 +9882,12 @@ var UDSServer = class {
|
|
|
8771
9882
|
if (!staleSocket) {
|
|
8772
9883
|
throw new Error(`Socket path is already in use: ${socketPath}`);
|
|
8773
9884
|
}
|
|
8774
|
-
await (0,
|
|
9885
|
+
await (0, import_promises13.rm)(socketPath, { force: true });
|
|
8775
9886
|
}
|
|
8776
9887
|
};
|
|
8777
9888
|
|
|
8778
9889
|
// src/core/engine/pipeline.ts
|
|
8779
|
-
var
|
|
9890
|
+
var import_node_path17 = require("path");
|
|
8780
9891
|
var import_zod4 = require("zod");
|
|
8781
9892
|
|
|
8782
9893
|
// src/core/events/cesp.ts
|
|
@@ -8816,7 +9927,7 @@ function getSocketPath(aisnitchHomePath) {
|
|
|
8816
9927
|
if (process.platform === "win32") {
|
|
8817
9928
|
return "\\\\.\\pipe\\aisnitch.sock";
|
|
8818
9929
|
}
|
|
8819
|
-
return (0,
|
|
9930
|
+
return (0, import_node_path17.join)(aisnitchHomePath, "aisnitch.sock");
|
|
8820
9931
|
}
|
|
8821
9932
|
var Pipeline = class {
|
|
8822
9933
|
eventBus = new EventBus();
|
|
@@ -9021,6 +10132,30 @@ var Pipeline = class {
|
|
|
9021
10132
|
getEventBus() {
|
|
9022
10133
|
return this.eventBus;
|
|
9023
10134
|
}
|
|
10135
|
+
/**
|
|
10136
|
+
* Returns the adapter registry for graceful shutdown coordination.
|
|
10137
|
+
*/
|
|
10138
|
+
getAdapterRegistry() {
|
|
10139
|
+
return this.adapterRegistry ?? void 0;
|
|
10140
|
+
}
|
|
10141
|
+
/**
|
|
10142
|
+
* Returns the HTTP receiver for graceful shutdown coordination.
|
|
10143
|
+
*/
|
|
10144
|
+
getHttpReceiver() {
|
|
10145
|
+
return this.httpReceiver;
|
|
10146
|
+
}
|
|
10147
|
+
/**
|
|
10148
|
+
* Returns the UDS server for graceful shutdown coordination.
|
|
10149
|
+
*/
|
|
10150
|
+
getUdsServer() {
|
|
10151
|
+
return this.udsServer;
|
|
10152
|
+
}
|
|
10153
|
+
/**
|
|
10154
|
+
* Returns the WebSocket server for graceful shutdown coordination.
|
|
10155
|
+
*/
|
|
10156
|
+
getWsServer() {
|
|
10157
|
+
return this.wsServer;
|
|
10158
|
+
}
|
|
9024
10159
|
getHealthSnapshot() {
|
|
9025
10160
|
const status = this.getStatus();
|
|
9026
10161
|
return {
|
|
@@ -9101,6 +10236,483 @@ var Pipeline = class {
|
|
|
9101
10236
|
}
|
|
9102
10237
|
};
|
|
9103
10238
|
|
|
10239
|
+
// src/core/timeout.ts
|
|
10240
|
+
var DEFAULT_TIMEOUTS = Object.freeze({
|
|
10241
|
+
/**
|
|
10242
|
+
* File read/write operations (JSONL transcripts, config files).
|
|
10243
|
+
* Default: 5 seconds
|
|
10244
|
+
*/
|
|
10245
|
+
fileOperation: 5e3,
|
|
10246
|
+
/**
|
|
10247
|
+
* HTTP requests to health endpoint or external APIs.
|
|
10248
|
+
* Default: 30 seconds
|
|
10249
|
+
*/
|
|
10250
|
+
httpRequest: 3e4,
|
|
10251
|
+
/**
|
|
10252
|
+
* Process detection commands (`pgrep`, `ps aux`).
|
|
10253
|
+
* Default: 3 seconds
|
|
10254
|
+
*/
|
|
10255
|
+
processDetection: 3e3,
|
|
10256
|
+
/**
|
|
10257
|
+
* Adapter startup (file watchers, hook bridges, pollers).
|
|
10258
|
+
* Default: 10 seconds
|
|
10259
|
+
*/
|
|
10260
|
+
adapterStartup: 1e4,
|
|
10261
|
+
/**
|
|
10262
|
+
* Adapter shutdown (graceful cleanup, watcher close).
|
|
10263
|
+
* Default: 5 seconds — after this, resources are force-closed
|
|
10264
|
+
*/
|
|
10265
|
+
adapterShutdown: 5e3,
|
|
10266
|
+
/**
|
|
10267
|
+
* Daemon graceful shutdown (stop all components in order).
|
|
10268
|
+
* Default: 30 seconds
|
|
10269
|
+
*/
|
|
10270
|
+
daemonShutdown: 3e4,
|
|
10271
|
+
/**
|
|
10272
|
+
* WebSocket connection establishment.
|
|
10273
|
+
* Default: 10 seconds
|
|
10274
|
+
*/
|
|
10275
|
+
wsConnection: 1e4,
|
|
10276
|
+
/**
|
|
10277
|
+
* Overall pipeline start (all components).
|
|
10278
|
+
* Default: 15 seconds
|
|
10279
|
+
*/
|
|
10280
|
+
pipelineStartup: 15e3
|
|
10281
|
+
});
|
|
10282
|
+
function withTimeout(promise, timeoutMs, context) {
|
|
10283
|
+
if (timeoutMs <= 0) {
|
|
10284
|
+
throw new TimeoutError(
|
|
10285
|
+
`Invalid timeout value: ${timeoutMs}ms (must be > 0)`,
|
|
10286
|
+
"TIMEOUT_INVALID_VALUE",
|
|
10287
|
+
{ context, timeoutMs }
|
|
10288
|
+
);
|
|
10289
|
+
}
|
|
10290
|
+
return Promise.race([
|
|
10291
|
+
promise,
|
|
10292
|
+
new Promise((_, reject) => {
|
|
10293
|
+
const timeoutId = setTimeout(() => {
|
|
10294
|
+
reject(
|
|
10295
|
+
new TimeoutError(
|
|
10296
|
+
`Operation exceeded ${timeoutMs}ms deadline`,
|
|
10297
|
+
"TIMEOUT_EXCEEDED",
|
|
10298
|
+
{ context, timeoutMs }
|
|
10299
|
+
)
|
|
10300
|
+
);
|
|
10301
|
+
}, timeoutMs);
|
|
10302
|
+
timeoutId.unref();
|
|
10303
|
+
})
|
|
10304
|
+
]);
|
|
10305
|
+
}
|
|
10306
|
+
async function timeoutWarning(promise, timeoutMs, context) {
|
|
10307
|
+
try {
|
|
10308
|
+
return await withTimeout(promise, timeoutMs, context);
|
|
10309
|
+
} catch (error) {
|
|
10310
|
+
if (error instanceof TimeoutError) {
|
|
10311
|
+
logger.warn(
|
|
10312
|
+
{ context: error.context, timeoutMs: error.context },
|
|
10313
|
+
`Best-effort operation timed out after ${timeoutMs}ms`
|
|
10314
|
+
);
|
|
10315
|
+
return await promise;
|
|
10316
|
+
}
|
|
10317
|
+
throw error;
|
|
10318
|
+
}
|
|
10319
|
+
}
|
|
10320
|
+
function getTimeout(name) {
|
|
10321
|
+
return DEFAULT_TIMEOUTS[name];
|
|
10322
|
+
}
|
|
10323
|
+
function isTimeoutError(error) {
|
|
10324
|
+
return error instanceof TimeoutError;
|
|
10325
|
+
}
|
|
10326
|
+
|
|
10327
|
+
// src/core/graceful-shutdown.ts
|
|
10328
|
+
async function withShutdownTimeout(fn, timeoutMs, component) {
|
|
10329
|
+
if (timeoutMs <= 0) {
|
|
10330
|
+
await fn();
|
|
10331
|
+
return;
|
|
10332
|
+
}
|
|
10333
|
+
const timeoutPromise = new Promise((resolve2) => {
|
|
10334
|
+
setTimeout(() => {
|
|
10335
|
+
resolve2("timed_out");
|
|
10336
|
+
}, timeoutMs).unref();
|
|
10337
|
+
});
|
|
10338
|
+
const result = await Promise.race([
|
|
10339
|
+
fn().then(() => "completed"),
|
|
10340
|
+
timeoutPromise
|
|
10341
|
+
]);
|
|
10342
|
+
if (result === "timed_out") {
|
|
10343
|
+
logger.warn(
|
|
10344
|
+
{ component, timeoutMs },
|
|
10345
|
+
`Graceful shutdown exceeded ${timeoutMs}ms timeout \u2014 forcing through`
|
|
10346
|
+
);
|
|
10347
|
+
}
|
|
10348
|
+
}
|
|
10349
|
+
async function shutdownInOrder(components, timeouts, label) {
|
|
10350
|
+
const getTimeout2 = (key) => {
|
|
10351
|
+
return timeouts[key] ?? DEFAULT_TIMEOUTS.daemonShutdown;
|
|
10352
|
+
};
|
|
10353
|
+
const stopSafely = async (key, fn) => {
|
|
10354
|
+
const timeoutMs = getTimeout2(key);
|
|
10355
|
+
try {
|
|
10356
|
+
await withShutdownTimeout(fn, timeoutMs, `${label}.${key}`);
|
|
10357
|
+
} catch (error) {
|
|
10358
|
+
logger.warn(
|
|
10359
|
+
{ error, key, label },
|
|
10360
|
+
`Error during shutdown of ${key} \u2014 continuing with remaining components`
|
|
10361
|
+
);
|
|
10362
|
+
}
|
|
10363
|
+
};
|
|
10364
|
+
if (components.cleanupFns) {
|
|
10365
|
+
for (const cleanupFn of components.cleanupFns) {
|
|
10366
|
+
try {
|
|
10367
|
+
await withShutdownTimeout(
|
|
10368
|
+
async () => {
|
|
10369
|
+
const result = cleanupFn();
|
|
10370
|
+
if (result instanceof Promise) {
|
|
10371
|
+
await result;
|
|
10372
|
+
}
|
|
10373
|
+
},
|
|
10374
|
+
1e3,
|
|
10375
|
+
`${label}.cleanup`
|
|
10376
|
+
);
|
|
10377
|
+
} catch (error) {
|
|
10378
|
+
logger.warn({ error, label }, "Cleanup function failed");
|
|
10379
|
+
}
|
|
10380
|
+
}
|
|
10381
|
+
}
|
|
10382
|
+
if (components.eventBus) {
|
|
10383
|
+
components.eventBus.unsubscribeAll();
|
|
10384
|
+
}
|
|
10385
|
+
await stopSafely("wsServer", () => components.wsServer.stop());
|
|
10386
|
+
await stopSafely("udsServer", () => components.udsServer.stop());
|
|
10387
|
+
await stopSafely("httpReceiver", () => components.httpReceiver.stop());
|
|
10388
|
+
if (components.adapterRegistry) {
|
|
10389
|
+
await stopSafely("adapterRegistry", () => components.adapterRegistry.stopAll());
|
|
10390
|
+
}
|
|
10391
|
+
}
|
|
10392
|
+
var GracefulShutdownManager = class {
|
|
10393
|
+
/**
|
|
10394
|
+
* Creates a new shutdown manager.
|
|
10395
|
+
*
|
|
10396
|
+
* @param options - Configuration options
|
|
10397
|
+
* @param options.onShutdown - Async function called when shutdown is triggered
|
|
10398
|
+
* @param options.exitCode - Exit code to use (default: 0 for graceful, 1 for errors)
|
|
10399
|
+
* @param options.exitDelayMs - Delay before `process.exit()` (default: 100ms for flush)
|
|
10400
|
+
*/
|
|
10401
|
+
constructor(options) {
|
|
10402
|
+
this.options = options;
|
|
10403
|
+
}
|
|
10404
|
+
shuttingDown = false;
|
|
10405
|
+
pendingHandlers = /* @__PURE__ */ new Set();
|
|
10406
|
+
/**
|
|
10407
|
+
* Synchronous handler function suitable for `process.on()`.
|
|
10408
|
+
*
|
|
10409
|
+
* Multiple calls are safe — only the first call executes `onShutdown`.
|
|
10410
|
+
* Subsequent calls queue to the internal pending set and run after the
|
|
10411
|
+
* first shutdown completes.
|
|
10412
|
+
*/
|
|
10413
|
+
get handler() {
|
|
10414
|
+
return (signal) => {
|
|
10415
|
+
if (!this.shuttingDown) {
|
|
10416
|
+
this.shuttingDown = true;
|
|
10417
|
+
void this.runShutdown(signal);
|
|
10418
|
+
} else {
|
|
10419
|
+
this.pendingHandlers.add(() => {
|
|
10420
|
+
void this.runShutdown(signal);
|
|
10421
|
+
});
|
|
10422
|
+
}
|
|
10423
|
+
};
|
|
10424
|
+
}
|
|
10425
|
+
/**
|
|
10426
|
+
* Manually triggers shutdown from async code (e.g., TUI quit button).
|
|
10427
|
+
*
|
|
10428
|
+
* @param signal - Signal name (for logging)
|
|
10429
|
+
*/
|
|
10430
|
+
shutdown(signal = "manual") {
|
|
10431
|
+
this.handler(signal);
|
|
10432
|
+
}
|
|
10433
|
+
/**
|
|
10434
|
+
* Returns whether shutdown is currently in progress.
|
|
10435
|
+
*/
|
|
10436
|
+
isShuttingDown() {
|
|
10437
|
+
return this.shuttingDown;
|
|
10438
|
+
}
|
|
10439
|
+
async runShutdown(signal) {
|
|
10440
|
+
const exitCode = this.options.exitCode ?? (signal === "uncaughtException" || signal === "unhandledRejection" ? 1 : 0);
|
|
10441
|
+
const exitDelayMs = this.options.exitDelayMs ?? 100;
|
|
10442
|
+
try {
|
|
10443
|
+
await this.options.onShutdown(signal);
|
|
10444
|
+
} catch (error) {
|
|
10445
|
+
logger.error(
|
|
10446
|
+
{ error, signal },
|
|
10447
|
+
"Error during graceful shutdown \u2014 forcing exit"
|
|
10448
|
+
);
|
|
10449
|
+
} finally {
|
|
10450
|
+
await new Promise((resolve2) => {
|
|
10451
|
+
setTimeout(resolve2, exitDelayMs).unref();
|
|
10452
|
+
});
|
|
10453
|
+
for (const pendingHandler of this.pendingHandlers) {
|
|
10454
|
+
pendingHandler();
|
|
10455
|
+
}
|
|
10456
|
+
process.exit(exitCode);
|
|
10457
|
+
}
|
|
10458
|
+
}
|
|
10459
|
+
};
|
|
10460
|
+
async function withOverallShutdownTimeout(shutdownPromise, timeoutMs, label) {
|
|
10461
|
+
try {
|
|
10462
|
+
await withTimeout(shutdownPromise, timeoutMs, `${label}-overall-shutdown`);
|
|
10463
|
+
} catch (error) {
|
|
10464
|
+
if (isTimeoutError(error)) {
|
|
10465
|
+
logger.error(
|
|
10466
|
+
{ timeoutMs, label },
|
|
10467
|
+
`Overall shutdown timeout exceeded \u2014 forcing process exit`
|
|
10468
|
+
);
|
|
10469
|
+
}
|
|
10470
|
+
process.exit(1);
|
|
10471
|
+
}
|
|
10472
|
+
}
|
|
10473
|
+
|
|
10474
|
+
// src/core/result.ts
|
|
10475
|
+
function isOk(result) {
|
|
10476
|
+
return result.success === true;
|
|
10477
|
+
}
|
|
10478
|
+
function isErr(result) {
|
|
10479
|
+
return result.success === false;
|
|
10480
|
+
}
|
|
10481
|
+
function ok(value) {
|
|
10482
|
+
return Object.freeze({ success: true, value });
|
|
10483
|
+
}
|
|
10484
|
+
function err(error) {
|
|
10485
|
+
return Object.freeze({ success: false, error });
|
|
10486
|
+
}
|
|
10487
|
+
function mapOk(result, fn) {
|
|
10488
|
+
if (!result.success) {
|
|
10489
|
+
return result;
|
|
10490
|
+
}
|
|
10491
|
+
return ok(fn(result.value));
|
|
10492
|
+
}
|
|
10493
|
+
function mapErr(result, fn) {
|
|
10494
|
+
if (result.success) {
|
|
10495
|
+
return result;
|
|
10496
|
+
}
|
|
10497
|
+
return err(fn(result.error));
|
|
10498
|
+
}
|
|
10499
|
+
async function flatMap(result, fn) {
|
|
10500
|
+
if (!result.success) {
|
|
10501
|
+
return result;
|
|
10502
|
+
}
|
|
10503
|
+
const mapped = await fn(result.value);
|
|
10504
|
+
if (!mapped.success) {
|
|
10505
|
+
return mapped;
|
|
10506
|
+
}
|
|
10507
|
+
return ok(mapped.value);
|
|
10508
|
+
}
|
|
10509
|
+
async function fromPromise(promise, mapError) {
|
|
10510
|
+
try {
|
|
10511
|
+
const value = await promise;
|
|
10512
|
+
return ok(value);
|
|
10513
|
+
} catch (reason) {
|
|
10514
|
+
return err(mapError(reason));
|
|
10515
|
+
}
|
|
10516
|
+
}
|
|
10517
|
+
function fromSync(fn, mapError) {
|
|
10518
|
+
try {
|
|
10519
|
+
return ok(fn());
|
|
10520
|
+
} catch (reason) {
|
|
10521
|
+
return err(mapError(reason));
|
|
10522
|
+
}
|
|
10523
|
+
}
|
|
10524
|
+
|
|
10525
|
+
// src/core/retry.ts
|
|
10526
|
+
var DefaultRetryOptions = {
|
|
10527
|
+
attempts: 3,
|
|
10528
|
+
backoff: 2,
|
|
10529
|
+
context: "unknown",
|
|
10530
|
+
delayMs: 500,
|
|
10531
|
+
jitter: true,
|
|
10532
|
+
maxTotalDelayMs: 3e4,
|
|
10533
|
+
shouldRetry: isRetryableError
|
|
10534
|
+
};
|
|
10535
|
+
function sleep(ms) {
|
|
10536
|
+
return new Promise((resolve2) => {
|
|
10537
|
+
setTimeout(resolve2, ms).unref();
|
|
10538
|
+
});
|
|
10539
|
+
}
|
|
10540
|
+
function computeDelay(attempt, baseDelayMs, backoff, jitter) {
|
|
10541
|
+
const exponentialDelay = baseDelayMs * Math.pow(backoff, attempt - 1);
|
|
10542
|
+
if (!jitter) {
|
|
10543
|
+
return exponentialDelay;
|
|
10544
|
+
}
|
|
10545
|
+
const jitterFactor = 0.75 + Math.random() * 0.5;
|
|
10546
|
+
return Math.round(exponentialDelay * jitterFactor);
|
|
10547
|
+
}
|
|
10548
|
+
async function withRetry(fn, options) {
|
|
10549
|
+
const {
|
|
10550
|
+
attempts = DefaultRetryOptions.attempts,
|
|
10551
|
+
backoff = DefaultRetryOptions.backoff,
|
|
10552
|
+
delayMs = DefaultRetryOptions.delayMs,
|
|
10553
|
+
maxTotalDelayMs = DefaultRetryOptions.maxTotalDelayMs,
|
|
10554
|
+
jitter = DefaultRetryOptions.jitter,
|
|
10555
|
+
shouldRetry = DefaultRetryOptions.shouldRetry
|
|
10556
|
+
} = options;
|
|
10557
|
+
let lastError;
|
|
10558
|
+
let totalDelayMs = 0;
|
|
10559
|
+
const shouldRetryWithDefault = shouldRetry ?? DefaultRetryOptions.shouldRetry;
|
|
10560
|
+
for (let attempt = 1; attempt <= attempts; attempt++) {
|
|
10561
|
+
try {
|
|
10562
|
+
return await fn();
|
|
10563
|
+
} catch (error) {
|
|
10564
|
+
lastError = error;
|
|
10565
|
+
const retryable = shouldRetryWithDefault(error);
|
|
10566
|
+
const attemptsRemaining = attempt < attempts;
|
|
10567
|
+
if (!retryable || !attemptsRemaining) {
|
|
10568
|
+
if (!retryable) {
|
|
10569
|
+
logger.debug(
|
|
10570
|
+
{ attempt, context: options.context, error },
|
|
10571
|
+
"Non-retryable error \u2014 giving up immediately"
|
|
10572
|
+
);
|
|
10573
|
+
} else {
|
|
10574
|
+
logger.error(
|
|
10575
|
+
{ attempt, attempts, context: options.context, error },
|
|
10576
|
+
"All retry attempts exhausted"
|
|
10577
|
+
);
|
|
10578
|
+
}
|
|
10579
|
+
throw error;
|
|
10580
|
+
}
|
|
10581
|
+
const delay = computeDelay(attempt, delayMs, backoff, jitter ?? false);
|
|
10582
|
+
totalDelayMs += delay;
|
|
10583
|
+
if (totalDelayMs > maxTotalDelayMs) {
|
|
10584
|
+
logger.warn(
|
|
10585
|
+
{ attempt, delay, totalDelayMs, maxTotalDelayMs, context: options.context },
|
|
10586
|
+
"Retry max total delay exceeded \u2014 giving up"
|
|
10587
|
+
);
|
|
10588
|
+
throw lastError;
|
|
10589
|
+
}
|
|
10590
|
+
logger.debug(
|
|
10591
|
+
{ attempt, attempts, delay, nextDelayMs: delay, context: options.context },
|
|
10592
|
+
`Operation failed \u2014 retrying in ${delay}ms`
|
|
10593
|
+
);
|
|
10594
|
+
await sleep(delay);
|
|
10595
|
+
}
|
|
10596
|
+
}
|
|
10597
|
+
throw lastError;
|
|
10598
|
+
}
|
|
10599
|
+
function fireAndForgetRetry(fn, options) {
|
|
10600
|
+
void withRetry(fn, {
|
|
10601
|
+
...options,
|
|
10602
|
+
attempts: options.attempts ?? 2
|
|
10603
|
+
}).catch((error) => {
|
|
10604
|
+
logger.warn(
|
|
10605
|
+
{ error, context: options.context },
|
|
10606
|
+
"Fire-and-forget retry also failed \u2014 giving up silently"
|
|
10607
|
+
);
|
|
10608
|
+
});
|
|
10609
|
+
}
|
|
10610
|
+
function withRetryOn(fn, options) {
|
|
10611
|
+
return ((...args) => withRetry(() => fn(...args), options));
|
|
10612
|
+
}
|
|
10613
|
+
|
|
10614
|
+
// src/core/safety.ts
|
|
10615
|
+
var MAX_PORT = 65535;
|
|
10616
|
+
var MIN_PORT = 1;
|
|
10617
|
+
var MAX_PATH_LENGTH = 4096;
|
|
10618
|
+
var MAX_GENERIC_STRING_LENGTH = 1e4;
|
|
10619
|
+
var MAX_LABEL_LENGTH = 255;
|
|
10620
|
+
function getString11(record, key) {
|
|
10621
|
+
const value = record[key];
|
|
10622
|
+
if (typeof value !== "string") {
|
|
10623
|
+
return void 0;
|
|
10624
|
+
}
|
|
10625
|
+
const trimmed = value.trim();
|
|
10626
|
+
return trimmed.length > 0 ? trimmed : void 0;
|
|
10627
|
+
}
|
|
10628
|
+
function getStringWithMaxLength(record, key, maxLength) {
|
|
10629
|
+
const value = getString11(record, key);
|
|
10630
|
+
if (value === void 0) {
|
|
10631
|
+
return void 0;
|
|
10632
|
+
}
|
|
10633
|
+
return value.length > maxLength ? value.slice(0, maxLength) : value;
|
|
10634
|
+
}
|
|
10635
|
+
function getNumber9(record, key) {
|
|
10636
|
+
const value = record[key];
|
|
10637
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
10638
|
+
return void 0;
|
|
10639
|
+
}
|
|
10640
|
+
return value;
|
|
10641
|
+
}
|
|
10642
|
+
function getSafeInteger(record, key, options = {}) {
|
|
10643
|
+
const value = getNumber9(record, key);
|
|
10644
|
+
if (value === void 0) {
|
|
10645
|
+
return void 0;
|
|
10646
|
+
}
|
|
10647
|
+
if (!Number.isInteger(value)) {
|
|
10648
|
+
return void 0;
|
|
10649
|
+
}
|
|
10650
|
+
if (options.min !== void 0 && value < options.min) {
|
|
10651
|
+
return void 0;
|
|
10652
|
+
}
|
|
10653
|
+
if (options.max !== void 0 && value > options.max) {
|
|
10654
|
+
return void 0;
|
|
10655
|
+
}
|
|
10656
|
+
return value;
|
|
10657
|
+
}
|
|
10658
|
+
function getPositiveNumber(record, key) {
|
|
10659
|
+
const value = getNumber9(record, key);
|
|
10660
|
+
return value !== void 0 && value > 0 ? value : void 0;
|
|
10661
|
+
}
|
|
10662
|
+
function getBoolean2(record, key) {
|
|
10663
|
+
const value = record[key];
|
|
10664
|
+
if (typeof value === "boolean") {
|
|
10665
|
+
return value;
|
|
10666
|
+
}
|
|
10667
|
+
if (typeof value === "string") {
|
|
10668
|
+
const lower = value.toLowerCase();
|
|
10669
|
+
if (lower === "true" || lower === "1") {
|
|
10670
|
+
return true;
|
|
10671
|
+
}
|
|
10672
|
+
if (lower === "false" || lower === "0") {
|
|
10673
|
+
return false;
|
|
10674
|
+
}
|
|
10675
|
+
}
|
|
10676
|
+
return void 0;
|
|
10677
|
+
}
|
|
10678
|
+
function getArray(record, key) {
|
|
10679
|
+
const value = record[key];
|
|
10680
|
+
return Array.isArray(value) ? value : void 0;
|
|
10681
|
+
}
|
|
10682
|
+
function getObject(record, key) {
|
|
10683
|
+
const value = record[key];
|
|
10684
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
10685
|
+
return void 0;
|
|
10686
|
+
}
|
|
10687
|
+
return value;
|
|
10688
|
+
}
|
|
10689
|
+
function isValidPort(port) {
|
|
10690
|
+
return port !== void 0 && Number.isInteger(port) && port >= MIN_PORT && port <= MAX_PORT;
|
|
10691
|
+
}
|
|
10692
|
+
function isValidPathLength(path) {
|
|
10693
|
+
return path !== void 0 && path.length > 0 && path.length <= MAX_PATH_LENGTH;
|
|
10694
|
+
}
|
|
10695
|
+
function isValidStringLength(value, maxLength) {
|
|
10696
|
+
return value !== void 0 && value.length <= maxLength;
|
|
10697
|
+
}
|
|
10698
|
+
function isRecord11(value) {
|
|
10699
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
10700
|
+
return false;
|
|
10701
|
+
}
|
|
10702
|
+
const proto = Object.prototype.toString.call(value);
|
|
10703
|
+
return proto === "[object Object]";
|
|
10704
|
+
}
|
|
10705
|
+
function isNotNull(value) {
|
|
10706
|
+
return value != null;
|
|
10707
|
+
}
|
|
10708
|
+
function getPort(record, key) {
|
|
10709
|
+
const value = getSafeInteger(record, key, { min: MIN_PORT, max: MAX_PORT });
|
|
10710
|
+
return value;
|
|
10711
|
+
}
|
|
10712
|
+
function getSeqnum(record, key) {
|
|
10713
|
+
return getSafeInteger(record, key, { min: 1 });
|
|
10714
|
+
}
|
|
10715
|
+
|
|
9104
10716
|
// src/tui/index.tsx
|
|
9105
10717
|
var import_ink13 = require("ink");
|
|
9106
10718
|
var import_fullscreen_ink = require("fullscreen-ink");
|
|
@@ -9162,7 +10774,7 @@ function getEventDetailSegments(event) {
|
|
|
9162
10774
|
break;
|
|
9163
10775
|
case "agent.asking_user":
|
|
9164
10776
|
segments.push(
|
|
9165
|
-
|
|
10777
|
+
getString12(raw, "notification_type") ?? getString12(raw, "notificationType") ?? getString12(raw, "type")
|
|
9166
10778
|
);
|
|
9167
10779
|
segments.push(event.data.errorMessage ?? extractLooseString3(raw, [
|
|
9168
10780
|
"message",
|
|
@@ -9236,10 +10848,10 @@ function extractContentPart(raw, partType, valueKey) {
|
|
|
9236
10848
|
}
|
|
9237
10849
|
for (const part of content) {
|
|
9238
10850
|
const record = getRecord10(part);
|
|
9239
|
-
if (!record ||
|
|
10851
|
+
if (!record || getString12(record, "type") !== partType) {
|
|
9240
10852
|
continue;
|
|
9241
10853
|
}
|
|
9242
|
-
const value =
|
|
10854
|
+
const value = getString12(record, valueKey);
|
|
9243
10855
|
if (value) {
|
|
9244
10856
|
return value;
|
|
9245
10857
|
}
|
|
@@ -9251,12 +10863,12 @@ function extractLooseString3(raw, keys) {
|
|
|
9251
10863
|
return void 0;
|
|
9252
10864
|
}
|
|
9253
10865
|
for (const key of keys) {
|
|
9254
|
-
const directValue =
|
|
10866
|
+
const directValue = getString12(raw, key);
|
|
9255
10867
|
if (directValue) {
|
|
9256
10868
|
return directValue;
|
|
9257
10869
|
}
|
|
9258
10870
|
const nestedRecord = getRecord10(raw[key]);
|
|
9259
|
-
const nestedValue =
|
|
10871
|
+
const nestedValue = getString12(nestedRecord, "text") ?? getString12(nestedRecord, "message") ?? getString12(nestedRecord, "content");
|
|
9260
10872
|
if (nestedValue) {
|
|
9261
10873
|
return nestedValue;
|
|
9262
10874
|
}
|
|
@@ -9270,13 +10882,13 @@ function truncateSegment(value) {
|
|
|
9270
10882
|
}
|
|
9271
10883
|
return `${normalized.slice(0, DETAIL_SEGMENT_LIMIT - 1)}\u2026`;
|
|
9272
10884
|
}
|
|
9273
|
-
function
|
|
10885
|
+
function isRecord12(value) {
|
|
9274
10886
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
9275
10887
|
}
|
|
9276
10888
|
function getRecord10(value) {
|
|
9277
|
-
return
|
|
10889
|
+
return isRecord12(value) ? value : void 0;
|
|
9278
10890
|
}
|
|
9279
|
-
function
|
|
10891
|
+
function getString12(payload, key) {
|
|
9280
10892
|
if (!payload) {
|
|
9281
10893
|
return void 0;
|
|
9282
10894
|
}
|
|
@@ -9304,9 +10916,11 @@ var TOOL_COLORS = {
|
|
|
9304
10916
|
"openhands": "#facc15",
|
|
9305
10917
|
"openclaw": "#ef4444",
|
|
9306
10918
|
"opencode": "#10b981",
|
|
10919
|
+
"pi": "#1db954",
|
|
9307
10920
|
"qwen-code": "#22c55e",
|
|
9308
10921
|
"unknown": "#94a3b8",
|
|
9309
|
-
"windsurf": "#a855f7"
|
|
10922
|
+
"windsurf": "#a855f7",
|
|
10923
|
+
"zed": "#e85d04"
|
|
9310
10924
|
};
|
|
9311
10925
|
var EVENT_COLORS = {
|
|
9312
10926
|
"agent.asking_user": "#ef4444",
|
|
@@ -11156,10 +12770,12 @@ async function renderManagedTui(options) {
|
|
|
11156
12770
|
AISNITCH_EVENT_TYPES,
|
|
11157
12771
|
AISNITCH_PACKAGE_NAME,
|
|
11158
12772
|
AISNITCH_VERSION,
|
|
12773
|
+
AISnitchError,
|
|
11159
12774
|
AISnitchEventSchema,
|
|
11160
12775
|
AISnitchEventTypeSchema,
|
|
11161
12776
|
AUTO_UPDATE_MANAGERS,
|
|
11162
12777
|
AdapterConfigSchema,
|
|
12778
|
+
AdapterError,
|
|
11163
12779
|
AdapterRegistry,
|
|
11164
12780
|
AiderAdapter,
|
|
11165
12781
|
App,
|
|
@@ -11168,6 +12784,8 @@ async function renderManagedTui(options) {
|
|
|
11168
12784
|
CESPCategorySchema,
|
|
11169
12785
|
CESP_CATEGORIES,
|
|
11170
12786
|
CESP_MAP,
|
|
12787
|
+
CircuitBreaker,
|
|
12788
|
+
CircuitOpenError,
|
|
11171
12789
|
ClaudeCodeAdapter,
|
|
11172
12790
|
CodexAdapter,
|
|
11173
12791
|
ConfigSchema,
|
|
@@ -11175,8 +12793,10 @@ async function renderManagedTui(options) {
|
|
|
11175
12793
|
CopilotCLIAdapter,
|
|
11176
12794
|
CursorAdapter,
|
|
11177
12795
|
DEFAULT_CONFIG,
|
|
12796
|
+
DEFAULT_TIMEOUTS,
|
|
11178
12797
|
DEFAULT_TUI_FILTERS,
|
|
11179
12798
|
DEFAULT_VISIBLE_EVENT_COUNT,
|
|
12799
|
+
DefaultRetryOptions,
|
|
11180
12800
|
DevinAdapter,
|
|
11181
12801
|
ERROR_TYPES,
|
|
11182
12802
|
EVENT_COLORS,
|
|
@@ -11188,33 +12808,51 @@ async function renderManagedTui(options) {
|
|
|
11188
12808
|
EventLine,
|
|
11189
12809
|
EventStream,
|
|
11190
12810
|
FilterBar,
|
|
12811
|
+
FinalMessageSchema,
|
|
11191
12812
|
GeminiCLIAdapter,
|
|
11192
12813
|
GenericPTYSession,
|
|
11193
12814
|
GlobalBadge,
|
|
11194
12815
|
GooseAdapter,
|
|
12816
|
+
GracefulShutdownManager,
|
|
11195
12817
|
HTTPReceiver,
|
|
11196
12818
|
Header,
|
|
11197
12819
|
HelpOverlay,
|
|
11198
12820
|
KiloAdapter,
|
|
11199
12821
|
LOG_LEVELS,
|
|
12822
|
+
MAX_GENERIC_STRING_LENGTH,
|
|
12823
|
+
MAX_LABEL_LENGTH,
|
|
12824
|
+
MAX_PATH_LENGTH,
|
|
12825
|
+
MAX_PORT,
|
|
12826
|
+
MIN_PORT,
|
|
11200
12827
|
ManagedDaemonApp,
|
|
12828
|
+
MessageContentSchema,
|
|
12829
|
+
NetworkError,
|
|
11201
12830
|
OpenClawAdapter,
|
|
11202
12831
|
OpenCodeAdapter,
|
|
11203
12832
|
Panel,
|
|
11204
12833
|
PanelStack,
|
|
12834
|
+
PiAdapter,
|
|
11205
12835
|
Pipeline,
|
|
12836
|
+
PipelineError,
|
|
11206
12837
|
RingBuffer,
|
|
11207
12838
|
SESSION_STALE_AFTER_MS,
|
|
12839
|
+
SHARED_BREAKERS,
|
|
11208
12840
|
SessionPanel,
|
|
11209
12841
|
StatusBar,
|
|
11210
12842
|
TOOL_COLORS,
|
|
11211
12843
|
TOOL_NAMES,
|
|
11212
12844
|
TUI_THEME,
|
|
11213
12845
|
TUI_VIEW_MODES,
|
|
12846
|
+
ThinkingContentSchema,
|
|
12847
|
+
TimeoutError,
|
|
12848
|
+
ToolCallNameSchema,
|
|
11214
12849
|
ToolInputSchema,
|
|
11215
12850
|
ToolNameSchema,
|
|
12851
|
+
ToolResultSchema,
|
|
11216
12852
|
UDSServer,
|
|
12853
|
+
ValidationError,
|
|
11217
12854
|
WSServer,
|
|
12855
|
+
ZedAdapter,
|
|
11218
12856
|
analyzeTerminalOutputChunk,
|
|
11219
12857
|
appendEventToStream,
|
|
11220
12858
|
applyEventFilters,
|
|
@@ -11228,6 +12866,9 @@ async function renderManagedTui(options) {
|
|
|
11228
12866
|
deriveGlobalActivityStatus,
|
|
11229
12867
|
deriveSessions,
|
|
11230
12868
|
ensureConfigDir,
|
|
12869
|
+
err,
|
|
12870
|
+
fireAndForgetRetry,
|
|
12871
|
+
flatMap,
|
|
11231
12872
|
formatEventDetail,
|
|
11232
12873
|
formatEventLine,
|
|
11233
12874
|
formatEventTime,
|
|
@@ -11235,16 +12876,42 @@ async function renderManagedTui(options) {
|
|
|
11235
12876
|
formatSessionLabelFromEvent,
|
|
11236
12877
|
formatSessionShortId,
|
|
11237
12878
|
formatWelcomeLine,
|
|
12879
|
+
fromPromise,
|
|
12880
|
+
fromSync,
|
|
11238
12881
|
getAISnitchHomePath,
|
|
12882
|
+
getArray,
|
|
12883
|
+
getBoolean,
|
|
11239
12884
|
getCESPCategory,
|
|
11240
12885
|
getConfigPath,
|
|
12886
|
+
getNumber,
|
|
12887
|
+
getObject,
|
|
11241
12888
|
getPackageScaffoldInfo,
|
|
11242
12889
|
getPendingFrozenEventCount,
|
|
12890
|
+
getPort,
|
|
12891
|
+
getPositiveNumber,
|
|
12892
|
+
getSafeInteger,
|
|
12893
|
+
getSeqnum,
|
|
11243
12894
|
getSocketPath,
|
|
12895
|
+
getString,
|
|
12896
|
+
getStringWithMaxLength,
|
|
12897
|
+
getTimeout,
|
|
11244
12898
|
getVisibleEventWindow,
|
|
12899
|
+
isAISnitchError,
|
|
12900
|
+
isErr,
|
|
11245
12901
|
isGenericSessionId,
|
|
12902
|
+
isNotNull,
|
|
12903
|
+
isOk,
|
|
12904
|
+
isRecord,
|
|
12905
|
+
isRetryableError,
|
|
12906
|
+
isTimeoutError,
|
|
12907
|
+
isValidPathLength,
|
|
12908
|
+
isValidPort,
|
|
12909
|
+
isValidStringLength,
|
|
11246
12910
|
loadConfig,
|
|
11247
12911
|
logger,
|
|
12912
|
+
mapErr,
|
|
12913
|
+
mapOk,
|
|
12914
|
+
ok,
|
|
11248
12915
|
parseAiderHistoryMarkdown,
|
|
11249
12916
|
renderAttachedTui,
|
|
11250
12917
|
renderForegroundTui,
|
|
@@ -11253,8 +12920,16 @@ async function renderManagedTui(options) {
|
|
|
11253
12920
|
resolveSessionId,
|
|
11254
12921
|
saveConfig,
|
|
11255
12922
|
setLoggerLevel,
|
|
12923
|
+
shutdownInOrder,
|
|
12924
|
+
sleep,
|
|
12925
|
+
timeoutWarning,
|
|
11256
12926
|
useEventStream,
|
|
11257
12927
|
useKeyBinds,
|
|
11258
|
-
useSessions
|
|
12928
|
+
useSessions,
|
|
12929
|
+
withOverallShutdownTimeout,
|
|
12930
|
+
withRetry,
|
|
12931
|
+
withRetryOn,
|
|
12932
|
+
withShutdownTimeout,
|
|
12933
|
+
withTimeout
|
|
11259
12934
|
});
|
|
11260
12935
|
//# sourceMappingURL=index.cjs.map
|