@caupulican/pi-adaptative 0.85.8 → 0.86.2

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.
Files changed (239) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/dist/core/agent-paths.d.ts +6 -3
  3. package/dist/core/agent-paths.d.ts.map +1 -1
  4. package/dist/core/agent-paths.js +10 -3
  5. package/dist/core/agent-paths.js.map +1 -1
  6. package/dist/core/agent-session.d.ts +5 -2
  7. package/dist/core/agent-session.d.ts.map +1 -1
  8. package/dist/core/agent-session.js +7 -1
  9. package/dist/core/agent-session.js.map +1 -1
  10. package/dist/core/autonomy/bounded-completion.d.ts.map +1 -1
  11. package/dist/core/autonomy/bounded-completion.js +19 -8
  12. package/dist/core/autonomy/bounded-completion.js.map +1 -1
  13. package/dist/core/autonomy/lane-tool-surface.d.ts +11 -5
  14. package/dist/core/autonomy/lane-tool-surface.d.ts.map +1 -1
  15. package/dist/core/autonomy/lane-tool-surface.js +34 -8
  16. package/dist/core/autonomy/lane-tool-surface.js.map +1 -1
  17. package/dist/core/autonomy/subagent-prompt.d.ts +3 -3
  18. package/dist/core/autonomy/subagent-prompt.js +9 -9
  19. package/dist/core/autonomy/subagent-prompt.js.map +1 -1
  20. package/dist/core/background-lane-controller.d.ts +10 -8
  21. package/dist/core/background-lane-controller.d.ts.map +1 -1
  22. package/dist/core/background-lane-controller.js +23 -13
  23. package/dist/core/background-lane-controller.js.map +1 -1
  24. package/dist/core/bash-execution-controller.d.ts +4 -0
  25. package/dist/core/bash-execution-controller.d.ts.map +1 -1
  26. package/dist/core/bash-execution-controller.js +11 -4
  27. package/dist/core/bash-execution-controller.js.map +1 -1
  28. package/dist/core/bash-executor.d.ts +2 -0
  29. package/dist/core/bash-executor.d.ts.map +1 -1
  30. package/dist/core/bash-executor.js +2 -1
  31. package/dist/core/bash-executor.js.map +1 -1
  32. package/dist/core/capability-contract.d.ts +1 -1
  33. package/dist/core/capability-contract.d.ts.map +1 -1
  34. package/dist/core/capability-contract.js +0 -1
  35. package/dist/core/capability-contract.js.map +1 -1
  36. package/dist/core/default-tool-surface.d.ts +1 -0
  37. package/dist/core/default-tool-surface.d.ts.map +1 -1
  38. package/dist/core/default-tool-surface.js +3 -2
  39. package/dist/core/default-tool-surface.js.map +1 -1
  40. package/dist/core/delegation/worker-agent-control-coordinator.d.ts +27 -8
  41. package/dist/core/delegation/worker-agent-control-coordinator.d.ts.map +1 -1
  42. package/dist/core/delegation/worker-agent-control-coordinator.js +191 -33
  43. package/dist/core/delegation/worker-agent-control-coordinator.js.map +1 -1
  44. package/dist/core/delegation/worker-agent-control.d.ts +45 -6
  45. package/dist/core/delegation/worker-agent-control.d.ts.map +1 -1
  46. package/dist/core/delegation/worker-agent-control.js +110 -13
  47. package/dist/core/delegation/worker-agent-control.js.map +1 -1
  48. package/dist/core/delegation/worker-attempt-executor.d.ts.map +1 -1
  49. package/dist/core/delegation/worker-attempt-executor.js +177 -158
  50. package/dist/core/delegation/worker-attempt-executor.js.map +1 -1
  51. package/dist/core/delegation/worker-authority-resolver.d.ts +23 -0
  52. package/dist/core/delegation/worker-authority-resolver.d.ts.map +1 -0
  53. package/dist/core/delegation/worker-authority-resolver.js +128 -0
  54. package/dist/core/delegation/worker-authority-resolver.js.map +1 -0
  55. package/dist/core/delegation/worker-conversation-store.d.ts.map +1 -1
  56. package/dist/core/delegation/worker-conversation-store.js +1 -1
  57. package/dist/core/delegation/worker-conversation-store.js.map +1 -1
  58. package/dist/core/delegation/worker-delegation-controller.d.ts +7 -1
  59. package/dist/core/delegation/worker-delegation-controller.d.ts.map +1 -1
  60. package/dist/core/delegation/worker-delegation-controller.js +260 -87
  61. package/dist/core/delegation/worker-delegation-controller.js.map +1 -1
  62. package/dist/core/delegation/worker-delegation-request.d.ts +25 -4
  63. package/dist/core/delegation/worker-delegation-request.d.ts.map +1 -1
  64. package/dist/core/delegation/worker-delegation-request.js +90 -1
  65. package/dist/core/delegation/worker-delegation-request.js.map +1 -1
  66. package/dist/core/delegation/worker-execution-policy.d.ts +2 -0
  67. package/dist/core/delegation/worker-execution-policy.d.ts.map +1 -1
  68. package/dist/core/delegation/worker-execution-policy.js +22 -12
  69. package/dist/core/delegation/worker-execution-policy.js.map +1 -1
  70. package/dist/core/delegation/worker-lifecycle.d.ts +1 -0
  71. package/dist/core/delegation/worker-lifecycle.d.ts.map +1 -1
  72. package/dist/core/delegation/worker-lifecycle.js +4 -2
  73. package/dist/core/delegation/worker-lifecycle.js.map +1 -1
  74. package/dist/core/delegation/worker-profile-resolver.d.ts +0 -2
  75. package/dist/core/delegation/worker-profile-resolver.d.ts.map +1 -1
  76. package/dist/core/delegation/worker-profile-resolver.js +8 -35
  77. package/dist/core/delegation/worker-profile-resolver.js.map +1 -1
  78. package/dist/core/delegation/worker-recovery-coordinator.d.ts.map +1 -1
  79. package/dist/core/delegation/worker-recovery-coordinator.js +1 -0
  80. package/dist/core/delegation/worker-recovery-coordinator.js.map +1 -1
  81. package/dist/core/delegation/worker-runner.d.ts +6 -1
  82. package/dist/core/delegation/worker-runner.d.ts.map +1 -1
  83. package/dist/core/delegation/worker-runner.js +20 -6
  84. package/dist/core/delegation/worker-runner.js.map +1 -1
  85. package/dist/core/delegation/worker-tree-budget-coordinator.d.ts +47 -0
  86. package/dist/core/delegation/worker-tree-budget-coordinator.d.ts.map +1 -0
  87. package/dist/core/delegation/worker-tree-budget-coordinator.js +210 -0
  88. package/dist/core/delegation/worker-tree-budget-coordinator.js.map +1 -0
  89. package/dist/core/memory/providers/file-store.d.ts +2 -0
  90. package/dist/core/memory/providers/file-store.d.ts.map +1 -1
  91. package/dist/core/memory/providers/file-store.js +22 -20
  92. package/dist/core/memory/providers/file-store.js.map +1 -1
  93. package/dist/core/models/runtime-process.d.ts.map +1 -1
  94. package/dist/core/models/runtime-process.js +5 -18
  95. package/dist/core/models/runtime-process.js.map +1 -1
  96. package/dist/core/orchestration/bounded-string-array.d.ts +11 -0
  97. package/dist/core/orchestration/bounded-string-array.d.ts.map +1 -0
  98. package/dist/core/orchestration/bounded-string-array.js +17 -0
  99. package/dist/core/orchestration/bounded-string-array.js.map +1 -0
  100. package/dist/core/orchestration/capability-gateway.d.ts +16 -0
  101. package/dist/core/orchestration/capability-gateway.d.ts.map +1 -1
  102. package/dist/core/orchestration/capability-gateway.js +38 -0
  103. package/dist/core/orchestration/capability-gateway.js.map +1 -1
  104. package/dist/core/orchestration/contracts.d.ts +11 -3
  105. package/dist/core/orchestration/contracts.d.ts.map +1 -1
  106. package/dist/core/orchestration/contracts.js +0 -1
  107. package/dist/core/orchestration/contracts.js.map +1 -1
  108. package/dist/core/orchestration/delegation-ledger.d.ts +1 -0
  109. package/dist/core/orchestration/delegation-ledger.d.ts.map +1 -1
  110. package/dist/core/orchestration/delegation-ledger.js +3 -0
  111. package/dist/core/orchestration/delegation-ledger.js.map +1 -1
  112. package/dist/core/orchestration/event-store.d.ts +12 -10
  113. package/dist/core/orchestration/event-store.d.ts.map +1 -1
  114. package/dist/core/orchestration/event-store.js +94 -104
  115. package/dist/core/orchestration/event-store.js.map +1 -1
  116. package/dist/core/orchestration/lane-tool-manifests.d.ts +2 -2
  117. package/dist/core/orchestration/lane-tool-manifests.d.ts.map +1 -1
  118. package/dist/core/orchestration/lane-tool-manifests.js +4 -1
  119. package/dist/core/orchestration/lane-tool-manifests.js.map +1 -1
  120. package/dist/core/orchestration/policy-compiler.d.ts.map +1 -1
  121. package/dist/core/orchestration/policy-compiler.js +8 -9
  122. package/dist/core/orchestration/policy-compiler.js.map +1 -1
  123. package/dist/core/orchestration/profile-registry.d.ts.map +1 -1
  124. package/dist/core/orchestration/profile-registry.js +4 -10
  125. package/dist/core/orchestration/profile-registry.js.map +1 -1
  126. package/dist/core/orchestration/task-runtime.d.ts +1 -0
  127. package/dist/core/orchestration/task-runtime.d.ts.map +1 -1
  128. package/dist/core/orchestration/task-runtime.js +58 -3
  129. package/dist/core/orchestration/task-runtime.js.map +1 -1
  130. package/dist/core/orchestration/worker-execution-contract.d.ts.map +1 -1
  131. package/dist/core/orchestration/worker-execution-contract.js +10 -17
  132. package/dist/core/orchestration/worker-execution-contract.js.map +1 -1
  133. package/dist/core/risk-classifier.js +3 -3
  134. package/dist/core/risk-classifier.js.map +1 -1
  135. package/dist/core/runtime-builder.d.ts +4 -1
  136. package/dist/core/runtime-builder.d.ts.map +1 -1
  137. package/dist/core/runtime-builder.js +34 -15
  138. package/dist/core/runtime-builder.js.map +1 -1
  139. package/dist/core/secrets/bitwarden-credential-storage.d.ts +51 -0
  140. package/dist/core/secrets/bitwarden-credential-storage.d.ts.map +1 -0
  141. package/dist/core/secrets/bitwarden-credential-storage.js +345 -0
  142. package/dist/core/secrets/bitwarden-credential-storage.js.map +1 -0
  143. package/dist/core/secrets/credential-exposure-guard.d.ts +8 -4
  144. package/dist/core/secrets/credential-exposure-guard.d.ts.map +1 -1
  145. package/dist/core/secrets/credential-exposure-guard.js +30 -31
  146. package/dist/core/secrets/credential-exposure-guard.js.map +1 -1
  147. package/dist/core/secrets/credential-manager.d.ts +109 -0
  148. package/dist/core/secrets/credential-manager.d.ts.map +1 -0
  149. package/dist/core/secrets/credential-manager.js +384 -0
  150. package/dist/core/secrets/credential-manager.js.map +1 -0
  151. package/dist/core/secrets/credential-project.d.ts +4 -0
  152. package/dist/core/secrets/credential-project.d.ts.map +1 -0
  153. package/dist/core/secrets/credential-project.js +145 -0
  154. package/dist/core/secrets/credential-project.js.map +1 -0
  155. package/dist/core/secrets/secret-dotenv.d.ts +1 -1
  156. package/dist/core/secrets/secret-dotenv.js +1 -1
  157. package/dist/core/secrets/secret-dotenv.js.map +1 -1
  158. package/dist/core/settings-manager.d.ts +4 -2
  159. package/dist/core/settings-manager.d.ts.map +1 -1
  160. package/dist/core/settings-manager.js +9 -5
  161. package/dist/core/settings-manager.js.map +1 -1
  162. package/dist/core/slash-commands.d.ts.map +1 -1
  163. package/dist/core/slash-commands.js +1 -0
  164. package/dist/core/slash-commands.js.map +1 -1
  165. package/dist/core/system-prompt.js +1 -1
  166. package/dist/core/system-prompt.js.map +1 -1
  167. package/dist/core/tool-capability-policy.js +2 -2
  168. package/dist/core/tool-capability-policy.js.map +1 -1
  169. package/dist/core/tools/delegate.d.ts +29 -1
  170. package/dist/core/tools/delegate.d.ts.map +1 -1
  171. package/dist/core/tools/delegate.js +179 -28
  172. package/dist/core/tools/delegate.js.map +1 -1
  173. package/dist/core/tools/git-filter.d.ts +1 -0
  174. package/dist/core/tools/git-filter.d.ts.map +1 -1
  175. package/dist/core/tools/git-filter.js +1 -1
  176. package/dist/core/tools/git-filter.js.map +1 -1
  177. package/dist/core/tools/profile-writer.js +3 -3
  178. package/dist/core/tools/profile-writer.js.map +1 -1
  179. package/dist/core/tools/python.d.ts +2 -0
  180. package/dist/core/tools/python.d.ts.map +1 -1
  181. package/dist/core/tools/python.js +17 -12
  182. package/dist/core/tools/python.js.map +1 -1
  183. package/dist/core/tools/run-process.d.ts +2 -0
  184. package/dist/core/tools/run-process.d.ts.map +1 -1
  185. package/dist/core/tools/run-process.js +7 -3
  186. package/dist/core/tools/run-process.js.map +1 -1
  187. package/dist/core/tools/secret-store.d.ts +8 -18
  188. package/dist/core/tools/secret-store.d.ts.map +1 -1
  189. package/dist/core/tools/secret-store.js +83 -348
  190. package/dist/core/tools/secret-store.js.map +1 -1
  191. package/dist/index.d.ts +3 -1
  192. package/dist/index.d.ts.map +1 -1
  193. package/dist/index.js +3 -1
  194. package/dist/index.js.map +1 -1
  195. package/dist/migrations.d.ts.map +1 -1
  196. package/dist/migrations.js +38 -1
  197. package/dist/migrations.js.map +1 -1
  198. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  199. package/dist/modes/interactive/components/settings-selector.js +35 -14
  200. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  201. package/dist/modes/interactive/interactive-mode.d.ts +1 -0
  202. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  203. package/dist/modes/interactive/interactive-mode.js +26 -3
  204. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  205. package/dist/modes/interactive/secret-menu-controller.d.ts +21 -0
  206. package/dist/modes/interactive/secret-menu-controller.d.ts.map +1 -0
  207. package/dist/modes/interactive/secret-menu-controller.js +276 -0
  208. package/dist/modes/interactive/secret-menu-controller.js.map +1 -0
  209. package/dist/utils/tools-manager.d.ts +2 -1
  210. package/dist/utils/tools-manager.d.ts.map +1 -1
  211. package/dist/utils/tools-manager.js +43 -48
  212. package/dist/utils/tools-manager.js.map +1 -1
  213. package/dist/utils/zip-extractor.d.ts +8 -0
  214. package/dist/utils/zip-extractor.d.ts.map +1 -0
  215. package/dist/utils/zip-extractor.js +275 -0
  216. package/dist/utils/zip-extractor.js.map +1 -0
  217. package/docs/harness-architecture.md +34 -22
  218. package/docs/index.md +1 -1
  219. package/docs/resources.md +1 -1
  220. package/docs/settings.md +39 -103
  221. package/docs/usage.md +7 -5
  222. package/docs/worker-profiles.md +9 -7
  223. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  224. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  225. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  226. package/examples/extensions/sandbox/package-lock.json +2 -2
  227. package/examples/extensions/sandbox/package.json +1 -1
  228. package/examples/extensions/with-deps/package-lock.json +2 -2
  229. package/examples/extensions/with-deps/package.json +1 -1
  230. package/npm-shrinkwrap.json +12 -12
  231. package/package.json +4 -4
  232. package/dist/core/secrets/secret-vault-format.d.ts +0 -112
  233. package/dist/core/secrets/secret-vault-format.d.ts.map +0 -1
  234. package/dist/core/secrets/secret-vault-format.js +0 -396
  235. package/dist/core/secrets/secret-vault-format.js.map +0 -1
  236. package/dist/core/secrets/secret-vault.d.ts +0 -72
  237. package/dist/core/secrets/secret-vault.d.ts.map +0 -1
  238. package/dist/core/secrets/secret-vault.js +0 -663
  239. package/dist/core/secrets/secret-vault.js.map +0 -1
