@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,502 @@
|
|
|
1
|
+
import { appendFileSync, existsSync, mkdirSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { createHash } from 'node:crypto';
|
|
3
|
+
import { dirname } from 'node:path';
|
|
4
|
+
import type { MemoryRecord, MemoryRegistry } from '@pellux/goodvibes-sdk/platform/state';
|
|
5
|
+
import { getTierForContextWindow, getTierPromptSupplement } from '@pellux/goodvibes-sdk/platform/providers';
|
|
6
|
+
import type { ShellPathService } from '@/runtime/index.ts';
|
|
7
|
+
import { buildReviewedMemoryPrompt, isPromptActiveMemory, MIN_PROMPT_MEMORY_CONFIDENCE } from './memory-prompt.ts';
|
|
8
|
+
import { AgentPersonaRegistry, buildActivePersonaPrompt } from './persona-registry.ts';
|
|
9
|
+
import { buildProjectContextPrompt, discoverProjectContextFiles } from './project-context-files.ts';
|
|
10
|
+
import { AgentRoutineRegistry, buildEnabledRoutinesPrompt, evaluateAgentRoutineReadiness } from './routine-registry.ts';
|
|
11
|
+
import { AgentSkillRegistry, buildEnabledSkillsPrompt, evaluateAgentSkillBundleReadiness, evaluateAgentSkillReadiness } from './skill-registry.ts';
|
|
12
|
+
import { buildVibePrompt, discoverVibeFiles } from './vibe-file.ts';
|
|
13
|
+
|
|
14
|
+
export type PromptContextReceiptStatus = 'active' | 'attention' | 'empty' | 'unavailable';
|
|
15
|
+
export type PromptContextReceiptSource = 'turn' | 'follow_up' | 'manual';
|
|
16
|
+
export type PromptContextTurnOutcomeStatus = 'completed' | 'error' | 'cancelled';
|
|
17
|
+
export type PromptContextTurnTerminalEvent = 'TURN_COMPLETED' | 'TURN_ERROR' | 'TURN_CANCEL';
|
|
18
|
+
|
|
19
|
+
export interface PromptContextTurnOutcome {
|
|
20
|
+
readonly turnId: string;
|
|
21
|
+
readonly status: PromptContextTurnOutcomeStatus;
|
|
22
|
+
readonly terminalEvent: PromptContextTurnTerminalEvent;
|
|
23
|
+
readonly stopReason: string;
|
|
24
|
+
readonly completedAt: number;
|
|
25
|
+
readonly receiptIds: readonly string[];
|
|
26
|
+
readonly detail?: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface PromptContextReceiptSegment {
|
|
30
|
+
readonly id: string;
|
|
31
|
+
readonly label: string;
|
|
32
|
+
readonly order: number;
|
|
33
|
+
readonly status: PromptContextReceiptStatus;
|
|
34
|
+
readonly activeCount: number;
|
|
35
|
+
readonly suppressedCount: number;
|
|
36
|
+
readonly promptChars: number;
|
|
37
|
+
readonly approxTokens: number;
|
|
38
|
+
readonly selected?: readonly Record<string, unknown>[];
|
|
39
|
+
readonly suppressed?: readonly Record<string, unknown>[];
|
|
40
|
+
readonly note?: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface PromptContextReceiptDraft {
|
|
44
|
+
readonly sessionId: string;
|
|
45
|
+
readonly turnId: string | null;
|
|
46
|
+
readonly source: PromptContextReceiptSource;
|
|
47
|
+
readonly provider: string;
|
|
48
|
+
readonly model: string;
|
|
49
|
+
readonly contextWindow: number;
|
|
50
|
+
readonly promptHash: string;
|
|
51
|
+
readonly promptChars: number;
|
|
52
|
+
readonly approxPromptTokens: number;
|
|
53
|
+
readonly activeRecords: number;
|
|
54
|
+
readonly suppressedRecords: number;
|
|
55
|
+
readonly segments: readonly PromptContextReceiptSegment[];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export interface PromptContextReceipt extends PromptContextReceiptDraft {
|
|
59
|
+
readonly receiptId: string;
|
|
60
|
+
readonly createdAt: number;
|
|
61
|
+
readonly sequence: number;
|
|
62
|
+
readonly turnOutcome?: PromptContextTurnOutcome;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export interface RuntimePromptCompositionInput {
|
|
66
|
+
readonly sessionId: string;
|
|
67
|
+
readonly turnId?: string | null;
|
|
68
|
+
readonly source?: PromptContextReceiptSource;
|
|
69
|
+
readonly provider: string;
|
|
70
|
+
readonly model: unknown;
|
|
71
|
+
readonly contextWindow: number;
|
|
72
|
+
readonly runtimePrompt: string;
|
|
73
|
+
readonly operatorPolicy: string;
|
|
74
|
+
readonly shellPaths: ShellPathService;
|
|
75
|
+
readonly memoryRegistry: MemoryRegistry;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const RECEIPT_STORE_LIMIT = 200;
|
|
79
|
+
const OUTCOME_DETAIL_LIMIT = 240;
|
|
80
|
+
|
|
81
|
+
function approxTokens(text: string): number {
|
|
82
|
+
return Math.ceil(text.length / 4);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function approxTokensForChars(chars: number): number {
|
|
86
|
+
return Math.ceil(chars / 4);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function promptHash(text: string): string {
|
|
90
|
+
return createHash('sha256').update(text).digest('hex');
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function compactOutcomeDetail(value: string | undefined): string | undefined {
|
|
94
|
+
const trimmed = value?.replace(/\s+/g, ' ').trim();
|
|
95
|
+
if (!trimmed) return undefined;
|
|
96
|
+
return trimmed.length > OUTCOME_DETAIL_LIMIT ? `${trimmed.slice(0, OUTCOME_DETAIL_LIMIT - 3)}...` : trimmed;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function joinPromptParts(...parts: Array<string | null | undefined>): string {
|
|
100
|
+
return parts.map((part) => part?.trim()).filter((part): part is string => Boolean(part)).join('\n\n');
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function modelLabel(model: unknown): string {
|
|
104
|
+
if (typeof model === 'string' && model.trim()) return model.trim();
|
|
105
|
+
if (typeof model === 'object' && model !== null) {
|
|
106
|
+
const candidate = model as { readonly registryKey?: unknown; readonly id?: unknown; readonly modelId?: unknown };
|
|
107
|
+
for (const value of [candidate.registryKey, candidate.id, candidate.modelId]) {
|
|
108
|
+
if (typeof value === 'string' && value.trim()) return value.trim();
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return 'unknown';
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function statusFor(active: number, suppressed: number): PromptContextReceiptStatus {
|
|
115
|
+
if (suppressed > 0) return 'attention';
|
|
116
|
+
if (active > 0) return 'active';
|
|
117
|
+
return 'empty';
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function memorySort(left: MemoryRecord, right: MemoryRecord): number {
|
|
121
|
+
if (right.confidence !== left.confidence) return right.confidence - left.confidence;
|
|
122
|
+
return right.updatedAt - left.updatedAt;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function receiptSegment(input: Omit<PromptContextReceiptSegment, 'approxTokens'> & { readonly promptText?: string }): PromptContextReceiptSegment {
|
|
126
|
+
const promptChars = input.promptText?.length ?? input.promptChars;
|
|
127
|
+
return {
|
|
128
|
+
id: input.id,
|
|
129
|
+
label: input.label,
|
|
130
|
+
order: input.order,
|
|
131
|
+
status: input.status,
|
|
132
|
+
activeCount: input.activeCount,
|
|
133
|
+
suppressedCount: input.suppressedCount,
|
|
134
|
+
promptChars,
|
|
135
|
+
approxTokens: input.promptText === undefined ? approxTokensForChars(promptChars) : approxTokens(input.promptText),
|
|
136
|
+
...(input.selected ? { selected: input.selected } : {}),
|
|
137
|
+
...(input.suppressed ? { suppressed: input.suppressed } : {}),
|
|
138
|
+
...(input.note ? { note: input.note } : {}),
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function buildRuntimePromptReceiptSegments(input: RuntimePromptCompositionInput): readonly PromptContextReceiptSegment[] {
|
|
143
|
+
const vibe = discoverVibeFiles(input.shellPaths);
|
|
144
|
+
const vibePrompt = buildVibePrompt(input.shellPaths) ?? '';
|
|
145
|
+
const projectContext = discoverProjectContextFiles(input.shellPaths);
|
|
146
|
+
const projectContextPrompt = buildProjectContextPrompt(input.shellPaths) ?? '';
|
|
147
|
+
const memoryRecords = input.memoryRegistry.getAll();
|
|
148
|
+
const activeMemory = memoryRecords.filter(isPromptActiveMemory).sort(memorySort).slice(0, 10);
|
|
149
|
+
const activeMemoryIds = new Set(activeMemory.map((record) => record.id));
|
|
150
|
+
const suppressedMemory = memoryRecords.filter((record) => !activeMemoryIds.has(record.id));
|
|
151
|
+
const memoryPrompt = buildReviewedMemoryPrompt(input.memoryRegistry) ?? '';
|
|
152
|
+
const routineSnapshot = AgentRoutineRegistry.fromShellPaths(input.shellPaths).snapshot();
|
|
153
|
+
const activeRoutines = routineSnapshot.enabledRoutines.filter((routine) => routine.reviewState === 'reviewed' && evaluateAgentRoutineReadiness(routine).ready);
|
|
154
|
+
const suppressedRoutines = routineSnapshot.enabledRoutines.filter((routine) => !activeRoutines.some((active) => active.id === routine.id));
|
|
155
|
+
const routinePrompt = buildEnabledRoutinesPrompt(input.shellPaths) ?? '';
|
|
156
|
+
const skillSnapshot = AgentSkillRegistry.fromShellPaths(input.shellPaths).snapshot();
|
|
157
|
+
const activeBundles = skillSnapshot.enabledBundles.filter((bundle) => {
|
|
158
|
+
if (bundle.reviewState !== 'reviewed') return false;
|
|
159
|
+
const readiness = evaluateAgentSkillBundleReadiness(bundle, skillSnapshot.skills);
|
|
160
|
+
return readiness.ready && readiness.includedSkills.every((skill) => skill.reviewState === 'reviewed');
|
|
161
|
+
});
|
|
162
|
+
const activeSkillIds = new Set([
|
|
163
|
+
...skillSnapshot.enabledSkills.map((skill) => skill.id),
|
|
164
|
+
...activeBundles.flatMap((bundle) => bundle.skillIds),
|
|
165
|
+
]);
|
|
166
|
+
const activeSkills = skillSnapshot.skills.filter((skill) => activeSkillIds.has(skill.id) && skill.reviewState === 'reviewed' && evaluateAgentSkillReadiness(skill).ready);
|
|
167
|
+
const candidateSkillIds = new Set([
|
|
168
|
+
...skillSnapshot.enabledSkills.map((skill) => skill.id),
|
|
169
|
+
...skillSnapshot.enabledBundles.flatMap((bundle) => bundle.skillIds),
|
|
170
|
+
]);
|
|
171
|
+
const suppressedSkills = skillSnapshot.skills.filter((skill) => candidateSkillIds.has(skill.id) && !activeSkills.some((active) => active.id === skill.id));
|
|
172
|
+
const suppressedBundles = skillSnapshot.enabledBundles.filter((bundle) => !activeBundles.some((active) => active.id === bundle.id));
|
|
173
|
+
const skillPrompt = buildEnabledSkillsPrompt(input.shellPaths) ?? '';
|
|
174
|
+
const personaSnapshot = AgentPersonaRegistry.fromShellPaths(input.shellPaths).snapshot();
|
|
175
|
+
const activePersona = personaSnapshot.activePersona;
|
|
176
|
+
const personaPrompt = buildActivePersonaPrompt(input.shellPaths) ?? '';
|
|
177
|
+
const tier = getTierForContextWindow(input.contextWindow);
|
|
178
|
+
const tierPrompt = getTierPromptSupplement(tier);
|
|
179
|
+
|
|
180
|
+
return [
|
|
181
|
+
receiptSegment({
|
|
182
|
+
id: 'base_system_prompt',
|
|
183
|
+
label: 'Base system prompt',
|
|
184
|
+
order: 1,
|
|
185
|
+
status: input.runtimePrompt ? 'active' : 'empty',
|
|
186
|
+
activeCount: input.runtimePrompt ? 1 : 0,
|
|
187
|
+
suppressedCount: 0,
|
|
188
|
+
promptChars: input.runtimePrompt.length,
|
|
189
|
+
promptText: input.runtimePrompt,
|
|
190
|
+
}),
|
|
191
|
+
receiptSegment({
|
|
192
|
+
id: 'vibe',
|
|
193
|
+
label: 'VIBE.md personality',
|
|
194
|
+
order: 2,
|
|
195
|
+
status: statusFor(vibe.files.length, vibe.blocked.length + vibe.files.filter((file) => file.truncated).length),
|
|
196
|
+
activeCount: vibe.files.length,
|
|
197
|
+
suppressedCount: vibe.blocked.length,
|
|
198
|
+
promptChars: vibePrompt.length,
|
|
199
|
+
promptText: vibePrompt,
|
|
200
|
+
selected: vibe.files.map((file) => ({ path: file.path, scope: file.scope, truncated: file.truncated })),
|
|
201
|
+
suppressed: vibe.blocked.map((file) => ({ path: file.path, scope: file.scope, reason: file.reason })),
|
|
202
|
+
}),
|
|
203
|
+
receiptSegment({
|
|
204
|
+
id: 'project_context',
|
|
205
|
+
label: 'Project context files',
|
|
206
|
+
order: 3,
|
|
207
|
+
status: statusFor(projectContext.files.length, projectContext.blocked.length + projectContext.files.filter((file) => file.truncated).length),
|
|
208
|
+
activeCount: projectContext.files.length,
|
|
209
|
+
suppressedCount: projectContext.blocked.length,
|
|
210
|
+
promptChars: projectContextPrompt.length,
|
|
211
|
+
promptText: projectContextPrompt,
|
|
212
|
+
selected: projectContext.files.map((file) => ({ id: file.id, source: file.source, scope: file.scope, truncated: file.truncated })),
|
|
213
|
+
suppressed: projectContext.blocked.map((file) => ({ id: file.id, source: file.source, reason: file.reason })),
|
|
214
|
+
}),
|
|
215
|
+
receiptSegment({
|
|
216
|
+
id: 'operator_policy',
|
|
217
|
+
label: 'GoodVibes Agent operator policy',
|
|
218
|
+
order: 4,
|
|
219
|
+
status: input.operatorPolicy ? 'active' : 'empty',
|
|
220
|
+
activeCount: input.operatorPolicy ? 1 : 0,
|
|
221
|
+
suppressedCount: 0,
|
|
222
|
+
promptChars: input.operatorPolicy.length,
|
|
223
|
+
promptText: input.operatorPolicy,
|
|
224
|
+
}),
|
|
225
|
+
receiptSegment({
|
|
226
|
+
id: 'memory',
|
|
227
|
+
label: 'Reviewed memory',
|
|
228
|
+
order: 5,
|
|
229
|
+
status: statusFor(activeMemory.length, suppressedMemory.length),
|
|
230
|
+
activeCount: activeMemory.length,
|
|
231
|
+
suppressedCount: suppressedMemory.length,
|
|
232
|
+
promptChars: memoryPrompt.length,
|
|
233
|
+
promptText: memoryPrompt,
|
|
234
|
+
selected: activeMemory.map((record) => ({ id: record.id, scope: record.scope, class: record.cls, confidence: record.confidence })),
|
|
235
|
+
suppressed: suppressedMemory.slice(0, 12).map((record) => ({
|
|
236
|
+
id: record.id,
|
|
237
|
+
reviewState: record.reviewState,
|
|
238
|
+
confidence: record.confidence,
|
|
239
|
+
reason: record.reviewState !== 'reviewed'
|
|
240
|
+
? 'not reviewed'
|
|
241
|
+
: record.confidence < MIN_PROMPT_MEMORY_CONFIDENCE
|
|
242
|
+
? `confidence below ${MIN_PROMPT_MEMORY_CONFIDENCE}`
|
|
243
|
+
: 'outside prompt limit',
|
|
244
|
+
})),
|
|
245
|
+
}),
|
|
246
|
+
receiptSegment({
|
|
247
|
+
id: 'routines',
|
|
248
|
+
label: 'Reviewed setup-ready routines',
|
|
249
|
+
order: 6,
|
|
250
|
+
status: statusFor(activeRoutines.length, suppressedRoutines.length),
|
|
251
|
+
activeCount: activeRoutines.length,
|
|
252
|
+
suppressedCount: suppressedRoutines.length,
|
|
253
|
+
promptChars: routinePrompt.length,
|
|
254
|
+
promptText: routinePrompt,
|
|
255
|
+
selected: activeRoutines.map((routine) => ({ id: routine.id, name: routine.name })),
|
|
256
|
+
suppressed: suppressedRoutines.slice(0, 12).map((routine) => ({
|
|
257
|
+
id: routine.id,
|
|
258
|
+
reviewState: routine.reviewState,
|
|
259
|
+
missingRequirements: evaluateAgentRoutineReadiness(routine).missing.length,
|
|
260
|
+
})),
|
|
261
|
+
}),
|
|
262
|
+
receiptSegment({
|
|
263
|
+
id: 'skills',
|
|
264
|
+
label: 'Reviewed setup-ready skills and bundles',
|
|
265
|
+
order: 7,
|
|
266
|
+
status: statusFor(activeSkills.length + activeBundles.length, suppressedSkills.length + suppressedBundles.length),
|
|
267
|
+
activeCount: activeSkills.length + activeBundles.length,
|
|
268
|
+
suppressedCount: suppressedSkills.length + suppressedBundles.length,
|
|
269
|
+
promptChars: skillPrompt.length,
|
|
270
|
+
promptText: skillPrompt,
|
|
271
|
+
selected: [
|
|
272
|
+
...activeBundles.map((bundle) => ({ id: bundle.id, domain: 'skill_bundle', name: bundle.name })),
|
|
273
|
+
...activeSkills.map((skill) => ({ id: skill.id, domain: 'skill', name: skill.name })),
|
|
274
|
+
],
|
|
275
|
+
suppressed: [
|
|
276
|
+
...suppressedBundles.slice(0, 6).map((bundle) => ({ id: bundle.id, domain: 'skill_bundle', reviewState: bundle.reviewState })),
|
|
277
|
+
...suppressedSkills.slice(0, 12).map((skill) => ({
|
|
278
|
+
id: skill.id,
|
|
279
|
+
domain: 'skill',
|
|
280
|
+
reviewState: skill.reviewState,
|
|
281
|
+
missingRequirements: evaluateAgentSkillReadiness(skill).missing.length,
|
|
282
|
+
})),
|
|
283
|
+
],
|
|
284
|
+
}),
|
|
285
|
+
receiptSegment({
|
|
286
|
+
id: 'persona',
|
|
287
|
+
label: 'Active persona',
|
|
288
|
+
order: 8,
|
|
289
|
+
status: activePersona && activePersona.reviewState !== 'reviewed' ? 'attention' : activePersona ? 'active' : 'empty',
|
|
290
|
+
activeCount: activePersona?.reviewState === 'reviewed' ? 1 : 0,
|
|
291
|
+
suppressedCount: activePersona && activePersona.reviewState !== 'reviewed' ? 1 : 0,
|
|
292
|
+
promptChars: personaPrompt.length,
|
|
293
|
+
promptText: personaPrompt,
|
|
294
|
+
selected: activePersona?.reviewState === 'reviewed' ? [{ id: activePersona.id, name: activePersona.name }] : [],
|
|
295
|
+
suppressed: activePersona && activePersona.reviewState !== 'reviewed' ? [{ id: activePersona.id, name: activePersona.name, reviewState: activePersona.reviewState }] : [],
|
|
296
|
+
}),
|
|
297
|
+
receiptSegment({
|
|
298
|
+
id: 'context_window_supplement',
|
|
299
|
+
label: 'Context-window supplement',
|
|
300
|
+
order: 9,
|
|
301
|
+
status: tierPrompt ? 'active' : 'empty',
|
|
302
|
+
activeCount: tierPrompt ? 1 : 0,
|
|
303
|
+
suppressedCount: 0,
|
|
304
|
+
promptChars: tierPrompt.length,
|
|
305
|
+
promptText: tierPrompt,
|
|
306
|
+
note: `Model ${modelLabel(input.model)} has context window ${input.contextWindow}; tier ${tier}.`,
|
|
307
|
+
}),
|
|
308
|
+
];
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
export function composeRuntimePromptWithReceipt(input: RuntimePromptCompositionInput): { readonly prompt: string; readonly receipt: PromptContextReceiptDraft } {
|
|
312
|
+
const currentModel = modelLabel(input.model);
|
|
313
|
+
const tier = getTierForContextWindow(input.contextWindow);
|
|
314
|
+
const supplement = getTierPromptSupplement(tier);
|
|
315
|
+
const prompt = joinPromptParts(
|
|
316
|
+
input.runtimePrompt,
|
|
317
|
+
buildVibePrompt(input.shellPaths),
|
|
318
|
+
buildProjectContextPrompt(input.shellPaths),
|
|
319
|
+
input.operatorPolicy,
|
|
320
|
+
buildReviewedMemoryPrompt(input.memoryRegistry),
|
|
321
|
+
buildEnabledRoutinesPrompt(input.shellPaths),
|
|
322
|
+
buildEnabledSkillsPrompt(input.shellPaths),
|
|
323
|
+
buildActivePersonaPrompt(input.shellPaths),
|
|
324
|
+
supplement,
|
|
325
|
+
);
|
|
326
|
+
const segments = buildRuntimePromptReceiptSegments(input);
|
|
327
|
+
const activeRecords = segments.reduce((total, segment) => total + segment.activeCount, 0);
|
|
328
|
+
const suppressedRecords = segments.reduce((total, segment) => total + segment.suppressedCount, 0);
|
|
329
|
+
return {
|
|
330
|
+
prompt,
|
|
331
|
+
receipt: {
|
|
332
|
+
sessionId: input.sessionId,
|
|
333
|
+
turnId: input.turnId ?? null,
|
|
334
|
+
source: input.source ?? 'turn',
|
|
335
|
+
provider: input.provider,
|
|
336
|
+
model: currentModel,
|
|
337
|
+
contextWindow: input.contextWindow,
|
|
338
|
+
promptHash: promptHash(prompt),
|
|
339
|
+
promptChars: prompt.length,
|
|
340
|
+
approxPromptTokens: approxTokens(prompt),
|
|
341
|
+
activeRecords,
|
|
342
|
+
suppressedRecords,
|
|
343
|
+
segments,
|
|
344
|
+
},
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
function isReceipt(value: unknown): value is PromptContextReceipt {
|
|
349
|
+
if (!value || typeof value !== 'object') return false;
|
|
350
|
+
const candidate = value as { readonly receiptId?: unknown; readonly createdAt?: unknown; readonly sequence?: unknown };
|
|
351
|
+
return typeof candidate.receiptId === 'string' && typeof candidate.createdAt === 'number' && typeof candidate.sequence === 'number';
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
function isTurnOutcome(value: unknown): value is PromptContextTurnOutcome {
|
|
355
|
+
if (!value || typeof value !== 'object') return false;
|
|
356
|
+
const candidate = value as {
|
|
357
|
+
readonly turnId?: unknown;
|
|
358
|
+
readonly status?: unknown;
|
|
359
|
+
readonly terminalEvent?: unknown;
|
|
360
|
+
readonly stopReason?: unknown;
|
|
361
|
+
readonly completedAt?: unknown;
|
|
362
|
+
readonly receiptIds?: unknown;
|
|
363
|
+
};
|
|
364
|
+
return typeof candidate.turnId === 'string'
|
|
365
|
+
&& (candidate.status === 'completed' || candidate.status === 'error' || candidate.status === 'cancelled')
|
|
366
|
+
&& (candidate.terminalEvent === 'TURN_COMPLETED' || candidate.terminalEvent === 'TURN_ERROR' || candidate.terminalEvent === 'TURN_CANCEL')
|
|
367
|
+
&& typeof candidate.stopReason === 'string'
|
|
368
|
+
&& typeof candidate.completedAt === 'number'
|
|
369
|
+
&& Array.isArray(candidate.receiptIds);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
function parseReceiptLine(line: string): { readonly receipt?: PromptContextReceipt; readonly outcome?: PromptContextTurnOutcome } | null {
|
|
373
|
+
try {
|
|
374
|
+
const parsed = JSON.parse(line) as unknown;
|
|
375
|
+
if (isReceipt(parsed)) return { receipt: parsed };
|
|
376
|
+
if (parsed && typeof parsed === 'object') {
|
|
377
|
+
const candidate = parsed as { readonly kind?: unknown; readonly receipt?: unknown; readonly outcome?: unknown };
|
|
378
|
+
if (candidate.kind === 'receipt' && isReceipt(candidate.receipt)) return { receipt: candidate.receipt };
|
|
379
|
+
if (candidate.kind === 'turn_outcome' && isTurnOutcome(candidate.outcome)) return { outcome: candidate.outcome };
|
|
380
|
+
}
|
|
381
|
+
return null;
|
|
382
|
+
} catch {
|
|
383
|
+
return null;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
export interface PromptContextTurnOutcomeInput {
|
|
388
|
+
readonly turnId: string;
|
|
389
|
+
readonly status: PromptContextTurnOutcomeStatus;
|
|
390
|
+
readonly terminalEvent: PromptContextTurnTerminalEvent;
|
|
391
|
+
readonly stopReason: string;
|
|
392
|
+
readonly completedAt?: number;
|
|
393
|
+
readonly detail?: string;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
export class AgentPromptContextReceiptStore {
|
|
397
|
+
private readonly receipts: PromptContextReceipt[] = [];
|
|
398
|
+
private sequence = 0;
|
|
399
|
+
|
|
400
|
+
public constructor(
|
|
401
|
+
private readonly receiptPath?: string,
|
|
402
|
+
private readonly limit = RECEIPT_STORE_LIMIT,
|
|
403
|
+
) {
|
|
404
|
+
this.load();
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
public record(draft: PromptContextReceiptDraft): PromptContextReceipt {
|
|
408
|
+
const sequence = this.sequence + 1;
|
|
409
|
+
this.sequence = sequence;
|
|
410
|
+
const createdAt = Date.now();
|
|
411
|
+
const receipt: PromptContextReceipt = {
|
|
412
|
+
...draft,
|
|
413
|
+
receiptId: `promptctx-${createdAt.toString(36)}-${sequence.toString(36)}`,
|
|
414
|
+
createdAt,
|
|
415
|
+
sequence,
|
|
416
|
+
};
|
|
417
|
+
this.receipts.unshift(receipt);
|
|
418
|
+
this.trim();
|
|
419
|
+
this.append(receipt);
|
|
420
|
+
return receipt;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
public latest(): PromptContextReceipt | null {
|
|
424
|
+
return this.receipts[0] ?? null;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
public list(limit = 20): readonly PromptContextReceipt[] {
|
|
428
|
+
return this.receipts.slice(0, Math.max(1, Math.min(this.limit, Math.trunc(limit))));
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
public get(receiptId: string): PromptContextReceipt | null {
|
|
432
|
+
return this.receipts.find((receipt) => receipt.receiptId === receiptId) ?? null;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
public count(): number {
|
|
436
|
+
return this.receipts.length;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
public recordTurnOutcome(input: PromptContextTurnOutcomeInput): PromptContextTurnOutcome | null {
|
|
440
|
+
const turnId = input.turnId.trim();
|
|
441
|
+
if (!turnId) return null;
|
|
442
|
+
const detail = compactOutcomeDetail(input.detail);
|
|
443
|
+
const matchingReceiptIds = this.receipts
|
|
444
|
+
.filter((receipt) => receipt.turnId === turnId)
|
|
445
|
+
.map((receipt) => receipt.receiptId);
|
|
446
|
+
const outcome: PromptContextTurnOutcome = {
|
|
447
|
+
turnId,
|
|
448
|
+
status: input.status,
|
|
449
|
+
terminalEvent: input.terminalEvent,
|
|
450
|
+
stopReason: input.stopReason,
|
|
451
|
+
completedAt: input.completedAt ?? Date.now(),
|
|
452
|
+
receiptIds: matchingReceiptIds,
|
|
453
|
+
...(detail ? { detail } : {}),
|
|
454
|
+
};
|
|
455
|
+
this.applyTurnOutcome(outcome);
|
|
456
|
+
this.appendTurnOutcome(outcome);
|
|
457
|
+
return outcome;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
private append(receipt: PromptContextReceipt): void {
|
|
461
|
+
if (!this.receiptPath) return;
|
|
462
|
+
mkdirSync(dirname(this.receiptPath), { recursive: true });
|
|
463
|
+
appendFileSync(this.receiptPath, `${JSON.stringify(receipt)}\n`, 'utf-8');
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
private appendTurnOutcome(outcome: PromptContextTurnOutcome): void {
|
|
467
|
+
if (!this.receiptPath) return;
|
|
468
|
+
mkdirSync(dirname(this.receiptPath), { recursive: true });
|
|
469
|
+
appendFileSync(this.receiptPath, `${JSON.stringify({ kind: 'turn_outcome', outcome })}\n`, 'utf-8');
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
private load(): void {
|
|
473
|
+
if (!this.receiptPath || !existsSync(this.receiptPath)) return;
|
|
474
|
+
const lines = readFileSync(this.receiptPath, 'utf-8').split('\n').filter(Boolean);
|
|
475
|
+
const parsed = lines.map(parseReceiptLine).filter((entry): entry is NonNullable<ReturnType<typeof parseReceiptLine>> => Boolean(entry));
|
|
476
|
+
const receipts = parsed.map((entry) => entry.receipt).filter((receipt): receipt is PromptContextReceipt => Boolean(receipt));
|
|
477
|
+
const outcomes = parsed.map((entry) => entry.outcome).filter((outcome): outcome is PromptContextTurnOutcome => Boolean(outcome));
|
|
478
|
+
const recentReceipts = receipts.slice(-this.limit);
|
|
479
|
+
for (const outcome of outcomes) {
|
|
480
|
+
for (const [index, receipt] of recentReceipts.entries()) {
|
|
481
|
+
if (receipt.turnId === outcome.turnId || outcome.receiptIds.includes(receipt.receiptId)) {
|
|
482
|
+
recentReceipts[index] = { ...receipt, turnOutcome: outcome };
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
this.receipts.push(...recentReceipts.reverse());
|
|
487
|
+
this.sequence = this.receipts.reduce((max, receipt) => Math.max(max, receipt.sequence), 0);
|
|
488
|
+
this.trim();
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
private applyTurnOutcome(outcome: PromptContextTurnOutcome): void {
|
|
492
|
+
for (const [index, receipt] of this.receipts.entries()) {
|
|
493
|
+
if (receipt.turnId === outcome.turnId || outcome.receiptIds.includes(receipt.receiptId)) {
|
|
494
|
+
this.receipts[index] = { ...receipt, turnOutcome: outcome };
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
private trim(): void {
|
|
500
|
+
this.receipts.splice(this.limit);
|
|
501
|
+
}
|
|
502
|
+
}
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
type ReminderSchedulePreview,
|
|
5
5
|
type ReminderScheduleSuccess,
|
|
6
6
|
} from './reminder-schedule.ts';
|
|
7
|
+
import { scheduleConfirmationRouteLines, scheduleNextRouteLines, scheduleRouteArg } from './schedule-next-routes.ts';
|
|
7
8
|
|
|
8
9
|
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
9
10
|
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
@@ -22,6 +23,17 @@ function scheduleValue(preview: ReminderSchedulePreview): string {
|
|
|
22
23
|
return String(preview.payload.at);
|
|
23
24
|
}
|
|
24
25
|
|
|
26
|
+
function createReminderRouteArgs(preview: ReminderSchedulePreview): string {
|
|
27
|
+
const args = [
|
|
28
|
+
`scheduleKind:${scheduleRouteArg(String(preview.payload.kind ?? ''))}`,
|
|
29
|
+
`scheduleValue:${scheduleRouteArg(scheduleValue(preview))}`,
|
|
30
|
+
];
|
|
31
|
+
if (typeof preview.payload.timezone === 'string' && preview.payload.timezone) {
|
|
32
|
+
args.push(`timezone:${scheduleRouteArg(preview.payload.timezone)}`);
|
|
33
|
+
}
|
|
34
|
+
return args.join(' ');
|
|
35
|
+
}
|
|
36
|
+
|
|
25
37
|
function formatReminderScheduleFailureKind(kind: string): string {
|
|
26
38
|
if (kind === 'auth_required') return 'authorization required';
|
|
27
39
|
if (kind === 'connected_host_unavailable') return 'connected host unavailable';
|
|
@@ -44,7 +56,9 @@ export function formatReminderSchedulePreview(preview: ReminderSchedulePreview):
|
|
|
44
56
|
` delivery ${delivery?.mode ?? 'none'}${deliveryTargetCount > 0 ? ` (${deliveryTargetCount} target${deliveryTargetCount === 1 ? '' : 's'})` : ''}`,
|
|
45
57
|
' target connected GoodVibes host/main conversation route',
|
|
46
58
|
' policy reminder delivery only; isolated Agent Knowledge only; no default knowledge/non-Agent fallback',
|
|
47
|
-
|
|
59
|
+
...scheduleConfirmationRouteLines({
|
|
60
|
+
model: `schedule action:"remind" message:${scheduleRouteArg(preview.message)} ${createReminderRouteArgs(preview)} confirm:true explicitUserRequest:"..."`,
|
|
61
|
+
}),
|
|
48
62
|
].join('\n');
|
|
49
63
|
}
|
|
50
64
|
|
|
@@ -58,7 +72,7 @@ export function formatReminderScheduleSuccess(result: ReminderScheduleSuccess):
|
|
|
58
72
|
` status ${status}`,
|
|
59
73
|
` route ${result.kind} ${result.route}`,
|
|
60
74
|
` reminder ${result.message}`,
|
|
61
|
-
|
|
75
|
+
...scheduleNextRouteLines(id),
|
|
62
76
|
].join('\n');
|
|
63
77
|
}
|
|
64
78
|
|