@tetsuo-ai/agenc 0.16.0 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "manifestVersion": 2,
3
- "runtimeVersion": "0.16.0",
3
+ "runtimeVersion": "0.17.0",
4
4
  "releaseRepository": "tetsuo-ai/agenc-releases",
5
- "releaseTag": "agenc-v0.16.0",
5
+ "releaseTag": "agenc-v0.17.0",
6
6
  "build": {
7
- "sourceCommit": "f9a5af7577594b88cf288f22cda7d50418714755",
8
- "sourceRef": "refs/tags/agenc-v0.16.0",
9
- "sourceDateEpoch": 1786786626,
10
- "lockfileSha256": "ab06700767518bf3d834009204eec362463851f60b930192bbbd0d832027c90c",
7
+ "sourceCommit": "76811dd598b1d6ccbcd17ad9492eaa68d3f8db48",
8
+ "sourceRef": "refs/tags/agenc-v0.17.0",
9
+ "sourceDateEpoch": 1787189935,
10
+ "lockfileSha256": "bfa73122635a2b41c6087e8aa5fef75b3899bb65dfc78835d29498b081b3e091",
11
11
  "nodeVersion": "v26.5.0",
12
12
  "nodeMajor": 26,
13
13
  "nodeModuleAbi": "147",
@@ -16,32 +16,32 @@
16
16
  "artifactProfile": "release",
17
17
  "releaseCandidate": {
18
18
  "workflow": "release-runtime.yml",
19
- "runId": 31878285107,
19
+ "runId": 32322959733,
20
20
  "runAttempt": 1,
21
- "runUrl": "https://github.com/tetsuo-ai/agenc-core/actions/runs/31878285107",
21
+ "runUrl": "https://github.com/tetsuo-ai/agenc-core/actions/runs/32322959733",
22
22
  "phase": "candidate",
23
23
  "sourceRef": "refs/heads/main",
24
- "evidenceSha256": "1c4a43b5b75cce4e17f12cd47977fa30be82354a4ff3354dbc0ad02c322e0a7f"
24
+ "evidenceSha256": "4b812d7e855d991077c162f48a20a4e8363d697a92aed1a732302d725fd52084"
25
25
  }
26
26
  },
