@pellux/goodvibes-agent 1.0.31 → 1.0.34
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 +59 -2
- package/README.md +72 -58
- package/dist/package/main.js +7572 -2430
- package/docs/README.md +25 -21
- package/docs/channels-remote-and-api.md +6 -2
- package/docs/connected-host.md +27 -6
- package/docs/getting-started.md +87 -66
- package/docs/knowledge-artifacts-and-multimodal.md +16 -4
- package/docs/project-planning.md +2 -2
- package/docs/providers-and-routing.md +3 -2
- package/docs/release-and-publishing.md +15 -11
- package/docs/tools-and-commands.md +150 -128
- package/docs/voice-and-live-tts.md +1 -1
- package/package.json +8 -3
- package/release/live-verification/live-verification.json +148 -0
- package/release/live-verification/live-verification.md +187 -0
- package/release/performance-snapshot.json +57 -0
- package/release/release-notes.md +19 -0
- package/release/release-readiness.json +581 -0
- package/src/agent/harness-control.ts +42 -3
- package/src/cli/agent-knowledge-command.ts +5 -5
- package/src/cli/agent-knowledge-format.ts +11 -0
- package/src/cli/agent-knowledge-runtime.ts +92 -13
- package/src/cli/bundle-command.ts +5 -4
- package/src/cli/entrypoint.ts +5 -2
- package/src/cli/external-runtime.ts +2 -15
- package/src/cli/management.ts +4 -3
- package/src/input/commands/guidance-runtime.ts +1 -1
- package/src/input/commands/knowledge.ts +2 -2
- package/src/runtime/bootstrap.ts +10 -18
- package/src/runtime/connected-host-auth.ts +16 -0
- package/src/tools/agent-analysis-registry-policy.ts +2 -9
- package/src/tools/agent-channel-send-tool.ts +3 -9
- package/src/tools/agent-context-policy.ts +1 -5
- package/src/tools/agent-find-policy.ts +1 -4
- package/src/tools/agent-harness-channel-metadata.ts +177 -0
- package/src/tools/agent-harness-cli-metadata.ts +4 -1
- package/src/tools/agent-harness-command-catalog.ts +10 -3
- package/src/tools/agent-harness-connected-host-status.ts +9 -3
- package/src/tools/agent-harness-delegation-posture.ts +216 -0
- package/src/tools/agent-harness-keybinding-metadata.ts +57 -22
- package/src/tools/agent-harness-mcp-metadata.ts +248 -0
- package/src/tools/agent-harness-media-posture.ts +282 -0
- package/src/tools/agent-harness-metadata.ts +44 -9
- package/src/tools/agent-harness-model-routing.ts +501 -0
- package/src/tools/agent-harness-model-tool-catalog.ts +7 -2
- package/src/tools/agent-harness-notification-metadata.ts +217 -0
- package/src/tools/agent-harness-operator-methods.ts +285 -0
- package/src/tools/agent-harness-pairing-posture.ts +265 -0
- package/src/tools/agent-harness-panel-metadata.ts +26 -12
- package/src/tools/agent-harness-provider-account-metadata.ts +205 -0
- package/src/tools/agent-harness-release-evidence.ts +364 -0
- package/src/tools/agent-harness-release-readiness.ts +298 -0
- package/src/tools/agent-harness-security-posture.ts +648 -0
- package/src/tools/agent-harness-service-posture.ts +207 -0
- package/src/tools/agent-harness-session-metadata.ts +284 -0
- package/src/tools/agent-harness-setup-posture.ts +295 -0
- package/src/tools/agent-harness-tool-schema.ts +104 -27
- package/src/tools/agent-harness-tool.ts +251 -235
- package/src/tools/agent-harness-ui-surface-metadata.ts +20 -12
- package/src/tools/agent-harness-workspace-actions.ts +260 -0
- package/src/tools/agent-knowledge-ingest-tool.ts +4 -10
- package/src/tools/agent-knowledge-tool.ts +120 -25
- package/src/tools/agent-local-registry-tool.ts +3 -7
- package/src/tools/agent-media-generate-tool.ts +2 -8
- package/src/tools/agent-notify-tool.ts +3 -8
- package/src/tools/agent-operator-action-tool.ts +4 -10
- package/src/tools/agent-operator-briefing-tool.ts +1 -6
- package/src/tools/agent-read-policy.ts +1 -4
- package/src/tools/agent-reminder-schedule-tool.ts +4 -9
- package/src/tools/agent-tool-policy-guard.ts +15 -51
- package/src/tools/agent-web-search-policy.ts +1 -4
- package/src/tools/agent-work-plan-tool.ts +1 -6
- package/src/version.ts +2 -2
|
@@ -11,6 +11,7 @@ export interface AgentHarnessUiSurfaceArgs {
|
|
|
11
11
|
readonly target?: unknown;
|
|
12
12
|
readonly key?: unknown;
|
|
13
13
|
readonly prefix?: unknown;
|
|
14
|
+
readonly includeParameters?: unknown;
|
|
14
15
|
readonly limit?: unknown;
|
|
15
16
|
readonly pane?: unknown;
|
|
16
17
|
}
|
|
@@ -216,7 +217,7 @@ const UI_SURFACES: readonly UiSurfaceDefinition[] = [
|
|
|
216
217
|
id: 'knowledge-panel',
|
|
217
218
|
label: 'Knowledge Panel',
|
|
218
219
|
kind: 'workspace',
|
|
219
|
-
summary: 'Agent Knowledge operator surface for isolated status, source libraries,
|
|
220
|
+
summary: 'Agent Knowledge operator surface for isolated status, source/node/issue libraries, item lookup, map review, connectors, ask/search, and ingest forms.',
|
|
220
221
|
command: '/knowledge',
|
|
221
222
|
preferredModelRoute: 'Use agent_knowledge, agent_knowledge_ingest, workspace_actions, or run_command /knowledge for concrete model operation.',
|
|
222
223
|
parameters: ['pane'],
|
|
@@ -727,22 +728,28 @@ function resolveHarnessUiSurface(args: AgentHarnessUiSurfaceArgs): UiSurfaceReso
|
|
|
727
728
|
return null;
|
|
728
729
|
}
|
|
729
730
|
|
|
730
|
-
function describeSurface(
|
|
731
|
+
function describeSurface(
|
|
732
|
+
context: CommandContext,
|
|
733
|
+
surface: UiSurfaceDefinition,
|
|
734
|
+
options: { readonly includeParameters?: boolean; readonly lookup?: UiSurfaceLookup } = {},
|
|
735
|
+
): Record<string, unknown> {
|
|
731
736
|
return {
|
|
732
737
|
id: surface.id,
|
|
733
738
|
label: surface.label,
|
|
734
739
|
kind: surface.kind,
|
|
735
740
|
summary: surface.summary,
|
|
736
741
|
command: surface.command,
|
|
737
|
-
...(lookup ? { lookup } : {}),
|
|
742
|
+
...(options.lookup ? { lookup: options.lookup } : {}),
|
|
738
743
|
preferredModelRoute: surface.preferredModelRoute,
|
|
739
|
-
parameters: surface.parameters ?? [],
|
|
740
744
|
available: surface.available(context),
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
745
|
+
...(options.includeParameters ? {
|
|
746
|
+
parameters: surface.parameters ?? [],
|
|
747
|
+
policy: {
|
|
748
|
+
effect: 'visible-ui-navigation',
|
|
749
|
+
confirmation: 'agent_harness mode:"open_ui_surface" requires confirm:true and explicitUserRequest.',
|
|
750
|
+
boundary: 'UI surface routing opens the same visible Agent shell surface the user can open. Use first-class model tools, settings modes, workspace actions, or confirmed slash-command mirrors for actual operations.',
|
|
751
|
+
},
|
|
752
|
+
} : {}),
|
|
746
753
|
};
|
|
747
754
|
}
|
|
748
755
|
|
|
@@ -753,15 +760,16 @@ export function totalHarnessUiSurfaces(): number {
|
|
|
753
760
|
export function listHarnessUiSurfaces(context: CommandContext, args: AgentHarnessUiSurfaceArgs): readonly Record<string, unknown>[] {
|
|
754
761
|
const query = readString(args.query);
|
|
755
762
|
const limit = readLimit(args.limit, 200);
|
|
763
|
+
const includeParameters = args.includeParameters === true;
|
|
756
764
|
return UI_SURFACES
|
|
757
|
-
.map((surface) => describeSurface(context, surface))
|
|
765
|
+
.map((surface) => describeSurface(context, surface, { includeParameters }))
|
|
758
766
|
.filter((surface) => surfaceMatches(surface, query))
|
|
759
767
|
.slice(0, limit);
|
|
760
768
|
}
|
|
761
769
|
|
|
762
770
|
export function describeHarnessUiSurface(context: CommandContext, args: AgentHarnessUiSurfaceArgs): Record<string, unknown> | null {
|
|
763
771
|
const resolved = resolveHarnessUiSurface(args);
|
|
764
|
-
if (resolved?.status === 'found') return describeSurface(context, resolved.surface, resolved.lookup);
|
|
772
|
+
if (resolved?.status === 'found') return describeSurface(context, resolved.surface, { includeParameters: true, lookup: resolved.lookup });
|
|
765
773
|
if (resolved?.status === 'ambiguous') {
|
|
766
774
|
return { status: 'ambiguous', input: resolved.input, candidates: resolved.candidates };
|
|
767
775
|
}
|
|
@@ -783,6 +791,6 @@ export async function openHarnessUiSurface(context: CommandContext, args: AgentH
|
|
|
783
791
|
const routed = await resolved.surface.open(context, args);
|
|
784
792
|
return {
|
|
785
793
|
...routed,
|
|
786
|
-
descriptor: describeSurface(context, resolved.surface, resolved.lookup),
|
|
794
|
+
descriptor: describeSurface(context, resolved.surface, { includeParameters: true, lookup: resolved.lookup }),
|
|
787
795
|
};
|
|
788
796
|
}
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
import type { CommandContext } from '../input/command-registry.ts';
|
|
2
|
+
import { createAgentWorkspaceEditor } from '../input/agent-workspace-activation.ts';
|
|
3
|
+
import { AGENT_WORKSPACE_CATEGORIES } from '../input/agent-workspace-categories.ts';
|
|
4
|
+
import { searchAgentWorkspaceActions } from '../input/agent-workspace-search.ts';
|
|
5
|
+
import { buildAgentWorkspaceRuntimeSnapshot } from '../input/agent-workspace-snapshot.ts';
|
|
6
|
+
import type { AgentWorkspaceAction, AgentWorkspaceCategory, AgentWorkspaceEditorKind, AgentWorkspaceLocalEditor, AgentWorkspaceLocalLibraryItem, AgentWorkspaceRuntimeSnapshot } from '../input/agent-workspace-types.ts';
|
|
7
|
+
import { describeLocalWorkspaceModelExecution } from './agent-harness-local-operations.ts';
|
|
8
|
+
import { describeWorkspaceEditorModelExecution } from './agent-harness-workspace-editor-execution.ts';
|
|
9
|
+
|
|
10
|
+
export { AGENT_WORKSPACE_CATEGORIES };
|
|
11
|
+
|
|
12
|
+
export interface AgentHarnessWorkspaceActionArgs {
|
|
13
|
+
readonly query?: unknown;
|
|
14
|
+
readonly command?: unknown;
|
|
15
|
+
readonly actionId?: unknown;
|
|
16
|
+
readonly recordId?: unknown;
|
|
17
|
+
readonly fields?: unknown;
|
|
18
|
+
readonly target?: unknown;
|
|
19
|
+
readonly category?: unknown;
|
|
20
|
+
readonly categoryId?: unknown;
|
|
21
|
+
readonly includeParameters?: unknown;
|
|
22
|
+
readonly limit?: unknown;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface WorkspaceEditorContext {
|
|
26
|
+
readonly runtimeStarterTemplates: AgentWorkspaceRuntimeSnapshot['runtimeStarterTemplates'];
|
|
27
|
+
readonly selectedRoutine: AgentWorkspaceLocalLibraryItem | null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface WorkspaceActionLookup {
|
|
31
|
+
readonly source: 'actionId' | 'command' | 'target' | 'query';
|
|
32
|
+
readonly input: string;
|
|
33
|
+
readonly resolvedBy: 'id' | 'case-insensitive-id' | 'label' | 'case-insensitive-label' | 'command' | 'search';
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export type WorkspaceActionResolution =
|
|
37
|
+
| {
|
|
38
|
+
readonly status: 'found';
|
|
39
|
+
readonly category: AgentWorkspaceCategory;
|
|
40
|
+
readonly action: AgentWorkspaceAction;
|
|
41
|
+
readonly lookup: WorkspaceActionLookup;
|
|
42
|
+
}
|
|
43
|
+
| {
|
|
44
|
+
readonly status: 'ambiguous';
|
|
45
|
+
readonly input: string;
|
|
46
|
+
readonly candidates: readonly { readonly actionId: string; readonly categoryId: string; readonly label: string; readonly command?: string }[];
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
function readString(value: unknown): string {
|
|
50
|
+
return typeof value === 'string' ? value.trim() : '';
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function readLimit(value: unknown, fallback: number): number {
|
|
54
|
+
const parsed = typeof value === 'string' && value.trim() ? Number(value) : value;
|
|
55
|
+
if (typeof parsed !== 'number' || !Number.isFinite(parsed)) return fallback;
|
|
56
|
+
return Math.max(1, Math.min(500, Math.trunc(parsed)));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function readFieldMap(value: unknown): Readonly<Record<string, string>> {
|
|
60
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value)) return {};
|
|
61
|
+
return Object.fromEntries(Object.entries(value).map(([key, entry]) => [key, typeof entry === 'string' ? entry : String(entry)]));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function allWorkspaceActions(): ReadonlyArray<{
|
|
65
|
+
readonly category: AgentWorkspaceCategory;
|
|
66
|
+
readonly action: AgentWorkspaceAction;
|
|
67
|
+
}> {
|
|
68
|
+
return AGENT_WORKSPACE_CATEGORIES.flatMap((category) => category.actions.map((action) => ({ category, action })));
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function describeWorkspaceCategory(category: AgentWorkspaceCategory): Record<string, unknown> {
|
|
72
|
+
return {
|
|
73
|
+
id: category.id,
|
|
74
|
+
group: category.group,
|
|
75
|
+
label: category.label,
|
|
76
|
+
summary: category.summary,
|
|
77
|
+
detail: category.detail,
|
|
78
|
+
actions: category.actions.length,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function describeWorkspaceEditor(editor: AgentWorkspaceLocalEditor): Record<string, unknown> {
|
|
83
|
+
return {
|
|
84
|
+
kind: editor.kind,
|
|
85
|
+
mode: editor.mode,
|
|
86
|
+
title: editor.title,
|
|
87
|
+
message: editor.message,
|
|
88
|
+
fields: editor.fields.map((field) => ({
|
|
89
|
+
id: field.id,
|
|
90
|
+
label: field.label,
|
|
91
|
+
required: field.required,
|
|
92
|
+
multiline: field.multiline,
|
|
93
|
+
hint: field.hint,
|
|
94
|
+
redact: field.redact === true,
|
|
95
|
+
default: field.redact ? '<redacted>' : field.value,
|
|
96
|
+
})),
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function previewText(value: string, maxLength = 120): string {
|
|
101
|
+
const normalized = value.replace(/\s+/g, ' ').trim();
|
|
102
|
+
return normalized.length <= maxLength ? normalized : `${normalized.slice(0, maxLength - 1).trimEnd()}...`;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function selectedRoutineFromArgs(
|
|
106
|
+
snapshot: AgentWorkspaceRuntimeSnapshot,
|
|
107
|
+
args: AgentHarnessWorkspaceActionArgs,
|
|
108
|
+
): AgentWorkspaceLocalLibraryItem | null {
|
|
109
|
+
const fields = readFieldMap(args.fields);
|
|
110
|
+
const routineId = readString(args.recordId) || readString(fields.routineId) || readString(fields.id);
|
|
111
|
+
if (!routineId) return null;
|
|
112
|
+
return snapshot.localRoutines.find((routine) => routine.id === routineId || routine.name.toLowerCase() === routineId.toLowerCase()) ?? null;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export function buildWorkspaceEditorContext(context: CommandContext, args: AgentHarnessWorkspaceActionArgs): WorkspaceEditorContext {
|
|
116
|
+
try {
|
|
117
|
+
const snapshot = buildAgentWorkspaceRuntimeSnapshot(context);
|
|
118
|
+
return {
|
|
119
|
+
runtimeStarterTemplates: snapshot.runtimeStarterTemplates,
|
|
120
|
+
selectedRoutine: selectedRoutineFromArgs(snapshot, args),
|
|
121
|
+
};
|
|
122
|
+
} catch {
|
|
123
|
+
return {
|
|
124
|
+
runtimeStarterTemplates: [],
|
|
125
|
+
selectedRoutine: null,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export function createWorkspaceEditor(
|
|
131
|
+
editorKind: AgentWorkspaceEditorKind,
|
|
132
|
+
editorContext: WorkspaceEditorContext | null,
|
|
133
|
+
): AgentWorkspaceLocalEditor | null {
|
|
134
|
+
return createAgentWorkspaceEditor(editorKind, {
|
|
135
|
+
runtimeStarterTemplates: editorContext?.runtimeStarterTemplates ?? [],
|
|
136
|
+
selectedRoutine: editorKind === 'routine-schedule' ? editorContext?.selectedRoutine ?? null : null,
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export function describeWorkspaceAction(
|
|
141
|
+
category: AgentWorkspaceCategory,
|
|
142
|
+
action: AgentWorkspaceAction,
|
|
143
|
+
options: { readonly includeEditor?: boolean; readonly editorContext?: WorkspaceEditorContext | null; readonly lookup?: WorkspaceActionLookup } = {},
|
|
144
|
+
): Record<string, unknown> {
|
|
145
|
+
const editor = options.includeEditor && action.editorKind ? createWorkspaceEditor(action.editorKind, options.editorContext ?? null) : null;
|
|
146
|
+
return {
|
|
147
|
+
id: action.id,
|
|
148
|
+
categoryId: category.id,
|
|
149
|
+
category: category.label,
|
|
150
|
+
group: category.group,
|
|
151
|
+
label: action.label,
|
|
152
|
+
detail: action.detail,
|
|
153
|
+
kind: action.kind,
|
|
154
|
+
safety: action.safety,
|
|
155
|
+
...(action.command ? { command: action.command } : {}),
|
|
156
|
+
...(action.targetCategoryId ? { targetCategoryId: action.targetCategoryId } : {}),
|
|
157
|
+
...(action.editorKind ? { editorKind: action.editorKind } : {}),
|
|
158
|
+
...(action.localKind ? { localKind: action.localKind } : {}),
|
|
159
|
+
...(action.localOperation ? { localOperation: action.localOperation } : {}),
|
|
160
|
+
...(options.lookup ? { lookup: options.lookup } : {}),
|
|
161
|
+
...(editor ? { editor: describeWorkspaceEditor(editor) } : {}),
|
|
162
|
+
...(action.kind === 'local-selection' || action.kind === 'local-operation' ? {
|
|
163
|
+
modelExecution: describeLocalWorkspaceModelExecution(action),
|
|
164
|
+
} : {}),
|
|
165
|
+
...(action.kind === 'editor' && action.editorKind ? {
|
|
166
|
+
modelExecution: describeWorkspaceEditorModelExecution(action.editorKind),
|
|
167
|
+
} : {}),
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function describeWorkspaceActionSummary(
|
|
172
|
+
category: AgentWorkspaceCategory,
|
|
173
|
+
action: AgentWorkspaceAction,
|
|
174
|
+
): Record<string, unknown> {
|
|
175
|
+
return {
|
|
176
|
+
id: action.id,
|
|
177
|
+
categoryId: category.id,
|
|
178
|
+
category: category.label,
|
|
179
|
+
group: category.group,
|
|
180
|
+
label: action.label,
|
|
181
|
+
summary: previewText(action.detail),
|
|
182
|
+
kind: action.kind,
|
|
183
|
+
safety: action.safety,
|
|
184
|
+
...(action.command ? { command: action.command } : {}),
|
|
185
|
+
...(action.targetCategoryId ? { targetCategoryId: action.targetCategoryId } : {}),
|
|
186
|
+
...(action.editorKind ? { editorKind: action.editorKind } : {}),
|
|
187
|
+
...(action.localKind ? { localKind: action.localKind } : {}),
|
|
188
|
+
...(action.localOperation ? { localOperation: action.localOperation } : {}),
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export function listWorkspaceActions(
|
|
193
|
+
context: CommandContext,
|
|
194
|
+
args: AgentHarnessWorkspaceActionArgs,
|
|
195
|
+
): readonly Record<string, unknown>[] {
|
|
196
|
+
const query = readString(args.query);
|
|
197
|
+
const categoryId = readString(args.categoryId || args.category);
|
|
198
|
+
const limit = readLimit(args.limit, 200);
|
|
199
|
+
const includeEditor = args.includeParameters === true;
|
|
200
|
+
const editorContext = includeEditor ? buildWorkspaceEditorContext(context, args) : null;
|
|
201
|
+
const source = query
|
|
202
|
+
? searchAgentWorkspaceActions(AGENT_WORKSPACE_CATEGORIES, query).map((result) => ({ category: result.category, action: result.action }))
|
|
203
|
+
: allWorkspaceActions();
|
|
204
|
+
return source
|
|
205
|
+
.filter((entry) => !categoryId || entry.category.id === categoryId)
|
|
206
|
+
.slice(0, limit)
|
|
207
|
+
.map((entry) => includeEditor
|
|
208
|
+
? describeWorkspaceAction(entry.category, entry.action, { includeEditor, editorContext })
|
|
209
|
+
: describeWorkspaceActionSummary(entry.category, entry.action));
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function workspaceActionLookupFromArgs(args: AgentHarnessWorkspaceActionArgs): { readonly source: WorkspaceActionLookup['source']; readonly input: string } | null {
|
|
213
|
+
const actionId = readString(args.actionId);
|
|
214
|
+
if (actionId) return { source: 'actionId', input: actionId };
|
|
215
|
+
const command = readString(args.command);
|
|
216
|
+
if (command) return { source: 'command', input: command };
|
|
217
|
+
const target = readString(args.target);
|
|
218
|
+
if (target) return { source: 'target', input: target };
|
|
219
|
+
const query = readString(args.query);
|
|
220
|
+
return query ? { source: 'query', input: query } : null;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function describeWorkspaceActionCandidates(
|
|
224
|
+
entries: readonly { readonly category: AgentWorkspaceCategory; readonly action: AgentWorkspaceAction }[],
|
|
225
|
+
): readonly { readonly actionId: string; readonly categoryId: string; readonly label: string; readonly command?: string }[] {
|
|
226
|
+
return entries.slice(0, 8).map((entry) => ({
|
|
227
|
+
actionId: entry.action.id,
|
|
228
|
+
categoryId: entry.category.id,
|
|
229
|
+
label: entry.action.label,
|
|
230
|
+
...(entry.action.command ? { command: entry.action.command } : {}),
|
|
231
|
+
}));
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
export function resolveWorkspaceActionDetail(args: AgentHarnessWorkspaceActionArgs): WorkspaceActionResolution | null {
|
|
235
|
+
const lookup = workspaceActionLookupFromArgs(args);
|
|
236
|
+
const categoryId = readString(args.categoryId || args.category);
|
|
237
|
+
if (!lookup) return null;
|
|
238
|
+
const entries = allWorkspaceActions().filter((entry) => !categoryId || entry.category.id === categoryId);
|
|
239
|
+
const normalized = lookup.input.toLowerCase();
|
|
240
|
+
const commandInput = lookup.source === 'command' ? lookup.input.trim() : '';
|
|
241
|
+
|
|
242
|
+
const exactId = entries.find((entry) => entry.action.id === lookup.input);
|
|
243
|
+
if (exactId) return { status: 'found', ...exactId, lookup: { ...lookup, resolvedBy: 'id' } };
|
|
244
|
+
const exactLabel = entries.find((entry) => entry.action.label === lookup.input);
|
|
245
|
+
if (exactLabel) return { status: 'found', ...exactLabel, lookup: { ...lookup, resolvedBy: 'label' } };
|
|
246
|
+
const exactCommand = commandInput ? entries.find((entry) => entry.action.command === commandInput) : null;
|
|
247
|
+
if (exactCommand) return { status: 'found', ...exactCommand, lookup: { ...lookup, resolvedBy: 'command' } };
|
|
248
|
+
|
|
249
|
+
const insensitiveId = entries.find((entry) => entry.action.id.toLowerCase() === normalized);
|
|
250
|
+
if (insensitiveId) return { status: 'found', ...insensitiveId, lookup: { ...lookup, resolvedBy: 'case-insensitive-id' } };
|
|
251
|
+
const insensitiveLabel = entries.find((entry) => entry.action.label.toLowerCase() === normalized);
|
|
252
|
+
if (insensitiveLabel) return { status: 'found', ...insensitiveLabel, lookup: { ...lookup, resolvedBy: 'case-insensitive-label' } };
|
|
253
|
+
|
|
254
|
+
const searched = searchAgentWorkspaceActions(AGENT_WORKSPACE_CATEGORIES, lookup.input)
|
|
255
|
+
.map((result) => ({ category: result.category, action: result.action }))
|
|
256
|
+
.filter((entry) => !categoryId || entry.category.id === categoryId);
|
|
257
|
+
if (searched.length === 1) return { status: 'found', ...searched[0]!, lookup: { ...lookup, resolvedBy: 'search' } };
|
|
258
|
+
if (searched.length > 1) return { status: 'ambiguous', input: lookup.input, candidates: describeWorkspaceActionCandidates(searched) };
|
|
259
|
+
return null;
|
|
260
|
+
}
|
|
@@ -261,13 +261,7 @@ export function createAgentKnowledgeIngestTool(
|
|
|
261
261
|
return {
|
|
262
262
|
definition: {
|
|
263
263
|
name: 'agent_knowledge_ingest',
|
|
264
|
-
description:
|
|
265
|
-
'Ingest explicit sources into isolated GoodVibes Agent Knowledge from the main conversation.',
|
|
266
|
-
'Use only when the user explicitly asks Agent to add, remember, import, or ingest a URL, local file, URL-list file, bookmarks file, browser history, or connector input into its Agent Knowledge.',
|
|
267
|
-
'This writes only to /api/goodvibes-agent/knowledge/* ingest routes on the connected GoodVibes host.',
|
|
268
|
-
'It must never call default knowledge, non-Agent knowledge spaces, separate Agent jobs, local schedulers, or delegated review.',
|
|
269
|
-
'Set confirm:true only for an explicit user request. Otherwise return the preview/confirmation error.',
|
|
270
|
-
].join(' '),
|
|
264
|
+
description: 'Ingest one confirmed source into isolated Agent Knowledge.',
|
|
271
265
|
parameters: {
|
|
272
266
|
type: 'object',
|
|
273
267
|
properties: {
|
|
@@ -282,7 +276,7 @@ export function createAgentKnowledgeIngestTool(
|
|
|
282
276
|
},
|
|
283
277
|
path: {
|
|
284
278
|
type: 'string',
|
|
285
|
-
description: 'Local file
|
|
279
|
+
description: 'Local file or import path.',
|
|
286
280
|
},
|
|
287
281
|
title: {
|
|
288
282
|
type: 'string',
|
|
@@ -299,7 +293,7 @@ export function createAgentKnowledgeIngestTool(
|
|
|
299
293
|
},
|
|
300
294
|
connectorId: {
|
|
301
295
|
type: 'string',
|
|
302
|
-
description: 'Connector id
|
|
296
|
+
description: 'Connector id.',
|
|
303
297
|
},
|
|
304
298
|
input: {
|
|
305
299
|
description: 'Connector input as JSON-compatible data or a JSON/text string.',
|
|
@@ -340,7 +334,7 @@ export function createAgentKnowledgeIngestTool(
|
|
|
340
334
|
},
|
|
341
335
|
explicitUserRequest: {
|
|
342
336
|
type: 'string',
|
|
343
|
-
description: '
|
|
337
|
+
description: 'User request authorizing this ingest.',
|
|
344
338
|
},
|
|
345
339
|
},
|
|
346
340
|
required: ['confirm', 'explicitUserRequest'],
|
|
@@ -4,29 +4,62 @@ import type { ShellPathService } from '@/runtime/index.ts';
|
|
|
4
4
|
import {
|
|
5
5
|
createAgentSdk,
|
|
6
6
|
classifyKnowledgeError,
|
|
7
|
+
getAgentKnowledgeJson,
|
|
7
8
|
resolveConnectedHostConnection,
|
|
9
|
+
validateAgentKnowledgeData,
|
|
8
10
|
type AgentKnowledgeFailure,
|
|
9
11
|
type AgentKnowledgeConnectionRuntime,
|
|
10
12
|
} from '../cli/agent-knowledge-runtime.ts';
|
|
11
13
|
import { AGENT_KNOWLEDGE_METHODS, type ConnectedHostCallMethod } from '../cli/agent-knowledge-methods.ts';
|
|
12
14
|
import {
|
|
13
15
|
formatAsk,
|
|
16
|
+
formatConnector,
|
|
17
|
+
formatConnectorDoctor,
|
|
18
|
+
formatConnectors,
|
|
19
|
+
formatEntityList,
|
|
14
20
|
formatFailure,
|
|
21
|
+
formatItem,
|
|
22
|
+
formatMap,
|
|
15
23
|
formatSearch,
|
|
16
24
|
formatStatus,
|
|
17
25
|
} from '../cli/agent-knowledge-format.ts';
|
|
18
26
|
|
|
19
|
-
export type AgentKnowledgeToolAction =
|
|
27
|
+
export type AgentKnowledgeToolAction =
|
|
28
|
+
| 'status'
|
|
29
|
+
| 'ask'
|
|
30
|
+
| 'search'
|
|
31
|
+
| 'sources'
|
|
32
|
+
| 'nodes'
|
|
33
|
+
| 'issues'
|
|
34
|
+
| 'item'
|
|
35
|
+
| 'map'
|
|
36
|
+
| 'connectors'
|
|
37
|
+
| 'connector'
|
|
38
|
+
| 'connector_doctor';
|
|
20
39
|
export type AgentKnowledgeAskMode = 'concise' | 'standard' | 'detailed';
|
|
21
40
|
|
|
22
41
|
export interface AgentKnowledgeToolArgs {
|
|
23
42
|
readonly action?: unknown;
|
|
24
43
|
readonly query?: unknown;
|
|
44
|
+
readonly id?: unknown;
|
|
45
|
+
readonly connectorId?: unknown;
|
|
25
46
|
readonly limit?: unknown;
|
|
26
47
|
readonly mode?: unknown;
|
|
27
48
|
}
|
|
28
49
|
|
|
29
|
-
const ACTIONS: readonly AgentKnowledgeToolAction[] = [
|
|
50
|
+
const ACTIONS: readonly AgentKnowledgeToolAction[] = [
|
|
51
|
+
'status',
|
|
52
|
+
'ask',
|
|
53
|
+
'search',
|
|
54
|
+
'sources',
|
|
55
|
+
'nodes',
|
|
56
|
+
'issues',
|
|
57
|
+
'item',
|
|
58
|
+
'map',
|
|
59
|
+
'connectors',
|
|
60
|
+
'connector',
|
|
61
|
+
'connector_doctor',
|
|
62
|
+
];
|
|
30
63
|
const ASK_MODES: readonly AgentKnowledgeAskMode[] = ['concise', 'standard', 'detailed'];
|
|
31
64
|
|
|
32
65
|
function isAction(value: unknown): value is AgentKnowledgeToolAction {
|
|
@@ -37,11 +70,11 @@ function readString(value: unknown): string {
|
|
|
37
70
|
return typeof value === 'string' ? value.trim() : '';
|
|
38
71
|
}
|
|
39
72
|
|
|
40
|
-
function readLimit(value: unknown, fallback: number): number {
|
|
41
|
-
if (typeof value === 'number' && Number.isInteger(value) && value > 0) return Math.min(value,
|
|
73
|
+
function readLimit(value: unknown, fallback: number, max = 25): number {
|
|
74
|
+
if (typeof value === 'number' && Number.isInteger(value) && value > 0) return Math.min(value, max);
|
|
42
75
|
if (typeof value === 'string' && value.trim()) {
|
|
43
76
|
const parsed = Number(value);
|
|
44
|
-
if (Number.isInteger(parsed) && parsed > 0) return Math.min(parsed,
|
|
77
|
+
if (Number.isInteger(parsed) && parsed > 0) return Math.min(parsed, max);
|
|
45
78
|
}
|
|
46
79
|
return fallback;
|
|
47
80
|
}
|
|
@@ -72,6 +105,17 @@ async function classifyToolKnowledgeError(
|
|
|
72
105
|
return toolFailure(formatKnowledgeFailure(await classifyKnowledgeError(error, connection, method.route)));
|
|
73
106
|
}
|
|
74
107
|
|
|
108
|
+
function validatedToolOutput<TData>(
|
|
109
|
+
data: TData,
|
|
110
|
+
connection: ReturnType<typeof resolveConnectedHostConnection>,
|
|
111
|
+
method: ConnectedHostCallMethod,
|
|
112
|
+
format: (validatedData: TData) => string,
|
|
113
|
+
): { readonly success: true; readonly output: string } | { readonly success: false; readonly error: string } {
|
|
114
|
+
const validated = validateAgentKnowledgeData(data, connection, method);
|
|
115
|
+
if (!validated.ok) return toolFailure(formatKnowledgeFailure(validated));
|
|
116
|
+
return toolOutput(format(validated.data));
|
|
117
|
+
}
|
|
118
|
+
|
|
75
119
|
export function createAgentKnowledgeTool(
|
|
76
120
|
shellPaths: ShellPathService,
|
|
77
121
|
configManager: AgentKnowledgeConnectionRuntime['configManager'],
|
|
@@ -79,12 +123,7 @@ export function createAgentKnowledgeTool(
|
|
|
79
123
|
return {
|
|
80
124
|
definition: {
|
|
81
125
|
name: 'agent_knowledge',
|
|
82
|
-
description:
|
|
83
|
-
'Read isolated GoodVibes Agent Knowledge from the main conversation.',
|
|
84
|
-
'Use for Agent-owned knowledge status, ask, and search only.',
|
|
85
|
-
'This tool calls /api/goodvibes-agent/knowledge/* on the connected GoodVibes host and must never fall back to default knowledge or non-Agent knowledge spaces.',
|
|
86
|
-
'It is read-only and does not ingest, reindex, mutate, or create background work.',
|
|
87
|
-
].join(' '),
|
|
126
|
+
description: 'Read isolated Agent Knowledge.',
|
|
88
127
|
parameters: {
|
|
89
128
|
type: 'object',
|
|
90
129
|
properties: {
|
|
@@ -95,11 +134,19 @@ export function createAgentKnowledgeTool(
|
|
|
95
134
|
},
|
|
96
135
|
query: {
|
|
97
136
|
type: 'string',
|
|
98
|
-
description: 'Question/query for ask or
|
|
137
|
+
description: 'Question/query for ask, search, or map filtering.',
|
|
138
|
+
},
|
|
139
|
+
id: {
|
|
140
|
+
type: 'string',
|
|
141
|
+
description: 'Agent Knowledge source, node, or issue id for action item.',
|
|
142
|
+
},
|
|
143
|
+
connectorId: {
|
|
144
|
+
type: 'string',
|
|
145
|
+
description: 'Connector id for action connector or connector_doctor. id may also be used.',
|
|
99
146
|
},
|
|
100
147
|
limit: {
|
|
101
148
|
type: 'number',
|
|
102
|
-
description: 'Maximum
|
|
149
|
+
description: 'Maximum result count.',
|
|
103
150
|
},
|
|
104
151
|
mode: {
|
|
105
152
|
type: 'string',
|
|
@@ -125,13 +172,16 @@ export function createAgentKnowledgeTool(
|
|
|
125
172
|
route: '/api/goodvibes-agent/knowledge/*',
|
|
126
173
|
}));
|
|
127
174
|
}
|
|
175
|
+
let method: ConnectedHostCallMethod = AGENT_KNOWLEDGE_METHODS.status;
|
|
128
176
|
try {
|
|
129
177
|
const sdk = createAgentSdk(connection);
|
|
130
178
|
if (args.action === 'status') {
|
|
179
|
+
method = AGENT_KNOWLEDGE_METHODS.status;
|
|
131
180
|
const data = await sdk.knowledge.status();
|
|
132
|
-
return
|
|
181
|
+
return validatedToolOutput(data, connection, method, formatStatus);
|
|
133
182
|
}
|
|
134
183
|
if (args.action === 'ask') {
|
|
184
|
+
method = AGENT_KNOWLEDGE_METHODS.ask;
|
|
135
185
|
const query = readString(args.query);
|
|
136
186
|
if (!query) return toolFailure('query is required for Agent Knowledge ask.');
|
|
137
187
|
const data = await sdk.knowledge.ask({
|
|
@@ -142,19 +192,64 @@ export function createAgentKnowledgeTool(
|
|
|
142
192
|
includeConfidence: true,
|
|
143
193
|
includeLinkedObjects: true,
|
|
144
194
|
});
|
|
145
|
-
return
|
|
195
|
+
return validatedToolOutput(data, connection, method, (validatedData) => formatAsk(validatedData, query));
|
|
196
|
+
}
|
|
197
|
+
if (args.action === 'search') {
|
|
198
|
+
method = AGENT_KNOWLEDGE_METHODS.search;
|
|
199
|
+
const query = readString(args.query);
|
|
200
|
+
if (!query) return toolFailure('query is required for Agent Knowledge search.');
|
|
201
|
+
const data = await sdk.knowledge.search({ query, limit: readLimit(args.limit, 10) });
|
|
202
|
+
return validatedToolOutput(data, connection, method, (validatedData) => formatSearch(validatedData, query));
|
|
203
|
+
}
|
|
204
|
+
if (args.action === 'sources' || args.action === 'nodes' || args.action === 'issues') {
|
|
205
|
+
const kind = args.action;
|
|
206
|
+
const limit = readLimit(args.limit, 25, 500);
|
|
207
|
+
method = kind === 'sources'
|
|
208
|
+
? AGENT_KNOWLEDGE_METHODS.sourcesList
|
|
209
|
+
: kind === 'nodes'
|
|
210
|
+
? AGENT_KNOWLEDGE_METHODS.nodesList
|
|
211
|
+
: AGENT_KNOWLEDGE_METHODS.issuesList;
|
|
212
|
+
const data = await getAgentKnowledgeJson(connection, method.route, { limit });
|
|
213
|
+
return validatedToolOutput(data, connection, method, (validatedData) => formatEntityList(validatedData, kind, limit));
|
|
214
|
+
}
|
|
215
|
+
if (args.action === 'item') {
|
|
216
|
+
method = AGENT_KNOWLEDGE_METHODS.itemGet;
|
|
217
|
+
const id = readString(args.id);
|
|
218
|
+
if (!id) return toolFailure('id is required for Agent Knowledge item lookup.');
|
|
219
|
+
const route = `/api/goodvibes-agent/knowledge/items/${encodeURIComponent(id)}`;
|
|
220
|
+
const data = await getAgentKnowledgeJson(connection, route);
|
|
221
|
+
return validatedToolOutput(data, connection, { ...method, route }, (validatedData) => formatItem(validatedData, id));
|
|
222
|
+
}
|
|
223
|
+
if (args.action === 'map') {
|
|
224
|
+
method = AGENT_KNOWLEDGE_METHODS.map;
|
|
225
|
+
const data = await getAgentKnowledgeJson(connection, method.route, {
|
|
226
|
+
limit: readLimit(args.limit, 50, 500),
|
|
227
|
+
query: readString(args.query),
|
|
228
|
+
});
|
|
229
|
+
return validatedToolOutput(data, connection, method, formatMap);
|
|
230
|
+
}
|
|
231
|
+
if (args.action === 'connectors') {
|
|
232
|
+
method = AGENT_KNOWLEDGE_METHODS.connectorsList;
|
|
233
|
+
const data = await getAgentKnowledgeJson(connection, method.route);
|
|
234
|
+
return validatedToolOutput(data, connection, method, formatConnectors);
|
|
235
|
+
}
|
|
236
|
+
if (args.action === 'connector' || args.action === 'connector_doctor') {
|
|
237
|
+
method = args.action === 'connector'
|
|
238
|
+
? AGENT_KNOWLEDGE_METHODS.connectorGet
|
|
239
|
+
: AGENT_KNOWLEDGE_METHODS.connectorDoctor;
|
|
240
|
+
const connectorId = readString(args.connectorId) || readString(args.id);
|
|
241
|
+
if (!connectorId) return toolFailure('connectorId or id is required for Agent Knowledge connector lookup.');
|
|
242
|
+
const suffix = args.action === 'connector_doctor' ? '/doctor' : '';
|
|
243
|
+
const route = `/api/goodvibes-agent/knowledge/connectors/${encodeURIComponent(connectorId)}${suffix}`;
|
|
244
|
+
const data = await getAgentKnowledgeJson(connection, route);
|
|
245
|
+
return validatedToolOutput(data, connection, { ...method, route }, (validatedData) => (
|
|
246
|
+
args.action === 'connector'
|
|
247
|
+
? formatConnector(validatedData, connectorId)
|
|
248
|
+
: formatConnectorDoctor(validatedData, connectorId)
|
|
249
|
+
));
|
|
146
250
|
}
|
|
147
|
-
|
|
148
|
-
if (!query) return toolFailure('query is required for Agent Knowledge search.');
|
|
149
|
-
const method = AGENT_KNOWLEDGE_METHODS.search;
|
|
150
|
-
const data = await sdk.knowledge.search({ query, limit: readLimit(args.limit, 10) });
|
|
151
|
-
return toolOutput(formatSearch(data, query));
|
|
251
|
+
return toolFailure(`Unknown Agent Knowledge action. Valid values ${ACTIONS.join(', ')}.`);
|
|
152
252
|
} catch (error) {
|
|
153
|
-
const method = args.action === 'status'
|
|
154
|
-
? AGENT_KNOWLEDGE_METHODS.status
|
|
155
|
-
: args.action === 'ask'
|
|
156
|
-
? AGENT_KNOWLEDGE_METHODS.ask
|
|
157
|
-
: AGENT_KNOWLEDGE_METHODS.search;
|
|
158
253
|
return classifyToolKnowledgeError(error, connection, method);
|
|
159
254
|
}
|
|
160
255
|
},
|
|
@@ -735,11 +735,7 @@ export function createAgentLocalRegistryTool(shellPaths: ShellPathService, memor
|
|
|
735
735
|
return {
|
|
736
736
|
definition: {
|
|
737
737
|
name: 'agent_local_registry',
|
|
738
|
-
description:
|
|
739
|
-
'Inspect and maintain GoodVibes Agent-local notes, memory, personas, skills, and routines from the main conversation.',
|
|
740
|
-
'Use this for safe self-improvement: capture scratchpad notes, remember durable non-secret facts, create or refine reusable behavior, bundle related skills, enable skills/routines, choose personas, review/stale records, and start routines in the same serial conversation.',
|
|
741
|
-
'Destructive record deletion requires confirm:true plus explicitUserRequest. This tool cannot create schedules, mutate connected hosts, send messages, run background jobs, or delegate build work.',
|
|
742
|
-
].join(' '),
|
|
738
|
+
description: 'Inspect or update Agent-local records.',
|
|
743
739
|
parameters: {
|
|
744
740
|
type: 'object',
|
|
745
741
|
properties: {
|
|
@@ -761,13 +757,13 @@ export function createAgentLocalRegistryTool(shellPaths: ShellPathService, memor
|
|
|
761
757
|
steps: { type: 'string', description: 'Routine steps.' },
|
|
762
758
|
skills: { type: 'array', items: { type: 'string' }, description: 'Skill ids for skill_bundle create/update.' },
|
|
763
759
|
skillIds: { type: 'array', items: { type: 'string' }, description: 'Skill ids for skill_bundle create/update.' },
|
|
764
|
-
requiresEnv: { type: 'array', items: { type: 'string' }, description: '
|
|
760
|
+
requiresEnv: { type: 'array', items: { type: 'string' }, description: 'Required environment variable names.' },
|
|
765
761
|
requiresCommands: { type: 'array', items: { type: 'string' }, description: 'Command names required by a skill or routine.' },
|
|
766
762
|
triggers: { type: 'array', items: { type: 'string' } },
|
|
767
763
|
tags: { type: 'array', items: { type: 'string' } },
|
|
768
764
|
reason: { type: 'string' },
|
|
769
765
|
enabled: { type: 'boolean' },
|
|
770
|
-
activate: { type: 'boolean', description: 'Activate
|
|
766
|
+
activate: { type: 'boolean', description: 'Activate or clear the persona.' },
|
|
771
767
|
provenance: { type: 'string' },
|
|
772
768
|
confirm: { type: 'boolean', description: 'Required true for delete after an explicit user request.' },
|
|
773
769
|
explicitUserRequest: { type: 'string', description: 'Exact user request or faithful short summary authorizing delete.' },
|
|
@@ -52,13 +52,7 @@ export function createAgentMediaGenerateTool(
|
|
|
52
52
|
return {
|
|
53
53
|
definition: {
|
|
54
54
|
name: 'agent_media_generate',
|
|
55
|
-
description:
|
|
56
|
-
'Generate image or video artifacts through configured GoodVibes Agent media providers from the main conversation.',
|
|
57
|
-
'Use only when the user explicitly asks Agent to generate media.',
|
|
58
|
-
'Generated bytes are stored as GoodVibes artifacts and the response returns artifact ids; do not print inline base64.',
|
|
59
|
-
'This tool does not use default knowledge, non-Agent knowledge segments, connected-host lifecycle routes, separate Agent jobs, delegated review, channel sends, or arbitrary route invocation.',
|
|
60
|
-
'Set confirm:true only for an explicit user request. Otherwise return the preview/confirmation error.',
|
|
61
|
-
].join(' '),
|
|
55
|
+
description: 'Generate one confirmed image or video artifact.',
|
|
62
56
|
parameters: {
|
|
63
57
|
type: 'object',
|
|
64
58
|
properties: {
|
|
@@ -68,7 +62,7 @@ export function createAgentMediaGenerateTool(
|
|
|
68
62
|
},
|
|
69
63
|
providerId: {
|
|
70
64
|
type: 'string',
|
|
71
|
-
description: 'Optional
|
|
65
|
+
description: 'Optional media provider id.',
|
|
72
66
|
},
|
|
73
67
|
modelId: {
|
|
74
68
|
type: 'string',
|