@cortexkit/aft-opencode 0.18.2 → 0.18.3

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.
@@ -7,6 +7,10 @@ export interface BgCompletion {
7
7
  duration_ms?: number;
8
8
  runtime_ms?: number;
9
9
  runtime?: number;
10
+ /** Tail of stdout+stderr captured at completion (≤300 bytes from Rust). */
11
+ output_preview?: string;
12
+ /** True when the captured tail is shorter than the actual output. */
13
+ output_truncated?: boolean;
10
14
  }
11
15
  type SessionBgState = {
12
16
  outstandingTaskIds: Set<string>;
@@ -16,6 +20,11 @@ type SessionBgState = {
16
20
  firstCompletionAt: number | null;
17
21
  scheduledFireAt: number | null;
18
22
  scheduledCompletionCount: number;
23
+ retryDelayMs: number | null;
24
+ unknownCompletions: Array<{
25
+ completion: BgCompletion;
26
+ receivedAt: number;
27
+ }>;
19
28
  lastSeenAt: number;
20
29
  };
21
30
  export declare const sessionBgStates: Map<string, SessionBgState>;
@@ -24,9 +33,13 @@ interface DrainContext {
24
33
  ctx: PluginContext;
25
34
  directory: string;
26
35
  sessionID: string;
36
+ isActive?: () => boolean;
27
37
  }
28
38
  export declare function trackBgTask(sessionID: string | undefined, taskId: string): void;
29
39
  export declare function ingestBgCompletions(sessionID: string | undefined, completions: unknown): BgCompletion[];
40
+ export declare function handlePushedBgCompletion(drainContext: DrainContext & {
41
+ client: unknown;
42
+ }, completion: unknown): Promise<void>;
30
43
  export declare function appendInTurnBgCompletions(drainContext: DrainContext, output: {
31
44
  output?: string;
32
45
  } | undefined): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"bg-notifications.d.ts","sourceRoot":"","sources":["../src/bg-notifications.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,KAAK,cAAc,GAAG;IACpB,kBAAkB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAChC,kBAAkB,EAAE,YAAY,EAAE,CAAC;IACnC,aAAa,EAAE,MAAM,CAAC,OAAO,GAAG,IAAI,CAAC;IACrC,iBAAiB,EAAE,OAAO,CAAC;IAC3B,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,wBAAwB,EAAE,MAAM,CAAC;IACjC,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAa,CAAC;AAGtE,eAAO,MAAM,4BAA4B,QAAiB,CAAC;AAM3D,UAAU,YAAY;IACpB,GAAG,EAAE,aAAa,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAQD,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAE/E;AAED,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,WAAW,EAAE,OAAO,GACnB,YAAY,EAAE,CAiBhB;AAED,wBAAsB,yBAAyB,CAC7C,YAAY,EAAE,YAAY,EAC1B,MAAM,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,GACtC,OAAO,CAAC,IAAI,CAAC,CAaf;AAED,wBAAsB,uBAAuB,CAC3C,YAAY,EAAE,YAAY,GAAG;IAAE,MAAM,EAAE,OAAO,CAAA;CAAE,GAC/C,OAAO,CAAC,IAAI,CAAC,CA0Bf;AAED,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAE/D;AAED,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,SAAS,YAAY,EAAE,GAAG,MAAM,CAGjF;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAcnE;AAED,wBAAgB,kCAAkC,IAAI,IAAI,CAKzD"}
1
+ {"version":3,"file":"bg-notifications.d.ts","sourceRoot":"","sources":["../src/bg-notifications.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,2EAA2E;IAC3E,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,qEAAqE;IACrE,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,KAAK,cAAc,GAAG;IACpB,kBAAkB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAChC,kBAAkB,EAAE,YAAY,EAAE,CAAC;IACnC,aAAa,EAAE,MAAM,CAAC,OAAO,GAAG,IAAI,CAAC;IACrC,iBAAiB,EAAE,OAAO,CAAC;IAC3B,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,wBAAwB,EAAE,MAAM,CAAC;IACjC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,kBAAkB,EAAE,KAAK,CAAC;QAAE,UAAU,EAAE,YAAY,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC5E,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAa,CAAC;AAGtE,eAAO,MAAM,4BAA4B,QAAiB,CAAC;AAQ3D,UAAU,YAAY;IACpB,GAAG,EAAE,aAAa,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,OAAO,CAAC;CAC1B;AASD,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAgB/E;AAED,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,WAAW,EAAE,OAAO,GACnB,YAAY,EAAE,CAoBhB;AAED,wBAAsB,wBAAwB,CAC5C,YAAY,EAAE,YAAY,GAAG;IAAE,MAAM,EAAE,OAAO,CAAA;CAAE,EAChD,UAAU,EAAE,OAAO,GAClB,OAAO,CAAC,IAAI,CAAC,CAGf;AAED,wBAAsB,yBAAyB,CAC7C,YAAY,EAAE,YAAY,EAC1B,MAAM,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,GACtC,OAAO,CAAC,IAAI,CAAC,CAaf;AAED,wBAAsB,uBAAuB,CAC3C,YAAY,EAAE,YAAY,GAAG;IAAE,MAAM,EAAE,OAAO,CAAA;CAAE,GAC/C,OAAO,CAAC,IAAI,CAAC,CAEf;AAgDD,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAE/D;AAED,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,SAAS,YAAY,EAAE,GAAG,MAAM,CASjF;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAcnE;AAED,wBAAgB,kCAAkC,IAAI,IAAI,CAKzD"}
package/dist/bridge.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import type { BgCompletion } from "./bg-notifications.js";
1
2
  /**
2
3
  * Compare two semver version strings (major.minor.patch plus pre-release).
3
4
  * Returns: negative if a < b, 0 if equal, positive if a > b.
@@ -20,6 +21,12 @@ export interface BridgeOptions {
20
21
  onVersionMismatch?: (binaryVersion: string, minVersion: string) => void;
21
22
  /** Called after the first successful configure returns user-visible warnings. */
22
23
  onConfigureWarnings?: (context: ConfigureWarningsContext) => void | Promise<void>;
24
+ /** Called for server-pushed background bash completions. */
25
+ onBashCompletion?: (completion: BashCompletedPayload, bridge: BinaryBridge) => void | Promise<void>;
26
+ }
27
+ export interface BashCompletedPayload extends BgCompletion {
28
+ type: "bash_completed";
29
+ session_id: string;
23
30
  }
24
31
  export interface BridgeRequestOptions {
25
32
  onProgress?: (chunk: {
@@ -28,6 +35,23 @@ export interface BridgeRequestOptions {
28
35
  }) => void;
29
36
  /** Per-call transport timeout in milliseconds. Defaults to the bridge-wide timeout. */
30
37
  transportTimeoutMs?: number;
38
+ /**
39
+ * Skip the "kill the child process on timeout" behavior for this request.
40
+ *
41
+ * The default (false) treats a transport-level timeout as evidence the bridge
42
+ * is wedged — Rust normally responds well within the budget, so silence past
43
+ * the deadline almost always means a stuck child. Killing forces a clean
44
+ * respawn on the next call.
45
+ *
46
+ * Some commands enforce their own timeouts on the Rust side (notably `bash`,
47
+ * which uses a watchdog thread to terminate the child shell and return a
48
+ * timeout response). For those, a transport timeout means the response was
49
+ * lost or queued behind something else — the bridge itself is still healthy
50
+ * and should keep its warm state (LSP servers, semantic index, callers
51
+ * cache, undo history). Pass `keepBridgeOnTimeout: true` to reject the
52
+ * request without tearing down the bridge.
53
+ */
54
+ keepBridgeOnTimeout?: boolean;
31
55
  }
32
56
  interface SendOptions extends BridgeRequestOptions {
33
57
  timeoutMs?: number;
@@ -60,12 +84,14 @@ export declare class BinaryBridge {
60
84
  private minVersion;
61
85
  private onVersionMismatch;
62
86
  private onConfigureWarnings;
87
+ private onBashCompletion;
63
88
  private restartResetTimer;
64
89
  constructor(binaryPath: string, cwd: string, options?: BridgeOptions, configOverrides?: Record<string, unknown>);
65
90
  /** Number of times the binary has been restarted after a crash. */
66
91
  get restartCount(): number;
67
92
  /** Whether the child process is currently alive. */
68
93
  isAlive(): boolean;
94
+ hasPendingRequests(): boolean;
69
95
  /**
70
96
  * Send a command to the binary and return the parsed response.
71
97
  * Lazy-spawns the binary on first call.
@@ -1 +1 @@
1
- {"version":3,"file":"bridge.d.ts","sourceRoot":"","sources":["../src/bridge.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"bridge.d.ts","sourceRoot":"","sources":["../src/bridge.ts"],"names":[],"mappings":"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"}
package/dist/config.d.ts CHANGED
@@ -11,6 +11,7 @@ export declare const LspServerSchema: z.ZodObject<{
11
11
  }, z.core.$strip>;
12
12
  export declare const AftConfigSchema: z.ZodObject<{
13
13
  format_on_edit: z.ZodOptional<z.ZodBoolean>;
14
+ formatter_timeout_secs: z.ZodOptional<z.ZodNumber>;
14
15
  validate_on_edit: z.ZodOptional<z.ZodEnum<{
15
16
  syntax: "syntax";
16
17
  full: "full";
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAmExB,eAAO,MAAM,eAAe;;;;;;;;;iBAE1B,CAAC;AA2CH,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBA2DjB,CAAC;AAEZ,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAExD,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAE9D,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,QAAQ,EAAE,OAAO,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,sBAAsB,CAAC,EAAE,OAAO,CAAC;CAClC;AAED,MAAM,WAAW,qBAAqB;IACpC,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,WAAW,CAAC,EAAE,kBAAkB,EAAE,CAAC;IACnC,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;CACzB;AAED,MAAM,WAAW,8BAA8B;IAC7C,yBAAyB,CAAC,EAAE,OAAO,CAAC;IACpC,0BAA0B,CAAC,EAAE,OAAO,CAAC;IACrC,4BAA4B,CAAC,EAAE,OAAO,CAAC;IACvC,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B;AAMD,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,SAAS,GAAG,qBAAqB,CAmDrF;AAED,wBAAgB,qCAAqC,CACnD,MAAM,EAAE,SAAS,GAChB,8BAA8B,CAehC;AAED,KAAK,MAAM,GAAG;IACZ,GAAG,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACjC,CAAC;AAiGF,wBAAgB,oBAAoB,CAClC,UAAU,EAAE,MAAM,EAClB,MAAM,GAAE,MAAsB,GAC7B;IAAE,QAAQ,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,CAwD1C;AAuTD;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,gBAAgB,EAAE,MAAM,GAAG,SAAS,CAiDjE"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAmExB,eAAO,MAAM,eAAe;;;;;;;;;iBAE1B,CAAC;AA2CH,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAkEjB,CAAC;AAEZ,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAExD,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAE9D,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,QAAQ,EAAE,OAAO,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,sBAAsB,CAAC,EAAE,OAAO,CAAC;CAClC;AAED,MAAM,WAAW,qBAAqB;IACpC,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,WAAW,CAAC,EAAE,kBAAkB,EAAE,CAAC;IACnC,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;CACzB;AAED,MAAM,WAAW,8BAA8B;IAC7C,yBAAyB,CAAC,EAAE,OAAO,CAAC;IACpC,0BAA0B,CAAC,EAAE,OAAO,CAAC;IACrC,4BAA4B,CAAC,EAAE,OAAO,CAAC;IACvC,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B;AAMD,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,SAAS,GAAG,qBAAqB,CAmDrF;AAED,wBAAgB,qCAAqC,CACnD,MAAM,EAAE,SAAS,GAChB,8BAA8B,CAehC;AAED,KAAK,MAAM,GAAG;IACZ,GAAG,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACjC,CAAC;AAiGF,wBAAgB,oBAAoB,CAClC,UAAU,EAAE,MAAM,EAClB,MAAM,GAAE,MAAsB,GAC7B;IAAE,QAAQ,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,CAwD1C;AAuTD;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,gBAAgB,EAAE,MAAM,GAAG,SAAS,CAiDjE"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAsJlD;;;;;;;;;;;;;;;;;;GAkBG;AACH,QAAA,MAAM,MAAM,EAAE,MAohBb,CAAC;AAEF,eAAe,MAAM,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAgKlD;;;;;;;;;;;;;;;;;;GAkBG;AACH,QAAA,MAAM,MAAM,EAAE,MAkiBb,CAAC;AAEF,eAAe,MAAM,CAAC"}
package/dist/index.js CHANGED
@@ -7813,7 +7813,8 @@ import * as fs from "fs";
7813
7813
  import * as os from "os";
7814
7814
  import * as path from "path";
7815
7815
  var TAG = "[aft-plugin]";
7816
- var logFile = path.join(os.tmpdir(), "aft-plugin.log");
7816
+ var isTestEnv = process.env.BUN_TEST === "1" || false;
7817
+ var logFile = path.join(os.tmpdir(), isTestEnv ? "aft-plugin-test.log" : "aft-plugin.log");
7817
7818
  var useStderr = process.env.AFT_LOG_STDERR === "1";
7818
7819
  var buffer = [];
7819
7820
  var flushTimer = null;
@@ -7871,6 +7872,9 @@ function warn(message, data) {
7871
7872
  function error(message, data) {
7872
7873
  write("ERROR", message, data);
7873
7874
  }
7875
+ function sessionLog(sessionId, message, data) {
7876
+ write("INFO", message, data, sessionId);
7877
+ }
7874
7878
  function sessionWarn(sessionId, message, data) {
7875
7879
  write("WARN", message, data, sessionId);
7876
7880
  }
@@ -7878,15 +7882,90 @@ function getLogFilePath() {
7878
7882
  return logFile;
7879
7883
  }
7880
7884
 
7885
+ // src/shared/last-user-model.ts
7886
+ var CACHE_TTL_MS = 5000;
7887
+ var CACHE_MAX_ENTRIES = 100;
7888
+ var cache = new Map;
7889
+ async function getLastUserModel(client, sessionId) {
7890
+ const now = Date.now();
7891
+ const cached = cache.get(sessionId);
7892
+ if (cached && cached.expiresAt > now) {
7893
+ cache.delete(sessionId);
7894
+ cache.set(sessionId, cached);
7895
+ return cached.model;
7896
+ }
7897
+ if (cached)
7898
+ cache.delete(sessionId);
7899
+ const c = client;
7900
+ const fetcher = c?.session?.messages;
7901
+ if (typeof fetcher !== "function") {
7902
+ return null;
7903
+ }
7904
+ let messages;
7905
+ try {
7906
+ const result = await fetcher({ path: { id: sessionId } });
7907
+ messages = result?.data ?? [];
7908
+ } catch {
7909
+ return null;
7910
+ }
7911
+ for (let i = messages.length - 1;i >= 0; i--) {
7912
+ const info = messages[i]?.info;
7913
+ if (info?.role !== "user")
7914
+ continue;
7915
+ if (isSyntheticIgnoredMessage(messages[i]))
7916
+ continue;
7917
+ const model = info.model;
7918
+ if (!model?.providerID || !model?.modelID)
7919
+ continue;
7920
+ const resolved = {
7921
+ providerID: model.providerID,
7922
+ modelID: model.modelID,
7923
+ ...model.variant ? { variant: model.variant } : {}
7924
+ };
7925
+ setCache(sessionId, resolved);
7926
+ return resolved;
7927
+ }
7928
+ return null;
7929
+ }
7930
+ function setCache(sessionId, model) {
7931
+ if (cache.has(sessionId))
7932
+ cache.delete(sessionId);
7933
+ cache.set(sessionId, { expiresAt: Date.now() + CACHE_TTL_MS, model });
7934
+ while (cache.size > CACHE_MAX_ENTRIES) {
7935
+ const oldest = cache.keys().next().value;
7936
+ if (typeof oldest !== "string")
7937
+ break;
7938
+ cache.delete(oldest);
7939
+ }
7940
+ }
7941
+ function isSyntheticIgnoredMessage(message) {
7942
+ const parts = message?.info?.parts ?? message?.parts;
7943
+ return Array.isArray(parts) && parts.length > 0 && parts.every((part) => part?.ignored === true);
7944
+ }
7945
+
7881
7946
  // src/bg-notifications.ts
7882
7947
  var sessionBgStates = new Map;
7883
7948
  var SESSION_BG_STATE_IDLE_TTL_MS = 60 * 60 * 1000;
7884
7949
  var DEBOUNCE_STEP_MS = 200;
7885
7950
  var DEBOUNCE_CAP_MS = 1000;
7951
+ var UNKNOWN_COMPLETION_TTL_MS = 5000;
7952
+ var UNKNOWN_COMPLETION_CAP = 32;
7886
7953
  var DEFAULT_SESSION_ID = "__default__";
7887
7954
  var LOG_PREFIX = "[aft-plugin] bg-notifications:";
7888
7955
  function trackBgTask(sessionID, taskId) {
7889
- stateFor(sessionID).outstandingTaskIds.add(taskId);
7956
+ const state = stateFor(sessionID);
7957
+ pruneUnknownCompletions(state, Date.now());
7958
+ const buffered = state.unknownCompletions.filter((entry) => entry.completion.task_id === taskId);
7959
+ state.unknownCompletions = state.unknownCompletions.filter((entry) => entry.completion.task_id !== taskId);
7960
+ if (buffered.length > 0) {
7961
+ for (const entry of buffered) {
7962
+ if (!state.pendingCompletions.some((pending) => pending.task_id === taskId)) {
7963
+ state.pendingCompletions.push(entry.completion);
7964
+ }
7965
+ }
7966
+ return;
7967
+ }
7968
+ state.outstandingTaskIds.add(taskId);
7890
7969
  }
7891
7970
  function ingestBgCompletions(sessionID, completions) {
7892
7971
  if (!Array.isArray(completions) || completions.length === 0)
@@ -7896,8 +7975,10 @@ function ingestBgCompletions(sessionID, completions) {
7896
7975
  for (const completion of completions) {
7897
7976
  if (!isBgCompletion(completion))
7898
7977
  continue;
7899
- if (!state.outstandingTaskIds.has(completion.task_id))
7978
+ if (!state.outstandingTaskIds.has(completion.task_id)) {
7979
+ bufferUnknownCompletion(state, completion);
7900
7980
  continue;
7981
+ }
7901
7982
  state.outstandingTaskIds.delete(completion.task_id);
7902
7983
  if (!state.pendingCompletions.some((pending) => pending.task_id === completion.task_id) && !accepted.some((pending) => pending.task_id === completion.task_id)) {
7903
7984
  accepted.push(completion);
@@ -7906,6 +7987,10 @@ function ingestBgCompletions(sessionID, completions) {
7906
7987
  state.pendingCompletions.push(...accepted);
7907
7988
  return accepted;
7908
7989
  }
7990
+ async function handlePushedBgCompletion(drainContext, completion) {
7991
+ ingestBgCompletions(drainContext.sessionID, [completion]);
7992
+ await triggerWakeIfPending(drainContext, true);
7993
+ }
7909
7994
  async function appendInTurnBgCompletions(drainContext, output) {
7910
7995
  if (!output)
7911
7996
  return;
@@ -7922,43 +8007,55 @@ async function appendInTurnBgCompletions(drainContext, output) {
7922
8007
  state.pendingCompletions = [];
7923
8008
  }
7924
8009
  async function handleIdleBgCompletions(drainContext) {
8010
+ await triggerWakeIfPending(drainContext, false);
8011
+ }
8012
+ async function triggerWakeIfPending(drainContext, skipDrain) {
7925
8013
  const state = stateFor(drainContext.sessionID);
7926
8014
  if (state.wakeFiredThisIdle)
7927
8015
  return;
7928
- if (state.outstandingTaskIds.size > 0) {
8016
+ if (drainContext.isActive?.())
8017
+ return;
8018
+ if (!skipDrain && state.outstandingTaskIds.size > 0) {
7929
8019
  await drainCompletions(drainContext);
7930
8020
  }
7931
8021
  if (state.pendingCompletions.length === 0)
7932
8022
  return;
7933
8023
  scheduleWake(state, async (reminder) => {
7934
- try {
7935
- const client = drainContext.client;
7936
- if (typeof client.session?.promptAsync !== "function") {
7937
- throw new Error("client.session.promptAsync is unavailable");
7938
- }
7939
- await client.session.promptAsync({
7940
- path: { id: drainContext.sessionID },
7941
- body: {
7942
- noReply: false,
7943
- parts: [{ type: "text", text: reminder }]
7944
- }
7945
- });
7946
- } catch (err) {
7947
- warn(`${LOG_PREFIX} wake send failed: ${err instanceof Error ? err.message : String(err)}`);
7948
- }
8024
+ const client = drainContext.client;
8025
+ if (typeof client.session?.promptAsync !== "function") {
8026
+ throw new Error("client.session.promptAsync is unavailable");
8027
+ }
8028
+ const lastModel = await getLastUserModel(client, drainContext.sessionID);
8029
+ const body = {
8030
+ noReply: false,
8031
+ parts: [{ type: "text", text: reminder }]
8032
+ };
8033
+ if (lastModel) {
8034
+ body.model = { providerID: lastModel.providerID, modelID: lastModel.modelID };
8035
+ if (lastModel.variant)
8036
+ body.variant = lastModel.variant;
8037
+ }
8038
+ await client.session.promptAsync({
8039
+ path: { id: drainContext.sessionID },
8040
+ body
8041
+ });
8042
+ }, (err) => {
8043
+ sessionWarn(drainContext.sessionID, `${LOG_PREFIX} wake send failed: ${err instanceof Error ? err.message : String(err)}`);
7949
8044
  });
7950
8045
  }
7951
8046
  function resetBgWake(sessionID) {
7952
8047
  stateFor(sessionID).wakeFiredThisIdle = false;
7953
8048
  }
7954
8049
  function formatSystemReminder(completions) {
7955
- const bullets = completions.map((completion) => `- ${formatCompletion(completion)}`).join(`
8050
+ const bullets = completions.map((completion) => formatCompletion(completion)).join(`
7956
8051
  `);
8052
+ const anyTruncated = completions.some((c) => c.output_truncated === true);
8053
+ const tail = anyTruncated ? `
8054
+
8055
+ For truncated tasks, use bash_status({ taskId: "..." }) to retrieve full output.` : "";
7957
8056
  return `<system-reminder>
7958
8057
  [BACKGROUND BASH COMPLETED]
7959
- ${bullets}
7960
-
7961
- Use bash_status({ task_id: "..." }) to retrieve full output.
8058
+ ${bullets}${tail}
7962
8059
  </system-reminder>`;
7963
8060
  }
7964
8061
  function extractSessionID(value) {
@@ -7984,15 +8081,15 @@ async function drainCompletions({ ctx, directory, sessionID }) {
7984
8081
  const bridge = ctx.pool.getAnyActiveBridge(directory) ?? ctx.pool.getBridge(directory);
7985
8082
  const response = await bridge.send("bash_drain_completions", { session_id: sessionID });
7986
8083
  if (response.success === false) {
7987
- warn(`${LOG_PREFIX} drain failed: ${String(response.message ?? "unknown error")}`);
8084
+ sessionWarn(sessionID, `${LOG_PREFIX} drain failed: ${String(response.message ?? "unknown error")}`);
7988
8085
  return;
7989
8086
  }
7990
8087
  ingestBgCompletions(sessionID, response.bg_completions);
7991
8088
  } catch (err) {
7992
- warn(`${LOG_PREFIX} drain failed: ${err instanceof Error ? err.message : String(err)}`);
8089
+ sessionWarn(sessionID, `${LOG_PREFIX} drain failed: ${err instanceof Error ? err.message : String(err)}`);
7993
8090
  }
7994
8091
  }
7995
- function scheduleWake(state, sendWake) {
8092
+ function scheduleWake(state, sendWake, onSendFailure) {
7996
8093
  const now = Date.now();
7997
8094
  if (state.debounceTimer && state.pendingCompletions.length <= state.scheduledCompletionCount) {
7998
8095
  return;
@@ -8007,16 +8104,24 @@ function scheduleWake(state, sendWake) {
8007
8104
  state.scheduledCompletionCount = state.pendingCompletions.length;
8008
8105
  if (state.debounceTimer)
8009
8106
  clearTimeout(state.debounceTimer);
8010
- const delay = Math.max(0, (state.scheduledFireAt ?? now) - now);
8107
+ const delay = state.retryDelayMs ?? Math.max(0, (state.scheduledFireAt ?? now) - now);
8011
8108
  state.debounceTimer = setTimeout(() => {
8012
- const reminder = formatSystemReminder(state.pendingCompletions);
8109
+ const pending = state.pendingCompletions;
8110
+ const reminder = formatSystemReminder(pending);
8013
8111
  state.pendingCompletions = [];
8014
8112
  state.debounceTimer = null;
8015
- state.wakeFiredThisIdle = true;
8016
8113
  state.firstCompletionAt = null;
8017
8114
  state.scheduledFireAt = null;
8018
8115
  state.scheduledCompletionCount = 0;
8019
- sendWake(reminder);
8116
+ sendWake(reminder).then(() => {
8117
+ state.retryDelayMs = null;
8118
+ state.wakeFiredThisIdle = true;
8119
+ }).catch((err) => {
8120
+ state.pendingCompletions = [...pending, ...state.pendingCompletions];
8121
+ state.retryDelayMs = Math.min((delay || DEBOUNCE_STEP_MS) * 2, DEBOUNCE_CAP_MS);
8122
+ onSendFailure(err);
8123
+ scheduleWake(state, sendWake, onSendFailure);
8124
+ });
8020
8125
  }, delay);
8021
8126
  state.debounceTimer.unref?.();
8022
8127
  }
@@ -8034,6 +8139,8 @@ function stateFor(sessionID) {
8034
8139
  firstCompletionAt: null,
8035
8140
  scheduledFireAt: null,
8036
8141
  scheduledCompletionCount: 0,
8142
+ retryDelayMs: null,
8143
+ unknownCompletions: [],
8037
8144
  lastSeenAt: now
8038
8145
  };
8039
8146
  sessionBgStates.set(key, state);
@@ -8054,6 +8161,18 @@ function cleanupIdleSessionStates(now) {
8054
8161
  sessionBgStates.delete(sessionID);
8055
8162
  }
8056
8163
  }
8164
+ function bufferUnknownCompletion(state, completion) {
8165
+ const now = Date.now();
8166
+ pruneUnknownCompletions(state, now);
8167
+ state.unknownCompletions = state.unknownCompletions.filter((entry) => entry.completion.task_id !== completion.task_id);
8168
+ state.unknownCompletions.push({ completion, receivedAt: now });
8169
+ if (state.unknownCompletions.length > UNKNOWN_COMPLETION_CAP) {
8170
+ state.unknownCompletions.splice(0, state.unknownCompletions.length - UNKNOWN_COMPLETION_CAP);
8171
+ }
8172
+ }
8173
+ function pruneUnknownCompletions(state, now) {
8174
+ state.unknownCompletions = state.unknownCompletions.filter((entry) => now - entry.receivedAt <= UNKNOWN_COMPLETION_TTL_MS);
8175
+ }
8057
8176
  function isBgCompletion(value) {
8058
8177
  if (!value || typeof value !== "object" || Array.isArray(value))
8059
8178
  return false;
@@ -8068,10 +8187,26 @@ ${reminder}` : reminder;
8068
8187
  function formatCompletion(completion) {
8069
8188
  const status = formatStatus(completion);
8070
8189
  const duration = formatDuration(completion);
8071
- return `task ${completion.task_id} (${status}${duration ? `, ${duration}` : ""}): ${completion.command}`;
8190
+ const header = `- task ${completion.task_id} (${status}${duration ? `, ${duration}` : ""})`;
8191
+ const previewBlock = formatOutputPreview(completion);
8192
+ return previewBlock ? `${header}
8193
+ ${previewBlock}` : header;
8194
+ }
8195
+ function formatOutputPreview(completion) {
8196
+ const ansiRegex = /\x1b\[[0-9;]*[a-zA-Z]/g;
8197
+ const raw = (completion.output_preview ?? "").replace(ansiRegex, "");
8198
+ if (!raw.trim())
8199
+ return "";
8200
+ const trimmed = raw.replace(/\n+$/, "");
8201
+ const ellipsis = completion.output_truncated ? "\u2026" : "";
8202
+ const indented = trimmed.split(`
8203
+ `).map((line) => ` ${line}`).join(`
8204
+ `);
8205
+ return ellipsis ? ` ${ellipsis}
8206
+ ${indented}` : indented;
8072
8207
  }
8073
8208
  function formatStatus(completion) {
8074
- if (completion.status === "timeout")
8209
+ if (completion.status === "timed_out" || completion.status === "timeout")
8075
8210
  return "timed out";
8076
8211
  if (completion.status === "killed")
8077
8212
  return "killed";
@@ -21693,6 +21828,7 @@ var ExperimentalConfigSchema = exports_external.object({
21693
21828
  });
21694
21829
  var AftConfigSchema = exports_external.object({
21695
21830
  format_on_edit: exports_external.boolean().optional(),
21831
+ formatter_timeout_secs: exports_external.number().int().min(1).max(600).optional(),
21696
21832
  validate_on_edit: exports_external.enum(["syntax", "full"]).optional(),
21697
21833
  formatter: exports_external.record(exports_external.string(), FormatterEnum).optional(),
21698
21834
  checker: exports_external.record(exports_external.string(), CheckerEnum).optional(),
@@ -24332,13 +24468,17 @@ async function getSessionMessages(client, sessionId) {
24332
24468
  async function sendIgnoredMessage(client, sessionId, text) {
24333
24469
  try {
24334
24470
  const c = client;
24335
- const promptInput = {
24336
- path: { id: sessionId },
24337
- body: {
24338
- noReply: true,
24339
- parts: [{ type: "text", text, ignored: true }]
24340
- }
24471
+ const lastModel = await getLastUserModel(client, sessionId);
24472
+ const body = {
24473
+ noReply: true,
24474
+ parts: [{ type: "text", text, ignored: true }]
24341
24475
  };
24476
+ if (lastModel) {
24477
+ body.model = { providerID: lastModel.providerID, modelID: lastModel.modelID };
24478
+ if (lastModel.variant)
24479
+ body.variant = lastModel.variant;
24480
+ }
24481
+ const promptInput = { path: { id: sessionId }, body };
24342
24482
  if (typeof c.session?.prompt === "function") {
24343
24483
  await Promise.resolve(c.session.prompt(promptInput));
24344
24484
  return true;
@@ -24348,7 +24488,7 @@ async function sendIgnoredMessage(client, sessionId, text) {
24348
24488
  return true;
24349
24489
  }
24350
24490
  } catch (err) {
24351
- log(`[aft-plugin] notification send failed: ${err instanceof Error ? err.message : String(err)}`);
24491
+ sessionLog(sessionId, `[aft-plugin] notification send failed: ${err instanceof Error ? err.message : String(err)}`);
24352
24492
  }
24353
24493
  return false;
24354
24494
  }
@@ -24374,7 +24514,7 @@ async function sendWarning(opts, message) {
24374
24514
  if (!sessionId)
24375
24515
  return;
24376
24516
  const text = `${WARNING_MARKER} ${message}`;
24377
- log(`[aft-plugin] sending warning to session ${sessionId}`);
24517
+ sessionLog(sessionId, `[aft-plugin] sending warning to session ${sessionId}`);
24378
24518
  await sendIgnoredMessage(opts.client, sessionId, text);
24379
24519
  }
24380
24520
  async function sendFeatureAnnouncement(opts, version2, features, storageDir) {
@@ -24397,7 +24537,7 @@ async function sendFeatureAnnouncement(opts, version2, features, storageDir) {
24397
24537
  return;
24398
24538
  const text = [`${FEATURE_MARKER} v${version2}:`, ...features.map((f) => ` \u2022 ${f}`)].join(`
24399
24539
  `);
24400
- log(`[aft-plugin] sending feature announcement for v${version2}`);
24540
+ sessionLog(sessionId, `[aft-plugin] sending feature announcement for v${version2}`);
24401
24541
  await sendIgnoredMessage(opts.client, sessionId, text);
24402
24542
  }
24403
24543
  if (storageDir) {
@@ -24515,7 +24655,7 @@ async function cleanupWarnings(opts) {
24515
24655
  }
24516
24656
  if (warningIds.length === 0)
24517
24657
  return;
24518
- log(`[aft-plugin] cleaning up ${warningIds.length} stale warning(s)`);
24658
+ sessionLog(sessionId, `[aft-plugin] cleaning up ${warningIds.length} stale warning(s)`);
24519
24659
  for (const id of warningIds) {
24520
24660
  await deleteMessage(effectiveServerUrl, sessionId, id);
24521
24661
  }
@@ -25060,6 +25200,7 @@ class BinaryBridge {
25060
25200
  minVersion;
25061
25201
  onVersionMismatch;
25062
25202
  onConfigureWarnings;
25203
+ onBashCompletion;
25063
25204
  restartResetTimer = null;
25064
25205
  constructor(binaryPath, cwd, options, configOverrides) {
25065
25206
  this.binaryPath = binaryPath;
@@ -25070,6 +25211,7 @@ class BinaryBridge {
25070
25211
  this.minVersion = options?.minVersion;
25071
25212
  this.onVersionMismatch = options?.onVersionMismatch;
25072
25213
  this.onConfigureWarnings = options?.onConfigureWarnings;
25214
+ this.onBashCompletion = options?.onBashCompletion;
25073
25215
  }
25074
25216
  get restartCount() {
25075
25217
  return this._restartCount;
@@ -25077,6 +25219,9 @@ class BinaryBridge {
25077
25219
  isAlive() {
25078
25220
  return this.process !== null && this.process.exitCode === null && !this.process.killed;
25079
25221
  }
25222
+ hasPendingRequests() {
25223
+ return this.pending.size > 0;
25224
+ }
25080
25225
  async send(command, params = {}, options) {
25081
25226
  if (this._shuttingDown) {
25082
25227
  throw new Error(`[aft-plugin] Bridge is shutting down, cannot send "${command}"`);
@@ -25130,17 +25275,21 @@ class BinaryBridge {
25130
25275
  `;
25131
25276
  const effectiveTimeoutMs = options?.transportTimeoutMs ?? options?.timeoutMs ?? this.timeoutMs;
25132
25277
  const requestSessionId = typeof params.session_id === "string" && params.session_id.length > 0 ? params.session_id : undefined;
25278
+ const keepBridgeOnTimeout = options?.keepBridgeOnTimeout === true;
25133
25279
  return new Promise((resolve4, reject) => {
25134
25280
  const timer = setTimeout(() => {
25135
25281
  this.pending.delete(id);
25136
- const timeoutMsg = `Request "${command}" (id=${id}) timed out after ${effectiveTimeoutMs}ms \u2014 restarting bridge`;
25282
+ const restartSuffix = keepBridgeOnTimeout ? "" : " \u2014 restarting bridge";
25283
+ const timeoutMsg = `Request "${command}" (id=${id}) timed out after ${effectiveTimeoutMs}ms${restartSuffix}`;
25137
25284
  if (requestSessionId) {
25138
25285
  sessionWarn(requestSessionId, timeoutMsg);
25139
25286
  } else {
25140
25287
  warn(timeoutMsg);
25141
25288
  }
25142
25289
  reject(new Error(`[aft-plugin] Request "${command}" (id=${id}) timed out after ${effectiveTimeoutMs}ms`));
25143
- this.handleTimeout();
25290
+ if (!keepBridgeOnTimeout) {
25291
+ this.handleTimeout();
25292
+ }
25144
25293
  }, effectiveTimeoutMs);
25145
25294
  this.pending.set(id, { resolve: resolve4, reject, timer, onProgress: options?.onProgress });
25146
25295
  if (!this.process?.stdin?.writable) {
@@ -25345,6 +25494,10 @@ class BinaryBridge {
25345
25494
  }
25346
25495
  continue;
25347
25496
  }
25497
+ if (response.type === "bash_completed") {
25498
+ this.onBashCompletion?.(response, this);
25499
+ continue;
25500
+ }
25348
25501
  const id = response.id;
25349
25502
  if (id && this.pending.has(id)) {
25350
25503
  const entry = this.pending.get(id);
@@ -25354,6 +25507,8 @@ class BinaryBridge {
25354
25507
  clearTimeout(entry.timer);
25355
25508
  this.scheduleRestartCountReset();
25356
25509
  entry.resolve(response);
25510
+ } else if (typeof response.type === "string") {
25511
+ log(`Ignoring unknown stdout push frame type: ${response.type}`);
25357
25512
  }
25358
25513
  } catch (_err) {
25359
25514
  warn(`Failed to parse stdout line: ${line}`);
@@ -25451,7 +25606,8 @@ class BridgePool {
25451
25606
  maxRestarts: options.maxRestarts,
25452
25607
  minVersion: options.minVersion,
25453
25608
  onVersionMismatch: options.onVersionMismatch,
25454
- onConfigureWarnings: options.onConfigureWarnings
25609
+ onConfigureWarnings: options.onConfigureWarnings,
25610
+ onBashCompletion: options.onBashCompletion
25455
25611
  };
25456
25612
  this.configOverrides = configOverrides;
25457
25613
  if (Number.isFinite(this.idleTimeoutMs)) {
@@ -25771,9 +25927,7 @@ class AftRpcServer {
25771
25927
  return;
25772
25928
  }
25773
25929
  const { token: _token, ...handlerParams } = params;
25774
- log(`RPC call: ${method} params=${JSON.stringify(handlerParams).slice(0, 200)}`);
25775
25930
  handler(handlerParams).then((result) => {
25776
- log(`RPC result: ${method} => ${JSON.stringify(result).slice(0, 200)}`);
25777
25931
  res.writeHead(200, { "Content-Type": "application/json" });
25778
25932
  res.end(JSON.stringify(result));
25779
25933
  }).catch((err) => {
@@ -26026,7 +26180,7 @@ import {
26026
26180
  import { isIP } from "net";
26027
26181
  import { join as join17 } from "path";
26028
26182
  var MAX_RESPONSE_BYTES = 10 * 1024 * 1024;
26029
- var CACHE_TTL_MS = 24 * 60 * 60 * 1000;
26183
+ var CACHE_TTL_MS2 = 24 * 60 * 60 * 1000;
26030
26184
  var FETCH_TIMEOUT_MS = 30000;
26031
26185
  var MAX_REDIRECTS = 5;
26032
26186
  function cacheDir(storageDir) {
@@ -26206,7 +26360,7 @@ async function fetchUrlToTempFile(url2, storageDir, options = {}) {
26206
26360
  const meta4 = JSON.parse(readFileSync8(metaFile, "utf8"));
26207
26361
  const age = Date.now() - meta4.fetchedAt;
26208
26362
  const cached2 = contentPath(storageDir, hash2, meta4.extension);
26209
- if (age < CACHE_TTL_MS && existsSync11(cached2)) {
26363
+ if (age < CACHE_TTL_MS2 && existsSync11(cached2)) {
26210
26364
  log(`URL cache hit: ${url2} (${Math.round(age / 1000)}s old)`);
26211
26365
  return cached2;
26212
26366
  }
@@ -26279,7 +26433,7 @@ function cleanupUrlCache(storageDir) {
26279
26433
  try {
26280
26434
  const meta3 = JSON.parse(readFileSync8(metaFile, "utf8"));
26281
26435
  const age = Date.now() - meta3.fetchedAt;
26282
- if (age > CACHE_TTL_MS) {
26436
+ if (age > CACHE_TTL_MS2) {
26283
26437
  const hash2 = entry.slice(0, -".meta.json".length);
26284
26438
  const content = contentPath(storageDir, hash2, meta3.extension);
26285
26439
  if (existsSync11(content))
@@ -27079,6 +27233,7 @@ function createBashTool(ctx) {
27079
27233
  permissions_requested: true
27080
27234
  }, callBridge, {
27081
27235
  transportTimeoutMs: bashTransportTimeoutMs(args.timeout),
27236
+ keepBridgeOnTimeout: true,
27082
27237
  onProgress: ({ text }) => {
27083
27238
  accumulatedOutput = preview(accumulatedOutput + text);
27084
27239
  metadata?.({ output: accumulatedOutput, description });
@@ -27140,6 +27295,49 @@ function createBashTool(ctx) {
27140
27295
  }
27141
27296
  };
27142
27297
  }
27298
+ function createBashStatusTool(ctx) {
27299
+ return {
27300
+ description: "Check the status and captured output of a background bash task spawned with bash({ background: true }). Returns status (running | completed | failed | killed | timed_out), exit code, duration, and a preview of captured output.",
27301
+ args: {
27302
+ taskId: z3.string().describe("Background task ID returned by bash({ background: true }), e.g. bgb-6b454047.")
27303
+ },
27304
+ execute: async (args, context) => {
27305
+ const data = await callBridge(ctx, context, "bash_status", {
27306
+ task_id: args.taskId
27307
+ });
27308
+ if (data.success === false) {
27309
+ throw new Error(data.message ?? "bash_status failed");
27310
+ }
27311
+ const status = data.status;
27312
+ const exit = typeof data.exit_code === "number" ? ` (exit ${data.exit_code})` : "";
27313
+ const dur = typeof data.duration_ms === "number" ? ` ${Math.round(data.duration_ms / 1000)}s` : "";
27314
+ let text = `Task ${args.taskId}: ${status}${exit}${dur}`;
27315
+ const preview = data.output_preview;
27316
+ if (preview && status !== "running") {
27317
+ text += `
27318
+ ${preview.slice(0, 2000)}`;
27319
+ }
27320
+ return text;
27321
+ }
27322
+ };
27323
+ }
27324
+ function createBashKillTool(ctx) {
27325
+ return {
27326
+ description: "Terminate a running background bash task spawned with bash({ background: true }). Returns confirmation of kill or an error if the task already finished.",
27327
+ args: {
27328
+ taskId: z3.string().describe("Background task ID returned by bash({ background: true }), e.g. bgb-6b454047.")
27329
+ },
27330
+ execute: async (args, context) => {
27331
+ const data = await callBridge(ctx, context, "bash_kill", {
27332
+ task_id: args.taskId
27333
+ });
27334
+ if (data.success === false) {
27335
+ throw new Error(data.message ?? "bash_kill failed");
27336
+ }
27337
+ return `Task ${args.taskId}: ${String(data.status ?? "killed")}`;
27338
+ }
27339
+ };
27340
+ }
27143
27341
  function bashTransportTimeoutMs(timeout) {
27144
27342
  const bashTimeout = timeout ?? DEFAULT_BASH_TIMEOUT_MS;
27145
27343
  return Math.max(30000, bashTimeout + BASH_TRANSPORT_TIMEOUT_OVERHEAD_MS);
@@ -27770,6 +27968,11 @@ function createEditTool(ctx, writeToolName = "write") {
27770
27968
  }
27771
27969
  }
27772
27970
  let result = JSON.stringify(data);
27971
+ const globSkipNote = formatGlobSkipReasonsNote(data.format_skip_reasons);
27972
+ if (globSkipNote)
27973
+ result += `
27974
+
27975
+ ${globSkipNote}`;
27773
27976
  if (!args.dryRun) {
27774
27977
  const diags = data.lsp_diagnostics;
27775
27978
  if (diags && diags.length > 0) {
@@ -27800,6 +28003,14 @@ Note: LSP server(s) exited during this edit: ${exitedServers.join(", ")}. Their
27800
28003
  }
27801
28004
  };
27802
28005
  }
28006
+ function formatGlobSkipReasonsNote(reasons) {
28007
+ if (!Array.isArray(reasons))
28008
+ return;
28009
+ const actionable = reasons.filter((reason) => typeof reason === "string").filter((reason) => ["formatter_not_installed", "formatter_excluded_path", "timeout", "error"].includes(reason));
28010
+ if (actionable.length === 0)
28011
+ return;
28012
+ return `Note: formatter skipped some glob edit result file(s): ${[...new Set(actionable)].sort().join(", ")}. See per-file format_skipped_reason values for details.`;
28013
+ }
27803
28014
  var APPLY_PATCH_DESCRIPTION = `Use the \`apply_patch\` tool to edit files. Your patch language is a stripped\u2011down, file\u2011oriented diff format designed to be easy to parse and safe to apply. You can think of it as a high\u2011level envelope:
27804
28015
 
27805
28016
  *** Begin Patch
@@ -28051,8 +28262,8 @@ ${diagLines}`);
28051
28262
  const summary = partial2 ? `Patch partially applied \u2014 ${hunks.length - failures.length} of ${hunks.length} hunk(s) succeeded. Failed: ${failures.join(", ")}. Successful changes are kept; use \`aft_safety\` to revert if you want to abort.` : `Patch failed \u2014 none of the ${hunks.length} hunk(s) applied: ${failures.join(", ")}.`;
28052
28263
  results.push(summary);
28053
28264
  if (!partial2) {
28054
- return results.join(`
28055
- `);
28265
+ throw new Error(results.join(`
28266
+ `));
28056
28267
  }
28057
28268
  }
28058
28269
  const callID = getCallID2(context);
@@ -28158,6 +28369,8 @@ function createMoveTool(ctx) {
28158
28369
  function hoistedTools(ctx) {
28159
28370
  return {
28160
28371
  bash: createBashTool(ctx),
28372
+ bash_status: createBashStatusTool(ctx),
28373
+ bash_kill: createBashKillTool(ctx),
28161
28374
  read: createReadTool(ctx),
28162
28375
  write: createWriteTool(ctx, "edit"),
28163
28376
  edit: createEditTool(ctx, "write"),
@@ -28622,7 +28835,7 @@ var LSP_SYMBOL_KIND_MAP = {
28622
28835
  12: "function",
28623
28836
  23: "struct"
28624
28837
  };
28625
- async function queryLspHints(client, symbolName, directory) {
28838
+ async function queryLspHints(client, symbolName, directory, sessionId) {
28626
28839
  try {
28627
28840
  const statusResult = await client.lsp.status();
28628
28841
  const servers = statusResult.data;
@@ -28664,7 +28877,7 @@ async function queryLspHints(client, symbolName, directory) {
28664
28877
  }
28665
28878
  return { symbols: hints };
28666
28879
  } catch (err) {
28667
- warn(`LSP query failed for "${symbolName}": ${err.message}`);
28880
+ sessionWarn(sessionId, `LSP query failed for "${symbolName}": ${err.message}`);
28668
28881
  return;
28669
28882
  }
28670
28883
  }
@@ -28753,7 +28966,7 @@ function refactoringTools(ctx) {
28753
28966
  params.call_site_line = Number(args.callSiteLine);
28754
28967
  break;
28755
28968
  }
28756
- const hints = await queryLspHints(ctx.client, args.symbol ?? args.name);
28969
+ const hints = await queryLspHints(ctx.client, args.symbol ?? args.name, undefined, context.sessionID);
28757
28970
  if (hints)
28758
28971
  params.lsp_hints = hints;
28759
28972
  const response = await callBridge(ctx, context, commandMap[op], params);
@@ -29129,13 +29342,17 @@ function coerceConfigureWarnings(warnings) {
29129
29342
  }
29130
29343
  async function sendIgnoredMessage2(client, sessionID, text) {
29131
29344
  const typedClient = client;
29132
- const promptInput = {
29133
- path: { id: sessionID },
29134
- body: {
29135
- noReply: true,
29136
- parts: [{ type: "text", text, ignored: true }]
29137
- }
29345
+ const lastModel = await getLastUserModel(client, sessionID);
29346
+ const body = {
29347
+ noReply: true,
29348
+ parts: [{ type: "text", text, ignored: true }]
29138
29349
  };
29350
+ if (lastModel) {
29351
+ body.model = { providerID: lastModel.providerID, modelID: lastModel.modelID };
29352
+ if (lastModel.variant)
29353
+ body.variant = lastModel.variant;
29354
+ }
29355
+ const promptInput = { path: { id: sessionID }, body };
29139
29356
  if (typeof typedClient.session?.prompt === "function") {
29140
29357
  await Promise.resolve(typedClient.session.prompt(promptInput));
29141
29358
  return;
@@ -29171,6 +29388,8 @@ var plugin = async (input) => {
29171
29388
  const configOverrides = {};
29172
29389
  if (aftConfig.format_on_edit !== undefined)
29173
29390
  configOverrides.format_on_edit = aftConfig.format_on_edit;
29391
+ if (aftConfig.formatter_timeout_secs !== undefined)
29392
+ configOverrides.formatter_timeout_secs = aftConfig.formatter_timeout_secs;
29174
29393
  if (aftConfig.validate_on_edit !== undefined)
29175
29394
  configOverrides.validate_on_edit = aftConfig.validate_on_edit;
29176
29395
  if (aftConfig.formatter !== undefined)
@@ -29306,6 +29525,15 @@ ${lines}
29306
29525
  pluginVersion: PLUGIN_VERSION,
29307
29526
  projectRoot
29308
29527
  }, validWarnings);
29528
+ },
29529
+ onBashCompletion: (completion, bridge) => {
29530
+ handlePushedBgCompletion({
29531
+ ctx,
29532
+ directory: input.directory,
29533
+ sessionID: completion.session_id,
29534
+ client: input.client,
29535
+ isActive: () => bridge.hasPendingRequests()
29536
+ }, completion);
29309
29537
  }
29310
29538
  }, configOverrides);
29311
29539
  const ctx = {
package/dist/logger.d.ts CHANGED
@@ -7,8 +7,8 @@ export declare function error(message: string, data?: unknown): void;
7
7
  * a session's tool call). Bridge-lifecycle logs (spawn, version, idle) are
8
8
  * project-scoped, not session-scoped — use `log`/`warn`/`error` for those.
9
9
  */
10
- export declare function sessionLog(sessionId: string, message: string, data?: unknown): void;
11
- export declare function sessionWarn(sessionId: string, message: string, data?: unknown): void;
12
- export declare function sessionError(sessionId: string, message: string, data?: unknown): void;
10
+ export declare function sessionLog(sessionId: string | undefined, message: string, data?: unknown): void;
11
+ export declare function sessionWarn(sessionId: string | undefined, message: string, data?: unknown): void;
12
+ export declare function sessionError(sessionId: string | undefined, message: string, data?: unknown): void;
13
13
  export declare function getLogFilePath(): string;
14
14
  //# sourceMappingURL=logger.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAkEA,wBAAgB,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,IAAI,CAEzD;AAED,wBAAgB,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,IAAI,CAE1D;AAED,wBAAgB,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,IAAI,CAE3D;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,IAAI,CAEnF;AAED,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,IAAI,CAEpF;AAED,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,IAAI,CAErF;AAED,wBAAgB,cAAc,IAAI,MAAM,CAEvC"}
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAuEA,wBAAgB,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,IAAI,CAEzD;AAED,wBAAgB,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,IAAI,CAE1D;AAED,wBAAgB,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,IAAI,CAE3D;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,IAAI,CAE/F;AAED,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,IAAI,CAEhG;AAED,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,IAAI,CAEjG;AAED,wBAAgB,cAAc,IAAI,MAAM,CAEvC"}
package/dist/lsp.d.ts CHANGED
@@ -27,6 +27,6 @@ export declare const LSP_SYMBOL_KIND_MAP: Record<number, string>;
27
27
  * Failures are silent at the caller level — the binary falls back to
28
28
  * tree-sitter-only disambiguation when `lsp_hints` is absent.
29
29
  */
30
- export declare function queryLspHints(client: PluginInput["client"], symbolName: string, directory?: string): Promise<LspHints | undefined>;
30
+ export declare function queryLspHints(client: PluginInput["client"], symbolName: string, directory?: string, sessionId?: string): Promise<LspHints | undefined>;
31
31
  export {};
32
32
  //# sourceMappingURL=lsp.d.ts.map
package/dist/lsp.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"lsp.d.ts","sourceRoot":"","sources":["../src/lsp.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAGvD,mEAAmE;AACnE,UAAU,aAAa;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,8DAA8D;AAC9D,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,aAAa,EAAE,CAAC;CAC1B;AAED;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAOtD,CAAC;AAEF;;;;;;;;;;GAUG;AACH,wBAAsB,aAAa,CACjC,MAAM,EAAE,WAAW,CAAC,QAAQ,CAAC,EAC7B,UAAU,EAAE,MAAM,EAClB,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,QAAQ,GAAG,SAAS,CAAC,CAwD/B"}
1
+ {"version":3,"file":"lsp.d.ts","sourceRoot":"","sources":["../src/lsp.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAGvD,mEAAmE;AACnE,UAAU,aAAa;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,8DAA8D;AAC9D,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,aAAa,EAAE,CAAC;CAC1B;AAED;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAOtD,CAAC;AAEF;;;;;;;;;;GAUG;AACH,wBAAsB,aAAa,CACjC,MAAM,EAAE,WAAW,CAAC,QAAQ,CAAC,EAC7B,UAAU,EAAE,MAAM,EAClB,SAAS,CAAC,EAAE,MAAM,EAClB,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,QAAQ,GAAG,SAAS,CAAC,CAwD/B"}
@@ -1 +1 @@
1
- {"version":3,"file":"notifications.d.ts","sourceRoot":"","sources":["../src/notifications.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAiOH,MAAM,WAAW,mBAAmB;IAClC,8BAA8B;IAC9B,MAAM,EAAE,OAAO,CAAC;IAChB,iDAAiD;IACjD,SAAS,EAAE,MAAM,CAAC;IAClB,sEAAsE;IACtE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,yBAAyB,GAAG,uBAAuB,GAAG,oBAAoB,CAAC;IACjF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,uBAAuB;IACtC,MAAM,EAAE,OAAO,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;GAIG;AACH,wBAAsB,WAAW,CAAC,IAAI,EAAE,mBAAmB,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAW3F;AAED;;;;GAIG;AACH,wBAAsB,UAAU,CAAC,IAAI,EAAE,mBAAmB,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA0C1F;AAED;;;;;GAKG;AACH,wBAAsB,uBAAuB,CAC3C,IAAI,EAAE,mBAAmB,EACzB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAAE,EAClB,UAAU,CAAC,EAAE,MAAM,GAClB,OAAO,CAAC,IAAI,CAAC,CAqCf;AAwED,wBAAsB,wBAAwB,CAC5C,IAAI,EAAE,uBAAuB,EAC7B,QAAQ,EAAE,gBAAgB,EAAE,GAC3B,OAAO,CAAC,IAAI,CAAC,CAwBf;AAED;;;GAGG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CA0C9E"}
1
+ {"version":3,"file":"notifications.d.ts","sourceRoot":"","sources":["../src/notifications.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AA0OH,MAAM,WAAW,mBAAmB;IAClC,8BAA8B;IAC9B,MAAM,EAAE,OAAO,CAAC;IAChB,iDAAiD;IACjD,SAAS,EAAE,MAAM,CAAC;IAClB,sEAAsE;IACtE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,yBAAyB,GAAG,uBAAuB,GAAG,oBAAoB,CAAC;IACjF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,uBAAuB;IACtC,MAAM,EAAE,OAAO,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;GAIG;AACH,wBAAsB,WAAW,CAAC,IAAI,EAAE,mBAAmB,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAW3F;AAED;;;;GAIG;AACH,wBAAsB,UAAU,CAAC,IAAI,EAAE,mBAAmB,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA0C1F;AAED;;;;;GAKG;AACH,wBAAsB,uBAAuB,CAC3C,IAAI,EAAE,mBAAmB,EACzB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAAE,EAClB,UAAU,CAAC,EAAE,MAAM,GAClB,OAAO,CAAC,IAAI,CAAC,CAqCf;AAwED,wBAAsB,wBAAwB,CAC5C,IAAI,EAAE,uBAAuB,EAC7B,QAAQ,EAAE,gBAAgB,EAAE,GAC3B,OAAO,CAAC,IAAI,CAAC,CAwBf;AAED;;;GAGG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CA0C9E"}
@@ -1 +1 @@
1
- {"version":3,"file":"pool.d.ts","sourceRoot":"","sources":["../src/pool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,KAAK,aAAa,EAAE,MAAM,UAAU,CAAC;AAY5D,MAAM,WAAW,WAAY,SAAQ,aAAa;IAChD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,UAAU;IACrB,iEAAiE;IACjE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAgC;IACxD,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAgB;IAC9C,OAAO,CAAC,QAAQ,CAAC,eAAe,CAA0B;IAC1D,OAAO,CAAC,YAAY,CAA+C;gBAGjE,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE,WAAgB,EACzB,eAAe,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM;IAoB/C;;;;;;;OAOG;IACH,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI;IAgB5D;;;;;;;OAOG;IACH,SAAS,CAAC,WAAW,EAAE,MAAM,GAAG,YAAY;IAmB5C,wEAAwE;IACxE,OAAO,CAAC,OAAO;IAUf,yDAAyD;IACzD,OAAO,CAAC,QAAQ;IAgBhB,wDAAwD;IAClD,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAU/B;;;OAGG;IACG,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAcnD,4CAA4C;IAC5C,IAAI,IAAI,IAAI,MAAM,CAEjB;CACF"}
1
+ {"version":3,"file":"pool.d.ts","sourceRoot":"","sources":["../src/pool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,KAAK,aAAa,EAAE,MAAM,UAAU,CAAC;AAY5D,MAAM,WAAW,WAAY,SAAQ,aAAa;IAChD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,UAAU;IACrB,iEAAiE;IACjE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAgC;IACxD,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAgB;IAC9C,OAAO,CAAC,QAAQ,CAAC,eAAe,CAA0B;IAC1D,OAAO,CAAC,YAAY,CAA+C;gBAGjE,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE,WAAgB,EACzB,eAAe,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM;IAqB/C;;;;;;;OAOG;IACH,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI;IAgB5D;;;;;;;OAOG;IACH,SAAS,CAAC,WAAW,EAAE,MAAM,GAAG,YAAY;IAmB5C,wEAAwE;IACxE,OAAO,CAAC,OAAO;IAUf,yDAAyD;IACzD,OAAO,CAAC,QAAQ;IAgBhB,wDAAwD;IAClD,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAU/B;;;OAGG;IACG,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAcnD,4CAA4C;IAC5C,IAAI,IAAI,IAAI,MAAM,CAEjB;CACF"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Cache-busting fix: every PromptInput we send to OpenCode starts a new
3
+ * assistant turn (via `noReply: false`) or stores a new user message
4
+ * (via `noReply: true`). OpenCode's `createUserMessage` (in
5
+ * `packages/opencode/src/session/prompt.ts`) only preserves the user's
6
+ * model variant if we pass it explicitly — otherwise it falls back to
7
+ * `agent.variant` or undefined, silently switching the next assistant
8
+ * turn off the variant the human chose. That switch evicts the
9
+ * provider's prefix cache on every notification.
10
+ *
11
+ * This module fetches the last real user message's `{ providerID,
12
+ * modelID, variant }` and exposes it so prompt callers can include it
13
+ * verbatim in `PromptInput.model` + `PromptInput.variant`. Results are
14
+ * memoised per session for a short window so a batch of warnings or
15
+ * bg-completions only hits the messages API once.
16
+ */
17
+ export interface LastUserModel {
18
+ providerID: string;
19
+ modelID: string;
20
+ variant?: string;
21
+ }
22
+ /**
23
+ * Returns the most recent real user message's model + variant for the
24
+ * session, or `null` when the session has no user messages or the
25
+ * client doesn't expose `session.messages`. Result is cached for
26
+ * {@link CACHE_TTL_MS} ms per session — long enough to coalesce a
27
+ * batch of notifications, short enough that an actual variant change
28
+ * inside the conversation is picked up promptly.
29
+ *
30
+ * NOTE: Skips synthetic user messages we just produced ourselves
31
+ * (those whose only parts are `ignored: true`). Otherwise our own
32
+ * announcement could pin the variant after a single round-trip and
33
+ * defeat the fix.
34
+ */
35
+ export declare function getLastUserModel(client: unknown, sessionId: string): Promise<LastUserModel | null>;
36
+ /** Test-only: drop the cache so unit tests can simulate fresh sessions. */
37
+ export declare function __resetLastUserModelCacheForTests(): void;
38
+ //# sourceMappingURL=last-user-model.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"last-user-model.d.ts","sourceRoot":"","sources":["../../src/shared/last-user-model.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAwBH,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAWD;;;;;;;;;;;;GAYG;AACH,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,OAAO,EACf,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CA0C/B;AAiBD,2EAA2E;AAC3E,wBAAgB,iCAAiC,IAAI,IAAI,CAExD"}
@@ -1 +1 @@
1
- {"version":3,"file":"rpc-server.d.ts","sourceRoot":"","sources":["../../src/shared/rpc-server.ts"],"names":[],"mappings":"AAOA,KAAK,UAAU,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAExF,qBAAa,YAAY;IACvB,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,IAAI,CAAK;IACjB,OAAO,CAAC,KAAK,CAAuB;IACpC,OAAO,CAAC,QAAQ,CAAiC;IACjD,OAAO,CAAC,YAAY,CAAS;gBAEjB,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;IAIjD,sCAAsC;IACtC,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,GAAG,IAAI;IAIjD,6DAA6D;IACvD,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC;IAuC9B,8CAA8C;IAC9C,IAAI,IAAI,IAAI;IAaZ,OAAO,CAAC,QAAQ;CAmEjB"}
1
+ {"version":3,"file":"rpc-server.d.ts","sourceRoot":"","sources":["../../src/shared/rpc-server.ts"],"names":[],"mappings":"AAOA,KAAK,UAAU,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAExF,qBAAa,YAAY;IACvB,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,IAAI,CAAK;IACjB,OAAO,CAAC,KAAK,CAAuB;IACpC,OAAO,CAAC,QAAQ,CAAiC;IACjD,OAAO,CAAC,YAAY,CAAS;gBAEjB,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;IAIjD,sCAAsC;IACtC,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,GAAG,IAAI;IAIjD,6DAA6D;IACvD,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC;IAuC9B,8CAA8C;IAC9C,IAAI,IAAI,IAAI;IAaZ,OAAO,CAAC,QAAQ;CAoEjB"}
@@ -1,4 +1,6 @@
1
1
  import type { ToolDefinition } from "@opencode-ai/plugin";
2
2
  import type { PluginContext } from "../types.js";
3
3
  export declare function createBashTool(ctx: PluginContext): ToolDefinition;
4
+ export declare function createBashStatusTool(ctx: PluginContext): ToolDefinition;
5
+ export declare function createBashKillTool(ctx: PluginContext): ToolDefinition;
4
6
  //# sourceMappingURL=bash.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"bash.d.ts","sourceRoot":"","sources":["../../src/tools/bash.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAe,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAKvE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAsDjD,wBAAgB,cAAc,CAAC,GAAG,EAAE,aAAa,GAAG,cAAc,CA8IjE"}
1
+ {"version":3,"file":"bash.d.ts","sourceRoot":"","sources":["../../src/tools/bash.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAe,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAKvE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAsDjD,wBAAgB,cAAc,CAAC,GAAG,EAAE,aAAa,GAAG,cAAc,CAoJjE;AAED,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,aAAa,GAAG,cAAc,CA4BvE;AAED,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,aAAa,GAAG,cAAc,CAmBrE"}
@@ -1 +1 @@
1
- {"version":3,"file":"hoisted.d.ts","sourceRoot":"","sources":["../../src/tools/hoisted.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAI1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAkEjD,wEAAwE;AACxE,eAAO,MAAM,wBAAwB,GAAI,IAAI,MAAM,EAAE,QAAQ,MAAM,EAAE,OAAO,MAAM,KAAG,MAChD,CAAC;AA+QtC;;GAEG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,aAAa,GAAG,cAAc,CAgJjE;AA08BD;;;GAGG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAU/E;AAMD;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAmEnF"}
1
+ {"version":3,"file":"hoisted.d.ts","sourceRoot":"","sources":["../../src/tools/hoisted.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAI1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAkEjD,wEAAwE;AACxE,eAAO,MAAM,wBAAwB,GAAI,IAAI,MAAM,EAAE,QAAQ,MAAM,EAAE,OAAO,MAAM,KAAG,MAChD,CAAC;AA+QtC;;GAEG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,aAAa,GAAG,cAAc,CAgJjE;AAm+BD;;;GAGG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAY/E;AAMD;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAmEnF"}
@@ -1 +1 @@
1
- {"version":3,"file":"refactoring.d.ts","sourceRoot":"","sources":["../../src/tools/refactoring.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAG1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAajD;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CA2HnF"}
1
+ {"version":3,"file":"refactoring.d.ts","sourceRoot":"","sources":["../../src/tools/refactoring.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAG1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAajD;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAgInF"}
package/dist/tui.js CHANGED
@@ -2,7 +2,7 @@
2
2
  // package.json
3
3
  var package_default = {
4
4
  name: "@cortexkit/aft-opencode",
5
- version: "0.18.2",
5
+ version: "0.18.3",
6
6
  type: "module",
7
7
  description: "OpenCode plugin for Agent File Tools (AFT) \u2014 tree-sitter and lsp powered code analysis",
8
8
  main: "dist/index.js",
@@ -25,6 +25,7 @@ var package_default = {
25
25
  pretest: "cd ../.. && cargo build",
26
26
  test: "bun test",
27
27
  "test:e2e": "bun test src/__tests__/e2e/",
28
+ lint: "biome check src",
28
29
  prepublishOnly: "bun run build"
29
30
  },
30
31
  dependencies: {
@@ -35,11 +36,11 @@ var package_default = {
35
36
  zod: "^4.1.8"
36
37
  },
37
38
  optionalDependencies: {
38
- "@cortexkit/aft-darwin-arm64": "0.18.2",
39
- "@cortexkit/aft-darwin-x64": "0.18.2",
40
- "@cortexkit/aft-linux-arm64": "0.18.2",
41
- "@cortexkit/aft-linux-x64": "0.18.2",
42
- "@cortexkit/aft-win32-x64": "0.18.2"
39
+ "@cortexkit/aft-darwin-arm64": "0.18.3",
40
+ "@cortexkit/aft-darwin-x64": "0.18.3",
41
+ "@cortexkit/aft-linux-arm64": "0.18.3",
42
+ "@cortexkit/aft-linux-x64": "0.18.3",
43
+ "@cortexkit/aft-win32-x64": "0.18.3"
43
44
  },
44
45
  devDependencies: {
45
46
  "@types/node": "^22.0.0",
@@ -72,7 +73,8 @@ import * as fs from "fs";
72
73
  import * as os from "os";
73
74
  import * as path from "path";
74
75
  var TAG = "[aft-plugin]";
75
- var logFile = path.join(os.tmpdir(), "aft-plugin.log");
76
+ var isTestEnv = process.env.BUN_TEST === "1" || false;
77
+ var logFile = path.join(os.tmpdir(), isTestEnv ? "aft-plugin-test.log" : "aft-plugin.log");
76
78
  var useStderr = process.env.AFT_LOG_STDERR === "1";
77
79
  var buffer = [];
78
80
  var flushTimer = null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cortexkit/aft-opencode",
3
- "version": "0.18.2",
3
+ "version": "0.18.3",
4
4
  "type": "module",
5
5
  "description": "OpenCode plugin for Agent File Tools (AFT) — tree-sitter and lsp powered code analysis",
6
6
  "main": "dist/index.js",
@@ -23,6 +23,7 @@
23
23
  "pretest": "cd ../.. && cargo build",
24
24
  "test": "bun test",
25
25
  "test:e2e": "bun test src/__tests__/e2e/",
26
+ "lint": "biome check src",
26
27
  "prepublishOnly": "bun run build"
27
28
  },
28
29
  "dependencies": {
@@ -33,11 +34,11 @@
33
34
  "zod": "^4.1.8"
34
35
  },
35
36
  "optionalDependencies": {
36
- "@cortexkit/aft-darwin-arm64": "0.18.2",
37
- "@cortexkit/aft-darwin-x64": "0.18.2",
38
- "@cortexkit/aft-linux-arm64": "0.18.2",
39
- "@cortexkit/aft-linux-x64": "0.18.2",
40
- "@cortexkit/aft-win32-x64": "0.18.2"
37
+ "@cortexkit/aft-darwin-arm64": "0.18.3",
38
+ "@cortexkit/aft-darwin-x64": "0.18.3",
39
+ "@cortexkit/aft-linux-arm64": "0.18.3",
40
+ "@cortexkit/aft-linux-x64": "0.18.3",
41
+ "@cortexkit/aft-win32-x64": "0.18.3"
41
42
  },
42
43
  "devDependencies": {
43
44
  "@types/node": "^22.0.0",
package/src/logger.ts CHANGED
@@ -3,7 +3,12 @@ import * as os from "node:os";
3
3
  import * as path from "node:path";
4
4
 
5
5
  const TAG = "[aft-plugin]";
6
- const logFile = path.join(os.tmpdir(), "aft-plugin.log");
6
+
7
+ // Route test runs to a separate log file so `bun test` never pollutes the
8
+ // live session log that users read to diagnose problems. Bun sets BUN_TEST=1
9
+ // automatically; NODE_ENV=test covers other test harnesses.
10
+ const isTestEnv = process.env.BUN_TEST === "1" || process.env.NODE_ENV === "test";
11
+ const logFile = path.join(os.tmpdir(), isTestEnv ? "aft-plugin-test.log" : "aft-plugin.log");
7
12
 
8
13
  /**
9
14
  * When AFT_LOG_STDERR=1, logs go to stderr (useful for subprocess tests that
@@ -82,15 +87,15 @@ export function error(message: string, data?: unknown): void {
82
87
  * a session's tool call). Bridge-lifecycle logs (spawn, version, idle) are
83
88
  * project-scoped, not session-scoped — use `log`/`warn`/`error` for those.
84
89
  */
85
- export function sessionLog(sessionId: string, message: string, data?: unknown): void {
90
+ export function sessionLog(sessionId: string | undefined, message: string, data?: unknown): void {
86
91
  write("INFO", message, data, sessionId);
87
92
  }
88
93
 
89
- export function sessionWarn(sessionId: string, message: string, data?: unknown): void {
94
+ export function sessionWarn(sessionId: string | undefined, message: string, data?: unknown): void {
90
95
  write("WARN", message, data, sessionId);
91
96
  }
92
97
 
93
- export function sessionError(sessionId: string, message: string, data?: unknown): void {
98
+ export function sessionError(sessionId: string | undefined, message: string, data?: unknown): void {
94
99
  write("ERROR", message, data, sessionId);
95
100
  }
96
101
 
@@ -0,0 +1,133 @@
1
+ /**
2
+ * Cache-busting fix: every PromptInput we send to OpenCode starts a new
3
+ * assistant turn (via `noReply: false`) or stores a new user message
4
+ * (via `noReply: true`). OpenCode's `createUserMessage` (in
5
+ * `packages/opencode/src/session/prompt.ts`) only preserves the user's
6
+ * model variant if we pass it explicitly — otherwise it falls back to
7
+ * `agent.variant` or undefined, silently switching the next assistant
8
+ * turn off the variant the human chose. That switch evicts the
9
+ * provider's prefix cache on every notification.
10
+ *
11
+ * This module fetches the last real user message's `{ providerID,
12
+ * modelID, variant }` and exposes it so prompt callers can include it
13
+ * verbatim in `PromptInput.model` + `PromptInput.variant`. Results are
14
+ * memoised per session for a short window so a batch of warnings or
15
+ * bg-completions only hits the messages API once.
16
+ */
17
+
18
+ interface SessionMessageInfo {
19
+ id?: string;
20
+ role?: string;
21
+ parts?: Array<{ ignored?: boolean }>;
22
+ model?: {
23
+ providerID?: string;
24
+ modelID?: string;
25
+ variant?: string;
26
+ };
27
+ }
28
+
29
+ interface SessionMessage {
30
+ info?: SessionMessageInfo;
31
+ parts?: Array<{ ignored?: boolean }>;
32
+ }
33
+
34
+ interface OpenCodeClientShape {
35
+ session?: {
36
+ messages?: (input: { path: { id: string } }) => Promise<{ data?: SessionMessage[] }>;
37
+ };
38
+ }
39
+
40
+ export interface LastUserModel {
41
+ providerID: string;
42
+ modelID: string;
43
+ variant?: string;
44
+ }
45
+
46
+ interface CacheEntry {
47
+ expiresAt: number;
48
+ model: LastUserModel | null;
49
+ }
50
+
51
+ const CACHE_TTL_MS = 5_000;
52
+ const CACHE_MAX_ENTRIES = 100;
53
+ const cache = new Map<string, CacheEntry>();
54
+
55
+ /**
56
+ * Returns the most recent real user message's model + variant for the
57
+ * session, or `null` when the session has no user messages or the
58
+ * client doesn't expose `session.messages`. Result is cached for
59
+ * {@link CACHE_TTL_MS} ms per session — long enough to coalesce a
60
+ * batch of notifications, short enough that an actual variant change
61
+ * inside the conversation is picked up promptly.
62
+ *
63
+ * NOTE: Skips synthetic user messages we just produced ourselves
64
+ * (those whose only parts are `ignored: true`). Otherwise our own
65
+ * announcement could pin the variant after a single round-trip and
66
+ * defeat the fix.
67
+ */
68
+ export async function getLastUserModel(
69
+ client: unknown,
70
+ sessionId: string,
71
+ ): Promise<LastUserModel | null> {
72
+ const now = Date.now();
73
+ const cached = cache.get(sessionId);
74
+ if (cached && cached.expiresAt > now) {
75
+ cache.delete(sessionId);
76
+ cache.set(sessionId, cached);
77
+ return cached.model;
78
+ }
79
+ if (cached) cache.delete(sessionId);
80
+
81
+ const c = client as OpenCodeClientShape;
82
+ const fetcher = c?.session?.messages;
83
+ if (typeof fetcher !== "function") {
84
+ return null;
85
+ }
86
+
87
+ let messages: SessionMessage[];
88
+ try {
89
+ const result = await fetcher({ path: { id: sessionId } });
90
+ messages = result?.data ?? [];
91
+ } catch {
92
+ // Don't cache failures — a transient API error shouldn't pin the
93
+ // session's variant resolution to "unknown" for 5 s.
94
+ return null;
95
+ }
96
+
97
+ for (let i = messages.length - 1; i >= 0; i--) {
98
+ const info = messages[i]?.info;
99
+ if (info?.role !== "user") continue;
100
+ if (isSyntheticIgnoredMessage(messages[i])) continue;
101
+ const model = info.model;
102
+ if (!model?.providerID || !model?.modelID) continue;
103
+ const resolved: LastUserModel = {
104
+ providerID: model.providerID,
105
+ modelID: model.modelID,
106
+ ...(model.variant ? { variant: model.variant } : {}),
107
+ };
108
+ setCache(sessionId, resolved);
109
+ return resolved;
110
+ }
111
+
112
+ return null;
113
+ }
114
+
115
+ function setCache(sessionId: string, model: LastUserModel | null): void {
116
+ if (cache.has(sessionId)) cache.delete(sessionId);
117
+ cache.set(sessionId, { expiresAt: Date.now() + CACHE_TTL_MS, model });
118
+ while (cache.size > CACHE_MAX_ENTRIES) {
119
+ const oldest = cache.keys().next().value;
120
+ if (typeof oldest !== "string") break;
121
+ cache.delete(oldest);
122
+ }
123
+ }
124
+
125
+ function isSyntheticIgnoredMessage(message: SessionMessage | undefined): boolean {
126
+ const parts = message?.info?.parts ?? message?.parts;
127
+ return Array.isArray(parts) && parts.length > 0 && parts.every((part) => part?.ignored === true);
128
+ }
129
+
130
+ /** Test-only: drop the cache so unit tests can simulate fresh sessions. */
131
+ export function __resetLastUserModelCacheForTests(): void {
132
+ cache.clear();
133
+ }
@@ -130,10 +130,11 @@ export class AftRpcServer {
130
130
 
131
131
  const { token: _token, ...handlerParams } = params;
132
132
 
133
- log(`RPC call: ${method} params=${JSON.stringify(handlerParams).slice(0, 200)}`);
133
+ // Successful RPC calls are intentionally NOT logged. The /aft-status
134
+ // TUI dialog polls every 1.5s while open, which used to spam the log
135
+ // with RPC call/result pairs. Errors are still logged (real signal).
134
136
  handler(handlerParams)
135
137
  .then((result) => {
136
- log(`RPC result: ${method} => ${JSON.stringify(result).slice(0, 200)}`);
137
138
  res.writeHead(200, { "Content-Type": "application/json" });
138
139
  res.end(JSON.stringify(result));
139
140
  })