@pellux/goodvibes-agent 1.1.7 → 1.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 (255) hide show
  1. package/CHANGELOG.md +170 -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 +132577 -91483
  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 +56 -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 +114 -45
  14. package/docs/voice-and-live-tts.md +3 -1
  15. package/package.json +3 -2
  16. package/release/release-notes.md +130 -5
  17. package/release/release-readiness.json +120 -90
  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 +296 -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-checkpoint.ts +140 -0
  42. package/src/agent/setup-wizard.ts +447 -0
  43. package/src/agent/skill-registry-types.ts +102 -0
  44. package/src/agent/skill-registry.ts +81 -107
  45. package/src/agent/vibe-confirmation-routes.ts +62 -0
  46. package/src/agent/vibe-file.ts +285 -0
  47. package/src/cli/agent-knowledge-command.ts +45 -2
  48. package/src/cli/help.ts +3 -0
  49. package/src/cli/profiles-command.ts +25 -11
  50. package/src/config/agent-settings-policy.ts +1 -18
  51. package/src/input/agent-workspace-activation.ts +53 -0
  52. package/src/input/agent-workspace-artifact-browser-editor.ts +494 -0
  53. package/src/input/agent-workspace-artifact-metadata.ts +17 -0
  54. package/src/input/agent-workspace-basic-command-editors.ts +5 -1
  55. package/src/input/agent-workspace-categories.ts +153 -155
  56. package/src/input/agent-workspace-channel-triage.ts +481 -0
  57. package/src/input/agent-workspace-channels.ts +263 -0
  58. package/src/input/agent-workspace-command-editor.ts +152 -0
  59. package/src/input/agent-workspace-context-snapshot.ts +349 -0
  60. package/src/input/agent-workspace-delegation-editor-submission.ts +8 -0
  61. package/src/input/agent-workspace-document-editor.ts +502 -0
  62. package/src/input/agent-workspace-document-ops-editor.ts +523 -0
  63. package/src/input/agent-workspace-host-category.ts +1 -1
  64. package/src/input/agent-workspace-local-library-snapshot.ts +167 -0
  65. package/src/input/agent-workspace-model-compare-editor.ts +376 -0
  66. package/src/input/agent-workspace-model-compare-prompt-submission.ts +552 -0
  67. package/src/input/agent-workspace-model-compare-types.ts +82 -0
  68. package/src/input/agent-workspace-model-compare-utils.ts +35 -0
  69. package/src/input/agent-workspace-onboarding-categories.ts +141 -0
  70. package/src/input/agent-workspace-operations-command-editor-submission.ts +53 -0
  71. package/src/input/agent-workspace-operations-command-editors.ts +22 -0
  72. package/src/input/agent-workspace-research-report-editor.ts +212 -0
  73. package/src/input/agent-workspace-research-run-editor.ts +144 -0
  74. package/src/input/agent-workspace-research-source-editor.ts +167 -0
  75. package/src/input/agent-workspace-review-packet-snapshot.ts +593 -0
  76. package/src/input/agent-workspace-review-packet-utils.ts +265 -0
  77. package/src/input/agent-workspace-settings.ts +262 -30
  78. package/src/input/agent-workspace-setup-checkpoint-action.ts +127 -0
  79. package/src/input/agent-workspace-setup-snapshot.ts +198 -0
  80. package/src/input/agent-workspace-setup.ts +34 -1
  81. package/src/input/agent-workspace-snapshot.ts +162 -147
  82. package/src/input/agent-workspace-types.ts +287 -2
  83. package/src/input/agent-workspace.ts +18 -0
  84. package/src/input/command-registry.ts +9 -2
  85. package/src/input/commands/agent-runtime-profile-runtime.ts +28 -13
  86. package/src/input/commands/channels-runtime.ts +102 -114
  87. package/src/input/commands/delegation-runtime.ts +87 -24
  88. package/src/input/commands/knowledge-browser-flags.ts +12 -0
  89. package/src/input/commands/knowledge.ts +28 -14
  90. package/src/input/commands/operator-actions-runtime.ts +6 -3
  91. package/src/input/commands/schedule-runtime.ts +71 -14
  92. package/src/input/commands/session-content.ts +2 -2
  93. package/src/input/commands/shell-core.ts +2 -0
  94. package/src/input/commands/vibe-runtime.ts +140 -0
  95. package/src/input/commands.ts +2 -0
  96. package/src/input/connected-host-routes.ts +123 -0
  97. package/src/input/settings-modal-types.ts +14 -0
  98. package/src/main.ts +15 -15
  99. package/src/panels/qr-panel.ts +2 -2
  100. package/src/renderer/agent-workspace-context-lines.ts +686 -0
  101. package/src/renderer/agent-workspace.ts +30 -419
  102. package/src/renderer/settings-modal-helpers.ts +12 -0
  103. package/src/renderer/settings-modal.ts +12 -0
  104. package/src/runtime/bootstrap-command-context.ts +11 -1
  105. package/src/runtime/bootstrap-command-parts.ts +14 -3
  106. package/src/runtime/bootstrap-core.ts +39 -1
  107. package/src/runtime/bootstrap-shell.ts +6 -0
  108. package/src/runtime/bootstrap.ts +113 -30
  109. package/src/runtime/connected-host-auth.ts +3 -2
  110. package/src/runtime/execution-ledger.ts +231 -0
  111. package/src/runtime/services.ts +88 -10
  112. package/src/runtime/tool-permission-safety.ts +199 -5
  113. package/src/shell/session-continuity-hints.ts +39 -0
  114. package/src/tools/agent-artifacts-tool.ts +606 -0
  115. package/src/tools/agent-audit-tool.ts +155 -0
  116. package/src/tools/agent-autonomy-schedule-tool.ts +238 -0
  117. package/src/tools/agent-autonomy-tool.ts +140 -0
  118. package/src/tools/agent-channel-send-tool.ts +24 -2
  119. package/src/tools/agent-channels-tool.ts +140 -0
  120. package/src/tools/agent-computer-tool.ts +168 -0
  121. package/src/tools/agent-context-policy.ts +137 -7
  122. package/src/tools/agent-context-tool.ts +143 -0
  123. package/src/tools/agent-delegation-tool.ts +128 -0
  124. package/src/tools/agent-device-tool.ts +240 -0
  125. package/src/tools/agent-documents-tool.ts +684 -0
  126. package/src/tools/agent-execution-tool.ts +230 -0
  127. package/src/tools/agent-harness-agent-orchestration.ts +703 -0
  128. package/src/tools/agent-harness-autonomy-intake.ts +785 -0
  129. package/src/tools/agent-harness-autonomy-live-records.ts +588 -0
  130. package/src/tools/agent-harness-autonomy-queue-types.ts +88 -0
  131. package/src/tools/agent-harness-autonomy-queue.ts +560 -0
  132. package/src/tools/agent-harness-background-processes-types.ts +35 -0
  133. package/src/tools/agent-harness-background-processes.ts +794 -0
  134. package/src/tools/agent-harness-browser-cockpit-route.ts +219 -0
  135. package/src/tools/agent-harness-browser-control.ts +338 -0
  136. package/src/tools/agent-harness-channel-metadata.ts +168 -10
  137. package/src/tools/agent-harness-cli-command-policy.ts +110 -0
  138. package/src/tools/agent-harness-command-catalog.ts +4 -2
  139. package/src/tools/agent-harness-command-runner.ts +42 -0
  140. package/src/tools/agent-harness-connected-host-status.ts +7 -4
  141. package/src/tools/agent-harness-delegation-posture.ts +234 -19
  142. package/src/tools/agent-harness-document-ops-reviewer-readiness.ts +234 -0
  143. package/src/tools/agent-harness-document-ops-types.ts +72 -0
  144. package/src/tools/agent-harness-document-ops.ts +671 -0
  145. package/src/tools/agent-harness-execution-history.ts +489 -0
  146. package/src/tools/agent-harness-execution-posture.ts +382 -0
  147. package/src/tools/agent-harness-file-recovery.ts +135 -0
  148. package/src/tools/agent-harness-keybinding-metadata.ts +16 -12
  149. package/src/tools/agent-harness-learning-curator-common.ts +102 -0
  150. package/src/tools/agent-harness-learning-curator-consolidation.ts +295 -0
  151. package/src/tools/agent-harness-learning-curator-proposals.ts +606 -0
  152. package/src/tools/agent-harness-learning-curator-types.ts +151 -0
  153. package/src/tools/agent-harness-learning-curator.ts +417 -0
  154. package/src/tools/agent-harness-local-model-benchmarks.ts +199 -0
  155. package/src/tools/agent-harness-local-model-cookbook.ts +238 -0
  156. package/src/tools/agent-harness-local-model-endpoints.ts +673 -0
  157. package/src/tools/agent-harness-media-posture.ts +208 -2
  158. package/src/tools/agent-harness-memory-posture.ts +556 -0
  159. package/src/tools/agent-harness-metadata.ts +100 -168
  160. package/src/tools/agent-harness-mode-catalog.ts +84 -33
  161. package/src/tools/agent-harness-model-catalog.ts +162 -0
  162. package/src/tools/agent-harness-model-provider-health.ts +186 -0
  163. package/src/tools/agent-harness-model-readiness.ts +406 -0
  164. package/src/tools/agent-harness-model-routing-types.ts +266 -0
  165. package/src/tools/agent-harness-model-routing-utils.ts +30 -0
  166. package/src/tools/agent-harness-model-routing.ts +137 -190
  167. package/src/tools/agent-harness-operator-methods.ts +115 -133
  168. package/src/tools/agent-harness-pairing-posture.ts +431 -18
  169. package/src/tools/agent-harness-personal-ops-discovery.ts +533 -0
  170. package/src/tools/agent-harness-personal-ops-intake.ts +385 -0
  171. package/src/tools/agent-harness-personal-ops-lanes.ts +204 -0
  172. package/src/tools/agent-harness-personal-ops-records.ts +732 -0
  173. package/src/tools/agent-harness-personal-ops-runner.ts +637 -0
  174. package/src/tools/agent-harness-personal-ops-types.ts +222 -0
  175. package/src/tools/agent-harness-personal-ops.ts +606 -0
  176. package/src/tools/agent-harness-project-context.ts +144 -0
  177. package/src/tools/agent-harness-prompt-context.ts +589 -0
  178. package/src/tools/agent-harness-provider-account-metadata.ts +2 -2
  179. package/src/tools/agent-harness-release-evidence.ts +9 -7
  180. package/src/tools/agent-harness-release-readiness.ts +9 -7
  181. package/src/tools/agent-harness-research-briefing.ts +427 -0
  182. package/src/tools/agent-harness-research-queue.ts +250 -0
  183. package/src/tools/agent-harness-research-runs.ts +295 -0
  184. package/src/tools/agent-harness-research-workflow.ts +322 -0
  185. package/src/tools/agent-harness-security-posture.ts +2 -2
  186. package/src/tools/agent-harness-service-posture.ts +9 -9
  187. package/src/tools/agent-harness-session-metadata.ts +3 -3
  188. package/src/tools/agent-harness-setup-connected-host.ts +501 -0
  189. package/src/tools/agent-harness-setup-describe.ts +557 -0
  190. package/src/tools/agent-harness-setup-handoffs.ts +502 -0
  191. package/src/tools/agent-harness-setup-model-helpers.ts +101 -0
  192. package/src/tools/agent-harness-setup-plan.ts +253 -0
  193. package/src/tools/agent-harness-setup-posture-types.ts +218 -0
  194. package/src/tools/agent-harness-setup-posture-utils.ts +173 -0
  195. package/src/tools/agent-harness-setup-posture.ts +449 -221
  196. package/src/tools/agent-harness-setup-smoke.ts +508 -0
  197. package/src/tools/agent-harness-sudo-posture.ts +114 -0
  198. package/src/tools/agent-harness-tool-schema.ts +120 -7
  199. package/src/tools/agent-harness-tool-types.ts +78 -0
  200. package/src/tools/agent-harness-tool-utils.ts +43 -0
  201. package/src/tools/agent-harness-tool.ts +311 -370
  202. package/src/tools/agent-harness-ui-surface-metadata.ts +76 -69
  203. package/src/tools/agent-harness-ui-surface-types.ts +46 -0
  204. package/src/tools/agent-harness-vibe-health.ts +105 -0
  205. package/src/tools/agent-harness-workspace-action-runner.ts +149 -0
  206. package/src/tools/agent-harness-workspace-actions.ts +114 -8
  207. package/src/tools/agent-harness-workspace-editor-execution.ts +214 -0
  208. package/src/tools/agent-harness-workspace-editor-runner.ts +755 -0
  209. package/src/tools/agent-host-tool.ts +159 -0
  210. package/src/tools/agent-knowledge-ingest-tool.ts +34 -1
  211. package/src/tools/agent-learning-consolidation-core.ts +327 -0
  212. package/src/tools/agent-learning-consolidation-tool.ts +758 -0
  213. package/src/tools/agent-memory-tool.ts +214 -0
  214. package/src/tools/agent-model-compare-export.ts +315 -0
  215. package/src/tools/agent-model-compare-handoff.ts +592 -0
  216. package/src/tools/agent-model-compare-judgment.ts +633 -0
  217. package/src/tools/agent-model-compare-run.ts +722 -0
  218. package/src/tools/agent-model-compare-tool.ts +698 -0
  219. package/src/tools/agent-model-compare-types.ts +191 -0
  220. package/src/tools/agent-model-compare-utils.ts +93 -0
  221. package/src/tools/agent-models-tool.ts +208 -0
  222. package/src/tools/agent-operator-action-tool.ts +1 -1
  223. package/src/tools/agent-operator-method-tool.ts +643 -0
  224. package/src/tools/agent-personal-ops-tool.ts +197 -0
  225. package/src/tools/agent-policy-explanation.ts +415 -0
  226. package/src/tools/agent-research-report-tool.ts +608 -0
  227. package/src/tools/agent-research-runs-tool.ts +434 -0
  228. package/src/tools/agent-research-sources-tool.ts +443 -0
  229. package/src/tools/agent-research-tool.ts +687 -0
  230. package/src/tools/agent-review-packet-presets-core.ts +757 -0
  231. package/src/tools/agent-review-packet-presets-tool.ts +466 -0
  232. package/src/tools/agent-review-packet-share-tool.ts +305 -0
  233. package/src/tools/agent-route-planner-candidates-setup.ts +411 -0
  234. package/src/tools/agent-route-planner-candidates-surfaces.ts +432 -0
  235. package/src/tools/agent-route-planner-candidates-work.ts +251 -0
  236. package/src/tools/agent-route-planner-helpers.ts +667 -0
  237. package/src/tools/agent-route-planner.ts +185 -0
  238. package/src/tools/agent-route-tool.ts +105 -0
  239. package/src/tools/agent-schedule-edit-tool.ts +210 -0
  240. package/src/tools/agent-schedule-tool.ts +282 -0
  241. package/src/tools/agent-security-tool.ts +145 -0
  242. package/src/tools/agent-sessions-tool.ts +122 -0
  243. package/src/tools/agent-settings-import-tool.ts +104 -0
  244. package/src/tools/agent-settings-tool.ts +176 -0
  245. package/src/tools/agent-setup-tool.ts +226 -0
  246. package/src/tools/agent-support-tool.ts +122 -0
  247. package/src/tools/agent-terminal-process-tools.ts +167 -0
  248. package/src/tools/agent-tool-policy-guard-types.ts +77 -0
  249. package/src/tools/agent-tool-policy-guard.ts +106 -85
  250. package/src/tools/agent-vibe-tool.ts +297 -0
  251. package/src/tools/agent-work-plan-tool.ts +265 -6
  252. package/src/tools/agent-workspace-tool.ts +305 -0
  253. package/src/tools/artifact-archive.ts +169 -0
  254. package/src/tools/tool-definition-compaction.ts +36 -0
  255. package/src/version.ts +1 -1