@@ -0,0 +1,275 @@
1
+ import { lstat, mkdir, readFile, writeFile } from "node:fs/promises";
2
+ import { isAbsolute, join, relative, resolve, sep } from "node:path";
3
+ import { inflateRaw } from "node:zlib";
4
+ const LOCAL_FILE_SIGNATURE = 0x04034b50;
5
+ const CENTRAL_FILE_SIGNATURE = 0x02014b50;
6
+ const END_OF_CENTRAL_DIRECTORY_SIGNATURE = 0x06054b50;
7
+ const END_OF_CENTRAL_DIRECTORY_BYTES = 22;
8
+ const MAX_ZIP_COMMENT_BYTES = 65_535;
9
+ const MAX_ARCHIVE_BYTES = 512 * 1024 * 1024;
10
+ const MAX_ENTRY_BYTES = 512 * 1024 * 1024;
11
+ const MAX_TOTAL_BYTES = 1024 * 1024 * 1024;
12
+ const MAX_ENTRIES = 4_096;
13
+ const MAX_PATH_BYTES = 4_096;
14
+ const ZIP64_16_BIT_SENTINEL = 0xffff;
15
+ const ZIP64_32_BIT_SENTINEL = 0xffffffff;
16
+ export class ZipExtractionError extends Error {
17
+ constructor(code, message) {
18
+ super(message);
19
+ this.name = "ZipExtractionError";
20
+ this.code = code;
21
+ }
22
+ }
23
+ const CRC32_TABLE = new Uint32Array(256);
24
+ for (let index = 0; index < CRC32_TABLE.length; index++) {
25
+ let value = index;
26
+ for (let bit = 0; bit < 8; bit++)
27
+ value = (value >>> 1) ^ (value & 1 ? 0xedb88320 : 0);
28
+ CRC32_TABLE[index] = value >>> 0;
29
+ }
30
+ function crc32(value) {
31
+ let checksum = 0xffffffff;
32
+ for (const byte of value)
33
+ checksum = (checksum >>> 8) ^ (CRC32_TABLE[(checksum ^ byte) & 0xff] ?? 0);
34
+ return (checksum ^ 0xffffffff) >>> 0;
35
+ }
36
+ function invalidArchive(message) {
37
+ throw new ZipExtractionError("invalid_archive", message);
38
+ }
39
+ function assertBufferRange(buffer, offset, bytes) {
40
+ if (!Number.isSafeInteger(offset) ||
41
+ !Number.isSafeInteger(bytes) ||
42
+ offset < 0 ||
43
+ bytes < 0 ||
44
+ offset + bytes > buffer.length) {
45
+ invalidArchive("ZIP structure extends beyond the archive boundary.");
46
+ }
47
+ }
48
+ function findEndOfCentralDirectory(archive) {
49
+ const minimumOffset = Math.max(0, archive.length - END_OF_CENTRAL_DIRECTORY_BYTES - MAX_ZIP_COMMENT_BYTES);
50
+ for (let offset = archive.length - END_OF_CENTRAL_DIRECTORY_BYTES; offset >= minimumOffset; offset--) {
51
+ if (archive.readUInt32LE(offset) !== END_OF_CENTRAL_DIRECTORY_SIGNATURE)
52
+ continue;
53
+ const commentBytes = archive.readUInt16LE(offset + 20);
54
+ if (offset + END_OF_CENTRAL_DIRECTORY_BYTES + commentBytes === archive.length)
55
+ return offset;
56
+ }
57
+ return invalidArchive("ZIP end-of-central-directory record is missing.");
58
+ }
59
+ function normalizeEntryPath(rawName) {
60
+ if (!rawName || rawName.includes("\0") || rawName.includes("\ufffd")) {
61
+ throw new ZipExtractionError("unsafe_entry", "ZIP contains an invalid entry path.");
62
+ }
63
+ const portableName = rawName.replaceAll("\\", "/");
64
+ if (portableName.startsWith("/") || isAbsolute(portableName) || /^[A-Za-z]:/u.test(portableName)) {
65
+ throw new ZipExtractionError("unsafe_entry", "ZIP contains an absolute entry path.");
66
+ }
67
+ const directory = portableName.endsWith("/");
68
+ const segments = portableName.split("/");
69
+ if (directory)
70
+ segments.pop();
71
+ if (segments.length === 0 ||
72
+ segments.some((segment) => segment.length === 0 || segment === "." || segment === ".." || segment.includes(":"))) {
73
+ throw new ZipExtractionError("unsafe_entry", "ZIP contains a traversal or ambiguous entry path.");
74
+ }
75
+ return { segments, directory };
76
+ }
77
+ function parseEntries(archive) {
78
+ const endOffset = findEndOfCentralDirectory(archive);
79
+ const diskNumber = archive.readUInt16LE(endOffset + 4);
80
+ const centralDiskNumber = archive.readUInt16LE(endOffset + 6);
81
+ const entriesOnDisk = archive.readUInt16LE(endOffset + 8);
82
+ const entryCount = archive.readUInt16LE(endOffset + 10);
83
+ const centralBytes = archive.readUInt32LE(endOffset + 12);
84
+ const centralOffset = archive.readUInt32LE(endOffset + 16);
85
+ if (diskNumber !== 0 ||
86
+ centralDiskNumber !== 0 ||
87
+ entriesOnDisk !== entryCount ||
88
+ entryCount === ZIP64_16_BIT_SENTINEL ||
89
+ centralBytes === ZIP64_32_BIT_SENTINEL ||
90
+ centralOffset === ZIP64_32_BIT_SENTINEL) {
91
+ throw new ZipExtractionError("unsupported_archive", "Multi-disk and ZIP64 archives are unsupported.");
92
+ }
93
+ if (entryCount > MAX_ENTRIES) {
94
+ throw new ZipExtractionError("limit_exceeded", `ZIP contains more than ${MAX_ENTRIES} entries.`);
95
+ }
96
+ assertBufferRange(archive, centralOffset, centralBytes);
97
+ if (centralOffset + centralBytes > endOffset)
98
+ invalidArchive("ZIP central directory overlaps its footer.");
99
+ const entries = [];
100
+ const seenPaths = new Set();
101
+ let totalBytes = 0;
102
+ let cursor = centralOffset;
103
+ for (let index = 0; index < entryCount; index++) {
104
+ assertBufferRange(archive, cursor, 46);
105
+ if (archive.readUInt32LE(cursor) !== CENTRAL_FILE_SIGNATURE) {
106
+ invalidArchive("ZIP central directory entry is malformed.");
107
+ }
108
+ const versionMadeBy = archive.readUInt16LE(cursor + 4);
109
+ const flags = archive.readUInt16LE(cursor + 8);
110
+ const method = archive.readUInt16LE(cursor + 10);
111
+ const checksum = archive.readUInt32LE(cursor + 16);
112
+ const compressedBytes = archive.readUInt32LE(cursor + 20);
113
+ const uncompressedBytes = archive.readUInt32LE(cursor + 24);
114
+ const nameBytes = archive.readUInt16LE(cursor + 28);
115
+ const extraBytes = archive.readUInt16LE(cursor + 30);
116
+ const commentBytes = archive.readUInt16LE(cursor + 32);
117
+ const startingDisk = archive.readUInt16LE(cursor + 34);
118
+ const externalAttributes = archive.readUInt32LE(cursor + 38);
119
+ const localHeaderOffset = archive.readUInt32LE(cursor + 42);
120
+ const variableBytes = nameBytes + extraBytes + commentBytes;
121
+ assertBufferRange(archive, cursor + 46, variableBytes);
122
+ if (startingDisk !== 0 ||
123
+ compressedBytes === ZIP64_32_BIT_SENTINEL ||
124
+ uncompressedBytes === ZIP64_32_BIT_SENTINEL ||
125
+ localHeaderOffset === ZIP64_32_BIT_SENTINEL) {
126
+ throw new ZipExtractionError("unsupported_archive", "ZIP64 entries are unsupported.");
127
+ }
128
+ if ((flags & 0x0001) !== 0) {
129
+ throw new ZipExtractionError("unsupported_archive", "Encrypted ZIP entries are unsupported.");
130
+ }
131
+ if (method !== 0 && method !== 8) {
132
+ throw new ZipExtractionError("unsupported_archive", "ZIP compression method is unsupported.");
133
+ }
134
+ if (nameBytes === 0 || nameBytes > MAX_PATH_BYTES) {
135
+ throw new ZipExtractionError("limit_exceeded", "ZIP entry path exceeds its bound.");
136
+ }
137
+ const rawName = archive.subarray(cursor + 46, cursor + 46 + nameBytes).toString("utf8");
138
+ const normalized = normalizeEntryPath(rawName);
139
+ const hostSystem = versionMadeBy >>> 8;
140
+ const unixMode = hostSystem === 3 ? (externalAttributes >>> 16) & 0xffff : 0;
141
+ const unixType = unixMode & 0xf000;
142
+ const directory = normalized.directory || unixType === 0x4000 || (externalAttributes & 0x10) !== 0;
143
+ if (unixType !== 0 && unixType !== 0x4000 && unixType !== 0x8000) {
144
+ throw new ZipExtractionError("unsafe_entry", "ZIP contains a non-file entry.");
145
+ }
146
+ if (uncompressedBytes > MAX_ENTRY_BYTES) {
147
+ throw new ZipExtractionError("limit_exceeded", "ZIP entry exceeds its uncompressed-size bound.");
148
+ }
149
+ totalBytes += uncompressedBytes;
150
+ if (!Number.isSafeInteger(totalBytes) || totalBytes > MAX_TOTAL_BYTES) {
151
+ throw new ZipExtractionError("limit_exceeded", "ZIP exceeds its total uncompressed-size bound.");
152
+ }
153
+ const identity = normalized.segments.join("/");
154
+ const collisionKey = process.platform === "win32" ? identity.toLowerCase() : identity;
155
+ if (seenPaths.has(collisionKey)) {
156
+ throw new ZipExtractionError("unsafe_entry", "ZIP contains duplicate entry paths.");
157
+ }
158
+ seenPaths.add(collisionKey);
159
+ entries.push({
160
+ segments: normalized.segments,
161
+ directory,
162
+ flags,
163
+ method,
164
+ checksum,
165
+ compressedBytes,
166
+ uncompressedBytes,
167
+ localHeaderOffset,
168
+ });
169
+ cursor += 46 + variableBytes;
170
+ }
171
+ if (cursor > centralOffset + centralBytes)
172
+ invalidArchive("ZIP central directory exceeds its declared size.");
173
+ return entries;
174
+ }
175
+ async function inflateEntry(compressed, expectedBytes) {
176
+ return new Promise((resolvePromise, reject) => {
177
+ inflateRaw(compressed, { maxOutputLength: Math.max(1, expectedBytes) }, (error, output) => {
178
+ if (error) {
179
+ reject(new ZipExtractionError("invalid_archive", "ZIP deflate stream is invalid."));
180
+ return;
181
+ }
182
+ resolvePromise(output);
183
+ });
184
+ });
185
+ }
186
+ async function ensureDirectory(root, segments) {
187
+ let current = root;
188
+ for (const segment of segments) {
189
+ current = join(current, segment);
190
+ try {
191
+ const metadata = await lstat(current);
192
+ if (metadata.isSymbolicLink() || !metadata.isDirectory()) {
193
+ throw new ZipExtractionError("unsafe_entry", "ZIP extraction path crosses a non-directory.");
194
+ }
195
+ }
196
+ catch (error) {
197
+ if (error instanceof ZipExtractionError)
198
+ throw error;
199
+ if (error.code !== "ENOENT")
200
+ throw error;
201
+ await mkdir(current, { mode: 0o700 });
202
+ }
203
+ }
204
+ }
205
+ function resolveEntryTarget(destination, segments) {
206
+ const target = resolve(destination, ...segments);
207
+ const fromDestination = relative(destination, target);
208
+ if (fromDestination === ".." || fromDestination.startsWith(`..${sep}`) || isAbsolute(fromDestination)) {
209
+ throw new ZipExtractionError("unsafe_entry", "ZIP entry escapes the extraction destination.");
210
+ }
211
+ return target;
212
+ }
213
+ async function extractEntry(archive, destination, entry) {
214
+ if (entry.directory) {
215
+ await ensureDirectory(destination, entry.segments);
216
+ return;
217
+ }
218
+ await ensureDirectory(destination, entry.segments.slice(0, -1));
219
+ const target = resolveEntryTarget(destination, entry.segments);
220
+ try {
221
+ await lstat(target);
222
+ throw new ZipExtractionError("unsafe_entry", "ZIP extraction refuses to overwrite an existing path.");
223
+ }
224
+ catch (error) {
225
+ if (error instanceof ZipExtractionError)
226
+ throw error;
227
+ if (error.code !== "ENOENT")
228
+ throw error;
229
+ }
230
+ assertBufferRange(archive, entry.localHeaderOffset, 30);
231
+ if (archive.readUInt32LE(entry.localHeaderOffset) !== LOCAL_FILE_SIGNATURE) {
232
+ invalidArchive("ZIP local file header is malformed.");
233
+ }
234
+ const localFlags = archive.readUInt16LE(entry.localHeaderOffset + 6);
235
+ const localMethod = archive.readUInt16LE(entry.localHeaderOffset + 8);
236
+ const localNameBytes = archive.readUInt16LE(entry.localHeaderOffset + 26);
237
+ const localExtraBytes = archive.readUInt16LE(entry.localHeaderOffset + 28);
238
+ if (localFlags !== entry.flags || localMethod !== entry.method) {
239
+ invalidArchive("ZIP local and central headers disagree.");
240
+ }
241
+ const dataOffset = entry.localHeaderOffset + 30 + localNameBytes + localExtraBytes;
242
+ assertBufferRange(archive, dataOffset, entry.compressedBytes);
243
+ const compressed = archive.subarray(dataOffset, dataOffset + entry.compressedBytes);
244
+ const output = entry.method === 0 ? Buffer.from(compressed) : await inflateEntry(compressed, entry.uncompressedBytes);
245
+ if (output.length !== entry.uncompressedBytes || crc32(output) !== entry.checksum) {
246
+ invalidArchive("ZIP entry size or checksum is invalid.");
247
+ }
248
+ await writeFile(target, output, { flag: "wx", mode: 0o600 });
249
+ }
250
+ /** Extract a bounded ordinary ZIP with no dependency on platform archive utilities. */
251
+ export async function extractZipFile(archivePath, destination) {
252
+ try {
253
+ const metadata = await lstat(archivePath);
254
+ if (metadata.isSymbolicLink() || !metadata.isFile())
255
+ invalidArchive("ZIP source is not a regular file.");
256
+ if (metadata.size > MAX_ARCHIVE_BYTES) {
257
+ throw new ZipExtractionError("limit_exceeded", "ZIP archive exceeds its 512 MiB input bound.");
258
+ }
259
+ const archive = await readFile(archivePath);
260
+ const entries = parseEntries(archive);
261
+ await mkdir(destination, { recursive: true, mode: 0o700 });
262
+ const destinationMetadata = await lstat(destination);
263
+ if (destinationMetadata.isSymbolicLink() || !destinationMetadata.isDirectory()) {
264
+ throw new ZipExtractionError("unsafe_entry", "ZIP destination is not a regular directory.");
265
+ }
266
+ for (const entry of entries)
267
+ await extractEntry(archive, destination, entry);
268
+ }
269
+ catch (error) {
270
+ if (error instanceof ZipExtractionError)
271
+ throw error;
272
+ throw new ZipExtractionError("write_failed", `ZIP extraction failed: ${error instanceof Error ? error.message : String(error)}`);
273
+ }
274
+ }
275
+ //# sourceMappingURL=zip-extractor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zip-extractor.js","sourceRoot":"","sources":["../../src/utils/zip-extractor.ts"],"names":[],"mappings":"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","sourcesContent":["import { lstat, mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { isAbsolute, join, relative, resolve, sep } from \"node:path\";\nimport { inflateRaw } from \"node:zlib\";\n\nconst LOCAL_FILE_SIGNATURE = 0x04034b50;\nconst CENTRAL_FILE_SIGNATURE = 0x02014b50;\nconst END_OF_CENTRAL_DIRECTORY_SIGNATURE = 0x06054b50;\nconst END_OF_CENTRAL_DIRECTORY_BYTES = 22;\nconst MAX_ZIP_COMMENT_BYTES = 65_535;\nconst MAX_ARCHIVE_BYTES = 512 * 1024 * 1024;\nconst MAX_ENTRY_BYTES = 512 * 1024 * 1024;\nconst MAX_TOTAL_BYTES = 1024 * 1024 * 1024;\nconst MAX_ENTRIES = 4_096;\nconst MAX_PATH_BYTES = 4_096;\nconst ZIP64_16_BIT_SENTINEL = 0xffff;\nconst ZIP64_32_BIT_SENTINEL = 0xffffffff;\n\nexport type ZipExtractionErrorCode =\n\t| \"invalid_archive\"\n\t| \"limit_exceeded\"\n\t| \"unsafe_entry\"\n\t| \"unsupported_archive\"\n\t| \"write_failed\";\n\nexport class ZipExtractionError extends Error {\n\treadonly code: ZipExtractionErrorCode;\n\n\tconstructor(code: ZipExtractionErrorCode, message: string) {\n\t\tsuper(message);\n\t\tthis.name = \"ZipExtractionError\";\n\t\tthis.code = code;\n\t}\n}\n\ninterface ZipEntry {\n\tsegments: string[];\n\tdirectory: boolean;\n\tflags: number;\n\tmethod: number;\n\tchecksum: number;\n\tcompressedBytes: number;\n\tuncompressedBytes: number;\n\tlocalHeaderOffset: number;\n}\n\nconst CRC32_TABLE = new Uint32Array(256);\nfor (let index = 0; index < CRC32_TABLE.length; index++) {\n\tlet value = index;\n\tfor (let bit = 0; bit < 8; bit++) value = (value >>> 1) ^ (value & 1 ? 0xedb88320 : 0);\n\tCRC32_TABLE[index] = value >>> 0;\n}\n\nfunction crc32(value: Buffer): number {\n\tlet checksum = 0xffffffff;\n\tfor (const byte of value) checksum = (checksum >>> 8) ^ (CRC32_TABLE[(checksum ^ byte) & 0xff] ?? 0);\n\treturn (checksum ^ 0xffffffff) >>> 0;\n}\n\nfunction invalidArchive(message: string): never {\n\tthrow new ZipExtractionError(\"invalid_archive\", message);\n}\n\nfunction assertBufferRange(buffer: Buffer, offset: number, bytes: number): void {\n\tif (\n\t\t!Number.isSafeInteger(offset) ||\n\t\t!Number.isSafeInteger(bytes) ||\n\t\toffset < 0 ||\n\t\tbytes < 0 ||\n\t\toffset + bytes > buffer.length\n\t) {\n\t\tinvalidArchive(\"ZIP structure extends beyond the archive boundary.\");\n\t}\n}\n\nfunction findEndOfCentralDirectory(archive: Buffer): number {\n\tconst minimumOffset = Math.max(0, archive.length - END_OF_CENTRAL_DIRECTORY_BYTES - MAX_ZIP_COMMENT_BYTES);\n\tfor (let offset = archive.length - END_OF_CENTRAL_DIRECTORY_BYTES; offset >= minimumOffset; offset--) {\n\t\tif (archive.readUInt32LE(offset) !== END_OF_CENTRAL_DIRECTORY_SIGNATURE) continue;\n\t\tconst commentBytes = archive.readUInt16LE(offset + 20);\n\t\tif (offset + END_OF_CENTRAL_DIRECTORY_BYTES + commentBytes === archive.length) return offset;\n\t}\n\treturn invalidArchive(\"ZIP end-of-central-directory record is missing.\");\n}\n\nfunction normalizeEntryPath(rawName: string): { segments: string[]; directory: boolean } {\n\tif (!rawName || rawName.includes(\"\\0\") || rawName.includes(\"\\ufffd\")) {\n\t\tthrow new ZipExtractionError(\"unsafe_entry\", \"ZIP contains an invalid entry path.\");\n\t}\n\tconst portableName = rawName.replaceAll(\"\\\\\", \"/\");\n\tif (portableName.startsWith(\"/\") || isAbsolute(portableName) || /^[A-Za-z]:/u.test(portableName)) {\n\t\tthrow new ZipExtractionError(\"unsafe_entry\", \"ZIP contains an absolute entry path.\");\n\t}\n\tconst directory = portableName.endsWith(\"/\");\n\tconst segments = portableName.split(\"/\");\n\tif (directory) segments.pop();\n\tif (\n\t\tsegments.length === 0 ||\n\t\tsegments.some((segment) => segment.length === 0 || segment === \".\" || segment === \"..\" || segment.includes(\":\"))\n\t) {\n\t\tthrow new ZipExtractionError(\"unsafe_entry\", \"ZIP contains a traversal or ambiguous entry path.\");\n\t}\n\treturn { segments, directory };\n}\n\nfunction parseEntries(archive: Buffer): ZipEntry[] {\n\tconst endOffset = findEndOfCentralDirectory(archive);\n\tconst diskNumber = archive.readUInt16LE(endOffset + 4);\n\tconst centralDiskNumber = archive.readUInt16LE(endOffset + 6);\n\tconst entriesOnDisk = archive.readUInt16LE(endOffset + 8);\n\tconst entryCount = archive.readUInt16LE(endOffset + 10);\n\tconst centralBytes = archive.readUInt32LE(endOffset + 12);\n\tconst centralOffset = archive.readUInt32LE(endOffset + 16);\n\tif (\n\t\tdiskNumber !== 0 ||\n\t\tcentralDiskNumber !== 0 ||\n\t\tentriesOnDisk !== entryCount ||\n\t\tentryCount === ZIP64_16_BIT_SENTINEL ||\n\t\tcentralBytes === ZIP64_32_BIT_SENTINEL ||\n\t\tcentralOffset === ZIP64_32_BIT_SENTINEL\n\t) {\n\t\tthrow new ZipExtractionError(\"unsupported_archive\", \"Multi-disk and ZIP64 archives are unsupported.\");\n\t}\n\tif (entryCount > MAX_ENTRIES) {\n\t\tthrow new ZipExtractionError(\"limit_exceeded\", `ZIP contains more than ${MAX_ENTRIES} entries.`);\n\t}\n\tassertBufferRange(archive, centralOffset, centralBytes);\n\tif (centralOffset + centralBytes > endOffset) invalidArchive(\"ZIP central directory overlaps its footer.\");\n\n\tconst entries: ZipEntry[] = [];\n\tconst seenPaths = new Set<string>();\n\tlet totalBytes = 0;\n\tlet cursor = centralOffset;\n\tfor (let index = 0; index < entryCount; index++) {\n\t\tassertBufferRange(archive, cursor, 46);\n\t\tif (archive.readUInt32LE(cursor) !== CENTRAL_FILE_SIGNATURE) {\n\t\t\tinvalidArchive(\"ZIP central directory entry is malformed.\");\n\t\t}\n\t\tconst versionMadeBy = archive.readUInt16LE(cursor + 4);\n\t\tconst flags = archive.readUInt16LE(cursor + 8);\n\t\tconst method = archive.readUInt16LE(cursor + 10);\n\t\tconst checksum = archive.readUInt32LE(cursor + 16);\n\t\tconst compressedBytes = archive.readUInt32LE(cursor + 20);\n\t\tconst uncompressedBytes = archive.readUInt32LE(cursor + 24);\n\t\tconst nameBytes = archive.readUInt16LE(cursor + 28);\n\t\tconst extraBytes = archive.readUInt16LE(cursor + 30);\n\t\tconst commentBytes = archive.readUInt16LE(cursor + 32);\n\t\tconst startingDisk = archive.readUInt16LE(cursor + 34);\n\t\tconst externalAttributes = archive.readUInt32LE(cursor + 38);\n\t\tconst localHeaderOffset = archive.readUInt32LE(cursor + 42);\n\t\tconst variableBytes = nameBytes + extraBytes + commentBytes;\n\t\tassertBufferRange(archive, cursor + 46, variableBytes);\n\t\tif (\n\t\t\tstartingDisk !== 0 ||\n\t\t\tcompressedBytes === ZIP64_32_BIT_SENTINEL ||\n\t\t\tuncompressedBytes === ZIP64_32_BIT_SENTINEL ||\n\t\t\tlocalHeaderOffset === ZIP64_32_BIT_SENTINEL\n\t\t) {\n\t\t\tthrow new ZipExtractionError(\"unsupported_archive\", \"ZIP64 entries are unsupported.\");\n\t\t}\n\t\tif ((flags & 0x0001) !== 0) {\n\t\t\tthrow new ZipExtractionError(\"unsupported_archive\", \"Encrypted ZIP entries are unsupported.\");\n\t\t}\n\t\tif (method !== 0 && method !== 8) {\n\t\t\tthrow new ZipExtractionError(\"unsupported_archive\", \"ZIP compression method is unsupported.\");\n\t\t}\n\t\tif (nameBytes === 0 || nameBytes > MAX_PATH_BYTES) {\n\t\t\tthrow new ZipExtractionError(\"limit_exceeded\", \"ZIP entry path exceeds its bound.\");\n\t\t}\n\t\tconst rawName = archive.subarray(cursor + 46, cursor + 46 + nameBytes).toString(\"utf8\");\n\t\tconst normalized = normalizeEntryPath(rawName);\n\t\tconst hostSystem = versionMadeBy >>> 8;\n\t\tconst unixMode = hostSystem === 3 ? (externalAttributes >>> 16) & 0xffff : 0;\n\t\tconst unixType = unixMode & 0xf000;\n\t\tconst directory = normalized.directory || unixType === 0x4000 || (externalAttributes & 0x10) !== 0;\n\t\tif (unixType !== 0 && unixType !== 0x4000 && unixType !== 0x8000) {\n\t\t\tthrow new ZipExtractionError(\"unsafe_entry\", \"ZIP contains a non-file entry.\");\n\t\t}\n\t\tif (uncompressedBytes > MAX_ENTRY_BYTES) {\n\t\t\tthrow new ZipExtractionError(\"limit_exceeded\", \"ZIP entry exceeds its uncompressed-size bound.\");\n\t\t}\n\t\ttotalBytes += uncompressedBytes;\n\t\tif (!Number.isSafeInteger(totalBytes) || totalBytes > MAX_TOTAL_BYTES) {\n\t\t\tthrow new ZipExtractionError(\"limit_exceeded\", \"ZIP exceeds its total uncompressed-size bound.\");\n\t\t}\n\t\tconst identity = normalized.segments.join(\"/\");\n\t\tconst collisionKey = process.platform === \"win32\" ? identity.toLowerCase() : identity;\n\t\tif (seenPaths.has(collisionKey)) {\n\t\t\tthrow new ZipExtractionError(\"unsafe_entry\", \"ZIP contains duplicate entry paths.\");\n\t\t}\n\t\tseenPaths.add(collisionKey);\n\t\tentries.push({\n\t\t\tsegments: normalized.segments,\n\t\t\tdirectory,\n\t\t\tflags,\n\t\t\tmethod,\n\t\t\tchecksum,\n\t\t\tcompressedBytes,\n\t\t\tuncompressedBytes,\n\t\t\tlocalHeaderOffset,\n\t\t});\n\t\tcursor += 46 + variableBytes;\n\t}\n\tif (cursor > centralOffset + centralBytes) invalidArchive(\"ZIP central directory exceeds its declared size.\");\n\treturn entries;\n}\n\nasync function inflateEntry(compressed: Buffer, expectedBytes: number): Promise<Buffer> {\n\treturn new Promise((resolvePromise, reject) => {\n\t\tinflateRaw(compressed, { maxOutputLength: Math.max(1, expectedBytes) }, (error, output) => {\n\t\t\tif (error) {\n\t\t\t\treject(new ZipExtractionError(\"invalid_archive\", \"ZIP deflate stream is invalid.\"));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tresolvePromise(output);\n\t\t});\n\t});\n}\n\nasync function ensureDirectory(root: string, segments: readonly string[]): Promise<void> {\n\tlet current = root;\n\tfor (const segment of segments) {\n\t\tcurrent = join(current, segment);\n\t\ttry {\n\t\t\tconst metadata = await lstat(current);\n\t\t\tif (metadata.isSymbolicLink() || !metadata.isDirectory()) {\n\t\t\t\tthrow new ZipExtractionError(\"unsafe_entry\", \"ZIP extraction path crosses a non-directory.\");\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tif (error instanceof ZipExtractionError) throw error;\n\t\t\tif ((error as NodeJS.ErrnoException).code !== \"ENOENT\") throw error;\n\t\t\tawait mkdir(current, { mode: 0o700 });\n\t\t}\n\t}\n}\n\nfunction resolveEntryTarget(destination: string, segments: readonly string[]): string {\n\tconst target = resolve(destination, ...segments);\n\tconst fromDestination = relative(destination, target);\n\tif (fromDestination === \"..\" || fromDestination.startsWith(`..${sep}`) || isAbsolute(fromDestination)) {\n\t\tthrow new ZipExtractionError(\"unsafe_entry\", \"ZIP entry escapes the extraction destination.\");\n\t}\n\treturn target;\n}\n\nasync function extractEntry(archive: Buffer, destination: string, entry: ZipEntry): Promise<void> {\n\tif (entry.directory) {\n\t\tawait ensureDirectory(destination, entry.segments);\n\t\treturn;\n\t}\n\tawait ensureDirectory(destination, entry.segments.slice(0, -1));\n\tconst target = resolveEntryTarget(destination, entry.segments);\n\ttry {\n\t\tawait lstat(target);\n\t\tthrow new ZipExtractionError(\"unsafe_entry\", \"ZIP extraction refuses to overwrite an existing path.\");\n\t} catch (error) {\n\t\tif (error instanceof ZipExtractionError) throw error;\n\t\tif ((error as NodeJS.ErrnoException).code !== \"ENOENT\") throw error;\n\t}\n\n\tassertBufferRange(archive, entry.localHeaderOffset, 30);\n\tif (archive.readUInt32LE(entry.localHeaderOffset) !== LOCAL_FILE_SIGNATURE) {\n\t\tinvalidArchive(\"ZIP local file header is malformed.\");\n\t}\n\tconst localFlags = archive.readUInt16LE(entry.localHeaderOffset + 6);\n\tconst localMethod = archive.readUInt16LE(entry.localHeaderOffset + 8);\n\tconst localNameBytes = archive.readUInt16LE(entry.localHeaderOffset + 26);\n\tconst localExtraBytes = archive.readUInt16LE(entry.localHeaderOffset + 28);\n\tif (localFlags !== entry.flags || localMethod !== entry.method) {\n\t\tinvalidArchive(\"ZIP local and central headers disagree.\");\n\t}\n\tconst dataOffset = entry.localHeaderOffset + 30 + localNameBytes + localExtraBytes;\n\tassertBufferRange(archive, dataOffset, entry.compressedBytes);\n\tconst compressed = archive.subarray(dataOffset, dataOffset + entry.compressedBytes);\n\tconst output =\n\t\tentry.method === 0 ? Buffer.from(compressed) : await inflateEntry(compressed, entry.uncompressedBytes);\n\tif (output.length !== entry.uncompressedBytes || crc32(output) !== entry.checksum) {\n\t\tinvalidArchive(\"ZIP entry size or checksum is invalid.\");\n\t}\n\tawait writeFile(target, output, { flag: \"wx\", mode: 0o600 });\n}\n\n/** Extract a bounded ordinary ZIP with no dependency on platform archive utilities. */\nexport async function extractZipFile(archivePath: string, destination: string): Promise<void> {\n\ttry {\n\t\tconst metadata = await lstat(archivePath);\n\t\tif (metadata.isSymbolicLink() || !metadata.isFile()) invalidArchive(\"ZIP source is not a regular file.\");\n\t\tif (metadata.size > MAX_ARCHIVE_BYTES) {\n\t\t\tthrow new ZipExtractionError(\"limit_exceeded\", \"ZIP archive exceeds its 512 MiB input bound.\");\n\t\t}\n\t\tconst archive = await readFile(archivePath);\n\t\tconst entries = parseEntries(archive);\n\t\tawait mkdir(destination, { recursive: true, mode: 0o700 });\n\t\tconst destinationMetadata = await lstat(destination);\n\t\tif (destinationMetadata.isSymbolicLink() || !destinationMetadata.isDirectory()) {\n\t\t\tthrow new ZipExtractionError(\"unsafe_entry\", \"ZIP destination is not a regular directory.\");\n\t\t}\n\t\tfor (const entry of entries) await extractEntry(archive, destination, entry);\n\t} catch (error) {\n\t\tif (error instanceof ZipExtractionError) throw error;\n\t\tthrow new ZipExtractionError(\n\t\t\t\"write_failed\",\n\t\t\t`ZIP extraction failed: ${error instanceof Error ? error.message : String(error)}`,\n\t\t);\n\t}\n}\n"]}
@@ -49,7 +49,9 @@ second line of defense, not the main implementation.
49
49
  context paths before content loading. Live extension loading repeats the profile check before
