impel-cli 0.20.29 → 0.20.31

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/RELEASE_NOTES.md CHANGED
@@ -1,5 +1,26 @@
1
1
  # Release notes
2
2
 
3
+ ## 0.20.31 — Complete embedded Tasks controls
4
+
5
+ - Allows the guarded `delete_task` operation through the tenant desktop bridge
6
+ so the MCP App can complete the full task CRUD lifecycle.
7
+ - Keeps the embedded board below the macOS title-bar drag region so Board,
8
+ List, scope, refresh, create, and external-link controls receive pointer
9
+ input normally.
10
+ - Rebuilds managed Claude and ChatGPT bundles onto the corrected Tasks host
11
+ contract while preserving tenant profiles and app state.
12
+
13
+ ## 0.20.30 — Stable Claude and Codex upgrades
14
+
15
+ - Updates the reviewed native CLI pins to Claude Code 2.1.220 and Codex
16
+ 0.147.0 using the vendors' signed stable installers.
17
+ - Updates the managed Claude desktop pin to 1.26832.0 with its exact embedded
18
+ Claude Code 2.1.222 manifest and signed macOS and Windows artifacts.
19
+ - Updates the managed ChatGPT desktop pin to 26.803.41515 with embedded Codex
20
+ 0.147.0-alpha.6.5, using the exact signed macOS production artifacts.
21
+ - Rebuilds tenant bundles only after both upgraded desktop contracts pass
22
+ validation.
23
+
3
24
  ## 0.20.28 — Exact opaque answer resumes
4
25
 
5
26
  - Aligns generated Claude and Codex direct-answer adapters with the opaque
@@ -1,15 +1,15 @@
1
1
  # Native-agent host capability matrix
2
2
 
3
- Validated on 2026-08-07 with isolated temporary homes and tenant profiles. The
3
+ Validated on 2026-08-08 with isolated temporary homes and tenant profiles. The
4
4
  matrix is intentionally pinned: a client upgrade must be re-qualified before it
5
5
  is treated as an eager host.
6
6
 
7
7
  | Surface | Pinned build | Exact selected tools | Selected path | Host behavior |
8
8
  | --- | --- | --- | --- | --- |
9
- | Claude Code | 2.1.212 | Yes, agent frontmatter `tools` | Eager | The bound run/answer, resume, and recovery tools are direct child tools. No catalog discovery or code-cell polling is required. |
10
- | Claude Desktop | 1.24012.9 (embedded Code 2.1.219) | Yes, agent frontmatter `tools` | Eager | Same direct-tool contract as Claude Code. |
11
- | Codex CLI through `impel codex` | 0.146.0 | Yes, with `features.code_mode.direct_only_tool_namespaces = ["mcp__impel_agent"]` | Eager; optional single-thread profile | Existing `@Agent` children call the fixed MCP namespace directly. `impel codex --agent` also removes the parent spawn/wait/relay thread. |
12
- | ChatGPT desktop Codex | 26.727.40816 (embedded Codex 0.146.0 contract) | Yes, with the same exact native namespace | Eager child, compatible parent | Keeps the app's supported `@Agent` parent/child route, but the child no longer needs code cells or code-mode waits. |
9
+ | Claude Code | 2.1.220 | Yes, agent frontmatter `tools` | Eager | The bound run/answer, resume, and recovery tools are direct child tools. No catalog discovery or code-cell polling is required. |
10
+ | Claude Desktop | 1.26832.0 (embedded Code 2.1.222) | Yes, agent frontmatter `tools` | Eager | Same direct-tool contract as Claude Code. |
11
+ | Codex CLI through `impel codex` | 0.147.0 | Yes, with `features.code_mode.direct_only_tool_namespaces = ["mcp__impel_agent"]` | Eager; optional single-thread profile | Existing `@Agent` children call the fixed MCP namespace directly. `impel codex --agent` also removes the parent spawn/wait/relay thread. |
12
+ | ChatGPT desktop Codex | 26.803.41515 (embedded Codex 0.147.0-alpha.6.5) | Yes, with the same exact native namespace | Eager child, compatible parent | Keeps the app's supported `@Agent` parent/child route, but the child no longer needs code cells or code-mode waits. |
13
13
 
