@work-graph/cli 0.2.0

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 (194) hide show
  1. package/README.md +31 -0
  2. package/bin/work-graph.mjs +238 -0
  3. package/package.json +38 -0
  4. package/vendor/packages/design-tokens/generated/gripe-dark-default.css +67 -0
  5. package/vendor/packages/design-tokens/generated/marketplace-default.css +67 -0
  6. package/vendor/packages/design-tokens/generated/workgraph-dark.css +67 -0
  7. package/vendor/packages/workgraph-mcp/README.md +28 -0
  8. package/vendor/packages/workgraph-mcp/bin/workgraph-mcp.mjs +21 -0
  9. package/vendor/packages/workgraph-mcp/package.json +37 -0
  10. package/vendor/packages/workgraph-mcp/src/handlers.mjs +761 -0
  11. package/vendor/packages/workgraph-mcp/src/index.mjs +638 -0
  12. package/vendor/packages/workgraph-mcp/src/prompts.mjs +162 -0
  13. package/vendor/public/assets/workgraph-logo.svg +11 -0
  14. package/vendor/public/fonts/GraphikLCG/GraphikLCG-Medium.woff2 +0 -0
  15. package/vendor/public/fonts/GraphikLCG/GraphikLCG-Regular.woff2 +0 -0
  16. package/vendor/public/fonts/GraphikLCG/GraphikLCG-Semibold.woff2 +0 -0
  17. package/vendor/public/fonts/GraphikLCG/stylesheet.css +25 -0
  18. package/vendor/public/graph-canvas-lit-flow.css +154 -0
  19. package/vendor/public/graph-canvas-lit-flow.css.map +7 -0
  20. package/vendor/public/graph-canvas-lit-flow.js +8530 -0
  21. package/vendor/public/graph-canvas-lit-flow.js.map +7 -0
  22. package/vendor/src/agentBehaviorRulesAudit.mjs +168 -0
  23. package/vendor/src/agentBehaviorRulesBundle.mjs +144 -0
  24. package/vendor/src/agentRunApi.mjs +136 -0
  25. package/vendor/src/agentToolLoopGuard.mjs +88 -0
  26. package/vendor/src/agentWorkerClaudeProvider.mjs +288 -0
  27. package/vendor/src/agentWorkerCursorSdkProvider.mjs +156 -0
  28. package/vendor/src/agentWorkerLiveLoop.mjs +455 -0
  29. package/vendor/src/agentWorkerLocalCliProvider.mjs +217 -0
  30. package/vendor/src/agentWorkerLocalRunner.mjs +246 -0
  31. package/vendor/src/agentWorkerOpenAiProvider.mjs +459 -0
  32. package/vendor/src/analyticsPanelProjection.mjs +212 -0
  33. package/vendor/src/analyticsRecordStore.mjs +165 -0
  34. package/vendor/src/analyticsRecordWorkItems.mjs +104 -0
  35. package/vendor/src/architectureL1Canon.mjs +419 -0
  36. package/vendor/src/architectureLayout.mjs +229 -0
  37. package/vendor/src/architectureSnapshot.mjs +490 -0
  38. package/vendor/src/architectureViewsProjection.mjs +116 -0
  39. package/vendor/src/atomInspector.mjs +253 -0
  40. package/vendor/src/atomInspectorApi.mjs +130 -0
  41. package/vendor/src/auditGapMatrixRefresh.mjs +121 -0
  42. package/vendor/src/backlogSchemaLint.mjs +176 -0
  43. package/vendor/src/blockedOnebaseGoPreflightEval.mjs +100 -0
  44. package/vendor/src/bracketIrTraceSignal.mjs +93 -0
  45. package/vendor/src/bvcAtomParser.mjs +210 -0
  46. package/vendor/src/bvcDialectRegistry.mjs +86 -0
  47. package/vendor/src/bvcFileFormat.mjs +218 -0
  48. package/vendor/src/bvcFormatCli.mjs +55 -0
  49. package/vendor/src/bvcLintCli.mjs +48 -0
  50. package/vendor/src/bvcNewWritePolicy.mjs +70 -0
  51. package/vendor/src/charterPreflightPromoteGate.mjs +194 -0
  52. package/vendor/src/claimNoEligibleEval.mjs +205 -0
  53. package/vendor/src/closingAnalysisSuggest.mjs +59 -0
  54. package/vendor/src/codeGapAnalyzer.mjs +308 -0
  55. package/vendor/src/codeGapBacklogFeeder.mjs +82 -0
  56. package/vendor/src/codeGapDraftIntakeApi.mjs +307 -0
  57. package/vendor/src/codeGapOperatorProjection.mjs +60 -0
  58. package/vendor/src/codeSyntaxHighlight.mjs +123 -0
  59. package/vendor/src/codegenEvidence.mjs +187 -0
  60. package/vendor/src/compilerRoundTripCli.mjs +164 -0
  61. package/vendor/src/dagreGraphLayout.mjs +78 -0
  62. package/vendor/src/draftIntakePromotionRules.mjs +205 -0
  63. package/vendor/src/epicWorkScope.mjs +85 -0
  64. package/vendor/src/evalLiveLlmEnv.mjs +63 -0
  65. package/vendor/src/evidenceReadModel.mjs +167 -0
  66. package/vendor/src/gfsOverlayProjectPassport.mjs +235 -0
  67. package/vendor/src/globalStepPathToBvcReferences.mjs +196 -0
  68. package/vendor/src/goldenPath.mjs +69 -0
  69. package/vendor/src/graphCanvasLayout.mjs +464 -0
  70. package/vendor/src/graphCanvasLitFlow/client/graphCanvasMinimap.ts +261 -0
  71. package/vendor/src/graphCanvasLitFlow/client/graphCanvasSvgEdges.ts +259 -0
  72. package/vendor/src/graphCanvasLitFlow/client/graphCanvasTheme.css +152 -0
  73. package/vendor/src/graphCanvasLitFlow/client/graphCardNode.ts +328 -0
  74. package/vendor/src/graphCanvasLitFlow/client/mountGraphCanvasLitFlow.ts +322 -0
  75. package/vendor/src/graphCanvasLitFlow/graphCanvasEdgeLabels.mjs +58 -0
  76. package/vendor/src/graphCanvasLitFlow/graphCanvasEdgeRouter.mjs +142 -0
  77. package/vendor/src/graphCanvasLitFlow/graphCanvasLayoutProfile.mjs +32 -0
  78. package/vendor/src/graphCanvasLitFlow/graphCanvasNodeMetrics.mjs +45 -0
  79. package/vendor/src/graphCanvasLitFlow/graphCanvasProjection.mjs +115 -0
  80. package/vendor/src/graphCanvasLitFlow/graphCanvasProjectionToFlow.mjs +133 -0
  81. package/vendor/src/graphCanvasLitFlow/graphCanvasTraversal.mjs +77 -0
  82. package/vendor/src/graphCanvasLitFlow/layoutIntentRoadmapWorkStack.mjs +73 -0
  83. package/vendor/src/graphCanvasLitFlow/resolveGraphCanvasOverlaps.mjs +77 -0
  84. package/vendor/src/graphRagContextSlice.mjs +461 -0
  85. package/vendor/src/gvmVerifyWorkerGate.mjs +95 -0
  86. package/vendor/src/homeSnapshotApi.mjs +131 -0
  87. package/vendor/src/homeSnapshotProjection.mjs +275 -0
  88. package/vendor/src/inboxEventStream.mjs +140 -0
  89. package/vendor/src/intentComposerApi.mjs +245 -0
  90. package/vendor/src/intentGraphGbcSliceBoundary.mjs +258 -0
  91. package/vendor/src/intentGraphProjection.mjs +208 -0
  92. package/vendor/src/intentHierarchy.mjs +241 -0
  93. package/vendor/src/intentNodeLint.mjs +107 -0
  94. package/vendor/src/intentNodeRuntime.mjs +185 -0
  95. package/vendor/src/intentRoadmapCanvas.mjs +393 -0
  96. package/vendor/src/intentRoadmapEpicProjection.mjs +122 -0
  97. package/vendor/src/intentRoadmapMermaid.mjs +165 -0
  98. package/vendor/src/intentRoadmapProjection.mjs +85 -0
  99. package/vendor/src/intentTreeLint.mjs +114 -0
  100. package/vendor/src/intentTreeMigration.mjs +150 -0
  101. package/vendor/src/intentTreeWorkItems.mjs +227 -0
  102. package/vendor/src/kanbanBoardProjection.mjs +58 -0
  103. package/vendor/src/languageAdapterRegistry.mjs +180 -0
  104. package/vendor/src/languageAdapters/goAdapter.mjs +62 -0
  105. package/vendor/src/languageAdapters/jsTsAdapter.mjs +60 -0
  106. package/vendor/src/languageAdapters/jsonYamlAdapter.mjs +103 -0
  107. package/vendor/src/languageAdapters/onebaseOsAdapter.mjs +55 -0
  108. package/vendor/src/languageAdapters/plaintextAdapter.mjs +36 -0
  109. package/vendor/src/languageAdapters/shared.mjs +68 -0
  110. package/vendor/src/languageAdapters/stepAdapter.mjs +81 -0
  111. package/vendor/src/lintPlanWorkAlignment.mjs +136 -0
  112. package/vendor/src/loopHintRepeatToolEval.mjs +153 -0
  113. package/vendor/src/lowcodeScaffoldCli.mjs +386 -0
  114. package/vendor/src/markdownDocumentRender.mjs +208 -0
  115. package/vendor/src/memoryPanelProjection.mjs +116 -0
  116. package/vendor/src/memoryRecordWriter.mjs +243 -0
  117. package/vendor/src/memoryWorkerSlice.mjs +238 -0
  118. package/vendor/src/migrateStepToBvc.mjs +133 -0
  119. package/vendor/src/missionControlServerHandlers.mjs +195 -0
  120. package/vendor/src/missionControlUiClient.mjs +278 -0
  121. package/vendor/src/onebaseCliCapabilityProbe.mjs +107 -0
  122. package/vendor/src/onebaseCliRunner.mjs +145 -0
  123. package/vendor/src/onebaseGrossProfitStaticVerify.mjs +98 -0
  124. package/vendor/src/onebaseParityEvidenceSync.mjs +88 -0
  125. package/vendor/src/onebasePvrgGraphNodes.mjs +257 -0
  126. package/vendor/src/onebaseRestEvidenceAdapter.mjs +216 -0
  127. package/vendor/src/onebaseVectorDslCodegenReadiness.mjs +137 -0
  128. package/vendor/src/onebaseWorkItemTemplate.mjs +154 -0
  129. package/vendor/src/onebaseWorkerTools.mjs +586 -0
  130. package/vendor/src/operatorShellProjection.mjs +102 -0
  131. package/vendor/src/pipelineProseRender.mjs +180 -0
  132. package/vendor/src/pipelineStageLint.mjs +118 -0
  133. package/vendor/src/promptRulesEditorApi.mjs +174 -0
  134. package/vendor/src/promptRulesProjection.mjs +134 -0
  135. package/vendor/src/pvrg/bladeAdapter.mjs +40 -0
  136. package/vendor/src/pvrgTaskScope.mjs +152 -0
  137. package/vendor/src/releaseGateMatrix.mjs +188 -0
  138. package/vendor/src/schematicView.mjs +305 -0
  139. package/vendor/src/seedAnalyticsRecord.mjs +217 -0
  140. package/vendor/src/semanticSearchBm25.mjs +103 -0
  141. package/vendor/src/semanticSearchExcerpts.mjs +68 -0
  142. package/vendor/src/semanticSearchTfidfVector.mjs +86 -0
  143. package/vendor/src/semanticSearchWorkflow.mjs +366 -0
  144. package/vendor/src/stepAtomFormatter.mjs +413 -0
  145. package/vendor/src/stepGraphSlice.mjs +318 -0
  146. package/vendor/src/ui/atoms/badge.mjs +40 -0
  147. package/vendor/src/ui/atoms/badgeClient.mjs +32 -0
  148. package/vendor/src/ui/atoms/button.mjs +114 -0
  149. package/vendor/src/ui/atoms/buttonClient.mjs +49 -0
  150. package/vendor/src/ui/atoms/icon.mjs +23 -0
  151. package/vendor/src/ui/atoms/input.mjs +38 -0
  152. package/vendor/src/ui/atoms/modal.mjs +44 -0
  153. package/vendor/src/ui/atoms/select.mjs +98 -0
  154. package/vendor/src/ui/backlogShellButtons.mjs +238 -0
  155. package/vendor/src/ui/htmlEscape.mjs +11 -0
  156. package/vendor/src/ui/molecules/rating.mjs +48 -0
  157. package/vendor/src/ui/molecules/tabs.mjs +70 -0
  158. package/vendor/src/ui/organisms/modal.mjs +1 -0
  159. package/vendor/src/ui/pages/uiKitPage.mjs +147 -0
  160. package/vendor/src/ui/workItemStatusTone.mjs +36 -0
  161. package/vendor/src/unifiedLinkageProjection.mjs +264 -0
  162. package/vendor/src/verificationLoop.mjs +206 -0
  163. package/vendor/src/workGraphBacklogPersist.mjs +234 -0
  164. package/vendor/src/workGraphBacklogUiServer.mjs +9192 -0
  165. package/vendor/src/workGraphBoundedTargetFileRead.mjs +178 -0
  166. package/vendor/src/workGraphCycleSlice.mjs +184 -0
  167. package/vendor/src/workGraphDaemonTick.mjs +307 -0
  168. package/vendor/src/workGraphDaemonWatch.mjs +157 -0
  169. package/vendor/src/workGraphEngineRoot.mjs +136 -0
  170. package/vendor/src/workGraphInstallLayout.mjs +65 -0
  171. package/vendor/src/workGraphLlmUsefulnessEval.mjs +611 -0
  172. package/vendor/src/workGraphPhasePromoteReadyQueue.mjs +159 -0
  173. package/vendor/src/workGraphProjectHost.mjs +149 -0
  174. package/vendor/src/workGraphProjectInit.mjs +392 -0
  175. package/vendor/src/workGraphPromoteReadyApi.mjs +115 -0
  176. package/vendor/src/workGraphRecoveryPolicy.mjs +124 -0
  177. package/vendor/src/workGraphRunnerQueueProjection.mjs +187 -0
  178. package/vendor/src/workGraphRuntime.mjs +1008 -0
  179. package/vendor/src/workGraphToolSurfaceAudit.mjs +372 -0
  180. package/vendor/src/workGraphToolTransportRuntime.mjs +195 -0
  181. package/vendor/src/workGraphWorkerProvider.mjs +600 -0
  182. package/vendor/src/workItemBvcQuality.mjs +262 -0
  183. package/vendor/src/workItemCreateAnalysis.mjs +157 -0
  184. package/vendor/src/workItemDecisionPipeline.mjs +278 -0
  185. package/vendor/src/workItemEpicCascade.mjs +176 -0
  186. package/vendor/src/workItemExecutionGate.mjs +78 -0
  187. package/vendor/src/workItemHierarchy.mjs +226 -0
  188. package/vendor/src/workItemProseLint.mjs +133 -0
  189. package/vendor/src/workItemTextRusify.mjs +794 -0
  190. package/vendor/src/workItemTraceEnvelope.mjs +158 -0
  191. package/vendor/src/workItemUiReferences.mjs +272 -0
  192. package/vendor/src/workflowEpicGrouping.mjs +67 -0
  193. package/vendor/src/workflowTreeProjection.mjs +53 -0
  194. package/vendor/src/workspaceRegistry.mjs +150 -0
