@fitlab-ai/agent-infra 0.9.12 → 0.9.13

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 (239) hide show
  1. package/README.md +11 -11
  2. package/README.zh-CN.md +11 -11
  3. package/dist/bin/internal-cli.js +24 -19
  4. package/dist/lib/agent-clients/next-steps.js +6 -1
  5. package/dist/lib/decide.js +3 -3
  6. package/dist/lib/internal/platform-checks.js +3 -3
  7. package/dist/lib/internal/platform-comment.js +9 -9
  8. package/dist/lib/internal/platform-context.js +2 -2
  9. package/dist/lib/internal/platform-issue.js +8 -9
  10. package/dist/lib/internal/platform-pr-review.js +10 -10
  11. package/dist/lib/internal/platform-pr.js +63 -24
  12. package/dist/lib/internal/platform-release-notes.js +3 -3
  13. package/dist/lib/internal/pr-review-grade.js +11 -9
  14. package/dist/lib/internal/release-workflow.js +4 -4
  15. package/dist/lib/internal/sandbox-control.js +30 -4
  16. package/dist/lib/internal/task-activity.js +3 -3
  17. package/dist/lib/internal/task-artifact.js +76 -24
  18. package/dist/lib/internal/task-create.js +2 -2
  19. package/dist/lib/internal/task-event.js +6 -3
  20. package/dist/lib/internal/task-finalization.js +2 -2
  21. package/dist/lib/internal/task-invalidation.js +60 -0
  22. package/dist/lib/internal/task-ledger.js +9 -6
  23. package/dist/lib/internal/task-lifecycle.js +2 -2
  24. package/dist/lib/internal/task-override.js +4 -4
  25. package/dist/lib/internal/task-review.js +3 -3
  26. package/dist/lib/internal/task-verify.js +6 -6
  27. package/dist/lib/internal/task-warning.js +3 -3
  28. package/dist/lib/platform/adapters.js +1 -3
  29. package/dist/lib/platform/change-request-git-evidence.js +27 -7
  30. package/dist/lib/platform/completion-backfill.js +6 -5
  31. package/dist/lib/platform/context.js +70 -130
  32. package/dist/lib/platform/github-client.js +1 -1
  33. package/dist/lib/platform/github-provider.js +903 -0
  34. package/dist/lib/platform/in-label-sync.js +143 -0
  35. package/dist/lib/platform/issue-comments.js +94 -41
  36. package/dist/lib/platform/issues.js +335 -189
  37. package/dist/lib/platform/metadata-labels.js +6 -2
  38. package/dist/lib/platform/none-provider.js +28 -0
  39. package/dist/lib/platform/pr-change-report.js +551 -0
  40. package/dist/lib/platform/pr-checks.js +119 -104
  41. package/dist/lib/platform/pr-review.js +88 -72
  42. package/dist/lib/platform/pr-summary.js +520 -50
  43. package/dist/lib/platform/provider-bridge.js +45 -0
  44. package/dist/lib/platform/provider-contract.d.ts +487 -0
  45. package/dist/lib/platform/provider-contract.js +95 -0
  46. package/dist/lib/platform/provider-loader.js +251 -0
  47. package/dist/lib/platform/provider-validation.js +504 -0
  48. package/dist/lib/platform/pull-request-metadata.js +8 -6
  49. package/dist/lib/platform/pull-requests.js +783 -537
  50. package/dist/lib/platform/release-notes.js +78 -55
  51. package/dist/lib/platform/releases.js +79 -50
  52. package/dist/lib/platform/resource-identity.d.ts +26 -0
  53. package/dist/lib/platform/resource-identity.js +92 -0
  54. package/dist/lib/platform/task-identities.js +31 -0
  55. package/dist/lib/platform/verification-required.js +11 -12
  56. package/dist/lib/platform/verification-sync.js +112 -238
  57. package/dist/lib/pr-review/evidence-grading.js +19 -6
  58. package/dist/lib/run/index.js +63 -23
  59. package/dist/lib/sandbox/control/client.js +96 -15
  60. package/dist/lib/sandbox/control/executor.js +3 -3
  61. package/dist/lib/sandbox/control/protocol.js +77 -0
  62. package/dist/lib/sandbox/control/server.js +398 -25
  63. package/dist/lib/sandbox/control/state.js +204 -1
  64. package/dist/lib/server/protocol.js +38 -1
  65. package/dist/lib/task/activity-log.js +8 -1
  66. package/dist/lib/task/artifact-lifecycle.js +33 -5
  67. package/dist/lib/task/capabilities.js +239 -0
  68. package/dist/lib/task/commands/status.js +32 -0
  69. package/dist/lib/task/commit-operation.js +12 -1
  70. package/dist/lib/task/control-authority.js +8 -5
  71. package/dist/lib/task/create-service.js +8 -8
  72. package/dist/lib/task/current-contract.js +5 -1
  73. package/dist/lib/task/decision-details.js +1 -1
  74. package/dist/lib/task/events.js +297 -17
  75. package/dist/lib/task/finalization.js +37 -13
  76. package/dist/lib/task/human-override.js +58 -36
  77. package/dist/lib/task/invalidation-command.js +86 -0
  78. package/dist/lib/task/invalidation.js +260 -0
  79. package/dist/lib/task/ledger-intents.js +37 -0
  80. package/dist/lib/task/local-artifact-finalization.js +374 -0
  81. package/dist/lib/task/orchestration.js +49 -93
  82. package/dist/lib/task/pr-delivery-fact.js +102 -101
  83. package/dist/lib/task/review-fingerprint.js +24 -2
  84. package/dist/lib/task/rework-intent.js +138 -0
  85. package/dist/lib/task/skill-scope.js +23 -0
  86. package/dist/lib/task/task-execution-lock.js +27 -17
  87. package/dist/lib/task/verification-engine.js +43 -16
  88. package/dist/lib/task/verification.js +3 -3
  89. package/dist/lib/task/workflow-warning-intents.js +1 -1
  90. package/dist/lib/task/write.js +11 -0
  91. package/dist/lib/version.js +11 -1
  92. package/dist/lifecycle-build-manifest.json +3 -3
  93. package/lib/agent-clients/next-steps.ts +6 -1
  94. package/lib/decide.ts +3 -3
  95. package/lib/init.ts +1 -1
  96. package/lib/internal/platform-checks.ts +3 -3
  97. package/lib/internal/platform-comment.ts +8 -8
  98. package/lib/internal/platform-context.ts +2 -2
  99. package/lib/internal/platform-issue.ts +7 -8
  100. package/lib/internal/platform-pr-review.ts +9 -9
  101. package/lib/internal/platform-pr.ts +47 -22
  102. package/lib/internal/platform-release-notes.ts +3 -3
  103. package/lib/internal/pr-review-grade.ts +10 -8
  104. package/lib/internal/release-workflow.ts +4 -4
  105. package/lib/internal/sandbox-control.ts +27 -3
  106. package/lib/internal/task-activity.ts +3 -3
  107. package/lib/internal/task-artifact.ts +65 -22
  108. package/lib/internal/task-create.ts +2 -2
  109. package/lib/internal/task-event.ts +6 -3
  110. package/lib/internal/task-finalization.ts +3 -3
  111. package/lib/internal/task-invalidation.ts +39 -0
  112. package/lib/internal/task-ledger.ts +11 -8
  113. package/lib/internal/task-lifecycle.ts +2 -2
  114. package/lib/internal/task-override.ts +6 -6
  115. package/lib/internal/task-review.ts +3 -3
  116. package/lib/internal/task-verify.ts +6 -6
  117. package/lib/internal/task-warning.ts +3 -3
  118. package/lib/platform/adapters.ts +3 -2
  119. package/lib/platform/change-request-git-evidence.ts +36 -9
  120. package/lib/platform/completion-backfill.ts +9 -8
  121. package/lib/platform/context.ts +97 -134
  122. package/lib/platform/github-client.ts +2 -2
  123. package/lib/platform/github-provider.ts +910 -0
  124. package/lib/platform/in-label-sync.ts +208 -0
  125. package/lib/platform/issue-comments.ts +97 -47
  126. package/lib/platform/issues.ts +344 -174
  127. package/lib/platform/metadata-labels.ts +5 -2
  128. package/lib/platform/none-provider.ts +35 -0
  129. package/lib/platform/pr-change-report.ts +645 -0
  130. package/lib/platform/pr-checks.ts +126 -106
  131. package/lib/platform/pr-review.ts +88 -85
  132. package/lib/platform/pr-summary.ts +560 -53
  133. package/lib/platform/provider-bridge.ts +82 -0
  134. package/lib/platform/provider-contract.ts +536 -0
  135. package/lib/platform/provider-loader.ts +307 -0
  136. package/lib/platform/provider-validation.ts +529 -0
  137. package/lib/platform/pull-request-metadata.ts +10 -7
  138. package/lib/platform/pull-requests.ts +826 -543
  139. package/lib/platform/release-notes.ts +76 -59
  140. package/lib/platform/releases.ts +77 -43
  141. package/lib/platform/resource-identity.ts +121 -0
  142. package/lib/platform/task-identities.ts +40 -0
  143. package/lib/platform/types.ts +4 -2
  144. package/lib/platform/verification-required.ts +11 -13
  145. package/lib/platform/verification-sync.ts +112 -292
  146. package/lib/pr-review/evidence-grading.ts +25 -8
  147. package/lib/run/index.ts +57 -22
  148. package/lib/sandbox/control/client.ts +105 -15
  149. package/lib/sandbox/control/executor.ts +4 -4
  150. package/lib/sandbox/control/protocol.ts +113 -0
  151. package/lib/sandbox/control/server.ts +431 -23
  152. package/lib/sandbox/control/state.ts +251 -1
  153. package/lib/server/protocol.ts +36 -1
  154. package/lib/task/activity-log.ts +8 -0
  155. package/lib/task/artifact-lifecycle.ts +32 -6
  156. package/lib/task/capabilities.ts +262 -0
  157. package/lib/task/commands/status.ts +38 -0
  158. package/lib/task/commit-operation.ts +12 -1
  159. package/lib/task/control-authority.ts +11 -8
  160. package/lib/task/create-service.ts +8 -8
  161. package/lib/task/current-contract.ts +6 -1
  162. package/lib/task/decision-details.ts +6 -2
  163. package/lib/task/events.ts +296 -16
  164. package/lib/task/finalization.ts +50 -14
  165. package/lib/task/human-override.ts +70 -46
  166. package/lib/task/invalidation-command.ts +86 -0
  167. package/lib/task/invalidation.ts +332 -0
  168. package/lib/task/ledger-intents.ts +35 -1
  169. package/lib/task/local-artifact-finalization.ts +563 -0
  170. package/lib/task/orchestration.ts +52 -85
  171. package/lib/task/pr-delivery-fact.ts +131 -148
  172. package/lib/task/review-fingerprint.ts +23 -2
  173. package/lib/task/rework-intent.ts +165 -0
  174. package/lib/task/skill-scope.ts +24 -0
  175. package/lib/task/task-execution-lock.ts +23 -14
  176. package/lib/task/verification-engine.ts +52 -17
  177. package/lib/task/verification.ts +4 -4
  178. package/lib/task/workflow-warning-intents.ts +1 -1
  179. package/lib/task/write.ts +19 -0
  180. package/lib/update.ts +1 -1
  181. package/lib/version.ts +15 -1
  182. package/package.json +7 -1
  183. package/templates/.agents/rules/README.en.md +1 -1
  184. package/templates/.agents/rules/README.zh-CN.md +1 -1
  185. package/templates/.agents/rules/issue-pr-commands.en.md +1 -1
  186. package/templates/.agents/rules/issue-pr-commands.zh-CN.md +1 -1
  187. package/templates/.agents/rules/issue-sync.en.md +2 -0
  188. package/templates/.agents/rules/issue-sync.zh-CN.md +2 -0
  189. package/templates/.agents/rules/local-artifact-repair.en.md +9 -2
  190. package/templates/.agents/rules/local-artifact-repair.zh-CN.md +9 -2
  191. package/templates/.agents/rules/pr-sync.en.md +10 -4
  192. package/templates/.agents/rules/pr-sync.zh-CN.md +17 -4
  193. package/templates/.agents/rules/task-short-id.en.md +1 -4
  194. package/templates/.agents/rules/task-short-id.zh-CN.md +1 -3
  195. package/templates/.agents/skills/analyze-task/SKILL.en.md +13 -3
  196. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +10 -3
  197. package/templates/.agents/skills/check-task/SKILL.en.md +16 -37
  198. package/templates/.agents/skills/check-task/SKILL.zh-CN.md +16 -37
  199. package/templates/.agents/skills/code-task/SKILL.en.md +27 -8
  200. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +29 -10
  201. package/templates/.agents/skills/code-task/config/verify.en.json +1 -0
  202. package/templates/.agents/skills/code-task/config/verify.zh-CN.json +1 -0
  203. package/templates/.agents/skills/commit/reference/issue-metadata-sync.en.md +2 -0
  204. package/templates/.agents/skills/commit/reference/issue-metadata-sync.zh-CN.md +2 -0
  205. package/templates/.agents/skills/commit/reference/pr-summary-sync.en.md +3 -3
  206. package/templates/.agents/skills/commit/reference/pr-summary-sync.zh-CN.md +3 -3
  207. package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +5 -3
  208. package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +5 -3
  209. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.en.md +4 -3
  210. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +4 -3
  211. package/templates/.agents/skills/complete-task/SKILL.en.md +21 -3
  212. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +20 -3
  213. package/templates/.agents/skills/create-pr/SKILL.en.md +7 -6
  214. package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +7 -7
  215. package/templates/.agents/skills/create-pr/config/verify.json +1 -0
  216. package/templates/.agents/skills/create-pr/reference/change-report.en.md +16 -4
  217. package/templates/.agents/skills/create-pr/reference/change-report.zh-CN.md +16 -4
  218. package/templates/.agents/skills/create-pr/reference/comment-publish.en.md +5 -3
  219. package/templates/.agents/skills/create-pr/reference/comment-publish.zh-CN.md +5 -3
  220. package/templates/.agents/skills/create-pr/reference/pr-body-template.en.md +1 -1
  221. package/templates/.agents/skills/create-pr/reference/pr-body-template.zh-CN.md +1 -1
  222. package/templates/.agents/skills/create-pr/scripts/change-report.mjs +3 -0
  223. package/templates/.agents/skills/plan-task/SKILL.en.md +13 -3
  224. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +10 -3
  225. package/templates/.agents/skills/review-analysis/SKILL.en.md +3 -3
  226. package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +3 -3
  227. package/templates/.agents/skills/review-code/SKILL.en.md +4 -4
  228. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +4 -4
  229. package/templates/.agents/skills/review-plan/SKILL.en.md +3 -3
  230. package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +3 -3
  231. package/templates/.agents/skills/run-manual-validation/SKILL.en.md +4 -2
  232. package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +4 -2
  233. package/templates/.agents/skills/watch-pr/SKILL.en.md +2 -2
  234. package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +2 -2
  235. package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.en.md +6 -0
  236. package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.zh-CN.md +6 -0
  237. package/templates/.agents/templates/task.en.md +18 -1
  238. package/templates/.agents/templates/task.zh-CN.md +18 -1
  239. package/templates/.github/workflows/pr-label.yml +7 -26
