@aiwg/cli 2026.10.5 → 2026.10.7

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 (263) hide show
  1. package/agentic/code/addons/composition-engine/lib/runtime.mjs +1401 -0
  2. package/agentic/code/addons/composition-engine/lib/validator.mjs +513 -0
  3. package/agentic/code/addons/composition-engine/schemas/flow-graph.schema.json +372 -0
  4. package/agentic/code/providers/model-capabilities.v1.json +74 -50
  5. package/bin/aiwg.mjs +7 -3
  6. package/dist/src/a2a/agent-card.js +11 -0
  7. package/dist/src/api/index.d.ts +1 -0
  8. package/dist/src/api/index.js +1 -0
  9. package/dist/src/artifacts/browser-export.js +1 -0
  10. package/dist/src/artifacts/citation-parser.js +8 -1
  11. package/dist/src/artifacts/cli.js +57 -6
  12. package/dist/src/artifacts/fortemi-core-query-adapter.js +2 -0
  13. package/dist/src/artifacts/fortemi-core-sync.js +26 -8
  14. package/dist/src/artifacts/fortemi-shard-contract/2.2.0/contract.json +103 -0
  15. package/dist/src/artifacts/fortemi-shard-contract/2.2.0/full-v1-embedding-config.schema.json +212 -0
  16. package/dist/src/artifacts/fortemi-shard-contract/2.2.0/full-v1-embedding-set.schema.json +240 -0
  17. package/dist/src/artifacts/fortemi-shard-contract/2.2.0/full-v1-manifest.schema.json +451 -0
  18. package/dist/src/artifacts/fortemi-shard-export.js +284 -0
  19. package/dist/src/artifacts/graph-query.js +18 -8
  20. package/dist/src/artifacts/index-adapters.js +339 -0
  21. package/dist/src/artifacts/index-builder.js +143 -14
  22. package/dist/src/artifacts/index-files.js +15 -4
  23. package/dist/src/artifacts/index-preparation.js +2 -0
  24. package/dist/src/artifacts/index-reader.js +44 -0
  25. package/dist/src/artifacts/index-refresh.js +114 -55
  26. package/dist/src/artifacts/index-status.js +20 -5
  27. package/dist/src/artifacts/memory-maintenance.js +503 -0
  28. package/dist/src/artifacts/prebuilt-relations.js +3 -1
  29. package/dist/src/artifacts/query-engine.js +263 -108
  30. package/dist/src/artifacts/relations-builder.js +2 -2
  31. package/dist/src/artifacts/source-graph.js +2 -2
  32. package/dist/src/artifacts/types.js +9 -1
  33. package/dist/src/auth/client.js +5 -1
  34. package/dist/src/auth/credential-store.js +5 -1
  35. package/dist/src/auth/sensitive-input.js +151 -0
  36. package/dist/src/channel/manager.mjs +3 -0
  37. package/dist/src/cli/handlers/agentcard.js +47 -25
  38. package/dist/src/cli/handlers/auth.js +22 -2
  39. package/dist/src/cli/handlers/decision.js +22 -3
  40. package/dist/src/cli/handlers/effect.js +4 -84
  41. package/dist/src/cli/handlers/guided-setup.js +470 -0
  42. package/dist/src/cli/handlers/help.js +2 -2
  43. package/dist/src/cli/handlers/housekeeper.js +1110 -0
  44. package/dist/src/cli/handlers/index.js +4 -2
  45. package/dist/src/cli/handlers/init.js +41 -6
  46. package/dist/src/cli/handlers/install.js +6 -0
  47. package/dist/src/cli/handlers/local-executor.js +5 -3
  48. package/dist/src/cli/handlers/marketplace.js +15 -4
  49. package/dist/src/cli/handlers/mc.js +509 -53
  50. package/dist/src/cli/handlers/models.js +32 -2
  51. package/dist/src/cli/handlers/ralph-launcher.js +76 -4
  52. package/dist/src/cli/handlers/rules.js +1 -1
  53. package/dist/src/cli/handlers/script-runner.js +73 -2
  54. package/dist/src/cli/handlers/serve.js +5 -3
  55. package/dist/src/cli/handlers/sessions.js +1 -0
  56. package/dist/src/cli/handlers/setup.js +432 -1
  57. package/dist/src/cli/handlers/skill-lint.js +1 -1
  58. package/dist/src/cli/handlers/subcommands.js +118 -8
  59. package/dist/src/cli/handlers/use.js +335 -107
  60. package/dist/src/cli/handlers/workspace.js +3 -1
  61. package/dist/src/cli/prompt-utils.js +81 -0
  62. package/dist/src/cli/router.js +16 -1
  63. package/dist/src/config/aiwg-config.js +42 -0
  64. package/dist/src/config/cli.js +7 -3
  65. package/dist/src/config/decision-config.js +14 -1
  66. package/dist/src/config/gitignore.js +4 -0
  67. package/dist/src/config/native-sandbox-warnings.js +25 -0
  68. package/dist/src/config/workspace.js +2 -1
  69. package/dist/src/contributors/validation.js +6 -5
  70. package/dist/src/decision/adapters/jev.js +20 -31
  71. package/dist/src/decision/admission.js +7 -3
  72. package/dist/src/decision/ask.js +6 -1
  73. package/dist/src/decision/benchmark.js +482 -0
  74. package/dist/src/decision/ledger.js +22 -3
  75. package/dist/src/decision/pricing.js +147 -0
  76. package/dist/src/decision/qualification/gate-evidence.js +23 -18
  77. package/dist/src/decision/qualification/load-staged.js +576 -0
  78. package/dist/src/decision/qualification/load.js +109 -33
  79. package/dist/src/decision/qualification/runner.js +12 -5
  80. package/dist/src/decision/review/service.js +6 -4
  81. package/dist/src/decision/review/validate.js +23 -2
  82. package/dist/src/decision/savings.js +8 -3
  83. package/dist/src/discovery/index.js +492 -0
  84. package/dist/src/discovery/interview.js +238 -0
  85. package/dist/src/discovery/plan-apply.js +567 -0
  86. package/dist/src/discovery/plan-store.js +171 -0
  87. package/dist/src/discovery/plan-types.js +2 -0
  88. package/dist/src/discovery/plan-validation.js +139 -0
  89. package/dist/src/discovery/types.js +2 -0
  90. package/dist/src/effects/host.js +91 -0
  91. package/dist/src/extensions/commands/definitions.js +49 -9
  92. package/dist/src/extensions/manifest.js +71 -44
  93. package/dist/src/extensions/project-local-discovery.js +1 -1
  94. package/dist/src/extensions/project-local-gitignore.js +2 -0
  95. package/dist/src/extensions/project-quickref.js +18 -17
  96. package/dist/src/extensions/validation.js +32 -29
  97. package/dist/src/housekeeper/daemon-config.js +148 -0
  98. package/dist/src/housekeeper/ipc.js +84 -0
  99. package/dist/src/housekeeper/lifecycle.js +148 -0
  100. package/dist/src/housekeeper/platform.js +35 -0
  101. package/dist/src/housekeeper/status.js +70 -0
  102. package/dist/src/housekeeper/units.js +114 -0
  103. package/dist/src/issues/cli.js +16 -5
  104. package/dist/src/issues/live.js +44 -12
  105. package/dist/src/jobs/flow.js +22 -21
  106. package/dist/src/lint/rule-activation.js +20 -5
  107. package/dist/src/lint/runner.js +1 -1
  108. package/dist/src/marketplace/exchange.js +18 -6
  109. package/dist/src/marketplace/provenance.js +3 -2
  110. package/dist/src/mcp/adapters/codex-runtime.js +46 -76
  111. package/dist/src/mcp/cli.mjs +22 -96
  112. package/dist/src/mcp/config-file.mjs +6 -2
  113. package/dist/src/mcp/config-layers.mjs +23 -7
  114. package/dist/src/mcp/grok-build-config.mjs +2 -1
  115. package/dist/src/mcp/muse-native-config.mjs +2 -1
  116. package/dist/src/mcp/profiles.js +18 -31
  117. package/dist/src/mcp/profiles.mjs +18 -32
  118. package/dist/src/mcp/registry.js +7 -27
  119. package/dist/src/mcp/registry.mjs +7 -28
  120. package/dist/src/mcp/server.mjs +34 -1
  121. package/dist/src/mcp/tools/agentic-sandbox.mjs +8 -7
  122. package/dist/src/mcp/tools/command-run.mjs +3 -2
  123. package/dist/src/mcp/tools/decision.mjs +49 -48
  124. package/dist/src/mcp/tools/discovery.mjs +46 -21
  125. package/dist/src/mcp/tools/orchestration.mjs +22 -22
  126. package/dist/src/mcp/tools/subsystems.mjs +9 -8
  127. package/dist/src/mcp/tools/worker-invoke.mjs +30 -6
  128. package/dist/src/mcp/tools/writer-profiles.mjs +1 -1
  129. package/dist/src/memory/audit.js +719 -0
  130. package/dist/src/memory/canonical-context.js +392 -13
  131. package/dist/src/memory/canonical-maintenance.js +95 -0
  132. package/dist/src/memory/cli-result.js +22 -0
  133. package/dist/src/memory/cli.js +176 -0
  134. package/dist/src/memory/context-pack.js +57 -6
  135. package/dist/src/memory/cost.js +512 -0
  136. package/dist/src/memory/firewall-classification.d.mts +7 -0
  137. package/dist/src/memory/firewall-classification.mjs +32 -0
  138. package/dist/src/memory/index.js +8 -0
  139. package/dist/src/memory/line-maintenance.js +68 -0
  140. package/dist/src/memory/maintenance-cli.js +286 -0
  141. package/dist/src/memory/maintenance-contract.js +136 -0
  142. package/dist/src/memory/maintenance-index.js +18 -0
  143. package/dist/src/memory/maintenance-owner.js +2 -0
  144. package/dist/src/memory/maintenance-record.js +138 -0
  145. package/dist/src/memory/maintenance.js +254 -0
  146. package/dist/src/memory/project-registry.js +2 -1
  147. package/dist/src/memory/provider-memory-adapter.js +154 -0
  148. package/dist/src/memory/recommend-cli.js +83 -0
  149. package/dist/src/memory/recommend.js +269 -0
  150. package/dist/src/memory/scheduled.js +81 -0
  151. package/dist/src/memory/taxonomy.js +44 -0
  152. package/dist/src/mission-protocol/codecs.js +19 -9
  153. package/dist/src/mission-protocol/worker-status.js +91 -1
  154. package/dist/src/models/config-loader.js +11 -1
  155. package/dist/src/models/model-capabilities.v1.json +74 -50
  156. package/dist/src/models/provider-policy.js +125 -62
  157. package/dist/src/models/resolver.js +45 -8
  158. package/dist/src/models/worker-installation.d.mts +8 -1
  159. package/dist/src/models/worker-installation.mjs +231 -38
  160. package/dist/src/models/wrapper-route.js +1 -1
  161. package/dist/src/output-modes/registry.js +8 -7
  162. package/dist/src/packages/adapters/git.js +99 -23
  163. package/dist/src/packages/adapters/gitea.js +8 -27
  164. package/dist/src/packages/adapters/github.js +7 -25
  165. package/dist/src/packages/registry.js +4 -4
  166. package/dist/src/plugin/metadata-validator.js +1 -1
  167. package/dist/src/process/cli-command.d.mts +5 -0
  168. package/dist/src/process/cli-command.mjs +42 -0
  169. package/dist/src/providers/deploy-version.d.mts +1 -0
  170. package/dist/src/providers/deploy-version.mjs +17 -0
  171. package/dist/src/providers/provider-definitions.js +19 -16
  172. package/dist/src/providers/skill-ownership.d.mts +11 -2
  173. package/dist/src/providers/skill-ownership.mjs +35 -17
  174. package/dist/src/rules/activation.d.mts +11 -2
  175. package/dist/src/rules/activation.mjs +82 -6
  176. package/dist/src/rules/inventory.js +1 -1
  177. package/dist/src/rules/rule-frontmatter.schema.json +2 -0
  178. package/dist/src/schema/zod-validation.d.mts +6 -0
  179. package/dist/src/schema/zod-validation.mjs +50 -0
  180. package/dist/src/serve/executor-registry.js +297 -20
  181. package/dist/src/serve/executor-token.d.mts +1 -0
  182. package/dist/src/serve/executor-token.mjs +8 -0
  183. package/dist/src/serve/shared-host-scheduler.js +1102 -31
  184. package/dist/src/serve/worker-invocation.js +124 -15
  185. package/dist/src/sessions/adapters/claude.js +19 -18
  186. package/dist/src/sessions/adapters/codex.js +10 -9
  187. package/dist/src/sessions/adapters/copilot.js +14 -13
  188. package/dist/src/sessions/adapters/cursor.js +19 -18
  189. package/dist/src/sessions/adapters/deepseek-harness.js +9 -8
  190. package/dist/src/sessions/adapters/factory.js +11 -10
  191. package/dist/src/sessions/adapters/generic.js +32 -23
  192. package/dist/src/sessions/adapters/grokbot.js +35 -35
  193. package/dist/src/sessions/adapters/hermes.js +20 -19
  194. package/dist/src/sessions/adapters/muse.js +50 -49
  195. package/dist/src/sessions/adapters/omp.js +5 -4
  196. package/dist/src/sessions/adapters/openclaw.js +30 -29
  197. package/dist/src/sessions/adapters/opencode.js +26 -26
  198. package/dist/src/sessions/adapters/openhuman.js +24 -23
  199. package/dist/src/sessions/adapters/pi.js +5 -4
  200. package/dist/src/sessions/adapters/windsurf.js +8 -8
  201. package/dist/src/sessions/analytics.js +10 -9
  202. package/dist/src/sessions/candidates.js +7 -6
  203. package/dist/src/sessions/contracts.js +63 -53
  204. package/dist/src/sessions/output-registration.js +11 -10
  205. package/dist/src/sessions/workspace-discovery.js +3 -4
  206. package/dist/src/smiths/context-pipeline/aiwg-md.js +18 -1
  207. package/dist/src/smiths/context-pipeline/claude-hook.js +46 -10
  208. package/dist/src/smiths/context-pipeline/context-file-safety.js +330 -9
  209. package/dist/src/smiths/context-pipeline/finalization.js +3 -2
  210. package/dist/src/smiths/context-pipeline/generator.js +7 -19
  211. package/dist/src/smiths/context-pipeline/legacy-inject.js +3 -10
  212. package/dist/src/smiths/context-pipeline/managed-hook.js +5 -5
  213. package/dist/src/smiths/context-pipeline/parallelism-section.js +72 -5
  214. package/dist/src/smiths/context-pipeline/workspace-context.js +110 -22
  215. package/dist/src/tracker/project-inputs.js +2 -0
  216. package/dist/src/update/checker.d.mts +4 -0
  217. package/dist/src/update/checker.mjs +14 -0
  218. package/dist/src/update/notifier.mjs +2 -4
  219. package/dist/src/writing/exemplar-selection.js +6 -6
  220. package/dist/src/writing/voice-evaluation.js +24 -23
  221. package/dist/src/writing/writer-migration.js +14 -13
  222. package/dist/src/writing/writer-profile.js +19 -18
  223. package/dist/src/writing/writing-brief.js +18 -17
  224. package/dist/src/writing/writing-receipt.js +47 -46
  225. package/package.json +5 -2
  226. package/schemas/decision/DecisionReview.schema.json +78 -1
  227. package/schemas/mission-v1.schema.json +3 -0
  228. package/tools/agents/deploy-agents.mjs +67 -39
  229. package/tools/agents/providers/antigravity.mjs +6 -1
  230. package/tools/agents/providers/base.mjs +24 -14
  231. package/tools/agents/providers/claude.mjs +6 -3
  232. package/tools/agents/providers/codex.mjs +54 -57
  233. package/tools/agents/providers/context-write.mjs +28 -0
  234. package/tools/agents/providers/copilot.mjs +13 -4
  235. package/tools/agents/providers/cursor.mjs +1 -1
  236. package/tools/agents/providers/deepseek-harness.mjs +1 -1
  237. package/tools/agents/providers/factory.mjs +7 -1
  238. package/tools/agents/providers/grok-build.mjs +18 -2
  239. package/tools/agents/providers/grokbot.mjs +1 -1
  240. package/tools/agents/providers/hermes.mjs +12 -15
  241. package/tools/agents/providers/muse-hooks.mjs +21 -14
  242. package/tools/agents/providers/muse.mjs +7 -2
  243. package/tools/agents/providers/omp.mjs +31 -5
  244. package/tools/agents/providers/opencode.mjs +14 -5
  245. package/tools/agents/providers/openhuman.mjs +1 -1
  246. package/tools/agents/providers/pi.mjs +1 -1
  247. package/tools/agents/providers/provider-prepare-coordinator.mjs +29 -9
  248. package/tools/agents/providers/rule-delivery.mjs +289 -80
  249. package/tools/agents/providers/warp.mjs +4 -1
  250. package/tools/agents/providers/windsurf.mjs +13 -3
  251. package/tools/cli/workspace-status.mjs +5 -0
  252. package/tools/commands/deploy-prompts-codex.mjs +11 -8
  253. package/tools/ralph-external/lib/codex-adapter.mjs +6 -2
  254. package/tools/ralph-external/lib/consecutive-failure-circuit-breaker.mjs +26 -0
  255. package/tools/ralph-external/lib/launcher-recovery-evidence.mjs +7 -1
  256. package/tools/ralph-external/lib/muse-adapter.mjs +12 -2
  257. package/tools/ralph-external/lib/recovery-probe.mjs +14 -4
  258. package/tools/ralph-external/lib/worker-health.mjs +1 -1
  259. package/tools/ralph-external/lib/worker-isolation.mjs +65 -9
  260. package/tools/ralph-external/process-monitor.mjs +49 -8
  261. package/tools/ralph-external/session-launcher.mjs +405 -35
  262. package/tools/skills/deploy-skills-codex.mjs +11 -7
  263. package/vscode-extension/schemas/aiwg.config.v1.json +11 -1
