aisnitch 0.2.20 → 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.20";
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 = [
@@ -129,6 +131,11 @@ var ToolInputSchema = import_zod.z.strictObject({
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);
@@ -151,7 +158,13 @@ var EventDataSchema = import_zod.z.strictObject({
151
158
  pid: import_zod.z.number().int().positive().optional(),
152
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"),
@@ -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();
@@ -4401,7 +4416,7 @@ var UDSServer = class {
4401
4416
  };
4402
4417
 
4403
4418
  // src/core/engine/pipeline.ts
4404
- var import_node_path16 = require("path");
4419
+ var import_node_path18 = require("path");
4405
4420
  var import_zod4 = require("zod");
4406
4421
 
4407
4422
  // src/adapters/aider.ts
@@ -4508,20 +4523,29 @@ var BaseAdapter = class {
4508
4523
  });
4509
4524
  let published;
4510
4525
  try {
4511
- published = await this.publishEventImplementation(event, {
4512
- cwd: context.cwd,
4513
- env: context.env,
4514
- hookPayload: context.hookPayload,
4515
- pid: context.pid,
4516
- sessionId,
4517
- source: context.source,
4518
- 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
+ });
4519
4536
  });
4520
4537
  } catch (error) {
4521
- logger.error(
4522
- { error, eventType: type, adapter: this.name, sessionId },
4523
- "\u{1F4D6} Failed to publish event \u2014 swallowing to prevent daemon crash"
4524
- );
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
+ }
4525
4549
  published = false;
4526
4550
  }
4527
4551
  if (published) {
@@ -5535,7 +5559,11 @@ var ClaudeCodeAdapter = class extends BaseAdapter {
5535
5559
  return;
5536
5560
  }
5537
5561
  case "SessionEnd": {
5538
- await this.emitStateChange("session.end", sharedData, context);
5562
+ const finalMessage = extractFinalMessageFromPayload(payload);
5563
+ await this.emitStateChange("session.end", {
5564
+ ...sharedData,
5565
+ finalMessage
5566
+ }, context);
5539
5567
  return;
5540
5568
  }
5541
5569
  case "UserPromptSubmit":
@@ -5552,12 +5580,22 @@ var ClaudeCodeAdapter = class extends BaseAdapter {
5552
5580
  return;
5553
5581
  }
5554
5582
  case "PreToolUse": {
5555
- 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);
5556
5588
  return;
5557
5589
  }
5558
5590
  case "PostToolUse": {
5591
+ const toolCallName = extractToolNameFromPayload(payload);
5592
+ const toolResult = extractToolResultFromPayload(payload);
5559
5593
  const emittedType = isClaudeCodingTool(sharedData.toolName) ? "agent.coding" : "agent.tool_call";
5560
- await this.emitStateChange(emittedType, sharedData, context);
5594
+ await this.emitStateChange(emittedType, {
5595
+ ...sharedData,
5596
+ toolCallName,
5597
+ toolResult
5598
+ }, context);
5561
5599
  return;
5562
5600
  }
5563
5601
  case "PostToolUseFailure":
@@ -5759,21 +5797,50 @@ function extractClaudeTranscriptObservations(payload, transcriptPath) {
5759
5797
  };
5760
5798
  const observations = [];
5761
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");
5762
5805
  observations.push({
5763
5806
  context: sharedContext,
5764
- data: sharedData,
5807
+ data: {
5808
+ ...sharedData,
5809
+ thinkingContent: thinkingText.length > 0 ? thinkingText : void 0
5810
+ },
5765
5811
  type: "agent.thinking"
5766
5812
  });
5767
5813
  }
5768
5814
  if (contentParts.some(
5769
5815
  (part) => part.type === "text" && typeof part.text === "string" && part.text.trim().length > 0
5770
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");
5771
5819
  observations.push({
5772
5820
  context: sharedContext,
5773
- data: sharedData,
5821
+ data: {
5822
+ ...sharedData,
5823
+ messageContent: messageContent.length > 0 ? messageContent : void 0
5824
+ },
5774
5825
  type: "agent.streaming"
5775
5826
  });
5776
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
+ }
5777
5844
  return observations;
5778
5845
  }
