@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,265 @@
|
|
|
1
|
+
import type { ArtifactDescriptor } from '@pellux/goodvibes-sdk/platform/artifacts';
|
|
2
|
+
import type { AgentWorkspaceRecentReviewerHandoffArtifact, AgentWorkspaceReviewPacketPresetLineage } from './agent-workspace-types.ts';
|
|
3
|
+
import { readArtifactMetadataNumber, readArtifactMetadataString, readArtifactMetadataStringList } from './agent-workspace-artifact-metadata.ts';
|
|
4
|
+
|
|
5
|
+
export function isoToEpoch(value: string): number {
|
|
6
|
+
const timestamp = Date.parse(value);
|
|
7
|
+
return Number.isFinite(timestamp) ? timestamp : 0;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function compactTimelineText(value: string, maxLength = 96): string {
|
|
11
|
+
const normalized = value.replace(/\s+/g, ' ').trim();
|
|
12
|
+
if (normalized.length <= maxLength) return normalized;
|
|
13
|
+
return `${normalized.slice(0, Math.max(0, maxLength - 3)).trimEnd()}...`;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function uniqueStrings(values: readonly (string | null | undefined)[], limit: number): readonly string[] {
|
|
17
|
+
const seen = new Set<string>();
|
|
18
|
+
const result: string[] = [];
|
|
19
|
+
for (const value of values) {
|
|
20
|
+
const trimmed = value?.trim();
|
|
21
|
+
if (!trimmed || seen.has(trimmed)) continue;
|
|
22
|
+
seen.add(trimmed);
|
|
23
|
+
result.push(trimmed);
|
|
24
|
+
if (result.length >= limit) break;
|
|
25
|
+
}
|
|
26
|
+
return result;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function isReviewerHandoffArtifact(artifact: ArtifactDescriptor): boolean {
|
|
30
|
+
const purpose = readArtifactMetadataString(artifact.metadata, 'purpose');
|
|
31
|
+
if (purpose === 'agent-model-compare-handoff') return true;
|
|
32
|
+
if (purpose.length > 0) return false;
|
|
33
|
+
return (artifact.filename ?? '').startsWith('blind-model-comparison-handoff-');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function isModelCompareArtifact(artifact: ArtifactDescriptor): boolean {
|
|
37
|
+
const purpose = readArtifactMetadataString(artifact.metadata, 'purpose');
|
|
38
|
+
if (purpose === 'agent-model-compare') return true;
|
|
39
|
+
if (purpose.length > 0) return false;
|
|
40
|
+
return (artifact.filename ?? '').startsWith('blind-model-comparison-cmp_');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function isModelCompareJudgmentArtifact(artifact: ArtifactDescriptor): boolean {
|
|
44
|
+
const purpose = readArtifactMetadataString(artifact.metadata, 'purpose');
|
|
45
|
+
if (purpose === 'agent-model-compare-judgment') return true;
|
|
46
|
+
if (purpose.length > 0) return false;
|
|
47
|
+
return (artifact.filename ?? '').startsWith('blind-model-comparison-judgment-');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function isDocumentExportArtifact(artifact: ArtifactDescriptor): boolean {
|
|
51
|
+
return readArtifactMetadataString(artifact.metadata, 'purpose') === 'agent-document-export';
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function isModelCompareExportArtifact(artifact: ArtifactDescriptor): boolean {
|
|
55
|
+
return readArtifactMetadataString(artifact.metadata, 'purpose') === 'agent-model-compare-export';
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function isReviewerHandoffArchiveArtifact(artifact: ArtifactDescriptor): boolean {
|
|
59
|
+
return readArtifactMetadataString(artifact.metadata, 'purpose') === 'agent-model-compare-handoff-archive';
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function isModelCompareRouteDecisionArtifact(artifact: ArtifactDescriptor): boolean {
|
|
63
|
+
return readArtifactMetadataString(artifact.metadata, 'purpose') === 'agent-model-compare-route-decision';
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function isReviewPacketPresetArtifact(artifact: ArtifactDescriptor): boolean {
|
|
67
|
+
return readArtifactMetadataString(artifact.metadata, 'purpose') === 'agent-review-packet-preset';
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function artifactById(artifacts: readonly ArtifactDescriptor[], artifactId: string): ArtifactDescriptor | null {
|
|
71
|
+
return artifacts.find((artifact) => artifact.id === artifactId) ?? null;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function firstArtifactMetadataString(
|
|
75
|
+
artifacts: readonly ArtifactDescriptor[],
|
|
76
|
+
artifactIds: readonly string[],
|
|
77
|
+
keys: readonly string[],
|
|
78
|
+
): string {
|
|
79
|
+
for (const artifactId of artifactIds) {
|
|
80
|
+
const artifact = artifactById(artifacts, artifactId);
|
|
81
|
+
if (!artifact) continue;
|
|
82
|
+
for (const key of keys) {
|
|
83
|
+
const value = readArtifactMetadataString(artifact.metadata, key);
|
|
84
|
+
if (value) return value;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return '';
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function latestMatchingArtifact(
|
|
91
|
+
artifacts: readonly ArtifactDescriptor[],
|
|
92
|
+
currentArtifactId: string,
|
|
93
|
+
predicate: (artifact: ArtifactDescriptor) => boolean,
|
|
94
|
+
): ArtifactDescriptor | null {
|
|
95
|
+
const current = artifactById(artifacts, currentArtifactId);
|
|
96
|
+
const currentCreatedAt = current?.createdAt ?? -1;
|
|
97
|
+
return artifacts
|
|
98
|
+
.filter((artifact) => artifact.id !== currentArtifactId && artifact.createdAt > currentCreatedAt && predicate(artifact))
|
|
99
|
+
.sort((left, right) => right.createdAt - left.createdAt || left.id.localeCompare(right.id))[0] ?? null;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function reviewPacketPresetFreshness(
|
|
103
|
+
preset: ArtifactDescriptor,
|
|
104
|
+
artifacts: readonly ArtifactDescriptor[],
|
|
105
|
+
): { readonly missingCount: number; readonly newerCount: number; readonly summary: string; readonly status: 'info' | 'attention' } {
|
|
106
|
+
const metadata = preset.metadata;
|
|
107
|
+
const primaryIds = [
|
|
108
|
+
readArtifactMetadataString(metadata, 'documentExportArtifactId'),
|
|
109
|
+
readArtifactMetadataString(metadata, 'comparisonArtifactId'),
|
|
110
|
+
readArtifactMetadataString(metadata, 'judgmentArtifactId'),
|
|
111
|
+
readArtifactMetadataString(metadata, 'revealedJudgmentArtifactId'),
|
|
112
|
+
readArtifactMetadataString(metadata, 'routeDecisionArtifactId'),
|
|
113
|
+
readArtifactMetadataString(metadata, 'handoffArtifactId'),
|
|
114
|
+
readArtifactMetadataString(metadata, 'handoffArchiveArtifactId'),
|
|
115
|
+
].filter(Boolean);
|
|
116
|
+
const relatedIds = readArtifactMetadataStringList(metadata, 'relatedArtifactIds');
|
|
117
|
+
const referencedIds = uniqueStrings([...primaryIds, ...relatedIds], 40);
|
|
118
|
+
const missingCount = referencedIds.filter((artifactId) => !artifactById(artifacts, artifactId)).length;
|
|
119
|
+
const documentId = readArtifactMetadataString(metadata, 'documentId')
|
|
120
|
+
|| firstArtifactMetadataString(artifacts, primaryIds, ['documentId', 'sourceDocumentId']);
|
|
121
|
+
const comparisonId = firstArtifactMetadataString(artifacts, primaryIds, ['comparisonId']);
|
|
122
|
+
const sourceArtifactId = firstArtifactMetadataString(artifacts, primaryIds, ['sourceArtifactId'])
|
|
123
|
+
|| readArtifactMetadataString(metadata, 'documentExportArtifactId');
|
|
124
|
+
const handoffSourceArtifactId = readArtifactMetadataString(metadata, 'revealedJudgmentArtifactId')
|
|
125
|
+
|| readArtifactMetadataString(metadata, 'judgmentArtifactId')
|
|
126
|
+
|| readArtifactMetadataString(metadata, 'comparisonArtifactId')
|
|
127
|
+
|| readArtifactMetadataString(metadata, 'documentExportArtifactId');
|
|
128
|
+
const judgmentArtifactId = readArtifactMetadataString(metadata, 'revealedJudgmentArtifactId')
|
|
129
|
+
|| readArtifactMetadataString(metadata, 'judgmentArtifactId')
|
|
130
|
+
|| firstArtifactMetadataString(artifacts, [readArtifactMetadataString(metadata, 'routeDecisionArtifactId')], ['judgmentArtifactId']);
|
|
131
|
+
const handoffArtifactId = readArtifactMetadataString(metadata, 'handoffArtifactId')
|
|
132
|
+
|| firstArtifactMetadataString(artifacts, [readArtifactMetadataString(metadata, 'handoffArchiveArtifactId')], ['handoffArtifactId']);
|
|
133
|
+
const documentExportArtifactId = readArtifactMetadataString(metadata, 'documentExportArtifactId');
|
|
134
|
+
const comparisonArtifactId = readArtifactMetadataString(metadata, 'comparisonArtifactId');
|
|
135
|
+
const judgmentPresetArtifactId = readArtifactMetadataString(metadata, 'judgmentArtifactId');
|
|
136
|
+
const revealedJudgmentArtifactId = readArtifactMetadataString(metadata, 'revealedJudgmentArtifactId');
|
|
137
|
+
const routeDecisionArtifactId = readArtifactMetadataString(metadata, 'routeDecisionArtifactId');
|
|
138
|
+
const handoffPresetArtifactId = readArtifactMetadataString(metadata, 'handoffArtifactId');
|
|
139
|
+
const handoffArchiveArtifactId = readArtifactMetadataString(metadata, 'handoffArchiveArtifactId');
|
|
140
|
+
const freshnessChecks: Array<ArtifactDescriptor | null> = [
|
|
141
|
+
documentId && documentExportArtifactId
|
|
142
|
+
? latestMatchingArtifact(artifacts, documentExportArtifactId, (artifact) => (
|
|
143
|
+
isDocumentExportArtifact(artifact) && readArtifactMetadataString(artifact.metadata, 'documentId') === documentId
|
|
144
|
+
))
|
|
145
|
+
: null,
|
|
146
|
+
comparisonArtifactId && (documentId || sourceArtifactId)
|
|
147
|
+
? latestMatchingArtifact(artifacts, comparisonArtifactId, (artifact) => (
|
|
148
|
+
isModelCompareArtifact(artifact)
|
|
149
|
+
&& (
|
|
150
|
+
Boolean(documentId && readArtifactMetadataString(artifact.metadata, 'documentId') === documentId)
|
|
151
|
+
|| Boolean(sourceArtifactId && readArtifactMetadataString(artifact.metadata, 'sourceArtifactId') === sourceArtifactId)
|
|
152
|
+
)
|
|
153
|
+
))
|
|
154
|
+
: null,
|
|
155
|
+
judgmentPresetArtifactId && (comparisonId || sourceArtifactId || documentId)
|
|
156
|
+
? latestMatchingArtifact(artifacts, judgmentPresetArtifactId, (artifact) => (
|
|
157
|
+
isModelCompareJudgmentArtifact(artifact)
|
|
158
|
+
&& (
|
|
159
|
+
Boolean(comparisonId && readArtifactMetadataString(artifact.metadata, 'comparisonId') === comparisonId)
|
|
160
|
+
|| Boolean(documentId && readArtifactMetadataString(artifact.metadata, 'documentId') === documentId)
|
|
161
|
+
|| Boolean(sourceArtifactId && readArtifactMetadataString(artifact.metadata, 'sourceArtifactId') === sourceArtifactId)
|
|
162
|
+
)
|
|
163
|
+
))
|
|
164
|
+
: null,
|
|
165
|
+
revealedJudgmentArtifactId && (comparisonId || sourceArtifactId || documentId)
|
|
166
|
+
? latestMatchingArtifact(artifacts, revealedJudgmentArtifactId, (artifact) => (
|
|
167
|
+
isModelCompareJudgmentArtifact(artifact)
|
|
168
|
+
&& artifact.metadata.revealIncludedInJudgment === true
|
|
169
|
+
&& readArtifactMetadataString(artifact.metadata, 'winnerModel').length > 0
|
|
170
|
+
&& (
|
|
171
|
+
Boolean(comparisonId && readArtifactMetadataString(artifact.metadata, 'comparisonId') === comparisonId)
|
|
172
|
+
|| Boolean(documentId && readArtifactMetadataString(artifact.metadata, 'documentId') === documentId)
|
|
173
|
+
|| Boolean(sourceArtifactId && readArtifactMetadataString(artifact.metadata, 'sourceArtifactId') === sourceArtifactId)
|
|
174
|
+
)
|
|
175
|
+
))
|
|
176
|
+
: null,
|
|
177
|
+
routeDecisionArtifactId && (comparisonId || judgmentArtifactId)
|
|
178
|
+
? latestMatchingArtifact(artifacts, routeDecisionArtifactId, (artifact) => (
|
|
179
|
+
isModelCompareRouteDecisionArtifact(artifact)
|
|
180
|
+
&& (
|
|
181
|
+
Boolean(judgmentArtifactId && readArtifactMetadataString(artifact.metadata, 'judgmentArtifactId') === judgmentArtifactId)
|
|
182
|
+
|| Boolean(comparisonId && readArtifactMetadataString(artifact.metadata, 'comparisonId') === comparisonId)
|
|
183
|
+
)
|
|
184
|
+
))
|
|
185
|
+
: null,
|
|
186
|
+
handoffPresetArtifactId && (comparisonId || handoffSourceArtifactId)
|
|
187
|
+
? latestMatchingArtifact(artifacts, handoffPresetArtifactId, (artifact) => (
|
|
188
|
+
isReviewerHandoffArtifact(artifact)
|
|
189
|
+
&& (
|
|
190
|
+
Boolean(handoffSourceArtifactId && readArtifactMetadataString(artifact.metadata, 'sourceArtifactId') === handoffSourceArtifactId)
|
|
191
|
+
|| Boolean(comparisonId && readArtifactMetadataString(artifact.metadata, 'comparisonId') === comparisonId)
|
|
192
|
+
)
|
|
193
|
+
))
|
|
194
|
+
: null,
|
|
195
|
+
handoffArchiveArtifactId && (comparisonId || handoffArtifactId)
|
|
196
|
+
? latestMatchingArtifact(artifacts, handoffArchiveArtifactId, (artifact) => (
|
|
197
|
+
isReviewerHandoffArchiveArtifact(artifact)
|
|
198
|
+
&& (
|
|
199
|
+
Boolean(handoffArtifactId && readArtifactMetadataString(artifact.metadata, 'handoffArtifactId') === handoffArtifactId)
|
|
200
|
+
|| Boolean(comparisonId && readArtifactMetadataString(artifact.metadata, 'comparisonId') === comparisonId)
|
|
201
|
+
)
|
|
202
|
+
))
|
|
203
|
+
: null,
|
|
204
|
+
];
|
|
205
|
+
const newerCount = new Set(freshnessChecks.filter((artifact): artifact is ArtifactDescriptor => artifact !== null).map((artifact) => artifact.id)).size;
|
|
206
|
+
const needsReview = missingCount > 0 || newerCount > 0;
|
|
207
|
+
return {
|
|
208
|
+
missingCount,
|
|
209
|
+
newerCount,
|
|
210
|
+
summary: needsReview ? `freshness needs review: ${missingCount} missing, ${newerCount} newer` : 'freshness current',
|
|
211
|
+
status: needsReview ? 'attention' : 'info',
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
export function buildReviewPacketPresetLineage(preset: ArtifactDescriptor | null): AgentWorkspaceReviewPacketPresetLineage | null {
|
|
216
|
+
if (!preset) return null;
|
|
217
|
+
const metadata = preset.metadata;
|
|
218
|
+
const refreshedFromArtifactId = readArtifactMetadataString(metadata, 'refreshOfArtifactId') || null;
|
|
219
|
+
const refreshedFromPresetId = readArtifactMetadataString(metadata, 'refreshOfPresetId') || null;
|
|
220
|
+
const freshnessMissingCount = readArtifactMetadataNumber(metadata, 'freshnessMissingCount');
|
|
221
|
+
const freshnessSupersededCount = readArtifactMetadataNumber(metadata, 'freshnessSupersededCount');
|
|
222
|
+
const freshnessUnresolvedCount = readArtifactMetadataNumber(metadata, 'freshnessUnresolvedCount');
|
|
223
|
+
const repairedCount = (freshnessMissingCount ?? 0) + (freshnessSupersededCount ?? 0);
|
|
224
|
+
const name = readArtifactMetadataString(metadata, 'name') || null;
|
|
225
|
+
const presetId = readArtifactMetadataString(metadata, 'presetId') || null;
|
|
226
|
+
const displayName = name ?? preset.filename ?? preset.id;
|
|
227
|
+
const summary = refreshedFromArtifactId
|
|
228
|
+
? [
|
|
229
|
+
`${displayName} refreshed from ${refreshedFromArtifactId}`,
|
|
230
|
+
refreshedFromPresetId ? `source preset ${refreshedFromPresetId}` : '',
|
|
231
|
+
`repaired ${repairedCount}`,
|
|
232
|
+
`unresolved ${freshnessUnresolvedCount ?? 0}`,
|
|
233
|
+
].filter(Boolean).join('; ')
|
|
234
|
+
: [
|
|
235
|
+
`${displayName} has no refresh lineage`,
|
|
236
|
+
presetId ? `preset ${presetId}` : '',
|
|
237
|
+
].filter(Boolean).join('; ');
|
|
238
|
+
return {
|
|
239
|
+
artifactId: preset.id,
|
|
240
|
+
presetId,
|
|
241
|
+
name,
|
|
242
|
+
refreshed: Boolean(refreshedFromArtifactId),
|
|
243
|
+
refreshedFromArtifactId,
|
|
244
|
+
refreshedFromPresetId,
|
|
245
|
+
freshnessMissingCount,
|
|
246
|
+
freshnessSupersededCount,
|
|
247
|
+
freshnessUnresolvedCount,
|
|
248
|
+
summary,
|
|
249
|
+
inspectRoute: `agent_review_packet_presets show artifactId:"${preset.id}"`,
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
export function summarizeReviewerHandoffArtifact(artifact: ArtifactDescriptor): AgentWorkspaceRecentReviewerHandoffArtifact {
|
|
254
|
+
const metadata = artifact.metadata;
|
|
255
|
+
return {
|
|
256
|
+
id: artifact.id,
|
|
257
|
+
filename: artifact.filename ?? '(unnamed handoff artifact)',
|
|
258
|
+
createdAt: artifact.createdAt,
|
|
259
|
+
handoffId: readArtifactMetadataString(metadata, 'handoffId') || 'unknown-handoff',
|
|
260
|
+
comparisonId: readArtifactMetadataString(metadata, 'comparisonId') || 'unknown-comparison',
|
|
261
|
+
sourceArtifactId: readArtifactMetadataString(metadata, 'sourceArtifactId') || '(missing source)',
|
|
262
|
+
sourceKind: readArtifactMetadataString(metadata, 'sourceKind') || 'unknown',
|
|
263
|
+
relatedArtifactCount: readArtifactMetadataStringList(metadata, 'relatedArtifactIds').length,
|
|
264
|
+
};
|
|
265
|
+
}
|
|
@@ -56,6 +56,95 @@ export interface TuiSettingsImportOutcome extends SettingMutationOutcome {
|
|
|
56
56
|
readonly runtimeSnapshot: AgentWorkspaceRuntimeSnapshot | null;
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
type TuiImportStatus = 'would_import' | 'unchanged' | 'skipped';
|
|
60
|
+
|
|
61
|
+
interface TuiSettingsImportSource {
|
|
62
|
+
readonly id: string;
|
|
63
|
+
readonly label: string;
|
|
64
|
+
readonly path: string;
|
|
65
|
+
readonly sourcePackage: string;
|
|
66
|
+
readonly surfaceRoot: string;
|
|
67
|
+
readonly scope: 'user' | 'project';
|
|
68
|
+
readonly ownership: 'source-owned';
|
|
69
|
+
readonly mutatesSource: false;
|
|
70
|
+
readonly status: 'missing' | 'loaded' | 'invalid' | 'error';
|
|
71
|
+
readonly importableSettings: number;
|
|
72
|
+
readonly error?: string;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
interface GoodVibesSettingsImportSourceCatalogEntry {
|
|
76
|
+
readonly packageId: string;
|
|
77
|
+
readonly surfaceRoot: string;
|
|
78
|
+
readonly scopes: readonly string[];
|
|
79
|
+
readonly status: 'implemented';
|
|
80
|
+
readonly ownership: 'source-owned';
|
|
81
|
+
readonly mutatesSource: false;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
interface TuiSettingImportPlanEntry {
|
|
85
|
+
readonly key: string;
|
|
86
|
+
readonly source: string;
|
|
87
|
+
readonly value: unknown;
|
|
88
|
+
readonly status: TuiImportStatus;
|
|
89
|
+
readonly current: unknown;
|
|
90
|
+
readonly incoming: unknown;
|
|
91
|
+
readonly reason?: string;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
interface TuiSubscriptionImportPlanEntry {
|
|
95
|
+
readonly provider?: string;
|
|
96
|
+
readonly kind: 'active' | 'pending';
|
|
97
|
+
readonly status: TuiImportStatus;
|
|
98
|
+
readonly reason?: string;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
interface TuiSettingsImportPlan {
|
|
102
|
+
readonly sources: readonly TuiSettingsImportSource[];
|
|
103
|
+
readonly settings: readonly TuiSettingImportPlanEntry[];
|
|
104
|
+
readonly subscriptions: readonly TuiSubscriptionImportPlanEntry[];
|
|
105
|
+
readonly parseErrors: readonly string[];
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export interface TuiSettingsImportPreview {
|
|
109
|
+
readonly summary: {
|
|
110
|
+
readonly settingsToImport: number;
|
|
111
|
+
readonly settingsUnchanged: number;
|
|
112
|
+
readonly settingsSkipped: number;
|
|
113
|
+
readonly activeSubscriptionsToImport: number;
|
|
114
|
+
readonly pendingSubscriptionsToImport: number;
|
|
115
|
+
readonly subscriptionsUnchanged: number;
|
|
116
|
+
readonly subscriptionsSkipped: number;
|
|
117
|
+
readonly parseErrors: number;
|
|
118
|
+
};
|
|
119
|
+
readonly sourceCatalog: readonly GoodVibesSettingsImportSourceCatalogEntry[];
|
|
120
|
+
readonly sources: readonly TuiSettingsImportSource[];
|
|
121
|
+
readonly settings: readonly {
|
|
122
|
+
readonly key: string;
|
|
123
|
+
readonly source: string;
|
|
124
|
+
readonly status: TuiImportStatus;
|
|
125
|
+
readonly current: unknown;
|
|
126
|
+
readonly incoming: unknown;
|
|
127
|
+
readonly reason?: string;
|
|
128
|
+
}[];
|
|
129
|
+
readonly subscriptions: readonly TuiSubscriptionImportPlanEntry[];
|
|
130
|
+
readonly policy: {
|
|
131
|
+
readonly effect: 'read-only' | 'state';
|
|
132
|
+
readonly confirmation: 'required-for-apply';
|
|
133
|
+
readonly boundary: string;
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const GOODVIBES_SETTINGS_IMPORT_SOURCE_CATALOG: readonly GoodVibesSettingsImportSourceCatalogEntry[] = [
|
|
138
|
+
{
|
|
139
|
+
packageId: 'goodvibes-tui',
|
|
140
|
+
surfaceRoot: GOODVIBES_TUI_SURFACE_ROOT,
|
|
141
|
+
scopes: ['user-settings', 'project-settings', 'provider-subscriptions'],
|
|
142
|
+
status: 'implemented',
|
|
143
|
+
ownership: 'source-owned',
|
|
144
|
+
mutatesSource: false,
|
|
145
|
+
},
|
|
146
|
+
];
|
|
147
|
+
|
|
59
148
|
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
60
149
|
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
61
150
|
}
|
|
@@ -84,6 +173,15 @@ function valuesMatch(left: unknown, right: unknown): boolean {
|
|
|
84
173
|
return JSON.stringify(left) === JSON.stringify(right);
|
|
85
174
|
}
|
|
86
175
|
|
|
176
|
+
function redactImportValue(key: string, value: unknown): unknown {
|
|
177
|
+
if (typeof value !== 'string') return value;
|
|
178
|
+
if (!value) return value;
|
|
179
|
+
if (/(?:secret|token|password|api[-_.]?key|signing)/i.test(key)) {
|
|
180
|
+
return value.startsWith('goodvibes://secrets/') ? '<secret-ref>' : '<redacted>';
|
|
181
|
+
}
|
|
182
|
+
return value;
|
|
183
|
+
}
|
|
184
|
+
|
|
87
185
|
function readRecordMap(record: Record<string, unknown>, key: string): readonly unknown[] {
|
|
88
186
|
const value = record[key];
|
|
89
187
|
return isRecord(value) ? Object.values(value) : [];
|
|
@@ -109,7 +207,45 @@ function isPendingSubscriptionLogin(value: unknown): value is PendingSubscriptio
|
|
|
109
207
|
&& typeof value.createdAt === 'number';
|
|
110
208
|
}
|
|
111
209
|
|
|
112
|
-
function
|
|
210
|
+
function planTuiSubscriptions(context: CommandContext, parseErrors: string[]): readonly TuiSubscriptionImportPlanEntry[] {
|
|
211
|
+
const shellPaths = context.workspace.shellPaths;
|
|
212
|
+
const manager = context.platform.subscriptionManager;
|
|
213
|
+
const result: TuiSubscriptionImportPlanEntry[] = [];
|
|
214
|
+
if (!shellPaths || !manager) return result;
|
|
215
|
+
|
|
216
|
+
const sourcePath = shellPaths.resolveUserPath(GOODVIBES_TUI_SURFACE_ROOT, 'subscriptions.json');
|
|
217
|
+
try {
|
|
218
|
+
const store = readJsonRecord(sourcePath);
|
|
219
|
+
if (!store) return result;
|
|
220
|
+
for (const entry of readRecordMap(store, 'subscriptions')) {
|
|
221
|
+
if (!isProviderSubscription(entry)) {
|
|
222
|
+
result.push({ kind: 'active', status: 'skipped', reason: 'active subscription with invalid shape' });
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
if (valuesMatch(manager.get(entry.provider), entry)) {
|
|
226
|
+
result.push({ provider: entry.provider, kind: 'active', status: 'unchanged' });
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
result.push({ provider: entry.provider, kind: 'active', status: 'would_import' });
|
|
230
|
+
}
|
|
231
|
+
for (const entry of readRecordMap(store, 'pending')) {
|
|
232
|
+
if (!isPendingSubscriptionLogin(entry)) {
|
|
233
|
+
result.push({ kind: 'pending', status: 'skipped', reason: 'pending subscription with invalid shape' });
|
|
234
|
+
continue;
|
|
235
|
+
}
|
|
236
|
+
if (valuesMatch(manager.getPending(entry.provider), entry)) {
|
|
237
|
+
result.push({ provider: entry.provider, kind: 'pending', status: 'unchanged' });
|
|
238
|
+
continue;
|
|
239
|
+
}
|
|
240
|
+
result.push({ provider: entry.provider, kind: 'pending', status: 'would_import' });
|
|
241
|
+
}
|
|
242
|
+
} catch (error) {
|
|
243
|
+
parseErrors.push(`subscriptions: ${error instanceof Error ? error.message : String(error)}`);
|
|
244
|
+
}
|
|
245
|
+
return result;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function applyTuiSubscriptions(context: CommandContext, parseErrors: string[]): {
|
|
113
249
|
readonly importedActive: string[];
|
|
114
250
|
readonly importedPending: string[];
|
|
115
251
|
readonly unchangedActive: string[];
|
|
@@ -275,51 +411,147 @@ export async function applyAgentWorkspaceSettingValue(
|
|
|
275
411
|
}
|
|
276
412
|
}
|
|
277
413
|
|
|
278
|
-
|
|
414
|
+
function buildTuiSettingsImportPlan(context: CommandContext | null): TuiSettingsImportPlan | null {
|
|
279
415
|
const shellPaths = context?.workspace?.shellPaths;
|
|
280
416
|
const configManager = context?.platform?.configManager;
|
|
281
|
-
if (!context || !shellPaths || !configManager)
|
|
282
|
-
return {
|
|
283
|
-
status: 'GoodVibes TUI settings import is unavailable in this runtime.',
|
|
284
|
-
runtimeSnapshot: null,
|
|
285
|
-
result: {
|
|
286
|
-
kind: 'error',
|
|
287
|
-
title: 'Import unavailable',
|
|
288
|
-
detail: 'The workspace cannot locate shell paths or the Agent config manager.',
|
|
289
|
-
safety: 'safe',
|
|
290
|
-
},
|
|
291
|
-
};
|
|
292
|
-
}
|
|
417
|
+
if (!context || !shellPaths || !configManager) return null;
|
|
293
418
|
|
|
294
|
-
const
|
|
295
|
-
{
|
|
296
|
-
|
|
419
|
+
const sourceInputs: readonly Omit<TuiSettingsImportSource, 'status' | 'importableSettings' | 'error'>[] = [
|
|
420
|
+
{
|
|
421
|
+
id: 'goodvibes-tui:user-settings',
|
|
422
|
+
label: 'goodvibes-tui user settings',
|
|
423
|
+
path: shellPaths.resolveUserPath(GOODVIBES_TUI_SURFACE_ROOT, 'settings.json'),
|
|
424
|
+
sourcePackage: 'goodvibes-tui',
|
|
425
|
+
surfaceRoot: GOODVIBES_TUI_SURFACE_ROOT,
|
|
426
|
+
scope: 'user',
|
|
427
|
+
ownership: 'source-owned',
|
|
428
|
+
mutatesSource: false,
|
|
429
|
+
},
|
|
430
|
+
{
|
|
431
|
+
id: 'goodvibes-tui:project-settings',
|
|
432
|
+
label: 'goodvibes-tui project settings',
|
|
433
|
+
path: shellPaths.resolveProjectPath(GOODVIBES_TUI_SURFACE_ROOT, 'settings.json'),
|
|
434
|
+
sourcePackage: 'goodvibes-tui',
|
|
435
|
+
surfaceRoot: GOODVIBES_TUI_SURFACE_ROOT,
|
|
436
|
+
scope: 'project',
|
|
437
|
+
ownership: 'source-owned',
|
|
438
|
+
mutatesSource: false,
|
|
439
|
+
},
|
|
297
440
|
];
|
|
298
441
|
const values = new Map<string, { readonly value: unknown; readonly source: string }>();
|
|
299
442
|
const parseErrors: string[] = [];
|
|
300
|
-
|
|
443
|
+
const sources: TuiSettingsImportSource[] = [];
|
|
444
|
+
for (const source of sourceInputs) {
|
|
445
|
+
if (!existsSync(source.path)) {
|
|
446
|
+
sources.push({ ...source, status: 'missing', importableSettings: 0 });
|
|
447
|
+
continue;
|
|
448
|
+
}
|
|
301
449
|
try {
|
|
302
450
|
const record = readJsonRecord(source.path);
|
|
303
|
-
if (!record)
|
|
451
|
+
if (!record) {
|
|
452
|
+
const message = `${source.label}: settings store is not an object`;
|
|
453
|
+
parseErrors.push(message);
|
|
454
|
+
sources.push({ ...source, status: 'invalid', importableSettings: 0, error: message });
|
|
455
|
+
continue;
|
|
456
|
+
}
|
|
457
|
+
let importableSettings = 0;
|
|
304
458
|
for (const setting of configManager.getSchema()) {
|
|
305
459
|
if (!canImportTuiSetting(setting.key)) continue;
|
|
306
460
|
const value = readNestedSettingValue(record, setting.key);
|
|
307
|
-
if (value
|
|
461
|
+
if (value === undefined) continue;
|
|
462
|
+
importableSettings += 1;
|
|
463
|
+
values.set(setting.key, { value, source: source.id });
|
|
308
464
|
}
|
|
465
|
+
sources.push({ ...source, status: 'loaded', importableSettings });
|
|
309
466
|
} catch (error) {
|
|
310
|
-
|
|
467
|
+
const message = `${source.label}: ${error instanceof Error ? error.message : String(error)}`;
|
|
468
|
+
parseErrors.push(message);
|
|
469
|
+
sources.push({ ...source, status: 'error', importableSettings: 0, error: message });
|
|
311
470
|
}
|
|
312
471
|
}
|
|
313
|
-
|
|
472
|
+
|
|
473
|
+
const settings: TuiSettingImportPlanEntry[] = [];
|
|
474
|
+
for (const [key, entry] of values) {
|
|
475
|
+
const setting = agentWorkspaceSettingSchema(context, key);
|
|
476
|
+
if (!setting) continue;
|
|
477
|
+
const current = configManager.get(setting.key as ConfigKey);
|
|
478
|
+
const status: TuiImportStatus = valuesMatch(current, entry.value) ? 'unchanged' : 'would_import';
|
|
479
|
+
settings.push({
|
|
480
|
+
key: setting.key,
|
|
481
|
+
source: entry.source,
|
|
482
|
+
value: entry.value,
|
|
483
|
+
status,
|
|
484
|
+
current: redactImportValue(setting.key, current),
|
|
485
|
+
incoming: redactImportValue(setting.key, entry.value),
|
|
486
|
+
});
|
|
487
|
+
}
|
|
488
|
+
const subscriptions = planTuiSubscriptions(context, parseErrors);
|
|
489
|
+
return { sources, settings, subscriptions, parseErrors };
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
export function previewAgentWorkspaceTuiSettingsImport(context: CommandContext | null): TuiSettingsImportPreview | null {
|
|
493
|
+
const plan = buildTuiSettingsImportPlan(context);
|
|
494
|
+
if (!plan) return null;
|
|
495
|
+
return {
|
|
496
|
+
summary: {
|
|
497
|
+
settingsToImport: plan.settings.filter((entry) => entry.status === 'would_import').length,
|
|
498
|
+
settingsUnchanged: plan.settings.filter((entry) => entry.status === 'unchanged').length,
|
|
499
|
+
settingsSkipped: plan.settings.filter((entry) => entry.status === 'skipped').length,
|
|
500
|
+
activeSubscriptionsToImport: plan.subscriptions.filter((entry) => entry.kind === 'active' && entry.status === 'would_import').length,
|
|
501
|
+
pendingSubscriptionsToImport: plan.subscriptions.filter((entry) => entry.kind === 'pending' && entry.status === 'would_import').length,
|
|
502
|
+
subscriptionsUnchanged: plan.subscriptions.filter((entry) => entry.status === 'unchanged').length,
|
|
503
|
+
subscriptionsSkipped: plan.subscriptions.filter((entry) => entry.status === 'skipped').length,
|
|
504
|
+
parseErrors: plan.parseErrors.length,
|
|
505
|
+
},
|
|
506
|
+
sourceCatalog: GOODVIBES_SETTINGS_IMPORT_SOURCE_CATALOG,
|
|
507
|
+
sources: plan.sources,
|
|
508
|
+
settings: plan.settings.map((entry) => ({
|
|
509
|
+
key: entry.key,
|
|
510
|
+
source: entry.source,
|
|
511
|
+
status: entry.status,
|
|
512
|
+
current: entry.current,
|
|
513
|
+
incoming: entry.incoming,
|
|
514
|
+
...(entry.reason ? { reason: entry.reason } : {}),
|
|
515
|
+
})),
|
|
516
|
+
subscriptions: plan.subscriptions,
|
|
517
|
+
policy: {
|
|
518
|
+
effect: 'read-only',
|
|
519
|
+
confirmation: 'required-for-apply',
|
|
520
|
+
boundary: 'Preview does not mutate state. Apply imports only Agent-owned settings and provider subscriptions from published GoodVibes platform sources.',
|
|
521
|
+
},
|
|
522
|
+
};
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
export async function importAgentWorkspaceTuiSettings(context: CommandContext | null): Promise<TuiSettingsImportOutcome> {
|
|
526
|
+
const shellPaths = context?.workspace?.shellPaths;
|
|
527
|
+
const configManager = context?.platform?.configManager;
|
|
528
|
+
if (!context || !shellPaths || !configManager) {
|
|
529
|
+
return {
|
|
530
|
+
status: 'GoodVibes settings import is unavailable in this runtime.',
|
|
531
|
+
runtimeSnapshot: null,
|
|
532
|
+
result: {
|
|
533
|
+
kind: 'error',
|
|
534
|
+
title: 'Import unavailable',
|
|
535
|
+
detail: 'The workspace cannot locate shell paths or the Agent config manager.',
|
|
536
|
+
safety: 'safe',
|
|
537
|
+
},
|
|
538
|
+
};
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
const plan = buildTuiSettingsImportPlan(context);
|
|
542
|
+
const values = plan?.settings ?? [];
|
|
543
|
+
const subscriptionParseErrors: string[] = [];
|
|
544
|
+
const subscriptions = applyTuiSubscriptions(context, subscriptionParseErrors);
|
|
545
|
+
const parseErrors: string[] = [...new Set([...(plan?.parseErrors ?? []), ...subscriptionParseErrors])];
|
|
314
546
|
const subscriptionImports = subscriptions.importedActive.length + subscriptions.importedPending.length;
|
|
315
547
|
const subscriptionUnchanged = subscriptions.unchangedActive.length + subscriptions.unchangedPending.length;
|
|
316
548
|
|
|
317
|
-
if (values.
|
|
549
|
+
if (values.length === 0 && subscriptionImports === 0 && subscriptionUnchanged === 0 && subscriptions.skipped.length === 0) {
|
|
318
550
|
const detail = parseErrors.length > 0
|
|
319
551
|
? `No importable settings or subscriptions found. ${parseErrors.join('; ')}`
|
|
320
|
-
: 'No GoodVibes
|
|
552
|
+
: 'No GoodVibes settings or subscription store with importable Agent-owned state was found.';
|
|
321
553
|
return {
|
|
322
|
-
status: 'No GoodVibes
|
|
554
|
+
status: 'No GoodVibes settings imported.',
|
|
323
555
|
runtimeSnapshot: null,
|
|
324
556
|
result: {
|
|
325
557
|
kind: parseErrors.length > 0 ? 'error' : 'guidance',
|
|
@@ -333,10 +565,10 @@ export async function importAgentWorkspaceTuiSettings(context: CommandContext |
|
|
|
333
565
|
const imported: string[] = [];
|
|
334
566
|
const unchanged: string[] = [];
|
|
335
567
|
const skipped: string[] = [];
|
|
336
|
-
for (const
|
|
337
|
-
const setting = agentWorkspaceSettingSchema(context, key);
|
|
568
|
+
for (const entry of values) {
|
|
569
|
+
const setting = agentWorkspaceSettingSchema(context, entry.key);
|
|
338
570
|
if (!setting) continue;
|
|
339
|
-
if (valuesMatch(configManager.get(setting.key as ConfigKey), entry.value)) {
|
|
571
|
+
if (entry.status === 'unchanged' || valuesMatch(configManager.get(setting.key as ConfigKey), entry.value)) {
|
|
340
572
|
unchanged.push(setting.key);
|
|
341
573
|
continue;
|
|
342
574
|
}
|
|
@@ -352,11 +584,11 @@ export async function importAgentWorkspaceTuiSettings(context: CommandContext |
|
|
|
352
584
|
const unchangedCount = unchanged.length + subscriptionUnchanged;
|
|
353
585
|
|
|
354
586
|
return {
|
|
355
|
-
status: changedCount > 0 ? `Imported ${changedCount} GoodVibes
|
|
587
|
+
status: changedCount > 0 ? `Imported ${changedCount} GoodVibes settings item(s).` : 'No GoodVibes settings changed.',
|
|
356
588
|
runtimeSnapshot: context ? buildAgentWorkspaceRuntimeSnapshot(context) : null,
|
|
357
589
|
result: {
|
|
358
590
|
kind: skipped.length > 0 && changedCount === 0 ? 'error' : changedCount > 0 ? 'refreshed' : 'guidance',
|
|
359
|
-
title: changedCount > 0 ? 'GoodVibes
|
|
591
|
+
title: changedCount > 0 ? 'GoodVibes settings imported' : 'No settings changed',
|
|
360
592
|
detail: [
|
|
361
593
|
imported.length > 0 ? `Imported: ${imported.slice(0, 10).join(', ')}${imported.length > 10 ? `, +${imported.length - 10} more` : ''}.` : '',
|
|
362
594
|
subscriptions.importedActive.length > 0 ? `Imported active subscription(s): ${subscriptions.importedActive.join(', ')}.` : '',
|