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.
@@ -41,7 +41,7 @@ var import_commander = require("commander");
41
41
 
42
42
  // src/package-info.ts
43
43
  var AISNITCH_PACKAGE_NAME = "aisnitch";
44
- var AISNITCH_VERSION = "0.2.19";
44
+ var AISNITCH_VERSION = "0.2.21";
45
45
  var AISNITCH_DESCRIPTION = "Universal bridge for AI coding tool activity \u2014 capture, normalize, stream.";
46
46
 
47
47
  // src/core/events/schema.ts
@@ -82,6 +82,8 @@ var TOOL_NAMES = [
82
82
  "kiro",
83
83
  "augment-code",
84
84
  "mistral",
85
+ "zed",
86
+ "pi",
85
87
  "unknown"
86
88
  ];
87
89
  var ERROR_TYPES = [
@@ -123,47 +125,58 @@ function createUuidV7() {
123
125
  return (0, import_uuid.v7)();
124
126
  }
125
127
  var ToolInputSchema = import_zod.z.strictObject({
126
- filePath: import_zod.z.string().min(1).optional(),
127
- command: import_zod.z.string().min(1).optional()
128
+ filePath: import_zod.z.string().min(1).max(4096).optional(),
129
+ command: import_zod.z.string().min(1).max(1e4).optional()
128
130
  }).refine(
129
131
  (value) => value.filePath !== void 0 || value.command !== void 0,
130
132
  "toolInput must include filePath or command"
131
133
  );
134
+ var ThinkingContentSchema = import_zod.z.string().max(1e5).describe("Raw thinking/reasoning content from the AI model");
135
+ var ToolCallNameSchema = import_zod.z.string().min(1).max(100).describe("Name of the tool being invoked (e.g., Edit, Bash, Grep)");
136
+ var FinalMessageSchema = import_zod.z.string().max(5e4).describe("End-of-run summary or completion message");
137
+ var ToolResultSchema = import_zod.z.string().max(1e4).describe("Tool execution result or output");
138
+ var MessageContentSchema = import_zod.z.string().max(1e5).describe("Raw text content from AI messages");
132
139
  var ToolNameSchema = import_zod.z.enum(TOOL_NAMES);
133
140
  var AISnitchEventTypeSchema = import_zod.z.enum(AISNITCH_EVENT_TYPES);
134
141
  var ErrorTypeSchema = import_zod.z.enum(ERROR_TYPES);
135
142
  var CESPCategorySchema = import_zod.z.enum(CESP_CATEGORIES);
136
143
  var EventDataSchema = import_zod.z.strictObject({
137
144
  state: AISnitchEventTypeSchema,
138
- project: import_zod.z.string().min(1).optional(),
139
- projectPath: import_zod.z.string().min(1).optional(),
145
+ project: import_zod.z.string().min(1).max(255).optional(),
146
+ projectPath: import_zod.z.string().min(1).max(4096).optional(),
140
147
  duration: import_zod.z.number().int().min(0).optional(),
141
- toolName: import_zod.z.string().min(1).optional(),
148
+ toolName: import_zod.z.string().min(1).max(100).optional(),
142
149
  toolInput: ToolInputSchema.optional(),
143
- activeFile: import_zod.z.string().min(1).optional(),
144
- model: import_zod.z.string().min(1).optional(),
150
+ activeFile: import_zod.z.string().min(1).max(4096).optional(),
151
+ model: import_zod.z.string().min(1).max(200).optional(),
145
152
  tokensUsed: import_zod.z.number().int().min(0).optional(),
146
- errorMessage: import_zod.z.string().min(1).optional(),
153
+ errorMessage: import_zod.z.string().min(1).max(1e4).optional(),
147
154
  errorType: ErrorTypeSchema.optional(),
148
155
  raw: import_zod.z.record(import_zod.z.string(), import_zod.z.unknown()).optional(),
149
- terminal: import_zod.z.string().min(1).optional(),
150
- cwd: import_zod.z.string().min(1).optional(),
156
+ terminal: import_zod.z.string().min(1).max(100).optional(),
157
+ cwd: import_zod.z.string().min(1).max(4096).optional(),
151
158
  pid: import_zod.z.number().int().positive().optional(),
152
- instanceId: import_zod.z.string().min(1).optional(),
159
+ instanceId: import_zod.z.string().min(1).max(255).optional(),
153
160
  instanceIndex: import_zod.z.number().int().min(1).optional(),
154
- instanceTotal: import_zod.z.number().int().min(1).optional()
161
+ instanceTotal: import_zod.z.number().int().min(1).optional(),
162
+ // New fields for enhanced content capture
163
+ thinkingContent: ThinkingContentSchema.optional(),
164
+ toolCallName: ToolCallNameSchema.optional(),
165
+ finalMessage: FinalMessageSchema.optional(),
166
+ toolResult: ToolResultSchema.optional(),
167
+ messageContent: MessageContentSchema.optional()
155
168
  });
156
169
  var AISnitchEventSchema = import_zod.z.strictObject({
157
170
  specversion: import_zod.z.literal("1.0"),
158
171
  id: import_zod.z.string().refine(isUuidV7, "id must be a valid UUIDv7 string"),
159
- source: import_zod.z.string().refine(
172
+ source: import_zod.z.string().max(2e3).refine(
160
173
  isValidUriReference,
161
174
  "source must be a valid non-empty CloudEvents URI-reference"
162
175
  ),
163
176
  type: AISnitchEventTypeSchema,
164
177
  time: ISO_TIMESTAMP_SCHEMA,
165
178
  "aisnitch.tool": ToolNameSchema,
166
- "aisnitch.sessionid": import_zod.z.string().min(1),
179
+ "aisnitch.sessionid": import_zod.z.string().min(1).max(500),
167
180
  "aisnitch.seqnum": import_zod.z.number().int().min(1),
168
181
  data: EventDataSchema
169
182
  });
@@ -2463,13 +2476,13 @@ function toConfigPathOptions(options) {
2463
2476
  }
2464
2477
 
2465
2478
  // src/cli/runtime.ts
2466
- var import_node_child_process15 = require("child_process");
2479
+ var import_node_child_process16 = require("child_process");
2467
2480
  var import_node_fs4 = require("fs");
2468
- var import_promises18 = require("fs/promises");
2481
+ var import_promises19 = require("fs/promises");
2469
2482
  var import_node_net4 = require("net");
2470
2483
  var import_node_os5 = require("os");
2471
- var import_node_path19 = require("path");
2472
- var import_node_util14 = require("util");
2484
+ var import_node_path21 = require("path");
2485
+ var import_node_util15 = require("util");
2473
2486
 
2474
2487
  // src/adapters/generic-pty.ts
2475
2488
  var import_node_path5 = require("path");
@@ -2540,9 +2553,11 @@ var TOOL_BINARY_MAP = {
2540
2553
  "openhands": "openhands",
2541
2554
  "openclaw": "openclaw",
2542
2555
  "opencode": "opencode",
2556
+ "pi": "pi",
2543
2557
  "qwen-code": "qwen",
2544
2558
  "unknown": "unknown",
2545
- "windsurf": "windsurf"
2559
+ "windsurf": "windsurf",
2560
+ "zed": "zed"
2546
2561
  };
2547
2562
  var ContextDetector = class {
2548
2563
  cache = /* @__PURE__ */ new Map();
@@ -3351,6 +3366,346 @@ function isPidRunning(pid) {
3351
3366
  }
3352
3367
  }
3353
3368
 
3369
+ // src/core/errors.ts
3370
+ var AISnitchError = class _AISnitchError extends Error {
3371
+ /**
3372
+ * Machine-readable error code for programmatic handling.
3373
+ * Format: `SUBCATEGORY_SPECIFIC_DETAIL` (uppercase with underscores).
3374
+ */
3375
+ code;
3376
+ /**
3377
+ * Arbitrary context bag forwarded to the logger for structured debugging.
3378
+ */
3379
+ context;
3380
+ constructor(message, code, context) {
3381
+ super(message);
3382
+ this.name = "AISnitchError";
3383
+ this.code = code;
3384
+ this.context = context;
3385
+ if (Error.captureStackTrace) {
3386
+ Error.captureStackTrace(this, _AISnitchError);
3387
+ }
3388
+ }
3389
+ /**
3390
+ * Full error chain for logging: `[name] code — message`.
3391
+ */
3392
+ toString() {
3393
+ return `${this.name} [${this.code}] \u2014 ${this.message}`;
3394
+ }
3395
+ /**
3396
+ * JSON serialization friendly to pino serializers.
3397
+ */
3398
+ toJSON() {
3399
+ return {
3400
+ name: this.name,
3401
+ code: this.code,
3402
+ message: this.message,
3403
+ context: this.context,
3404
+ stack: this.stack
3405
+ };
3406
+ }
3407
+ };
3408
+ function isAISnitchError(error) {
3409
+ return error instanceof AISnitchError;
3410
+ }
3411
+ function isRetryableError(error) {
3412
+ if (!isAISnitchError(error)) {
3413
+ if (error instanceof Error) {
3414
+ const code = error.code;
3415
+ const retryableCodes = /* @__PURE__ */ new Set([
3416
+ "ECONNREFUSED",
3417
+ "ECONNRESET",
3418
+ "ETIMEDOUT",
3419
+ "ENOTFOUND",
3420
+ "EHOSTUNREACH",
3421
+ "EPIPE",
3422
+ "EPERM"
3423
+ // sometimes transient on macOS file locks
3424
+ ]);
3425
+ if (typeof code === "string" && retryableCodes.has(code)) {
3426
+ return true;
3427
+ }
3428
+ }
3429
+ return false;
3430
+ }
3431
+ const retryableCategories = /* @__PURE__ */ new Set(["TIMEOUT", "NETWORK"]);
3432
+ for (const category of retryableCategories) {
3433
+ if (error.code.startsWith(category)) {
3434
+ return true;
3435
+ }
3436
+ }
3437
+ const retryablePatterns = [
3438
+ /^ADAPTER_.*_(FILE_IO|NETWORK|PROCESS_DETECT)_ERROR$/,
3439
+ /^PIPELINE_.*_(RETRY|RECONNECT)_ERROR$/
3440
+ ];
3441
+ for (const pattern of retryablePatterns) {
3442
+ if (pattern.test(error.code)) {
3443
+ return true;
3444
+ }
3445
+ }
3446
+ return false;
3447
+ }
3448
+
3449
+ // src/core/circuit-breaker.ts
3450
+ var CircuitOpenError = class _CircuitOpenError extends AISnitchError {
3451
+ constructor(circuitId, state) {
3452
+ super(
3453
+ `Circuit "${circuitId}" is OPEN \u2014 operation rejected`,
3454
+ "CIRCUIT_OPEN",
3455
+ { circuitId, failures: state.failures, lastFailureAt: state.lastFailureAt }
3456
+ );
3457
+ this.circuitId = circuitId;
3458
+ this.state = state;
3459
+ this.name = "CircuitOpenError";
3460
+ if (Error.captureStackTrace) {
3461
+ Error.captureStackTrace(this, _CircuitOpenError);
3462
+ }
3463
+ }
3464
+ toString() {
3465
+ return `${this.name} [${this.code}] "${this.circuitId}" \u2014 failures=${this.state.failures}`;
3466
+ }
3467
+ };
3468
+ var DEFAULT_OPTIONS = {
3469
+ failureThreshold: 5,
3470
+ halfOpenAfterMs: 3e4,
3471
+ id: "unnamed",
3472
+ resetOnSuccess: true,
3473
+ shouldCountAsFailure: isRetryableError,
3474
+ windowMs: 6e4
3475
+ };
3476
+ var CircuitBreaker = class {
3477
+ failures = 0;
3478
+ lastFailureAt = null;
3479
+ state = "closed";
3480
+ halfOpenTestStartedAt = null;
3481
+ options;
3482
+ constructor(options = {}) {
3483
+ this.options = {
3484
+ ...DEFAULT_OPTIONS,
3485
+ ...options,
3486
+ // Re-spread to ensure all fields have defaults
3487
+ failureThreshold: options.failureThreshold ?? DEFAULT_OPTIONS.failureThreshold,
3488
+ halfOpenAfterMs: options.halfOpenAfterMs ?? DEFAULT_OPTIONS.halfOpenAfterMs,
3489
+ id: options.id ?? DEFAULT_OPTIONS.id,
3490
+ resetOnSuccess: options.resetOnSuccess ?? DEFAULT_OPTIONS.resetOnSuccess,
3491
+ shouldCountAsFailure: options.shouldCountAsFailure ?? DEFAULT_OPTIONS.shouldCountAsFailure,
3492
+ windowMs: options.windowMs ?? DEFAULT_OPTIONS.windowMs
3493
+ };
3494
+ }
3495
+ /**
3496
+ * Executes an async operation through the circuit breaker.
3497
+ *
3498
+ * - If the circuit is CLOSED → runs `fn` and updates state based on result
3499
+ * - If the circuit is HALF-OPEN → runs `fn` once to test recovery
3500
+ * - If the circuit is OPEN → throws `CircuitOpenError` immediately (no call)
3501
+ *
3502
+ * @param fn - The async operation to protect
3503
+ * @returns The result of `fn` if successful
3504
+ * @throws CircuitOpenError if the circuit is OPEN
3505
+ * @throws The error from `fn` if it throws (and `shouldCountAsFailure` returns true)
3506
+ */
3507
+ async execute(fn) {
3508
+ switch (this.state) {
3509
+ case "closed":
3510
+ return this.executeClosed(fn);
3511
+ case "half-open":
3512
+ return this.executeHalfOpen(fn);
3513
+ case "open":
3514
+ if (this.shouldTransitionToHalfOpen()) {
3515
+ this.transitionToHalfOpen();
3516
+ return this.executeHalfOpen(fn);
3517
+ }
3518
+ throw new CircuitOpenError(this.options.id, this.getState());
3519
+ }
3520
+ }
3521
+ /**
3522
+ * Returns the current observable circuit state.
3523
+ */
3524
+ getState() {
3525
+ return {
3526
+ failures: this.failures,
3527
+ lastFailureAt: this.lastFailureAt,
3528
+ state: this.state
3529
+ };
3530
+ }
3531
+ /**
3532
+ * Forces the circuit to CLOSED (resets failure count and state).
3533
+ * Useful for manual recovery after a known-fix or after a maintenance window.
3534
+ */
3535
+ reset() {
3536
+ this.failures = 0;
3537
+ this.lastFailureAt = null;
3538
+ this.state = "closed";
3539
+ this.halfOpenTestStartedAt = null;
3540
+ logger.debug({ circuitId: this.options.id }, "Circuit breaker manually reset");
3541
+ }
3542
+ /**
3543
+ * Pre-warms the circuit by performing one test call in HALF-OPEN state.
3544
+ * If the circuit is already HALF-OPEN, this does nothing.
3545
+ * If the circuit is CLOSED, this does nothing.
3546
+ */
3547
+ async preWarm(fn) {
3548
+ if (this.state !== "open") {
3549
+ return;
3550
+ }
3551
+ this.transitionToHalfOpen();
3552
+ try {
3553
+ await fn();
3554
+ this.transitionToClosed();
3555
+ } catch {
3556
+ this.transitionToOpen();
3557
+ }
3558
+ }
3559
+ // ─────────────────────────────────────────────────────────────────────────
3560
+ // Private methods
3561
+ // ─────────────────────────────────────────────────────────────────────────
3562
+ async executeClosed(fn) {
3563
+ try {
3564
+ const result = await fn();
3565
+ this.onSuccess();
3566
+ return result;
3567
+ } catch (error) {
3568
+ this.onFailure(error);
3569
+ throw error;
3570
+ }
3571
+ }
3572
+ async executeHalfOpen(fn) {
3573
+ this.halfOpenTestStartedAt = Date.now();
3574
+ try {
3575
+ const result = await fn();
3576
+ this.transitionToClosed();
3577
+ return result;
3578
+ } catch (error) {
3579
+ this.transitionToOpen();
3580
+ throw error;
3581
+ }
3582
+ }
3583
+ onSuccess() {
3584
+ if (this.options.resetOnSuccess) {
3585
+ this.failures = 0;
3586
+ this.lastFailureAt = null;
3587
+ } else {
3588
+ this.failures = Math.max(0, this.failures - 1);
3589
+ if (this.failures === 0) {
3590
+ this.lastFailureAt = null;
3591
+ }
3592
+ }
3593
+ logger.debug(
3594
+ {
3595
+ circuitId: this.options.id,
3596
+ failures: this.failures
3597
+ },
3598
+ "Circuit breaker operation succeeded"
3599
+ );
3600
+ }
3601
+ onFailure(error) {
3602
+ if (!this.options.shouldCountAsFailure(error)) {
3603
+ logger.debug(
3604
+ { circuitId: this.options.id, error },
3605
+ "Circuit breaker operation failed but error is not counted as failure"
3606
+ );
3607
+ return;
3608
+ }
3609
+ this.failures += 1;
3610
+ this.lastFailureAt = Date.now();
3611
+ if (this.failures >= this.options.failureThreshold) {
3612
+ this.transitionToOpen();
3613
+ } else {
3614
+ logger.debug(
3615
+ {
3616
+ circuitId: this.options.id,
3617
+ failures: this.failures,
3618
+ threshold: this.options.failureThreshold
3619
+ },
3620
+ "Circuit breaker recorded failure"
3621
+ );
3622
+ }
3623
+ }
3624
+ transitionToOpen() {
3625
+ if (this.state === "open") {
3626
+ return;
3627
+ }
3628
+ this.state = "open";
3629
+ this.halfOpenTestStartedAt = null;
3630
+ logger.warn(
3631
+ {
3632
+ circuitId: this.options.id,
3633
+ failures: this.failures,
3634
+ windowMs: this.options.windowMs
3635
+ },
3636
+ "\u{1F534} Circuit breaker OPEN \u2014 blocking operations"
3637
+ );
3638
+ }
3639
+ transitionToHalfOpen() {
3640
+ this.state = "half-open";
3641
+ this.halfOpenTestStartedAt = Date.now();
3642
+ logger.info(
3643
+ { circuitId: this.options.id },
3644
+ "\u{1F7E1} Circuit breaker HALF-OPEN \u2014 testing recovery"
3645
+ );
3646
+ }
3647
+ transitionToClosed() {
3648
+ this.state = "closed";
3649
+ this.failures = 0;
3650
+ this.lastFailureAt = null;
3651
+ this.halfOpenTestStartedAt = null;
3652
+ logger.info(
3653
+ { circuitId: this.options.id },
3654
+ "\u{1F7E2} Circuit breaker CLOSED \u2014 recovery successful"
3655
+ );
3656
+ }
3657
+ shouldTransitionToHalfOpen() {
3658
+ if (this.lastFailureAt === null) {
3659
+ return true;
3660
+ }
3661
+ const elapsed = Date.now() - this.lastFailureAt;
3662
+ return elapsed >= this.options.halfOpenAfterMs;
3663
+ }
3664
+ };
3665
+ var SHARED_BREAKERS = Object.freeze({
3666
+ /**
3667
+ * Breaker for adapter event emission.
3668
+ * Threshold: 5 failures in 60s → open for 30s → half-open test.
3669
+ */
3670
+ adapterEmit: new CircuitBreaker({
3671
+ id: "adapter.emit",
3672
+ failureThreshold: 5,
3673
+ halfOpenAfterMs: 3e4,
3674
+ shouldCountAsFailure: isRetryableError,
3675
+ windowMs: 6e4
3676
+ }),
3677
+ /**
3678
+ * Breaker for file system operations (transcript reading, config loading).
3679
+ * More tolerant: 10 failures in 60s → open for 30s.
3680
+ */
3681
+ fileSystem: new CircuitBreaker({
3682
+ id: "filesystem",
3683
+ failureThreshold: 10,
3684
+ halfOpenAfterMs: 3e4,
3685
+ windowMs: 6e4
3686
+ }),
3687
+ /**
3688
+ * Breaker for HTTP/HTTPS requests.
3689
+ * Stricter: 3 failures in 30s → open for 15s.
3690
+ */
3691
+ httpRequest: new CircuitBreaker({
3692
+ id: "http-request",
3693
+ failureThreshold: 3,
3694
+ halfOpenAfterMs: 15e3,
3695
+ windowMs: 3e4
3696
+ }),
3697
+ /**
3698
+ * Breaker for process detection operations.
3699
+ * Most tolerant: 20 failures in 60s → open for 10s.
3700
+ */
3701
+ processDetection: new CircuitBreaker({
3702
+ id: "process-detection",
3703
+ failureThreshold: 20,
3704
+ halfOpenAfterMs: 1e4,
3705
+ windowMs: 6e4
3706
+ })
3707
+ });
3708
+
3354
3709
  // src/core/engine/event-bus.ts
3355
3710
  var import_eventemitter3 = require("eventemitter3");
3356
3711
  var EventBus = class {
@@ -4061,7 +4416,7 @@ var UDSServer = class {
4061
4416
  };
4062
4417
 
4063
4418
  // src/core/engine/pipeline.ts
4064
- var import_node_path16 = require("path");
4419
+ var import_node_path18 = require("path");
4065
4420
  var import_zod4 = require("zod");
4066
4421
 
4067
4422
  // src/adapters/aider.ts
@@ -4168,20 +4523,29 @@ var BaseAdapter = class {
4168
4523
  });
4169
4524
  let published;
4170
4525
  try {
4171
- published = await this.publishEventImplementation(event, {
4172
- cwd: context.cwd,
4173
- env: context.env,
4174
- hookPayload: context.hookPayload,
4175
- pid: context.pid,
4176
- sessionId,
4177
- source: context.source,
4178
- transcriptPath: context.transcriptPath
4526
+ published = await SHARED_BREAKERS.adapterEmit.execute(async () => {
4527
+ return await this.publishEventImplementation(event, {
4528
+ cwd: context.cwd,
4529
+ env: context.env,
4530
+ hookPayload: context.hookPayload,
4531
+ pid: context.pid,
4532
+ sessionId,
4533
+ source: context.source,
4534
+ transcriptPath: context.transcriptPath
4535
+ });
4179
4536
  });
4180
4537
  } catch (error) {
4181
- logger.error(
4182
- { error, eventType: type, adapter: this.name, sessionId },
4183
- "\u{1F4D6} Failed to publish event \u2014 swallowing to prevent daemon crash"
4184
- );
4538
+ if (error instanceof Error && error.name === "CircuitOpenError") {
4539
+ logger.warn(
4540
+ { error, eventType: type, adapter: this.name },
4541
+ "\u{1F4D6} Adapter emit blocked by open circuit \u2014 event dropped"
4542
+ );
4543
+ } else {
4544
+ logger.error(
4545
+ { error, eventType: type, adapter: this.name, sessionId },
4546
+ "\u{1F4D6} Failed to publish event \u2014 swallowing to prevent daemon crash"
4547
+ );
4548
+ }
4185
4549
  published = false;
4186
4550
  }
4187
4551
  if (published) {
@@ -5195,7 +5559,11 @@ var ClaudeCodeAdapter = class extends BaseAdapter {
5195
5559
  return;
5196
5560
  }
5197
5561
  case "SessionEnd": {
5198
- await this.emitStateChange("session.end", sharedData, context);
5562
+ const finalMessage = extractFinalMessageFromPayload(payload);
5563
+ await this.emitStateChange("session.end", {
5564
+ ...sharedData,
5565
+ finalMessage
5566
+ }, context);
5199
5567
  return;
5200
5568
  }
5201
5569
  case "UserPromptSubmit":
@@ -5212,12 +5580,22 @@ var ClaudeCodeAdapter = class extends BaseAdapter {
5212
5580
  return;
5213
5581
  }
5214
5582
  case "PreToolUse": {
5215
- await this.emitStateChange("agent.tool_call", sharedData, context);
5583
+ const toolCallName = extractToolNameFromPayload(payload);
5584
+ await this.emitStateChange("agent.tool_call", {
5585
+ ...sharedData,
5586
+ toolCallName
5587
+ }, context);
5216
5588
  return;
5217
5589
  }
5218
5590
  case "PostToolUse": {
5591
+ const toolCallName = extractToolNameFromPayload(payload);
5592
+ const toolResult = extractToolResultFromPayload(payload);
5219
5593
  const emittedType = isClaudeCodingTool(sharedData.toolName) ? "agent.coding" : "agent.tool_call";
5220
- await this.emitStateChange(emittedType, sharedData, context);
5594
+ await this.emitStateChange(emittedType, {
5595
+ ...sharedData,
5596
+ toolCallName,
5597
+ toolResult
5598
+ }, context);
5221
5599
  return;
5222
5600
  }
5223
5601
  case "PostToolUseFailure":
@@ -5419,21 +5797,50 @@ function extractClaudeTranscriptObservations(payload, transcriptPath) {
5419
5797
  };
5420
5798
  const observations = [];
5421
5799
  if (contentParts.some((part) => part.type === "thinking")) {
5800
+ const thinkingParts = contentParts.filter((part) => part.type === "thinking");
5801
+ const thinkingText = thinkingParts.map((part) => {
5802
+ const text = part.text;
5803
+ return typeof text === "string" ? text : void 0;
5804
+ }).filter((text) => text !== void 0).join("\n");
5422
5805
  observations.push({
5423
5806
  context: sharedContext,
5424
- data: sharedData,
5807
+ data: {
5808
+ ...sharedData,
5809
+ thinkingContent: thinkingText.length > 0 ? thinkingText : void 0
5810
+ },
5425
5811
  type: "agent.thinking"
5426
5812
  });
5427
5813
  }
5428
5814
  if (contentParts.some(
5429
5815
  (part) => part.type === "text" && typeof part.text === "string" && part.text.trim().length > 0
5430
5816
  )) {
5817
+ const messageTexts = contentParts.filter((part) => part.type === "text").map((part) => part.text).filter((text) => text.trim().length > 0);
5818
+ const messageContent = messageTexts.join("\n");
5431
5819
  observations.push({
5432
5820
  context: sharedContext,
5433
- data: sharedData,
5821
+ data: {
5822
+ ...sharedData,
5823
+ messageContent: messageContent.length > 0 ? messageContent : void 0
5824
+ },
5434
5825
  type: "agent.streaming"
5435
5826
  });
5436
5827
  }
5828
+ const toolUseParts = contentParts.filter(
5829
+ (part) => part.type === "tool_use" || part.type === "toolUse"
5830
+ );
5831
+ if (toolUseParts.length > 0) {
5832
+ const toolName = getString(toolUseParts[0], "name") ?? getString(toolUseParts[0], "tool");
5833
+ if (toolName) {
5834
+ observations.push({
5835
+ context: sharedContext,
5836
+ data: {
5837
+ ...sharedData,
5838
+ toolCallName: toolName
5839
+ },
5840
+ type: "agent.tool_call"
5841
+ });
5842
+ }
5843
+ }
5437
5844
  return observations;
5438
5845
  }
5439
5846
  function extractClaudeContentParts(payload) {
@@ -5556,6 +5963,51 @@ function getString(payload, key) {
5556
5963
  const value = payload[key];
5557
5964
  return typeof value === "string" && value.trim().length > 0 ? value : void 0;
5558
5965
  }
5966
+ function extractToolNameFromPayload(payload) {
5967
+ const directToolName = getString(payload, "tool_name") ?? getString(payload, "toolName");
5968
+ if (directToolName) {
5969
+ return directToolName;
5970
+ }
5971
+ const toolUse = getRecord(payload.tool_use) ?? getRecord(payload.toolUse);
5972
+ if (toolUse) {
5973
+ return getString(toolUse, "name") ?? getString(toolUse, "tool");
5974
+ }
5975
+ const toolInput = getRecord(payload.tool_input) ?? getRecord(payload.toolInput);
5976
+ if (toolInput) {
5977
+ return getString(toolInput, "tool_name") ?? getString(toolInput, "type");
5978
+ }
5979
+ return void 0;
5980
+ }
5981
+ function extractToolResultFromPayload(payload) {
5982
+ const directResult = getString(payload, "result") ?? getString(payload, "output");
5983
+ if (directResult) {
5984
+ return directResult;
5985
+ }
5986
+ const toolResult = getRecord(payload.tool_result) ?? getRecord(payload.toolResult);
5987
+ if (toolResult) {
5988
+ return getString(toolResult, "content") ?? getString(toolResult, "output");
5989
+ }
5990
+ const errorField = getString(payload, "error") ?? getString(payload, "error_message");
5991
+ if (errorField) {
5992
+ return errorField;
5993
+ }
5994
+ return void 0;
5995
+ }
5996
+ function extractFinalMessageFromPayload(payload) {
5997
+ const directMessage = getString(payload, "final_message") ?? getString(payload, "finalMessage") ?? getString(payload, "summary") ?? getString(payload, "completion_message");
5998
+ if (directMessage) {
5999
+ return directMessage;
6000
+ }
6001
+ const result = getString(payload, "result") ?? getString(payload, "output") ?? getString(payload, "message");
6002
+ if (result) {
6003
+ return result;
6004
+ }
6005
+ const stats = getRecord(payload.stats);
6006
+ if (stats) {
6007
+ return getString(stats, "summary") ?? getString(stats, "completion_summary");
6008
+ }
6009
+ return void 0;
6010
+ }
5559
6011
 
5560
6012
  // src/adapters/copilot-cli.ts
5561
6013
  var import_node_child_process5 = require("child_process");
@@ -10529,7 +10981,11 @@ var OpenCodeAdapter = class extends BaseAdapter {
10529
10981
  return;
10530
10982
  }
10531
10983
  case "session.deleted": {
10532
- await this.emitStateChange("session.end", sharedData, context);
10984
+ const finalMessage = extractOpenCodeFinalMessage(payload);
10985
+ await this.emitStateChange("session.end", {
10986
+ ...sharedData,
10987
+ finalMessage
10988
+ }, context);
10533
10989
  return;
10534
10990
  }
10535
10991
  case "session.error": {
@@ -10554,12 +11010,22 @@ var OpenCodeAdapter = class extends BaseAdapter {
10554
11010
  return;
10555
11011
  }
10556
11012
  case "tool.execute.before": {
10557
- await this.emitStateChange("agent.tool_call", sharedData, context);
11013
+ const toolCallName = extractOpenCodeToolName(payload);
11014
+ await this.emitStateChange("agent.tool_call", {
11015
+ ...sharedData,
11016
+ toolCallName
11017
+ }, context);
10558
11018
  return;
10559
11019
  }
10560
11020
  case "tool.execute.after": {
11021
+ const toolCallName = extractOpenCodeToolName(payload);
11022
+ const toolResult = extractOpenCodeToolResult(payload);
10561
11023
  const emittedType = isOpenCodeCodingTool(sharedData.toolName) ? "agent.coding" : "agent.tool_call";
10562
- await this.emitStateChange(emittedType, sharedData, context);
11024
+ await this.emitStateChange(emittedType, {
11025
+ ...sharedData,
11026
+ toolCallName,
11027
+ toolResult
11028
+ }, context);
10563
11029
  return;
10564
11030
  }
10565
11031
  default: {
@@ -10726,6 +11192,535 @@ function getString10(payload, key) {
10726
11192
  const value = payload[key];
10727
11193
  return typeof value === "string" && value.trim().length > 0 ? value : void 0;
10728
11194
  }
11195
+ function extractOpenCodeFinalMessage(payload) {
11196
+ const directMessage = getString10(payload, "final_message") ?? getString10(payload, "finalMessage") ?? getString10(payload, "summary") ?? getString10(payload, "completion_message");
11197
+ if (directMessage) {
11198
+ return directMessage;
11199
+ }
11200
+ const result = getString10(payload, "result") ?? getString10(payload, "output") ?? getString10(getRecord9(payload.properties), "result");
11201
+ if (result) {
11202
+ return result;
11203
+ }
11204
+ return void 0;
11205
+ }
11206
+ function extractOpenCodeToolResult(payload) {
11207
+ const directResult = getString10(payload, "result") ?? getString10(payload, "output") ?? getString10(payload, "toolResult");
11208
+ if (directResult) {
11209
+ return directResult;
11210
+ }
11211
+ const toolResult = getRecord9(payload.tool_result) ?? getRecord9(payload.toolResult);
11212
+ if (toolResult) {
11213
+ return getString10(toolResult, "content") ?? getString10(toolResult, "output");
11214
+ }
11215
+ const props = getRecord9(payload.properties);
11216
+ if (props) {
11217
+ const nestedResult = getRecord9(props.tool_result) ?? getRecord9(props.toolResult);
11218
+ if (nestedResult) {
11219
+ return getString10(nestedResult, "content") ?? getString10(nestedResult, "output");
11220
+ }
11221
+ }
11222
+ return void 0;
11223
+ }
11224
+
11225
+ // src/adapters/pi.ts
11226
+ var import_node_child_process14 = require("child_process");
11227
+ var import_node_path16 = require("path");
11228
+ var import_node_util14 = require("util");
11229
+ var execFileAsync = (0, import_node_util14.promisify)(import_node_child_process14.execFile);
11230
+ var PiAdapter = class extends BaseAdapter {
11231
+ displayName = "Pi (MiniMax)";
11232
+ name = "pi";
11233
+ strategies = [
11234
+ "process-detect",
11235
+ "api-client",
11236
+ "log-watch"
11237
+ ];
11238
+ apiPort = 7890;
11239
+ logPath;
11240
+ poller = null;
11241
+ activePiSessions = /* @__PURE__ */ new Map();
11242
+ lastCheckedTime = 0;
11243
+ constructor(options) {
11244
+ super(options);
11245
+ this.logPath = (0, import_node_path16.join)(
11246
+ options.homeDirectory ?? process.env.HOME ?? "",
11247
+ ".pi",
11248
+ "agent.log"
11249
+ );
11250
+ }
11251
+ start() {
11252
+ if (this.getStatus().running) {
11253
+ return Promise.resolve();
11254
+ }
11255
+ this.setRunning(true);
11256
+ this.startPolling();
11257
+ logger.info({ adapter: this.name }, "Pi adapter started");
11258
+ return Promise.resolve();
11259
+ }
11260
+ stop() {
11261
+ if (this.poller !== null) {
11262
+ clearInterval(this.poller);
11263
+ this.poller = null;
11264
+ }
11265
+ this.setRunning(false);
11266
+ logger.info({ adapter: this.name }, "Pi adapter stopped");
11267
+ return Promise.resolve();
11268
+ }
11269
+ async handleHook(payload) {
11270
+ const normalized = this.parseNormalizedHookPayload(payload);
11271
+ if (normalized === null) {
11272
+ return;
11273
+ }
11274
+ const context = {
11275
+ cwd: normalized.cwd,
11276
+ pid: normalized.pid,
11277
+ sessionId: normalized.sessionId,
11278
+ source: "pi-hook"
11279
+ };
11280
+ const eventType = this.mapEventType(normalized.type ?? "");
11281
+ const eventData = this.buildEventData(eventType, normalized);
11282
+ await this.emit(eventType, eventData, context);
11283
+ }
11284
+ startPolling() {
11285
+ this.poller = setInterval(() => {
11286
+ void this.pollPiActivity();
11287
+ }, 2e3);
11288
+ }
11289
+ async pollPiActivity() {
11290
+ const running = await this.detectPiInstance();
11291
+ if (!running) {
11292
+ for (const [sessionId, activity] of this.activePiSessions) {
11293
+ if (activity.state !== "idle") {
11294
+ activity.state = "idle";
11295
+ await this.emitIdle(sessionId);
11296
+ }
11297
+ }
11298
+ return;
11299
+ }
11300
+ try {
11301
+ const response = await fetch(
11302
+ `http://127.0.0.1:${this.apiPort}/api/status`,
11303
+ {
11304
+ signal: AbortSignal.timeout(500)
11305
+ }
11306
+ );
11307
+ if (response.ok) {
11308
+ const data = await response.json();
11309
+ await this.processPiApiResponse(data);
11310
+ }
11311
+ } catch {
11312
+ await this.checkMiniMaxApi();
11313
+ }
11314
+ }
11315
+ async detectPiInstance() {
11316
+ try {
11317
+ const result = await execFileAsync("pgrep", ["-l", "pi|minimax"]);
11318
+ if (result.stdout.includes("pi") || result.stdout.includes("minimax")) {
11319
+ return true;
11320
+ }
11321
+ } catch {
11322
+ }
11323
+ try {
11324
+ const response = await fetch(
11325
+ `http://127.0.0.1:${this.apiPort}/health`,
11326
+ {
11327
+ signal: AbortSignal.timeout(200)
11328
+ }
11329
+ );
11330
+ if (response.ok) {
11331
+ return true;
11332
+ }
11333
+ } catch {
11334
+ }
11335
+ return false;
11336
+ }
11337
+ async checkMiniMaxApi() {
11338
+ try {
11339
+ const response = await fetch("http://127.0.0.1:3000/api/agent/status", {
11340
+ signal: AbortSignal.timeout(500)
11341
+ });
11342
+ if (response.ok) {
11343
+ const data = await response.json();
11344
+ await this.processPiApiResponse(data);
11345
+ }
11346
+ } catch {
11347
+ }
11348
+ }
11349
+ async processPiApiResponse(data) {
11350
+ const rawSession = data.sessionId ?? data.project ?? "default";
11351
+ const sessionId = `pi:${rawSession.replace(/[^a-zA-Z0-9-_]/g, "-")}`;
11352
+ let activity = this.activePiSessions.get(sessionId);
11353
+ if (!activity) {
11354
+ activity = { sessionId, state: "idle" };
11355
+ this.activePiSessions.set(sessionId, activity);
11356
+ await this.emitSessionStart(sessionId, data);
11357
+ }
11358
+ const rawState = data.state ?? "idle";
11359
+ const state = rawState;
11360
+ if (state !== activity.state) {
11361
+ switch (state) {
11362
+ case "thinking": {
11363
+ const rawThinking = data.thinking;
11364
+ if (rawThinking) {
11365
+ await this.emitThinking(sessionId, rawThinking);
11366
+ }
11367
+ break;
11368
+ }
11369
+ case "tool": {
11370
+ const rawFilePath = data.filePath;
11371
+ const rawCommand = data.command;
11372
+ const rawToolName = data.toolName ?? "unknown";
11373
+ await this.emitToolCall(
11374
+ sessionId,
11375
+ {
11376
+ filePath: rawFilePath ?? "",
11377
+ command: rawCommand ?? ""
11378
+ },
11379
+ rawToolName
11380
+ );
11381
+ break;
11382
+ }
11383
+ case "output": {
11384
+ const rawOutput = data.output;
11385
+ if (rawOutput) {
11386
+ await this.emitOutput(sessionId, rawOutput);
11387
+ }
11388
+ break;
11389
+ }
11390
+ case "error": {
11391
+ const rawError = data.error ?? "Unknown error";
11392
+ await this.emitError(sessionId, rawError);
11393
+ break;
11394
+ }
11395
+ case "idle":
11396
+ await this.emitIdle(sessionId);
11397
+ break;
11398
+ }
11399
+ activity.state = state;
11400
+ }
11401
+ }
11402
+ async emitSessionStart(sessionId, data) {
11403
+ const rawProject = data.project ?? "pi-project";
11404
+ const rawModel = data.model ?? "minimax/moonshot";
11405
+ const eventData = {
11406
+ state: "session.start",
11407
+ project: rawProject,
11408
+ model: rawModel,
11409
+ raw: data
11410
+ };
11411
+ await this.emit("session.start", eventData, { sessionId });
11412
+ }
11413
+ async emitThinking(sessionId, content) {
11414
+ if (!content) return;
11415
+ const eventData = {
11416
+ state: "agent.thinking",
11417
+ thinkingContent: content
11418
+ };
11419
+ await this.emit("agent.thinking", eventData, { sessionId });
11420
+ }
11421
+ async emitToolCall(sessionId, toolInput, toolName) {
11422
+ const eventData = {
11423
+ state: "agent.tool_call",
11424
+ toolCallName: toolName,
11425
+ toolInput,
11426
+ activeFile: toolInput.filePath
11427
+ };
11428
+ await this.emit("agent.tool_call", eventData, { sessionId });
11429
+ }
11430
+ async emitOutput(sessionId, content) {
11431
+ if (!content) return;
11432
+ const eventData = {
11433
+ state: "agent.streaming",
11434
+ messageContent: content
11435
+ };
11436
+ await this.emit("agent.streaming", eventData, { sessionId });
11437
+ }
11438
+ async emitError(sessionId, errorMessage) {
11439
+ const eventData = {
11440
+ state: "agent.error",
11441
+ errorMessage,
11442
+ errorType: "api_error"
11443
+ };
11444
+ await this.emit("agent.error", eventData, { sessionId });
11445
+ }
11446
+ async emitIdle(sessionId) {
11447
+ const eventData = {
11448
+ state: "agent.idle"
11449
+ };
11450
+ await this.emit("agent.idle", eventData, { sessionId });
11451
+ }
11452
+ mapEventType(type) {
11453
+ const mapping = {
11454
+ "session.start": "session.start",
11455
+ "session.end": "session.end",
11456
+ "task.start": "task.start",
11457
+ "task.complete": "task.complete",
11458
+ thinking: "agent.thinking",
11459
+ tool: "agent.tool_call",
11460
+ coding: "agent.coding",
11461
+ output: "agent.streaming",
11462
+ message: "agent.streaming",
11463
+ ask: "agent.asking_user",
11464
+ error: "agent.error",
11465
+ idle: "agent.idle",
11466
+ compact: "agent.compact"
11467
+ };
11468
+ return mapping[type] ?? "agent.streaming";
11469
+ }
11470
+ buildEventData(eventType, payload) {
11471
+ const data = payload.data ?? {};
11472
+ return {
11473
+ state: eventType,
11474
+ project: data.project,
11475
+ activeFile: data.activeFile,
11476
+ model: data.model,
11477
+ toolInput: data.toolInput,
11478
+ toolCallName: data.toolCallName,
11479
+ thinkingContent: data.thinkingContent,
11480
+ messageContent: data.messageContent,
11481
+ finalMessage: data.finalMessage,
11482
+ toolResult: data.toolResult,
11483
+ errorMessage: data.errorMessage,
11484
+ errorType: data.errorType,
11485
+ raw: data.raw
11486
+ };
11487
+ }
11488
+ };
11489
+
11490
+ // src/adapters/zed.ts
11491
+ var import_promises15 = require("fs/promises");
11492
+ var import_node_path17 = require("path");
11493
+ var ZedAdapter = class extends BaseAdapter {
11494
+ displayName = "Zed AI";
11495
+ name = "zed";
11496
+ strategies = [
11497
+ "process-detect",
11498
+ "api-client"
11499
+ ];
11500
+ logPaths;
11501
+ apiPort = 9876;
11502
+ pollIntervalMs;
11503
+ poller = null;
11504
+ lastEventTime = 0;
11505
+ activeZedSessions = /* @__PURE__ */ new Map();
11506
+ constructor(options) {
11507
+ super(options);
11508
+ this.pollIntervalMs = 2e3;
11509
+ this.logPaths = [
11510
+ (0, import_node_path17.join)(options.homeDirectory ?? process.env.HOME ?? "", ".config", "zed", "logs", "agent.log"),
11511
+ "/tmp/zed-agent.log"
11512
+ ];
11513
+ }
11514
+ start() {
11515
+ if (this.getStatus().running) {
11516
+ return Promise.resolve();
11517
+ }
11518
+ this.setRunning(true);
11519
+ this.startPolling();
11520
+ logger.info({ adapter: this.name }, "Zed adapter started");
11521
+ return Promise.resolve();
11522
+ }
11523
+ stop() {
11524
+ if (this.poller !== null) {
11525
+ clearInterval(this.poller);
11526
+ this.poller = null;
11527
+ }
11528
+ this.setRunning(false);
11529
+ logger.info({ adapter: this.name }, "Zed adapter stopped");
11530
+ return Promise.resolve();
11531
+ }
11532
+ async handleHook(payload) {
11533
+ const normalized = this.parseNormalizedHookPayload(payload);
11534
+ if (normalized === null) {
11535
+ return;
11536
+ }
11537
+ const context = {
11538
+ cwd: normalized.cwd,
11539
+ pid: normalized.pid,
11540
+ sessionId: normalized.sessionId,
11541
+ source: "zed-hook"
11542
+ };
11543
+ const eventType = this.mapEventType(normalized.type ?? "");
11544
+ const eventData = this.buildEventData(eventType, normalized);
11545
+ await this.emit(eventType, eventData, context);
11546
+ }
11547
+ startPolling() {
11548
+ this.poller = setInterval(() => {
11549
+ void this.pollZedStatus();
11550
+ }, this.pollIntervalMs);
11551
+ }
11552
+ async pollZedStatus() {
11553
+ try {
11554
+ const response = await fetch(`http://127.0.0.1:${this.apiPort}/api/agent/status`, {
11555
+ signal: AbortSignal.timeout(1e3)
11556
+ });
11557
+ if (response.ok) {
11558
+ const data = await response.json();
11559
+ if (data.sessionId && typeof data.sessionId === "string") {
11560
+ const sessionId = `zed:${data.sessionId}`;
11561
+ if (!this.activeZedSessions.has(sessionId)) {
11562
+ this.activeZedSessions.set(sessionId, sessionId);
11563
+ await this.emitSessionStart(sessionId, data);
11564
+ }
11565
+ if (data.state === "thinking" && this.lastEventTime < Date.now() - 5e3) {
11566
+ const rawThinking = data.thinking;
11567
+ if (rawThinking) {
11568
+ await this.emitThinking(sessionId, rawThinking);
11569
+ }
11570
+ } else if (data.state === "tool" && data.toolName) {
11571
+ const rawFilePath = data.filePath;
11572
+ const rawCommand = data.command;
11573
+ const rawToolName = data.toolName ?? "unknown";
11574
+ await this.emitToolCall(
11575
+ sessionId,
11576
+ {
11577
+ filePath: rawFilePath ?? "",
11578
+ command: rawCommand ?? ""
11579
+ },
11580
+ rawToolName
11581
+ );
11582
+ } else if (data.state === "idle") {
11583
+ await this.emitIdle(sessionId);
11584
+ }
11585
+ }
11586
+ }
11587
+ } catch {
11588
+ await this.checkLogFiles();
11589
+ }
11590
+ }
11591
+ async checkLogFiles() {
11592
+ for (const logPath of this.logPaths) {
11593
+ try {
11594
+ const content = await (0, import_promises15.readFile)(logPath, "utf8");
11595
+ await this.parseLogContent(content);
11596
+ } catch {
11597
+ }
11598
+ }
11599
+ }
11600
+ async parseLogContent(content) {
11601
+ const lines = content.split("\n").filter((line) => line.trim());
11602
+ for (const line of lines) {
11603
+ if (this.lastEventTime > 0 && this.lastEventTime >= Date.now() - 2e3) {
11604
+ continue;
11605
+ }
11606
+ const event = this.extractZedEventFromLog(line);
11607
+ if (event) {
11608
+ await this.handleHook(event);
11609
+ this.lastEventTime = Date.now();
11610
+ }
11611
+ }
11612
+ }
11613
+ extractZedEventFromLog(line) {
11614
+ try {
11615
+ const parsed = JSON.parse(line);
11616
+ if (!parsed.type || typeof parsed.type !== "string") {
11617
+ return null;
11618
+ }
11619
+ const rawSessionId = parsed.sessionId;
11620
+ const rawWorkspace = parsed.workspace;
11621
+ const sessionId = rawSessionId ? rawSessionId : rawWorkspace ? `zed:${(0, import_node_path17.basename)(rawWorkspace)}` : `zed:${Date.now()}`;
11622
+ const rawCwd = parsed.cwd ?? rawWorkspace;
11623
+ return {
11624
+ type: parsed.type,
11625
+ sessionId,
11626
+ cwd: rawCwd,
11627
+ data: {
11628
+ project: parsed.project ?? rawWorkspace ? (0, import_node_path17.basename)(String(rawWorkspace)) : void 0,
11629
+ model: parsed.model,
11630
+ state: parsed.type,
11631
+ thinkingContent: parsed.thinking,
11632
+ toolCallName: parsed.toolName,
11633
+ toolInput: parsed.toolInput,
11634
+ messageContent: parsed.message ?? parsed.output,
11635
+ errorMessage: parsed.error,
11636
+ raw: parsed
11637
+ }
11638
+ };
11639
+ } catch {
11640
+ if (line.includes("[zed:agent]")) {
11641
+ const cleaned = line.replace(/^\[.*?\] \[.*?\] /, "");
11642
+ if (cleaned.includes("Thinking:")) {
11643
+ return { type: "thinking", thinkingContent: cleaned.replace("Thinking:", "").trim() };
11644
+ }
11645
+ if (cleaned.includes("Executing tool:")) {
11646
+ return { type: "tool", toolCallName: cleaned.replace("Executing tool:", "").trim() };
11647
+ }
11648
+ if (cleaned.includes("Error:")) {
11649
+ return { type: "error", errorMessage: cleaned.replace("Error:", "").trim() };
11650
+ }
11651
+ }
11652
+ return null;
11653
+ }
11654
+ }
11655
+ async emitSessionStart(sessionId, _data) {
11656
+ const eventData = {
11657
+ state: "session.start",
11658
+ project: sessionId.split(":")[1] ?? "unknown"
11659
+ };
11660
+ await this.emit("session.start", eventData, { sessionId });
11661
+ }
11662
+ async emitThinking(sessionId, content) {
11663
+ if (!content) return;
11664
+ const eventData = {
11665
+ state: "agent.thinking",
11666
+ thinkingContent: content
11667
+ };
11668
+ await this.emit("agent.thinking", eventData, { sessionId });
11669
+ this.lastEventTime = Date.now();
11670
+ }
11671
+ async emitToolCall(sessionId, toolInput, toolName) {
11672
+ const eventData = {
11673
+ state: "agent.tool_call",
11674
+ toolCallName: toolName,
11675
+ toolInput,
11676
+ activeFile: toolInput.filePath
11677
+ };
11678
+ await this.emit("agent.tool_call", eventData, { sessionId });
11679
+ this.lastEventTime = Date.now();
11680
+ }
11681
+ async emitIdle(sessionId) {
11682
+ const eventData = {
11683
+ state: "agent.idle"
11684
+ };
11685
+ await this.emit("agent.idle", eventData, { sessionId });
11686
+ }
11687
+ mapEventType(type) {
11688
+ const mapping = {
11689
+ "session.start": "session.start",
11690
+ "session.end": "session.end",
11691
+ "task.start": "task.start",
11692
+ "task.complete": "task.complete",
11693
+ thinking: "agent.thinking",
11694
+ tool: "agent.tool_call",
11695
+ coding: "agent.coding",
11696
+ output: "agent.streaming",
11697
+ message: "agent.streaming",
11698
+ ask: "agent.asking_user",
11699
+ error: "agent.error",
11700
+ idle: "agent.idle",
11701
+ compact: "agent.compact"
11702
+ };
11703
+ return mapping[type] ?? "agent.streaming";
11704
+ }
11705
+ buildEventData(eventType, payload) {
11706
+ const data = payload.data ?? {};
11707
+ return {
11708
+ state: eventType,
11709
+ project: data.project,
11710
+ activeFile: data.activeFile,
11711
+ model: data.model,
11712
+ toolInput: data.toolInput,
11713
+ toolCallName: data.toolCallName,
11714
+ thinkingContent: data.thinkingContent,
11715
+ messageContent: data.messageContent,
11716
+ finalMessage: data.finalMessage,
11717
+ toolResult: data.toolResult,
11718
+ errorMessage: data.errorMessage,
11719
+ errorType: data.errorType,
11720
+ raw: data.raw
11721
+ };
11722
+ }
11723
+ };
10729
11724
 
10730
11725
  // src/adapters/registry.ts
10731
11726
  var AdapterRegistry = class {
@@ -10810,7 +11805,9 @@ function createDefaultAdapters(options) {
10810
11805
  new KiloAdapter(options),
10811
11806
  new CodexAdapter(options),
10812
11807
  new OpenClawAdapter(options),
10813
- new OpenCodeAdapter(options)
11808
+ new OpenCodeAdapter(options),
11809
+ new PiAdapter(options),
11810
+ new ZedAdapter(options)
10814
11811
  ];
10815
11812
  }
10816
11813
 
@@ -10831,7 +11828,7 @@ function getSocketPath(aisnitchHomePath) {
10831
11828
  if (process.platform === "win32") {
10832
11829
  return "\\\\.\\pipe\\aisnitch.sock";
10833
11830
  }
10834
- return (0, import_node_path16.join)(aisnitchHomePath, "aisnitch.sock");
11831
+ return (0, import_node_path18.join)(aisnitchHomePath, "aisnitch.sock");
10835
11832
  }
10836
11833
  var Pipeline = class {
10837
11834
  eventBus = new EventBus();
@@ -11036,6 +12033,30 @@ var Pipeline = class {
11036
12033
  getEventBus() {
11037
12034
  return this.eventBus;
11038
12035
  }
12036
+ /**
12037
+ * Returns the adapter registry for graceful shutdown coordination.
12038
+ */
12039
+ getAdapterRegistry() {
12040
+ return this.adapterRegistry ?? void 0;
12041
+ }
12042
+ /**
12043
+ * Returns the HTTP receiver for graceful shutdown coordination.
12044
+ */
12045
+ getHttpReceiver() {
12046
+ return this.httpReceiver;
12047
+ }
12048
+ /**
12049
+ * Returns the UDS server for graceful shutdown coordination.
12050
+ */
12051
+ getUdsServer() {
12052
+ return this.udsServer;
12053
+ }
12054
+ /**
12055
+ * Returns the WebSocket server for graceful shutdown coordination.
12056
+ */
12057
+ getWsServer() {
12058
+ return this.wsServer;
12059
+ }
11039
12060
  getHealthSnapshot() {
11040
12061
  const status = this.getStatus();
11041
12062
  return {
@@ -11116,6 +12137,116 @@ var Pipeline = class {
11116
12137
  }
11117
12138
  };
11118
12139
 
12140
+ // src/core/timeout.ts
12141
+ var DEFAULT_TIMEOUTS = Object.freeze({
12142
+ /**
12143
+ * File read/write operations (JSONL transcripts, config files).
12144
+ * Default: 5 seconds
12145
+ */
12146
+ fileOperation: 5e3,
12147
+ /**
12148
+ * HTTP requests to health endpoint or external APIs.
12149
+ * Default: 30 seconds
12150
+ */
12151
+ httpRequest: 3e4,
12152
+ /**
12153
+ * Process detection commands (`pgrep`, `ps aux`).
12154
+ * Default: 3 seconds
12155
+ */
12156
+ processDetection: 3e3,
12157
+ /**
12158
+ * Adapter startup (file watchers, hook bridges, pollers).
12159
+ * Default: 10 seconds
12160
+ */
12161
+ adapterStartup: 1e4,
12162
+ /**
12163
+ * Adapter shutdown (graceful cleanup, watcher close).
12164
+ * Default: 5 seconds — after this, resources are force-closed
12165
+ */
12166
+ adapterShutdown: 5e3,
12167
+ /**
12168
+ * Daemon graceful shutdown (stop all components in order).
12169
+ * Default: 30 seconds
12170
+ */
12171
+ daemonShutdown: 3e4,
12172
+ /**
12173
+ * WebSocket connection establishment.
12174
+ * Default: 10 seconds
12175
+ */
12176
+ wsConnection: 1e4,
12177
+ /**
12178
+ * Overall pipeline start (all components).
12179
+ * Default: 15 seconds
12180
+ */
12181
+ pipelineStartup: 15e3
12182
+ });
12183
+
12184
+ // src/core/graceful-shutdown.ts
12185
+ async function withShutdownTimeout(fn, timeoutMs, component) {
12186
+ if (timeoutMs <= 0) {
12187
+ await fn();
12188
+ return;
12189
+ }
12190
+ const timeoutPromise = new Promise((resolve2) => {
12191
+ setTimeout(() => {
12192
+ resolve2("timed_out");
12193
+ }, timeoutMs).unref();
12194
+ });
12195
+ const result = await Promise.race([
12196
+ fn().then(() => "completed"),
12197
+ timeoutPromise
12198
+ ]);
12199
+ if (result === "timed_out") {
12200
+ logger.warn(
12201
+ { component, timeoutMs },
12202
+ `Graceful shutdown exceeded ${timeoutMs}ms timeout \u2014 forcing through`
12203
+ );
12204
+ }
12205
+ }
12206
+ async function shutdownInOrder(components, timeouts, label) {
12207
+ const getTimeout = (key) => {
12208
+ return timeouts[key] ?? DEFAULT_TIMEOUTS.daemonShutdown;
12209
+ };
12210
+ const stopSafely = async (key, fn) => {
12211
+ const timeoutMs = getTimeout(key);
12212
+ try {
12213
+ await withShutdownTimeout(fn, timeoutMs, `${label}.${key}`);
12214
+ } catch (error) {
12215
+ logger.warn(
12216
+ { error, key, label },
12217
+ `Error during shutdown of ${key} \u2014 continuing with remaining components`
12218
+ );
12219
+ }
12220
+ };
12221
+ if (components.cleanupFns) {
12222
+ for (const cleanupFn of components.cleanupFns) {
12223
+ try {
12224
+ await withShutdownTimeout(
12225
+ async () => {
12226
+ const result = cleanupFn();
12227
+ if (result instanceof Promise) {
12228
+ await result;
12229
+ }
12230
+ },
12231
+ 1e3,
12232
+ `${label}.cleanup`
12233
+ );
12234
+ } catch (error) {
12235
+ logger.warn({ error, label }, "Cleanup function failed");
12236
+ }
12237
+ }
12238
+ }
12239
+ if (components.eventBus) {
12240
+ components.eventBus.unsubscribeAll();
12241
+ }
12242
+ await stopSafely("wsServer", () => components.wsServer.stop());
12243
+ await stopSafely("udsServer", () => components.udsServer.stop());
12244
+ await stopSafely("httpReceiver", () => components.httpReceiver.stop());
12245
+ if (components.adapterRegistry) {
12246
+ await stopSafely("adapterRegistry", () => components.adapterRegistry.stopAll());
12247
+ }
12248
+ }
12249
+
11119
12250
  // src/tui/index.tsx
11120
12251
  var import_ink13 = require("ink");
11121
12252
  var import_fullscreen_ink = require("fullscreen-ink");
@@ -11319,9 +12450,11 @@ var TOOL_COLORS = {
11319
12450
  "openhands": "#facc15",
11320
12451
  "openclaw": "#ef4444",
11321
12452
  "opencode": "#10b981",
12453
+ "pi": "#1db954",
11322
12454
  "qwen-code": "#22c55e",
11323
12455
  "unknown": "#94a3b8",
11324
- "windsurf": "#a855f7"
12456
+ "windsurf": "#a855f7",
12457
+ "zed": "#e85d04"
11325
12458
  };
11326
12459
  var EVENT_COLORS = {
11327
12460
  "agent.asking_user": "#ef4444",
@@ -13075,10 +14208,10 @@ async function renderManagedTui(options) {
13075
14208
 
13076
14209
  // src/cli/pid.ts
13077
14210
  var import_node_fs3 = require("fs");
13078
- var import_promises15 = require("fs/promises");
14211
+ var import_promises16 = require("fs/promises");
13079
14212
  var import_node_net3 = require("net");
13080
14213
  var import_node_os4 = require("os");
13081
- var import_node_path17 = require("path");
14214
+ var import_node_path19 = require("path");
13082
14215
  var import_zod5 = require("zod");
13083
14216
  var DaemonStateSchema = import_zod5.z.strictObject({
13084
14217
  pid: import_zod5.z.number().int().positive(),
@@ -13093,14 +14226,14 @@ function getDefaultSocketPath(options) {
13093
14226
  if (process.platform === "win32") {
13094
14227
  return "\\\\.\\pipe\\aisnitch.sock";
13095
14228
  }
13096
- return (0, import_node_path17.join)(getAISnitchHomePath(options), "aisnitch.sock");
14229
+ return (0, import_node_path19.join)(getAISnitchHomePath(options), "aisnitch.sock");
13097
14230
  }
13098
14231
  async function cleanupSocketPathIfStale(socketPath) {
13099
14232
  if (process.platform === "win32") {
13100
14233
  return false;
13101
14234
  }
13102
14235
  try {
13103
- await (0, import_promises15.access)(socketPath, import_node_fs3.constants.F_OK);
14236
+ await (0, import_promises16.access)(socketPath, import_node_fs3.constants.F_OK);
13104
14237
  } catch (error) {
13105
14238
  if (error instanceof Error && "code" in error && error.code === "ENOENT") {
13106
14239
  return false;
@@ -13124,20 +14257,20 @@ async function cleanupSocketPathIfStale(socketPath) {
13124
14257
  if (!staleSocket) {
13125
14258
  return false;
13126
14259
  }
13127
- await (0, import_promises15.rm)(socketPath, { force: true });
14260
+ await (0, import_promises16.rm)(socketPath, { force: true });
13128
14261
  return true;
13129
14262
  }
13130
14263
  function getPidFilePath(options = {}) {
13131
- return (0, import_node_path17.join)(getAISnitchHomePath(options), "aisnitch.pid");
14264
+ return (0, import_node_path19.join)(getAISnitchHomePath(options), "aisnitch.pid");
13132
14265
  }
13133
14266
  function getDaemonStatePath(options = {}) {
13134
- return (0, import_node_path17.join)(getAISnitchHomePath(options), "daemon-state.json");
14267
+ return (0, import_node_path19.join)(getAISnitchHomePath(options), "daemon-state.json");
13135
14268
  }
13136
14269
  function getDaemonLogPath(options = {}) {
13137
- return (0, import_node_path17.join)(getAISnitchHomePath(options), "daemon.log");
14270
+ return (0, import_node_path19.join)(getAISnitchHomePath(options), "daemon.log");
13138
14271
  }
13139
14272
  function getLaunchAgentPath(options = {}) {
13140
- return (0, import_node_path17.join)(
14273
+ return (0, import_node_path19.join)(
13141
14274
  options.launchAgentHomeDirectory ?? (0, import_node_os4.homedir)(),
13142
14275
  "Library",
13143
14276
  "LaunchAgents",
@@ -13147,13 +14280,13 @@ function getLaunchAgentPath(options = {}) {
13147
14280
  async function writePid(pid, options = {}) {
13148
14281
  await ensureConfigDir(options);
13149
14282
  const pidFilePath = getPidFilePath(options);
13150
- await (0, import_promises15.writeFile)(pidFilePath, `${pid}
14283
+ await (0, import_promises16.writeFile)(pidFilePath, `${pid}
13151
14284
  `, "utf8");
13152
14285
  return pidFilePath;
13153
14286
  }
13154
14287
  async function readPid(options = {}) {
13155
14288
  try {
13156
- const rawPid = await (0, import_promises15.readFile)(getPidFilePath(options), "utf8");
14289
+ const rawPid = await (0, import_promises16.readFile)(getPidFilePath(options), "utf8");
13157
14290
  const parsedPid = Number.parseInt(rawPid.trim(), 10);
13158
14291
  if (!Number.isInteger(parsedPid) || parsedPid <= 0) {
13159
14292
  throw new Error("Invalid PID file contents.");
@@ -13167,13 +14300,13 @@ async function readPid(options = {}) {
13167
14300
  }
13168
14301
  }
13169
14302
  async function removePid(options = {}) {
13170
- await (0, import_promises15.rm)(getPidFilePath(options), { force: true });
14303
+ await (0, import_promises16.rm)(getPidFilePath(options), { force: true });
13171
14304
  }
13172
14305
  async function writeDaemonState(state, options = {}) {
13173
14306
  await ensureConfigDir(options);
13174
14307
  const daemonStatePath = getDaemonStatePath(options);
13175
14308
  const validatedState = DaemonStateSchema.parse(state);
13176
- await (0, import_promises15.writeFile)(
14309
+ await (0, import_promises16.writeFile)(
13177
14310
  daemonStatePath,
13178
14311
  `${JSON.stringify(validatedState, null, 2)}
13179
14312
  `,
@@ -13183,7 +14316,7 @@ async function writeDaemonState(state, options = {}) {
13183
14316
  }
13184
14317
  async function readDaemonState(options = {}) {
13185
14318
  try {
13186
- const rawJson = await (0, import_promises15.readFile)(getDaemonStatePath(options), "utf8");
14319
+ const rawJson = await (0, import_promises16.readFile)(getDaemonStatePath(options), "utf8");
13187
14320
  const parsedJson = JSON.parse(rawJson);
13188
14321
  return DaemonStateSchema.parse(parsedJson);
13189
14322
  } catch (error) {
@@ -13194,7 +14327,7 @@ async function readDaemonState(options = {}) {
13194
14327
  }
13195
14328
  }
13196
14329
  async function removeDaemonState(options = {}) {
13197
- await (0, import_promises15.rm)(getDaemonStatePath(options), { force: true });
14330
+ await (0, import_promises16.rm)(getDaemonStatePath(options), { force: true });
13198
14331
  }
13199
14332
  function isProcessRunning(pid) {
13200
14333
  try {
@@ -13230,13 +14363,13 @@ async function cleanupStaleDaemonFiles(options = {}) {
13230
14363
  }
13231
14364
  async function ensureLaunchAgentDir(options = {}) {
13232
14365
  const launchAgentPath = getLaunchAgentPath(options);
13233
- const directoryPath = (0, import_node_path17.dirname)(launchAgentPath);
13234
- await (0, import_promises15.mkdir)(directoryPath, { recursive: true });
14366
+ const directoryPath = (0, import_node_path19.dirname)(launchAgentPath);
14367
+ await (0, import_promises16.mkdir)(directoryPath, { recursive: true });
13235
14368
  return directoryPath;
13236
14369
  }
13237
14370
  async function getDaemonLogSize(options = {}) {
13238
14371
  try {
13239
- const logStats = await (0, import_promises15.stat)(getDaemonLogPath(options));
14372
+ const logStats = await (0, import_promises16.stat)(getDaemonLogPath(options));
13240
14373
  return logStats.size;
13241
14374
  } catch (error) {
13242
14375
  if (error instanceof Error && "code" in error && error.code === "ENOENT") {
@@ -13250,16 +14383,16 @@ function getEffectiveCliConfigPath(options = {}) {
13250
14383
  }
13251
14384
 
13252
14385
  // src/cli/auto-update.ts
13253
- var import_node_child_process14 = require("child_process");
13254
- var import_promises16 = require("fs/promises");
14386
+ var import_node_child_process15 = require("child_process");
13255
14387
  var import_promises17 = require("fs/promises");
13256
- var import_node_path18 = require("path");
14388
+ var import_promises18 = require("fs/promises");
14389
+ var import_node_path20 = require("path");
13257
14390
  var AUTO_UPDATE_STATE_FILE = "auto-update.json";
13258
14391
  var AUTO_UPDATE_LOG_FILE = "auto-update.log";
13259
14392
  function createAutoUpdateController(dependencies = {}) {
13260
14393
  const fetchImplementation = dependencies.fetch ?? globalThis.fetch;
13261
14394
  const now = dependencies.now ?? (() => /* @__PURE__ */ new Date());
13262
- const spawnImplementation = dependencies.spawn ?? import_node_child_process14.spawn;
14395
+ const spawnImplementation = dependencies.spawn ?? import_node_child_process15.spawn;
13263
14396
  return {
13264
14397
  runDetachedUpdate: async (options) => {
13265
14398
  const pathOptions = toPathOptions(options);
@@ -13267,8 +14400,8 @@ function createAutoUpdateController(dependencies = {}) {
13267
14400
  const command = resolveUpdateCommand(options.manager);
13268
14401
  const args = resolveUpdateArgs(options.manager);
13269
14402
  const aisnitchHomePath = getAISnitchHomePath(pathOptions);
13270
- await (0, import_promises16.mkdir)(aisnitchHomePath, { recursive: true });
13271
- const logFilePath = (0, import_node_path18.join)(aisnitchHomePath, AUTO_UPDATE_LOG_FILE);
14403
+ await (0, import_promises17.mkdir)(aisnitchHomePath, { recursive: true });
14404
+ const logFilePath = (0, import_node_path20.join)(aisnitchHomePath, AUTO_UPDATE_LOG_FILE);
13272
14405
  const startedAt = now().toISOString();
13273
14406
  await writeAutoUpdateState(
13274
14407
  {
@@ -13301,7 +14434,7 @@ function createAutoUpdateController(dependencies = {}) {
13301
14434
  });
13302
14435
  combinedLog += `[${now().toISOString()}] finished with code ${exitCode}
13303
14436
  `;
13304
- await (0, import_promises16.writeFile)(logFilePath, combinedLog, "utf8");
14437
+ await (0, import_promises17.writeFile)(logFilePath, combinedLog, "utf8");
13305
14438
  await writeAutoUpdateState(
13306
14439
  {
13307
14440
  attemptedVersion: options.latestVersion,
@@ -13392,7 +14525,7 @@ async function detectInstallManager(options) {
13392
14525
  }
13393
14526
  let resolvedCliPath = options.cliEntryPath;
13394
14527
  try {
13395
- resolvedCliPath = await (0, import_promises17.realpath)(options.cliEntryPath);
14528
+ resolvedCliPath = await (0, import_promises18.realpath)(options.cliEntryPath);
13396
14529
  } catch {
13397
14530
  }
13398
14531
  if (resolvedCliPath.includes("/Cellar/aisnitch/")) {
@@ -13456,7 +14589,7 @@ function resolveUpdateArgs(manager) {
13456
14589
  async function readAutoUpdateState(options) {
13457
14590
  const statePath = getAutoUpdateStatePath(options);
13458
14591
  try {
13459
- const rawJson = await (0, import_promises16.readFile)(statePath, "utf8");
14592
+ const rawJson = await (0, import_promises17.readFile)(statePath, "utf8");
13460
14593
  return JSON.parse(rawJson);
13461
14594
  } catch (error) {
13462
14595
  if (error instanceof Error && "code" in error && error.code === "ENOENT") {
@@ -13467,8 +14600,8 @@ async function readAutoUpdateState(options) {
13467
14600
  }
13468
14601
  async function writeAutoUpdateState(state, options) {
13469
14602
  const aisnitchHomePath = getAISnitchHomePath(options);
13470
- await (0, import_promises16.mkdir)(aisnitchHomePath, { recursive: true });
13471
- await (0, import_promises16.writeFile)(
14603
+ await (0, import_promises17.mkdir)(aisnitchHomePath, { recursive: true });
14604
+ await (0, import_promises17.writeFile)(
13472
14605
  getAutoUpdateStatePath(options),
13473
14606
  `${JSON.stringify(state, null, 2)}
13474
14607
  `,
@@ -13476,7 +14609,7 @@ async function writeAutoUpdateState(state, options) {
13476
14609
  );
13477
14610
  }
13478
14611
  function getAutoUpdateStatePath(options) {
13479
- return (0, import_node_path18.join)(getAISnitchHomePath(options), AUTO_UPDATE_STATE_FILE);
14612
+ return (0, import_node_path20.join)(getAISnitchHomePath(options), AUTO_UPDATE_STATE_FILE);
13480
14613
  }
13481
14614
  function toPathOptions(options) {
13482
14615
  return {
@@ -13667,7 +14800,7 @@ function isRecord13(value) {
13667
14800
  }
13668
14801
 
13669
14802
  // src/cli/runtime.ts
13670
- var execFile14 = (0, import_node_util14.promisify)(import_node_child_process15.execFile);
14803
+ var execFile15 = (0, import_node_util15.promisify)(import_node_child_process16.execFile);
13671
14804
  var DAEMON_READY_TIMEOUT_MS = 4e3;
13672
14805
  var DAEMON_READY_POLL_INTERVAL_MS = 100;
13673
14806
  var DAEMON_STOP_TIMEOUT_MS = 4e3;
@@ -13677,13 +14810,13 @@ function createCliRuntime(dependencies = {}) {
13677
14810
  const output = dependencies.output ?? createProcessOutput();
13678
14811
  const fetchImplementation = dependencies.fetch ?? globalThis.fetch;
13679
14812
  const renderManagedTuiImplementation = dependencies.renderManagedTui ?? renderManagedTui;
13680
- const spawnImplementation = dependencies.spawn ?? import_node_child_process15.spawn;
14813
+ const spawnImplementation = dependencies.spawn ?? import_node_child_process16.spawn;
13681
14814
  const autoUpdateController = createAutoUpdateController({
13682
14815
  fetch: fetchImplementation,
13683
14816
  spawn: spawnImplementation
13684
14817
  });
13685
14818
  const execFileImplementation = dependencies.execFile ?? (async (file, args) => {
13686
- return await execFile14(file, [...args], {
14819
+ return await execFile15(file, [...args], {
13687
14820
  encoding: "utf8"
13688
14821
  });
13689
14822
  });
@@ -13822,8 +14955,8 @@ function createCliRuntime(dependencies = {}) {
13822
14955
  if (logSize < DAEMON_LOG_MAX_BYTES) {
13823
14956
  return;
13824
14957
  }
13825
- await (0, import_promises18.rm)(backupPath, { force: true });
13826
- await (0, import_promises18.rename)(logFilePath, backupPath);
14958
+ await (0, import_promises19.rm)(backupPath, { force: true });
14959
+ await (0, import_promises19.rename)(logFilePath, backupPath);
13827
14960
  }
13828
14961
  async function waitForDaemonReady(pathOptions) {
13829
14962
  const deadline = Date.now() + DAEMON_READY_TIMEOUT_MS;
@@ -13858,7 +14991,7 @@ function createCliRuntime(dependencies = {}) {
13858
14991
  }
13859
14992
  async function readDaemonStartupFailure(pathOptions) {
13860
14993
  try {
13861
- const daemonLog = await (0, import_promises18.readFile)(getDaemonLogPath(pathOptions), "utf8");
14994
+ const daemonLog = await (0, import_promises19.readFile)(getDaemonLogPath(pathOptions), "utf8");
13862
14995
  const logLines = daemonLog.split(/\r?\n/u).map((line) => line.trim()).filter((line) => line.length > 0);
13863
14996
  const lastLine = logLines.at(-1);
13864
14997
  if (!lastLine) {
@@ -13956,22 +15089,35 @@ function createCliRuntime(dependencies = {}) {
13956
15089
  return;
13957
15090
  }
13958
15091
  shuttingDown = true;
13959
- try {
13960
- await pipeline.stop();
13961
- } finally {
13962
- if (daemonMode) {
13963
- const daemonPathOptions = toPathOptions2(options);
15092
+ const shutdownTimeouts = {
15093
+ adapterRegistry: DEFAULT_TIMEOUTS.adapterShutdown,
15094
+ httpReceiver: DEFAULT_TIMEOUTS.httpRequest,
15095
+ udsServer: DEFAULT_TIMEOUTS.fileOperation,
15096
+ wsServer: DEFAULT_TIMEOUTS.wsConnection,
15097
+ cleanupFns: 1e3
15098
+ };
15099
+ const components = {
15100
+ adapterRegistry: pipeline.getAdapterRegistry(),
15101
+ httpReceiver: pipeline.getHttpReceiver(),
15102
+ udsServer: pipeline.getUdsServer(),
15103
+ wsServer: pipeline.getWsServer(),
15104
+ eventBus: pipeline.getEventBus(),
15105
+ cleanupFns: daemonMode ? [async () => {
13964
15106
  await Promise.all([
13965
- removePid(daemonPathOptions),
13966
- removeDaemonState(daemonPathOptions)
15107
+ removePid(toPathOptions2(options)),
15108
+ removeDaemonState(toPathOptions2(options))
13967
15109
  ]);
13968
- }
13969
- }
13970
- if (!daemonMode) {
13971
- output.stdout(`AISnitch stopped after ${signal}.
15110
+ }] : []
15111
+ };
15112
+ try {
15113
+ await shutdownInOrder(components, shutdownTimeouts, "pipeline");
15114
+ } finally {
15115
+ if (!daemonMode) {
15116
+ output.stdout(`AISnitch stopped after ${signal}.
13972
15117
  `);
15118
+ }
15119
+ process.exit(exitCode);
13973
15120
  }
13974
- process.exit(exitCode);
13975
15121
  };
13976
15122
  if (daemonMode) {
13977
15123
  process.once("SIGTERM", () => {
@@ -14228,7 +15374,7 @@ function createCliRuntime(dependencies = {}) {
14228
15374
  }
14229
15375
  const { config } = await loadEffectiveConfig(options);
14230
15376
  setLoggerLevel(getForegroundSafeLogLevel(config.logLevel, false));
14231
- const ephemeralHomeDirectory = await (0, import_promises18.mkdtemp)((0, import_node_path19.join)((0, import_node_os5.tmpdir)(), "aisnitch-mock-"));
15377
+ const ephemeralHomeDirectory = await (0, import_promises19.mkdtemp)((0, import_node_path21.join)((0, import_node_os5.tmpdir)(), "aisnitch-mock-"));
14232
15378
  const ephemeralPipeline = new Pipeline();
14233
15379
  const status2 = await ephemeralPipeline.start({
14234
15380
  config: {
@@ -14267,7 +15413,7 @@ function createCliRuntime(dependencies = {}) {
14267
15413
  } finally {
14268
15414
  await Promise.resolve(monitorClose());
14269
15415
  await ephemeralPipeline.stop();
14270
- await (0, import_promises18.rm)(ephemeralHomeDirectory, { force: true, recursive: true });
15416
+ await (0, import_promises19.rm)(ephemeralHomeDirectory, { force: true, recursive: true });
14271
15417
  }
14272
15418
  }
14273
15419
  async function install(options) {
@@ -14289,7 +15435,7 @@ function createCliRuntime(dependencies = {}) {
14289
15435
  await execFileImplementation("launchctl", ["bootout", domainTarget, launchAgentPath]);
14290
15436
  } catch {
14291
15437
  }
14292
- await (0, import_promises18.writeFile)(launchAgentPath, plistContents, "utf8");
15438
+ await (0, import_promises19.writeFile)(launchAgentPath, plistContents, "utf8");
14293
15439
  await execFileImplementation("launchctl", ["bootstrap", domainTarget, launchAgentPath]);
14294
15440
  output.stdout(`AISnitch LaunchAgent installed at ${launchAgentPath}
14295
15441
  `);
@@ -14312,7 +15458,7 @@ function createCliRuntime(dependencies = {}) {
14312
15458
  } else {
14313
15459
  const { config } = await loadEffectiveConfig(options);
14314
15460
  setLoggerLevel(getForegroundSafeLogLevel(config.logLevel, false));
14315
- ephemeralHomeDirectory = await (0, import_promises18.mkdtemp)((0, import_node_path19.join)((0, import_node_os5.tmpdir)(), "aisnitch-wrap-"));
15461
+ ephemeralHomeDirectory = await (0, import_promises19.mkdtemp)((0, import_node_path21.join)((0, import_node_os5.tmpdir)(), "aisnitch-wrap-"));
14316
15462
  ephemeralPipeline = new Pipeline();
14317
15463
  await ephemeralPipeline.start({
14318
15464
  config: {
@@ -14367,7 +15513,7 @@ function createCliRuntime(dependencies = {}) {
14367
15513
  await ephemeralPipeline.stop();
14368
15514
  }
14369
15515
  if (ephemeralHomeDirectory !== null) {
14370
- await (0, import_promises18.rm)(ephemeralHomeDirectory, { force: true, recursive: true });
15516
+ await (0, import_promises19.rm)(ephemeralHomeDirectory, { force: true, recursive: true });
14371
15517
  }
14372
15518
  }
14373
15519
  process.exit(wrappedExitCode);
@@ -14387,7 +15533,7 @@ function createCliRuntime(dependencies = {}) {
14387
15533
  },
14388
15534
  pid: process.ppid > 1 ? process.ppid : void 0,
14389
15535
  source: "aisnitch://adapters/aider/notifications-command",
14390
- transcriptPath: (0, import_node_path19.join)(process.cwd(), ".aider.chat.history.md"),
15536
+ transcriptPath: (0, import_node_path21.join)(process.cwd(), ".aider.chat.history.md"),
14391
15537
  type: "agent.idle"
14392
15538
  }),
14393
15539
  headers: {
@@ -14409,7 +15555,7 @@ function createCliRuntime(dependencies = {}) {
14409
15555
  await execFileImplementation("launchctl", ["bootout", domainTarget, launchAgentPath]);
14410
15556
  } catch {
14411
15557
  }
14412
- await (0, import_promises18.rm)(launchAgentPath, { force: true });
15558
+ await (0, import_promises19.rm)(launchAgentPath, { force: true });
14413
15559
  output.stdout(`AISnitch LaunchAgent removed from ${launchAgentPath}
14414
15560
  `);
14415
15561
  }
@@ -14588,7 +15734,7 @@ function joinSocketPath(pathOptions) {
14588
15734
  }
14589
15735
  function resolveCliEntryPath() {
14590
15736
  const cliEntryPath = process.argv[1];
14591
- if (!cliEntryPath || (0, import_node_path19.basename)(cliEntryPath).length === 0) {
15737
+ if (!cliEntryPath || (0, import_node_path21.basename)(cliEntryPath).length === 0) {
14592
15738
  throw new Error("Unable to resolve the AISnitch CLI entry path.");
14593
15739
  }
14594
15740
  return cliEntryPath;