5779
5846
  function extractClaudeContentParts(payload) {
@@ -5896,6 +5963,51 @@ function getString(payload, key) {
5896
5963
  const value = payload[key];
5897
5964
  return typeof value === "string" && value.trim().length > 0 ? value : void 0;
5898
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
+ }
5899
6011
 
5900
6012
  // src/adapters/copilot-cli.ts
5901
6013
  var import_node_child_process5 = require("child_process");
@@ -10869,7 +10981,11 @@ var OpenCodeAdapter = class extends BaseAdapter {
10869
10981
  return;
10870
10982
  }
10871
10983
  case "session.deleted": {
10872
- await this.emitStateChange("session.end", sharedData, context);
10984
+ const finalMessage = extractOpenCodeFinalMessage(payload);
10985
+ await this.emitStateChange("session.end", {
10986
+ ...sharedData,
10987
+ finalMessage
10988
+ }, context);
10873
10989
  return;
10874
10990
  }
10875
10991
  case "session.error": {
@@ -10894,12 +11010,22 @@ var OpenCodeAdapter = class extends BaseAdapter {
10894
11010
  return;
10895
11011
  }
10896
11012
  case "tool.execute.before": {
10897
- 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);
10898
11018
  return;
10899
11019
  }
10900
11020
  case "tool.execute.after": {
11021
+ const toolCallName = extractOpenCodeToolName(payload);
11022
+ const toolResult = extractOpenCodeToolResult(payload);
10901
11023
  const emittedType = isOpenCodeCodingTool(sharedData.toolName) ? "agent.coding" : "agent.tool_call";
10902
- await this.emitStateChange(emittedType, sharedData, context);
11024
+ await this.emitStateChange(emittedType, {
11025
+ ...sharedData,
11026
+ toolCallName,
11027
+ toolResult
11028
+ }, context);
10903
11029
  return;
10904
11030
  }
10905
11031
  default: {
@@ -11066,6 +11192,535 @@ function getString10(payload, key) {
11066
11192
  const value = payload[key];
11067
11193
  return typeof value === "string" && value.trim().length > 0 ? value : void 0;
11068
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
+ };
11069
11724
 
11070
11725
  // src/adapters/registry.ts
11071
11726
  var AdapterRegistry = class {
@@ -11150,7 +11805,9 @@ function createDefaultAdapters(options) {
11150
11805
  new KiloAdapter(options),
11151
11806
  new CodexAdapter(options),
11152
11807
  new OpenClawAdapter(options),
11153
- new OpenCodeAdapter(options)
11808
+ new OpenCodeAdapter(options),
11809
+ new PiAdapter(options),
11810
+ new ZedAdapter(options)
11154
11811
  ];
11155
11812
  }
11156
11813
 
@@ -11171,7 +11828,7 @@ function getSocketPath(aisnitchHomePath) {
11171
11828
  if (process.platform === "win32") {
11172
11829
  return "\\\\.\\pipe\\aisnitch.sock";
11173
11830
  }
11174
- return (0, import_node_path16.join)(aisnitchHomePath, "aisnitch.sock");
11831
+ return (0, import_node_path18.join)(aisnitchHomePath, "aisnitch.sock");
11175
11832
  }
11176
11833
  var Pipeline = class {
11177
11834
  eventBus = new EventBus();
@@ -11376,6 +12033,30 @@ var Pipeline = class {
11376
12033
  getEventBus() {
11377
12034
  return this.eventBus;
11378
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
+ }
11379
12060
  getHealthSnapshot() {
11380
12061
  const status = this.getStatus();
11381
12062
  return {
@@ -11500,6 +12181,72 @@ var DEFAULT_TIMEOUTS = Object.freeze({
11500
12181
  pipelineStartup: 15e3
11501
12182
  });
11502
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
+
11503
12250
  // src/tui/index.tsx
11504
12251
  var import_ink13 = require("ink");
11505
12252
  var import_fullscreen_ink = require("fullscreen-ink");
@@ -11703,9 +12450,11 @@ var TOOL_COLORS = {
11703
12450
  "openhands": "#facc15",
11704
12451
  "openclaw": "#ef4444",
11705
12452
  "opencode": "#10b981",
12453
+ "pi": "#1db954",
11706
12454
  "qwen-code": "#22c55e",
11707
12455
  "unknown": "#94a3b8",
11708
- "windsurf": "#a855f7"
12456
+ "windsurf": "#a855f7",
12457
+ "zed": "#e85d04"
11709
12458
  };
11710
12459
  var EVENT_COLORS = {
11711
12460
  "agent.asking_user": "#ef4444",
@@ -13459,10 +14208,10 @@ async function renderManagedTui(options) {
13459
14208
 
13460
14209
  // src/cli/pid.ts
13461
14210
  var import_node_fs3 = require("fs");
13462
- var import_promises15 = require("fs/promises");
14211
+ var import_promises16 = require("fs/promises");
13463
14212
  var import_node_net3 = require("net");
13464
14213
  var import_node_os4 = require("os");
13465
- var import_node_path17 = require("path");
14214
+ var import_node_path19 = require("path");
13466
14215
  var import_zod5 = require("zod");
13467
14216
  var DaemonStateSchema = import_zod5.z.strictObject({
13468
14217
  pid: import_zod5.z.number().int().positive(),
@@ -13477,14 +14226,14 @@ function getDefaultSocketPath(options) {
13477
14226
  if (process.platform === "win32") {
13478
14227
  return "\\\\.\\pipe\\aisnitch.sock";
13479
14228
  }
13480
- return (0, import_node_path17.join)(getAISnitchHomePath(options), "aisnitch.sock");
14229
+ return (0, import_node_path19.join)(getAISnitchHomePath(options), "aisnitch.sock");
13481
14230
  }
13482
14231
  async function cleanupSocketPathIfStale(socketPath) {
13483
14232
  if (process.platform === "win32") {
13484
14233
  return false;
13485
14234
  }
13486
14235
  try {
13487
- 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);
13488
14237
  } catch (error) {
13489
14238
  if (error instanceof Error && "code" in error && error.code === "ENOENT") {
13490
14239
  return false;
@@ -13508,20 +14257,20 @@ async function cleanupSocketPathIfStale(socketPath) {
13508
14257
  if (!staleSocket) {
13509
14258
  return false;
13510
14259
  }
13511
- await (0, import_promises15.rm)(socketPath, { force: true });
14260
+ await (0, import_promises16.rm)(socketPath, { force: true });
13512
14261
  return true;
13513
14262
  }
13514
14263
  function getPidFilePath(options = {}) {
13515
- return (0, import_node_path17.join)(getAISnitchHomePath(options), "aisnitch.pid");
14264
+ return (0, import_node_path19.join)(getAISnitchHomePath(options), "aisnitch.pid");
13516
14265
  }
13517
14266
  function getDaemonStatePath(options = {}) {
13518
- return (0, import_node_path17.join)(getAISnitchHomePath(options), "daemon-state.json");
14267
+ return (0, import_node_path19.join)(getAISnitchHomePath(options), "daemon-state.json");
13519
14268
  }
13520
14269
  function getDaemonLogPath(options = {}) {
13521
- return (0, import_node_path17.join)(getAISnitchHomePath(options), "daemon.log");
14270
+ return (0, import_node_path19.join)(getAISnitchHomePath(options), "daemon.log");
13522
14271
  }
13523
14272
  function getLaunchAgentPath(options = {}) {
13524
- return (0, import_node_path17.join)(
14273
+ return (0, import_node_path19.join)(
13525
14274
  options.launchAgentHomeDirectory ?? (0, import_node_os4.homedir)(),
13526
14275
  "Library",
13527
14276
  "LaunchAgents",
@@ -13531,13 +14280,13 @@ function getLaunchAgentPath(options = {}) {
13531
14280
  async function writePid(pid, options = {}) {
13532
14281
  await ensureConfigDir(options);
13533
14282
  const pidFilePath = getPidFilePath(options);
13534
- await (0, import_promises15.writeFile)(pidFilePath, `${pid}
14283
+ await (0, import_promises16.writeFile)(pidFilePath, `${pid}
13535
14284
  `, "utf8");
13536
14285
  return pidFilePath;
13537
14286
  }
13538
14287
  async function readPid(options = {}) {
13539
14288
  try {
13540
- const rawPid = await (0, import_promises15.readFile)(getPidFilePath(options), "utf8");
14289
+ const rawPid = await (0, import_promises16.readFile)(getPidFilePath(options), "utf8");
13541
14290
  const parsedPid = Number.parseInt(rawPid.trim(), 10);
13542
14291
  if (!Number.isInteger(parsedPid) || parsedPid <= 0) {
13543
14292
  throw new Error("Invalid PID file contents.");
@@ -13551,13 +14300,13 @@ async function readPid(options = {}) {
13551
14300
  }
13552
14301
  }
13553
14302
  async function removePid(options = {}) {
13554
- await (0, import_promises15.rm)(getPidFilePath(options), { force: true });
14303
+ await (0, import_promises16.rm)(getPidFilePath(options), { force: true });
13555
14304
  }
13556
14305
  async function writeDaemonState(state, options = {}) {
13557
14306
  await ensureConfigDir(options);
13558
14307
  const daemonStatePath = getDaemonStatePath(options);
13559
14308
  const validatedState = DaemonStateSchema.parse(state);
13560
- await (0, import_promises15.writeFile)(
14309
+ await (0, import_promises16.writeFile)(
13561
14310
  daemonStatePath,
13562
14311
  `${JSON.stringify(validatedState, null, 2)}
13563
14312
  `,
@@ -13567,7 +14316,7 @@ async function writeDaemonState(state, options = {}) {
13567
14316
  }
13568
14317
  async function readDaemonState(options = {}) {
13569
14318
  try {
13570
- const rawJson = await (0, import_promises15.readFile)(getDaemonStatePath(options), "utf8");
14319
+ const rawJson = await (0, import_promises16.readFile)(getDaemonStatePath(options), "utf8");
13571
14320
  const parsedJson = JSON.parse(rawJson);
13572
14321
  return DaemonStateSchema.parse(parsedJson);
13573
14322
  } catch (error) {
@@ -13578,7 +14327,7 @@ async function readDaemonState(options = {}) {
13578
14327
  }
13579
14328
  }
13580
14329
  async function removeDaemonState(options = {}) {
13581
- await (0, import_promises15.rm)(getDaemonStatePath(options), { force: true });
14330
+ await (0, import_promises16.rm)(getDaemonStatePath(options), { force: true });
13582
14331
  }
13583
14332
  function isProcessRunning(pid) {
13584
14333
  try {
@@ -13614,13 +14363,13 @@ async function cleanupStaleDaemonFiles(options = {}) {
13614
14363
  }
13615
14364
  async function ensureLaunchAgentDir(options = {}) {
13616
14365
  const launchAgentPath = getLaunchAgentPath(options);
13617
- const directoryPath = (0, import_node_path17.dirname)(launchAgentPath);
13618
- 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 });
13619
14368
  return directoryPath;
13620
14369
  }
13621
14370
  async function getDaemonLogSize(options = {}) {
13622
14371
  try {
13623
- const logStats = await (0, import_promises15.stat)(getDaemonLogPath(options));
14372
+ const logStats = await (0, import_promises16.stat)(getDaemonLogPath(options));
13624
14373
  return logStats.size;
13625
14374
  } catch (error) {
13626
14375
  if (error instanceof Error && "code" in error && error.code === "ENOENT") {
@@ -13634,16 +14383,16 @@ function getEffectiveCliConfigPath(options = {}) {
13634
14383
  }
13635
14384
 
13636
14385
  // src/cli/auto-update.ts
13637
- var import_node_child_process14 = require("child_process");
13638
- var import_promises16 = require("fs/promises");
14386
+ var import_node_child_process15 = require("child_process");
13639
14387
  var import_promises17 = require("fs/promises");
13640
- var import_node_path18 = require("path");
14388
+ var import_promises18 = require("fs/promises");
14389
+ var import_node_path20 = require("path");
13641
14390
  var AUTO_UPDATE_STATE_FILE = "auto-update.json";
13642
14391
  var AUTO_UPDATE_LOG_FILE = "auto-update.log";
13643
14392
  function createAutoUpdateController(dependencies = {}) {
13644
14393
  const fetchImplementation = dependencies.fetch ?? globalThis.fetch;
13645
14394
  const now = dependencies.now ?? (() => /* @__PURE__ */ new Date());
13646
- const spawnImplementation = dependencies.spawn ?? import_node_child_process14.spawn;
14395
+ const spawnImplementation = dependencies.spawn ?? import_node_child_process15.spawn;
13647
14396
  return {
13648
14397
  runDetachedUpdate: async (options) => {
13649
14398
  const pathOptions = toPathOptions(options);
@@ -13651,8 +14400,8 @@ function createAutoUpdateController(dependencies = {}) {
13651
14400
  const command = resolveUpdateCommand(options.manager);
13652
14401
  const args = resolveUpdateArgs(options.manager);
13653
14402
  const aisnitchHomePath = getAISnitchHomePath(pathOptions);
13654
- await (0, import_promises16.mkdir)(aisnitchHomePath, { recursive: true });
13655
- 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);
13656
14405
  const startedAt = now().toISOString();
13657
14406
  await writeAutoUpdateState(
13658
14407
  {
@@ -13685,7 +14434,7 @@ function createAutoUpdateController(dependencies = {}) {
13685
14434
  });
13686
14435
  combinedLog += `[${now().toISOString()}] finished with code ${exitCode}
13687
14436
  `;
13688
- await (0, import_promises16.writeFile)(logFilePath, combinedLog, "utf8");
14437
+ await (0, import_promises17.writeFile)(logFilePath, combinedLog, "utf8");
13689
14438
  await writeAutoUpdateState(
13690
14439
  {
13691
14440
  attemptedVersion: options.latestVersion,
@@ -13776,7 +14525,7 @@ async function detectInstallManager(options) {
13776
14525
  }
13777
14526
  let resolvedCliPath = options.cliEntryPath;
13778
14527
  try {
13779
- resolvedCliPath = await (0, import_promises17.realpath)(options.cliEntryPath);
14528
+ resolvedCliPath = await (0, import_promises18.realpath)(options.cliEntryPath);
13780
14529
  } catch {
13781
14530
  }
13782
14531
  if (resolvedCliPath.includes("/Cellar/aisnitch/")) {
@@ -13840,7 +14589,7 @@ function resolveUpdateArgs(manager) {
13840
14589
  async function readAutoUpdateState(options) {
13841
14590
  const statePath = getAutoUpdateStatePath(options);
13842
14591
  try {
13843
- const rawJson = await (0, import_promises16.readFile)(statePath, "utf8");
14592
+ const rawJson = await (0, import_promises17.readFile)(statePath, "utf8");
13844
14593
  return JSON.parse(rawJson);
13845
14594
  } catch (error) {
13846
14595
  if (error instanceof Error && "code" in error && error.code === "ENOENT") {
@@ -13851,8 +14600,8 @@ async function readAutoUpdateState(options) {
13851
14600
  }
13852
14601
  async function writeAutoUpdateState(state, options) {
13853
14602
  const aisnitchHomePath = getAISnitchHomePath(options);
13854
- await (0, import_promises16.mkdir)(aisnitchHomePath, { recursive: true });
13855
- await (0, import_promises16.writeFile)(
14603
+ await (0, import_promises17.mkdir)(aisnitchHomePath, { recursive: true });
14604
+ await (0, import_promises17.writeFile)(
13856
14605
  getAutoUpdateStatePath(options),
13857
14606
  `${JSON.stringify(state, null, 2)}
13858
14607
  `,
@@ -13860,7 +14609,7 @@ async function writeAutoUpdateState(state, options) {
13860
14609
  );
13861
14610
  }
13862
14611
  function getAutoUpdateStatePath(options) {
13863
- 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);
13864
14613
  }
13865
14614
  function toPathOptions(options) {
13866
14615
  return {
@@ -14051,7 +14800,7 @@ function isRecord13(value) {
14051
14800
  }
14052
14801
 
14053
14802
  // src/cli/runtime.ts
14054
- 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);
14055
14804
  var DAEMON_READY_TIMEOUT_MS = 4e3;
14056
14805
  var DAEMON_READY_POLL_INTERVAL_MS = 100;
14057
14806
  var DAEMON_STOP_TIMEOUT_MS = 4e3;
@@ -14061,13 +14810,13 @@ function createCliRuntime(dependencies = {}) {
14061
14810
  const output = dependencies.output ?? createProcessOutput();
14062
14811
  const fetchImplementation = dependencies.fetch ?? globalThis.fetch;
14063
14812
  const renderManagedTuiImplementation = dependencies.renderManagedTui ?? renderManagedTui;
14064
- const spawnImplementation = dependencies.spawn ?? import_node_child_process15.spawn;
14813
+ const spawnImplementation = dependencies.spawn ?? import_node_child_process16.spawn;
14065
14814
  const autoUpdateController = createAutoUpdateController({
14066
14815
  fetch: fetchImplementation,
14067
14816
  spawn: spawnImplementation
14068
14817
  });
14069
14818
  const execFileImplementation = dependencies.execFile ?? (async (file, args) => {
14070
- return await execFile14(file, [...args], {
14819
+ return await execFile15(file, [...args], {
14071
14820
  encoding: "utf8"
14072
14821
  });
14073
14822
  });
@@ -14206,8 +14955,8 @@ function createCliRuntime(dependencies = {}) {
14206
14955
  if (logSize < DAEMON_LOG_MAX_BYTES) {
14207
14956
  return;
14208
14957
  }
14209
- await (0, import_promises18.rm)(backupPath, { force: true });
14210
- await (0, import_promises18.rename)(logFilePath, backupPath);
14958
+ await (0, import_promises19.rm)(backupPath, { force: true });
14959
+ await (0, import_promises19.rename)(logFilePath, backupPath);
14211
14960
  }
14212
14961
  async function waitForDaemonReady(pathOptions) {
14213
14962
  const deadline = Date.now() + DAEMON_READY_TIMEOUT_MS;
@@ -14242,7 +14991,7 @@ function createCliRuntime(dependencies = {}) {
14242
14991
  }
14243
14992
  async function readDaemonStartupFailure(pathOptions) {
14244
14993
  try {
14245
- const daemonLog = await (0, import_promises18.readFile)(getDaemonLogPath(pathOptions), "utf8");
14994
+ const daemonLog = await (0, import_promises19.readFile)(getDaemonLogPath(pathOptions), "utf8");
14246
14995
  const logLines = daemonLog.split(/\r?\n/u).map((line) => line.trim()).filter((line) => line.length > 0);
14247
14996
  const lastLine = logLines.at(-1);
14248
14997
  if (!lastLine) {
@@ -14340,22 +15089,35 @@ function createCliRuntime(dependencies = {}) {
14340
15089
  return;
14341
15090
  }
14342
15091
  shuttingDown = true;
14343
- try {
14344
- await pipeline.stop();
14345
- } finally {
14346
- if (daemonMode) {
14347
- 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 () => {
14348
15106
  await Promise.all([
14349
- removePid(daemonPathOptions),
14350
- removeDaemonState(daemonPathOptions)
15107
+ removePid(toPathOptions2(options)),
15108
+ removeDaemonState(toPathOptions2(options))
14351
15109
  ]);
14352
- }
14353
- }
14354
- if (!daemonMode) {
14355
- 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}.
14356
15117
  `);
15118
+ }
15119
+ process.exit(exitCode);
14357
15120
  }
14358
- process.exit(exitCode);
14359
15121
  };
14360
15122
  if (daemonMode) {
14361
15123
  process.once("SIGTERM", () => {
@@ -14612,7 +15374,7 @@ function createCliRuntime(dependencies = {}) {
14612
15374
  }
14613
15375
  const { config } = await loadEffectiveConfig(options);
14614
15376
  setLoggerLevel(getForegroundSafeLogLevel(config.logLevel, false));
14615
- 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-"));
14616
15378
  const ephemeralPipeline = new Pipeline();
14617
15379
  const status2 = await ephemeralPipeline.start({
14618
15380
  config: {
@@ -14651,7 +15413,7 @@ function createCliRuntime(dependencies = {}) {
14651
15413
  } finally {
14652
15414
  await Promise.resolve(monitorClose());
14653
15415
  await ephemeralPipeline.stop();
14654
- await (0, import_promises18.rm)(ephemeralHomeDirectory, { force: true, recursive: true });
15416
+ await (0, import_promises19.rm)(ephemeralHomeDirectory, { force: true, recursive: true });
14655
15417
  }
14656
15418
  }
14657
15419
  async function install(options) {
@@ -14673,7 +15435,7 @@ function createCliRuntime(dependencies = {}) {
14673
15435
  await execFileImplementation("launchctl", ["bootout", domainTarget, launchAgentPath]);
14674
15436
  } catch {
14675
15437
  }
14676
- await (0, import_promises18.writeFile)(launchAgentPath, plistContents, "utf8");
15438
+ await (0, import_promises19.writeFile)(launchAgentPath, plistContents, "utf8");
14677
15439
  await execFileImplementation("launchctl", ["bootstrap", domainTarget, launchAgentPath]);
14678
15440
  output.stdout(`AISnitch LaunchAgent installed at ${launchAgentPath}
14679
15441
  `);
@@ -14696,7 +15458,7 @@ function createCliRuntime(dependencies = {}) {
14696
15458
  } else {
14697
15459
  const { config } = await loadEffectiveConfig(options);
14698
15460
  setLoggerLevel(getForegroundSafeLogLevel(config.logLevel, false));
14699
- 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-"));
14700
15462
  ephemeralPipeline = new Pipeline();
14701
15463
  await ephemeralPipeline.start({
14702
15464
  config: {
@@ -14751,7 +15513,7 @@ function createCliRuntime(dependencies = {}) {
14751
15513
  await ephemeralPipeline.stop();
14752
15514
  }
14753
15515
  if (ephemeralHomeDirectory !== null) {
14754
- await (0, import_promises18.rm)(ephemeralHomeDirectory, { force: true, recursive: true });
15516
+ await (0, import_promises19.rm)(ephemeralHomeDirectory, { force: true, recursive: true });
14755
15517
  }
14756
15518
  }
14757
15519
  process.exit(wrappedExitCode);
@@ -14771,7 +15533,7 @@ function createCliRuntime(dependencies = {}) {
14771
15533
  },
14772
15534
  pid: process.ppid > 1 ? process.ppid : void 0,
14773
15535
  source: "aisnitch://adapters/aider/notifications-command",
14774
- 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"),
14775
15537
  type: "agent.idle"
14776
15538
  }),
14777
15539
  headers: {
@@ -14793,7 +15555,7 @@ function createCliRuntime(dependencies = {}) {
14793
15555
  await execFileImplementation("launchctl", ["bootout", domainTarget, launchAgentPath]);
14794
15556
  } catch {
14795
15557
  }
14796
- await (0, import_promises18.rm)(launchAgentPath, { force: true });
15558
+ await (0, import_promises19.rm)(launchAgentPath, { force: true });
14797
15559
  output.stdout(`AISnitch LaunchAgent removed from ${launchAgentPath}
14798
15560
  `);
14799
15561
  }
@@ -14972,7 +15734,7 @@ function joinSocketPath(pathOptions) {
14972
15734
  }
14973
15735
  function resolveCliEntryPath() {
14974
15736
  const cliEntryPath = process.argv[1];
14975
- if (!cliEntryPath || (0, import_node_path19.basename)(cliEntryPath).length === 0) {
15737
+ if (!cliEntryPath || (0, import_node_path21.basename)(cliEntryPath).length === 0) {
14976
15738
  throw new Error("Unable to resolve the AISnitch CLI entry path.");
14977
15739
  }
14978
15740
  return cliEntryPath;