dsh-codex-subscription 1.13.1 → 1.14.1

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/lib/index.js CHANGED
@@ -1303,7 +1303,7 @@ function openaiCodexSubscriptionProvider({ resolveSpeedMode = () => void 0, reso
1303
1303
  }
1304
1304
  //#endregion
1305
1305
  //#region src/version.js
1306
- const PACKAGE_VERSION = "1.13.1";
1306
+ const PACKAGE_VERSION = "1.14.1";
1307
1307
  const USER_AGENT = `dsh-codex-subscription/${PACKAGE_VERSION}`;
1308
1308
  //#endregion
1309
1309
  //#region src/model-catalog.js
@@ -1637,6 +1637,10 @@ const PNG_SIGNATURE = Buffer.from([
1637
1637
  26,
1638
1638
  10
1639
1639
  ]);
1640
+ const FULL_ATTACHMENT_ID = /^sha256:[0-9a-f]{64}$/u;
1641
+ const BARE_ATTACHMENT_DIGEST = /^[0-9a-f]{64}$/iu;
1642
+ const PATH_LIKE_ATTACHMENT_ID = /[\\/]/u;
1643
+ const FILE_NAME_ATTACHMENT_ID = /\.[A-Za-z0-9]{1,16}$/u;
1640
1644
  const record$2 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
1641
1645
  const nonEmpty = (value) => typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
1642
1646
  function normalizeImageOptions(args) {
@@ -1706,22 +1710,63 @@ function decodeCodexPng(value, maximumBytes) {
1706
1710
  return new Uint8Array(data);
1707
1711
  }
1708
1712
  function imageReference(value) {
1713
+ const originalDimensions = record$2(value.originalDimensions) ? {
1714
+ width: value.originalDimensions.width,
1715
+ height: value.originalDimensions.height
1716
+ } : void 0;
1709
1717
  return {
1710
1718
  attachmentId: value.attachmentId,
1711
1719
  mediaType: value.mediaType,
1712
1720
  bytes: value.bytes,
1713
1721
  width: value.width,
1714
1722
  height: value.height,
1715
- ...value.name === void 0 ? {} : { name: value.name }
1723
+ ...value.name === void 0 ? {} : { name: value.name },
1724
+ ...originalDimensions === void 0 ? {} : { originalDimensions }
1716
1725
  };
1717
1726
  }
1727
+ function normalizeAttachmentId(value) {
1728
+ if (typeof value !== "string") return void 0;
1729
+ if (FULL_ATTACHMENT_ID.test(value)) return value;
1730
+ const bare = BARE_ATTACHMENT_DIGEST.exec(value);
1731
+ return bare === null ? void 0 : `sha256:${bare[0].toLowerCase()}`;
1732
+ }
1733
+ function invalidAttachmentId(value) {
1734
+ const attachmentId = value?.attachmentId;
1735
+ if (typeof attachmentId === "string" && (PATH_LIKE_ATTACHMENT_ID.test(attachmentId) || FILE_NAME_ATTACHMENT_ID.test(attachmentId))) throw new Error("referenceImages attachmentId is a file path or filename; call read_image on that file and retry with its complete sha256:<64 lowercase hex> attachment reference. Do not omit referenceImages or fall back to new image generation.");
1736
+ throw new Error("referenceImages attachmentId must be sha256:<64 lowercase hex> copied from an image block or read_image result. Do not omit referenceImages or fall back to new image generation.");
1737
+ }
1718
1738
  function referenceOf(value, attachments) {
1719
- if (!record$2(value) || typeof value.attachmentId !== "string" || value.attachmentId.length === 0 || value.attachmentId.length > 256 || !attachments.imageLimits.mediaTypes.includes(value.mediaType) || !Number.isSafeInteger(value.bytes) || value.bytes <= 0 || !Number.isSafeInteger(value.width) || value.width <= 0 || !Number.isSafeInteger(value.height) || value.height <= 0 || value.name !== void 0 && (typeof value.name !== "string" || value.name.length > 256)) throw new Error("referenceImages contains an invalid image reference");
1720
- return imageReference(value);
1739
+ const attachmentId = normalizeAttachmentId(value?.attachmentId);
1740
+ if (attachmentId === void 0) invalidAttachmentId(value);
1741
+ if (!record$2(value) || !attachments.imageLimits.mediaTypes.includes(value.mediaType) || !Number.isSafeInteger(value.bytes) || value.bytes <= 0 || !Number.isSafeInteger(value.width) || value.width <= 0 || !Number.isSafeInteger(value.height) || value.height <= 0 || value.name !== void 0 && (typeof value.name !== "string" || value.name.length > 256) || value.originalDimensions !== void 0 && (!record$2(value.originalDimensions) || !Number.isSafeInteger(value.originalDimensions.width) || value.originalDimensions.width <= 0 || !Number.isSafeInteger(value.originalDimensions.height) || value.originalDimensions.height <= 0)) throw new Error("referenceImages contains an invalid image reference");
1742
+ return imageReference({
1743
+ ...value,
1744
+ attachmentId
1745
+ });
1721
1746
  }
1722
- async function editImages(values, attachments, signal) {
1747
+ function sessionImageReferences(messages) {
1748
+ const references = /* @__PURE__ */ new Map();
1749
+ const visit = (content) => {
1750
+ if (!Array.isArray(content)) return;
1751
+ for (const block of content) if (block?.type === "image" && record$2(block.attachment)) {
1752
+ const id = normalizeAttachmentId(block.attachment.attachmentId);
1753
+ if (id !== void 0) references.set(id, block.attachment);
1754
+ } else if (block?.type === "tool-result") visit(block.content);
1755
+ };
1756
+ for (const message of messages ?? []) visit(message?.content);
1757
+ return references;
1758
+ }
1759
+ async function editImages(values, attachments, signal, messages) {
1723
1760
  if (!Array.isArray(values) || values.length === 0 || values.length > MAX_REFERENCE_IMAGES) throw new Error(`referenceImages must contain between 1 and ${MAX_REFERENCE_IMAGES} images`);
1724
- const references = values.map((value) => referenceOf(value, attachments));
1761
+ const available = messages === void 0 ? void 0 : sessionImageReferences(messages);
1762
+ const references = values.map((value) => {
1763
+ const id = normalizeAttachmentId(value?.attachmentId);
1764
+ if (id === void 0) invalidAttachmentId(value);
1765
+ if (available === void 0) return referenceOf(value, attachments);
1766
+ const selected = available.get(id);
1767
+ if (selected === void 0) throw new Error("The selected image attachment cannot be found in the current session. Call read_image on the intended image and retry with its returned reference. Do not omit referenceImages or substitute another image.");
1768
+ return referenceOf(selected, attachments);
1769
+ });
1725
1770
  if (new Set(references.map((value) => value.attachmentId)).size !== references.length) throw new Error("referenceImages must not contain duplicates");
1726
1771
  const images = [];
1727
1772
  let totalBytes = 0;
@@ -1734,9 +1779,10 @@ async function editImages(values, attachments, signal) {
1734
1779
  return images;
1735
1780
  }
1736
1781
  function imageContent(value) {
1782
+ const label = typeof value.size === "string" && value.size.length > 0 ? `Generated a ${value.size} image.` : "Generated an image.";
1737
1783
  return [{
1738
1784
  type: "text",
1739
- text: typeof value.size === "string" && value.size.length > 0 ? `Generated a ${value.size} image.` : "Generated an image."
1785
+ text: value.localPath === void 0 ? label : `${label}\nOriginal PNG saved on the DSH host at: ${JSON.stringify(value.localPath)}. Read this file or copy it to the workspace with a .png extension; this host path is not a browser URL.`
1740
1786
  }, {
1741
1787
  type: "image",
1742
1788
  attachment: imageReference(value.image)
@@ -1773,7 +1819,21 @@ function imageOutputSchema() {
1773
1819
  type: "integer",
1774
1820
  required: true
1775
1821
  },
1776
- name: { type: "string" }
1822
+ name: { type: "string" },
1823
+ originalDimensions: {
1824
+ type: "object",
1825
+ additionalProperties: false,
1826
+ properties: {
1827
+ width: {
1828
+ type: "integer",
1829
+ required: true
1830
+ },
1831
+ height: {
1832
+ type: "integer",
1833
+ required: true
1834
+ }
1835
+ }
1836
+ }
1777
1837
  }
1778
1838
  },
1779
1839
  original: {
@@ -1813,6 +1873,11 @@ function imageOutputSchema() {
1813
1873
  }
1814
1874
  },
1815
1875
  background: { type: "string" },
1876
+ localPath: {
1877
+ type: "string",
1878
+ required: true,
1879
+ description: "Absolute path to the original PNG on the DSH host."
1880
+ },
1816
1881
  quality: { type: "string" },
1817
1882
  size: { type: "string" }
1818
1883
  }
@@ -1835,7 +1900,7 @@ function createCodexImageTool(options) {
1835
1900
  const attachments = options.attachments;
1836
1901
  return defineTool({
1837
1902
  name: CODEX_IMAGE_TOOL_NAME,
1838
- description: "Create a new image or explicitly edit selected prior images using the signed-in Codex subscription. Omit referenceImages for a completely new image. Include only the exact prior image references the user asked to edit; never assume every image in the conversation is a reference.",
1903
+ description: "Create a new image or explicitly edit selected prior images using the signed-in Codex subscription. Omit referenceImages only for a completely new image. For an edit, copy each complete image reference from the session image block or read_image result, including attachmentId in the exact form sha256:<64 lowercase hex>; never use a workspace path, absolute path, or filename, and do not omit the references to turn an edit into text-to-image generation. A bare 64-character hex digest is accepted and normalized to sha256:<64 lowercase hex>. If a reference is unavailable, call read_image and retry with its returned reference. Include only the exact prior image references the user asked to edit; never assume every image in the conversation is a reference. For annotation-guided edits, include both the named clean source and its numbered location reference, and preserve the numbered coordinates and requested changes in the prompt. The location-reference markers are guidance only and must not appear in the result. If those references cannot be identified, do not substitute unrelated images or silently ignore the annotations.",
1839
1904
  parameters: {
1840
1905
  prompt: {
1841
1906
  type: "string",
@@ -1867,14 +1932,15 @@ function createCodexImageTool(options) {
1867
1932
  },
1868
1933
  referenceImages: {
1869
1934
  type: "array",
1870
- description: "Optional explicit references to 1-5 prior images to edit. Omit for a new image.",
1935
+ description: "Optional explicit references to 1-5 prior images to edit. Copy each complete reference from the session image block or read_image result. Omit only for a new image; an invalid reference must be fixed and retried rather than omitted.",
1871
1936
  items: {
1872
1937
  type: "object",
1873
1938
  additionalProperties: false,
1874
1939
  properties: {
1875
1940
  attachmentId: {
1876
1941
  type: "string",
1877
- required: true
1942
+ required: true,
1943
+ description: "Copy the complete attachmentId from the image block or read_image result: sha256:<64 lowercase hex>. Never pass a workspace path, absolute path, or filename. A bare 64-character hex digest is accepted and normalized to sha256:<64 lowercase hex>."
1878
1944
  },
1879
1945
  mediaType: {
1880
1946
  type: "string",
@@ -1892,7 +1958,21 @@ function createCodexImageTool(options) {
1892
1958
  type: "integer",
1893
1959
  required: true
1894
1960
  },
1895
- name: { type: "string" }
1961
+ name: { type: "string" },
1962
+ originalDimensions: {
1963
+ type: "object",
1964
+ additionalProperties: false,
1965
+ properties: {
1966
+ width: {
1967
+ type: "integer",
1968
+ required: true
1969
+ },
1970
+ height: {
1971
+ type: "integer",
1972
+ required: true
1973
+ }
1974
+ }
1975
+ }
1896
1976
  }
1897
1977
  }
1898
1978
  }
@@ -1920,7 +2000,8 @@ function createCodexImageTool(options) {
1920
2000
  if (!attachments.imageLimits.mediaTypes.includes("image/png")) throw new Error("This DSH installation does not accept PNG image attachments");
1921
2001
  const maximumBytes = Math.min(attachments.imageLimits.maxImageBytes, attachments.imageLimits.maxMessageImageBytes);
1922
2002
  const editing = args.referenceImages !== void 0;
1923
- const images = editing ? await editImages(args.referenceImages, attachments, exec.signal) : void 0;
2003
+ const sessionMessages = editing && typeof options.getSessionMessages === "function" ? await options.getSessionMessages(exec.agent?.id) : void 0;
2004
+ const images = editing ? await editImages(args.referenceImages, attachments, exec.signal, sessionMessages ?? (options.getSessionMessages ? [] : void 0)) : void 0;
1924
2005
  let response;
1925
2006
  try {
1926
2007
  response = await fetchImage(editing ? CODEX_IMAGE_EDIT_URL : CODEX_IMAGE_GENERATION_URL, {
@@ -1973,6 +2054,7 @@ function createCodexImageTool(options) {
1973
2054
  const result = {
1974
2055
  image: imageReference(ref),
1975
2056
  original,
2057
+ localPath: options.originalImages.originalPath(original.assetId),
1976
2058
  ...metadata.background === void 0 ? {} : { background: metadata.background },
1977
2059
  ...metadata.quality === void 0 ? {} : { quality: metadata.quality },
1978
2060
  ...metadata.size === void 0 ? {} : { size: metadata.size }
@@ -2045,6 +2127,9 @@ var OriginalImageStore = class {
2045
2127
  if (!ORIGINAL_IMAGE_ID_PATTERN.test(assetId)) throw new TypeError("invalid original image asset id");
2046
2128
  return join(this.root, assetId.slice(4, 6), assetId);
2047
2129
  }
2130
+ originalPath(assetId) {
2131
+ return join(this.directory(assetId), "original");
2132
+ }
2048
2133
  async save(sessionId, data, name = "codex-generated-original.png") {
2049
2134
  if (!validSessionId(sessionId) || !(data instanceof Uint8Array) || data.byteLength === 0 || data.byteLength > 48 * 1024 * 1024) throw new TypeError("invalid original image input");
2050
2135
  const { width, height } = pngDimensions(data);
@@ -3294,6 +3379,7 @@ function apply(ctx) {
3294
3379
  getAuth: resolveAuth,
3295
3380
  readCredential: (options) => store.read(PROVIDER, options),
3296
3381
  attachments: ctx.attachments,
3382
+ getSessionMessages: (sessionId) => ctx.get?.("sessions")?.get?.(sessionId)?.deriveMessages?.() ?? [],
3297
3383
  originalImages,
3298
3384
  fetch: (input, init) => network.fetch("image", input, init)
3299
3385
  }));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-codex-subscription",
3
- "version": "1.13.1",
3
+ "version": "1.14.1",
4
4
  "description": "Use ChatGPT and Codex subscriptions in DeepSeek Harness with OAuth, quota, safe resets, web search, images, and Fast mode",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",
@@ -17,7 +17,6 @@
17
17
  "README.en.md",
18
18
  "README.zh-CN.md",
19
19
  "DIRECTORY.md",
20
- "AGENTS.md",
21
20
  "compatibility.json",
22
21
  "LICENSE",
23
22
  "SECURITY.md",
package/AGENTS.md DELETED
@@ -1,93 +0,0 @@
1
- # Agent installation guide
2
-
3
- Use this guide when a user asks an Agent to install, update, verify, or remove
4
- `dsh-codex-subscription` in a selected DeepSeek Harness profile.
5
-
6
- ## Safety
7
-
8
- - Confirm the target DSH installation and profile. Use `web` only when it is the user's target.
9
- - Use the exact `1.13.1` package below; do not install a moving branch.
10
- - Never print OAuth credentials, account IDs, authorization callbacks, or credential-store contents.
11
- - Preserve the DSH profile, unrelated plugins, sessions, and saved sign-in.
12
- - Do not start, stop, or restart DSH without explicit permission.
13
- - If more than one DSH installation exists, ask which one is the target before changing it.
14
-
15
- ## Locate DSH
16
-
17
- On Windows, check the standard DSH and official npm entry points without recursively scanning disks:
18
-
19
- ```powershell
20
- Get-Command dsh -ErrorAction SilentlyContinue
21
- Get-Command npx -ErrorAction SilentlyContinue
22
- Get-CimInstance Win32_Process -Filter "Name = 'node.exe'" |
23
- Where-Object { $_.CommandLine -match '@deepseek-ai[\\/]dsh[\\/]lib[\\/]bin\.js' } |
24
- Select-Object ExecutablePath, CommandLine
25
- ```
26
-
27
- DSH-Portable exposes the same standard `dsh plugin` contract. Missing system pnpm is normal; do not
28
- install it globally for this plugin. If several targets remain possible, stop and ask the user which one
29
- to change.
30
-
31
- ## Install or update
32
-
33
- For an official DSH installation run through npm, keep the complete `npx` prefix because the official
34
- run command does not create a global `dsh` command:
35
-
36
- ```sh
37
- npx -y @deepseek-ai/dsh@0.1.2-rc.1 plugin --profile web add dsh-codex-subscription@1.13.1
38
- ```
39
-
40
- With an existing global `dsh` command:
41
-
42
- ```sh
43
- dsh plugin --profile web add dsh-codex-subscription@1.13.1
44
- ```
45
-
46
- Use the same `add` command to update or repair. This is the complete package-changing operation.
47
- Do not download pnpm, create a second package store, save profile snapshots, add a resident manager,
48
- or restart DSH automatically. The DSH CLI owns package resolution, locking, and profile composition.
49
-
50
- ## Verify
51
-
52
- Use the same executable selected above:
53
-
54
- ```sh
55
- dsh plugin --profile web list dsh-codex-subscription --depth 0
56
- dsh --profile web --dump-config
57
- ```
58
-
59
- For the official npm route, run the same checks with `npx -y @deepseek-ai/dsh@0.1.2-rc.1` in place of `dsh`.
60
-
61
- 1. `dsh-codex-subscription` version `1.13.1` appears exactly once.
62
- 2. `codex-subscription` appears exactly once in the composed config.
63
- 3. No unrelated plugin or profile was changed and DSH was not restarted.
64
-
65
- With permission to restart DSH, open **Settings -> Codex** and verify the settings page loads. Confirm
66
- that search offers **Auto**, **DSH default**, and **Codex subscription**, the composer quota switch defaults
67
- to off, and `codex_image_generate` is available. Do not consume quota merely to test installation unless
68
- the user explicitly asks for a live model, search, or image-generation check.
69
-
70
- ## Uninstall
71
-
72
- ```sh
73
- dsh plugin --profile web remove dsh-codex-subscription
74
- ```
75
-
76
- For the official npm route:
77
-
78
- ```sh
79
- npx -y @deepseek-ai/dsh@0.1.2-rc.1 plugin --profile web remove dsh-codex-subscription
80
- ```
81
-
82
- Uninstall removes only this plugin. It must preserve the profile, sessions, other plugins, and saved
83
- ChatGPT sign-in. Signing out is a separate action and requires explicit permission.
84
-
85
- ## Failure handling
86
-
87
- Distinguish command discovery, network, HTTP/TLS, package-manager, profile-lock, version, and peer
88
- dependency failures. Do not disable TLS validation, delete a lock owned by a live process, install a
89
- second package manager, wipe a profile, expose credentials, or switch to another paid route.
90
-
91
- On failure, report the sanitized command error, DSH version, selected profile, requested plugin version,
92
- installation mode, what changed, and what remains unverified. A successful CLI exit is not live UI
93
- acceptance.