50
50
  import.
51
51
  - Isolated research and worker lanes create fresh tools only for their expanded lane grant. They do
52
- not copy the foreground registry, extensions, skills, shell, or mutable memory surface.
52
+ not copy the foreground registry, extensions, or skills. Native agents may materialize fresh
53
+ memory, write/edit, platform-shell, and recursive-delegation adapters when their immutable grant
54
+ and live service switches admit them.
53
55
  - Worker orchestration state is lazily materialized only when the active surface grants `delegate`
54
56
  (or a managed-lane terminal report requires the shared notifier); worker-role sessions allocate no
55
57
  worker scheduler, lifecycle store, or notification coordinator.
@@ -65,20 +67,23 @@ footprint must omit that extension at the resource layer, not merely block one o
65
67
 
66
68
  ## Child work contracts
67
69
 
68
- Owner-authored orchestration profiles are the routing authority. A profile fixes role, ordered model
69
- policy, exact thinking level, tool names, resource profiles, semantic capability ceiling, budgets,
70
- concurrency, lease duration, and verification policy. An architect profile additionally declares the
71
- only worker profile IDs it may dispatch. The model-facing dispatch request contains a task, profile
72
- ID, instructions, and resource pointers; it has no model, thinking, tool, budget, or concurrency
73
- override fields.
70
+ The model-facing `delegate` request can choose role label, authenticated model, exact supported
71
+ reasoning level, classified tools, semantic capabilities, read/write paths, and budget. An optional
72
+ owner-authored orchestration profile supplies reusable defaults; it is not a dispatch allowlist.
73
+ Profile-free roots start from the foreground model and maximum host-permitted classified core
74
+ surface. Descendants inherit immutable parent execution authority by default. The host validates and
75
+ materializes every choice, intersects it with parent authority and live global switches, and persists
76
+ the exact resulting contract before execution. Built-in roles are routing/audit labels unless an
77
+ embedding supplies an explicit role ceiling.
74
78
 
