@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,481 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { formatAgentChannelDeliveryReceiptLine, readAgentChannelDeliveryReceipts } from '../agent/channel-delivery-receipts.ts';
|
|
3
|
+
import type { CommandContext } from './command-registry.ts';
|
|
4
|
+
import { fetchConnectedHostReadOnlyRoute, type ConnectedHostRouteFailure, type ConnectedHostRouteResult } from './connected-host-routes.ts';
|
|
5
|
+
import type { AgentWorkspaceChannelStatus } from './agent-workspace-channels.ts';
|
|
6
|
+
import { buildAgentWorkspaceChannels } from './agent-workspace-channels.ts';
|
|
7
|
+
|
|
8
|
+
type JsonRecord = Record<string, unknown>;
|
|
9
|
+
|
|
10
|
+
type TriageRouteState = 'ready' | 'empty' | 'unavailable';
|
|
11
|
+
|
|
12
|
+
export interface AgentWorkspaceChannelTriageArgs {
|
|
13
|
+
readonly limit?: unknown;
|
|
14
|
+
readonly includeParameters?: unknown;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface AgentWorkspaceChannelTriage {
|
|
18
|
+
readonly mode: 'channel_triage';
|
|
19
|
+
readonly status: 'ready' | 'attention' | 'blocked';
|
|
20
|
+
readonly summary: string;
|
|
21
|
+
readonly readiness: Record<string, unknown>;
|
|
22
|
+
readonly deliveries: Record<string, unknown>;
|
|
23
|
+
readonly surfaceMessages: Record<string, unknown>;
|
|
24
|
+
readonly routeBindings: Record<string, unknown>;
|
|
25
|
+
readonly receipts: Record<string, unknown>;
|
|
26
|
+
readonly inboundFeed: Record<string, unknown>;
|
|
27
|
+
readonly connectedHost: Record<string, unknown>;
|
|
28
|
+
readonly routes: Record<string, unknown>;
|
|
29
|
+
readonly policy: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
interface DeliveryAttemptView {
|
|
33
|
+
readonly id: string;
|
|
34
|
+
readonly runId: string;
|
|
35
|
+
readonly jobId: string;
|
|
36
|
+
readonly status: string;
|
|
37
|
+
readonly target: Record<string, unknown>;
|
|
38
|
+
readonly startedAt?: number;
|
|
39
|
+
readonly endedAt?: number;
|
|
40
|
+
readonly error?: string;
|
|
41
|
+
readonly responseId?: string;
|
|
42
|
+
readonly inspectRoute: string;
|
|
43
|
+
readonly modelRoute: string;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const DELIVERY_ATTENTION_STATUSES = new Set(['failed', 'dead_lettered', 'pending', 'sending']);
|
|
47
|
+
|
|
48
|
+
function isRecord(value: unknown): value is JsonRecord {
|
|
49
|
+
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function readString(record: JsonRecord, key: string, fallback = ''): string {
|
|
53
|
+
const value = record[key];
|
|
54
|
+
return typeof value === 'string' ? value : fallback;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function readNumber(record: JsonRecord, key: string): number | undefined {
|
|
58
|
+
const value = record[key];
|
|
59
|
+
return typeof value === 'number' && Number.isFinite(value) ? value : undefined;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function readRecord(record: JsonRecord, key: string): JsonRecord {
|
|
63
|
+
const value = record[key];
|
|
64
|
+
return isRecord(value) ? value : {};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function readRecordArray(record: JsonRecord, key: string): readonly JsonRecord[] {
|
|
68
|
+
const value = record[key];
|
|
69
|
+
return Array.isArray(value) ? value.filter(isRecord) : [];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function readLimit(value: unknown, fallback: number): number {
|
|
73
|
+
const parsed = typeof value === 'string' && value.trim() ? Number(value) : value;
|
|
74
|
+
if (typeof parsed !== 'number' || !Number.isFinite(parsed)) return fallback;
|
|
75
|
+
return Math.max(1, Math.min(100, Math.trunc(parsed)));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function digest(value: string): string {
|
|
79
|
+
return createHash('sha256').update(value).digest('hex').slice(0, 12);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function boundedText(value: string, max = 160): string {
|
|
83
|
+
const collapsed = value.replace(/\s+/g, ' ').trim();
|
|
84
|
+
if (collapsed.length <= max) return collapsed;
|
|
85
|
+
return `${collapsed.slice(0, Math.max(0, max - 1))}...`;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function redactText(value: string, max = 160): string {
|
|
89
|
+
return boundedText(value
|
|
90
|
+
.replace(/https?:\/\/\S+/gi, '[redacted-url]')
|
|
91
|
+
.replace(/\b([A-Z0-9_]*(TOKEN|SECRET|PASSWORD|API[_-]?KEY)[A-Z0-9_]*)(\s*[:=]\s*)([^\s,;]+)/gi, '$1$3[redacted]')
|
|
92
|
+
.replace(/\b(xox[baprs]-[A-Za-z0-9-]+)/g, '[redacted-token]'), max);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function redactedAddress(value: string): Record<string, unknown> {
|
|
96
|
+
if (!value.trim()) return {};
|
|
97
|
+
let display = '[redacted-address]';
|
|
98
|
+
try {
|
|
99
|
+
const parsed = new URL(value);
|
|
100
|
+
display = `${parsed.protocol}//${parsed.host}/...`;
|
|
101
|
+
} catch {
|
|
102
|
+
display = '[redacted-address]';
|
|
103
|
+
}
|
|
104
|
+
return { address: display, addressDigest: `sha256:${digest(value)}` };
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function routeFailureView(failure: ConnectedHostRouteFailure): Record<string, unknown> {
|
|
108
|
+
return {
|
|
109
|
+
route: failure.route,
|
|
110
|
+
status: 'unavailable',
|
|
111
|
+
kind: failure.kind,
|
|
112
|
+
baseUrl: failure.baseUrl,
|
|
113
|
+
message: redactText(failure.message),
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function routeState(result: ConnectedHostRouteResult, empty: boolean): TriageRouteState {
|
|
118
|
+
if (!result.ok) return 'unavailable';
|
|
119
|
+
return empty ? 'empty' : 'ready';
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function channelTriageReadiness(channels: readonly AgentWorkspaceChannelStatus[], limit: number): Record<string, unknown> {
|
|
123
|
+
const ready = channels.filter((channel) => channel.ready);
|
|
124
|
+
const enabled = channels.filter((channel) => channel.enabled);
|
|
125
|
+
const needsConfig = channels.filter((channel) => channel.setupState === 'needs-config');
|
|
126
|
+
const needsTarget = channels.filter((channel) => channel.setupState === 'needs-target');
|
|
127
|
+
const attention = channels.filter((channel) => channel.enabled && channel.setupState !== 'ready');
|
|
128
|
+
return {
|
|
129
|
+
totalChannels: channels.length,
|
|
130
|
+
enabled: enabled.length,
|
|
131
|
+
ready: ready.length,
|
|
132
|
+
needsConfig: needsConfig.length,
|
|
133
|
+
needsTarget: needsTarget.length,
|
|
134
|
+
attention: attention.length,
|
|
135
|
+
attentionChannels: attention.slice(0, limit).map((channel) => ({
|
|
136
|
+
channelId: channel.id,
|
|
137
|
+
label: channel.label,
|
|
138
|
+
setupState: channel.setupState,
|
|
139
|
+
delivery: channel.delivery,
|
|
140
|
+
nextStep: channel.nextStep,
|
|
141
|
+
userRoute: `/channels show ${channel.id}`,
|
|
142
|
+
modelRoute: `channels action:"channel" channelId:"${channel.id}"`,
|
|
143
|
+
})),
|
|
144
|
+
routes: {
|
|
145
|
+
readiness: '/channels',
|
|
146
|
+
attention: '/channels attention',
|
|
147
|
+
guide: '/channels guide',
|
|
148
|
+
},
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function deliveryTargetView(target: JsonRecord): Record<string, unknown> {
|
|
153
|
+
const address = readString(target, 'address');
|
|
154
|
+
return {
|
|
155
|
+
kind: readString(target, 'kind', 'unknown'),
|
|
156
|
+
surfaceKind: readString(target, 'surfaceKind') || null,
|
|
157
|
+
routeId: readString(target, 'routeId') || null,
|
|
158
|
+
label: redactText(readString(target, 'label'), 80) || null,
|
|
159
|
+
...redactedAddress(address),
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function deliveryAttemptView(attempt: JsonRecord): DeliveryAttemptView {
|
|
164
|
+
const id = readString(attempt, 'id', 'delivery');
|
|
165
|
+
const runId = readString(attempt, 'runId');
|
|
166
|
+
const jobId = readString(attempt, 'jobId');
|
|
167
|
+
const responseId = readString(attempt, 'responseId');
|
|
168
|
+
const error = readString(attempt, 'error');
|
|
169
|
+
return {
|
|
170
|
+
id,
|
|
171
|
+
runId,
|
|
172
|
+
jobId,
|
|
173
|
+
status: readString(attempt, 'status', 'unknown'),
|
|
174
|
+
target: deliveryTargetView(readRecord(attempt, 'target')),
|
|
175
|
+
...(readNumber(attempt, 'startedAt') !== undefined ? { startedAt: readNumber(attempt, 'startedAt') } : {}),
|
|
176
|
+
...(readNumber(attempt, 'endedAt') !== undefined ? { endedAt: readNumber(attempt, 'endedAt') } : {}),
|
|
177
|
+
...(error ? { error: redactText(error) } : {}),
|
|
178
|
+
...(responseId ? { responseId } : {}),
|
|
179
|
+
inspectRoute: `/api/deliveries/${encodeURIComponent(id)}`,
|
|
180
|
+
modelRoute: runId
|
|
181
|
+
? `autonomy action:"queue" query:"${runId}"`
|
|
182
|
+
: 'autonomy action:"queue"',
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function deliveryTriage(result: ConnectedHostRouteResult, limit: number): Record<string, unknown> {
|
|
187
|
+
if (!result.ok) {
|
|
188
|
+
return {
|
|
189
|
+
route: result.route,
|
|
190
|
+
state: 'unavailable',
|
|
191
|
+
...routeFailureView(result),
|
|
192
|
+
totals: null,
|
|
193
|
+
attempts: [],
|
|
194
|
+
attentionCount: 0,
|
|
195
|
+
retryCandidates: [],
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
const root = isRecord(result.body) ? result.body : {};
|
|
199
|
+
const attempts = readRecordArray(root, 'attempts');
|
|
200
|
+
const views = attempts.map(deliveryAttemptView);
|
|
201
|
+
const attention = views.filter((attempt) => DELIVERY_ATTENTION_STATUSES.has(attempt.status));
|
|
202
|
+
const retryCandidates = views.filter((attempt) => attempt.status === 'failed' || attempt.status === 'dead_lettered');
|
|
203
|
+
return {
|
|
204
|
+
route: result.route,
|
|
205
|
+
state: routeState(result, attempts.length === 0),
|
|
206
|
+
totals: isRecord(root.totals) ? root.totals : null,
|
|
207
|
+
totalAttempts: attempts.length,
|
|
208
|
+
attentionCount: attention.length,
|
|
209
|
+
retryCandidateCount: retryCandidates.length,
|
|
210
|
+
retryCandidates: retryCandidates.slice(0, limit),
|
|
211
|
+
attempts: views.slice(0, limit),
|
|
212
|
+
policy: 'Read-only delivery attempt snapshot. Retry decisions should inspect the owning automation run/job before any confirmed mutation.',
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function surfaceMessageView(message: JsonRecord): Record<string, unknown> {
|
|
217
|
+
return {
|
|
218
|
+
id: readString(message, 'id', 'message'),
|
|
219
|
+
surface: readString(message, 'surface', 'unknown'),
|
|
220
|
+
createdAt: readNumber(message, 'createdAt') ?? null,
|
|
221
|
+
level: readString(message, 'level', 'info'),
|
|
222
|
+
title: redactText(readString(message, 'title'), 100),
|
|
223
|
+
bodyPreview: redactText(readString(message, 'body'), 180),
|
|
224
|
+
routeId: readString(message, 'routeId') || null,
|
|
225
|
+
surfaceId: readString(message, 'surfaceId') || null,
|
|
226
|
+
clientId: readString(message, 'clientId') || null,
|
|
227
|
+
attachmentCount: Array.isArray(message.attachments) ? message.attachments.length : 0,
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function surfaceMessageTriage(result: ConnectedHostRouteResult, limit: number): Record<string, unknown> {
|
|
232
|
+
if (!result.ok) {
|
|
233
|
+
return {
|
|
234
|
+
route: result.route,
|
|
235
|
+
state: 'unavailable',
|
|
236
|
+
...routeFailureView(result),
|
|
237
|
+
totalMessages: 0,
|
|
238
|
+
messages: [],
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
const root = isRecord(result.body) ? result.body : {};
|
|
242
|
+
const messages = readRecordArray(root, 'messages');
|
|
243
|
+
return {
|
|
244
|
+
route: result.route,
|
|
245
|
+
state: routeState(result, messages.length === 0),
|
|
246
|
+
totalMessages: messages.length,
|
|
247
|
+
messages: messages.slice(0, limit).map(surfaceMessageView),
|
|
248
|
+
policy: 'Read-only control-plane surface messages. This is a visible surface message feed, not provider-specific inbox polling.',
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
function routeBindingView(binding: JsonRecord): Record<string, unknown> {
|
|
253
|
+
const externalId = readString(binding, 'externalId');
|
|
254
|
+
return {
|
|
255
|
+
id: readString(binding, 'id', 'binding'),
|
|
256
|
+
kind: readString(binding, 'kind', 'unknown'),
|
|
257
|
+
surfaceKind: readString(binding, 'surfaceKind', 'unknown'),
|
|
258
|
+
surfaceId: readString(binding, 'surfaceId') || null,
|
|
259
|
+
title: redactText(readString(binding, 'title'), 100) || null,
|
|
260
|
+
externalIdDigest: externalId ? `sha256:${digest(externalId)}` : null,
|
|
261
|
+
sessionPolicy: readString(binding, 'sessionPolicy') || null,
|
|
262
|
+
threadPolicy: readString(binding, 'threadPolicy') || null,
|
|
263
|
+
deliveryGuarantee: readString(binding, 'deliveryGuarantee') || null,
|
|
264
|
+
lastSeenAt: readNumber(binding, 'lastSeenAt') ?? null,
|
|
265
|
+
sessionId: readString(binding, 'sessionId') || null,
|
|
266
|
+
runId: readString(binding, 'runId') || null,
|
|
267
|
+
jobId: readString(binding, 'jobId') || null,
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function routeBindingTriage(result: ConnectedHostRouteResult, limit: number): Record<string, unknown> {
|
|
272
|
+
if (!result.ok) {
|
|
273
|
+
return {
|
|
274
|
+
route: result.route,
|
|
275
|
+
state: 'unavailable',
|
|
276
|
+
...routeFailureView(result),
|
|
277
|
+
totalBindings: 0,
|
|
278
|
+
bindings: [],
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
const root = isRecord(result.body) ? result.body : {};
|
|
282
|
+
const bindings = readRecordArray(root, 'bindings');
|
|
283
|
+
return {
|
|
284
|
+
route: result.route,
|
|
285
|
+
state: routeState(result, bindings.length === 0),
|
|
286
|
+
totalBindings: bindings.length,
|
|
287
|
+
bindings: bindings.slice(0, limit).map(routeBindingView),
|
|
288
|
+
policy: 'Read-only route continuity. External ids are digested so channel identifiers are not printed into chat.',
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function receiptTriage(context: CommandContext, limit: number): Record<string, unknown> {
|
|
293
|
+
const shellPaths = context.workspace?.shellPaths;
|
|
294
|
+
if (!shellPaths) {
|
|
295
|
+
return {
|
|
296
|
+
state: 'unavailable',
|
|
297
|
+
totalReceipts: 0,
|
|
298
|
+
receipts: [],
|
|
299
|
+
reason: 'Agent shell paths unavailable.',
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
const snapshot = readAgentChannelDeliveryReceipts(shellPaths);
|
|
303
|
+
return {
|
|
304
|
+
state: snapshot.parseError ? 'attention' : snapshot.exists ? 'ready' : 'empty',
|
|
305
|
+
path: snapshot.path,
|
|
306
|
+
totalReceipts: snapshot.receipts.length,
|
|
307
|
+
receipts: snapshot.receipts.slice(0, limit).map((receipt) => ({
|
|
308
|
+
receiptId: receipt.id,
|
|
309
|
+
line: formatAgentChannelDeliveryReceiptLine(receipt),
|
|
310
|
+
createdAt: receipt.createdAt,
|
|
311
|
+
source: receipt.source,
|
|
312
|
+
status: receipt.status,
|
|
313
|
+
target: receipt.target,
|
|
314
|
+
messageDigest: receipt.messageDigest,
|
|
315
|
+
messagePreview: receipt.messagePreview,
|
|
316
|
+
})),
|
|
317
|
+
...(snapshot.parseError ? { parseError: snapshot.parseError } : {}),
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
function routeFailureCount(results: readonly ConnectedHostRouteResult[]): number {
|
|
322
|
+
return results.filter((result) => !result.ok).length;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
function triageStatus(input: {
|
|
326
|
+
readonly readinessAttention: number;
|
|
327
|
+
readonly deliveryAttention: number;
|
|
328
|
+
readonly routeFailures: number;
|
|
329
|
+
}): AgentWorkspaceChannelTriage['status'] {
|
|
330
|
+
if (input.routeFailures > 0) return 'blocked';
|
|
331
|
+
if (input.readinessAttention > 0 || input.deliveryAttention > 0) return 'attention';
|
|
332
|
+
return 'ready';
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
export async function buildAgentWorkspaceChannelTriage(
|
|
336
|
+
context: CommandContext,
|
|
337
|
+
args: AgentWorkspaceChannelTriageArgs = {},
|
|
338
|
+
): Promise<AgentWorkspaceChannelTriage> {
|
|
339
|
+
const limit = readLimit(args.limit, 12);
|
|
340
|
+
const channels = buildAgentWorkspaceChannels(context);
|
|
341
|
+
const [deliveriesResult, messagesResult, bindingsResult] = await Promise.all([
|
|
342
|
+
fetchConnectedHostReadOnlyRoute(context, '/api/deliveries'),
|
|
343
|
+
fetchConnectedHostReadOnlyRoute(context, '/api/control-plane/messages'),
|
|
344
|
+
fetchConnectedHostReadOnlyRoute(context, '/api/routes/bindings'),
|
|
345
|
+
]);
|
|
346
|
+
const readiness = channelTriageReadiness(channels, limit);
|
|
347
|
+
const deliveries = deliveryTriage(deliveriesResult, limit);
|
|
348
|
+
const surfaceMessages = surfaceMessageTriage(messagesResult, limit);
|
|
349
|
+
const routeBindings = routeBindingTriage(bindingsResult, limit);
|
|
350
|
+
const receipts = receiptTriage(context, limit);
|
|
351
|
+
const routeFailures = routeFailureCount([deliveriesResult, messagesResult, bindingsResult]);
|
|
352
|
+
const readinessAttention = typeof readiness.attention === 'number' ? readiness.attention : 0;
|
|
353
|
+
const deliveryAttention = typeof deliveries.attentionCount === 'number' ? deliveries.attentionCount : 0;
|
|
354
|
+
const status = triageStatus({ readinessAttention, deliveryAttention, routeFailures });
|
|
355
|
+
const messageCount = typeof surfaceMessages.totalMessages === 'number' ? surfaceMessages.totalMessages : 0;
|
|
356
|
+
const bindingCount = typeof routeBindings.totalBindings === 'number' ? routeBindings.totalBindings : 0;
|
|
357
|
+
const retryCount = typeof deliveries.retryCandidateCount === 'number' ? deliveries.retryCandidateCount : 0;
|
|
358
|
+
return {
|
|
359
|
+
mode: 'channel_triage',
|
|
360
|
+
status,
|
|
361
|
+
summary: `${readinessAttention} channel setup blocker(s), ${deliveryAttention} delivery attention item(s), ${retryCount} retry candidate(s), ${messageCount} visible surface message(s), ${bindingCount} route binding(s).`,
|
|
362
|
+
readiness,
|
|
363
|
+
deliveries,
|
|
364
|
+
surfaceMessages,
|
|
365
|
+
routeBindings,
|
|
366
|
+
receipts,
|
|
367
|
+
inboundFeed: {
|
|
368
|
+
status: messageCount > 0 ? 'visible_surface_messages' : 'no_visible_surface_messages',
|
|
369
|
+
route: '/api/control-plane/messages',
|
|
370
|
+
providerInboxFeed: 'not_published_by_current_channel_contract',
|
|
371
|
+
summary: 'GoodVibes daemon exposes control-plane surface messages, route bindings, delivery attempts, and session/companion message APIs. It does not expose a general provider-specific channel inbox feed, so Agent triage does not claim unread Slack/Discord/email polling.',
|
|
372
|
+
},
|
|
373
|
+
connectedHost: {
|
|
374
|
+
routesChecked: ['/api/deliveries', '/api/control-plane/messages', '/api/routes/bindings'],
|
|
375
|
+
failures: [deliveriesResult, messagesResult, bindingsResult].filter((result): result is ConnectedHostRouteFailure => !result.ok).map(routeFailureView),
|
|
376
|
+
},
|
|
377
|
+
routes: {
|
|
378
|
+
command: '/channels triage',
|
|
379
|
+
deliveries: '/channels deliveries',
|
|
380
|
+
readiness: '/channels attention',
|
|
381
|
+
status: '/channels status',
|
|
382
|
+
policies: '/channels policies',
|
|
383
|
+
setupGuide: '/channels guide',
|
|
384
|
+
modelRoute: 'channels action:"triage"',
|
|
385
|
+
deliveryReceipts: 'channels action:"deliveries"',
|
|
386
|
+
sendTool: 'agent_channel_send',
|
|
387
|
+
},
|
|
388
|
+
policy: 'Read-only channel triage. It never sends messages, mutates route bindings, repairs channels, retries jobs, or prints raw external addresses; every effect remains on an explicit confirmed route.',
|
|
389
|
+
};
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
function deliveryTargetLine(target: Record<string, unknown>): string {
|
|
393
|
+
const parts = [
|
|
394
|
+
typeof target.kind === 'string' ? target.kind : 'target',
|
|
395
|
+
typeof target.surfaceKind === 'string' ? target.surfaceKind : '',
|
|
396
|
+
typeof target.routeId === 'string' ? `route=${target.routeId}` : '',
|
|
397
|
+
typeof target.label === 'string' ? `label=${target.label}` : '',
|
|
398
|
+
typeof target.address === 'string' ? target.address : '',
|
|
399
|
+
].filter(Boolean);
|
|
400
|
+
return parts.join(' ');
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
export function formatAgentWorkspaceChannelTriage(triage: AgentWorkspaceChannelTriage): string {
|
|
404
|
+
const readiness = triage.readiness;
|
|
405
|
+
const deliveries = triage.deliveries;
|
|
406
|
+
const messages = triage.surfaceMessages;
|
|
407
|
+
const bindings = triage.routeBindings;
|
|
408
|
+
const receipts = triage.receipts;
|
|
409
|
+
const lines = [
|
|
410
|
+
'Channel Triage',
|
|
411
|
+
` status: ${triage.status}`,
|
|
412
|
+
` summary: ${triage.summary}`,
|
|
413
|
+
` policy: ${triage.policy}`,
|
|
414
|
+
` inbox: ${triage.inboundFeed.status}; provider inbox feed ${triage.inboundFeed.providerInboxFeed}`,
|
|
415
|
+
'',
|
|
416
|
+
' Readiness',
|
|
417
|
+
` ready: ${readiness.ready}/${readiness.totalChannels}; enabled: ${readiness.enabled}; needs config: ${readiness.needsConfig}; needs target: ${readiness.needsTarget}`,
|
|
418
|
+
];
|
|
419
|
+
const attentionChannels = Array.isArray(readiness.attentionChannels) ? readiness.attentionChannels.filter(isRecord) : [];
|
|
420
|
+
for (const channel of attentionChannels.slice(0, 6)) {
|
|
421
|
+
lines.push(` - ${readString(channel, 'label', readString(channel, 'channelId'))}: ${readString(channel, 'setupState')}; ${readString(channel, 'userRoute')}`);
|
|
422
|
+
}
|
|
423
|
+
if (attentionChannels.length === 0) lines.push(' no enabled channel setup blockers');
|
|
424
|
+
|
|
425
|
+
lines.push('', ' Delivery Attempts');
|
|
426
|
+
if (deliveries.state === 'unavailable') {
|
|
427
|
+
lines.push(` unavailable: ${deliveries.kind}; ${deliveries.message}`);
|
|
428
|
+
} else {
|
|
429
|
+
const totals = isRecord(deliveries.totals) ? deliveries.totals : {};
|
|
430
|
+
lines.push(` state: ${deliveries.state}; total: ${deliveries.totalAttempts}; attention: ${deliveries.attentionCount}; retry candidates: ${deliveries.retryCandidateCount}`);
|
|
431
|
+
lines.push(` totals: queued=${totals.queued ?? 0}; started=${totals.started ?? 0}; succeeded=${totals.succeeded ?? 0}; failed=${totals.failed ?? 0}; dead-lettered=${totals.deadLettered ?? 0}`);
|
|
432
|
+
const retryCandidates = Array.isArray(deliveries.retryCandidates) ? deliveries.retryCandidates.filter(isRecord) : [];
|
|
433
|
+
for (const attempt of retryCandidates.slice(0, 5)) {
|
|
434
|
+
const target = isRecord(attempt.target) ? attempt.target : {};
|
|
435
|
+
const error = readString(attempt, 'error');
|
|
436
|
+
lines.push(` - ${readString(attempt, 'id')}: ${readString(attempt, 'status')} ${deliveryTargetLine(target)}${error ? ` error=${error}` : ''} inspect=${readString(attempt, 'inspectRoute')}`);
|
|
437
|
+
}
|
|
438
|
+
if (retryCandidates.length === 0) lines.push(' no failed or dead-lettered delivery attempts reported');
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
lines.push('', ' Surface Messages');
|
|
442
|
+
if (messages.state === 'unavailable') {
|
|
443
|
+
lines.push(` unavailable: ${messages.kind}; ${messages.message}`);
|
|
444
|
+
} else {
|
|
445
|
+
lines.push(` state: ${messages.state}; total: ${messages.totalMessages}`);
|
|
446
|
+
const visibleMessages = Array.isArray(messages.messages) ? messages.messages.filter(isRecord) : [];
|
|
447
|
+
for (const message of visibleMessages.slice(0, 5)) {
|
|
448
|
+
lines.push(` - ${readString(message, 'surface')}: ${readString(message, 'level')} ${readString(message, 'title')}${readString(message, 'routeId') ? ` route=${readString(message, 'routeId')}` : ''}`);
|
|
449
|
+
}
|
|
450
|
+
if (visibleMessages.length === 0) lines.push(' no visible control-plane surface messages reported');
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
lines.push('', ' Route Bindings');
|
|
454
|
+
if (bindings.state === 'unavailable') {
|
|
455
|
+
lines.push(` unavailable: ${bindings.kind}; ${bindings.message}`);
|
|
456
|
+
} else {
|
|
457
|
+
lines.push(` state: ${bindings.state}; total: ${bindings.totalBindings}`);
|
|
458
|
+
const routeRows = Array.isArray(bindings.bindings) ? bindings.bindings.filter(isRecord) : [];
|
|
459
|
+
for (const binding of routeRows.slice(0, 5)) {
|
|
460
|
+
lines.push(` - ${readString(binding, 'id')}: ${readString(binding, 'surfaceKind')} ${readString(binding, 'kind')} external=${readString(binding, 'externalIdDigest')}`);
|
|
461
|
+
}
|
|
462
|
+
if (routeRows.length === 0) lines.push(' no route bindings reported');
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
lines.push('', ' Agent Receipts');
|
|
466
|
+
if (receipts.state === 'unavailable') {
|
|
467
|
+
lines.push(` unavailable: ${receipts.reason}`);
|
|
468
|
+
} else {
|
|
469
|
+
lines.push(` state: ${receipts.state}; total: ${receipts.totalReceipts}`);
|
|
470
|
+
const receiptRows = Array.isArray(receipts.receipts) ? receipts.receipts.filter(isRecord) : [];
|
|
471
|
+
for (const receipt of receiptRows.slice(0, 5)) lines.push(` - ${readString(receipt, 'line')}`);
|
|
472
|
+
if (receiptRows.length === 0) lines.push(' no confirmed Agent channel sends recorded');
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
lines.push('', ' Next Routes');
|
|
476
|
+
lines.push(' /channels attention');
|
|
477
|
+
lines.push(' /channels deliveries');
|
|
478
|
+
lines.push(' /channels status');
|
|
479
|
+
lines.push(' channels action:"triage"');
|
|
480
|
+
return lines.join('\n');
|
|
481
|
+
}
|