subharness 0.0.3 → 0.0.5

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 (241) hide show
  1. package/README.md +39 -9
  2. package/dist/adapters/approval-context.d.ts +2 -0
  3. package/dist/adapters/approval-context.js +9 -0
  4. package/dist/adapters/approval-context.js.map +1 -0
  5. package/dist/adapters/approval-lifetime.d.ts +8 -0
  6. package/dist/adapters/approval-lifetime.js +50 -0
  7. package/dist/adapters/approval-lifetime.js.map +1 -0
  8. package/dist/adapters/claude-approvals.d.ts +4 -0
  9. package/dist/adapters/claude-approvals.js +32 -0
  10. package/dist/adapters/claude-approvals.js.map +1 -0
  11. package/dist/adapters/claude-permissions.js +1 -1
  12. package/dist/adapters/claude-permissions.js.map +1 -1
  13. package/dist/adapters/claude-process.d.ts +3 -0
  14. package/dist/adapters/claude-process.js +11 -1
  15. package/dist/adapters/claude-process.js.map +1 -1
  16. package/dist/adapters/claude-result.d.ts +2 -0
  17. package/dist/adapters/claude-result.js +22 -0
  18. package/dist/adapters/claude-result.js.map +1 -0
  19. package/dist/adapters/claude.js +60 -12
  20. package/dist/adapters/claude.js.map +1 -1
  21. package/dist/adapters/codex-approvals.d.ts +6 -0
  22. package/dist/adapters/codex-approvals.js +111 -0
  23. package/dist/adapters/codex-approvals.js.map +1 -0
  24. package/dist/adapters/codex.js +36 -3
  25. package/dist/adapters/codex.js.map +1 -1
  26. package/dist/adapters/copilot-permissions.d.ts +26 -0
  27. package/dist/adapters/copilot-permissions.js +121 -0
  28. package/dist/adapters/copilot-permissions.js.map +1 -0
  29. package/dist/adapters/copilot-tools.d.ts +12 -0
  30. package/dist/adapters/copilot-tools.js +61 -0
  31. package/dist/adapters/copilot-tools.js.map +1 -0
  32. package/dist/adapters/copilot.d.ts +108 -0
  33. package/dist/adapters/copilot.js +819 -0
  34. package/dist/adapters/copilot.js.map +1 -0
  35. package/dist/adapters/cursor-cli-approvals.d.ts +2 -0
  36. package/dist/adapters/cursor-cli-approvals.js +83 -0
  37. package/dist/adapters/cursor-cli-approvals.js.map +1 -0
  38. package/dist/adapters/cursor-cli-model.d.ts +4 -0
  39. package/dist/adapters/cursor-cli-model.js +64 -0
  40. package/dist/adapters/cursor-cli-model.js.map +1 -0
  41. package/dist/adapters/cursor-cli-session.d.ts +32 -0
  42. package/dist/adapters/cursor-cli-session.js +316 -0
  43. package/dist/adapters/cursor-cli-session.js.map +1 -0
  44. package/dist/adapters/cursor-cli-tools.d.ts +20 -0
  45. package/dist/adapters/cursor-cli-tools.js +181 -0
  46. package/dist/adapters/cursor-cli-tools.js.map +1 -0
  47. package/dist/adapters/cursor-cli.d.ts +2 -0
  48. package/dist/adapters/cursor-cli.js +306 -0
  49. package/dist/adapters/cursor-cli.js.map +1 -0
  50. package/dist/adapters/cursor-model.d.ts +7 -0
  51. package/dist/adapters/cursor-model.js +67 -0
  52. package/dist/adapters/cursor-model.js.map +1 -0
  53. package/dist/adapters/cursor-rpc.d.ts +42 -0
  54. package/dist/adapters/cursor-rpc.js +271 -0
  55. package/dist/adapters/cursor-rpc.js.map +1 -0
  56. package/dist/adapters/cursor-session.d.ts +4 -0
  57. package/dist/adapters/cursor-session.js +327 -0
  58. package/dist/adapters/cursor-session.js.map +1 -0
  59. package/dist/adapters/cursor-startup.d.ts +16 -0
  60. package/dist/adapters/cursor-startup.js +74 -0
  61. package/dist/adapters/cursor-startup.js.map +1 -0
  62. package/dist/adapters/cursor-tools.d.ts +11 -0
  63. package/dist/adapters/cursor-tools.js +49 -0
  64. package/dist/adapters/cursor-tools.js.map +1 -0
  65. package/dist/adapters/cursor.d.ts +6 -0
  66. package/dist/adapters/cursor.js +17 -0
  67. package/dist/adapters/cursor.js.map +1 -0
  68. package/dist/adapters/fx-approvals.d.ts +2 -0
  69. package/dist/adapters/fx-approvals.js +14 -0
  70. package/dist/adapters/fx-approvals.js.map +1 -0
  71. package/dist/adapters/fx-auth.d.ts +12 -2
  72. package/dist/adapters/fx-auth.js +51 -61
  73. package/dist/adapters/fx-auth.js.map +1 -1
  74. package/dist/adapters/fx-profile.d.ts +8 -0
  75. package/dist/adapters/fx-profile.js +101 -0
  76. package/dist/adapters/fx-profile.js.map +1 -0
  77. package/dist/adapters/fx-rpc.d.ts +2 -0
  78. package/dist/adapters/fx-rpc.js +30 -4
  79. package/dist/adapters/fx-rpc.js.map +1 -1
  80. package/dist/adapters/fx-status.d.ts +2 -0
  81. package/dist/adapters/fx-status.js +96 -0
  82. package/dist/adapters/fx-status.js.map +1 -0
  83. package/dist/adapters/fx.js +77 -30
  84. package/dist/adapters/fx.js.map +1 -1
  85. package/dist/adapters/opencode-access.d.ts +13 -0
  86. package/dist/adapters/opencode-access.js +76 -0
  87. package/dist/adapters/opencode-access.js.map +1 -0
  88. package/dist/adapters/opencode-config.d.ts +11 -0
  89. package/dist/adapters/opencode-config.js +238 -0
  90. package/dist/adapters/opencode-config.js.map +1 -0
  91. package/dist/adapters/opencode-http.d.ts +28 -0
  92. package/dist/adapters/opencode-http.js +297 -0
  93. package/dist/adapters/opencode-http.js.map +1 -0
  94. package/dist/adapters/opencode-tools.d.ts +19 -0
  95. package/dist/adapters/opencode-tools.js +127 -0
  96. package/dist/adapters/opencode-tools.js.map +1 -0
  97. package/dist/adapters/opencode.d.ts +2 -0
  98. package/dist/adapters/opencode.js +569 -0
  99. package/dist/adapters/opencode.js.map +1 -0
  100. package/dist/adapters/rpc.d.ts +3 -0
  101. package/dist/adapters/rpc.js +45 -4
  102. package/dist/adapters/rpc.js.map +1 -1
  103. package/dist/adapters/types.d.ts +7 -0
  104. package/dist/adapters/types.js.map +1 -1
  105. package/dist/approvals/schema.d.ts +4 -0
  106. package/dist/approvals/schema.js +70 -0
  107. package/dist/approvals/schema.js.map +1 -0
  108. package/dist/approvals/types.d.ts +47 -0
  109. package/dist/approvals/types.js +2 -0
  110. package/dist/approvals/types.js.map +1 -0
  111. package/dist/cli/args.d.ts +3 -2
  112. package/dist/cli/args.js +24 -3
  113. package/dist/cli/args.js.map +1 -1
  114. package/dist/cli/dashboard-client.d.ts +5 -1
  115. package/dist/cli/dashboard-client.js +162 -15
  116. package/dist/cli/dashboard-client.js.map +1 -1
  117. package/dist/cli/dashboard-controller.d.ts +7 -0
  118. package/dist/cli/dashboard-controller.js +27 -0
  119. package/dist/cli/dashboard-controller.js.map +1 -0
  120. package/dist/cli/dashboard-detail-view.d.ts +16 -0
  121. package/dist/cli/dashboard-detail-view.js +162 -0
  122. package/dist/cli/dashboard-detail-view.js.map +1 -0
  123. package/dist/cli/dashboard-history-view.d.ts +29 -0
  124. package/dist/cli/dashboard-history-view.js +211 -0
  125. package/dist/cli/dashboard-history-view.js.map +1 -0
  126. package/dist/cli/dashboard-input.d.ts +14 -0
  127. package/dist/cli/dashboard-input.js +128 -0
  128. package/dist/cli/dashboard-input.js.map +1 -0
  129. package/dist/cli/dashboard-layout.d.ts +12 -1
  130. package/dist/cli/dashboard-layout.js +32 -30
  131. package/dist/cli/dashboard-layout.js.map +1 -1
  132. package/dist/cli/dashboard-renderer.d.ts +30 -2
  133. package/dist/cli/dashboard-renderer.js +125 -7
  134. package/dist/cli/dashboard-renderer.js.map +1 -1
  135. package/dist/cli/dashboard-style.d.ts +9 -0
  136. package/dist/cli/dashboard-style.js +36 -0
  137. package/dist/cli/dashboard-style.js.map +1 -0
  138. package/dist/cli/dashboard.d.ts +5 -3
  139. package/dist/cli/dashboard.js +102 -12
  140. package/dist/cli/dashboard.js.map +1 -1
  141. package/dist/cli/help.d.ts +1 -1
  142. package/dist/cli/help.js +37 -12
  143. package/dist/cli/help.js.map +1 -1
  144. package/dist/cli/input.d.ts +1 -0
  145. package/dist/cli/input.js +23 -17
  146. package/dist/cli/input.js.map +1 -1
  147. package/dist/cli/main.js +6 -2
  148. package/dist/cli/main.js.map +1 -1
  149. package/dist/cli/output.js +17 -1
  150. package/dist/cli/output.js.map +1 -1
  151. package/dist/config/access-provenance.d.ts +10 -0
  152. package/dist/config/access-provenance.js +31 -0
  153. package/dist/config/access-provenance.js.map +1 -0
  154. package/dist/config/access.d.ts +12 -2
  155. package/dist/config/access.js +125 -27
  156. package/dist/config/access.js.map +1 -1
  157. package/dist/config/loader.js +1 -0
  158. package/dist/config/loader.js.map +1 -1
  159. package/dist/config/oidc.js +4 -2
  160. package/dist/config/oidc.js.map +1 -1
  161. package/dist/config/project.js +2 -1
  162. package/dist/config/project.js.map +1 -1
  163. package/dist/config/resolve-access.js +24 -10
  164. package/dist/config/resolve-access.js.map +1 -1
  165. package/dist/index.d.ts +2 -2
  166. package/dist/index.js +1 -1
  167. package/dist/index.js.map +1 -1
  168. package/dist/process-diagnostics.d.ts +2 -0
  169. package/dist/process-diagnostics.js +11 -0
  170. package/dist/process-diagnostics.js.map +1 -0
  171. package/dist/runtime/access-errors.d.ts +4 -0
  172. package/dist/runtime/access-errors.js +25 -0
  173. package/dist/runtime/access-errors.js.map +1 -0
  174. package/dist/runtime/approval-registry.d.ts +27 -0
  175. package/dist/runtime/approval-registry.js +126 -0
  176. package/dist/runtime/approval-registry.js.map +1 -0
  177. package/dist/runtime/client.js +54 -14
  178. package/dist/runtime/client.js.map +1 -1
  179. package/dist/runtime/coordinator.d.ts +17 -1
  180. package/dist/runtime/coordinator.js +124 -6
  181. package/dist/runtime/coordinator.js.map +1 -1
  182. package/dist/runtime/dashboard-sanitize.d.ts +2 -0
  183. package/dist/runtime/dashboard-sanitize.js +7 -0
  184. package/dist/runtime/dashboard-sanitize.js.map +1 -0
  185. package/dist/runtime/dashboard.d.ts +43 -1
  186. package/dist/runtime/dashboard.js +2 -3
  187. package/dist/runtime/dashboard.js.map +1 -1
  188. package/dist/runtime/definition.js +11 -1
  189. package/dist/runtime/definition.js.map +1 -1
  190. package/dist/runtime/select-native.d.ts +2 -1
  191. package/dist/runtime/select-native.js +28 -10
  192. package/dist/runtime/select-native.js.map +1 -1
  193. package/dist/runtime/service.js +24 -1
  194. package/dist/runtime/service.js.map +1 -1
  195. package/dist/runtime/types.d.ts +9 -1
  196. package/dist/runtime/types.js.map +1 -1
  197. package/dist/runtime/worker-approvals.d.ts +21 -0
  198. package/dist/runtime/worker-approvals.js +51 -0
  199. package/dist/runtime/worker-approvals.js.map +1 -0
  200. package/dist/runtime/worker-client.js +73 -20
  201. package/dist/runtime/worker-client.js.map +1 -1
  202. package/dist/runtime/worker-server.d.ts +2 -0
  203. package/dist/runtime/worker-server.js +39 -10
  204. package/dist/runtime/worker-server.js.map +1 -1
  205. package/dist/sdk/definitions.d.ts +4 -1
  206. package/dist/sdk/definitions.js +16 -2
  207. package/dist/sdk/definitions.js.map +1 -1
  208. package/dist/sdk/permission-validation.js +10 -1
  209. package/dist/sdk/permission-validation.js.map +1 -1
  210. package/dist/sdk/types.d.ts +29 -1
  211. package/dist/sdk/types.js.map +1 -1
  212. package/package.json +4 -1
  213. package/sdk/access-config.md +86 -4
  214. package/sdk/adapter-contract.md +7 -13
  215. package/sdk/additional-harnesses.md +43 -0
  216. package/sdk/agent-skill.md +7 -4
  217. package/sdk/agent.md +5 -3
  218. package/sdk/approvals.md +120 -0
  219. package/sdk/authentication.md +1 -1
  220. package/sdk/cli/dashboard-design.md +44 -4
  221. package/sdk/cli/dashboard-inspection.md +25 -0
  222. package/sdk/cli/index.md +20 -11
  223. package/sdk/cli/output.md +10 -4
  224. package/sdk/completion-notifications.md +2 -0
  225. package/sdk/config.md +1 -1
  226. package/sdk/copilot.md +53 -0
  227. package/sdk/cursor.md +68 -0
  228. package/sdk/diagnostics.md +29 -0
  229. package/sdk/distribution.md +53 -1
  230. package/sdk/evals.md +10 -4
  231. package/sdk/fx.md +51 -6
  232. package/sdk/harnesses.md +13 -13
  233. package/sdk/index.md +4 -2
  234. package/sdk/message-delivery.md +1 -1
  235. package/sdk/opencode.md +57 -0
  236. package/sdk/permissions.md +7 -5
  237. package/sdk/pr-integration.md +1 -1
  238. package/sdk/project-team.md +2 -0
  239. package/sdk/sessions.md +4 -1
  240. package/sdk/tools.md +1 -1
  241. package/sdk/v1-runtime.md +6 -6