75
79
  The durable control plane is an append-only session event store projected into objectives, DAG
76
80
  tasks, attempts, leases, checkpoints, logical agent bindings, typed results, and a notification
77
81
  outbox. Every result carries its attempt lease and fencing token. A stale worker therefore cannot
78
- win a race after retry or resume. In-process isolated completions have no resumable transcript: an
79
- interrupted lease is fenced and its persisted dispatch is re-queued as a new attempt. A process-backed
80
- Pi worker instead retains its logical agent ID and resume context and is woken with `/resume` against
81
- the same session, worktree, orchestration profile, and checkpoint pointers when present.
82
+ win a race after retry or resume. In-process agents retain durable logical identity, lineage, exact
83
+ transcript, mailbox, immutable execution contract, and checkpoint usage across interruption and
84
+ resume. A process-backed Pi worker similarly retains its logical agent ID and resume context and is
85
+ woken with `/resume` against the same session, worktree, orchestration preset, and checkpoint
86
+ pointers when present.
82
87
 
83
88
  Policy is a mandatory pre-lease gate. An attempt cannot lease without a compiled execution grant.
84
89
  When compilation crosses owner authority or budget, the gate persists a typed approval request and
@@ -91,17 +96,24 @@ Acceptance is a runtime invariant, not a worker claim. Task criterion IDs must r
91
96
  objective, a completed criterion-bound result must carry trusted evidence for every linked criterion,
