@eddyskywalker/dsh-chatgpt-subscription 0.58.0 → 0.59.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/CHANGELOG.md +17 -0
- package/lib/client.js +136 -23
- package/lib/client.js.map +1 -1
- package/lib/codemode.js +1565 -0
- package/lib/index.js +414 -64
- package/lib/types/client/CodexImageToolView.d.ts.map +1 -1
- package/lib/types/client/progress-updates/progress-row.d.ts.map +1 -1
- package/lib/types/client/styles.d.ts.map +1 -1
- package/lib/types/compat.d.ts +10 -1
- package/lib/types/compat.d.ts.map +1 -1
- package/lib/types/entry-codemode.d.ts +37 -0
- package/lib/types/entry-codemode.d.ts.map +1 -0
- package/lib/types/host/agent-preset.d.ts +24 -2
- package/lib/types/host/agent-preset.d.ts.map +1 -1
- package/lib/types/host/antigravity/image-tool.d.ts +10 -2
- package/lib/types/host/antigravity/image-tool.d.ts.map +1 -1
- package/lib/types/host/cache-warmup/install.d.ts +10 -4
- package/lib/types/host/cache-warmup/install.d.ts.map +1 -1
- package/lib/types/host/cache-warmup/projection.d.ts +17 -0
- package/lib/types/host/cache-warmup/projection.d.ts.map +1 -1
- package/lib/types/host/codemode/discovery.d.ts +33 -0
- package/lib/types/host/codemode/discovery.d.ts.map +1 -0
- package/lib/types/host/codemode/executor.d.ts +140 -0
- package/lib/types/host/codemode/executor.d.ts.map +1 -0
- package/lib/types/host/codemode/retention.d.ts +34 -0
- package/lib/types/host/codemode/retention.d.ts.map +1 -0
- package/lib/types/host/codemode/rules.d.ts +183 -0
- package/lib/types/host/codemode/rules.d.ts.map +1 -0
- package/lib/types/host/codemode/scope.d.ts +59 -0
- package/lib/types/host/codemode/scope.d.ts.map +1 -0
- package/lib/types/host/codemode/transport.d.ts +258 -0
- package/lib/types/host/codemode/transport.d.ts.map +1 -0
- package/lib/types/host/codex-images.d.ts +12 -0
- package/lib/types/host/codex-images.d.ts.map +1 -1
- package/package.json +6 -1
- package/presets/dispatch/README.md +12 -3
- package/presets/dispatch/agent.cordis.yml +10 -9
- package/presets/dispatch/preset.yml +1 -1
package/lib/index.js
CHANGED
|
@@ -9,7 +9,7 @@ import { defineTool } from "@deepseek-ai/dsh-tools";
|
|
|
9
9
|
import { z } from "zod";
|
|
10
10
|
import z$1 from "@deepseek-ai/schemastery";
|
|
11
11
|
import fs, { chmod, lstat, mkdir, open, readFile, realpath, rename, stat, unlink, writeFile } from "node:fs/promises";
|
|
12
|
-
import path, { basename, dirname, join, relative, resolve } from "node:path";
|
|
12
|
+
import path, { basename, dirname, extname, join, relative, resolve } from "node:path";
|
|
13
13
|
import os, { homedir, platform, userInfo } from "node:os";
|
|
14
14
|
import { constants, createCipheriv, createDecipheriv, createHash, createHmac, publicEncrypt, randomBytes, randomUUID } from "node:crypto";
|
|
15
15
|
import { isDeepStrictEqual } from "node:util";
|
|
@@ -807,11 +807,20 @@ const ROUTE_PREFIX$5 = "/api/dsh-chatgpt-subscription";
|
|
|
807
807
|
* `test/responses-client.test.ts` asserts the two agree, so a release that
|
|
808
808
|
* forgets this constant fails the suite instead of shipping quietly.
|
|
809
809
|
*/
|
|
810
|
-
const PLUGIN_VERSION = "0.
|
|
810
|
+
const PLUGIN_VERSION = "0.59.0";
|
|
811
811
|
const CODEX_CHATGPT_PROVIDER_ID = "codex-chatgpt";
|
|
812
812
|
const CODEX_API_BASE = "https://chatgpt.com/backend-api/codex";
|
|
813
813
|
const CODEX_RESPONSES_URL = `${CODEX_API_BASE}/responses`;
|
|
814
814
|
const CODEX_IMAGE_GENERATION_URL = `${CODEX_API_BASE}/images/generations`;
|
|
815
|
+
/**
|
|
816
|
+
* The edit route: the same image surface, addressed for reference-image input.
|
|
817
|
+
*
|
|
818
|
+
* A generation has nothing to modify and goes to `/images/generations`; a
|
|
819
|
+
* request that names reference images is an EDIT and goes here, with those
|
|
820
|
+
* images carried inline in a JSON `images` array. It is a separate route, not a
|
|
821
|
+
* flag on the generation body, so the two are stated as two constants.
|
|
822
|
+
*/
|
|
823
|
+
const CODEX_IMAGE_EDIT_URL = `${CODEX_API_BASE}/images/edits`;
|
|
815
824
|
const CODEX_SEARCH_URL = `${CODEX_API_BASE}/alpha/search`;
|
|
816
825
|
const CODEX_USAGE_URL = "https://chatgpt.com/backend-api/wham/usage";
|
|
817
826
|
/**
|
|
@@ -6099,6 +6108,33 @@ function normalizeGenerateOptions(options) {
|
|
|
6099
6108
|
}
|
|
6100
6109
|
//#endregion
|
|
6101
6110
|
//#region src/host/codex-images.ts
|
|
6111
|
+
/**
|
|
6112
|
+
* Output canvases the Codex image endpoint accepts in `size`.
|
|
6113
|
+
*
|
|
6114
|
+
* These are the official client's own three canvases plus the default `auto`,
|
|
6115
|
+
* which lets the service pick. A size outside this set is not negotiated: the
|
|
6116
|
+
* backend answers it with a 400, so the tool refuses it before the request.
|
|
6117
|
+
*/
|
|
6118
|
+
const CODEX_IMAGE_SIZES = [
|
|
6119
|
+
"auto",
|
|
6120
|
+
"1024x1024",
|
|
6121
|
+
"1536x1024",
|
|
6122
|
+
"1024x1536"
|
|
6123
|
+
];
|
|
6124
|
+
/** Render effort the endpoint accepts in `quality`; `auto` is the service's own default. */
|
|
6125
|
+
const CODEX_IMAGE_QUALITIES = [
|
|
6126
|
+
"auto",
|
|
6127
|
+
"low",
|
|
6128
|
+
"medium",
|
|
6129
|
+
"high"
|
|
6130
|
+
];
|
|
6131
|
+
/** Frame budget of an animated GIF the endpoint accepts. */
|
|
6132
|
+
const GIF_MIN_FRAMES = 2;
|
|
6133
|
+
const GIF_MAX_FRAMES = 16;
|
|
6134
|
+
const GIF_MIN_FRAME_MS = 20;
|
|
6135
|
+
const GIF_MAX_FRAME_MS = 2e3;
|
|
6136
|
+
/** How much of an upstream error body is quoted back to the caller. */
|
|
6137
|
+
const MAX_UPSTREAM_EXCERPT_CHARS = 300;
|
|
6102
6138
|
const PNG_SIGNATURE = [
|
|
6103
6139
|
137,
|
|
6104
6140
|
80,
|
|
@@ -6109,16 +6145,78 @@ const PNG_SIGNATURE = [
|
|
|
6109
6145
|
26,
|
|
6110
6146
|
10
|
|
6111
6147
|
];
|
|
6148
|
+
/**
|
|
6149
|
+
* Magic-byte checks per media type.
|
|
6150
|
+
*
|
|
6151
|
+
* An extension is a claim about a file's name; the bytes are the claim the file
|
|
6152
|
+
* itself makes. Both are checked, because sending a file whose declared type the
|
|
6153
|
+
* service cannot decode returns a plain upstream error rather than an image.
|
|
6154
|
+
*/
|
|
6155
|
+
const SIGNATURES$1 = {
|
|
6156
|
+
"image/png": (bytes) => bytes[0] === 137 && bytes[1] === 80 && bytes[2] === 78 && bytes[3] === 71,
|
|
6157
|
+
"image/jpeg": (bytes) => bytes[0] === 255 && bytes[1] === 216 && bytes[2] === 255,
|
|
6158
|
+
"image/webp": (bytes) => bytes[0] === 82 && bytes[1] === 73 && bytes[2] === 70 && bytes[3] === 70 && bytes[8] === 87 && bytes[9] === 69 && bytes[10] === 66 && bytes[11] === 80,
|
|
6159
|
+
"image/gif": (bytes) => bytes[0] === 71 && bytes[1] === 73 && bytes[2] === 70
|
|
6160
|
+
};
|
|
6161
|
+
/**
|
|
6162
|
+
* Extensions a reference image may carry.
|
|
6163
|
+
*
|
|
6164
|
+
* Deliberately narrower than {@link SIGNATURES}: an edit accepts stills only.
|
|
6165
|
+
* Animated GIF input is not part of the edit wire contract, so it is refused
|
|
6166
|
+
* here instead of being sent to an endpoint that does not take it.
|
|
6167
|
+
*/
|
|
6168
|
+
const REFERENCE_EXTENSIONS = {
|
|
6169
|
+
".png": "image/png",
|
|
6170
|
+
".jpg": "image/jpeg",
|
|
6171
|
+
".jpeg": "image/jpeg",
|
|
6172
|
+
".webp": "image/webp"
|
|
6173
|
+
};
|
|
6112
6174
|
function createCodexImageTool(oauth, attachments, options = {}) {
|
|
6113
6175
|
const fetchFn = options.fetchFn ?? fetch;
|
|
6114
6176
|
return defineTool({
|
|
6115
6177
|
name: CODEX_IMAGE_TOOL_NAME,
|
|
6116
|
-
description: "Generate a PNG image
|
|
6117
|
-
parameters: {
|
|
6118
|
-
|
|
6119
|
-
|
|
6120
|
-
|
|
6121
|
-
|
|
6178
|
+
description: "Generate or edit a PNG or GIF image with the signed-in ChatGPT subscription-backed Codex image endpoint. Describe the subject, style, composition and any text to render in the prompt; pass referenceImagePaths to edit local PNG/JPEG/WebP images, and gif to animate the result. Scheduling: call this tool yourself for a single generation, an edit, or a retry - never create a subagent just to generate one image. Delegate only when the creative task needs several candidate images or is large enough to be a multi-step task of its own.",
|
|
6179
|
+
parameters: {
|
|
6180
|
+
prompt: {
|
|
6181
|
+
type: "string",
|
|
6182
|
+
required: true,
|
|
6183
|
+
description: "A detailed image generation prompt."
|
|
6184
|
+
},
|
|
6185
|
+
size: {
|
|
6186
|
+
type: "string",
|
|
6187
|
+
enum: CODEX_IMAGE_SIZES,
|
|
6188
|
+
description: "Output canvas. Omit or pass auto to let the service choose. 1024x1024 is square, 1536x1024 landscape and 1024x1536 portrait."
|
|
6189
|
+
},
|
|
6190
|
+
quality: {
|
|
6191
|
+
type: "string",
|
|
6192
|
+
enum: CODEX_IMAGE_QUALITIES,
|
|
6193
|
+
description: "Render effort. Omit or pass auto for the service default; low is fastest and high is most detailed."
|
|
6194
|
+
},
|
|
6195
|
+
transparentBackground: {
|
|
6196
|
+
type: "boolean",
|
|
6197
|
+
description: "true for a transparent background (PNG or GIF alpha), false for an explicitly opaque one. Omit it to leave the background decision to the service."
|
|
6198
|
+
},
|
|
6199
|
+
referenceImagePaths: {
|
|
6200
|
+
type: "array",
|
|
6201
|
+
items: { type: "string" },
|
|
6202
|
+
description: "Up to 5 local PNG, JPEG or WebP files to edit. Absolute paths recommended. The request becomes an edit against those images; nothing else on disk is read."
|
|
6203
|
+
},
|
|
6204
|
+
gif: {
|
|
6205
|
+
type: "object",
|
|
6206
|
+
additionalProperties: false,
|
|
6207
|
+
description: "Ask for an animated GIF. An empty object is allowed and lets the service choose the frames.",
|
|
6208
|
+
properties: {
|
|
6209
|
+
frameCount: {
|
|
6210
|
+
type: "integer",
|
|
6211
|
+
description: "Number of frames, 2 to 16."
|
|
6212
|
+
},
|
|
6213
|
+
frameDurationMs: {
|
|
6214
|
+
type: "integer",
|
|
6215
|
+
description: "Milliseconds each frame is shown, 20 to 2000."
|
|
6216
|
+
}
|
|
6217
|
+
}
|
|
6218
|
+
}
|
|
6219
|
+
},
|
|
6122
6220
|
output: {
|
|
6123
6221
|
schema: {
|
|
6124
6222
|
type: "object",
|
|
@@ -6143,7 +6241,7 @@ function createCodexImageTool(oauth, attachments, options = {}) {
|
|
|
6143
6241
|
},
|
|
6144
6242
|
mediaType: {
|
|
6145
6243
|
type: "string",
|
|
6146
|
-
enum: ["image/png"],
|
|
6244
|
+
enum: ["image/png", "image/gif"],
|
|
6147
6245
|
required: true
|
|
6148
6246
|
},
|
|
6149
6247
|
bytes: {
|
|
@@ -6190,34 +6288,52 @@ function createCodexImageTool(oauth, attachments, options = {}) {
|
|
|
6190
6288
|
async execute(args, exec) {
|
|
6191
6289
|
const prompt = args.prompt.trim();
|
|
6192
6290
|
if (prompt === "") throw new HarnessError("Image prompt cannot be empty.", "CODEX_IMAGE_INVALID_PROMPT");
|
|
6193
|
-
|
|
6291
|
+
const gif = readGifOptions(args.gif);
|
|
6292
|
+
const references = await readReferenceImages(args.referenceImagePaths ?? [], attachments);
|
|
6293
|
+
assertOutputTypeSupported(attachments, gif === void 0 ? "image/png" : "image/gif");
|
|
6294
|
+
const body = buildImageBody({
|
|
6295
|
+
prompt,
|
|
6296
|
+
size: args.size ?? "auto",
|
|
6297
|
+
quality: args.quality ?? "auto",
|
|
6298
|
+
...args.transparentBackground !== void 0 ? { transparentBackground: args.transparentBackground } : {},
|
|
6299
|
+
...gif !== void 0 ? { gif } : {},
|
|
6300
|
+
references
|
|
6301
|
+
});
|
|
6302
|
+
const url = references.length > 0 ? CODEX_IMAGE_EDIT_URL : CODEX_IMAGE_GENERATION_URL;
|
|
6303
|
+
const turnId = String(exec.callId);
|
|
6194
6304
|
let credentials = await imageCredentials(oauth);
|
|
6195
|
-
let response = await requestImage$1(fetchFn, credentials,
|
|
6305
|
+
let response = await requestImage$1(fetchFn, credentials, url, body, turnId, exec.signal);
|
|
6196
6306
|
if (response.status === 401) {
|
|
6197
6307
|
await response.body?.cancel().catch(() => void 0);
|
|
6198
6308
|
credentials = await imageCredentials(oauth, true);
|
|
6199
|
-
response = await requestImage$1(fetchFn, credentials,
|
|
6309
|
+
response = await requestImage$1(fetchFn, credentials, url, body, turnId, exec.signal);
|
|
6200
6310
|
}
|
|
6201
6311
|
if (response.status === 429) {
|
|
6202
|
-
await response.
|
|
6203
|
-
throw new HarnessError(
|
|
6312
|
+
const detail = upstreamExcerpt(await response.text().catch(() => ""));
|
|
6313
|
+
throw new HarnessError(`Codex image generation was rate limited (429).${detail === "" ? "" : ` ${detail}`}`, "CODEX_IMAGE_RATE_LIMITED");
|
|
6314
|
+
}
|
|
6315
|
+
if (response.status === 400) {
|
|
6316
|
+
const detail = upstreamExcerpt(await response.text().catch(() => ""));
|
|
6317
|
+
throw new HarnessError(`Codex rejected the image request (400). The parameters this call stated were: ${statedParameterSummary(args, gif, references.length)}. One of them is not accepted as given, and no field was dropped to retry.` + (detail === "" ? "" : ` Upstream said: ${detail}`), "CODEX_IMAGE_REQUEST_REJECTED");
|
|
6204
6318
|
}
|
|
6205
6319
|
if (!response.ok) {
|
|
6206
|
-
await response.
|
|
6207
|
-
throw new HarnessError(`Codex image generation failed (${response.status})
|
|
6320
|
+
const detail = upstreamExcerpt(await response.text().catch(() => ""));
|
|
6321
|
+
throw new HarnessError(`Codex image generation failed (${response.status}).${detail === "" ? "" : ` ${detail}`}`, "CODEX_IMAGE_FAILED");
|
|
6208
6322
|
}
|
|
6209
|
-
const
|
|
6323
|
+
const decoded = decodeImageBytes(readBase64Image(await response.json()), maxGeneratedImageBytes(attachments));
|
|
6324
|
+
if (gif !== void 0 && decoded.mediaType !== "image/gif") throw new HarnessError(`Codex was asked for an animated GIF but returned ${decoded.mediaType}. Nothing was saved: the answer is not the animation that was requested. Omit the gif parameter to accept a still image.`, "CODEX_IMAGE_RESPONSE_INVALID");
|
|
6325
|
+
if (!attachments.imageLimits.mediaTypes.includes(decoded.mediaType)) throw new HarnessError(`Codex returned ${decoded.mediaType} but this DSH environment does not accept that attachment type.`, "CODEX_IMAGE_ATTACHMENT_UNSUPPORTED");
|
|
6210
6326
|
const image = await attachments.saveImage({
|
|
6211
|
-
data:
|
|
6212
|
-
mediaType:
|
|
6213
|
-
name:
|
|
6327
|
+
data: decoded.data,
|
|
6328
|
+
mediaType: decoded.mediaType,
|
|
6329
|
+
name: `codex-generated-image.${decoded.mediaType === "image/gif" ? "gif" : "png"}`
|
|
6214
6330
|
});
|
|
6215
6331
|
const output = {
|
|
6216
6332
|
prompt,
|
|
6217
6333
|
model: CODEX_IMAGE_MODEL,
|
|
6218
6334
|
image: {
|
|
6219
6335
|
attachmentId: image.attachmentId,
|
|
6220
|
-
mediaType:
|
|
6336
|
+
mediaType: decoded.mediaType,
|
|
6221
6337
|
bytes: image.bytes,
|
|
6222
6338
|
width: image.width,
|
|
6223
6339
|
height: image.height,
|
|
@@ -6239,6 +6355,124 @@ function createCodexImageTool(oauth, attachments, options = {}) {
|
|
|
6239
6355
|
}
|
|
6240
6356
|
});
|
|
6241
6357
|
}
|
|
6358
|
+
/**
|
|
6359
|
+
* Validate the frame options of a GIF request.
|
|
6360
|
+
*
|
|
6361
|
+
* An empty object is legal: it asks for an animation and leaves both numbers to
|
|
6362
|
+
* the service. A stated number is checked here rather than by the parameter
|
|
6363
|
+
* schema because the schema DSL carries no numeric bounds, and because
|
|
6364
|
+
* `execute` is reachable directly.
|
|
6365
|
+
*/
|
|
6366
|
+
function readGifOptions(gif) {
|
|
6367
|
+
if (gif === void 0) return void 0;
|
|
6368
|
+
const result = {};
|
|
6369
|
+
const frameCount = gif.frameCount;
|
|
6370
|
+
if (frameCount !== void 0) {
|
|
6371
|
+
if (!Number.isInteger(frameCount) || frameCount < GIF_MIN_FRAMES || frameCount > GIF_MAX_FRAMES) throw new HarnessError(`gif.frameCount must be a whole number from ${GIF_MIN_FRAMES} to ${GIF_MAX_FRAMES} (got ${String(frameCount)}).`, "CODEX_IMAGE_GIF_INVALID");
|
|
6372
|
+
result.frameCount = frameCount;
|
|
6373
|
+
}
|
|
6374
|
+
const frameDurationMs = gif.frameDurationMs;
|
|
6375
|
+
if (frameDurationMs !== void 0) {
|
|
6376
|
+
if (!Number.isInteger(frameDurationMs) || frameDurationMs < GIF_MIN_FRAME_MS || frameDurationMs > GIF_MAX_FRAME_MS) throw new HarnessError(`gif.frameDurationMs must be a whole number of milliseconds from ${GIF_MIN_FRAME_MS} to ${GIF_MAX_FRAME_MS} (got ${String(frameDurationMs)}).`, "CODEX_IMAGE_GIF_INVALID");
|
|
6377
|
+
result.frameDurationMs = frameDurationMs;
|
|
6378
|
+
}
|
|
6379
|
+
return result;
|
|
6380
|
+
}
|
|
6381
|
+
/** Refuse an output type this deployment cannot store, before anything is requested. */
|
|
6382
|
+
function assertOutputTypeSupported(attachments, outputType) {
|
|
6383
|
+
if (attachments.imageLimits.mediaTypes.includes(outputType)) return;
|
|
6384
|
+
throw new HarnessError(outputType === "image/gif" ? "GIF output was requested, but this DSH environment does not accept GIF attachments, so nothing was requested. Omit the gif parameter to generate a PNG instead." : "PNG image attachments are not enabled in this DSH environment.", "CODEX_IMAGE_ATTACHMENT_UNSUPPORTED");
|
|
6385
|
+
}
|
|
6386
|
+
/**
|
|
6387
|
+
* Build the wire body of one request.
|
|
6388
|
+
*
|
|
6389
|
+
* Field order and the defaults are deliberate: a call with no new options
|
|
6390
|
+
* produces exactly `prompt/background auto/model/quality auto/size auto`, which
|
|
6391
|
+
* is the body this tool has always sent. Every new field appears only when the
|
|
6392
|
+
* caller stated it, so an old call site keeps its old bytes on the wire.
|
|
6393
|
+
*/
|
|
6394
|
+
function buildImageBody(request) {
|
|
6395
|
+
const body = {
|
|
6396
|
+
prompt: request.prompt,
|
|
6397
|
+
background: request.transparentBackground === true ? "transparent" : request.transparentBackground === false ? "opaque" : "auto",
|
|
6398
|
+
model: CODEX_IMAGE_MODEL,
|
|
6399
|
+
quality: request.quality,
|
|
6400
|
+
size: request.size
|
|
6401
|
+
};
|
|
6402
|
+
if (request.gif !== void 0) {
|
|
6403
|
+
body.output_format = "gif";
|
|
6404
|
+
if (request.gif.frameCount !== void 0) body.frame_count = request.gif.frameCount;
|
|
6405
|
+
if (request.gif.frameDurationMs !== void 0) body.frame_duration_ms = request.gif.frameDurationMs;
|
|
6406
|
+
}
|
|
6407
|
+
if (request.references.length > 0) body.images = request.references.map((image) => ({ image_url: `data:${image.mediaType};base64,${Buffer.from(image.data).toString("base64")}` }));
|
|
6408
|
+
return body;
|
|
6409
|
+
}
|
|
6410
|
+
/**
|
|
6411
|
+
* The optional parameters one request actually stated, in words.
|
|
6412
|
+
*
|
|
6413
|
+
* A 400 says only that the request was invalid, which is useless on a tool with
|
|
6414
|
+
* seven parameters: the caller has to be told WHICH ones it sent, or it has no
|
|
6415
|
+
* way to know what to change. Only stated options are named - a default that the
|
|
6416
|
+
* tool has always sent cannot be the field to fix - and each GIF sub-field is
|
|
6417
|
+
* named separately, because `gif: {}` is a legal call that stated neither number.
|
|
6418
|
+
*/
|
|
6419
|
+
function statedParameterSummary(args, gif, referenceCount) {
|
|
6420
|
+
const stated = [];
|
|
6421
|
+
if (args.size !== void 0) stated.push(`size=${args.size}`);
|
|
6422
|
+
if (args.quality !== void 0) stated.push(`quality=${args.quality}`);
|
|
6423
|
+
if (args.transparentBackground !== void 0) stated.push(`transparentBackground=${String(args.transparentBackground)}`);
|
|
6424
|
+
if (referenceCount > 0) stated.push(`referenceImagePaths (${referenceCount} image${referenceCount === 1 ? "" : "s"})`);
|
|
6425
|
+
if (gif !== void 0) {
|
|
6426
|
+
const frames = [];
|
|
6427
|
+
if (gif.frameCount !== void 0) frames.push(`frameCount=${String(gif.frameCount)}`);
|
|
6428
|
+
if (gif.frameDurationMs !== void 0) frames.push(`frameDurationMs=${String(gif.frameDurationMs)}`);
|
|
6429
|
+
stated.push(frames.length === 0 ? "gif (frame numbers left to the service)" : `gif (${frames.join(", ")})`);
|
|
6430
|
+
}
|
|
6431
|
+
return stated.length === 0 ? "no optional parameter was stated" : stated.join(", ");
|
|
6432
|
+
}
|
|
6433
|
+
/**
|
|
6434
|
+
* Read and validate the reference images one edit request names.
|
|
6435
|
+
*
|
|
6436
|
+
* Every named file is checked as a local still image: the path must not be a
|
|
6437
|
+
* URL, the extension must be PNG/JPEG/WebP, the file must be a regular non-empty
|
|
6438
|
+
* file inside the attachment byte limits, and its leading bytes must match the
|
|
6439
|
+
* declared type. Nothing else on disk is read, and the returned bytes are the
|
|
6440
|
+
* ONLY image data that reaches the request.
|
|
6441
|
+
*/
|
|
6442
|
+
async function readReferenceImages(files, attachments) {
|
|
6443
|
+
if (files.length === 0) return [];
|
|
6444
|
+
const limit = Math.min(5, attachments.imageLimits.maxImagesPerMessage);
|
|
6445
|
+
if (files.length > limit) throw new HarnessError(`At most ${limit} reference images are accepted; ${files.length} were given.`, "CODEX_IMAGE_REFERENCE_LIMIT");
|
|
6446
|
+
const perImageBytes = attachments.imageLimits.maxImageBytes;
|
|
6447
|
+
const totalBytes = attachments.imageLimits.maxMessageImageBytes;
|
|
6448
|
+
const images = [];
|
|
6449
|
+
let sentBytes = 0;
|
|
6450
|
+
for (const [index, raw] of files.entries()) {
|
|
6451
|
+
const label = `referenceImagePaths[${index}]`;
|
|
6452
|
+
const value = raw.trim();
|
|
6453
|
+
if (value === "") throw new HarnessError(`${label} is empty; every reference image must name a local file.`, "CODEX_IMAGE_REFERENCE_INVALID_PATH");
|
|
6454
|
+
if (/^[a-z][a-z0-9+.-]*:\/\//i.test(value) || /^data:/i.test(value)) throw new HarnessError(`${label} is a URL (${value}). Reference images must be local PNG, JPEG or WebP files; nothing is fetched.`, "CODEX_IMAGE_REFERENCE_INVALID_PATH");
|
|
6455
|
+
const extension = extname(value).toLowerCase();
|
|
6456
|
+
const mediaType = REFERENCE_EXTENSIONS[extension];
|
|
6457
|
+
if (mediaType === void 0) throw new HarnessError(extension === ".gif" ? `${label} (${value}) is a GIF. Reference images must be still PNG, JPEG or WebP files; animated input is not part of the edit request.` : `${label} (${value}) is not a PNG, JPEG or WebP file (extension ${extension === "" ? "(none)" : `"${extension}"`}).`, "CODEX_IMAGE_REFERENCE_UNSUPPORTED");
|
|
6458
|
+
if (!attachments.imageLimits.mediaTypes.includes(mediaType)) throw new HarnessError(`${label} is ${mediaType}, which this DSH environment does not accept as an attachment.`, "CODEX_IMAGE_ATTACHMENT_UNSUPPORTED");
|
|
6459
|
+
const absolute = resolve(value);
|
|
6460
|
+
const stats = await stat(absolute).catch(() => void 0);
|
|
6461
|
+
if (stats === void 0) throw new HarnessError(`${label} (${value}) does not exist or cannot be read.`, "CODEX_IMAGE_REFERENCE_NOT_FOUND");
|
|
6462
|
+
if (!stats.isFile()) throw new HarnessError(`${label} (${value}) is not a regular file; pass a path to one PNG, JPEG or WebP file.`, "CODEX_IMAGE_REFERENCE_NOT_A_FILE");
|
|
6463
|
+
if (stats.size === 0) throw new HarnessError(`${label} (${value}) is empty.`, "CODEX_IMAGE_REFERENCE_EMPTY");
|
|
6464
|
+
if (stats.size > perImageBytes) throw new HarnessError(`${label} (${value}) is ${stats.size} bytes, above the ${perImageBytes} byte limit for one attachment.`, "CODEX_IMAGE_REFERENCE_TOO_LARGE");
|
|
6465
|
+
const data = new Uint8Array(await readFile(absolute));
|
|
6466
|
+
if (!SIGNATURES$1[mediaType](data)) throw new HarnessError(`${label} (${value}) does not start like a ${mediaType} file, so its bytes contradict its extension.`, "CODEX_IMAGE_REFERENCE_INVALID_CONTENT");
|
|
6467
|
+
sentBytes += data.byteLength;
|
|
6468
|
+
if (sentBytes > totalBytes) throw new HarnessError(`The reference images total ${sentBytes} bytes, above the ${totalBytes} byte limit for one message.`, "CODEX_IMAGE_REFERENCE_TOO_LARGE");
|
|
6469
|
+
images.push({
|
|
6470
|
+
mediaType,
|
|
6471
|
+
data
|
|
6472
|
+
});
|
|
6473
|
+
}
|
|
6474
|
+
return images;
|
|
6475
|
+
}
|
|
6242
6476
|
async function imageCredentials(oauth, force = false) {
|
|
6243
6477
|
try {
|
|
6244
6478
|
return await oauth.credentials(force, { purpose: "tool" });
|
|
@@ -6246,8 +6480,8 @@ async function imageCredentials(oauth, force = false) {
|
|
|
6246
6480
|
throw new HarnessError(`ChatGPT subscription credentials are required for Codex image generation.${error instanceof Error && error.message !== "" ? ` (${error.message})` : ""}`, "CODEX_IMAGE_CREDENTIAL_MISSING", { cause: error });
|
|
6247
6481
|
}
|
|
6248
6482
|
}
|
|
6249
|
-
function requestImage$1(fetchFn, credentials,
|
|
6250
|
-
return fetchFn(
|
|
6483
|
+
function requestImage$1(fetchFn, credentials, url, body, turnId, signal) {
|
|
6484
|
+
return fetchFn(url, {
|
|
6251
6485
|
method: "POST",
|
|
6252
6486
|
headers: {
|
|
6253
6487
|
...codexHeaders(credentials),
|
|
@@ -6255,16 +6489,19 @@ function requestImage$1(fetchFn, credentials, prompt, turnId, signal) {
|
|
|
6255
6489
|
"content-type": "application/json",
|
|
6256
6490
|
"x-codex-image-turn-id": turnId
|
|
6257
6491
|
},
|
|
6258
|
-
body: JSON.stringify(
|
|
6259
|
-
prompt,
|
|
6260
|
-
background: "auto",
|
|
6261
|
-
model: CODEX_IMAGE_MODEL,
|
|
6262
|
-
quality: "auto",
|
|
6263
|
-
size: "auto"
|
|
6264
|
-
}),
|
|
6492
|
+
body: JSON.stringify(body),
|
|
6265
6493
|
signal
|
|
6266
6494
|
});
|
|
6267
6495
|
}
|
|
6496
|
+
/**
|
|
6497
|
+
* The upstream error body, short enough to read in a message and stripped of
|
|
6498
|
+
* anything that must never be echoed: bearer credentials, base64 image payloads
|
|
6499
|
+
* and any long base64-shaped run.
|
|
6500
|
+
*/
|
|
6501
|
+
function upstreamExcerpt(text) {
|
|
6502
|
+
const collapsed = text.replace(/data:[a-z0-9.+-]+\/[a-z0-9.+-]+;base64,[A-Za-z0-9+/=]*/gi, "data:<redacted>").replace(/Bearer\s+[A-Za-z0-9._~+/=-]{6,}/gi, "Bearer <redacted>").replace(/[A-Za-z0-9+/]{200,}={0,2}/g, "<redacted>").replace(/\s+/g, " ").trim();
|
|
6503
|
+
return collapsed.length > MAX_UPSTREAM_EXCERPT_CHARS ? `${collapsed.slice(0, MAX_UPSTREAM_EXCERPT_CHARS)}…` : collapsed;
|
|
6504
|
+
}
|
|
6268
6505
|
function readBase64Image(value) {
|
|
6269
6506
|
const root = record$7(value);
|
|
6270
6507
|
const data = Array.isArray(root?.data) ? root.data[0] : null;
|
|
@@ -6272,12 +6509,27 @@ function readBase64Image(value) {
|
|
|
6272
6509
|
if (image === null) throw new HarnessError("Codex image response did not include image data.", "CODEX_IMAGE_RESPONSE_INVALID");
|
|
6273
6510
|
return image;
|
|
6274
6511
|
}
|
|
6512
|
+
/**
|
|
6513
|
+
* Decode one returned image and identify it by its own magic bytes.
|
|
6514
|
+
*
|
|
6515
|
+
* The endpoint answers a requested GIF with GIF bytes in the same `b64_json`
|
|
6516
|
+
* field it uses for PNG, and states nothing about which one it produced, so the
|
|
6517
|
+
* bytes are the only reliable source for the media type – and storing a GIF
|
|
6518
|
+
* under `image/png` would fail at save time or, worse, mislabel the file.
|
|
6519
|
+
*/
|
|
6275
6520
|
function decodeImageBytes(base64, maxBytes) {
|
|
6276
6521
|
if (!/^[A-Za-z0-9+/]+={0,2}$/.test(base64) || base64.length % 4 !== 0) throw new HarnessError("Codex image response was not valid base64.", "CODEX_IMAGE_RESPONSE_INVALID");
|
|
6277
6522
|
const buffer = Buffer.from(base64, "base64");
|
|
6278
6523
|
if (buffer.length > maxBytes) throw new HarnessError("Generated image exceeds the configured attachment size limit.", "CODEX_IMAGE_TOO_LARGE");
|
|
6279
|
-
|
|
6280
|
-
|
|
6524
|
+
if (SIGNATURES$1["image/gif"](buffer)) return {
|
|
6525
|
+
mediaType: "image/gif",
|
|
6526
|
+
data: buffer
|
|
6527
|
+
};
|
|
6528
|
+
for (const [index, byte] of PNG_SIGNATURE.entries()) if (buffer[index] !== byte) throw new HarnessError("Codex image response was neither PNG nor GIF image data.", "CODEX_IMAGE_RESPONSE_INVALID");
|
|
6529
|
+
return {
|
|
6530
|
+
mediaType: "image/png",
|
|
6531
|
+
data: buffer
|
|
6532
|
+
};
|
|
6281
6533
|
}
|
|
6282
6534
|
function maxGeneratedImageBytes(attachments) {
|
|
6283
6535
|
return Math.min(attachments.imageLimits.maxImageBytes, attachments.imageLimits.maxMessageImageBytes);
|
|
@@ -10451,6 +10703,9 @@ var AntigravityAdapter = class extends LlmAdapter {
|
|
|
10451
10703
|
/**
|
|
10452
10704
|
* Aspect ratios Gemini image models accept in `imageConfig.aspectRatio`.
|
|
10453
10705
|
* Omitted, the model picks one itself, which is what the request then does.
|
|
10706
|
+
*
|
|
10707
|
+
* The last four are the extreme strips: a wider or taller frame than the square
|
|
10708
|
+
* family covers, useful for banners, headers and phone wallpapers.
|
|
10454
10709
|
*/
|
|
10455
10710
|
const ANTIGRAVITY_IMAGE_ASPECT_RATIOS = [
|
|
10456
10711
|
"1:1",
|
|
@@ -10462,7 +10717,21 @@ const ANTIGRAVITY_IMAGE_ASPECT_RATIOS = [
|
|
|
10462
10717
|
"5:4",
|
|
10463
10718
|
"9:16",
|
|
10464
10719
|
"16:9",
|
|
10465
|
-
"21:9"
|
|
10720
|
+
"21:9",
|
|
10721
|
+
"1:4",
|
|
10722
|
+
"4:1",
|
|
10723
|
+
"1:8",
|
|
10724
|
+
"8:1"
|
|
10725
|
+
];
|
|
10726
|
+
/**
|
|
10727
|
+
* Image sizes Gemini image models accept in `imageConfig.imageSize`.
|
|
10728
|
+
* Omitted, the model picks one itself, which is what the request then does.
|
|
10729
|
+
*/
|
|
10730
|
+
const ANTIGRAVITY_IMAGE_SIZES = [
|
|
10731
|
+
"0.5K",
|
|
10732
|
+
"1K",
|
|
10733
|
+
"2K",
|
|
10734
|
+
"4K"
|
|
10466
10735
|
];
|
|
10467
10736
|
/** Images one call keeps; the model normally returns one. */
|
|
10468
10737
|
const MAX_IMAGES_PER_CALL = 4;
|
|
@@ -10476,7 +10745,11 @@ const MAX_IMAGES_PER_CALL = 4;
|
|
|
10476
10745
|
* Antigravity clients send for image runtimes; it has not been captured from
|
|
10477
10746
|
* the official client.
|
|
10478
10747
|
*/
|
|
10479
|
-
function buildImageRequest(prompt, projectId, aspectRatio) {
|
|
10748
|
+
function buildImageRequest(prompt, projectId, aspectRatio, imageSize) {
|
|
10749
|
+
const imageConfig = {
|
|
10750
|
+
...aspectRatio ? { aspectRatio } : {},
|
|
10751
|
+
...imageSize ? { imageSize } : {}
|
|
10752
|
+
};
|
|
10480
10753
|
return {
|
|
10481
10754
|
project: projectId,
|
|
10482
10755
|
model: ANTIGRAVITY_IMAGE_MODEL,
|
|
@@ -10487,7 +10760,7 @@ function buildImageRequest(prompt, projectId, aspectRatio) {
|
|
|
10487
10760
|
}],
|
|
10488
10761
|
generationConfig: {
|
|
10489
10762
|
responseModalities: ["TEXT", "IMAGE"],
|
|
10490
|
-
...
|
|
10763
|
+
...Object.keys(imageConfig).length > 0 ? { imageConfig } : {}
|
|
10491
10764
|
}
|
|
10492
10765
|
},
|
|
10493
10766
|
requestType: "image_gen",
|
|
@@ -10599,7 +10872,7 @@ function createAntigravityImageTool(accountPool, store, attachments, options = {
|
|
|
10599
10872
|
const fetchFn = options.fetchFn ?? fetch;
|
|
10600
10873
|
return defineTool({
|
|
10601
10874
|
name: ANTIGRAVITY_IMAGE_TOOL_NAME,
|
|
10602
|
-
description: "Generate an image with Gemini image generation through the signed-in Antigravity account. Describe the subject, style, composition and any text to render in the prompt.",
|
|
10875
|
+
description: "Generate an image with Gemini image generation through the signed-in Antigravity account. Describe the subject, style, composition and any text to render in the prompt. Scheduling: a single generation, a change to an image already generated, and a retry of a failed attempt are direct calls from the agent that needs the image — do not create a subagent just to generate one image. Delegate only a task that genuinely needs several alternative directions or complex creative planning, and pass the prompt that comes back to this tool.",
|
|
10603
10876
|
parameters: {
|
|
10604
10877
|
prompt: {
|
|
10605
10878
|
type: "string",
|
|
@@ -10609,7 +10882,12 @@ function createAntigravityImageTool(accountPool, store, attachments, options = {
|
|
|
10609
10882
|
aspectRatio: {
|
|
10610
10883
|
type: "string",
|
|
10611
10884
|
enum: ANTIGRAVITY_IMAGE_ASPECT_RATIOS,
|
|
10612
|
-
description: "Optional aspect ratio. Omit it to let the model choose."
|
|
10885
|
+
description: "Optional aspect ratio, one of the listed values. Omit it to let the model choose. If the upstream rejects the value, re-issue the call with another value from the list rather than the same one."
|
|
10886
|
+
},
|
|
10887
|
+
imageSize: {
|
|
10888
|
+
type: "string",
|
|
10889
|
+
enum: ANTIGRAVITY_IMAGE_SIZES,
|
|
10890
|
+
description: "Optional output size, one of 0.5K, 1K, 2K or 4K. Omit it to let the model choose. It is sent only when given, so it never overrides an aspect ratio the caller did not set."
|
|
10613
10891
|
}
|
|
10614
10892
|
},
|
|
10615
10893
|
output: {
|
|
@@ -10693,7 +10971,7 @@ function createAntigravityImageTool(accountPool, store, attachments, options = {
|
|
|
10693
10971
|
const prompt = args.prompt.trim();
|
|
10694
10972
|
if (prompt === "") throw new HarnessError("Image prompt cannot be empty.", "ANTIGRAVITY_IMAGE_INVALID_PROMPT");
|
|
10695
10973
|
if (options.isEnabled?.() === false) throw new HarnessError("The Antigravity line is switched off; enable it in Settings → Antigravity to generate images.", "ANTIGRAVITY_IMAGE_DISABLED");
|
|
10696
|
-
const response = await requestImage(accountPool, store, fetchFn, prompt, args.aspectRatio, exec.signal);
|
|
10974
|
+
const response = await requestImage(accountPool, store, fetchFn, prompt, args.aspectRatio, args.imageSize, exec.signal);
|
|
10697
10975
|
if (response.images.length === 0) throw new HarnessError(noImageMessage(response), "ANTIGRAVITY_IMAGE_EMPTY");
|
|
10698
10976
|
const keep = Math.min(MAX_IMAGES_PER_CALL, attachments.imageLimits.maxImagesPerMessage, response.images.length);
|
|
10699
10977
|
const saved = [];
|
|
@@ -10752,12 +11030,37 @@ async function acquireAccount(accountPool, store, tried, fetchFn) {
|
|
|
10752
11030
|
}
|
|
10753
11031
|
}
|
|
10754
11032
|
}
|
|
10755
|
-
|
|
11033
|
+
/**
|
|
11034
|
+
* Whether a status means the upstream refused the SHAPE of the request rather
|
|
11035
|
+
* than the account, the allowance or the transient state of the service.
|
|
11036
|
+
*/
|
|
11037
|
+
const PARAMETER_REJECTION_STATUSES = /* @__PURE__ */ new Set([400, 422]);
|
|
11038
|
+
/**
|
|
11039
|
+
* What a failed image request reports: the status, a bounded excerpt of what
|
|
11040
|
+
* the upstream said, and — when the refusal was about the request — which
|
|
11041
|
+
* optional fields were sent.
|
|
11042
|
+
*
|
|
11043
|
+
* A parameter rejection is never retried without the field it names. Dropping
|
|
11044
|
+
* `aspectRatio` or `imageSize` would answer with an image that silently has a
|
|
11045
|
+
* shape nobody asked for, so the caller is told which fields to change and
|
|
11046
|
+
* makes that call itself.
|
|
11047
|
+
*/
|
|
11048
|
+
function imageFailureMessage(status, errorText, sent) {
|
|
11049
|
+
const parts = [`Antigravity image generation failed (${status ?? "no response"}).`];
|
|
11050
|
+
if (status !== void 0 && PARAMETER_REJECTION_STATUSES.has(status)) {
|
|
11051
|
+
const fields = [sent.aspectRatio === void 0 ? void 0 : `aspectRatio "${sent.aspectRatio}"`, sent.imageSize === void 0 ? void 0 : `imageSize "${sent.imageSize}"`].filter((field) => field !== void 0);
|
|
11052
|
+
parts.push(fields.length > 0 ? `The request itself was refused, and these optional fields were what it carried: ${fields.join(", ")}. It is not retried without them: re-issue the call with a different candidate value, or omit the field, because the upstream refused this request shape.` : "The request itself was refused while no aspectRatio or imageSize was set, so a different value for either cannot fix it; change the prompt or check the account before retrying.");
|
|
11053
|
+
}
|
|
11054
|
+
const excerpt = upstreamBodyExcerpt(errorText);
|
|
11055
|
+
if (excerpt !== "") parts.push(`Upstream said: ${excerpt}`);
|
|
11056
|
+
return parts.join(" ");
|
|
11057
|
+
}
|
|
11058
|
+
async function requestImage(accountPool, store, fetchFn, prompt, aspectRatio, imageSize, signal) {
|
|
10756
11059
|
const tried = /* @__PURE__ */ new Set();
|
|
10757
11060
|
while (true) {
|
|
10758
11061
|
const { account, token, projectId } = await acquireAccount(accountPool, store, tried, fetchFn);
|
|
10759
11062
|
tried.add(account.id);
|
|
10760
|
-
const body = JSON.stringify(buildImageRequest(prompt, projectId || "antigravity-default", aspectRatio));
|
|
11063
|
+
const body = JSON.stringify(buildImageRequest(prompt, projectId || "antigravity-default", aspectRatio, imageSize));
|
|
10761
11064
|
let response;
|
|
10762
11065
|
let firstResponse;
|
|
10763
11066
|
for (const endpoint of endpointCandidates()) try {
|
|
@@ -10789,7 +11092,10 @@ async function requestImage(accountPool, store, fetchFn, prompt, aspectRatio, si
|
|
|
10789
11092
|
}
|
|
10790
11093
|
throw new HarnessError(`Antigravity image generation was rate limited (429) without a stated reset time, so it is handled as a transient rate limit rather than a spent quota. ${upstreamBodyExcerpt(errorText)}`.trim(), "ANTIGRAVITY_IMAGE_RATE_LIMITED");
|
|
10791
11094
|
}
|
|
10792
|
-
throw new HarnessError(
|
|
11095
|
+
throw new HarnessError(imageFailureMessage(status, errorText, {
|
|
11096
|
+
aspectRatio,
|
|
11097
|
+
imageSize
|
|
11098
|
+
}), "ANTIGRAVITY_IMAGE_FAILED");
|
|
10793
11099
|
}
|
|
10794
11100
|
}
|
|
10795
11101
|
//#endregion
|
|
@@ -27971,6 +28277,25 @@ const TOOL_SOURCE = /(?:^|[-_/])(?:read|view|cat|open|edit|write|patch|apply_pat
|
|
|
27971
28277
|
/** 独立开关覆盖的检索工具;只有明确导航任务才允许进入新投影。 */
|
|
27972
28278
|
const TOOL_RETRIEVAL = /^(?:read|web_fetch|grep|glob)$/;
|
|
27973
28279
|
/**
|
|
28280
|
+
* PTC 家族的**程序传输**工具名(一个集合,两个名字)。
|
|
28281
|
+
*
|
|
28282
|
+
* 「模型写一段程序、工具调用发生在程序内部」是同一种传输,只是不同预设给主工具起
|
|
28283
|
+
* 了不同名字:PTC 预设叫 `run_code`,Code Mode 预设叫 `code_run`。凡是按工具名判断
|
|
28284
|
+
* 「这条结果来自程序传输」的地方都必须认这**整个集合**——名字只写一个,预设一换,
|
|
28285
|
+
* 大结果的 `ref://` 预览与精确补读就会静默失效。
|
|
28286
|
+
*/
|
|
28287
|
+
const CACHE_WARMUP_PTC_TRANSPORT_TOOLS = /* @__PURE__ */ new Set(["run_code", "code_run"]);
|
|
28288
|
+
/**
|
|
28289
|
+
* 这个名字是否属于 PTC 家族的程序传输工具。
|
|
28290
|
+
*
|
|
28291
|
+
* 只做**精确**匹配:`code_runner` 之类的近似名不算,宁可少投影也不能误伤。
|
|
28292
|
+
* @param name - 工具名。
|
|
28293
|
+
* @returns 属于 {@link CACHE_WARMUP_PTC_TRANSPORT_TOOLS} 时为 true。
|
|
28294
|
+
*/
|
|
28295
|
+
function isPtcTransportName(name) {
|
|
28296
|
+
return CACHE_WARMUP_PTC_TRANSPORT_TOOLS.has(name);
|
|
28297
|
+
}
|
|
28298
|
+
/**
|
|
27974
28299
|
* 多节/多读输出进入新投影的最小长度门槛(字符)。
|
|
27975
28300
|
*
|
|
27976
28301
|
* 短于这个长度、又没有 spill 的普通源码与契约读一律逐字保留:
|
|
@@ -29266,7 +29591,7 @@ function planProjection(text, toolName, navigation, retrievalOnly, spilled = fal
|
|
|
29266
29591
|
kind: "verbatim",
|
|
29267
29592
|
strategy: "verbatim-contract"
|
|
29268
29593
|
};
|
|
29269
|
-
if (toolName
|
|
29594
|
+
if (isPtcTransportName(toolName) && navigation === true) {
|
|
29270
29595
|
const outline = planRetrievalOutline(text, "read");
|
|
29271
29596
|
if (outline !== void 0) return outline;
|
|
29272
29597
|
if (retrievalOnly === true) return {
|
|
@@ -92527,6 +92852,20 @@ const PRESET_REGISTRY_PACKAGE = "@deepseek-ai/dsh-agent-preset";
|
|
|
92527
92852
|
const WORKING_DIRECTORY_PACKAGE = "@deepseek-ai/dsh-working-directory";
|
|
92528
92853
|
/** The standalone PTC executor introduced when tool presentation split into executor plugins. */
|
|
92529
92854
|
const PTC_EXECUTOR_PACKAGE = "@deepseek-ai/dsh-tool-executor-ptc";
|
|
92855
|
+
/**
|
|
92856
|
+
* The native executor this package delegates Code Mode execution to.
|
|
92857
|
+
*
|
|
92858
|
+
* A program sub-call must run through the harness's own policy, approval, and
|
|
92859
|
+
* post-execute pipeline; re-implementing that pipeline is how a plugin silently
|
|
92860
|
+
* drops policy. A profile plugin cannot resolve the host's kernel packages, so
|
|
92861
|
+
* this package carries the executor as a real dependency and drives the host
|
|
92862
|
+
* registry with it.
|
|
92863
|
+
*/
|
|
92864
|
+
const NATIVE_EXECUTOR_PACKAGE = "@deepseek-ai/dsh-tool-executor-native";
|
|
92865
|
+
/** This package's Code Mode row: the preset's own tools stay native, the rest is program-only. */
|
|
92866
|
+
const CODEMODE_PACKAGE = "@eddyskywalker/dsh-chatgpt-subscription/codemode";
|
|
92867
|
+
/** The deprecated presentation row generations without a native executor keep. */
|
|
92868
|
+
const AGENT_TOOL_PRESENTATION_PACKAGE = "@deepseek-ai/dsh-agent-tool-presentation";
|
|
92530
92869
|
/** The id the `dispatch` preset is selectable under. */
|
|
92531
92870
|
const DISPATCH_PRESET_ID = "dispatch";
|
|
92532
92871
|
/**
|
|
@@ -92542,7 +92881,7 @@ const DISPATCH_PRESET_ID = "dispatch";
|
|
|
92542
92881
|
const DISPATCH_PRESET = {
|
|
92543
92882
|
id: DISPATCH_PRESET_ID,
|
|
92544
92883
|
name: "调度模式",
|
|
92545
|
-
description: "基于
|
|
92884
|
+
description: "基于 Code Mode 的调度 Agent:先分诊任务复杂度,L2 任务在规划前先做澄清访谈,再由主代理规划、派发子代理(显式选择子代理模型)、审查与验收。",
|
|
92546
92885
|
order: 5,
|
|
92547
92886
|
plugins: [
|
|
92548
92887
|
{
|
|
@@ -92589,6 +92928,7 @@ L0/L1: execute directly (or 1 subagent for L1). L2: R-G, R9, and R6 are mandator
|
|
|
92589
92928
|
- Lead-Core: tightly coupled cross-module/contract changes or sequential chains where splitting duplicates reads or causes hand_back loops. -> Implement directly in continuous steps (track in todo_write, no self-briefs) and run R6 verification.
|
|
92590
92929
|
- Hybrid: unsettled shared contracts/seams plus orthogonal downstream work. -> Implement & freeze core seams yourself first, then delegate orthogonal adapters/tests/noisy exploration per R2/R5 (or subagent_fork for warm-context isolation).
|
|
92591
92930
|
- Fan-out: >=3 orthogonal subtasks with disjoint files, settled interfaces, and clear parallel/routing gain. -> Coordinate (plan, R2/R5 dispatch, R6 review/acceptance, docs, non-delegable or disjoint parallel modules); never edit an active child's Boundary.
|
|
92931
|
+
- [Media generation] Generate/revise/retry images directly; delegate complex creative work, never a bare call.
|
|
92592
92932
|
|
|
92593
92933
|
## R2 · Model Routing (Check Availability First, Then Decide How to Dispatch)
|
|
92594
92934
|
Check whether list_subagent_models exists (in PTC mode: tools.list_subagent_models). The tool row and provider/model/reasoning_effort parameters are mounted statically, so they are present even in a session that authorized no route at all. Read that fact instead of inferring it from the tool's output:
|
|
@@ -92833,9 +93173,8 @@ Fallback is a task-scoped, non-session-wide exception for delegated briefs: it c
|
|
|
92833
93173
|
}
|
|
92834
93174
|
},
|
|
92835
93175
|
{
|
|
92836
|
-
id: "
|
|
92837
|
-
name:
|
|
92838
|
-
config: { mode: "ptc" }
|
|
93176
|
+
id: "code-mode",
|
|
93177
|
+
name: CODEMODE_PACKAGE
|
|
92839
93178
|
},
|
|
92840
93179
|
{
|
|
92841
93180
|
id: "present",
|
|
@@ -92859,8 +93198,16 @@ Fallback is a task-scoped, non-session-wide exception for delegated briefs: it c
|
|
|
92859
93198
|
* the export is absent; older harnesses keep it because their main entry does not
|
|
92860
93199
|
* register the tool.
|
|
92861
93200
|
*
|
|
92862
|
-
*
|
|
92863
|
-
*
|
|
93201
|
+
* Code Mode needs the native tool executor (it is the pipeline the program's
|
|
93202
|
+
* sub-calls are delegated to), so that package's presence decides the
|
|
93203
|
+
* presentation rows:
|
|
93204
|
+
*
|
|
93205
|
+
* - resolvable: the `code-mode` row mounts as-is and no PTC executor row exists
|
|
93206
|
+
* beside it (one executor per scope);
|
|
93207
|
+
* - absent: the row falls back to the deprecated agent-tool-presentation row and,
|
|
93208
|
+
* when the standalone PTC executor exists, to that executor instead — the
|
|
93209
|
+
* behaviour of every generation before Code Mode.
|
|
93210
|
+
*
|
|
92864
93211
|
* @param resolves - whether a bare specifier resolves in this installation.
|
|
92865
93212
|
* @returns the preset definition for the running harness generation.
|
|
92866
93213
|
*/
|
|
@@ -92868,7 +93215,8 @@ function resolveDispatchPreset(resolves = resolvesFromHere) {
|
|
|
92868
93215
|
const hasWorkingDirectory = resolves(WORKING_DIRECTORY_PACKAGE);
|
|
92869
93216
|
const hasLegacyListAgents = resolves("@deepseek-ai/dsh-tool-subagent-control/list-agents");
|
|
92870
93217
|
const hasPtcExecutor = resolves(PTC_EXECUTOR_PACKAGE);
|
|
92871
|
-
|
|
93218
|
+
const hasNativeExecutor = resolves(NATIVE_EXECUTOR_PACKAGE);
|
|
93219
|
+
if (!hasWorkingDirectory && hasLegacyListAgents && !hasPtcExecutor && hasNativeExecutor) return DISPATCH_PRESET;
|
|
92872
93220
|
return {
|
|
92873
93221
|
...DISPATCH_PRESET,
|
|
92874
93222
|
plugins: DISPATCH_PRESET.plugins.map((row) => {
|
|
@@ -92876,14 +93224,14 @@ function resolveDispatchPreset(resolves = resolvesFromHere) {
|
|
|
92876
93224
|
...row,
|
|
92877
93225
|
config: row.config.filter((nested) => nested.id !== "tool-subagent-list-agents")
|
|
92878
93226
|
};
|
|
92879
|
-
if (row.id === "
|
|
92880
|
-
|
|
92881
|
-
|
|
92882
|
-
|
|
92883
|
-
|
|
92884
|
-
|
|
92885
|
-
}
|
|
92886
|
-
}
|
|
93227
|
+
if (row.id === "code-mode" && !hasNativeExecutor) return hasPtcExecutor ? {
|
|
93228
|
+
id: "tool-executor-ptc",
|
|
93229
|
+
name: PTC_EXECUTOR_PACKAGE
|
|
93230
|
+
} : {
|
|
93231
|
+
id: "tool-presentation",
|
|
93232
|
+
name: AGENT_TOOL_PRESENTATION_PACKAGE,
|
|
93233
|
+
config: { mode: "ptc" }
|
|
93234
|
+
};
|
|
92887
93235
|
if (row.id !== "persona" || !hasWorkingDirectory || typeof row.config !== "object" || row.config === null) return row;
|
|
92888
93236
|
const { suffix: _omitted, ...rest } = row.config;
|
|
92889
93237
|
return {
|
|
@@ -94760,12 +95108,13 @@ const CACHE_WARMUP_PROVIDER = "kimi-code";
|
|
|
94760
95108
|
/**
|
|
94761
95109
|
* 允许进入结果投影的工具集合。
|
|
94762
95110
|
*
|
|
94763
|
-
* 除了 PTC 的 `
|
|
94764
|
-
*
|
|
94765
|
-
*
|
|
95111
|
+
* 除了 PTC 家族的程序传输(`run_code` 与 Code Mode 的 `code_run`),宿主的独立
|
|
95112
|
+
* Shell 与后台任务输出工具(`win_bash`、`bash`、`pwsh`、`job_output`)同样经常
|
|
95113
|
+
* 吐出整屏测试/构建日志。源码读取/编辑类工具(`read`、`edit`、`grep`、`glob`、
|
|
95114
|
+
* CodeGraph 等)不在此列,保持 100% 原样。
|
|
94766
95115
|
*/
|
|
94767
95116
|
const CACHE_WARMUP_SOURCE_TOOLS = /* @__PURE__ */ new Set([
|
|
94768
|
-
|
|
95117
|
+
...CACHE_WARMUP_PTC_TRANSPORT_TOOLS,
|
|
94769
95118
|
"win_bash",
|
|
94770
95119
|
"bash",
|
|
94771
95120
|
"pwsh",
|
|
@@ -94782,7 +95131,7 @@ const CACHE_WARMUP_SECTION_NAME = "plugin:cache-warmup";
|
|
|
94782
95131
|
const CACHE_WARMUP_GUIDANCE = [
|
|
94783
95132
|
"## Result previews",
|
|
94784
95133
|
"",
|
|
94785
|
-
"On large `run_code` results this line may replace the output with a preview: a short description plus one or more `ref://<session>/<sha256>` handles.",
|
|
95134
|
+
"On large `run_code`/`code_run` results this line may replace the output with a preview: a short description plus one or more `ref://<session>/<sha256>` handles.",
|
|
94786
95135
|
"",
|
|
94787
95136
|
"- A preview is **not** the full result. It states `complete`, `omitted` and `nextOffset` explicitly; treat an incomplete preview as incomplete data, not as the whole output.",
|
|
94788
95137
|
"- To read the original, call `cache_warmup_result` with `action: \"page\"` and that `refId`, then follow `nextOffset` until `complete` is true.",
|
|
@@ -95001,7 +95350,8 @@ function installCacheWarmup(ctx, options) {
|
|
|
95001
95350
|
*
|
|
95002
95351
|
* 决策取舍写在代码里,因为它全是「什么时候**不动**」:
|
|
95003
95352
|
* - 不是 `accept`(`block` 的 feedback 是别人的东西);
|
|
95004
|
-
* -
|
|
95353
|
+
* - 工具名不在源工具集合里(`run_code`/`code_run`/Shell/`job_output`)、线路不是
|
|
95354
|
+
* 本线、会话拿不到;
|
|
95005
95355
|
* - 没有存储、内容为空。
|
|
95006
95356
|
* 上面任何一条成立就原样返回同一个决策对象。
|
|
95007
95357
|
*
|
|
@@ -95019,7 +95369,7 @@ function installCacheWarmup(ctx, options) {
|
|
|
95019
95369
|
const retrievalTool = CACHE_WARMUP_RETRIEVAL_TOOLS.has(exec.name);
|
|
95020
95370
|
if (!sourceTool && !retrievalTool) return decision;
|
|
95021
95371
|
const settings = options.settings();
|
|
95022
|
-
const ptcRetrieval = exec.name
|
|
95372
|
+
const ptcRetrieval = isPtcTransportName(exec.name) && settings.retrievalEnabled === true;
|
|
95023
95373
|
if (sourceTool && !settings.enabled && !ptcRetrieval) return decision;
|
|
95024
95374
|
if (retrievalTool && settings.retrievalEnabled !== true) return decision;
|
|
95025
95375
|
const navigation = retrievalTool || ptcRetrieval ? cacheWarmupNavigationIntentOf(exec.agent) : void 0;
|