@pellux/goodvibes-agent 1.1.7 → 1.3.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 (274) hide show
  1. package/CHANGELOG.md +329 -0
  2. package/README.md +46 -30
  3. package/dist/package/{ast-grep-napi.linux-x64-gnu-mkk8xwww.node → ast-grep-napi.linux-x64-gnu-swtppvy9.node} +0 -0
  4. package/dist/package/{ast-grep-napi.linux-x64-musl-ryqtgdv6.node → ast-grep-napi.linux-x64-musl-ttfcdtap.node} +0 -0
  5. package/dist/package/main.js +138903 -91477
  6. package/docs/README.md +14 -7
  7. package/docs/channels-remote-and-api.md +8 -5
  8. package/docs/connected-host.md +14 -12
  9. package/docs/getting-started.md +58 -30
  10. package/docs/knowledge-artifacts-and-multimodal.md +7 -5
  11. package/docs/project-planning.md +2 -2
  12. package/docs/providers-and-routing.md +11 -3
  13. package/docs/tools-and-commands.md +116 -45
  14. package/docs/voice-and-live-tts.md +3 -1
  15. package/package.json +3 -2
  16. package/release/release-notes.md +142 -5
  17. package/release/release-readiness.json +124 -94
  18. package/src/agent/assistant-cockpit.ts +247 -0
  19. package/src/agent/autonomy-schedule-format.ts +96 -0
  20. package/src/agent/autonomy-schedule.ts +514 -0
  21. package/src/agent/channel-delivery-receipts.ts +292 -0
  22. package/src/agent/competitive-feature-inventory.ts +294 -0
  23. package/src/agent/document-registry-types.ts +106 -0
  24. package/src/agent/document-registry.ts +734 -0
  25. package/src/agent/harness-control.ts +2 -2
  26. package/src/agent/memory-prompt.ts +2 -2
  27. package/src/agent/operator-actions.ts +50 -6
  28. package/src/agent/persona-registry.ts +8 -0
  29. package/src/agent/project-context-files.ts +273 -0
  30. package/src/agent/prompt-context-receipts.ts +502 -0
  31. package/src/agent/reminder-schedule-format.ts +16 -2
  32. package/src/agent/research-run-registry.ts +582 -0
  33. package/src/agent/research-source-registry.ts +441 -0
  34. package/src/agent/routine-registry.ts +19 -3
  35. package/src/agent/routine-schedule-format.ts +27 -2
  36. package/src/agent/runtime-profile-starters.ts +7 -0
  37. package/src/agent/runtime-profile.ts +106 -4
  38. package/src/agent/schedule-edit-format.ts +129 -0
  39. package/src/agent/schedule-edit.ts +496 -0
  40. package/src/agent/schedule-next-routes.ts +42 -0
  41. package/src/agent/setup-wizard-artifact-receipts.ts +366 -0
  42. package/src/agent/setup-wizard-checkpoint.ts +140 -0
  43. package/src/agent/setup-wizard.ts +683 -0
  44. package/src/agent/skill-registry-types.ts +102 -0
  45. package/src/agent/skill-registry.ts +81 -107
  46. package/src/agent/vibe-confirmation-routes.ts +62 -0
  47. package/src/agent/vibe-file.ts +285 -0
  48. package/src/cli/agent-knowledge-command.ts +45 -2
  49. package/src/cli/help.ts +3 -0
  50. package/src/cli/profiles-command.ts +25 -11
  51. package/src/config/agent-settings-policy.ts +1 -18
  52. package/src/input/agent-workspace-activation.ts +53 -0
  53. package/src/input/agent-workspace-artifact-browser-editor.ts +494 -0
  54. package/src/input/agent-workspace-artifact-metadata.ts +17 -0
  55. package/src/input/agent-workspace-basic-command-editors.ts +5 -1
  56. package/src/input/agent-workspace-categories.ts +153 -155
  57. package/src/input/agent-workspace-channel-triage.ts +481 -0
  58. package/src/input/agent-workspace-channels.ts +263 -0
  59. package/src/input/agent-workspace-command-editor.ts +152 -0
  60. package/src/input/agent-workspace-context-snapshot.ts +349 -0
  61. package/src/input/agent-workspace-delegation-editor-submission.ts +8 -0
  62. package/src/input/agent-workspace-document-editor.ts +502 -0
  63. package/src/input/agent-workspace-document-ops-editor.ts +523 -0
  64. package/src/input/agent-workspace-host-category.ts +1 -1
  65. package/src/input/agent-workspace-local-library-snapshot.ts +167 -0
  66. package/src/input/agent-workspace-model-compare-editor.ts +376 -0
  67. package/src/input/agent-workspace-model-compare-prompt-submission.ts +552 -0
  68. package/src/input/agent-workspace-model-compare-types.ts +82 -0
  69. package/src/input/agent-workspace-model-compare-utils.ts +35 -0
  70. package/src/input/agent-workspace-onboarding-categories.ts +141 -0
  71. package/src/input/agent-workspace-operations-command-editor-submission.ts +53 -0
  72. package/src/input/agent-workspace-operations-command-editors.ts +22 -0
  73. package/src/input/agent-workspace-research-report-editor.ts +212 -0
  74. package/src/input/agent-workspace-research-run-editor.ts +144 -0
  75. package/src/input/agent-workspace-research-source-editor.ts +167 -0
  76. package/src/input/agent-workspace-review-packet-snapshot.ts +593 -0
  77. package/src/input/agent-workspace-review-packet-utils.ts +265 -0
  78. package/src/input/agent-workspace-settings.ts +313 -30
  79. package/src/input/agent-workspace-setup-checkpoint-action.ts +127 -0
  80. package/src/input/agent-workspace-setup-snapshot.ts +217 -0
  81. package/src/input/agent-workspace-setup.ts +61 -1
  82. package/src/input/agent-workspace-snapshot.ts +184 -149
  83. package/src/input/agent-workspace-types.ts +287 -2
  84. package/src/input/agent-workspace.ts +23 -1
  85. package/src/input/command-registry.ts +9 -2
  86. package/src/input/commands/agent-runtime-profile-runtime.ts +28 -13
  87. package/src/input/commands/channels-runtime.ts +102 -114
  88. package/src/input/commands/delegation-runtime.ts +87 -24
  89. package/src/input/commands/knowledge-browser-flags.ts +12 -0
  90. package/src/input/commands/knowledge.ts +28 -14
  91. package/src/input/commands/operator-actions-runtime.ts +6 -3
  92. package/src/input/commands/schedule-runtime.ts +71 -14
  93. package/src/input/commands/session-content.ts +2 -2
  94. package/src/input/commands/shell-core.ts +2 -0
  95. package/src/input/commands/vibe-runtime.ts +140 -0
  96. package/src/input/commands.ts +2 -0
  97. package/src/input/connected-host-routes.ts +123 -0
  98. package/src/input/settings-modal-types.ts +14 -0
  99. package/src/input/setup-wizard-live-receipts.ts +76 -0
  100. package/src/main.ts +15 -15
  101. package/src/panels/qr-panel.ts +2 -2
  102. package/src/renderer/agent-workspace-context-lines.ts +701 -0
  103. package/src/renderer/agent-workspace.ts +68 -421
  104. package/src/renderer/settings-modal-helpers.ts +12 -0
  105. package/src/renderer/settings-modal.ts +12 -0
  106. package/src/runtime/bootstrap-command-context.ts +11 -1
  107. package/src/runtime/bootstrap-command-parts.ts +14 -3
  108. package/src/runtime/bootstrap-core.ts +39 -1
  109. package/src/runtime/bootstrap-shell.ts +6 -0
  110. package/src/runtime/bootstrap.ts +113 -30
  111. package/src/runtime/connected-host-auth.ts +3 -2
  112. package/src/runtime/execution-ledger.ts +231 -0
  113. package/src/runtime/services.ts +88 -10
  114. package/src/runtime/tool-permission-safety.ts +199 -5
  115. package/src/shell/session-continuity-hints.ts +39 -0
  116. package/src/tools/agent-artifacts-tool.ts +606 -0
  117. package/src/tools/agent-audit-tool.ts +155 -0
  118. package/src/tools/agent-autonomy-schedule-tool.ts +238 -0
  119. package/src/tools/agent-autonomy-tool.ts +140 -0
  120. package/src/tools/agent-channel-send-tool.ts +24 -2
  121. package/src/tools/agent-channels-tool.ts +140 -0
  122. package/src/tools/agent-computer-tool.ts +168 -0
  123. package/src/tools/agent-context-policy.ts +137 -7
  124. package/src/tools/agent-context-tool.ts +143 -0
  125. package/src/tools/agent-delegation-tool.ts +128 -0
  126. package/src/tools/agent-device-tool.ts +240 -0
  127. package/src/tools/agent-documents-tool.ts +684 -0
  128. package/src/tools/agent-execution-tool.ts +230 -0
  129. package/src/tools/agent-harness-agent-orchestration-policy.ts +75 -0
  130. package/src/tools/agent-harness-agent-orchestration.ts +791 -0
  131. package/src/tools/agent-harness-autonomy-intake.ts +785 -0
  132. package/src/tools/agent-harness-autonomy-live-records.ts +742 -0
  133. package/src/tools/agent-harness-autonomy-queue-types.ts +88 -0
  134. package/src/tools/agent-harness-autonomy-queue.ts +571 -0
  135. package/src/tools/agent-harness-autonomy-watcher-read-models.ts +509 -0
  136. package/src/tools/agent-harness-background-processes-types.ts +35 -0
  137. package/src/tools/agent-harness-background-processes.ts +798 -0
  138. package/src/tools/agent-harness-browser-cockpit-route.ts +373 -0
  139. package/src/tools/agent-harness-browser-control.ts +346 -0
  140. package/src/tools/agent-harness-browser-pwa-read-models.ts +622 -0
  141. package/src/tools/agent-harness-channel-metadata.ts +168 -10
  142. package/src/tools/agent-harness-cli-command-policy.ts +110 -0
  143. package/src/tools/agent-harness-command-catalog.ts +4 -2
  144. package/src/tools/agent-harness-command-runner.ts +42 -0
  145. package/src/tools/agent-harness-connected-host-status.ts +7 -4
  146. package/src/tools/agent-harness-delegation-posture.ts +234 -19
  147. package/src/tools/agent-harness-device-live-read-models.ts +366 -0
  148. package/src/tools/agent-harness-document-ops-reviewer-readiness.ts +234 -0
  149. package/src/tools/agent-harness-document-ops-types.ts +72 -0
  150. package/src/tools/agent-harness-document-ops.ts +671 -0
  151. package/src/tools/agent-harness-execution-history.ts +489 -0
  152. package/src/tools/agent-harness-execution-posture.ts +385 -0
  153. package/src/tools/agent-harness-file-recovery.ts +135 -0
  154. package/src/tools/agent-harness-interactive-runtime-records.ts +421 -0
  155. package/src/tools/agent-harness-keybinding-metadata.ts +16 -12
  156. package/src/tools/agent-harness-learning-curator-common.ts +102 -0
  157. package/src/tools/agent-harness-learning-curator-consolidation.ts +295 -0
  158. package/src/tools/agent-harness-learning-curator-proposals.ts +606 -0
  159. package/src/tools/agent-harness-learning-curator-types.ts +151 -0
  160. package/src/tools/agent-harness-learning-curator.ts +417 -0
  161. package/src/tools/agent-harness-local-model-benchmarks.ts +269 -0
  162. package/src/tools/agent-harness-local-model-cookbook.ts +238 -0
  163. package/src/tools/agent-harness-local-model-endpoints.ts +788 -0
  164. package/src/tools/agent-harness-local-model-smoke.ts +277 -0
  165. package/src/tools/agent-harness-local-model-url.ts +78 -0
  166. package/src/tools/agent-harness-media-posture.ts +223 -2
  167. package/src/tools/agent-harness-memory-external-providers.ts +796 -0
  168. package/src/tools/agent-harness-memory-posture.ts +672 -0
  169. package/src/tools/agent-harness-memory-provider-certification.ts +219 -0
  170. package/src/tools/agent-harness-memory-refinement.ts +340 -0
  171. package/src/tools/agent-harness-metadata.ts +100 -168
  172. package/src/tools/agent-harness-mode-catalog.ts +86 -33
  173. package/src/tools/agent-harness-model-catalog.ts +162 -0
  174. package/src/tools/agent-harness-model-provider-health.ts +283 -0
  175. package/src/tools/agent-harness-model-readiness.ts +432 -0
  176. package/src/tools/agent-harness-model-routing-types.ts +327 -0
  177. package/src/tools/agent-harness-model-routing-utils.ts +30 -0
  178. package/src/tools/agent-harness-model-routing.ts +165 -193
  179. package/src/tools/agent-harness-operator-methods.ts +115 -133
  180. package/src/tools/agent-harness-pairing-posture.ts +452 -18
  181. package/src/tools/agent-harness-personal-ops-certification.ts +116 -0
  182. package/src/tools/agent-harness-personal-ops-discovery.ts +533 -0
  183. package/src/tools/agent-harness-personal-ops-intake.ts +385 -0
  184. package/src/tools/agent-harness-personal-ops-lanes.ts +270 -0
  185. package/src/tools/agent-harness-personal-ops-operations.ts +225 -0
  186. package/src/tools/agent-harness-personal-ops-provider-records.ts +358 -0
  187. package/src/tools/agent-harness-personal-ops-provider-task-records.ts +321 -0
  188. package/src/tools/agent-harness-personal-ops-records.ts +684 -0
  189. package/src/tools/agent-harness-personal-ops-runner.ts +637 -0
  190. package/src/tools/agent-harness-personal-ops-types.ts +240 -0
  191. package/src/tools/agent-harness-personal-ops.ts +613 -0
  192. package/src/tools/agent-harness-project-context.ts +144 -0
  193. package/src/tools/agent-harness-prompt-context.ts +589 -0
  194. package/src/tools/agent-harness-provider-account-metadata.ts +2 -2
  195. package/src/tools/agent-harness-release-evidence.ts +9 -7
  196. package/src/tools/agent-harness-release-readiness.ts +9 -7
  197. package/src/tools/agent-harness-remote-read-models.ts +541 -0
  198. package/src/tools/agent-harness-research-briefing.ts +446 -0
  199. package/src/tools/agent-harness-research-live-read-models.ts +500 -0
  200. package/src/tools/agent-harness-research-queue.ts +250 -0
  201. package/src/tools/agent-harness-research-runs.ts +307 -0
  202. package/src/tools/agent-harness-research-workflow.ts +395 -0
  203. package/src/tools/agent-harness-security-posture.ts +2 -2
  204. package/src/tools/agent-harness-service-posture.ts +9 -9
  205. package/src/tools/agent-harness-session-metadata.ts +3 -3
  206. package/src/tools/agent-harness-setup-connected-host.ts +501 -0
  207. package/src/tools/agent-harness-setup-describe.ts +557 -0
  208. package/src/tools/agent-harness-setup-handoffs.ts +502 -0
  209. package/src/tools/agent-harness-setup-model-helpers.ts +102 -0
  210. package/src/tools/agent-harness-setup-plan.ts +253 -0
  211. package/src/tools/agent-harness-setup-posture-types.ts +218 -0
  212. package/src/tools/agent-harness-setup-posture-utils.ts +173 -0
  213. package/src/tools/agent-harness-setup-posture.ts +449 -221
  214. package/src/tools/agent-harness-setup-smoke.ts +533 -0
  215. package/src/tools/agent-harness-sudo-posture.ts +114 -0
  216. package/src/tools/agent-harness-tool-schema.ts +142 -7
  217. package/src/tools/agent-harness-tool-types.ts +83 -0
  218. package/src/tools/agent-harness-tool-utils.ts +43 -0
  219. package/src/tools/agent-harness-tool.ts +319 -370
  220. package/src/tools/agent-harness-ui-surface-metadata.ts +76 -69
  221. package/src/tools/agent-harness-ui-surface-types.ts +46 -0
  222. package/src/tools/agent-harness-vibe-health.ts +105 -0
  223. package/src/tools/agent-harness-workspace-action-runner.ts +149 -0
  224. package/src/tools/agent-harness-workspace-actions.ts +114 -8
  225. package/src/tools/agent-harness-workspace-editor-execution.ts +214 -0
  226. package/src/tools/agent-harness-workspace-editor-runner.ts +755 -0
  227. package/src/tools/agent-host-tool.ts +159 -0
  228. package/src/tools/agent-knowledge-ingest-tool.ts +34 -1
  229. package/src/tools/agent-learning-consolidation-core.ts +327 -0
  230. package/src/tools/agent-learning-consolidation-tool.ts +758 -0
  231. package/src/tools/agent-memory-tool.ts +253 -0
  232. package/src/tools/agent-model-compare-export.ts +315 -0
  233. package/src/tools/agent-model-compare-handoff.ts +592 -0
  234. package/src/tools/agent-model-compare-judgment.ts +633 -0
  235. package/src/tools/agent-model-compare-run.ts +735 -0
  236. package/src/tools/agent-model-compare-tool.ts +698 -0
  237. package/src/tools/agent-model-compare-types.ts +191 -0
  238. package/src/tools/agent-model-compare-utils.ts +93 -0
  239. package/src/tools/agent-models-tool.ts +208 -0
  240. package/src/tools/agent-operator-action-tool.ts +1 -1
  241. package/src/tools/agent-operator-method-tool.ts +643 -0
  242. package/src/tools/agent-personal-ops-tool.ts +197 -0
  243. package/src/tools/agent-policy-explanation.ts +415 -0
  244. package/src/tools/agent-research-report-tool.ts +608 -0
  245. package/src/tools/agent-research-runner.ts +367 -0
  246. package/src/tools/agent-research-runs-tool.ts +434 -0
  247. package/src/tools/agent-research-sources-tool.ts +443 -0
  248. package/src/tools/agent-research-tool.ts +515 -0
  249. package/src/tools/agent-review-packet-presets-core.ts +757 -0
  250. package/src/tools/agent-review-packet-presets-tool.ts +466 -0
  251. package/src/tools/agent-review-packet-share-tool.ts +305 -0
  252. package/src/tools/agent-route-planner-candidates-setup.ts +411 -0
  253. package/src/tools/agent-route-planner-candidates-surfaces.ts +432 -0
  254. package/src/tools/agent-route-planner-candidates-work.ts +251 -0
  255. package/src/tools/agent-route-planner-helpers.ts +667 -0
  256. package/src/tools/agent-route-planner.ts +185 -0
  257. package/src/tools/agent-route-tool.ts +105 -0
  258. package/src/tools/agent-schedule-edit-tool.ts +210 -0
  259. package/src/tools/agent-schedule-tool.ts +282 -0
  260. package/src/tools/agent-security-tool.ts +145 -0
  261. package/src/tools/agent-sessions-tool.ts +122 -0
  262. package/src/tools/agent-settings-import-tool.ts +104 -0
  263. package/src/tools/agent-settings-tool.ts +176 -0
  264. package/src/tools/agent-setup-tool.ts +226 -0
  265. package/src/tools/agent-support-tool.ts +122 -0
  266. package/src/tools/agent-terminal-process-tools.ts +167 -0
  267. package/src/tools/agent-tool-policy-guard-types.ts +77 -0
  268. package/src/tools/agent-tool-policy-guard.ts +106 -85
  269. package/src/tools/agent-vibe-tool.ts +297 -0
  270. package/src/tools/agent-work-plan-tool.ts +265 -6
  271. package/src/tools/agent-workspace-tool.ts +305 -0
  272. package/src/tools/artifact-archive.ts +169 -0
  273. package/src/tools/tool-definition-compaction.ts +36 -0
  274. package/src/version.ts +1 -1
