@f5-sales-demo/xcsh 20.4.4 → 20.4.5
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 +8 -8
- package/src/browser/extension-bridge-tools.ts +5 -3
- package/src/browser/headless-bridge.ts +1 -1
- package/src/config/auto-config.ts +3 -0
- package/src/config/model-registry.ts +6 -0
- package/src/config/settings-schema.ts +4 -4
- package/src/internal-urls/build-info.generated.ts +8 -8
- package/src/prompts/tools/task-summary.md +5 -1
- package/src/task/index.ts +4 -36
- package/src/task/summary.ts +53 -0
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@f5-sales-demo/xcsh",
|
|
4
|
-
"version": "20.4.
|
|
4
|
+
"version": "20.4.5",
|
|
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",
|
|
@@ -60,13 +60,13 @@
|
|
|
60
60
|
"dependencies": {
|
|
61
61
|
"@agentclientprotocol/sdk": "1.3.0",
|
|
62
62
|
"@mozilla/readability": "^0.6",
|
|
63
|
-
"@f5-sales-demo/xcsh-stats": "20.4.
|
|
64
|
-
"@f5-sales-demo/pi-agent-core": "20.4.
|
|
65
|
-
"@f5-sales-demo/pi-ai": "20.4.
|
|
66
|
-
"@f5-sales-demo/pi-natives": "20.4.
|
|
67
|
-
"@f5-sales-demo/pi-resource-management": "20.4.
|
|
68
|
-
"@f5-sales-demo/pi-tui": "20.4.
|
|
69
|
-
"@f5-sales-demo/pi-utils": "20.4.
|
|
63
|
+
"@f5-sales-demo/xcsh-stats": "20.4.5",
|
|
64
|
+
"@f5-sales-demo/pi-agent-core": "20.4.5",
|
|
65
|
+
"@f5-sales-demo/pi-ai": "20.4.5",
|
|
66
|
+
"@f5-sales-demo/pi-natives": "20.4.5",
|
|
67
|
+
"@f5-sales-demo/pi-resource-management": "20.4.5",
|
|
68
|
+
"@f5-sales-demo/pi-tui": "20.4.5",
|
|
69
|
+
"@f5-sales-demo/pi-utils": "20.4.5",
|
|
70
70
|
"@sinclair/typebox": "^0.34",
|
|
71
71
|
"@xterm/headless": "^6.0",
|
|
72
72
|
"ajv": "^8.20",
|
|
@@ -104,9 +104,10 @@ export const BROWSER_TOOL_NAMES: readonly string[] = [
|
|
|
104
104
|
* local xcsh agent, so it gets the same general-purpose native tools the CLI has —
|
|
105
105
|
* `bash` (so it can shell out to `az`, `gh`, terraform, git, …), the file tools
|
|
106
106
|
* (`read`/`write`/`edit`), search (`grep`), plus planning (`todo_write`,
|
|
107
|
-
* `task`) and `
|
|
108
|
-
* tool is omitted because its name collides with a
|
|
109
|
-
*
|
|
107
|
+
* `task`), `calc`, and image analysis (`inspect_image`). (File-finding is covered
|
|
108
|
+
* by `bash`; the builtin `find` tool is omitted because its name collides with a
|
|
109
|
+
* browser tool.) The document's own Excel/PowerPoint/Word tools arrive at runtime
|
|
110
|
+
* over the bridge via `set_host_tools`.
|
|
110
111
|
*
|
|
111
112
|
* DELIBERATELY EXCLUDED:
|
|
112
113
|
* - Every {@link BROWSER_TOOL_NAMES} entry — there is no browser to drive in a
|
|
@@ -143,4 +144,5 @@ export const OFFICE_TOOL_NAMES: readonly string[] = [
|
|
|
143
144
|
"todo_write",
|
|
144
145
|
"task",
|
|
145
146
|
"calc",
|
|
147
|
+
"inspect_image",
|
|
146
148
|
];
|
|
@@ -142,7 +142,7 @@ export async function startHeadlessChatBridge(deps: HeadlessBridgeDeps = default
|
|
|
142
142
|
// the rethrow as a non-fatal "pane only" fallback.
|
|
143
143
|
try {
|
|
144
144
|
// Create ONE headless Office session with the full CLI-parity builtin set
|
|
145
|
-
// (OFFICE_TOOL_NAMES: bash/read/write/edit/grep/
|
|
145
|
+
// (OFFICE_TOOL_NAMES: bash/read/write/edit/grep/inspect_image/… — NO browser tools, which
|
|
146
146
|
// would be hallucinated in a document task pane). The document's own tools
|
|
147
147
|
// (Excel/Word/PowerPoint) arrive at runtime via set_host_tools.
|
|
148
148
|
const { session } = await deps.createAgentSession({
|
|
@@ -2,6 +2,7 @@ import * as path from "node:path";
|
|
|
2
2
|
import {
|
|
3
3
|
type Api,
|
|
4
4
|
type AssistantMessageEventStream,
|
|
5
|
+
applyGeneratedModelPolicies,
|
|
5
6
|
type Context,
|
|
6
7
|
createModelManager,
|
|
7
8
|
DEFAULT_LOCAL_TOKEN,
|
|
@@ -1227,6 +1228,7 @@ export class ModelRegistry {
|
|
|
1227
1228
|
return {
|
|
1228
1229
|
...model,
|
|
1229
1230
|
baseUrl: existing.baseUrl,
|
|
1231
|
+
input: existing.input.includes("image") ? ["text", "image"] : model.input,
|
|
1230
1232
|
headers: existing.headers ? { ...existing.headers, ...model.headers } : model.headers,
|
|
1231
1233
|
};
|
|
1232
1234
|
}
|
|
@@ -1810,6 +1812,10 @@ export class ModelRegistry {
|
|
|
1810
1812
|
});
|
|
1811
1813
|
}
|
|
1812
1814
|
#applyHardcodedModelPolicies(models: Model<Api>[]): Model<Api>[] {
|
|
1815
|
+
// Discovery caches contain provider-advertised metadata, which can lag the
|
|
1816
|
+
// bundled corrections. Reapply generated-model policy before a cached entry
|
|
1817
|
+
// replaces its bundled peer; explicit user overrides still run afterward.
|
|
1818
|
+
applyGeneratedModelPolicies(models);
|
|
1813
1819
|
return models.map(model => {
|
|
1814
1820
|
if (model.id !== "gpt-5.4" || model.provider === "github-copilot") {
|
|
1815
1821
|
return model;
|
|
@@ -157,16 +157,16 @@ const DEFAULT_CYCLE_ORDER: string[] = ["smol", "default", "slow"];
|
|
|
157
157
|
/**
|
|
158
158
|
* Binary-baked default model role. Ships in the binary so a fresh install needs
|
|
159
159
|
* NO `~/.xcsh/agent/config.yml` — `/login` only supplies the (PII) proxy URL + key.
|
|
160
|
-
*
|
|
161
|
-
*
|
|
160
|
+
* GPT-5.6 Sol High is the vision-capable production default through the OpenAI
|
|
161
|
+
* Chat Completions route on LiteLLM. Keep the effort explicit in the role so returning
|
|
162
162
|
* from a lower-effort role cannot inherit that role's effort.
|
|
163
163
|
*/
|
|
164
|
-
export const DEFAULT_MODEL_ROLE = "
|
|
164
|
+
export const DEFAULT_MODEL_ROLE = "litellm/gpt-5.6-sol:high";
|
|
165
165
|
/** Fast role for lightweight work (commit messages, titles, memory summaries). */
|
|
166
166
|
const SMOL_MODEL_ROLE = "litellm/gpt-5.6-sol:low";
|
|
167
167
|
/**
|
|
168
168
|
* Baked role map. `smol` keeps GPT-5.6 Sol at low effort for latency-sensitive
|
|
169
|
-
* work; both `default` and `slow` restore vision-capable
|
|
169
|
+
* work; both `default` and `slow` restore vision-capable GPT-5.6 Sol High.
|
|
170
170
|
*/
|
|
171
171
|
const DEFAULT_MODEL_ROLES: Record<string, string> = {
|
|
172
172
|
default: DEFAULT_MODEL_ROLE,
|
|
@@ -17,17 +17,17 @@ export interface BuildInfo {
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
export const BUILD_INFO: BuildInfo = {
|
|
20
|
-
"version": "20.4.
|
|
21
|
-
"commit": "
|
|
22
|
-
"shortCommit": "
|
|
20
|
+
"version": "20.4.5",
|
|
21
|
+
"commit": "0ccf19916f316fb84d06bd9a5a9ab36bd67d8759",
|
|
22
|
+
"shortCommit": "0ccf199",
|
|
23
23
|
"branch": "main",
|
|
24
|
-
"tag": "v20.4.
|
|
25
|
-
"commitDate": "2026-08-
|
|
26
|
-
"buildDate": "2026-08-
|
|
24
|
+
"tag": "v20.4.5",
|
|
25
|
+
"commitDate": "2026-08-05T09:48:31Z",
|
|
26
|
+
"buildDate": "2026-08-05T10:16:36.684Z",
|
|
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/v20.4.
|
|
31
|
+
"commitUrl": "https://github.com/f5-sales-demo/xcsh/commit/0ccf19916f316fb84d06bd9a5a9ab36bd67d8759",
|
|
32
|
+
"releaseUrl": "https://github.com/f5-sales-demo/xcsh/releases/tag/v20.4.5"
|
|
33
33
|
};
|
|
@@ -6,7 +6,11 @@
|
|
|
6
6
|
<status>{{status}}</status>
|
|
7
7
|
{{#if meta}}<meta lines="{{meta.lineCount}}" size="{{meta.charSize}}" />{{/if}}
|
|
8
8
|
{{#if truncated}}
|
|
9
|
-
|
|
9
|
+
{{#if fullOutputUrl}}
|
|
10
|
+
<preview full-path="{{fullOutputUrl}}">
|
|
11
|
+
{{else}}
|
|
12
|
+
<preview>
|
|
13
|
+
{{/if}}
|
|
10
14
|
{{preview}}
|
|
11
15
|
</preview>
|
|
12
16
|
{{else}}
|
package/src/task/index.ts
CHANGED
|
@@ -24,7 +24,7 @@ import type { Theme } from "../modes/theme/theme";
|
|
|
24
24
|
import planModeSubagentPrompt from "../prompts/system/plan-mode-subagent.md" with { type: "text" };
|
|
25
25
|
import taskDescriptionTemplate from "../prompts/tools/task.md" with { type: "text" };
|
|
26
26
|
import taskSummaryTemplate from "../prompts/tools/task-summary.md" with { type: "text" };
|
|
27
|
-
import {
|
|
27
|
+
import { formatDuration } from "../tools/render-utils";
|
|
28
28
|
// Import review tools for side effects (registers subagent tool handlers)
|
|
29
29
|
import "../tools/review";
|
|
30
30
|
import { generateCommitMessage } from "../utils/commit-message-generator";
|
|
@@ -35,6 +35,7 @@ import { resolveIsolationBackendForTaskExecution } from "./isolation-backend";
|
|
|
35
35
|
import { AgentOutputManager } from "./output-manager";
|
|
36
36
|
import { mapWithConcurrencyLimit, Semaphore } from "./parallel";
|
|
37
37
|
import { renderCall, renderResult } from "./render";
|
|
38
|
+
import { buildTaskOutputSummaries } from "./summary";
|
|
38
39
|
import { renderTemplate } from "./template";
|
|
39
40
|
import {
|
|
40
41
|
type AgentDefinition,
|
|
@@ -108,6 +109,7 @@ export { loadBundledAgents as BUNDLED_AGENTS } from "./agents";
|
|
|
108
109
|
export { discoverCommands, expandCommand, getCommand } from "./commands";
|
|
109
110
|
export { discoverAgents, getAgent } from "./discovery";
|
|
110
111
|
export { AgentOutputManager } from "./output-manager";
|
|
112
|
+
export { buildTaskOutputSummaries, type TaskOutputSummary } from "./summary";
|
|
111
113
|
export type {
|
|
112
114
|
AgentDefinition,
|
|
113
115
|
AgentProgress,
|
|
@@ -1123,41 +1125,8 @@ export class TaskTool implements AgentTool<TaskSchema, TaskToolDetails, Theme> {
|
|
|
1123
1125
|
const successCount = results.filter(r => r.exitCode === 0 && !r.error && !r.aborted).length;
|
|
1124
1126
|
const totalDuration = Date.now() - startTime;
|
|
1125
1127
|
|
|
1126
|
-
const summaries = results
|
|
1127
|
-
const status = r.aborted
|
|
1128
|
-
? "cancelled"
|
|
1129
|
-
: r.exitCode === 0 && r.error
|
|
1130
|
-
? "merge failed"
|
|
1131
|
-
: r.exitCode === 0
|
|
1132
|
-
? "completed"
|
|
1133
|
-
: `failed (exit ${r.exitCode})`;
|
|
1134
|
-
const output = r.output.trim() || r.stderr.trim() || "(no output)";
|
|
1135
|
-
const outputCharCount = r.outputMeta?.charCount ?? output.length;
|
|
1136
|
-
const fullOutputThreshold = 5000;
|
|
1137
|
-
let preview = output;
|
|
1138
|
-
let truncated = false;
|
|
1139
|
-
if (outputCharCount > fullOutputThreshold) {
|
|
1140
|
-
const slice = output.slice(0, fullOutputThreshold);
|
|
1141
|
-
const lastNewline = slice.lastIndexOf("\n");
|
|
1142
|
-
preview = lastNewline >= 0 ? slice.slice(0, lastNewline) : slice;
|
|
1143
|
-
truncated = true;
|
|
1144
|
-
}
|
|
1145
|
-
return {
|
|
1146
|
-
agent: r.agent,
|
|
1147
|
-
status,
|
|
1148
|
-
id: r.id,
|
|
1149
|
-
preview,
|
|
1150
|
-
truncated,
|
|
1151
|
-
meta: r.outputMeta
|
|
1152
|
-
? {
|
|
1153
|
-
lineCount: r.outputMeta.lineCount,
|
|
1154
|
-
charSize: formatBytes(r.outputMeta.charCount),
|
|
1155
|
-
}
|
|
1156
|
-
: undefined,
|
|
1157
|
-
};
|
|
1158
|
-
});
|
|
1128
|
+
const summaries = buildTaskOutputSummaries(results, artifactsDir !== null);
|
|
1159
1129
|
|
|
1160
|
-
const outputIds = results.filter(r => !r.aborted || r.output.trim()).map(r => `agent://${r.id}`);
|
|
1161
1130
|
const backendSummaryPrefix = isolationBackendWarning ? `\n\n${isolationBackendWarning}` : "";
|
|
1162
1131
|
const summary = prompt.render(taskSummaryTemplate, {
|
|
1163
1132
|
successCount,
|
|
@@ -1166,7 +1135,6 @@ export class TaskTool implements AgentTool<TaskSchema, TaskToolDetails, Theme> {
|
|
|
1166
1135
|
hasCancelledNote: aborted && cancelledCount > 0,
|
|
1167
1136
|
duration: formatDuration(totalDuration),
|
|
1168
1137
|
summaries,
|
|
1169
|
-
outputIds,
|
|
1170
1138
|
agentName,
|
|
1171
1139
|
mergeSummary: `${backendSummaryPrefix}${mergeSummary}`,
|
|
1172
1140
|
});
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { formatBytes } from "../tools/render-utils";
|
|
2
|
+
import type { SingleResult } from "./types";
|
|
3
|
+
|
|
4
|
+
export interface TaskOutputSummary {
|
|
5
|
+
agent: string;
|
|
6
|
+
status: string;
|
|
7
|
+
id: string;
|
|
8
|
+
preview: string;
|
|
9
|
+
truncated: boolean;
|
|
10
|
+
fullOutputUrl?: string;
|
|
11
|
+
meta?: { lineCount: number; charSize: string };
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** Build the model-facing task summaries and their optional persistent output references. */
|
|
15
|
+
export function buildTaskOutputSummaries(
|
|
16
|
+
results: SingleResult[],
|
|
17
|
+
hasPersistentArtifacts: boolean,
|
|
18
|
+
): TaskOutputSummary[] {
|
|
19
|
+
return results.map(result => {
|
|
20
|
+
const status = result.aborted
|
|
21
|
+
? "cancelled"
|
|
22
|
+
: result.exitCode === 0 && result.error
|
|
23
|
+
? "merge failed"
|
|
24
|
+
: result.exitCode === 0
|
|
25
|
+
? "completed"
|
|
26
|
+
: `failed (exit ${result.exitCode})`;
|
|
27
|
+
const output = result.output.trim() || result.stderr.trim() || "(no output)";
|
|
28
|
+
const outputCharCount = result.outputMeta?.charCount ?? output.length;
|
|
29
|
+
const fullOutputThreshold = 5000;
|
|
30
|
+
let preview = output;
|
|
31
|
+
let truncated = false;
|
|
32
|
+
if (outputCharCount > fullOutputThreshold) {
|
|
33
|
+
const slice = output.slice(0, fullOutputThreshold);
|
|
34
|
+
const lastNewline = slice.lastIndexOf("\n");
|
|
35
|
+
preview = lastNewline >= 0 ? slice.slice(0, lastNewline) : slice;
|
|
36
|
+
truncated = true;
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
agent: result.agent,
|
|
40
|
+
status,
|
|
41
|
+
id: result.id,
|
|
42
|
+
preview,
|
|
43
|
+
truncated,
|
|
44
|
+
...(truncated && hasPersistentArtifacts ? { fullOutputUrl: `agent://${result.id}` } : {}),
|
|
45
|
+
meta: result.outputMeta
|
|
46
|
+
? {
|
|
47
|
+
lineCount: result.outputMeta.lineCount,
|
|
48
|
+
charSize: formatBytes(result.outputMeta.charCount),
|
|
49
|
+
}
|
|
50
|
+
: undefined,
|
|
51
|
+
};
|
|
52
|
+
});
|
|
53
|
+
}
|