@f5-sales-demo/xcsh 19.85.5 → 19.85.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@f5-sales-demo/xcsh",
4
- "version": "19.85.5",
4
+ "version": "19.85.6",
5
5
  "description": "Coding agent CLI with read, bash, edit, write tools and session management",
6
6
  "homepage": "https://github.com/f5-sales-demo/xcsh",
7
7
  "author": "Can Boluk",
@@ -56,13 +56,13 @@
56
56
  "dependencies": {
57
57
  "@agentclientprotocol/sdk": "0.16.1",
58
58
  "@mozilla/readability": "^0.6",
59
- "@f5-sales-demo/xcsh-stats": "19.85.5",
60
- "@f5-sales-demo/pi-agent-core": "19.85.5",
61
- "@f5-sales-demo/pi-ai": "19.85.5",
62
- "@f5-sales-demo/pi-natives": "19.85.5",
63
- "@f5-sales-demo/pi-resource-management": "19.85.5",
64
- "@f5-sales-demo/pi-tui": "19.85.5",
65
- "@f5-sales-demo/pi-utils": "19.85.5",
59
+ "@f5-sales-demo/xcsh-stats": "19.85.6",
60
+ "@f5-sales-demo/pi-agent-core": "19.85.6",
61
+ "@f5-sales-demo/pi-ai": "19.85.6",
62
+ "@f5-sales-demo/pi-natives": "19.85.6",
63
+ "@f5-sales-demo/pi-resource-management": "19.85.6",
64
+ "@f5-sales-demo/pi-tui": "19.85.6",
65
+ "@f5-sales-demo/pi-utils": "19.85.6",
66
66
  "@sinclair/typebox": "^0.34",
67
67
  "@xterm/headless": "^6.0",
68
68
  "ajv": "^8.20",
@@ -82,13 +82,25 @@ class RpcHostToolAdapter<TParams extends TSchema = TSchema, TTheme extends Theme
82
82
  }
83
83
  }
84
84
 
