subharness 0.0.4 → 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 (188) hide show
  1. package/README.md +36 -6
  2. package/dist/adapters/claude-process.d.ts +3 -0
  3. package/dist/adapters/claude-process.js +11 -1
  4. package/dist/adapters/claude-process.js.map +1 -1
  5. package/dist/adapters/claude-result.d.ts +2 -0
  6. package/dist/adapters/claude-result.js +22 -0
  7. package/dist/adapters/claude-result.js.map +1 -0
  8. package/dist/adapters/claude.js +30 -11
  9. package/dist/adapters/claude.js.map +1 -1
  10. package/dist/adapters/codex.js +4 -1
  11. package/dist/adapters/codex.js.map +1 -1
  12. package/dist/adapters/copilot-permissions.d.ts +26 -0
  13. package/dist/adapters/copilot-permissions.js +121 -0
  14. package/dist/adapters/copilot-permissions.js.map +1 -0
  15. package/dist/adapters/copilot-tools.d.ts +12 -0
  16. package/dist/adapters/copilot-tools.js +61 -0
  17. package/dist/adapters/copilot-tools.js.map +1 -0
  18. package/dist/adapters/copilot.d.ts +108 -0
  19. package/dist/adapters/copilot.js +819 -0
  20. package/dist/adapters/copilot.js.map +1 -0
  21. package/dist/adapters/cursor-cli-approvals.d.ts +2 -0
  22. package/dist/adapters/cursor-cli-approvals.js +83 -0
  23. package/dist/adapters/cursor-cli-approvals.js.map +1 -0
  24. package/dist/adapters/cursor-cli-model.d.ts +4 -0
  25. package/dist/adapters/cursor-cli-model.js +64 -0
  26. package/dist/adapters/cursor-cli-model.js.map +1 -0
  27. package/dist/adapters/cursor-cli-session.d.ts +32 -0
  28. package/dist/adapters/cursor-cli-session.js +316 -0
  29. package/dist/adapters/cursor-cli-session.js.map +1 -0
  30. package/dist/adapters/cursor-cli-tools.d.ts +20 -0
  31. package/dist/adapters/cursor-cli-tools.js +181 -0
  32. package/dist/adapters/cursor-cli-tools.js.map +1 -0
  33. package/dist/adapters/cursor-cli.d.ts +2 -0
  34. package/dist/adapters/cursor-cli.js +306 -0
  35. package/dist/adapters/cursor-cli.js.map +1 -0
  36. package/dist/adapters/cursor-model.d.ts +7 -0
  37. package/dist/adapters/cursor-model.js +67 -0
  38. package/dist/adapters/cursor-model.js.map +1 -0
  39. package/dist/adapters/cursor-rpc.d.ts +42 -0
  40. package/dist/adapters/cursor-rpc.js +271 -0
  41. package/dist/adapters/cursor-rpc.js.map +1 -0
  42. package/dist/adapters/cursor-session.d.ts +4 -0
  43. package/dist/adapters/cursor-session.js +327 -0
  44. package/dist/adapters/cursor-session.js.map +1 -0
  45. package/dist/adapters/cursor-startup.d.ts +16 -0
  46. package/dist/adapters/cursor-startup.js +74 -0
  47. package/dist/adapters/cursor-startup.js.map +1 -0
  48. package/dist/adapters/cursor-tools.d.ts +11 -0
  49. package/dist/adapters/cursor-tools.js +49 -0
  50. package/dist/adapters/cursor-tools.js.map +1 -0
  51. package/dist/adapters/cursor.d.ts +6 -0
  52. package/dist/adapters/cursor.js +17 -0
  53. package/dist/adapters/cursor.js.map +1 -0
  54. package/dist/adapters/fx-auth.d.ts +12 -2
  55. package/dist/adapters/fx-auth.js +51 -61
  56. package/dist/adapters/fx-auth.js.map +1 -1
  57. package/dist/adapters/fx-profile.d.ts +8 -0
  58. package/dist/adapters/fx-profile.js +101 -0
  59. package/dist/adapters/fx-profile.js.map +1 -0
  60. package/dist/adapters/fx-rpc.d.ts +2 -0
  61. package/dist/adapters/fx-rpc.js +30 -4
  62. package/dist/adapters/fx-rpc.js.map +1 -1
  63. package/dist/adapters/fx-status.d.ts +2 -0
  64. package/dist/adapters/fx-status.js +96 -0
  65. package/dist/adapters/fx-status.js.map +1 -0
  66. package/dist/adapters/fx.js +28 -12
  67. package/dist/adapters/fx.js.map +1 -1
  68. package/dist/adapters/opencode-access.d.ts +13 -0
  69. package/dist/adapters/opencode-access.js +76 -0
  70. package/dist/adapters/opencode-access.js.map +1 -0
  71. package/dist/adapters/opencode-config.d.ts +11 -0
  72. package/dist/adapters/opencode-config.js +238 -0
  73. package/dist/adapters/opencode-config.js.map +1 -0
  74. package/dist/adapters/opencode-http.d.ts +28 -0
  75. package/dist/adapters/opencode-http.js +297 -0
  76. package/dist/adapters/opencode-http.js.map +1 -0
  77. package/dist/adapters/opencode-tools.d.ts +19 -0
  78. package/dist/adapters/opencode-tools.js +127 -0
  79. package/dist/adapters/opencode-tools.js.map +1 -0
  80. package/dist/adapters/opencode.d.ts +2 -0
  81. package/dist/adapters/opencode.js +569 -0
  82. package/dist/adapters/opencode.js.map +1 -0
  83. package/dist/adapters/rpc.d.ts +3 -0
  84. package/dist/adapters/rpc.js +45 -4
  85. package/dist/adapters/rpc.js.map +1 -1
  86. package/dist/adapters/types.d.ts +5 -0
  87. package/dist/adapters/types.js.map +1 -1
  88. package/dist/approvals/types.d.ts +1 -1
  89. package/dist/approvals/types.js.map +1 -1
  90. package/dist/cli/args.d.ts +1 -1
  91. package/dist/cli/args.js +4 -1
  92. package/dist/cli/args.js.map +1 -1
  93. package/dist/cli/dashboard-client.js +1 -1
  94. package/dist/cli/dashboard-client.js.map +1 -1
  95. package/dist/cli/dashboard-controller.d.ts +7 -0
  96. package/dist/cli/dashboard-controller.js +27 -0
  97. package/dist/cli/dashboard-controller.js.map +1 -0
  98. package/dist/cli/dashboard-detail-view.d.ts +1 -1
  99. package/dist/cli/dashboard-detail-view.js +3 -4
  100. package/dist/cli/dashboard-detail-view.js.map +1 -1
  101. package/dist/cli/dashboard-history-view.js.map +1 -1
  102. package/dist/cli/dashboard-input.d.ts +1 -1
  103. package/dist/cli/dashboard-input.js +11 -1
  104. package/dist/cli/dashboard-input.js.map +1 -1
  105. package/dist/cli/dashboard-layout.js +3 -3
  106. package/dist/cli/dashboard-layout.js.map +1 -1
  107. package/dist/cli/dashboard-renderer.js.map +1 -1
  108. package/dist/cli/dashboard-style.js +2 -2
  109. package/dist/cli/dashboard-style.js.map +1 -1
  110. package/dist/cli/dashboard.d.ts +2 -2
  111. package/dist/cli/dashboard.js +7 -32
  112. package/dist/cli/dashboard.js.map +1 -1
  113. package/dist/cli/help.d.ts +1 -1
  114. package/dist/cli/help.js +30 -12
  115. package/dist/cli/help.js.map +1 -1
  116. package/dist/cli/main.js +5 -1
  117. package/dist/cli/main.js.map +1 -1
  118. package/dist/config/access-provenance.d.ts +10 -0
  119. package/dist/config/access-provenance.js +31 -0
  120. package/dist/config/access-provenance.js.map +1 -0
  121. package/dist/config/access.d.ts +12 -2
  122. package/dist/config/access.js +125 -27
  123. package/dist/config/access.js.map +1 -1
  124. package/dist/config/loader.js +1 -0
  125. package/dist/config/loader.js.map +1 -1
  126. package/dist/config/oidc.js +4 -2
  127. package/dist/config/oidc.js.map +1 -1
  128. package/dist/config/project.js +2 -1
  129. package/dist/config/project.js.map +1 -1
  130. package/dist/config/resolve-access.js +24 -10
  131. package/dist/config/resolve-access.js.map +1 -1
  132. package/dist/index.d.ts +2 -2
  133. package/dist/index.js +1 -1
  134. package/dist/index.js.map +1 -1
  135. package/dist/process-diagnostics.d.ts +2 -0
  136. package/dist/process-diagnostics.js +11 -0
  137. package/dist/process-diagnostics.js.map +1 -0
  138. package/dist/runtime/access-errors.d.ts +4 -0
  139. package/dist/runtime/access-errors.js +25 -0
  140. package/dist/runtime/access-errors.js.map +1 -0
  141. package/dist/runtime/approval-registry.js +1 -1
  142. package/dist/runtime/approval-registry.js.map +1 -1
  143. package/dist/runtime/client.js +54 -14
  144. package/dist/runtime/client.js.map +1 -1
  145. package/dist/runtime/dashboard-sanitize.d.ts +2 -0
  146. package/dist/runtime/dashboard-sanitize.js +7 -0
  147. package/dist/runtime/dashboard-sanitize.js.map +1 -0
  148. package/dist/runtime/dashboard.d.ts +1 -1
  149. package/dist/runtime/dashboard.js +2 -3
  150. package/dist/runtime/dashboard.js.map +1 -1
  151. package/dist/runtime/definition.js +11 -1
  152. package/dist/runtime/definition.js.map +1 -1
  153. package/dist/runtime/select-native.js +26 -8
  154. package/dist/runtime/select-native.js.map +1 -1
  155. package/dist/runtime/worker-client.js +38 -13
  156. package/dist/runtime/worker-client.js.map +1 -1
  157. package/dist/sdk/definitions.d.ts +4 -1
  158. package/dist/sdk/definitions.js +16 -2
  159. package/dist/sdk/definitions.js.map +1 -1
  160. package/dist/sdk/permission-validation.js +10 -1
  161. package/dist/sdk/permission-validation.js.map +1 -1
  162. package/dist/sdk/types.d.ts +29 -1
  163. package/dist/sdk/types.js.map +1 -1
  164. package/package.json +4 -1
  165. package/sdk/access-config.md +58 -4
  166. package/sdk/adapter-contract.md +5 -3
  167. package/sdk/additional-harnesses.md +43 -0
  168. package/sdk/agent-skill.md +2 -2
  169. package/sdk/agent.md +5 -3
  170. package/sdk/approvals.md +3 -1
  171. package/sdk/authentication.md +1 -1
  172. package/sdk/cli/dashboard-design.md +6 -0
  173. package/sdk/cli/index.md +9 -6
  174. package/sdk/cli/output.md +3 -1
  175. package/sdk/config.md +1 -1
  176. package/sdk/copilot.md +53 -0
  177. package/sdk/cursor.md +68 -0
  178. package/sdk/diagnostics.md +29 -0
  179. package/sdk/distribution.md +1 -1
  180. package/sdk/evals.md +1 -1
  181. package/sdk/fx.md +50 -5
  182. package/sdk/harnesses.md +13 -13
  183. package/sdk/index.md +3 -2
  184. package/sdk/opencode.md +57 -0
  185. package/sdk/permissions.md +4 -2
  186. package/sdk/project-team.md +2 -0
  187. package/sdk/tools.md +1 -1
  188. package/sdk/v1-runtime.md +3 -3