@@ -1,175 +1,17 @@
1
+ import { arch, cpus, freemem, platform, totalmem } from 'node:os';
2
+ import type { ArtifactDescriptor } from '@pellux/goodvibes-sdk/platform/artifacts';
1
3
  import type { CommandContext } from '../input/command-registry.ts';
2
4
  import { requireProviderApi } from '../input/commands/runtime-services.ts';
3
5
  import { previewHarnessText } from './agent-harness-text.ts';
4
-
5
- export interface AgentHarnessModelRoutingArgs {
6
- readonly modelRouteId?: unknown;
7
- readonly target?: unknown;
8
- readonly query?: unknown;
9
- readonly includeParameters?: unknown;
10
- readonly limit?: unknown;
11
- }
12
-
13
- type ModelRouteResolution =
14
- | { readonly status: 'found'; readonly route: Record<string, unknown> }
15
- | { readonly status: 'ambiguous'; readonly input: string; readonly candidates: readonly Record<string, unknown>[] }
16
- | { readonly status: 'missing_lookup'; readonly usage: string };
17
-
18
- type ModelRouteLookupSource = 'modelRouteId' | 'target' | 'query';
19
-
20
- interface ModelCandidate {
21
- readonly kind: 'model';
22
- readonly id: string;
23
- readonly registryKey: string;
24
- readonly modelId: string;
25
- readonly providerId: string;
26
- readonly displayName: string;
27
- readonly current: boolean;
28
- readonly contextWindow: number | null;
29
- readonly reasoningEffort: readonly string[];
30
- readonly capabilities: unknown;
31
- readonly pinned: boolean;
32
- }
33
-
34
- interface RouteCandidate {
35
- readonly kind: 'route';
36
- readonly id: string;
37
- readonly label: string;
38
- readonly detail: string;
39
- readonly currentValue: unknown;
40
- readonly settingKeys: readonly string[];
41
- readonly commands: readonly string[];
42
- readonly uiSurfaces: readonly string[];
43
- }
44
-
45
- interface ProviderApiLike {
46
- readonly getFavorites: () => Promise<unknown>;
47
- readonly getCurrentModel: () => Promise<unknown>;
48
- readonly listModels: (options?: { readonly selectableOnly?: boolean }) => Promise<readonly unknown[]>;
49
- readonly listProviderIds: () => readonly string[];
50
- }
51
-
52
- function readString(value: unknown): string {
53
- return typeof value === 'string' ? value.trim() : '';
54
- }
55
-
56
- function readLimit(value: unknown, fallback: number): number {
57
- const parsed = typeof value === 'string' && value.trim() ? Number(value) : value;
58
- if (typeof parsed !== 'number' || !Number.isFinite(parsed)) return fallback;
59
- return Math.max(1, Math.min(500, Math.trunc(parsed)));
60
- }
61
-
62
- function readRecord(value: unknown): Record<string, unknown> {
63
- return value && typeof value === 'object' ? value as Record<string, unknown> : {};
64
- }
65
-
66
- function readStringArray(value: unknown): readonly string[] {
67
- return Array.isArray(value) ? value.map((entry) => readString(entry)).filter(Boolean) : [];
68
- }
69
-
70
- function readConfig(context: CommandContext, key: string): unknown {
71
- try {
72
- return (context.platform.configManager as { get(settingKey: string): unknown }).get(key);
73
- } catch {
74
- return undefined;
75
- }
76
- }
77
-
78
- function contextWindowFor(context: CommandContext, model: unknown): number | null {
79
- try {
80
- const registry = context.provider.providerRegistry as { getContextWindowForModel(candidate: unknown): number };
81
- const value = registry.getContextWindowForModel(model);
82
- return typeof value === 'number' && Number.isFinite(value) ? value : null;
83
- } catch {
84
- return null;
85
- }
86
- }
87
-
88
- function modelRegistryKey(model: unknown): string {
89
- const record = readRecord(model);
90
- return readString(record.registryKey) || readString(record.id) || readString(record.modelId);
91
- }
92
-
93
- function modelProviderId(model: unknown): string {
94
- const record = readRecord(model);
95
- return readString(record.providerId) || readString(record.provider);
96
- }
97
-
98
- function modelModelId(model: unknown): string {
99
- const record = readRecord(model);
100
- return readString(record.modelId) || readString(record.id) || modelRegistryKey(model);
101
- }
102
-
103
- function modelDisplayName(model: unknown): string {
104
- const record = readRecord(model);
105
- return readString(record.displayName) || readString(record.name) || modelRegistryKey(model);
106
- }
107
-
108
- function modelCurrent(model: unknown): boolean {
109
- const record = readRecord(model);
110
- return record.current === true;
111
- }
112
-
113
- function modelReasoning(model: unknown): readonly string[] {
114
- const record = readRecord(model);
115
- return readStringArray(record.reasoningEffort);
116
- }
117
-
118
- function modelCapabilities(model: unknown): unknown {
119
- return readRecord(model).capabilities ?? null;
120
- }
121
-
122
- function readProviderApi(context: CommandContext): ProviderApiLike | null {
123
- try {
124
- return requireProviderApi(context) as ProviderApiLike;
125
- } catch {
126
- return null;
127
- }
128
- }
129
-
130
- async function loadPinnedModelIds(context: CommandContext): Promise<ReadonlySet<string>> {
131
- const providerApi = readProviderApi(context);
132
- if (!providerApi) return new Set();
133
- try {
134
- const favorites = await providerApi.getFavorites();
135
- const pinned = readRecord(favorites).pinned;
136
- if (!Array.isArray(pinned)) return new Set();
137
- return new Set(pinned.flatMap((entry) => {
138
- const record = readRecord(entry);
139
- return [readString(record.registryKey), readString(record.modelId)].filter(Boolean);
140
- }));
141
- } catch {
142
- return new Set();
143
- }
144
- }
145
-
146
- async function loadModels(context: CommandContext): Promise<readonly ModelCandidate[]> {
147
- const providerApi = readProviderApi(context);
148
- if (!providerApi) return [];
149
- const pinned = await loadPinnedModelIds(context);
150
- const models = await providerApi.listModels({ selectableOnly: true });
151
- return models.map((model) => {
152
- const registryKey = modelRegistryKey(model);
153
- const modelId = modelModelId(model);
154
- return {
155
- kind: 'model',
156
- id: registryKey,
157
- registryKey,
158
- modelId,
159
- providerId: modelProviderId(model),
160
- displayName: modelDisplayName(model),
161
- current: modelCurrent(model) || registryKey === context.session.runtime.model,
162
- contextWindow: contextWindowFor(context, model),
163
- reasoningEffort: modelReasoning(model),
164
- capabilities: modelCapabilities(model),
165
- pinned: pinned.has(registryKey) || pinned.has(modelId),
166
- };
167
- });
168
- }
169
-
170
- function listProviderIds(context: CommandContext): readonly string[] {
171
- return readProviderApi(context)?.listProviderIds() ?? [];
172
- }
6
+ import { localModelCookbook } from './agent-harness-local-model-cookbook.ts';
7
+ import { collectLocalServerEndpointCandidates, describeLocalServerEndpoint, localModelDetection, localModelServerHealthMap, runLocalModelServerSmoke } from './agent-harness-local-model-endpoints.ts';
8
+ import { localHardwareProfile, modelReadinessScore } from './agent-harness-model-readiness.ts';
9
+ import { contextWindowFor, loadModels, listProviderIds, modelBenchmarkCompositeScore, modelBenchmarkQualityTier, modelCapabilities, modelCurrent, modelDisplayName, modelModelId, modelProviderId, modelReasoning, modelRegistryKey, modelTier, readConfig, readProviderApi } from './agent-harness-model-catalog.ts';
10
+ import type { AgentHarnessModelRoutingArgs, LocalModelServerEndpoint, ModelCandidate, ModelProviderHealthSignal, ModelRouteLookupSource, ModelRouteResolution, RouteCandidate } from './agent-harness-model-routing-types.ts';
11
+ import { readLimit, readString } from './agent-harness-model-routing-utils.ts';
12
+ export type { AgentHarnessModelRoutingArgs } from './agent-harness-model-routing-types.ts';
13
+ export { localModelCookbook } from './agent-harness-local-model-cookbook.ts';
14
+ export { runLocalModelServerSmoke } from './agent-harness-local-model-endpoints.ts';
173
15
 