@@ -0,0 +1,600 @@
1
+ import { runOpenAiCompatibleWorker } from './agentWorkerOpenAiProvider.mjs';
2
+ import { runLocalWorker } from './agentWorkerLocalRunner.mjs';
3
+ import { runCursorSdkWorker } from './agentWorkerCursorSdkProvider.mjs';
4
+ import { runClaudeSdkApiWorker } from './agentWorkerClaudeProvider.mjs';
5
+ import { runLocalCliWorker } from './agentWorkerLocalCliProvider.mjs';
6
+ import {
7
+ ONEBASE_WORKER_TOOL_IDS,
8
+ isOnebaseDomainTask,
9
+ resolveOnebaseAllowedTools,
10
+ } from './onebaseWorkerTools.mjs';
11
+
12
+ export const WORKER_PROVIDER_IDS = ['local', 'openai'];
13
+
14
+ export const PROVIDER_CAPABILITY_KEYS = [
15
+ 'deterministic',
16
+ 'nativeToolCalls',
17
+ 'structuredOutput',
18
+ 'timeoutSupport',
19
+ 'cancelSupport',
20
+ 'fileContext',
21
+ 'networkAccess',
22
+ 'shellAccess',
23
+ 'longContext',
24
+ 'ideWorkspaceActions',
25
+ ];
26
+
27
+ const REGISTRY_ENTRIES = {
28
+ local: {
29
+ id: 'local',
30
+ title: 'Deterministic local dry-run provider',
31
+ aliases: [],
32
+ implementationStatus: 'implemented',
33
+ async: false,
34
+ liveEnvFlag: null,
35
+ runWorker: runLocalWorker,
36
+ capabilities: {
37
+ deterministic: true,
38
+ nativeToolCalls: false,
39
+ structuredOutput: 'high',
40
+ timeoutSupport: true,
41
+ cancelSupport: false,
42
+ fileContext: 'targetFiles',
43
+ networkAccess: false,
44
+ shellAccess: false,
45
+ longContext: false,
46
+ ideWorkspaceActions: false,
47
+ },
48
+ costHint: 'free',
49
+ latencyHint: 'low',
50
+ reliabilityHint: 'high',
51
+ defaultForCi: true,
52
+ },
53
+ openai: {
54
+ id: 'openai',
55
+ title: 'OpenAI-compatible HTTP provider',
56
+ aliases: ['openai-compatible'],
57
+ implementationStatus: 'implemented',
58
+ async: true,
59
+ liveEnvFlag: 'IOHASC_E2E_REAL_LLM',
60
+ runWorker: runOpenAiCompatibleWorker,
61
+ capabilities: {
62
+ deterministic: false,
63
+ nativeToolCalls: true,
64
+ structuredOutput: 'medium',
65
+ timeoutSupport: true,
66
+ cancelSupport: false,
67
+ fileContext: 'targetFiles',
68
+ networkAccess: true,
69
+ shellAccess: false,
70
+ longContext: 'medium',
71
+ ideWorkspaceActions: false,
72
+ },
73
+ costHint: 'variable',
74
+ latencyHint: 'medium',
75
+ reliabilityHint: 'medium',
76
+ defaultForCi: false,
77
+ },
78
+ 'cursor-sdk': {
79
+ id: 'cursor-sdk',
80
+ title: 'Cursor SDK agent provider',
81
+ aliases: [],
82
+ implementationStatus: 'implemented',
83
+ async: true,
84
+ liveEnvFlag: 'IOHASC_CURSOR_SDK_WORKER',
85
+ runWorker: runCursorSdkWorker,
86
+ capabilities: {
87
+ deterministic: false,
88
+ nativeToolCalls: true,
89
+ structuredOutput: 'medium',
90
+ timeoutSupport: true,
91
+ cancelSupport: true,
92
+ fileContext: 'workspace',
93
+ networkAccess: true,
94
+ shellAccess: true,
95
+ longContext: 'medium',
96
+ ideWorkspaceActions: true,
97
+ },
98
+ costHint: 'subscription',
99
+ latencyHint: 'medium',
100
+ reliabilityHint: 'medium',
101
+ defaultForCi: false,
102
+ },
103
+ 'claude-sdk-api': {
104
+ id: 'claude-sdk-api',
105
+ title: 'Claude SDK/API provider',
106
+ aliases: ['claude', 'anthropic'],
107
+ implementationStatus: 'implemented',
108
+ async: true,
109
+ liveEnvFlag: 'IOHASC_CLAUDE_WORKER',
110
+ runWorker: runClaudeSdkApiWorker,
111
+ capabilities: {
112
+ deterministic: false,
113
+ nativeToolCalls: true,
114
+ structuredOutput: 'high',
115
+ timeoutSupport: true,
116
+ cancelSupport: true,
117
+ fileContext: 'targetFiles',
118
+ networkAccess: true,
119
+ shellAccess: false,
120
+ longContext: 'high',
121
+ ideWorkspaceActions: false,
122
+ },
123
+ costHint: 'variable',
124
+ latencyHint: 'medium',
125
+ reliabilityHint: 'high',
126
+ defaultForCi: false,
127
+ },
128
+ 'local-cli': {
129
+ id: 'local-cli',
130
+ title: 'Local CLI/runner provider',
131
+ aliases: ['cli'],
132
+ implementationStatus: 'implemented',
133
+ async: true,
134
+ liveEnvFlag: 'IOHASC_LOCAL_CLI_WORKER',
135
+ runWorker: runLocalCliWorker,
136
+ capabilities: {
137
+ deterministic: true,
138
+ nativeToolCalls: false,
139
+ structuredOutput: 'medium',
140
+ timeoutSupport: true,
141
+ cancelSupport: true,
142
+ fileContext: 'targetFiles',
143
+ networkAccess: false,
144
+ shellAccess: true,
145
+ longContext: false,
146
+ ideWorkspaceActions: false,
147
+ },
148
+ costHint: 'free',
149
+ latencyHint: 'low',
150
+ reliabilityHint: 'high',
151
+ defaultForCi: false,
152
+ },
153
+ };
154
+
155
+ function normalizeProviderId(providerId) {
156
+ const id = String(providerId ?? 'local').trim().toLowerCase();
157
+
158
+ for (const entry of Object.values(REGISTRY_ENTRIES)) {
159
+ if (entry.id === id) {
160
+ return entry.id;
161
+ }
162
+
163
+ if (entry.aliases.includes(id)) {
164
+ return entry.id;
165
+ }
166
+ }
167
+
168
+ return id;
169
+ }
170
+
171
+ function getRegistryEntry(providerId) {
172
+ const id = normalizeProviderId(providerId);
173
+ const entry = REGISTRY_ENTRIES[id];
174
+
175
+ if (!entry) {
176
+ throw new Error(`unsupported worker provider: ${providerId}`);
177
+ }
178
+
179
+ return entry;
180
+ }
181
+
182
+ function capabilityMatches(required, actual) {
183
+ if (required === undefined || required === null) {
184
+ return true;
185
+ }
186
+
187
+ if (typeof required === 'boolean') {
188
+ return Boolean(actual) === required;
189
+ }
190
+
191
+ if (typeof required === 'string' && typeof actual === 'string') {
192
+ const rank = { low: 1, medium: 2, high: 3, targetFiles: 2, workspace: 3 };
193
+ return (rank[actual] ?? 0) >= (rank[required] ?? 0);
194
+ }
195
+
196
+ return actual === required;
197
+ }
198
+
199
+ function scoreProvider(entry, requiredCapabilities = {}, policy = {}) {
200
+ let score = 0;
201
+ const matchedCapabilities = [];
202
+ const missingCapabilities = [];
203
+
204
+ for (const [key, required] of Object.entries(requiredCapabilities)) {
205
+ const actual = entry.capabilities[key];
206
+
207
+ if (capabilityMatches(required, actual)) {
208
+ score += 10;
209
+ matchedCapabilities.push(key);
210
+ } else {
211
+ score -= 25;
212
+ missingCapabilities.push(key);
213
+ }
214
+ }
215
+
216
+ if (policy.denyNetworkAccess && entry.capabilities.networkAccess) {
217
+ score -= 100;
218
+ }
219
+
220
+ if (policy.denyShellAccess && entry.capabilities.shellAccess) {
221
+ score -= 100;
222
+ }
223
+
224
+ if (policy.requireDeterministic && !entry.capabilities.deterministic) {
225
+ score -= 50;
226
+ }
227
+
228
+ if (entry.implementationStatus !== 'implemented') {
229
+ score -= 1000;
230
+ }
231
+
232
+ if (entry.defaultForCi) {
233
+ score += 1;
234
+ }
235
+
236
+ const reliabilityBonus = { high: 3, medium: 2, low: 1 }[entry.reliabilityHint] ?? 0;
237
+ const latencyBonus = { low: 3, medium: 2, high: 1 }[entry.latencyHint] ?? 0;
238
+ score += reliabilityBonus + latencyBonus;
239
+
240
+ return {
241
+ providerId: entry.id,
242
+ score,
243
+ matchedCapabilities,
244
+ missingCapabilities,
245
+ implementationStatus: entry.implementationStatus,
246
+ };
247
+ }
248
+
249
+ export function buildWorkerProviderRegistry() {
250
+ return {
251
+ schema: 'workgraph.worker.provider.registry.v1',
252
+ providers: Object.values(REGISTRY_ENTRIES).map((entry) => ({
253
+ id: entry.id,
254
+ title: entry.title,
255
+ aliases: [...entry.aliases],
256
+ implementationStatus: entry.implementationStatus,
257
+ async: entry.async,
258
+ liveEnvFlag: entry.liveEnvFlag,
259
+ capabilities: { ...entry.capabilities },
260
+ costHint: entry.costHint,
261
+ latencyHint: entry.latencyHint,
262
+ reliabilityHint: entry.reliabilityHint,
263
+ defaultForCi: entry.defaultForCi,
264
+ })),
265
+ selectionPolicy: {
266
+ order: ['requiredCapabilities', 'policyCompatibility', 'reliability', 'cost', 'latency'],
267
+ fallbackEvidenceKind: 'provider_fallback',
268
+ },
269
+ };
270
+ }
271
+
272
+ export function selectWorkerProvider(input = {}, options = {}) {
273
+ const requiredCapabilities = {
274
+ ...(input.providerHints?.requiredCapabilities ?? {}),
275
+ ...(options.requiredCapabilities ?? {}),
276
+ };
277
+ const policy = {
278
+ denyNetworkAccess: options.policy?.denyNetworkAccess ?? input.policy?.denyNetworkAccess ?? false,
279
+ denyShellAccess: options.policy?.denyShellAccess ?? input.policy?.denyShellAccess ?? false,
280
+ requireDeterministic: options.policy?.requireDeterministic ?? input.policy?.requireDeterministic ?? false,
281
+ };
282
+ const preferredProviderId = options.preferredProviderId ?? input.providerHints?.preferredProviderId ?? null;
283
+ const allowPlanned = options.allowPlanned === true;
284
+
285
+ const candidates = Object.values(REGISTRY_ENTRIES)
286
+ .filter((entry) => allowPlanned || entry.implementationStatus === 'implemented')
287
+ .map((entry) => scoreProvider(entry, requiredCapabilities, policy))
288
+ .sort((left, right) => right.score - left.score);
289
+
290
+ const best = candidates[0] ?? null;
291
+
292
+ if (!best || best.score < 0) {
293
+ return {
294
+ ok: false,
295
+ selectedProviderId: null,
296
+ selectionRationale: {
297
+ requiredCapabilities,
298
+ policy,
299
+ candidates,
300
+ reason: 'no_provider_satisfies_capabilities_or_policy',
301
+ },
302
+ };
303
+ }
304
+
305
+ if (preferredProviderId) {
306
+ const preferredId = normalizeProviderId(preferredProviderId);
307
+ const preferredEntry = REGISTRY_ENTRIES[preferredId];
308
+
309
+ if (preferredEntry && (allowPlanned || preferredEntry.implementationStatus === 'implemented')) {
310
+ const preferredScore = scoreProvider(preferredEntry, requiredCapabilities, policy);
311
+
312
+ if (preferredScore.score >= 0) {
313
+ return {
314
+ ok: true,
315
+ selectedProviderId: preferredId,
316
+ selectionRationale: {
317
+ requiredCapabilities,
318
+ policy,
319
+ preferredProviderId: preferredId,
320
+ matchedCapabilities: preferredScore.matchedCapabilities,
321
+ missingCapabilities: preferredScore.missingCapabilities,
322
+ selectionMode: 'preferred_provider',
323
+ candidates,
324
+ },
325
+ };
326
+ }
327
+ }
328
+ }
329
+
330
+ return {
331
+ ok: true,
332
+ selectedProviderId: best.providerId,
333
+ selectionRationale: {
334
+ requiredCapabilities,
335
+ policy,
336
+ matchedCapabilities: best.matchedCapabilities,
337
+ missingCapabilities: best.missingCapabilities,
338
+ selectionMode: 'capability_score',
339
+ candidates,
340
+ },
341
+ };
342
+ }
343
+
344
+ export function buildProviderFallbackEvidence({
345
+ previousProviderId,
346
+ nextProviderId,
347
+ failureClass,
348
+ reason,
349
+ retryAdvice = '',
350
+ runId = '',
351
+ taskId = '',
352
+ }) {
353
+ return {
354
+ kind: 'provider_fallback',
355
+ source: 'workgraph.worker.provider.registry.v1',
356
+ result: 'switched',
357
+ summary: [
358
+ 'provider-fallback',
359
+ previousProviderId ? `from=${previousProviderId}` : '',
360
+ nextProviderId ? `to=${nextProviderId}` : '',
361
+ failureClass ? `failureClass=${failureClass}` : '',
362
+ reason ? `reason=${String(reason).trim()}` : '',
363
+ ].filter(Boolean).join(' '),
364
+ details: {
365
+ previousProviderId: previousProviderId ?? null,
366
+ nextProviderId: nextProviderId ?? null,
367
+ failureClass: failureClass ?? 'unknown',
368
+ reason: String(reason ?? '').trim(),
369
+ retryAdvice: String(retryAdvice ?? '').trim(),
370
+ runId: runId || null,
371
+ taskId: taskId || null,
372
+ },
373
+ };
374
+ }
375
+
376
+ export function classifyWorkerFailureForFallback(output) {
377
+ if (!output || output.status === 'succeeded') {
378
+ return 'succeeded';
379
+ }
380
+
381
+ const reason = String(output.failureReason || output.transitionRequest?.reason || '').toLowerCase();
382
+
383
+ if (reason.includes('timeout') || reason.includes('timed out')) {
384
+ return 'timeout';
385
+ }
386
+
387
+ if (reason.includes('env') || reason.includes('missing_model') || reason.includes('econnrefused')) {
388
+ return 'env_blocker';
389
+ }
390
+
391
+ if (reason.includes('rate limit') || reason.includes('429')) {
392
+ return 'rate_limit';
393
+ }
394
+
395
+ if (reason.includes('policy') || reason.includes('denied')) {
396
+ return 'policy_denied';
397
+ }
398
+
399
+ return 'model_failure';
400
+ }
401
+
402
+ export function shouldFallbackWorkerProvider(output, context = {}) {
403
+ if (context.enableFallback === false) {
404
+ return false;
405
+ }
406
+
407
+ if (!output || output.status === 'succeeded') {
408
+ return false;
409
+ }
410
+
411
+ if (context.currentProviderId === 'local') {
412
+ return false;
413
+ }
414
+
415
+ const failureClass = classifyWorkerFailureForFallback(output);
416
+ return failureClass !== 'policy_denied';
417
+ }
418
+
419
+ export function mergeFallbackEvidenceIntoOutput(output, fallbackTrail = []) {
420
+ if (!Array.isArray(fallbackTrail) || fallbackTrail.length === 0) {
421
+ return output;
422
+ }
423
+
424
+ const existingEvidence = Array.isArray(output.evidence) ? output.evidence : [];
425
+
426
+ return {
427
+ ...output,
428
+ evidence: [...existingEvidence, ...fallbackTrail],
429
+ };
430
+ }
431
+
432
+ export function buildLocalFallbackWorkerInput(input) {
433
+ return {
434
+ ...input,
435
+ policy: {
436
+ mode: 'dry-run',
437
+ allowShell: false,
438
+ allowNetwork: false,
439
+ allowFileWrite: false,
440
+ timeoutMs: input?.policy?.timeoutMs ?? 0,
441
+ },
442
+ providerHints: {
443
+ ...(input?.providerHints ?? {}),
444
+ provider: 'local-runner',
445
+ deterministic: true,
446
+ },
447
+ };
448
+ }
449
+
450
+ export async function runWorkerWithSelectionAndFallback(input, options = {}) {
451
+ const explicitProviderId = options.provider ?? null;
452
+ const enableFallback = options.enableFallback !== false;
453
+ const maxFallbackAttempts = Number(options.maxFallbackAttempts ?? 1);
454
+
455
+ let providerId = explicitProviderId;
456
+ let selectionRationale = null;
457
+ const fallbackTrail = [];
458
+
459
+ if (!providerId) {
460
+ const selection = selectWorkerProvider(input, options.selectionOptions ?? {});
461
+
462
+ if (!selection.ok) {
463
+ throw new Error(`provider selection failed: ${selection.selectionRationale.reason}`);
464
+ }
465
+
466
+ providerId = selection.selectedProviderId;
467
+ selectionRationale = selection.selectionRationale;
468
+ }
469
+
470
+ let attempt = 0;
471
+ let workerInput = input;
472
+
473
+ while (attempt <= maxFallbackAttempts) {
474
+ const provider = resolveWorkerProvider(providerId);
475
+ const output = await Promise.resolve(
476
+ provider.runWorker(workerInput, options.providerOptions ?? {}),
477
+ );
478
+
479
+ const fallbackContext = {
480
+ enableFallback,
481
+ explicitProvider: Boolean(explicitProviderId),
482
+ currentProviderId: provider.id,
483
+ };
484
+
485
+ if (
486
+ shouldFallbackWorkerProvider(output, fallbackContext)
487
+ && attempt < maxFallbackAttempts
488
+ ) {
489
+ const failureClass = classifyWorkerFailureForFallback(output);
490
+ fallbackTrail.push(buildProviderFallbackEvidence({
491
+ previousProviderId: provider.id,
492
+ nextProviderId: 'local',
493
+ failureClass,
494
+ reason: output.failureReason || output.transitionRequest?.reason || failureClass,
495
+ retryAdvice: output.retryAdvice || 'retry with local deterministic provider',
496
+ runId: output.runId,
497
+ taskId: output.taskId,
498
+ }));
499
+ providerId = 'local';
500
+ workerInput = buildLocalFallbackWorkerInput(input);
501
+ attempt += 1;
502
+ continue;
503
+ }
504
+
505
+ const mergedOutput = mergeFallbackEvidenceIntoOutput(output, fallbackTrail);
506
+
507
+ return {
508
+ providerId: provider.id,
509
+ providerTitle: provider.title,
510
+ selectionRationale: attempt === 0 ? selectionRationale : selectionRationale,
511
+ explicitProvider: Boolean(explicitProviderId),
512
+ fallbackTrail,
513
+ usedFallback: fallbackTrail.length > 0,
514
+ output: mergedOutput,
515
+ };
516
+ }
517
+
518
+ throw new Error('provider fallback loop exhausted without result');
519
+ }
520
+
521
+ export function resolveWorkerProvider(providerId = 'local') {
522
+ const entry = getRegistryEntry(providerId);
523
+
524
+ if (entry.implementationStatus !== 'implemented' || typeof entry.runWorker !== 'function') {
525
+ throw new Error(`worker provider not implemented: ${entry.id}`);
526
+ }
527
+
528
+ return {
529
+ id: entry.id,
530
+ title: entry.title,
531
+ runWorker: entry.runWorker,
532
+ async: entry.async,
533
+ liveEnvFlag: entry.liveEnvFlag,
534
+ implementationStatus: entry.implementationStatus,
535
+ capabilities: { ...entry.capabilities },
536
+ };
537
+ }
538
+
539
+ export async function runWorkerWithProvider(input, options = {}) {
540
+ const result = await runWorkerWithSelectionAndFallback(input, options);
541
+ return {
542
+ providerId: result.providerId,
543
+ providerTitle: result.providerTitle,
544
+ selectionRationale: result.selectionRationale,
545
+ explicitProvider: result.explicitProvider,
546
+ fallbackTrail: result.fallbackTrail,
547
+ usedFallback: result.usedFallback,
548
+ output: result.output,
549
+ };
550
+ }
551
+
552
+ export function buildWorkerProviderCatalog() {
553
+ const registry = buildWorkerProviderRegistry();
554
+
555
+ return {
556
+ schema: 'workgraph.worker.provider.catalog.v1',
557
+ registrySchema: registry.schema,
558
+ providers: registry.providers
559
+ .filter((entry) => entry.implementationStatus === 'implemented')
560
+ .map((entry) => ({
561
+ id: entry.id,
562
+ title: entry.title,
563
+ async: entry.async,
564
+ liveEnvFlag: entry.liveEnvFlag,
565
+ defaultForCi: entry.defaultForCi,
566
+ capabilities: entry.capabilities,
567
+ })),
568
+ plannedProviders: registry.providers
569
+ .filter((entry) => entry.implementationStatus === 'planned')
570
+ .map((entry) => ({
571
+ id: entry.id,
572
+ title: entry.title,
573
+ capabilities: entry.capabilities,
574
+ })),
575
+ policy: {
576
+ mandatoryCiProvider: 'local',
577
+ optionalLiveProvider: 'openai',
578
+ liveEnableEnv: 'IOHASC_E2E_REAL_LLM=1',
579
+ llmBaseUrlEnv: 'IOHASC_LLM_BASE_URL',
580
+ llmModelEnv: 'IOHASC_LLM_MODEL',
581
+ selectionPolicy: registry.selectionPolicy,
582
+ },
583
+ };
584
+ }
585
+
586
+ export function resolveDomainWorkerCapabilities(task) {
587
+ if (isOnebaseDomainTask(task)) {
588
+ return {
589
+ domainId: 'onebase',
590
+ allowedTools: resolveOnebaseAllowedTools(task),
591
+ toolIds: ONEBASE_WORKER_TOOL_IDS,
592
+ };
593
+ }
594
+
595
+ return {
596
+ domainId: null,
597
+ allowedTools: [],
598
+ toolIds: [],
599
+ };
600
+ }