@@ -0,0 +1 @@
1
+ {"version":3,"file":"access-errors.js","sourceRoot":"","sources":["../../src/runtime/access-errors.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE1C,MAAM,UAAU,iBAAiB,CAAC,KAAiB,EAAE,OAAe;IAClE,OAAO,IAAI,UAAU,CAAC,KAAK,CAAC,IAAI,EAAE,GAAG,KAAK,CAAC,OAAO,KAAK,OAAO,EAAE,CAAC,CAAC;AACpE,CAAC;AAED,KAAK,UAAU,WAAW,CAAI,SAA2B,EAAE,OAAe;IACxE,IAAI,CAAC;QAAC,OAAO,MAAM,SAAS,EAAE,CAAC;IAAC,CAAC;IACjC,OAAO,KAAK,EAAE,CAAC;QACb,IAAI,KAAK,YAAY,UAAU,IAAI,KAAK,CAAC,IAAI,KAAK,gBAAgB,EAAE,CAAC;YACnE,MAAM,iBAAiB,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;QAC1C,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,UAAU,wBAAwB,CAAC,MAAqB,EAAE,OAAe;IAC7E,OAAO;QACL,IAAI,EAAE,MAAM,CAAC,EAAE,CAAC,WAAW,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;QAC/D,KAAK,EAAE,MAAM,CAAC,EAAE,CAAC,WAAW,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;QACjE,SAAS,EAAE,GAAG,EAAE,CAAC,WAAW,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,SAAS,EAAE,EAAE,OAAO,CAAC;QAC/D,MAAM,EAAE,GAAG,EAAE,CAAC,WAAW,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,MAAM,EAAE,EAAE,OAAO,CAAC;QACzD,KAAK,EAAE,GAAG,EAAE,CAAC,WAAW,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,OAAO,CAAC;KACxD,CAAC;AACJ,CAAC","sourcesContent":["import type { NativeSession } from \"../adapters/types.js\";\nimport { AgentError } from \"../errors.js\";\n\nexport function withAccessContext(error: AgentError, context: string): AgentError {\n return new AgentError(error.code, `${error.message}\\n${context}`);\n}\n\nasync function sessionCall<T>(operation: () => Promise<T>, context: string): Promise<T> {\n try { return await operation(); }\n catch (error) {\n if (error instanceof AgentError && error.code === \"INVALID_CONFIG\") {\n throw withAccessContext(error, context);\n }\n throw error;\n }\n}\n\nexport function withSessionAccessContext(native: NativeSession, context: string): NativeSession {\n return {\n turn: prompt => sessionCall(() => native.turn(prompt), context),\n steer: prompt => sessionCall(() => native.steer(prompt), context),\n interrupt: () => sessionCall(() => native.interrupt(), context),\n resume: () => sessionCall(() => native.resume(), context),\n close: () => sessionCall(() => native.close(), context),\n };\n}\n"]}
@@ -31,7 +31,7 @@ export class ApprovalRegistry {
31
31
  catch {
32
32
  throw new AgentError('INPUT_REQUIRED', 'The native permission request cannot be represented within 64 KiB.');
33
33
  }
34
- if (copy.kind !== 'permission' || !['codex', 'claudeCode', 'fx'].includes(copy.harness) || typeof copy.message !== 'string')
34
+ if (copy.kind !== 'permission' || !['codex', 'claudeCode', 'fx', 'opencode', 'copilot', 'cursor'].includes(copy.harness) || typeof copy.message !== 'string')
35
35
  throw new AgentError('INPUT_REQUIRED', 'The native permission request cannot be represented.');
36
36
  validateApprovalSchema(copy.requestedSchema);
37
37
  const requestId = `req_${randomUUID()}`;
@@ -1 +1 @@
1
- {"version":3,"file":"approval-registry.js","sourceRoot":"","sources":["../../src/runtime/approval-registry.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from 'node:crypto';\nimport { isDeepStrictEqual } from 'node:util';\nimport { AgentError } from '../errors.js';\nimport { validateApprovalContent, validateApprovalSchema } from '../approvals/schema.js';\nimport type { ApprovalContent, NativeApprovalRequest } from '../approvals/types.js';\nimport type { ApprovalRequest } from './types.js';\n\ntype RequestIdentity = Pick<ApprovalRequest,'requestId'|'taskId'|'sessionId'>;\ninterface Continuation {\n resolve(content: ApprovalContent): void;\n reject(error: AgentError): void;\n cleanup(): void;\n}\ninterface PendingEntry {\n outcome: 'pending';\n request: ApprovalRequest;\n continuation: Continuation;\n}\ninterface AnsweredEntry { outcome: 'answered'; identity: RequestIdentity; answer: ApprovalContent }\ninterface WithdrawnEntry { outcome: 'withdrawn'; identity: RequestIdentity }\ntype SettledEntry = AnsweredEntry | WithdrawnEntry;\n\nconst MAX_SETTLED_REQUESTS = 1_024;\nconst expired = () => new AgentError('REQUEST_EXPIRED','The native permission request is no longer pending.');\nconst unknown = () => new AgentError('UNKNOWN_REQUEST','Permission request is unavailable in this coordinator.');\nconst identity = (request: ApprovalRequest): RequestIdentity => ({\n requestId:request.requestId,taskId:request.taskId,sessionId:request.sessionId,\n});\n\n/** Retains pending requests and a bounded settlement-ordered outcome history. */\nexport class ApprovalRegistry {\n private pending = new Map<string,PendingEntry>();\n private settled = new Map<string,SettledEntry>();\n constructor(private readonly changed: (taskId: string) => void) {}\n\n async register(taskId: string, sessionId: string, request: NativeApprovalRequest, signal: AbortSignal): Promise<ApprovalContent> {\n if (signal.aborted) throw expired();\n if (this.pendingFor(taskId).length >= 32)\n throw new AgentError('INPUT_REQUIRED','A task cannot wait on more than 32 permission requests.');\n let copy: NativeApprovalRequest;\n try {\n const serialized = JSON.stringify(request);\n if (Buffer.byteLength(serialized) > 64 * 1024) throw new Error();\n copy = JSON.parse(serialized) as NativeApprovalRequest;\n } catch { throw new AgentError('INPUT_REQUIRED','The native permission request cannot be represented within 64 KiB.'); }\n if (copy.kind !== 'permission' || !['codex','claudeCode','fx'].includes(copy.harness) || typeof copy.message !== 'string')\n throw new AgentError('INPUT_REQUIRED','The native permission request cannot be represented.');\n validateApprovalSchema(copy.requestedSchema);\n const requestId = `req_${randomUUID()}`;\n const snapshot: ApprovalRequest = {...copy,requestId,taskId,sessionId};\n if (Buffer.byteLength(JSON.stringify(snapshot)) > 64 * 1024)\n throw new AgentError('INPUT_REQUIRED','The native permission request cannot be represented within 64 KiB.');\n const answer = new Promise<ApprovalContent>((resolve,reject) => {\n const abort = () => this.retire(requestId);\n this.pending.set(requestId,{outcome:'pending',request:snapshot,\n continuation:{resolve,reject,cleanup:() => signal.removeEventListener('abort',abort)}});\n signal.addEventListener('abort',abort,{once:true});\n });\n this.changed(taskId);\n return answer;\n }\n\n request(id: string): RequestIdentity {\n const pending = this.pending.get(id);\n if (pending) return identity(pending.request);\n const settled = this.settled.get(id);\n if (!settled) throw unknown();\n return {...settled.identity};\n }\n\n respond(id: string, content: unknown) {\n const pending = this.pending.get(id);\n if (!pending) {\n const settled = this.settled.get(id);\n if (!settled) throw unknown();\n if (settled.outcome === 'withdrawn') throw expired();\n this.validateBoundedContent(content);\n if (!isDeepStrictEqual(settled.answer,content))\n throw new AgentError('INVALID_APPROVAL_RESPONSE','This permission request already has a different answer.');\n return this.receipt(settled.identity);\n }\n this.validateBoundedContent(content);\n const answer = validateApprovalContent(pending.request.requestedSchema,content);\n const requestIdentity = identity(pending.request);\n this.pending.delete(id);\n pending.continuation.cleanup();\n this.retain({outcome:'answered',identity:requestIdentity,answer:structuredClone(answer)});\n pending.continuation.resolve(answer);\n this.changed(requestIdentity.taskId);\n return this.receipt(requestIdentity);\n }\n\n pendingFor(taskId: string): ApprovalRequest[] {\n return [...this.pending.values()].filter(entry => entry.request.taskId === taskId)\n .map(entry => structuredClone(entry.request));\n }\n\n retireTask(taskId: string) {\n for (const [id,entry] of this.pending) if (entry.request.taskId === taskId) this.retire(id);\n }\n\n close() { for (const id of [...this.pending.keys()]) this.retire(id); }\n\n private retire(id: string) {\n const entry = this.pending.get(id);\n if (!entry) return;\n const requestIdentity = identity(entry.request);\n this.pending.delete(id);\n entry.continuation.cleanup();\n this.retain({outcome:'withdrawn',identity:requestIdentity});\n entry.continuation.reject(expired());\n this.changed(requestIdentity.taskId);\n }\n\n private retain(entry: SettledEntry) {\n this.settled.set(entry.identity.requestId,entry);\n while (this.settled.size > MAX_SETTLED_REQUESTS) {\n const oldest = this.settled.keys().next().value;\n if (oldest !== undefined) this.settled.delete(oldest);\n }\n }\n\n private validateBoundedContent(content: unknown) {\n let serialized: string | undefined;\n try { serialized = JSON.stringify(content); } catch { /* Report no submitted data. */ }\n if (!serialized || Buffer.byteLength(serialized) > 16 * 1024)\n throw new AgentError('INVALID_APPROVAL_RESPONSE','Approval content must be a JSON object within 16 KiB.');\n }\n\n private receipt(request: RequestIdentity) {\n return {version:1 as const,type:'approval_answered' as const,requestId:request.requestId,\n taskId:request.taskId,sessionId:request.sessionId};\n }\n}\n"]}
1
+ {"version":3,"file":"approval-registry.js","sourceRoot":"","sources":["../../src/runtime/approval-registry.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from 'node:crypto';\nimport { isDeepStrictEqual } from 'node:util';\nimport { AgentError } from '../errors.js';\nimport { validateApprovalContent, validateApprovalSchema } from '../approvals/schema.js';\nimport type { ApprovalContent, NativeApprovalRequest } from '../approvals/types.js';\nimport type { ApprovalRequest } from './types.js';\n\ntype RequestIdentity = Pick<ApprovalRequest,'requestId'|'taskId'|'sessionId'>;\ninterface Continuation {\n resolve(content: ApprovalContent): void;\n reject(error: AgentError): void;\n cleanup(): void;\n}\ninterface PendingEntry {\n outcome: 'pending';\n request: ApprovalRequest;\n continuation: Continuation;\n}\ninterface AnsweredEntry { outcome: 'answered'; identity: RequestIdentity; answer: ApprovalContent }\ninterface WithdrawnEntry { outcome: 'withdrawn'; identity: RequestIdentity }\ntype SettledEntry = AnsweredEntry | WithdrawnEntry;\n\nconst MAX_SETTLED_REQUESTS = 1_024;\nconst expired = () => new AgentError('REQUEST_EXPIRED','The native permission request is no longer pending.');\nconst unknown = () => new AgentError('UNKNOWN_REQUEST','Permission request is unavailable in this coordinator.');\nconst identity = (request: ApprovalRequest): RequestIdentity => ({\n requestId:request.requestId,taskId:request.taskId,sessionId:request.sessionId,\n});\n\n/** Retains pending requests and a bounded settlement-ordered outcome history. */\nexport class ApprovalRegistry {\n private pending = new Map<string,PendingEntry>();\n private settled = new Map<string,SettledEntry>();\n constructor(private readonly changed: (taskId: string) => void) {}\n\n async register(taskId: string, sessionId: string, request: NativeApprovalRequest, signal: AbortSignal): Promise<ApprovalContent> {\n if (signal.aborted) throw expired();\n if (this.pendingFor(taskId).length >= 32)\n throw new AgentError('INPUT_REQUIRED','A task cannot wait on more than 32 permission requests.');\n let copy: NativeApprovalRequest;\n try {\n const serialized = JSON.stringify(request);\n if (Buffer.byteLength(serialized) > 64 * 1024) throw new Error();\n copy = JSON.parse(serialized) as NativeApprovalRequest;\n } catch { throw new AgentError('INPUT_REQUIRED','The native permission request cannot be represented within 64 KiB.'); }\n if (copy.kind !== 'permission' || !['codex','claudeCode','fx','opencode','copilot','cursor'].includes(copy.harness) || typeof copy.message !== 'string')\n throw new AgentError('INPUT_REQUIRED','The native permission request cannot be represented.');\n validateApprovalSchema(copy.requestedSchema);\n const requestId = `req_${randomUUID()}`;\n const snapshot: ApprovalRequest = {...copy,requestId,taskId,sessionId};\n if (Buffer.byteLength(JSON.stringify(snapshot)) > 64 * 1024)\n throw new AgentError('INPUT_REQUIRED','The native permission request cannot be represented within 64 KiB.');\n const answer = new Promise<ApprovalContent>((resolve,reject) => {\n const abort = () => this.retire(requestId);\n this.pending.set(requestId,{outcome:'pending',request:snapshot,\n continuation:{resolve,reject,cleanup:() => signal.removeEventListener('abort',abort)}});\n signal.addEventListener('abort',abort,{once:true});\n });\n this.changed(taskId);\n return answer;\n }\n\n request(id: string): RequestIdentity {\n const pending = this.pending.get(id);\n if (pending) return identity(pending.request);\n const settled = this.settled.get(id);\n if (!settled) throw unknown();\n return {...settled.identity};\n }\n\n respond(id: string, content: unknown) {\n const pending = this.pending.get(id);\n if (!pending) {\n const settled = this.settled.get(id);\n if (!settled) throw unknown();\n if (settled.outcome === 'withdrawn') throw expired();\n this.validateBoundedContent(content);\n if (!isDeepStrictEqual(settled.answer,content))\n throw new AgentError('INVALID_APPROVAL_RESPONSE','This permission request already has a different answer.');\n return this.receipt(settled.identity);\n }\n this.validateBoundedContent(content);\n const answer = validateApprovalContent(pending.request.requestedSchema,content);\n const requestIdentity = identity(pending.request);\n this.pending.delete(id);\n pending.continuation.cleanup();\n this.retain({outcome:'answered',identity:requestIdentity,answer:structuredClone(answer)});\n pending.continuation.resolve(answer);\n this.changed(requestIdentity.taskId);\n return this.receipt(requestIdentity);\n }\n\n pendingFor(taskId: string): ApprovalRequest[] {\n return [...this.pending.values()].filter(entry => entry.request.taskId === taskId)\n .map(entry => structuredClone(entry.request));\n }\n\n retireTask(taskId: string) {\n for (const [id,entry] of this.pending) if (entry.request.taskId === taskId) this.retire(id);\n }\n\n close() { for (const id of [...this.pending.keys()]) this.retire(id); }\n\n private retire(id: string) {\n const entry = this.pending.get(id);\n if (!entry) return;\n const requestIdentity = identity(entry.request);\n this.pending.delete(id);\n entry.continuation.cleanup();\n this.retain({outcome:'withdrawn',identity:requestIdentity});\n entry.continuation.reject(expired());\n this.changed(requestIdentity.taskId);\n }\n\n private retain(entry: SettledEntry) {\n this.settled.set(entry.identity.requestId,entry);\n while (this.settled.size > MAX_SETTLED_REQUESTS) {\n const oldest = this.settled.keys().next().value;\n if (oldest !== undefined) this.settled.delete(oldest);\n }\n }\n\n private validateBoundedContent(content: unknown) {\n let serialized: string | undefined;\n try { serialized = JSON.stringify(content); } catch { /* Report no submitted data. */ }\n if (!serialized || Buffer.byteLength(serialized) > 16 * 1024)\n throw new AgentError('INVALID_APPROVAL_RESPONSE','Approval content must be a JSON object within 16 KiB.');\n }\n\n private receipt(request: RequestIdentity) {\n return {version:1 as const,type:'approval_answered' as const,requestId:request.requestId,\n taskId:request.taskId,sessionId:request.sessionId};\n }\n}\n"]}
@@ -1,9 +1,20 @@
1
1
  import { spawn } from 'node:child_process';
2
2
  import { setTimeout as delay } from 'node:timers/promises';
3
3
  import { AgentError } from '../errors.js';
4
+ import { processExitDescription } from '../process-diagnostics.js';
4
5
  import { localProgram, privateDirectory, readEndpoint, stateDirectory } from './state.js';
5
6
  import { startupOutcome, startupRetirementRequest } from './startup-protocol.js';
6
7
  const retirementConfirmationMs = 1_000;
8
+ const coordinatorOperations = new Set([
9
+ 'dashboard', 'dashboard-session', 'dashboard-detail', 'parent', 'run', 'send', 'resume', 'wait',
10
+ 'status', 'queue', 'cancel', 'respond',
11
+ ]);
12
+ function coordinatorOperation(body) {
13
+ if (!body || typeof body !== 'object' || Array.isArray(body))
14
+ return;
15
+ const command = body.command;
16
+ return typeof command === 'string' && coordinatorOperations.has(command) ? command : undefined;
17
+ }
7
18
  export async function connect(start, options = {}) {
8
19
  const directory = options.directory ?? stateDirectory();
9
20
  const available = await readEndpoint(directory);
@@ -19,9 +30,14 @@ export async function connect(start, options = {}) {
19
30
  if (Date.now() >= deadline)
20
31
  throw new AgentError('STARTUP_FAILED', 'Another command did not finish coordinator startup.');
21
32
  const program = options.program ?? localProgram('daemon');
22
- child = spawn(process.execPath, [...program.execArgv, program.file, String(Math.max(1, deadline - Date.now()))], {
23
- detached: true, stdio: ['ignore', 'ignore', 'ignore', 'ipc'], env: { ...process.env, AGENT_STATE_DIR: directory },
24
- });
33
+ try {
34
+ child = spawn(process.execPath, [...program.execArgv, program.file, String(Math.max(1, deadline - Date.now()))], {
35
+ detached: true, stdio: ['ignore', 'ignore', 'ignore', 'ipc'], env: { ...process.env, AGENT_STATE_DIR: directory },
36
+ });
37
+ }
38
+ catch {
39
+ throw new AgentError('STARTUP_FAILED', 'The local coordinator could not be spawned.');
40
+ }
25
41
  candidate = observeCandidate(child);
26
42
  options.afterSpawn?.(child);
27
43
  child.unref();
@@ -44,7 +60,15 @@ export async function connect(start, options = {}) {
44
60
  const replacement = await readEndpoint(directory);
45
61
  if (replacement)
46
62
  return replacement;
47
- if (outcome?.state === 'retired' || outcome?.state === 'exited')
63
+ if (!observed && outcome)
64
+ throw new AgentError('STARTUP_FAILED', 'The local coordinator timed out before publishing an endpoint.');
65
+ if (observed?.state === 'spawn-failed')
66
+ throw new AgentError('STARTUP_FAILED', 'The local coordinator could not be spawned.');
67
+ if (observed?.state === 'exited')
68
+ throw new AgentError('STARTUP_FAILED', `The local coordinator exited before startup completed${processExitDescription(observed.code, observed.signal)}.`);
69
+ if (observed?.state === 'retired')
70
+ throw new AgentError('STARTUP_FAILED', 'The local coordinator retired before startup completed.');
71
+ if (outcome?.state === 'retired' || outcome?.state === 'exited' || outcome?.state === 'spawn-failed')
48
72
  throw new AgentError('STARTUP_FAILED', 'The local coordinator could not start.');
49
73
  throw new AgentError('STARTUP_FAILED', 'The local coordinator timed out, and its retirement could not be confirmed. It was left untouched because it may have published.');
50
74
  }
@@ -73,16 +97,17 @@ function observeCandidate(child) {
73
97
  if (value)
74
98
  finish(value);
75
99
  };
76
- const onExit = () => finish({ state: 'exited' });
100
+ const onError = () => finish({ state: 'spawn-failed' });
101
+ const onExit = (code, signal) => finish({ state: 'exited', code, signal });
77
102
  child.on('message', onMessage);
78
- child.once('error', onExit);
103
+ child.once('error', onError);
79
104
  child.once('exit', onExit);
80
105
  return {
81
106
  outcome,
82
107
  current: () => current,
83
108
  close() {
84
109
  child.off('message', onMessage);
85
- child.off('error', onExit);
110
+ child.off('error', onError);
86
111
  child.off('exit', onExit);
87
112
  },
88
113
  };
@@ -109,6 +134,7 @@ async function within(promise, timeoutMs) {
109
134
  }
110
135
  }
111
136
  export async function* request(endpoint, body) {
137
+ const operation = coordinatorOperation(body);
112
138
  let response;
113
139
  try {
114
140
  response = await fetch(endpoint.url, { method: 'POST', headers: { authorization: `Bearer ${endpoint.token}`, 'content-type': 'application/json' }, body: JSON.stringify(body) });
@@ -116,8 +142,12 @@ export async function* request(endpoint, body) {
116
142
  catch {
117
143
  throw new AgentError('COORDINATOR_UNAVAILABLE', 'Connection to the local coordinator failed. The task was not automatically retried.');
118
144
  }
119
- if (!response.ok || !response.body)
120
- throw new AgentError('COORDINATOR_UNAVAILABLE', 'The coordinator rejected the request.');
145
+ if (!response.ok) {
146
+ void response.body?.cancel().catch(() => { });
147
+ throw new AgentError('COORDINATOR_UNAVAILABLE', `The coordinator rejected ${operation ? `the ${operation} operation` : 'the request'} with HTTP status ${response.status}.`);
148
+ }
149
+ if (!response.body)
150
+ throw new AgentError('COORDINATOR_UNAVAILABLE', `The coordinator returned a successful response with no body${operation ? ` for the ${operation} operation` : ''}.`);
121
151
  let buffer = '';
122
152
  const decoder = new TextDecoder();
123
153
  try {
@@ -127,16 +157,26 @@ export async function* request(endpoint, body) {
127
157
  while ((newline = buffer.indexOf('\n')) >= 0) {
128
158
  const line = buffer.slice(0, newline);
129
159
  buffer = buffer.slice(newline + 1);
130
- if (line)
131
- yield JSON.parse(line);
160
+ if (line) {
161
+ let record;
162
+ try {
163
+ record = JSON.parse(line);
164
+ }
165
+ catch {
166
+ throw new AgentError('COORDINATOR_UNAVAILABLE', `The coordinator returned a malformed JSON record${operation ? ` for the ${operation} operation` : ''}. The task was not automatically retried.`);
167
+ }
168
+ yield record;
169
+ }
132
170
  }
133
171
  }
134
172
  buffer += decoder.decode();
135
173
  if (buffer.trim())
136
- throw new Error('Incomplete response record');
174
+ throw new AgentError('COORDINATOR_UNAVAILABLE', `The coordinator response ended with an incomplete final record${operation ? ` for the ${operation} operation` : ''}. The task was not automatically retried.`);
137
175
  }
138
- catch {
139
- throw new AgentError('COORDINATOR_UNAVAILABLE', 'The coordinator response was interrupted or invalid. The task was not automatically retried.');
176
+ catch (error) {
177
+ if (error instanceof AgentError)
178
+ throw error;
179
+ throw new AgentError('COORDINATOR_UNAVAILABLE', 'The coordinator response stream was interrupted. The task was not automatically retried.');
140
180
  }
141
181
  }
142
182
  //# sourceMappingURL=client.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/runtime/client.ts"],"names":[],"mappings":"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","sourcesContent":["import { spawn, type ChildProcess } from 'node:child_process';\nimport { setTimeout as delay } from 'node:timers/promises';\nimport { AgentError } from '../errors.js';\nimport type { OutputRecord } from '../cli/output.js';\nimport { type Endpoint, localProgram, privateDirectory, readEndpoint, stateDirectory } from './state.js';\nimport { startupOutcome, startupRetirementRequest, type StartupOutcome } from './startup-protocol.js';\n\ninterface ConnectionOptions {\n directory?: string;\n timeoutMs?: number;\n afterSpawn?: (child: ChildProcess) => void;\n beforeRetirement?: (child: ChildProcess) => void | Promise<void>;\n program?: ReturnType<typeof localProgram>;\n}\n\ntype CandidateOutcome = StartupOutcome | {state:'exited'};\nconst retirementConfirmationMs = 1_000;\n\nexport async function connect(start: boolean, options: ConnectionOptions = {}): Promise<Endpoint> {\n const directory = options.directory ?? stateDirectory();\n const available = await readEndpoint(directory);\n if (available) return available;\n if (!start) throw new AgentError('UNKNOWN_SESSION','No coordinator is reachable from this environment. Its sessions may have ended, or native permissions may block access to its local endpoint.');\n await privateDirectory(directory);\n const deadline = Date.now() + (options.timeoutMs ?? 15_000);\n let child: ChildProcess | undefined;\n let candidate: ReturnType<typeof observeCandidate> | undefined;\n try {\n if (Date.now() >= deadline) throw new AgentError('STARTUP_FAILED','Another command did not finish coordinator startup.');\n const program = options.program ?? localProgram('daemon');\n child = spawn(process.execPath,[...program.execArgv,program.file,String(Math.max(1,deadline-Date.now()))],{\n detached:true,stdio:['ignore','ignore','ignore','ipc'],env:{...process.env,AGENT_STATE_DIR:directory},\n });\n candidate = observeCandidate(child);\n options.afterSpawn?.(child);\n child.unref();\n while (Date.now() < deadline && !candidate.current()) {\n await delay(50);\n const endpoint = await readEndpoint(directory);\n if (endpoint) return endpoint;\n }\n const observed = candidate.current();\n if (observed?.state === 'published') return observed.endpoint;\n if (!observed) {\n await options.beforeRetirement?.(child);\n requestRetirement(child);\n }\n const outcome = observed ?? await within(candidate.outcome,retirementConfirmationMs);\n if (outcome?.state === 'published') return outcome.endpoint;\n const replacement = await readEndpoint(directory);\n if (replacement) return replacement;\n if (outcome?.state === 'retired' || outcome?.state === 'exited')\n throw new AgentError('STARTUP_FAILED','The local coordinator could not start.');\n throw new AgentError('STARTUP_FAILED','The local coordinator timed out, and its retirement could not be confirmed. It was left untouched because it may have published.');\n } finally {\n candidate?.close();\n if (child?.connected) {\n try { child.disconnect(); } catch { /* The candidate may have disconnected after retirement. */ }\n }\n }\n}\n\nfunction observeCandidate(child: ChildProcess) {\n let current: CandidateOutcome | undefined;\n let settle!: (outcome: CandidateOutcome) => void;\n const outcome = new Promise<CandidateOutcome>(resolve => { settle = resolve; });\n const finish = (value: CandidateOutcome) => {\n if (current) return;\n current = value;\n settle(value);\n };\n const onMessage = (message: unknown) => {\n const value = startupOutcome(message);\n if (value) finish(value);\n };\n const onExit = () => finish({state:'exited'});\n child.on('message',onMessage);\n child.once('error',onExit);\n child.once('exit',onExit);\n return {\n outcome,\n current:() => current,\n close() {\n child.off('message',onMessage);\n child.off('error',onExit);\n child.off('exit',onExit);\n },\n };\n}\n\nfunction requestRetirement(child: ChildProcess) {\n if (!child.connected) return;\n try { child.send(startupRetirementRequest,()=>{}); }\n catch { /* The bounded outcome wait reports an unconfirmed retirement. */ }\n}\n\nasync function within<T>(promise: Promise<T>, timeoutMs: number): Promise<T | undefined> {\n let timer: number | NodeJS.Timeout | undefined;\n try {\n return await Promise.race([\n promise,\n new Promise<undefined>(resolve => { timer = setTimeout(resolve,timeoutMs); }),\n ]);\n } finally { if (timer) clearTimeout(timer); }\n}\n\nexport async function* request(endpoint: Endpoint, body: unknown): AsyncGenerator<OutputRecord> {\n let response: globalThis.Response;\n try {\n response = await fetch(endpoint.url,{method:'POST',headers:{authorization:`Bearer ${endpoint.token}`,'content-type':'application/json'},body:JSON.stringify(body)});\n } catch { throw new AgentError('COORDINATOR_UNAVAILABLE','Connection to the local coordinator failed. The task was not automatically retried.'); }\n if (!response.ok || !response.body) throw new AgentError('COORDINATOR_UNAVAILABLE','The coordinator rejected the request.');\n let buffer = '';\n const decoder = new TextDecoder();\n try {\n for await (const chunk of response.body) {\n buffer += decoder.decode(chunk,{stream:true});\n let newline;\n while ((newline = buffer.indexOf('\\n')) >= 0) {\n const line = buffer.slice(0,newline); buffer = buffer.slice(newline + 1);\n if (line) yield JSON.parse(line) as OutputRecord;\n }\n }\n buffer += decoder.decode();\n if (buffer.trim()) throw new Error('Incomplete response record');\n } catch {\n throw new AgentError('COORDINATOR_UNAVAILABLE','The coordinator response was interrupted or invalid. The task was not automatically retried.');\n }\n}\n"]}
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/runtime/client.ts"],"names":[],"mappings":"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","sourcesContent":["import { spawn, type ChildProcess } from 'node:child_process';\nimport { setTimeout as delay } from 'node:timers/promises';\nimport { AgentError } from '../errors.js';\nimport { processExitDescription } from '../process-diagnostics.js';\nimport type { OutputRecord } from '../cli/output.js';\nimport { type Endpoint, localProgram, privateDirectory, readEndpoint, stateDirectory } from './state.js';\nimport { startupOutcome, startupRetirementRequest, type StartupOutcome } from './startup-protocol.js';\n\ninterface ConnectionOptions {\n directory?: string;\n timeoutMs?: number;\n afterSpawn?: (child: ChildProcess) => void;\n beforeRetirement?: (child: ChildProcess) => void | Promise<void>;\n program?: ReturnType<typeof localProgram>;\n}\n\ntype CandidateOutcome = StartupOutcome\n | {state:'spawn-failed'}\n | {state:'exited';code:unknown;signal:unknown};\nconst retirementConfirmationMs = 1_000;\nconst coordinatorOperations = new Set([\n 'dashboard', 'dashboard-session', 'dashboard-detail', 'parent', 'run', 'send', 'resume', 'wait',\n 'status', 'queue', 'cancel', 'respond',\n]);\n\nfunction coordinatorOperation(body: unknown): string | undefined {\n if (!body || typeof body !== 'object' || Array.isArray(body)) return;\n const command = (body as {command?:unknown}).command;\n return typeof command === 'string' && coordinatorOperations.has(command) ? command : undefined;\n}\n\nexport async function connect(start: boolean, options: ConnectionOptions = {}): Promise<Endpoint> {\n const directory = options.directory ?? stateDirectory();\n const available = await readEndpoint(directory);\n if (available) return available;\n if (!start) throw new AgentError('UNKNOWN_SESSION','No coordinator is reachable from this environment. Its sessions may have ended, or native permissions may block access to its local endpoint.');\n await privateDirectory(directory);\n const deadline = Date.now() + (options.timeoutMs ?? 15_000);\n let child: ChildProcess | undefined;\n let candidate: ReturnType<typeof observeCandidate> | undefined;\n try {\n if (Date.now() >= deadline) throw new AgentError('STARTUP_FAILED','Another command did not finish coordinator startup.');\n const program = options.program ?? localProgram('daemon');\n try {\n child = spawn(process.execPath,[...program.execArgv,program.file,String(Math.max(1,deadline-Date.now()))],{\n detached:true,stdio:['ignore','ignore','ignore','ipc'],env:{...process.env,AGENT_STATE_DIR:directory},\n });\n } catch {\n throw new AgentError('STARTUP_FAILED','The local coordinator could not be spawned.');\n }\n candidate = observeCandidate(child);\n options.afterSpawn?.(child);\n child.unref();\n while (Date.now() < deadline && !candidate.current()) {\n await delay(50);\n const endpoint = await readEndpoint(directory);\n if (endpoint) return endpoint;\n }\n const observed = candidate.current();\n if (observed?.state === 'published') return observed.endpoint;\n if (!observed) {\n await options.beforeRetirement?.(child);\n requestRetirement(child);\n }\n const outcome = observed ?? await within(candidate.outcome,retirementConfirmationMs);\n if (outcome?.state === 'published') return outcome.endpoint;\n const replacement = await readEndpoint(directory);\n if (replacement) return replacement;\n if (!observed && outcome)\n throw new AgentError('STARTUP_FAILED','The local coordinator timed out before publishing an endpoint.');\n if (observed?.state === 'spawn-failed')\n throw new AgentError('STARTUP_FAILED','The local coordinator could not be spawned.');\n if (observed?.state === 'exited')\n throw new AgentError('STARTUP_FAILED',\n `The local coordinator exited before startup completed${processExitDescription(observed.code, observed.signal)}.`);\n if (observed?.state === 'retired')\n throw new AgentError('STARTUP_FAILED','The local coordinator retired before startup completed.');\n if (outcome?.state === 'retired' || outcome?.state === 'exited' || outcome?.state === 'spawn-failed')\n throw new AgentError('STARTUP_FAILED','The local coordinator could not start.');\n throw new AgentError('STARTUP_FAILED','The local coordinator timed out, and its retirement could not be confirmed. It was left untouched because it may have published.');\n } finally {\n candidate?.close();\n if (child?.connected) {\n try { child.disconnect(); } catch { /* The candidate may have disconnected after retirement. */ }\n }\n }\n}\n\nfunction observeCandidate(child: ChildProcess) {\n let current: CandidateOutcome | undefined;\n let settle!: (outcome: CandidateOutcome) => void;\n const outcome = new Promise<CandidateOutcome>(resolve => { settle = resolve; });\n const finish = (value: CandidateOutcome) => {\n if (current) return;\n current = value;\n settle(value);\n };\n const onMessage = (message: unknown) => {\n const value = startupOutcome(message);\n if (value) finish(value);\n };\n const onError = () => finish({state:'spawn-failed'});\n const onExit = (code: unknown, signal: unknown) => finish({state:'exited',code,signal});\n child.on('message',onMessage);\n child.once('error',onError);\n child.once('exit',onExit);\n return {\n outcome,\n current:() => current,\n close() {\n child.off('message',onMessage);\n child.off('error',onError);\n child.off('exit',onExit);\n },\n };\n}\n\nfunction requestRetirement(child: ChildProcess) {\n if (!child.connected) return;\n try { child.send(startupRetirementRequest,()=>{}); }\n catch { /* The bounded outcome wait reports an unconfirmed retirement. */ }\n}\n\nasync function within<T>(promise: Promise<T>, timeoutMs: number): Promise<T | undefined> {\n let timer: number | NodeJS.Timeout | undefined;\n try {\n return await Promise.race([\n promise,\n new Promise<undefined>(resolve => { timer = setTimeout(resolve,timeoutMs); }),\n ]);\n } finally { if (timer) clearTimeout(timer); }\n}\n\nexport async function* request(endpoint: Endpoint, body: unknown): AsyncGenerator<OutputRecord> {\n const operation = coordinatorOperation(body);\n let response: globalThis.Response;\n try {\n response = await fetch(endpoint.url,{method:'POST',headers:{authorization:`Bearer ${endpoint.token}`,'content-type':'application/json'},body:JSON.stringify(body)});\n } catch { throw new AgentError('COORDINATOR_UNAVAILABLE','Connection to the local coordinator failed. The task was not automatically retried.'); }\n if (!response.ok) {\n void response.body?.cancel().catch(() => {});\n throw new AgentError('COORDINATOR_UNAVAILABLE',\n `The coordinator rejected ${operation ? `the ${operation} operation` : 'the request'} with HTTP status ${response.status}.`);\n }\n if (!response.body) throw new AgentError('COORDINATOR_UNAVAILABLE',\n `The coordinator returned a successful response with no body${operation ? ` for the ${operation} operation` : ''}.`);\n let buffer = '';\n const decoder = new TextDecoder();\n try {\n for await (const chunk of response.body) {\n buffer += decoder.decode(chunk,{stream:true});\n let newline;\n while ((newline = buffer.indexOf('\\n')) >= 0) {\n const line = buffer.slice(0,newline); buffer = buffer.slice(newline + 1);\n if (line) {\n let record: OutputRecord;\n try { record = JSON.parse(line) as OutputRecord; }\n catch {\n throw new AgentError('COORDINATOR_UNAVAILABLE',\n `The coordinator returned a malformed JSON record${operation ? ` for the ${operation} operation` : ''}. The task was not automatically retried.`);\n }\n yield record;\n }\n }\n }\n buffer += decoder.decode();\n if (buffer.trim()) throw new AgentError('COORDINATOR_UNAVAILABLE',\n `The coordinator response ended with an incomplete final record${operation ? ` for the ${operation} operation` : ''}. The task was not automatically retried.`);\n } catch (error) {\n if (error instanceof AgentError) throw error;\n throw new AgentError('COORDINATOR_UNAVAILABLE','The coordinator response stream was interrupted. The task was not automatically retried.');\n }\n}\n"]}
@@ -0,0 +1,2 @@
1
+ /** Removes terminal instructions while retaining whitespace for the caller to normalize. */
2
+ export declare function sanitizeDashboardTerminalText(text: string): string;
@@ -0,0 +1,7 @@
1
+ import stripAnsi from 'strip-ansi';
2
+ const unsafeControls = /[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f-\u009f]/gu;
3
+ /** Removes terminal instructions while retaining whitespace for the caller to normalize. */
4
+ export function sanitizeDashboardTerminalText(text) {
5
+ return stripAnsi(text).replace(unsafeControls, '');
6
+ }
7
+ //# sourceMappingURL=dashboard-sanitize.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dashboard-sanitize.js","sourceRoot":"","sources":["../../src/runtime/dashboard-sanitize.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,YAAY,CAAC;AAEnC,MAAM,cAAc,GAAG,yDAAyD,CAAC;AAEjF,4FAA4F;AAC5F,MAAM,UAAU,6BAA6B,CAAC,IAAY;IACxD,OAAO,SAAS,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC;AACrD,CAAC","sourcesContent":["import stripAnsi from 'strip-ansi';\n\nconst unsafeControls = /[\\u0000-\\u0008\\u000b\\u000c\\u000e-\\u001f\\u007f-\\u009f]/gu;\n\n/** Removes terminal instructions while retaining whitespace for the caller to normalize. */\nexport function sanitizeDashboardTerminalText(text: string): string {\n return stripAnsi(text).replace(unsafeControls, '');\n}\n"]}
@@ -1,7 +1,7 @@
1
1
  import type { ErrorInfo } from '../errors.js';
2
2
  import type { HarnessKind } from '../sdk/types.js';
3
3
  import type { TaskState } from './types.js';
4
- export type DashboardHarness = 'codex' | 'claude' | 'fx' | 'pending';
4
+ export type DashboardHarness = 'codex' | 'claude' | 'fx' | 'opencode' | 'copilot' | 'cursor' | 'pending';
5
5
  export interface DashboardWorkspace {
6
6
  directory: string;
7
7
  repository?: string;
@@ -1,14 +1,13 @@
1
- import { stripVTControlCharacters } from 'node:util';
1
+ import { sanitizeDashboardTerminalText } from './dashboard-sanitize.js';
2
2
  export function dashboardHarness(kind) {
3
3
  if (kind === 'claudeCode')
4
4
  return 'claude';
5
5
  return kind ?? 'pending';
6
6
  }
7
7
  export function dashboardTitle(prompt) {
8
- const plain = stripVTControlCharacters(prompt);
8
+ const plain = sanitizeDashboardTerminalText(prompt);
9
9
  for (const line of plain.split(/\r\n?|\n/u)) {
10
10
  const normalized = line
11
- .replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f-\u009f]/gu, '')
12
11
  .replace(/\s+/gu, ' ')
13
12
  .trim();
14
13
  if (normalized)
@@ -1 +1 @@
1
- {"version":3,"file":"dashboard.js","sourceRoot":"","sources":["../../src/runtime/dashboard.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,WAAW,CAAC;AAwErD,MAAM,UAAU,gBAAgB,CAAC,IAAkB;IACjD,IAAI,IAAI,KAAK,YAAY;QAAE,OAAO,QAAQ,CAAC;IAC3C,OAAO,IAAI,IAAI,SAAS,CAAC;AAC3B,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,MAAc;IAC3C,MAAM,KAAK,GAAG,wBAAwB,CAAC,MAAM,CAAC,CAAC;IAC/C,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,CAAC,WAAW,CAAC,EAAE,CAAC;QAC5C,MAAM,UAAU,GAAG,IAAI;aACpB,OAAO,CAAC,yDAAyD,EAAE,EAAE,CAAC;aACtE,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC;aACrB,IAAI,EAAE,CAAC;QACV,IAAI,UAAU;YAAE,OAAO,CAAC,GAAG,UAAU,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAChE,CAAC;IACD,OAAO,YAAY,CAAC;AACtB,CAAC","sourcesContent":["import { stripVTControlCharacters } from 'node:util';\nimport type { ErrorInfo } from '../errors.js';\nimport type { HarnessKind } from '../sdk/types.js';\nimport type { TaskState } from './types.js';\n\nexport type DashboardHarness = 'codex' | 'claude' | 'fx' | 'pending';\n\nexport interface DashboardWorkspace {\n directory: string;\n repository?: string;\n}\n\nexport interface DashboardRow {\n sessionId: string;\n taskId: string;\n harness: DashboardHarness;\n title: string;\n state: TaskState;\n workspace: DashboardWorkspace;\n updatedAt: number;\n dispatchedAt?: number;\n failedAt?: number;\n finishedAt?: number;\n}\n\nexport interface DashboardSnapshot {\n version: 1;\n type: 'dashboard';\n history: true;\n grouping: true;\n sessions: DashboardRow[];\n}\n\nexport interface DashboardRelatedTask {\n taskId: string;\n sessionId: string;\n harness: DashboardHarness;\n title: string;\n state: TaskState;\n}\n\nexport interface DashboardRequest extends DashboardRow {\n ordinal: number;\n stopUnconfirmed: boolean;\n}\n\nexport interface DashboardSession {\n version: 1;\n type: 'dashboard-session';\n taskId: string;\n sessionId: string;\n title: string;\n harness: DashboardHarness;\n state: TaskState;\n workspace: DashboardWorkspace;\n requests: DashboardRequest[];\n subagents: DashboardRelatedTask[];\n queuePaused: boolean;\n}\n\nexport interface DashboardDetail {\n version: 1;\n type: 'dashboard-detail';\n task: DashboardRow;\n prompt?: string;\n response?: {responseId: string; text?: string};\n error?: ErrorInfo;\n children: DashboardRelatedTask[];\n queue: {paused: boolean; tasks: DashboardRelatedTask[]};\n stopUnconfirmed: boolean;\n}\n\nexport function dashboardHarness(kind?: HarnessKind): DashboardHarness {\n if (kind === 'claudeCode') return 'claude';\n return kind ?? 'pending';\n}\n\nexport function dashboardTitle(prompt: string): string {\n const plain = stripVTControlCharacters(prompt);\n for (const line of plain.split(/\\r\\n?|\\n/u)) {\n const normalized = line\n .replace(/[\\u0000-\\u0008\\u000b\\u000c\\u000e-\\u001f\\u007f-\\u009f]/gu, '')\n .replace(/\\s+/gu, ' ')\n .trim();\n if (normalized) return [...normalized].slice(0, 120).join('');\n }\n return '(untitled)';\n}\n"]}
1
+ {"version":3,"file":"dashboard.js","sourceRoot":"","sources":["../../src/runtime/dashboard.ts"],"names":[],"mappings":"AAEA,OAAO,EAAC,6BAA6B,EAAC,MAAM,yBAAyB,CAAC;AAsEtE,MAAM,UAAU,gBAAgB,CAAC,IAAkB;IACjD,IAAI,IAAI,KAAK,YAAY;QAAE,OAAO,QAAQ,CAAC;IAC3C,OAAO,IAAI,IAAI,SAAS,CAAC;AAC3B,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,MAAc;IAC3C,MAAM,KAAK,GAAG,6BAA6B,CAAC,MAAM,CAAC,CAAC;IACpD,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,CAAC,WAAW,CAAC,EAAE,CAAC;QAC5C,MAAM,UAAU,GAAG,IAAI;aACpB,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC;aACrB,IAAI,EAAE,CAAC;QACV,IAAI,UAAU;YAAE,OAAO,CAAC,GAAG,UAAU,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAChE,CAAC;IACD,OAAO,YAAY,CAAC;AACtB,CAAC","sourcesContent":["import type { ErrorInfo } from '../errors.js';\nimport type { HarnessKind } from '../sdk/types.js';\nimport {sanitizeDashboardTerminalText} from './dashboard-sanitize.ts';\nimport type { TaskState } from './types.js';\n\nexport type DashboardHarness = 'codex' | 'claude' | 'fx' | 'opencode' | 'copilot' | 'cursor' | 'pending';\n\nexport interface DashboardWorkspace {\n directory: string;\n repository?: string;\n}\n\nexport interface DashboardRow {\n sessionId: string;\n taskId: string;\n harness: DashboardHarness;\n title: string;\n state: TaskState;\n workspace: DashboardWorkspace;\n updatedAt: number;\n dispatchedAt?: number;\n failedAt?: number;\n finishedAt?: number;\n}\n\nexport interface DashboardSnapshot {\n version: 1;\n type: 'dashboard';\n history: true;\n grouping: true;\n sessions: DashboardRow[];\n}\n\nexport interface DashboardRelatedTask {\n taskId: string;\n sessionId: string;\n harness: DashboardHarness;\n title: string;\n state: TaskState;\n}\n\nexport interface DashboardRequest extends DashboardRow {\n ordinal: number;\n stopUnconfirmed: boolean;\n}\n\nexport interface DashboardSession {\n version: 1;\n type: 'dashboard-session';\n taskId: string;\n sessionId: string;\n title: string;\n harness: DashboardHarness;\n state: TaskState;\n workspace: DashboardWorkspace;\n requests: DashboardRequest[];\n subagents: DashboardRelatedTask[];\n queuePaused: boolean;\n}\n\nexport interface DashboardDetail {\n version: 1;\n type: 'dashboard-detail';\n task: DashboardRow;\n prompt?: string;\n response?: {responseId: string; text?: string};\n error?: ErrorInfo;\n children: DashboardRelatedTask[];\n queue: {paused: boolean; tasks: DashboardRelatedTask[]};\n stopUnconfirmed: boolean;\n}\n\nexport function dashboardHarness(kind?: HarnessKind): DashboardHarness {\n if (kind === 'claudeCode') return 'claude';\n return kind ?? 'pending';\n}\n\nexport function dashboardTitle(prompt: string): string {\n const plain = sanitizeDashboardTerminalText(prompt);\n for (const line of plain.split(/\\r\\n?|\\n/u)) {\n const normalized = line\n .replace(/\\s+/gu, ' ')\n .trim();\n if (normalized) return [...normalized].slice(0, 120).join('');\n }\n return '(untitled)';\n}\n"]}
@@ -1,13 +1,23 @@
1
1
  import { loadDefinition } from '../config/loader.js';
2
2
  import { AgentError } from '../errors.js';
3
3
  export function harnessReference(kind, model, effort) {
4
+ if ((kind === 'opencode' || kind === 'copilot' || kind === 'cursor') && !model?.trim()) {
5
+ throw new AgentError('INVALID_CONFIG', `${kind} requires --model with an explicit nonempty model identifier.`);
6
+ }
7
+ if ((kind === 'opencode' || kind === 'copilot' || kind === 'cursor') && effort !== undefined) {
8
+ throw new AgentError('INVALID_ARGUMENT', `${kind} does not support --effort.`);
9
+ }
4
10
  if (kind === 'claudeCode') {
5
11
  if (effort !== undefined && effort !== 'low' && effort !== 'medium' && effort !== 'high' && effort !== 'xhigh' && effort !== 'max') {
6
12
  throw new AgentError('INVALID_ARGUMENT', 'Claude effort must be low, medium, high, xhigh, or max.');
7
13
  }
8
14
  return { kind: 'harness', harness: { kind, model, effort, fast: false } };
9
15
  }
10
- return { kind: 'harness', harness: kind === 'fx' ? { kind, model, effort } : { kind, model, effort, fast: false } };
16
+ if (kind === 'fx')
17
+ return { kind: 'harness', harness: { kind, model, effort } };
18
+ if (kind === 'codex')
19
+ return { kind: 'harness', harness: { kind, model, effort, fast: false } };
20
+ return { kind: 'harness', harness: { kind, model: model } };
11
21
  }
12
22
  /** Generic sessions do not import user definitions or add a specialist role. */
13
23
  export async function loadExecutionDefinition(reference) {
@@ -1 +1 @@
1
- {"version":3,"file":"definition.js","sourceRoot":"","sources":["../../src/runtime/definition.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAK1C,MAAM,UAAU,gBAAgB,CAAC,IAAiB,EAAE,KAAc,EAAE,MAAe;IACjF,IAAI,IAAI,KAAK,YAAY,EAAE,CAAC;QAC1B,IAAI,MAAM,KAAK,SAAS,IAAI,MAAM,KAAK,KAAK,IAAI,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,MAAM,IAAI,MAAM,KAAK,OAAO,IAAI,MAAM,KAAK,KAAK,EAAE,CAAC;YACnI,MAAM,IAAI,UAAU,CAAC,kBAAkB,EAAE,yDAAyD,CAAC,CAAC;QACtG,CAAC;QACD,OAAO,EAAC,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,EAAC,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAC,EAAC,CAAC;IACxE,CAAC;IACD,OAAO,EAAC,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,EAAC,IAAI,EAAE,KAAK,EAAE,MAAM,EAAC,CAAC,CAAC,CAAC,EAAC,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAC,EAAC,CAAC;AAChH,CAAC;AAED,gFAAgF;AAChF,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAAC,SAA6B;IACzE,IAAI,MAAM,IAAI,SAAS;QAAE,OAAO,cAAc,CAAC,SAAS,CAAC,IAAI,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACpF,MAAM,IAAI,GAAG,SAAS,CAAC,OAAO,CAAC,IAAI,KAAK,YAAY,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC;IACzF,OAAO,EAAC,IAAI,EAAE,WAAW,EAAE,kBAAkB,IAAI,WAAW,EAAE,YAAY,EAAE,EAAE,EAAE,OAAO,EAAE,SAAS,CAAC,OAAO,EAAC,CAAC;AAC9G,CAAC","sourcesContent":["import { loadDefinition } from '../config/loader.js';\nimport { AgentError } from '../errors.js';\nimport type { ExecutionDefinition } from '../adapters/types.js';\nimport type { HarnessKind } from '../sdk/types.js';\nimport type { ExecutionReference, HarnessReference } from './types.js';\n\nexport function harnessReference(kind: HarnessKind, model?: string, effort?: string): HarnessReference {\n if (kind === 'claudeCode') {\n if (effort !== undefined && effort !== 'low' && effort !== 'medium' && effort !== 'high' && effort !== 'xhigh' && effort !== 'max') {\n throw new AgentError('INVALID_ARGUMENT', 'Claude effort must be low, medium, high, xhigh, or max.');\n }\n return {kind: 'harness', harness: {kind, model, effort, fast: false}};\n }\n return {kind: 'harness', harness: kind === 'fx' ? {kind, model, effort} : {kind, model, effort, fast: false}};\n}\n\n/** Generic sessions do not import user definitions or add a specialist role. */\nexport async function loadExecutionDefinition(reference: ExecutionReference): Promise<ExecutionDefinition> {\n if ('file' in reference) return loadDefinition(reference.file, reference.subagents);\n const name = reference.harness.kind === 'claudeCode' ? 'claude' : reference.harness.kind;\n return {name, description: `Run the native ${name} harness.`, instructions: '', harness: reference.harness};\n}\n"]}
1
+ {"version":3,"file":"definition.js","sourceRoot":"","sources":["../../src/runtime/definition.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAK1C,MAAM,UAAU,gBAAgB,CAAC,IAAiB,EAAE,KAAc,EAAE,MAAe;IACjF,IAAI,CAAC,IAAI,KAAK,UAAU,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,QAAQ,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC;QACvF,MAAM,IAAI,UAAU,CAAC,gBAAgB,EAAE,GAAG,IAAI,+DAA+D,CAAC,CAAC;IACjH,CAAC;IACD,IAAI,CAAC,IAAI,KAAK,UAAU,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,QAAQ,CAAC,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QAC7F,MAAM,IAAI,UAAU,CAAC,kBAAkB,EAAE,GAAG,IAAI,6BAA6B,CAAC,CAAC;IACjF,CAAC;IACD,IAAI,IAAI,KAAK,YAAY,EAAE,CAAC;QAC1B,IAAI,MAAM,KAAK,SAAS,IAAI,MAAM,KAAK,KAAK,IAAI,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,MAAM,IAAI,MAAM,KAAK,OAAO,IAAI,MAAM,KAAK,KAAK,EAAE,CAAC;YACnI,MAAM,IAAI,UAAU,CAAC,kBAAkB,EAAE,yDAAyD,CAAC,CAAC;QACtG,CAAC;QACD,OAAO,EAAC,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,EAAC,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAC,EAAC,CAAC;IACxE,CAAC;IACD,IAAI,IAAI,KAAK,IAAI;QAAE,OAAO,EAAC,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,EAAC,IAAI,EAAE,KAAK,EAAE,MAAM,EAAC,EAAC,CAAC;IAC5E,IAAI,IAAI,KAAK,OAAO;QAAE,OAAO,EAAC,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,EAAC,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAC,EAAC,CAAC;IAC5F,OAAO,EAAC,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,EAAC,IAAI,EAAE,KAAK,EAAE,KAAM,EAAC,EAAC,CAAC;AAC3D,CAAC;AAED,gFAAgF;AAChF,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAAC,SAA6B;IACzE,IAAI,MAAM,IAAI,SAAS;QAAE,OAAO,cAAc,CAAC,SAAS,CAAC,IAAI,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACpF,MAAM,IAAI,GAAG,SAAS,CAAC,OAAO,CAAC,IAAI,KAAK,YAAY,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC;IACzF,OAAO,EAAC,IAAI,EAAE,WAAW,EAAE,kBAAkB,IAAI,WAAW,EAAE,YAAY,EAAE,EAAE,EAAE,OAAO,EAAE,SAAS,CAAC,OAAO,EAAC,CAAC;AAC9G,CAAC","sourcesContent":["import { loadDefinition } from '../config/loader.js';\nimport { AgentError } from '../errors.js';\nimport type { ExecutionDefinition } from '../adapters/types.js';\nimport type { HarnessKind } from '../sdk/types.js';\nimport type { ExecutionReference, HarnessReference } from './types.js';\n\nexport function harnessReference(kind: HarnessKind, model?: string, effort?: string): HarnessReference {\n if ((kind === 'opencode' || kind === 'copilot' || kind === 'cursor') && !model?.trim()) {\n throw new AgentError('INVALID_CONFIG', `${kind} requires --model with an explicit nonempty model identifier.`);\n }\n if ((kind === 'opencode' || kind === 'copilot' || kind === 'cursor') && effort !== undefined) {\n throw new AgentError('INVALID_ARGUMENT', `${kind} does not support --effort.`);\n }\n if (kind === 'claudeCode') {\n if (effort !== undefined && effort !== 'low' && effort !== 'medium' && effort !== 'high' && effort !== 'xhigh' && effort !== 'max') {\n throw new AgentError('INVALID_ARGUMENT', 'Claude effort must be low, medium, high, xhigh, or max.');\n }\n return {kind: 'harness', harness: {kind, model, effort, fast: false}};\n }\n if (kind === 'fx') return {kind: 'harness', harness: {kind, model, effort}};\n if (kind === 'codex') return {kind: 'harness', harness: {kind, model, effort, fast: false}};\n return {kind: 'harness', harness: {kind, model: model!}};\n}\n\n/** Generic sessions do not import user definitions or add a specialist role. */\nexport async function loadExecutionDefinition(reference: ExecutionReference): Promise<ExecutionDefinition> {\n if ('file' in reference) return loadDefinition(reference.file, reference.subagents);\n const name = reference.harness.kind === 'claudeCode' ? 'claude' : reference.harness.kind;\n return {name, description: `Run the native ${name} harness.`, instructions: '', harness: reference.harness};\n}\n"]}
@@ -1,33 +1,51 @@
1
1
  import { openCodex } from '../adapters/codex.js';
2
2
  import { openClaude } from '../adapters/claude.js';
3
3
  import { openFx } from '../adapters/fx.js';
4
+ import { openOpencode } from '../adapters/opencode.js';
5
+ import { openCopilot } from '../adapters/copilot.js';
6
+ import { openCursor } from '../adapters/cursor.js';
4
7
  import { resolveAccess } from '../config/access.js';
8
+ import { accessAttemptContext } from '../config/access-provenance.js';
5
9
  import { AgentError } from '../errors.js';
6
- const openers = { codex: openCodex, claudeCode: openClaude, fx: openFx };
10
+ import { withAccessContext, withSessionAccessContext } from './access-errors.js';
11
+ const openers = {
12
+ codex: openCodex,
13
+ claudeCode: openClaude,
14
+ fx: openFx,
15
+ opencode: openOpencode,
16
+ copilot: openCopilot,
17
+ cursor: openCursor,
18
+ };
7
19
  export async function selectNative(agent, cwd, env, settings, open = (options) => openers[options.harness.kind](options), selected = () => { }, onApproval) {
8
20
  const alternatives = Array.isArray(agent.harness) ? agent.harness : [agent.harness];
9
21
  const unavailable = [];
10
22
  for (const harness of alternatives) {
11
23
  const configuredConnections = settings.access?.[harness.kind];
24
+ const explicitAccessRequired = ['fx', 'opencode', 'copilot', 'cursor'].includes(harness.kind);
12
25
  const connections = configuredConnections ??
13
- (harness.kind === 'fx' ? [] : [{ type: 'subscription' }]);
26
+ (explicitAccessRequired ? [] : [{ type: 'subscription' }]);
14
27
  if (configuredConnections?.length === 0) {
15
28
  unavailable.push(`${harness.kind}: Access is disabled in the main checkout's .subharness/agents.local.json.`);
16
29
  }
17
- else if (harness.kind === 'fx' && !connections.length) {
18
- unavailable.push('fx: Enable an explicit AI Gateway connection in personal access settings.');
30
+ else if (!connections.length && explicitAccessRequired) {
31
+ unavailable.push(harness.kind === 'cursor'
32
+ ? 'cursor: Enable an explicit Cursor subscription or API-key connection in personal access settings.'
33
+ : `${harness.kind}: Enable an explicit native login, API-key, or Gateway connection in personal access settings.`);
19
34
  }
20
- for (const connection of connections) {
35
+ for (const [index, connection] of connections.entries()) {
36
+ const context = accessAttemptContext(settings, harness.kind, connection, index, connections.length);
21
37
  try {
22
38
  const access = await resolveAccess(connection, harness.kind, cwd, env);
23
39
  const native = await open({ agent, harness, cwd, env, access, ...(onApproval ? { onApproval } : {}) });
24
40
  selected(harness.kind);
25
- return native;
41
+ return withSessionAccessContext(native, context);
26
42
  }
27
43
  catch (error) {
28
- if (!(error instanceof AgentError) || !['HARNESS_UNAVAILABLE', 'ACCESS_UNAVAILABLE'].includes(error.code))
44
+ if (!(error instanceof AgentError))
29
45
  throw error;
30
- unavailable.push(`${harness.kind}: ${error.message}`);
46
+ if (!['HARNESS_UNAVAILABLE', 'ACCESS_UNAVAILABLE'].includes(error.code))
47
+ throw withAccessContext(error, context);
48
+ unavailable.push(`${harness.kind}: ${withAccessContext(error, context).message}`);
31
49
  }
32
50
  }
33
51
  }
@@ -1 +1 @@
1
- {"version":3,"file":"select-native.js","sourceRoot":"","sources":["../../src/runtime/select-native.ts"],"names":[],"mappings":"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","sourcesContent":["import type { HarnessKind } from '../sdk/types.js';\nimport type { AdapterOptions, ExecutionDefinition, NativeSession } from '../adapters/types.js';\nimport { openCodex } from '../adapters/codex.js';\nimport { openClaude } from '../adapters/claude.js';\nimport { openFx } from '../adapters/fx.js';\nimport { resolveAccess, type AccessConfig } from '../config/access.js';\nimport { AgentError } from '../errors.js';\nimport type { ApprovalHandler } from '../approvals/types.js';\n\nconst openers = {codex:openCodex,claudeCode:openClaude,fx:openFx};\n\nexport async function selectNative(\n agent: ExecutionDefinition, cwd: string, env: NodeJS.ProcessEnv, settings: AccessConfig,\n open = (options: AdapterOptions): Promise<NativeSession> => openers[options.harness.kind](options),\n selected: (kind: HarnessKind) => void = () => {},\n onApproval?: ApprovalHandler,\n): Promise<NativeSession> {\n const alternatives = Array.isArray(agent.harness) ? agent.harness : [agent.harness];\n const unavailable: string[] = [];\n for (const harness of alternatives) {\n const configuredConnections = settings.access?.[harness.kind as HarnessKind];\n const connections = configuredConnections ??\n (harness.kind === 'fx' ? [] : [{type:'subscription' as const}]);\n if (configuredConnections?.length === 0) {\n unavailable.push(`${harness.kind}: Access is disabled in the main checkout's .subharness/agents.local.json.`);\n } else if (harness.kind === 'fx' && !connections.length) {\n unavailable.push('fx: Enable an explicit AI Gateway connection in personal access settings.');\n }\n for (const connection of connections) {\n try {\n const access = await resolveAccess(connection, harness.kind, cwd, env);\n const native = await open({agent,harness,cwd,env,access,...(onApproval ? {onApproval} : {})});\n selected(harness.kind);\n return native;\n } catch (error) {\n if (!(error instanceof AgentError) || !['HARNESS_UNAVAILABLE','ACCESS_UNAVAILABLE'].includes(error.code)) throw error;\n unavailable.push(`${harness.kind}: ${error.message}`);\n }\n }\n }\n throw new AgentError('ACCESS_UNAVAILABLE', unavailable.length ? unavailable.join('\\n') : 'No declared harness has enabled access.');\n}\n"]}
1
+ {"version":3,"file":"select-native.js","sourceRoot":"","sources":["../../src/runtime/select-native.ts"],"names":[],"mappings":"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","sourcesContent":["import type { HarnessKind } from '../sdk/types.js';\nimport type { AdapterOptions, ExecutionDefinition, NativeSession } from '../adapters/types.js';\nimport { openCodex } from '../adapters/codex.js';\nimport { openClaude } from '../adapters/claude.js';\nimport { openFx } from '../adapters/fx.js';\nimport { openOpencode } from '../adapters/opencode.js';\nimport { openCopilot } from '../adapters/copilot.js';\nimport { openCursor } from '../adapters/cursor.js';\nimport { resolveAccess, type AccessConfig } from '../config/access.js';\nimport { accessAttemptContext } from '../config/access-provenance.js';\nimport { AgentError } from '../errors.js';\nimport type { ApprovalHandler } from '../approvals/types.js';\nimport { withAccessContext, withSessionAccessContext } from './access-errors.js';\n\nconst openers = {\n codex:openCodex,\n claudeCode:openClaude,\n fx:openFx,\n opencode:openOpencode,\n copilot:openCopilot,\n cursor:openCursor,\n} satisfies Record<HarnessKind, (options: AdapterOptions) => Promise<NativeSession>>;\n\nexport async function selectNative(\n agent: ExecutionDefinition, cwd: string, env: NodeJS.ProcessEnv, settings: AccessConfig,\n open = (options: AdapterOptions): Promise<NativeSession> => openers[options.harness.kind](options),\n selected: (kind: HarnessKind) => void = () => {},\n onApproval?: ApprovalHandler,\n): Promise<NativeSession> {\n const alternatives = Array.isArray(agent.harness) ? agent.harness : [agent.harness];\n const unavailable: string[] = [];\n for (const harness of alternatives) {\n const configuredConnections = settings.access?.[harness.kind as HarnessKind];\n const explicitAccessRequired = ['fx','opencode','copilot','cursor'].includes(harness.kind);\n const connections = configuredConnections ??\n (explicitAccessRequired ? [] : [{type:'subscription' as const}]);\n if (configuredConnections?.length === 0) {\n unavailable.push(`${harness.kind}: Access is disabled in the main checkout's .subharness/agents.local.json.`);\n } else if (!connections.length && explicitAccessRequired) {\n unavailable.push(harness.kind === 'cursor'\n ? 'cursor: Enable an explicit Cursor subscription or API-key connection in personal access settings.'\n : `${harness.kind}: Enable an explicit native login, API-key, or Gateway connection in personal access settings.`);\n }\n for (const [index, connection] of connections.entries()) {\n const context = accessAttemptContext(settings, harness.kind, connection, index, connections.length);\n try {\n const access = await resolveAccess(connection, harness.kind, cwd, env);\n const native = await open({agent,harness,cwd,env,access,...(onApproval ? {onApproval} : {})});\n selected(harness.kind);\n return withSessionAccessContext(native, context);\n } catch (error) {\n if (!(error instanceof AgentError)) throw error;\n if (!['HARNESS_UNAVAILABLE','ACCESS_UNAVAILABLE'].includes(error.code)) throw withAccessContext(error, context);\n unavailable.push(`${harness.kind}: ${withAccessContext(error, context).message}`);\n }\n }\n }\n throw new AgentError('ACCESS_UNAVAILABLE', unavailable.length ? unavailable.join('\\n') : 'No declared harness has enabled access.');\n}\n"]}
@@ -3,8 +3,18 @@ import { fileURLToPath } from 'node:url';
3
3
  import { delimiter } from 'node:path';
4
4
  import { AgentError, errorInfo } from '../errors.js';
5
5
  import { createSessionLauncher } from './session-launcher.js';
6
+ import { processExitDescription } from '../process-diagnostics.js';
6
7
  const closeResponseTimeoutMs = 15_000;
7
8
  const shutdownTimeoutMs = 1_000;
9
+ const workerOperationLabels = {
10
+ open: 'startup',
11
+ turn: 'turn submission',
12
+ steer: 'steering',
13
+ interrupt: 'interruption',
14
+ resume: 'recovery',
15
+ close: 'closing',
16
+ 'approval.respond': 'approval response',
17
+ };
8
18
  function waitWithin(promise, timeout) {
9
19
  return new Promise(resolve => {
10
20
  const timer = setTimeout(() => resolve(false), timeout);
@@ -30,9 +40,9 @@ export async function createWorker(input, options = {}) {
30
40
  stdio: ['ignore', 'ignore', 'ignore', 'ipc'],
31
41
  });
32
42
  }
33
- catch (error) {
43
+ catch {
34
44
  await launcher.close();
35
- throw error;
45
+ throw new AgentError('HARNESS_FAILED', 'The session worker could not be spawned during startup.');
36
46
  }
37
47
  let nextId = 1;
38
48
  const pending = new Map();
@@ -40,22 +50,28 @@ export async function createWorker(input, options = {}) {
40
50
  const withdrawApprovals = () => { for (const controller of approvals.values())
41
51
  controller.abort(); approvals.clear(); };
42
52
  let exited = false;
53
+ let closing = false;
54
+ let observedExit = '';
43
55
  let markExited;
44
56
  const processExit = new Promise(resolve => { markExited = resolve; });
45
- const exit = () => {
57
+ const exit = (code, signal, spawnFailure = false) => {
46
58
  if (exited)
47
59
  return;
48
60
  exited = true;
61
+ observedExit = spawnFailure || closing ? '' : processExitDescription(code, signal);
49
62
  withdrawApprovals();
50
63
  markExited();
51
64
  void launcher.close().catch(() => { }).finally(() => {
52
- for (const waiting of pending.values())
53
- waiting.reject(new AgentError('HARNESS_FAILED', 'The session worker exited.'));
65
+ for (const waiting of pending.values()) {
66
+ waiting.reject(new AgentError('HARNESS_FAILED', spawnFailure
67
+ ? `The session worker process failed while ${waiting.operation} was pending.`
68
+ : `The session worker exited while ${waiting.operation} was pending${observedExit}.`));
69
+ }
54
70
  pending.clear();
55
71
  });
56
72
  };
57
- child.once('error', exit);
58
- child.once('exit', exit);
73
+ child.once('error', () => exit(undefined, undefined, true));
74
+ child.once('exit', (code, signal) => exit(code, signal));
59
75
  child.on('message', (message) => {
60
76
  if ('event' in message) {
61
77
  if (message.event === 'approval.withdraw') {
@@ -89,16 +105,24 @@ export async function createWorker(input, options = {}) {
89
105
  waiting.resolve(message.result);
90
106
  });
91
107
  const request = (method, extra = {}) => new Promise((resolve, reject) => {
108
+ const operation = workerOperationLabels[method] ?? 'native operation';
92
109
  if (exited) {
93
- reject(new AgentError('HARNESS_FAILED', 'The session worker is unavailable.'));
110
+ reject(new AgentError('HARNESS_FAILED', `The session worker is unavailable while ${operation} was pending${observedExit}.`));
94
111
  return;
95
112
  }
96
113
  const id = nextId++;
97
- pending.set(id, { resolve, reject });
98
- child.send({ id, method, ...extra }, error => { if (error) {
99
- pending.delete(id);
100
- reject(new AgentError('HARNESS_FAILED', 'Cannot reach the session worker.'));
101
- } });
114
+ pending.set(id, { operation, resolve, reject });
115
+ child.send({ id, method, ...extra }, error => {
116
+ if (!error)
117
+ return;
118
+ setImmediate(() => {
119
+ const waiting = pending.get(id);
120
+ if (!waiting || exited)
121
+ return;
122
+ pending.delete(id);
123
+ waiting.reject(new AgentError('HARNESS_FAILED', `Cannot reach the session worker while ${operation} was pending.`));
124
+ });
125
+ });
102
126
  });
103
127
  let selectedHarness;
104
128
  const { onApproval, ...serializedInput } = input;
@@ -136,6 +160,7 @@ export async function createWorker(input, options = {}) {
136
160
  interrupt: async () => { withdrawApprovals(); await request('interrupt'); },
137
161
  resume: () => request('resume'),
138
162
  close: async () => {
163
+ closing = true;
139
164
  withdrawApprovals();
140
165
  let failure;
141
166
  if (!exited) {