@@ -0,0 +1,307 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { createRequire } from 'node:module';
3
+ import fs from 'node:fs';
4
+ import path from 'node:path';
5
+ import { pathToFileURL } from 'node:url';
6
+ import { execFileSync } from 'node:child_process';
7
+
8
+ import {
9
+ PLATFORM_PROVIDER_CONTRACT_VERSION,
10
+ isPlatformProviderFactory,
11
+ validatePlatformProvider
12
+ } from './provider-contract.ts';
13
+ import type {
14
+ JsonValue,
15
+ PlatformError,
16
+ PlatformProvider,
17
+ PlatformProviderFactoryInput
18
+ } from './provider-contract.ts';
19
+ import { createGitHubProvider } from './github-provider.ts';
20
+ import { createNoneProvider } from './none-provider.ts';
21
+ import { wrapProviderOperations } from './provider-validation.ts';
22
+ import type { PlatformClient } from './context.ts';
23
+
24
+ type ProviderLoaderOptions = {
25
+ cwd?: string;
26
+ platformType?: string;
27
+ client?: PlatformClient;
28
+ };
29
+
30
+ type LoadedPlatformProvider = {
31
+ provider: PlatformProvider;
32
+ providerType: string;
33
+ repositoryRoot: string;
34
+ workingDirectory: string;
35
+ sourceIdentity: string;
36
+ };
37
+
38
+ type ProviderLoadResult =
39
+ | { ok: true; value: LoadedPlatformProvider }
40
+ | { ok: false; error: PlatformError };
41
+
42
+ type ResolvedExternalSource = {
43
+ sourceIdentity: string;
44
+ importUrl: string;
45
+ };
46
+
47
+ const sessions = new Map<string, Promise<PlatformProvider>>();
48
+
49
+ function findRepositoryRoot(cwd: string): string {
50
+ try {
51
+ return execFileSync('git', ['rev-parse', '--show-toplevel'], {
52
+ cwd, encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe']
53
+ }).trim();
54
+ } catch {
55
+ return cwd;
56
+ }
57
+ }
58
+
59
+ function failure(
60
+ providerType: string,
61
+ phase: string,
62
+ code: string,
63
+ message: string
64
+ ): ProviderLoadResult {
65
+ return {
66
+ ok: false,
67
+ error: { code, message, retryable: false, providerType, phase }
68
+ };
69
+ }
70
+
71
+ function isJsonValue(value: unknown): value is JsonValue {
72
+ if (value === null || typeof value === 'string' || typeof value === 'boolean') return true;
73
+ if (typeof value === 'number') return Number.isFinite(value);
74
+ if (Array.isArray(value)) return value.every(isJsonValue);
75
+ if (!value || typeof value !== 'object') return false;
76
+ return Object.values(value).every(isJsonValue);
77
+ }
78
+
79
+ function canonicalJson(value: JsonValue): string {
80
+ if (Array.isArray(value)) return '[' + value.map(canonicalJson).join(',') + ']';
81
+ if (value && typeof value === 'object') {
82
+ return '{' + Object.keys(value).sort().map((key) =>
83
+ JSON.stringify(key) + ':' + canonicalJson(value[key]!)
84
+ ).join(',') + '}';
85
+ }
86
+ return JSON.stringify(value);
87
+ }
88
+
89
+ function configFingerprint(config: Readonly<Record<string, JsonValue>>): string {
90
+ return createHash('sha256').update(canonicalJson(config)).digest('hex');
91
+ }
92
+
93
+ function readPlatformConfig(repositoryRoot: string): Record<string, unknown> {
94
+ try {
95
+ const file = path.join(repositoryRoot, '.agents', '.airc.json');
96
+ const value: unknown = JSON.parse(fs.readFileSync(file, 'utf8'));
97
+ return value && typeof value === 'object' && !Array.isArray(value)
98
+ ? value as Record<string, unknown>
99
+ : {};
100
+ } catch {
101
+ return {};
102
+ }
103
+ }
104
+
105
+ function selectedEntry(
106
+ config: Record<string, unknown>,
107
+ providerType: string
108
+ ): { source?: unknown; config?: unknown } {
109
+ const platform = config.platform && typeof config.platform === 'object'
110
+ ? config.platform as Record<string, unknown>
111
+ : {};
112
+ const providers = platform.providers && typeof platform.providers === 'object' && !Array.isArray(platform.providers)
113
+ ? platform.providers as Record<string, unknown>
114
+ : {};
115
+ const entry = providers[providerType];
116
+ return entry && typeof entry === 'object' && !Array.isArray(entry)
117
+ ? entry as { source?: unknown; config?: unknown }
118
+ : {};
119
+ }
120
+
121
+ function resolveExternalSource(
122
+ repositoryRoot: string,
123
+ source: string,
124
+ providerType: string
125
+ ): { ok: false; error: PlatformError } | { ok: true; value: ResolvedExternalSource } {
126
+ try {
127
+ const isPath = source.startsWith('.') || path.isAbsolute(source) || /^[A-Za-z]:[\\/]/u.test(source);
128
+ const resolved = isPath
129
+ ? path.resolve(repositoryRoot, source)
130
+ : createRequire(path.join(repositoryRoot, 'package.json')).resolve(source);
131
+ const realPath = fs.realpathSync(resolved);
132
+ const importUrl = pathToFileURL(realPath).href;
133
+ return { ok: true, value: { sourceIdentity: importUrl, importUrl } };
134
+ } catch {
135
+ return {
136
+ ok: false,
137
+ error: {
138
+ code: 'PLATFORM_PROVIDER_SOURCE_RESOLUTION_FAILED',
139
+ message: 'Selected provider source could not be resolved',
140
+ retryable: false,
141
+ providerType,
142
+ phase: 'source-resolution'
143
+ }
144
+ };
145
+ }
146
+ }
147
+
148
+ async function instantiateProvider(
149
+ providerType: string,
150
+ repositoryRoot: string,
151
+ importUrl: string | null,
152
+ config: Readonly<Record<string, JsonValue>>,
153
+ client?: PlatformClient
154
+ ): Promise<PlatformProvider> {
155
+ const input: PlatformProviderFactoryInput = {
156
+ providerType,
157
+ contractVersion: PLATFORM_PROVIDER_CONTRACT_VERSION,
158
+ repositoryRoot,
159
+ config
160
+ };
161
+ if (providerType === 'github') return createGitHubProvider(input, client as never);
162
+ if (providerType === 'none') return createNoneProvider(input);
163
+
164
+ let moduleValue: Record<string, unknown>;
165
+ try {
166
+ moduleValue = await import(importUrl!);
167
+ } catch {
168
+ throw Object.assign(new Error('Selected provider source could not be imported'), {
169
+ code: 'PLATFORM_PROVIDER_IMPORT_FAILED'
170
+ });
171
+ }
172
+ const factory = moduleValue.default;
173
+ if (!isPlatformProviderFactory(factory)) {
174
+ throw Object.assign(new Error('Selected provider must default-export an async factory'), {
175
+ code: 'PLATFORM_PROVIDER_EXPORT_INVALID'
176
+ });
177
+ }
178
+ let provider: unknown;
179
+ try {
180
+ const candidate = factory(input);
181
+ if (!candidate || typeof (candidate as PromiseLike<unknown>).then !== 'function') {
182
+ throw Object.assign(new Error('Provider factory must return a Promise'), {
183
+ code: 'PLATFORM_PROVIDER_EXPORT_INVALID'
184
+ });
185
+ }
186
+ provider = await candidate;
187
+ } catch (cause) {
188
+ if (cause && typeof cause === 'object' && 'code' in cause
189
+ && cause.code === 'PLATFORM_PROVIDER_EXPORT_INVALID') throw cause;
190
+ throw Object.assign(new Error('Provider factory failed'), {
191
+ code: 'PLATFORM_PROVIDER_FACTORY_FAILED'
192
+ });
193
+ }
194
+ if (provider && typeof provider === 'object' && 'type' in provider && provider.type !== providerType) {
195
+ throw Object.assign(new Error('Provider type does not match platform.type'), {
196
+ code: 'PLATFORM_PROVIDER_TYPE_MISMATCH'
197
+ });
198
+ }
199
+ if (provider && typeof provider === 'object' && 'contractVersion' in provider
200
+ && provider.contractVersion !== PLATFORM_PROVIDER_CONTRACT_VERSION) {
201
+ throw Object.assign(new Error('Provider contract version is unsupported'), {
202
+ code: 'PLATFORM_PROVIDER_VERSION_UNSUPPORTED'
203
+ });
204
+ }
205
+ const validated = validatePlatformProvider(provider, providerType);
206
+ if (!validated.ok) {
207
+ throw Object.assign(new Error(validated.error.message), {
208
+ code: validated.error.code
209
+ });
210
+ }
211
+ return wrapProviderOperations(validated.value);
212
+ }
213
+
214
+ async function loadPlatformProvider(options: ProviderLoaderOptions = {}): Promise<ProviderLoadResult> {
215
+ const workingDirectory = path.resolve(options.cwd || process.cwd());
216
+ const repositoryRoot = path.resolve(findRepositoryRoot(workingDirectory));
217
+ const config = readPlatformConfig(repositoryRoot);
218
+ const platform = config.platform && typeof config.platform === 'object'
219
+ ? config.platform as Record<string, unknown>
220
+ : {};
221
+ const providerType = options.platformType
222
+ ?? (typeof platform.type === 'string' ? platform.type : '');
223
+ if (!providerType) return failure('', 'config', 'PLATFORM_PROVIDER_CONFIG_INVALID', 'platform.type must be a non-empty string');
224
+
225
+ const entry = selectedEntry(config, providerType);
226
+ const rawConfig = entry.config === undefined ? {} : entry.config;
227
+ if (!rawConfig || typeof rawConfig !== 'object' || Array.isArray(rawConfig) || !isJsonValue(rawConfig)) {
228
+ return failure(providerType, 'config', 'PLATFORM_PROVIDER_CONFIG_INVALID', 'Selected provider config must be a JSON object');
229
+ }
230
+ const providerConfig = rawConfig as Record<string, JsonValue>;
231
+ let sourceIdentity: string;
232
+ let importUrl: string | null = null;
233
+ if (providerType === 'github' || providerType === 'none') {
234
+ sourceIdentity = 'builtin:' + providerType + '@' + PLATFORM_PROVIDER_CONTRACT_VERSION;
235
+ } else {
236
+ if (typeof entry.source !== 'string' || !entry.source.trim()) {
237
+ return failure(providerType, 'config', 'PLATFORM_PROVIDER_SOURCE_MISSING', 'Selected provider source is required');
238
+ }
239
+ const resolved = resolveExternalSource(repositoryRoot, entry.source, providerType);
240
+ if (!resolved.ok) return resolved;
241
+ sourceIdentity = resolved.value.sourceIdentity;
242
+ importUrl = resolved.value.importUrl;
243
+ }
244
+
245
+ const key = [
246
+ repositoryRoot,
247
+ providerType,
248
+ sourceIdentity,
249
+ configFingerprint(providerConfig)
250
+ ].join('\u0000');
251
+ const reuseSession = !(providerType === 'github' && options.client);
252
+ let session = reuseSession ? sessions.get(key) : undefined;
253
+ if (!session) {
254
+ session = instantiateProvider(providerType, repositoryRoot, importUrl, providerConfig, options.client);
255
+ if (reuseSession) {
256
+ sessions.set(key, session);
257
+ session.catch(() => {
258
+ if (sessions.get(key) === session) sessions.delete(key);
259
+ });
260
+ }
261
+ }
262
+ try {
263
+ const provider = await session;
264
+ return {
265
+ ok: true,
266
+ value: { provider, providerType, repositoryRoot, workingDirectory, sourceIdentity }
267
+ };
268
+ } catch (cause) {
269
+ const code = cause && typeof cause === 'object' && 'code' in cause && typeof cause.code === 'string'
270
+ ? cause.code
271
+ : 'PLATFORM_PROVIDER_FACTORY_FAILED';
272
+ const phase = code === 'PLATFORM_PROVIDER_IMPORT_FAILED'
273
+ ? 'import'
274
+ : code === 'PLATFORM_PROVIDER_EXPORT_INVALID'
275
+ ? 'export'
276
+ : code === 'PLATFORM_PROVIDER_TYPE_MISMATCH' || code === 'PLATFORM_PROVIDER_VERSION_UNSUPPORTED' || code === 'PLATFORM_PROVIDER_CONTRACT_INVALID'
277
+ ? 'validation'
278
+ : 'factory';
279
+ const messages: Record<string, string> = {
280
+ PLATFORM_PROVIDER_IMPORT_FAILED: 'Selected provider source could not be imported',
281
+ PLATFORM_PROVIDER_EXPORT_INVALID: 'Selected provider must default-export an async factory',
282
+ PLATFORM_PROVIDER_TYPE_MISMATCH: 'Provider type does not match platform.type',
283
+ PLATFORM_PROVIDER_VERSION_UNSUPPORTED: 'Provider contract version is unsupported',
284
+ PLATFORM_PROVIDER_CONTRACT_INVALID: 'Provider does not satisfy the platform provider contract',
285
+ PLATFORM_PROVIDER_FACTORY_FAILED: 'Selected provider factory failed'
286
+ };
287
+ return failure(providerType, phase, code, messages[code] ?? 'Selected provider failed to load');
288
+ }
289
+ }
290
+
291
+ function clearProviderSessions(): void {
292
+ sessions.clear();
293
+ }
294
+
295
+ export {
296
+ canonicalJson,
297
+ clearProviderSessions,
298
+ configFingerprint,
299
+ findRepositoryRoot,
300
+ loadPlatformProvider
301
+ };
302
+
303
+ export type {
304
+ LoadedPlatformProvider,
305
+ ProviderLoadResult,
306
+ ProviderLoaderOptions
307
+ };