@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.
|
|
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.
|
|
60
|
-
"@f5-sales-demo/pi-agent-core": "19.85.
|
|
61
|
-
"@f5-sales-demo/pi-ai": "19.85.
|
|
62
|
-
"@f5-sales-demo/pi-natives": "19.85.
|
|
63
|
-
"@f5-sales-demo/pi-resource-management": "19.85.
|
|
64
|
-
"@f5-sales-demo/pi-tui": "19.85.
|
|
65
|
-
"@f5-sales-demo/pi-utils": "19.85.
|
|
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.
|
|
21
|
-
"commit": "
|
|
22
|
-
"shortCommit": "
|
|
20
|
+
"version": "19.85.6",
|
|
21
|
+
"commit": "58616737ba891a0d71cbdec7314292ed2efbd38f",
|
|
22
|
+
"shortCommit": "5861673",
|
|
23
23
|
"branch": "main",
|
|
24
|
-
"tag": "v19.85.
|
|
25
|
-
"commitDate": "2026-07-
|
|
26
|
-
"buildDate": "2026-07-
|
|
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/
|
|
32
|
-
"releaseUrl": "https://github.com/f5-sales-demo/xcsh/releases/tag/v19.85.
|
|
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
|
};
|