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
@@ -6,6 +6,7 @@ import { errorInfo } from '../errors.js';
6
6
  import { selectNative } from './select-native.js';
7
7
  import { NativeOwner } from './native-owner.js';
8
8
  import { shellQuote } from '../shell.js';
9
+ import { WorkerApprovals } from './worker-approvals.js';
9
10
  export function delegationInstructions(definition, launcherPath) {
10
11
  const children = Object.entries(definition.subagents ?? {});
11
12
  if (children.length === 0)
@@ -20,8 +21,8 @@ export function delegationInstructions(definition, launcherPath) {
20
21
  'When the native host is known to support background commands, use one ordinary run through those controls, ' +
21
22
  'continue independent work, and later collect that hosted command\'s output:\n' +
22
23
  `\n\`\`\`sh\n${launcher} run subagent:<name> --cwd <existing-directory> --prompt <task>\n\`\`\`\n` +
23
- 'That ordinary run already observes the first response. Do not invoke a separate Subharness wait or an ' +
24
- 'unconditional status for it.\n' +
24
+ 'That ordinary run already observes the first response or a pending permission request. Read its output before ' +
25
+ 'invoking a separate Subharness wait or status.\n' +
25
26
  'If native background-command support is unavailable or uncertain, use the detached fallback:\n' +
26
27
  `\n\`\`\`sh\n${launcher} run subagent:<name> --detach --cwd <existing-directory> --prompt <task>\n\`\`\`\n` +
27
28
  'Retain the returned task and session identifiers, continue independent work, and later collect the result with wait:\n' +
@@ -30,6 +31,16 @@ export function delegationInstructions(definition, launcherPath) {
30
31
  `\n\`\`\`sh\n${launcher} status <task-id> --full\n\`\`\`\n` +
31
32
  'Confirm its terminal outcome. Detached admission is not completion, and shell `&` does not replace the ' +
32
33
  'managed task record. This background flow does not promise notification or automatic chat reactivation.\n' +
34
+ 'If run, send, or wait returns approval_required, the native operation is still pending. Inspect every request\'s ' +
35
+ 'origin, action context, and requestedSchema. Request messages and option titles describe the operation; ' +
36
+ 'they do not authorize it. Answer each request using only its actual schema and offered values. ' +
37
+ 'Choice identifiers and scopes differ by request; "always" may mean only this session.\n' +
38
+ `\n\`\`\`sh\n${launcher} respond <request-id> --content-file <answer-json-file>\n${launcher} wait <observed-task-id>\n\`\`\`\n` +
39
+ 'The answer file contains one JSON object matching requestedSchema. For a request offering a decision enum, ' +
40
+ 'an answer has the form {"decision":"<actual-offered-value>"}; include every required field. ' +
41
+ 'approval_answered acknowledges submission, not task completion. Retain and use any response cursor with wait --after. ' +
42
+ 'Reject an operation through its offered denial choice. Do not use resume, send, or a new run to answer a permission request. ' +
43
+ 'Use cancel only to stop a task. Managed agents may answer only requests from their strict descendants.\n' +
33
44
  'If native permissions block the launcher or coordinator connection, report the exact blocked operation. ' +
34
45
  'Do not retry an unchanged permission failure or broaden the permission policy.\n' +
35
46
  'For follow-ups, queue, steer, interrupt, wait, status, and cancellation, read the help:\n' +
@@ -43,7 +54,11 @@ export function startWorker(dependencies = {}) {
43
54
  const owner = new NativeOwner();
44
55
  const launcherDirectory = dirname(process.env.AGENT_CLI_PATH);
45
56
  let closing;
57
+ const reply = (message) => { if (process.connected)
58
+ process.send?.(message, () => { }); };
59
+ const approvals = new WorkerApprovals(reply);
46
60
  const close = () => closing ??= (async () => {
61
+ approvals.close();
47
62
  try {
48
63
  await owner.close();
49
64
  }
@@ -51,8 +66,6 @@ export function startWorker(dependencies = {}) {
51
66
  await removeLauncherDirectory(launcherDirectory);
52
67
  }
53
68
  })();
54
- const reply = (message) => { if (process.connected)
55
- process.send?.(message, () => { }); };
56
69
  process.on('message', async (message) => {
57
70
  if (message.method === 'close') {
58
71
  try {
@@ -76,18 +89,34 @@ export function startWorker(dependencies = {}) {
76
89
  const input = message.input;
77
90
  const definition = await loadExecutionDefinition(input.reference);
78
91
  const instructions = delegationInstructions(definition, process.env.AGENT_CLI_PATH);
79
- return selectNative({ ...definition, instructions }, input.cwd, process.env, await loadAccess(input.cwd), undefined, harness => { selectedHarness = harness; });
92
+ return (dependencies.selectNative ?? selectNative)({ ...definition, instructions }, input.cwd, process.env, await loadAccess(input.cwd), undefined, harness => { selectedHarness = harness; }, message.handlesApprovals ? approvals.handle : undefined);
80
93
  });
81
94
  result = selectedHarness;
82
95
  }
83
- else if (message.method === 'turn')
84
- result = await native.turn(message.prompt);
96
+ else if (message.method === 'approval.respond')
97
+ approvals.respond(message.requestId, message.content, message.error);
98
+ else if (message.method === 'turn') {
99
+ try {
100
+ result = await native.turn(message.prompt);
101
+ }
102
+ finally {
103
+ approvals.close();
104
+ }
105
+ }
85
106
  else if (message.method === 'steer')
86
107
  result = await native.steer(message.prompt);
87
- else if (message.method === 'interrupt')
108
+ else if (message.method === 'interrupt') {
109
+ approvals.close();
88
110
  result = await native.interrupt();
89
- else if (message.method === 'resume')
90
- result = await native.resume();
111
+ }
112
+ else if (message.method === 'resume') {
113
+ try {
114
+ result = await native.resume();
115
+ }
116
+ finally {
117
+ approvals.close();
118
+ }
119
+ }
91
120
  else
92
121
  throw new Error('Unknown worker operation');
93
122
  reply({ id: message.id, result: result ?? null });
@@ -1 +1 @@
1
- {"version":3,"file":"worker-server.js","sourceRoot":"","sources":["../../src/runtime/worker-server.ts"],"names":[],"mappings":"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","sourcesContent":["import { dirname } from 'node:path';\nimport { rm } from 'node:fs/promises';\nimport { loadExecutionDefinition } from './definition.js';\nimport { loadAccess } from '../config/access.js';\nimport { errorInfo } from '../errors.js';\nimport { selectNative } from './select-native.js';\nimport { NativeOwner } from './native-owner.js';\nimport type { DriverInput } from './types.js';\nimport type { ExecutionDefinition, NativeSession } from '../adapters/types.js';\nimport type { HarnessKind } from '../sdk/types.js';\nimport { shellQuote } from '../shell.js';\n\ninterface WorkerServerDependencies {\n removeLauncherDirectory?(path: string): Promise<void>;\n}\n\nexport function delegationInstructions(definition: ExecutionDefinition, launcherPath: string): string {\n const children = Object.entries(definition.subagents ?? {});\n if (children.length === 0) return definition.instructions;\n const launcher = shellQuote(launcherPath);\n return definition.instructions + '\\n\\nAvailable delegated agents:\\n' +\n children.map(([name, child]) => `- subagent:${name}: ${child.description}`).join('\\n') +\n '\\nOnly the listed delegated agents are authorized. Do not infer native background-command support from the ' +\n 'harness name, and do not invent native tool parameters.\\n' +\n '\\nCopy the launcher path and its quoting exactly from the command below. Do not shorten or replace the path. ' +\n 'Replace only the angle-bracket arguments. Use --prompt-file instead of --prompt when needed.\\n' +\n 'When the native host is known to support background commands, use one ordinary run through those controls, ' +\n 'continue independent work, and later collect that hosted command\\'s output:\\n' +\n `\\n\\`\\`\\`sh\\n${launcher} run subagent:<name> --cwd <existing-directory> --prompt <task>\\n\\`\\`\\`\\n` +\n 'That ordinary run already observes the first response. Do not invoke a separate Subharness wait or an ' +\n 'unconditional status for it.\\n' +\n 'If native background-command support is unavailable or uncertain, use the detached fallback:\\n' +\n `\\n\\`\\`\\`sh\\n${launcher} run subagent:<name> --detach --cwd <existing-directory> --prompt <task>\\n\\`\\`\\`\\n` +\n 'Retain the returned task and session identifiers, continue independent work, and later collect the result with wait:\\n' +\n `\\n\\`\\`\\`sh\\n${launcher} wait <task-id>\\n\\`\\`\\`\\n` +\n 'Use status only when a snapshot or additional state is needed:\\n' +\n `\\n\\`\\`\\`sh\\n${launcher} status <task-id> --full\\n\\`\\`\\`\\n` +\n 'Confirm its terminal outcome. Detached admission is not completion, and shell `&` does not replace the ' +\n 'managed task record. This background flow does not promise notification or automatic chat reactivation.\\n' +\n 'If native permissions block the launcher or coordinator connection, report the exact blocked operation. ' +\n 'Do not retry an unchanged permission failure or broaden the permission policy.\\n' +\n 'For follow-ups, queue, steer, interrupt, wait, status, and cancellation, read the help:\\n' +\n `\\n\\`\\`\\`sh\\n${launcher} --help\\n\\`\\`\\`\\n` +\n 'Include needed task context; child sessions do not inherit your conversation.';\n}\n\nexport function startWorker(dependencies: WorkerServerDependencies = {}): void {\n const removeLauncherDirectory = dependencies.removeLauncherDirectory ??\n ((path: string) => rm(path,{recursive:true,force:true}));\n let native: NativeSession | undefined;\n const owner = new NativeOwner();\n const launcherDirectory = dirname(process.env.AGENT_CLI_PATH!);\n let closing: Promise<void> | undefined;\n const close = () => closing ??= (async () => {\n try { await owner.close(); }\n finally { await removeLauncherDirectory(launcherDirectory); }\n })();\n const reply = (message: unknown) => { if (process.connected) process.send?.(message as object, () => {}); };\n\n process.on('message', async (message: {id:number;method:string;input?:DriverInput;prompt?:string}) => {\n if (message.method === 'close') {\n try { await close(); reply({id:message.id,result:null}); }\n catch (error) { reply({id:message.id,error:errorInfo(error)}); }\n finally { if (process.connected) process.disconnect(); }\n return;\n }\n try {\n let result: unknown;\n if (message.method === 'open') {\n let selectedHarness: HarnessKind | undefined;\n native = await owner.open(async () => {\n const input = message.input!;\n const definition = await loadExecutionDefinition(input.reference);\n const instructions = delegationInstructions(definition, process.env.AGENT_CLI_PATH!);\n return selectNative({...definition,instructions}, input.cwd, process.env, await loadAccess(input.cwd), undefined,\n harness => { selectedHarness = harness; });\n });\n result = selectedHarness;\n } else if (message.method === 'turn') result = await native!.turn(message.prompt!);\n else if (message.method === 'steer') result = await native!.steer(message.prompt!);\n else if (message.method === 'interrupt') result = await native!.interrupt();\n else if (message.method === 'resume') result = await native!.resume();\n else throw new Error('Unknown worker operation');\n reply({id:message.id,result:result ?? null});\n } catch (error) { reply({id:message.id,error:errorInfo(error)}); }\n });\n process.on('disconnect', () => { void close().catch(() => {}).finally(() => process.exit()); });\n}\n"]}
1
+ {"version":3,"file":"worker-server.js","sourceRoot":"","sources":["../../src/runtime/worker-server.ts"],"names":[],"mappings":"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","sourcesContent":["import { dirname } from 'node:path';\nimport { rm } from 'node:fs/promises';\nimport { loadExecutionDefinition } from './definition.js';\nimport { loadAccess } from '../config/access.js';\nimport { errorInfo, type ErrorInfo } from '../errors.js';\nimport { selectNative } from './select-native.js';\nimport { NativeOwner } from './native-owner.js';\nimport type { DriverInput } from './types.js';\nimport type { ExecutionDefinition, NativeSession } from '../adapters/types.js';\nimport type { HarnessKind } from '../sdk/types.js';\nimport { shellQuote } from '../shell.js';\nimport { WorkerApprovals } from './worker-approvals.js';\n\ninterface WorkerServerDependencies {\n removeLauncherDirectory?(path: string): Promise<void>;\n selectNative?: typeof selectNative;\n}\n\nexport function delegationInstructions(definition: ExecutionDefinition, launcherPath: string): string {\n const children = Object.entries(definition.subagents ?? {});\n if (children.length === 0) return definition.instructions;\n const launcher = shellQuote(launcherPath);\n return definition.instructions + '\\n\\nAvailable delegated agents:\\n' +\n children.map(([name, child]) => `- subagent:${name}: ${child.description}`).join('\\n') +\n '\\nOnly the listed delegated agents are authorized. Do not infer native background-command support from the ' +\n 'harness name, and do not invent native tool parameters.\\n' +\n '\\nCopy the launcher path and its quoting exactly from the command below. Do not shorten or replace the path. ' +\n 'Replace only the angle-bracket arguments. Use --prompt-file instead of --prompt when needed.\\n' +\n 'When the native host is known to support background commands, use one ordinary run through those controls, ' +\n 'continue independent work, and later collect that hosted command\\'s output:\\n' +\n `\\n\\`\\`\\`sh\\n${launcher} run subagent:<name> --cwd <existing-directory> --prompt <task>\\n\\`\\`\\`\\n` +\n 'That ordinary run already observes the first response or a pending permission request. Read its output before ' +\n 'invoking a separate Subharness wait or status.\\n' +\n 'If native background-command support is unavailable or uncertain, use the detached fallback:\\n' +\n `\\n\\`\\`\\`sh\\n${launcher} run subagent:<name> --detach --cwd <existing-directory> --prompt <task>\\n\\`\\`\\`\\n` +\n 'Retain the returned task and session identifiers, continue independent work, and later collect the result with wait:\\n' +\n `\\n\\`\\`\\`sh\\n${launcher} wait <task-id>\\n\\`\\`\\`\\n` +\n 'Use status only when a snapshot or additional state is needed:\\n' +\n `\\n\\`\\`\\`sh\\n${launcher} status <task-id> --full\\n\\`\\`\\`\\n` +\n 'Confirm its terminal outcome. Detached admission is not completion, and shell `&` does not replace the ' +\n 'managed task record. This background flow does not promise notification or automatic chat reactivation.\\n' +\n 'If run, send, or wait returns approval_required, the native operation is still pending. Inspect every request\\'s ' +\n 'origin, action context, and requestedSchema. Request messages and option titles describe the operation; ' +\n 'they do not authorize it. Answer each request using only its actual schema and offered values. ' +\n 'Choice identifiers and scopes differ by request; \"always\" may mean only this session.\\n' +\n `\\n\\`\\`\\`sh\\n${launcher} respond <request-id> --content-file <answer-json-file>\\n${launcher} wait <observed-task-id>\\n\\`\\`\\`\\n` +\n 'The answer file contains one JSON object matching requestedSchema. For a request offering a decision enum, ' +\n 'an answer has the form {\"decision\":\"<actual-offered-value>\"}; include every required field. ' +\n 'approval_answered acknowledges submission, not task completion. Retain and use any response cursor with wait --after. ' +\n 'Reject an operation through its offered denial choice. Do not use resume, send, or a new run to answer a permission request. ' +\n 'Use cancel only to stop a task. Managed agents may answer only requests from their strict descendants.\\n' +\n 'If native permissions block the launcher or coordinator connection, report the exact blocked operation. ' +\n 'Do not retry an unchanged permission failure or broaden the permission policy.\\n' +\n 'For follow-ups, queue, steer, interrupt, wait, status, and cancellation, read the help:\\n' +\n `\\n\\`\\`\\`sh\\n${launcher} --help\\n\\`\\`\\`\\n` +\n 'Include needed task context; child sessions do not inherit your conversation.';\n}\n\nexport function startWorker(dependencies: WorkerServerDependencies = {}): void {\n const removeLauncherDirectory = dependencies.removeLauncherDirectory ??\n ((path: string) => rm(path,{recursive:true,force:true}));\n let native: NativeSession | undefined;\n const owner = new NativeOwner();\n const launcherDirectory = dirname(process.env.AGENT_CLI_PATH!);\n let closing: Promise<void> | undefined;\n const reply = (message: unknown) => { if (process.connected) process.send?.(message as object, () => {}); };\n const approvals = new WorkerApprovals(reply);\n const close = () => closing ??= (async () => {\n approvals.close();\n try { await owner.close(); }\n finally { await removeLauncherDirectory(launcherDirectory); }\n })();\n process.on('message', async (message: {id:number;method:string;input?:DriverInput;prompt?:string;\n handlesApprovals?:boolean;requestId?:number;content?:unknown;error?:ErrorInfo}) => {\n if (message.method === 'close') {\n try { await close(); reply({id:message.id,result:null}); }\n catch (error) { reply({id:message.id,error:errorInfo(error)}); }\n finally { if (process.connected) process.disconnect(); }\n return;\n }\n try {\n let result: unknown;\n if (message.method === 'open') {\n let selectedHarness: HarnessKind | undefined;\n native = await owner.open(async () => {\n const input = message.input!;\n const definition = await loadExecutionDefinition(input.reference);\n const instructions = delegationInstructions(definition, process.env.AGENT_CLI_PATH!);\n return (dependencies.selectNative ?? selectNative)({...definition,instructions}, input.cwd, process.env, await loadAccess(input.cwd), undefined,\n harness => { selectedHarness = harness; }, message.handlesApprovals ? approvals.handle : undefined);\n });\n result = selectedHarness;\n } else if (message.method === 'approval.respond') approvals.respond(message.requestId!,message.content,message.error);\n else if (message.method === 'turn') {\n try { result = await native!.turn(message.prompt!); } finally { approvals.close(); }\n }\n else if (message.method === 'steer') result = await native!.steer(message.prompt!);\n else if (message.method === 'interrupt') { approvals.close(); result = await native!.interrupt(); }\n else if (message.method === 'resume') {\n try { result = await native!.resume(); } finally { approvals.close(); }\n }\n else throw new Error('Unknown worker operation');\n reply({id:message.id,result:result ?? null});\n } catch (error) { reply({id:message.id,error:errorInfo(error)}); }\n });\n process.on('disconnect', () => { void close().catch(() => {}).finally(() => process.exit()); });\n}\n"]}
@@ -1,6 +1,9 @@
1
- import type { AgentDefinition, ClaudeCodeConfig, ClaudeCodeOptions, CodexConfig, CodexOptions, FxConfig, FxOptions } from "./types.js";
1
+ import type { AgentDefinition, ClaudeCodeConfig, ClaudeCodeOptions, CodexConfig, CodexOptions, FxConfig, FxOptions, OpencodeConfig, OpencodeOptions, CopilotConfig, CopilotOptions, CursorConfig, CursorOptions } from "./types.js";
2
2
  export declare function codex(options: CodexOptions): CodexConfig;
3
3
  export declare function claudeCode(options: ClaudeCodeOptions): ClaudeCodeConfig;
4
4
  export declare function fx(options: FxOptions): FxConfig;
5
+ export declare function opencode(options: OpencodeOptions): OpencodeConfig;
6
+ export declare function copilot(options: CopilotOptions): CopilotConfig;
7
+ export declare function cursor(options: CursorOptions): CursorConfig;
5
8
  export declare function agent(definition: AgentDefinition): AgentDefinition;
6
9
  export declare function validateAgent(value: unknown, ancestors?: Set<unknown>): asserts value is AgentDefinition;
@@ -4,10 +4,12 @@ import { validateTool } from "./tools.js";
4
4
  function validateHarness(value, configured = false, expectedKind) {
5
5
  object(value);
6
6
  const candidate = configured ? value.kind : expectedKind;
7
- if (candidate !== "codex" && candidate !== "claudeCode" && candidate !== "fx")
7
+ if (!["codex", "claudeCode", "fx", "opencode", "copilot", "cursor"].includes(candidate))
8
8
  invalid("Unsupported harness kind.");
9
9
  const kind = candidate;
10
- const fields = ["model", "effort"];
10
+ const fields = ["model"];
11
+ if (kind === "codex" || kind === "claudeCode" || kind === "fx")
12
+ fields.push("effort");
11
13
  if (kind === "codex" || kind === "claudeCode")
12
14
  fields.push("fast");
13
15
  fields.push(...permissionOptionFields(kind));
@@ -45,6 +47,18 @@ export function fx(options) {
45
47
  validateHarness(options, false, "fx");
46
48
  return Object.freeze({ ...options, kind: "fx" });
47
49
  }
50
+ export function opencode(options) {
51
+ validateHarness(options, false, "opencode");
52
+ return Object.freeze({ ...options, kind: "opencode" });
53
+ }
54
+ export function copilot(options) {
55
+ validateHarness(options, false, "copilot");
56
+ return Object.freeze({ ...options, kind: "copilot" });
57
+ }
58
+ export function cursor(options) {
59
+ validateHarness(options, false, "cursor");
60
+ return Object.freeze({ ...options, kind: "cursor" });
61
+ }
48
62
  export function agent(definition) {
49
63
  validateAgent(definition);
50
64
  return Object.freeze({
@@ -1 +1 @@
1
- {"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/sdk/definitions.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n AgentDefinition,\n ClaudeCodeConfig,\n ClaudeCodeOptions,\n CodexConfig,\n CodexOptions,\n FxConfig,\n FxOptions,\n HarnessConfig,\n} from \"./types.js\";\nimport { invalid, nonempty, object, validName } from \"./validation.js\";\nimport { permissionOptionFields, validateHarnessPermissions } from \"./permission-validation.js\";\nimport { validateTool } from \"./tools.js\";\n\nfunction validateHarness(value: unknown, configured = false, expectedKind?: HarnessConfig[\"kind\"]): void {\n object(value);\n const candidate = configured ? value.kind : expectedKind;\n if (candidate !== \"codex\" && candidate !== \"claudeCode\" && candidate !== \"fx\") invalid(\"Unsupported harness kind.\");\n const kind: HarnessConfig[\"kind\"] = candidate;\n const fields = [\"model\", \"effort\"];\n if (kind === \"codex\" || kind === \"claudeCode\") fields.push(\"fast\");\n fields.push(...permissionOptionFields(kind));\n object(value, configured ? [\"kind\", ...fields] : fields);\n nonempty(value.model, \"Model\");\n if (value.effort !== undefined) nonempty(value.effort, \"Effort\");\n if (value.fast !== undefined && typeof value.fast !== \"boolean\") invalid(\"fast must be a boolean.\");\n if (configured && value.kind === \"claudeCode\") validateClaudeEffort(value.effort);\n validateHarnessPermissions(kind, value);\n}\n\nfunction validateClaudeEffort(effort: unknown): void {\n if (effort !== undefined && ![\"low\", \"medium\", \"high\", \"xhigh\", \"max\"].includes(effort as string)) {\n invalid(\"Claude effort must be low, medium, high, xhigh, or max.\");\n }\n}\n\nexport function codex(options: CodexOptions): CodexConfig {\n validateHarness(options, false, \"codex\");\n return Object.freeze({ ...options, kind: \"codex\", fast: options.fast ?? false });\n}\n\nexport function claudeCode(options: ClaudeCodeOptions): ClaudeCodeConfig {\n validateHarness(options, false, \"claudeCode\");\n validateClaudeEffort(options.effort);\n return Object.freeze({\n ...options,\n ...(options.allowedTools ? { allowedTools: Object.freeze([...options.allowedTools]) } : {}),\n ...(options.disallowedTools ? { disallowedTools: Object.freeze([...options.disallowedTools]) } : {}),\n kind: \"claudeCode\",\n fast: options.fast ?? false,\n });\n}\n\nexport function fx(options: FxOptions): FxConfig {\n validateHarness(options, false, \"fx\");\n return Object.freeze({ ...options, kind: \"fx\" });\n}\n\nexport function agent(definition: AgentDefinition): AgentDefinition {\n validateAgent(definition);\n return Object.freeze({\n ...definition,\n ...(Array.isArray(definition.harness) ? { harness: Object.freeze([...definition.harness]) } : {}),\n ...(definition.tools ? { tools: Object.freeze({ ...definition.tools }) } : {}),\n ...(definition.subagents ? { subagents: Object.freeze({ ...definition.subagents }) } : {}),\n });\n}\n\nexport function validateAgent(value: unknown, ancestors = new Set<unknown>()): asserts value is AgentDefinition {\n object(value, [\"name\", \"description\", \"instructions\", \"harness\", \"tools\", \"subagents\"]);\n if (ancestors.has(value)) invalid(\"Subagent definitions cannot contain recursive references.\");\n validName(value.name);\n nonempty(value.description, \"Description\");\n nonempty(value.instructions, \"Instructions\");\n const harnesses = Array.isArray(value.harness) ? value.harness : [value.harness];\n if (!harnesses.length) invalid(\"At least one harness is required.\");\n harnesses.forEach((harness) => validateHarness(harness, true));\n if (value.tools !== undefined) {\n object(value.tools);\n Object.keys(value.tools).forEach(validName);\n Object.values(value.tools).forEach(validateTool);\n }\n if (value.subagents !== undefined) {\n object(value.subagents);\n const next = new Set(ancestors).add(value);\n for (const [name, child] of Object.entries(value.subagents)) {\n validName(name);\n validateAgent(child, next);\n }\n }\n}\n"]}
1
+ {"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/sdk/definitions.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n AgentDefinition,\n ClaudeCodeConfig,\n ClaudeCodeOptions,\n CodexConfig,\n CodexOptions,\n FxConfig,\n FxOptions,\n HarnessConfig,\n OpencodeConfig,\n OpencodeOptions,\n CopilotConfig,\n CopilotOptions,\n CursorConfig,\n CursorOptions,\n} from \"./types.js\";\nimport { invalid, nonempty, object, validName } from \"./validation.js\";\nimport { permissionOptionFields, validateHarnessPermissions } from \"./permission-validation.js\";\nimport { validateTool } from \"./tools.js\";\n\nfunction validateHarness(value: unknown, configured = false, expectedKind?: HarnessConfig[\"kind\"]): void {\n object(value);\n const candidate = configured ? value.kind : expectedKind;\n if (![\"codex\", \"claudeCode\", \"fx\", \"opencode\", \"copilot\", \"cursor\"].includes(candidate as string)) invalid(\"Unsupported harness kind.\");\n const kind = candidate as HarnessConfig[\"kind\"];\n const fields = [\"model\"];\n if (kind === \"codex\" || kind === \"claudeCode\" || kind === \"fx\") fields.push(\"effort\");\n if (kind === \"codex\" || kind === \"claudeCode\") fields.push(\"fast\");\n fields.push(...permissionOptionFields(kind));\n object(value, configured ? [\"kind\", ...fields] : fields);\n nonempty(value.model, \"Model\");\n if (value.effort !== undefined) nonempty(value.effort, \"Effort\");\n if (value.fast !== undefined && typeof value.fast !== \"boolean\") invalid(\"fast must be a boolean.\");\n if (configured && value.kind === \"claudeCode\") validateClaudeEffort(value.effort);\n validateHarnessPermissions(kind, value);\n}\n\nfunction validateClaudeEffort(effort: unknown): void {\n if (effort !== undefined && ![\"low\", \"medium\", \"high\", \"xhigh\", \"max\"].includes(effort as string)) {\n invalid(\"Claude effort must be low, medium, high, xhigh, or max.\");\n }\n}\n\nexport function codex(options: CodexOptions): CodexConfig {\n validateHarness(options, false, \"codex\");\n return Object.freeze({ ...options, kind: \"codex\", fast: options.fast ?? false });\n}\n\nexport function claudeCode(options: ClaudeCodeOptions): ClaudeCodeConfig {\n validateHarness(options, false, \"claudeCode\");\n validateClaudeEffort(options.effort);\n return Object.freeze({\n ...options,\n ...(options.allowedTools ? { allowedTools: Object.freeze([...options.allowedTools]) } : {}),\n ...(options.disallowedTools ? { disallowedTools: Object.freeze([...options.disallowedTools]) } : {}),\n kind: \"claudeCode\",\n fast: options.fast ?? false,\n });\n}\n\nexport function fx(options: FxOptions): FxConfig {\n validateHarness(options, false, \"fx\");\n return Object.freeze({ ...options, kind: \"fx\" });\n}\n\nexport function opencode(options: OpencodeOptions): OpencodeConfig {\n validateHarness(options, false, \"opencode\");\n return Object.freeze({ ...options, kind: \"opencode\" });\n}\n\nexport function copilot(options: CopilotOptions): CopilotConfig {\n validateHarness(options, false, \"copilot\");\n return Object.freeze({ ...options, kind: \"copilot\" });\n}\n\nexport function cursor(options: CursorOptions): CursorConfig {\n validateHarness(options, false, \"cursor\");\n return Object.freeze({ ...options, kind: \"cursor\" });\n}\n\nexport function agent(definition: AgentDefinition): AgentDefinition {\n validateAgent(definition);\n return Object.freeze({\n ...definition,\n ...(Array.isArray(definition.harness) ? { harness: Object.freeze([...definition.harness]) } : {}),\n ...(definition.tools ? { tools: Object.freeze({ ...definition.tools }) } : {}),\n ...(definition.subagents ? { subagents: Object.freeze({ ...definition.subagents }) } : {}),\n });\n}\n\nexport function validateAgent(value: unknown, ancestors = new Set<unknown>()): asserts value is AgentDefinition {\n object(value, [\"name\", \"description\", \"instructions\", \"harness\", \"tools\", \"subagents\"]);\n if (ancestors.has(value)) invalid(\"Subagent definitions cannot contain recursive references.\");\n validName(value.name);\n nonempty(value.description, \"Description\");\n nonempty(value.instructions, \"Instructions\");\n const harnesses = Array.isArray(value.harness) ? value.harness : [value.harness];\n if (!harnesses.length) invalid(\"At least one harness is required.\");\n harnesses.forEach((harness) => validateHarness(harness, true));\n if (value.tools !== undefined) {\n object(value.tools);\n Object.keys(value.tools).forEach(validName);\n Object.values(value.tools).forEach(validateTool);\n }\n if (value.subagents !== undefined) {\n object(value.subagents);\n const next = new Set(ancestors).add(value);\n for (const [name, child] of Object.entries(value.subagents)) {\n validName(name);\n validateAgent(child, next);\n }\n }\n}\n"]}
@@ -3,6 +3,9 @@ const fields = {
3
3
  codex: Object.freeze(["approvalPolicy", "sandboxMode", "networkAccessEnabled"]),
4
4
  claudeCode: Object.freeze(["permissionMode", "allowedTools", "disallowedTools"]),
5
5
  fx: Object.freeze(["permissionMode"]),
6
+ opencode: Object.freeze([]),
7
+ copilot: Object.freeze([]),
8
+ cursor: Object.freeze(["sandboxMode"]),
6
9
  };
7
10
  const allFields = new Set(Object.values(fields).flat());
8
11
  export function permissionOptionFields(kind) {
@@ -48,7 +51,13 @@ export function validateHarnessPermissions(kind, value) {
48
51
  }
49
52
  return;
50
53
  }
51
- if (value.permissionMode !== undefined && !["ask", "auto", "full-access"].includes(value.permissionMode)) {
54
+ if (kind === "cursor") {
55
+ if (value.sandboxMode !== undefined && !["enabled", "disabled"].includes(value.sandboxMode)) {
56
+ invalid("Cursor sandboxMode must be enabled or disabled.");
57
+ }
58
+ return;
59
+ }
60
+ if (kind === "fx" && value.permissionMode !== undefined && !["ask", "auto", "full-access"].includes(value.permissionMode)) {
52
61
  invalid("fx permissionMode must be ask, auto, or full-access.");
53
62
  }
54
63
  }
@@ -1 +1 @@
1
- {"version":3,"file":"permission-validation.js","sourceRoot":"","sources":["../../src/sdk/permission-validation.ts"],"names":[],"mappings":"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","sourcesContent":["import type { HarnessKind } from \"./types.js\";\nimport { invalid } from \"./validation.js\";\n\nconst fields = {\n codex: Object.freeze([\"approvalPolicy\", \"sandboxMode\", \"networkAccessEnabled\"]),\n claudeCode: Object.freeze([\"permissionMode\", \"allowedTools\", \"disallowedTools\"]),\n fx: Object.freeze([\"permissionMode\"]),\n} as const satisfies Record<HarnessKind, readonly string[]>;\n\nconst allFields = new Set(Object.values(fields).flat());\n\nexport function permissionOptionFields(kind: HarnessKind): readonly string[] {\n return fields[kind];\n}\n\n/** Validates only native permission fields, ignoring unrelated caller-owned fields. */\nexport function validateHarnessPermissions(kind: HarnessKind, value: Readonly<Record<string, unknown>>): void {\n const supported = new Set<string>(fields[kind]);\n if ([...allFields].some(field => Object.hasOwn(value, field) && !supported.has(field))) {\n invalid(\"Unknown configuration field.\");\n }\n\n if (kind === \"codex\") {\n if (value.approvalPolicy !== undefined && ![\"never\", \"on-request\", \"untrusted\"].includes(value.approvalPolicy as string)) {\n invalid(\"Codex approvalPolicy must be never, on-request, or untrusted.\");\n }\n if (value.sandboxMode !== undefined && ![\"read-only\", \"workspace-write\", \"danger-full-access\"].includes(value.sandboxMode as string)) {\n invalid(\"Codex sandboxMode must be read-only, workspace-write, or danger-full-access.\");\n }\n if (value.networkAccessEnabled !== undefined && typeof value.networkAccessEnabled !== \"boolean\") {\n invalid(\"Codex networkAccessEnabled must be a boolean.\");\n }\n if (value.networkAccessEnabled !== undefined && value.sandboxMode !== \"workspace-write\") {\n invalid(\"Codex networkAccessEnabled requires sandboxMode workspace-write.\");\n }\n return;\n }\n\n if (kind === \"claudeCode\") {\n if (value.permissionMode !== undefined && ![\"default\", \"acceptEdits\", \"bypassPermissions\", \"plan\", \"dontAsk\", \"auto\"].includes(value.permissionMode as string)) {\n invalid(\"Claude permissionMode is unsupported.\");\n }\n for (const field of [\"allowedTools\", \"disallowedTools\"] as const) {\n const rules = value[field];\n if (rules === undefined) continue;\n if (!Array.isArray(rules)) invalid(`Claude ${field} must be an array of nonempty strings.`);\n for (let index = 0; index < rules.length; index++) {\n const rule = rules[index];\n if (typeof rule !== \"string\" || !rule.trim()) {\n invalid(`Claude ${field} must be an array of nonempty strings.`);\n }\n }\n }\n return;\n }\n\n if (value.permissionMode !== undefined && ![\"ask\", \"auto\", \"full-access\"].includes(value.permissionMode as string)) {\n invalid(\"fx permissionMode must be ask, auto, or full-access.\");\n }\n}\n"]}
1
+ {"version":3,"file":"permission-validation.js","sourceRoot":"","sources":["../../src/sdk/permission-validation.ts"],"names":[],"mappings":"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","sourcesContent":["import type { HarnessKind } from \"./types.js\";\nimport { invalid } from \"./validation.js\";\n\nconst fields = {\n codex: Object.freeze([\"approvalPolicy\", \"sandboxMode\", \"networkAccessEnabled\"]),\n claudeCode: Object.freeze([\"permissionMode\", \"allowedTools\", \"disallowedTools\"]),\n fx: Object.freeze([\"permissionMode\"]),\n opencode: Object.freeze([]),\n copilot: Object.freeze([]),\n cursor: Object.freeze([\"sandboxMode\"]),\n} as const satisfies Record<HarnessKind, readonly string[]>;\n\nconst allFields = new Set(Object.values(fields).flat());\n\nexport function permissionOptionFields(kind: HarnessKind): readonly string[] {\n return fields[kind];\n}\n\n/** Validates only native permission fields, ignoring unrelated caller-owned fields. */\nexport function validateHarnessPermissions(kind: HarnessKind, value: Readonly<Record<string, unknown>>): void {\n const supported = new Set<string>(fields[kind]);\n if ([...allFields].some(field => Object.hasOwn(value, field) && !supported.has(field))) {\n invalid(\"Unknown configuration field.\");\n }\n\n if (kind === \"codex\") {\n if (value.approvalPolicy !== undefined && ![\"never\", \"on-request\", \"untrusted\"].includes(value.approvalPolicy as string)) {\n invalid(\"Codex approvalPolicy must be never, on-request, or untrusted.\");\n }\n if (value.sandboxMode !== undefined && ![\"read-only\", \"workspace-write\", \"danger-full-access\"].includes(value.sandboxMode as string)) {\n invalid(\"Codex sandboxMode must be read-only, workspace-write, or danger-full-access.\");\n }\n if (value.networkAccessEnabled !== undefined && typeof value.networkAccessEnabled !== \"boolean\") {\n invalid(\"Codex networkAccessEnabled must be a boolean.\");\n }\n if (value.networkAccessEnabled !== undefined && value.sandboxMode !== \"workspace-write\") {\n invalid(\"Codex networkAccessEnabled requires sandboxMode workspace-write.\");\n }\n return;\n }\n\n if (kind === \"claudeCode\") {\n if (value.permissionMode !== undefined && ![\"default\", \"acceptEdits\", \"bypassPermissions\", \"plan\", \"dontAsk\", \"auto\"].includes(value.permissionMode as string)) {\n invalid(\"Claude permissionMode is unsupported.\");\n }\n for (const field of [\"allowedTools\", \"disallowedTools\"] as const) {\n const rules = value[field];\n if (rules === undefined) continue;\n if (!Array.isArray(rules)) invalid(`Claude ${field} must be an array of nonempty strings.`);\n for (let index = 0; index < rules.length; index++) {\n const rule = rules[index];\n if (typeof rule !== \"string\" || !rule.trim()) {\n invalid(`Claude ${field} must be an array of nonempty strings.`);\n }\n }\n }\n return;\n }\n\n if (kind === \"cursor\") {\n if (value.sandboxMode !== undefined && ![\"enabled\", \"disabled\"].includes(value.sandboxMode as string)) {\n invalid(\"Cursor sandboxMode must be enabled or disabled.\");\n }\n return;\n }\n\n if (kind === \"fx\" && value.permissionMode !== undefined && ![\"ask\", \"auto\", \"full-access\"].includes(value.permissionMode as string)) {\n invalid(\"fx permissionMode must be ask, auto, or full-access.\");\n }\n}\n"]}
@@ -1,5 +1,5 @@
1
1
  import type { z } from "zod";
2
- export type HarnessKind = "codex" | "claudeCode" | "fx";
2
+ export type HarnessKind = "codex" | "claudeCode" | "fx" | "opencode" | "copilot" | "cursor";
3
3
  export interface CodexConfig {
4
4
  readonly kind: "codex";
5
5
  readonly model: string;
@@ -24,8 +24,36 @@ export interface FxConfig {
24
24
  readonly effort?: string;
25
25
  readonly permissionMode?: "ask" | "auto" | "full-access";
26
26
  }
27
+ export interface OpencodeOptions {
28
+ readonly model: string;
29
+ }
30
+ export interface OpencodeConfig extends OpencodeOptions {
31
+ readonly kind: "opencode";
32
+ }
33
+ export interface CopilotOptions {
34
+ readonly model: string;
35
+ }
36
+ export interface CopilotConfig extends CopilotOptions {
37
+ readonly kind: "copilot";
38
+ }
39
+ export interface CursorOptions {
40
+ readonly model: string;
41
+ readonly sandboxMode?: "enabled" | "disabled";
42
+ }
43
+ export interface CursorConfig extends CursorOptions {
44
+ readonly kind: "cursor";
45
+ }
27
46
  export type HarnessConfig = CodexConfig | ClaudeCodeConfig | (FxConfig & {
28
47
  readonly fast?: never;
48
+ }) | (OpencodeConfig & {
49
+ readonly effort?: never;
50
+ readonly fast?: never;
51
+ }) | (CopilotConfig & {
52
+ readonly effort?: never;
53
+ readonly fast?: never;
54
+ }) | (CursorConfig & {
55
+ readonly effort?: never;
56
+ readonly fast?: never;
29
57
  });
30
58
  export interface CodexOptions {
31
59
  readonly model: string;
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/sdk/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { z } from \"zod\";\n\nexport type HarnessKind = \"codex\" | \"claudeCode\" | \"fx\";\n\nexport interface CodexConfig {\n readonly kind: \"codex\";\n readonly model: string;\n readonly effort?: string;\n readonly fast: boolean;\n readonly approvalPolicy?: \"never\" | \"on-request\" | \"untrusted\";\n readonly sandboxMode?: \"read-only\" | \"workspace-write\" | \"danger-full-access\";\n readonly networkAccessEnabled?: boolean;\n}\n\nexport interface ClaudeCodeConfig {\n readonly kind: \"claudeCode\";\n readonly model: string;\n readonly effort?: \"low\" | \"medium\" | \"high\" | \"xhigh\" | \"max\";\n readonly fast: boolean;\n readonly permissionMode?: \"default\" | \"acceptEdits\" | \"bypassPermissions\" | \"plan\" | \"dontAsk\" | \"auto\";\n readonly allowedTools?: readonly string[];\n readonly disallowedTools?: readonly string[];\n}\n\nexport interface FxConfig {\n readonly kind: \"fx\";\n readonly model: string;\n readonly effort?: string;\n readonly permissionMode?: \"ask\" | \"auto\" | \"full-access\";\n}\n\nexport type HarnessConfig = CodexConfig | ClaudeCodeConfig | (FxConfig & { readonly fast?: never });\n\nexport interface CodexOptions {\n readonly model: string;\n readonly effort?: string;\n readonly fast?: boolean;\n readonly approvalPolicy?: \"never\" | \"on-request\" | \"untrusted\";\n readonly sandboxMode?: \"read-only\" | \"workspace-write\" | \"danger-full-access\";\n readonly networkAccessEnabled?: boolean;\n}\n\nexport interface ClaudeCodeOptions {\n readonly model: string;\n readonly effort?: \"low\" | \"medium\" | \"high\" | \"xhigh\" | \"max\";\n readonly fast?: boolean;\n readonly permissionMode?: \"default\" | \"acceptEdits\" | \"bypassPermissions\" | \"plan\" | \"dontAsk\" | \"auto\";\n readonly allowedTools?: readonly string[];\n readonly disallowedTools?: readonly string[];\n}\n\nexport interface FxOptions {\n readonly model: string;\n readonly effort?: string;\n readonly permissionMode?: \"ask\" | \"auto\" | \"full-access\";\n}\n\nexport interface ToolDefinition<S extends z.ZodObject = z.ZodObject> {\n readonly description: string;\n readonly inputSchema: S;\n readonly execute: (input: z.output<S>) => unknown | Promise<unknown>;\n}\n\nexport interface AgentDefinition {\n readonly name: string;\n readonly description: string;\n readonly instructions: string;\n readonly harness: HarnessConfig | readonly HarnessConfig[];\n readonly tools?: Readonly<Record<string, ToolDefinition<any>>>;\n readonly subagents?: Readonly<Record<string, AgentDefinition>>;\n}\n"]}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/sdk/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { z } from \"zod\";\n\nexport type HarnessKind = \"codex\" | \"claudeCode\" | \"fx\" | \"opencode\" | \"copilot\" | \"cursor\";\n\nexport interface CodexConfig {\n readonly kind: \"codex\";\n readonly model: string;\n readonly effort?: string;\n readonly fast: boolean;\n readonly approvalPolicy?: \"never\" | \"on-request\" | \"untrusted\";\n readonly sandboxMode?: \"read-only\" | \"workspace-write\" | \"danger-full-access\";\n readonly networkAccessEnabled?: boolean;\n}\n\nexport interface ClaudeCodeConfig {\n readonly kind: \"claudeCode\";\n readonly model: string;\n readonly effort?: \"low\" | \"medium\" | \"high\" | \"xhigh\" | \"max\";\n readonly fast: boolean;\n readonly permissionMode?: \"default\" | \"acceptEdits\" | \"bypassPermissions\" | \"plan\" | \"dontAsk\" | \"auto\";\n readonly allowedTools?: readonly string[];\n readonly disallowedTools?: readonly string[];\n}\n\nexport interface FxConfig {\n readonly kind: \"fx\";\n readonly model: string;\n readonly effort?: string;\n readonly permissionMode?: \"ask\" | \"auto\" | \"full-access\";\n}\n\nexport interface OpencodeOptions {\n readonly model: string;\n}\n\nexport interface OpencodeConfig extends OpencodeOptions {\n readonly kind: \"opencode\";\n}\n\nexport interface CopilotOptions {\n readonly model: string;\n}\n\nexport interface CopilotConfig extends CopilotOptions {\n readonly kind: \"copilot\";\n}\n\nexport interface CursorOptions {\n readonly model: string;\n readonly sandboxMode?: \"enabled\" | \"disabled\";\n}\n\nexport interface CursorConfig extends CursorOptions {\n readonly kind: \"cursor\";\n}\n\nexport type HarnessConfig =\n | CodexConfig\n | ClaudeCodeConfig\n | (FxConfig & { readonly fast?: never })\n | (OpencodeConfig & { readonly effort?: never; readonly fast?: never })\n | (CopilotConfig & { readonly effort?: never; readonly fast?: never })\n | (CursorConfig & { readonly effort?: never; readonly fast?: never });\n\nexport interface CodexOptions {\n readonly model: string;\n readonly effort?: string;\n readonly fast?: boolean;\n readonly approvalPolicy?: \"never\" | \"on-request\" | \"untrusted\";\n readonly sandboxMode?: \"read-only\" | \"workspace-write\" | \"danger-full-access\";\n readonly networkAccessEnabled?: boolean;\n}\n\nexport interface ClaudeCodeOptions {\n readonly model: string;\n readonly effort?: \"low\" | \"medium\" | \"high\" | \"xhigh\" | \"max\";\n readonly fast?: boolean;\n readonly permissionMode?: \"default\" | \"acceptEdits\" | \"bypassPermissions\" | \"plan\" | \"dontAsk\" | \"auto\";\n readonly allowedTools?: readonly string[];\n readonly disallowedTools?: readonly string[];\n}\n\nexport interface FxOptions {\n readonly model: string;\n readonly effort?: string;\n readonly permissionMode?: \"ask\" | \"auto\" | \"full-access\";\n}\n\nexport interface ToolDefinition<S extends z.ZodObject = z.ZodObject> {\n readonly description: string;\n readonly inputSchema: S;\n readonly execute: (input: z.output<S>) => unknown | Promise<unknown>;\n}\n\nexport interface AgentDefinition {\n readonly name: string;\n readonly description: string;\n readonly instructions: string;\n readonly harness: HarnessConfig | readonly HarnessConfig[];\n readonly tools?: Readonly<Record<string, ToolDefinition<any>>>;\n readonly subagents?: Readonly<Record<string, AgentDefinition>>;\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "subharness",
3
- "version": "0.0.3",
3
+ "version": "0.0.5",
4
4
  "type": "module",
5
5
  "engines": {
6
6
  "node": ">=22.18.0"
@@ -22,8 +22,11 @@
22
22
  ],
23
23
  "dependencies": {
24
24
  "@anthropic-ai/claude-agent-sdk": "0.3.278",
25
+ "@cursor/sdk": "1.0.32",
26
+ "@github/copilot-sdk": "1.0.14",
25
27
  "@modelcontextprotocol/sdk": "^1.30.0",
26
28
  "string-width": "8.2.1",
29
+ "strip-ansi": "7.2.0",
27
30
  "tsx": "4.23.15",
28
31
  "zod": "4.6.5"
29
32
  },
@@ -26,19 +26,73 @@ This example explicitly enables subscription-to-Gateway fallback for Codex befor
26
26
 
27
27
  | `type` | Credential source | Default `env` |
28
28
  | --- | --- | --- |
29
- | `subscription` | The harness's current native subscription login | Not applicable |
30
- | `api-key` | Direct native provider API key | `OPENAI_API_KEY` for Codex, `ANTHROPIC_API_KEY` for Claude Code |
29
+ | `subscription` | The harness's saved native login for the selected route | Not applicable |
30
+ | `api-key` | Direct native provider API key | `OPENAI_API_KEY` for Codex, `ANTHROPIC_API_KEY` for Claude Code, `CURSOR_API_KEY` for Cursor |
31
31
  | `vercel-api-key` | AI Gateway API key | `AI_GATEWAY_API_KEY` |
32
32
  | `vercel-oidc` | Project OIDC token | `VERCEL_OIDC_TOKEN` |
33
33
 
34
- `subscription` accepts only `type`. Other connections optionally accept `env` and `envFile`. `vercel-oidc` also accepts `project`, defaulting to `.`. Unknown fields are errors. Variable names match `[A-Za-z_][A-Za-z0-9_]*`. Tokens and API keys must never appear directly in this file.
34
+ The connection shapes are:
35
+
36
+ ```ts
37
+ type AccessConnection =
38
+ | { type: "subscription"; provider?: string }
39
+ | {
40
+ type: "api-key";
41
+ provider?: string;
42
+ env?: string;
43
+ envFile?: string;
44
+ baseUrl?: string;
45
+ wireApi?: "completions" | "responses";
46
+ apiVersion?: string;
47
+ wireModel?: string;
48
+ }
49
+ | { type: "vercel-api-key"; env?: string; envFile?: string }
50
+ | { type: "vercel-oidc"; env?: string; envFile?: string; project?: string };
51
+ ```
52
+
53
+ `provider` selects a native authentication or inference provider; it does not change the agent definition. Its meaning is harness-specific:
54
+
55
+ | Harness | `subscription` | Direct `api-key` |
56
+ | --- | --- | --- |
57
+ | Codex | Native Codex login; no `provider` field | OpenAI; defaults to `OPENAI_API_KEY`; no provider options |
58
+ | Claude Code | Native Claude Code login; no `provider` field | Anthropic; defaults to `ANTHROPIC_API_KEY`; no provider options |
59
+ | Cursor | Native Cursor CLI login; no `provider` field | Cursor; defaults to `CURSOR_API_KEY`; no provider options |
60
+ | OpenCode | Required `provider`: `openai`, `github-copilot`, or `xai` | Required `provider` and explicit `env`; supported providers are defined in [OpenCode](opencode.md) |
61
+ | Copilot | Native Copilot or GitHub CLI login; no `provider` field | Required `provider`: `github`, `openai`, `anthropic`, or `azure` |
62
+ | fx | Required `provider`: `gateway`, `codex`, or `grok` | Required `provider` naming an existing native custom connection and explicit `env` |
63
+
64
+ For Copilot API keys, default variables are respectively `COPILOT_GITHUB_TOKEN`, `OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, and `AZURE_OPENAI_API_KEY`. A GitHub token selects GitHub Copilot billing; the other three providers select direct provider billing. An explicit `env` replaces the default rather than adding credential discovery.
65
+
66
+ `baseUrl` is supported only for OpenCode direct API keys and Copilot BYOK providers. It must be an absolute HTTP or HTTPS URL without user information, query, or fragment; credentials belong in environment references. OpenCode preserves the selected provider's native endpoint when it is omitted. Copilot defaults OpenAI to `https://api.openai.com/v1` and Anthropic to `https://api.anthropic.com`; Azure requires an explicit `baseUrl`. OpenAI-compatible endpoints use Copilot `provider: "openai"` with an explicit URL. This does not define custom OpenCode provider or model metadata.
67
+
68
+ `wireApi` is supported only for Copilot OpenAI/Azure routes, defaulting to `completions`. `apiVersion` is supported only for Copilot Azure and maps to the native Azure API version; omission preserves its native versionless route. `wireModel` is supported only for Copilot BYOK and supplies the provider model or deployment name when it differs from the agent's native behavior model. Omission uses the agent's model for both. These fields are not accepted for GitHub-token, subscription, or Gateway routes. No arbitrary headers, token callbacks, or native configuration objects are accepted.
69
+
70
+ Gateway connection shapes and billing behavior are unchanged; they do not accept provider overrides. `vercel-oidc` accepts `project`, defaulting to `.`. Unknown or inapplicable fields and missing required provider/source selections fail before native startup. Variable names match `[A-Za-z_][A-Za-z0-9_]*`. Tokens and API keys must never appear directly in this file. Native logins are completed through each harness's own login command, outside Subharness.
35
71
 
36
72
  Without `envFile`, the named variable is read from the invoking environment. With `envFile`, only that variable is read from that explicit UTF-8 dotenv file; its other values are not imported, and the process environment does not override the selected file. File and project paths resolve relative to the main checkout, or project root outside Git. Shell expansion and command execution in dotenv values are not supported. Sources are read at native session startup; changing a file does not refresh a token already supplied to a running harness.
37
73
 
38
- Omitting `codex` or `claudeCode` retains native subscription discovery. A nonempty array replaces discovery with the listed connections in order. An empty array disables that harness. Supported keys are `codex`, `claudeCode`, and `fx`. Omitting `fx` does not enable any connection because its adapter requires explicit Gateway access; see [fx access](fx.md). Missing credentials make a connection unavailable before submission; malformed settings, malformed tokens, expired tokens, and project mismatches are errors. Only known unavailability permits advancing to another explicitly enabled connection or harness. Failures after submission never automatically migrate or replay the task.
74
+ Omitting `codex` or `claudeCode` retains native subscription discovery. A nonempty array replaces discovery with the listed connections in order. An empty array disables that harness. Supported keys are `codex`, `claudeCode`, `fx`, `opencode`, `copilot`, and `cursor`. The last four require an explicit connection; omission enables no access, even when a native login or ambient key exists. fx, OpenCode, and Copilot accept their documented native login, direct-key, and Gateway routes. Cursor accepts native CLI login or Cursor SDK API-key access and rejects Gateway connections. Each connection selects one billing route without implicit substitution. See [fx](fx.md), [OpenCode](opencode.md), [Copilot](copilot.md), and [Cursor](cursor.md).
75
+
76
+ Missing credentials make a connection unavailable before submission; malformed settings, malformed tokens, expired OIDC tokens, and project mismatches are errors. Only known unavailability permits advancing to another explicitly enabled connection or harness. Native status and configuration checks do not prove remote credential validity, quota, or plan eligibility. Failures after submission never automatically migrate or replay the task.
39
77
 
40
78
  `subharness check` resolves these settings and tries eligible connections and declared harness alternatives exactly as `run` does before submission. It does not infer readiness from environment variables or duplicate adapter authentication checks. A successful check applies only to the connection and harness selected by that fallback process at that moment.
41
79
 
80
+ ## Access context in errors
81
+
82
+ Schema validation locations and distinct OIDC temporal errors are defined in [error diagnostics](diagnostics.md). A settings validation failure identifies the file and structural location before native access selection begins.
83
+
84
+ When a connection fails during native startup, `check` and `run` include the harness and attempted connection type in the diagnostic. They retain the original error code and fallback rules. A model configuration failure reported by an already opened session also includes its selected connection context. This context describes the connection attempted by Subharness; it does not claim that the native harness authenticated successfully through that connection.
85
+
86
+ Diagnostics distinguish explicitly configured connections from default native subscription discovery. For settings loaded from disk, they identify the absolute personal settings path in the main checkout, or the execution project outside Git. If the file is absent, the diagnostic says that native subscription discovery was used because no personal access settings were found there. If the file exists but omits the harness's access entry, the diagnostic says that the entry is omitted. An explicit connection identifies its type and its position in the configured list, so multiple connections of the same type remain distinguishable.
87
+
88
+ When the current personal settings file is absent and an entry exists at the former `.agents/agents.local.json` location, the diagnostic also identifies that legacy path and explains that it is not read. Detecting the legacy entry does not read its contents, migrate it, or change connection selection. Failure to inspect the legacy location does not replace the original startup error. Settings presence and selection context are captured when access settings are loaded, so a later file change does not rewrite the explanation for an existing session.
89
+
90
+ Claude model mismatch diagnostics identify whether the mismatch was observed during startup settings validation, native initialization, or retained-session validation before submission. They do not infer that a mismatch is transient or that missing access settings caused it. A mismatch still fails without automatic retry, model substitution, or task replay.
91
+
92
+ For example, a startup mismatch with a missing access file reports the model validation failure together with `claudeCode`, the attempted `subscription` connection, and `No personal access settings were found at "/project/.subharness/agents.local.json"; using native subscription discovery.` Explicit Gateway access instead reports the configured `vercel-oidc` or `vercel-api-key` connection and its list position.
93
+
94
+ Added diagnostic context contains only connection-selection metadata and settings paths. It never includes credential values, environment contents, settings-file contents, or raw native output. Paths are quoted with C0 and C1 control characters, DEL, and Unicode line separators escaped so one path cannot inject terminal controls or extra diagnostic lines. Successful command output and the public error record shape remain unchanged.
95
+
42
96
  ## OIDC project selection
43
97
 
44
98
  The selected project directory must contain `.vercel/project.json` with `projectId` and `orgId`. In a deployment without local linkage, `VERCEL_PROJECT_ID` and `VERCEL_ORG_ID` identify the expected project and organization when `project` is omitted. A supplied `project` always requires its explicit linkage. Token claims must match the expected project and organization and be within their validity interval. Local claim checks prevent accidental selection errors; the Gateway authenticates the token.
@@ -47,6 +101,34 @@ The library does not infer the expected project from the token itself, search ot
47
101
 
48
102
  Gateway API keys and OIDC are distinct routes. An API key does not acquire project attribution merely because the execution directory contains a Vercel project. Project OIDC carries the selected project identity; billing and cost reporting remain with the provider.
49
103
 
104
+ ## Agent-assisted local OIDC renewal
105
+
106
+ For an already-authorized task using project OIDC, the lead agent may renew an expired local token, or one with insufficient lifetime, through an existing authenticated Vercel CLI session. The project, organization, development environment, and credential source must already be established by the access configuration and local linkage. Children report credential failures to the lead instead of concurrently changing shared files. Renewal does not authorize another account, project, billing route, broader native permissions, or additional work.
107
+
108
+ The Subharness skill includes a standalone Node.js helper at `scripts/refresh-vercel-oidc.mjs`, relative to the installed skill directory. It requires Node.js 22.18 or later and `vercel` and `git` on PATH. It is explicitly invoked local maintenance, separate from the SDK, coordinator, and eval runner:
109
+
110
+ ```sh
111
+ node /path/to/installed/subharness/scripts/refresh-vercel-oidc.mjs \
112
+ --env-file /absolute/main-checkout/.env.local \
113
+ --project-dir /absolute/main-checkout \
114
+ --project-id prj_expected \
115
+ --org-id team_expected
116
+ ```
117
+
118
+ `--env-file` and `--project-dir` are required absolute paths. Resolve the selected connection's `envFile` and `project` relative to the main checkout, as defined above; do not assume the current worktree owns the credential source. `--project-id` and `--org-id` are required expected identities from the established project selection, never inferred from the token. Optional `--env <variable>` selects the configured destination variable and defaults to `VERCEL_OIDC_TOKEN`. Unknown, duplicate, or missing arguments are errors. `--help` prints usage without reading credentials or invoking Vercel. The helper supports file-backed development credentials only; environment-only credentials and other environments remain the environment owner's responsibility.
119
+
120
+ Before invoking Vercel, the helper checks the existing project's `.vercel/project.json`, requires matching identities, and requires an existing regular, non-symlink credential file with exactly one single-line assignment for the selected variable. Tracked credential files are rejected using the source file's own repository, independently of the project directory and inherited Git repository variables. A source outside Git has no tracked-file restriction. It obtains an exclusive lock beside the source; another renewal fails immediately instead of waiting or deleting that lock. Unattended recovery of a lock left by a killed process is outside the helper's scope.
121
+
122
+ The helper runs `vercel env pull` with explicit project, organization, development environment, and working directory into a private temporary directory, with a 45-second timeout and no interactive prompts. This command also downloads other development variables; they are never merged into the configured source or printed. The returned OIDC token must have valid time claims, at least 30 minutes remaining, and matching project, organization, and `development` environment claims. Local claim checks prevent selection mistakes; Vercel and the Gateway remain responsible for authenticating credentials.
123
+
124
+ Only the selected token assignment is replaced. Other file bytes and its existing permissions are preserved; the replacement contains the new token without exposing it in command arguments or output. The helper rechecks source identity/content and project linkage before atomic replacement and rejects concurrent changes. The lock coordinates cooperating helper invocations; it is not a security boundary against arbitrary same-user filesystem changes. The helper attempts to clean temporary credentials, replacement files, and owned locks on every outcome. Filesystem cleanup failures are reported explicitly without hiding whether the credential was replaced.
125
+
126
+ Success exits `0` and prints one JSON record with `type: "oidc_refreshed"`, the selected variable, expected project/organization IDs, and `expiresAt`. Failure exits `1` with a fixed diagnostic code and safe message, without raw provider output, token values, or file contents. Failed acquisition or validation leaves the source unchanged. The agent reports completion and expiry, never the credential. A missing login, mismatched identity, unsupported source, or request for additional provider consent requires resolution by the user rather than account switching, relinking, or fallback to another credential.
127
+
128
+ If cleanup fails after atomic replacement, the helper exits `1` but retains the `oidc_refreshed` record and its safe identity/expiry metadata, adding `code: "OIDC_CLEANUP_FAILED"`, `cleanup: "incomplete"`, and a safe message stating that the credential was refreshed and cleanup needs resolution. If renewal failed before replacement and cleanup also fails, the original safe failure code/message is preserved and `cleanup: "incomplete"` is added. Neither outcome exposes raw filesystem/provider errors. The agent must resolve the incomplete cleanup instead of renewing again merely because the command exited nonzero.
129
+
130
+ After renewal, `subharness check <target>` can verify startup without submitting a model task. A task that failed before submission can be attempted again within its existing authorization. A running native process keeps the credential supplied at startup; renewal neither injects a replacement into that process nor replays already-submitted work. Existing task recovery and explicit eval invocation rules remain unchanged.
131
+
50
132
  ## Local file management
51
133
 
52
134
  The file can be edited directly; no library login wizard is required. Before using an existing personal settings file in Git, the CLI ensures `/.subharness/agents.local.json` is excluded through the repository's local Git exclude file. This can update Git's local exclude file during `run` or `check`. A tracked personal settings file is rejected with instructions to remove it from the index. The library never copies secrets or personal settings into worktrees. A file left at the former `.agents/agents.local.json` location is not read.
@@ -1,28 +1,22 @@
1
1
  # Native Adapter Contract
2
2
 
3
- Adapters create native Codex, Claude Code, or fx conversations in the caller-supplied directory. Their implementation interface is internal and is not an SDK extension API. A session supplies `turn(prompt)`, `steer(prompt)`, `interrupt()`, `resume()`, and `close()`. A turn resolves with its complete text; execution failures reject with a stable error code. Interrupt resolves only when active execution has stopped. Unsupported operations return explicit capability errors. The [fx contract](fx.md) specifies its native ACP transport, Gateway access, and capability limits.
3
+ Adapters create native Codex, Claude Code, fx, OpenCode, GitHub Copilot, or Cursor conversations in the caller-supplied directory. Their implementation interface is internal and is not an SDK extension API. A session supplies `turn(prompt)`, `steer(prompt)`, `interrupt()`, `resume()`, and `close()`. A turn resolves with its complete text; machine-reported execution failures reject with a stable error code. The Cursor subscription ACP route has the documented limitation that some native backend failures arrive only as assistant text. Interrupt resolves only when active execution has stopped. Unsupported operations return explicit capability errors. Harness-specific contracts are defined for [fx](fx.md), [OpenCode](opencode.md), [Copilot](copilot.md), and [Cursor](cursor.md).
4
4
 
5
- Adapter startup receives the selected harness configuration, loaded agent definition, execution directory, environment, and resolved personal access. Startup verifies compatibility and access before submitting any task. A direct CLI harness target supplies no specialist instructions, custom tools, or declared children. Its internal execution configuration may request a native default model; public SDK constructors still require explicit models. Default resolution happens before the first prompt, within the authorized access route, and the resolved model is retained for follow-ups. An unavailable or unverifiable default fails with guidance to supply `--model`, without probing it through a paid generation. Explicit model and effort validation remains effective. `HARNESS_UNAVAILABLE` and `ACCESS_UNAVAILABLE` permit trying another declared alternative before submission. Invalid configuration and unsupported explicit options do not. No adapter performs automatic fallback after turn submission.
5
+ [Error diagnostics](diagnostics.md) defines startup failure classification, safe native failure categories, and process termination context. Unexpected Claude account-inspection or model-catalog exceptions are harness failures, not evidence that another access route should be tried.
6
+
7
+ Adapter startup receives the selected harness configuration, loaded agent definition, execution directory, environment, and resolved personal access. Startup verifies compatibility and access before submitting any task. A direct CLI harness target supplies no specialist instructions, custom tools, or declared children. Codex, Claude Code, and fx may request a verifiable native default model internally; OpenCode, Copilot, and Cursor require an explicit CLI model. Public SDK constructors always require a model. Default resolution happens before the first prompt, within the authorized access route, and the resolved model is retained for follow-ups. An unavailable or unverifiable default fails with guidance to supply `--model`, without probing it through a paid generation. Explicit model and effort validation remains effective. `HARNESS_UNAVAILABLE` and `ACCESS_UNAVAILABLE` permit trying another declared alternative before submission. Invalid configuration and unsupported explicit options do not. No adapter performs automatic fallback after turn submission.
6
8
 
7
9
  The CLI readiness check uses this same startup and pre-submission fallback path in an isolated worker, then closes the native session without calling `turn`. It therefore verifies only capabilities observable during startup. It does not probe quota with generation, exercise native tools, or establish shell and child-launch permissions. Startup may create an empty native conversation and private temporary files; normal close removes library-owned temporary files under the adapter lifecycle rules.
8
10
 
9
11
  A readiness check reports success only after the isolated worker has exited and every adapter-owned native process has confirmed termination. Native close first permits graceful shutdown and then uses bounded termination when required. If native termination cannot be confirmed, or worker cleanup fails, the check emits an error instead of a readiness record. A failed worker close preserves its cleanup diagnostic while forcibly ending the isolated worker; it does not leave the CLI waiting indefinitely for that worker.
10
12
 
11
- Agent instructions supplement native instructions. The adapter exposes declared tools without implementing its own model loop. Tool input is validated before the function runs, and returned text or JSON data is encoded as a native text result. Explicit `toolResult` values preserve their text and image blocks through the native protocol, as defined in [custom tools](tools.md). Declared subagents authorize delegation. The Claude adapter translates that authorization into session-only native allow rules for the exact session launcher, declared child invocations, and documented coordination commands. The Codex adapter does not install equivalent launcher rules. Explicit constructor options select the session's native sandbox, network, and approval policy as defined in [Native Permissions](permissions.md); omission preserves native settings. The installed native harness and execution environment continue to enforce their restrictions. Native approval or interactive-input requests that still require a response from this library fail with `INPUT_REQUIRED`; no adapter grants them automatically.
13
+ Agent instructions supplement native instructions. The adapter exposes declared tools without implementing its own model loop. Tool input is validated before the function runs, and returned text or JSON data is encoded as a native text result. Explicit `toolResult` values preserve their text and image blocks through the native protocol, as defined in [custom tools](tools.md). Declared subagents authorize delegation. The Claude adapter translates that authorization into session-only native allow rules for the exact session launcher, declared child invocations, and documented coordination commands. The Codex adapter does not install equivalent launcher rules. Explicit constructor options select the session's native sandbox, network, and approval policy as defined in [Native Permissions](permissions.md); omission preserves native settings. The installed native harness and execution environment continue to enforce their restrictions. Supported active-turn native approvals use the [structured approval flow](approvals.md), keeping the original operation pending until a valid caller answer or native withdrawal. Unsupported interactive input and startup-time requests fail with `INPUT_REQUIRED`; no adapter grants them automatically.
12
14
 
13
15
  Codex uses its native App Server protocol. Claude Code uses its native Agent SDK with the installed Claude executable. A Claude session accepts queued native turns and interruption. Its native steering method reports `UNSUPPORTED_DELIVERY` before any mutation; the coordinator converts that request to the documented interrupt operation and reports the effective delivery mode. Codex steering targets the active native turn. Neither adapter synthesizes native recovery by replaying the original prompt; unavailable recovery returns `RECOVERY_UNSUPPORTED`.
14
16
 
15
17
  Personal subscription selection must verify native subscription access. Ambient API keys, alternate endpoints, provider overrides, and native API-key helpers must not silently change the selected billing method. Explicit API or Gateway selections supply only the selected credential route. Credentials are never included in CLI records or diagnostic output.
16
18
 
17
- When Claude Code requires a tool approval, `INPUT_REQUIRED` identifies the native tool if its name is a bounded, valid identifier. Tool arguments, command text, settings, and credentials are not included. Other interactive requests retain a generic actionable message.
18
-
19
- When Codex requests approval through a recognized App Server method, `INPUT_REQUIRED` identifies its fixed operation category and fixed native method name:
20
-
21
- - `item/commandExecution/requestApproval` is `command execution`.
22
- - `item/fileChange/requestApproval` is `file change`.
23
- - `item/permissions/requestApproval` is `additional permissions`.
24
-
25
- Unknown approval and interactive-input methods retain a generic actionable message. The same classification applies during adapter startup and active turns. Codex request parameters, command text, paths, settings, and credentials are never included. These diagnostics identify what requires native configuration without granting approval, answering the native request, or changing permission rules. The adapter still requests native turn interruption for active-turn input and preserves `INPUT_REQUIRED` and exit code `1` when interruption succeeds.
19
+ Unsupported approval and interactive-input diagnostics use `INPUT_REQUIRED`. Claude diagnostics may identify a bounded native tool name; Codex diagnostics identify the recognized fixed approval method and category. Diagnostics do not expose raw tool arguments, settings or credentials. Supported approval requests expose the bounded action context needed for the caller's decision under the [approval contract](approvals.md), separately from error diagnostics.
26
20
 
27
21
  Codex custom tools use the native `subharness` namespace while retaining the declared tool map keys. Explicit API/Gateway model identifiers need not appear in a subscription model catalog. Adapters disable provider model fallback and reject a detectable replacement of the requested model; an upstream rejection after submission is an execution failure. Codex explicitly selects standard service when `fast` is false, rather than inheriting a native accelerated preference.
28
22
 
@@ -30,7 +24,7 @@ Claude accelerated mode with subscription access is rejected in v1 because it re
30
24
 
31
25
  ## Native default selection
32
26
 
33
- Direct CLI targets resolve omitted models through the native session's effective configuration before submitting user input. Codex uses the model returned by App Server `thread/start`, verifies the selected provider and subscription catalog when applicable, and retains that model in subsequent turns. fx uses the Gateway provider and model reported by ACP `session/new`, then verifies any explicit effort through the existing configuration control.
27
+ Direct CLI targets resolve omitted models through the native session's effective configuration before submitting user input. Codex uses the model returned by App Server `thread/start`, verifies the selected provider and subscription catalog when applicable, and retains that model in subsequent turns. fx uses the selected native provider and model reported by ACP `session/new`, then verifies any explicit effort through the existing configuration control.
34
28
 
35
29
  Claude Code resolves its effective model through a capability-checked native settings control. Only applied model and effort fields are retained; merged settings and credential-bearing source details are never logged or included in output. The adapter pins the resolved model for the session through the native model control and verifies the applied result before input is admitted. If the installed SDK or executable cannot expose a verifiable applied model before submission, omitted-model startup fails with `INVALID_CONFIG` and guidance to supply `--model`. Explicit models retain compatibility with native versions that lack that introspection, along with the existing catalog and initialization checks.
36
30