174
16
  async function readCurrentModel(context: CommandContext): Promise<unknown> {
175
17
  try {
@@ -253,6 +95,20 @@ function routeCandidates(context: CommandContext): readonly RouteCandidate[] {
253
95
  commands: ['/refresh-models'],
254
96
  uiSurfaces: ['model-picker'],
255
97
  },
98
+ {
99
+ kind: 'route',
100
+ id: 'local-model-cookbook',
101
+ label: 'Local model cookbook',
102
+ detail: 'Hardware-aware recommendations for Ollama, llama.cpp, vLLM, and local OpenAI-compatible servers.',
103
+ currentValue: {
104
+ detected: localModelDetection(context),
105
+ hardwareProfile: localHardwareProfile(),
106
+ localServerHealth: localModelServerHealthMap(context, false),
107
+ },
108
+ settingKeys: [],
109
+ commands: ['/provider add <name> <baseURL> [apiKey] --yes', '/refresh-models', '/model'],
110
+ uiSurfaces: ['provider-picker', 'model-picker', 'settings'],
111
+ },
256
112
  {
257
113
  kind: 'route',
258
114
  id: 'pinned-models',
@@ -310,7 +166,39 @@ function modelSearchText(model: ModelCandidate): string {
310
166
  ].join('\n').toLowerCase();
311
167
  }
312
168
 
313
- function describeRoute(route: RouteCandidate, options: { readonly includeParameters?: boolean; readonly lookup?: Record<string, unknown> } = {}): Record<string, unknown> {
169
+ function localEndpointSearchText(endpoint: LocalModelServerEndpoint): string {
170
+ return [
171
+ endpoint.id,
172
+ endpoint.providerId ?? '',
173
+ endpoint.stack ?? '',
174
+ endpoint.baseUrl,
175
+ endpoint.modelsUrl,
176
+ endpoint.diagnosticStatus,
177
+ ...endpoint.sources,
178
+ ...endpoint.sourceDetails,
179
+ ...endpoint.modelRoutes,
180
+ ...endpoint.notes,
181
+ ].join('\n').toLowerCase();
182
+ }
183
+
184
+ function describeLocalServerEndpointRoute(endpoint: LocalModelServerEndpoint, lookup?: Record<string, unknown>): Record<string, unknown> {
185
+ return {
186
+ ...endpoint,
187
+ ...(lookup ? { lookup } : {}),
188
+ modelRouteId: endpoint.id,
189
+ label: `Local model server ${endpoint.baseUrl}`,
190
+ modelRoute: endpoint.inspectRoute,
191
+ modelAccess: {
192
+ smoke: endpoint.smokeRoute,
193
+ refresh: endpoint.refreshRoute,
194
+ addProvider: endpoint.addProviderRoute,
195
+ cookbook: 'models action:"local" includeParameters:true',
196
+ },
197
+ policy: endpoint.diagnostics?.policy ?? 'Read-only local model endpoint inspection. The smoke test, refresh, provider add, benchmark, and route changes remain separate visible confirmed actions.',
198
+ };
199
+ }
200
+
201
+ function describeRoute(route: RouteCandidate, options: { readonly context: CommandContext; readonly includeParameters?: boolean; readonly lookup?: Record<string, unknown> }): Record<string, unknown> {
314
202
  return {
315
203
  kind: 'route',
316
204
  modelRouteId: route.id,
@@ -324,6 +212,7 @@ function describeRoute(route: RouteCandidate, options: { readonly includeParamet
324
212
  uiSurfaces: route.uiSurfaces,
325
213
  } : {}),
326
214
  ...(options.lookup ? { lookup: options.lookup } : {}),
215
+ ...(route.id === 'local-model-cookbook' && options.includeParameters === true ? { localCookbook: localModelCookbook(options.context, true) } : {}),
327
216
  ...(options.includeParameters ? {
328
217
  policy: {
329
218
  effect: 'read-only',
@@ -331,10 +220,10 @@ function describeRoute(route: RouteCandidate, options: { readonly includeParamet
331
220
  mutation: 'Model/provider selection, catalog refresh, favorites, custom provider edits, and route setting changes stay explicit user-facing picker, settings, workspace, or slash-command flows.',
332
221
  },
333
222
  modelAccess: {
334
- inspectRouting: 'agent_harness mode:"model_routing"',
223
+ inspectRouting: 'models action:"status"',
335
224
  inspectRoute: 'agent_harness mode:"model_route"',
336
- settingRead: 'agent_harness mode:"get_setting"',
337
- settingMutation: 'agent_harness mode:"set_setting" confirm:true explicitUserRequest:"..."',
225
+ settingRead: 'settings action:"get"',
226
+ settingMutation: 'settings action:"set" confirm:true explicitUserRequest:"..."',
338
227
  openModelPicker: 'agent_harness mode:"open_ui_surface" surfaceId:"model-picker" confirm:true explicitUserRequest:"..."',
339
228
  openProviderPicker: 'agent_harness mode:"open_ui_surface" surfaceId:"provider-picker" confirm:true explicitUserRequest:"..."',
340
229
  },
@@ -342,7 +231,21 @@ function describeRoute(route: RouteCandidate, options: { readonly includeParamet
342
231
  };
343
232
  }
344
233
 
345
- function describeModel(model: ModelCandidate, options: { readonly includeParameters?: boolean; readonly lookup?: Record<string, unknown> } = {}): Record<string, unknown> {
234
+ function compactProviderHealthSignal(signal: ModelProviderHealthSignal): Record<string, unknown> {
235
+ return {
236
+ status: signal.status,
237
+ sdkContract: signal.sdkContract,
238
+ daemonPublication: signal.daemonPublication,
239
+ agentConsumption: signal.agentConsumption,
240
+ ...(signal.healthStatus ? { healthStatus: signal.healthStatus } : {}),
241
+ ...(signal.avgLatencyMs !== undefined ? { avgLatencyMs: signal.avgLatencyMs } : {}),
242
+ missingSignals: signal.missingSignals,
243
+ policy: signal.policy,
244
+ };
245
+ }
246
+
247
+ function describeModel(model: ModelCandidate, options: { readonly context: CommandContext; readonly includeParameters?: boolean; readonly lookup?: Record<string, unknown> }): Record<string, unknown> {
248
+ const readiness = modelReadinessScore(options.context, model);
346
249
  return {
347
250
  kind: 'model',
348
251
  modelRouteId: model.registryKey,
@@ -354,6 +257,20 @@ function describeModel(model: ModelCandidate, options: { readonly includeParamet
354
257
  pinned: model.pinned,
355
258
  contextWindow: model.contextWindow,
356
259
  reasoningEffort: model.reasoningEffort,
260
+ tier: model.tier ?? null,
261
+ benchmarkCompositeScore: model.benchmarkCompositeScore ?? null,
262
+ benchmarkQualityTier: model.benchmarkQualityTier ?? null,
263
+ readinessScore: readiness.score,
264
+ readinessLevel: readiness.level,
265
+ readiness: options.includeParameters
266
+ ? readiness
267
+ : {
268
+ score: readiness.score,
269
+ level: readiness.level,
270
+ confidence: readiness.confidence,
271
+ providerHealth: compactProviderHealthSignal(readiness.providerHealth),
272
+ nextStep: readiness.nextStep,
273
+ },
357
274
  modelRoute: modelCandidateModelRoute(),
358
275
  ...(options.lookup ? { lookup: options.lookup } : {}),
359
276
  ...(options.includeParameters ? {
@@ -367,7 +284,7 @@ function describeModel(model: ModelCandidate, options: { readonly includeParamet
367
284
  selectProviderCommand: `/provider ${model.providerId}`,
368
285
  pinCommand: `/pin ${model.registryKey}`,
369
286
  unpinCommand: `/unpin ${model.registryKey}`,
370
- setMainModel: `agent_harness mode:"set_setting" key:"provider.model" value:"${model.registryKey}" confirm:true explicitUserRequest:"..."`,
287
+ setMainModel: `settings action:"set" key:"provider.model" value:"${model.registryKey}" confirm:true explicitUserRequest:"..."`,
371
288
  },
372
289
  } : {}),
373
290
  };
@@ -393,8 +310,19 @@ function describeCandidate(entry: RouteCandidate | ModelCandidate): Record<strin
393
310
  };
394
311
  }
395
312
 
313
+ function describeEndpointCandidate(endpoint: LocalModelServerEndpoint): Record<string, unknown> {
314
+ return {
315
+ kind: endpoint.kind,
316
+ modelRouteId: endpoint.id,
317
+ baseUrl: endpoint.baseUrl,
318
+ stack: endpoint.stack,
319
+ diagnosticStatus: endpoint.diagnosticStatus,
320
+ modelRoute: endpoint.inspectRoute,
321
+ };
322
+ }
323
+
396
324
  function modelRoutingModelRoute(): string {
397
- return 'agent_harness mode:"model_route" or mode:"run_command"';
325
+ return 'models action:"route" or action:"smoke"';
398
326
  }
399
327
 
400
328
  function modelCandidateModelRoute(): string {
@@ -447,12 +375,16 @@ export async function modelRoutingSummary(context: CommandContext, args: AgentHa
447
375
  contextWindow: contextWindowFor(context, currentModel),
448
376
  reasoningEffort: modelReasoning(currentModel),
449
377
  capabilities: modelCapabilities(currentModel),
378
+ tier: modelTier(currentModel),
379
+ benchmarkCompositeScore: modelBenchmarkCompositeScore(currentModel),
380
+ benchmarkQualityTier: modelBenchmarkQualityTier(currentModel),
450
381
  pinned: false,
451
- }, { includeParameters }) : null,
382
+ }, { context, includeParameters }) : null,
452
383
  },
453
384
  providers: providerIds,
454
- routes: filteredRoutes.slice(0, limit).map((route) => describeRoute(route, { includeParameters })),
455
- models: filteredModels.slice(0, limit).map((model) => describeModel(model, { includeParameters })),
385
+ localCookbook: localModelCookbook(context, includeParameters),
386
+ routes: filteredRoutes.slice(0, limit).map((route) => describeRoute(route, { context, includeParameters })),
387
+ models: filteredModels.slice(0, limit).map((model) => describeModel(model, { context, includeParameters })),
456
388
  returned: {
457
389
  routes: Math.min(filteredRoutes.length, limit),
458
390
  models: Math.min(filteredModels.length, limit),
@@ -475,42 +407,57 @@ export async function describeHarnessModelRoute(context: CommandContext, args: A
475
407
  if (!lookup) {
476
408
  return {
477
409
  status: 'missing_lookup',
478
- usage: 'model_route requires modelRouteId, target, or query. Use mode:"model_routing" to inspect route and model ids.',
410
+ usage: 'model_route requires modelRouteId, target, or query. Prefer models action:"status" to inspect route, model, and local endpoint ids.',
479
411
  };
480
412
  }
481
413
  const [models] = await Promise.all([loadModels(context)]);
482
414
  const routes = routeCandidates(context);
415
+ const endpoints = collectLocalServerEndpointCandidates(context).map((endpoint) => describeLocalServerEndpoint(endpoint, true));
483
416
  const normalized = lookup.input.toLowerCase();
484
417
  const exactRoute = routes.find((route) => route.id === lookup.input);
485
- if (exactRoute) return { status: 'found', route: describeRoute(exactRoute, { includeParameters: true, lookup: { ...lookup, resolvedBy: 'route-id' } }) };
418
+ if (exactRoute) return { status: 'found', route: describeRoute(exactRoute, { context, includeParameters: true, lookup: { ...lookup, resolvedBy: 'route-id' } }) };
486
419
  const exactModel = models.find((model) => model.registryKey === lookup.input || model.modelId === lookup.input);
487
- if (exactModel) return { status: 'found', route: describeModel(exactModel, { includeParameters: true, lookup: { ...lookup, resolvedBy: 'model-id' } }) };
420
+ if (exactModel) return { status: 'found', route: describeModel(exactModel, { context, includeParameters: true, lookup: { ...lookup, resolvedBy: 'model-id' } }) };
421
+ const exactEndpoint = endpoints.find((endpoint) => endpoint.id === lookup.input || endpoint.baseUrl === lookup.input || endpoint.modelsUrl === lookup.input);
422
+ if (exactEndpoint) return { status: 'found', route: describeLocalServerEndpointRoute(exactEndpoint, { ...lookup, resolvedBy: 'local-endpoint-id' }) };
488
423
  const insensitiveRoute = routes.find((route) => route.id.toLowerCase() === normalized);
489
- if (insensitiveRoute) return { status: 'found', route: describeRoute(insensitiveRoute, { includeParameters: true, lookup: { ...lookup, resolvedBy: 'case-insensitive-route-id' } }) };
424
+ if (insensitiveRoute) return { status: 'found', route: describeRoute(insensitiveRoute, { context, includeParameters: true, lookup: { ...lookup, resolvedBy: 'case-insensitive-route-id' } }) };
490
425
  const insensitiveModel = models.find((model) => model.registryKey.toLowerCase() === normalized || model.modelId.toLowerCase() === normalized);
491
- if (insensitiveModel) return { status: 'found', route: describeModel(insensitiveModel, { includeParameters: true, lookup: { ...lookup, resolvedBy: 'case-insensitive-model-id' } }) };
426
+ if (insensitiveModel) return { status: 'found', route: describeModel(insensitiveModel, { context, includeParameters: true, lookup: { ...lookup, resolvedBy: 'case-insensitive-model-id' } }) };
427
+ const insensitiveEndpoint = endpoints.find((endpoint) => endpoint.id.toLowerCase() === normalized || endpoint.baseUrl.toLowerCase() === normalized || endpoint.modelsUrl.toLowerCase() === normalized);
428
+ if (insensitiveEndpoint) return { status: 'found', route: describeLocalServerEndpointRoute(insensitiveEndpoint, { ...lookup, resolvedBy: 'case-insensitive-local-endpoint-id' }) };
492
429
  const searched = [
493
430
  ...routes.filter((route) => routeSearchText(route).includes(normalized)),
494
431
  ...models.filter((model) => modelSearchText(model).includes(normalized)),
495
432
  ];
496
- if (searched.length === 1) {
433
+ const searchedEndpoints = endpoints.filter((endpoint) => localEndpointSearchText(endpoint).includes(normalized));
434
+ if (searched.length === 0 && searchedEndpoints.length === 1) {
435
+ return {
436
+ status: 'found',
437
+ route: describeLocalServerEndpointRoute(searchedEndpoints[0]!, { ...lookup, resolvedBy: 'local-endpoint-search' }),
438
+ };
439
+ }
440
+ if (searched.length === 1 && searchedEndpoints.length === 0) {
497
441
  const found = searched[0]!;
498
442
  return {
499
443
  status: 'found',
500
444
  route: found.kind === 'route'
501
- ? describeRoute(found, { includeParameters: true, lookup: { ...lookup, resolvedBy: 'search' } })
502
- : describeModel(found, { includeParameters: true, lookup: { ...lookup, resolvedBy: 'search' } }),
445
+ ? describeRoute(found, { context, includeParameters: true, lookup: { ...lookup, resolvedBy: 'search' } })
446
+ : describeModel(found, { context, includeParameters: true, lookup: { ...lookup, resolvedBy: 'search' } }),
503
447
  };
504
448
  }
505
- if (searched.length > 1) {
449
+ if (searched.length + searchedEndpoints.length > 1) {
506
450
  return {
507
451
  status: 'ambiguous',
508
452
  input: lookup.input,
509
- candidates: searched.slice(0, 8).map(describeCandidate),
453
+ candidates: [
454
+ ...searched.slice(0, 8).map(describeCandidate),
455
+ ...searchedEndpoints.slice(0, Math.max(0, 8 - searched.length)).map(describeEndpointCandidate),
456
+ ],
510
457
  };
511
458
  }
512
459
  return {
513
460
  status: 'missing_lookup',
514
- usage: `Unknown model route ${lookup.input}. Use mode:"model_routing" to inspect route and model ids.`,
461
+ usage: `Unknown model route ${lookup.input}. Prefer models action:"status" to inspect route, model, and local endpoint ids.`,
515
462
  };
516
463
  }