85
+ /**
86
+ * How long a host tool may go with NO response — neither a `host_tool_result` nor a
87
+ * `host_tool_update` — before we give up on it. An unanswered call (e.g. an Office
88
+ * task-pane whose WebView the host suspended when it lost focus) would otherwise hang
89
+ * the agent turn forever, and since turns are queued behind the active one, every
90
+ * later turn wedges too. This is IDLE time, reset by each streamed update, so a
91
+ * legitimately slow-but-progressing tool is never cut off — only true silence trips it.
92
+ */
93
+ export const HOST_TOOL_IDLE_TIMEOUT_MS = 60_000;
94
+
85
95
  export class RpcHostToolBridge {
86
96
  #output: RpcHostToolOutput;
87
97
  #definitions = new Map<string, RpcHostToolDefinition>();
88
98
  #pendingCalls = new Map<string, PendingHostToolCall>();
99
+ #idleTimeoutMs: number;
89
100
 
90
- constructor(output: RpcHostToolOutput) {
101
+ constructor(output: RpcHostToolOutput, opts?: { idleTimeoutMs?: number }) {
91
102
  this.#output = output;
103
+ this.#idleTimeoutMs = opts?.idleTimeoutMs ?? HOST_TOOL_IDLE_TIMEOUT_MS;
92
104
  }
93
105
 
94
106
  getToolNames(): string[] {
@@ -141,7 +153,23 @@ export class RpcHostToolBridge {
141
153
  const { promise, resolve, reject } = Promise.withResolvers<AgentToolResult<unknown>>();
142
154
  let settled = false;
143
155
 
156
+ // ---- idle timer (reset by updates; fires → cancel + reject) ----
157
+ let idleTimer: ReturnType<typeof setTimeout> | undefined;
158
+ const clearIdle = (): void => {
159
+ if (idleTimer !== undefined) {
160
+ clearTimeout(idleTimer);
161
+ idleTimer = undefined;
162
+ }
163
+ };
164
+ const armIdle = (): void => {
165
+ clearIdle();
166
+ idleTimer = setTimeout(onIdleTimeout, this.#idleTimeoutMs);
167
+ // Don't keep the process alive just for this backstop.
168
+ if (typeof idleTimer === "object" && "unref" in idleTimer) idleTimer.unref();
169
+ };
170
+
144
171
  const cleanup = () => {
172
+ clearIdle();
145
173
  signal?.removeEventListener("abort", onAbort);
146
174
  this.#pendingCalls.delete(id);
147
175
  };
@@ -158,6 +186,23 @@ export class RpcHostToolBridge {
158
186
  reject(new Error(`Host tool "${definition.name}" was aborted`));
159
187
  };
160
188
 
189
+ const onIdleTimeout = () => {
190
+ if (settled) return;
191
+ settled = true;
192
+ cleanup();
193
+ this.#output({
194
+ type: "host_tool_cancel",
195
+ id: Snowflake.next() as string,
196
+ targetId: id,
197
+ });
198
+ reject(
199
+ new Error(
200
+ `Host tool "${definition.name}" did not respond within ${this.#idleTimeoutMs / 1000}s ` +
201
+ "(the host may be unresponsive — a suspended Office WebView loses its connection)",
202
+ ),
203
+ );
204
+ };
205
+
161
206
  signal?.addEventListener("abort", onAbort, { once: true });
162
207
  this.#pendingCalls.set(id, {
163
208
  resolve: result => {
@@ -172,7 +217,11 @@ export class RpcHostToolBridge {
172
217
  cleanup();
173
218
  reject(error);
174
219
  },
175
- onUpdate,
220
+ onUpdate: partial => {
221
+ // A streamed update proves the host is alive — reset the idle clock.
222
+ armIdle();
223
+ onUpdate?.(partial);
224
+ },
176
225
  });
177
226
 
178
227
  this.#output({
@@ -183,6 +232,9 @@ export class RpcHostToolBridge {
183
232
  arguments: args,
184
233
  });
185
234
 
235
+ // Start the idle clock AFTER sending the call.
236
+ armIdle();
237
+
186
238
  return promise;
187
239
  }
188
240
 
@@ -17,17 +17,17 @@ export interface BuildInfo {
17
17
  }
18
18
 
19
19
  export const BUILD_INFO: BuildInfo = {
20
- "version": "19.85.5",
21
- "commit": "78846ea460721267f0fda099042fba2390dd6887",
22
- "shortCommit": "78846ea",
20
+ "version": "19.85.6",
21
+ "commit": "58616737ba891a0d71cbdec7314292ed2efbd38f",
22
+ "shortCommit": "5861673",
23
23
  "branch": "main",
24
- "tag": "v19.85.5",
25
- "commitDate": "2026-07-23T22:07:14Z",
26
- "buildDate": "2026-07-23T22:31:05.353Z",
24
+ "tag": "v19.85.6",
25
+ "commitDate": "2026-07-23T23:21:56Z",
26
+ "buildDate": "2026-07-23T23:42:32.289Z",
27
27
  "dirty": true,
28
28
  "prNumber": "",
29
29
  "repoUrl": "https://github.com/f5-sales-demo/xcsh",
30
30
  "repoSlug": "f5-sales-demo/xcsh",
31
- "commitUrl": "https://github.com/f5-sales-demo/xcsh/commit/78846ea460721267f0fda099042fba2390dd6887",
32
- "releaseUrl": "https://github.com/f5-sales-demo/xcsh/releases/tag/v19.85.5"
31
+ "commitUrl": "https://github.com/f5-sales-demo/xcsh/commit/58616737ba891a0d71cbdec7314292ed2efbd38f",
32
+ "releaseUrl": "https://github.com/f5-sales-demo/xcsh/releases/tag/v19.85.6"
33
33
  };