@vellumai/assistant 0.4.2 → 0.4.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +3 -0
- package/ARCHITECTURE.md +124 -10
- package/README.md +43 -35
- package/docs/trusted-contact-access.md +20 -0
- package/package.json +1 -1
- package/scripts/ipc/generate-swift.ts +1 -0
- package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +58 -120
- package/src/__tests__/access-request-decision.test.ts +0 -1
- package/src/__tests__/actor-token-service.test.ts +1099 -0
- package/src/__tests__/agent-loop.test.ts +51 -0
- package/src/__tests__/approval-routes-http.test.ts +2 -0
- package/src/__tests__/assistant-events-sse-hardening.test.ts +7 -5
- package/src/__tests__/assistant-id-boundary-guard.test.ts +415 -0
- package/src/__tests__/call-controller.test.ts +49 -0
- package/src/__tests__/call-pointer-message-composer.test.ts +171 -0
- package/src/__tests__/call-pointer-messages.test.ts +93 -3
- package/src/__tests__/call-pointer-no-hardcoded-copy.guard.test.ts +42 -0
- package/src/__tests__/call-routes-http.test.ts +0 -25
- package/src/__tests__/callback-handoff-copy.test.ts +186 -0
- package/src/__tests__/channel-approval-routes.test.ts +133 -12
- package/src/__tests__/channel-guardian.test.ts +0 -86
- package/src/__tests__/channel-readiness-service.test.ts +10 -16
- package/src/__tests__/checker.test.ts +33 -12
- package/src/__tests__/config-schema.test.ts +6 -0
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +410 -0
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +256 -0
- package/src/__tests__/conversation-routes.test.ts +12 -3
- package/src/__tests__/credential-security-invariants.test.ts +1 -1
- package/src/__tests__/daemon-server-session-init.test.ts +4 -0
- package/src/__tests__/deterministic-verification-control-plane.test.ts +0 -1
- package/src/__tests__/guardian-actions-endpoint.test.ts +39 -13
- package/src/__tests__/guardian-dispatch.test.ts +8 -0
- package/src/__tests__/guardian-outbound-http.test.ts +4 -5
- package/src/__tests__/guardian-question-mode.test.ts +200 -0
- package/src/__tests__/guardian-routing-invariants.test.ts +178 -0
- package/src/__tests__/guardian-routing-state.test.ts +525 -0
- package/src/__tests__/handle-user-message-secret-resume.test.ts +2 -0
- package/src/__tests__/handlers-telegram-config.test.ts +0 -83
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +55 -0
- package/src/__tests__/headless-browser-navigate.test.ts +2 -0
- package/src/__tests__/inbound-invite-redemption.test.ts +0 -1
- package/src/__tests__/ingress-routes-http.test.ts +55 -0
- package/src/__tests__/ipc-snapshot.test.ts +18 -51
- package/src/__tests__/non-member-access-request.test.ts +159 -9
- package/src/__tests__/notification-decision-fallback.test.ts +129 -4
- package/src/__tests__/notification-decision-strategy.test.ts +106 -2
- package/src/__tests__/notification-guardian-path.test.ts +3 -0
- package/src/__tests__/recording-intent-handler.test.ts +1 -0
- package/src/__tests__/relay-server.test.ts +1475 -33
- package/src/__tests__/send-endpoint-busy.test.ts +5 -0
- package/src/__tests__/session-agent-loop.test.ts +1 -0
- package/src/__tests__/session-confirmation-signals.test.ts +523 -0
- package/src/__tests__/session-init.benchmark.test.ts +0 -2
- package/src/__tests__/session-runtime-assembly.test.ts +4 -1
- package/src/__tests__/session-surfaces-task-progress.test.ts +44 -1
- package/src/__tests__/session-tool-setup-app-refresh.test.ts +81 -2
- package/src/__tests__/session-tool-setup-memory-scope.test.ts +1 -1
- package/src/__tests__/session-tool-setup-side-effect-flag.test.ts +1 -1
- package/src/__tests__/tool-executor.test.ts +21 -2
- package/src/__tests__/tool-grant-request-escalation.test.ts +333 -27
- package/src/__tests__/trusted-contact-approval-notifier.test.ts +678 -0
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +1064 -0
- package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +11 -1
- package/src/__tests__/trusted-contact-multichannel.test.ts +0 -1
- package/src/__tests__/trusted-contact-verification.test.ts +0 -1
- package/src/__tests__/twilio-config.test.ts +2 -13
- package/src/__tests__/twilio-routes.test.ts +4 -3
- package/src/__tests__/update-bulletin.test.ts +0 -1
- package/src/agent/loop.ts +1 -1
- package/src/approvals/guardian-decision-primitive.ts +12 -3
- package/src/approvals/guardian-request-resolvers.ts +169 -11
- package/src/calls/call-constants.ts +29 -0
- package/src/calls/call-controller.ts +11 -3
- package/src/calls/call-domain.ts +33 -11
- package/src/calls/call-pointer-message-composer.ts +154 -0
- package/src/calls/call-pointer-messages.ts +106 -27
- package/src/calls/guardian-dispatch.ts +4 -2
- package/src/calls/relay-server.ts +921 -112
- package/src/calls/twilio-config.ts +4 -11
- package/src/calls/twilio-routes.ts +4 -6
- package/src/calls/types.ts +3 -1
- package/src/calls/voice-session-bridge.ts +4 -3
- package/src/cli/core-commands.ts +7 -4
- package/src/cli.ts +5 -4
- package/src/config/bundled-skills/agentmail/SKILL.md +4 -0
- package/src/config/bundled-skills/app-builder/SKILL.md +309 -10
- package/src/config/bundled-skills/app-builder/TOOLS.json +1 -1
- package/src/config/bundled-skills/email-setup/SKILL.md +1 -1
- package/src/config/bundled-skills/google-oauth-setup/SKILL.md +105 -81
- package/src/config/bundled-skills/messaging/SKILL.md +61 -12
- package/src/config/bundled-skills/messaging/TOOLS.json +58 -0
- package/src/config/bundled-skills/messaging/tools/gmail-sender-digest.ts +6 -1
- package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +35 -0
- package/src/config/bundled-skills/messaging/tools/messaging-sender-digest.ts +52 -0
- package/src/config/bundled-skills/phone-calls/SKILL.md +30 -39
- package/src/config/bundled-skills/twitter/SKILL.md +3 -3
- package/src/config/bundled-skills/vercel-token-setup/SKILL.md +215 -0
- package/src/config/calls-schema.ts +36 -0
- package/src/config/env.ts +22 -0
- package/src/config/feature-flag-registry.json +8 -8
- package/src/config/schema.ts +2 -2
- package/src/config/skills.ts +11 -0
- package/src/config/system-prompt.ts +11 -1
- package/src/config/templates/SOUL.md +2 -0
- package/src/config/vellum-skills/sms-setup/SKILL.md +71 -82
- package/src/config/vellum-skills/trusted-contacts/SKILL.md +8 -1
- package/src/config/vellum-skills/twilio-setup/SKILL.md +88 -73
- package/src/daemon/call-pointer-generators.ts +59 -0
- package/src/daemon/computer-use-session.ts +2 -5
- package/src/daemon/handlers/apps.ts +76 -20
- package/src/daemon/handlers/config-channels.ts +9 -61
- package/src/daemon/handlers/config-inbox.ts +11 -3
- package/src/daemon/handlers/config-ingress.ts +28 -3
- package/src/daemon/handlers/config-telegram.ts +12 -0
- package/src/daemon/handlers/config.ts +2 -6
- package/src/daemon/handlers/index.ts +2 -1
- package/src/daemon/handlers/pairing.ts +2 -0
- package/src/daemon/handlers/publish.ts +11 -46
- package/src/daemon/handlers/sessions.ts +59 -5
- package/src/daemon/handlers/shared.ts +17 -2
- package/src/daemon/ipc-contract/apps.ts +1 -0
- package/src/daemon/ipc-contract/inbox.ts +4 -0
- package/src/daemon/ipc-contract/integrations.ts +1 -97
- package/src/daemon/ipc-contract/messages.ts +47 -1
- package/src/daemon/ipc-contract/notifications.ts +11 -0
- package/src/daemon/ipc-contract-inventory.json +2 -4
- package/src/daemon/lifecycle.ts +17 -0
- package/src/daemon/server.ts +16 -2
- package/src/daemon/session-agent-loop-handlers.ts +20 -0
- package/src/daemon/session-agent-loop.ts +24 -12
- package/src/daemon/session-lifecycle.ts +1 -1
- package/src/daemon/session-process.ts +11 -1
- package/src/daemon/session-runtime-assembly.ts +6 -1
- package/src/daemon/session-surfaces.ts +32 -3
- package/src/daemon/session.ts +88 -1
- package/src/daemon/tool-side-effects.ts +22 -0
- package/src/home-base/prebuilt/brain-graph.html +1483 -0
- package/src/home-base/prebuilt/index.html +40 -0
- package/src/inbound/platform-callback-registration.ts +157 -0
- package/src/memory/canonical-guardian-store.ts +1 -1
- package/src/memory/conversation-crud.ts +2 -1
- package/src/memory/conversation-title-service.ts +16 -2
- package/src/memory/db-init.ts +8 -0
- package/src/memory/delivery-crud.ts +2 -1
- package/src/memory/guardian-action-store.ts +2 -1
- package/src/memory/guardian-approvals.ts +3 -2
- package/src/memory/ingress-invite-store.ts +12 -2
- package/src/memory/ingress-member-store.ts +4 -3
- package/src/memory/migrations/038-actor-token-records.ts +39 -0
- package/src/memory/migrations/124-voice-invite-display-metadata.ts +14 -0
- package/src/memory/migrations/index.ts +2 -0
- package/src/memory/schema.ts +26 -5
- package/src/messaging/provider-types.ts +24 -0
- package/src/messaging/provider.ts +7 -0
- package/src/messaging/providers/gmail/adapter.ts +127 -0
- package/src/messaging/providers/sms/adapter.ts +40 -37
- package/src/notifications/adapters/macos.ts +45 -2
- package/src/notifications/broadcaster.ts +16 -0
- package/src/notifications/copy-composer.ts +50 -2
- package/src/notifications/decision-engine.ts +22 -9
- package/src/notifications/destination-resolver.ts +16 -2
- package/src/notifications/emit-signal.ts +18 -9
- package/src/notifications/guardian-question-mode.ts +419 -0
- package/src/notifications/signal.ts +14 -3
- package/src/permissions/checker.ts +13 -1
- package/src/permissions/prompter.ts +14 -0
- package/src/providers/anthropic/client.ts +20 -0
- package/src/providers/provider-send-message.ts +15 -3
- package/src/runtime/access-request-helper.ts +82 -4
- package/src/runtime/actor-token-service.ts +234 -0
- package/src/runtime/actor-token-store.ts +236 -0
- package/src/runtime/actor-trust-resolver.ts +2 -2
- package/src/runtime/assistant-scope.ts +10 -0
- package/src/runtime/channel-approvals.ts +5 -3
- package/src/runtime/channel-readiness-service.ts +23 -64
- package/src/runtime/channel-readiness-types.ts +3 -4
- package/src/runtime/channel-retry-sweep.ts +4 -1
- package/src/runtime/confirmation-request-guardian-bridge.ts +197 -0
- package/src/runtime/guardian-action-followup-executor.ts +1 -1
- package/src/runtime/guardian-context-resolver.ts +82 -0
- package/src/runtime/guardian-outbound-actions.ts +5 -7
- package/src/runtime/guardian-reply-router.ts +67 -30
- package/src/runtime/guardian-vellum-migration.ts +57 -0
- package/src/runtime/http-server.ts +75 -31
- package/src/runtime/http-types.ts +13 -0
- package/src/runtime/ingress-service.ts +14 -0
- package/src/runtime/invite-redemption-service.ts +10 -1
- package/src/runtime/local-actor-identity.ts +76 -0
- package/src/runtime/middleware/actor-token.ts +271 -0
- package/src/runtime/middleware/twilio-validation.ts +2 -4
- package/src/runtime/routes/approval-routes.ts +82 -7
- package/src/runtime/routes/brain-graph-routes.ts +222 -0
- package/src/runtime/routes/call-routes.ts +2 -1
- package/src/runtime/routes/channel-readiness-routes.ts +71 -0
- package/src/runtime/routes/channel-route-shared.ts +3 -3
- package/src/runtime/routes/conversation-attention-routes.ts +2 -1
- package/src/runtime/routes/conversation-routes.ts +142 -53
- package/src/runtime/routes/events-routes.ts +22 -8
- package/src/runtime/routes/guardian-action-routes.ts +45 -3
- package/src/runtime/routes/guardian-approval-interception.ts +29 -0
- package/src/runtime/routes/guardian-bootstrap-routes.ts +145 -0
- package/src/runtime/routes/inbound-conversation.ts +4 -3
- package/src/runtime/routes/inbound-message-handler.ts +147 -5
- package/src/runtime/routes/ingress-routes.ts +2 -0
- package/src/runtime/routes/integration-routes.ts +7 -15
- package/src/runtime/routes/pairing-routes.ts +163 -0
- package/src/runtime/routes/twilio-routes.ts +934 -0
- package/src/runtime/tool-grant-request-helper.ts +3 -1
- package/src/security/oauth2.ts +27 -2
- package/src/security/token-manager.ts +46 -10
- package/src/tools/browser/browser-execution.ts +4 -3
- package/src/tools/browser/browser-handoff.ts +10 -18
- package/src/tools/browser/browser-manager.ts +80 -25
- package/src/tools/browser/browser-screencast.ts +35 -119
- package/src/tools/calls/call-start.ts +2 -1
- package/src/tools/permission-checker.ts +15 -4
- package/src/tools/terminal/parser.ts +12 -0
- package/src/tools/tool-approval-handler.ts +244 -19
- package/src/workspace/git-service.ts +19 -0
- package/src/__tests__/handlers-twilio-config.test.ts +0 -1928
- package/src/daemon/handlers/config-twilio.ts +0 -1082
|
@@ -25,6 +25,32 @@ const log = getLogger('session-surfaces');
|
|
|
25
25
|
const MAX_UNDO_DEPTH = 10;
|
|
26
26
|
const TASK_PROGRESS_TEMPLATE_FIELDS = ['title', 'status', 'steps'] as const;
|
|
27
27
|
|
|
28
|
+
/**
|
|
29
|
+
* Migrate dynamic_page fields from the top-level tool input into `data`.
|
|
30
|
+
*
|
|
31
|
+
* The LLM sometimes sends `html`, `width`, `height`, or `preview` at the
|
|
32
|
+
* top level instead of nested inside `data`. Without this normalization the
|
|
33
|
+
* surface opens blank because `rawData` is `{}`.
|
|
34
|
+
*/
|
|
35
|
+
function normalizeDynamicPageShowData(input: Record<string, unknown>, rawData: Record<string, unknown>): DynamicPageSurfaceData {
|
|
36
|
+
const normalized: Record<string, unknown> = { ...rawData };
|
|
37
|
+
|
|
38
|
+
if (typeof normalized.html !== 'string' && typeof input.html === 'string') {
|
|
39
|
+
normalized.html = input.html;
|
|
40
|
+
}
|
|
41
|
+
if (normalized.width == null && input.width != null) {
|
|
42
|
+
normalized.width = input.width;
|
|
43
|
+
}
|
|
44
|
+
if (normalized.height == null && input.height != null) {
|
|
45
|
+
normalized.height = input.height;
|
|
46
|
+
}
|
|
47
|
+
if (!isPlainObject(normalized.preview) && isPlainObject(input.preview)) {
|
|
48
|
+
normalized.preview = input.preview;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return normalized as unknown as DynamicPageSurfaceData;
|
|
52
|
+
}
|
|
53
|
+
|
|
28
54
|
function normalizeCardShowData(input: Record<string, unknown>, rawData: Record<string, unknown>): CardSurfaceData {
|
|
29
55
|
const normalized: Record<string, unknown> = { ...rawData };
|
|
30
56
|
|
|
@@ -105,7 +131,7 @@ export interface SurfaceSessionContext {
|
|
|
105
131
|
sendToClient(msg: ServerMessage): void;
|
|
106
132
|
pendingSurfaceActions: Map<string, { surfaceType: SurfaceType }>;
|
|
107
133
|
lastSurfaceAction: Map<string, { actionId: string; data?: Record<string, unknown> }>;
|
|
108
|
-
surfaceState: Map<string, { surfaceType: SurfaceType; data: SurfaceData }>;
|
|
134
|
+
surfaceState: Map<string, { surfaceType: SurfaceType; data: SurfaceData; title?: string }>;
|
|
109
135
|
surfaceUndoStacks: Map<string, string[]>;
|
|
110
136
|
currentTurnSurfaces: Array<{
|
|
111
137
|
surfaceId: string;
|
|
@@ -592,7 +618,9 @@ export async function surfaceProxyResolver(
|
|
|
592
618
|
const rawData = isPlainObject(input.data) ? input.data : {};
|
|
593
619
|
const data = (surfaceType === 'card'
|
|
594
620
|
? normalizeCardShowData(input, rawData)
|
|
595
|
-
:
|
|
621
|
+
: surfaceType === 'dynamic_page'
|
|
622
|
+
? normalizeDynamicPageShowData(input, rawData)
|
|
623
|
+
: rawData) as SurfaceData;
|
|
596
624
|
const actions = input.actions as Array<{ id: string; label: string; style?: string }> | undefined;
|
|
597
625
|
// Interactive surfaces default to awaiting user action.
|
|
598
626
|
const hasActions = Array.isArray(actions) && actions.length > 0;
|
|
@@ -604,7 +632,7 @@ export async function surfaceProxyResolver(
|
|
|
604
632
|
const awaitAction = (input.await_action as boolean) ?? isInteractive;
|
|
605
633
|
|
|
606
634
|
// Track surface state for ui_update merging
|
|
607
|
-
ctx.surfaceState.set(surfaceId, { surfaceType, data });
|
|
635
|
+
ctx.surfaceState.set(surfaceId, { surfaceType, data, title });
|
|
608
636
|
|
|
609
637
|
const display = (input.display as string) === 'panel' ? 'panel' : 'inline';
|
|
610
638
|
|
|
@@ -754,6 +782,7 @@ export async function surfaceProxyResolver(
|
|
|
754
782
|
ctx.surfaceState.set(surfaceId, {
|
|
755
783
|
surfaceType: 'dynamic_page',
|
|
756
784
|
data: surfaceData,
|
|
785
|
+
title: app.name,
|
|
757
786
|
});
|
|
758
787
|
|
|
759
788
|
ctx.sendToClient({
|
package/src/daemon/session.ts
CHANGED
|
@@ -36,6 +36,7 @@ import type { Message } from '../providers/types.js';
|
|
|
36
36
|
import type { Provider } from '../providers/types.js';
|
|
37
37
|
import { ToolExecutor } from '../tools/executor.js';
|
|
38
38
|
import type { AssistantAttachmentDraft } from './assistant-attachments.js';
|
|
39
|
+
import type { AssistantActivityState, ConfirmationStateChanged } from './ipc-contract/messages.js';
|
|
39
40
|
import type { ServerMessage, SurfaceData,SurfaceType, UsageStats, UserMessageAttachment } from './ipc-protocol.js';
|
|
40
41
|
import {
|
|
41
42
|
classifyResponseTierAsync,
|
|
@@ -146,7 +147,7 @@ export class Session {
|
|
|
146
147
|
/** @internal */ commandIntent?: { type: string; payload?: string; languageCode?: string };
|
|
147
148
|
/** @internal */ pendingSurfaceActions = new Map<string, { surfaceType: SurfaceType }>();
|
|
148
149
|
/** @internal */ lastSurfaceAction = new Map<string, { actionId: string; data?: Record<string, unknown> }>();
|
|
149
|
-
/** @internal */ surfaceState = new Map<string, { surfaceType: SurfaceType; data: SurfaceData }>();
|
|
150
|
+
/** @internal */ surfaceState = new Map<string, { surfaceType: SurfaceType; data: SurfaceData; title?: string }>();
|
|
150
151
|
/** @internal */ surfaceUndoStacks = new Map<string, string[]>();
|
|
151
152
|
/** @internal */ withSurface = createSurfaceMutex();
|
|
152
153
|
/** @internal */ currentTurnSurfaces: Array<{ surfaceId: string; surfaceType: SurfaceType; title?: string; data: SurfaceData; actions?: Array<{ id: string; label: string; style?: string }>; display?: string }> = [];
|
|
@@ -161,6 +162,16 @@ export class Session {
|
|
|
161
162
|
public lastAttachmentWarnings: string[] = [];
|
|
162
163
|
/** @internal */ currentTurnChannelContext: TurnChannelContext | null = null;
|
|
163
164
|
/** @internal */ currentTurnInterfaceContext: TurnInterfaceContext | null = null;
|
|
165
|
+
/** @internal */ activityVersion = 0;
|
|
166
|
+
/**
|
|
167
|
+
* Optional callback invoked whenever a server-authoritative state signal
|
|
168
|
+
* (confirmation_state_changed or assistant_activity_state) is emitted.
|
|
169
|
+
*
|
|
170
|
+
* HTTP/SSE sessions set this so the hub publisher receives these events —
|
|
171
|
+
* without it, the signals only travel through `sendToClient`, which is a
|
|
172
|
+
* no-op for socketless sessions.
|
|
173
|
+
*/
|
|
174
|
+
private onStateSignal?: (msg: ServerMessage) => void;
|
|
164
175
|
|
|
165
176
|
constructor(
|
|
166
177
|
conversationId: string,
|
|
@@ -180,6 +191,22 @@ export class Session {
|
|
|
180
191
|
this.memoryPolicy = memoryPolicy ? { ...memoryPolicy } : { ...DEFAULT_MEMORY_POLICY };
|
|
181
192
|
this.traceEmitter = new TraceEmitter(conversationId, sendToClient);
|
|
182
193
|
this.prompter = new PermissionPrompter(sendToClient);
|
|
194
|
+
this.prompter.setOnStateChanged((requestId, state, source) => {
|
|
195
|
+
// Route through emitConfirmationStateChanged so the onStateSignal
|
|
196
|
+
// listener publishes to the SSE hub for HTTP/SSE consumers.
|
|
197
|
+
this.emitConfirmationStateChanged({
|
|
198
|
+
sessionId: this.conversationId,
|
|
199
|
+
requestId,
|
|
200
|
+
state,
|
|
201
|
+
source,
|
|
202
|
+
});
|
|
203
|
+
// Emit activity state transitions for confirmation lifecycle
|
|
204
|
+
if (state === 'pending') {
|
|
205
|
+
this.emitActivityState('awaiting_confirmation', 'confirmation_requested', 'assistant_turn');
|
|
206
|
+
} else if (state === 'timed_out') {
|
|
207
|
+
this.emitActivityState('thinking', 'confirmation_resolved', 'assistant_turn');
|
|
208
|
+
}
|
|
209
|
+
});
|
|
183
210
|
this.secretPrompter = new SecretPrompter(sendToClient);
|
|
184
211
|
|
|
185
212
|
// Register watch/call notifiers (reads ctx properties lazily)
|
|
@@ -356,6 +383,17 @@ export class Session {
|
|
|
356
383
|
return this.sendToClient;
|
|
357
384
|
}
|
|
358
385
|
|
|
386
|
+
/**
|
|
387
|
+
* Register a callback for server-authoritative state signals
|
|
388
|
+
* (confirmation_state_changed, assistant_activity_state).
|
|
389
|
+
*
|
|
390
|
+
* This enables HTTP/SSE sessions to receive these events through the
|
|
391
|
+
* hub publisher, since `sendToClient` is a no-op for socketless sessions.
|
|
392
|
+
*/
|
|
393
|
+
setStateSignalListener(listener: (msg: ServerMessage) => void): void {
|
|
394
|
+
this.onStateSignal = listener;
|
|
395
|
+
}
|
|
396
|
+
|
|
359
397
|
setSandboxOverride(enabled: boolean | undefined): void {
|
|
360
398
|
this.sandboxOverride = enabled;
|
|
361
399
|
}
|
|
@@ -453,6 +491,11 @@ export class Session {
|
|
|
453
491
|
selectedPattern?: string,
|
|
454
492
|
selectedScope?: string,
|
|
455
493
|
decisionContext?: string,
|
|
494
|
+
emissionContext?: {
|
|
495
|
+
source?: ConfirmationStateChanged['source'];
|
|
496
|
+
causedByRequestId?: string;
|
|
497
|
+
decisionText?: string;
|
|
498
|
+
},
|
|
456
499
|
): void {
|
|
457
500
|
this.prompter.resolveConfirmation(
|
|
458
501
|
requestId,
|
|
@@ -461,12 +504,56 @@ export class Session {
|
|
|
461
504
|
selectedScope,
|
|
462
505
|
decisionContext,
|
|
463
506
|
);
|
|
507
|
+
|
|
508
|
+
// Emit authoritative confirmation state and activity transition centrally
|
|
509
|
+
// so ALL callers (IPC handlers, /v1/confirm, channel bridges) get
|
|
510
|
+
// consistent events without duplicating emission logic.
|
|
511
|
+
const resolvedState = (decision === 'deny' || decision === 'always_deny')
|
|
512
|
+
? 'denied' as const
|
|
513
|
+
: 'approved' as const;
|
|
514
|
+
this.emitConfirmationStateChanged({
|
|
515
|
+
sessionId: this.conversationId,
|
|
516
|
+
requestId,
|
|
517
|
+
state: resolvedState,
|
|
518
|
+
source: emissionContext?.source ?? 'button',
|
|
519
|
+
...(emissionContext?.causedByRequestId ? { causedByRequestId: emissionContext.causedByRequestId } : {}),
|
|
520
|
+
...(emissionContext?.decisionText ? { decisionText: emissionContext.decisionText } : {}),
|
|
521
|
+
});
|
|
522
|
+
this.emitActivityState('thinking', 'confirmation_resolved', 'assistant_turn');
|
|
464
523
|
}
|
|
465
524
|
|
|
466
525
|
handleSecretResponse(requestId: string, value?: string, delivery?: 'store' | 'transient_send'): void {
|
|
467
526
|
this.secretPrompter.resolveSecret(requestId, value, delivery);
|
|
468
527
|
}
|
|
469
528
|
|
|
529
|
+
// ── Server-authoritative state signals ─────────────────────────────
|
|
530
|
+
|
|
531
|
+
emitConfirmationStateChanged(params: Omit<ConfirmationStateChanged, 'type'>): void {
|
|
532
|
+
const msg: ServerMessage = { type: 'confirmation_state_changed', ...params } as ServerMessage;
|
|
533
|
+
this.sendToClient(msg);
|
|
534
|
+
this.onStateSignal?.(msg);
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
emitActivityState(
|
|
538
|
+
phase: AssistantActivityState['phase'],
|
|
539
|
+
reason: AssistantActivityState['reason'],
|
|
540
|
+
anchor: AssistantActivityState['anchor'] = 'assistant_turn',
|
|
541
|
+
requestId?: string,
|
|
542
|
+
): void {
|
|
543
|
+
this.activityVersion++;
|
|
544
|
+
const msg: ServerMessage = {
|
|
545
|
+
type: 'assistant_activity_state',
|
|
546
|
+
sessionId: this.conversationId,
|
|
547
|
+
activityVersion: this.activityVersion,
|
|
548
|
+
phase,
|
|
549
|
+
anchor,
|
|
550
|
+
requestId,
|
|
551
|
+
reason,
|
|
552
|
+
} as ServerMessage;
|
|
553
|
+
this.sendToClient(msg);
|
|
554
|
+
this.onStateSignal?.(msg);
|
|
555
|
+
}
|
|
556
|
+
|
|
470
557
|
setChannelCapabilities(caps: ChannelCapabilities | null): void {
|
|
471
558
|
this.channelCapabilities = caps ?? undefined;
|
|
472
559
|
}
|
|
@@ -68,6 +68,19 @@ function registerHook(toolNames: string | string[], hook: PostExecutionHook): vo
|
|
|
68
68
|
}
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
+
// Broadcast app_files_changed when a new app is created so clients
|
|
72
|
+
// (e.g. macOS "Things" sidebar) refresh their app list immediately.
|
|
73
|
+
registerHook('app_create', (_name, _input, result, { ctx, broadcastToAllClients }) => {
|
|
74
|
+
try {
|
|
75
|
+
const parsed = JSON.parse(result.content) as { id?: string };
|
|
76
|
+
if (parsed.id) {
|
|
77
|
+
handleAppChange(ctx, parsed.id, broadcastToAllClients);
|
|
78
|
+
}
|
|
79
|
+
} catch {
|
|
80
|
+
// Result wasn't valid JSON — skip the broadcast.
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
|
|
71
84
|
// Auto-refresh workspace surfaces when a persisted app is updated.
|
|
72
85
|
// If no surface is currently showing the app, auto-open it.
|
|
73
86
|
registerHook('app_update', (_name, input, _result, { ctx, broadcastToAllClients }) => {
|
|
@@ -77,6 +90,15 @@ registerHook('app_update', (_name, input, _result, { ctx, broadcastToAllClients
|
|
|
77
90
|
}
|
|
78
91
|
});
|
|
79
92
|
|
|
93
|
+
// Broadcast app_files_changed when an app is deleted so clients remove it
|
|
94
|
+
// from their cached app lists.
|
|
95
|
+
registerHook('app_delete', (_name, input, _result, { broadcastToAllClients }) => {
|
|
96
|
+
const appId = input.app_id as string | undefined;
|
|
97
|
+
if (appId) {
|
|
98
|
+
broadcastToAllClients?.({ type: 'app_files_changed', appId });
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
|
|
80
102
|
// Broadcast tasks_changed so connected clients (e.g. macOS Tasks window)
|
|
81
103
|
// auto-refresh when the LLM mutates the task queue via tools
|
|
82
104
|
registerHook(
|