@pellux/goodvibes-agent 0.1.107 → 0.1.108
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 +38 -13
- package/README.md +27 -11
- package/docs/README.md +5 -5
- package/docs/{runtime-connection.md → connected-services.md} +8 -8
- package/docs/getting-started.md +28 -12
- package/docs/release-and-publishing.md +4 -4
- package/package.json +2 -12
- package/src/agent/reminder-schedule-format.ts +75 -0
- package/src/agent/reminder-schedule.ts +494 -0
- package/src/agent/routine-schedule-format.ts +7 -7
- package/src/agent/routine-schedule-promotion.ts +1 -1
- package/src/agent/routine-schedule-receipts.ts +1 -1
- package/src/agent/skill-discovery.ts +2 -0
- package/src/cli/agent-knowledge-args.ts +93 -0
- package/src/cli/agent-knowledge-command.ts +200 -369
- package/src/cli/agent-knowledge-format.ts +58 -7
- package/src/cli/agent-knowledge-methods.ts +84 -0
- package/src/cli/agent-knowledge-runtime.ts +240 -0
- package/src/cli/completion.ts +0 -2
- package/src/cli/config-overrides.ts +2 -2
- package/src/cli/help.ts +34 -15
- package/src/cli/management-commands.ts +2 -2
- package/src/cli/management.ts +8 -8
- package/src/cli/package-verification.ts +7 -3
- package/src/cli/parser.ts +10 -4
- package/src/cli/service-posture.ts +6 -6
- package/src/cli/status.ts +32 -32
- package/src/input/agent-workspace-activation.ts +24 -13
- package/src/input/agent-workspace-basic-command-editors.ts +448 -0
- package/src/input/agent-workspace-categories.ts +42 -34
- package/src/input/agent-workspace-channels.ts +3 -3
- package/src/input/agent-workspace-command-editor.ts +65 -0
- package/src/input/agent-workspace-editors.ts +17 -2
- package/src/input/agent-workspace-knowledge-query-editor.ts +74 -0
- package/src/input/agent-workspace-knowledge-url-editor.ts +95 -0
- package/src/input/agent-workspace-reminder-schedule-editor.ts +125 -0
- package/src/input/agent-workspace-routine-schedule-editor.ts +127 -0
- package/src/input/agent-workspace-setup.ts +2 -2
- package/src/input/agent-workspace-types.ts +20 -2
- package/src/input/agent-workspace-voice-media.ts +5 -5
- package/src/input/agent-workspace.ts +39 -2
- package/src/input/commands/agent-runtime-profile-runtime.ts +1 -1
- package/src/input/commands/agent-skills-runtime.ts +94 -2
- package/src/input/commands/brief-runtime.ts +126 -0
- package/src/input/commands/channels-runtime.ts +47 -0
- package/src/input/commands/health-runtime.ts +10 -10
- package/src/input/commands/knowledge.ts +176 -1
- package/src/input/commands/planning-runtime.ts +1 -1
- package/src/input/commands/platform-access-runtime.ts +10 -10
- package/src/input/commands/policy-dispatch.ts +1 -1
- package/src/input/commands/schedule-runtime.ts +42 -5
- package/src/input/commands/security-runtime.ts +1 -1
- package/src/input/commands/session-content.ts +1 -1
- package/src/input/commands/session-workflow.ts +1 -1
- package/src/input/commands/shell-core.ts +4 -2
- package/src/input/commands/tasks-runtime.ts +3 -3
- package/src/input/commands.ts +4 -0
- package/src/input/handler-onboarding.ts +4 -4
- package/src/input/handler.ts +3 -2
- package/src/input/onboarding/onboarding-wizard-helpers.ts +1 -1
- package/src/input/onboarding/onboarding-wizard-operator-steps.ts +13 -13
- package/src/input/onboarding/onboarding-wizard-steps.ts +8 -8
- package/src/input/settings-modal-agent-policy.ts +1 -1
- package/src/input/slash-command-parser.ts +60 -0
- package/src/panels/builtin/agent.ts +1 -1
- package/src/panels/builtin/operations.ts +2 -2
- package/src/panels/provider-account-snapshot.ts +1 -1
- package/src/panels/provider-health-domains.ts +6 -6
- package/src/panels/subscription-panel.ts +1 -1
- package/src/panels/tasks-panel.ts +3 -3
- package/src/renderer/agent-workspace.ts +43 -30
- package/src/renderer/help-overlay.ts +1 -1
- package/src/renderer/settings-modal.ts +13 -13
- package/src/runtime/bootstrap-hook-bridge.ts +1 -1
- package/src/runtime/bootstrap.ts +8 -8
- package/src/runtime/index.ts +2 -2
- package/src/runtime/onboarding/derivation.ts +6 -6
- package/src/shell/service-settings-sync.ts +1 -1
- package/src/version.ts +1 -1
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import type { AutomationJob } from '@pellux/goodvibes-sdk/platform/automation';
|
|
2
|
+
import type { CommandContext, CommandRegistry } from '../command-registry.ts';
|
|
3
|
+
import { buildAgentWorkspaceRuntimeSnapshot } from '../agent-workspace-snapshot.ts';
|
|
4
|
+
import { WORK_PLAN_STATUSES, type WorkPlanItemStatus } from '../../work-plans/work-plan-store.ts';
|
|
5
|
+
|
|
6
|
+
type StatusCounts = Record<WorkPlanItemStatus, number>;
|
|
7
|
+
|
|
8
|
+
function countReady<T extends { readonly status: string }>(items: readonly T[], status: string): number {
|
|
9
|
+
return items.filter((item) => item.status === status).length;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function workPlanCounts(ctx: CommandContext): { readonly total: number; readonly counts: StatusCounts } {
|
|
13
|
+
const counts = Object.fromEntries(WORK_PLAN_STATUSES.map((status) => [status, 0])) as StatusCounts;
|
|
14
|
+
try {
|
|
15
|
+
const items = ctx.workspace?.workPlanStore?.listItems?.() ?? [];
|
|
16
|
+
for (const item of items) {
|
|
17
|
+
counts[item.status] += 1;
|
|
18
|
+
}
|
|
19
|
+
return { total: items.length, counts };
|
|
20
|
+
} catch {
|
|
21
|
+
return { total: 0, counts };
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function automationJobs(ctx: CommandContext): readonly AutomationJob[] {
|
|
26
|
+
try {
|
|
27
|
+
return ctx.ops?.automationManager?.listJobs?.() ?? [];
|
|
28
|
+
} catch {
|
|
29
|
+
return [];
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function plural(count: number, singular: string, pluralWord = `${singular}s`): string {
|
|
34
|
+
return `${count} ${count === 1 ? singular : pluralWord}`;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function formatWorkPlanLine(total: number, counts: StatusCounts): string {
|
|
38
|
+
if (total === 0) return ' work plan: empty';
|
|
39
|
+
const active = counts.pending + counts.in_progress + counts.blocked;
|
|
40
|
+
return [
|
|
41
|
+
` work plan: ${plural(total, 'item')}`,
|
|
42
|
+
`active ${active}`,
|
|
43
|
+
`pending ${counts.pending}`,
|
|
44
|
+
`in progress ${counts.in_progress}`,
|
|
45
|
+
`blocked ${counts.blocked}`,
|
|
46
|
+
`done ${counts.done}`,
|
|
47
|
+
].join('; ');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function formatWarnings(warnings: readonly string[]): readonly string[] {
|
|
51
|
+
if (warnings.length === 0) return [' warnings: none'];
|
|
52
|
+
return [' warnings:', ...warnings.slice(0, 4).map((warning) => ` - ${warning}`)];
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function formatAgentOperatorBriefing(ctx: CommandContext): string {
|
|
56
|
+
const snapshot = buildAgentWorkspaceRuntimeSnapshot(ctx);
|
|
57
|
+
const workPlan = workPlanCounts(ctx);
|
|
58
|
+
const jobs = automationJobs(ctx);
|
|
59
|
+
const enabledJobs = jobs.filter((job) => job.enabled).length;
|
|
60
|
+
const setupReady = countReady(snapshot.setupChecklist, 'ready');
|
|
61
|
+
const setupRecommended = countReady(snapshot.setupChecklist, 'recommended');
|
|
62
|
+
const setupBlocked = countReady(snapshot.setupChecklist, 'blocked');
|
|
63
|
+
const readyChannels = snapshot.channels.filter((channel) => channel.ready).length;
|
|
64
|
+
const enabledChannels = snapshot.channels.filter((channel) => channel.enabled).length;
|
|
65
|
+
|
|
66
|
+
const nextActions = [
|
|
67
|
+
snapshot.provider === 'unknown' || snapshot.model === 'unknown'
|
|
68
|
+
? 'Choose the assistant model with /model.'
|
|
69
|
+
: '',
|
|
70
|
+
snapshot.localMemoryCount === 0
|
|
71
|
+
? 'Store durable non-secret facts with /memory add or the Agent workspace memory form.'
|
|
72
|
+
: snapshot.localMemoryReviewQueueCount > 0
|
|
73
|
+
? `Review ${plural(snapshot.localMemoryReviewQueueCount, 'memory record')} with /memory queue.`
|
|
74
|
+
: '',
|
|
75
|
+
snapshot.localSkillCount === 0 && snapshot.localSkillBundleCount === 0
|
|
76
|
+
? 'Create reusable procedures with /agent-skills create or import reviewed skill files.'
|
|
77
|
+
: snapshot.activeSkillCount === 0
|
|
78
|
+
? 'Enable reviewed skills or bundles with /agent-skills enabled and /agent-skills bundle enabled.'
|
|
79
|
+
: '',
|
|
80
|
+
snapshot.localRoutineCount === 0
|
|
81
|
+
? 'Create repeatable workflows with /routines create; promote schedules only with explicit confirmation.'
|
|
82
|
+
: snapshot.enabledRoutineCount === 0
|
|
83
|
+
? 'Enable reviewed routines with /routines enable.'
|
|
84
|
+
: '',
|
|
85
|
+
'Use /knowledge status, /knowledge search, and explicit ingest forms for Agent Knowledge only.',
|
|
86
|
+
'Use /delegate only for explicit build, fix, implementation, or review handoff to GoodVibes TUI.',
|
|
87
|
+
].filter((line): line is string => line.length > 0);
|
|
88
|
+
|
|
89
|
+
return [
|
|
90
|
+
'Agent Briefing',
|
|
91
|
+
` chat route: ${snapshot.provider} / ${snapshot.modelDisplayName}`,
|
|
92
|
+
` session: ${snapshot.sessionId}`,
|
|
93
|
+
` profile: ${snapshot.activeRuntimeProfile}`,
|
|
94
|
+
` policy: ${snapshot.executionPolicy}; WRFC ${snapshot.wrfcPolicy}`,
|
|
95
|
+
` knowledge: ${snapshot.knowledgeRoute} (${snapshot.knowledgeIsolation}; no fallback)`,
|
|
96
|
+
'',
|
|
97
|
+
'Readiness',
|
|
98
|
+
` setup: ${setupReady}/${snapshot.setupChecklist.length} ready; ${setupRecommended} recommended; ${setupBlocked} blocked`,
|
|
99
|
+
` local memory: ${plural(snapshot.localMemoryCount, 'record')}; review queue ${snapshot.localMemoryReviewQueueCount}`,
|
|
100
|
+
` personas: ${plural(snapshot.localPersonaCount, 'persona')}; active ${snapshot.activePersonaName}`,
|
|
101
|
+
` skills: ${snapshot.enabledSkillCount}/${snapshot.localSkillCount} enabled; bundles ${snapshot.enabledSkillBundleCount}/${snapshot.localSkillBundleCount}; active ${snapshot.activeSkillCount}`,
|
|
102
|
+
` routines: ${snapshot.enabledRoutineCount}/${snapshot.localRoutineCount} enabled`,
|
|
103
|
+
` channels: ${readyChannels}/${snapshot.channels.length} ready; ${enabledChannels} enabled`,
|
|
104
|
+
` voice/media: ${snapshot.voiceProviderCount} voice, ${snapshot.mediaProviderCount} media; browser surface ${snapshot.browserSurfaceEnabled ? 'enabled' : 'disabled'}`,
|
|
105
|
+
formatWorkPlanLine(workPlan.total, workPlan.counts),
|
|
106
|
+
` schedules: ${enabledJobs}/${jobs.length} visible jobs enabled`,
|
|
107
|
+
'',
|
|
108
|
+
'Next Actions',
|
|
109
|
+
...nextActions.map((line) => ` - ${line}`),
|
|
110
|
+
'',
|
|
111
|
+
...formatWarnings(snapshot.warnings),
|
|
112
|
+
].join('\n');
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export function registerBriefRuntimeCommands(registry: CommandRegistry): void {
|
|
116
|
+
registry.register({
|
|
117
|
+
name: 'brief',
|
|
118
|
+
aliases: ['briefing'],
|
|
119
|
+
description: 'Show a concise Agent operator briefing and next actions',
|
|
120
|
+
usage: '',
|
|
121
|
+
argsHint: '',
|
|
122
|
+
handler(_args, ctx) {
|
|
123
|
+
ctx.print(formatAgentOperatorBriefing(ctx));
|
|
124
|
+
},
|
|
125
|
+
});
|
|
126
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { CommandRegistry } from '../command-registry.ts';
|
|
2
|
+
import { buildAgentWorkspaceChannels } from '../agent-workspace-channels.ts';
|
|
3
|
+
|
|
4
|
+
export function registerChannelsRuntimeCommands(registry: CommandRegistry): void {
|
|
5
|
+
registry.register({
|
|
6
|
+
name: 'channels',
|
|
7
|
+
aliases: ['channel'],
|
|
8
|
+
description: 'Inspect Agent channel readiness without sending messages',
|
|
9
|
+
usage: '[list|readiness]',
|
|
10
|
+
argsHint: 'list|readiness',
|
|
11
|
+
handler(_args, ctx) {
|
|
12
|
+
const channels = buildAgentWorkspaceChannels(ctx);
|
|
13
|
+
const ready = channels.filter((channel) => channel.ready);
|
|
14
|
+
const enabled = channels.filter((channel) => channel.enabled);
|
|
15
|
+
const needsTarget = channels.filter((channel) => channel.setupState === 'needs-target');
|
|
16
|
+
const needsConfig = channels.filter((channel) => channel.setupState === 'needs-config');
|
|
17
|
+
const lines: string[] = [
|
|
18
|
+
'Channel Readiness',
|
|
19
|
+
` ready: ${ready.length}/${channels.length}`,
|
|
20
|
+
` enabled: ${enabled.length}/${channels.length}`,
|
|
21
|
+
` needs target: ${needsTarget.length}`,
|
|
22
|
+
` needs config: ${needsConfig.length}`,
|
|
23
|
+
' policy: read-only inspection; sends require explicit user action and Agent policy',
|
|
24
|
+
'',
|
|
25
|
+
...channels.map((channel) => {
|
|
26
|
+
const missing = channel.missingRequiredKeys.length > 0
|
|
27
|
+
? ` missing=${channel.missingRequiredKeys.join('|')}`
|
|
28
|
+
: '';
|
|
29
|
+
const target = channel.configuredDefaultTargetKeys.length > 0
|
|
30
|
+
? ` target=${channel.configuredDefaultTargetKeys.join('|')}`
|
|
31
|
+
: channel.defaultTargetKeys.length > 0
|
|
32
|
+
? ` target=missing(${channel.defaultTargetKeys.join('|')})`
|
|
33
|
+
: ' target=not-required';
|
|
34
|
+
return [
|
|
35
|
+
` ${channel.label}: ${channel.setupState}`,
|
|
36
|
+
`ready=${channel.ready ? 'yes' : 'no'}`,
|
|
37
|
+
`delivery=${channel.delivery}`,
|
|
38
|
+
`risk=${channel.risk}`,
|
|
39
|
+
target,
|
|
40
|
+
missing,
|
|
41
|
+
].join(' ');
|
|
42
|
+
}),
|
|
43
|
+
];
|
|
44
|
+
ctx.print(lines.join('\n'));
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
}
|
|
@@ -77,13 +77,13 @@ export function registerHealthRuntimeCommands(registry: CommandRegistry): void {
|
|
|
77
77
|
if (sub === 'auth') {
|
|
78
78
|
const auth = readModels.localAuth.getSnapshot();
|
|
79
79
|
ctx.print([
|
|
80
|
-
'Health Review:
|
|
81
|
-
' owner:
|
|
80
|
+
'Health Review: Connected-Service Auth',
|
|
81
|
+
' owner: connected GoodVibes services',
|
|
82
82
|
` compatibility users visible: ${auth.userCount}`,
|
|
83
83
|
` compatibility sessions visible: ${auth.sessionCount}`,
|
|
84
84
|
` bootstrap file signal: ${auth.bootstrapCredentialPresent ? 'present' : 'cleared'}`,
|
|
85
|
-
' Agent action: review provider/subscription auth only; do not mutate
|
|
86
|
-
...(auth.bootstrapCredentialPresent ? [' issue: bootstrap cleanup belongs
|
|
85
|
+
' Agent action: review provider/subscription auth only; do not mutate connected-service auth users or bootstrap credentials.',
|
|
86
|
+
...(auth.bootstrapCredentialPresent ? [' issue: bootstrap cleanup belongs outside Agent'] : []),
|
|
87
87
|
].join('\n'));
|
|
88
88
|
return;
|
|
89
89
|
}
|
|
@@ -132,7 +132,7 @@ export function registerHealthRuntimeCommands(registry: CommandRegistry): void {
|
|
|
132
132
|
` degraded: ${snapshot.degradedConnections}`,
|
|
133
133
|
...(issues.length > 0 ? issues.map((issue) => ` issue: ${issue}`) : [' no active remote recovery issues detected']),
|
|
134
134
|
' next: /delegate <build/fix/review task> for explicit TUI build work',
|
|
135
|
-
' next:
|
|
135
|
+
' next: repair remote worker state outside Agent',
|
|
136
136
|
].join('\n'));
|
|
137
137
|
return;
|
|
138
138
|
}
|
|
@@ -217,7 +217,7 @@ export function registerHealthRuntimeCommands(registry: CommandRegistry): void {
|
|
|
217
217
|
lines.push(' domain: settings');
|
|
218
218
|
lines.push(...(
|
|
219
219
|
settings.conflicts.length > 0
|
|
220
|
-
? [' /settings', ' /config <key>', '
|
|
220
|
+
? [' /settings', ' /config <key>', ' service-owned managed setting repair stays external']
|
|
221
221
|
: [' no active settings repair actions suggested']
|
|
222
222
|
));
|
|
223
223
|
lines.push(' verify: /health settings');
|
|
@@ -226,7 +226,7 @@ export function registerHealthRuntimeCommands(registry: CommandRegistry): void {
|
|
|
226
226
|
lines.push(' /auth review');
|
|
227
227
|
lines.push(' /provider');
|
|
228
228
|
lines.push(' /subscription providers');
|
|
229
|
-
lines.push('
|
|
229
|
+
lines.push(' connected-service auth users/bootstrap cleanup: manage outside Agent');
|
|
230
230
|
lines.push(' verify: /health auth');
|
|
231
231
|
} else if (domain === 'accounts') {
|
|
232
232
|
lines.push(' domain: accounts');
|
|
@@ -238,12 +238,12 @@ export function registerHealthRuntimeCommands(registry: CommandRegistry): void {
|
|
|
238
238
|
} else if (domain === 'services') {
|
|
239
239
|
lines.push(' domain: services');
|
|
240
240
|
lines.push(' /health services');
|
|
241
|
-
lines.push(' runtime service repair belongs
|
|
241
|
+
lines.push(' runtime service repair belongs outside Agent');
|
|
242
242
|
lines.push(' verify: /health services');
|
|
243
243
|
} else if (domain === 'remote') {
|
|
244
244
|
lines.push(' domain: remote');
|
|
245
245
|
lines.push(' /delegate <build/fix/review task> for explicit TUI build work');
|
|
246
|
-
lines.push(' remote runner setup and recovery belong
|
|
246
|
+
lines.push(' remote runner setup and recovery belong outside Agent');
|
|
247
247
|
lines.push(' verify: /health remote');
|
|
248
248
|
} else if (domain === 'mcp') {
|
|
249
249
|
lines.push(' domain: mcp');
|
|
@@ -311,7 +311,7 @@ export function registerHealthRuntimeCommands(registry: CommandRegistry): void {
|
|
|
311
311
|
` account issues: ${accountSnapshot.issueCount}`,
|
|
312
312
|
` settings conflicts: ${settingsSnapshot.conflicts.length}`,
|
|
313
313
|
` managed locks: ${settingsSnapshot.managedLockCount}`,
|
|
314
|
-
`
|
|
314
|
+
` connected-service auth owner: outside Agent`,
|
|
315
315
|
` remote workers: ${snapshot.remoteRunnerCount}`,
|
|
316
316
|
...formatSessionMaintenanceLines(maintenance, 'guided').map((line) => ` ${line}`),
|
|
317
317
|
...(snapshot.issues.length > 0 ? ['', ...snapshot.issues.map((issue) => ` [${issue.severity.toUpperCase()}] ${issue.area}: ${issue.message}`)] : []),
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import type { KnowledgeService } from '@pellux/goodvibes-sdk/platform/knowledge';
|
|
2
|
+
import type { BrowserKnowledgeKind, BrowserKnowledgeSourceKind } from '@pellux/goodvibes-sdk/platform/knowledge';
|
|
2
3
|
import type { CommandContext, SlashCommand } from '../command-registry.ts';
|
|
3
4
|
import { requireYesFlag, stripYesFlag } from './confirmation.ts';
|
|
4
5
|
|
|
5
6
|
const KNOWLEDGE_REVIEW_ACTIONS = ['accept', 'reject', 'resolve', 'reopen', 'edit', 'forget'] as const;
|
|
7
|
+
const BROWSER_KINDS = ['chrome', 'chromium', 'brave', 'edge', 'vivaldi', 'arc', 'opera', 'firefox', 'zen', 'librewolf', 'waterfox', 'floorp', 'safari', 'orion', 'epiphany'] as const satisfies readonly BrowserKnowledgeKind[];
|
|
8
|
+
const BROWSER_SOURCE_KINDS = ['history', 'bookmark'] as const satisfies readonly BrowserKnowledgeSourceKind[];
|
|
6
9
|
|
|
7
10
|
type KnowledgeReviewAction = typeof KNOWLEDGE_REVIEW_ACTIONS[number];
|
|
8
11
|
type KnowledgeAskInput = Parameters<KnowledgeService['ask']>[0];
|
|
@@ -63,6 +66,45 @@ function readPositiveIntFlag(args: string[], name: string, fallback: number): nu
|
|
|
63
66
|
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
64
67
|
}
|
|
65
68
|
|
|
69
|
+
function readFirstStringListFlag(args: string[], names: readonly string[]): string[] {
|
|
70
|
+
for (const name of names) {
|
|
71
|
+
const values = readStringListFlag(args, name);
|
|
72
|
+
if (values.length > 0) return values;
|
|
73
|
+
}
|
|
74
|
+
return [];
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function toBrowserKinds(values: readonly string[]): readonly BrowserKnowledgeKind[] {
|
|
78
|
+
return values.filter((value): value is BrowserKnowledgeKind => BROWSER_KINDS.includes(value as BrowserKnowledgeKind));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function toBrowserSourceKinds(values: readonly string[]): readonly BrowserKnowledgeSourceKind[] {
|
|
82
|
+
return values.filter((value): value is BrowserKnowledgeSourceKind => BROWSER_SOURCE_KINDS.includes(value as BrowserKnowledgeSourceKind));
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function readSinceMsFlag(args: string[]): number | undefined {
|
|
86
|
+
const raw = readFlag(args, '--since-days');
|
|
87
|
+
if (!raw) return undefined;
|
|
88
|
+
const parsed = Number.parseInt(raw, 10);
|
|
89
|
+
if (!Number.isFinite(parsed) || parsed <= 0) return undefined;
|
|
90
|
+
return Date.now() - parsed * 24 * 60 * 60 * 1000;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function parseConnectorInputFlag(args: string[]): unknown {
|
|
94
|
+
const value = readFlag(args, '--input');
|
|
95
|
+
if (!value) return undefined;
|
|
96
|
+
const trimmed = value.trim();
|
|
97
|
+
if (!trimmed) return undefined;
|
|
98
|
+
if (trimmed.startsWith('{') || trimmed.startsWith('[')) {
|
|
99
|
+
try {
|
|
100
|
+
return JSON.parse(trimmed) as unknown;
|
|
101
|
+
} catch {
|
|
102
|
+
return trimmed;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return trimmed;
|
|
106
|
+
}
|
|
107
|
+
|
|
66
108
|
function readJsonObjectFlag(args: string[], name: string): Record<string, unknown> | null | undefined {
|
|
67
109
|
const value = readFlag(args, name);
|
|
68
110
|
if (!value) return undefined;
|
|
@@ -159,7 +201,7 @@ export const knowledgeCommand: SlashCommand = {
|
|
|
159
201
|
aliases: ['know', 'kb'],
|
|
160
202
|
description: 'Agent Knowledge/Wiki: isolated Agent-owned sources, graph, review queue, and compact prompt packets.',
|
|
161
203
|
usage: '<subcommand> [args]',
|
|
162
|
-
argsHint: 'status|ask|ingest-url --yes|import-bookmarks --yes|list|search|get|queue|review-issue --yes',
|
|
204
|
+
argsHint: 'status|ask|ingest-url --yes|ingest-file --yes|import-bookmarks --yes|list|search|get|queue|review-issue --yes',
|
|
163
205
|
handler: async (args: string[], context: CommandContext): Promise<void> => {
|
|
164
206
|
const knowledge = requireAgentKnowledgeApi(context);
|
|
165
207
|
if (!knowledge) {
|
|
@@ -243,6 +285,31 @@ export const knowledgeCommand: SlashCommand = {
|
|
|
243
285
|
break;
|
|
244
286
|
}
|
|
245
287
|
|
|
288
|
+
case 'ingest-file':
|
|
289
|
+
case 'ingest-artifact': {
|
|
290
|
+
const [path] = positionalArgs(rest, ['--title', '--tags', '--folder', '--connector']);
|
|
291
|
+
if (!path) {
|
|
292
|
+
context.print('[knowledge] Usage: /knowledge ingest-file <path> [--title <title>] [--tags <a,b>] [--folder <path>] --yes');
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
if (!confirmation.yes) {
|
|
296
|
+
requireYesFlag(context, `ingest file into Agent Knowledge ${path}`, '/knowledge ingest-file <path> [--title <title>] [--tags <a,b>] [--folder <path>] --yes');
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
const result = await knowledge.ingest.artifact({
|
|
300
|
+
path,
|
|
301
|
+
title: readFlag(rest, '--title'),
|
|
302
|
+
tags: readStringListFlag(rest, '--tags'),
|
|
303
|
+
folderPath: readFlag(rest, '--folder'),
|
|
304
|
+
sessionId: context.session.runtime.sessionId,
|
|
305
|
+
connectorId: readFlag(rest, '--connector') ?? 'goodvibes-agent-file',
|
|
306
|
+
});
|
|
307
|
+
context.print(`[knowledge] Ingested ${result.source.id} ${result.source.canonicalUri ?? result.source.sourceUri ?? path}`);
|
|
308
|
+
if (result.source.summary) context.print(` ${result.source.summary}`);
|
|
309
|
+
if (result.artifactId) context.print(` artifact: ${result.artifactId}`);
|
|
310
|
+
break;
|
|
311
|
+
}
|
|
312
|
+
|
|
246
313
|
case 'import-bookmarks': {
|
|
247
314
|
const [path] = positionalArgs(rest);
|
|
248
315
|
if (!path) {
|
|
@@ -279,6 +346,61 @@ export const knowledgeCommand: SlashCommand = {
|
|
|
279
346
|
break;
|
|
280
347
|
}
|
|
281
348
|
|
|
349
|
+
case 'import-browser-history':
|
|
350
|
+
case 'sync-browser-history': {
|
|
351
|
+
if (!confirmation.yes) {
|
|
352
|
+
requireYesFlag(context, 'import browser history into Agent Knowledge', '/knowledge import-browser-history [--browsers chrome,firefox] [--sources history,bookmark] [--limit <n>] [--since-days <n>] --yes');
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
const result = await knowledge.ingest.browserHistory({
|
|
356
|
+
browsers: toBrowserKinds(readFirstStringListFlag(rest, ['--browsers', '--browser'])),
|
|
357
|
+
sourceKinds: toBrowserSourceKinds(readFirstStringListFlag(rest, ['--sources', '--source-kinds', '--source-kind'])),
|
|
358
|
+
homeOverride: readFlag(rest, '--home'),
|
|
359
|
+
limit: readPositiveIntFlag(rest, '--limit', 250),
|
|
360
|
+
sinceMs: readSinceMsFlag(rest),
|
|
361
|
+
connectorId: 'goodvibes-agent-browser-history',
|
|
362
|
+
sessionId: context.session.runtime.sessionId,
|
|
363
|
+
});
|
|
364
|
+
context.print(`[knowledge] Imported browser knowledge: ${result.imported} ok, ${result.failed} failed`);
|
|
365
|
+
if (result.profiles.length > 0) context.print(` profiles: ${result.profiles.length}`);
|
|
366
|
+
if (result.errors.length > 0) {
|
|
367
|
+
for (const error of result.errors.slice(0, 10)) context.print(` error: ${error}`);
|
|
368
|
+
}
|
|
369
|
+
break;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
case 'ingest-connector': {
|
|
373
|
+
const [connectorId] = positionalArgs(rest, ['--input', '--path', '--content']);
|
|
374
|
+
if (!connectorId) {
|
|
375
|
+
context.print('[knowledge] Usage: /knowledge ingest-connector <connectorId> [--input <json-or-text>|--path <path>|--content <text>] --yes');
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
const input = parseConnectorInputFlag(rest);
|
|
379
|
+
const path = readFlag(rest, '--path');
|
|
380
|
+
const content = readFlag(rest, '--content');
|
|
381
|
+
if (input === undefined && !path && !content) {
|
|
382
|
+
context.print('[knowledge] Usage: /knowledge ingest-connector <connectorId> [--input <json-or-text>|--path <path>|--content <text>] --yes');
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
if (!confirmation.yes) {
|
|
386
|
+
requireYesFlag(context, `ingest connector input into Agent Knowledge for ${connectorId}`, '/knowledge ingest-connector <connectorId> [--input <json-or-text>|--path <path>|--content <text>] --yes');
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
const result = await knowledge.ingest.connectorInput({
|
|
390
|
+
connectorId,
|
|
391
|
+
input,
|
|
392
|
+
path,
|
|
393
|
+
content,
|
|
394
|
+
sessionId: context.session.runtime.sessionId,
|
|
395
|
+
allowPrivateHosts: rest.includes('--allow-private-hosts'),
|
|
396
|
+
});
|
|
397
|
+
context.print(`[knowledge] Imported connector input: ${result.imported} ok, ${result.failed} failed`);
|
|
398
|
+
if (result.errors.length > 0) {
|
|
399
|
+
for (const error of result.errors.slice(0, 10)) context.print(` error: ${error}`);
|
|
400
|
+
}
|
|
401
|
+
break;
|
|
402
|
+
}
|
|
403
|
+
|
|
282
404
|
case 'list': {
|
|
283
405
|
const limit = Math.max(1, Number.parseInt(readFlag(rest, '--limit') ?? '10', 10) || 10);
|
|
284
406
|
const kind = (readFlag(rest, '--kind') ?? 'sources').toLowerCase();
|
|
@@ -464,6 +586,55 @@ export const knowledgeCommand: SlashCommand = {
|
|
|
464
586
|
break;
|
|
465
587
|
}
|
|
466
588
|
|
|
589
|
+
case 'connectors': {
|
|
590
|
+
const [first, second] = positionalArgs(rest);
|
|
591
|
+
if (first === 'doctor') {
|
|
592
|
+
if (!second) {
|
|
593
|
+
context.print('[knowledge] Usage: /knowledge connectors doctor <connectorId>');
|
|
594
|
+
return;
|
|
595
|
+
}
|
|
596
|
+
const report = await knowledge.connectors.doctor(second);
|
|
597
|
+
if (!report) {
|
|
598
|
+
context.print(`[knowledge] Connector doctor report unavailable: ${second}`);
|
|
599
|
+
return;
|
|
600
|
+
}
|
|
601
|
+
context.print([
|
|
602
|
+
`[knowledge] Connector doctor ${report.connectorId}`,
|
|
603
|
+
` ready: ${report.ready ? 'yes' : 'no'}`,
|
|
604
|
+
` summary: ${report.summary}`,
|
|
605
|
+
...(report.checks.length > 0 ? [' checks:', ...report.checks.slice(0, 10).map((check) => ` - ${check.id} [${check.status}] ${check.label} - ${check.detail}`)] : []),
|
|
606
|
+
...(report.hints.length > 0 ? [' hints:', ...report.hints.slice(0, 8).map((hint) => ` - ${hint}`)] : []),
|
|
607
|
+
].join('\n'));
|
|
608
|
+
return;
|
|
609
|
+
}
|
|
610
|
+
if (first) {
|
|
611
|
+
const connector = knowledge.connectors.get(first);
|
|
612
|
+
if (!connector) {
|
|
613
|
+
context.print(`[knowledge] Unknown connector: ${first}`);
|
|
614
|
+
return;
|
|
615
|
+
}
|
|
616
|
+
context.print([
|
|
617
|
+
`[knowledge] Connector ${connector.id}`,
|
|
618
|
+
` name: ${connector.displayName ?? connector.id}`,
|
|
619
|
+
` sourceType: ${connector.sourceType}`,
|
|
620
|
+
` description: ${connector.description}`,
|
|
621
|
+
` capabilities: ${connector.capabilities?.join(', ') || 'none'}`,
|
|
622
|
+
].join('\n'));
|
|
623
|
+
return;
|
|
624
|
+
}
|
|
625
|
+
const connectors = knowledge.connectors.list();
|
|
626
|
+
if (connectors.length === 0) {
|
|
627
|
+
context.print('[knowledge] No connectors.');
|
|
628
|
+
return;
|
|
629
|
+
}
|
|
630
|
+
context.print(`[knowledge] Connectors (${connectors.length}):`);
|
|
631
|
+
for (const connector of connectors) {
|
|
632
|
+
context.print(` ${connector.id} [${connector.sourceType}] ${connector.displayName ?? connector.id}`);
|
|
633
|
+
context.print(` ${connector.description}`);
|
|
634
|
+
}
|
|
635
|
+
break;
|
|
636
|
+
}
|
|
637
|
+
|
|
467
638
|
case 'reports': {
|
|
468
639
|
const [limitArg] = positionalArgs(rest);
|
|
469
640
|
const limit = Math.max(1, Number.parseInt(limitArg ?? '10', 10) || 10);
|
|
@@ -564,14 +735,18 @@ export const knowledgeCommand: SlashCommand = {
|
|
|
564
735
|
' status',
|
|
565
736
|
' ask <query> [--limit <n>] [--mode <concise|standard|detailed>]',
|
|
566
737
|
' ingest-url <url> [--title <title>] [--tags <a,b>] [--folder <path>] --yes',
|
|
738
|
+
' ingest-file <path> [--title <title>] [--tags <a,b>] [--folder <path>] --yes',
|
|
739
|
+
' ingest-connector <connectorId> [--input <json-or-text>|--path <path>|--content <text>] --yes',
|
|
567
740
|
' import-bookmarks <path> --yes',
|
|
568
741
|
' import-urls <path> --yes',
|
|
742
|
+
' import-browser-history [--browsers chrome,firefox] [--sources history,bookmark] [--limit <n>] [--since-days <n>] --yes',
|
|
569
743
|
' list [--kind <sources|nodes|issues>] [--limit <n>]',
|
|
570
744
|
' search <query> [--limit <n>]',
|
|
571
745
|
' get <id>',
|
|
572
746
|
' queue [limit]',
|
|
573
747
|
' review-issue <issueId> <accept|reject|resolve|reopen|edit|forget> [--reviewer <name>] [--value <json-object>] --yes',
|
|
574
748
|
' candidates [limit]',
|
|
749
|
+
' connectors [connectorId|doctor <connectorId>]',
|
|
575
750
|
' reports [limit]',
|
|
576
751
|
' schedules',
|
|
577
752
|
' lint',
|
|
@@ -62,7 +62,7 @@ export function registerPlanningRuntimeCommands(registry: CommandRegistry): void
|
|
|
62
62
|
const parsed = stripYesFlag(args);
|
|
63
63
|
const subcommand = parsed.rest[0]?.toLowerCase() ?? '';
|
|
64
64
|
if ((subcommand === 'override' || subcommand === 'clear') && !parsed.yes) {
|
|
65
|
-
requireYesFlag(ctx, `${subcommand} planner
|
|
65
|
+
requireYesFlag(ctx, `${subcommand} planner state`, `/plan ${subcommand}${subcommand === 'override' ? ' <strategy>' : ''} --yes`);
|
|
66
66
|
return;
|
|
67
67
|
}
|
|
68
68
|
const result = ctx.ops.planRuntime
|
|
@@ -55,9 +55,9 @@ export function registerPlatformAccessRuntimeCommands(registry: CommandRegistry)
|
|
|
55
55
|
}
|
|
56
56
|
if (target === 'service' || target === 'runtime' || target === 'listener' || target === 'daemon') {
|
|
57
57
|
ctx.print([
|
|
58
|
-
'
|
|
59
|
-
'Agent does not create, exchange, store, rotate, revoke, or clear
|
|
60
|
-
'Use the
|
|
58
|
+
'Connected-service login is outside GoodVibes Agent.',
|
|
59
|
+
'Agent does not create, exchange, store, rotate, revoke, or clear connected-service sessions.',
|
|
60
|
+
'Use the owning GoodVibes host for connected-service auth administration.',
|
|
61
61
|
'Agent login supports provider subscriptions only: /login provider <name> start|finish <code> --yes.',
|
|
62
62
|
].join('\n'));
|
|
63
63
|
return;
|
|
@@ -100,18 +100,18 @@ export function registerPlatformAccessRuntimeCommands(registry: CommandRegistry)
|
|
|
100
100
|
const sub = commandArgs[0] ?? 'review';
|
|
101
101
|
if (sub === 'local') {
|
|
102
102
|
ctx.print([
|
|
103
|
-
'
|
|
104
|
-
'Agent connects to
|
|
105
|
-
'Use the
|
|
103
|
+
'Connected-service auth management is outside GoodVibes Agent.',
|
|
104
|
+
'Agent connects to GoodVibes services owned outside this package and does not create, delete, rotate, revoke, or clear connected-service auth users, sessions, or bootstrap credentials.',
|
|
105
|
+
'Use the owning GoodVibes host for connected-service auth administration.',
|
|
106
106
|
'Agent auth commands available here: /auth review, /auth show <provider>, /auth repair <provider>, /auth bundle export <path> --yes, /auth bundle inspect <path>.',
|
|
107
107
|
].join('\n'));
|
|
108
108
|
return;
|
|
109
109
|
}
|
|
110
110
|
if (sub === 'login') {
|
|
111
111
|
ctx.print([
|
|
112
|
-
'
|
|
113
|
-
'Agent does not create, exchange, store, rotate, revoke, or clear
|
|
114
|
-
'Use the
|
|
112
|
+
'Connected-service login is outside GoodVibes Agent.',
|
|
113
|
+
'Agent does not create, exchange, store, rotate, revoke, or clear connected-service sessions.',
|
|
114
|
+
'Use the owning GoodVibes host for connected-service auth administration.',
|
|
115
115
|
].join('\n'));
|
|
116
116
|
return;
|
|
117
117
|
}
|
|
@@ -129,7 +129,7 @@ export function registerPlatformAccessRuntimeCommands(registry: CommandRegistry)
|
|
|
129
129
|
const builtinProviders = listBuiltinSubscriptionProviders().map((entry) => entry.provider);
|
|
130
130
|
ctx.print([
|
|
131
131
|
'Auth Review',
|
|
132
|
-
'
|
|
132
|
+
' connected-service auth: managed outside goodvibes-agent',
|
|
133
133
|
` stored secrets: ${snapshot.secretKeyCount}`,
|
|
134
134
|
` built-in providers: ${builtinProviders.length}${builtinProviders.length > 0 ? ` (${builtinProviders.join(', ')})` : ''}`,
|
|
135
135
|
` active subscriptions: ${snapshot.activeSubscriptions}${snapshot.activeSubscriptions > 0 ? ` (${snapshot.providers.filter((provider) => provider.activeSubscription).map((provider) => provider.provider).join(', ')})` : ''}`,
|
|
@@ -15,7 +15,7 @@ import { requireShellPaths } from './runtime-services.ts';
|
|
|
15
15
|
function getPolicyState(ctx?: CommandContext): PolicyRuntimeState {
|
|
16
16
|
const policyRuntimeState = ctx?.extensions.policyRuntimeState;
|
|
17
17
|
if (!policyRuntimeState) {
|
|
18
|
-
throw new Error('Policy
|
|
18
|
+
throw new Error('Policy state is not available in this Agent session.');
|
|
19
19
|
}
|
|
20
20
|
return policyRuntimeState;
|
|
21
21
|
}
|
|
@@ -4,6 +4,17 @@ import {
|
|
|
4
4
|
} from '@pellux/goodvibes-sdk/platform/automation';
|
|
5
5
|
import type { AutomationJob } from '@pellux/goodvibes-sdk/platform/automation';
|
|
6
6
|
import type { AutomationScheduleDefinition } from '@pellux/goodvibes-sdk/platform/automation';
|
|
7
|
+
import {
|
|
8
|
+
buildReminderSchedulePreview,
|
|
9
|
+
createReminderSchedule,
|
|
10
|
+
parseReminderScheduleArgs,
|
|
11
|
+
resolveReminderDaemonConnection,
|
|
12
|
+
} from '../../agent/reminder-schedule.ts';
|
|
13
|
+
import {
|
|
14
|
+
formatReminderScheduleFailure,
|
|
15
|
+
formatReminderSchedulePreview,
|
|
16
|
+
formatReminderScheduleSuccess,
|
|
17
|
+
} from '../../agent/reminder-schedule-format.ts';
|
|
7
18
|
import { AgentRoutineRegistry } from '../../agent/routine-registry.ts';
|
|
8
19
|
import {
|
|
9
20
|
buildRoutineSchedulePreview,
|
|
@@ -56,7 +67,7 @@ function printReadOnlyScheduleBoundary(print: (text: string) => void, requestedA
|
|
|
56
67
|
` requested: ${requestedAction}`,
|
|
57
68
|
' policy: no local Agent automation jobs, scheduled spawns, or immediate automation runs',
|
|
58
69
|
' use: /schedule list',
|
|
59
|
-
' schedule route: use /schedule promote-routine <routine> --cron <expr> --yes to create
|
|
70
|
+
' schedule route: use /schedule promote-routine <routine> --cron <expr> --yes to create a connected schedule explicitly',
|
|
60
71
|
].join('\n'));
|
|
61
72
|
}
|
|
62
73
|
|
|
@@ -86,16 +97,41 @@ async function promoteRoutineSchedule(args: readonly string[], ctx: CommandConte
|
|
|
86
97
|
ctx.print(result.ok ? `${formatRoutineScheduleSuccess(result)}\n receipt: ${receipt.id}` : `${formatRoutineScheduleFailure(result)}\n receipt: ${receipt.id}`);
|
|
87
98
|
}
|
|
88
99
|
|
|
100
|
+
async function createReminder(args: readonly string[], ctx: CommandContext): Promise<void> {
|
|
101
|
+
const parsed = parseReminderScheduleArgs(args);
|
|
102
|
+
if (parsed.errors.length > 0) {
|
|
103
|
+
ctx.print([
|
|
104
|
+
'Usage: /schedule remind (--cron <expr>|--every <interval>|--at <iso-time>) (--message <text>|<text...>) [--timezone <tz>] [--name <schedule-name>] [--provider <id>] [--model <model>] [--delivery-channel <channel[:route[:label]]>|--delivery-route <route[:label]>|--delivery-webhook <url>|--delivery-link <url>] [--disabled] --yes',
|
|
105
|
+
...parsed.errors.map((error) => ` ${error}`),
|
|
106
|
+
].join('\n'));
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
const preview = buildReminderSchedulePreview(parsed);
|
|
110
|
+
if (!parsed.yes) {
|
|
111
|
+
ctx.print(formatReminderSchedulePreview(preview));
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
const shellPaths = requireShellPaths(ctx);
|
|
115
|
+
const connection = resolveReminderDaemonConnection(ctx.platform.configManager, shellPaths.homeDirectory);
|
|
116
|
+
const result = await createReminderSchedule(connection, preview);
|
|
117
|
+
ctx.print(result.ok ? formatReminderScheduleSuccess(result) : formatReminderScheduleFailure(result));
|
|
118
|
+
}
|
|
119
|
+
|
|
89
120
|
export function registerScheduleRuntimeCommands(registry: CommandRegistry): void {
|
|
90
121
|
registry.register({
|
|
91
122
|
name: 'schedule',
|
|
92
123
|
aliases: ['sched'],
|
|
93
|
-
description: 'Inspect schedules and explicitly promote local Agent routines to
|
|
94
|
-
usage: 'list | receipts | reconcile | receipt <id> | promote-routine <routine-id> --cron <expr> [--delivery-channel slack] --yes',
|
|
95
|
-
argsHint: 'list | receipts | reconcile | receipt <id> | promote-routine <routine-id> --cron <expr> [--delivery-channel slack] --yes',
|
|
124
|
+
description: 'Inspect schedules, create confirmed reminders, and explicitly promote local Agent routines to connected schedules',
|
|
125
|
+
usage: 'list | remind --at <iso> --message <text> --yes | receipts | reconcile | receipt <id> | promote-routine <routine-id> --cron <expr> [--delivery-channel slack] --yes',
|
|
126
|
+
argsHint: 'list | remind --at <iso> --message <text> --yes | receipts | reconcile | receipt <id> | promote-routine <routine-id> --cron <expr> [--delivery-channel slack] --yes',
|
|
96
127
|
async handler(args, ctx) {
|
|
97
128
|
const sub = args[0];
|
|
98
129
|
|
|
130
|
+
if (sub === 'remind' || sub === 'reminder') {
|
|
131
|
+
await createReminder(args.slice(1), ctx);
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
|
|
99
135
|
if (sub === 'promote-routine' || sub === 'promote' || sub === 'create-routine-schedule') {
|
|
100
136
|
await promoteRoutineSchedule(args.slice(1), ctx);
|
|
101
137
|
return;
|
|
@@ -136,7 +172,7 @@ export function registerScheduleRuntimeCommands(registry: CommandRegistry): void
|
|
|
136
172
|
if (jobs.length === 0) {
|
|
137
173
|
ctx.print(
|
|
138
174
|
'No automation jobs.\n'
|
|
139
|
-
+ 'Local add/run/enable/disable/remove are blocked. Use /schedule promote-routine <routine> --cron <expr> --yes for
|
|
175
|
+
+ 'Local add/run/enable/disable/remove are blocked. Use /schedule remind --at <time> --message <text> --yes for reminders or /schedule promote-routine <routine> --cron <expr> --yes for explicit connected schedules.'
|
|
140
176
|
);
|
|
141
177
|
return;
|
|
142
178
|
}
|
|
@@ -164,6 +200,7 @@ export function registerScheduleRuntimeCommands(registry: CommandRegistry): void
|
|
|
164
200
|
+ ' /schedule receipts\n'
|
|
165
201
|
+ ' /schedule reconcile\n'
|
|
166
202
|
+ ' /schedule receipt <receipt-id>\n'
|
|
203
|
+
+ ' /schedule remind (--cron <expr>|--every <interval>|--at <iso-time>) (--message <text>|<text...>) [--delivery-channel <channel>|--delivery-route <route>|--delivery-webhook <url>] --yes\n'
|
|
167
204
|
+ ' /schedule promote-routine <routine-id> (--cron <expr>|--every <interval>|--at <iso-time>) [--delivery-channel <channel>|--delivery-route <route>|--delivery-webhook <url>] --yes\n'
|
|
168
205
|
+ ' Local schedule mutations and runs remain blocked.'
|
|
169
206
|
);
|
|
@@ -24,7 +24,7 @@ export function registerSecurityRuntimeCommands(registry: CommandRegistry): void
|
|
|
24
24
|
const securitySnapshot = requireReadModels(ctx).security.getSnapshot();
|
|
25
25
|
const policySnapshot = ctx.extensions.policyRuntimeState?.getSnapshot();
|
|
26
26
|
if (!policySnapshot) {
|
|
27
|
-
ctx.print('Policy
|
|
27
|
+
ctx.print('Policy state is not available in this Agent session.');
|
|
28
28
|
return;
|
|
29
29
|
}
|
|
30
30
|
const attackPaths = buildMcpAttackPathReview({
|