@@ -0,0 +1,432 @@
1
+ import { arch, cpus, freemem, platform, totalmem } from 'node:os';
2
+ import type { CommandContext } from '../input/command-registry.ts';
3
+ import { previewHarnessText } from './agent-harness-text.ts';
4
+ import { readProviderHealthSignal } from './agent-harness-model-provider-health.ts';
5
+ import { localStackFor } from './agent-harness-local-model-endpoints.ts';
6
+ import type { LocalModelBenchmarkEvidence, LocalModelBenchmarkRouteLatency, LocalModelDetection, LocalModelHardwareProfile, LocalModelRecipe, LocalModelRecipeFit, ModelCandidate, ModelReadinessDimension, ModelReadinessScore, ModelRouteReadinessScore, ModelProviderHealthSignal } from './agent-harness-model-routing-types.ts';
7
+ import { readRecord } from './agent-harness-model-routing-utils.ts';
8
+
9
+ export function localRecipeStackId(recipe: LocalModelRecipe): string {
10
+ return recipe.id === 'openai-compatible-local' ? 'openai-compatible' : recipe.id === 'llama-cpp' ? 'llama.cpp' : recipe.id;
11
+ }
12
+
13
+ export function roundGb(bytes: number): number {
14
+ if (!Number.isFinite(bytes) || bytes <= 0) return 0;
15
+ return Math.max(0, Math.round((bytes / 1024 / 1024 / 1024) * 10) / 10);
16
+ }
17
+
18
+ export function localHardwareProfile(): LocalModelHardwareProfile {
19
+ const cpuList = cpus();
20
+ const ramGb = roundGb(totalmem());
21
+ const freeRamGb = roundGb(freemem());
22
+ const runtimePlatform = platform();
23
+ const runtimeArch = arch();
24
+ const acceleratorHint = runtimePlatform === 'darwin' && runtimeArch === 'arm64'
25
+ ? 'apple-silicon'
26
+ : (process.env.CUDA_VISIBLE_DEVICES || process.env.NVIDIA_VISIBLE_DEVICES)
27
+ ? 'cuda-env'
28
+ : 'none-detected';
29
+ return {
30
+ platform: runtimePlatform,
31
+ arch: runtimeArch,
32
+ cpuModel: previewHarnessText(cpuList[0]?.model ?? 'unknown CPU', 96),
33
+ cpuThreads: cpuList.length,
34
+ ramGb,
35
+ freeRamGb,
36
+ memoryTier: ramGb >= 64 ? 'large' : ramGb >= 32 ? 'comfortable' : ramGb >= 16 ? 'starter' : 'constrained',
37
+ acceleratorHint,
38
+ privacy: 'local-only',
39
+ caveat: 'Hardware scan uses local OS memory/CPU data and safe accelerator hints only; it does not probe drivers, download models, or benchmark live inference.',
40
+ };
41
+ }
42
+
43
+ export function fitLevel(score: number): LocalModelRecipeFit['level'] {
44
+ if (score >= 85) return 'strong';
45
+ if (score >= 70) return 'good';
46
+ if (score >= 50) return 'usable';
47
+ return 'weak';
48
+ }
49
+
50
+ export function clampFit(score: number): number {
51
+ return Math.max(0, Math.min(100, Math.round(score)));
52
+ }
53
+
54
+ export function modelReadinessLevel(score: number): ModelReadinessScore['level'] {
55
+ if (score >= 85) return 'excellent';
56
+ if (score >= 70) return 'good';
57
+ if (score >= 50) return 'usable';
58
+ return 'risky';
59
+ }
60
+
61
+ export function capabilityEnabled(capabilities: unknown, key: 'toolCalling' | 'multimodal'): boolean | null {
62
+ const value = readRecord(capabilities)[key];
63
+ if (typeof value === 'boolean') return value;
64
+ return null;
65
+ }
66
+
67
+ export function isLocalCandidate(fields: readonly string[]): boolean {
68
+ return fields.some((field) => Boolean(localStackFor(field)));
69
+ }
70
+
71
+ export function contextWindowScore(contextWindow: number | null): ModelReadinessDimension {
72
+ const score = contextWindow == null
73
+ ? 45
74
+ : contextWindow >= 128_000
75
+ ? 100
76
+ : contextWindow >= 64_000
77
+ ? 88
78
+ : contextWindow >= 32_000
79
+ ? 76
80
+ : contextWindow >= 16_000
81
+ ? 62
82
+ : 45;
83
+ return {
84
+ id: 'context-window',
85
+ label: 'Context window',
86
+ score,
87
+ weight: 20,
88
+ summary: contextWindow == null
89
+ ? 'No context-window metadata; inspect the provider route before long-context work.'
90
+ : `${contextWindow.toLocaleString()} token context window.`,
91
+ };
92
+ }
93
+
94
+ export function toolSupportScore(capabilities: unknown): ModelReadinessDimension {
95
+ const enabled = capabilityEnabled(capabilities, 'toolCalling');
96
+ return {
97
+ id: 'tool-support',
98
+ label: 'Tool support',
99
+ score: enabled === true ? 100 : enabled === false ? 35 : 55,
100
+ weight: 20,
101
+ summary: enabled === true
102
+ ? 'Tool calling is advertised.'
103
+ : enabled === false
104
+ ? 'Tool calling is not advertised; use for chat or drafting, not autonomous tool workflows.'
105
+ : 'Tool-calling support is unknown; inspect the provider before tool-heavy work.',
106
+ };
107
+ }
108
+
109
+ export function visionScore(capabilities: unknown): ModelReadinessDimension {
110
+ const enabled = capabilityEnabled(capabilities, 'multimodal');
111
+ return {
112
+ id: 'vision',
113
+ label: 'Vision',
114
+ score: enabled === true ? 100 : enabled === false ? 45 : 55,
115
+ weight: 10,
116
+ summary: enabled === true
117
+ ? 'Vision or multimodal input is advertised.'
118
+ : enabled === false
119
+ ? 'Vision is not advertised; avoid image/screen-heavy work on this route.'
120
+ : 'Vision support is unknown.',
121
+ };
122
+ }
123
+
124
+ export function costScore(tier: string | undefined, local: boolean): ModelReadinessDimension {
125
+ const normalized = (tier ?? '').toLowerCase();
126
+ const score = local
127
+ ? 100
128
+ : normalized === 'free'
129
+ ? 95
130
+ : normalized === 'subscription'
131
+ ? 86
132
+ : normalized === 'standard'
133
+ ? 72
134
+ : normalized === 'premium'
135
+ ? 55
136
+ : 62;
137
+ return {
138
+ id: 'cost',
139
+ label: 'Cost',
140
+ score,
141
+ weight: 15,
142
+ summary: local
143
+ ? 'Local route; marginal token cost is user hardware and power.'
144
+ : tier
145
+ ? `${tier} tier route.`
146
+ : 'Cost tier is unknown; inspect provider pricing before long runs.',
147
+ };
148
+ }
149
+
150
+ export function privacyScore(local: boolean, providerId: string): ModelReadinessDimension {
151
+ const normalized = providerId.toLowerCase();
152
+ const score = local
153
+ ? 100
154
+ : /subscription|account|openrouter|openai|anthropic|google|gemini|xai|mistral|cohere/.test(normalized)
155
+ ? 48
156
+ : 60;
157
+ return {
158
+ id: 'privacy',
159
+ label: 'Privacy',
160
+ score,
161
+ weight: 15,
162
+ summary: local
163
+ ? 'Local/private route detected.'
164
+ : 'Cloud/provider route; treat sensitive data according to provider policy.',
165
+ };
166
+ }
167
+
168
+ export function latencyScore(
169
+ local: boolean,
170
+ benchmarkCompositeScore: number | null | undefined,
171
+ providerHealth: ModelProviderHealthSignal,
172
+ localBenchmarkLatency: LocalModelBenchmarkRouteLatency | null | undefined,
173
+ ): ModelReadinessDimension {
174
+ const liveLatency = providerHealth.status === 'record-found' ? providerHealth.avgLatencyMs : undefined;
175
+ if (liveLatency !== undefined) {
176
+ const score = liveLatency <= 750
177
+ ? 95
178
+ : liveLatency <= 1500
179
+ ? 86
180
+ : liveLatency <= 3000
181
+ ? 72
182
+ : 55;
183
+ const details = [
184
+ `Live provider-health latency is ${liveLatency} ms`,
185
+ ...(providerHealth.minLatencyMs !== undefined || providerHealth.maxLatencyMs !== undefined
186
+ ? [`range ${providerHealth.minLatencyMs ?? '?'}-${providerHealth.maxLatencyMs ?? '?'} ms`]
187
+ : []),
188
+ ...(providerHealth.healthStatus ? [`provider status ${providerHealth.healthStatus}`] : []),
189
+ ...(providerHealth.lastErrorMessage ? [`last error: ${providerHealth.lastErrorMessage}`] : []),
190
+ ];
191
+ return {
192
+ id: 'latency',
193
+ label: 'Latency',
194
+ score,
195
+ weight: 20,
196
+ summary: `${details.join('; ')}.`,
197
+ };
198
+ }
199
+
200
+ if (localBenchmarkLatency && localBenchmarkLatency.status !== 'failed') {
201
+ const latency = localBenchmarkLatency.latencyMs;
202
+ const score = latency <= 750
203
+ ? 93
204
+ : latency <= 1500
205
+ ? 84
206
+ : latency <= 3000
207
+ ? 70
208
+ : 54;
209
+ return {
210
+ id: 'latency',
211
+ label: 'Latency',
212
+ score,
213
+ weight: 20,
214
+ summary: `Measured local benchmark latency is ${latency} ms from ${localBenchmarkLatency.artifactId}${localBenchmarkLatency.comparisonId ? ` (${localBenchmarkLatency.comparisonId})` : ''}.`,
215
+ };
216
+ }
217
+
218
+ const score = local
219
+ ? 55
220
+ : benchmarkCompositeScore != null
221
+ ? 78
222
+ : 70;
223
+ return {
224
+ id: 'latency',
225
+ label: 'Latency',
226
+ score,
227
+ weight: 20,
228
+ summary: local
229
+ ? 'Local latency is unmeasured until the user runs the benchmark prompt on this machine.'
230
+ : benchmarkCompositeScore != null
231
+ ? 'No daemon-published provider-health latency is reachable; benchmark metadata is quality context only.'
232
+ : 'No daemon-published provider-health latency is reachable; assume normal provider latency until measured.',
233
+ };
234
+ }
235
+
236
+ export function weightedReadiness(dimensions: readonly ModelReadinessDimension[]): number {
237
+ const totalWeight = dimensions.reduce((total, dimension) => total + dimension.weight, 0);
238
+ if (totalWeight <= 0) return 0;
239
+ return clampFit(dimensions.reduce((total, dimension) => total + (dimension.score * dimension.weight), 0) / totalWeight);
240
+ }
241
+
242
+ export function modelReadinessScore(context: CommandContext, model: ModelCandidate): ModelRouteReadinessScore {
243
+ const local = isLocalCandidate([model.providerId, model.registryKey, model.modelId, model.displayName]);
244
+ const providerHealth = readProviderHealthSignal(context, model.providerId, model.registryKey);
245
+ const dimensions: readonly ModelReadinessDimension[] = [
246
+ latencyScore(local, model.benchmarkCompositeScore, providerHealth, model.localBenchmarkLatency),
247
+ contextWindowScore(model.contextWindow),
248
+ toolSupportScore(model.capabilities),
249
+ visionScore(model.capabilities),
250
+ costScore(model.tier, local),
251
+ privacyScore(local, model.providerId),
252
+ ];
253
+ const score = weightedReadiness(dimensions);
254
+ const hasLiveLatency = providerHealth.status === 'record-found' && providerHealth.avgLatencyMs !== undefined;
255
+ const hasBenchmarkLatency = Boolean(model.localBenchmarkLatency && model.localBenchmarkLatency.status !== 'failed');
256
+ const missingSignals = [
257
+ ...providerHealth.missingSignals,
258
+ ...(hasLiveLatency || hasBenchmarkLatency ? [] : ['No live latency benchmark has been recorded for this Agent route.']),
259
+ ...(model.contextWindow == null ? ['Context-window metadata is missing.'] : []),
260
+ ...(capabilityEnabled(model.capabilities, 'toolCalling') == null ? ['Tool-calling support is unknown.'] : []),
261
+ ...(capabilityEnabled(model.capabilities, 'multimodal') == null ? ['Vision support is unknown.'] : []),
262
+ ...(!model.tier && !local ? ['Cost tier is unknown.'] : []),
263
+ ];
264
+ return {
265
+ score,
266
+ level: modelReadinessLevel(score),
267
+ confidence: providerHealth.status === 'record-found' && missingSignals.length === 0
268
+ ? 'provider-health-backed'
269
+ : hasBenchmarkLatency && missingSignals.length === 0
270
+ ? 'measured'
271
+ : missingSignals.length === 0
272
+ ? 'metadata-backed'
273
+ : 'estimated',
274
+ dimensions,
275
+ missingSignals,
276
+ providerHealth,
277
+ nextStep: local
278
+ ? hasBenchmarkLatency
279
+ ? `Review local benchmark latency evidence ${model.localBenchmarkLatency?.artifactId}, then use a separate confirmed apply/update route only if the user wants this route as default.`
280
+ : 'Run the local benchmark prompt before making this route the default.'
281
+ : providerHealth.status === 'record-found'
282
+ ? 'Use provider-health-backed route posture for triage; run a task-specific comparison before changing the default model.'
283
+ : hasBenchmarkLatency
284
+ ? `Review benchmark latency evidence ${model.localBenchmarkLatency?.artifactId}; wait for daemon provider-health publication for live status, rate-limit, and error posture before changing defaults.`
285
+ : 'Use this score for routing triage; wait for daemon provider-health publication or run a task-specific comparison before changing the default model.',
286
+ };
287
+ }
288
+
289
+ export function localRecipeReadinessScore(
290
+ recipe: LocalModelRecipe,
291
+ fit: LocalModelRecipeFit,
292
+ detected: boolean,
293
+ evidence: LocalModelBenchmarkEvidence,
294
+ ): ModelReadinessScore {
295
+ const contextScore = recipe.id === 'vllm' ? 70 : recipe.id === 'openai-compatible-local' ? 60 : 65;
296
+ const toolScore = recipe.id === 'ollama' || recipe.id === 'openai-compatible-local' ? 70 : 55;
297
+ const visionSupport = recipe.modelExamples.some((model) => /vision|vl|multimodal/i.test(model));
298
+ const reviewedWinner = evidence.winnerStacks.includes(localRecipeStackId(recipe));
299
+ const measured = evidence.comparisonCount > 0;
300
+ const dimensions: readonly ModelReadinessDimension[] = [
301
+ {
302
+ id: 'latency',
303
+ label: 'Latency',
304
+ score: reviewedWinner ? 82 : measured ? 68 : detected ? 62 : 50,
305
+ weight: 20,
306
+ summary: reviewedWinner
307
+ ? 'A revealed saved local benchmark judgment selected this stack.'
308
+ : measured
309
+ ? 'A saved local benchmark comparison exists, but no revealed winner is tied to this stack yet.'
310
+ : detected
311
+ ? 'Local stack is detected, but latency still needs an on-machine benchmark.'
312
+ : 'Latency is unknown until the local server and model are running.',
313
+ },
314
+ {
315
+ id: 'context-window',
316
+ label: 'Context window',
317
+ score: contextScore,
318
+ weight: 20,
319
+ summary: 'Depends on the selected local model and serving stack; verify after the route is available.',
320
+ },
321
+ {
322
+ id: 'tool-support',
323
+ label: 'Tool support',
324
+ score: toolScore,
325
+ weight: 20,
326
+ summary: 'Tool behavior depends on the selected local model and OpenAI-compatible server support.',
327
+ },
328
+ {
329
+ id: 'vision',
330
+ label: 'Vision',
331
+ score: visionSupport ? 75 : 45,
332
+ weight: 10,
333
+ summary: visionSupport ? 'Example list includes a vision-capable route.' : 'No vision route is assumed for this local recipe.',
334
+ },
335
+ {
336
+ id: 'cost',
337
+ label: 'Cost',
338
+ score: 100,
339
+ weight: 15,
340
+ summary: 'Local route; marginal token cost is user hardware and power.',
341
+ },
342
+ {
343
+ id: 'privacy',
344
+ label: 'Privacy',
345
+ score: 100,
346
+ weight: 15,
347
+ summary: 'Local route can keep prompts on user-controlled hardware.',
348
+ },
349
+ ];
350
+ const score = clampFit((weightedReadiness(dimensions) * 0.72) + (fit.score * 0.28));
351
+ return {
352
+ score,
353
+ level: modelReadinessLevel(score),
354
+ confidence: reviewedWinner ? 'measured' : 'estimated',
355
+ dimensions,
356
+ missingSignals: [
357
+ ...(measured ? [] : ['No live latency benchmark has been recorded for this local recipe.']),
358
+ ...(reviewedWinner ? [] : ['No revealed local benchmark judgment has selected this recipe yet.']),
359
+ 'Context window, tool support, and vision support depend on the exact model served.',
360
+ ],
361
+ nextStep: reviewedWinner
362
+ ? 'Review the saved benchmark judgment, then use a separate confirmed apply/update route only if the user wants this winner as the default.'
363
+ : measured
364
+ ? 'Review the saved comparison and save a revealed judgment before recommending a default-model change.'
365
+ : 'Start the local server, refresh models, then run the setupPlan benchmark action before changing the default model.',
366
+ };
367
+ }
368
+
369
+ export function scoreLocalModelRecipe(
370
+ recipe: LocalModelRecipe,
371
+ hardware: LocalModelHardwareProfile,
372
+ detection: LocalModelDetection,
373
+ ): LocalModelRecipeFit {
374
+ const stackId = localRecipeStackId(recipe);
375
+ const detected = detection.stacks.includes(stackId);
376
+ const reasons: string[] = [];
377
+ let score = 45;
378
+ if (detected) {
379
+ score += 18;
380
+ reasons.push('matching local provider or model route already detected');
381
+ }
382
+ if (recipe.id === 'ollama') {
383
+ score += 20;
384
+ reasons.push('lowest setup friction for most local users');
385
+ if (hardware.ramGb >= 16) {
386
+ score += 12;
387
+ reasons.push(`${hardware.ramGb} GB RAM is enough for practical 7B/8B quantized models`);
388
+ } else {
389
+ score -= 10;
390
+ reasons.push('RAM is below the comfortable 16 GB local-model baseline');
391
+ }
392
+ if (hardware.acceleratorHint === 'apple-silicon') {
393
+ score += 10;
394
+ reasons.push('Apple Silicon is a good Ollama path');
395
+ }
396
+ } else if (recipe.id === 'llama-cpp') {
397
+ score += 16;
398
+ reasons.push('best offline fallback when downloads and serving stay manual');
399
+ if (hardware.ramGb >= 8) {
400
+ score += 10;
401
+ reasons.push('can use smaller GGUF quantized models within available system memory');
402
+ }
403
+ if (hardware.acceleratorHint === 'apple-silicon') {
404
+ score += 8;
405
+ reasons.push('Metal-backed llama.cpp is a strong local path on Apple Silicon');
406
+ }
407
+ } else if (recipe.id === 'vllm') {
408
+ score += hardware.acceleratorHint === 'cuda-env' ? 30 : -12;
409
+ reasons.push(hardware.acceleratorHint === 'cuda-env'
410
+ ? 'CUDA environment hints are present'
411
+ : 'no CUDA hint was detected; vLLM may still work, but requires GPU/driver verification');
412
+ if (hardware.ramGb >= 32) {
413
+ score += 10;
414
+ reasons.push('system memory is comfortable for GPU serving overhead');
415
+ }
416
+ } else {
417
+ score += detected ? 10 : 4;
418
+ reasons.push(detected
419
+ ? 'existing OpenAI-compatible local route can be reused'
420
+ : 'useful when the user already runs LM Studio, LocalAI, TGI, or another local endpoint');
421
+ }
422
+ if (hardware.cpuThreads >= 8 && recipe.id !== 'vllm') {
423
+ score += 5;
424
+ reasons.push(`${hardware.cpuThreads} CPU threads help local inference`);
425
+ }
426
+ const finalScore = clampFit(score);
427
+ return {
428
+ score: finalScore,
429
+ level: fitLevel(finalScore),
430
+ reasons,
431
+ };
432
+ }