@zaip-ai/cli 4.8.0 → 5.0.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.
Files changed (195) hide show
  1. package/dist/services/adapter-hook-sources.d.ts +3 -0
  2. package/dist/services/adapter-hook-sources.d.ts.map +1 -1
  3. package/dist/services/adapter-hook-sources.js +3 -0
  4. package/dist/services/adapter-hook-sources.js.map +1 -1
  5. package/dist/services/adapters.d.ts +1 -1
  6. package/dist/services/adapters.d.ts.map +1 -1
  7. package/dist/services/adapters.js +5 -2
  8. package/dist/services/adapters.js.map +1 -1
  9. package/dist/services/adapters.test.js +1 -1
  10. package/dist/services/adapters.test.js.map +1 -1
  11. package/dist/services/codex-conversation-lifecycle-hook.d.ts.map +1 -1
  12. package/dist/services/codex-conversation-lifecycle-hook.js +16 -6
  13. package/dist/services/codex-conversation-lifecycle-hook.js.map +1 -1
  14. package/dist/services/codex-hook-activation.d.ts +1 -1
  15. package/dist/services/codex-hook-activation.d.ts.map +1 -1
  16. package/dist/services/codex-hook-activation.js +116 -10
  17. package/dist/services/codex-hook-activation.js.map +1 -1
  18. package/dist/services/codex-pre-tool-cli-capture-template.d.ts +2 -0
  19. package/dist/services/codex-pre-tool-cli-capture-template.d.ts.map +1 -0
  20. package/dist/services/codex-pre-tool-cli-capture-template.js +104 -0
  21. package/dist/services/codex-pre-tool-cli-capture-template.js.map +1 -0
  22. package/dist/services/codex-pre-tool-cloud-template.d.ts +2 -0
  23. package/dist/services/codex-pre-tool-cloud-template.d.ts.map +1 -0
  24. package/dist/services/codex-pre-tool-cloud-template.js +190 -0
  25. package/dist/services/codex-pre-tool-cloud-template.js.map +1 -0
  26. package/dist/services/codex-pre-tool-dependencies.test-support.d.ts +2 -0
  27. package/dist/services/codex-pre-tool-dependencies.test-support.d.ts.map +1 -0
  28. package/dist/services/codex-pre-tool-dependencies.test-support.js +9 -0
  29. package/dist/services/codex-pre-tool-dependencies.test-support.js.map +1 -0
  30. package/dist/services/codex-pre-tool-hook-fixture.test-support.d.ts +5 -0
  31. package/dist/services/codex-pre-tool-hook-fixture.test-support.d.ts.map +1 -0
  32. package/dist/services/codex-pre-tool-hook-fixture.test-support.js +19 -0
  33. package/dist/services/codex-pre-tool-hook-fixture.test-support.js.map +1 -0
  34. package/dist/services/codex-pre-tool-hook-template-bootstrap.test.d.ts +2 -0
  35. package/dist/services/codex-pre-tool-hook-template-bootstrap.test.d.ts.map +1 -0
  36. package/dist/services/codex-pre-tool-hook-template-bootstrap.test.js +107 -0
  37. package/dist/services/codex-pre-tool-hook-template-bootstrap.test.js.map +1 -0
  38. package/dist/services/codex-pre-tool-hook-template.d.ts.map +1 -1
  39. package/dist/services/codex-pre-tool-hook-template.js +152 -190
  40. package/dist/services/codex-pre-tool-hook-template.js.map +1 -1
  41. package/dist/services/codex-pre-tool-hook-template.test.js +63 -75
  42. package/dist/services/codex-pre-tool-hook-template.test.js.map +1 -1
  43. package/dist/services/codex-pre-tool-host-context-template.d.ts +2 -0
  44. package/dist/services/codex-pre-tool-host-context-template.d.ts.map +1 -0
  45. package/dist/services/codex-pre-tool-host-context-template.js +139 -0
  46. package/dist/services/codex-pre-tool-host-context-template.js.map +1 -0
  47. package/dist/services/codex-pre-tool-support-template.d.ts.map +1 -1
  48. package/dist/services/codex-pre-tool-support-template.js +77 -100
  49. package/dist/services/codex-pre-tool-support-template.js.map +1 -1
  50. package/dist/services/codex-subagent-hook-template.d.ts +1 -1
  51. package/dist/services/codex-subagent-hook-template.d.ts.map +1 -1
  52. package/dist/services/codex-subagent-hook-template.js +59 -15
  53. package/dist/services/codex-subagent-hook-template.js.map +1 -1
  54. package/dist/services/codex-validation-runtime-template.d.ts.map +1 -1
  55. package/dist/services/codex-validation-runtime-template.js +37 -10
  56. package/dist/services/codex-validation-runtime-template.js.map +1 -1
  57. package/dist/services/runtime-template-test-support.d.ts +1 -0
  58. package/dist/services/runtime-template-test-support.d.ts.map +1 -1
  59. package/dist/services/runtime-template-test-support.js +23 -2
  60. package/dist/services/runtime-template-test-support.js.map +1 -1
  61. package/dist/services/runtime-template.test.js +7 -24
  62. package/dist/services/runtime-template.test.js.map +1 -1
  63. package/package.json +1 -1
  64. package/templates/.agents/README-ENG.md +49 -7
  65. package/templates/.agents/README.md +41 -10
  66. package/templates/.agents/config/agents/discovery.json +1 -1
  67. package/templates/.agents/dashboard/.zaip-managed.json +125 -73
  68. package/templates/.agents/dashboard/apps/frontend/dist/assets/{CostCalculationGuidePage-BW7Vy1cp.js → CostCalculationGuidePage-w6pjt4OM.js} +1 -1
  69. package/templates/.agents/dashboard/apps/frontend/dist/assets/{DashboardHelpDialog-BN8KQPrr.js → DashboardHelpDialog-DhlntQcR.js} +117 -17
  70. package/templates/.agents/dashboard/apps/frontend/dist/assets/{PricingSettingsDialog-Prcs-ydy.js → PricingSettingsDialog-BHoZT2an.js} +1 -1
  71. package/templates/.agents/dashboard/apps/frontend/dist/assets/{ProjectSettingsPage-HqGUImIG.js → ProjectSettingsPage-IcncIZ9Y.js} +1 -1
  72. package/templates/.agents/dashboard/apps/frontend/dist/assets/{TraceabilityWorkspace-CNpmQMO4.js → TraceabilityWorkspace-C6rORjpr.js} +1 -1
  73. package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-uPnOBShc.js +68 -0
  74. package/templates/.agents/dashboard/apps/frontend/dist/assets/{dashboard-localization-CF7B1Hjn.js → dashboard-localization-DWXjEIgc.js} +49 -7
  75. package/templates/.agents/dashboard/apps/frontend/dist/assets/{icons-vendor-DOhy6D2-.js → icons-vendor-Hc9fp_gs.js} +1 -1
  76. package/templates/.agents/dashboard/apps/frontend/dist/assets/{index-DX65Ufuu.js → index-BqeOEZW5.js} +2 -2
  77. package/templates/.agents/dashboard/apps/frontend/dist/assets/{react-vendor-CkPC-yMz.js → react-vendor-C9ta_O10.js} +1 -1
  78. package/templates/.agents/dashboard/apps/frontend/dist/assets/{traceability-BEdGoIDm.js → traceability-IcefoTOv.js} +1 -1
  79. package/templates/.agents/dashboard/apps/frontend/dist/index.html +5 -5
  80. package/templates/.agents/dashboard/apps/frontend/package.json +1 -1
  81. package/templates/.agents/dashboard/modules/cost-monitor/package.json +1 -1
  82. package/templates/.agents/dashboard/modules/cost-monitor/src/contracts.ts +10 -3
  83. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostFilters.run-page.test.ts +5 -1
  84. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostGroupsTable.period-values.test.tsx +2 -2
  85. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTables-keeps-legacy-codex-identity-fields-blank.test.ts +5 -5
  86. package/templates/.agents/dashboard/modules/cost-monitor/src/react/CostTables.test.ts +36 -43
  87. package/templates/.agents/dashboard/modules/cost-monitor/src/react/cost-monitor-loaders.ts +1 -0
  88. package/templates/.agents/dashboard/modules/cost-monitor/src/react/cost-table-formatters.ts +6 -4
  89. package/templates/.agents/dashboard/modules/cost-monitor/src/server/aggregate.ts +2 -2
  90. package/templates/.agents/dashboard/modules/cost-monitor/src/server/build-cost-monitor-projection.ts +1 -1
  91. package/templates/.agents/dashboard/modules/cost-monitor/src/server/build-cost-summary-projection.ts +11 -25
  92. package/templates/.agents/dashboard/modules/cost-monitor/src/server/cost-summary-defaults.ts +26 -0
  93. package/templates/.agents/dashboard/modules/operations/package.json +1 -1
  94. package/templates/.agents/dashboard/modules/operations/src/contracts.ts +11 -16
  95. package/templates/.agents/dashboard/modules/operations/src/planning-contracts.ts +14 -0
  96. package/templates/.agents/dashboard/modules/operations/src/react/DecisionTable.tsx +65 -0
  97. package/templates/.agents/dashboard/modules/operations/src/react/EntityTables.tsx +5 -71
  98. package/templates/.agents/dashboard/modules/operations/src/react/OperationsPage.tsx +10 -36
  99. package/templates/.agents/dashboard/modules/operations/src/react/operations-overview-period-filter.test.ts +7 -2
  100. package/templates/.agents/dashboard/modules/operations/src/react/use-operations-filter-sync.ts +66 -0
  101. package/templates/.agents/dashboard/modules/traceability/package.json +1 -1
  102. package/templates/.agents/dashboard/packages/branding/package.json +1 -1
  103. package/templates/.agents/dashboard/packages/contracts/package.json +1 -1
  104. package/templates/.agents/dashboard/packages/data-export/package.json +1 -1
  105. package/templates/.agents/dashboard/packages/local-runtime/dist/server.cjs +229 -222
  106. package/templates/.agents/dashboard/packages/local-runtime/dist/sqlite-read-model.cjs +167 -167
  107. package/templates/.agents/dashboard/packages/local-runtime/package.json +3 -3
  108. package/templates/.agents/dashboard/packages/local-runtime/server.mjs +229 -222
  109. package/templates/.agents/dashboard/packages/local-runtime/src/active-capture-failure.test.ts +38 -0
  110. package/templates/.agents/dashboard/packages/local-runtime/src/active-capture-failure.ts +35 -0
  111. package/templates/.agents/dashboard/packages/local-runtime/src/active-codex-telemetry.test.ts +69 -6
  112. package/templates/.agents/dashboard/packages/local-runtime/src/active-codex-telemetry.ts +62 -15
  113. package/templates/.agents/dashboard/packages/local-runtime/src/manifest-runs.ts +8 -2
  114. package/templates/.agents/dashboard/packages/local-runtime/src/server-capture-projection-order.test.ts +118 -0
  115. package/templates/.agents/dashboard/packages/local-runtime/src/server-cost-run-search.test.ts +90 -0
  116. package/templates/.agents/dashboard/packages/local-runtime/src/server-cost-run-search.ts +86 -0
  117. package/templates/.agents/dashboard/packages/local-runtime/src/server-operations-routes.ts +8 -1
  118. package/templates/.agents/dashboard/packages/local-runtime/src/server-projection-loaders.ts +13 -0
  119. package/templates/.agents/dashboard/packages/local-runtime/src/server-projections.ts +30 -74
  120. package/templates/.agents/dashboard/packages/local-runtime/src/server-route-handlers.ts +42 -0
  121. package/templates/.agents/dashboard/packages/local-runtime/src/server-routes.test.ts +60 -0
  122. package/templates/.agents/dashboard/packages/local-runtime/src/server-run-projections.test.ts +12 -2
  123. package/templates/.agents/dashboard/packages/local-runtime/src/server-telemetry-command.test.ts +29 -4
  124. package/templates/.agents/dashboard/packages/local-runtime/src/server-telemetry-refresh.ts +7 -0
  125. package/templates/.agents/dashboard/packages/local-runtime/src/server.test.ts +51 -54
  126. package/templates/.agents/dashboard/packages/local-runtime/src/server.ts +37 -48
  127. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-filter-matrix.test.ts +5 -0
  128. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-invocation-page.test.ts +49 -0
  129. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-cost-invocation-page.ts +44 -1
  130. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-query-cached-cost-page.ts +11 -10
  131. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-query-summary.ts +1 -1
  132. package/templates/.agents/dashboard/packages/local-runtime/src/sqlite-work-item-index.test.ts +18 -1
  133. package/templates/.agents/dashboard/packages/local-runtime/vitest.config.ts +12 -0
  134. package/templates/.agents/dashboard/packages/ui/package.json +1 -1
  135. package/templates/.agents/dashboard/tests/e2e/package.json +1 -1
  136. package/templates/.agents/dashboard/tests/e2e/specs/cost-monitor-pagination.spec.ts +13 -6
  137. package/templates/.agents/dashboard/tests/e2e/specs/dashboard.spec.ts +1 -1
  138. package/templates/.agents/design/.zaip-managed.json +3 -3
  139. package/templates/.agents/design/apps/catalog/package.json +1 -1
  140. package/templates/.agents/design/packages/local-runtime/package.json +1 -1
  141. package/templates/.agents/requirements/autonomous-assurance.md +39 -3
  142. package/templates/.agents/requirements/cloud-hostinger-autonomy.md +1 -1
  143. package/templates/.agents/requirements/comparative-audit.md +1 -1
  144. package/templates/.agents/requirements/context-engine.md +3 -3
  145. package/templates/.agents/requirements/cost-observability.md +1 -1
  146. package/templates/.agents/requirements/dashboard-launch.md +1 -1
  147. package/templates/.agents/requirements/data-export.md +1 -1
  148. package/templates/.agents/requirements/design-system.md +1 -1
  149. package/templates/.agents/requirements/execution-engine.md +11 -2
  150. package/templates/.agents/requirements/kanban-dashboard.md +1 -1
  151. package/templates/.agents/requirements/model-orchestration.md +1 -1
  152. package/templates/.agents/requirements/public-distribution.md +1 -1
  153. package/templates/.agents/requirements/software-architecture.md +1 -1
  154. package/templates/.agents/requirements/strategic-improvements.md +1 -1
  155. package/templates/.agents/requirements/work-lifecycle.md +2 -1
  156. package/templates/.agents/requirements/zaip-strategic-95.md +1 -1
  157. package/templates/.agents/rules/cloud-hostinger-autonomy.md +1 -1
  158. package/templates/.agents/rules/production-readiness.md +24 -0
  159. package/templates/.agents/rules/run-lifecycle-closure.md +2 -1
  160. package/templates/.agents/rules/security.md +2 -2
  161. package/templates/.agents/rules/workflow.md +4 -3
  162. package/templates/.agents/runtime/VERSION +1 -1
  163. package/templates/.agents/runtime/bin/zaip.mjs +1185 -610
  164. package/templates/.agents/runtime/checksums.sha256 +6 -6
  165. package/templates/.agents/runtime/manifest.json +1 -1
  166. package/templates/.agents/runtime/pnpm-lock.sha256 +1 -1
  167. package/templates/.agents/runtime/provenance.json +5 -5
  168. package/templates/.agents/runtime/sbom.cdx.json +71 -71
  169. package/templates/.agents/skills/zaip-skill-execution/SKILL.md +4 -2
  170. package/templates/.agents/skills/zaip-skill-execution/references/controlled-release-recovery.md +44 -0
  171. package/templates/.agents/skills/zaip-skill-execution/references/runtime-governance.md +1 -1
  172. package/templates/.agents/skills/zaip-skill-github-deploy/SKILL.md +7 -1
  173. package/templates/.agents/skills/zaip-skill-github-deploy/references/release-gate-matrix.md +15 -20
  174. package/templates/.agents/skills/zaip-skill-github-deploy/scripts/deploy-contract.mjs +38 -2
  175. package/templates/.agents/skills/zaip-skill-github-deploy/scripts/deploy-contract.test.mjs +62 -4
  176. package/templates/.agents/skills/zaip-skill-github-deploy/scripts/github-deploy-contract.test.mjs +8 -6
  177. package/templates/.agents/skills/zaip-skill-github-deploy/scripts/guarded-commits.mjs +5 -3
  178. package/templates/.agents/skills/zaip-skill-lifecycle-closure/SKILL.md +7 -0
  179. package/templates/.agents/skills/zaip-skill-maintenance-enter/SKILL.md +23 -0
  180. package/templates/.agents/skills/zaip-skill-maintenance-enter/agents/openai.yaml +4 -0
  181. package/templates/.agents/skills/zaip-skill-maintenance-enter/scripts/maintenance.mjs +213 -0
  182. package/templates/.agents/skills/zaip-skill-maintenance-enter/scripts/maintenance.test.mjs +207 -0
  183. package/templates/.agents/skills/zaip-skill-maintenance-exit/SKILL.md +21 -0
  184. package/templates/.agents/skills/zaip-skill-maintenance-exit/agents/openai.yaml +4 -0
  185. package/templates/.agents/skills/zaip-skill-prd/SKILL.md +6 -0
  186. package/templates/.agents/skills/zaip-skill-release-qualification/SKILL.md +26 -0
  187. package/templates/.agents/skills/zaip-skill-release-qualification/references/local-preparation.md +46 -11
  188. package/templates/.agents/skills/zaip-skill-spec/SKILL.md +9 -0
  189. package/templates/.agents/skills/zaip-skill-testing-strategy/SKILL.md +15 -0
  190. package/templates/.agents/zaip-aios/VERSION +1 -1
  191. package/templates/.agents/zaip-aios/guides/11-observabilidade-e-custos.md +22 -0
  192. package/templates/.agents/zaip-aios/guides/14-troubleshooting.md +1 -1
  193. package/templates/.agents/zaip-aios/guides/26-qualidade-e-release.md +52 -5
  194. package/templates/template-manifest.json +210 -126
  195. package/templates/.agents/dashboard/apps/frontend/dist/assets/cost-monitor-Co2Jiuzh.js +0 -68