92
97
  and an objective cannot close until all required criteria have trusted evidence in its task results.
93
98
 
94
- In-process workers are bounded child loops with fresh context, a synthetic cache-affinity key, a
95
- profile-pinned model and exact reasoning level, a construction-filtered tool surface,
96
- turn/time/token/tool/cost bounds, and parent validation of the result. Process-capable profiles may
97
- expose `run_process`, which uses an exact executable allowlist, direct argv, a scoped environment,
98
- bounded output, and process-tree termination; unrestricted shell is not inherited. Worker output is
99
- untrusted; the parent retrieves it through `delegate_status` after a terminal lane event. A profile
100
- that requires independent verification names a separate owner-pinned verifier profile. The runtime
101
- automatically dispatches that verifier as a durable task, withholds the implementation's terminal
102
- handoff, and reconciles the typed verdict before the implementation can become accepted. Restart
103
- recovery closes both persistence gaps: an implementation awaiting a not-yet-created verifier is
104
- re-dispatched, while a persisted verifier result awaiting reconciliation is applied exactly once.
99
+ In-process agents have fresh context, a synthetic cache-affinity key, a selected model and exact
100
+ reasoning level, and a construction-filtered tool surface. They inherit `delegate`, may inspect exact
101
+ paginated transcripts for any logical peer in the session tree, exchange threaded messages, and may
102
+ materialize a persistent per-agent platform shell. There is no framework depth or fan-out cap; one
103
+ global scheduler owns concurrency and durable queuing, one root coordinator owns cumulative
104
+ token/cost/tool/active-time/attempt budgets, and exact ancestor task cycles are rejected. `run_process`
105
+ keeps its separate exact executable allowlist, direct argv, scoped environment, bounded output, and
106
+ process-tree termination contract. The platform shell is real host process authority, not container
107
+ isolation or a path-scoped substitute for `write`/`edit`.
108
+
109
+ Worker output remains untrusted evidence. The parent receives a bounded terminal event and retrieves
110
+ lane results explicitly; raw peer transcripts require an explicit `transcript` action and are paged
111
+ only for context size. A preset that requires independent verification names a separate owner-pinned
112
+ verifier profile. The runtime automatically dispatches that verifier as a durable task, withholds the
113
+ implementation's terminal handoff, and reconciles the typed verdict before the implementation can
114
+ become accepted. Restart recovery closes both persistence gaps: an implementation awaiting a
115
+ not-yet-created verifier is re-dispatched, while a persisted verifier result awaiting reconciliation
116
+ is applied exactly once.
105
117
 