@@ -1,62 +1,145 @@
1
- import { appendFile, mkdir, readFile } from "node:fs/promises";
1
+ import { appendFile, lstat, mkdir, readFile } from "node:fs/promises";
2
2
  import { dirname, join } from "node:path";
3
3
  import { AgentError } from "../errors.js";
4
4
  import { git, resolveProject } from "./project.js";
5
+ import { quoteDiagnosticPath, recordAccessProvenance } from "./access-provenance.js";
5
6
  export { resolveAccess } from "./resolve-access.js";
6
7
  export function configError(message) {
7
8
  throw new AgentError("INVALID_CONFIG", message);
8
9
  }
10
+ function schemaError(location, message, file, code = "INVALID_CONFIG") {
11
+ const settings = file === undefined
12
+ ? "Personal access settings"
13
+ : `Personal access settings at ${quoteDiagnosticPath(file)}`;
14
+ throw new AgentError(code, `${settings} are invalid at ${location}: ${message}`);
15
+ }
9
16
  function record(value) {
10
17
  return value !== null && typeof value === "object" && !Array.isArray(value);
11
18
  }
12
- export function parseConnection(value) {
13
- if (!record(value) || !["subscription", "api-key", "vercel-api-key", "vercel-oidc"].includes(value.type))
14
- configError("Unknown access connection type.");
15
- const allowed = value.type === "subscription" ? ["type"] : ["type", "env", "envFile", ...(value.type === "vercel-oidc" ? ["project"] : [])];
19
+ function parseConnectionAt(value, location, file, harness) {
20
+ if (!record(value))
21
+ schemaError(location, "Connection must be an object.", file);
22
+ if (!["subscription", "api-key", "vercel-api-key", "vercel-oidc"].includes(value.type))
23
+ schemaError(`${location}.type`, "Unknown access connection type.", file);
24
+ const allowed = value.type === "subscription" ? ["type", "provider"] : ["type", "env", "envFile", ...(value.type === "api-key" ? ["provider", "baseUrl", "wireApi", "apiVersion", "wireModel"] : value.type === "vercel-oidc" ? ["project"] : [])];
16
25
  if (Object.keys(value).some((key) => !allowed.includes(key)))
17
- configError("Unknown access connection field; secrets must be supplied through environment references.");
26
+ schemaError(location, "Unknown access connection field; secrets must be supplied through environment references.", file);
18
27
  if (value.env !== undefined && (typeof value.env !== "string" || !/^[A-Za-z_][A-Za-z0-9_]*$/.test(value.env)))
19
- configError("Invalid credential variable name.");
20
- for (const field of ["envFile", "project"]) {
28
+ schemaError(`${location}.env`, "Invalid credential variable name.", file);
29
+ for (const field of ["envFile", "project", "provider", "baseUrl", "apiVersion", "wireModel"]) {
21
30
  if (value[field] !== undefined && (typeof value[field] !== "string" || !value[field].trim()))
22
- configError(`${field} must be a nonempty path.`);
31
+ schemaError(`${location}.${field}`, field === "envFile" || field === "project" ? "Value must be a nonempty path." : "Value must be a nonempty string.", file);
23
32
  }
33
+ if (value.wireApi !== undefined && !["completions", "responses"].includes(value.wireApi))
34
+ schemaError(`${location}.wireApi`, "Invalid wire API selection.", file);
35
+ if (value.baseUrl !== undefined)
36
+ validateBaseUrl(value.baseUrl, `${location}.baseUrl`, file);
37
+ if (harness)
38
+ validateHarnessConnection(value, harness, location, file);
24
39
  return value;
25
40
  }
26
- export function parseAccess(value) {
41
+ function validateBaseUrl(value, location, file) {
42
+ let url;
43
+ try {
44
+ url = new URL(value);
45
+ }
46
+ catch {
47
+ return schemaError(location, "baseUrl must be an absolute HTTP or HTTPS URL without credentials, query, or fragment.", file);
48
+ }
49
+ if (!/^https?:\/\//iu.test(value) || value.trim() !== value || /[\s?#\\]/u.test(value)
50
+ || !["http:", "https:"].includes(url.protocol) || !url.hostname || url.username || url.password
51
+ || /^https?:\/\/[^/]*@/iu.test(value)) {
52
+ schemaError(location, "baseUrl must be an absolute HTTP or HTTPS URL without credentials, query, or fragment.", file);
53
+ }
54
+ }
55
+ function validateHarnessConnection(connection, kind, location, file) {
56
+ if (connection.type === "vercel-api-key" || connection.type === "vercel-oidc") {
57
+ if (kind === "cursor")
58
+ schemaError(`${location}.type`, `cursor does not support ${connection.type} access.`, file, "UNSUPPORTED_OPTION");
59
+ return;
60
+ }
61
+ if (connection.type === "subscription") {
62
+ const providers = kind === "fx" ? ["gateway", "codex", "grok"]
63
+ : kind === "opencode" ? ["openai", "github-copilot", "xai"] : undefined;
64
+ if (providers ? !providers.includes(connection.provider ?? "") : connection.provider !== undefined) {
65
+ schemaError(`${location}.provider`, `${kind} requires its documented native login provider selection.`, file);
66
+ }
67
+ return;
68
+ }
69
+ let allowed = ["type", "env", "envFile"];
70
+ if (kind === "fx" || kind === "opencode") {
71
+ if (!connection.provider)
72
+ schemaError(`${location}.provider`, `${kind} direct API keys require provider and env.`, file);
73
+ if (!connection.env)
74
+ schemaError(`${location}.env`, `${kind} direct API keys require provider and env.`, file);
75
+ allowed.push("provider");
76
+ if (kind === "fx" && ["gateway", "codex", "grok"].includes(connection.provider))
77
+ schemaError(`${location}.provider`, "fx direct API keys require a named custom connection.", file);
78
+ if (kind === "opencode") {
79
+ if (!["anthropic", "openai", "google", "groq", "openrouter", "xai", "mistral", "cohere", "opencode", "opencode-go"].includes(connection.provider))
80
+ schemaError(`${location}.provider`, "Unsupported OpenCode API-key provider.", file);
81
+ allowed.push("baseUrl");
82
+ }
83
+ }
84
+ else if (kind === "copilot") {
85
+ if (!["github", "openai", "anthropic", "azure"].includes(connection.provider ?? ""))
86
+ schemaError(`${location}.provider`, "Copilot API keys require a supported provider.", file);
87
+ allowed.push("provider");
88
+ if (connection.provider !== "github")
89
+ allowed.push("baseUrl", "wireModel");
90
+ if (connection.provider === "openai" || connection.provider === "azure")
91
+ allowed.push("wireApi");
92
+ if (connection.provider === "azure") {
93
+ allowed.push("apiVersion");
94
+ if (!connection.baseUrl)
95
+ schemaError(`${location}.baseUrl`, "Copilot Azure API keys require baseUrl.", file);
96
+ }
97
+ }
98
+ const inapplicable = ["provider", "baseUrl", "wireApi", "apiVersion", "wireModel"]
99
+ .find(field => Object.hasOwn(connection, field) && !allowed.includes(field));
100
+ if (inapplicable)
101
+ schemaError(`${location}.${inapplicable}`, `Inapplicable ${kind} API-key connection field.`, file);
102
+ }
103
+ export function parseConnection(value, harness) {
104
+ return parseConnectionAt(value, "connection", undefined, harness);
105
+ }
106
+ function parseAccessAt(value, file) {
27
107
  if (!record(value) || Object.keys(value).some((key) => key !== "access"))
28
- configError("Personal settings must contain only an optional access map.");
108
+ schemaError("root object", "Expected only an optional access map.", file);
29
109
  if (value.access === undefined)
30
110
  return {};
31
111
  if (!record(value.access))
32
- configError("access must be an object.");
112
+ schemaError("access", "Value must be an object.", file);
33
113
  for (const [kind, connections] of Object.entries(value.access)) {
34
- if (kind !== "codex" && kind !== "claudeCode" && kind !== "fx")
35
- configError("Unsupported harness in personal access settings.");
114
+ if (!["codex", "claudeCode", "fx", "opencode", "copilot", "cursor"].includes(kind))
115
+ schemaError("access", "Unsupported harness entry.", file);
36
116
  if (!Array.isArray(connections))
37
- configError("Each harness access selection must be an array.");
38
- connections.forEach(parseConnection);
117
+ schemaError(`access.${kind}`, "Harness access selection must be an array.", file);
118
+ connections.forEach((connection, index) => parseConnectionAt(connection, `access.${kind}[${index}]`, file, kind));
39
119
  }
40
120
  return value;
41
121
  }
42
- async function ensureExcluded(mainRoot) {
122
+ export function parseAccess(value) {
123
+ return parseAccessAt(value);
124
+ }
125
+ async function ensureExcluded(mainRoot, file) {
43
126
  try {
44
127
  await git(mainRoot, ["rev-parse", "--git-dir"]);
45
128
  }
46
129
  catch (error) {
47
130
  if (error instanceof Error && error.message.includes("not a git repository"))
48
131
  return;
49
- configError("Cannot inspect Git configuration for personal settings.");
132
+ settingsFileError(file, "Cannot inspect Git configuration for personal settings");
50
133
  }
51
134
  let tracked = false;
52
135
  try {
53
136
  tracked = !!await git(mainRoot, ["ls-files", "--", ".subharness/agents.local.json"]);
54
137
  }
55
138
  catch {
56
- configError("Cannot inspect whether personal access settings are tracked by Git.");
139
+ settingsFileError(file, "Cannot inspect whether personal access settings are tracked by Git");
57
140
  }
58
141
  if (tracked)
59
- configError("Personal access settings are tracked. Remove .subharness/agents.local.json from the Git index before using it.");
142
+ settingsFileError(file, "Personal access settings are tracked. Remove .subharness/agents.local.json from the Git index before using it");
60
143
  const exclude = await git(mainRoot, ["rev-parse", "--path-format=absolute", "--git-path", "info/exclude"]);
61
144
  let contents = "";
62
145
  try {
@@ -64,7 +147,7 @@ async function ensureExcluded(mainRoot) {
64
147
  }
65
148
  catch (error) {
66
149
  if (error.code !== "ENOENT")
67
- configError("Cannot read Git's local exclude file.");
150
+ settingsFileError(file, "Cannot read Git's local exclude file");
68
151
  }
69
152
  const pattern = "/.subharness/agents.local.json";
70
153
  if (!contents.split(/\r?\n/).includes(pattern)) {
@@ -72,6 +155,9 @@ async function ensureExcluded(mainRoot) {
72
155
  await appendFile(exclude, `${contents.endsWith("\n") || !contents ? "" : "\n"}${pattern}\n`);
73
156
  }
74
157
  }
158
+ function settingsFileError(file, message) {
159
+ configError(`${message}. Personal access settings file: ${quoteDiagnosticPath(file)}.`);
160
+ }
75
161
  export async function loadAccess(cwd) {
76
162
  const { mainRoot } = await resolveProject(cwd);
77
163
  const file = join(mainRoot, ".subharness", "agents.local.json");
@@ -80,18 +166,30 @@ export async function loadAccess(cwd) {
80
166
  contents = await readFile(file, "utf8");
81
167
  }
82
168
  catch (error) {
83
- if (error.code === "ENOENT")
84
- return {};
85
- configError(`Cannot read personal settings: ${file}`);
169
+ if (error.code === "ENOENT") {
170
+ const settings = {};
171
+ const legacyFile = join(mainRoot, ".agents", "agents.local.json");
172
+ let legacyEntry;
173
+ try {
174
+ await lstat(legacyFile);
175
+ legacyEntry = legacyFile;
176
+ }
177
+ catch { /* Legacy inspection must not replace the original startup error. */ }
178
+ recordAccessProvenance(settings, { file, present: false, ...(legacyEntry ? { legacyFile: legacyEntry } : {}) });
179
+ return settings;
180
+ }
181
+ settingsFileError(file, "Cannot read personal settings");
86
182
  }
87
- await ensureExcluded(mainRoot);
183
+ await ensureExcluded(mainRoot, file);
88
184
  let value;
89
185
  try {
90
186
  value = JSON.parse(contents);
91
187
  }
92
188
  catch {
93
- configError(`Invalid JSON in personal settings: ${file}`);
189
+ settingsFileError(file, "Invalid JSON in personal settings");
94
190
  }
95
- return parseAccess(value);
191
+ const settings = parseAccessAt(value, file);
192
+ recordAccessProvenance(settings, { file, present: true });
193
+ return settings;
96
194
  }
97
195
  //# sourceMappingURL=access.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"access.js","sourceRoot":"","sources":["../../src/config/access.ts"],"names":[],"mappings":"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","sourcesContent":["import { appendFile, mkdir, readFile } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport { AgentError } from \"../errors.js\";\nimport type { HarnessKind } from \"../sdk/types.js\";\nimport { git, resolveProject } from \"./project.js\";\nexport { resolveAccess } from \"./resolve-access.js\";\n\nexport type AccessConnection =\n | { type: \"subscription\" }\n | { type: \"api-key\" | \"vercel-api-key\"; env?: string; envFile?: string }\n | { type: \"vercel-oidc\"; env?: string; envFile?: string; project?: string };\n\nexport interface AccessConfig {\n access?: Partial<Record<HarnessKind, AccessConnection[]>>;\n}\n\nexport function configError(message: string): never {\n throw new AgentError(\"INVALID_CONFIG\", message);\n}\n\nfunction record(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === \"object\" && !Array.isArray(value);\n}\n\nexport function parseConnection(value: unknown): AccessConnection {\n if (!record(value) || ![\"subscription\", \"api-key\", \"vercel-api-key\", \"vercel-oidc\"].includes(value.type as string)) configError(\"Unknown access connection type.\");\n const allowed = value.type === \"subscription\" ? [\"type\"] : [\"type\", \"env\", \"envFile\", ...(value.type === \"vercel-oidc\" ? [\"project\"] : [])];\n if (Object.keys(value).some((key) => !allowed.includes(key))) configError(\"Unknown access connection field; secrets must be supplied through environment references.\");\n if (value.env !== undefined && (typeof value.env !== \"string\" || !/^[A-Za-z_][A-Za-z0-9_]*$/.test(value.env))) configError(\"Invalid credential variable name.\");\n for (const field of [\"envFile\", \"project\"]) {\n if (value[field] !== undefined && (typeof value[field] !== \"string\" || !value[field].trim())) configError(`${field} must be a nonempty path.`);\n }\n return value as AccessConnection;\n}\n\nexport function parseAccess(value: unknown): AccessConfig {\n if (!record(value) || Object.keys(value).some((key) => key !== \"access\")) configError(\"Personal settings must contain only an optional access map.\");\n if (value.access === undefined) return {};\n if (!record(value.access)) configError(\"access must be an object.\");\n for (const [kind, connections] of Object.entries(value.access)) {\n if (kind !== \"codex\" && kind !== \"claudeCode\" && kind !== \"fx\") configError(\"Unsupported harness in personal access settings.\");\n if (!Array.isArray(connections)) configError(\"Each harness access selection must be an array.\");\n connections.forEach(parseConnection);\n }\n return value as AccessConfig;\n}\n\nasync function ensureExcluded(mainRoot: string): Promise<void> {\n try { await git(mainRoot, [\"rev-parse\", \"--git-dir\"]); }\n catch (error) {\n if (error instanceof Error && error.message.includes(\"not a git repository\")) return;\n configError(\"Cannot inspect Git configuration for personal settings.\");\n }\n let tracked = false;\n try { tracked = !!await git(mainRoot, [\"ls-files\", \"--\", \".subharness/agents.local.json\"]); }\n catch { configError(\"Cannot inspect whether personal access settings are tracked by Git.\"); }\n if (tracked) configError(\"Personal access settings are tracked. Remove .subharness/agents.local.json from the Git index before using it.\");\n const exclude = await git(mainRoot, [\"rev-parse\", \"--path-format=absolute\", \"--git-path\", \"info/exclude\"]);\n let contents = \"\";\n try { contents = await readFile(exclude, \"utf8\"); }\n catch (error) { if ((error as NodeJS.ErrnoException).code !== \"ENOENT\") configError(\"Cannot read Git's local exclude file.\"); }\n const pattern = \"/.subharness/agents.local.json\";\n if (!contents.split(/\\r?\\n/).includes(pattern)) {\n await mkdir(dirname(exclude), { recursive: true });\n await appendFile(exclude, `${contents.endsWith(\"\\n\") || !contents ? \"\" : \"\\n\"}${pattern}\\n`);\n }\n}\n\nexport async function loadAccess(cwd: string): Promise<AccessConfig> {\n const { mainRoot } = await resolveProject(cwd);\n const file = join(mainRoot, \".subharness\", \"agents.local.json\");\n let contents: string;\n try { contents = await readFile(file, \"utf8\"); }\n catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return {};\n configError(`Cannot read personal settings: ${file}`);\n }\n await ensureExcluded(mainRoot);\n let value: unknown;\n try { value = JSON.parse(contents); }\n catch { configError(`Invalid JSON in personal settings: ${file}`); }\n return parseAccess(value);\n}\n"]}
1
+ {"version":3,"file":"access.js","sourceRoot":"","sources":["../../src/config/access.ts"],"names":[],"mappings":"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","sourcesContent":["import { appendFile, lstat, mkdir, readFile } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport { AgentError } from \"../errors.js\";\nimport type { HarnessKind } from \"../sdk/types.js\";\nimport { git, resolveProject } from \"./project.js\";\nimport { quoteDiagnosticPath, recordAccessProvenance } from \"./access-provenance.js\";\nexport { resolveAccess } from \"./resolve-access.js\";\n\nexport type AccessConnection =\n | { type: \"subscription\"; provider?: string }\n | { type: \"api-key\"; provider?: string; env?: string; envFile?: string; baseUrl?: string;\n wireApi?: \"completions\" | \"responses\"; apiVersion?: string; wireModel?: string }\n | { type: \"vercel-api-key\"; env?: string; envFile?: string }\n | { type: \"vercel-oidc\"; env?: string; envFile?: string; project?: string };\n\nexport interface AccessConfig {\n access?: Partial<Record<HarnessKind, AccessConnection[]>>;\n}\n\nexport function configError(message: string): never {\n throw new AgentError(\"INVALID_CONFIG\", message);\n}\n\nfunction schemaError(location: string, message: string, file?: string, code = \"INVALID_CONFIG\"): never {\n const settings = file === undefined\n ? \"Personal access settings\"\n : `Personal access settings at ${quoteDiagnosticPath(file)}`;\n throw new AgentError(code, `${settings} are invalid at ${location}: ${message}`);\n}\n\nfunction record(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction parseConnectionAt(value: unknown, location: string, file?: string, harness?: HarnessKind): AccessConnection {\n if (!record(value)) schemaError(location, \"Connection must be an object.\", file);\n if (![\"subscription\", \"api-key\", \"vercel-api-key\", \"vercel-oidc\"].includes(value.type as string)) schemaError(`${location}.type`, \"Unknown access connection type.\", file);\n const allowed = value.type === \"subscription\" ? [\"type\", \"provider\"] : [\"type\", \"env\", \"envFile\", ...(value.type === \"api-key\" ? [\"provider\", \"baseUrl\", \"wireApi\", \"apiVersion\", \"wireModel\"] : value.type === \"vercel-oidc\" ? [\"project\"] : [])];\n if (Object.keys(value).some((key) => !allowed.includes(key))) schemaError(location, \"Unknown access connection field; secrets must be supplied through environment references.\", file);\n if (value.env !== undefined && (typeof value.env !== \"string\" || !/^[A-Za-z_][A-Za-z0-9_]*$/.test(value.env))) schemaError(`${location}.env`, \"Invalid credential variable name.\", file);\n for (const field of [\"envFile\", \"project\", \"provider\", \"baseUrl\", \"apiVersion\", \"wireModel\"]) {\n if (value[field] !== undefined && (typeof value[field] !== \"string\" || !value[field].trim())) schemaError(`${location}.${field}`, field === \"envFile\" || field === \"project\" ? \"Value must be a nonempty path.\" : \"Value must be a nonempty string.\", file);\n }\n if (value.wireApi !== undefined && ![\"completions\", \"responses\"].includes(value.wireApi as string)) schemaError(`${location}.wireApi`, \"Invalid wire API selection.\", file);\n if (value.baseUrl !== undefined) validateBaseUrl(value.baseUrl as string, `${location}.baseUrl`, file);\n if (harness) validateHarnessConnection(value as AccessConnection, harness, location, file);\n return value as AccessConnection;\n}\n\nfunction validateBaseUrl(value: string, location: string, file?: string): void {\n let url: URL;\n try { url = new URL(value); } catch { return schemaError(location, \"baseUrl must be an absolute HTTP or HTTPS URL without credentials, query, or fragment.\", file); }\n if (!/^https?:\\/\\//iu.test(value) || value.trim() !== value || /[\\s?#\\\\]/u.test(value)\n || ![\"http:\", \"https:\"].includes(url.protocol) || !url.hostname || url.username || url.password\n || /^https?:\\/\\/[^/]*@/iu.test(value)) {\n schemaError(location, \"baseUrl must be an absolute HTTP or HTTPS URL without credentials, query, or fragment.\", file);\n }\n}\n\nfunction validateHarnessConnection(connection: AccessConnection, kind: HarnessKind, location: string, file?: string): void {\n if (connection.type === \"vercel-api-key\" || connection.type === \"vercel-oidc\") {\n if (kind === \"cursor\") schemaError(`${location}.type`, `cursor does not support ${connection.type} access.`, file, \"UNSUPPORTED_OPTION\");\n return;\n }\n if (connection.type === \"subscription\") {\n const providers = kind === \"fx\" ? [\"gateway\", \"codex\", \"grok\"]\n : kind === \"opencode\" ? [\"openai\", \"github-copilot\", \"xai\"] : undefined;\n if (providers ? !providers.includes(connection.provider ?? \"\") : connection.provider !== undefined) {\n schemaError(`${location}.provider`, `${kind} requires its documented native login provider selection.`, file);\n }\n return;\n }\n let allowed: string[] = [\"type\", \"env\", \"envFile\"];\n if (kind === \"fx\" || kind === \"opencode\") {\n if (!connection.provider) schemaError(`${location}.provider`, `${kind} direct API keys require provider and env.`, file);\n if (!connection.env) schemaError(`${location}.env`, `${kind} direct API keys require provider and env.`, file);\n allowed.push(\"provider\");\n if (kind === \"fx\" && [\"gateway\", \"codex\", \"grok\"].includes(connection.provider)) schemaError(`${location}.provider`, \"fx direct API keys require a named custom connection.\", file);\n if (kind === \"opencode\") {\n if (![\"anthropic\", \"openai\", \"google\", \"groq\", \"openrouter\", \"xai\", \"mistral\", \"cohere\", \"opencode\", \"opencode-go\"].includes(connection.provider)) schemaError(`${location}.provider`, \"Unsupported OpenCode API-key provider.\", file);\n allowed.push(\"baseUrl\");\n }\n } else if (kind === \"copilot\") {\n if (![\"github\", \"openai\", \"anthropic\", \"azure\"].includes(connection.provider ?? \"\")) schemaError(`${location}.provider`, \"Copilot API keys require a supported provider.\", file);\n allowed.push(\"provider\");\n if (connection.provider !== \"github\") allowed.push(\"baseUrl\", \"wireModel\");\n if (connection.provider === \"openai\" || connection.provider === \"azure\") allowed.push(\"wireApi\");\n if (connection.provider === \"azure\") {\n allowed.push(\"apiVersion\");\n if (!connection.baseUrl) schemaError(`${location}.baseUrl`, \"Copilot Azure API keys require baseUrl.\", file);\n }\n }\n const inapplicable = [\"provider\", \"baseUrl\", \"wireApi\", \"apiVersion\", \"wireModel\"]\n .find(field => Object.hasOwn(connection, field) && !allowed.includes(field));\n if (inapplicable) schemaError(`${location}.${inapplicable}`, `Inapplicable ${kind} API-key connection field.`, file);\n}\n\nexport function parseConnection(value: unknown, harness?: HarnessKind): AccessConnection {\n return parseConnectionAt(value, \"connection\", undefined, harness);\n}\n\nfunction parseAccessAt(value: unknown, file?: string): AccessConfig {\n if (!record(value) || Object.keys(value).some((key) => key !== \"access\")) schemaError(\"root object\", \"Expected only an optional access map.\", file);\n if (value.access === undefined) return {};\n if (!record(value.access)) schemaError(\"access\", \"Value must be an object.\", file);\n for (const [kind, connections] of Object.entries(value.access)) {\n if (![\"codex\", \"claudeCode\", \"fx\", \"opencode\", \"copilot\", \"cursor\"].includes(kind)) schemaError(\"access\", \"Unsupported harness entry.\", file);\n if (!Array.isArray(connections)) schemaError(`access.${kind}`, \"Harness access selection must be an array.\", file);\n connections.forEach((connection, index) => parseConnectionAt(connection, `access.${kind}[${index}]`, file, kind as HarnessKind));\n }\n return value as AccessConfig;\n}\n\nexport function parseAccess(value: unknown): AccessConfig {\n return parseAccessAt(value);\n}\n\nasync function ensureExcluded(mainRoot: string, file: string): Promise<void> {\n try { await git(mainRoot, [\"rev-parse\", \"--git-dir\"]); }\n catch (error) {\n if (error instanceof Error && error.message.includes(\"not a git repository\")) return;\n settingsFileError(file, \"Cannot inspect Git configuration for personal settings\");\n }\n let tracked = false;\n try { tracked = !!await git(mainRoot, [\"ls-files\", \"--\", \".subharness/agents.local.json\"]); }\n catch { settingsFileError(file, \"Cannot inspect whether personal access settings are tracked by Git\"); }\n if (tracked) settingsFileError(file, \"Personal access settings are tracked. Remove .subharness/agents.local.json from the Git index before using it\");\n const exclude = await git(mainRoot, [\"rev-parse\", \"--path-format=absolute\", \"--git-path\", \"info/exclude\"]);\n let contents = \"\";\n try { contents = await readFile(exclude, \"utf8\"); }\n catch (error) { if ((error as NodeJS.ErrnoException).code !== \"ENOENT\") settingsFileError(file, \"Cannot read Git's local exclude file\"); }\n const pattern = \"/.subharness/agents.local.json\";\n if (!contents.split(/\\r?\\n/).includes(pattern)) {\n await mkdir(dirname(exclude), { recursive: true });\n await appendFile(exclude, `${contents.endsWith(\"\\n\") || !contents ? \"\" : \"\\n\"}${pattern}\\n`);\n }\n}\n\nfunction settingsFileError(file: string, message: string): never {\n configError(`${message}. Personal access settings file: ${quoteDiagnosticPath(file)}.`);\n}\n\nexport async function loadAccess(cwd: string): Promise<AccessConfig> {\n const { mainRoot } = await resolveProject(cwd);\n const file = join(mainRoot, \".subharness\", \"agents.local.json\");\n let contents: string;\n try { contents = await readFile(file, \"utf8\"); }\n catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") {\n const settings: AccessConfig = {};\n const legacyFile = join(mainRoot, \".agents\", \"agents.local.json\");\n let legacyEntry: string | undefined;\n try { await lstat(legacyFile); legacyEntry = legacyFile; }\n catch { /* Legacy inspection must not replace the original startup error. */ }\n recordAccessProvenance(settings, {file, present: false, ...(legacyEntry ? {legacyFile: legacyEntry} : {})});\n return settings;\n }\n settingsFileError(file, \"Cannot read personal settings\");\n }\n await ensureExcluded(mainRoot, file);\n let value: unknown;\n try { value = JSON.parse(contents); }\n catch { settingsFileError(file, \"Invalid JSON in personal settings\"); }\n const settings = parseAccessAt(value, file);\n recordAccessProvenance(settings, {file, present: true});\n return settings;\n}\n"]}
@@ -13,6 +13,7 @@ const safeDiagnostics = new Set([
13
13
  "Names must start with a letter and contain at most 64 letters, digits, underscores, or hyphens.",
14
14
  "fast must be a boolean.", "Unsupported harness kind.", "At least one harness is required.",
15
15
  "Claude effort must be low, medium, high, xhigh, or max.",
16
+ "Cursor sandboxMode must be enabled or disabled.",
16
17
  "Subagent definitions cannot contain recursive references.",
17
18
  "Tools require a Zod 4 object schema.", "Tool execute must be a function.",
18
19
  "Tool input schema cannot be represented as JSON Schema.",
@@ -1 +1 @@
1
- {"version":3,"file":"loader.js","sourceRoot":"","sources":["../../src/config/loader.ts"],"names":[],"mappings":"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","sourcesContent":["import { readdir } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { join, resolve } from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport { tsImport } from \"tsx/esm/api\";\nimport { AgentError } from \"../errors.js\";\nimport { agent } from \"../sdk/definitions.js\";\nimport type { AgentDefinition } from \"../sdk/types.js\";\nimport { resolveProject } from \"./project.js\";\n\nexport interface CatalogEntry {\n id: string;\n name: string;\n description: string;\n scope: \"repo\" | \"global\";\n file: string;\n definition: AgentDefinition;\n}\n\n// Only fixed SDK diagnostics may cross the module-evaluation boundary.\n// User exceptions can contain credential values, including exceptions named AgentError.\nconst safeDiagnostics = new Set([\n \"Expected a configuration object.\", \"Unknown configuration field.\",\n \"Names must start with a letter and contain at most 64 letters, digits, underscores, or hyphens.\",\n \"fast must be a boolean.\", \"Unsupported harness kind.\", \"At least one harness is required.\",\n \"Claude effort must be low, medium, high, xhigh, or max.\",\n \"Subagent definitions cannot contain recursive references.\",\n \"Tools require a Zod 4 object schema.\", \"Tool execute must be a function.\",\n \"Tool input schema cannot be represented as JSON Schema.\",\n ...[\"Model\", \"Effort\", \"Description\", \"Instructions\", \"Tool description\"].map(label => `${label} must be a nonempty string.`),\n]);\n\nexport async function loadDefinition(file: string, subagents: string[] = []): Promise<AgentDefinition> {\n let definition: AgentDefinition;\n try {\n const url = pathToFileURL(resolve(file));\n url.searchParams.set(\"load\", crypto.randomUUID());\n const loaded = await tsImport(url.href, import.meta.url);\n const exported = loaded.default?.__esModule ? loaded.default.default : loaded.default;\n definition = agent(exported);\n } catch (error) {\n const reason = error instanceof Error && safeDiagnostics.has(error.message)\n && (error as AgentError).code === \"INVALID_DEFINITION\" ? error.message\n : \"Definition could not be loaded. Check its imports, dependencies, and module initialization.\";\n throw new AgentError(\"INVALID_DEFINITION\", `${file}: ${reason}`);\n }\n for (const name of subagents) {\n const children = definition.subagents ?? {};\n const child = Object.hasOwn(children, name) ? children[name] : undefined;\n if (!child) throw new AgentError(\"INVALID_DEFINITION\", `${file}: Subagent '${name}' is not declared.`);\n definition = child;\n }\n return definition;\n}\n\nasync function files(directory: string): Promise<string[]> {\n let entries;\n try { entries = await readdir(directory, { withFileTypes: true }); }\n catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return [];\n throw new AgentError(\"INVALID_CONFIG\", `Cannot read agent directory: ${directory}`);\n }\n const found: string[] = [];\n for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {\n const file = join(directory, entry.name);\n if (entry.isDirectory()) found.push(...await files(file));\n else if (entry.isFile() && entry.name.endsWith(\".ts\") && !entry.name.endsWith(\".d.ts\")) found.push(file);\n }\n return found;\n}\n\nexport async function discover(cwd: string, options: { home?: string } = {}): Promise<CatalogEntry[]> {\n const { root } = await resolveProject(cwd);\n const entries: CatalogEntry[] = [];\n for (const [scope, base] of [[\"repo\", root], [\"global\", options.home ?? homedir()]] as const) {\n const names = new Set<string>();\n for (const file of await files(join(base, \".subharness\", \"agents\"))) {\n const definition = await loadDefinition(file);\n if (names.has(definition.name)) throw new AgentError(\"INVALID_DEFINITION\", `${file}: Duplicate agent name '${definition.name}' in ${scope} scope.`);\n names.add(definition.name);\n entries.push({ id: `${scope}:${definition.name}`, name: definition.name, description: definition.description, scope, file, definition });\n }\n }\n return entries;\n}\n"]}
1
+ {"version":3,"file":"loader.js","sourceRoot":"","sources":["../../src/config/loader.ts"],"names":[],"mappings":"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","sourcesContent":["import { readdir } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { join, resolve } from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport { tsImport } from \"tsx/esm/api\";\nimport { AgentError } from \"../errors.js\";\nimport { agent } from \"../sdk/definitions.js\";\nimport type { AgentDefinition } from \"../sdk/types.js\";\nimport { resolveProject } from \"./project.js\";\n\nexport interface CatalogEntry {\n id: string;\n name: string;\n description: string;\n scope: \"repo\" | \"global\";\n file: string;\n definition: AgentDefinition;\n}\n\n// Only fixed SDK diagnostics may cross the module-evaluation boundary.\n// User exceptions can contain credential values, including exceptions named AgentError.\nconst safeDiagnostics = new Set([\n \"Expected a configuration object.\", \"Unknown configuration field.\",\n \"Names must start with a letter and contain at most 64 letters, digits, underscores, or hyphens.\",\n \"fast must be a boolean.\", \"Unsupported harness kind.\", \"At least one harness is required.\",\n \"Claude effort must be low, medium, high, xhigh, or max.\",\n \"Cursor sandboxMode must be enabled or disabled.\",\n \"Subagent definitions cannot contain recursive references.\",\n \"Tools require a Zod 4 object schema.\", \"Tool execute must be a function.\",\n \"Tool input schema cannot be represented as JSON Schema.\",\n ...[\"Model\", \"Effort\", \"Description\", \"Instructions\", \"Tool description\"].map(label => `${label} must be a nonempty string.`),\n]);\n\nexport async function loadDefinition(file: string, subagents: string[] = []): Promise<AgentDefinition> {\n let definition: AgentDefinition;\n try {\n const url = pathToFileURL(resolve(file));\n url.searchParams.set(\"load\", crypto.randomUUID());\n const loaded = await tsImport(url.href, import.meta.url);\n const exported = loaded.default?.__esModule ? loaded.default.default : loaded.default;\n definition = agent(exported);\n } catch (error) {\n const reason = error instanceof Error && safeDiagnostics.has(error.message)\n && (error as AgentError).code === \"INVALID_DEFINITION\" ? error.message\n : \"Definition could not be loaded. Check its imports, dependencies, and module initialization.\";\n throw new AgentError(\"INVALID_DEFINITION\", `${file}: ${reason}`);\n }\n for (const name of subagents) {\n const children = definition.subagents ?? {};\n const child = Object.hasOwn(children, name) ? children[name] : undefined;\n if (!child) throw new AgentError(\"INVALID_DEFINITION\", `${file}: Subagent '${name}' is not declared.`);\n definition = child;\n }\n return definition;\n}\n\nasync function files(directory: string): Promise<string[]> {\n let entries;\n try { entries = await readdir(directory, { withFileTypes: true }); }\n catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return [];\n throw new AgentError(\"INVALID_CONFIG\", `Cannot read agent directory: ${directory}`);\n }\n const found: string[] = [];\n for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {\n const file = join(directory, entry.name);\n if (entry.isDirectory()) found.push(...await files(file));\n else if (entry.isFile() && entry.name.endsWith(\".ts\") && !entry.name.endsWith(\".d.ts\")) found.push(file);\n }\n return found;\n}\n\nexport async function discover(cwd: string, options: { home?: string } = {}): Promise<CatalogEntry[]> {\n const { root } = await resolveProject(cwd);\n const entries: CatalogEntry[] = [];\n for (const [scope, base] of [[\"repo\", root], [\"global\", options.home ?? homedir()]] as const) {\n const names = new Set<string>();\n for (const file of await files(join(base, \".subharness\", \"agents\"))) {\n const definition = await loadDefinition(file);\n if (names.has(definition.name)) throw new AgentError(\"INVALID_DEFINITION\", `${file}: Duplicate agent name '${definition.name}' in ${scope} scope.`);\n names.add(definition.name);\n entries.push({ id: `${scope}:${definition.name}`, name: definition.name, description: definition.description, scope, file, definition });\n }\n }\n return entries;\n}\n"]}
@@ -44,8 +44,10 @@ export async function validateOidc(token, mainRoot, project, env) {
44
44
  || (claims.nbf !== undefined && (typeof claims.nbf !== "number" || !Number.isFinite(claims.nbf)))) {
45
45
  configError("Configured OIDC token has invalid validity claims.");
46
46
  }
47
- if (claims.exp <= now || (claims.nbf !== undefined && claims.nbf > now))
48
- configError("Configured OIDC token is expired or not yet valid; refresh its credential source.");
47
+ if (claims.exp <= now)
48
+ configError("Configured OIDC token has expired; refresh the configured credential source.");
49
+ if (claims.nbf !== undefined && claims.nbf > now)
50
+ configError("Configured OIDC token is not yet valid; check the system clock or wait until validity begins.");
49
51
  const expected = await projectIdentity(mainRoot, project, env);
50
52
  if (claims.project_id !== expected.projectId || claims.owner_id !== expected.orgId)
51
53
  configError("Configured OIDC token does not match the selected Vercel project and organization.");
@@ -1 +1 @@
1
- {"version":3,"file":"oidc.js","sourceRoot":"","sources":["../../src/config/oidc.ts"],"names":[],"mappings":"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","sourcesContent":["import { readFile } from \"node:fs/promises\";\nimport { resolve } from \"node:path\";\nimport { configError } from \"./access.js\";\n\ninterface ProjectIdentity { projectId: string; orgId: string }\n\nasync function projectIdentity(mainRoot: string, project: string | undefined, env: NodeJS.ProcessEnv): Promise<ProjectIdentity> {\n let content: string;\n try { content = await readFile(resolve(mainRoot, project ?? \".\", \".vercel/project.json\"), \"utf8\"); }\n catch (error) {\n if (project === undefined && (error as NodeJS.ErrnoException).code === \"ENOENT\" && env.VERCEL_PROJECT_ID && env.VERCEL_ORG_ID) {\n return { projectId: env.VERCEL_PROJECT_ID, orgId: env.VERCEL_ORG_ID };\n }\n configError(\"Selected OIDC project requires accessible .vercel/project.json linkage.\");\n }\n let identity;\n try { identity = JSON.parse(content); }\n catch { configError(\"Selected Vercel project linkage is not valid JSON.\"); }\n if (typeof identity?.projectId !== \"string\" || !identity.projectId || typeof identity.orgId !== \"string\" || !identity.orgId) {\n configError(\"Selected Vercel project linkage requires projectId and orgId.\");\n }\n return identity;\n}\n\nexport async function validateOidc(token: string, mainRoot: string, project: string | undefined, env: NodeJS.ProcessEnv): Promise<void> {\n const parts = token.split(\".\");\n if (parts.length !== 3 || parts.some((part) => !/^[A-Za-z0-9_-]+$/.test(part))) configError(\"Configured OIDC token is malformed.\");\n let claims;\n try {\n const header = JSON.parse(Buffer.from(parts[0]!, \"base64url\").toString(\"utf8\"));\n if (!header || typeof header !== \"object\" || Array.isArray(header)) throw new Error();\n claims = JSON.parse(Buffer.from(parts[1]!, \"base64url\").toString(\"utf8\"));\n } catch { configError(\"Configured OIDC token is malformed.\"); }\n const now = Date.now() / 1000;\n if (!claims || typeof claims !== \"object\" || Array.isArray(claims)\n || typeof claims.exp !== \"number\" || !Number.isFinite(claims.exp)\n || (claims.nbf !== undefined && (typeof claims.nbf !== \"number\" || !Number.isFinite(claims.nbf)))) {\n configError(\"Configured OIDC token has invalid validity claims.\");\n }\n if (claims.exp <= now || (claims.nbf !== undefined && claims.nbf > now)) configError(\"Configured OIDC token is expired or not yet valid; refresh its credential source.\");\n const expected = await projectIdentity(mainRoot, project, env);\n if (claims.project_id !== expected.projectId || claims.owner_id !== expected.orgId) configError(\"Configured OIDC token does not match the selected Vercel project and organization.\");\n}\n"]}
1
+ {"version":3,"file":"oidc.js","sourceRoot":"","sources":["../../src/config/oidc.ts"],"names":[],"mappings":"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","sourcesContent":["import { readFile } from \"node:fs/promises\";\nimport { resolve } from \"node:path\";\nimport { configError } from \"./access.js\";\n\ninterface ProjectIdentity { projectId: string; orgId: string }\n\nasync function projectIdentity(mainRoot: string, project: string | undefined, env: NodeJS.ProcessEnv): Promise<ProjectIdentity> {\n let content: string;\n try { content = await readFile(resolve(mainRoot, project ?? \".\", \".vercel/project.json\"), \"utf8\"); }\n catch (error) {\n if (project === undefined && (error as NodeJS.ErrnoException).code === \"ENOENT\" && env.VERCEL_PROJECT_ID && env.VERCEL_ORG_ID) {\n return { projectId: env.VERCEL_PROJECT_ID, orgId: env.VERCEL_ORG_ID };\n }\n configError(\"Selected OIDC project requires accessible .vercel/project.json linkage.\");\n }\n let identity;\n try { identity = JSON.parse(content); }\n catch { configError(\"Selected Vercel project linkage is not valid JSON.\"); }\n if (typeof identity?.projectId !== \"string\" || !identity.projectId || typeof identity.orgId !== \"string\" || !identity.orgId) {\n configError(\"Selected Vercel project linkage requires projectId and orgId.\");\n }\n return identity;\n}\n\nexport async function validateOidc(token: string, mainRoot: string, project: string | undefined, env: NodeJS.ProcessEnv): Promise<void> {\n const parts = token.split(\".\");\n if (parts.length !== 3 || parts.some((part) => !/^[A-Za-z0-9_-]+$/.test(part))) configError(\"Configured OIDC token is malformed.\");\n let claims;\n try {\n const header = JSON.parse(Buffer.from(parts[0]!, \"base64url\").toString(\"utf8\"));\n if (!header || typeof header !== \"object\" || Array.isArray(header)) throw new Error();\n claims = JSON.parse(Buffer.from(parts[1]!, \"base64url\").toString(\"utf8\"));\n } catch { configError(\"Configured OIDC token is malformed.\"); }\n const now = Date.now() / 1000;\n if (!claims || typeof claims !== \"object\" || Array.isArray(claims)\n || typeof claims.exp !== \"number\" || !Number.isFinite(claims.exp)\n || (claims.nbf !== undefined && (typeof claims.nbf !== \"number\" || !Number.isFinite(claims.nbf)))) {\n configError(\"Configured OIDC token has invalid validity claims.\");\n }\n if (claims.exp <= now) configError(\"Configured OIDC token has expired; refresh the configured credential source.\");\n if (claims.nbf !== undefined && claims.nbf > now) configError(\"Configured OIDC token is not yet valid; check the system clock or wait until validity begins.\");\n const expected = await projectIdentity(mainRoot, project, env);\n if (claims.project_id !== expected.projectId || claims.owner_id !== expected.orgId) configError(\"Configured OIDC token does not match the selected Vercel project and organization.\");\n}\n"]}
@@ -2,6 +2,7 @@ import { execFile } from "node:child_process";
2
2
  import { realpath, stat } from "node:fs/promises";
3
3
  import { promisify } from "node:util";
4
4
  import { AgentError } from "../errors.js";
5
+ import { quoteDiagnosticPath } from "./access-provenance.js";
5
6
  const execute = promisify(execFile);
6
7
  // These variables describe a caller's repository, not the explicit execution directory.
7
8
  // Keep normal user/system Git configuration and leave the caller's environment untouched.
@@ -30,7 +31,7 @@ export async function resolveProject(cwd) {
30
31
  throw new Error();
31
32
  }
32
33
  catch {
33
- throw new AgentError("INVALID_CONFIG", `Execution directory is not accessible: ${cwd}`);
34
+ throw new AgentError("INVALID_CONFIG", `Execution directory is not accessible: ${quoteDiagnosticPath(cwd)}`);
34
35
  }
35
36
  try {
36
37
  if ((await git(directory, ["rev-parse", "--is-bare-repository"])) === "true") {
@@ -1 +1 @@
1
- {"version":3,"file":"project.js","sourceRoot":"","sources":["../../src/config/project.ts"],"names":[],"mappings":"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","sourcesContent":["import { execFile } from \"node:child_process\";\nimport { realpath, stat } from \"node:fs/promises\";\nimport { promisify } from \"node:util\";\nimport { AgentError } from \"../errors.js\";\n\nconst execute = promisify(execFile);\n\n// These variables describe a caller's repository, not the explicit execution directory.\n// Keep normal user/system Git configuration and leave the caller's environment untouched.\nconst repositoryVariables = new Set([\n \"GIT_DIR\", \"GIT_WORK_TREE\", \"GIT_COMMON_DIR\", \"GIT_IMPLICIT_WORK_TREE\",\n \"GIT_INDEX_FILE\", \"GIT_OBJECT_DIRECTORY\", \"GIT_ALTERNATE_OBJECT_DIRECTORIES\",\n \"GIT_GRAFT_FILE\", \"GIT_SHALLOW_FILE\", \"GIT_NAMESPACE\", \"GIT_QUARANTINE_PATH\",\n \"GIT_PREFIX\", \"GIT_INTERNAL_SUPER_PREFIX\", \"GIT_NO_REPLACE_OBJECTS\", \"GIT_REPLACE_REF_BASE\",\n \"GIT_CEILING_DIRECTORIES\", \"GIT_DISCOVERY_ACROSS_FILESYSTEM\", \"GIT_REFERENCE_BACKEND\",\n \"GIT_CONFIG\", \"GIT_CONFIG_PARAMETERS\", \"GIT_CONFIG_COUNT\",\n]);\n\nexport async function git(cwd: string, args: string[]): Promise<string> {\n const env = { ...process.env };\n for (const key of Object.keys(env)) {\n if (repositoryVariables.has(key) || /^GIT_CONFIG_(KEY|VALUE)_\\d+$/.test(key)) delete env[key];\n }\n const { stdout } = await execute(\"git\", [\"-C\", cwd, ...args], { env, maxBuffer: 1024 * 1024 });\n return stdout.trim();\n}\n\nexport async function resolveProject(cwd: string): Promise<{ root: string; mainRoot: string }> {\n let directory: string;\n try {\n directory = await realpath(cwd);\n if (!(await stat(directory)).isDirectory()) throw new Error();\n } catch {\n throw new AgentError(\"INVALID_CONFIG\", `Execution directory is not accessible: ${cwd}`);\n }\n try {\n if ((await git(directory, [\"rev-parse\", \"--is-bare-repository\"])) === \"true\") {\n throw new AgentError(\"INVALID_CONFIG\", \"Bare repositories are unsupported execution directories.\");\n }\n const root = await git(directory, [\"rev-parse\", \"--show-toplevel\"]);\n const worktrees = await git(directory, [\"worktree\", \"list\", \"--porcelain\", \"-z\"]);\n const first = worktrees.split(\"\\0\").find((line) => line.startsWith(\"worktree \"));\n if (!first) throw new Error();\n const mainRoot = await realpath(first.slice(\"worktree \".length));\n return { root: await realpath(root), mainRoot };\n } catch (error) {\n if (error instanceof AgentError) throw error;\n const message = error instanceof Error ? error.message : \"\";\n if (message.includes(\"not a git repository\")) return { root: directory, mainRoot: directory };\n throw new AgentError(\"INVALID_CONFIG\", \"The Git project or its main checkout cannot be accessed.\");\n }\n}\n"]}
1
+ {"version":3,"file":"project.js","sourceRoot":"","sources":["../../src/config/project.ts"],"names":[],"mappings":"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","sourcesContent":["import { execFile } from \"node:child_process\";\nimport { realpath, stat } from \"node:fs/promises\";\nimport { promisify } from \"node:util\";\nimport { AgentError } from \"../errors.js\";\nimport { quoteDiagnosticPath } from \"./access-provenance.js\";\n\nconst execute = promisify(execFile);\n\n// These variables describe a caller's repository, not the explicit execution directory.\n// Keep normal user/system Git configuration and leave the caller's environment untouched.\nconst repositoryVariables = new Set([\n \"GIT_DIR\", \"GIT_WORK_TREE\", \"GIT_COMMON_DIR\", \"GIT_IMPLICIT_WORK_TREE\",\n \"GIT_INDEX_FILE\", \"GIT_OBJECT_DIRECTORY\", \"GIT_ALTERNATE_OBJECT_DIRECTORIES\",\n \"GIT_GRAFT_FILE\", \"GIT_SHALLOW_FILE\", \"GIT_NAMESPACE\", \"GIT_QUARANTINE_PATH\",\n \"GIT_PREFIX\", \"GIT_INTERNAL_SUPER_PREFIX\", \"GIT_NO_REPLACE_OBJECTS\", \"GIT_REPLACE_REF_BASE\",\n \"GIT_CEILING_DIRECTORIES\", \"GIT_DISCOVERY_ACROSS_FILESYSTEM\", \"GIT_REFERENCE_BACKEND\",\n \"GIT_CONFIG\", \"GIT_CONFIG_PARAMETERS\", \"GIT_CONFIG_COUNT\",\n]);\n\nexport async function git(cwd: string, args: string[]): Promise<string> {\n const env = { ...process.env };\n for (const key of Object.keys(env)) {\n if (repositoryVariables.has(key) || /^GIT_CONFIG_(KEY|VALUE)_\\d+$/.test(key)) delete env[key];\n }\n const { stdout } = await execute(\"git\", [\"-C\", cwd, ...args], { env, maxBuffer: 1024 * 1024 });\n return stdout.trim();\n}\n\nexport async function resolveProject(cwd: string): Promise<{ root: string; mainRoot: string }> {\n let directory: string;\n try {\n directory = await realpath(cwd);\n if (!(await stat(directory)).isDirectory()) throw new Error();\n } catch {\n throw new AgentError(\"INVALID_CONFIG\", `Execution directory is not accessible: ${quoteDiagnosticPath(cwd)}`);\n }\n try {\n if ((await git(directory, [\"rev-parse\", \"--is-bare-repository\"])) === \"true\") {\n throw new AgentError(\"INVALID_CONFIG\", \"Bare repositories are unsupported execution directories.\");\n }\n const root = await git(directory, [\"rev-parse\", \"--show-toplevel\"]);\n const worktrees = await git(directory, [\"worktree\", \"list\", \"--porcelain\", \"-z\"]);\n const first = worktrees.split(\"\\0\").find((line) => line.startsWith(\"worktree \"));\n if (!first) throw new Error();\n const mainRoot = await realpath(first.slice(\"worktree \".length));\n return { root: await realpath(root), mainRoot };\n } catch (error) {\n if (error instanceof AgentError) throw error;\n const message = error instanceof Error ? error.message : \"\";\n if (message.includes(\"not a git repository\")) return { root: directory, mainRoot: directory };\n throw new AgentError(\"INVALID_CONFIG\", \"The Git project or its main checkout cannot be accessed.\");\n }\n}\n"]}
@@ -3,21 +3,17 @@ import { resolve } from "node:path";
3
3
  import { parseEnv } from "node:util";
4
4
  import { AgentError } from "../errors.js";
5
5
  import { configError, parseConnection } from "./access.js";
6
+ import { quoteDiagnosticPath } from "./access-provenance.js";
6
7
  import { resolveProject } from "./project.js";
7
8
  import { validateOidc } from "./oidc.js";
8
9
  export async function resolveAccess(connection, harness, cwd, env = process.env) {
9
- parseConnection(connection);
10
10
  const kind = typeof harness === "string" ? harness : harness.kind;
11
+ parseConnection(connection, kind);
11
12
  if (connection.type === "subscription") {
12
- if (kind === "fx")
13
- throw new AgentError("ACCESS_UNAVAILABLE", "fx does not support subscription access.");
14
- return { type: "subscription" };
15
- }
16
- if (kind === "fx" && connection.type === "api-key") {
17
- throw new AgentError("UNSUPPORTED_OPTION", "fx does not support direct provider API keys.");
13
+ return { ...connection };
18
14
  }
19
15
  const variable = connection.env ?? (connection.type === "api-key"
20
- ? kind === "codex" ? "OPENAI_API_KEY" : "ANTHROPIC_API_KEY"
16
+ ? defaultApiKeyVariable(kind, connection.provider)
21
17
  : connection.type === "vercel-api-key" ? "AI_GATEWAY_API_KEY" : "VERCEL_OIDC_TOKEN");
22
18
  const { mainRoot } = await resolveProject(cwd);
23
19
  let token;
@@ -27,7 +23,7 @@ export async function resolveAccess(connection, harness, cwd, env = process.env)
27
23
  }
28
24
  catch (error) {
29
25
  if (error.code === "ENOENT")
30
- throw new AgentError("ACCESS_UNAVAILABLE", `Credential file is missing: ${connection.envFile}`);
26
+ throw new AgentError("ACCESS_UNAVAILABLE", `Credential file is missing: ${quoteDiagnosticPath(connection.envFile)}`);
31
27
  configError(`Cannot read credential variable ${variable} from its configured file.`);
32
28
  }
33
29
  }
@@ -37,6 +33,24 @@ export async function resolveAccess(connection, harness, cwd, env = process.env)
37
33
  throw new AgentError("ACCESS_UNAVAILABLE", `Credential variable ${variable} is missing or empty.`);
38
34
  if (connection.type === "vercel-oidc")
39
35
  await validateOidc(token, mainRoot, connection.project, env);
40
- return { type: connection.type, token };
36
+ const access = { type: connection.type, token };
37
+ if (connection.type === "api-key") {
38
+ for (const field of ["provider", "baseUrl", "wireApi", "apiVersion", "wireModel"]) {
39
+ if (connection[field] !== undefined)
40
+ Object.assign(access, { [field]: connection[field] });
41
+ }
42
+ }
43
+ return access;
44
+ }
45
+ function defaultApiKeyVariable(kind, provider) {
46
+ if (kind === "copilot")
47
+ return ({ github: "COPILOT_GITHUB_TOKEN", openai: "OPENAI_API_KEY", anthropic: "ANTHROPIC_API_KEY", azure: "AZURE_OPENAI_API_KEY" })[provider];
48
+ if (kind === "codex")
49
+ return "OPENAI_API_KEY";
50
+ if (kind === "cursor")
51
+ return "CURSOR_API_KEY";
52
+ if (kind === "claudeCode")
53
+ return "ANTHROPIC_API_KEY";
54
+ return configError(`${kind} direct API keys require env.`);
41
55
  }
42
56
  //# sourceMappingURL=resolve-access.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"resolve-access.js","sourceRoot":"","sources":["../../src/config/resolve-access.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAErC,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE1C,OAAO,EAAyB,WAAW,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAClF,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAEzC,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,UAA4B,EAC5B,OAAoC,EACpC,GAAW,EACX,GAAG,GAAsB,OAAO,CAAC,GAAG;IAEpC,eAAe,CAAC,UAAU,CAAC,CAAC;IAC5B,MAAM,IAAI,GAAG,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC;IAClE,IAAI,UAAU,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;QACvC,IAAI,IAAI,KAAK,IAAI;YAAE,MAAM,IAAI,UAAU,CAAC,oBAAoB,EAAE,0CAA0C,CAAC,CAAC;QAC1G,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC;IAClC,CAAC;IACD,IAAI,IAAI,KAAK,IAAI,IAAI,UAAU,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QACnD,MAAM,IAAI,UAAU,CAAC,oBAAoB,EAAE,+CAA+C,CAAC,CAAC;IAC9F,CAAC;IACD,MAAM,QAAQ,GAAG,UAAU,CAAC,GAAG,IAAI,CAAC,UAAU,CAAC,IAAI,KAAK,SAAS;QAC/D,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,mBAAmB;QAC3D,CAAC,CAAC,UAAU,CAAC,IAAI,KAAK,gBAAgB,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC;IACvF,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,cAAc,CAAC,GAAG,CAAC,CAAC;IAC/C,IAAI,KAAyB,CAAC;IAC9B,IAAI,UAAU,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;QACrC,IAAI,CAAC;YAAC,KAAK,GAAG,QAAQ,CAAC,MAAM,QAAQ,CAAC,OAAO,CAAC,QAAQ,EAAE,UAAU,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;QAAC,CAAC;QAClG,OAAO,KAAK,EAAE,CAAC;YACb,IAAK,KAA+B,CAAC,IAAI,KAAK,QAAQ;gBAAE,MAAM,IAAI,UAAU,CAAC,oBAAoB,EAAE,+BAA+B,UAAU,CAAC,OAAO,EAAE,CAAC,CAAC;YACxJ,WAAW,CAAC,mCAAmC,QAAQ,4BAA4B,CAAC,CAAC;QACvF,CAAC;IACH,CAAC;;QAAM,KAAK,GAAG,GAAG,CAAC,QAAQ,CAAC,CAAC;IAC7B,IAAI,CAAC,KAAK,EAAE,IAAI,EAAE;QAAE,MAAM,IAAI,UAAU,CAAC,oBAAoB,EAAE,uBAAuB,QAAQ,uBAAuB,CAAC,CAAC;IACvH,IAAI,UAAU,CAAC,IAAI,KAAK,aAAa;QAAE,MAAM,YAAY,CAAC,KAAK,EAAE,QAAQ,EAAE,UAAU,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;IACpG,OAAO,EAAE,IAAI,EAAE,UAAU,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC;AAC1C,CAAC","sourcesContent":["import { readFile } from \"node:fs/promises\";\nimport { resolve } from \"node:path\";\nimport { parseEnv } from \"node:util\";\nimport type { ResolvedAccess } from \"../adapters/types.js\";\nimport { AgentError } from \"../errors.js\";\nimport type { HarnessConfig, HarnessKind } from \"../sdk/types.js\";\nimport { type AccessConnection, configError, parseConnection } from \"./access.js\";\nimport { resolveProject } from \"./project.js\";\nimport { validateOidc } from \"./oidc.js\";\n\nexport async function resolveAccess(\n connection: AccessConnection,\n harness: HarnessKind | HarnessConfig,\n cwd: string,\n env: NodeJS.ProcessEnv = process.env,\n): Promise<ResolvedAccess> {\n parseConnection(connection);\n const kind = typeof harness === \"string\" ? harness : harness.kind;\n if (connection.type === \"subscription\") {\n if (kind === \"fx\") throw new AgentError(\"ACCESS_UNAVAILABLE\", \"fx does not support subscription access.\");\n return { type: \"subscription\" };\n }\n if (kind === \"fx\" && connection.type === \"api-key\") {\n throw new AgentError(\"UNSUPPORTED_OPTION\", \"fx does not support direct provider API keys.\");\n }\n const variable = connection.env ?? (connection.type === \"api-key\"\n ? kind === \"codex\" ? \"OPENAI_API_KEY\" : \"ANTHROPIC_API_KEY\"\n : connection.type === \"vercel-api-key\" ? \"AI_GATEWAY_API_KEY\" : \"VERCEL_OIDC_TOKEN\");\n const { mainRoot } = await resolveProject(cwd);\n let token: string | undefined;\n if (connection.envFile !== undefined) {\n try { token = parseEnv(await readFile(resolve(mainRoot, connection.envFile), \"utf8\"))[variable]; }\n catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") throw new AgentError(\"ACCESS_UNAVAILABLE\", `Credential file is missing: ${connection.envFile}`);\n configError(`Cannot read credential variable ${variable} from its configured file.`);\n }\n } else token = env[variable];\n if (!token?.trim()) throw new AgentError(\"ACCESS_UNAVAILABLE\", `Credential variable ${variable} is missing or empty.`);\n if (connection.type === \"vercel-oidc\") await validateOidc(token, mainRoot, connection.project, env);\n return { type: connection.type, token };\n}\n"]}
1
+ {"version":3,"file":"resolve-access.js","sourceRoot":"","sources":["../../src/config/resolve-access.ts"],"names":[],"mappings":"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","sourcesContent":["import { readFile } from \"node:fs/promises\";\nimport { resolve } from \"node:path\";\nimport { parseEnv } from \"node:util\";\nimport type { ResolvedAccess } from \"../adapters/types.js\";\nimport { AgentError } from \"../errors.js\";\nimport type { HarnessConfig, HarnessKind } from \"../sdk/types.js\";\nimport { type AccessConnection, configError, parseConnection } from \"./access.js\";\nimport { quoteDiagnosticPath } from \"./access-provenance.js\";\nimport { resolveProject } from \"./project.js\";\nimport { validateOidc } from \"./oidc.js\";\n\nexport async function resolveAccess(\n connection: AccessConnection,\n harness: HarnessKind | HarnessConfig,\n cwd: string,\n env: NodeJS.ProcessEnv = process.env,\n): Promise<ResolvedAccess> {\n const kind = typeof harness === \"string\" ? harness : harness.kind;\n parseConnection(connection, kind);\n if (connection.type === \"subscription\") {\n return { ...connection };\n }\n const variable = connection.env ?? (connection.type === \"api-key\"\n ? defaultApiKeyVariable(kind, connection.provider)\n : connection.type === \"vercel-api-key\" ? \"AI_GATEWAY_API_KEY\" : \"VERCEL_OIDC_TOKEN\");\n const { mainRoot } = await resolveProject(cwd);\n let token: string | undefined;\n if (connection.envFile !== undefined) {\n try { token = parseEnv(await readFile(resolve(mainRoot, connection.envFile), \"utf8\"))[variable]; }\n catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") throw new AgentError(\"ACCESS_UNAVAILABLE\", `Credential file is missing: ${quoteDiagnosticPath(connection.envFile)}`);\n configError(`Cannot read credential variable ${variable} from its configured file.`);\n }\n } else token = env[variable];\n if (!token?.trim()) throw new AgentError(\"ACCESS_UNAVAILABLE\", `Credential variable ${variable} is missing or empty.`);\n if (connection.type === \"vercel-oidc\") await validateOidc(token, mainRoot, connection.project, env);\n const access: ResolvedAccess = { type: connection.type, token };\n if (connection.type === \"api-key\") {\n for (const field of [\"provider\", \"baseUrl\", \"wireApi\", \"apiVersion\", \"wireModel\"] as const) {\n if (connection[field] !== undefined) Object.assign(access, { [field]: connection[field] });\n }\n }\n return access;\n}\n\nfunction defaultApiKeyVariable(kind: HarnessKind, provider?: string): string {\n if (kind === \"copilot\") return ({ github: \"COPILOT_GITHUB_TOKEN\", openai: \"OPENAI_API_KEY\", anthropic: \"ANTHROPIC_API_KEY\", azure: \"AZURE_OPENAI_API_KEY\" })[provider as \"github\" | \"openai\" | \"anthropic\" | \"azure\"];\n if (kind === \"codex\") return \"OPENAI_API_KEY\";\n if (kind === \"cursor\") return \"CURSOR_API_KEY\";\n if (kind === \"claudeCode\") return \"ANTHROPIC_API_KEY\";\n return configError(`${kind} direct API keys require env.`);\n}\n"]}
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { agent, codex, claudeCode, fx } from "./sdk/definitions.js";
1
+ export { agent, codex, claudeCode, fx, opencode, copilot, cursor } from "./sdk/definitions.js";
2
2
  export { tool, toolResult } from "./sdk/tools.js";
3
3
  export type { ToolContent, ToolResult } from "./sdk/tools.js";
4
- export type { AgentDefinition, ClaudeCodeConfig, ClaudeCodeOptions, CodexConfig, CodexOptions, FxConfig, FxOptions, HarnessConfig, HarnessKind, ToolDefinition, } from "./sdk/types.js";
4
+ export type { AgentDefinition, ClaudeCodeConfig, ClaudeCodeOptions, CodexConfig, CodexOptions, FxConfig, FxOptions, OpencodeConfig, OpencodeOptions, CopilotConfig, CopilotOptions, CursorConfig, CursorOptions, HarnessConfig, HarnessKind, ToolDefinition, } from "./sdk/types.js";
package/dist/index.js CHANGED
@@ -1,3 +1,3 @@
1
- export { agent, codex, claudeCode, fx } from "./sdk/definitions.js";
1
+ export { agent, codex, claudeCode, fx, opencode, copilot, cursor } from "./sdk/definitions.js";
2
2
  export { tool, toolResult } from "./sdk/tools.js";
3
3
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,sBAAsB,CAAC;AACpE,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC","sourcesContent":["export { agent, codex, claudeCode, fx } from \"./sdk/definitions.js\";\nexport { tool, toolResult } from \"./sdk/tools.js\";\nexport type { ToolContent, ToolResult } from \"./sdk/tools.js\";\nexport type {\n AgentDefinition,\n ClaudeCodeConfig,\n ClaudeCodeOptions,\n CodexConfig,\n CodexOptions,\n FxConfig,\n FxOptions,\n HarnessConfig,\n HarnessKind,\n ToolDefinition,\n} from \"./sdk/types.js\";\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAC/F,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC","sourcesContent":["export { agent, codex, claudeCode, fx, opencode, copilot, cursor } from \"./sdk/definitions.js\";\nexport { tool, toolResult } from \"./sdk/tools.js\";\nexport type { ToolContent, ToolResult } from \"./sdk/tools.js\";\nexport type {\n AgentDefinition,\n ClaudeCodeConfig,\n ClaudeCodeOptions,\n CodexConfig,\n CodexOptions,\n FxConfig,\n FxOptions,\n OpencodeConfig,\n OpencodeOptions,\n CopilotConfig,\n CopilotOptions,\n CursorConfig,\n CursorOptions,\n HarnessConfig,\n HarnessKind,\n ToolDefinition,\n} from \"./sdk/types.js\";\n"]}
@@ -0,0 +1,2 @@
1
+ /** Formats only bounded, observed child-process termination metadata. */
2
+ export declare function processExitDescription(code: unknown, signal: unknown): string;
@@ -0,0 +1,11 @@
1
+ import { constants } from "node:os";
2
+ /** Formats only bounded, observed child-process termination metadata. */
3
+ export function processExitDescription(code, signal) {
4
+ if (typeof signal === "string" && Object.hasOwn(constants.signals, signal)) {
5
+ return ` (signal ${signal})`;
6
+ }
7
+ return typeof code === "number" && Number.isSafeInteger(code)
8
+ ? ` (exit code ${code})`
9
+ : "";
10
+ }
11
+ //# sourceMappingURL=process-diagnostics.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"process-diagnostics.js","sourceRoot":"","sources":["../src/process-diagnostics.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEpC,yEAAyE;AACzE,MAAM,UAAU,sBAAsB,CAAC,IAAa,EAAE,MAAe;IACnE,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,OAAO,EAAE,MAAM,CAAC,EAAE,CAAC;QAC3E,OAAO,YAAY,MAAM,GAAG,CAAC;IAC/B,CAAC;IACD,OAAO,OAAO,IAAI,KAAK,QAAQ,IAAI,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC;QAC3D,CAAC,CAAC,eAAe,IAAI,GAAG;QACxB,CAAC,CAAC,EAAE,CAAC;AACT,CAAC","sourcesContent":["import { constants } from \"node:os\";\n\n/** Formats only bounded, observed child-process termination metadata. */\nexport function processExitDescription(code: unknown, signal: unknown): string {\n if (typeof signal === \"string\" && Object.hasOwn(constants.signals, signal)) {\n return ` (signal ${signal})`;\n }\n return typeof code === \"number\" && Number.isSafeInteger(code)\n ? ` (exit code ${code})`\n : \"\";\n}\n"]}
@@ -0,0 +1,4 @@
1
+ import type { NativeSession } from "../adapters/types.js";
2
+ import { AgentError } from "../errors.js";
3
+ export declare function withAccessContext(error: AgentError, context: string): AgentError;
4
+ export declare function withSessionAccessContext(native: NativeSession, context: string): NativeSession;
@@ -0,0 +1,25 @@
1
+ import { AgentError } from "../errors.js";
2
+ export function withAccessContext(error, context) {
3
+ return new AgentError(error.code, `${error.message}\n${context}`);
4
+ }
5
+ async function sessionCall(operation, context) {
6
+ try {
7
+ return await operation();
8
+ }
9
+ catch (error) {
10
+ if (error instanceof AgentError && error.code === "INVALID_CONFIG") {
11
+ throw withAccessContext(error, context);
12
+ }
13
+ throw error;
14
+ }
15
+ }
16
+ export function withSessionAccessContext(native, context) {
17
+ return {
18
+ turn: prompt => sessionCall(() => native.turn(prompt), context),
19
+ steer: prompt => sessionCall(() => native.steer(prompt), context),
20
+ interrupt: () => sessionCall(() => native.interrupt(), context),
21
+ resume: () => sessionCall(() => native.resume(), context),
22
+ close: () => sessionCall(() => native.close(), context),
23
+ };
24
+ }
25
+ //# sourceMappingURL=access-errors.js.map