@pellux/goodvibes-agent 1.5.5 → 1.5.6
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 +9 -0
- package/README.md +1 -1
- package/dist/package/main.js +94130 -79335
- package/docs/tools-and-commands.md +2 -1
- package/package.json +6 -3
- package/src/agent/autonomy-schedule-format.ts +1 -1
- package/src/agent/autonomy-schedule.ts +3 -3
- package/src/agent/calendar/calendar-oauth-service.ts +324 -0
- package/src/agent/calendar-subscription-registry.ts +497 -0
- package/src/agent/email/email-service.ts +57 -0
- package/src/agent/email/smtp-client.ts +30 -10
- package/src/agent/memory-prompt.ts +143 -12
- package/src/agent/operator-actions.ts +26 -22
- package/src/agent/prompt-context-receipts.ts +45 -18
- package/src/agent/reminder-schedule-format.ts +1 -1
- package/src/agent/reminder-schedule.ts +3 -3
- package/src/agent/routine-schedule-format.ts +2 -2
- package/src/agent/routine-schedule-promotion.ts +5 -5
- package/src/agent/routine-schedule-receipts.ts +2 -2
- package/src/agent/schedule-edit-format.ts +1 -1
- package/src/agent/schedule-edit.ts +5 -5
- package/src/agent/session-registration.ts +281 -0
- package/src/agent/vibe-file.ts +50 -0
- package/src/cli/local-library-command-shared.ts +146 -0
- package/src/cli/management.ts +2 -1
- package/src/cli/personas-command.ts +252 -0
- package/src/cli/resume-relaunch-notice.ts +151 -0
- package/src/cli/routines-command.ts +25 -2
- package/src/cli/service-posture.ts +3 -2
- package/src/cli/skill-bundle-command.ts +175 -0
- package/src/cli/skills-command.ts +309 -0
- package/src/cli/status.ts +43 -7
- package/src/cli/tui-startup.ts +24 -1
- package/src/config/agent-settings-policy.ts +0 -1
- package/src/config/secret-config.ts +4 -0
- package/src/core/conversation-rendering.ts +20 -11
- package/src/core/focus-tracker.ts +41 -0
- package/src/core/system-message-noise.ts +108 -0
- package/src/core/system-message-router.ts +54 -1
- package/src/core/thinking-overlay.ts +83 -0
- package/src/input/agent-workspace-access-command-editor-submission.ts +75 -108
- package/src/input/agent-workspace-access-command-editors.ts +108 -128
- package/src/input/agent-workspace-activation.ts +15 -0
- package/src/input/agent-workspace-basic-command-editor-submission.ts +292 -513
- package/src/input/agent-workspace-basic-command-editors.ts +433 -564
- package/src/input/agent-workspace-calendar-connect-editor.ts +116 -0
- package/src/input/agent-workspace-calendar-oauth-editor.ts +152 -0
- package/src/input/agent-workspace-calendar-subscribe-editor.ts +128 -0
- package/src/input/agent-workspace-categories.ts +7 -2
- package/src/input/agent-workspace-channel-command-editor-submission.ts +38 -52
- package/src/input/agent-workspace-channel-command-editors.ts +42 -46
- package/src/input/agent-workspace-command-editor-engine.ts +133 -0
- package/src/input/agent-workspace-command-editor.ts +4 -0
- package/src/input/agent-workspace-direct-editor-submission.ts +59 -0
- package/src/input/agent-workspace-email-connect-editor.ts +162 -0
- package/src/input/agent-workspace-knowledge-command-editor-submission.ts +52 -81
- package/src/input/agent-workspace-knowledge-command-editors.ts +71 -71
- package/src/input/agent-workspace-library-command-editor-submission.ts +10 -28
- package/src/input/agent-workspace-library-command-editors.ts +16 -2
- package/src/input/agent-workspace-live-counters.ts +55 -0
- package/src/input/agent-workspace-mcp-command-editor-submission.ts +60 -82
- package/src/input/agent-workspace-mcp-command-editors.ts +52 -0
- package/src/input/agent-workspace-media-command-editor-submission.ts +12 -39
- package/src/input/agent-workspace-media-command-editors.ts +10 -8
- package/src/input/agent-workspace-memory-command-editor-submission.ts +76 -151
- package/src/input/agent-workspace-memory-command-editors.ts +116 -141
- package/src/input/agent-workspace-operations-command-editor-submission.ts +131 -184
- package/src/input/agent-workspace-operations-command-editors.ts +150 -162
- package/src/input/agent-workspace-provider-command-editor-submission.ts +60 -106
- package/src/input/agent-workspace-provider-command-editors.ts +58 -68
- package/src/input/agent-workspace-search.ts +8 -1
- package/src/input/agent-workspace-session-command-editor-submission.ts +104 -132
- package/src/input/agent-workspace-session-command-editors.ts +160 -195
- package/src/input/agent-workspace-settings.ts +40 -1
- package/src/input/agent-workspace-skill-bundle-command-editor-submission.ts +55 -71
- package/src/input/agent-workspace-skill-bundle-command-editors.ts +65 -69
- package/src/input/agent-workspace-snapshot-builders.ts +431 -0
- package/src/input/agent-workspace-snapshot-config.ts +43 -0
- package/src/input/agent-workspace-snapshot.ts +198 -432
- package/src/input/agent-workspace-task-command-editor-submission.ts +34 -44
- package/src/input/agent-workspace-task-command-editors.ts +27 -28
- package/src/input/agent-workspace-types.ts +35 -1
- package/src/input/agent-workspace.ts +5 -14
- package/src/input/command-registry.ts +23 -1
- package/src/input/commands/calendar-connect-runtime.ts +226 -0
- package/src/input/commands/calendar-runtime.ts +110 -7
- package/src/input/commands/calendar-subscription-runtime.ts +225 -0
- package/src/input/commands/email-runtime.ts +100 -40
- package/src/input/commands/knowledge.ts +1 -1
- package/src/input/commands/network-scan-runtime.ts +75 -0
- package/src/input/commands/operator-actions-runtime.ts +9 -6
- package/src/input/commands/personas-runtime.ts +1 -1
- package/src/input/commands/schedule-runtime.ts +4 -4
- package/src/input/commands/session-content.ts +13 -1
- package/src/input/commands/session-workflow.ts +15 -20
- package/src/input/commands/session.ts +3 -1
- package/src/input/commands/shell-core.ts +20 -6
- package/src/input/commands.ts +2 -2
- package/src/input/delete-key-policy.ts +46 -0
- package/src/input/feed-context-factory.ts +10 -0
- package/src/input/handler-feed.ts +87 -0
- package/src/input/handler-modal-routes.ts +6 -1
- package/src/input/handler.ts +5 -0
- package/src/input/panel-paste-flood-guard.ts +94 -0
- package/src/input/settings-modal-types.ts +5 -3
- package/src/input/settings-modal.ts +21 -0
- package/src/main.ts +36 -36
- package/src/permissions/approval-posture.ts +141 -0
- package/src/renderer/agent-workspace-context-lines.ts +10 -2
- package/src/renderer/compositor.ts +27 -4
- package/src/renderer/diff.ts +61 -18
- package/src/renderer/fullscreen-primitives.ts +37 -18
- package/src/renderer/markdown.ts +20 -10
- package/src/renderer/modal-factory.ts +25 -15
- package/src/renderer/overlay-box.ts +23 -12
- package/src/renderer/process-indicator.ts +8 -3
- package/src/renderer/prompt-content-width.ts +16 -0
- package/src/renderer/settings-modal-helpers.ts +2 -0
- package/src/renderer/settings-modal.ts +2 -0
- package/src/renderer/startup-theme-probe.ts +35 -0
- package/src/renderer/status-glyphs.ts +11 -15
- package/src/renderer/system-message.ts +17 -2
- package/src/renderer/term-caps.ts +318 -0
- package/src/renderer/terminal-bg-probe.ts +373 -0
- package/src/renderer/terminal-escapes.ts +24 -0
- package/src/renderer/theme-mode-config.ts +87 -0
- package/src/renderer/theme.ts +241 -0
- package/src/renderer/thinking.ts +12 -3
- package/src/renderer/tool-call.ts +7 -3
- package/src/renderer/ui-factory.ts +92 -36
- package/src/renderer/ui-primitives.ts +33 -93
- package/src/runtime/bootstrap-core.ts +15 -0
- package/src/runtime/bootstrap-hook-bridge.ts +6 -0
- package/src/runtime/bootstrap-shell.ts +2 -0
- package/src/runtime/bootstrap.ts +68 -5
- package/src/runtime/calendar-boot-refresh.ts +105 -0
- package/src/runtime/lan-scan-consent.ts +253 -0
- package/src/runtime/services.ts +127 -2
- package/src/runtime/session-spine-rest-transport.ts +160 -0
- package/src/runtime/terminal-output-guard.ts +6 -1
- package/src/runtime/ui-services.ts +3 -0
- package/src/shell/agent-workspace-fullscreen.ts +5 -0
- package/src/shell/terminal-focus-mode.ts +120 -0
- package/src/shell/ui-openers.ts +13 -4
- package/src/tools/agent-harness-autonomy-intake.ts +5 -5
- package/src/tools/agent-harness-autonomy-queue.ts +1 -1
- package/src/tools/agent-harness-background-processes.ts +2 -1
- package/src/tools/agent-harness-metadata.ts +16 -8
- package/src/tools/agent-harness-operator-methods.ts +44 -8
- package/src/tools/agent-harness-personal-ops-discovery.ts +57 -10
- package/src/tools/agent-harness-personal-ops-lanes.ts +17 -5
- package/src/tools/agent-harness-personal-ops-operations.ts +7 -7
- package/src/tools/agent-harness-personal-ops-types.ts +6 -0
- package/src/tools/agent-harness-prompt-context.ts +26 -12
- package/src/tools/agent-harness-workspace-actions.ts +4 -0
- package/src/tools/agent-local-registry-args.ts +117 -0
- package/src/tools/agent-local-registry-memory.ts +227 -0
- package/src/tools/agent-local-registry-tool.ts +19 -237
- package/src/tools/agent-operator-briefing-tool.ts +2 -2
- package/src/tools/agent-operator-method-tool.ts +13 -0
- package/src/tools/agent-policy-explanation.ts +39 -4
- package/src/tools/agent-schedule-tool.ts +5 -5
- package/src/utils/terminal-width.ts +98 -1
- package/src/version.ts +1 -1
- package/src/cli/local-library-command.ts +0 -825
|
@@ -1,54 +1,35 @@
|
|
|
1
1
|
import type { Tool } from '@pellux/goodvibes-sdk/platform/types';
|
|
2
2
|
import type { ToolRegistry } from '@pellux/goodvibes-sdk/platform/tools';
|
|
3
3
|
import type { ShellPathService } from '@/runtime/index.ts';
|
|
4
|
-
import { MemoryRegistry
|
|
4
|
+
import { MemoryRegistry } from '@pellux/goodvibes-sdk/platform/state';
|
|
5
5
|
import { AgentPersonaRegistry, type AgentPersonaRecord } from '../agent/persona-registry.ts';
|
|
6
6
|
import { AgentNoteRegistry, type AgentNoteRecord } from '../agent/note-registry.ts';
|
|
7
7
|
import { AgentRoutineRegistry, type AgentRoutineRecord } from '../agent/routine-registry.ts';
|
|
8
8
|
import { AgentSkillRegistry, type AgentSkillBundleRecord, type AgentSkillRecord } from '../agent/skill-registry.ts';
|
|
9
|
-
import {
|
|
10
|
-
import { formatAgentRecordOrigin, formatAgentRecordReference, formatAgentRecordReviewState } from '../agent/record-labels.ts';
|
|
9
|
+
import { formatAgentRecordOrigin, formatAgentRecordReviewState } from '../agent/record-labels.ts';
|
|
11
10
|
import { buildAgentLocalRequirements } from './agent-local-registry-requirements.ts';
|
|
11
|
+
import {
|
|
12
|
+
AGENT_TOOL_PROVENANCE,
|
|
13
|
+
type AgentLocalRegistryToolArgs,
|
|
14
|
+
readAffirmative,
|
|
15
|
+
readString,
|
|
16
|
+
readStringList,
|
|
17
|
+
registryError,
|
|
18
|
+
registryOutput,
|
|
19
|
+
requireConfirmedDelete,
|
|
20
|
+
requireDescription,
|
|
21
|
+
requireId,
|
|
22
|
+
requireName,
|
|
23
|
+
requireTextField,
|
|
24
|
+
} from './agent-local-registry-args.ts';
|
|
25
|
+
import { handleMemory, MEMORY_CLASSES, MEMORY_SCOPES } from './agent-local-registry-memory.ts';
|
|
12
26
|
|
|
13
27
|
export type AgentLocalRegistryDomain = 'memory' | 'note' | 'persona' | 'skill' | 'skill_bundle' | 'routine';
|
|
14
28
|
export type AgentLocalRegistryAction = 'list' | 'search' | 'get' | 'create' | 'update' | 'enable' | 'disable' | 'review' | 'stale' | 'use' | 'clear_active' | 'start' | 'delete';
|
|
15
|
-
|
|
16
|
-
export interface AgentLocalRegistryToolArgs {
|
|
17
|
-
readonly domain?: unknown;
|
|
18
|
-
readonly action?: unknown;
|
|
19
|
-
readonly id?: unknown;
|
|
20
|
-
readonly query?: unknown;
|
|
21
|
-
readonly cls?: unknown;
|
|
22
|
-
readonly scope?: unknown;
|
|
23
|
-
readonly summary?: unknown;
|
|
24
|
-
readonly detail?: unknown;
|
|
25
|
-
readonly confidence?: unknown;
|
|
26
|
-
readonly title?: unknown;
|
|
27
|
-
readonly name?: unknown;
|
|
28
|
-
readonly description?: unknown;
|
|
29
|
-
readonly body?: unknown;
|
|
30
|
-
readonly sourceUrl?: unknown;
|
|
31
|
-
readonly procedure?: unknown;
|
|
32
|
-
readonly steps?: unknown;
|
|
33
|
-
readonly skills?: unknown;
|
|
34
|
-
readonly skillIds?: unknown;
|
|
35
|
-
readonly requiresEnv?: unknown;
|
|
36
|
-
readonly requiresCommands?: unknown;
|
|
37
|
-
readonly triggers?: unknown;
|
|
38
|
-
readonly tags?: unknown;
|
|
39
|
-
readonly reason?: unknown;
|
|
40
|
-
readonly enabled?: unknown;
|
|
41
|
-
readonly activate?: unknown;
|
|
42
|
-
readonly provenance?: unknown;
|
|
43
|
-
readonly confirm?: unknown;
|
|
44
|
-
readonly explicitUserRequest?: unknown;
|
|
45
|
-
}
|
|
29
|
+
export type { AgentLocalRegistryToolArgs } from './agent-local-registry-args.ts';
|
|
46
30
|
|
|
47
31
|
const DOMAINS: readonly AgentLocalRegistryDomain[] = ['memory', 'note', 'persona', 'skill', 'skill_bundle', 'routine'];
|
|
48
32
|
const ACTIONS: readonly AgentLocalRegistryAction[] = ['list', 'search', 'get', 'create', 'update', 'enable', 'disable', 'review', 'stale', 'use', 'clear_active', 'start', 'delete'];
|
|
49
|
-
const MEMORY_CLASSES: readonly MemoryClass[] = ['decision', 'constraint', 'incident', 'pattern', 'fact', 'risk', 'runbook', 'architecture', 'ownership'];
|
|
50
|
-
const MEMORY_SCOPES: readonly MemoryScope[] = ['session', 'project', 'team'];
|
|
51
|
-
const AGENT_TOOL_PROVENANCE = 'agent-local-registry-tool';
|
|
52
33
|
|
|
53
34
|
function isDomain(value: unknown): value is AgentLocalRegistryDomain {
|
|
54
35
|
return typeof value === 'string' && DOMAINS.includes(value as AgentLocalRegistryDomain);
|
|
@@ -58,206 +39,6 @@ function isAction(value: unknown): value is AgentLocalRegistryAction {
|
|
|
58
39
|
return typeof value === 'string' && ACTIONS.includes(value as AgentLocalRegistryAction);
|
|
59
40
|
}
|
|
60
41
|
|
|
61
|
-
function readString(value: unknown): string {
|
|
62
|
-
return typeof value === 'string' ? value.trim() : '';
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
function readAffirmative(value: unknown): boolean {
|
|
66
|
-
const normalized = readString(value).toLowerCase(); return value === true || (typeof value === 'string' && (normalized === '' || normalized === 'yes' || normalized === 'y' || normalized === 'true' || normalized === 'on'));
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
function readStringList(value: unknown): readonly string[] {
|
|
70
|
-
if (typeof value === 'string') {
|
|
71
|
-
return value.split(',').map((entry) => entry.trim()).filter(Boolean);
|
|
72
|
-
}
|
|
73
|
-
if (!Array.isArray(value)) return [];
|
|
74
|
-
return value.filter((entry): entry is string => typeof entry === 'string').map((entry) => entry.trim()).filter(Boolean);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
function readOptionalNumber(value: unknown): number | undefined {
|
|
78
|
-
if (typeof value === 'number' && Number.isFinite(value)) return value;
|
|
79
|
-
if (typeof value !== 'string' || !value.trim()) return undefined;
|
|
80
|
-
const parsed = Number(value);
|
|
81
|
-
return Number.isFinite(parsed) ? parsed : undefined;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
function readOptionalConfidence(value: unknown): number | undefined {
|
|
85
|
-
const confidence = readOptionalNumber(value);
|
|
86
|
-
if (confidence === undefined) return undefined;
|
|
87
|
-
if (confidence < 0 || confidence > 100) throw new Error('confidence must be between 0 and 100.');
|
|
88
|
-
return confidence;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
function isMemoryClass(value: unknown): value is MemoryClass {
|
|
92
|
-
return typeof value === 'string' && MEMORY_CLASSES.includes(value as MemoryClass);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
function isMemoryScope(value: unknown): value is MemoryScope {
|
|
96
|
-
return typeof value === 'string' && MEMORY_SCOPES.includes(value as MemoryScope);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
function registryError(message: string): { readonly success: false; readonly error: string } {
|
|
100
|
-
return { success: false, error: message };
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
function registryOutput(output: string): { readonly success: true; readonly output: string } {
|
|
104
|
-
return { success: true, output };
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
function requireId(args: AgentLocalRegistryToolArgs): string {
|
|
108
|
-
const id = readString(args.id);
|
|
109
|
-
if (!id) throw new Error('id is required.');
|
|
110
|
-
return id;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
function requireConfirmedDelete(args: AgentLocalRegistryToolArgs, label: string): void {
|
|
114
|
-
const explicitUserRequest = readString(args.explicitUserRequest);
|
|
115
|
-
if (!explicitUserRequest) throw new Error(`${label} deletion requires explicitUserRequest with the user's exact request or a short faithful summary.`);
|
|
116
|
-
if (args.confirm !== true) throw new Error(`${label} deletion requires confirm:true after an explicit user request.`);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
function requireName(args: AgentLocalRegistryToolArgs): string {
|
|
120
|
-
const name = readString(args.name);
|
|
121
|
-
if (!name) throw new Error('name is required.');
|
|
122
|
-
return name;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
function requireDescription(args: AgentLocalRegistryToolArgs): string {
|
|
126
|
-
const description = readString(args.description);
|
|
127
|
-
if (!description) throw new Error('description is required.');
|
|
128
|
-
return description;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
function requireTextField(value: unknown, fieldName: string): string {
|
|
132
|
-
const text = readString(value);
|
|
133
|
-
if (!text) throw new Error(`${fieldName} is required.`);
|
|
134
|
-
return text;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
function requireSummary(args: AgentLocalRegistryToolArgs): string {
|
|
138
|
-
const summary = readString(args.summary || args.description);
|
|
139
|
-
if (!summary) throw new Error('summary is required.');
|
|
140
|
-
return summary;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
function requireMemoryClass(args: AgentLocalRegistryToolArgs): MemoryClass {
|
|
144
|
-
const cls = args.cls || 'fact';
|
|
145
|
-
if (!isMemoryClass(cls)) throw new Error(`Invalid memory class. Valid values ${MEMORY_CLASSES.join(', ')}.`);
|
|
146
|
-
return cls;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
function readMemoryScope(args: AgentLocalRegistryToolArgs): MemoryScope {
|
|
150
|
-
const scope = args.scope || 'project';
|
|
151
|
-
if (!isMemoryScope(scope)) throw new Error(`Invalid memory scope. Valid values ${MEMORY_SCOPES.join(', ')}.`);
|
|
152
|
-
return scope;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
function formatMemory(record: MemoryRecord): string {
|
|
156
|
-
const tags = record.tags.length > 0 ? ` tags ${record.tags.join(', ')}` : '';
|
|
157
|
-
return `${record.id} ${record.scope}/${record.cls} ${formatAgentRecordReviewState(record.reviewState)} ${record.confidence}%${tags} ${record.summary}`;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
async function handleMemory(registry: MemoryRegistry, action: AgentLocalRegistryAction, args: AgentLocalRegistryToolArgs): Promise<string> {
|
|
161
|
-
if (action === 'list') {
|
|
162
|
-
const records = registry.getAll();
|
|
163
|
-
return records.length === 0
|
|
164
|
-
? 'Agent-local memory\nNo Agent-local memory records.'
|
|
165
|
-
: ['Agent-local memory', ...records.map(formatMemory)].join('\n');
|
|
166
|
-
}
|
|
167
|
-
if (action === 'search') {
|
|
168
|
-
const query = readString(args.query);
|
|
169
|
-
const records = registry.search({ query, limit: 10 });
|
|
170
|
-
return records.length === 0
|
|
171
|
-
? `Agent-local memory search\nNo Agent-local memory records matched "${query}".`
|
|
172
|
-
: ['Agent-local memory search', `query ${query || '(all)'}`, ...records.map(formatMemory)].join('\n');
|
|
173
|
-
}
|
|
174
|
-
if (action === 'get') {
|
|
175
|
-
const record = registry.get(requireId(args));
|
|
176
|
-
if (!record) return `Unknown Agent-local memory ${readString(args.id)}`;
|
|
177
|
-
return [
|
|
178
|
-
formatMemory(record),
|
|
179
|
-
`created ${new Date(record.createdAt).toISOString()}`,
|
|
180
|
-
`updated ${new Date(record.updatedAt).toISOString()}`,
|
|
181
|
-
`origin ${record.provenance.map((entry) => formatAgentRecordReference({ kind: String(entry.kind), ref: String(entry.ref) })).join(', ') || '(none)'}`,
|
|
182
|
-
`provenance: ${record.provenance.map((entry) => `${String(entry.kind)}:${String(entry.ref)}`).join(',') || '(none)'}`,
|
|
183
|
-
'',
|
|
184
|
-
record.detail || '(no detail)',
|
|
185
|
-
].join('\n');
|
|
186
|
-
}
|
|
187
|
-
if (action === 'create') {
|
|
188
|
-
const summary = requireSummary(args);
|
|
189
|
-
const detail = readString(args.detail || args.body);
|
|
190
|
-
const tags = readStringList(args.tags);
|
|
191
|
-
const confidence = readOptionalConfidence(args.confidence);
|
|
192
|
-
assertNoSecretLikeMemoryText([summary, detail, ...tags]);
|
|
193
|
-
const record = await registry.add({
|
|
194
|
-
scope: readMemoryScope(args),
|
|
195
|
-
cls: requireMemoryClass(args),
|
|
196
|
-
summary,
|
|
197
|
-
detail,
|
|
198
|
-
tags: [...tags],
|
|
199
|
-
...(confidence === undefined ? {} : { review: { state: 'fresh' as const, confidence } }),
|
|
200
|
-
provenance: [{ kind: 'event', ref: readString(args.provenance) || AGENT_TOOL_PROVENANCE }],
|
|
201
|
-
});
|
|
202
|
-
return [
|
|
203
|
-
'Created Agent-local memory',
|
|
204
|
-
` id ${record.id}`,
|
|
205
|
-
` summary ${record.summary}`,
|
|
206
|
-
].join('\n');
|
|
207
|
-
}
|
|
208
|
-
if (action === 'update') {
|
|
209
|
-
const summary = readString(args.summary || args.description);
|
|
210
|
-
const detail = readString(args.detail || args.body);
|
|
211
|
-
const tags = args.tags === undefined ? undefined : [...readStringList(args.tags)];
|
|
212
|
-
assertNoSecretLikeMemoryText([summary, detail, ...(tags ?? [])]);
|
|
213
|
-
const record = registry.update(requireId(args), {
|
|
214
|
-
summary: summary || undefined,
|
|
215
|
-
detail: detail || undefined,
|
|
216
|
-
tags,
|
|
217
|
-
scope: args.scope === undefined ? undefined : readMemoryScope(args),
|
|
218
|
-
});
|
|
219
|
-
if (!record) return `Unknown Agent-local memory ${readString(args.id)}`;
|
|
220
|
-
return [
|
|
221
|
-
'Updated Agent-local memory',
|
|
222
|
-
` id ${record.id}`,
|
|
223
|
-
` summary ${record.summary}`,
|
|
224
|
-
].join('\n');
|
|
225
|
-
}
|
|
226
|
-
if (action === 'review') {
|
|
227
|
-
const record = registry.review(requireId(args), {
|
|
228
|
-
state: 'reviewed',
|
|
229
|
-
confidence: readOptionalConfidence(args.confidence),
|
|
230
|
-
reviewedBy: 'agent',
|
|
231
|
-
});
|
|
232
|
-
if (!record) return `Unknown Agent-local memory ${readString(args.id)}`;
|
|
233
|
-
return [
|
|
234
|
-
'Reviewed Agent-local memory',
|
|
235
|
-
` id ${record.id}`,
|
|
236
|
-
].join('\n');
|
|
237
|
-
}
|
|
238
|
-
if (action === 'stale') {
|
|
239
|
-
const record = registry.review(requireId(args), {
|
|
240
|
-
state: 'stale',
|
|
241
|
-
staleReason: readString(args.reason) || 'Marked stale by Agent.',
|
|
242
|
-
});
|
|
243
|
-
if (!record) return `Unknown Agent-local memory ${readString(args.id)}`;
|
|
244
|
-
return [
|
|
245
|
-
'Marked Agent-local memory stale',
|
|
246
|
-
` id ${record.id}`,
|
|
247
|
-
].join('\n');
|
|
248
|
-
}
|
|
249
|
-
if (action === 'delete') {
|
|
250
|
-
const id = requireId(args);
|
|
251
|
-
requireConfirmedDelete(args, 'Agent-local memory');
|
|
252
|
-
if (!registry.delete(id)) return `Unknown Agent-local memory ${id}`;
|
|
253
|
-
return [
|
|
254
|
-
'Deleted Agent-local memory',
|
|
255
|
-
` id ${id}`,
|
|
256
|
-
].join('\n');
|
|
257
|
-
}
|
|
258
|
-
throw new Error(`Action ${action} is not valid for memory.`);
|
|
259
|
-
}
|
|
260
|
-
|
|
261
42
|
function formatNote(note: AgentNoteRecord): string {
|
|
262
43
|
const tags = note.tags.length > 0 ? ` tags ${note.tags.join(', ')}` : '';
|
|
263
44
|
const sourceUrl = note.sourceUrl ? ` source ${note.sourceUrl}` : '';
|
|
@@ -743,6 +524,7 @@ export function createAgentLocalRegistryTool(shellPaths: ShellPathService, memor
|
|
|
743
524
|
action: { type: 'string', enum: [...ACTIONS] },
|
|
744
525
|
id: { type: 'string' },
|
|
745
526
|
query: { type: 'string' },
|
|
527
|
+
semantic: { type: 'boolean', description: 'Memory search: semantic by default; false forces literal matching.' },
|
|
746
528
|
cls: { type: 'string', enum: [...MEMORY_CLASSES], description: 'Memory class when domain is memory.' },
|
|
747
529
|
scope: { type: 'string', enum: [...MEMORY_SCOPES], description: 'Memory scope when domain is memory.' },
|
|
748
530
|
summary: { type: 'string', description: 'Memory summary when domain is memory.' },
|
|
@@ -30,7 +30,7 @@ const OPERATOR_BRIEFING_ROUTES: readonly OperatorRouteDescriptor[] = [
|
|
|
30
30
|
{ id: 'projectPlanning.workPlan.snapshot', path: '/api/projects/planning/work-plan' },
|
|
31
31
|
{ id: 'approvals.list', path: '/api/approvals' },
|
|
32
32
|
{ id: 'automation.integration.snapshot', path: '/api/automation' },
|
|
33
|
-
{ id: 'schedules.list', path: '/api/automation/schedules' },
|
|
33
|
+
{ id: 'automation.schedules.list', path: '/api/automation/schedules' },
|
|
34
34
|
{ id: 'scheduler.capacity', path: '/api/runtime/scheduler' },
|
|
35
35
|
] as const;
|
|
36
36
|
|
|
@@ -149,7 +149,7 @@ function formatRoute(result: OperatorRouteResult): string {
|
|
|
149
149
|
if (result.route.id === 'projectPlanning.workPlan.snapshot') return formatWorkPlan(result.body);
|
|
150
150
|
if (result.route.id === 'approvals.list') return formatApprovals(result.body);
|
|
151
151
|
if (result.route.id === 'automation.integration.snapshot') return formatAutomation(result.body);
|
|
152
|
-
if (result.route.id === 'schedules.list') return formatSchedules(result.body);
|
|
152
|
+
if (result.route.id === 'automation.schedules.list') return formatSchedules(result.body);
|
|
153
153
|
return formatCapacity(result.body);
|
|
154
154
|
}
|
|
155
155
|
|
|
@@ -17,6 +17,7 @@ interface OperatorContractMethod {
|
|
|
17
17
|
readonly method?: string;
|
|
18
18
|
readonly path?: string;
|
|
19
19
|
};
|
|
20
|
+
readonly invokable?: boolean;
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
interface OperatorMethodToolArgs {
|
|
@@ -562,6 +563,18 @@ export function createAgentOperatorMethodTool(
|
|
|
562
563
|
error: `Unknown GoodVibes operator method '${methodId}'. Inspect host action:"methods" first.`,
|
|
563
564
|
};
|
|
564
565
|
}
|
|
566
|
+
// W4-A3 (capability-advertisement honesty): the operator contract marks a
|
|
567
|
+
// cataloged-but-not-route-backed method invokable:false (email.inbox.list,
|
|
568
|
+
// email.send, etc. — see @pellux/goodvibes-sdk's method-catalog-route-reconcile).
|
|
569
|
+
// Refuse here, before prepareOperatorRoute/fetch, rather than letting the
|
|
570
|
+
// model discover a bare 404 from a route that was never wired up. The ad
|
|
571
|
+
// must degrade at the source, not rely on the model coping with a 404.
|
|
572
|
+
if (method.invokable === false) {
|
|
573
|
+
return {
|
|
574
|
+
success: false,
|
|
575
|
+
error: `operator method '${method.id}' is unavailable (route not served by this daemon). Do not retry it; the daemon has not wired this capability up.`,
|
|
576
|
+
};
|
|
577
|
+
}
|
|
565
578
|
|
|
566
579
|
let route: PreparedOperatorRoute;
|
|
567
580
|
try {
|
|
@@ -6,6 +6,7 @@ import type { CommandContext } from '../input/command-registry.ts';
|
|
|
6
6
|
import { fallbackPermissionCategoryForArgs } from '../runtime/tool-permission-safety.ts';
|
|
7
7
|
import { HARNESS_MODE_DESCRIPTORS } from './agent-harness-mode-catalog.ts';
|
|
8
8
|
import { explainAgentToolPolicyInvocation } from './agent-tool-policy-guard.ts';
|
|
9
|
+
import { computeApprovalPosture, type ApprovalPosture } from '../permissions/approval-posture.ts';
|
|
9
10
|
|
|
10
11
|
type PolicyExplanationStatus = 'allowed' | 'confirmation_required' | 'denied';
|
|
11
12
|
type PermissionPredictionOutcome = 'allowed' | 'prompt' | 'denied';
|
|
@@ -186,18 +187,42 @@ function readAutoApprove(context: CommandContext): boolean {
|
|
|
186
187
|
return context.platform.config?.behavior?.autoApprove === true;
|
|
187
188
|
}
|
|
188
189
|
|
|
190
|
+
/**
|
|
191
|
+
* Reads the SAME effective approval posture that cli/status.ts, the doctor
|
|
192
|
+
* surface, and the footer read (via computeApprovalPosture) — the shared
|
|
193
|
+
* single source of truth, so this tool's "explain" output never disagrees
|
|
194
|
+
* with what those other surfaces say about auto-approve / permission mode.
|
|
195
|
+
*/
|
|
196
|
+
function readEffectivePosture(context: CommandContext): ApprovalPosture {
|
|
197
|
+
const mode = readPermissionMode(context);
|
|
198
|
+
const customTools: Record<string, unknown> = {};
|
|
199
|
+
if (mode === 'custom') {
|
|
200
|
+
for (const key of Object.values(TOOL_CONFIG_KEYS)) {
|
|
201
|
+
if (key in customTools) continue;
|
|
202
|
+
customTools[key] = readPermissionToolAction(context, key) ?? undefined;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
return computeApprovalPosture({
|
|
206
|
+
autoApprove: readAutoApprove(context),
|
|
207
|
+
mode,
|
|
208
|
+
customTools,
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
|
|
189
212
|
function predictPermission(context: CommandContext, toolName: string, category: PermissionCategory): PermissionPrediction {
|
|
190
|
-
|
|
213
|
+
const posture = readEffectivePosture(context);
|
|
214
|
+
|
|
215
|
+
if (posture.autoApprove) {
|
|
191
216
|
return {
|
|
192
217
|
outcome: 'allowed',
|
|
193
218
|
sourceLayer: 'config_policy',
|
|
194
219
|
reasonCode: 'config_allow',
|
|
195
|
-
mode:
|
|
196
|
-
reason:
|
|
220
|
+
mode: posture.mode,
|
|
221
|
+
reason: posture.detail,
|
|
197
222
|
};
|
|
198
223
|
}
|
|
199
224
|
|
|
200
|
-
const mode =
|
|
225
|
+
const mode = posture.mode;
|
|
201
226
|
if (mode === 'allow-all') {
|
|
202
227
|
return {
|
|
203
228
|
outcome: 'allowed',
|
|
@@ -350,6 +375,10 @@ export function explainAgentPolicyDecision(
|
|
|
350
375
|
...(permission.outcome === 'prompt' ? [`Answer the ${category} permission prompt for ${toolName}.`] : []),
|
|
351
376
|
...(confirmation.required && !confirmation.confirmed ? ['Call the route with confirm:true and explicitUserRequest.'] : []),
|
|
352
377
|
];
|
|
378
|
+
// The overall approval posture — computed by the SAME shared helper that
|
|
379
|
+
// cli/status.ts, the doctor surface, and the footer use, so this tool's
|
|
380
|
+
// own displayed posture text can never disagree with theirs.
|
|
381
|
+
const posture = readEffectivePosture(context);
|
|
353
382
|
|
|
354
383
|
return {
|
|
355
384
|
status: 'found',
|
|
@@ -359,6 +388,12 @@ export function explainAgentPolicyDecision(
|
|
|
359
388
|
registered: definition !== undefined,
|
|
360
389
|
category,
|
|
361
390
|
userExplanation: userExplanation(status, category),
|
|
391
|
+
posture: {
|
|
392
|
+
label: posture.label,
|
|
393
|
+
autoApprove: posture.autoApprove,
|
|
394
|
+
mode: posture.mode,
|
|
395
|
+
bypassesPrompts: posture.bypassesPrompts,
|
|
396
|
+
},
|
|
362
397
|
policyLayers: [
|
|
363
398
|
{
|
|
364
399
|
layer: 'Agent route guard',
|
|
@@ -52,10 +52,10 @@ interface AgentScheduleToolArgs {
|
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
const LIFECYCLE_ACTIONS: Readonly<Record<'run' | 'pause' | 'resume' | 'delete', string>> = {
|
|
55
|
-
run: 'schedules.run',
|
|
56
|
-
pause: 'schedules.disable',
|
|
57
|
-
resume: 'schedules.enable',
|
|
58
|
-
delete: 'schedules.delete',
|
|
55
|
+
run: 'automation.schedules.run',
|
|
56
|
+
pause: 'automation.schedules.disable',
|
|
57
|
+
resume: 'automation.schedules.enable',
|
|
58
|
+
delete: 'automation.schedules.delete',
|
|
59
59
|
};
|
|
60
60
|
|
|
61
61
|
function readString(value: unknown): string {
|
|
@@ -248,7 +248,7 @@ export function createAgentScheduleTool(
|
|
|
248
248
|
const action = readAction(args);
|
|
249
249
|
if (action === 'list') {
|
|
250
250
|
return operatorMethodTool.execute({
|
|
251
|
-
methodId: 'schedules.list',
|
|
251
|
+
methodId: 'automation.schedules.list',
|
|
252
252
|
input: listInput(args),
|
|
253
253
|
});
|
|
254
254
|
}
|
|
@@ -1,9 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Strip ANSI SGR/CSI escape sequences and OSC-8 hyperlink sequences
|
|
3
|
+
* from a string so that only visible characters remain for width measurement.
|
|
4
|
+
*
|
|
5
|
+
* Covers:
|
|
6
|
+
* - CSI sequences: ESC [ ... <final byte 0x40-0x7E> (includes SGR \x1b[...m)
|
|
7
|
+
* - OSC sequences: ESC ] ... ST where ST is ESC\\ or BEL (0x07)
|
|
8
|
+
* - Simple ESC followed by a single non-bracket/non-] character (e.g. ESC c)
|
|
9
|
+
*/
|
|
10
|
+
function stripAnsi(text: string): string {
|
|
11
|
+
let result = '';
|
|
12
|
+
let i = 0;
|
|
13
|
+
while (i < text.length) {
|
|
14
|
+
if (text[i] === '\x1b') {
|
|
15
|
+
const next = text[i + 1];
|
|
16
|
+
if (next === '[') {
|
|
17
|
+
// CSI: skip until final byte (0x40-0x7E)
|
|
18
|
+
i += 2;
|
|
19
|
+
while (i < text.length) {
|
|
20
|
+
const c = text.charCodeAt(i);
|
|
21
|
+
i++;
|
|
22
|
+
if (c >= 0x40 && c <= 0x7e) break;
|
|
23
|
+
}
|
|
24
|
+
} else if (next === ']') {
|
|
25
|
+
// OSC: skip until ST (ESC\\ or BEL)
|
|
26
|
+
i += 2;
|
|
27
|
+
while (i < text.length) {
|
|
28
|
+
if (text[i] === '\x07') { i++; break; }
|
|
29
|
+
if (text[i] === '\x1b' && text[i + 1] === '\\') { i += 2; break; }
|
|
30
|
+
i++;
|
|
31
|
+
}
|
|
32
|
+
} else {
|
|
33
|
+
// Simple two-byte escape (ESC + one char)
|
|
34
|
+
i += next !== undefined ? 2 : 1;
|
|
35
|
+
}
|
|
36
|
+
} else {
|
|
37
|
+
result += text[i];
|
|
38
|
+
i++;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return result;
|
|
42
|
+
}
|
|
43
|
+
|
|
1
44
|
/**
|
|
2
45
|
* Calculates the visual width of a string in the terminal.
|
|
3
46
|
* Handles CJK characters, emoji (including ZWJ sequences), and
|
|
4
|
-
* variation selectors correctly as double-width.
|
|
47
|
+
* variation selectors correctly as double-width. ANSI escape sequences
|
|
48
|
+
* (SGR/CSI/OSC-8) are stripped before measurement.
|
|
5
49
|
*/
|
|
6
50
|
export function getDisplayWidth(text: string): number {
|
|
51
|
+
text = stripAnsi(text);
|
|
7
52
|
let width = 0;
|
|
8
53
|
let i = 0;
|
|
9
54
|
while (i < text.length) {
|
|
@@ -34,6 +79,13 @@ export function getDisplayWidth(text: string): number {
|
|
|
34
79
|
code === 0x2717 ||
|
|
35
80
|
code === 0x2714 ||
|
|
36
81
|
code === 0x2718 ||
|
|
82
|
+
// ✕ (0x2715) and ✖ (0x2716) — the multiplication-X cross family used for
|
|
83
|
+
// the error-line prefix. Terminals draw them one cell wide, but they sit
|
|
84
|
+
// inside the 0x2600–0x27bf emoji block below and would otherwise be counted
|
|
85
|
+
// as width 2, desyncing the styled cell grid from the physical glyph and
|
|
86
|
+
// corrupting the following text (the "✕t" glitch on the steer error line).
|
|
87
|
+
code === 0x2715 ||
|
|
88
|
+
code === 0x2716 ||
|
|
37
89
|
code === 0x2022 ||
|
|
38
90
|
code === 0x258d ||
|
|
39
91
|
(code >= 0x2500 && code <= 0x257f)
|
|
@@ -107,6 +159,51 @@ export function truncateDisplay(text: string, width: number, ellipsis = '…'):
|
|
|
107
159
|
return result + ellipsis;
|
|
108
160
|
}
|
|
109
161
|
|
|
162
|
+
/** A single footer/status-line segment plus its survival priority. */
|
|
163
|
+
export interface PrioritizedSegment {
|
|
164
|
+
readonly text: string;
|
|
165
|
+
/** Lower number = higher priority = dropped LAST under width pressure. */
|
|
166
|
+
readonly priority: number;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Join segments left-to-right with `separator`, but when the joined line
|
|
171
|
+
* would exceed `width`, drop whole low-priority segments (highest `priority`
|
|
172
|
+
* number first) one at a time until it fits — rather than character-truncating
|
|
173
|
+
* the joined string, which can mangle a high-value segment mid-word (e.g. a
|
|
174
|
+
* `spine:online` daemon-liveness marker clipped to `spi…`).
|
|
175
|
+
*
|
|
176
|
+
* Only falls back to character truncation (via truncateDisplay) if the
|
|
177
|
+
* remaining highest-priority segments still don't fit at width — a rare,
|
|
178
|
+
* very-narrow-terminal case.
|
|
179
|
+
*/
|
|
180
|
+
export function joinPrioritizedSegments(
|
|
181
|
+
segments: readonly PrioritizedSegment[],
|
|
182
|
+
separator: string,
|
|
183
|
+
width: number,
|
|
184
|
+
): string {
|
|
185
|
+
const join = (list: readonly PrioritizedSegment[]) => list.map(s => s.text).join(separator);
|
|
186
|
+
let kept = segments;
|
|
187
|
+
// Keep dropping whole segments while more than one remains; once a single
|
|
188
|
+
// segment is left, stop — an empty result would be a worse outcome than
|
|
189
|
+
// falling through to character truncation on that last segment below.
|
|
190
|
+
while (kept.length > 1 && getDisplayWidth(join(kept)) > width) {
|
|
191
|
+
// Drop the single lowest-priority (highest `priority` number) segment;
|
|
192
|
+
// ties broken toward the leftmost (earlier-declared) segment surviving —
|
|
193
|
+
// `>=` (not `>`) so that on equal priority the LATER index keeps winning
|
|
194
|
+
// as the drop candidate, leaving the earliest-declared segment of that
|
|
195
|
+
// priority tier intact (e.g. cwd survives over model when both are
|
|
196
|
+
// priority 0, since cwd is declared first).
|
|
197
|
+
let dropIdx = 0;
|
|
198
|
+
for (let i = 1; i < kept.length; i++) {
|
|
199
|
+
if (kept[i].priority >= kept[dropIdx].priority) dropIdx = i;
|
|
200
|
+
}
|
|
201
|
+
kept = kept.slice(0, dropIdx).concat(kept.slice(dropIdx + 1));
|
|
202
|
+
}
|
|
203
|
+
const joined = join(kept);
|
|
204
|
+
return getDisplayWidth(joined) > width ? truncateDisplay(joined, width) : joined;
|
|
205
|
+
}
|
|
206
|
+
|
|
110
207
|
export function padDisplayEnd(text: string, width: number): string {
|
|
111
208
|
const currentWidth = getDisplayWidth(text);
|
|
112
209
|
if (currentWidth >= width) return text;
|
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.5.
|
|
9
|
+
let _version = '1.5.6';
|
|
10
10
|
try {
|
|
11
11
|
const pkg = JSON.parse(readFileSync(join(import.meta.dir, '..', 'package.json'), 'utf-8')) as {
|
|
12
12
|
readonly version?: unknown;
|