27
27
  "artifacts": [
28
28
  {
29
29
  "platform": "darwin",
30
30
  "arch": "arm64",
31
- "runtimeVersion": "0.16.0",
31
+ "runtimeVersion": "0.17.0",
32
32
  "nodeMajor": 26,
33
33
  "nodeModuleAbi": "147",
34
34
  "nodeApiVersion": "10",
35
- "url": "https://github.com/tetsuo-ai/agenc-releases/releases/download/agenc-v0.16.0/agenc-runtime-0.16.0-darwin-arm64-node26-abi147.tar.gz",
36
- "sha256": "b677c6df4918f57cc569846a5b9fc478b7af09cad875703bcca0d2b570c43c55",
37
- "bytes": 114848678,
38
- "attestationUrl": "https://github.com/tetsuo-ai/agenc-releases/releases/download/agenc-v0.16.0/agenc-runtime-0.16.0-darwin-arm64-node26-abi147.tar.gz.sigstore.json",
39
- "attestationSha256": "aa1ceb6cd3818c07845a00a1e48d468dfb483694e3501d90ec11e3d10fe79777",
40
- "attestationBytes": 11366,
41
- "buildProvenanceUrl": "https://github.com/tetsuo-ai/agenc-releases/releases/download/agenc-v0.16.0/agenc-runtime-0.16.0-darwin-arm64-node26-abi147.tar.gz.build.sigstore.json",
42
- "buildProvenanceSha256": "87a3a19a0076543d44fb3e29a7c6ac803d6f75e6e71717fd29aaa2b46b39f800",
43
- "buildProvenanceBytes": 10950,
44
- "metadataSha256": "8af7e0c3f22c6cb96e430ddff44533bbef9624c8c5e34f1867eaf83cfbb65350",
35
+ "url": "https://github.com/tetsuo-ai/agenc-releases/releases/download/agenc-v0.17.0/agenc-runtime-0.17.0-darwin-arm64-node26-abi147.tar.gz",
36
+ "sha256": "2dfa6d4e7b83e2cc617d81e8b6a41c5ff7cd725ef0eb7dd19fbc21707afb5a49",
37
+ "bytes": 115247777,
38
+ "attestationUrl": "https://github.com/tetsuo-ai/agenc-releases/releases/download/agenc-v0.17.0/agenc-runtime-0.17.0-darwin-arm64-node26-abi147.tar.gz.sigstore.json",
39
+ "attestationSha256": "fced669b312e8931da45787d660764e1e9a7514c601314cebb192b28baa3aebf",
40
+ "attestationBytes": 11456,
41
+ "buildProvenanceUrl": "https://github.com/tetsuo-ai/agenc-releases/releases/download/agenc-v0.17.0/agenc-runtime-0.17.0-darwin-arm64-node26-abi147.tar.gz.build.sigstore.json",
42
+ "buildProvenanceSha256": "703d5989c3588c79db86fc13687878eb733d920bb402971ac2e18c96bab24c79",
43
+ "buildProvenanceBytes": 11154,
44
+ "metadataSha256": "bab7b8dcf07e352cb41fd2a18a7ed7be25272cd17a5a79d5d62f39ae4b3d3b1e",
45
45
  "nativeToolchain": {
46
46
  "schemaVersion": 1,
47
47
  "builder": "github-hosted:macos-15:macos15:20260727.0256.1:ARM64",
@@ -71,13 +71,13 @@
71
71
  "xcode": "Xcode 16.4\nBuild version 16F6",
72
72
  "sdk": "15.5"
73
73
  },
74
- "dependencyTreeSha256": "c9a2b1d5e4ae7da97fbb6dab5f8e639a483f124c15e6e7fee7da6d99a30e1ba5",
74
+ "dependencyTreeSha256": "a7f679b076d3c5cd52992d969127502fcb5ea0252c93789e94f2968281224754",
75
75
  "dependencyPackages": 467,
76
76
  "archiveFormat": "tar+gzip; portable; utf8-byte-order; normalized-mtime",
77
77
  "archiveValidation": {
78
78
  "policy": "agenc-runtime-archive-v1",
79
- "entries": 29425,
80
- "uncompressedBytes": 468161536
79
+ "entries": 29427,
80
+ "uncompressedBytes": 470290944
81
81
  },
82
82
  "minimumMacosVersion": "13.5",
83
83
  "bins": {
@@ -88,20 +88,20 @@
88
88
  {
89
89
  "platform": "darwin",
90
90
  "arch": "x64",
91
- "runtimeVersion": "0.16.0",
91
+ "runtimeVersion": "0.17.0",
92
92
  "nodeMajor": 26,
93
93
  "nodeModuleAbi": "147",
94
94
  "nodeApiVersion": "10",
95
- "url": "https://github.com/tetsuo-ai/agenc-releases/releases/download/agenc-v0.16.0/agenc-runtime-0.16.0-darwin-x64-node26-abi147.tar.gz",
96
- "sha256": "27c5ea64a8378efff327df72ac8e07d325c05f5b85e8f69dd9748b9588698b5c",
97
- "bytes": 116575721,
98
- "attestationUrl": "https://github.com/tetsuo-ai/agenc-releases/releases/download/agenc-v0.16.0/agenc-runtime-0.16.0-darwin-x64-node26-abi147.tar.gz.sigstore.json",
99
- "attestationSha256": "16952c30fc8f69913e1b704b0ef40bbd657a0c0cbc014a7c4b3dc01a0bc73e37",
100
- "attestationBytes": 11268,
101
- "buildProvenanceUrl": "https://github.com/tetsuo-ai/agenc-releases/releases/download/agenc-v0.16.0/agenc-runtime-0.16.0-darwin-x64-node26-abi147.tar.gz.build.sigstore.json",
102
- "buildProvenanceSha256": "d8882830546aee28ea8e5fd76dd14a06f4bf66061a10f6d1fd4929b9b1abd0af",
103
- "buildProvenanceBytes": 10946,
104
- "metadataSha256": "d42ebcf37b112a04e70a615e915d31f9bbce6e76047a44a223fbabbf50e54f56",
95
+ "url": "https://github.com/tetsuo-ai/agenc-releases/releases/download/agenc-v0.17.0/agenc-runtime-0.17.0-darwin-x64-node26-abi147.tar.gz",
96
+ "sha256": "7d39aaa0c54782d45fe41a08ba6fe78f7f6616bb6489795db339ff831a7ed5f1",
97
+ "bytes": 116974284,
98
+ "attestationUrl": "https://github.com/tetsuo-ai/agenc-releases/releases/download/agenc-v0.17.0/agenc-runtime-0.17.0-darwin-x64-node26-abi147.tar.gz.sigstore.json",
99
+ "attestationSha256": "ff935d3aa02dbffcb9bda4618fc41ce8822c968b06abd3eb75c71ebdbeb7b98b",
100
+ "attestationBytes": 11499,
101
+ "buildProvenanceUrl": "https://github.com/tetsuo-ai/agenc-releases/releases/download/agenc-v0.17.0/agenc-runtime-0.17.0-darwin-x64-node26-abi147.tar.gz.build.sigstore.json",
102
+ "buildProvenanceSha256": "37aa04c34e10a43350d97db0913cf8239ff05610d2c84fa23c70e30bbc975a7d",
103
+ "buildProvenanceBytes": 11248,
104
+ "metadataSha256": "663c47381c12c967d19f5aa80a8deb758a431fa7a5ed4b281cc09496666826c8",
105
105
  "nativeToolchain": {
106
106
  "schemaVersion": 1,
107
107
  "builder": "github-hosted:macos-15-intel:macos15:20260727.0377.1:X64",
@@ -131,13 +131,13 @@
131
131
  "xcode": "Xcode 16.4\nBuild version 16F6",
132
132
  "sdk": "15.5"
133
133
  },
134
- "dependencyTreeSha256": "4e208e7e398a3ec62b21c84bae4d64a018df931ca6d069d3f5da1ee38a974ac7",
134
+ "dependencyTreeSha256": "b75c1de51c77679e4431cee663b3dbcf3140c8efc070f9a45129fac2ff64215e",
135
135
  "dependencyPackages": 467,
136
136
  "archiveFormat": "tar+gzip; portable; utf8-byte-order; normalized-mtime",
137
137
  "archiveValidation": {
138
138
  "policy": "agenc-runtime-archive-v1",
139
- "entries": 29425,
140
- "uncompressedBytes": 472156672
139
+ "entries": 29427,
140
+ "uncompressedBytes": 474286080
141
141
  },
142
142
  "minimumMacosVersion": "13.5",
143
143
  "bins": {
@@ -148,20 +148,20 @@
148
148
  {
149
149
  "platform": "linux",
150
150
  "arch": "arm64",
151
- "runtimeVersion": "0.16.0",
151
+ "runtimeVersion": "0.17.0",
152
152
  "nodeMajor": 26,
153
153
  "nodeModuleAbi": "147",
154
154
  "nodeApiVersion": "10",
155
- "url": "https://github.com/tetsuo-ai/agenc-releases/releases/download/agenc-v0.16.0/agenc-runtime-0.16.0-linux-arm64-node26-abi147.tar.gz",
156
- "sha256": "66d7a1c263ed28490f06f6370a02d992b028e8835486b4bd32855292fd6b4749",
157
- "bytes": 128427279,
158
- "attestationUrl": "https://github.com/tetsuo-ai/agenc-releases/releases/download/agenc-v0.16.0/agenc-runtime-0.16.0-linux-arm64-node26-abi147.tar.gz.sigstore.json",
159
- "attestationSha256": "c99148bfbc7dd61f08a1c5326224d9bf4e64a6fe54d052c281cc212bb82a7473",
160
- "attestationBytes": 11358,
161
- "buildProvenanceUrl": "https://github.com/tetsuo-ai/agenc-releases/releases/download/agenc-v0.16.0/agenc-runtime-0.16.0-linux-arm64-node26-abi147.tar.gz.build.sigstore.json",
162
- "buildProvenanceSha256": "6940c8c2836b9a1ed0793dafd35dea529668fa5128ffb51b0fbf3ff9039d10cd",
163
- "buildProvenanceBytes": 11009,
164
- "metadataSha256": "8129089600bbea0a6bb8b02bb7ce4081ad94c3ce5645989256fa15484e89e48e",
155
+ "url": "https://github.com/tetsuo-ai/agenc-releases/releases/download/agenc-v0.17.0/agenc-runtime-0.17.0-linux-arm64-node26-abi147.tar.gz",
156
+ "sha256": "0f6fb61bc3a8efeb3b02e02f0db6226c5c7918fcaa455f622f6012dacfe89149",
157
+ "bytes": 128833789,
158
+ "attestationUrl": "https://github.com/tetsuo-ai/agenc-releases/releases/download/agenc-v0.17.0/agenc-runtime-0.17.0-linux-arm64-node26-abi147.tar.gz.sigstore.json",
159
+ "attestationSha256": "7f6b1bbddf349570f0f25f2ce86c53d132872acbadee077de82bfaed5e15423d",
160
+ "attestationBytes": 11452,
161
+ "buildProvenanceUrl": "https://github.com/tetsuo-ai/agenc-releases/releases/download/agenc-v0.17.0/agenc-runtime-0.17.0-linux-arm64-node26-abi147.tar.gz.build.sigstore.json",
162
+ "buildProvenanceSha256": "265e5e5199fe9ce847376f635ed38d745109daf199b628a3058a33a8852c25cf",
163
+ "buildProvenanceBytes": 11181,
164
+ "metadataSha256": "db891bafcbf27fd039581dd6646f62b4d960c3739e954e5bf9ad3c0cfeb2d8fb",
165
165
  "nativeToolchain": {
166
166
  "schemaVersion": 1,
167
167
  "builder": "rockylinux@sha256:9794037624aaa6212aeada1d28861ef5e0a935adaf93e4ef79837119f2a2d04c+rpm-content-sha256:98d1259fca66152fb91e739a15a4fa7f76b0a222369cb34ba853d7d72f02239f",
@@ -202,13 +202,13 @@
202
202
  "tar-1.30-11.el8_10"
203
203
  ]
204
204
  },
205
- "dependencyTreeSha256": "34b251055d943089ae58cb702246eb4b9239351cedd772ab098e2412b4deb3ca",
205
+ "dependencyTreeSha256": "d94fa45434fa59b0a3e84178e213294519a918185275bc8e3a5ca5b8555fcfe6",
206
206
  "dependencyPackages": 469,
207
207
  "archiveFormat": "tar+gzip; portable; utf8-byte-order; normalized-mtime",
208
208
  "archiveValidation": {
209
209
  "policy": "agenc-runtime-archive-v1",
210
- "entries": 29445,
211
- "uncompressedBytes": 490505216
210
+ "entries": 29448,
211
+ "uncompressedBytes": 492716544
212
212
  },
213
213
  "libcFamily": "glibc",
214
214
  "minimumGlibcVersion": "2.28",
@@ -223,20 +223,20 @@
223
223
  {
224
224
  "platform": "linux",
225
225
  "arch": "x64",
226
- "runtimeVersion": "0.16.0",
226
+ "runtimeVersion": "0.17.0",
227
227
  "nodeMajor": 26,
228
228
  "nodeModuleAbi": "147",
229
229
  "nodeApiVersion": "10",
230
- "url": "https://github.com/tetsuo-ai/agenc-releases/releases/download/agenc-v0.16.0/agenc-runtime-0.16.0-linux-x64-node26-abi147.tar.gz",
231
- "sha256": "a850981f4895c1dc2b94859e80ac4eae692ffe9e51308c4afc0122ce363f57e3",
232
- "bytes": 128042657,
233
- "attestationUrl": "https://github.com/tetsuo-ai/agenc-releases/releases/download/agenc-v0.16.0/agenc-runtime-0.16.0-linux-x64-node26-abi147.tar.gz.sigstore.json",
234
- "attestationSha256": "f737c745ab703c9832a45b488627217083eae18b278c7dfcc5f108ba04cbd7a4",
235
- "attestationBytes": 11299,
236
- "buildProvenanceUrl": "https://github.com/tetsuo-ai/agenc-releases/releases/download/agenc-v0.16.0/agenc-runtime-0.16.0-linux-x64-node26-abi147.tar.gz.build.sigstore.json",
237
- "buildProvenanceSha256": "d6d86380b46fbf370d4de8030a50696e75768f57c646929d7f93690624f753a2",
238
- "buildProvenanceBytes": 11009,
239
- "metadataSha256": "b87a6ced73bdf3a6c1f7cf5f42e350a0da6af0ecc09657bff37c310c2853a99d",
230
+ "url": "https://github.com/tetsuo-ai/agenc-releases/releases/download/agenc-v0.17.0/agenc-runtime-0.17.0-linux-x64-node26-abi147.tar.gz",
231
+ "sha256": "9e6c1f8fea9595b50d0006207577f2a7b7114f6f334ad635f3dc5447c2394230",
232
+ "bytes": 128448586,
233
+ "attestationUrl": "https://github.com/tetsuo-ai/agenc-releases/releases/download/agenc-v0.17.0/agenc-runtime-0.17.0-linux-x64-node26-abi147.tar.gz.sigstore.json",
234
+ "attestationSha256": "3f78fde9027430ea447b759f9ab91551a9ed333fc7626c80d35a9f3753c1fc5f",
235
+ "attestationBytes": 11440,
236
+ "buildProvenanceUrl": "https://github.com/tetsuo-ai/agenc-releases/releases/download/agenc-v0.17.0/agenc-runtime-0.17.0-linux-x64-node26-abi147.tar.gz.build.sigstore.json",
237
+ "buildProvenanceSha256": "678fe11567083319acf6fcde41f65e977b24abb6f0b36e827dcd760dc951e0df",
238
+ "buildProvenanceBytes": 11177,
239
+ "metadataSha256": "397ee08d3109c0ec799d82ee335444b0cb6264270330dbc5eb9c6684bbdd6ebc",
240
240
  "nativeToolchain": {
241
241
  "schemaVersion": 1,
242
242
  "builder": "rockylinux@sha256:9794037624aaa6212aeada1d28861ef5e0a935adaf93e4ef79837119f2a2d04c+rpm-content-sha256:20da0fd525fe8dbe596068bb896b949fe84c5ede3c2829e9481f6c2c97de0a5e",
@@ -277,13 +277,13 @@
277
277
  "tar-1.30-11.el8_10"
278
278
  ]
279
279
  },
280
- "dependencyTreeSha256": "1f123c91d212b336729bef78cfd211c9ad166b7c8fc5b407c59d61f40930646a",
280
+ "dependencyTreeSha256": "b6121643638a0c98ed7a668243f834ea962ff88ea1b02997ab110fd73d434f17",
281
281
  "dependencyPackages": 469,
282
282
  "archiveFormat": "tar+gzip; portable; utf8-byte-order; normalized-mtime",
283
283
  "archiveValidation": {
284
284
  "policy": "agenc-runtime-archive-v1",
285
- "entries": 29445,
286
- "uncompressedBytes": 491623424
285
+ "entries": 29448,
286
+ "uncompressedBytes": 493779968
287
287
  },
288
288
  "libcFamily": "glibc",
289
289
  "minimumGlibcVersion": "2.28",
@@ -298,20 +298,20 @@
298
298
  {
299
299
  "platform": "win",
300
300
  "arch": "x64",
301
- "runtimeVersion": "0.16.0",
301
+ "runtimeVersion": "0.17.0",
302
302
  "nodeMajor": 26,
303
303
  "nodeModuleAbi": "147",
304
304
  "nodeApiVersion": "10",
305
- "url": "https://github.com/tetsuo-ai/agenc-releases/releases/download/agenc-v0.16.0/agenc-runtime-0.16.0-win-x64-node26-abi147.tar.gz",
306
- "sha256": "0db07265ef473306743844df83115ee4f81d764e1257951806cb5e2600d9fd96",
307
- "bytes": 110964983,
308
- "attestationUrl": "https://github.com/tetsuo-ai/agenc-releases/releases/download/agenc-v0.16.0/agenc-runtime-0.16.0-win-x64-node26-abi147.tar.gz.sigstore.json",
309
- "attestationSha256": "87e330a52e45cd33a4de97d74ed992bd1812c4f3de6e4c68a911259922a572b1",
310
- "attestationBytes": 11401,
311
- "buildProvenanceUrl": "https://github.com/tetsuo-ai/agenc-releases/releases/download/agenc-v0.16.0/agenc-runtime-0.16.0-win-x64-node26-abi147.tar.gz.build.sigstore.json",
312
- "buildProvenanceSha256": "1fcdf6f3e5d6833877d8dec6cf17f7ca54324340d2f681e2fe3e9d28b26c8ba4",
313
- "buildProvenanceBytes": 11006,
314
- "metadataSha256": "0d4a14612e409a87e477f2ca3fb2b1dda4fbb9bcf29c1ca939c8f603f34ed2b8",
305
+ "url": "https://github.com/tetsuo-ai/agenc-releases/releases/download/agenc-v0.17.0/agenc-runtime-0.17.0-win-x64-node26-abi147.tar.gz",
306
+ "sha256": "d11885737c6696198a1b396df31203938f4c4b07c00919401a6280d4be1f5423",
307
+ "bytes": 111364531,
308
+ "attestationUrl": "https://github.com/tetsuo-ai/agenc-releases/releases/download/agenc-v0.17.0/agenc-runtime-0.17.0-win-x64-node26-abi147.tar.gz.sigstore.json",
309
+ "attestationSha256": "751be37fff0bbdf7481954e26978ebc8745424336396bc2c72ff9b2b3fec3b56",
310
+ "attestationBytes": 11585,
311
+ "buildProvenanceUrl": "https://github.com/tetsuo-ai/agenc-releases/releases/download/agenc-v0.17.0/agenc-runtime-0.17.0-win-x64-node26-abi147.tar.gz.build.sigstore.json",
312
+ "buildProvenanceSha256": "7904a4cada4dcebff05e2aa7e593ce8795b6b34c988073c9384a397102270feb",
313
+ "buildProvenanceBytes": 11268,
314
+ "metadataSha256": "97ba9768422b104228eb16089c35d9c4c77188feff1c318ee864a62a9273c5e4",
315
315
  "nativeToolchain": {
316
316
  "schemaVersion": 1,
317
317
  "builder": "github-hosted:windows-2025-vs2026:win25-vs2026:20260810.198.2:X64",
@@ -353,13 +353,13 @@
353
353
  "msvcCompilerSha256": "c94cdac6a780142920110e5cb8b7339817029eead696e0e97700b45e03216a00",
354
354
  "msvcLinkerSha256": "f233b8e337cec96a69868a8cde676808bfa81152493968d0b27b7cd0daac15be"
355
355
  },
356
- "dependencyTreeSha256": "8d15d1caedeeba5e012a257aaf4763264bb1069f4352281ba976c7e3d5206b0a",
356
+ "dependencyTreeSha256": "874ac4e08df60e2649dc5b9a9e6d96f01c6e161aa7463329d6f355ce1d2fd5de",
357
357
  "dependencyPackages": 466,
358
358
  "archiveFormat": "tar+gzip; portable; utf8-byte-order; normalized-mtime",
359
359
  "archiveValidation": {
360
360
  "policy": "agenc-runtime-archive-v1",
361
- "entries": 29464,
362
- "uncompressedBytes": 432413696
361
+ "entries": 29466,
362
+ "uncompressedBytes": 434596864
363
363
  },
364
364
  "bins": {
365
365
  "agenc": "node_modules/@tetsuo-ai/runtime/bin/agenc",
@@ -75,6 +75,141 @@ const MANIFEST_PATH = resolve(
75
75
  const DEFAULT_RUNTIME_FETCH_TIMEOUT_MS = 120_000;
76
76
  const DEFAULT_RUNTIME_EXTRACTION_TIMEOUT_MS = 120_000;
77
77
  const WINDOWS_SYSTEM_ROOT = String.raw`\\?\GLOBALROOT\SystemRoot`;
78
+ const WINDOWS_INVALID_FILE_ID = 0xffff_ffff_ffff_ffffn;
79
+ const WINDOWS_RESERVED_DEVICE_BASENAME =
80
+ /^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\..*)?$/iu;
81
+ const WINDOWS_EXECUTABLE_FILESYSTEM = {
82
+ lstat: (path) => lstatSync(path, { bigint: true }),
83
+ open: (path) => openSync(
84
+ path,
85
+ fsConstants.O_RDONLY | (fsConstants.O_NOFOLLOW ?? 0),
86
+ ),
87
+ fstat: (descriptor) => fstatSync(descriptor, { bigint: true }),
88
+ close: closeSync,
89
+ };
90
+
91
+ function trustedWindowsSystemPaths(canonicalize = realpathSync.native) {
92
+ const systemRoot = canonicalize(WINDOWS_SYSTEM_ROOT);
93
+ if (!/^[a-z]:\\/iu.test(systemRoot) || win32.normalize(systemRoot) !== systemRoot) {
94
+ throw new Error(
95
+ "agenc: trusted Windows SystemRoot did not resolve to a canonical local DOS path",
96
+ );
97
+ }
98
+ const system32 = win32.join(systemRoot, "System32");
99
+ const powershellRoot = win32.join(system32, "WindowsPowerShell", "v1.0");
100
+ return {
101
+ systemRoot,
102
+ system32,
103
+ powershellRoot,
104
+ namespaceSystemRoot: WINDOWS_SYSTEM_ROOT,
105
+ };
106
+ }
107
+
108
+ function trustedWindowsSystemExecutable(
109
+ paths,
110
+ relativeSegments,
111
+ filesystem = WINDOWS_EXECUTABLE_FILESYSTEM,
112
+ ) {
113
+ if (
114
+ relativeSegments.length === 0 ||
115
+ relativeSegments.some((segment) => !isSafeWindowsExecutableSegment(segment))
116
+ ) {
117
+ throw new TypeError("trusted Windows executable segments are invalid");
118
+ }
119
+ const executable = win32.join(paths.systemRoot, ...relativeSegments);
120
+ const namespaceExecutable = win32.join(
121
+ paths.namespaceSystemRoot,
122
+ ...relativeSegments,
123
+ );
124
+ verifyWindowsExecutableAliases(namespaceExecutable, executable, filesystem);
125
+ return executable;
126
+ }
127
+
128
+ function isSafeWindowsExecutableSegment(segment) {
129
+ return segment.length > 0 && segment !== "." && segment !== ".." &&
130
+ win32.basename(segment) === segment &&
131
+ !/[\u0000-\u001f<>:"/\\|?*]/u.test(segment) && !/[ .]$/u.test(segment) &&
132
+ !WINDOWS_RESERVED_DEVICE_BASENAME.test(segment);
133
+ }
134
+
135
+ function verifyWindowsExecutableAliases(namespaceExecutable, executable, filesystem) {
136
+ let namespaceDescriptor;
137
+ let candidateDescriptor;
138
+ let operationError;
139
+ try {
140
+ const namespaceBefore = filesystem.lstat(namespaceExecutable);
141
+ const candidateBefore = filesystem.lstat(executable);
142
+ assertRegularWindowsExecutable(namespaceBefore, "GLOBALROOT path");
143
+ assertRegularWindowsExecutable(candidateBefore, "DOS path");
144
+ namespaceDescriptor = filesystem.open(namespaceExecutable);
145
+ candidateDescriptor = filesystem.open(executable);
146
+ const namespaceOpened = filesystem.fstat(namespaceDescriptor);
147
+ const candidateOpened = filesystem.fstat(candidateDescriptor);
148
+ const namespaceAfter = filesystem.lstat(namespaceExecutable);
149
+ const candidateAfter = filesystem.lstat(executable);
150
+ assertRegularWindowsExecutable(namespaceOpened, "GLOBALROOT descriptor");
151
+ assertRegularWindowsExecutable(candidateOpened, "DOS descriptor");
152
+ assertRegularWindowsExecutable(namespaceAfter, "GLOBALROOT path");
153
+ assertRegularWindowsExecutable(candidateAfter, "DOS path");
154
+ for (const identity of [
155
+ namespaceBefore,
156
+ candidateBefore,
157
+ namespaceOpened,
158
+ candidateOpened,
159
+ namespaceAfter,
160
+ candidateAfter,
161
+ ]) {
162
+ if (
163
+ identity.dev <= 0n || identity.ino <= 0n ||
164
+ identity.dev === WINDOWS_INVALID_FILE_ID || identity.ino === WINDOWS_INVALID_FILE_ID
165
+ ) {
166
+ throw new Error("trusted Windows system executable identity is unavailable");
167
+ }
168
+ }
169
+ if (
170
+ !sameWindowsExecutableIdentity(namespaceBefore, namespaceOpened) ||
171
+ !sameWindowsExecutableIdentity(namespaceOpened, namespaceAfter) ||
172
+ !sameWindowsExecutableIdentity(candidateBefore, candidateOpened) ||
173
+ !sameWindowsExecutableIdentity(candidateOpened, candidateAfter) ||
174
+ !sameWindowsExecutableIdentity(namespaceOpened, candidateOpened)
175
+ ) {
176
+ throw new Error("trusted Windows system executable identity mismatch");
177
+ }
178
+ } catch (error) {
179
+ operationError = error;
180
+ }
181
+ const closeErrors = [];
182
+ for (const descriptor of [candidateDescriptor, namespaceDescriptor]) {
183
+ if (descriptor === undefined) continue;
184
+ try { filesystem.close(descriptor); } catch (error) { closeErrors.push(error); }
185
+ }
186
+ if (operationError !== undefined) {
187
+ if (closeErrors.length > 0) {
188
+ throw new AggregateError(
189
+ [operationError, ...closeErrors],
190
+ "trusted Windows executable validation and cleanup both failed",
191
+ );
192
+ }
193
+ throw operationError;
194
+ }
195
+ if (closeErrors.length === 1) throw closeErrors[0];
196
+ if (closeErrors.length > 1) {
197
+ throw new AggregateError(
198
+ closeErrors,
199
+ "trusted Windows executable descriptor cleanup failed",
200
+ );
201
+ }
202
+ }
203
+
204
+ function assertRegularWindowsExecutable(identity, spelling) {
205
+ if (!identity.isFile() || identity.isSymbolicLink()) {
206
+ throw new Error(`trusted Windows ${spelling} executable is not a regular non-link file`);
207
+ }
208
+ }
209
+
210
+ function sameWindowsExecutableIdentity(left, right) {
211
+ return left.dev === right.dev && left.ino === right.ino;
212
+ }
78
213
 
79
214
  export function resolveAgenCHome(env = process.env, userHome = homedir()) {
80
215
  const configured = env.AGENC_HOME;
@@ -1057,9 +1192,19 @@ async function download(
1057
1192
  function assertRootOwnedSystemExecutable(path) {
1058
1193
  const canonical = realpathSync.native(path);
1059
1194
  const executable = lstatSync(canonical, { bigint: true });
1195
+ // Deliberately no link-count requirement, matching the shell installer's
1196
+ // resolve_system_tool. Stock system tools legitimately ship as several names
1197
+ // for one inode (macOS and Ubuntu both hardlink unzip/zipinfo; some
1198
+ // distributions link tar/gtar), and no user can change that under SIP.
1199
+ // Root ownership plus the fully root-owned, non-group/other-writable
1200
+ // ancestor chain below is what buys the guarantee: a non-root user can
1201
+ // neither add a link inside such a tree nor alter the root-owned inode
1202
+ // through a link made elsewhere. The nlink !== 1n checks on files this
1203
+ // launcher downloads into its own private tree stay, because there a second
1204
+ // link is a genuine TOCTOU signal.
1060
1205
  if (
1061
1206
  !executable.isFile() || executable.isSymbolicLink() ||
1062
- executable.nlink !== 1n || executable.uid !== 0n ||
1207
+ executable.uid !== 0n ||
1063
1208
  (executable.mode & 0o022n) !== 0n
1064
1209
  ) {
1065
1210
  throw new Error(`agenc: operating-system tar is not a trusted regular file: ${canonical}`);
@@ -1096,31 +1241,39 @@ export function resolveTrustedSystemTar(platform = process.platform) {
1096
1241
  throw new Error(`agenc: trusted operating-system tar is unsupported on ${platform}`);
1097
1242
  }
1098
1243
 
1099
- // GLOBALROOT reaches the kernel's real SystemRoot namespace and cannot be
1100
- // redirected with caller-controlled SystemRoot/WINDIR values or drive maps.
1101
- const system32 = win32.join(WINDOWS_SYSTEM_ROOT, "System32");
1102
- const powershellRoot = win32.join(system32, "WindowsPowerShell", "v1.0");
1103
- const tarPath = win32.join(system32, "tar.exe");
1104
- const powershell = win32.join(powershellRoot, "powershell.exe");
1105
- for (const [path, label] of [[tarPath, "tar"], [powershell, "PowerShell"]]) {
1106
- const metadata = lstatSync(path, { bigint: true });
1107
- if (!metadata.isFile() || metadata.isSymbolicLink()) {
1108
- throw new Error(`agenc: trusted Windows ${label} is unavailable`);
1109
- }
1110
- }
1244
+ // Derive CreateProcess-compatible DOS spellings from GLOBALROOT and prove
1245
+ // each executable still aliases its namespace path immediately before use.
1246
+ const windowsPaths = trustedWindowsSystemPaths();
1247
+ const { systemRoot, system32, powershellRoot } = windowsPaths;
1248
+ const tarPath = trustedWindowsSystemExecutable(windowsPaths, [
1249
+ "System32",
1250
+ "tar.exe",
1251
+ ]);
1252
+ const powershell = trustedWindowsSystemExecutable(windowsPaths, [
1253
+ "System32",
1254
+ "WindowsPowerShell",
1255
+ "v1.0",
1256
+ "powershell.exe",
1257
+ ]);
1111
1258
  const env = {
1112
1259
  APPDATA: "",
1113
1260
  COMSPEC: win32.join(system32, "cmd.exe"),
1114
1261
  HOME: "",
1262
+ HOMEDRIVE: "",
1263
+ HOMEPATH: "",
1115
1264
  LOCALAPPDATA: "",
1265
+ LOGONSERVER: "",
1116
1266
  PATH: `${system32};${powershellRoot}`,
1117
1267
  PATHEXT: ".COM;.EXE",
1118
1268
  PSModulePath: win32.join(powershellRoot, "Modules"),
1119
- SystemRoot: WINDOWS_SYSTEM_ROOT,
1120
- TEMP: win32.join(WINDOWS_SYSTEM_ROOT, "Temp"),
1121
- TMP: win32.join(WINDOWS_SYSTEM_ROOT, "Temp"),
1269
+ SYSTEMDRIVE: "",
1270
+ SystemRoot: systemRoot,
1271
+ TEMP: win32.join(systemRoot, "Temp"),
1272
+ TMP: win32.join(systemRoot, "Temp"),
1273
+ USERDOMAIN: "",
1274
+ USERNAME: "",
1122
1275
  USERPROFILE: powershellRoot,
1123
- WINDIR: WINDOWS_SYSTEM_ROOT,
1276
+ WINDIR: systemRoot,
1124
1277
  };
1125
1278
  const signature = spawnSync(
1126
1279
  powershell,
@@ -1149,7 +1302,10 @@ export function resolveTrustedSystemTar(platform = process.platform) {
1149
1302
  if (signature.error !== undefined || signature.status !== 0) {
1150
1303
  throw new Error("agenc: Windows operating-system tar failed Authenticode validation");
1151
1304
  }
1152
- return { path: tarPath, env };
1305
+ return {
1306
+ path: trustedWindowsSystemExecutable(windowsPaths, ["System32", "tar.exe"]),
1307
+ env,
1308
+ };
1153
1309
  }
1154
1310
 
1155
1311
  function extractTarGz(archive, destDir) {
@@ -7,6 +7,14 @@
7
7
  // contention is retried asynchronously against one monotonic deadline.
8
8
 
9
9
  import { execFile } from "node:child_process";
10
+ import {
11
+ closeSync,
12
+ constants as fsConstants,
13
+ fstatSync,
14
+ lstatSync,
15
+ openSync,
16
+ realpathSync,
17
+ } from "node:fs";
10
18
  import {
11
19
  chmod,
12
20
  lstat,
@@ -33,7 +41,18 @@ const REGISTRY_SYMBOL = Symbol.for("@tetsuo-ai/agenc.sqlite-lock-registry");
33
41
  const MAX_BUSY_RETRY_MS = 50;
34
42
  const MAX_TIMER_DELAY_MS = 2_147_483_647;
35
43
  const UNSUPPORTED_FILE_ID_64 = 0xffff_ffff_ffff_ffffn;
44
+ const WINDOWS_PATH_TRANSPORT_MAX_CHARS = 16_384;
45
+ const WINDOWS_PATH_TRANSPORT_MAX_ENTRIES = 128;
36
46
  const WINDOWS_SYSTEM_ROOT = String.raw`\\?\GLOBALROOT\SystemRoot`;
47
+ const WINDOWS_EXECUTABLE_FILESYSTEM = {
48
+ lstat: (path) => lstatSync(path, { bigint: true }),
49
+ open: (path) => openSync(
50
+ path,
51
+ fsConstants.O_RDONLY | (fsConstants.O_NOFOLLOW ?? 0),
52
+ ),
53
+ fstat: (descriptor) => fstatSync(descriptor, { bigint: true }),
54
+ close: closeSync,
55
+ };
37
56
  const LOCAL_FILESYSTEM_TYPES = new Set([
38
57
  "apfs", "bcachefs", "btrfs", "exfat", "ext2", "ext3", "ext4", "f2fs",
39
58
  "hfs", "hfsplus", "jfs", "msdos", "nilfs2", "ntfs", "ntfs3", "overlay",
@@ -57,7 +76,15 @@ const DARWIN_ACL_KNOWN_TOKENS = new Set([
57
76
 
58
77
  const WINDOWS_SECURITY_SCRIPT = String.raw`
59
78
  $ErrorActionPreference = 'Stop'
60
- $entries = @(ConvertFrom-Json -InputObject $env:AGENC_LOCK_PATHS_JSON)
79
+ # Keep this direct-.NET only: module autoload can exhaust the shared lock deadline.
80
+ $transport = [string]$env:AGENC_LOCK_PATHS
81
+ if ([string]::IsNullOrEmpty($transport) -or $transport.Length -gt ${WINDOWS_PATH_TRANSPORT_MAX_CHARS}) {
82
+ throw 'invalid protected-path transport'
83
+ }
84
+ $entries = [string[]]$transport.Split([char]10)
85
+ if ($entries.Count -lt 1 -or $entries.Count -gt ${WINDOWS_PATH_TRANSPORT_MAX_ENTRIES}) {
86
+ throw 'invalid protected-path transport'
87
+ }
61
88
  $currentSid = [System.Security.Principal.WindowsIdentity]::GetCurrent().User.Value
62
89
  $trusted = @(
63
90
  $currentSid,
@@ -65,27 +92,50 @@ $trusted = @(
65
92
  'S-1-5-32-544',
66
93
  'S-1-5-80-956008885-3418522649-1831038044-1853292631-2271478464'
67
94
  )
68
- $leafMutationMask = [int64]852310
69
- $ancestorMutationMask = [int64]852306
95
+ # Specific mutation rights plus GENERIC_WRITE and GENERIC_ALL.
96
+ $leafMutationMask = [int64]1343029590
97
+ $ancestorMutationMask = [int64]1343029586
70
98
  foreach ($entry in $entries) {
71
- $requested = [string]$entry.path
72
- $role = [string]$entry.role
99
+ $separator = $entry.IndexOf([char]58)
100
+ if ($separator -lt 1 -or $separator -eq ($entry.Length - 1)) {
101
+ throw 'invalid protected-path transport'
102
+ }
103
+ $role = $entry.Substring(0, $separator)
73
104
  if (@('leafDirectory', 'ancestorDirectory', 'file') -notcontains $role) {
74
105
  throw "invalid protected-path role: $role"
75
106
  }
107
+ $encodedPath = $entry.Substring($separator + 1)
108
+ if (($encodedPath.Length % 4) -ne 0) { throw 'invalid protected-path transport' }
109
+ $pathBytes = [System.Convert]::FromBase64String($encodedPath)
110
+ if (($pathBytes.Length % 2) -ne 0) { throw 'invalid protected-path transport' }
111
+ if ([System.Convert]::ToBase64String($pathBytes) -cne $encodedPath) {
112
+ throw 'non-canonical protected-path transport'
113
+ }
114
+ $pathCharacters = [char[]]::new([int]($pathBytes.Length / 2))
115
+ for ($index = 0; $index -lt $pathCharacters.Length; $index += 1) {
116
+ $byteOffset = $index * 2
117
+ $lowByte = [int]$pathBytes[$byteOffset]
118
+ $highByte = [int]$pathBytes[$byteOffset + 1]
119
+ $pathCharacters[$index] = [char]($lowByte -bor ($highByte -shl 8))
120
+ }
121
+ $requested = [string]::new($pathCharacters)
76
122
  $mutationMask = if ($role -eq 'ancestorDirectory') {
77
123
  $ancestorMutationMask
78
124
  } else {
79
125
  $leafMutationMask
80
126
  }
81
- $full = [System.IO.Path]::GetFullPath([string]$requested)
127
+ $full = [System.IO.Path]::GetFullPath($requested)
82
128
  if ($full.StartsWith('\\') -or $full.StartsWith('\\?\') -or $full.StartsWith('\\.\')) {
83
129
  throw "network and device paths are unsupported: $full"
84
130
  }
85
- $item = Get-Item -LiteralPath $full -Force
86
- if (($item.Attributes -band [System.IO.FileAttributes]::ReparsePoint) -ne 0) {
131
+ $attributes = [System.IO.File]::GetAttributes($full)
132
+ if (($attributes -band [System.IO.FileAttributes]::ReparsePoint) -ne 0) {
87
133
  throw "reparse points are unsupported: $full"
88
134
  }
135
+ $isDirectory = ($attributes -band [System.IO.FileAttributes]::Directory) -ne 0
136
+ if ($isDirectory -ne ($role -ne 'file')) {
137
+ throw "protected-path role does not match its type: $full"
138
+ }
89
139
  $drive = [System.IO.DriveInfo]::new([System.IO.Path]::GetPathRoot($full))
90
140
  if (@(2, 3, 6) -notcontains [int]$drive.DriveType) {
91
141
  throw "non-local drive is unsupported: $full"
@@ -93,12 +143,16 @@ foreach ($entry in $entries) {
93
143
  if ($drive.DriveFormat -ne 'NTFS') {
94
144
  throw "filesystem cannot enforce the required ACL contract: $full"
95
145
  }
96
- $acl = Get-Acl -LiteralPath $full
146
+ $aclSections = [System.Security.AccessControl.AccessControlSections]::Owner -bor [System.Security.AccessControl.AccessControlSections]::Access
147
+ if ($isDirectory) {
148
+ $acl = [System.IO.Directory]::GetAccessControl($full, $aclSections)
149
+ } else {
150
+ $acl = [System.IO.File]::GetAccessControl($full, $aclSections)
151
+ }
97
152
  if (-not $acl.AreAccessRulesCanonical) {
98
153
  throw "non-canonical ACL is unsupported: $full"
99
154
  }
100
- $bytes = [byte[]]::new($acl.BinaryLength)
101
- $acl.GetSecurityDescriptorBinaryForm($bytes, 0)
155
+ $bytes = $acl.GetSecurityDescriptorBinaryForm()
102
156
  $raw = [System.Security.AccessControl.RawSecurityDescriptor]::new($bytes, 0)
103
157
  if ($null -eq $raw.DiscretionaryAcl) {
104
158
  throw "null DACL is unsupported: $full"
@@ -125,7 +179,8 @@ foreach ($entry in $entries) {
125
179
  }
126
180
  }
127
181
  $sid = $rule.IdentityReference.Value
128
- if ($trusted -notcontains $sid -and (([int64]$rule.FileSystemRights -band $mutationMask) -ne 0)) {
182
+ $rights = ([int64]$rule.FileSystemRights) -band [int64]4294967295
183
+ if ($trusted -notcontains $sid -and (($rights -band $mutationMask) -ne 0)) {
129
184
  throw "untrusted mutation ACE on lock path: $full"
130
185
  }
131
186
  }
@@ -378,31 +433,124 @@ async function assertDarwinPathSecurity(path, role, context) {
378
433
  throwIfExpired(context, path);
379
434
  }
380
435
 
381
- function trustedWindowsPowerShellPath() {
382
- // GLOBALROOT enters the true system object-manager namespace instead of a
383
- // session-specific or environment-selected DOS path. Never resolve this
384
- // executable through PATH, SystemRoot, WINDIR, or another caller-controlled
385
- // value.
436
+ function trustedWindowsPowerShellPath(
437
+ canonicalize = realpathSync.native,
438
+ filesystem = WINDOWS_EXECUTABLE_FILESYSTEM,
439
+ ) {
440
+ // Derive a CreateProcess-compatible DOS spelling from GLOBALROOT, then prove
441
+ // both spellings still name the same regular system file before launch.
442
+ const systemRoot = canonicalize(WINDOWS_SYSTEM_ROOT);
443
+ if (!/^[a-z]:\\/iu.test(systemRoot) || win32.normalize(systemRoot) !== systemRoot) {
444
+ throw new Error("trusted Windows SystemRoot did not resolve to a canonical local DOS path");
445
+ }
446
+ const workingDirectory = win32.join(systemRoot, "System32");
447
+ const executable = win32.join(
448
+ workingDirectory,
449
+ "WindowsPowerShell",
450
+ "v1.0",
451
+ "powershell.exe",
452
+ );
453
+ const namespaceExecutable = win32.join(
454
+ WINDOWS_SYSTEM_ROOT,
455
+ "System32",
456
+ "WindowsPowerShell",
457
+ "v1.0",
458
+ "powershell.exe",
459
+ );
460
+ verifyWindowsExecutableAliases(namespaceExecutable, executable, filesystem);
386
461
  return {
387
- systemRoot: WINDOWS_SYSTEM_ROOT,
388
- workingDirectory: win32.join(WINDOWS_SYSTEM_ROOT, "System32"),
389
- executable: win32.join(
390
- WINDOWS_SYSTEM_ROOT,
391
- "System32",
392
- "WindowsPowerShell",
393
- "v1.0",
394
- "powershell.exe",
395
- ),
462
+ systemRoot,
463
+ workingDirectory,
464
+ executable,
396
465
  };
397
466
  }
398
467
 
399
- function windowsPowerShellEnvironment(paths) {
400
- const { systemRoot, workingDirectory } = trustedWindowsPowerShellPath();
468
+ function verifyWindowsExecutableAliases(namespaceExecutable, executable, filesystem) {
469
+ let namespaceDescriptor;
470
+ let candidateDescriptor;
471
+ let operationError;
472
+ try {
473
+ const namespaceBefore = filesystem.lstat(namespaceExecutable);
474
+ const candidateBefore = filesystem.lstat(executable);
475
+ assertRegularWindowsExecutable(namespaceBefore, "GLOBALROOT path");
476
+ assertRegularWindowsExecutable(candidateBefore, "DOS path");
477
+ namespaceDescriptor = filesystem.open(namespaceExecutable);
478
+ candidateDescriptor = filesystem.open(executable);
479
+ const namespaceOpened = filesystem.fstat(namespaceDescriptor);
480
+ const candidateOpened = filesystem.fstat(candidateDescriptor);
481
+ const namespaceAfter = filesystem.lstat(namespaceExecutable);
482
+ const candidateAfter = filesystem.lstat(executable);
483
+ assertRegularWindowsExecutable(namespaceOpened, "GLOBALROOT descriptor");
484
+ assertRegularWindowsExecutable(candidateOpened, "DOS descriptor");
485
+ assertRegularWindowsExecutable(namespaceAfter, "GLOBALROOT path");
486
+ assertRegularWindowsExecutable(candidateAfter, "DOS path");
487
+ for (const identity of [
488
+ namespaceBefore,
489
+ candidateBefore,
490
+ namespaceOpened,
491
+ candidateOpened,
492
+ namespaceAfter,
493
+ candidateAfter,
494
+ ]) {
495
+ if (
496
+ identity.dev <= 0n || identity.ino <= 0n ||
497
+ identity.dev === UNSUPPORTED_FILE_ID_64 || identity.ino === UNSUPPORTED_FILE_ID_64
498
+ ) {
499
+ throw new Error("trusted Windows system executable identity is unavailable");
500
+ }
501
+ }
502
+ if (
503
+ !sameFileIdentity(namespaceBefore, namespaceOpened) ||
504
+ !sameFileIdentity(namespaceOpened, namespaceAfter) ||
505
+ !sameFileIdentity(candidateBefore, candidateOpened) ||
506
+ !sameFileIdentity(candidateOpened, candidateAfter) ||
507
+ !sameFileIdentity(namespaceOpened, candidateOpened)
508
+ ) {
509
+ throw new Error("trusted Windows system executable identity mismatch");
510
+ }
511
+ } catch (error) {
512
+ operationError = error;
513
+ }
514
+ const closeErrors = [];
515
+ for (const descriptor of [candidateDescriptor, namespaceDescriptor]) {
516
+ if (descriptor === undefined) continue;
517
+ try { filesystem.close(descriptor); } catch (error) { closeErrors.push(error); }
518
+ }
519
+ if (operationError !== undefined) {
520
+ if (closeErrors.length > 0) {
521
+ throw new AggregateError(
522
+ [operationError, ...closeErrors],
523
+ "trusted Windows executable validation and cleanup both failed",
524
+ );
525
+ }
526
+ throw operationError;
527
+ }
528
+ if (closeErrors.length === 1) throw closeErrors[0];
529
+ if (closeErrors.length > 1) {
530
+ throw new AggregateError(
531
+ closeErrors,
532
+ "trusted Windows executable descriptor cleanup failed",
533
+ );
534
+ }
535
+ }
536
+
537
+ function assertRegularWindowsExecutable(identity, spelling) {
538
+ if (!identity.isFile() || identity.isSymbolicLink()) {
539
+ throw new Error(`trusted Windows ${spelling} executable is not a regular non-link file`);
540
+ }
541
+ }
542
+
543
+ function sameFileIdentity(left, right) {
544
+ return left.dev === right.dev && left.ino === right.ino;
545
+ }
546
+
547
+ function windowsPowerShellEnvironment(paths, trustedPaths = trustedWindowsPowerShellPath()) {
548
+ const { systemRoot, workingDirectory } = trustedPaths;
401
549
  // libuv fills a fixed set of "required" Windows variables from the parent
402
550
  // when they are absent. Define every one so poisoned caller state cannot be
403
551
  // silently inherited into the validation helper.
404
552
  return {
405
- AGENC_LOCK_PATHS_JSON: JSON.stringify(paths),
553
+ AGENC_LOCK_PATHS: windowsPathTransport(paths),
406
554
  APPDATA: "",
407
555
  COMSPEC: "",
408
556
  HOMEDRIVE: "",
@@ -423,10 +571,42 @@ function windowsPowerShellEnvironment(paths) {
423
571
  };
424
572
  }
425
573
 
574
+ function windowsPathTransport(entries) {
575
+ if (
576
+ !Array.isArray(entries) ||
577
+ entries.length < 1 ||
578
+ entries.length > WINDOWS_PATH_TRANSPORT_MAX_ENTRIES
579
+ ) {
580
+ throw new Error("agenc: Windows protected-path transport has an invalid entry count");
581
+ }
582
+ const records = [];
583
+ let transportChars = 0;
584
+ for (const entry of entries) {
585
+ const path = entry?.path;
586
+ const role = entry?.role;
587
+ if (
588
+ typeof path !== "string" ||
589
+ path.length === 0 ||
590
+ path.length > WINDOWS_PATH_TRANSPORT_MAX_CHARS ||
591
+ (role !== "leafDirectory" && role !== "ancestorDirectory" && role !== "file")
592
+ ) {
593
+ throw new Error("agenc: Windows protected-path transport entry is invalid");
594
+ }
595
+ const record = `${role}:${Buffer.from(path, "utf16le").toString("base64")}`;
596
+ transportChars += record.length + (records.length === 0 ? 0 : 1);
597
+ if (transportChars > WINDOWS_PATH_TRANSPORT_MAX_CHARS) {
598
+ throw new Error("agenc: Windows protected-path transport exceeds its limit");
599
+ }
600
+ records.push(record);
601
+ }
602
+ return records.join("\n");
603
+ }
604
+
426
605
  async function assertWindowsPathSecurity(entries, context) {
427
606
  const displayPath = entries.at(-1)?.path ?? "unknown";
428
607
  throwIfExpired(context, displayPath);
429
- const { workingDirectory, executable } = trustedWindowsPowerShellPath();
608
+ const trustedPaths = trustedWindowsPowerShellPath();
609
+ const { workingDirectory, executable } = trustedPaths;
430
610
  let result;
431
611
  try {
432
612
  result = await execFileUtf8(
@@ -440,7 +620,7 @@ async function assertWindowsPathSecurity(entries, context) {
440
620
  ],
441
621
  {
442
622
  cwd: workingDirectory,
443
- env: windowsPowerShellEnvironment(entries),
623
+ env: windowsPowerShellEnvironment(entries, trustedPaths),
444
624
  maxBuffer: 1024 * 1024,
445
625
  windowsHide: true,
446
626
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tetsuo-ai/agenc",
3
- "version": "0.16.0",
3
+ "version": "0.17.0",
4
4
  "description": "Public AgenC CLI launcher package",
5
5
  "license": "MIT",
6
6
  "type": "module",