@@ -1,3 +1,5 @@
1
+ import { resolveSensitiveInput } from '../auth/sensitive-input.js';
2
+ import { isInteractive } from '../cli/env.js';
1
3
  export class LiveIssueClient {
2
4
  options;
3
5
  baseUrl;
@@ -63,24 +65,51 @@ export class LiveIssueClient {
63
65
  headers['Content-Type'] = 'application/json';
64
66
  if (this.options.token)
65
67
  headers.Authorization = `Bearer ${this.options.token}`;
66
- const response = await this.fetchFn(`${this.baseUrl}${path}`, {
67
- method,
68
- headers,
69
- body: body === undefined ? undefined : JSON.stringify(body),
70
- });
71
- if (!response.ok) {
72
- const text = await response.text().catch(() => '');
73
- throw new Error(`${this.options.provider} issue API ${method} ${path} failed: ${response.status}${text ? ` ${redactSecrets(text)}` : ''}`);
68
+ try {
69
+ const response = await this.fetchFn(`${this.baseUrl}${path}`, {
70
+ method,
71
+ headers,
72
+ body: body === undefined ? undefined : JSON.stringify(body),
73
+ });
74
+ if (!response.ok) {
75
+ const text = await response.text().catch(() => '');
76
+ throw new Error(`${this.options.provider} issue API ${method} ${path} failed: ${response.status}${text ? ` ${redactSecrets(text, this.options.token)}` : ''}`);
77
+ }
78
+ const result = await response.json();
79
+ // A server can reflect request credentials in issue fields or comments.
80
+ // Scrub them before they reach local storage or CLI output.
81
+ if (!this.options.token)
82
+ return result;
83
+ const encodedToken = JSON.stringify(this.options.token).slice(1, -1);
84
+ return JSON.parse(JSON.stringify(result).split(encodedToken).join('[REDACTED]'));
85
+ }
86
+ catch (error) {
87
+ throw new Error(redactSecrets(error instanceof Error ? error.message : 'Live issue request failed', this.options.token));
74
88
  }
75
- return response.json();
76
89
  }
77
90
  }
78
91
  export function createLiveIssueClient(options) {
79
92
  return new LiveIssueClient({
80
93
  ...options,
81
- token: resolveToken(options.provider, options.tokenEnv ?? process.env),
94
+ token: options.token ?? resolveToken(options.provider, options.tokenEnv ?? process.env),
82
95
  });
83
96
  }
97
+ /** CLI source selection; the synchronous client factory remains available to callers. */
98
+ export async function resolveLiveIssueToken(provider, source = {}, dependencies = {}) {
99
+ const environment = dependencies.environment ?? process.env;
100
+ const isTTY = dependencies.isTTY ?? isInteractive();
101
+ const selected = { ...source, prefix: 'token' };
102
+ const hasSource = selected.interactive || selected.stdin || selected.file !== undefined || selected.store !== undefined || selected.env !== undefined;
103
+ if (!hasSource) {
104
+ // Established behavior: use the provider variable when set, otherwise make
105
+ // unauthenticated requests (public repositories). Prompt only on request.
106
+ const names = provider === 'gitea' ? ['AIWG_GITEA_TOKEN', 'GITEA_TOKEN'] : ['AIWG_GITHUB_TOKEN', 'GITHUB_TOKEN'];
107
+ selected.env = names.find(name => environment[name] !== undefined);
108
+ if (selected.env === undefined)
109
+ return undefined;
110
+ }
111
+ return resolveSensitiveInput(selected, { ...dependencies, environment, isTTY });
112
+ }
84
113
  export function resolveToken(provider, env) {
85
114
  if (provider === 'gitea')
86
115
  return env.AIWG_GITEA_TOKEN ?? env.GITEA_TOKEN;
@@ -148,7 +177,10 @@ function requireString(value, field) {
148
177
  return value;
149
178
  throw new Error(`live issue response missing ${field}`);
150
179
  }
151
- function redactSecrets(value) {
152
- return value.replace(/(token|authorization|password|secret)["':=\s]+[^\s"',}]+/gi, '$1=[REDACTED]');
180
+ function redactSecrets(value, token) {
181
+ const redacted = token ? value.split(token).join('[REDACTED]') : value;
182
+ return redacted
183
+ .replace(/Bearer\s+[^\s"',}]+/gi, 'Bearer [REDACTED]')
184
+ .replace(/(token|authorization|password|secret)["':=\s]+[^\s"',}]+/gi, '$1=[REDACTED]');
153
185
  }
154
186
  //# sourceMappingURL=live.js.map
@@ -2,6 +2,7 @@ import { promises as fs } from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import { load as loadYaml } from 'js-yaml';
4
4
  import { z } from 'zod';
5
+ import { integer, url } from '../schema/zod-validation.mjs';
5
6
  import { JOB_API_VERSION, JOB_KIND } from './types.js';
6
7
  const relativeFile = z.string().min(1).refine(value => {
7
8
  if (path.isAbsolute(value))
@@ -12,51 +13,51 @@ const relativeFile = z.string().min(1).refine(value => {
12
13
  const absolutePath = z.string().min(1)
13
14
  .refine(path.isAbsolute, 'must be an absolute path')
14
15
  .refine(value => path.resolve(value) !== path.parse(path.resolve(value)).root, 'must not be a filesystem root');
15
- const httpsOrigin = z.string().url().refine(value => new URL(value).protocol === 'https:', 'must use https');
16
+ const httpsOrigin = url().refine(value => new URL(value).protocol === 'https:', 'must use https');
16
17
  const accountName = z.string().regex(/^[A-Za-z0-9][A-Za-z0-9_.@:-]{0,127}$/u);
17
- export const externalJobFlowSchema = z.object({
18
+ export const externalJobFlowSchema = z.strictObject({
18
19
  apiVersion: z.literal(JOB_API_VERSION),
19
20
  kind: z.literal(JOB_KIND),
20
- metadata: z.object({
21
+ metadata: z.strictObject({
21
22
  name: z.string().regex(/^[a-z][a-z0-9-]{0,62}$/u),
22
23
  revision: z.string().regex(/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/u),
23
- }).strict(),
24
- spec: z.object({
25
- trigger: z.object({ type: z.literal('external') }).strict(),
26
- executor: z.object({
24
+ }),
25
+ spec: z.strictObject({
26
+ trigger: z.strictObject({ type: z.literal('external') }),
27
+ executor: z.strictObject({
27
28
  provider: z.literal('codex'),
28
29
  mode: z.literal('exec'),
29
30
  workspace: absolutePath,
30
31
  prompt: relativeFile,
31
32
  resultSchema: relativeFile,
32
33
  binary: absolutePath,
33
- }).strict(),
34
- workItem: z.object({
34
+ }),
35
+ workItem: z.strictObject({
35
36
  provider: z.literal('gitea'),
36
- baseUrl: z.string().url().refine(value => new URL(value).protocol === 'https:', 'must use https'),
37
+ baseUrl: url().refine(value => new URL(value).protocol === 'https:', 'must use https'),
37
38
  repository: z.string().regex(/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/u),
38
39
  tokenFile: absolutePath,
39
40
  eligibleLabels: z.array(z.string().min(1)).min(1),
40
- claimTtlSeconds: z.number().int().min(30).max(86400).optional(),
41
- claimSettleMs: z.number().int().min(100).max(30000).optional(),
42
- }).strict(),
43
- approval: z.object({
41
+ claimTtlSeconds: integer().min(30).max(86400).optional(),
42
+ claimSettleMs: integer().min(100).max(30000).optional(),
43
+ }),
44
+ approval: z.strictObject({
44
45
  required: z.boolean().optional(),
45
46
  label: z.string().min(1).optional(),
46
- }).strict().optional(),
47
- security: z.object({
47
+ }).optional(),
48
+ security: z.strictObject({
48
49
  allowedOrigins: z.array(httpsOrigin).min(1),
49
50
  allowedAccounts: z.array(accountName).min(1),
50
51
  approvedAttachmentRoots: z.array(absolutePath),
51
52
  sensitiveValueFiles: z.array(absolutePath).optional(),
52
- }).strict(),
53
- completion: z.object({
53
+ }),
54
+ completion: z.strictObject({
54
55
  require: z.array(z.enum([
55
56
  'external-result-url', 'issue-comment', 'idempotency-key', 'verification',
56
57
  ])).min(1),
57
- }).strict(),
58
- }).strict(),
59
- }).strict().superRefine((flow, ctx) => {
58
+ }),
59
+ }),
60
+ }).superRefine((flow, ctx) => {
60
61
  const workspace = path.resolve(flow.spec.executor.workspace);
61
62
  const within = (candidate) => {
62
63
  const relative = path.relative(workspace, path.resolve(candidate));
@@ -1,5 +1,8 @@
1
+ /** Activation checks within the existing lint runner, not a delivery selector. */
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
1
4
  import { loadLocalCapabilityEntries, resolveRuleBindingTarget } from '../artifacts/query-engine.js';
2
- import { parseRuleFrontmatter, RULE_BINDING_TYPES } from '../rules/activation.mjs';
5
+ import { parseRuleFrontmatter, RULE_BINDING_TYPES, coreBodyPath, validateCoreBody } from '../rules/activation.mjs';
3
6
  export const ruleActivationRuleset = {
4
7
  id: 'rule-activation', name: 'Rule activation', framework: 'core', version: '1.0.0',
5
8
  description: 'Validate rule and behavior activation contracts and exact indexed bindings',
@@ -10,17 +13,29 @@ export const ruleActivationRuleset = {
10
13
  checks: [{ type: 'rule-activation' }],
11
14
  }],
12
15
  };
13
- export function lintRuleActivation(content, file, projectRoot, entries) {
14
- // Generated structural indexes are not authored rules.
15
- if (/^(?:RULES-INDEX|RULES-ONDEMAND)\.md$/.test(file.split('/').pop()))
16
+ export function lintRuleActivation(content, file, projectRoot, entries, absolutePath) {
17
+ // Directory documentation and generated indexes are not authored rules.
18
+ if (/^(?:README|RULES-INDEX|RULES-ONDEMAND)\.md$/.test(file.split('/').pop()))
16
19
  return [];
20
+ // <rule>.core.md is a delivery body (#3450), validated through its owning rule.
21
+ if (/\.core\.md$/.test(file))
22
+ return [];
23
+ const shipped = /(?:^|\/)agentic\/code\//.test((absolutePath ?? path.resolve(projectRoot, file)).replaceAll('\\', '/'));
17
24
  const result = parseRuleFrontmatter(content);
18
25
  const diagnostics = result.diagnostics.map(item => ({
19
26
  ruleId: 'rules/activation', ruleName: 'Rule activation contract',
20
- file, severity: item.severity, message: item.message,
27
+ file, severity: item.code === 'missing-activation' && shipped ? 'error' : item.severity, message: item.message,
21
28
  }));
22
29
  if (!result.rule)
23
30
  return diagnostics;
31
+ if (result.rule.activation === 'core') {
32
+ const body = coreBodyPath(absolutePath ?? (path.isAbsolute(file) ? file : path.resolve(projectRoot, file)));
33
+ const problems = fs.existsSync(body)
34
+ ? validateCoreBody(fs.readFileSync(body, 'utf8').trim(), path.basename(body))
35
+ : [`${path.basename(body)}: activation core requires a compact body file beside the rule`];
36
+ for (const message of problems)
37
+ diagnostics.push({ ruleId: 'rules/activation', ruleName: 'Rule activation contract', file, severity: 'error', message });
38
+ }
24
39
  const bindings = result.rule.bound_to;
25
40
  if (!Object.values(bindings).some(list => list.length > 0))
26
41
  return diagnostics;
@@ -563,7 +563,7 @@ async function runRule(rule, targetDir, allFiles, referenceIndex, projectRoot, a
563
563
  if (check.type === 'id-unique')
564
564
  continue; // Already handled
565
565
  if (check.type === 'rule-activation') {
566
- diagnostics.push(...lintRuleActivation(content, file, projectRoot, artifactEntries));
566
+ diagnostics.push(...lintRuleActivation(content, file, projectRoot, artifactEntries, absPath));
567
567
  continue;
568
568
  }
569
569
  const checkDiagnostics = runCheck(check, content, frontmatter, file, targetDir, allFiles, referenceIndex, projectRoot);
@@ -9,6 +9,7 @@ import { access, chmod, mkdir, mkdtemp, readFile, rename, rm, writeFile, } from
9
9
  import { existsSync } from 'node:fs';
10
10
  import os from 'node:os';
11
11
  import path from 'node:path';
12
+ import { resolveSensitiveInput } from '../auth/sensitive-input.js';
12
13
  import { setPackageEntry } from '../packages/package-registry.js';
13
14
  import { discoverInstallablePackage } from '../packages/package-discovery.js';
14
15
  import { parseTrustRoot, sha256 as artifactSha256, } from '../security/artifact-trust.js';
@@ -236,7 +237,13 @@ export async function verifyIndexedPackage(options) {
236
237
  });
237
238
  return { verification, receipt, entry };
238
239
  }
239
- export async function publishLocalPackage(options) {
240
+ export async function publishLocalPackage(options, sensitiveDependencies = {}) {
241
+ const privateKey = await resolveSensitiveInput({
242
+ ...options.keyInput,
243
+ prefix: 'key',
244
+ file: options.keyInput?.file ?? options.privateKeyPath,
245
+ multiline: true,
246
+ }, sensitiveDependencies);
240
247
  const sourceDir = path.resolve(options.sourceDir);
241
248
  const discovered = await discoverInstallablePackage(sourceDir, options.packageSelector);
242
249
  const unsigned = await createProvenanceEnvelope({
@@ -248,12 +255,17 @@ export async function publishLocalPackage(options) {
248
255
  publisher: options.publisher,
249
256
  sequence: options.sequence,
250
257
  });
251
- const privateKey = await readFile(path.resolve(options.privateKeyPath), 'utf8');
252
258
  const publicKey = options.publicKeyPath ? await readFile(path.resolve(options.publicKeyPath), 'utf8') : undefined;
253
- const envelope = signProvenanceEnvelope(unsigned, privateKey, {
254
- keyId: options.keyId,
255
- publicKeyPem: publicKey,
256
- });
259
+ let envelope;
260
+ try {
261
+ envelope = signProvenanceEnvelope(unsigned, privateKey, {
262
+ keyId: options.keyId,
263
+ publicKeyPem: publicKey,
264
+ });
265
+ }
266
+ catch {
267
+ throw new Error('Marketplace signing failed; check the Ed25519 private key and public-key options.');
268
+ }
257
269
  const lock = createPackageLock(envelope, envelope.publication.publishedAt);
258
270
  const fortemi = await buildFortemiEnvelopeShard(envelope);
259
271
  const receipt = createOperationReceipt({
@@ -8,6 +8,7 @@
8
8
  * @implements #2009
9
9
  */
10
10
  import { execFile } from 'node:child_process';
11
+ import { helperEnvironment } from '../process/cli-command.mjs';
11
12
  import { createHash, createPrivateKey, createPublicKey, sign as cryptoSign, verify as cryptoVerify, } from 'node:crypto';
12
13
  import { lstat, readFile, readdir } from 'node:fs/promises';
13
14
  import path from 'node:path';
@@ -157,7 +158,7 @@ async function git(cwd, args) {
157
158
  cwd,
158
159
  encoding: 'utf8',
159
160
  timeout: 120_000,
160
- env: { ...process.env, GIT_TERMINAL_PROMPT: '0' },
161
+ env: { ...helperEnvironment(), GIT_TERMINAL_PROMPT: '0' },
161
162
  });
162
163
  return stdout.trim();
163
164
  }
@@ -166,7 +167,7 @@ async function gitBytes(cwd, args) {
166
167
  cwd,
167
168
  encoding: 'buffer',
168
169
  timeout: 120_000,
169
- env: { ...process.env, GIT_TERMINAL_PROMPT: '0' },
170
+ env: { ...helperEnvironment(), GIT_TERMINAL_PROMPT: '0' },
170
171
  });
171
172
  return stdout;
172
173
  }
@@ -14,11 +14,13 @@
14
14
  *
15
15
  * @implements #892
16
16
  */
17
+ import { readPrivateValueFile } from '../../auth/sensitive-input.js';
17
18
  import { signalChild } from '../../process/signal-child.mjs';
18
- import { readFile, mkdir, symlink, access, readdir, chmod, lstat, open } from 'fs/promises';
19
+ import { helperEnvironment, HELPER_ENV_ALLOWLIST, DESKTOP_HELPER_ENV_ALLOWLIST } from '../../process/cli-command.mjs';
20
+ import { readFile, mkdir, symlink, access, readdir, chmod } from 'fs/promises';
19
21
  import { join } from 'path';
20
22
  import { homedir } from 'os';
21
- import { existsSync, constants } from 'fs';
23
+ import { existsSync } from 'fs';
22
24
  import { spawn, spawnSync } from 'child_process';
23
25
  import { boundedSpawnSync, resolveSyncProcessTimeoutMs } from '../../process/bounded-child-process.mjs';
24
26
  import { isInteractive } from '../../cli/env.js';
@@ -228,73 +230,9 @@ function envWithoutSecretValue(base, envVarName, secretValue) {
228
230
  }
229
231
  return result;
230
232
  }
231
- /**
232
- * Read an API key from a private file (#3062 go/no-go review, blocker 1C):
233
- * `--api-key-file <path>` reads the key from disk instead of an
234
- * environment variable, so a file that does not meet the same bar the
235
- * decision ledger's own file writer enforces
236
- * (`src/decision/ledger.ts`'s `openLedgerFileForAppend`) is refused rather
237
- * than silently trusted. This mirrors that check for a read instead of a
238
- * write — there is no already-exported helper at this layer to reuse (the
239
- * ledger's checks are private to that module and tied to its own
240
- * create-if-missing write path), so this re-implements the same checks for
241
- * a read: never follow a symlink; the open file descriptor must resolve to
242
- * a regular file with exactly one hard link; and, on POSIX, must be owned
243
- * by the current user with no group/other permission bits. The file's
244
- * contents are returned trimmed; no error message here ever includes them.
245
- */
233
+ /** Backward-compatible MCP export; the validation is shared by all input commands. */
246
234
  async function readApiKeyFromFile(filePath) {
247
- try {
248
- const pre = await lstat(filePath);
249
- if (pre.isSymbolicLink()) {
250
- throw new Error(`--api-key-file: "${filePath}" is a symlink; refusing to follow it.`);
251
- }
252
- }
253
- catch (error) {
254
- if (error.code === 'ENOENT') {
255
- throw new Error(`--api-key-file: "${filePath}" does not exist.`);
256
- }
257
- if (error instanceof Error && error.message.startsWith('--api-key-file:'))
258
- throw error;
259
- throw new Error(`--api-key-file: cannot stat "${filePath}": ${error.message}`);
260
- }
261
- const hasNoFollow = typeof constants.O_NOFOLLOW === 'number';
262
- const flags = constants.O_RDONLY | (hasNoFollow ? constants.O_NOFOLLOW : 0);
263
- let handle;
264
- try {
265
- handle = await open(filePath, flags);
266
- }
267
- catch (error) {
268
- const code = error.code;
269
- if (code === 'ELOOP')
270
- throw new Error(`--api-key-file: "${filePath}" is a symlink; refusing to follow it.`);
271
- if (code === 'ENOENT')
272
- throw new Error(`--api-key-file: "${filePath}" does not exist.`);
273
- throw new Error(`--api-key-file: cannot open "${filePath}": ${error.message}`);
274
- }
275
- try {
276
- const stat = await handle.stat();
277
- if (!stat.isFile()) {
278
- throw new Error(`--api-key-file: "${filePath}" is not a regular file.`);
279
- }
280
- if (stat.nlink !== 1) {
281
- throw new Error(`--api-key-file: "${filePath}" has more than one hard link; refusing to read it.`);
282
- }
283
- if (process.platform !== 'win32') {
284
- const uid = typeof process.getuid === 'function' ? process.getuid() : null;
285
- if (uid !== null && stat.uid !== uid) {
286
- throw new Error(`--api-key-file: "${filePath}" is not owned by the current user.`);
287
- }
288
- if ((stat.mode & 0o077) !== 0) {
289
- throw new Error(`--api-key-file: "${filePath}" must not be readable or writable by group or other ` +
290
- '(chmod 600 it).');
291
- }
292
- }
293
- return (await handle.readFile({ encoding: 'utf-8' })).trim();
294
- }
295
- finally {
296
- await handle.close();
297
- }
235
+ return readPrivateValueFile(filePath, '--api-key-file');
298
236
  }
299
237
  /**
300
238
  * Async, credential-safe launch of `codex login --with-api-key` (#3062
@@ -320,17 +258,37 @@ function spawnCodexLoginWithApiKey(apiKey, env) {
320
258
  return new Promise((resolve) => {
321
259
  const child = spawn('codex', ['login', '--with-api-key'], {
322
260
  env,
323
- stdio: ['pipe', 'inherit', 'inherit'],
261
+ stdio: ['pipe', 'pipe', 'pipe'],
324
262
  });
325
263
  let settled = false;
326
264
  let timer;
265
+ const stdout = [];
266
+ const stderr = [];
267
+ let outputBytes = 0;
268
+ // Buffer through close so a credential split between chunks cannot escape
269
+ // redaction. The combined cap bounds both memory and forwarded diagnostics.
270
+ const collect = (target, chunk) => {
271
+ if (settled)
272
+ return;
273
+ const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
274
+ outputBytes += buffer.length;
275
+ if (outputBytes > 1024 * 1024) {
276
+ settle({ status: null, signal: 'SIGKILL', error: new Error('codex login output exceeded the 1 MiB limit.') });
277
+ return;
278
+ }
279
+ target.push(buffer);
280
+ };
281
+ const onStdout = (chunk) => collect(stdout, chunk);
282
+ const onStderr = (chunk) => collect(stderr, chunk);
283
+ child.stdout?.on('data', onStdout);
284
+ child.stderr?.on('data', onStderr);
327
285
  // #3062 go/no-go review, blocker 2: stdin error, write completion,
328
- // child 'error', child 'exit', and the deadline all funnel through this
286
+ // child 'error', child 'close', and the deadline all funnel through this
329
287
  // one function, which settles exactly once, always clears the timer and
330
288
  // removes every listener this call added, and kills the child if it is
331
289
  // still alive on a failing settlement (a running process on the
332
290
  // timeout path; a process that may still be running on an unexpected
333
- // stdin-write error). The 'exit' path never reaches the kill — by the
291
+ // stdin-write error). The 'close' path never reaches the kill — by the
334
292
  // time that event fires the child has already exited.
335
293
  const settle = (result) => {
336
294
  if (settled)
@@ -340,13 +298,23 @@ function spawnCodexLoginWithApiKey(apiKey, env) {
340
298
  clearTimeout(timer);
341
299
  child.stdin?.removeListener('error', onStdinError);
342
300
  child.removeListener('error', onChildError);
343
- child.removeListener('exit', onExit);
301
+ child.removeListener('close', onClose);
302
+ child.stdout?.removeListener('data', onStdout);
303
+ child.stderr?.removeListener('data', onStderr);
304
+ child.stdout?.destroy();
305
+ child.stderr?.destroy();
344
306
  if (result.error && child.exitCode === null && child.signalCode === null) {
345
307
  try {
346
308
  signalChild(child, 'SIGKILL');
347
309
  }
348
310
  catch { /* already gone */ }
349
311
  }
312
+ // On a failed/oversized stream, suppress buffered fragments entirely:
313
+ // the missing remainder might contain the rest of a credential.
314
+ if (!result.error) {
315
+ process.stdout.write(Buffer.concat(stdout).toString('utf8').split(apiKey).join('[redacted]'));
316
+ process.stderr.write(Buffer.concat(stderr).toString('utf8').split(apiKey).join('[redacted]'));
317
+ }
350
318
  resolve(result);
351
319
  };
352
320
  timer = setTimeout(() => {
@@ -358,15 +326,15 @@ function spawnCodexLoginWithApiKey(apiKey, env) {
358
326
  }, boundMs);
359
327
  const onChildError = (err) => settle({ status: null, signal: null, error: err });
360
328
  child.on('error', onChildError);
361
- const onExit = (code, signal) => settle({ status: code, signal });
362
- child.on('exit', onExit);
329
+ const onClose = (code, signal) => settle({ status: code, signal });
330
+ child.on('close', onClose);
363
331
  // A child that exits (or closes its read end) before reading stdin
364
332
  // raises EPIPE/ECONNRESET on the *stdin stream*, not on the child
365
333
  // object — `child.on('error', ...)` above never sees it, so without
366
334
  // this listener it is an unhandled 'error' event, which Node turns into
367
335
  // an uncaught exception. Installed before the write/end below, per the
368
336
  // review. EPIPE/ECONNRESET specifically mean "the child already went
369
- // away"; the already-firing (or about-to-fire) 'exit' event carries the
337
+ // away"; the already-firing (or about-to-fire) 'close' event carries the
370
338
  // real, non-secret result, so this swallows exactly those two codes and
371
339
  // lets settle() happen there instead. Anything else is a genuine,
372
340
  // unexpected stdin failure and is itself the settling error.
@@ -413,7 +381,9 @@ export async function loginInProfile(profile, options = {}) {
413
381
  await ensureRuntimeHome(profile);
414
382
  const rtHome = runtimeHomePath(profile);
415
383
  const env = {
416
- ...process.env,
384
+ ...helperEnvironment(process.env, [
385
+ ...HELPER_ENV_ALLOWLIST, ...DESKTOP_HELPER_ENV_ALLOWLIST, 'NODE_EXTRA_CA_CERTS',
386
+ ]),
417
387
  HOME: rtHome,
418
388
  CODEX_HOME: rtHome,
419
389
  AIWG_CONFIG: resolveUserConfigDir(),