@@ -5,15 +5,18 @@ import { lstat, readFile, realpath } from "node:fs/promises";
5
5
  import { dirname, isAbsolute, relative, resolve } from "node:path";
6
6
  import { fileURLToPath } from "node:url";
7
7
  import { promisify } from "node:util";
8
- import { captureCliTurn, checkDestructivePatterns, configuredDeploymentBranches, gitInvocation, inside, isLiteralReadSequence, requiresShellMutationRun, reviewedContextReadPaths, reviewedReleasePaths, rewriteCliInput, selectHookExecutionWorkspace, shellSyntax, tokenizeLiteralCommand, validateInlineExecution } from "./zaip-pre-tool-support.mjs";
8
+ import { captureCliTurn, checkDestructivePatterns, configuredDeploymentBranches, configuredPermissionProfile, gitInvocation, inside, isLiteralReadSequence, parseHookEvent, readBoundedCanonicalText, requiresShellMutationRun, reviewedContextReadPaths, reviewedReleasePaths, rewriteCliInput, selectHookExecutionWorkspace, selectHookRepositoryRoot, shellSyntax, tokenizeLiteralCommand, validateInlineExecution } from "./zaip-pre-tool-support.mjs";
9
+ import { createCloudValidators } from "./zaip-pre-tool-cloud.mjs";
9
10
  const execute = promisify(execFile);
