@cjhyy/code-shell-core 0.6.0-rc.9 → 0.7.0
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/THIRD_PARTY_NOTICES.md +206 -0
- package/dist/automation/scheduler.d.ts +13 -7
- package/dist/automation/scheduler.js +116 -37
- package/dist/capability-control/service.d.ts +2 -0
- package/dist/capability-control/service.js +4 -2
- package/dist/cc-orchestrator/agent-adapter.d.ts +4 -0
- package/dist/cc-orchestrator/agent-adapter.js +11 -1
- package/dist/cc-orchestrator/codex-session-history.d.ts +14 -1
- package/dist/cc-orchestrator/codex-session-history.js +65 -5
- package/dist/cc-orchestrator/cwd-normalize.d.ts +2 -0
- package/dist/cc-orchestrator/cwd-normalize.js +19 -0
- package/dist/cc-orchestrator/external-agent-bindings.d.ts +27 -0
- package/dist/cc-orchestrator/external-agent-bindings.js +150 -0
- package/dist/cc-orchestrator/external-agent-changes.js +22 -5
- package/dist/cc-orchestrator/external-agent-driver.d.ts +1 -0
- package/dist/cc-orchestrator/external-agent-driver.js +265 -50
- package/dist/cc-orchestrator/external-agent-session-store.d.ts +23 -0
- package/dist/cc-orchestrator/external-agent-session-store.js +146 -0
- package/dist/cc-orchestrator/session-history.d.ts +35 -0
- package/dist/cc-orchestrator/session-history.js +98 -15
- package/dist/cli/agent-server-stdio.js +9 -2
- package/dist/context/compaction.d.ts +8 -1
- package/dist/context/compaction.js +49 -4
- package/dist/context/manager.d.ts +16 -2
- package/dist/context/manager.js +103 -19
- package/dist/credentials/access.d.ts +57 -0
- package/dist/credentials/access.js +185 -0
- package/dist/credentials/index.d.ts +3 -1
- package/dist/credentials/index.js +2 -0
- package/dist/credentials/inject-credential-tool.d.ts +3 -1
- package/dist/credentials/inject-credential-tool.js +30 -11
- package/dist/credentials/oauth.d.ts +20 -0
- package/dist/credentials/oauth.js +114 -0
- package/dist/credentials/store.d.ts +1 -0
- package/dist/credentials/store.js +3 -1
- package/dist/credentials/types.d.ts +49 -3
- package/dist/credentials/use-credential-tool.d.ts +9 -1
- package/dist/credentials/use-credential-tool.js +58 -45
- package/dist/engine/engine.d.ts +23 -3
- package/dist/engine/engine.js +380 -255
- package/dist/engine/goal.d.ts +17 -0
- package/dist/engine/goal.js +16 -6
- package/dist/engine/image-policy.d.ts +6 -0
- package/dist/engine/image-policy.js +17 -6
- package/dist/engine/input-attachments.d.ts +13 -0
- package/dist/engine/input-attachments.js +398 -0
- package/dist/engine/model-facade.d.ts +5 -2
- package/dist/engine/model-facade.js +4 -4
- package/dist/engine/parse-task.d.ts +10 -0
- package/dist/engine/parse-task.js +5 -0
- package/dist/engine/run-image-input.d.ts +22 -0
- package/dist/engine/run-image-input.js +195 -0
- package/dist/engine/steer-queue.d.ts +3 -1
- package/dist/engine/steer-queue.js +10 -2
- package/dist/engine/streaming-tool-queue.d.ts +11 -7
- package/dist/engine/streaming-tool-queue.js +11 -7
- package/dist/engine/turn-loop.d.ts +37 -2
- package/dist/engine/turn-loop.js +228 -43
- package/dist/engine/types.d.ts +8 -0
- package/dist/git/worktree/crud.d.ts +69 -0
- package/dist/git/worktree/crud.js +206 -0
- package/dist/git/worktree/diff.d.ts +14 -0
- package/dist/git/worktree/diff.js +82 -0
- package/dist/git/worktree/git-exec.d.ts +7 -0
- package/dist/git/worktree/git-exec.js +51 -0
- package/dist/git/worktree/index.d.ts +5 -0
- package/dist/git/worktree/index.js +5 -0
- package/dist/git/worktree/query.d.ts +42 -0
- package/dist/git/worktree/query.js +121 -0
- package/dist/git/worktree/slug.d.ts +11 -0
- package/dist/git/worktree/slug.js +58 -0
- package/dist/git/worktree.d.ts +1 -84
- package/dist/git/worktree.js +5 -230
- package/dist/hooks/goal-stop-hook.d.ts +33 -1
- package/dist/hooks/goal-stop-hook.js +202 -34
- package/dist/index.d.ts +31 -28
- package/dist/index.js +27 -25
- package/dist/logging/logger.js +6 -6
- package/dist/logging/sanitize-messages.d.ts +10 -2
- package/dist/logging/sanitize-messages.js +21 -6
- package/dist/plugins/installer/checkUpdate.d.ts +4 -1
- package/dist/plugins/installer/checkUpdate.js +4 -2
- package/dist/plugins/installer/install.js +2 -0
- package/dist/plugins/installer/installFromSource.js +9 -1
- package/dist/plugins/installer/parseSource.d.ts +4 -1
- package/dist/plugins/installer/parseSource.js +28 -10
- package/dist/plugins/installer/sourcePath.d.ts +9 -0
- package/dist/plugins/installer/sourcePath.js +50 -0
- package/dist/plugins/installer/update.d.ts +4 -1
- package/dist/plugins/installer/update.js +5 -3
- package/dist/plugins/parseMarketplaceInput.d.ts +4 -1
- package/dist/plugins/parseMarketplaceInput.js +4 -3
- package/dist/plugins/pluginInstaller.js +24 -22
- package/dist/preset/index.d.ts +1 -0
- package/dist/preset/index.js +30 -13
- package/dist/prompt/sections/base.md +1 -1
- package/dist/protocol/chat-session-manager.d.ts +2 -0
- package/dist/protocol/chat-session-manager.js +38 -2
- package/dist/protocol/chat-session.d.ts +3 -0
- package/dist/protocol/chat-session.js +1 -0
- package/dist/protocol/client.d.ts +9 -4
- package/dist/protocol/client.js +18 -1
- package/dist/protocol/server.d.ts +30 -0
- package/dist/protocol/server.js +247 -47
- package/dist/protocol/types.d.ts +49 -0
- package/dist/protocol/types.js +6 -0
- package/dist/run/FileRunStore.js +10 -1
- package/dist/run/Heartbeat.js +12 -0
- package/dist/run/RunApprovalBackend.d.ts +3 -0
- package/dist/run/RunApprovalBackend.js +41 -6
- package/dist/run/RunLock.js +2 -0
- package/dist/run/RunManager.d.ts +2 -0
- package/dist/run/RunManager.js +64 -24
- package/dist/run/ids.d.ts +2 -0
- package/dist/run/ids.js +23 -0
- package/dist/runtime/background-shell.d.ts +1 -0
- package/dist/runtime/background-shell.js +23 -13
- package/dist/runtime/spawn-common.js +10 -0
- package/dist/services/auto-dream.d.ts +15 -4
- package/dist/services/auto-dream.js +20 -20
- package/dist/services/dream-consolidation.d.ts +2 -3
- package/dist/services/dream-consolidation.js +65 -15
- package/dist/services/extract-memories.d.ts +14 -5
- package/dist/services/extract-memories.js +20 -3
- package/dist/services/global-dream-promotion.d.ts +23 -0
- package/dist/services/global-dream-promotion.js +112 -0
- package/dist/services/memory-orchestrator.js +347 -34
- package/dist/session/memory.d.ts +61 -18
- package/dist/session/memory.js +342 -79
- package/dist/session/session-manager.d.ts +35 -1
- package/dist/session/session-manager.js +224 -4
- package/dist/session/transcript.d.ts +1 -1
- package/dist/session/transcript.js +17 -5
- package/dist/settings/manager.d.ts +1 -0
- package/dist/settings/manager.js +87 -37
- package/dist/settings/schema-export.d.ts +2 -3
- package/dist/settings/schema-export.js +2 -3
- package/dist/settings/schema.d.ts +21 -0
- package/dist/settings/schema.js +12 -0
- package/dist/skills/scanner.d.ts +3 -2
- package/dist/skills/scanner.js +12 -9
- package/dist/tool-system/builtin/agent-notifications.d.ts +11 -4
- package/dist/tool-system/builtin/agent-notifications.js +19 -7
- package/dist/tool-system/builtin/background-jobs.d.ts +35 -6
- package/dist/tool-system/builtin/background-jobs.js +116 -7
- package/dist/tool-system/builtin/background-work.d.ts +17 -18
- package/dist/tool-system/builtin/background-work.js +51 -5
- package/dist/tool-system/builtin/config.d.ts +2 -1
- package/dist/tool-system/builtin/config.js +16 -11
- package/dist/tool-system/builtin/cron-list.definition.d.ts +3 -0
- package/dist/tool-system/builtin/cron-list.definition.js +6 -0
- package/dist/tool-system/builtin/cron.d.ts +1 -2
- package/dist/tool-system/builtin/cron.js +9 -7
- package/dist/tool-system/builtin/drive-claude-code.d.ts +23 -2
- package/dist/tool-system/builtin/drive-claude-code.js +589 -48
- package/dist/tool-system/builtin/edit.js +5 -2
- package/dist/tool-system/builtin/generate-video.d.ts +1 -0
- package/dist/tool-system/builtin/generate-video.js +13 -4
- package/dist/tool-system/builtin/index.d.ts +8 -3
- package/dist/tool-system/builtin/index.js +50 -22
- package/dist/tool-system/builtin/lsp.d.ts +2 -1
- package/dist/tool-system/builtin/lsp.js +6 -3
- package/dist/tool-system/builtin/memory.js +40 -8
- package/dist/tool-system/builtin/notebook-edit.js +5 -2
- package/dist/tool-system/builtin/powershell.d.ts +5 -2
- package/dist/tool-system/builtin/powershell.js +11 -7
- package/dist/tool-system/builtin/read.js +118 -6
- package/dist/tool-system/builtin/sleep.d.ts +1 -2
- package/dist/tool-system/builtin/sleep.definition.d.ts +8 -0
- package/dist/tool-system/builtin/sleep.definition.js +28 -0
- package/dist/tool-system/builtin/sleep.js +1 -22
- package/dist/tool-system/builtin/view-image.d.ts +2 -2
- package/dist/tool-system/builtin/view-image.js +109 -19
- package/dist/tool-system/builtin/worktree.d.ts +4 -4
- package/dist/tool-system/builtin/worktree.js +297 -74
- package/dist/tool-system/builtin/write.js +5 -3
- package/dist/tool-system/context.d.ts +41 -1
- package/dist/tool-system/executor.d.ts +1 -5
- package/dist/tool-system/executor.js +94 -115
- package/dist/tool-system/mcp-manager.d.ts +31 -6
- package/dist/tool-system/mcp-manager.js +193 -78
- package/dist/tool-system/path-policy.d.ts +2 -0
- package/dist/tool-system/path-policy.js +41 -12
- package/dist/tool-system/permission.d.ts +28 -7
- package/dist/tool-system/permission.js +130 -49
- package/dist/tool-system/registry.js +11 -4
- package/dist/tool-system/tool-result-redaction.d.ts +7 -0
- package/dist/tool-system/tool-result-redaction.js +48 -0
- package/dist/tool-system/workspace-bridge.d.ts +11 -0
- package/dist/tool-system/workspace-bridge.js +1 -0
- package/dist/types.d.ts +78 -11
- package/dist/utils/toolDisplay.js +1 -1
- package/package.json +4 -3
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Built-in Read file tool.
|
|
3
3
|
*/
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
4
|
+
import { createHash } from "node:crypto";
|
|
5
|
+
import { readFile, stat, open } from "node:fs/promises";
|
|
6
|
+
import { createReadStream, existsSync } from "node:fs";
|
|
7
|
+
import { extname, isAbsolute, relative, resolve, sep } from "node:path";
|
|
6
8
|
import { fileCache } from "./file-cache.js";
|
|
7
9
|
import { toLf } from "./eol.js";
|
|
8
10
|
export const readToolDef = {
|
|
@@ -10,7 +12,8 @@ export const readToolDef = {
|
|
|
10
12
|
description: "Read a file from the local filesystem. Returns the file content with line numbers. " +
|
|
11
13
|
"By default reads up to 2000 lines from the beginning. " +
|
|
12
14
|
"Use offset and limit to read specific portions of large files. " +
|
|
13
|
-
"For
|
|
15
|
+
"For images and binary files, returns path/metadata instead of raw bytes. " +
|
|
16
|
+
"For large text files, consider using Grep first to find the relevant lines.\n\n" +
|
|
14
17
|
"Do NOT re-read a file you just edited to verify — Edit/Write would have errored " +
|
|
15
18
|
"if the change failed.\n" +
|
|
16
19
|
"Do NOT re-read a file (or the same range of a file) you've already read earlier in " +
|
|
@@ -27,18 +30,42 @@ export const readToolDef = {
|
|
|
27
30
|
},
|
|
28
31
|
};
|
|
29
32
|
const MAX_CONTENT_CHARS = 200_000;
|
|
33
|
+
const LARGE_TEXT_BYTES = 5 * 1024 * 1024;
|
|
34
|
+
const SAMPLE_BYTES = 8192;
|
|
35
|
+
const IMAGE_EXT_MIME = {
|
|
36
|
+
".png": "image/png",
|
|
37
|
+
".jpg": "image/jpeg",
|
|
38
|
+
".jpeg": "image/jpeg",
|
|
39
|
+
".gif": "image/gif",
|
|
40
|
+
".webp": "image/webp",
|
|
41
|
+
};
|
|
30
42
|
export async function readTool(args, ctx) {
|
|
31
|
-
const
|
|
32
|
-
if (!
|
|
43
|
+
const rawPath = args.file_path;
|
|
44
|
+
if (!rawPath)
|
|
33
45
|
return "Error: file_path is required";
|
|
46
|
+
const cwd = ctx?.cwd ?? process.cwd();
|
|
47
|
+
const filePath = isAbsolute(rawPath) ? rawPath : resolve(cwd, rawPath);
|
|
34
48
|
if (!existsSync(filePath))
|
|
35
49
|
return `Error: File not found: ${filePath}`;
|
|
36
50
|
try {
|
|
37
51
|
// Get file info first
|
|
38
52
|
const fileInfo = await stat(filePath);
|
|
39
53
|
const sizeKB = Math.round(fileInfo.size / 1024);
|
|
54
|
+
const sample = await readSample(filePath, Math.min(fileInfo.size, SAMPLE_BYTES));
|
|
55
|
+
const binary = detectBinary(filePath, sample);
|
|
56
|
+
if (binary) {
|
|
57
|
+
const sha256 = await sha256File(filePath);
|
|
58
|
+
return formatBinaryReadResult({
|
|
59
|
+
filePath,
|
|
60
|
+
cwd,
|
|
61
|
+
size: fileInfo.size,
|
|
62
|
+
sha256,
|
|
63
|
+
mime: binary.mime,
|
|
64
|
+
isImage: binary.kind === "image",
|
|
65
|
+
});
|
|
66
|
+
}
|
|
40
67
|
// Skip binary or excessively large files
|
|
41
|
-
if (fileInfo.size >
|
|
68
|
+
if (fileInfo.size > LARGE_TEXT_BYTES) {
|
|
42
69
|
return `Error: File is too large (${sizeKB}KB). Use Grep to search for specific content, or provide offset and limit to read a portion.`;
|
|
43
70
|
}
|
|
44
71
|
// Try cache first, fall back to disk read
|
|
@@ -76,3 +103,88 @@ export async function readTool(args, ctx) {
|
|
|
76
103
|
return `Error reading file: ${err.message}`;
|
|
77
104
|
}
|
|
78
105
|
}
|
|
106
|
+
async function readSample(filePath, length) {
|
|
107
|
+
if (length <= 0)
|
|
108
|
+
return Buffer.alloc(0);
|
|
109
|
+
const handle = await open(filePath, "r");
|
|
110
|
+
try {
|
|
111
|
+
const buffer = Buffer.alloc(length);
|
|
112
|
+
const { bytesRead } = await handle.read(buffer, 0, length, 0);
|
|
113
|
+
return buffer.subarray(0, bytesRead);
|
|
114
|
+
}
|
|
115
|
+
finally {
|
|
116
|
+
await handle.close();
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
function detectBinary(filePath, sample) {
|
|
120
|
+
const imageMime = detectImageMime(filePath, sample);
|
|
121
|
+
if (imageMime)
|
|
122
|
+
return { kind: "image", mime: imageMime };
|
|
123
|
+
if (sample.length === 0)
|
|
124
|
+
return null;
|
|
125
|
+
if (sample.includes(0))
|
|
126
|
+
return { kind: "binary" };
|
|
127
|
+
try {
|
|
128
|
+
new TextDecoder("utf-8", { fatal: true }).decode(sample);
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
return { kind: "binary" };
|
|
132
|
+
}
|
|
133
|
+
let controls = 0;
|
|
134
|
+
for (const byte of sample) {
|
|
135
|
+
if (byte < 32 && byte !== 9 && byte !== 10 && byte !== 12 && byte !== 13)
|
|
136
|
+
controls += 1;
|
|
137
|
+
}
|
|
138
|
+
return controls > Math.max(8, sample.length * 0.01) ? { kind: "binary" } : null;
|
|
139
|
+
}
|
|
140
|
+
function detectImageMime(filePath, sample) {
|
|
141
|
+
if (sample.length >= 8 &&
|
|
142
|
+
sample[0] === 0x89 &&
|
|
143
|
+
sample[1] === 0x50 &&
|
|
144
|
+
sample[2] === 0x4e &&
|
|
145
|
+
sample[3] === 0x47 &&
|
|
146
|
+
sample[4] === 0x0d &&
|
|
147
|
+
sample[5] === 0x0a &&
|
|
148
|
+
sample[6] === 0x1a &&
|
|
149
|
+
sample[7] === 0x0a) {
|
|
150
|
+
return "image/png";
|
|
151
|
+
}
|
|
152
|
+
if (sample.length >= 3 && sample[0] === 0xff && sample[1] === 0xd8 && sample[2] === 0xff) {
|
|
153
|
+
return "image/jpeg";
|
|
154
|
+
}
|
|
155
|
+
const header = sample.subarray(0, 12).toString("ascii");
|
|
156
|
+
if (header.startsWith("GIF87a") || header.startsWith("GIF89a"))
|
|
157
|
+
return "image/gif";
|
|
158
|
+
if (header.startsWith("RIFF") && header.slice(8, 12) === "WEBP")
|
|
159
|
+
return "image/webp";
|
|
160
|
+
return IMAGE_EXT_MIME[extname(filePath).toLowerCase()];
|
|
161
|
+
}
|
|
162
|
+
async function sha256File(filePath) {
|
|
163
|
+
const hash = createHash("sha256");
|
|
164
|
+
for await (const chunk of createReadStream(filePath)) {
|
|
165
|
+
hash.update(chunk);
|
|
166
|
+
}
|
|
167
|
+
return hash.digest("hex");
|
|
168
|
+
}
|
|
169
|
+
function formatBinaryReadResult(input) {
|
|
170
|
+
const path = displayPath(input.filePath, input.cwd);
|
|
171
|
+
const lines = [
|
|
172
|
+
input.isImage ? "Image file (not displayed by Read)." : "Binary file (not displayed by Read).",
|
|
173
|
+
`Path: ${path}`,
|
|
174
|
+
`Absolute path: ${input.filePath}`,
|
|
175
|
+
...(input.mime ? [`MIME: ${input.mime}`] : []),
|
|
176
|
+
`Size: ${input.size} bytes`,
|
|
177
|
+
`SHA-256: ${input.sha256}`,
|
|
178
|
+
];
|
|
179
|
+
if (input.isImage) {
|
|
180
|
+
lines.push(`Use view_image({ path: "${path}" }) to inspect pixels.`);
|
|
181
|
+
}
|
|
182
|
+
return lines.join("\n");
|
|
183
|
+
}
|
|
184
|
+
function displayPath(filePath, cwd) {
|
|
185
|
+
const rel = relative(cwd, filePath);
|
|
186
|
+
if (rel && rel !== ".." && !rel.startsWith(`..${sep}`)) {
|
|
187
|
+
return sep === "\\" ? rel.replace(/\\/g, "/") : rel;
|
|
188
|
+
}
|
|
189
|
+
return filePath;
|
|
190
|
+
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* SleepTool — pause execution for a specified duration.
|
|
3
3
|
*/
|
|
4
|
-
|
|
5
|
-
export declare const sleepToolDef: ToolDefinition;
|
|
4
|
+
export { sleepToolDef } from "./sleep.definition.js";
|
|
6
5
|
export declare function sleepTool(args: Record<string, unknown>): Promise<string>;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sleep tool metadata.
|
|
3
|
+
*
|
|
4
|
+
* Kept separate from the executor so registration and presets can consume the
|
|
5
|
+
* definition without importing implementation code.
|
|
6
|
+
*/
|
|
7
|
+
import type { ToolDefinition } from "../../types.js";
|
|
8
|
+
export declare const sleepToolDef: ToolDefinition;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sleep tool metadata.
|
|
3
|
+
*
|
|
4
|
+
* Kept separate from the executor so registration and presets can consume the
|
|
5
|
+
* definition without importing implementation code.
|
|
6
|
+
*/
|
|
7
|
+
export const sleepToolDef = {
|
|
8
|
+
name: "Sleep",
|
|
9
|
+
description: "Pause execution for a brief, deterministic wait (e.g. letting a just-started service settle for a few seconds). " +
|
|
10
|
+
"Do NOT use Sleep to poll for or wait on background work (background shells, async sub-agents, video generation): " +
|
|
11
|
+
"the system wakes you automatically when that work completes — just end your turn instead of looping Sleep. " +
|
|
12
|
+
"If you want a safety net in case a background task hangs and never signals completion, do NOT loop Sleep either — " +
|
|
13
|
+
"instead end your turn and schedule a one-shot self-wakeup with CronCreate " +
|
|
14
|
+
"({ schedule: '5m', once: true, continueInSession: true, permissionLevel: 'read-only', " +
|
|
15
|
+
"prompt: 'check whether <that task> finished; if still running, wait again' }). " +
|
|
16
|
+
"That returns control to you at the interval without burning a turn spinning. " +
|
|
17
|
+
"Maximum duration is 300 seconds (5 minutes).",
|
|
18
|
+
inputSchema: {
|
|
19
|
+
type: "object",
|
|
20
|
+
properties: {
|
|
21
|
+
seconds: {
|
|
22
|
+
type: "number",
|
|
23
|
+
description: "Number of seconds to sleep (max 300)",
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
required: ["seconds"],
|
|
27
|
+
},
|
|
28
|
+
};
|
|
@@ -1,28 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* SleepTool — pause execution for a specified duration.
|
|
3
3
|
*/
|
|
4
|
-
export
|
|
5
|
-
name: "Sleep",
|
|
6
|
-
description: "Pause execution for a brief, deterministic wait (e.g. letting a just-started service settle for a few seconds). " +
|
|
7
|
-
"Do NOT use Sleep to poll for or wait on background work (background shells, async sub-agents, video generation): " +
|
|
8
|
-
"the system wakes you automatically when that work completes — just end your turn instead of looping Sleep. " +
|
|
9
|
-
"If you want a safety net in case a background task hangs and never signals completion, do NOT loop Sleep either — " +
|
|
10
|
-
"instead end your turn and schedule a one-shot self-wakeup with CronCreate " +
|
|
11
|
-
"({ schedule: '5m', once: true, continueInSession: true, permissionLevel: 'read-only', " +
|
|
12
|
-
"prompt: 'check whether <that task> finished; if still running, wait again' }). " +
|
|
13
|
-
"That returns control to you at the interval without burning a turn spinning. " +
|
|
14
|
-
"Maximum duration is 300 seconds (5 minutes).",
|
|
15
|
-
inputSchema: {
|
|
16
|
-
type: "object",
|
|
17
|
-
properties: {
|
|
18
|
-
seconds: {
|
|
19
|
-
type: "number",
|
|
20
|
-
description: "Number of seconds to sleep (max 300)",
|
|
21
|
-
},
|
|
22
|
-
},
|
|
23
|
-
required: ["seconds"],
|
|
24
|
-
},
|
|
25
|
-
};
|
|
4
|
+
export { sleepToolDef } from "./sleep.definition.js";
|
|
26
5
|
export async function sleepTool(args) {
|
|
27
6
|
const seconds = Math.min(Math.max(Number(args.seconds) || 1, 0.1), 300);
|
|
28
7
|
const signal = args.__signal;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Built-in view_image tool —
|
|
3
|
-
* 回传进上下文,让 vision 模型「看」它(对照 codex 的 view_image)。
|
|
2
|
+
* Built-in view_image tool — 把一个本地图片文件或历史图片以 base64 image
|
|
3
|
+
* ContentBlock 回传进上下文,让 vision 模型「看」它(对照 codex 的 view_image)。
|
|
4
4
|
*
|
|
5
5
|
* 典型用法:模型先写 SVG/Mermaid 并用 shell 转成 PNG,再调 view_image(png)
|
|
6
6
|
* 检查图画对没有(标签是否重叠、文字是否溢出),不对就改源再重转。
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Built-in view_image tool —
|
|
3
|
-
* 回传进上下文,让 vision 模型「看」它(对照 codex 的 view_image)。
|
|
2
|
+
* Built-in view_image tool — 把一个本地图片文件或历史图片以 base64 image
|
|
3
|
+
* ContentBlock 回传进上下文,让 vision 模型「看」它(对照 codex 的 view_image)。
|
|
4
4
|
*
|
|
5
5
|
* 典型用法:模型先写 SVG/Mermaid 并用 shell 转成 PNG,再调 view_image(png)
|
|
6
6
|
* 检查图画对没有(标签是否重叠、文字是否溢出),不对就改源再重转。
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
import { readFile, stat } from "node:fs/promises";
|
|
15
15
|
import { extname, isAbsolute, resolve } from "node:path";
|
|
16
16
|
import { capabilitiesFor } from "../../llm/capabilities/index.js";
|
|
17
|
+
import { collectBase64Images, findImageByNumber } from "../../context/compaction.js";
|
|
17
18
|
const MAX_BYTES = 5 * 1024 * 1024; // 5MB —— vision 模型按 tile 计 token,大图无益且撑大请求
|
|
18
19
|
const MEDIA_TYPES = {
|
|
19
20
|
".png": "image/png",
|
|
@@ -24,11 +25,11 @@ const MEDIA_TYPES = {
|
|
|
24
25
|
};
|
|
25
26
|
export const viewImageToolDef = {
|
|
26
27
|
name: "view_image",
|
|
27
|
-
description: "Load
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
"
|
|
28
|
+
description: "Load an image into the conversation so you can SEE it (vision). Pass path to view a " +
|
|
29
|
+
"workspace image file, or pass imageNumber to retrieve the original image behind an " +
|
|
30
|
+
"earlier [image #N, already provided] history placeholder. Use exactly one of path or " +
|
|
31
|
+
"imageNumber. File paths support PNG/JPEG/GIF/WebP only; convert SVG/PDF to PNG first. " +
|
|
32
|
+
"Requires a vision-capable model; otherwise the image is skipped.",
|
|
32
33
|
inputSchema: {
|
|
33
34
|
type: "object",
|
|
34
35
|
properties: {
|
|
@@ -36,14 +37,40 @@ export const viewImageToolDef = {
|
|
|
36
37
|
type: "string",
|
|
37
38
|
description: "Path to the image file (absolute, or relative to the working directory).",
|
|
38
39
|
},
|
|
40
|
+
imageNumber: {
|
|
41
|
+
type: "number",
|
|
42
|
+
description: "Image history number N from an earlier [image #N, already provided] placeholder.",
|
|
43
|
+
},
|
|
44
|
+
detail: {
|
|
45
|
+
type: "string",
|
|
46
|
+
enum: ["low", "standard", "high"],
|
|
47
|
+
description: "Optional image detail preference. Accepted for compatibility; current providers use the runtime image detail default.",
|
|
48
|
+
},
|
|
39
49
|
},
|
|
40
|
-
required: ["path"],
|
|
41
50
|
},
|
|
42
51
|
};
|
|
43
52
|
export async function viewImageTool(args, ctx) {
|
|
44
53
|
const rawPath = args.path;
|
|
54
|
+
const rawImageNumber = args.imageNumber;
|
|
55
|
+
const hasPath = typeof rawPath === "string" && rawPath.trim().length > 0;
|
|
56
|
+
const hasImageNumber = rawImageNumber !== undefined && rawImageNumber !== null;
|
|
57
|
+
if (hasPath === hasImageNumber) {
|
|
58
|
+
return "Error: provide exactly one of path or imageNumber";
|
|
59
|
+
}
|
|
60
|
+
const detail = args.detail;
|
|
61
|
+
if (detail !== undefined && detail !== "low" && detail !== "standard" && detail !== "high") {
|
|
62
|
+
return "Error: detail must be one of low, standard, high";
|
|
63
|
+
}
|
|
64
|
+
if (hasImageNumber) {
|
|
65
|
+
if (typeof rawImageNumber !== "number" ||
|
|
66
|
+
!Number.isSafeInteger(rawImageNumber) ||
|
|
67
|
+
rawImageNumber <= 0) {
|
|
68
|
+
return "Error: imageNumber must be a positive integer";
|
|
69
|
+
}
|
|
70
|
+
return viewHistoricalImage(rawImageNumber, ctx);
|
|
71
|
+
}
|
|
45
72
|
if (typeof rawPath !== "string" || !rawPath.trim()) {
|
|
46
|
-
return "Error: path
|
|
73
|
+
return "Error: path must be a non-empty string";
|
|
47
74
|
}
|
|
48
75
|
const cwd = ctx?.cwd ?? process.cwd();
|
|
49
76
|
const abs = isAbsolute(rawPath) ? rawPath : resolve(cwd, rawPath);
|
|
@@ -51,13 +78,7 @@ export async function viewImageTool(args, ctx) {
|
|
|
51
78
|
// 缺 llmConfig 时按「非视觉」处理(fail closed),对齐 DEFAULT_CAPABILITY
|
|
52
79
|
// 的保守姿态:运行时 ToolContext.llmConfig 必有,缺失只发生在测试 / 异常
|
|
53
80
|
// 装配下,此时绝不把 base64 读进上下文。
|
|
54
|
-
|
|
55
|
-
if (!ctx?.llmConfig)
|
|
56
|
-
return false;
|
|
57
|
-
const kind = (ctx.llmConfig.providerKind ?? ctx.llmConfig.provider);
|
|
58
|
-
return capabilitiesFor(kind, ctx.llmConfig.model).supportsVision;
|
|
59
|
-
})();
|
|
60
|
-
if (!supportsVision) {
|
|
81
|
+
if (!supportsVision(ctx)) {
|
|
61
82
|
return `[图片未加载: ${abs} —— 当前模型不支持视觉输入,已跳过。切换到 vision 模型后再 view_image。]`;
|
|
62
83
|
}
|
|
63
84
|
// 闸门 2:格式 gate
|
|
@@ -88,9 +109,78 @@ export async function viewImageTool(args, ctx) {
|
|
|
88
109
|
const data = buf.toString("base64");
|
|
89
110
|
const kb = Math.round(buf.length / 1024);
|
|
90
111
|
return {
|
|
91
|
-
contentBlocks: [
|
|
92
|
-
{ type: "image", source: { type: "base64", media_type: mediaType, data } },
|
|
93
|
-
],
|
|
112
|
+
contentBlocks: [{ type: "image", source: { type: "base64", media_type: mediaType, data } }],
|
|
94
113
|
result: `[已加载图片: ${abs} (${mediaType}, ${kb} KB)]`,
|
|
95
114
|
};
|
|
96
115
|
}
|
|
116
|
+
function supportsVision(ctx) {
|
|
117
|
+
if (!ctx?.llmConfig)
|
|
118
|
+
return false;
|
|
119
|
+
const kind = (ctx.llmConfig.providerKind ?? ctx.llmConfig.provider);
|
|
120
|
+
return capabilitiesFor(kind, ctx.llmConfig.model).supportsVision;
|
|
121
|
+
}
|
|
122
|
+
async function viewHistoricalImage(imageNumber, ctx) {
|
|
123
|
+
if (!supportsVision(ctx)) {
|
|
124
|
+
return `[图片未取回: image #${imageNumber} —— 当前模型不支持视觉输入,已跳过。切换到 vision 模型后再 view_image。]`;
|
|
125
|
+
}
|
|
126
|
+
const sessionManager = ctx?.engine?.getSessionManager?.();
|
|
127
|
+
const sessionId = ctx?.sessionId;
|
|
128
|
+
if (!sessionManager || !sessionId) {
|
|
129
|
+
return `Error: 无法取回 image #${imageNumber}: 当前工具上下文没有可读取的 session 历史。`;
|
|
130
|
+
}
|
|
131
|
+
let messages;
|
|
132
|
+
try {
|
|
133
|
+
messages = sessionManager.resume(sessionId).transcript.toMessages();
|
|
134
|
+
}
|
|
135
|
+
catch (err) {
|
|
136
|
+
return `Error: 无法读取 session 历史以取回 image #${imageNumber}: ${err.message}`;
|
|
137
|
+
}
|
|
138
|
+
const images = collectBase64Images(messages);
|
|
139
|
+
const found = findImageByNumber(messages, imageNumber);
|
|
140
|
+
if (!found) {
|
|
141
|
+
return `Error: 未找到 image #${imageNumber}; 当前 session 历史中共有 ${images.length} 张可取回图片。`;
|
|
142
|
+
}
|
|
143
|
+
const block = normalizeImageBlockForReturn(found.block);
|
|
144
|
+
const size = decodedImageBlockBytes(block);
|
|
145
|
+
if (size !== undefined && size > MAX_BYTES) {
|
|
146
|
+
const mb = (size / 1024 / 1024).toFixed(1);
|
|
147
|
+
return `[图片未取回: image #${imageNumber} —— 图片过大 (${mb} MB > 5 MB),请先压缩或缩放原图。]`;
|
|
148
|
+
}
|
|
149
|
+
const kb = size === undefined ? "unknown" : String(Math.round(size / 1024));
|
|
150
|
+
const mediaType = mediaTypeOfImageBlock(block) ?? "base64 image";
|
|
151
|
+
return {
|
|
152
|
+
contentBlocks: [block],
|
|
153
|
+
result: `[已取回 image #${imageNumber}: ${mediaType}, ${kb} KB]`,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
function normalizeImageBlockForReturn(block) {
|
|
157
|
+
if (block.type === "image" && block.source?.type === "base64")
|
|
158
|
+
return block;
|
|
159
|
+
const source = openAIDataUrlImageSource(block);
|
|
160
|
+
return source ? { type: "image", source } : block;
|
|
161
|
+
}
|
|
162
|
+
function decodedImageBlockBytes(block) {
|
|
163
|
+
const data = base64DataOfImageBlock(block);
|
|
164
|
+
if (!data)
|
|
165
|
+
return undefined;
|
|
166
|
+
return Buffer.byteLength(data, "base64");
|
|
167
|
+
}
|
|
168
|
+
function base64DataOfImageBlock(block) {
|
|
169
|
+
if (block.type === "image" && block.source?.type === "base64") {
|
|
170
|
+
return block.source.data;
|
|
171
|
+
}
|
|
172
|
+
return openAIDataUrlImageSource(block)?.data;
|
|
173
|
+
}
|
|
174
|
+
function mediaTypeOfImageBlock(block) {
|
|
175
|
+
if (block.type === "image" && block.source?.type === "base64") {
|
|
176
|
+
return block.source.media_type;
|
|
177
|
+
}
|
|
178
|
+
return openAIDataUrlImageSource(block)?.media_type;
|
|
179
|
+
}
|
|
180
|
+
function openAIDataUrlImageSource(block) {
|
|
181
|
+
const maybeOpenAI = block;
|
|
182
|
+
const match = maybeOpenAI.image_url?.url?.match(/^data:(image\/[^;,]+);base64,(.+)$/i);
|
|
183
|
+
if (maybeOpenAI.type !== "image_url" || !match?.[1] || !match[2])
|
|
184
|
+
return undefined;
|
|
185
|
+
return { type: "base64", media_type: match[1], data: match[2] };
|
|
186
|
+
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* EnterWorktree / ExitWorktree tools —
|
|
2
|
+
* EnterWorktree / ExitWorktree tools — switch a session between git workspaces.
|
|
3
3
|
*/
|
|
4
4
|
import type { ToolDefinition } from "../../types.js";
|
|
5
5
|
import type { ToolContext } from "../context.js";
|
|
6
|
-
|
|
7
|
-
export declare function
|
|
6
|
+
export declare const switchSessionWorkspaceToolDef: ToolDefinition;
|
|
7
|
+
export declare function switchSessionWorkspaceTool(args: Record<string, unknown>, ctx?: ToolContext): Promise<string>;
|
|
8
8
|
export declare const enterWorktreeToolDef: ToolDefinition;
|
|
9
9
|
export declare function enterWorktreeTool(args: Record<string, unknown>, ctx?: ToolContext): Promise<string>;
|
|
10
10
|
export declare const exitWorktreeToolDef: ToolDefinition;
|
|
11
|
-
export declare function exitWorktreeTool(args: Record<string, unknown
|
|
11
|
+
export declare function exitWorktreeTool(args: Record<string, unknown>, ctx?: ToolContext): Promise<string>;
|