@pellux/goodvibes-agent 1.2.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +159 -0
- package/dist/package/main.js +19016 -12684
- package/docs/README.md +3 -3
- package/docs/connected-host.md +2 -2
- package/docs/getting-started.md +11 -9
- package/docs/tools-and-commands.md +12 -10
- package/docs/voice-and-live-tts.md +2 -2
- package/package.json +1 -1
- package/release/release-notes.md +17 -5
- package/release/release-readiness.json +36 -36
- package/src/agent/competitive-feature-inventory.ts +42 -44
- package/src/agent/setup-wizard-artifact-receipts.ts +366 -0
- package/src/agent/setup-wizard.ts +245 -9
- package/src/input/agent-workspace-categories.ts +2 -2
- package/src/input/agent-workspace-onboarding-categories.ts +4 -4
- package/src/input/agent-workspace-settings.ts +52 -1
- package/src/input/agent-workspace-setup-snapshot.ts +20 -1
- package/src/input/agent-workspace-setup.ts +32 -5
- package/src/input/agent-workspace-snapshot.ts +23 -3
- package/src/input/agent-workspace.ts +5 -1
- package/src/input/setup-wizard-live-receipts.ts +76 -0
- package/src/renderer/agent-workspace-context-lines.ts +21 -6
- package/src/renderer/agent-workspace.ts +46 -10
- package/src/runtime/tool-permission-safety.ts +1 -1
- package/src/tools/agent-harness-agent-orchestration-policy.ts +75 -0
- package/src/tools/agent-harness-agent-orchestration.ts +216 -128
- package/src/tools/agent-harness-autonomy-live-records.ts +154 -0
- package/src/tools/agent-harness-autonomy-queue-types.ts +1 -1
- package/src/tools/agent-harness-autonomy-queue.ts +19 -8
- package/src/tools/agent-harness-autonomy-watcher-read-models.ts +509 -0
- package/src/tools/agent-harness-background-processes.ts +8 -4
- package/src/tools/agent-harness-browser-cockpit-route.ts +188 -34
- package/src/tools/agent-harness-browser-control.ts +12 -4
- package/src/tools/agent-harness-browser-pwa-read-models.ts +622 -0
- package/src/tools/agent-harness-device-live-read-models.ts +366 -0
- package/src/tools/agent-harness-execution-posture.ts +3 -0
- package/src/tools/agent-harness-interactive-runtime-records.ts +421 -0
- package/src/tools/agent-harness-local-model-benchmarks.ts +71 -1
- package/src/tools/agent-harness-local-model-endpoints.ts +469 -354
- package/src/tools/agent-harness-local-model-smoke.ts +277 -0
- package/src/tools/agent-harness-local-model-url.ts +78 -0
- package/src/tools/agent-harness-media-posture.ts +27 -12
- package/src/tools/agent-harness-memory-external-providers.ts +796 -0
- package/src/tools/agent-harness-memory-posture.ts +253 -137
- package/src/tools/agent-harness-memory-provider-certification.ts +219 -0
- package/src/tools/agent-harness-memory-refinement.ts +340 -0
- package/src/tools/agent-harness-mode-catalog.ts +4 -2
- package/src/tools/agent-harness-model-provider-health.ts +139 -42
- package/src/tools/agent-harness-model-readiness.ts +31 -5
- package/src/tools/agent-harness-model-routing-types.ts +61 -0
- package/src/tools/agent-harness-model-routing.ts +31 -6
- package/src/tools/agent-harness-pairing-posture.ts +30 -9
- package/src/tools/agent-harness-personal-ops-certification.ts +116 -0
- package/src/tools/agent-harness-personal-ops-lanes.ts +81 -15
- package/src/tools/agent-harness-personal-ops-operations.ts +225 -0
- package/src/tools/agent-harness-personal-ops-provider-records.ts +358 -0
- package/src/tools/agent-harness-personal-ops-provider-task-records.ts +321 -0
- package/src/tools/agent-harness-personal-ops-records.ts +176 -224
- package/src/tools/agent-harness-personal-ops-types.ts +19 -1
- package/src/tools/agent-harness-personal-ops.ts +18 -11
- package/src/tools/agent-harness-remote-read-models.ts +541 -0
- package/src/tools/agent-harness-research-briefing.ts +26 -7
- package/src/tools/agent-harness-research-live-read-models.ts +500 -0
- package/src/tools/agent-harness-research-runs.ts +15 -3
- package/src/tools/agent-harness-research-workflow.ts +92 -19
- package/src/tools/agent-harness-setup-model-helpers.ts +1 -0
- package/src/tools/agent-harness-setup-smoke.ts +26 -1
- package/src/tools/agent-harness-tool-schema.ts +23 -1
- package/src/tools/agent-harness-tool-types.ts +5 -0
- package/src/tools/agent-harness-tool.ts +9 -1
- package/src/tools/agent-harness-workspace-actions.ts +1 -1
- package/src/tools/agent-memory-tool.ts +40 -1
- package/src/tools/agent-model-compare-run.ts +13 -0
- package/src/tools/agent-research-runner.ts +367 -0
- package/src/tools/agent-research-tool.ts +7 -179
- package/src/tools/agent-route-planner-candidates-surfaces.ts +1 -1
- package/src/version.ts +1 -1
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { previewHarnessText } from './agent-harness-text.ts';
|
|
2
|
+
import { readRecord, readString } from './agent-harness-model-routing-utils.ts';
|
|
3
|
+
import { redactedPersonalOpsText } from './agent-harness-personal-ops-runner.ts';
|
|
4
|
+
import type { PersonalOpsRecordCertification } from './agent-harness-personal-ops-types.ts';
|
|
5
|
+
|
|
6
|
+
function firstString(record: Readonly<Record<string, unknown>>, keys: readonly string[]): string {
|
|
7
|
+
for (const key of keys) {
|
|
8
|
+
const value = readString(record[key]);
|
|
9
|
+
if (value) return value;
|
|
10
|
+
}
|
|
11
|
+
return '';
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function readStringArray(value: unknown): readonly string[] {
|
|
15
|
+
if (Array.isArray(value)) {
|
|
16
|
+
return [...new Set(value.map((entry) => readString(entry)).filter(Boolean))].slice(0, 12);
|
|
17
|
+
}
|
|
18
|
+
const text = readString(value);
|
|
19
|
+
return text ? text.split(',').map((entry) => entry.trim()).filter(Boolean).slice(0, 12) : [];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function recordsForCertification(record: Readonly<Record<string, unknown>>): readonly Readonly<Record<string, unknown>>[] {
|
|
23
|
+
return [
|
|
24
|
+
record,
|
|
25
|
+
readRecord(record.schema),
|
|
26
|
+
readRecord(record.contract),
|
|
27
|
+
readRecord(record.receipt),
|
|
28
|
+
readRecord(record.publication),
|
|
29
|
+
readRecord(record.certification),
|
|
30
|
+
];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function firstAcross(records: readonly Readonly<Record<string, unknown>>[], keys: readonly string[]): string {
|
|
34
|
+
for (const record of records) {
|
|
35
|
+
const value = firstString(record, keys);
|
|
36
|
+
if (value) return value;
|
|
37
|
+
}
|
|
38
|
+
return '';
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function normalizeSchemaStatus(records: readonly Readonly<Record<string, unknown>>[]): PersonalOpsRecordCertification['schemaStatus'] {
|
|
42
|
+
const explicit = firstAcross(records, ['schemaStatus', 'receiptSchemaStatus', 'certificationStatus'])
|
|
43
|
+
.toLowerCase()
|
|
44
|
+
.replace(/[_\s]+/g, '-');
|
|
45
|
+
if (['certified', 'valid', 'verified', 'schema-certified'].includes(explicit)) return 'certified';
|
|
46
|
+
const schemaVersion = firstAcross(records, ['schemaVersion', 'receiptSchemaVersion', 'contractVersion']);
|
|
47
|
+
const publicationGuarantee = firstAcross(records, ['publicationGuarantee', 'hostPublicationGuarantee', 'providerPublicationGuarantee', 'personalOpsPublicationGuarantee']);
|
|
48
|
+
const provenance = firstAcross(records, ['methodId', 'sourceTool', 'actionId', 'publisher', 'publisherId']);
|
|
49
|
+
return schemaVersion && publicationGuarantee && provenance ? 'certified' : 'legacy';
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function redactedPreview(value: string, limit = 180): string {
|
|
53
|
+
return previewHarnessText(redactedPersonalOpsText(value), limit);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function receiptIds(record: Readonly<Record<string, unknown>>, records: readonly Readonly<Record<string, unknown>>[]): readonly string[] {
|
|
57
|
+
const explicit = [
|
|
58
|
+
...readStringArray(record.receiptIds),
|
|
59
|
+
...readStringArray(record.effectReceiptIds),
|
|
60
|
+
...readStringArray(record.providerReceiptIds),
|
|
61
|
+
firstAcross(records, ['receiptId', 'effectReceiptId', 'readReceiptId', 'syncReceiptId', 'operationReceiptId']),
|
|
62
|
+
].filter(Boolean);
|
|
63
|
+
return [...new Set(explicit.map((entry) => redactedPreview(entry, 96)))].slice(0, 12);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function provenance(record: Readonly<Record<string, unknown>>, records: readonly Readonly<Record<string, unknown>>[], sourcePath: string): readonly string[] {
|
|
67
|
+
const values = [
|
|
68
|
+
...readStringArray(record.provenance),
|
|
69
|
+
sourcePath ? `source ${sourcePath}` : '',
|
|
70
|
+
firstAcross(records, ['methodId']) ? `method ${firstAcross(records, ['methodId'])}` : '',
|
|
71
|
+
firstAcross(records, ['actionId']) ? `action ${firstAcross(records, ['actionId'])}` : '',
|
|
72
|
+
firstAcross(records, ['sourceTool']) ? `sourceTool ${firstAcross(records, ['sourceTool'])}` : '',
|
|
73
|
+
];
|
|
74
|
+
return [...new Set(values.map((entry) => redactedPreview(entry, 180)).filter(Boolean))].slice(0, 8);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function personalOpsRecordCertification(input: {
|
|
78
|
+
readonly record: Readonly<Record<string, unknown>>;
|
|
79
|
+
readonly sourcePath: string;
|
|
80
|
+
readonly durableId: string;
|
|
81
|
+
readonly recordKind: string;
|
|
82
|
+
readonly hasConfirmedEffectRoute?: boolean;
|
|
83
|
+
readonly requireReceipt?: boolean;
|
|
84
|
+
}): PersonalOpsRecordCertification {
|
|
85
|
+
const records = recordsForCertification(input.record);
|
|
86
|
+
const schemaStatus = normalizeSchemaStatus(records);
|
|
87
|
+
const schemaVersion = firstAcross(records, ['schemaVersion', 'receiptSchemaVersion', 'contractVersion']);
|
|
88
|
+
const publicationGuarantee = firstAcross(records, ['publicationGuarantee', 'hostPublicationGuarantee', 'providerPublicationGuarantee', 'personalOpsPublicationGuarantee']);
|
|
89
|
+
const publisher = firstAcross(records, ['publisher', 'publisherId', 'daemonId', 'hostId']);
|
|
90
|
+
const ids = receiptIds(input.record, records);
|
|
91
|
+
const primaryReceiptId = ids[0] ?? '';
|
|
92
|
+
const receiptStatus = firstAcross(records, ['receiptStatus', 'receiptOutcome', 'operationStatus']);
|
|
93
|
+
const receiptRoute = firstAcross(records, ['receiptRoute', 'inspectReceiptRoute', 'artifactRoute']);
|
|
94
|
+
const proof = provenance(input.record, records, input.sourcePath);
|
|
95
|
+
const missingSignals = [
|
|
96
|
+
...(schemaStatus === 'certified' ? [] : [`Certified ${input.recordKind} schema is not published.`]),
|
|
97
|
+
...(input.durableId ? [] : [`Durable ${input.recordKind} id is not published.`]),
|
|
98
|
+
...(publicationGuarantee ? [] : [`Provider-side ${input.recordKind} publication guarantee is not published.`]),
|
|
99
|
+
...(publisher ? [] : [`Provider-side ${input.recordKind} publisher is not published.`]),
|
|
100
|
+
...(input.hasConfirmedEffectRoute ? [] : [`Confirmed ${input.recordKind} follow-up route is not published.`]),
|
|
101
|
+
...(input.requireReceipt && ids.length === 0 ? [`Confirmed ${input.recordKind} execution receipt id is not published.`] : []),
|
|
102
|
+
];
|
|
103
|
+
return {
|
|
104
|
+
schemaStatus,
|
|
105
|
+
...(schemaVersion ? { schemaVersion: redactedPreview(schemaVersion, 80) } : {}),
|
|
106
|
+
...(publicationGuarantee ? { publicationGuarantee: redactedPreview(publicationGuarantee, 220) } : {}),
|
|
107
|
+
...(publisher ? { publisher: redactedPreview(publisher, 80) } : {}),
|
|
108
|
+
...(proof.length > 0 ? { provenance: proof } : {}),
|
|
109
|
+
...(primaryReceiptId ? { receiptId: primaryReceiptId } : {}),
|
|
110
|
+
...(receiptStatus ? { receiptStatus: redactedPreview(receiptStatus, 80) } : {}),
|
|
111
|
+
...(receiptRoute ? { receiptRoute: redactedPreview(receiptRoute, 180) } : {}),
|
|
112
|
+
...(ids.length > 0 ? { receiptIds: ids } : {}),
|
|
113
|
+
missingSignals,
|
|
114
|
+
policy: 'Personal Ops treats provider records and effect receipts as certified only when the host or SDK publishes schema, durable id, publication guarantee, publisher/provenance, and exact confirmed routes or receipt ids; otherwise the row stays visible but not release-certifying.',
|
|
115
|
+
};
|
|
116
|
+
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import type { CommandContext } from '../input/command-registry.ts';
|
|
2
2
|
import { buildAgentWorkspaceRuntimeSnapshot } from '../input/agent-workspace-snapshot.ts';
|
|
3
3
|
import { calendarWorkflows, connectorSignalsMatching, inboxWorkflows, methodIdsMatching, reminderWorkflows, taskWorkflows } from './agent-harness-personal-ops-discovery.ts';
|
|
4
|
-
import {
|
|
4
|
+
import { providerBackedQueueRecords } from './agent-harness-personal-ops-provider-records.ts';
|
|
5
|
+
import { providerBackedReminderRecords, providerBackedTaskRecords } from './agent-harness-personal-ops-provider-task-records.ts';
|
|
6
|
+
import { channelRecords, connectorRecords, localRecord, refreshableSavedRecordCount, reminderOperationRecords, routineReceiptRecord, savedProviderEffectReceiptRecords, savedReviewArtifactRecords, savedReviewQueueRecords, taskOperationRecords } from './agent-harness-personal-ops-records.ts';
|
|
5
7
|
import type { McpToolRecord, McpToolSchema, PersonalOpsLane, PersonalOpsLiveRecord } from './agent-harness-personal-ops-types.ts';
|
|
6
8
|
|
|
7
9
|
export function buildLanes(
|
|
@@ -18,14 +20,22 @@ export function buildLanes(
|
|
|
18
20
|
const schemasByQualifiedName = options.schemasByQualifiedName ?? new Map<string, McpToolSchema>();
|
|
19
21
|
const emailConnectors = connectorSignalsMatching(context, ['email', 'mail', 'imap', 'smtp', 'gmail'], { lane: 'inbox', toolsByServer: toolsByName, schemasByQualifiedName });
|
|
20
22
|
const calendarConnectors = connectorSignalsMatching(context, ['calendar', 'caldav', 'agenda'], { lane: 'calendar', toolsByServer: toolsByName, schemasByQualifiedName });
|
|
23
|
+
const inboxProviderRecords = providerBackedQueueRecords(context, 'inbox');
|
|
24
|
+
const calendarProviderRecords = providerBackedQueueRecords(context, 'calendar');
|
|
21
25
|
const inboxArtifactRecords = savedReviewArtifactRecords(context, 'inbox', emailConnectors);
|
|
22
26
|
const calendarArtifactRecords = savedReviewArtifactRecords(context, 'calendar', calendarConnectors);
|
|
23
27
|
const inboxReviewQueueRecords = savedReviewQueueRecords(context, 'inbox', emailConnectors);
|
|
24
28
|
const calendarReviewQueueRecords = savedReviewQueueRecords(context, 'calendar', calendarConnectors);
|
|
29
|
+
const inboxEffectReceiptRecords = savedProviderEffectReceiptRecords(context, 'inbox');
|
|
30
|
+
const calendarEffectReceiptRecords = savedProviderEffectReceiptRecords(context, 'calendar');
|
|
25
31
|
const refreshableInboxQueueRecords = refreshableSavedRecordCount(inboxReviewQueueRecords);
|
|
26
32
|
const refreshableCalendarQueueRecords = refreshableSavedRecordCount(calendarReviewQueueRecords);
|
|
27
33
|
const taskMethods = methodIdsMatching(['task', 'work-plan', 'workplan']);
|
|
28
34
|
const scheduleMethods = methodIdsMatching(['schedule', 'reminder']);
|
|
35
|
+
const taskProviderRecords = providerBackedTaskRecords(context);
|
|
36
|
+
const reminderProviderRecords = providerBackedReminderRecords(context);
|
|
37
|
+
const taskEffectReceiptRecords = savedProviderEffectReceiptRecords(context, 'tasks');
|
|
38
|
+
const reminderEffectReceiptRecords = savedProviderEffectReceiptRecords(context, 'reminders');
|
|
29
39
|
const readyChannels = snapshot.channels.filter((channel) => channel.ready).length;
|
|
30
40
|
const enabledChannels = snapshot.channels.filter((channel) => channel.enabled).length;
|
|
31
41
|
const configuredTargets = snapshot.channels.filter((channel) => channel.defaultTarget === 'configured').length;
|
|
@@ -39,29 +49,41 @@ export function buildLanes(
|
|
|
39
49
|
{
|
|
40
50
|
id: 'inbox',
|
|
41
51
|
label: 'Inbox',
|
|
42
|
-
status:
|
|
52
|
+
status: inboxProviderRecords.length > 0
|
|
53
|
+
? 'ready'
|
|
54
|
+
: emailMethods.length > 0 || emailConnectors.length > 0 || inboxArtifactRecords.length > 0 || inboxEffectReceiptRecords.length > 0 ? 'partial' : 'gap',
|
|
43
55
|
outcome: 'Triage inbound email or message inboxes, summarize threads, draft replies, and send only after confirmation.',
|
|
44
|
-
current:
|
|
56
|
+
current: inboxProviderRecords.length > 0
|
|
57
|
+
? 'Fresh provider-backed inbox thread records are published by the connected daemon or SDK read model; Personal Ops can triage current queue state without inventing sends, labels, or archive effects.'
|
|
58
|
+
: emailMethods.length > 0
|
|
45
59
|
? 'The daemon contract exposes email-like methods; Personal Ops workflow cards now guide inbox triage and draft boundaries around exact methods.'
|
|
46
60
|
: emailConnectors.length > 0
|
|
47
61
|
? 'A configured MCP connector looks email-capable; Personal Ops workflow cards now guide inbox triage, schema-derived operation records, and draft boundaries around its exact tools.'
|
|
48
62
|
: inboxArtifactRecords.length > 0
|
|
49
63
|
? 'Saved inbox review artifacts and thread queue items are available for recap, local draft review, or promotion; no fresh email connector is currently ready.'
|
|
64
|
+
: inboxEffectReceiptRecords.length > 0
|
|
65
|
+
? 'Saved inbox provider-effect receipts are available for audit and follow-up; no fresh email connector is currently ready.'
|
|
50
66
|
: 'No email/IMAP/SMTP methods are present in the current GoodVibes SDK operator contract.',
|
|
51
|
-
next:
|
|
67
|
+
next: inboxProviderRecords.length > 0
|
|
68
|
+
? 'Inspect one fresh thread record, summarize or draft locally, and use only the published confirmed follow-up routes for replies, sends, labels, or archive.'
|
|
69
|
+
: emailMethods.length > 0
|
|
52
70
|
? 'Use the inbox workflow cards to inspect exact methods, read selected threads, summarize priorities, and keep send as a separate confirmation.'
|
|
53
71
|
: emailConnectors.length > 0
|
|
54
72
|
? 'Use the inbox workflow cards and operation records to inspect matching MCP connector schemas, then route triage only through reviewed connector actions.'
|
|
55
73
|
: inboxArtifactRecords.length > 0
|
|
56
74
|
? 'Use saved thread queue records for local draft review or recap, then repair an email connector before reading fresh inbox data or sending.'
|
|
75
|
+
: inboxEffectReceiptRecords.length > 0
|
|
76
|
+
? 'Review saved inbox provider-effect receipts, then repair an email connector before reading fresh inbox data or sending another provider effect.'
|
|
57
77
|
: 'Install or build an email connector/MCP/plugin, then expose triage and draft-reply actions here.',
|
|
58
78
|
userRoute: 'Agent Workspace -> Personal Ops -> Channels or connector setup',
|
|
59
79
|
modelRoute: emailConnectors.length > 0 ? 'agent_harness mode:"mcp_servers" query:"email"' : 'host action:"methods" query:"email"',
|
|
60
80
|
signals: [
|
|
81
|
+
`${inboxProviderRecords.length} fresh provider-backed inbox thread record(s)`,
|
|
61
82
|
`${emailMethods.length} email-like daemon method(s)`,
|
|
62
83
|
`${emailConnectors.length} email-like MCP connector(s)`,
|
|
63
84
|
`${inboxArtifactRecords.length} saved inbox review artifact(s)`,
|
|
64
85
|
`${inboxReviewQueueRecords.length} saved inbox thread queue item(s)`,
|
|
86
|
+
`${inboxEffectReceiptRecords.length} saved inbox provider-effect receipt(s)`,
|
|
65
87
|
`${refreshableInboxQueueRecords} refreshable saved inbox queue item(s)`,
|
|
66
88
|
`${readyChannels}/${snapshot.channels.length} channel(s) ready for delivery`,
|
|
67
89
|
],
|
|
@@ -69,37 +91,51 @@ export function buildLanes(
|
|
|
69
91
|
connectorSignals: emailConnectors,
|
|
70
92
|
workflows: inboxWorkflows(emailMethods, emailConnectors),
|
|
71
93
|
liveRecords: [
|
|
94
|
+
...inboxProviderRecords,
|
|
72
95
|
...inboxReviewQueueRecords,
|
|
73
96
|
...inboxArtifactRecords,
|
|
97
|
+
...inboxEffectReceiptRecords,
|
|
74
98
|
...connectorRecords(emailConnectors, 'Inbox', 'inbox'),
|
|
75
99
|
],
|
|
76
100
|
},
|
|
77
101
|
{
|
|
78
102
|
id: 'calendar',
|
|
79
103
|
label: 'Calendar',
|
|
80
|
-
status:
|
|
104
|
+
status: calendarProviderRecords.length > 0
|
|
105
|
+
? 'ready'
|
|
106
|
+
: calendarMethods.length > 0 || calendarConnectors.length > 0 || calendarArtifactRecords.length > 0 || calendarEffectReceiptRecords.length > 0 ? 'partial' : 'gap',
|
|
81
107
|
outcome: 'Read agenda context, identify conflicts, prepare briefings, and create reminders for calendar-driven work.',
|
|
82
|
-
current:
|
|
108
|
+
current: calendarProviderRecords.length > 0
|
|
109
|
+
? 'Fresh provider-backed calendar event records are published by the connected daemon or SDK read model; Personal Ops can brief current agenda and conflict state without inventing event edits or RSVP effects.'
|
|
110
|
+
: calendarMethods.length > 0
|
|
83
111
|
? 'The daemon contract exposes calendar-like methods; Personal Ops workflow cards now guide agenda briefing and conflict-scan boundaries.'
|
|
84
112
|
: calendarConnectors.length > 0
|
|
85
113
|
? 'A configured MCP connector looks calendar-capable; Personal Ops workflow cards now guide agenda briefing, schema-derived operation records, and conflict-scan boundaries around its exact tools.'
|
|
86
114
|
: calendarArtifactRecords.length > 0
|
|
87
115
|
? 'Saved calendar review artifacts and event queue items are available for agenda recap, reminder creation, or follow-up planning; no fresh calendar connector is currently ready.'
|
|
116
|
+
: calendarEffectReceiptRecords.length > 0
|
|
117
|
+
? 'Saved calendar provider-effect receipts are available for audit and follow-up; no fresh calendar connector is currently ready.'
|
|
88
118
|
: 'No calendar/CalDAV/agenda methods are present in the current GoodVibes SDK operator contract.',
|
|
89
|
-
next:
|
|
119
|
+
next: calendarProviderRecords.length > 0
|
|
120
|
+
? 'Inspect one fresh event record, brief the agenda or conflicts locally, and use only the published confirmed follow-up routes for edits, RSVP, reschedule, or delete.'
|
|
121
|
+
: calendarMethods.length > 0
|
|
90
122
|
? 'Use the calendar workflow cards to inspect exact methods, fetch a bounded agenda window, and propose reminders or follow-ups.'
|
|
91
123
|
: calendarConnectors.length > 0
|
|
92
124
|
? 'Use the calendar workflow cards and operation records to inspect matching MCP connector schemas, then route agenda work only through reviewed connector actions.'
|
|
93
125
|
: calendarArtifactRecords.length > 0
|
|
94
126
|
? 'Use saved event queue records for recap or reminder creation, then repair a calendar connector before reading fresh agenda data or editing events.'
|
|
127
|
+
: calendarEffectReceiptRecords.length > 0
|
|
128
|
+
? 'Review saved calendar provider-effect receipts, then repair a calendar connector before reading fresh agenda data or editing events.'
|
|
95
129
|
: 'Add a CalDAV/calendar connector and route agenda briefing, conflicts, and reminders through this lane.',
|
|
96
130
|
userRoute: 'Agent Workspace -> Personal Ops -> Create reminder',
|
|
97
131
|
modelRoute: calendarConnectors.length > 0 ? 'agent_harness mode:"mcp_servers" query:"calendar"' : 'host action:"methods" query:"calendar"',
|
|
98
132
|
signals: [
|
|
133
|
+
`${calendarProviderRecords.length} fresh provider-backed calendar event record(s)`,
|
|
99
134
|
`${calendarMethods.length} calendar-like daemon method(s)`,
|
|
100
135
|
`${calendarConnectors.length} calendar-like MCP connector(s)`,
|
|
101
136
|
`${calendarArtifactRecords.length} saved calendar review artifact(s)`,
|
|
102
137
|
`${calendarReviewQueueRecords.length} saved calendar event queue item(s)`,
|
|
138
|
+
`${calendarEffectReceiptRecords.length} saved calendar provider-effect receipt(s)`,
|
|
103
139
|
`${refreshableCalendarQueueRecords} refreshable saved calendar queue item(s)`,
|
|
104
140
|
`${scheduleMethods.length} schedule/reminder method(s) available for follow-up`,
|
|
105
141
|
],
|
|
@@ -107,8 +143,10 @@ export function buildLanes(
|
|
|
107
143
|
connectorSignals: calendarConnectors,
|
|
108
144
|
workflows: calendarWorkflows(calendarMethods, calendarConnectors),
|
|
109
145
|
liveRecords: [
|
|
146
|
+
...calendarProviderRecords,
|
|
110
147
|
...calendarReviewQueueRecords,
|
|
111
148
|
...calendarArtifactRecords,
|
|
149
|
+
...calendarEffectReceiptRecords,
|
|
112
150
|
...connectorRecords(calendarConnectors, 'Calendar', 'calendar'),
|
|
113
151
|
],
|
|
114
152
|
},
|
|
@@ -132,36 +170,64 @@ export function buildLanes(
|
|
|
132
170
|
{
|
|
133
171
|
id: 'tasks',
|
|
134
172
|
label: 'Tasks',
|
|
135
|
-
status: taskMethods.length > 0 ? 'ready' : 'partial',
|
|
173
|
+
status: taskProviderRecords.length > 0 || taskMethods.length > 0 || taskEffectReceiptRecords.length > 0 ? 'ready' : 'partial',
|
|
136
174
|
outcome: 'Track user-visible work items, inspect host task state, and update work plan status without hidden jobs.',
|
|
137
|
-
current:
|
|
138
|
-
|
|
175
|
+
current: taskProviderRecords.length > 0
|
|
176
|
+
? `Fresh provider-backed task records are published by the connected daemon or SDK read model; Agent can review ${taskProviderRecords.length} current external task record(s) before any provider update, completion, defer, or delete route.`
|
|
177
|
+
: taskEffectReceiptRecords.length > 0
|
|
178
|
+
? 'Saved task provider-effect receipts are available for audit and follow-up; no fresh task provider record is currently ready.'
|
|
179
|
+
: `Agent has work-plan actions and ${taskMethods.length} task/work-plan daemon method(s) in the SDK contract.`,
|
|
180
|
+
next: taskProviderRecords.length > 0
|
|
181
|
+
? 'Inspect one provider task record, then use only a published confirmed follow-up route for updates, completion, defer, archive, or delete.'
|
|
182
|
+
: taskEffectReceiptRecords.length > 0
|
|
183
|
+
? 'Review saved task provider-effect receipts, then repair or refresh the task provider before another provider update.'
|
|
184
|
+
: 'Use work plans for user-visible task tracking; inspect runtime host tasks separately before mutating anything.',
|
|
139
185
|
userRoute: 'Agent Workspace -> Personal Ops -> Add work item',
|
|
140
186
|
modelRoute: 'agent_work_plan action:"create"',
|
|
141
187
|
signals: [
|
|
188
|
+
`${taskProviderRecords.length} fresh provider-backed task record(s)`,
|
|
189
|
+
`${taskEffectReceiptRecords.length} saved task provider-effect receipt(s)`,
|
|
142
190
|
`${taskMethods.length} task/work-plan daemon method(s)`,
|
|
143
191
|
'Work plan add/show/status/delete actions are available',
|
|
144
192
|
],
|
|
145
193
|
methodIds: taskMethods,
|
|
146
194
|
workflows: taskWorkflows(taskMethods),
|
|
147
|
-
liveRecords:
|
|
195
|
+
liveRecords: [
|
|
196
|
+
...taskProviderRecords,
|
|
197
|
+
...taskEffectReceiptRecords,
|
|
198
|
+
...taskOperationRecords(taskMethods),
|
|
199
|
+
],
|
|
148
200
|
},
|
|
149
201
|
{
|
|
150
202
|
id: 'reminders',
|
|
151
203
|
label: 'Reminders',
|
|
152
|
-
status: scheduleMethods.length > 0 ? 'ready' : 'partial',
|
|
204
|
+
status: reminderProviderRecords.length > 0 || scheduleMethods.length > 0 || reminderEffectReceiptRecords.length > 0 ? 'ready' : 'partial',
|
|
153
205
|
outcome: 'Turn a user request into a visible reminder or autonomous schedule with delivery target and cancellation path.',
|
|
154
|
-
current:
|
|
155
|
-
|
|
206
|
+
current: reminderProviderRecords.length > 0
|
|
207
|
+
? `Fresh provider-backed reminder records are published by the connected daemon or SDK read model; Agent can review ${reminderProviderRecords.length} current reminder record(s) before any edit, snooze, complete, or delete route.`
|
|
208
|
+
: reminderEffectReceiptRecords.length > 0
|
|
209
|
+
? 'Saved reminder provider-effect receipts are available for audit and follow-up; no fresh reminder provider record is currently ready.'
|
|
210
|
+
: `Reminder and autonomous schedule creation are available through Agent tools; ${scheduleMethods.length} schedule/reminder daemon method(s) are discoverable.`,
|
|
211
|
+
next: reminderProviderRecords.length > 0
|
|
212
|
+
? 'Inspect one provider reminder record, then use only a published confirmed follow-up route for edits, snooze, completion, cancellation, or delete.'
|
|
213
|
+
: reminderEffectReceiptRecords.length > 0
|
|
214
|
+
? 'Review saved reminder provider-effect receipts, then repair or refresh the reminder provider before another provider update.'
|
|
215
|
+
: 'Create one confirmed reminder or autonomous schedule with title, time, scope, delivery target, success criteria, and explicit user request.',
|
|
156
216
|
userRoute: 'Agent Workspace -> Personal Ops -> Create reminder',
|
|
157
217
|
modelRoute: 'schedule action:"remind|create"',
|
|
158
218
|
signals: [
|
|
219
|
+
`${reminderProviderRecords.length} fresh provider-backed reminder record(s)`,
|
|
220
|
+
`${reminderEffectReceiptRecords.length} saved reminder provider-effect receipt(s)`,
|
|
159
221
|
`${scheduleMethods.length} schedule/reminder daemon method(s)`,
|
|
160
222
|
`${configuredTargets} configured delivery target(s)`,
|
|
161
223
|
],
|
|
162
224
|
methodIds: scheduleMethods,
|
|
163
225
|
workflows: reminderWorkflows(scheduleMethods, configuredTargets > 0 || readyChannels > 0),
|
|
164
|
-
liveRecords:
|
|
226
|
+
liveRecords: [
|
|
227
|
+
...reminderProviderRecords,
|
|
228
|
+
...reminderEffectReceiptRecords,
|
|
229
|
+
...reminderOperationRecords(scheduleMethods, configuredTargets > 0 || readyChannels > 0),
|
|
230
|
+
],
|
|
165
231
|
},
|
|
166
232
|
{
|
|
167
233
|
id: 'routines',
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import { hasMethod } from './agent-harness-personal-ops-discovery.ts';
|
|
2
|
+
import type { PersonalOpsLiveRecord } from './agent-harness-personal-ops-types.ts';
|
|
3
|
+
|
|
4
|
+
export function taskOperationRecords(methodIds: readonly string[]): readonly PersonalOpsLiveRecord[] {
|
|
5
|
+
const records: PersonalOpsLiveRecord[] = [
|
|
6
|
+
{
|
|
7
|
+
id: 'workplan-list',
|
|
8
|
+
label: 'Review visible work plan',
|
|
9
|
+
status: 'ready',
|
|
10
|
+
summary: 'Read Agent-owned work-plan items before starting or switching multi-step work.',
|
|
11
|
+
userRoute: 'Agent Workspace -> Work -> Review work plan',
|
|
12
|
+
modelRoute: 'agent_work_plan action:"list"',
|
|
13
|
+
tags: ['work-plan', 'task-read'],
|
|
14
|
+
effect: 'read-only',
|
|
15
|
+
capability: 'task-read',
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
id: 'workplan-add',
|
|
19
|
+
label: 'Add visible work item',
|
|
20
|
+
status: 'ready',
|
|
21
|
+
summary: 'Create one local Agent work-plan item instead of hiding task state in chat.',
|
|
22
|
+
userRoute: 'Agent Workspace -> Personal Ops -> Add work item',
|
|
23
|
+
modelRoute: 'agent_work_plan action:"create" title:"..."',
|
|
24
|
+
tags: ['work-plan', 'task-write'],
|
|
25
|
+
effect: 'confirmed-effect',
|
|
26
|
+
capability: 'task-write',
|
|
27
|
+
requiredFields: ['title'],
|
|
28
|
+
optionalFields: ['detail', 'priority', 'status'],
|
|
29
|
+
confirmationRequired: false,
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
id: 'workplan-status',
|
|
33
|
+
label: 'Update work item status',
|
|
34
|
+
status: 'ready',
|
|
35
|
+
summary: 'Move one visible work item through pending, active, blocked, done, failed, or cancelled state.',
|
|
36
|
+
userRoute: 'Agent Workspace -> Work -> Update work item status',
|
|
37
|
+
modelRoute: 'agent_work_plan action:"set_status" id:"..." status:"..."',
|
|
38
|
+
tags: ['work-plan', 'task-write'],
|
|
39
|
+
effect: 'confirmed-effect',
|
|
40
|
+
capability: 'task-write',
|
|
41
|
+
requiredFields: ['id', 'status'],
|
|
42
|
+
confirmationRequired: false,
|
|
43
|
+
},
|
|
44
|
+
];
|
|
45
|
+
if (hasMethod(methodIds, 'tasks.list')) {
|
|
46
|
+
records.push({
|
|
47
|
+
id: 'host-tasks-list',
|
|
48
|
+
label: 'List connected-host tasks',
|
|
49
|
+
status: 'ready',
|
|
50
|
+
summary: 'Inspect connected-host task state without creating, retrying, or mutating host tasks.',
|
|
51
|
+
userRoute: 'Agent Workspace -> Work -> Host tasks',
|
|
52
|
+
modelRoute: 'workspace action:"action" actionId:"tasks-list"',
|
|
53
|
+
tags: ['host-task', 'task-read'],
|
|
54
|
+
effect: 'read-only',
|
|
55
|
+
capability: 'host-task-read',
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
if (hasMethod(methodIds, 'tasks.get') || hasMethod(methodIds, 'tasks.status')) {
|
|
59
|
+
records.push({
|
|
60
|
+
id: 'host-task-inspect',
|
|
61
|
+
label: 'Inspect connected-host task',
|
|
62
|
+
status: 'ready',
|
|
63
|
+
summary: 'Inspect one exact connected-host task id and output before considering controls.',
|
|
64
|
+
userRoute: 'Agent Workspace -> Work -> Inspect host task',
|
|
65
|
+
modelRoute: 'workspace action:"action" actionId:"task-show"',
|
|
66
|
+
tags: ['host-task', 'task-read'],
|
|
67
|
+
effect: 'read-only',
|
|
68
|
+
capability: 'host-task-read',
|
|
69
|
+
requiredFields: ['taskId'],
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
if (hasMethod(methodIds, 'tasks.cancel')) {
|
|
73
|
+
records.push({
|
|
74
|
+
id: 'host-task-cancel',
|
|
75
|
+
label: 'Cancel connected-host task',
|
|
76
|
+
status: 'ready',
|
|
77
|
+
summary: 'Cancel one exact connected-host task id only when the user authorizes it.',
|
|
78
|
+
userRoute: 'Agent Workspace -> Work -> Host task controls',
|
|
79
|
+
modelRoute: 'agent_operator_method methodId:"tasks.cancel" input:{"taskId":"..."} confirm:true explicitUserRequest:"..."',
|
|
80
|
+
tags: ['host-task', 'task-write'],
|
|
81
|
+
effect: 'confirmed-effect',
|
|
82
|
+
capability: 'host-task-control',
|
|
83
|
+
requiredFields: ['taskId'],
|
|
84
|
+
confirmationRequired: true,
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
if (hasMethod(methodIds, 'tasks.retry')) {
|
|
88
|
+
records.push({
|
|
89
|
+
id: 'host-task-retry',
|
|
90
|
+
label: 'Retry connected-host task',
|
|
91
|
+
status: 'ready',
|
|
92
|
+
summary: 'Retry one failed or cancelled connected-host task id only after inspection.',
|
|
93
|
+
userRoute: 'Agent Workspace -> Work -> Host task controls',
|
|
94
|
+
modelRoute: 'agent_operator_method methodId:"tasks.retry" input:{"taskId":"..."} confirm:true explicitUserRequest:"..."',
|
|
95
|
+
tags: ['host-task', 'task-write'],
|
|
96
|
+
effect: 'confirmed-effect',
|
|
97
|
+
capability: 'host-task-control',
|
|
98
|
+
requiredFields: ['taskId'],
|
|
99
|
+
confirmationRequired: true,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
return records;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function reminderOperationRecords(methodIds: readonly string[], deliveryConfigured: boolean): readonly PersonalOpsLiveRecord[] {
|
|
106
|
+
const records: PersonalOpsLiveRecord[] = [
|
|
107
|
+
{
|
|
108
|
+
id: 'reminder-create',
|
|
109
|
+
label: 'Create confirmed reminder',
|
|
110
|
+
status: hasMethod(methodIds, 'schedules.create') ? deliveryConfigured ? 'ready' : 'attention' : 'needs-setup',
|
|
111
|
+
summary: deliveryConfigured
|
|
112
|
+
? 'Create one connected reminder schedule with real timing and a visible delivery path.'
|
|
113
|
+
: 'Create one reminder only after confirming timing and delivery scope; no configured delivery target was detected.',
|
|
114
|
+
userRoute: 'Agent Workspace -> Personal Ops -> Create reminder',
|
|
115
|
+
modelRoute: 'schedule action:"remind" message:"..." scheduleKind:"..." scheduleValue:"..." confirm:true explicitUserRequest:"..."',
|
|
116
|
+
tags: ['reminder', 'schedule-write'],
|
|
117
|
+
effect: 'confirmed-effect',
|
|
118
|
+
capability: 'reminder-create',
|
|
119
|
+
requiredFields: ['title', 'scheduleKind', 'scheduleValue'],
|
|
120
|
+
optionalFields: ['deliveryTargetId', 'timezone', 'message'],
|
|
121
|
+
confirmationRequired: true,
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
id: 'autonomous-schedule-create',
|
|
125
|
+
label: 'Create autonomous schedule',
|
|
126
|
+
status: hasMethod(methodIds, 'schedules.create') ? 'ready' : 'needs-setup',
|
|
127
|
+
summary: 'Create one visible autonomous schedule only when task, cadence, success criteria, and user request provenance are explicit.',
|
|
128
|
+
userRoute: 'Agent Workspace -> Automation -> Create schedule',
|
|
129
|
+
modelRoute: 'schedule action:"create" task:"..." successCriteria:"..." scheduleKind:"..." scheduleValue:"..." confirm:true explicitUserRequest:"..."',
|
|
130
|
+
tags: ['autonomy', 'schedule-write'],
|
|
131
|
+
effect: 'confirmed-effect',
|
|
132
|
+
capability: 'schedule-create',
|
|
133
|
+
requiredFields: ['task', 'successCriteria', 'scheduleKind', 'scheduleValue'],
|
|
134
|
+
confirmationRequired: true,
|
|
135
|
+
},
|
|
136
|
+
];
|
|
137
|
+
if (hasMethod(methodIds, 'schedules.list')) {
|
|
138
|
+
records.push({
|
|
139
|
+
id: 'schedule-list',
|
|
140
|
+
label: 'List connected schedules',
|
|
141
|
+
status: 'ready',
|
|
142
|
+
summary: 'Inspect configured schedules and history before running or mutating one.',
|
|
143
|
+
userRoute: 'Agent Workspace -> Automation -> Schedules',
|
|
144
|
+
modelRoute: 'workspace action:"action" actionId:"schedule-list"',
|
|
145
|
+
tags: ['schedule', 'schedule-read'],
|
|
146
|
+
effect: 'read-only',
|
|
147
|
+
capability: 'schedule-read',
|
|
148
|
+
});
|
|
149
|
+
records.push({
|
|
150
|
+
id: 'schedule-edit',
|
|
151
|
+
label: 'Edit connected schedule',
|
|
152
|
+
status: 'ready',
|
|
153
|
+
summary: 'Preview and edit one exact connected schedule id with before/after diff context.',
|
|
154
|
+
userRoute: 'Agent Workspace -> Automation -> Edit schedule',
|
|
155
|
+
modelRoute: 'schedule action:"edit" scheduleId:"..." confirm:true explicitUserRequest:"..."',
|
|
156
|
+
tags: ['schedule', 'schedule-write'],
|
|
157
|
+
effect: 'confirmed-effect',
|
|
158
|
+
capability: 'schedule-control',
|
|
159
|
+
requiredFields: ['scheduleId'],
|
|
160
|
+
optionalFields: ['name', 'scheduleKind', 'scheduleValue', 'prompt'],
|
|
161
|
+
confirmationRequired: true,
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
if (hasMethod(methodIds, 'schedules.run')) {
|
|
165
|
+
records.push({
|
|
166
|
+
id: 'schedule-run-now',
|
|
167
|
+
label: 'Run schedule now',
|
|
168
|
+
status: 'ready',
|
|
169
|
+
summary: 'Run one exact connected schedule id now after the user confirms.',
|
|
170
|
+
userRoute: 'Agent Workspace -> Automation -> Run job now',
|
|
171
|
+
modelRoute: 'schedule action:"run" scheduleId:"..." confirm:true explicitUserRequest:"..."',
|
|
172
|
+
tags: ['schedule', 'schedule-write'],
|
|
173
|
+
effect: 'confirmed-effect',
|
|
174
|
+
capability: 'schedule-control',
|
|
175
|
+
requiredFields: ['scheduleId'],
|
|
176
|
+
confirmationRequired: true,
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
if (hasMethod(methodIds, 'schedules.disable')) {
|
|
180
|
+
records.push({
|
|
181
|
+
id: 'schedule-pause',
|
|
182
|
+
label: 'Pause connected schedule',
|
|
183
|
+
status: 'ready',
|
|
184
|
+
summary: 'Disable one exact connected schedule id after reviewing current state.',
|
|
185
|
+
userRoute: 'Agent Workspace -> Automation -> Schedule controls',
|
|
186
|
+
modelRoute: 'schedule action:"pause" scheduleId:"..." confirm:true explicitUserRequest:"..."',
|
|
187
|
+
tags: ['schedule', 'schedule-write'],
|
|
188
|
+
effect: 'confirmed-effect',
|
|
189
|
+
capability: 'schedule-control',
|
|
190
|
+
requiredFields: ['scheduleId'],
|
|
191
|
+
confirmationRequired: true,
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
if (hasMethod(methodIds, 'schedules.enable')) {
|
|
195
|
+
records.push({
|
|
196
|
+
id: 'schedule-resume',
|
|
197
|
+
label: 'Resume connected schedule',
|
|
198
|
+
status: 'ready',
|
|
199
|
+
summary: 'Enable one exact connected schedule id after reviewing current state.',
|
|
200
|
+
userRoute: 'Agent Workspace -> Automation -> Schedule controls',
|
|
201
|
+
modelRoute: 'schedule action:"resume" scheduleId:"..." confirm:true explicitUserRequest:"..."',
|
|
202
|
+
tags: ['schedule', 'schedule-write'],
|
|
203
|
+
effect: 'confirmed-effect',
|
|
204
|
+
capability: 'schedule-control',
|
|
205
|
+
requiredFields: ['scheduleId'],
|
|
206
|
+
confirmationRequired: true,
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
if (hasMethod(methodIds, 'schedules.delete')) {
|
|
210
|
+
records.push({
|
|
211
|
+
id: 'schedule-delete',
|
|
212
|
+
label: 'Delete connected schedule',
|
|
213
|
+
status: 'ready',
|
|
214
|
+
summary: 'Delete one exact connected schedule id only after explicit user confirmation.',
|
|
215
|
+
userRoute: 'Agent Workspace -> Automation -> Schedule controls',
|
|
216
|
+
modelRoute: 'schedule action:"delete" scheduleId:"..." confirm:true explicitUserRequest:"..."',
|
|
217
|
+
tags: ['schedule', 'schedule-write'],
|
|
218
|
+
effect: 'confirmed-effect',
|
|
219
|
+
capability: 'schedule-control',
|
|
220
|
+
requiredFields: ['scheduleId'],
|
|
221
|
+
confirmationRequired: true,
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
return records;
|
|
225
|
+
}
|