10
11
  const nativeAuthority = await import("./zaip-native-tool-authority.mjs").catch(() => null);
11
12
  const activation = await import("./zaip-hook-activation.mjs").catch(() => null);
12
13
  const installedRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..", "..");
13
14
  let repositoryRoot = installedRoot;
15
+ let principalFullAccess = false;
16
+ let principalFullNetwork = false;
14
17
  const deletionCommandPattern = /\b(?:Remove-Item|ri|rm|rmdir|rd|del|erase)\b/i;
15
18
  const allowedExplicitNetworkHosts = new Set([
16
- "127.0.0.1", "api.github.com", "github.com", "localhost", "objects.githubusercontent.com", "::1",
19
+ "127.0.0.1", "api.github.com", "github.com", "localhost", "objects.githubusercontent.com", "releases.hashicorp.com", "::1",
17
20
  ]);
18
21
  const networkClientPattern = /\b(?:curl|wget|Invoke-WebRequest|Invoke-RestMethod|iwr|irm)\b/i;
19
22
  const cloudRunnerScript = ".agents/tools/cloud-runner.mjs";
@@ -32,6 +35,10 @@ const homeReferencePattern = /(?:\$HOME\b|\$env:(?:USERPROFILE|HOMEDRIVE|HOMEPAT
32
35
  const credentialReferencePattern = /(?:^|[\\/\s"'=])(?:\.ssh|\.aws|\.azure|\.kube|\.docker|\.gnupg|\.git-credentials|\.npmrc|\.pypirc|id_(?:rsa|ed25519)|[^\\/\s"']+\.(?:pem|key|p12|pfx))(?:[\\/\s"'=]|$)|(?:^|[\\/\s"'=])\.env(?:\.[A-Za-z0-9_-]+)*(?:[\\/\s"']|$)/i;
33
36
  const sensitiveEnvironmentPattern = /(?:\$env:|%|\$)[A-Z0-9_]*(?:TOKEN|SECRET|PASSWORD|API_KEY|PRIVATE_KEY|CREDENTIAL)[A-Z0-9_]*%?/i;
34
37
  const destructivePatterns = [
38
+ {
39
+ pattern: /\b(?:Restart-Computer|Stop-Computer|Restart-Host|shutdown(?:\.exe)?|reboot|poweroff|halt|Suspend-Computer|hibernate|logoff|Win32Shutdown)\b/i,
40
+ reason: "Local computer restart, shutdown, suspension, hibernation and session termination are blocked.",
41
+ },
35
42
  {
36
43
  pattern: /\b(?:Clear-Content|shred)\b/i,
37
44
  reason: "Unbounded content destruction is blocked.",
@@ -148,7 +155,7 @@ function repositoryPath(path) {
148
155
  }
149
156
  function isGitPath(path) {
150
157
  const scoped = repositoryPath(path).toLowerCase();
151
- return scoped === ".git" || scoped.startsWith(".git/");
158
+ return scoped.split("/").includes(".git");
152
159
  }
153
160
 
154
161
  function unquote(value) {
@@ -172,7 +179,18 @@ function explicitPathCandidates(command) {
172
179
  }
173
180
  return [...candidates].map(unquote).filter(Boolean);
174
181
  }
175
- function validateExplicitFilesystemReferences(command, cwd, reviewedPaths = []) {
182
+ async function assertCanonicalLiteralPath(path, cwd = repositoryRoot) {
183
+ if (path.split(/[\\/]/).includes("..")) deny("Filesystem traversal is blocked.");
184
+ for (let cursor = resolve(cwd, path);; cursor = dirname(cursor)) {
185
+ try {
186
+ const info = await lstat(cursor);
187
+ if (info.isSymbolicLink() || (await realpath(cursor)).toLowerCase() !== cursor.toLowerCase())
188
+ deny("Filesystem links and aliases are blocked.");
189
+ } catch (error) { if (error?.code !== "ENOENT") deny("Filesystem path could not be safely verified."); }
190
+ if (dirname(cursor) === cursor) break;
191
+ }
192
+ }
193
+ async function validateExplicitFilesystemReferences(command, cwd, reviewedPaths = []) {
176
194
  const checked = reviewedPaths.reduce((text, path) => text.replaceAll(path, "REVIEWED_RELEASE_PATH"), command);
177
195
  if (homeReferencePattern.test(checked)) {
178
196
  deny("Direct user-profile path references are blocked; use repository-scoped paths.");
@@ -180,7 +198,7 @@ function validateExplicitFilesystemReferences(command, cwd, reviewedPaths = [])
180
198
  if (credentialReferencePattern.test(checked) || sensitiveEnvironmentPattern.test(checked)) {
181
199
  deny("Direct credential and secret access is blocked.");
182
200
  }
183
- if (/\b(?:Get-ChildItem|gci|dir)\s+Env:|\b(?:printenv|set)\s*$/i.test(checked) || /GetEnvironmentVariables\s*\(/i.test(checked)) {
201
+ if (/\b(?:Get-ChildItem|gci|dir|Get-Item|gi)(?:\s+-(?:LiteralPath|Path))?\s+(?:Env|Variable|Function|Alias):|\b(?:printenv|set)\s*$/i.test(checked) || /GetEnvironmentVariables\s*\(/i.test(checked)) {
184
202
  deny("Bulk environment enumeration is blocked.");
185
203
  }
186
204
  if (/(?:^|[\s"'=(,]|-[A-Za-z][A-Za-z0-9_-]*:)(?:(?:\\\\){1,2}|\/\/)[^\\/\s"']+[\\/]/.test(checked)) {
@@ -191,13 +209,16 @@ function validateExplicitFilesystemReferences(command, cwd, reviewedPaths = [])
191
209
  ? checked.replace(/(^|\s)--(?:agent-id|parent-agent-id)\s+\/root(?:\/[a-z][a-z0-9_]*_zaip_agent(?:_[a-z0-9]+)?)+(?=\s|$)/g, "$1--native-agent-id zaip-agent")
192
210
  : checked; if (/[\r\n]/.test(checked)) deny("Multiline shell commands are blocked.");
193
211
  for (const match of osPathCommand.matchAll(/(["'])(\/(?:etc|home|Users|root|var|opt|private|mnt|proc|sys|dev|tmp)(?:\/[^\r\n]*?)?)\1|(?:^|[\s=(,]|-[A-Za-z][A-Za-z0-9_-]*:)(\/(?:etc|home|Users|root|var|opt|private|mnt|proc|sys|dev|tmp)(?:\/[^\s"'\x60;,)|&]*)?)/gi)) {
194
- const path = match[2] ?? match[3], before = osPathCommand[match.index - 1] ?? "", after = osPathCommand[match.index + match[0].length] ?? "", jsonPath = /(?:^|\s)'\{"path":$/.test(osPathCommand.slice(0, match.index)) && /^}'(?:\s|$)/.test(osPathCommand.slice(match.index + match[0].length)); if (jsonPath) { if (path.includes("\\") || !isWithinRepository(resolve(path))) deny("Explicit filesystem path escapes the repository boundary."); continue; }
212
+ const path = match[2] ?? match[3], before = osPathCommand[match.index - 1] ?? "", after = osPathCommand[match.index + match[0].length] ?? "", jsonPath = /(?:^|\s)'\{"path":$/.test(osPathCommand.slice(0, match.index)) && /^}'(?:\s|$)/.test(osPathCommand.slice(match.index + match[0].length));
213
+ if (principalFullAccess) { await assertCanonicalLiteralPath(path); continue; }
214
+ if (jsonPath) { if (path.includes("\\") || !isWithinRepository(resolve(path))) deny("Explicit filesystem path escapes the repository boundary."); continue; }
195
215
  if (path.includes("\\") || !isWithinRepository(resolve(path)) || (match[2] && before && !(/\s/.test(before) || before === "=" || jsonPath)) || (after && !/\s/.test(after) && !jsonPath)) deny("Explicit operating-system paths outside the repository are blocked.");
196
216
  }
197
217
  if (process.platform === "win32" && /(?:^|[\s"'=(,])\\(?!\\)[^\s\\/"'<>|;]+(?:\\[^\s"'<>|;]+|\.[A-Za-z0-9]{1,10})/.test(checked)) {
198
218
  deny("Drive-rooted filesystem paths outside the repository are blocked.");
199
219
  }
200
220
  for (const candidate of explicitPathCandidates(checked)) {
221
+ if (principalFullAccess) { await assertCanonicalLiteralPath(candidate, cwd); continue; }
201
222
  if (process.platform !== "win32" && /^[A-Za-z]:[\\/]/.test(candidate)) deny("Explicit filesystem path escapes the repository boundary.");
202
223
  const absolute = resolve(cwd, candidate);
203
224
  if (!isWithinRepository(absolute)) {
@@ -206,15 +227,20 @@ function validateExplicitFilesystemReferences(command, cwd, reviewedPaths = [])
206
227
  }
207
228
  }
208
229
  async function validateGitPublication(command) {
209
- if (/(?:^|[;&|\r\n])\s*(?:(?:&\s*)?(?:['"]git(?:\.exe)?['"]|\(\s*['"]git(?:\.exe)?['"]\s*\))|(?:env|command)\s+git(?:\.exe)?\b)/i.test(command) ||
230
+ const tokens = tokenizeLiteralCommand(command);
231
+ if (tokens !== null) {
232
+ const launcher = tokens[0]?.toLowerCase();
233
+ if (((launcher === "env" || launcher === "command") && /^git(?:\.exe)?$/i.test(tokens[1] ?? "")) ||
234
+ (/^(?:Set-Alias|New-Alias)$/i.test(launcher ?? "") && tokens.slice(1).some(token => /^git(?:\.exe)?$/i.test(token))))
235
+ deny("Indirect Git execution is blocked; use a single literal command.");
236
+ } else if (/(?:^|[;&|\r\n])\s*(?:(?:&\s*)?(?:['"]git(?:\.exe)?['"]|\(\s*['"]git(?:\.exe)?['"]\s*\))|(?:env|command)\s+git(?:\.exe)?\b)/i.test(command) ||
210
237
  /&\s*\(\s*Get-Command\s+git(?:\.exe)?\b/i.test(command) ||
211
238
  /(?:^|[\s;&|])&\s*\$[A-Za-z_]/i.test(command) || /\b(?:Set-Alias|New-Alias)\b[^\r\n;]*\bgit\b/i.test(command)) {
212
239
  deny("Indirect Git execution is blocked; use a single literal command.");
213
240
  }
214
- if (/\(\s*git(?:\.exe)?\b/i.test(command)) {
241
+ if (tokens === null && /\(\s*git(?:\.exe)?\b/i.test(command)) {
215
242
  deny("Nested Git execution is blocked; use a single literal command.");
216
243
  }
217
- const tokens = tokenizeLiteralCommand(command);
218
244
  if (tokens === null) {
219
245
  if (/(?:^|[;&|\r\n(])\s*git(?:\.exe)?\b/i.test(command)) {
220
246
  deny("Git commands must be a single literal command; separate commands before requesting execution.");
@@ -276,6 +302,33 @@ async function validateGitPublication(command) {
276
302
  }
277
303
  }
278
304
 
305
+ function validateHashicorpRead(command) {
306
+ const tokens = tokenizeLiteralCommand(command);
307
+ if (!tokens || !/^(?:curl(?:\.exe)?|Invoke-WebRequest|Invoke-RestMethod|iwr|irm)$/i.test(tokens[0]))
308
+ deny("HashiCorp releases require a literal supported read command.");
309
+ let urlCount = 0;
310
+ for (let i = 1; i < tokens.length; i++) {
311
+ const token = tokens[i];
312
+ if (/^https:\/\//i.test(token)) {
313
+ const url = new URL(token);
314
+ if (url.hostname !== "releases.hashicorp.com" || url.username || url.password || url.port)
315
+ deny("HashiCorp reads require one exact HTTPS destination.");
316
+ urlCount++; continue;
317
+ }
318
+ if (["-I", "--head", "-s", "-S", "-sS", "--silent", "--show-error", "--fail", "-q", "--disable"].includes(token)) continue;
319
+ const namedMethod = /^(?:--request|--method|-Method)(?:=(.*))?$/i.exec(token);
320
+ const curlMethod = /^-X(.*)$/.exec(token);
321
+ if (namedMethod || curlMethod) {
322
+ const value = namedMethod ? (namedMethod[1] ?? tokens[++i]) : (curlMethod[1] || tokens[++i]);
323
+ if (!/^(?:GET|HEAD)$/i.test(value ?? "")) deny("HashiCorp releases permit only GET or HEAD requests.");
324
+ continue;
325
+ }
326
+ if (/^(?:-Uri|--url)$/i.test(token) && /^https:\/\//i.test(tokens[i + 1] ?? "")) continue;
327
+ deny("Unsupported option in the bounded HashiCorp read command.");
328
+ }
329
+ if (urlCount !== 1) deny("HashiCorp reads require exactly one HTTPS URL.");
330
+ }
331
+
279
332
  function validateExplicitNetwork(command) {
280
333
  if (/(?:^|[;&|]\s*)\b(?:ssh|scp|sftp|rsync|ftp|telnet|nc|ncat)\b/i.test(command)) {
281
334
  deny("Direct remote-shell and file-transfer clients are blocked.");
@@ -289,174 +342,24 @@ function validateExplicitNetwork(command) {
289
342
  deny("Network clients require an explicit allowlisted URL.");
290
343
  }
291
344
  for (const match of urls) {
292
- let host;
345
+ let target;
293
346
  try {
294
- host = new URL("https://" + match[1]).hostname.toLowerCase();
347
+ target = new URL(match[0]);
295
348
  } catch {
296
349
  deny("Malformed network target was rejected.");
297
350
  }
298
- if (!allowedExplicitNetworkHosts.has(host)) {
351
+ const host = target.hostname.toLowerCase();
352
+ if (!principalFullNetwork && !allowedExplicitNetworkHosts.has(host)) {
299
353
  deny("Explicit network target is outside the ZAIP allowlist.");
300
354
  }
355
+ if (host === "releases.hashicorp.com") {
356
+ if (target.protocol !== "https:" || target.username || target.password)
357
+ deny("HashiCorp releases require an exact HTTPS read target.");
358
+ validateHashicorpRead(command);
359
+ }
301
360
  }
302
361
  }
303
362
 
304
- function optionMap(tokens) {
305
- if (tokens.length % 2 !== 0) return null;
306
- const values = new Map();
307
- for (let index = 0; index < tokens.length; index += 2) {
308
- const key = tokens[index];
309
- const value = tokens[index + 1];
310
- if (!key.startsWith("--") || value.startsWith("--") || values.has(key)) return null;
311
- values.set(key, value);
312
- }
313
- return values;
314
- }
315
-
316
- function requireExactOptions(options, allowed, required) {
317
- if ([...options.keys()].some((key) => !allowed.has(key))) return false;
318
- return [...required].every((key) => typeof options.get(key) === "string" && options.get(key).length > 0);
319
- }
320
-
321
- function isStringArray(value) {
322
- return Array.isArray(value) && value.every((item) => typeof item === "string" && item.length > 0);
323
- }
324
-
325
- async function readCloudContract(options) {
326
- if (options.get("--contract") !== cloudContractPath) {
327
- deny("Cloud runner must use the canonical autonomous-cloud contract path.");
328
- }
329
- const absolute = resolve(repositoryRoot, cloudContractPath);
330
- if (!isWithinRepository(absolute)) deny("Cloud runner contract escapes the repository boundary.");
331
- let contract;
332
- try {
333
- contract = JSON.parse(await readFile(absolute, "utf8"));
334
- } catch {
335
- deny("Cloud runner contract is absent or malformed.");
336
- }
337
- if (
338
- contract === null ||
339
- typeof contract !== "object" ||
340
- contract.version !== 1 ||
341
- contract.profile !== "autonomous-cloud" ||
342
- contract.cloudflare === null ||
343
- typeof contract.cloudflare !== "object" ||
344
- contract.hostinger === null ||
345
- typeof contract.hostinger !== "object"
346
- ) {
347
- deny("Cloud runner contract has no valid autonomous-cloud profile.");
348
- }
349
- return contract;
350
- }
351
-
352
- function cloudflareTargetMatches(contract, options, operation) {
353
- const cloudflare = contract.cloudflare;
354
- const record = options.get("--dns-record");
355
- const managed = isStringArray(cloudflare.dns_records) && cloudflare.dns_records.includes(record);
356
- const direct = cloudflare.direct_dns_records !== null && typeof cloudflare.direct_dns_records === "object" && Object.hasOwn(cloudflare.direct_dns_records, record);
357
- return (
358
- typeof cloudflare.account_id === "string" &&
359
- typeof cloudflare.zone_id === "string" &&
360
- isStringArray(cloudflare.allowed_operations) &&
361
- cloudflare.account_id === options.get("--account-id") &&
362
- cloudflare.zone_id === options.get("--zone-id") &&
363
- cloudflare.allowed_operations.includes(operation) &&
364
- (operation === "cloudflare:dns:read" ? managed || direct : direct && !managed)
365
- );
366
- }
367
-
368
- function hostingerTargetMatches(contract, options, operation) {
369
- const hostinger = contract.hostinger;
370
- const role = options.get("--vps-role");
371
- if (
372
- typeof hostinger.account_id !== "string" ||
373
- !isStringArray(hostinger.allowed_operations) ||
374
- hostinger.account_id !== options.get("--account-id") ||
375
- !hostinger.allowed_operations.includes(operation) ||
376
- (role !== "edge" && role !== "application") ||
377
- hostinger.vps === null ||
378
- typeof hostinger.vps !== "object"
379
- ) {
380
- return false;
381
- }
382
- const target = hostinger.vps[role];
383
- if (target === null || typeof target !== "object") return false;
384
-
385
- if (typeof target.id !== "string" || target.id !== options.get("--vps-id")) return false;
386
- if (operation.startsWith("hostinger:ssh:")) {
387
- return typeof target.host_key_sha256 === "string" && target.host_key_sha256 === options.get("--host-key-sha256");
388
- }
389
- return true;
390
- }
391
-
392
- function terraformTargetMatches(contract, options, operation) {
393
- const terraform = contract.terraform;
394
- if (
395
- terraform === null ||
396
- typeof terraform !== "object" ||
397
- !isStringArray(terraform.allowed_operations) ||
398
- !terraform.allowed_operations.includes(operation)
399
- ) return false;
400
- if (operation === "terraform:plan") return terraform.working_directory === options.get("--terraform-directory");
401
- if (operation === "terraform:apply") {
402
- const plan = terraform.plan;
403
- return plan !== null && typeof plan === "object" && typeof plan.path === "string" && typeof plan.sha256 === "string" && typeof plan.policy_result_sha256 === "string" && plan.path === options.get("--plan-path") && plan.sha256 === options.get("--plan-sha256") && plan.policy_result_sha256 === options.get("--policy-result-sha256");
404
- }
405
- return terraform.import_targets !== null && typeof terraform.import_targets === "object" && terraform.import_targets[options.get("--resource-address")] === options.get("--resource-id");
406
- }
407
-
408
- function validateDirectCloudProviderMutation(command) {
409
- if (command.includes(cloudRunnerScript)) return;
410
- if (/\bterraform(?:\.exe)?\s+(?:apply|destroy|import)\b/i.test(command)) deny("Direct Terraform mutation is blocked; use the contract-bound cloud runner.");
411
- if (/\b(?:wrangler|cloudflare|hostinger)(?:\.exe)?\b/i.test(command)) deny("Direct Cloudflare or Hostinger commands are blocked; use the contract-bound cloud runner.");
412
- }
413
-
414
- async function validateCloudRunner(command) {
415
- const tokens = tokenizeLiteralCommand(command);
416
- if (tokens === null || tokens.length < 2 || !/^(?:node|node\.exe)$/i.test(tokens[0]) || tokens[1] !== cloudRunnerScript) {
417
- return false;
418
- }
419
- const options = optionMap(tokens.slice(2));
420
- if (options === null) deny("Cloud runner requires literal, named arguments.");
421
- const operation = options.get("--operation");
422
- if (operation === "contract:derive") {
423
- if (!requireExactOptions(options, new Set(["--operation", "--credentials-source"]), new Set(["--operation", "--credentials-source"]))) {
424
- deny("Cloud contract derivation has unrecognized arguments.");
425
- }
426
- if (options.get("--credentials-source") !== "root") {
427
- deny("Cloud contract derivation requires the selected root credential source.");
428
- }
429
- return true;
430
- }
431
- const provider = cloudOperationProviders.get(operation);
432
- if (provider === undefined) deny("Cloud runner operation is unrecognized or destructive.");
433
- const common = new Set(["--contract", "--operation", "--provider", "--account-id", "--credentials-source", "--zone-id", "--dns-record", "--vps-role", "--vps-id", "--host-key-sha256", "--terraform-directory", "--plan-path", "--plan-sha256", "--policy-result-sha256", "--resource-address", "--resource-id"]);
434
- const baseRequired = provider === "terraform"
435
- ? new Set(["--contract", "--operation", "--credentials-source"])
436
- : new Set(["--contract", "--operation", "--provider", "--account-id", "--credentials-source"]);
437
- if (!requireExactOptions(options, common, baseRequired)) {
438
- deny("Cloud runner invocation has unrecognized arguments.");
439
- }
440
- if ((provider !== "terraform" && options.get("--provider") !== provider) || !["root", "agents"].includes(options.get("--credentials-source"))) {
441
- deny("Cloud runner provider or credential source is outside the approved contract.");
442
- }
443
- const contract = await readCloudContract(options);
444
- const valid = provider === "cloudflare"
445
- ? requireExactOptions(options, common, new Set(["--contract", "--operation", "--provider", "--account-id", "--credentials-source", "--zone-id", "--dns-record"])) && cloudflareTargetMatches(contract, options, operation)
446
- : provider === "hostinger"
447
- ? requireExactOptions(options, common, new Set(["--contract", "--operation", "--provider", "--account-id", "--credentials-source", "--vps-role"])) && hostingerTargetMatches(contract, options, operation)
448
- : requireExactOptions(
449
- options,
450
- common,
451
- operation === "terraform:plan"
452
- ? new Set(["--contract", "--operation", "--credentials-source", "--terraform-directory"])
453
- : operation === "terraform:apply"
454
- ? new Set(["--contract", "--operation", "--credentials-source", "--plan-path", "--plan-sha256", "--policy-result-sha256"])
455
- : new Set(["--contract", "--operation", "--credentials-source", "--resource-address", "--resource-id"]),
456
- ) && terraformTargetMatches(contract, options, operation);
457
- if (!valid) deny("Cloud runner target, operation, or host identity is outside the approved contract.");
458
- return true;
459
- }
460
363
 
461
364
  ${CODEX_STALE_GIT_LOCK_HELPER}
462
365
 
@@ -533,6 +436,10 @@ async function validateShellDeletion(command, cwd) {
533
436
  }
534
437
 
535
438
  async function validatePatch(command) {
439
+ for (const match of command.matchAll(/^\*\*\* (?:Add|Update|Delete) File:\s*(.+)$|^\*\*\* Move to:\s*(.+)$/gm)) {
440
+ const rawPath = (match[1] ?? match[2]).trim().replace(/^["']|["']$/g, "");
441
+ if (principalFullAccess) await assertCanonicalLiteralPath(rawPath);
442
+ }
536
443
  const paths = patchPaths(command);
537
444
  const lifecycleRecordTouched = paths.some((path) =>
538
445
  /^\.agents[\\/]records[\\/](?:prds|specs|tasks)[\\/]/i.test(path));
@@ -541,8 +448,10 @@ async function validatePatch(command) {
541
448
  }
542
449
  for (const path of paths) {
543
450
  const absolute = resolve(repositoryRoot, path);
544
- if (!isWithinRepository(absolute)) deny("Patch path escapes the repository boundary.");
545
- if (!await nativeAuthority?.isSafePatchPath(absolute, repositoryRoot)) deny("Patch path traverses a link or could not be safely verified.");
451
+ if (credentialReferencePattern.test(absolute)) deny("Direct credential and secret access is blocked.");
452
+ if (!isWithinRepository(absolute) && !principalFullAccess) deny("Patch path escapes the repository boundary.");
453
+ if (principalFullAccess) await assertCanonicalLiteralPath(absolute);
454
+ else if (!await nativeAuthority?.isSafePatchPath(absolute, repositoryRoot)) deny("Patch path traverses a link or could not be safely verified.");
546
455
  if (repositoryPath(absolute) === "") deny("Patching the repository root is blocked.");
547
456
  if (protectedTaskWorkspacePath(repositoryPath(absolute))) deny("Task workspace claims are managed only by the canonical CLI.");
548
457
  if (isGitPath(absolute)) {
@@ -564,13 +473,25 @@ async function requiresOrchestratorRun(event, command) {
564
473
  return paths.length === 0 || paths.some((path) => !lifecycleExemptPathPattern.test(path));
565
474
  }
566
475
  if (event.tool_name === "Edit" || event.tool_name === "Write") {
567
- if (!await nativeAuthority?.isSafePatchPath(resolve(event.cwd ?? repositoryRoot, command), repositoryRoot)) deny("File write path traverses a link or could not be safely verified.");
476
+ if (principalFullAccess) await assertCanonicalLiteralPath(command, event.cwd ?? repositoryRoot);
477
+ else if (!await nativeAuthority?.isSafePatchPath(resolve(event.cwd ?? repositoryRoot, command), repositoryRoot)) deny("File write path traverses a link or could not be safely verified.");
568
478
  const path = repositoryPath(resolve(event.cwd ?? repositoryRoot, command));
569
479
  if (protectedTaskWorkspacePath(path)) deny("Task workspace claims are managed only by the canonical CLI.");
570
480
  return !lifecycleExemptPathPattern.test(path);
571
481
  }
572
482
  // Only single literal writes claim exemptions; compound commands remain gated.
573
483
  const tokens = tokenizeLiteralCommand(command);
484
+ // The public CLI validates the immutable post-publication receipt, observed
485
+ // invocation, completed maintenance Gui and exact target before any write.
486
+ // Its first call necessarily follows the maintenance root's terminal Stop.
487
+ if (tokens && !Object.values(shellSyntax(command)).some(Boolean) &&
488
+ /^(?:node|node\.exe)$/i.test(tokens[0] ?? "") &&
489
+ /^\.agents[\\/]runtime[\\/]bin[\\/]zaip\.mjs$/.test(tokens[1] ?? "") &&
490
+ tokens[2] === "task" && tokens[3] === "resume-post-publication" &&
491
+ /^TASK-[A-Z0-9-]+$/.test(tokens[4] ?? "") && tokens[5] === "--receipt" &&
492
+ /^\.local[\\/]recovery[\\/]post-publication[\\/][a-f0-9]{64}\.json$/.test(tokens[6] ?? "") &&
493
+ (tokens.length === 7 || (tokens.length === 8 && tokens[7] === "--json"))) return false;
494
+ if (tokens && tokens[2] === "task" && tokens[3] === "resume-post-publication") return true;
574
495
  if (tokens && /^(?:Set-Content|Add-Content|Out-File|New-Item|Copy-Item|Move-Item|Remove-Item|Rename-Item|sc|ac|ni|cp|mv|mi|ri|rm|git(?:\.exe)?)$/i.test(tokens[0] ?? "") &&
575
496
  tokens.some(token => token.toLowerCase().includes("task-workspaces") &&
576
497
  protectedTaskWorkspacePath(repositoryPath(resolve(event.cwd ?? repositoryRoot, token)))))
@@ -585,40 +506,81 @@ async function requiresOrchestratorRun(event, command) {
585
506
  }
586
507
  if (valid && options.has("-literalpath") && options.has("-value")) {
587
508
  const path = repositoryPath(resolve(event.cwd ?? repositoryRoot, options.get("-literalpath")));
588
- if (!await nativeAuthority?.isSafePatchPath(resolve(repositoryRoot, path), repositoryRoot)) deny("Shell write path traverses a link or could not be safely verified.");
509
+ if (principalFullAccess) await assertCanonicalLiteralPath(options.get("-literalpath"), event.cwd ?? repositoryRoot);
510
+ else if (!await nativeAuthority?.isSafePatchPath(resolve(repositoryRoot, path), repositoryRoot)) deny("Shell write path traverses a link or could not be safely verified.");
589
511
  if (protectedTaskWorkspacePath(path)) deny("Task workspace claims are managed only by the canonical CLI.");
590
512
  if (lifecycleExemptPathPattern.test(path)) return false;
591
513
  }
592
514
  }
515
+ // The external Node is only a literal canonical validation command when the
516
+ // native authority has verified this workspace's exact opt-in executable.
517
+ if (tokens && isAbsolute(tokens[0]) &&
518
+ /^\.agents[\\/]runtime[\\/]bin[\\/]zaip\.mjs$/.test(tokens[1] ?? "") &&
519
+ tokens[2] === "validation" && ["execute", "batch"].includes(tokens[3]) &&
520
+ !Object.values(shellSyntax(command)).some(Boolean)) {
521
+ let optIn;
522
+ try { optIn = JSON.parse(await readFile(resolve(repositoryRoot, ".local/validation-runtime.json"), "utf8")); }
523
+ catch { optIn = null; }
524
+ const reviewed = await (nativeAuthority?.reviewedValidationRuntimePaths?.(command, tokenizeLiteralCommand, repositoryRoot) ?? []);
525
+ if (typeof optIn?.executable === "string" &&
526
+ tokens[0].replaceAll("/", "\\").toLowerCase() === optIn.executable.replaceAll("/", "\\").toLowerCase() &&
527
+ reviewed.length === 1 && reviewed[0] === tokens[0]) return false;
528
+ }
593
529
  return requiresShellMutationRun(command, tokens, event.cwd ?? repositoryRoot, repositoryRoot);
594
530
  }
595
531
 
596
- async function boundedCanonicalText(path, maximumBytes, authorityRoot = repositoryRoot) {
597
- const info = await lstat(path);
598
- if (!info.isFile() || info.isSymbolicLink() || info.size > maximumBytes) throw new Error("Unsafe Run lookup file");
599
- const physical = await realpath(path);
600
- const identity = value => process.platform === "win32" ? value.toLowerCase() : value;
601
- if (!inside(physical, authorityRoot) || identity(physical) !== identity(resolve(path))) throw new Error("Run lookup must not follow links");
602
- return readFile(path, "utf8");
603
- }
532
+ const boundedCanonicalText = (path, maximumBytes, authorityRoot = repositoryRoot) => readBoundedCanonicalText(path, maximumBytes, authorityRoot);
604
533
 
605
534
  let raw = "";
606
535
  for await (const chunk of process.stdin) raw += chunk;
607
536
 
608
537
  let event;
609
- try {
610
- event = JSON.parse(raw);
611
- } catch {
612
- deny("Malformed hook input was rejected.");
613
- }
538
+ event = parseHookEvent(raw, deny);
614
539
 
615
540
  const toolInput = event?.tool_input;
616
- try { const scope = await selectHookExecutionWorkspace(installedRoot, event); repositoryRoot = scope.repositoryRoot; event = { ...event, cwd: scope.cwd }; }
541
+ const candidateWorkdir = typeof toolInput?.workdir === "string" && isAbsolute(toolInput.workdir) ? toolInput.workdir : event.cwd;
542
+ try { repositoryRoot = await selectHookRepositoryRoot(installedRoot, candidateWorkdir); }
543
+ catch { repositoryRoot = installedRoot; }
544
+ if (typeof activation?.hookPolicies !== "function") {
545
+ let projectConfig;
546
+ try {
547
+ projectConfig = await readBoundedCanonicalText(resolve(repositoryRoot, ".agents/config/project.yaml"), 65536, repositoryRoot);
548
+ } catch (error) {
549
+ if (error?.code === "ENOENT") { writeSync(1, "{}\n"); process.exit(0); }
550
+ deny("ZAIP project policy cannot be verified without hook activation.");
551
+ }
552
+ const sections = [...projectConfig.matchAll(/^zaip:([^\r\n]*)$/gm)];
553
+ const inlineDisabled = sections.length === 1 && /^\s*\{\s*enabled:\s*false\s*\}(?:\s*#.*)?$/.test(sections[0][1]);
554
+ const following = sections.length === 1 ? projectConfig.slice(sections[0].index + sections[0][0].length).split(/(?=^[^\s#])/m)[0] : "";
555
+ const enabledEntries = following.match(/^\s+enabled:/gm) ?? [];
556
+ const blockDisabled = sections.length === 1 && /^\s*(?:#.*)?$/.test(sections[0][1]) && enabledEntries.length === 1 && /^\s+enabled:\s*false(?:\s*#.*)?$/m.test(following);
557
+ if (inlineDisabled || blockDisabled) { writeSync(1, "{}\n"); process.exit(0); }
558
+ deny("ZAIP hook activation module is unavailable for an enabled or invalid project policy.");
559
+ }
560
+ const hookPolicy = await activation.hookPolicies(repositoryRoot, (path, maximumBytes) => readBoundedCanonicalText(path, maximumBytes, repositoryRoot));
561
+ activation?.recordPolicyDiagnostic?.(repositoryRoot, hookPolicy);
562
+ if (hookPolicy.tracking) activation?.recordHookObservation?.(repositoryRoot, event, hookPolicy.governance);
563
+ if (!hookPolicy.governance) {
564
+ if (hookPolicy.diagnostic && !["config_missing", "policy_missing"].includes(hookPolicy.diagnostic))
565
+ deny("ZAIP project policy is invalid or unavailable; restore the canonical activation and project configuration.");
566
+ writeSync(1, "{}\n");
567
+ process.exit(0);
568
+ }
569
+ const configuredPolicy = await configuredPermissionProfile(repositoryRoot);
570
+ const principal = [event.agent_id, event.agent_type, event.agent_path, event.parent_agent_id].every(value => value === undefined) && event.permission_mode !== "plan";
571
+ principalFullAccess = principal && configuredPolicy.filesystem;
572
+ principalFullNetwork = principal && configuredPolicy.network;
573
+ try { const scope = await selectHookExecutionWorkspace(installedRoot, event, principalFullAccess); repositoryRoot = scope.repositoryRoot; event = { ...event, cwd: scope.cwd }; }
617
574
  catch (error) { deny(error.message); }
618
- if (!isWithinRepository(event.cwd)) {
575
+ if (credentialReferencePattern.test(event.cwd)) deny("Direct credential and secret access is blocked.");
576
+ if (!principalFullAccess && !isWithinRepository(event.cwd)) {
619
577
  deny("Tool execution outside the ZAIP-AIOS repository is blocked.");
620
578
  }
621
579
 
580
+ const { validateCloudRunner, validateDirectCloudProviderMutation } = createCloudValidators({
581
+ tokenizeLiteralCommand, deny, repositoryRoot, isWithinRepository, cloudRunnerScript, cloudContractPath, cloudOperationProviders,
582
+ });
583
+
622
584
  const commandFromCmd = toolInput?.cmd;
623
585
  const commandFromCommand = toolInput?.command;
624
586
  if (typeof commandFromCmd === "string" && typeof commandFromCommand === "string" && commandFromCmd !== commandFromCommand) {
@@ -627,7 +589,7 @@ if (typeof commandFromCmd === "string" && typeof commandFromCommand === "string"
627
589
  const command = typeof commandFromCmd === "string" ? commandFromCmd : commandFromCommand;
628
590
  if (typeof command !== "string") deny("A matched tool call without a command was rejected.");
629
591
 
630
- if (await requiresOrchestratorRun(event, command) && !(await activation?.isZaipDisabled?.(repositoryRoot, boundedCanonicalText))) {
592
+ if (await requiresOrchestratorRun(event, command)) {
631
593
  const directRun = await nativeAuthority?.hasCurrentTaskOrchestratorRun(event, installedRoot, boundedCanonicalText);
632
594
  const primaryRead = (path, maximumBytes) => boundedCanonicalText(path, maximumBytes, installedRoot);
633
595
  const delegated = directRun ? false : await nativeAuthority?.hasNativeDelegatedPatch(event, repositoryRoot, primaryRead, installedRoot);
@@ -644,9 +606,9 @@ if (event.tool_name === "apply_patch") await validatePatch(command);
644
606
  else {
645
607
  const reviewedPaths = [
646
608
  ...await reviewedReleasePaths(command, event.cwd, repositoryRoot, credentialReferencePattern, deny),
647
- ...await reviewedContextReadPaths(command, credentialReferencePattern), ...await (nativeAuthority?.reviewedValidationRuntimePaths?.(command, tokenizeLiteralCommand) ?? []),
609
+ ...await reviewedContextReadPaths(command, credentialReferencePattern, installedRoot), ...await (nativeAuthority?.reviewedValidationRuntimePaths?.(command, tokenizeLiteralCommand, repositoryRoot) ?? []),
648
610
  ];
649
- validateExplicitFilesystemReferences(command, event.cwd, reviewedPaths);
611
+ await validateExplicitFilesystemReferences(command, event.cwd, reviewedPaths);
650
612
  validateInlineExecution(command, deny);
651
613
  if (!isLiteralReadSequence(command)) {
652
614
  await validateGitPublication(command);
@@ -1 +1 @@
1
- {"version":3,"file":"codex-pre-tool-hook-template.js","sourceRoot":"","sources":["../../src/services/codex-pre-tool-hook-template.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,2BAA2B,EAAE,MAAM,oCAAoC,CAAC;AAEjF,MAAM,CAAC,MAAM,mBAAmB,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA2c3C,2BAA2B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0M5B,CAAC"}
1
+ {"version":3,"file":"codex-pre-tool-hook-template.js","sourceRoot":"","sources":["../../src/services/codex-pre-tool-hook-template.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,2BAA2B,EAAE,MAAM,oCAAoC,CAAC;AAEjF,MAAM,CAAC,MAAM,mBAAmB,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA0W3C,2BAA2B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqQ5B,CAAC"}