@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,608 @@
|
|
|
1
|
+
import type { ArtifactDescriptor, ArtifactStore } from '@pellux/goodvibes-sdk/platform/artifacts';
|
|
2
|
+
import type { Tool } from '@pellux/goodvibes-sdk/platform/types';
|
|
3
|
+
import type { ToolRegistry } from '@pellux/goodvibes-sdk/platform/tools';
|
|
4
|
+
|
|
5
|
+
export interface AgentResearchReportToolArgs {
|
|
6
|
+
readonly runId?: unknown;
|
|
7
|
+
readonly id?: unknown;
|
|
8
|
+
readonly title?: unknown;
|
|
9
|
+
readonly question?: unknown;
|
|
10
|
+
readonly summary?: unknown;
|
|
11
|
+
readonly reportMarkdown?: unknown;
|
|
12
|
+
readonly sources?: unknown;
|
|
13
|
+
readonly findings?: unknown;
|
|
14
|
+
readonly gaps?: unknown;
|
|
15
|
+
readonly recommendations?: unknown;
|
|
16
|
+
readonly methodology?: unknown;
|
|
17
|
+
readonly confidence?: unknown;
|
|
18
|
+
readonly tags?: unknown;
|
|
19
|
+
readonly requireCitationCoverage?: unknown;
|
|
20
|
+
readonly visualReport?: unknown;
|
|
21
|
+
readonly confirm?: unknown;
|
|
22
|
+
readonly explicitUserRequest?: unknown;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
type AgentResearchReportArtifactStore = Pick<ArtifactStore, 'create'>;
|
|
26
|
+
|
|
27
|
+
interface ResearchSource {
|
|
28
|
+
readonly title: string;
|
|
29
|
+
readonly url?: string;
|
|
30
|
+
readonly publisher?: string;
|
|
31
|
+
readonly publishedAt?: string;
|
|
32
|
+
readonly accessedAt?: string;
|
|
33
|
+
readonly credibility: string;
|
|
34
|
+
readonly note?: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
interface CitationCoverage {
|
|
38
|
+
readonly sourceCount: number;
|
|
39
|
+
readonly citedSourceIds: readonly string[];
|
|
40
|
+
readonly missingSourceIds: readonly string[];
|
|
41
|
+
readonly unknownCitationIds: readonly string[];
|
|
42
|
+
readonly repairSuggestions: readonly string[];
|
|
43
|
+
readonly coverageRatio: number;
|
|
44
|
+
readonly pass: boolean;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const MAX_REPORT_CHARS = 80_000;
|
|
48
|
+
const MAX_SOURCE_COUNT = 50;
|
|
49
|
+
const SECRETISH = /token|secret|password|authorization|credential|api[-_]?key/i;
|
|
50
|
+
|
|
51
|
+
function readString(value: unknown): string {
|
|
52
|
+
return typeof value === 'string' ? value.trim() : '';
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function readBoolean(value: unknown): boolean {
|
|
56
|
+
return value === true || value === 'true' || value === 'yes';
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function readList(value: unknown): readonly string[] {
|
|
60
|
+
if (Array.isArray(value)) return value.map(readString).filter(Boolean);
|
|
61
|
+
return readString(value)
|
|
62
|
+
.split(/\n/)
|
|
63
|
+
.map((entry) => entry.replace(/^[-*]\s+/, '').trim())
|
|
64
|
+
.filter(Boolean);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function readTags(value: unknown): readonly string[] {
|
|
68
|
+
if (Array.isArray(value)) return value.map(readString).filter(Boolean);
|
|
69
|
+
return readString(value).split(/[,\n]/).map((entry) => entry.trim()).filter(Boolean);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function previewText(value: string, limit = 240): string {
|
|
73
|
+
const normalized = value.replace(/\s+/g, ' ').trim();
|
|
74
|
+
if (normalized.length <= limit) return normalized;
|
|
75
|
+
return `${normalized.slice(0, Math.max(0, limit - 3)).trimEnd()}...`;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function readSource(value: unknown): ResearchSource | null {
|
|
79
|
+
if (typeof value === 'string') {
|
|
80
|
+
const text = value.trim();
|
|
81
|
+
if (!text) return null;
|
|
82
|
+
const parts = text.split('|').map((part) => part.trim()).filter(Boolean);
|
|
83
|
+
const maybeUrlIndex = parts.findIndex((part) => /^https?:\/\//i.test(part));
|
|
84
|
+
const maybeUrl = maybeUrlIndex >= 0 ? parts[maybeUrlIndex] : '';
|
|
85
|
+
const detailStart = maybeUrlIndex >= 0 ? maybeUrlIndex + 1 : 1;
|
|
86
|
+
const noteParts = parts.slice(detailStart + 1);
|
|
87
|
+
const nonUrlParts = parts.filter((_, index) => index !== maybeUrlIndex);
|
|
88
|
+
const title = maybeUrlIndex === 0 ? (nonUrlParts.length > 1 ? nonUrlParts[0] : maybeUrl) : (parts[0] ?? maybeUrl ?? text);
|
|
89
|
+
return {
|
|
90
|
+
title: title || text,
|
|
91
|
+
...(maybeUrl ? { url: sanitizeSourceUrl(maybeUrl) } : {}),
|
|
92
|
+
credibility: parts[detailStart] ?? 'unreviewed',
|
|
93
|
+
...(noteParts.length > 0 ? { note: noteParts.join(' | ') } : {}),
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) return null;
|
|
97
|
+
const record = value as Record<string, unknown>;
|
|
98
|
+
const title = readString(record.title) || readString(record.name) || readString(record.url);
|
|
99
|
+
if (!title) return null;
|
|
100
|
+
return {
|
|
101
|
+
title,
|
|
102
|
+
...(readString(record.url) ? { url: sanitizeSourceUrl(readString(record.url)) } : {}),
|
|
103
|
+
...(readString(record.publisher) ? { publisher: readString(record.publisher) } : {}),
|
|
104
|
+
...(readString(record.publishedAt) ? { publishedAt: readString(record.publishedAt) } : {}),
|
|
105
|
+
...(readString(record.accessedAt) ? { accessedAt: readString(record.accessedAt) } : {}),
|
|
106
|
+
credibility: readString(record.credibility) || 'unreviewed',
|
|
107
|
+
...(readString(record.note) ? { note: readString(record.note) } : {}),
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function readSources(value: unknown): readonly ResearchSource[] {
|
|
112
|
+
const raw = Array.isArray(value) ? value : readList(value);
|
|
113
|
+
const sources: ResearchSource[] = [];
|
|
114
|
+
for (const entry of raw) {
|
|
115
|
+
const source = readSource(entry);
|
|
116
|
+
if (!source) continue;
|
|
117
|
+
sources.push(source);
|
|
118
|
+
if (sources.length >= MAX_SOURCE_COUNT) break;
|
|
119
|
+
}
|
|
120
|
+
return sources;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function sanitizeSourceUrl(value: string): string {
|
|
124
|
+
try {
|
|
125
|
+
const url = new URL(value);
|
|
126
|
+
for (const key of [...url.searchParams.keys()]) {
|
|
127
|
+
if (SECRETISH.test(key)) url.searchParams.set(key, '<redacted>');
|
|
128
|
+
}
|
|
129
|
+
return url.toString();
|
|
130
|
+
} catch {
|
|
131
|
+
return value.replace(/([?&\s](?:token|secret|password|authorization|credential|api[-_]?key)=)[^\s&]+/gi, '$1<redacted>');
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function slug(value: string): string {
|
|
136
|
+
const normalized = value.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '');
|
|
137
|
+
return normalized.slice(0, 64) || 'research-report';
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function failure(error: string): { readonly success: false; readonly error: string } {
|
|
141
|
+
return { success: false, error };
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function output(text: string): { readonly success: true; readonly output: string } {
|
|
145
|
+
return { success: true, output: text };
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function routeString(value: string): string {
|
|
149
|
+
return JSON.stringify(value);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function preview(args: AgentResearchReportToolArgs, sources: readonly ResearchSource[]): string {
|
|
153
|
+
return [
|
|
154
|
+
'Agent research report preview',
|
|
155
|
+
` title ${readString(args.title) || '(missing)'}`,
|
|
156
|
+
` question ${readString(args.question) || '(missing)'}`,
|
|
157
|
+
` sources ${sources.length}`,
|
|
158
|
+
` confidence ${readString(args.confidence) || '(unspecified)'}`,
|
|
159
|
+
' policy saving a sourced report artifact requires confirm:true',
|
|
160
|
+
].join('\n');
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function listSection(title: string, items: readonly string[]): string {
|
|
164
|
+
if (items.length === 0) return '';
|
|
165
|
+
return [`## ${title}`, '', ...items.map((item) => `- ${item}`), ''].join('\n');
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function markdownCell(value: string): string {
|
|
169
|
+
const text = value.replace(/\s+/g, ' ').trim();
|
|
170
|
+
return (text || '(none)').replace(/\|/g, '\\|');
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function citationIdsInText(value: string): readonly string[] {
|
|
174
|
+
const ids = new Set<string>();
|
|
175
|
+
for (const match of value.matchAll(/\[S(\d+)\]/gi)) ids.add(`S${Number(match[1] ?? 0)}`);
|
|
176
|
+
return Array.from(ids).sort((left, right) => left.localeCompare(right, undefined, { numeric: true }));
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function evidenceLabel(value: string): string {
|
|
180
|
+
return citationIdsInText(value).join(', ') || 'needs citation';
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function visualReportSection(
|
|
184
|
+
args: AgentResearchReportToolArgs,
|
|
185
|
+
sources: readonly ResearchSource[],
|
|
186
|
+
coverage: CitationCoverage,
|
|
187
|
+
): string {
|
|
188
|
+
const summary = readString(args.summary);
|
|
189
|
+
const findings = readList(args.findings);
|
|
190
|
+
const gaps = readList(args.gaps);
|
|
191
|
+
const recommendations = readList(args.recommendations);
|
|
192
|
+
const datedSources = sources
|
|
193
|
+
.map((source, index) => ({ id: `S${index + 1}`, source, date: source.publishedAt || source.accessedAt || '' }))
|
|
194
|
+
.filter((entry) => entry.date);
|
|
195
|
+
const sourceUse = new Set(coverage.citedSourceIds);
|
|
196
|
+
const lines = [
|
|
197
|
+
'## Visual Report Packet',
|
|
198
|
+
'',
|
|
199
|
+
'### At A Glance',
|
|
200
|
+
'',
|
|
201
|
+
`- Answer: ${summary ? previewText(summary) : '(summary not provided)'}`,
|
|
202
|
+
`- Confidence: ${readString(args.confidence) || 'unspecified'}`,
|
|
203
|
+
`- Sources: ${coverage.sourceCount}`,
|
|
204
|
+
`- Citation coverage: ${coverage.citedSourceIds.length}/${coverage.sourceCount} cited`,
|
|
205
|
+
`- Needs repair: ${coverage.repairSuggestions.join(' ') || 'none'}`,
|
|
206
|
+
'',
|
|
207
|
+
'### Evidence Matrix',
|
|
208
|
+
'',
|
|
209
|
+
'| Source | Credibility | Publisher | Evidence Note | Report Use |',
|
|
210
|
+
'| --- | --- | --- | --- | --- |',
|
|
211
|
+
...sources.map((source, index) => {
|
|
212
|
+
const id = `S${index + 1}`;
|
|
213
|
+
return [
|
|
214
|
+
`| ${markdownCell(`${id} ${source.title}`)}`,
|
|
215
|
+
markdownCell(source.credibility),
|
|
216
|
+
markdownCell(source.publisher ?? ''),
|
|
217
|
+
markdownCell(previewText(source.note ?? '', 180)),
|
|
218
|
+
`${sourceUse.has(id) ? 'cited in body' : 'needs body citation'} |`,
|
|
219
|
+
].join(' | ');
|
|
220
|
+
}),
|
|
221
|
+
'',
|
|
222
|
+
];
|
|
223
|
+
if (findings.length > 0) {
|
|
224
|
+
lines.push(
|
|
225
|
+
'### Findings Board',
|
|
226
|
+
'',
|
|
227
|
+
'| Finding | Evidence |',
|
|
228
|
+
'| --- | --- |',
|
|
229
|
+
...findings.map((finding) => `| ${markdownCell(finding)} | ${markdownCell(evidenceLabel(finding))} |`),
|
|
230
|
+
'',
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
if (datedSources.length > 0) {
|
|
234
|
+
lines.push(
|
|
235
|
+
'### Dated Sources',
|
|
236
|
+
'',
|
|
237
|
+
'| Date | Source | Use |',
|
|
238
|
+
'| --- | --- | --- |',
|
|
239
|
+
...datedSources.map((entry) => `| ${markdownCell(entry.date)} | ${markdownCell(`${entry.id} ${entry.source.title}`)} | ${markdownCell(sourceUse.has(entry.id) ? 'cited' : 'needs citation')} |`),
|
|
240
|
+
'',
|
|
241
|
+
);
|
|
242
|
+
} else {
|
|
243
|
+
lines.push(
|
|
244
|
+
'### Dated Sources',
|
|
245
|
+
'',
|
|
246
|
+
'No dated sources were provided. Treat this as a comparison packet rather than a chronology.',
|
|
247
|
+
'',
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
if (gaps.length > 0) lines.push('### Open Questions', '', ...gaps.map((gap) => `- ${gap}`), '');
|
|
251
|
+
if (recommendations.length > 0) {
|
|
252
|
+
lines.push(
|
|
253
|
+
'### Next Actions',
|
|
254
|
+
'',
|
|
255
|
+
'| Action | Evidence |',
|
|
256
|
+
'| --- | --- |',
|
|
257
|
+
...recommendations.map((recommendation) => `| ${markdownCell(recommendation)} | ${markdownCell(evidenceLabel(recommendation))} |`),
|
|
258
|
+
'',
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
lines.push(
|
|
262
|
+
'### Handoff Checklist',
|
|
263
|
+
'',
|
|
264
|
+
'- Review citation coverage and repair suggestions before sharing.',
|
|
265
|
+
'- Inspect the saved artifact with `agent_artifacts mode:"show" includeContent:true`.',
|
|
266
|
+
'- Archive the report with related research source artifacts through `agent_artifacts mode:"archive"`.',
|
|
267
|
+
'- Promote to Knowledge only through a separate confirmed `agent_knowledge_ingest sourceKind:"artifact"` call.',
|
|
268
|
+
'',
|
|
269
|
+
);
|
|
270
|
+
return lines.join('\n');
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function sourceMapSection(sources: readonly ResearchSource[]): string {
|
|
274
|
+
const lines = ['## Source Map', ''];
|
|
275
|
+
sources.forEach((source, index) => {
|
|
276
|
+
const id = `S${index + 1}`;
|
|
277
|
+
lines.push(`- [${id}] ${source.title}`);
|
|
278
|
+
if (source.url) lines.push(` - URL: ${source.url}`);
|
|
279
|
+
if (source.publisher) lines.push(` - Publisher: ${source.publisher}`);
|
|
280
|
+
if (source.publishedAt) lines.push(` - Published: ${source.publishedAt}`);
|
|
281
|
+
if (source.accessedAt) lines.push(` - Accessed: ${source.accessedAt}`);
|
|
282
|
+
lines.push(` - Credibility: ${source.credibility}`);
|
|
283
|
+
if (source.note) lines.push(` - Note: ${source.note}`);
|
|
284
|
+
});
|
|
285
|
+
lines.push('');
|
|
286
|
+
return lines.join('\n');
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
function reportBodyText(args: AgentResearchReportToolArgs): string {
|
|
290
|
+
return [
|
|
291
|
+
readString(args.summary),
|
|
292
|
+
readString(args.reportMarkdown),
|
|
293
|
+
...readList(args.findings),
|
|
294
|
+
...readList(args.gaps),
|
|
295
|
+
...readList(args.recommendations),
|
|
296
|
+
readString(args.methodology),
|
|
297
|
+
].filter(Boolean).join('\n');
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
function citationCoverage(args: AgentResearchReportToolArgs, sources: readonly ResearchSource[]): CitationCoverage {
|
|
301
|
+
const sourceIds = sources.map((_, index) => `S${index + 1}`);
|
|
302
|
+
const valid = new Set(sourceIds);
|
|
303
|
+
const cited = new Set<string>();
|
|
304
|
+
const unknown = new Set<string>();
|
|
305
|
+
for (const match of reportBodyText(args).matchAll(/\[S(\d+)\]/gi)) {
|
|
306
|
+
const id = `S${Number(match[1] ?? 0)}`;
|
|
307
|
+
if (valid.has(id)) cited.add(id);
|
|
308
|
+
else unknown.add(id);
|
|
309
|
+
}
|
|
310
|
+
const citedSourceIds = sourceIds.filter((id) => cited.has(id));
|
|
311
|
+
const missingSourceIds = sourceIds.filter((id) => !cited.has(id));
|
|
312
|
+
const unknownCitationIds = Array.from(unknown).sort((left, right) => left.localeCompare(right, undefined, { numeric: true }));
|
|
313
|
+
const sourceTitleById = new Map(sourceIds.map((id, index) => [id, sources[index]?.title ?? id]));
|
|
314
|
+
const validRange = sourceIds.length > 1 ? `${sourceIds[0]}-${sourceIds[sourceIds.length - 1]}` : sourceIds[0] ?? '(none)';
|
|
315
|
+
const repairSuggestions = [
|
|
316
|
+
...missingSourceIds.map((id) => `Add body citation for ${id} (${sourceTitleById.get(id) ?? id}).`),
|
|
317
|
+
...unknownCitationIds.map((id) => `Replace or remove unknown citation ${id}. Valid source ids are ${validRange}.`),
|
|
318
|
+
];
|
|
319
|
+
return {
|
|
320
|
+
sourceCount: sourceIds.length,
|
|
321
|
+
citedSourceIds,
|
|
322
|
+
missingSourceIds,
|
|
323
|
+
unknownCitationIds,
|
|
324
|
+
repairSuggestions,
|
|
325
|
+
coverageRatio: sourceIds.length > 0 ? Number((citedSourceIds.length / sourceIds.length).toFixed(3)) : 1,
|
|
326
|
+
pass: missingSourceIds.length === 0 && unknownCitationIds.length === 0,
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
function citationCoverageSection(coverage: CitationCoverage): string {
|
|
331
|
+
return [
|
|
332
|
+
'## Citation Coverage',
|
|
333
|
+
'',
|
|
334
|
+
`- Sources: ${coverage.sourceCount}`,
|
|
335
|
+
`- Cited in body: ${coverage.citedSourceIds.join(', ') || '(none)'}`,
|
|
336
|
+
`- Uncited in body: ${coverage.missingSourceIds.join(', ') || '(none)'}`,
|
|
337
|
+
`- Unknown citations: ${coverage.unknownCitationIds.join(', ') || '(none)'}`,
|
|
338
|
+
`- Repair suggestions: ${coverage.repairSuggestions.join(' ') || '(none)'}`,
|
|
339
|
+
'',
|
|
340
|
+
].join('\n');
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
function buildMarkdown(args: AgentResearchReportToolArgs, sources: readonly ResearchSource[], includeVisualReport: boolean): string {
|
|
344
|
+
const title = readString(args.title);
|
|
345
|
+
const question = readString(args.question);
|
|
346
|
+
const summary = readString(args.summary);
|
|
347
|
+
const reportMarkdown = readString(args.reportMarkdown);
|
|
348
|
+
const findings = readList(args.findings);
|
|
349
|
+
const gaps = readList(args.gaps);
|
|
350
|
+
const recommendations = readList(args.recommendations);
|
|
351
|
+
const methodology = readString(args.methodology);
|
|
352
|
+
const confidence = readString(args.confidence);
|
|
353
|
+
const coverage = citationCoverage(args, sources);
|
|
354
|
+
const generatedAt = new Date().toISOString();
|
|
355
|
+
const lines = [
|
|
356
|
+
`# ${title}`,
|
|
357
|
+
'',
|
|
358
|
+
`Question: ${question}`,
|
|
359
|
+
`Generated: ${generatedAt}`,
|
|
360
|
+
`Confidence: ${confidence || 'unspecified'}`,
|
|
361
|
+
'',
|
|
362
|
+
];
|
|
363
|
+
if (summary) lines.push('## Summary', '', summary, '');
|
|
364
|
+
if (reportMarkdown) lines.push('## Report', '', reportMarkdown, '');
|
|
365
|
+
const findingSection = listSection('Findings', findings);
|
|
366
|
+
if (findingSection) lines.push(findingSection);
|
|
367
|
+
const gapSection = listSection('Gaps And Caveats', gaps);
|
|
368
|
+
if (gapSection) lines.push(gapSection);
|
|
369
|
+
const recommendationSection = listSection('Recommendations', recommendations);
|
|
370
|
+
if (recommendationSection) lines.push(recommendationSection);
|
|
371
|
+
if (methodology) lines.push('## Method', '', methodology, '');
|
|
372
|
+
lines.push(citationCoverageSection(coverage));
|
|
373
|
+
if (includeVisualReport) lines.push(visualReportSection(args, sources, coverage));
|
|
374
|
+
lines.push(sourceMapSection(sources));
|
|
375
|
+
return lines.join('\n').replace(/\n{4,}/g, '\n\n\n').trimEnd() + '\n';
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
function sourceMetadata(sources: readonly ResearchSource[]): readonly Record<string, unknown>[] {
|
|
379
|
+
return sources.map((source, index) => ({
|
|
380
|
+
id: `S${index + 1}`,
|
|
381
|
+
title: source.title,
|
|
382
|
+
...(source.url ? { url: source.url } : {}),
|
|
383
|
+
...(source.publisher ? { publisher: source.publisher } : {}),
|
|
384
|
+
...(source.publishedAt ? { publishedAt: source.publishedAt } : {}),
|
|
385
|
+
...(source.accessedAt ? { accessedAt: source.accessedAt } : {}),
|
|
386
|
+
credibility: source.credibility,
|
|
387
|
+
...(source.note ? { note: source.note } : {}),
|
|
388
|
+
}));
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
function visualReportMetadata(
|
|
392
|
+
args: AgentResearchReportToolArgs,
|
|
393
|
+
sources: readonly ResearchSource[],
|
|
394
|
+
coverage: CitationCoverage,
|
|
395
|
+
): Record<string, unknown> {
|
|
396
|
+
const findingCount = readList(args.findings).length;
|
|
397
|
+
const gapCount = readList(args.gaps).length;
|
|
398
|
+
const recommendationCount = readList(args.recommendations).length;
|
|
399
|
+
return {
|
|
400
|
+
format: 'markdown-visual-report-packet',
|
|
401
|
+
sections: [
|
|
402
|
+
'at-a-glance',
|
|
403
|
+
'evidence-matrix',
|
|
404
|
+
...(findingCount > 0 ? ['findings-board'] : []),
|
|
405
|
+
'dated-sources-or-comparison',
|
|
406
|
+
'citation-coverage',
|
|
407
|
+
'source-map',
|
|
408
|
+
...(gapCount > 0 ? ['open-questions'] : []),
|
|
409
|
+
...(recommendationCount > 0 ? ['next-actions'] : []),
|
|
410
|
+
'handoff-checklist',
|
|
411
|
+
],
|
|
412
|
+
sourceCount: sources.length,
|
|
413
|
+
findingCount,
|
|
414
|
+
gapCount,
|
|
415
|
+
recommendationCount,
|
|
416
|
+
datedSourceCount: sources.filter((source) => source.publishedAt || source.accessedAt).length,
|
|
417
|
+
citationCoveragePass: coverage.pass,
|
|
418
|
+
missingSourceIds: coverage.missingSourceIds,
|
|
419
|
+
unknownCitationIds: coverage.unknownCitationIds,
|
|
420
|
+
routes: {
|
|
421
|
+
inspect: 'agent_artifacts mode:"show" artifactId:"..." includeContent:true',
|
|
422
|
+
archive: 'agent_artifacts mode:"archive" artifactIds:["..."] destinationPath:"exports/research-report.zip" confirm:true explicitUserRequest:"..."',
|
|
423
|
+
promoteKnowledge: 'agent_knowledge_ingest sourceKind:"artifact" artifactId:"..." confirm:true explicitUserRequest:"..."',
|
|
424
|
+
},
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
function runIdFromArgs(args: AgentResearchReportToolArgs): string {
|
|
429
|
+
return readString(args.runId) || readString(args.id);
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
function reportNextRouteLines(descriptor: ArtifactDescriptor, args: AgentResearchReportToolArgs): readonly string[] {
|
|
433
|
+
const filename = descriptor.filename ?? `${descriptor.id}.md`;
|
|
434
|
+
const title = readString(args.title);
|
|
435
|
+
const runId = runIdFromArgs(args);
|
|
436
|
+
return [
|
|
437
|
+
` inspect research action:"report_artifact" artifactId:${routeString(descriptor.id)}`,
|
|
438
|
+
` artifact agent_artifacts mode:"show" artifactId:${routeString(descriptor.id)} includeContent:true`,
|
|
439
|
+
` export agent_artifacts mode:"export" artifactId:${routeString(descriptor.id)} destinationPath:${routeString(`exports/${filename}`)} confirm:true explicitUserRequest:"..."`,
|
|
440
|
+
` archive agent_artifacts mode:"archive" artifactIds:[${routeString(descriptor.id)}] destinationPath:"exports/research-report.zip" confirm:true explicitUserRequest:"..."`,
|
|
441
|
+
` promoteKnowledge agent_knowledge_ingest sourceKind:"artifact" artifactId:${routeString(descriptor.id)} confirm:true explicitUserRequest:"..."`,
|
|
442
|
+
...(runId ? [
|
|
443
|
+
` completeRun research action:"complete" id:${routeString(runId)} reportArtifactId:${routeString(descriptor.id)} confirm:true explicitUserRequest:"..."`,
|
|
444
|
+
] : []),
|
|
445
|
+
` reports research action:"reports"${title ? ` query:${routeString(title)}` : ''}`,
|
|
446
|
+
];
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
async function saveResearchReport(
|
|
450
|
+
artifactStore: AgentResearchReportArtifactStore,
|
|
451
|
+
args: AgentResearchReportToolArgs,
|
|
452
|
+
): Promise<ArtifactDescriptor> {
|
|
453
|
+
const title = readString(args.title);
|
|
454
|
+
const question = readString(args.question);
|
|
455
|
+
if (!title) throw new Error('title is required.');
|
|
456
|
+
if (!question) throw new Error('question is required.');
|
|
457
|
+
const sources = readSources(args.sources);
|
|
458
|
+
if (sources.length === 0) throw new Error('At least one reviewed source is required for a research report artifact.');
|
|
459
|
+
if (!readString(args.summary) && !readString(args.reportMarkdown) && readList(args.findings).length === 0) {
|
|
460
|
+
throw new Error('summary, reportMarkdown, or findings are required.');
|
|
461
|
+
}
|
|
462
|
+
const explicitUserRequest = readString(args.explicitUserRequest);
|
|
463
|
+
if (!explicitUserRequest) throw new Error('explicitUserRequest is required so report export stays user-directed.');
|
|
464
|
+
if (!readBoolean(args.confirm)) {
|
|
465
|
+
throw new Error([
|
|
466
|
+
preview(args, sources),
|
|
467
|
+
'',
|
|
468
|
+
'Model tool confirmation required. Call this tool with confirm:true only after the user explicitly asked GoodVibes Agent to save this research report.',
|
|
469
|
+
].join('\n'));
|
|
470
|
+
}
|
|
471
|
+
const coverage = citationCoverage(args, sources);
|
|
472
|
+
if (readBoolean(args.requireCitationCoverage) && !coverage.pass) {
|
|
473
|
+
throw new Error([
|
|
474
|
+
`Citation coverage check failed. Missing body citations: ${coverage.missingSourceIds.join(', ') || '(none)'}. Unknown citations: ${coverage.unknownCitationIds.join(', ') || '(none)'}.`,
|
|
475
|
+
`Repair suggestions: ${coverage.repairSuggestions.join(' ') || '(none)'}`,
|
|
476
|
+
].join('\n'));
|
|
477
|
+
}
|
|
478
|
+
const includeVisualReport = readBoolean(args.visualReport);
|
|
479
|
+
const markdown = buildMarkdown(args, sources, includeVisualReport);
|
|
480
|
+
if (markdown.length > MAX_REPORT_CHARS) throw new Error(`Research report is too large (${markdown.length} chars). Keep it under ${MAX_REPORT_CHARS}.`);
|
|
481
|
+
return artifactStore.create({
|
|
482
|
+
kind: 'document',
|
|
483
|
+
mimeType: 'text/markdown',
|
|
484
|
+
filename: `${slug(title)}.md`,
|
|
485
|
+
text: markdown,
|
|
486
|
+
metadata: {
|
|
487
|
+
purpose: 'agent-research-report',
|
|
488
|
+
source: 'agent-research-report',
|
|
489
|
+
title,
|
|
490
|
+
question,
|
|
491
|
+
confidence: readString(args.confidence) || 'unspecified',
|
|
492
|
+
tags: readTags(args.tags),
|
|
493
|
+
sourceCount: sources.length,
|
|
494
|
+
sources: sourceMetadata(sources),
|
|
495
|
+
citationCoverage: coverage,
|
|
496
|
+
...(includeVisualReport ? { visualReport: visualReportMetadata(args, sources, coverage) } : {}),
|
|
497
|
+
explicitUserRequest,
|
|
498
|
+
},
|
|
499
|
+
});
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
function coverageFromMetadata(metadata: ArtifactDescriptor['metadata']): CitationCoverage | null {
|
|
503
|
+
if (!metadata || typeof metadata !== 'object' || Array.isArray(metadata)) return null;
|
|
504
|
+
const value = (metadata as Record<string, unknown>).citationCoverage;
|
|
505
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) return null;
|
|
506
|
+
const record = value as Record<string, unknown>;
|
|
507
|
+
if (typeof record.sourceCount !== 'number') return null;
|
|
508
|
+
return {
|
|
509
|
+
sourceCount: record.sourceCount,
|
|
510
|
+
citedSourceIds: Array.isArray(record.citedSourceIds) ? record.citedSourceIds.filter((entry): entry is string => typeof entry === 'string') : [],
|
|
511
|
+
missingSourceIds: Array.isArray(record.missingSourceIds) ? record.missingSourceIds.filter((entry): entry is string => typeof entry === 'string') : [],
|
|
512
|
+
unknownCitationIds: Array.isArray(record.unknownCitationIds) ? record.unknownCitationIds.filter((entry): entry is string => typeof entry === 'string') : [],
|
|
513
|
+
repairSuggestions: Array.isArray(record.repairSuggestions) ? record.repairSuggestions.filter((entry): entry is string => typeof entry === 'string') : [],
|
|
514
|
+
coverageRatio: typeof record.coverageRatio === 'number' ? record.coverageRatio : 0,
|
|
515
|
+
pass: record.pass === true,
|
|
516
|
+
};
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
export function createAgentResearchReportTool(
|
|
520
|
+
artifactStore?: AgentResearchReportArtifactStore,
|
|
521
|
+
): Tool {
|
|
522
|
+
return {
|
|
523
|
+
definition: {
|
|
524
|
+
name: 'agent_research_report',
|
|
525
|
+
description: 'Save one confirmed sourced research report artifact.',
|
|
526
|
+
parameters: {
|
|
527
|
+
type: 'object',
|
|
528
|
+
properties: {
|
|
529
|
+
runId: { type: 'string', description: 'Optional visible research run id to complete after saving the artifact.' },
|
|
530
|
+
id: { type: 'string', description: 'Alias for runId when saving through the high-level research adapter.' },
|
|
531
|
+
title: { type: 'string', description: 'Report title.' },
|
|
532
|
+
question: { type: 'string', description: 'Research question answered by the report.' },
|
|
533
|
+
summary: { type: 'string', description: 'Short executive summary.' },
|
|
534
|
+
reportMarkdown: { type: 'string', description: 'Full report markdown.' },
|
|
535
|
+
sources: {
|
|
536
|
+
type: 'array',
|
|
537
|
+
items: { type: 'object' },
|
|
538
|
+
description: 'Reviewed sources with title, url, and credibility.',
|
|
539
|
+
},
|
|
540
|
+
findings: {
|
|
541
|
+
type: 'array',
|
|
542
|
+
items: { type: 'string' },
|
|
543
|
+
description: 'Key findings.',
|
|
544
|
+
},
|
|
545
|
+
gaps: {
|
|
546
|
+
type: 'array',
|
|
547
|
+
items: { type: 'string' },
|
|
548
|
+
description: 'Open questions or caveats.',
|
|
549
|
+
},
|
|
550
|
+
recommendations: {
|
|
551
|
+
type: 'array',
|
|
552
|
+
items: { type: 'string' },
|
|
553
|
+
description: 'Recommended next actions.',
|
|
554
|
+
},
|
|
555
|
+
methodology: { type: 'string', description: 'How sources were selected and judged.' },
|
|
556
|
+
confidence: { type: 'string', description: 'Overall confidence label.' },
|
|
557
|
+
requireCitationCoverage: { type: 'boolean', description: 'Fail save unless all sources are cited in body.' },
|
|
558
|
+
visualReport: { type: 'boolean', description: 'Append a visual report packet section.' },
|
|
559
|
+
tags: {
|
|
560
|
+
type: 'array',
|
|
561
|
+
items: { type: 'string' },
|
|
562
|
+
description: 'Optional report tags.',
|
|
563
|
+
},
|
|
564
|
+
confirm: { type: 'boolean', description: 'Required true to save the report.' },
|
|
565
|
+
explicitUserRequest: { type: 'string', description: 'User request authorizing report save.' },
|
|
566
|
+
},
|
|
567
|
+
required: ['title', 'question', 'sources', 'confirm', 'explicitUserRequest'],
|
|
568
|
+
additionalProperties: false,
|
|
569
|
+
},
|
|
570
|
+
sideEffects: ['state'],
|
|
571
|
+
concurrency: 'serial',
|
|
572
|
+
},
|
|
573
|
+
execute: async (rawArgs: Record<string, unknown>) => {
|
|
574
|
+
if (!artifactStore?.create) return failure('Research report export is unavailable because this runtime did not provide an artifact store.');
|
|
575
|
+
const args = rawArgs as AgentResearchReportToolArgs;
|
|
576
|
+
try {
|
|
577
|
+
const descriptor = await saveResearchReport(artifactStore, args);
|
|
578
|
+
const coverage = coverageFromMetadata(descriptor.metadata);
|
|
579
|
+
const lines = [
|
|
580
|
+
'Saved Agent research report artifact',
|
|
581
|
+
` artifact ${descriptor.id}`,
|
|
582
|
+
` filename ${descriptor.filename ?? '(none)'}`,
|
|
583
|
+
` bytes ${descriptor.sizeBytes}`,
|
|
584
|
+
` mime ${descriptor.mimeType}`,
|
|
585
|
+
` sources ${readSources(args.sources).length}`,
|
|
586
|
+
readBoolean(args.visualReport) ? ' visualReport markdown-visual-report-packet' : '',
|
|
587
|
+
coverage ? ` citationCoverage ${coverage.citedSourceIds.length}/${coverage.sourceCount} cited; uncited ${coverage.missingSourceIds.length}; unknown ${coverage.unknownCitationIds.length}` : '',
|
|
588
|
+
coverage && coverage.repairSuggestions.length > 0 ? ` citationRepair ${coverage.repairSuggestions.join(' ')}` : '',
|
|
589
|
+
` sha256 ${descriptor.sha256}`,
|
|
590
|
+
' nextRoutes',
|
|
591
|
+
...reportNextRouteLines(descriptor, args),
|
|
592
|
+
' policy sourced markdown saved as artifact; content not printed',
|
|
593
|
+
` inspect agent_artifacts mode:"show" artifactId:"${descriptor.id}" includeContent:true`,
|
|
594
|
+
].filter(Boolean);
|
|
595
|
+
return output(lines.join('\n'));
|
|
596
|
+
} catch (error) {
|
|
597
|
+
return failure(error instanceof Error ? error.message : String(error));
|
|
598
|
+
}
|
|
599
|
+
},
|
|
600
|
+
};
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
export function registerAgentResearchReportTool(
|
|
604
|
+
registry: ToolRegistry,
|
|
605
|
+
artifactStore?: AgentResearchReportArtifactStore,
|
|
606
|
+
): void {
|
|
607
|
+
registry.register(createAgentResearchReportTool(artifactStore));
|
|
608
|
+
}
|