@pellux/goodvibes-agent 1.1.2 → 1.1.4
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 +12 -0
- package/README.md +1 -1
- package/dist/package/main.js +862 -337
- package/docs/channels-remote-and-api.md +1 -1
- package/docs/connected-host.md +1 -1
- package/docs/getting-started.md +1 -1
- package/docs/knowledge-artifacts-and-multimodal.md +1 -1
- package/docs/tools-and-commands.md +1 -1
- package/package.json +2 -2
- package/release/release-notes.md +1 -1
- package/release/release-readiness.json +4 -4
- package/src/agent/knowledge-scope-alias.ts +56 -0
- package/src/cli/agent-knowledge-runtime.ts +25 -9
- package/src/input/agent-workspace-activation.ts +11 -1
- package/src/input/agent-workspace-categories.ts +225 -69
- package/src/input/agent-workspace-category-actions.ts +25 -0
- package/src/input/agent-workspace-settings.ts +314 -0
- package/src/input/agent-workspace-types.ts +14 -1
- package/src/input/agent-workspace.ts +69 -34
- package/src/renderer/agent-workspace.ts +13 -12
- package/src/tools/agent-harness-ui-surface-metadata.ts +1 -1
- package/src/tools/agent-harness-workspace-actions.ts +2 -2
- package/src/version.ts +1 -1
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
+
import type { ConfigKey, ConfigSetting } from '@pellux/goodvibes-sdk/platform/config';
|
|
3
|
+
import { setHarnessSetting } from '../agent/harness-control.ts';
|
|
4
|
+
import { isExternalHostOwnedSettingKey } from '../config/agent-settings-policy.ts';
|
|
5
|
+
import { buildAgentWorkspaceRuntimeSnapshot } from './agent-workspace-snapshot.ts';
|
|
6
|
+
import type { CommandContext } from './command-registry.ts';
|
|
7
|
+
import type {
|
|
8
|
+
AgentWorkspaceAction,
|
|
9
|
+
AgentWorkspaceActionResult,
|
|
10
|
+
AgentWorkspaceLocalEditor,
|
|
11
|
+
AgentWorkspaceRuntimeSnapshot,
|
|
12
|
+
} from './agent-workspace-types.ts';
|
|
13
|
+
|
|
14
|
+
const GOODVIBES_TUI_SURFACE_ROOT = 'tui';
|
|
15
|
+
|
|
16
|
+
const TUI_IMPORTABLE_SETTING_PREFIXES = [
|
|
17
|
+
'display.',
|
|
18
|
+
'provider.',
|
|
19
|
+
'behavior.',
|
|
20
|
+
'storage.',
|
|
21
|
+
'permissions.',
|
|
22
|
+
'ui.',
|
|
23
|
+
'tts.',
|
|
24
|
+
'surfaces.',
|
|
25
|
+
'helper.',
|
|
26
|
+
'tools.',
|
|
27
|
+
'release.',
|
|
28
|
+
'automation.',
|
|
29
|
+
] as const;
|
|
30
|
+
|
|
31
|
+
type SettingActionEffect =
|
|
32
|
+
| {
|
|
33
|
+
readonly kind: 'result';
|
|
34
|
+
readonly status: string;
|
|
35
|
+
readonly result: AgentWorkspaceActionResult;
|
|
36
|
+
}
|
|
37
|
+
| {
|
|
38
|
+
readonly kind: 'apply';
|
|
39
|
+
readonly setting: ConfigSetting;
|
|
40
|
+
readonly value: unknown;
|
|
41
|
+
}
|
|
42
|
+
| {
|
|
43
|
+
readonly kind: 'editor';
|
|
44
|
+
readonly editor: AgentWorkspaceLocalEditor;
|
|
45
|
+
readonly status: string;
|
|
46
|
+
readonly result: AgentWorkspaceActionResult;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export interface SettingMutationOutcome {
|
|
50
|
+
readonly status: string;
|
|
51
|
+
readonly result: AgentWorkspaceActionResult;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface TuiSettingsImportOutcome extends SettingMutationOutcome {
|
|
55
|
+
readonly runtimeSnapshot: AgentWorkspaceRuntimeSnapshot | null;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
59
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function readJsonRecord(path: string): Record<string, unknown> | null {
|
|
63
|
+
if (!existsSync(path)) return null;
|
|
64
|
+
const parsed = JSON.parse(readFileSync(path, 'utf-8')) as unknown;
|
|
65
|
+
return isRecord(parsed) ? parsed : null;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function readNestedSettingValue(record: Record<string, unknown>, key: string): unknown {
|
|
69
|
+
let cursor: unknown = record;
|
|
70
|
+
for (const part of key.split('.')) {
|
|
71
|
+
if (!isRecord(cursor) || !(part in cursor)) return undefined;
|
|
72
|
+
cursor = cursor[part];
|
|
73
|
+
}
|
|
74
|
+
return cursor;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function canImportTuiSetting(key: string): boolean {
|
|
78
|
+
return !isExternalHostOwnedSettingKey(key)
|
|
79
|
+
&& TUI_IMPORTABLE_SETTING_PREFIXES.some((prefix) => key.startsWith(prefix));
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function valuesMatch(left: unknown, right: unknown): boolean {
|
|
83
|
+
return JSON.stringify(left) === JSON.stringify(right);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function agentWorkspaceSettingSchema(context: CommandContext | null, key: string): ConfigSetting | null {
|
|
87
|
+
return context?.platform?.configManager
|
|
88
|
+
?.getSchema()
|
|
89
|
+
.find((setting) => setting.key === key) ?? null;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export function isAgentWorkspaceActionVisible(context: CommandContext | null, action: AgentWorkspaceAction): boolean {
|
|
93
|
+
const key = action.visibleWhenSettingKey?.trim();
|
|
94
|
+
if (!key) return true;
|
|
95
|
+
const configManager = context?.platform?.configManager;
|
|
96
|
+
if (!configManager) return false;
|
|
97
|
+
return configManager.get(key as ConfigKey) === action.visibleWhenSettingValue;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function buildAgentWorkspaceSettingActionEffect(
|
|
101
|
+
context: CommandContext | null,
|
|
102
|
+
action: AgentWorkspaceAction,
|
|
103
|
+
): SettingActionEffect {
|
|
104
|
+
const settingKey = action.settingKey?.trim();
|
|
105
|
+
const configManager = context?.platform?.configManager;
|
|
106
|
+
if (!settingKey || !configManager) {
|
|
107
|
+
return {
|
|
108
|
+
kind: 'result',
|
|
109
|
+
status: 'Setting is unavailable in this runtime.',
|
|
110
|
+
result: {
|
|
111
|
+
kind: 'error',
|
|
112
|
+
title: 'Setting unavailable',
|
|
113
|
+
detail: action.detail,
|
|
114
|
+
safety: action.safety,
|
|
115
|
+
},
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
const setting = agentWorkspaceSettingSchema(context, settingKey);
|
|
119
|
+
if (!setting) {
|
|
120
|
+
return {
|
|
121
|
+
kind: 'result',
|
|
122
|
+
status: `Unknown setting: ${settingKey}`,
|
|
123
|
+
result: {
|
|
124
|
+
kind: 'error',
|
|
125
|
+
title: 'Unknown setting',
|
|
126
|
+
detail: `No Agent setting exists for ${settingKey}.`,
|
|
127
|
+
safety: action.safety,
|
|
128
|
+
},
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (action.settingValueHint !== undefined) {
|
|
133
|
+
return { kind: 'apply', setting, value: action.settingValueHint };
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const currentValue = configManager.get(setting.key as ConfigKey);
|
|
137
|
+
if (setting.type === 'boolean') {
|
|
138
|
+
return { kind: 'apply', setting, value: !Boolean(currentValue) };
|
|
139
|
+
}
|
|
140
|
+
if (setting.type === 'enum' && setting.enumValues && setting.enumValues.length > 0) {
|
|
141
|
+
const currentIndex = Math.max(0, setting.enumValues.indexOf(String(currentValue)));
|
|
142
|
+
return { kind: 'apply', setting, value: setting.enumValues[(currentIndex + 1) % setting.enumValues.length]! };
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return {
|
|
146
|
+
kind: 'editor',
|
|
147
|
+
editor: createSettingEditor(setting, String(currentValue ?? ''), action),
|
|
148
|
+
status: `Editing ${setting.key}.`,
|
|
149
|
+
result: {
|
|
150
|
+
kind: 'guidance',
|
|
151
|
+
title: `Edit ${setting.key}`,
|
|
152
|
+
detail: setting.description,
|
|
153
|
+
safety: action.safety,
|
|
154
|
+
},
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export async function applyAgentWorkspaceSettingValue(
|
|
159
|
+
context: CommandContext | null,
|
|
160
|
+
setting: ConfigSetting,
|
|
161
|
+
value: unknown,
|
|
162
|
+
): Promise<SettingMutationOutcome> {
|
|
163
|
+
const configManager = context?.platform?.configManager;
|
|
164
|
+
if (!configManager) {
|
|
165
|
+
return {
|
|
166
|
+
status: 'Setting is unavailable in this runtime.',
|
|
167
|
+
result: {
|
|
168
|
+
kind: 'error',
|
|
169
|
+
title: `${setting.key} update failed`,
|
|
170
|
+
detail: 'The Agent workspace has no config manager for this runtime.',
|
|
171
|
+
safety: 'safe',
|
|
172
|
+
},
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
try {
|
|
176
|
+
const result = await setHarnessSetting(configManager, context?.platform?.secretsManager, setting.key, value);
|
|
177
|
+
return {
|
|
178
|
+
status: `${result.key} set.`,
|
|
179
|
+
result: {
|
|
180
|
+
kind: 'refreshed',
|
|
181
|
+
title: `${result.key} updated`,
|
|
182
|
+
detail: `Current value: ${String(result.current)}`,
|
|
183
|
+
safety: 'safe',
|
|
184
|
+
},
|
|
185
|
+
};
|
|
186
|
+
} catch (error) {
|
|
187
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
188
|
+
return {
|
|
189
|
+
status: detail,
|
|
190
|
+
result: {
|
|
191
|
+
kind: 'error',
|
|
192
|
+
title: `${setting.key} update failed`,
|
|
193
|
+
detail,
|
|
194
|
+
safety: 'safe',
|
|
195
|
+
},
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
export async function importAgentWorkspaceTuiSettings(context: CommandContext | null): Promise<TuiSettingsImportOutcome> {
|
|
201
|
+
const shellPaths = context?.workspace?.shellPaths;
|
|
202
|
+
const configManager = context?.platform?.configManager;
|
|
203
|
+
if (!shellPaths || !configManager) {
|
|
204
|
+
return {
|
|
205
|
+
status: 'GoodVibes TUI settings import is unavailable in this runtime.',
|
|
206
|
+
runtimeSnapshot: null,
|
|
207
|
+
result: {
|
|
208
|
+
kind: 'error',
|
|
209
|
+
title: 'Import unavailable',
|
|
210
|
+
detail: 'The workspace cannot locate shell paths or the Agent config manager.',
|
|
211
|
+
safety: 'safe',
|
|
212
|
+
},
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const sources = [
|
|
217
|
+
{ label: 'user', path: shellPaths.resolveUserPath(GOODVIBES_TUI_SURFACE_ROOT, 'settings.json') },
|
|
218
|
+
{ label: 'project', path: shellPaths.resolveProjectPath(GOODVIBES_TUI_SURFACE_ROOT, 'settings.json') },
|
|
219
|
+
];
|
|
220
|
+
const values = new Map<string, { readonly value: unknown; readonly source: string }>();
|
|
221
|
+
const parseErrors: string[] = [];
|
|
222
|
+
for (const source of sources) {
|
|
223
|
+
try {
|
|
224
|
+
const record = readJsonRecord(source.path);
|
|
225
|
+
if (!record) continue;
|
|
226
|
+
for (const setting of configManager.getSchema()) {
|
|
227
|
+
if (!canImportTuiSetting(setting.key)) continue;
|
|
228
|
+
const value = readNestedSettingValue(record, setting.key);
|
|
229
|
+
if (value !== undefined) values.set(setting.key, { value, source: source.label });
|
|
230
|
+
}
|
|
231
|
+
} catch (error) {
|
|
232
|
+
parseErrors.push(`${source.label}: ${error instanceof Error ? error.message : String(error)}`);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if (values.size === 0) {
|
|
237
|
+
const detail = parseErrors.length > 0
|
|
238
|
+
? `No importable settings found. ${parseErrors.join('; ')}`
|
|
239
|
+
: 'No GoodVibes TUI settings file with importable Agent-owned settings was found.';
|
|
240
|
+
return {
|
|
241
|
+
status: 'No GoodVibes TUI settings imported.',
|
|
242
|
+
runtimeSnapshot: null,
|
|
243
|
+
result: {
|
|
244
|
+
kind: parseErrors.length > 0 ? 'error' : 'guidance',
|
|
245
|
+
title: 'Nothing imported',
|
|
246
|
+
detail,
|
|
247
|
+
safety: 'safe',
|
|
248
|
+
},
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const imported: string[] = [];
|
|
253
|
+
const unchanged: string[] = [];
|
|
254
|
+
const skipped: string[] = [];
|
|
255
|
+
for (const [key, entry] of values) {
|
|
256
|
+
const setting = agentWorkspaceSettingSchema(context, key);
|
|
257
|
+
if (!setting) continue;
|
|
258
|
+
if (valuesMatch(configManager.get(setting.key as ConfigKey), entry.value)) {
|
|
259
|
+
unchanged.push(setting.key);
|
|
260
|
+
continue;
|
|
261
|
+
}
|
|
262
|
+
try {
|
|
263
|
+
await setHarnessSetting(configManager, context?.platform?.secretsManager, setting.key, entry.value);
|
|
264
|
+
imported.push(`${setting.key} (${entry.source})`);
|
|
265
|
+
} catch (error) {
|
|
266
|
+
skipped.push(`${setting.key}: ${error instanceof Error ? error.message : String(error)}`);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
return {
|
|
271
|
+
status: imported.length > 0 ? `Imported ${imported.length} GoodVibes TUI setting(s).` : 'No GoodVibes TUI settings changed.',
|
|
272
|
+
runtimeSnapshot: context ? buildAgentWorkspaceRuntimeSnapshot(context) : null,
|
|
273
|
+
result: {
|
|
274
|
+
kind: skipped.length > 0 && imported.length === 0 ? 'error' : imported.length > 0 ? 'refreshed' : 'guidance',
|
|
275
|
+
title: imported.length > 0 ? 'GoodVibes TUI settings imported' : 'No settings changed',
|
|
276
|
+
detail: [
|
|
277
|
+
imported.length > 0 ? `Imported: ${imported.slice(0, 10).join(', ')}${imported.length > 10 ? `, +${imported.length - 10} more` : ''}.` : '',
|
|
278
|
+
unchanged.length > 0 ? `${unchanged.length} setting(s) already matched.` : '',
|
|
279
|
+
skipped.length > 0 ? `Skipped: ${skipped.slice(0, 5).join('; ')}${skipped.length > 5 ? `; +${skipped.length - 5} more` : ''}.` : '',
|
|
280
|
+
parseErrors.length > 0 ? `Parse issues: ${parseErrors.join('; ')}.` : '',
|
|
281
|
+
].filter((line) => line.length > 0).join(' '),
|
|
282
|
+
safety: 'safe',
|
|
283
|
+
},
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
function createSettingEditor(setting: ConfigSetting, currentValue: string, action: AgentWorkspaceAction): AgentWorkspaceLocalEditor {
|
|
288
|
+
const valueHint = setting.type === 'number'
|
|
289
|
+
? 'Enter a number.'
|
|
290
|
+
: setting.type === 'string'
|
|
291
|
+
? 'Enter a value. Leave empty to clear it.'
|
|
292
|
+
: setting.enumValues
|
|
293
|
+
? `Allowed values: ${setting.enumValues.join(', ')}.`
|
|
294
|
+
: action.detail;
|
|
295
|
+
return {
|
|
296
|
+
kind: 'setting-set',
|
|
297
|
+
mode: 'update',
|
|
298
|
+
recordId: setting.key,
|
|
299
|
+
title: `Set ${setting.key}`,
|
|
300
|
+
selectedFieldIndex: 0,
|
|
301
|
+
message: action.detail,
|
|
302
|
+
fields: [
|
|
303
|
+
{
|
|
304
|
+
id: 'value',
|
|
305
|
+
label: setting.key,
|
|
306
|
+
value: currentValue,
|
|
307
|
+
required: false,
|
|
308
|
+
multiline: false,
|
|
309
|
+
hint: action.settingValueHint ?? valueHint,
|
|
310
|
+
redact: /(?:secret|token|password|api[-_.]?key|signing)/i.test(setting.key),
|
|
311
|
+
},
|
|
312
|
+
],
|
|
313
|
+
};
|
|
314
|
+
}
|
|
@@ -10,6 +10,14 @@ export type AgentWorkspaceFocusPane = 'categories' | 'actions';
|
|
|
10
10
|
export const AGENT_WORKSPACE_CATEGORY_IDS = [
|
|
11
11
|
'home',
|
|
12
12
|
'setup',
|
|
13
|
+
'account-model',
|
|
14
|
+
'assistant-behavior',
|
|
15
|
+
'tools-permissions',
|
|
16
|
+
'onboarding-display',
|
|
17
|
+
'onboarding-channels',
|
|
18
|
+
'onboarding-voice-media',
|
|
19
|
+
'onboarding-context',
|
|
20
|
+
'onboarding-verify',
|
|
13
21
|
'research',
|
|
14
22
|
'artifacts',
|
|
15
23
|
'conversation',
|
|
@@ -32,7 +40,7 @@ export const AGENT_WORKSPACE_CATEGORY_IDS = [
|
|
|
32
40
|
|
|
33
41
|
export type AgentWorkspaceCategoryId = (typeof AGENT_WORKSPACE_CATEGORY_IDS)[number];
|
|
34
42
|
|
|
35
|
-
export type AgentWorkspaceActionKind = 'command' | 'guidance' | 'workspace' | 'editor' | 'local-selection' | 'local-operation' | 'onboarding-complete';
|
|
43
|
+
export type AgentWorkspaceActionKind = 'command' | 'guidance' | 'workspace' | 'editor' | 'setting' | 'settings-import' | 'local-selection' | 'local-operation' | 'onboarding-complete';
|
|
36
44
|
|
|
37
45
|
export type AgentWorkspaceLocalEditorKind = 'memory' | 'note' | 'persona' | 'skill' | 'routine' | 'profile';
|
|
38
46
|
|
|
@@ -178,6 +186,7 @@ export type AgentWorkspaceEditorKind =
|
|
|
178
186
|
| 'schedule-receipt'
|
|
179
187
|
| 'mode-preset'
|
|
180
188
|
| 'mode-domain'
|
|
189
|
+
| 'setting-set'
|
|
181
190
|
| 'model-pin'
|
|
182
191
|
| 'model-unpin'
|
|
183
192
|
| 'delegate-task'
|
|
@@ -247,6 +256,10 @@ export interface AgentWorkspaceAction {
|
|
|
247
256
|
readonly command?: string;
|
|
248
257
|
readonly targetCategoryId?: AgentWorkspaceCategoryId;
|
|
249
258
|
readonly editorKind?: AgentWorkspaceEditorKind;
|
|
259
|
+
readonly settingKey?: string;
|
|
260
|
+
readonly settingValueHint?: string;
|
|
261
|
+
readonly visibleWhenSettingKey?: string;
|
|
262
|
+
readonly visibleWhenSettingValue?: string | boolean | number;
|
|
250
263
|
readonly localKind?: AgentWorkspaceLocalEditorKind;
|
|
251
264
|
readonly selectionDelta?: number;
|
|
252
265
|
readonly localOperation?: AgentWorkspaceLocalOperation;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { MemoryApi } from '@pellux/goodvibes-sdk/platform/knowledge';
|
|
2
2
|
import type { MemoryRecord } from '@pellux/goodvibes-sdk/platform/state';
|
|
3
|
+
import type { ConfigSetting } from '@pellux/goodvibes-sdk/platform/config';
|
|
3
4
|
import type { ShellPathService } from '@/runtime/index.ts';
|
|
4
5
|
import type { CommandContext } from './command-registry.ts';
|
|
5
6
|
import { AgentNoteRegistry } from '../agent/note-registry.ts';
|
|
@@ -18,6 +19,7 @@ import { deleteAgentWorkspaceMemoryEditor, submitAgentWorkspaceMemoryEditor } fr
|
|
|
18
19
|
import { jumpAgentWorkspaceSelection, moveAgentWorkspaceSelection, selectAgentWorkspaceCategory } from './agent-workspace-navigation.ts';
|
|
19
20
|
import { buildAgentWorkspaceRequirements } from './agent-workspace-requirements.ts';
|
|
20
21
|
import { appendAgentWorkspaceActionSearchText, backspaceAgentWorkspaceActionSearch, beginAgentWorkspaceActionSearch, clearAgentWorkspaceActionSearch, commitAgentWorkspaceActionSearchSelection, searchAgentWorkspaceActions } from './agent-workspace-search.ts';
|
|
22
|
+
import { agentWorkspaceSettingSchema, applyAgentWorkspaceSettingValue, buildAgentWorkspaceSettingActionEffect, importAgentWorkspaceTuiSettings, isAgentWorkspaceActionVisible } from './agent-workspace-settings.ts';
|
|
21
23
|
import { buildAgentWorkspaceRuntimeSnapshot } from './agent-workspace-snapshot.ts';
|
|
22
24
|
import type { AgentWorkspaceAction, AgentWorkspaceActionResult, AgentWorkspaceActionSearchResult, AgentWorkspaceCategory, AgentWorkspaceCommandDispatcher, AgentWorkspaceEditorField, AgentWorkspaceFocusPane, AgentWorkspaceLocalEditor, AgentWorkspaceLocalEditorKind, AgentWorkspaceLocalLibraryItem, AgentWorkspaceLocalOperation, AgentWorkspacePromptDispatcher, AgentWorkspaceRuntimeSnapshot } from './agent-workspace-types.ts';
|
|
23
25
|
import { writeOnboardingCheckMarker, writeOnboardingCompletionMarker } from '../runtime/onboarding/index.ts';
|
|
@@ -39,14 +41,7 @@ export class AgentWorkspace {
|
|
|
39
41
|
public localEditor: AgentWorkspaceLocalEditor | null = null;
|
|
40
42
|
public actionSearchActive = false;
|
|
41
43
|
public actionSearchQuery = '';
|
|
42
|
-
public readonly selectedLibraryItemIndexes: AgentWorkspaceLocalSelectionIndexes = {
|
|
43
|
-
memory: 0,
|
|
44
|
-
note: 0,
|
|
45
|
-
persona: 0,
|
|
46
|
-
skill: 0,
|
|
47
|
-
routine: 0,
|
|
48
|
-
profile: 0,
|
|
49
|
-
};
|
|
44
|
+
public readonly selectedLibraryItemIndexes: AgentWorkspaceLocalSelectionIndexes = { memory: 0, note: 0, persona: 0, skill: 0, routine: 0, profile: 0 };
|
|
50
45
|
private context: CommandContext | null = null;
|
|
51
46
|
private dispatchCommand: AgentWorkspaceCommandDispatcher | null = null;
|
|
52
47
|
private dispatchPrompt: AgentWorkspacePromptDispatcher | null = null;
|
|
@@ -63,7 +58,10 @@ export class AgentWorkspace {
|
|
|
63
58
|
this.localEditor = null;
|
|
64
59
|
this.actionSearchActive = false;
|
|
65
60
|
this.actionSearchQuery = '';
|
|
66
|
-
if (
|
|
61
|
+
if (!categoryId) {
|
|
62
|
+
this.selectedCategoryIndex = 0;
|
|
63
|
+
this.selectedActionIndex = 0;
|
|
64
|
+
} else if (!this.selectCategory(categoryId)) {
|
|
67
65
|
const normalized = categoryId.trim();
|
|
68
66
|
this.status = `Unknown Agent workspace area: ${normalized}`;
|
|
69
67
|
this.lastActionResult = {
|
|
@@ -98,7 +96,7 @@ export class AgentWorkspace {
|
|
|
98
96
|
|
|
99
97
|
get actions(): readonly AgentWorkspaceAction[] {
|
|
100
98
|
if (this.actionSearchActive) return this.actionSearchResults.map((result) => result.action);
|
|
101
|
-
return this.selectedCategory.actions;
|
|
99
|
+
return this.selectedCategory.actions.filter((action) => isAgentWorkspaceActionVisible(this.context, action));
|
|
102
100
|
}
|
|
103
101
|
|
|
104
102
|
get selectedAction(): AgentWorkspaceAction | null {
|
|
@@ -111,7 +109,12 @@ export class AgentWorkspace {
|
|
|
111
109
|
}
|
|
112
110
|
|
|
113
111
|
get actionSearchResults(): readonly AgentWorkspaceActionSearchResult[] {
|
|
114
|
-
|
|
112
|
+
if (!this.actionSearchActive) return [];
|
|
113
|
+
const categories = this.categories.map((category) => ({
|
|
114
|
+
...category,
|
|
115
|
+
actions: category.actions.filter((action) => isAgentWorkspaceActionVisible(this.context, action)),
|
|
116
|
+
}));
|
|
117
|
+
return searchAgentWorkspaceActions(categories, this.actionSearchQuery);
|
|
115
118
|
}
|
|
116
119
|
|
|
117
120
|
get selectedActionSearchResult(): AgentWorkspaceActionSearchResult | null {
|
|
@@ -178,11 +181,7 @@ export class AgentWorkspace {
|
|
|
178
181
|
}
|
|
179
182
|
this.runtimeSnapshot = buildAgentWorkspaceRuntimeSnapshot(this.context);
|
|
180
183
|
this.status = 'Runtime context refreshed.';
|
|
181
|
-
this.lastActionResult = {
|
|
182
|
-
kind: 'refreshed',
|
|
183
|
-
title: 'Runtime context refreshed',
|
|
184
|
-
detail: 'Provider, model, session, local memory, runtime endpoint, and Agent knowledge route posture were re-read from the live command context.',
|
|
185
|
-
};
|
|
184
|
+
this.lastActionResult = { kind: 'refreshed', title: 'Runtime context refreshed', detail: 'Provider, model, session, local memory, runtime endpoint, and Agent knowledge route posture were re-read from the live command context.' };
|
|
186
185
|
}
|
|
187
186
|
|
|
188
187
|
cancelLocalEditor(): void {
|
|
@@ -321,32 +320,37 @@ export class AgentWorkspace {
|
|
|
321
320
|
}
|
|
322
321
|
|
|
323
322
|
try {
|
|
324
|
-
const marker = {
|
|
325
|
-
scope: 'user',
|
|
326
|
-
source: 'wizard',
|
|
327
|
-
mode: 'new',
|
|
328
|
-
workspaceRoot: shellPaths.workingDirectory,
|
|
329
|
-
} as const;
|
|
323
|
+
const marker = { scope: 'user', source: 'wizard', mode: 'new', workspaceRoot: shellPaths.workingDirectory } as const;
|
|
330
324
|
writeOnboardingCheckMarker(shellPaths, marker);
|
|
331
325
|
writeOnboardingCompletionMarker(shellPaths, marker);
|
|
332
326
|
this.status = 'Onboarding applied and closed.';
|
|
333
|
-
this.lastActionResult = {
|
|
334
|
-
kind: 'refreshed',
|
|
335
|
-
title: 'Onboarding complete',
|
|
336
|
-
detail: 'Saved the user onboarding completion marker. Future normal launches start in the main conversation.',
|
|
337
|
-
safety: 'safe',
|
|
338
|
-
};
|
|
327
|
+
this.lastActionResult = { kind: 'refreshed', title: 'Onboarding complete', detail: 'Saved the user onboarding completion marker. Future normal launches start in the main conversation.', safety: 'safe' };
|
|
339
328
|
if (!this.context?.dismissAgentWorkspace?.()) this.close();
|
|
340
329
|
} catch (error) {
|
|
341
330
|
const detail = error instanceof Error ? error.message : String(error);
|
|
342
331
|
this.status = 'Onboarding completion failed.';
|
|
343
|
-
this.lastActionResult = {
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
332
|
+
this.lastActionResult = { kind: 'error', title: 'Onboarding completion failed', detail, safety: 'safe' };
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
applySettingAction(action: AgentWorkspaceAction, requestRender?: () => void): void {
|
|
337
|
+
const effect = buildAgentWorkspaceSettingActionEffect(this.context, action);
|
|
338
|
+
if (effect.kind === 'result') {
|
|
339
|
+
this.status = effect.status;
|
|
340
|
+
this.lastActionResult = effect.result;
|
|
341
|
+
return;
|
|
349
342
|
}
|
|
343
|
+
if (effect.kind === 'editor') {
|
|
344
|
+
this.localEditor = effect.editor;
|
|
345
|
+
this.status = effect.status;
|
|
346
|
+
this.lastActionResult = effect.result;
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
void this.applySettingValue(effect.setting, effect.value, requestRender);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
importTuiSettings(requestRender?: () => void): void {
|
|
353
|
+
void this.importTuiSettingsAsync(requestRender);
|
|
350
354
|
}
|
|
351
355
|
|
|
352
356
|
private selectedItemForOperation(operation: AgentWorkspaceLocalOperation): AgentWorkspaceLocalLibraryItem | null {
|
|
@@ -357,6 +361,24 @@ export class AgentWorkspace {
|
|
|
357
361
|
return this.selectedLocalLibraryItem('routine');
|
|
358
362
|
}
|
|
359
363
|
|
|
364
|
+
private async applySettingValue(setting: ConfigSetting, value: unknown, requestRender?: () => void): Promise<void> {
|
|
365
|
+
const outcome = await applyAgentWorkspaceSettingValue(this.context, setting, value);
|
|
366
|
+
this.runtimeSnapshot = this.context ? buildAgentWorkspaceRuntimeSnapshot(this.context) : this.runtimeSnapshot;
|
|
367
|
+
this.clampSelection();
|
|
368
|
+
this.status = outcome.status;
|
|
369
|
+
this.lastActionResult = outcome.result;
|
|
370
|
+
requestRender?.();
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
private async importTuiSettingsAsync(requestRender?: () => void): Promise<void> {
|
|
374
|
+
const outcome = await importAgentWorkspaceTuiSettings(this.context);
|
|
375
|
+
this.runtimeSnapshot = outcome.runtimeSnapshot ?? this.runtimeSnapshot;
|
|
376
|
+
this.clampSelection();
|
|
377
|
+
this.status = outcome.status;
|
|
378
|
+
this.lastActionResult = outcome.result;
|
|
379
|
+
requestRender?.();
|
|
380
|
+
}
|
|
381
|
+
|
|
360
382
|
private memoryApi(): MemoryApi {
|
|
361
383
|
const memory = this.context?.clients?.agentKnowledgeApi?.memory;
|
|
362
384
|
if (!memory) throw new Error('Agent Memory API is unavailable; refusing default knowledge or non-Agent fallback.');
|
|
@@ -429,6 +451,19 @@ export class AgentWorkspace {
|
|
|
429
451
|
requestRender?.();
|
|
430
452
|
return;
|
|
431
453
|
}
|
|
454
|
+
if (editor.kind === 'setting-set') {
|
|
455
|
+
const setting = editor.recordId ? agentWorkspaceSettingSchema(this.context, editor.recordId) : null;
|
|
456
|
+
if (!setting) {
|
|
457
|
+
this.localEditor = { ...editor, message: 'Unknown setting; cannot save.' };
|
|
458
|
+
this.status = 'Unknown setting; cannot save.';
|
|
459
|
+
requestRender?.();
|
|
460
|
+
return;
|
|
461
|
+
}
|
|
462
|
+
const value = this.editorField('value');
|
|
463
|
+
this.localEditor = null;
|
|
464
|
+
void this.applySettingValue(setting, value, requestRender);
|
|
465
|
+
return;
|
|
466
|
+
}
|
|
432
467
|
if (editor.kind === 'memory') {
|
|
433
468
|
if (editor.mode === 'delete') {
|
|
434
469
|
try {
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
WORKSPACE_PALETTE as PALETTE,
|
|
18
18
|
type WorkspaceRow,
|
|
19
19
|
} from './fullscreen-workspace.ts';
|
|
20
|
-
import { actionResultColor,
|
|
20
|
+
import { actionResultColor, setupStatusColor, type AgentWorkspaceContextLine as ContextLine } from './agent-workspace-style.ts';
|
|
21
21
|
|
|
22
22
|
function buildLeftRows(workspace: AgentWorkspace, height: number): WorkspaceRow[] {
|
|
23
23
|
const rows: WorkspaceRow[] = [];
|
|
@@ -57,6 +57,8 @@ function buildLeftRows(workspace: AgentWorkspace, height: number): WorkspaceRow[
|
|
|
57
57
|
function actionCommand(action: AgentWorkspaceAction): string {
|
|
58
58
|
if (action.kind === 'workspace') return action.targetCategoryId ? `open ${action.targetCategoryId}` : '(workspace)';
|
|
59
59
|
if (action.kind === 'editor') return action.editorKind ? `edit ${action.editorKind}` : '(editor)';
|
|
60
|
+
if (action.kind === 'setting') return action.settingKey ? `set ${action.settingKey}` : 'set setting';
|
|
61
|
+
if (action.kind === 'settings-import') return 'import GoodVibes settings';
|
|
60
62
|
if (action.kind === 'local-selection') return action.selectionDelta && action.selectionDelta < 0 ? 'select previous' : 'select next';
|
|
61
63
|
if (action.kind === 'local-operation') return action.localOperation ?? '(local action)';
|
|
62
64
|
return action.command ?? '(guidance)';
|
|
@@ -91,8 +93,8 @@ function compactText(text: string, maxWidth = 104): string {
|
|
|
91
93
|
|
|
92
94
|
function actionMetaLine(action: AgentWorkspaceAction): ContextLine {
|
|
93
95
|
return {
|
|
94
|
-
text:
|
|
95
|
-
fg: action.kind === 'command' ? PALETTE.info :
|
|
96
|
+
text: `Does: ${actionCommand(action)}`,
|
|
97
|
+
fg: action.safety === 'blocked' ? PALETTE.warn : action.kind === 'command' ? PALETTE.info : PALETTE.muted,
|
|
96
98
|
};
|
|
97
99
|
}
|
|
98
100
|
|
|
@@ -116,7 +118,7 @@ function setupOverviewLines(snapshot: AgentWorkspaceRuntimeSnapshot): ContextLin
|
|
|
116
118
|
const counts = setupCounts(snapshot);
|
|
117
119
|
const nextItems = setupAttentionItems(snapshot, 3);
|
|
118
120
|
const lines: ContextLine[] = [
|
|
119
|
-
{ text: '
|
|
121
|
+
{ text: 'Onboarding', fg: PALETTE.title, bold: true },
|
|
120
122
|
{ text: `${counts.ready}/${snapshot.setupChecklist.length} ready; ${counts.recommended} recommended; ${counts.optional} optional; ${counts.blocked} blocked.`, fg: counts.blocked > 0 ? PALETTE.warn : PALETTE.info },
|
|
121
123
|
{ text: `Chat: ${snapshot.provider} / ${snapshot.modelDisplayName}.`, fg: PALETTE.info },
|
|
122
124
|
{ text: `Local: ${snapshot.localPersonaCount} personas, ${snapshot.localSkillCount} skills, ${snapshot.localRoutineCount} routines, ${snapshot.localMemoryCount} memories.`, fg: PALETTE.info },
|
|
@@ -309,7 +311,7 @@ function snapshotLines(workspace: AgentWorkspace, category: AgentWorkspaceCatego
|
|
|
309
311
|
companionAccessLine(snapshot),
|
|
310
312
|
{ text: `Channels: ${readyCount}/${snapshot.channels.length} ready; ${enabledCount} enabled; ${configuredDefaults} target(s).`, fg: PALETTE.info },
|
|
311
313
|
{ text: `Next: ${nextAttentionChannel ? `${nextAttentionChannel.label} - ${compactText(nextAttentionChannel.nextStep)}` : 'All enabled channels ready.'}`, fg: nextAttentionChannel ? PALETTE.warn : PALETTE.good },
|
|
312
|
-
{ text: '
|
|
314
|
+
{ text: 'Secrets hidden; sends require explicit action.', fg: PALETTE.warn },
|
|
313
315
|
);
|
|
314
316
|
} else if (category.id === 'knowledge') {
|
|
315
317
|
base.push(
|
|
@@ -536,9 +538,8 @@ function buildEditorFieldRows(editor: AgentWorkspaceLocalEditor, index: number,
|
|
|
536
538
|
function buildActionRows(workspace: AgentWorkspace, width: number, height: number): WorkspaceRow[] {
|
|
537
539
|
if (workspace.localEditor) return buildEditorRows(workspace.localEditor, width, height);
|
|
538
540
|
const rows: WorkspaceRow[] = [];
|
|
539
|
-
const labelWidth = Math.min(
|
|
540
|
-
const
|
|
541
|
-
const commandWidth = Math.max(10, width - labelWidth - safetyWidth - 9);
|
|
541
|
+
const labelWidth = Math.min(34, Math.max(18, Math.floor(width * 0.38)));
|
|
542
|
+
const commandWidth = Math.max(10, width - labelWidth - 6);
|
|
542
543
|
if (workspace.actionSearchActive) {
|
|
543
544
|
rows.push({
|
|
544
545
|
text: ` Search: ${workspace.actionSearchQuery || '(type to filter actions)'}`,
|
|
@@ -547,7 +548,7 @@ function buildActionRows(workspace: AgentWorkspace, width: number, height: numbe
|
|
|
547
548
|
});
|
|
548
549
|
}
|
|
549
550
|
rows.push({
|
|
550
|
-
text: ` ${padDisplay(workspace.actionSearchActive ? 'Result' : 'Action', labelWidth)} ${padDisplay('
|
|
551
|
+
text: ` ${padDisplay(workspace.actionSearchActive ? 'Result' : 'Action', labelWidth)} ${padDisplay('Does', commandWidth)}`,
|
|
551
552
|
fg: PALETTE.muted,
|
|
552
553
|
bold: true,
|
|
553
554
|
});
|
|
@@ -564,9 +565,9 @@ function buildActionRows(workspace: AgentWorkspace, width: number, height: numbe
|
|
|
564
565
|
const label = searchResult ? `${searchResult.category.label} / ${action.label}` : action.label;
|
|
565
566
|
const marker = selected ? GLYPHS.navigation.selected : ' ';
|
|
566
567
|
rows.push({
|
|
567
|
-
text: `${marker} ${padDisplay(label, labelWidth)} ${padDisplay(
|
|
568
|
+
text: `${marker} ${padDisplay(label, labelWidth)} ${padDisplay(actionCommand(action), commandWidth)}`,
|
|
568
569
|
selected: selected && workspace.focusPane === 'actions',
|
|
569
|
-
fg:
|
|
570
|
+
fg: action.safety === 'blocked' ? PALETTE.warn : selected ? PALETTE.text : PALETTE.info,
|
|
570
571
|
bold: selected,
|
|
571
572
|
});
|
|
572
573
|
}
|
|
@@ -620,7 +621,7 @@ export function renderAgentWorkspace(workspace: AgentWorkspace, width: number, h
|
|
|
620
621
|
leftHeader: 'Operator Areas',
|
|
621
622
|
mainHeader: workspace.actionSearchActive
|
|
622
623
|
? `Search actions · ${workspace.actions.length} result(s)`
|
|
623
|
-
: `${category.label} · ${
|
|
624
|
+
: `${category.label} · ${workspace.actions.length} action(s)`,
|
|
624
625
|
leftRows: buildLeftRows(workspace, metrics.bodyRows),
|
|
625
626
|
contextRows: buildContextRows(workspace, category, action, metrics.contextWidth),
|
|
626
627
|
controlRows: buildActionRows(workspace, metrics.contextWidth, metrics.controlRows),
|
|
@@ -327,7 +327,7 @@ const UI_SURFACES: readonly UiSurfaceDefinition[] = [
|
|
|
327
327
|
kind: 'picker',
|
|
328
328
|
summary: 'Reasoning-effort selector for models that expose effort levels.',
|
|
329
329
|
command: '/effort',
|
|
330
|
-
preferredModelRoute: `Use ${agentHarnessModes('settings', 'get_setting', 'set_setting')} for provider.reasoningEffort when a concrete level is known, or mode:"run_workspace_action" setup-effort with confirmation.`,
|
|
330
|
+
preferredModelRoute: `Use ${agentHarnessModes('settings', 'get_setting', 'set_setting')} for provider.reasoningEffort when a concrete level is known, or mode:"run_workspace_action" setup-reasoning-effort with confirmation.`,
|
|
331
331
|
available: (context) => typeof context.openReasoningEffortPicker === 'function',
|
|
332
332
|
open: (context) => {
|
|
333
333
|
const surface = findSurfaceById('reasoning-effort-picker')!;
|
|
@@ -55,7 +55,7 @@ function readString(value: unknown): string {
|
|
|
55
55
|
function readLimit(value: unknown, fallback: number): number {
|
|
56
56
|
const parsed = typeof value === 'string' && value.trim() ? Number(value) : value;
|
|
57
57
|
if (typeof parsed !== 'number' || !Number.isFinite(parsed)) return fallback;
|
|
58
|
-
return Math.max(1, Math.min(
|
|
58
|
+
return Math.max(1, Math.min(1000, Math.trunc(parsed)));
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
function readFieldMap(value: unknown): Readonly<Record<string, string>> {
|
|
@@ -256,7 +256,7 @@ export function listWorkspaceActions(
|
|
|
256
256
|
): readonly Record<string, unknown>[] {
|
|
257
257
|
const query = readString(args.query);
|
|
258
258
|
const categoryId = readString(args.categoryId || args.category);
|
|
259
|
-
const limit = readLimit(args.limit,
|
|
259
|
+
const limit = readLimit(args.limit, 1000);
|
|
260
260
|
const includeEditor = args.includeParameters === true;
|
|
261
261
|
const editorContext = includeEditor ? buildWorkspaceEditorContext(context, args) : null;
|
|
262
262
|
const source = query
|
package/src/version.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { join } from 'node:path';
|
|
|
6
6
|
// The prebuild script updates the fallback value before compilation.
|
|
7
7
|
// Uses import.meta.dir (Bun) to locate package.json relative to this file,
|
|
8
8
|
// which is correct regardless of the process working directory.
|
|
9
|
-
let _version = '1.1.
|
|
9
|
+
let _version = '1.1.4';
|
|
10
10
|
try {
|
|
11
11
|
const pkg = JSON.parse(readFileSync(join(import.meta.dir, '..', 'package.json'), 'utf-8')) as {
|
|
12
12
|
readonly version?: unknown;
|