106
118
  Out-of-process managed workers use the same lifecycle shape:
107
119
 
package/docs/index.md CHANGED
@@ -55,7 +55,7 @@ For the full first-run flow, see [Quickstart](quickstart.md).
55
55
 
56
56
  - [Extensions](extensions.md) - TypeScript modules for tools, commands, events, and custom UI.
57
57
  - [tmux agent manager](tmux-agent-manager.md) - bundled event-driven external-provider teams with parent wake handoffs.
58
- - [Task worker profiles](worker-profiles.md) - immutable session-scoped worker profiles with bounded model and tool authority.
58
+ - [Task worker presets](worker-profiles.md) - optional immutable session presets for recursive agents with model-authored authority.
59
59
  - [Skills](skills.md) - Agent Skills for reusable on-demand capabilities.
60
60
  - [Resource profiles & library](resources.md) - curate extensions, skills, and agents per project or situation; share a catalog; install and back up.
61
61
  - [Self-adaptation](self-adaptation.md) - draft live skills/extensions and run evidence-backed Pi harness improvement loops.
package/docs/resources.md CHANGED
@@ -49,7 +49,7 @@ A profile may also bind a **model**, **thinking level**, **soul**, and **modelRo
49
49
 
50
50
  Pi activates the complete situation as one runtime generation: model/router, thinking, soul, extensions, skills, prompts, agents, themes, and tools are reloaded together. A failed reload restores the prior valid generation instead of persisting a partial selection.
51
51
 
52
- Built-in research and worker lanes also ship a profile as one unit: its model, thinking, soul, and tool filter govern the isolated child loop. Lane tool globs expand to exact names over Pi's classified lane surface. Research can receive only `read`, `grep`, `find`, and `ls`; workers can additionally receive `write` and `edit` only with the explicit write setting and path roots. Recursive delegation, shell, memory/lifecycle tools, and extension tools without capability metadata stay fail-closed. A concrete profile grant that cannot bind to this classified surface produces a deduplicated warning instead of silently gaining authority.
52
+ Built-in research lanes still use a fixed read-only profile with `read`, `grep`, `find`, and `ls`. Native worker agents use a different contract: orchestration profiles are optional presets, a profile-free root receives the maximum host-permitted classified core surface, and descendants inherit immutable authority while retaining recursive `delegate`, peer-transcript, messaging, memory, write/edit, and platform-shell capabilities when their parent and live service switches permit them. Lane tool globs expand to exact classified names. Opaque extension tools without orchestration metadata are not guessed into a child surface, and an explicit tool request that cannot be materialized is rejected visibly.
53
53
 
54
54
  Profiles are stored in settings under `resourceProfiles`, and the active one(s) in `activeResourceProfile`:
55
55