@pellux/goodvibes-agent 1.1.6 → 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.
- package/CHANGELOG.md +176 -0
- package/README.md +46 -30
- package/dist/package/{ast-grep-napi.linux-x64-gnu-mkk8xwww.node → ast-grep-napi.linux-x64-gnu-swtppvy9.node} +0 -0
- package/dist/package/{ast-grep-napi.linux-x64-musl-ryqtgdv6.node → ast-grep-napi.linux-x64-musl-ttfcdtap.node} +0 -0
- package/dist/package/main.js +132662 -91399
- package/docs/README.md +14 -7
- package/docs/channels-remote-and-api.md +8 -5
- package/docs/connected-host.md +14 -12
- package/docs/getting-started.md +56 -30
- package/docs/knowledge-artifacts-and-multimodal.md +7 -5
- package/docs/project-planning.md +2 -2
- package/docs/providers-and-routing.md +11 -3
- package/docs/tools-and-commands.md +114 -45
- package/docs/voice-and-live-tts.md +3 -1
- package/package.json +3 -2
- package/release/release-notes.md +130 -5
- package/release/release-readiness.json +120 -90
- package/src/agent/assistant-cockpit.ts +247 -0
- package/src/agent/autonomy-schedule-format.ts +96 -0
- package/src/agent/autonomy-schedule.ts +514 -0
- package/src/agent/channel-delivery-receipts.ts +292 -0
- package/src/agent/competitive-feature-inventory.ts +296 -0
- package/src/agent/document-registry-types.ts +106 -0
- package/src/agent/document-registry.ts +734 -0
- package/src/agent/harness-control.ts +2 -2
- package/src/agent/memory-prompt.ts +2 -2
- package/src/agent/operator-actions.ts +50 -6
- package/src/agent/persona-registry.ts +8 -0
- package/src/agent/project-context-files.ts +273 -0
- package/src/agent/prompt-context-receipts.ts +502 -0
- package/src/agent/reminder-schedule-format.ts +16 -2
- package/src/agent/research-run-registry.ts +582 -0
- package/src/agent/research-source-registry.ts +441 -0
- package/src/agent/routine-registry.ts +19 -3
- package/src/agent/routine-schedule-format.ts +27 -2
- package/src/agent/runtime-profile-starters.ts +7 -0
- package/src/agent/runtime-profile.ts +106 -4
- package/src/agent/schedule-edit-format.ts +129 -0
- package/src/agent/schedule-edit.ts +496 -0
- package/src/agent/schedule-next-routes.ts +42 -0
- package/src/agent/setup-wizard-checkpoint.ts +140 -0
- package/src/agent/setup-wizard.ts +447 -0
- package/src/agent/skill-registry-types.ts +102 -0
- package/src/agent/skill-registry.ts +81 -107
- package/src/agent/vibe-confirmation-routes.ts +62 -0
- package/src/agent/vibe-file.ts +285 -0
- package/src/cli/agent-knowledge-command.ts +45 -2
- package/src/cli/help.ts +3 -0
- package/src/cli/profiles-command.ts +25 -11
- package/src/config/agent-settings-policy.ts +1 -18
- package/src/input/agent-workspace-activation.ts +53 -0
- package/src/input/agent-workspace-artifact-browser-editor.ts +494 -0
- package/src/input/agent-workspace-artifact-metadata.ts +17 -0
- package/src/input/agent-workspace-basic-command-editors.ts +5 -1
- package/src/input/agent-workspace-categories.ts +153 -155
- package/src/input/agent-workspace-channel-triage.ts +481 -0
- package/src/input/agent-workspace-channels.ts +263 -0
- package/src/input/agent-workspace-command-editor.ts +152 -0
- package/src/input/agent-workspace-context-snapshot.ts +349 -0
- package/src/input/agent-workspace-delegation-editor-submission.ts +8 -0
- package/src/input/agent-workspace-document-editor.ts +502 -0
- package/src/input/agent-workspace-document-ops-editor.ts +523 -0
- package/src/input/agent-workspace-host-category.ts +1 -1
- package/src/input/agent-workspace-local-library-snapshot.ts +167 -0
- package/src/input/agent-workspace-model-compare-editor.ts +376 -0
- package/src/input/agent-workspace-model-compare-prompt-submission.ts +552 -0
- package/src/input/agent-workspace-model-compare-types.ts +82 -0
- package/src/input/agent-workspace-model-compare-utils.ts +35 -0
- package/src/input/agent-workspace-onboarding-categories.ts +141 -0
- package/src/input/agent-workspace-operations-command-editor-submission.ts +53 -0
- package/src/input/agent-workspace-operations-command-editors.ts +22 -0
- package/src/input/agent-workspace-research-report-editor.ts +212 -0
- package/src/input/agent-workspace-research-run-editor.ts +144 -0
- package/src/input/agent-workspace-research-source-editor.ts +167 -0
- package/src/input/agent-workspace-review-packet-snapshot.ts +593 -0
- package/src/input/agent-workspace-review-packet-utils.ts +265 -0
- package/src/input/agent-workspace-settings.ts +262 -30
- package/src/input/agent-workspace-setup-checkpoint-action.ts +127 -0
- package/src/input/agent-workspace-setup-snapshot.ts +198 -0
- package/src/input/agent-workspace-setup.ts +34 -1
- package/src/input/agent-workspace-snapshot.ts +162 -147
- package/src/input/agent-workspace-types.ts +287 -2
- package/src/input/agent-workspace.ts +18 -0
- package/src/input/command-registry.ts +9 -2
- package/src/input/commands/agent-runtime-profile-runtime.ts +28 -13
- package/src/input/commands/channels-runtime.ts +102 -114
- package/src/input/commands/delegation-runtime.ts +87 -24
- package/src/input/commands/knowledge-browser-flags.ts +12 -0
- package/src/input/commands/knowledge.ts +28 -14
- package/src/input/commands/operator-actions-runtime.ts +6 -3
- package/src/input/commands/schedule-runtime.ts +71 -14
- package/src/input/commands/session-content.ts +2 -2
- package/src/input/commands/shell-core.ts +2 -0
- package/src/input/commands/vibe-runtime.ts +140 -0
- package/src/input/commands.ts +2 -0
- package/src/input/connected-host-routes.ts +123 -0
- package/src/input/settings-modal-types.ts +14 -0
- package/src/main.ts +15 -15
- package/src/panels/qr-panel.ts +2 -2
- package/src/renderer/agent-workspace-context-lines.ts +686 -0
- package/src/renderer/agent-workspace.ts +30 -419
- package/src/renderer/settings-modal-helpers.ts +12 -0
- package/src/renderer/settings-modal.ts +12 -0
- package/src/runtime/bootstrap-command-context.ts +11 -1
- package/src/runtime/bootstrap-command-parts.ts +14 -3
- package/src/runtime/bootstrap-core.ts +43 -1
- package/src/runtime/bootstrap-shell.ts +6 -0
- package/src/runtime/bootstrap.ts +113 -30
- package/src/runtime/connected-host-auth.ts +3 -2
- package/src/runtime/execution-ledger.ts +231 -0
- package/src/runtime/services.ts +88 -10
- package/src/runtime/tool-permission-safety.ts +287 -0
- package/src/shell/session-continuity-hints.ts +39 -0
- package/src/tools/agent-artifacts-tool.ts +606 -0
- package/src/tools/agent-audit-tool.ts +155 -0
- package/src/tools/agent-autonomy-schedule-tool.ts +238 -0
- package/src/tools/agent-autonomy-tool.ts +140 -0
- package/src/tools/agent-channel-send-tool.ts +24 -2
- package/src/tools/agent-channels-tool.ts +140 -0
- package/src/tools/agent-computer-tool.ts +168 -0
- package/src/tools/agent-context-policy.ts +267 -10
- package/src/tools/agent-context-tool.ts +143 -0
- package/src/tools/agent-delegation-tool.ts +128 -0
- package/src/tools/agent-device-tool.ts +240 -0
- package/src/tools/agent-documents-tool.ts +684 -0
- package/src/tools/agent-execution-tool.ts +230 -0
- package/src/tools/agent-harness-agent-orchestration.ts +703 -0
- package/src/tools/agent-harness-autonomy-intake.ts +785 -0
- package/src/tools/agent-harness-autonomy-live-records.ts +588 -0
- package/src/tools/agent-harness-autonomy-queue-types.ts +88 -0
- package/src/tools/agent-harness-autonomy-queue.ts +560 -0
- package/src/tools/agent-harness-background-processes-types.ts +35 -0
- package/src/tools/agent-harness-background-processes.ts +794 -0
- package/src/tools/agent-harness-browser-cockpit-route.ts +219 -0
- package/src/tools/agent-harness-browser-control.ts +338 -0
- package/src/tools/agent-harness-channel-metadata.ts +168 -10
- package/src/tools/agent-harness-cli-command-policy.ts +110 -0
- package/src/tools/agent-harness-command-catalog.ts +4 -2
- package/src/tools/agent-harness-command-runner.ts +42 -0
- package/src/tools/agent-harness-connected-host-status.ts +7 -4
- package/src/tools/agent-harness-delegation-posture.ts +234 -19
- package/src/tools/agent-harness-document-ops-reviewer-readiness.ts +234 -0
- package/src/tools/agent-harness-document-ops-types.ts +72 -0
- package/src/tools/agent-harness-document-ops.ts +671 -0
- package/src/tools/agent-harness-execution-history.ts +489 -0
- package/src/tools/agent-harness-execution-posture.ts +382 -0
- package/src/tools/agent-harness-file-recovery.ts +135 -0
- package/src/tools/agent-harness-keybinding-metadata.ts +16 -12
- package/src/tools/agent-harness-learning-curator-common.ts +102 -0
- package/src/tools/agent-harness-learning-curator-consolidation.ts +295 -0
- package/src/tools/agent-harness-learning-curator-proposals.ts +606 -0
- package/src/tools/agent-harness-learning-curator-types.ts +151 -0
- package/src/tools/agent-harness-learning-curator.ts +417 -0
- package/src/tools/agent-harness-local-model-benchmarks.ts +199 -0
- package/src/tools/agent-harness-local-model-cookbook.ts +238 -0
- package/src/tools/agent-harness-local-model-endpoints.ts +673 -0
- package/src/tools/agent-harness-media-posture.ts +208 -2
- package/src/tools/agent-harness-memory-posture.ts +556 -0
- package/src/tools/agent-harness-metadata.ts +100 -168
- package/src/tools/agent-harness-mode-catalog.ts +84 -33
- package/src/tools/agent-harness-model-catalog.ts +162 -0
- package/src/tools/agent-harness-model-provider-health.ts +186 -0
- package/src/tools/agent-harness-model-readiness.ts +406 -0
- package/src/tools/agent-harness-model-routing-types.ts +266 -0
- package/src/tools/agent-harness-model-routing-utils.ts +30 -0
- package/src/tools/agent-harness-model-routing.ts +137 -190
- package/src/tools/agent-harness-operator-methods.ts +115 -133
- package/src/tools/agent-harness-pairing-posture.ts +431 -18
- package/src/tools/agent-harness-personal-ops-discovery.ts +533 -0
- package/src/tools/agent-harness-personal-ops-intake.ts +385 -0
- package/src/tools/agent-harness-personal-ops-lanes.ts +204 -0
- package/src/tools/agent-harness-personal-ops-records.ts +732 -0
- package/src/tools/agent-harness-personal-ops-runner.ts +637 -0
- package/src/tools/agent-harness-personal-ops-types.ts +222 -0
- package/src/tools/agent-harness-personal-ops.ts +606 -0
- package/src/tools/agent-harness-project-context.ts +144 -0
- package/src/tools/agent-harness-prompt-context.ts +589 -0
- package/src/tools/agent-harness-provider-account-metadata.ts +2 -2
- package/src/tools/agent-harness-release-evidence.ts +9 -7
- package/src/tools/agent-harness-release-readiness.ts +9 -7
- package/src/tools/agent-harness-research-briefing.ts +427 -0
- package/src/tools/agent-harness-research-queue.ts +250 -0
- package/src/tools/agent-harness-research-runs.ts +295 -0
- package/src/tools/agent-harness-research-workflow.ts +322 -0
- package/src/tools/agent-harness-security-posture.ts +2 -2
- package/src/tools/agent-harness-service-posture.ts +9 -9
- package/src/tools/agent-harness-session-metadata.ts +3 -3
- package/src/tools/agent-harness-setup-connected-host.ts +501 -0
- package/src/tools/agent-harness-setup-describe.ts +557 -0
- package/src/tools/agent-harness-setup-handoffs.ts +502 -0
- package/src/tools/agent-harness-setup-model-helpers.ts +101 -0
- package/src/tools/agent-harness-setup-plan.ts +253 -0
- package/src/tools/agent-harness-setup-posture-types.ts +218 -0
- package/src/tools/agent-harness-setup-posture-utils.ts +173 -0
- package/src/tools/agent-harness-setup-posture.ts +449 -221
- package/src/tools/agent-harness-setup-smoke.ts +508 -0
- package/src/tools/agent-harness-sudo-posture.ts +114 -0
- package/src/tools/agent-harness-tool-schema.ts +120 -7
- package/src/tools/agent-harness-tool-types.ts +78 -0
- package/src/tools/agent-harness-tool-utils.ts +43 -0
- package/src/tools/agent-harness-tool.ts +311 -370
- package/src/tools/agent-harness-ui-surface-metadata.ts +76 -69
- package/src/tools/agent-harness-ui-surface-types.ts +46 -0
- package/src/tools/agent-harness-vibe-health.ts +105 -0
- package/src/tools/agent-harness-workspace-action-runner.ts +149 -0
- package/src/tools/agent-harness-workspace-actions.ts +114 -8
- package/src/tools/agent-harness-workspace-editor-execution.ts +214 -0
- package/src/tools/agent-harness-workspace-editor-runner.ts +755 -0
- package/src/tools/agent-host-tool.ts +159 -0
- package/src/tools/agent-knowledge-ingest-tool.ts +34 -1
- package/src/tools/agent-learning-consolidation-core.ts +327 -0
- package/src/tools/agent-learning-consolidation-tool.ts +758 -0
- package/src/tools/agent-memory-tool.ts +214 -0
- package/src/tools/agent-model-compare-export.ts +315 -0
- package/src/tools/agent-model-compare-handoff.ts +592 -0
- package/src/tools/agent-model-compare-judgment.ts +633 -0
- package/src/tools/agent-model-compare-run.ts +722 -0
- package/src/tools/agent-model-compare-tool.ts +698 -0
- package/src/tools/agent-model-compare-types.ts +191 -0
- package/src/tools/agent-model-compare-utils.ts +93 -0
- package/src/tools/agent-models-tool.ts +208 -0
- package/src/tools/agent-operator-action-tool.ts +1 -1
- package/src/tools/agent-operator-method-tool.ts +643 -0
- package/src/tools/agent-personal-ops-tool.ts +197 -0
- package/src/tools/agent-policy-explanation.ts +415 -0
- package/src/tools/agent-research-report-tool.ts +608 -0
- package/src/tools/agent-research-runs-tool.ts +434 -0
- package/src/tools/agent-research-sources-tool.ts +443 -0
- package/src/tools/agent-research-tool.ts +687 -0
- package/src/tools/agent-review-packet-presets-core.ts +757 -0
- package/src/tools/agent-review-packet-presets-tool.ts +466 -0
- package/src/tools/agent-review-packet-share-tool.ts +305 -0
- package/src/tools/agent-route-planner-candidates-setup.ts +411 -0
- package/src/tools/agent-route-planner-candidates-surfaces.ts +432 -0
- package/src/tools/agent-route-planner-candidates-work.ts +251 -0
- package/src/tools/agent-route-planner-helpers.ts +667 -0
- package/src/tools/agent-route-planner.ts +185 -0
- package/src/tools/agent-route-tool.ts +105 -0
- package/src/tools/agent-schedule-edit-tool.ts +210 -0
- package/src/tools/agent-schedule-tool.ts +282 -0
- package/src/tools/agent-security-tool.ts +145 -0
- package/src/tools/agent-sessions-tool.ts +122 -0
- package/src/tools/agent-settings-import-tool.ts +104 -0
- package/src/tools/agent-settings-tool.ts +176 -0
- package/src/tools/agent-setup-tool.ts +226 -0
- package/src/tools/agent-support-tool.ts +122 -0
- package/src/tools/agent-terminal-process-tools.ts +167 -0
- package/src/tools/agent-tool-policy-guard-types.ts +77 -0
- package/src/tools/agent-tool-policy-guard.ts +110 -89
- package/src/tools/agent-vibe-tool.ts +297 -0
- package/src/tools/agent-work-plan-tool.ts +265 -6
- package/src/tools/agent-workspace-tool.ts +305 -0
- package/src/tools/artifact-archive.ts +169 -0
- package/src/tools/tool-definition-compaction.ts +36 -0
- package/src/tools/tool-execution-safety.ts +41 -0
- package/src/version.ts +1 -1
|
@@ -0,0 +1,406 @@
|
|
|
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, 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
|
+
): ModelReadinessDimension {
|
|
173
|
+
const liveLatency = providerHealth.status === 'record-found' ? providerHealth.avgLatencyMs : undefined;
|
|
174
|
+
if (liveLatency !== undefined) {
|
|
175
|
+
const score = liveLatency <= 750
|
|
176
|
+
? 95
|
|
177
|
+
: liveLatency <= 1500
|
|
178
|
+
? 86
|
|
179
|
+
: liveLatency <= 3000
|
|
180
|
+
? 72
|
|
181
|
+
: 55;
|
|
182
|
+
const details = [
|
|
183
|
+
`Live provider-health latency is ${liveLatency} ms`,
|
|
184
|
+
...(providerHealth.minLatencyMs !== undefined || providerHealth.maxLatencyMs !== undefined
|
|
185
|
+
? [`range ${providerHealth.minLatencyMs ?? '?'}-${providerHealth.maxLatencyMs ?? '?'} ms`]
|
|
186
|
+
: []),
|
|
187
|
+
...(providerHealth.healthStatus ? [`provider status ${providerHealth.healthStatus}`] : []),
|
|
188
|
+
...(providerHealth.lastErrorMessage ? [`last error: ${providerHealth.lastErrorMessage}`] : []),
|
|
189
|
+
];
|
|
190
|
+
return {
|
|
191
|
+
id: 'latency',
|
|
192
|
+
label: 'Latency',
|
|
193
|
+
score,
|
|
194
|
+
weight: 20,
|
|
195
|
+
summary: `${details.join('; ')}.`,
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const score = local
|
|
200
|
+
? 55
|
|
201
|
+
: benchmarkCompositeScore != null
|
|
202
|
+
? 78
|
|
203
|
+
: 70;
|
|
204
|
+
return {
|
|
205
|
+
id: 'latency',
|
|
206
|
+
label: 'Latency',
|
|
207
|
+
score,
|
|
208
|
+
weight: 20,
|
|
209
|
+
summary: local
|
|
210
|
+
? 'Local latency is unmeasured until the user runs the benchmark prompt on this machine.'
|
|
211
|
+
: benchmarkCompositeScore != null
|
|
212
|
+
? 'No daemon-published provider-health latency is reachable; benchmark metadata is quality context only.'
|
|
213
|
+
: 'No daemon-published provider-health latency is reachable; assume normal provider latency until measured.',
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export function weightedReadiness(dimensions: readonly ModelReadinessDimension[]): number {
|
|
218
|
+
const totalWeight = dimensions.reduce((total, dimension) => total + dimension.weight, 0);
|
|
219
|
+
if (totalWeight <= 0) return 0;
|
|
220
|
+
return clampFit(dimensions.reduce((total, dimension) => total + (dimension.score * dimension.weight), 0) / totalWeight);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
export function modelReadinessScore(context: CommandContext, model: ModelCandidate): ModelRouteReadinessScore {
|
|
224
|
+
const local = isLocalCandidate([model.providerId, model.registryKey, model.modelId, model.displayName]);
|
|
225
|
+
const providerHealth = readProviderHealthSignal(context, model.providerId);
|
|
226
|
+
const dimensions: readonly ModelReadinessDimension[] = [
|
|
227
|
+
latencyScore(local, model.benchmarkCompositeScore, providerHealth),
|
|
228
|
+
contextWindowScore(model.contextWindow),
|
|
229
|
+
toolSupportScore(model.capabilities),
|
|
230
|
+
visionScore(model.capabilities),
|
|
231
|
+
costScore(model.tier, local),
|
|
232
|
+
privacyScore(local, model.providerId),
|
|
233
|
+
];
|
|
234
|
+
const score = weightedReadiness(dimensions);
|
|
235
|
+
const hasLiveLatency = providerHealth.status === 'record-found' && providerHealth.avgLatencyMs !== undefined;
|
|
236
|
+
const missingSignals = [
|
|
237
|
+
...providerHealth.missingSignals,
|
|
238
|
+
...(hasLiveLatency ? [] : ['No live latency benchmark has been recorded for this Agent route.']),
|
|
239
|
+
...(model.contextWindow == null ? ['Context-window metadata is missing.'] : []),
|
|
240
|
+
...(capabilityEnabled(model.capabilities, 'toolCalling') == null ? ['Tool-calling support is unknown.'] : []),
|
|
241
|
+
...(capabilityEnabled(model.capabilities, 'multimodal') == null ? ['Vision support is unknown.'] : []),
|
|
242
|
+
...(!model.tier && !local ? ['Cost tier is unknown.'] : []),
|
|
243
|
+
];
|
|
244
|
+
return {
|
|
245
|
+
score,
|
|
246
|
+
level: modelReadinessLevel(score),
|
|
247
|
+
confidence: providerHealth.status === 'record-found' && missingSignals.length === 0
|
|
248
|
+
? 'provider-health-backed'
|
|
249
|
+
: missingSignals.length === 0
|
|
250
|
+
? 'metadata-backed'
|
|
251
|
+
: 'estimated',
|
|
252
|
+
dimensions,
|
|
253
|
+
missingSignals,
|
|
254
|
+
providerHealth,
|
|
255
|
+
nextStep: local
|
|
256
|
+
? 'Run the local benchmark prompt before making this route the default.'
|
|
257
|
+
: providerHealth.status === 'record-found'
|
|
258
|
+
? 'Use provider-health-backed route posture for triage; run a task-specific comparison before changing the default model.'
|
|
259
|
+
: 'Use this score for routing triage; wait for daemon provider-health publication or run a task-specific comparison before changing the default model.',
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
export function localRecipeReadinessScore(
|
|
264
|
+
recipe: LocalModelRecipe,
|
|
265
|
+
fit: LocalModelRecipeFit,
|
|
266
|
+
detected: boolean,
|
|
267
|
+
evidence: LocalModelBenchmarkEvidence,
|
|
268
|
+
): ModelReadinessScore {
|
|
269
|
+
const contextScore = recipe.id === 'vllm' ? 70 : recipe.id === 'openai-compatible-local' ? 60 : 65;
|
|
270
|
+
const toolScore = recipe.id === 'ollama' || recipe.id === 'openai-compatible-local' ? 70 : 55;
|
|
271
|
+
const visionSupport = recipe.modelExamples.some((model) => /vision|vl|multimodal/i.test(model));
|
|
272
|
+
const reviewedWinner = evidence.winnerStacks.includes(localRecipeStackId(recipe));
|
|
273
|
+
const measured = evidence.comparisonCount > 0;
|
|
274
|
+
const dimensions: readonly ModelReadinessDimension[] = [
|
|
275
|
+
{
|
|
276
|
+
id: 'latency',
|
|
277
|
+
label: 'Latency',
|
|
278
|
+
score: reviewedWinner ? 82 : measured ? 68 : detected ? 62 : 50,
|
|
279
|
+
weight: 20,
|
|
280
|
+
summary: reviewedWinner
|
|
281
|
+
? 'A revealed saved local benchmark judgment selected this stack.'
|
|
282
|
+
: measured
|
|
283
|
+
? 'A saved local benchmark comparison exists, but no revealed winner is tied to this stack yet.'
|
|
284
|
+
: detected
|
|
285
|
+
? 'Local stack is detected, but latency still needs an on-machine benchmark.'
|
|
286
|
+
: 'Latency is unknown until the local server and model are running.',
|
|
287
|
+
},
|
|
288
|
+
{
|
|
289
|
+
id: 'context-window',
|
|
290
|
+
label: 'Context window',
|
|
291
|
+
score: contextScore,
|
|
292
|
+
weight: 20,
|
|
293
|
+
summary: 'Depends on the selected local model and serving stack; verify after the route is available.',
|
|
294
|
+
},
|
|
295
|
+
{
|
|
296
|
+
id: 'tool-support',
|
|
297
|
+
label: 'Tool support',
|
|
298
|
+
score: toolScore,
|
|
299
|
+
weight: 20,
|
|
300
|
+
summary: 'Tool behavior depends on the selected local model and OpenAI-compatible server support.',
|
|
301
|
+
},
|
|
302
|
+
{
|
|
303
|
+
id: 'vision',
|
|
304
|
+
label: 'Vision',
|
|
305
|
+
score: visionSupport ? 75 : 45,
|
|
306
|
+
weight: 10,
|
|
307
|
+
summary: visionSupport ? 'Example list includes a vision-capable route.' : 'No vision route is assumed for this local recipe.',
|
|
308
|
+
},
|
|
309
|
+
{
|
|
310
|
+
id: 'cost',
|
|
311
|
+
label: 'Cost',
|
|
312
|
+
score: 100,
|
|
313
|
+
weight: 15,
|
|
314
|
+
summary: 'Local route; marginal token cost is user hardware and power.',
|
|
315
|
+
},
|
|
316
|
+
{
|
|
317
|
+
id: 'privacy',
|
|
318
|
+
label: 'Privacy',
|
|
319
|
+
score: 100,
|
|
320
|
+
weight: 15,
|
|
321
|
+
summary: 'Local route can keep prompts on user-controlled hardware.',
|
|
322
|
+
},
|
|
323
|
+
];
|
|
324
|
+
const score = clampFit((weightedReadiness(dimensions) * 0.72) + (fit.score * 0.28));
|
|
325
|
+
return {
|
|
326
|
+
score,
|
|
327
|
+
level: modelReadinessLevel(score),
|
|
328
|
+
confidence: reviewedWinner ? 'measured' : 'estimated',
|
|
329
|
+
dimensions,
|
|
330
|
+
missingSignals: [
|
|
331
|
+
...(measured ? [] : ['No live latency benchmark has been recorded for this local recipe.']),
|
|
332
|
+
...(reviewedWinner ? [] : ['No revealed local benchmark judgment has selected this recipe yet.']),
|
|
333
|
+
'Context window, tool support, and vision support depend on the exact model served.',
|
|
334
|
+
],
|
|
335
|
+
nextStep: reviewedWinner
|
|
336
|
+
? 'Review the saved benchmark judgment, then use a separate confirmed apply/update route only if the user wants this winner as the default.'
|
|
337
|
+
: measured
|
|
338
|
+
? 'Review the saved comparison and save a revealed judgment before recommending a default-model change.'
|
|
339
|
+
: 'Start the local server, refresh models, then run the setupPlan benchmark action before changing the default model.',
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
export function scoreLocalModelRecipe(
|
|
344
|
+
recipe: LocalModelRecipe,
|
|
345
|
+
hardware: LocalModelHardwareProfile,
|
|
346
|
+
detection: LocalModelDetection,
|
|
347
|
+
): LocalModelRecipeFit {
|
|
348
|
+
const stackId = localRecipeStackId(recipe);
|
|
349
|
+
const detected = detection.stacks.includes(stackId);
|
|
350
|
+
const reasons: string[] = [];
|
|
351
|
+
let score = 45;
|
|
352
|
+
if (detected) {
|
|
353
|
+
score += 18;
|
|
354
|
+
reasons.push('matching local provider or model route already detected');
|
|
355
|
+
}
|
|
356
|
+
if (recipe.id === 'ollama') {
|
|
357
|
+
score += 20;
|
|
358
|
+
reasons.push('lowest setup friction for most local users');
|
|
359
|
+
if (hardware.ramGb >= 16) {
|
|
360
|
+
score += 12;
|
|
361
|
+
reasons.push(`${hardware.ramGb} GB RAM is enough for practical 7B/8B quantized models`);
|
|
362
|
+
} else {
|
|
363
|
+
score -= 10;
|
|
364
|
+
reasons.push('RAM is below the comfortable 16 GB local-model baseline');
|
|
365
|
+
}
|
|
366
|
+
if (hardware.acceleratorHint === 'apple-silicon') {
|
|
367
|
+
score += 10;
|
|
368
|
+
reasons.push('Apple Silicon is a good Ollama path');
|
|
369
|
+
}
|
|
370
|
+
} else if (recipe.id === 'llama-cpp') {
|
|
371
|
+
score += 16;
|
|
372
|
+
reasons.push('best offline fallback when downloads and serving stay manual');
|
|
373
|
+
if (hardware.ramGb >= 8) {
|
|
374
|
+
score += 10;
|
|
375
|
+
reasons.push('can use smaller GGUF quantized models within available system memory');
|
|
376
|
+
}
|
|
377
|
+
if (hardware.acceleratorHint === 'apple-silicon') {
|
|
378
|
+
score += 8;
|
|
379
|
+
reasons.push('Metal-backed llama.cpp is a strong local path on Apple Silicon');
|
|
380
|
+
}
|
|
381
|
+
} else if (recipe.id === 'vllm') {
|
|
382
|
+
score += hardware.acceleratorHint === 'cuda-env' ? 30 : -12;
|
|
383
|
+
reasons.push(hardware.acceleratorHint === 'cuda-env'
|
|
384
|
+
? 'CUDA environment hints are present'
|
|
385
|
+
: 'no CUDA hint was detected; vLLM may still work, but requires GPU/driver verification');
|
|
386
|
+
if (hardware.ramGb >= 32) {
|
|
387
|
+
score += 10;
|
|
388
|
+
reasons.push('system memory is comfortable for GPU serving overhead');
|
|
389
|
+
}
|
|
390
|
+
} else {
|
|
391
|
+
score += detected ? 10 : 4;
|
|
392
|
+
reasons.push(detected
|
|
393
|
+
? 'existing OpenAI-compatible local route can be reused'
|
|
394
|
+
: 'useful when the user already runs LM Studio, LocalAI, TGI, or another local endpoint');
|
|
395
|
+
}
|
|
396
|
+
if (hardware.cpuThreads >= 8 && recipe.id !== 'vllm') {
|
|
397
|
+
score += 5;
|
|
398
|
+
reasons.push(`${hardware.cpuThreads} CPU threads help local inference`);
|
|
399
|
+
}
|
|
400
|
+
const finalScore = clampFit(score);
|
|
401
|
+
return {
|
|
402
|
+
score: finalScore,
|
|
403
|
+
level: fitLevel(finalScore),
|
|
404
|
+
reasons,
|
|
405
|
+
};
|
|
406
|
+
}
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
import type { ArtifactDescriptor } from '@pellux/goodvibes-sdk/platform/artifacts';
|
|
2
|
+
|
|
3
|
+
export interface AgentHarnessModelRoutingArgs {
|
|
4
|
+
readonly modelRouteId?: unknown;
|
|
5
|
+
readonly target?: unknown;
|
|
6
|
+
readonly query?: unknown;
|
|
7
|
+
readonly fields?: unknown;
|
|
8
|
+
readonly includeParameters?: unknown;
|
|
9
|
+
readonly limit?: unknown;
|
|
10
|
+
readonly timeoutMs?: unknown;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export 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
|
+
export type ModelRouteLookupSource = 'modelRouteId' | 'target' | 'query';
|
|
19
|
+
|
|
20
|
+
export 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 tier?: string;
|
|
32
|
+
readonly benchmarkCompositeScore?: number | null;
|
|
33
|
+
readonly benchmarkQualityTier?: string;
|
|
34
|
+
readonly pinned: boolean;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface RouteCandidate {
|
|
38
|
+
readonly kind: 'route';
|
|
39
|
+
readonly id: string;
|
|
40
|
+
readonly label: string;
|
|
41
|
+
readonly detail: string;
|
|
42
|
+
readonly currentValue: unknown;
|
|
43
|
+
readonly settingKeys: readonly string[];
|
|
44
|
+
readonly commands: readonly string[];
|
|
45
|
+
readonly uiSurfaces: readonly string[];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface LocalModelDetection {
|
|
49
|
+
readonly providerIds: readonly string[];
|
|
50
|
+
readonly modelRoutes: readonly string[];
|
|
51
|
+
readonly stacks: readonly string[];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface LocalModelHardwareProfile {
|
|
55
|
+
readonly platform: string;
|
|
56
|
+
readonly arch: string;
|
|
57
|
+
readonly cpuModel: string;
|
|
58
|
+
readonly cpuThreads: number;
|
|
59
|
+
readonly ramGb: number;
|
|
60
|
+
readonly freeRamGb: number;
|
|
61
|
+
readonly memoryTier: 'constrained' | 'starter' | 'comfortable' | 'large';
|
|
62
|
+
readonly acceleratorHint: 'apple-silicon' | 'cuda-env' | 'none-detected';
|
|
63
|
+
readonly privacy: 'local-only';
|
|
64
|
+
readonly caveat: string;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export interface LocalModelRecipe {
|
|
68
|
+
readonly id: string;
|
|
69
|
+
readonly label: string;
|
|
70
|
+
readonly fit: string;
|
|
71
|
+
readonly bestFor: string;
|
|
72
|
+
readonly hardware: string;
|
|
73
|
+
readonly setup: readonly string[];
|
|
74
|
+
readonly modelExamples: readonly string[];
|
|
75
|
+
readonly cautions: readonly string[];
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export interface LocalModelRecipeFit {
|
|
79
|
+
readonly score: number;
|
|
80
|
+
readonly level: 'weak' | 'usable' | 'good' | 'strong';
|
|
81
|
+
readonly reasons: readonly string[];
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export interface ModelReadinessDimension {
|
|
85
|
+
readonly id: 'latency' | 'context-window' | 'tool-support' | 'vision' | 'cost' | 'privacy';
|
|
86
|
+
readonly label: string;
|
|
87
|
+
readonly score: number;
|
|
88
|
+
readonly weight: number;
|
|
89
|
+
readonly summary: string;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export interface ModelProviderHealthSignal {
|
|
93
|
+
readonly status: 'not-reachable-in-command-context' | 'read-model-empty' | 'read-model-error' | 'record-found';
|
|
94
|
+
readonly providerId: string;
|
|
95
|
+
readonly sdkContract: {
|
|
96
|
+
readonly providerHealthTypes: 'available';
|
|
97
|
+
readonly importSurface: string;
|
|
98
|
+
readonly note: string;
|
|
99
|
+
};
|
|
100
|
+
readonly daemonPublication: {
|
|
101
|
+
readonly status: 'not-published' | 'published-read-model';
|
|
102
|
+
readonly requiredPath: string;
|
|
103
|
+
readonly evidence: string;
|
|
104
|
+
};
|
|
105
|
+
readonly agentConsumption: {
|
|
106
|
+
readonly status: 'waiting-for-published-feed' | 'consumed';
|
|
107
|
+
readonly readModelPath: string | null;
|
|
108
|
+
readonly evidence: string;
|
|
109
|
+
};
|
|
110
|
+
readonly healthStatus?: string;
|
|
111
|
+
readonly isConfigured?: boolean;
|
|
112
|
+
readonly isActive?: boolean;
|
|
113
|
+
readonly avgLatencyMs?: number;
|
|
114
|
+
readonly minLatencyMs?: number;
|
|
115
|
+
readonly maxLatencyMs?: number;
|
|
116
|
+
readonly lastSuccessAt?: string | null;
|
|
117
|
+
readonly lastErrorAt?: string | null;
|
|
118
|
+
readonly lastErrorMessage?: string;
|
|
119
|
+
readonly lastCheckedAt?: string | null;
|
|
120
|
+
readonly rateLimitResetAt?: string | null;
|
|
121
|
+
readonly missingSignals: readonly string[];
|
|
122
|
+
readonly policy: string;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export interface ModelReadinessScore {
|
|
126
|
+
readonly score: number;
|
|
127
|
+
readonly level: 'risky' | 'usable' | 'good' | 'excellent';
|
|
128
|
+
readonly confidence: 'estimated' | 'metadata-backed' | 'provider-health-backed' | 'measured';
|
|
129
|
+
readonly dimensions: readonly ModelReadinessDimension[];
|
|
130
|
+
readonly missingSignals: readonly string[];
|
|
131
|
+
readonly providerHealth?: ModelProviderHealthSignal;
|
|
132
|
+
readonly nextStep: string;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export interface ModelRouteReadinessScore extends ModelReadinessScore {
|
|
136
|
+
readonly providerHealth: ModelProviderHealthSignal;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export interface LocalModelBenchmarkPlan {
|
|
140
|
+
readonly status: 'plan-ready';
|
|
141
|
+
readonly prompt: string;
|
|
142
|
+
readonly measurements: readonly string[];
|
|
143
|
+
readonly workspaceActionRoute: string;
|
|
144
|
+
readonly compareRoute: string;
|
|
145
|
+
readonly refreshRoute: string;
|
|
146
|
+
readonly notes: readonly string[];
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export interface LocalModelBenchmarkWinner {
|
|
150
|
+
readonly judgmentArtifactId: string;
|
|
151
|
+
readonly sourceArtifactId: string | null;
|
|
152
|
+
readonly registryKey: string;
|
|
153
|
+
readonly stack: string | null;
|
|
154
|
+
readonly promptPreview: string;
|
|
155
|
+
readonly reviewRoute: string;
|
|
156
|
+
readonly exportRoute: string;
|
|
157
|
+
readonly applyRoute: string;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export interface LocalModelBenchmarkEvidence {
|
|
161
|
+
readonly status: 'unavailable' | 'unmeasured' | 'comparison-saved' | 'reviewed-winner';
|
|
162
|
+
readonly comparisonCount: number;
|
|
163
|
+
readonly completedCandidateCount: number;
|
|
164
|
+
readonly revealedJudgmentCount: number;
|
|
165
|
+
readonly hiddenJudgmentCount: number;
|
|
166
|
+
readonly winnerStacks: readonly string[];
|
|
167
|
+
readonly winnerModels: readonly LocalModelBenchmarkWinner[];
|
|
168
|
+
readonly summary: string;
|
|
169
|
+
readonly confidence: 'estimated' | 'measured';
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export type LocalModelEndpointSource = 'provider-registry' | 'model-registry' | 'environment';
|
|
173
|
+
|
|
174
|
+
export interface LocalModelServerEndpoint {
|
|
175
|
+
readonly kind: 'local-server-endpoint';
|
|
176
|
+
readonly id: string;
|
|
177
|
+
readonly providerId: string | null;
|
|
178
|
+
readonly stack: string | null;
|
|
179
|
+
readonly baseUrl: string;
|
|
180
|
+
readonly modelsUrl: string;
|
|
181
|
+
readonly diagnosticStatus: 'registered-route-needs-smoke' | 'needs-provider-after-smoke';
|
|
182
|
+
readonly inspectRoute: string;
|
|
183
|
+
readonly sources: readonly LocalModelEndpointSource[];
|
|
184
|
+
readonly sourceDetails: readonly string[];
|
|
185
|
+
readonly modelRoutes: readonly string[];
|
|
186
|
+
readonly smokeCommand: string;
|
|
187
|
+
readonly smokeRoute: string;
|
|
188
|
+
readonly refreshRoute: string;
|
|
189
|
+
readonly addProviderRoute: string | null;
|
|
190
|
+
readonly notes: readonly string[];
|
|
191
|
+
readonly diagnostics?: {
|
|
192
|
+
readonly liveProbe: 'not-run';
|
|
193
|
+
readonly successCriteria: readonly string[];
|
|
194
|
+
readonly failureTriage: readonly string[];
|
|
195
|
+
readonly afterSmoke: readonly string[];
|
|
196
|
+
readonly policy: string;
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
export interface LocalModelServerDefaultEndpoint {
|
|
201
|
+
readonly id: string;
|
|
202
|
+
readonly label: string;
|
|
203
|
+
readonly stack: string;
|
|
204
|
+
readonly baseUrl: string;
|
|
205
|
+
readonly modelsUrl: string;
|
|
206
|
+
readonly smokeCommand: string;
|
|
207
|
+
readonly addProviderRoute: string;
|
|
208
|
+
readonly startHint: string;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
export interface LocalModelServerHealthMap {
|
|
212
|
+
readonly status: 'candidate-endpoints' | 'no-local-endpoints';
|
|
213
|
+
readonly liveProbe: 'not-run';
|
|
214
|
+
readonly endpointCount: number;
|
|
215
|
+
readonly returnedEndpoints: number;
|
|
216
|
+
readonly endpoints: readonly LocalModelServerEndpoint[];
|
|
217
|
+
readonly suggestedDefaults: readonly LocalModelServerDefaultEndpoint[];
|
|
218
|
+
readonly nextActions: readonly string[];
|
|
219
|
+
readonly policy: string;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export interface LocalModelSmokeTarget {
|
|
223
|
+
readonly kind: 'local-server-endpoint' | 'suggested-local-server';
|
|
224
|
+
readonly id: string;
|
|
225
|
+
readonly label: string;
|
|
226
|
+
readonly providerId: string | null;
|
|
227
|
+
readonly stack: string | null;
|
|
228
|
+
readonly baseUrl: string;
|
|
229
|
+
readonly modelsUrl: string;
|
|
230
|
+
readonly smokeCommand: string;
|
|
231
|
+
readonly smokeRoute: string;
|
|
232
|
+
readonly refreshRoute: string;
|
|
233
|
+
readonly addProviderRoute: string | null;
|
|
234
|
+
readonly source: string;
|
|
235
|
+
readonly notes: readonly string[];
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
export interface MutableLocalModelServerEndpoint {
|
|
239
|
+
providerId: string | null;
|
|
240
|
+
stack: string | null;
|
|
241
|
+
readonly baseUrl: string;
|
|
242
|
+
readonly sources: Set<LocalModelEndpointSource>;
|
|
243
|
+
readonly sourceDetails: Set<string>;
|
|
244
|
+
readonly modelRoutes: Set<string>;
|
|
245
|
+
readonly notes: Set<string>;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
export interface LocalModelSetupPlan {
|
|
249
|
+
readonly status: 'detected' | 'ready-to-try' | 'needs-hardware-review';
|
|
250
|
+
readonly priority: number;
|
|
251
|
+
readonly downloadGuidance: readonly string[];
|
|
252
|
+
readonly providerRoutes: readonly string[];
|
|
253
|
+
readonly benchmarkPlan: LocalModelBenchmarkPlan;
|
|
254
|
+
readonly confirmationBoundary: string;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
export interface ArtifactListLike {
|
|
258
|
+
readonly list?: (limit?: number) => readonly ArtifactDescriptor[];
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
export interface ProviderApiLike {
|
|
262
|
+
readonly getFavorites: () => Promise<unknown>;
|
|
263
|
+
readonly getCurrentModel: () => Promise<unknown>;
|
|
264
|
+
readonly listModels: (options?: { readonly selectableOnly?: boolean }) => Promise<readonly unknown[]>;
|
|
265
|
+
readonly listProviderIds: () => readonly string[];
|
|
266
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export function readString(value: unknown): string {
|
|
2
|
+
return typeof value === 'string' ? value.trim() : '';
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export function readLimit(value: unknown, fallback: number): number {
|
|
6
|
+
const parsed = typeof value === 'string' && value.trim() ? Number(value) : value;
|
|
7
|
+
if (typeof parsed !== 'number' || !Number.isFinite(parsed)) return fallback;
|
|
8
|
+
return Math.max(1, Math.min(500, Math.trunc(parsed)));
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function readRecord(value: unknown): Record<string, unknown> {
|
|
12
|
+
return value && typeof value === 'object' ? value as Record<string, unknown> : {};
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function readFiniteNumber(value: unknown): number | undefined {
|
|
16
|
+
return typeof value === 'number' && Number.isFinite(value) ? value : undefined;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function readBoolean(value: unknown): boolean | undefined {
|
|
20
|
+
return typeof value === 'boolean' ? value : undefined;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function readStringArray(value: unknown): readonly string[] {
|
|
24
|
+
return Array.isArray(value) ? value.map((entry) => readString(entry)).filter(Boolean) : [];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function safeIso(value: number | null | undefined): string | null {
|
|
28
|
+
if (typeof value !== 'number' || !Number.isFinite(value)) return null;
|
|
29
|
+
return new Date(value).toISOString();
|
|
30
|
+
}
|