14
14
  ## Qualified contract
15
15
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "impel-cli",
3
- "version": "0.20.29",
3
+ "version": "0.20.31",
4
4
  "description": "Prepare isolated Claude and Codex workspaces for every accessible Impel tenant",
5
5
  "type": "module",
6
6
  "bin": {
package/src/apps.js CHANGED
@@ -67,47 +67,45 @@ const portableBundleMutationLocks = new Set();
67
67
  // CLI release therefore installs and accepts only the exact app builds tested
68
68
  // with its compatibility patches. Updating a pin requires a new CLI release.
69
69
  export const PINNED_VENDOR_APPS = Object.freeze({
70
- // 1.22209.3 routed managed 3P gateway profiles into /code/disabled because
71
- // it did not translate the managed desktop-Code switch into renderer access.
72
- // 1.24012.9 restores that contract by synthesizing claude_code and
73
- // claude_code_desktop access from the configured code surface. The pinned
74
- // contract test asserts that source path directly so this regression cannot
75
- // hide behind otherwise-valid configLibrary schema markers again.
70
+ // Keep the desktop build, its embedded Claude Code manifest, and every
71
+ // renderer patch on one exact reviewed release. The pinned contract test
72
+ // asserts the managed desktop-Code access path directly so that regression
73
+ // cannot hide behind otherwise-valid configLibrary schema markers.
76
74
  claude: Object.freeze({
77
- version: "1.24012.9",
78
- claudeCodeVersion: "2.1.219",
79
- claudeCodeCommit: "7006c4c3acac98e554d3997baeda6a7fa4d1ff7c",
75
+ version: "1.26832.0",
76
+ claudeCodeVersion: "2.1.222",
77
+ claudeCodeCommit: "fbf49312c28437bf9c2546b9ace3bd7b34eb6ff6",
80
78
  bundleName: "Claude.app",
81
79
  windows: Object.freeze({
82
80
  packageName: "Claude",
83
- packageVersion: "1.24012.9.0",
81
+ packageVersion: "1.26832.0.0",
84
82
  publisher: 'CN="Anthropic, PBC", O="Anthropic, PBC", L=San Francisco, S=California, C=US, SERIALNUMBER=4860621, OID.2.5.4.15=Private Organization, OID.1.3.6.1.4.1.311.60.2.1.2=Delaware, OID.1.3.6.1.4.1.311.60.2.1.3=US',
85
83
  executable: "app\\Claude.exe",
86
84
  downloads: Object.freeze({
87
85
  arm64: Object.freeze({
88
- url: "https://downloads.claude.ai/releases/win32/arm64/1.24012.9/Claude-03c61d06f8e01a4db2273b9514e225f21d2ba62e.msix",
89
- sha256: "52ed0402ca70fc95e064417ff9bdf3a6336fe5dbf0612f7797937fd0d35ecdba",
86
+ url: "https://downloads.claude.ai/releases/win32/arm64/1.26832.0/Claude-056ee2be623b207f6a4d24dfb1b2fb5a82db0ecf.msix",
87
+ sha256: "16f9f9b074b1ce1bbc67c7facfd2279825c6b15b6abee28ccf7fedffe3e23a23",
90
88
  }),
91
89
  x64: Object.freeze({
92
- url: "https://downloads.claude.ai/releases/win32/x64/1.24012.9/Claude-03c61d06f8e01a4db2273b9514e225f21d2ba62e.msix",
93
- sha256: "c7d61479f06a3490bd2598405aa95a428abb9fe31ed97f9e0a1fba7cd2745fe2",
90
+ url: "https://downloads.claude.ai/releases/win32/x64/1.26832.0/Claude-056ee2be623b207f6a4d24dfb1b2fb5a82db0ecf.msix",
91
+ sha256: "6dc210bca31b55c9fa307d11c6b13a42c7f3a3886ccc35ca2ecb7e9fceba0139",
94
92
  }),
95
93
  }),
96
94
  }),
97
95
  downloads: Object.freeze({
98
96
  arm64: Object.freeze({
99
- url: "https://downloads.claude.ai/releases/darwin/universal/1.24012.9/Claude-03c61d06f8e01a4db2273b9514e225f21d2ba62e.zip",
100
- sha256: "39ad2d181e2b1b017c5506f66c08399c23575c802d8b63927f40128e77103a9b",
97
+ url: "https://downloads.claude.ai/releases/darwin/universal/1.26832.0/Claude-056ee2be623b207f6a4d24dfb1b2fb5a82db0ecf.zip",
98
+ sha256: "6b0f2c51c5e1c3f6db3885233ad96e48f92e61438b0bd9892c69f2ea11c54950",
101
99
  }),
102
100
  x64: Object.freeze({
103
- url: "https://downloads.claude.ai/releases/darwin/universal/1.24012.9/Claude-03c61d06f8e01a4db2273b9514e225f21d2ba62e.zip",
104
- sha256: "39ad2d181e2b1b017c5506f66c08399c23575c802d8b63927f40128e77103a9b",
101
+ url: "https://downloads.claude.ai/releases/darwin/universal/1.26832.0/Claude-056ee2be623b207f6a4d24dfb1b2fb5a82db0ecf.zip",
102
+ sha256: "6b0f2c51c5e1c3f6db3885233ad96e48f92e61438b0bd9892c69f2ea11c54950",
105
103
  }),
106
104
  }),
107
105
  }),
108
106
  chatgpt: Object.freeze({
109
- version: "26.727.40816",
110
- codexVersion: "0.146.0-alpha.9.2",
107
+ version: "26.803.41515",
108
+ codexVersion: "0.147.0-alpha.6.5",
111
109
  bundleName: "ChatGPT.app",
112
110
  windows: Object.freeze({
113
111
  storeProductId: "9PLM9XGG6VKS",
@@ -123,12 +121,12 @@ export const PINNED_VENDOR_APPS = Object.freeze({
123
121
  }),
124
122
  downloads: Object.freeze({
125
123
  arm64: Object.freeze({
126
- url: "https://persistent.oaistatic.com/codex-app-prod/ChatGPT-darwin-arm64-26.727.40816.zip",
127
- sha256: "fdbede9b8a28b5bf3bbf1213fa04291724faa6ed2d1d61bb04853bbdfebce219",
124
+ url: "https://persistent.oaistatic.com/codex-app-prod/ChatGPT-darwin-arm64-26.803.41515.zip",
125
+ sha256: "8abd46bf063bc27cbadcbc2863007ac44365b13a23ede17ceaee81fb9eeaeb9a",
128
126
  }),
129
127
  x64: Object.freeze({
130
- url: "https://persistent.oaistatic.com/codex-app-prod/ChatGPT-darwin-x64-26.727.40816.zip",
131
- sha256: "12622d916a3993d61fb52a318dcff7af7449eea5070cdd7e094da29f380e3374",
128
+ url: "https://persistent.oaistatic.com/codex-app-prod/ChatGPT-darwin-x64-26.803.41515.zip",
129
+ sha256: "87239a3dd12e2761de515ee78a0b73c02ac907c34c1f01a68dd6f093cf433fb1",
132
130
  }),
133
131
  }),
134
132
  }),
@@ -149,14 +147,14 @@ const CHATGPT_THREAD_FORK_PROVIDER = /let ([A-Za-z_$][\w$]*)=\{\.\.\.([A-Za-z_$]
149
147
  const CHATGPT_THREAD_RESUME_PROVIDER = /modelProvider:null,cwd:([A-Za-z_$][\w$]*)\?\.cwd\?\?null,approvalPolicy:null,sandbox:null,config:\1\?\.config\?\?null,developerInstructions:\1\?\.developerInstructions\?\?void 0/gu;
150
148
  const CHATGPT_STRUCTURED_PROVIDER_FALLBACK = /allowProviderModelFallback:!0/gu;
151
149
  const CHATGPT_RESOURCE_ROOT = "linked_vendor_resources_with_patched_asar";
152
- const CLAUDE_PLAN_USAGE_GUARD = "if(!PSe()){cz();return}if(!Ge().hasOrgPolicyBackend())return;if((r=ar())";
153
- const IMPEL_CLAUDE_PLAN_USAGE_GUARD = `if(!PSe()){cz();return}${"".padEnd("if(!Ge().hasOrgPolicyBackend())return;".length, " ")}if((r=ar())`;
154
- const CLAUDE_DRAFT_AGENT_MENTIONS = 'agentMentionsEnabled:"draft"===Ge&&t';
155
- const IMPEL_CLAUDE_BOUND_AGENT_MENTIONS = 'agentMentionsEnabled:"draft"===Ge?t:Ue?.agent??!1';
150
+ const CLAUDE_PLAN_USAGE_GUARD = "if(!Pi()){ua();return}if(!d.an().hasOrgPolicyBackend())return;let r=p.n();";
151
+ const IMPEL_CLAUDE_PLAN_USAGE_GUARD = `if(!Pi()){ua();return}${"".padEnd("if(!d.an().hasOrgPolicyBackend())return;".length, " ")}let r=p.n();`;
152
+ const CLAUDE_DRAFT_AGENT_MENTIONS = 'agentMentionsEnabled:"draft"===Ke&&t';
153
+ const IMPEL_CLAUDE_BOUND_AGENT_MENTIONS = 'agentMentionsEnabled:"draft"===Ke?t:E?.agent??!1';
156
154
  const CLAUDE_AGENT_MENTION_FILTER = 'return e.filter(e=>"user"===e.source&&(!n||e.name.toLowerCase().includes(n))).slice(0,10)';
157
- const IMPEL_CLAUDE_BOUND_AGENT_MENTION_FILTER = 'return e.filter(e=>"user"===e.source&&(!n||e.name.toLowerCase().includes(n))&&("string"!=typeof x||e.name===x)).slice(0,10)';
155
+ const IMPEL_CLAUDE_BOUND_AGENT_MENTION_FILTER = 'return e.filter(e=>"user"===e.source&&(!n||e.name.toLowerCase().includes(n))&&("string"!=typeof v||e.name===v)).slice(0,10)';
158
156
  const CLAUDE_AGENT_MENTION_SELECT = 'onSelect:t=>(e(String(t)),Promise.resolve(null))';
159
- const IMPEL_CLAUDE_BOUND_AGENT_MENTION_SELECT = 'onSelect:t=>"string"==typeof x?Promise.resolve({chipText:String(t)}):(e(String(t)),Promise.resolve(null))';
157
+ const IMPEL_CLAUDE_BOUND_AGENT_MENTION_SELECT = 'onSelect:t=>"string"==typeof v?Promise.resolve({chipText:String(t)}):(e(String(t)),Promise.resolve(null))';
160
158
  const LEGACY_CLAUDE_SAFE_STORAGE_NAME = "Claude";
161
159
  const CLAUDE_SAFE_STORAGE_METADATA = "safe-storage.json";
162
160
  // The pinned Claude renderer decides desktop-vs-web mode SOLELY by matching its
@@ -301,7 +299,7 @@ export const CURRENT_CONFIG_VERSION = 32;
301
299
  // — which is what made every `impel update` re-trigger macOS permission
302
300
  // prompts. Bump this ONLY when a code change alters the bytes of a built
303
301
  // bundle; leave it alone for changes that don't touch bundle contents.
304
- export const BUNDLE_BUILD_FINGERPRINT = "bundle-2026-08-08.2";
302
+ export const BUNDLE_BUILD_FINGERPRINT = "bundle-2026-08-08.4";
305
303
 
306
304
  /** Parse the tenant's install manifest, or null when absent/corrupt. */
307
305
  export function readTenantManifest(homeDir = os.homedir(), tenantId = null) {
@@ -2102,11 +2100,61 @@ function patchFixedWidthAsarString(asarPath, original, replacement, label) {
2102
2100
  return applyFixedWidthAsarPatches(asarPath, archive, patches, label);
2103
2101
  }
2104
2102
 
2103
+ function exportedAsarSymbol(source, localSymbol) {
2104
+ const match = source.match(new RegExp(
2105
+ `Object\\.defineProperty\\(exports,"([^"]+)",\\{enumerable:!0,get:function\\(\\)\\{return ${escapeRegex(localSymbol)}\\}\\}\\)`,
2106
+ "u",
2107
+ ));
2108
+ return match?.[1] ?? null;
2109
+ }
2110
+
2111
+ function claudeModularCredentialExpression(archive, requestOffset) {
2112
+ const metadata = readAsarMetadata(archive);
2113
+ const requestRecord = findAsarEntryRecord(metadata.header, metadata.dataOffset, requestOffset);
2114
+ const requestStart = metadata.dataOffset + Number(requestRecord.entry.offset);
2115
+ const requestSource = archive.subarray(
2116
+ requestStart,
2117
+ requestStart + Number(requestRecord.entry.size),
2118
+ ).toString("latin1");
2119
+ const importPattern = /([A-Za-z_$][\w$]*)=require\("([^"\n]+)"\)/gu;
2120
+
2121
+ for (const imported of requestSource.matchAll(importPattern)) {
2122
+ const importedPath = path.posix.normalize(path.posix.join(
2123
+ path.posix.dirname(requestRecord.path),
2124
+ imported[2],
2125
+ ));
2126
+ const importedEntry = findAsarEntryByPath(metadata.header, importedPath);
2127
+ if (!importedEntry) continue;
2128
+ const importedStart = metadata.dataOffset + Number(importedEntry.offset);
2129
+ const importedSource = archive.subarray(
2130
+ importedStart,
2131
+ importedStart + Number(importedEntry.size),
2132
+ ).toString("latin1");
2133
+ if (!importedSource.includes("[custom-3p] Credentials loaded from managed config")) continue;
2134
+
2135
+ const credentialParser = importedSource.match(
2136
+ /([A-Za-z_$][\w$]*)=([A-Za-z_$][\w$]*)\(e,([A-Za-z_$][\w$]*)\(\)\),\1&&[\s\S]{0,250}?\[custom-3p\] Credentials loaded from managed config/u,
2137
+ );
2138
+ const configAccessor = importedSource.match(
2139
+ /function ([A-Za-z_$][\w$]*)\(e\)\{let ([A-Za-z_$][\w$]*)=[A-Za-z_$][\w$]*\(\);return e\?\.ignoreScopes\?[\s\S]{0,200}?\2\.config\}/u,
2140
+ )?.[1];
2141
+ if (!credentialParser || !configAccessor) continue;
2142
+
2143
+ const parserExport = exportedAsarSymbol(importedSource, credentialParser[2]);
2144
+ const errorsExport = exportedAsarSymbol(importedSource, credentialParser[3]);
2145
+ const configExport = exportedAsarSymbol(importedSource, configAccessor);
2146
+ if (!parserExport || !errorsExport || !configExport) continue;
2147
+ const namespace = imported[1];
2148
+ return `${namespace}.${parserExport}(${namespace}.${configExport}(),${namespace}.${errorsExport}()).apiKey`;
2149
+ }
2150
+ return null;
2151
+ }
2152
+
2105
2153
  function patchClaudePlanUsageRequest(asarPath) {
2106
2154
  const archive = fs.readFileSync(asarPath);
2107
2155
  const source = archive.toString("latin1");
2108
2156
  const credentialAccessor = source.match(
2109
- /function ([A-Za-z_$][\w$]*)\(e\)\{var t;return [\s\S]{0,500}?\[custom-3p\] Credentials loaded from managed config/u,
2157
+ /function ([A-Za-z_$][\w$]*)\(e\)\{(?:var t;)?return [\s\S]{0,500}?\[custom-3p\] Credentials loaded from managed config/u,
2110
2158
  )?.[1];
2111
2159
  if (!credentialAccessor) {
2112
2160
  return applyFixedWidthAsarPatches(asarPath, archive, [], "Claude plan-usage gateway request");
@@ -2116,14 +2164,16 @@ function patchClaudePlanUsageRequest(asarPath) {
2116
2164
  "u",
2117
2165
  );
2118
2166
  const configAccessor = source.match(configAccessorPattern)?.[1];
2119
- if (!configAccessor) {
2120
- return applyFixedWidthAsarPatches(asarPath, archive, [], "Claude plan-usage gateway request");
2121
- }
2122
-
2123
- const requestPattern = /const ([A-Za-z_$][\w$]*)=`\$\{[A-Za-z_$][\w$]*\(\)\}\/api\/organizations\/\$\{[A-Za-z_$][\w$]*\}\/usage\$\{[A-Za-z_$][\w$]*\?"\?skip_spend=1":""\}`,([A-Za-z_$][\w$]*)=await ([A-Za-z_$][\w$]*)\.net\.fetch\(\1,\{signal:AbortSignal\.timeout\([A-Za-z_$][\w$]*\)\}\)/gu;
2167
+ const requestPattern = /(?:const|let) ([A-Za-z_$][\w$]*)=`\$\{[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)?\(\)\}\/api\/organizations\/\$\{[A-Za-z_$][\w$]*\}\/usage\$\{[A-Za-z_$][\w$]*\?(?:"\?skip_spend=1"|`\?skip_spend=1`):(?:""|``)\}`,([A-Za-z_$][\w$]*)=await ([A-Za-z_$][\w$]*)\.net\.fetch\(\1,\{signal:AbortSignal\.timeout\([A-Za-z_$][\w$]*\)\}\)/gu;
2124
2168
  const patches = [...source.matchAll(requestPattern)].map((match) => {
2125
2169
  const original = match[0];
2126
- const replacement = `const ${match[1]}="https://gateway.useimpel.com/ui/a/u",${match[2]}=await ${match[3]}.net.fetch(${match[1]},{headers:{key:${credentialAccessor}(${configAccessor}()).apiKey}})`;
2170
+ const credentialExpression = configAccessor
2171
+ ? `${credentialAccessor}(${configAccessor}()).apiKey`
2172
+ : claudeModularCredentialExpression(archive, match.index);
2173
+ if (!credentialExpression) {
2174
+ throw new Error("this desktop app version has an unsupported Claude plan-usage gateway credential accessor; update impel-cli before installing it");
2175
+ }
2176
+ const replacement = `const ${match[1]}="https://gateway.useimpel.com/ui/a/u",${match[2]}=await ${match[3]}.net.fetch(${match[1]},{headers:{key:${credentialExpression}}})`;
2127
2177
  if (replacement.length > original.length) {
2128
2178
  throw new Error("Claude plan-usage gateway request replacement no longer fits the vendor ASAR contract");
2129
2179
  }
@@ -2135,7 +2185,7 @@ function patchClaudePlanUsageRequest(asarPath) {
2135
2185
  function patchClaudeSafeStorageName(asarPath) {
2136
2186
  const archive = fs.readFileSync(asarPath);
2137
2187
  const source = archive.toString("latin1");
2138
- const startupPattern = /([A-Za-z_$][\w$]*)\.app\.isPackaged\|\|\1\.app\.setName\("Claude"\)/gu;
2188
+ const startupPattern = /([A-Za-z_$][\w$]*)\.app\.isPackaged\|\|\1\.app\.setName\((?:"Claude"|`Claude`)\)/gu;
2139
2189
  const patches = [...source.matchAll(startupPattern)].map((match) => {
2140
2190
  const original = match[0];
2141
2191
  const replacement = `${match[1]}.app.setName(process.env.IMPEL_CN)`;
@@ -2165,10 +2215,41 @@ function patchFastModeAuthGate(asarPath) {
2165
2215
  }
2166
2216
 
2167
2217
  function patchDesktopTasksPreload(asarPath, target) {
2218
+ const legacyClaudeFooter = "//# sourceMappingURL=index.pre.js.map\n";
2168
2219
  const footer = target === "claude"
2169
- ? "//# sourceMappingURL=index.pre.js.map\n"
2220
+ ? legacyClaudeFooter
2170
2221
  : "//# sourceMappingURL=early-bootstrap.js.map";
2171
2222
  const loader = "require(process.env.IMPEL_DT)";
2223
+ if (target === "claude") {
2224
+ const archive = fs.readFileSync(asarPath);
2225
+ const source = archive.toString("latin1");
2226
+ const legacyMatches = exactTextMatchCount(source, legacyClaudeFooter);
2227
+ if (legacyMatches === 0) {
2228
+ const appNameMatches = [...source.matchAll(/[A-Za-z_$][\w$]*\.app\.setName\(process\.env\.IMPEL_CN\)/gu)];
2229
+ if (appNameMatches.length !== 1) {
2230
+ return applyFixedWidthAsarPatches(asarPath, archive, [], "Claude desktop Tasks preload");
2231
+ }
2232
+ const metadata = readAsarMetadata(archive);
2233
+ const record = findAsarEntryRecord(metadata.header, metadata.dataOffset, appNameMatches[0].index);
2234
+ const currentFooter = "//# sourceMappingURL=index.js.map";
2235
+ const footerOffset = metadata.dataOffset + Number(record.entry.offset) + Number(record.entry.size)
2236
+ - Buffer.byteLength(currentFooter, "latin1");
2237
+ if (source.slice(footerOffset, footerOffset + currentFooter.length) !== currentFooter) {
2238
+ return applyFixedWidthAsarPatches(asarPath, archive, [], "Claude desktop Tasks preload");
2239
+ }
2240
+ if (loader.length >= currentFooter.length) {
2241
+ throw new Error("desktop Tasks preload no longer fits the vendor ASAR contract");
2242
+ }
2243
+ return applyFixedWidthAsarPatches(asarPath, archive, [{
2244
+ offset: footerOffset,
2245
+ original: currentFooter,
2246
+ replacement: loader.padEnd(currentFooter.length, " "),
2247
+ }], "Claude desktop Tasks preload");
2248
+ }
2249
+ if (legacyMatches !== 1) {
2250
+ return applyFixedWidthAsarPatches(asarPath, archive, [], "Claude desktop Tasks preload");
2251
+ }
2252
+ }
2172
2253
  const trailingNewline = footer.endsWith("\n") ? "\n" : "";
2173
2254
  if (loader.length >= footer.length - trailingNewline.length) {
2174
2255
  throw new Error("desktop Tasks preload no longer fits the vendor ASAR contract");
@@ -2324,23 +2405,36 @@ function readAsarMetadata(archive) {
2324
2405
  return { dataOffset, header, headerLength };
2325
2406
  }
2326
2407
 
2327
- function findAsarEntry(header, dataOffset, archiveOffset) {
2408
+ function findAsarEntryRecord(header, dataOffset, archiveOffset) {
2328
2409
  const relativeOffset = archiveOffset - dataOffset;
2329
2410
  let found = null;
2330
- const visit = (directory) => {
2331
- for (const entry of Object.values(directory?.files || {})) {
2332
- if (entry.files) visit(entry);
2411
+ const visit = (directory, parent = "") => {
2412
+ for (const [name, entry] of Object.entries(directory?.files || {})) {
2413
+ const entryPath = parent ? `${parent}/${name}` : name;
2414
+ if (entry.files) visit(entry, entryPath);
2333
2415
  else if (entry.offset != null) {
2334
2416
  const start = Number(entry.offset);
2335
- if (start <= relativeOffset && relativeOffset < start + Number(entry.size)) found = entry;
2417
+ if (start <= relativeOffset && relativeOffset < start + Number(entry.size)) {
2418
+ found = { entry, path: entryPath };
2419
+ }
2336
2420
  }
2337
2421
  }
2338
2422
  };
2339
2423
  visit(header);
2340
- if (!found?.integrity) throw new Error("Fast-mode renderer asset has no supported ASAR integrity metadata");
2424
+ if (!found?.entry?.integrity) throw new Error("Fast-mode renderer asset has no supported ASAR integrity metadata");
2341
2425
  return found;
2342
2426
  }
2343
2427
 
2428
+ function findAsarEntry(header, dataOffset, archiveOffset) {
2429
+ return findAsarEntryRecord(header, dataOffset, archiveOffset).entry;
2430
+ }
2431
+
2432
+ function findAsarEntryByPath(header, targetPath) {
2433
+ let current = header;
2434
+ for (const part of targetPath.split("/")) current = current?.files?.[part];
2435
+ return current?.offset != null ? current : null;
2436
+ }
2437
+
2344
2438
  function updateAsarEntryIntegrity(archive, dataOffset, entry) {
2345
2439
  const start = dataOffset + Number(entry.offset);
2346
2440
  const contents = archive.subarray(start, start + Number(entry.size));
@@ -509,12 +509,16 @@ if (
509
509
  const parseBounds = (url, window) => {
510
510
  const parsed = new URL(url);
511
511
  const content = window.getContentBounds();
512
+ const interactiveTop = process.platform === "darwin" ? 48 : 0;
512
513
  const number = (name, fallback) => {
513
514
  const value = Number.parseInt(parsed.searchParams.get(name) || "", 10);
514
515
  return Number.isFinite(value) ? value : fallback;
515
516
  };
516
517
  const x = Math.max(0, Math.min(content.width - 240, number("x", 0)));
517
- const y = Math.max(0, Math.min(content.height - 240, number("y", 0)));
518
+ const y = Math.max(
519
+ interactiveTop,
520
+ Math.min(content.height - 240, number("y", 0)),
521
+ );
518
522
  return {
519
523
  x,
520
524
  y,
@@ -606,7 +610,13 @@ if (
606
610
  const nativeBoardBounds = (window) => {
607
611
  const content = window.getContentBounds();
608
612
  const x = Math.min(296, Math.max(0, content.width - 240));
609
- return { x, y: 0, width: Math.max(240, content.width - x), height: Math.max(240, content.height) };
613
+ const interactiveTop = process.platform === "darwin" ? 48 : 0;
614
+ return {
615
+ x,
616
+ y: interactiveTop,
617
+ width: Math.max(240, content.width - x),
618
+ height: Math.max(240, content.height - interactiveTop),
619
+ };
610
620
  };
611
621
 
612
622
  const layoutNativeNav = (window) => {
@@ -825,7 +835,15 @@ if (
825
835
  }
826
836
  if (request?.method === "tools/call") {
827
837
  const name = request?.params?.name;
828
- const allowed = new Set(["show_tasks", "list_tasks", "get_task", "list_members", "create_task", "update_task"]);
838
+ const allowed = new Set([
839
+ "show_tasks",
840
+ "list_tasks",
841
+ "get_task",
842
+ "list_members",
843
+ "create_task",
844
+ "update_task",
845
+ "delete_task",
846
+ ]);
829
847
  if (!allowed.has(name)) throw new Error("Unsupported Tasks tool.");
830
848
  const args = request?.params?.arguments;
831
849
  if (args != null && (typeof args !== "object" || Array.isArray(args))) {
@@ -2,6 +2,6 @@
2
2
  // Windows installers on the same versions; do not replace these with moving
3
3
  // channels such as "stable" or "latest".
4
4
  export const PINNED_VENDOR_CLI_VERSIONS = Object.freeze({
5
- claude: "2.1.212",
6
- codex: "0.146.0",
5
+ claude: "2.1.220",
6
+ codex: "0.147.0",
7
7
  });