@vellumai/assistant 0.3.15 → 0.3.18
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/ARCHITECTURE.md +211 -12
- package/Dockerfile +1 -1
- package/README.md +11 -5
- package/docs/architecture/http-token-refresh.md +274 -0
- package/docs/architecture/memory.md +5 -4
- package/docs/architecture/scheduling.md +4 -88
- package/docs/runbook-trusted-contacts.md +283 -0
- package/docs/trusted-contact-access.md +247 -0
- package/package.json +1 -1
- package/scripts/ipc/check-swift-decoder-drift.ts +2 -0
- package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +2 -6
- package/src/__tests__/access-request-decision.test.ts +328 -0
- package/src/__tests__/asset-materialize-tool.test.ts +7 -7
- package/src/__tests__/asset-search-tool.test.ts +15 -15
- package/src/__tests__/attachments-store.test.ts +13 -13
- package/src/__tests__/call-controller.test.ts +150 -4
- package/src/__tests__/call-conversation-messages.test.ts +2 -2
- package/src/__tests__/call-pointer-messages.test.ts +28 -0
- package/src/__tests__/call-start-guardian-guard.test.ts +93 -0
- package/src/__tests__/channel-approval-routes.test.ts +108 -12
- package/src/__tests__/channel-guardian.test.ts +19 -15
- package/src/__tests__/checker.test.ts +103 -48
- package/src/__tests__/computer-use-skill-manifest-regression.test.ts +2 -2
- package/src/__tests__/config-watcher.test.ts +356 -0
- package/src/__tests__/conversation-pairing.test.ts +127 -27
- package/src/__tests__/conversation-store.test.ts +36 -36
- package/src/__tests__/date-context.test.ts +179 -1
- package/src/__tests__/db-migration-rollback.test.ts +4 -7
- package/src/__tests__/deterministic-verification-control-plane.test.ts +5 -5
- package/src/__tests__/emit-signal-routing-intent.test.ts +179 -0
- package/src/__tests__/gateway-only-guard.test.ts +188 -0
- package/src/__tests__/guardian-action-conversation-turn.test.ts +451 -0
- package/src/__tests__/guardian-action-copy-generator.test.ts +197 -0
- package/src/__tests__/guardian-action-followup-executor.test.ts +379 -0
- package/src/__tests__/guardian-action-followup-store.test.ts +376 -0
- package/src/__tests__/guardian-action-late-reply.test.ts +425 -0
- package/src/__tests__/guardian-action-no-hardcoded-copy.test.ts +71 -0
- package/src/__tests__/guardian-action-store.test.ts +182 -0
- package/src/__tests__/guardian-action-sweep.test.ts +9 -9
- package/src/__tests__/guardian-dispatch.test.ts +120 -0
- package/src/__tests__/guardian-outbound-http.test.ts +194 -2
- package/src/__tests__/guardian-verification-intent-routing.test.ts +179 -0
- package/src/__tests__/guardian-verify-setup-skill-regression.test.ts +141 -0
- package/src/__tests__/handlers-telegram-config.test.ts +6 -6
- package/src/__tests__/hooks-runner.test.ts +13 -4
- package/src/__tests__/ingress-routes-http.test.ts +443 -0
- package/src/__tests__/intent-routing.test.ts +14 -0
- package/src/__tests__/ipc-snapshot.test.ts +23 -5
- package/src/__tests__/media-reuse-story.e2e.test.ts +7 -7
- package/src/__tests__/memory-regressions.test.ts +16 -12
- package/src/__tests__/non-member-access-request.test.ts +281 -0
- package/src/__tests__/notification-broadcaster.test.ts +115 -4
- package/src/__tests__/notification-decision-strategy.test.ts +138 -1
- package/src/__tests__/notification-deep-link.test.ts +44 -1
- package/src/__tests__/notification-guardian-path.test.ts +157 -0
- package/src/__tests__/notification-routing-intent.test.ts +11 -1
- package/src/__tests__/notification-thread-candidate-validation.test.ts +215 -0
- package/src/__tests__/notification-thread-candidates.test.ts +166 -0
- package/src/__tests__/recording-intent.test.ts +1 -0
- package/src/__tests__/recording-state-machine.test.ts +328 -17
- package/src/__tests__/registry.test.ts +17 -8
- package/src/__tests__/relay-server.test.ts +105 -0
- package/src/__tests__/reminder.test.ts +13 -0
- package/src/__tests__/runtime-attachment-metadata.test.ts +4 -4
- package/src/__tests__/scheduler-recurrence.test.ts +50 -0
- package/src/__tests__/server-history-render.test.ts +8 -8
- package/src/__tests__/session-agent-loop.test.ts +1 -0
- package/src/__tests__/session-runtime-assembly.test.ts +49 -0
- package/src/__tests__/session-skill-tools.test.ts +1 -0
- package/src/__tests__/skill-projection.benchmark.test.ts +11 -3
- package/src/__tests__/slack-channel-config.test.ts +230 -0
- package/src/__tests__/subagent-manager-notify.test.ts +4 -4
- package/src/__tests__/swarm-session-integration.test.ts +2 -2
- package/src/__tests__/system-prompt.test.ts +43 -0
- package/src/__tests__/task-management-tools.test.ts +3 -3
- package/src/__tests__/task-tools.test.ts +3 -3
- package/src/__tests__/trust-store.test.ts +38 -22
- package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +489 -0
- package/src/__tests__/trusted-contact-multichannel.test.ts +405 -0
- package/src/__tests__/trusted-contact-verification.test.ts +360 -0
- package/src/__tests__/update-bulletin-format.test.ts +119 -0
- package/src/__tests__/update-bulletin-state.test.ts +129 -0
- package/src/__tests__/update-bulletin.test.ts +323 -0
- package/src/__tests__/update-template-contract.test.ts +24 -0
- package/src/__tests__/voice-session-bridge.test.ts +109 -9
- package/src/agent/loop.ts +2 -2
- package/src/amazon/client.ts +2 -3
- package/src/calls/call-controller.ts +241 -39
- package/src/calls/call-conversation-messages.ts +2 -2
- package/src/calls/call-domain.ts +10 -3
- package/src/calls/call-pointer-messages.ts +17 -5
- package/src/calls/guardian-action-sweep.ts +77 -36
- package/src/calls/guardian-dispatch.ts +8 -0
- package/src/calls/relay-server.ts +51 -12
- package/src/calls/twilio-routes.ts +3 -1
- package/src/calls/types.ts +1 -1
- package/src/calls/voice-session-bridge.ts +8 -6
- package/src/cli/core-commands.ts +43 -3
- package/src/cli/map.ts +8 -5
- package/src/config/bundled-skills/phone-calls/SKILL.md +16 -1
- package/src/config/bundled-skills/tasks/SKILL.md +1 -1
- package/src/config/bundled-skills/tasks/TOOLS.json +4 -4
- package/src/config/bundled-skills/time-based-actions/SKILL.md +11 -1
- package/src/config/computer-use-prompt.ts +1 -0
- package/src/config/core-schema.ts +16 -0
- package/src/config/env-registry.ts +1 -0
- package/src/config/env.ts +16 -1
- package/src/config/memory-schema.ts +5 -0
- package/src/config/schema.ts +4 -0
- package/src/config/system-prompt.ts +69 -2
- package/src/config/templates/BOOTSTRAP.md +1 -1
- package/src/config/templates/IDENTITY.md +8 -4
- package/src/config/templates/SOUL.md +14 -0
- package/src/config/templates/UPDATES.md +15 -0
- package/src/config/templates/USER.md +5 -1
- package/src/config/types.ts +1 -0
- package/src/config/update-bulletin-format.ts +54 -0
- package/src/config/update-bulletin-state.ts +49 -0
- package/src/config/update-bulletin-template-path.ts +6 -0
- package/src/config/update-bulletin.ts +97 -0
- package/src/config/vellum-skills/catalog.json +6 -0
- package/src/config/vellum-skills/chatgpt-import/tools/chatgpt-import.ts +1 -1
- package/src/config/vellum-skills/guardian-verify-setup/SKILL.md +44 -10
- package/src/config/vellum-skills/telegram-setup/SKILL.md +4 -4
- package/src/config/vellum-skills/trusted-contacts/SKILL.md +147 -0
- package/src/config/vellum-skills/twilio-setup/SKILL.md +2 -2
- package/src/context/window-manager.ts +43 -3
- package/src/daemon/config-watcher.ts +4 -2
- package/src/daemon/connection-policy.ts +21 -1
- package/src/daemon/daemon-control.ts +219 -8
- package/src/daemon/date-context.ts +174 -1
- package/src/daemon/guardian-action-generators.ts +175 -0
- package/src/daemon/guardian-verification-intent.ts +120 -0
- package/src/daemon/handlers/apps.ts +1 -3
- package/src/daemon/handlers/config-channels.ts +2 -2
- package/src/daemon/handlers/config-heartbeat.ts +1 -1
- package/src/daemon/handlers/config-inbox.ts +55 -159
- package/src/daemon/handlers/config-ingress.ts +1 -1
- package/src/daemon/handlers/config-integrations.ts +1 -1
- package/src/daemon/handlers/config-platform.ts +1 -1
- package/src/daemon/handlers/config-scheduling.ts +2 -2
- package/src/daemon/handlers/config-slack-channel.ts +190 -0
- package/src/daemon/handlers/config-telegram.ts +1 -1
- package/src/daemon/handlers/config-twilio.ts +1 -1
- package/src/daemon/handlers/config-voice.ts +100 -0
- package/src/daemon/handlers/config.ts +3 -0
- package/src/daemon/handlers/identity.ts +45 -25
- package/src/daemon/handlers/misc.ts +83 -5
- package/src/daemon/handlers/navigate-settings.ts +27 -0
- package/src/daemon/handlers/recording.ts +270 -144
- package/src/daemon/handlers/sessions.ts +100 -17
- package/src/daemon/handlers/subagents.ts +3 -3
- package/src/daemon/handlers/work-items.ts +10 -7
- package/src/daemon/ipc-contract/integrations.ts +9 -1
- package/src/daemon/ipc-contract/messages.ts +4 -0
- package/src/daemon/ipc-contract/sessions.ts +1 -1
- package/src/daemon/ipc-contract/settings.ts +26 -0
- package/src/daemon/ipc-contract/shared.ts +2 -0
- package/src/daemon/ipc-contract/work-items.ts +1 -7
- package/src/daemon/ipc-contract/workspace.ts +12 -1
- package/src/daemon/ipc-contract-inventory.json +6 -1
- package/src/daemon/ipc-contract.ts +5 -1
- package/src/daemon/lifecycle.ts +314 -266
- package/src/daemon/recording-intent.ts +0 -41
- package/src/daemon/response-tier.ts +2 -2
- package/src/daemon/server.ts +31 -9
- package/src/daemon/session-agent-loop-handlers.ts +34 -9
- package/src/daemon/session-agent-loop.ts +15 -8
- package/src/daemon/session-history.ts +3 -2
- package/src/daemon/session-media-retry.ts +3 -0
- package/src/daemon/session-messaging.ts +38 -4
- package/src/daemon/session-notifiers.ts +2 -2
- package/src/daemon/session-process.ts +546 -59
- package/src/daemon/session-queue-manager.ts +2 -0
- package/src/daemon/session-runtime-assembly.ts +39 -0
- package/src/daemon/session-skill-tools.ts +13 -4
- package/src/daemon/session-tool-setup.ts +5 -6
- package/src/daemon/session.ts +19 -8
- package/src/daemon/tls-certs.ts +60 -13
- package/src/daemon/tool-side-effects.ts +13 -5
- package/src/gallery/default-gallery.ts +32 -9
- package/src/influencer/client.ts +2 -1
- package/src/memory/channel-delivery-store.ts +35 -567
- package/src/memory/channel-guardian-store.ts +63 -1317
- package/src/memory/conflict-store.ts +4 -4
- package/src/memory/conversation-attention-store.ts +0 -3
- package/src/memory/conversation-crud.ts +668 -0
- package/src/memory/conversation-queries.ts +361 -0
- package/src/memory/conversation-store.ts +44 -983
- package/src/memory/db-connection.ts +3 -0
- package/src/memory/db-init.ts +33 -0
- package/src/memory/delivery-channels.ts +175 -0
- package/src/memory/delivery-crud.ts +211 -0
- package/src/memory/delivery-status.ts +199 -0
- package/src/memory/embedding-backend.ts +70 -4
- package/src/memory/embedding-local.ts +12 -2
- package/src/memory/entity-extractor.ts +3 -8
- package/src/memory/fts-reconciler.ts +136 -0
- package/src/memory/guardian-action-store.ts +418 -5
- package/src/memory/guardian-approvals.ts +569 -0
- package/src/memory/guardian-bindings.ts +130 -0
- package/src/memory/guardian-rate-limits.ts +196 -0
- package/src/memory/guardian-verification.ts +521 -0
- package/src/memory/job-handlers/index-maintenance.ts +2 -1
- package/src/memory/job-utils.ts +8 -5
- package/src/memory/jobs-store.ts +66 -6
- package/src/memory/jobs-worker.ts +23 -1
- package/src/memory/migrations/030-guardian-action-followup.ts +21 -0
- package/src/memory/migrations/030-guardian-verification-purpose.ts +17 -0
- package/src/memory/migrations/031-conversations-thread-type-index.ts +5 -0
- package/src/memory/migrations/032-guardian-delivery-conversation-index.ts +15 -0
- package/src/memory/migrations/032-notification-delivery-thread-decision.ts +20 -0
- package/src/memory/migrations/100-core-tables.ts +1 -1
- package/src/memory/migrations/101-watchers-and-logs.ts +4 -0
- package/src/memory/migrations/108-tasks-and-work-items.ts +1 -1
- package/src/memory/migrations/112-assistant-inbox.ts +1 -1
- package/src/memory/migrations/113-late-migrations.ts +1 -1
- package/src/memory/migrations/116-messages-fts.ts +13 -0
- package/src/memory/migrations/119-schema-indexes-and-columns.ts +37 -0
- package/src/memory/migrations/120-fk-cascade-rebuilds.ts +161 -0
- package/src/memory/migrations/index.ts +10 -3
- package/src/memory/migrations/validate-migration-state.ts +114 -15
- package/src/memory/qdrant-circuit-breaker.ts +105 -0
- package/src/memory/retriever.ts +46 -13
- package/src/memory/schema-migration.ts +4 -0
- package/src/memory/schema.ts +31 -8
- package/src/memory/search/semantic.ts +8 -90
- package/src/notifications/README.md +159 -18
- package/src/notifications/broadcaster.ts +69 -33
- package/src/notifications/conversation-pairing.ts +99 -21
- package/src/notifications/decision-engine.ts +176 -8
- package/src/notifications/deliveries-store.ts +39 -8
- package/src/notifications/emit-signal.ts +1 -0
- package/src/notifications/preferences-store.ts +7 -7
- package/src/notifications/thread-candidates.ts +269 -0
- package/src/notifications/types.ts +19 -0
- package/src/permissions/checker.ts +1 -16
- package/src/permissions/defaults.ts +25 -5
- package/src/permissions/prompter.ts +17 -0
- package/src/permissions/trust-store.ts +2 -0
- package/src/providers/failover.ts +19 -0
- package/src/providers/registry.ts +46 -1
- package/src/runtime/approval-message-composer.ts +1 -1
- package/src/runtime/channel-guardian-service.ts +15 -3
- package/src/runtime/channel-retry-sweep.ts +7 -2
- package/src/runtime/guardian-action-conversation-turn.ts +85 -0
- package/src/runtime/guardian-action-followup-executor.ts +301 -0
- package/src/runtime/guardian-action-message-composer.ts +245 -0
- package/src/runtime/guardian-outbound-actions.ts +26 -6
- package/src/runtime/guardian-verification-templates.ts +15 -9
- package/src/runtime/http-errors.ts +93 -0
- package/src/runtime/http-server.ts +133 -44
- package/src/runtime/http-types.ts +53 -0
- package/src/runtime/ingress-service.ts +237 -0
- package/src/runtime/middleware/error-handler.ts +4 -3
- package/src/runtime/middleware/rate-limiter.ts +160 -0
- package/src/runtime/middleware/request-logger.ts +71 -0
- package/src/runtime/middleware/twilio-validation.ts +7 -6
- package/src/runtime/pending-interactions.ts +12 -0
- package/src/runtime/routes/access-request-decision.ts +215 -0
- package/src/runtime/routes/app-routes.ts +25 -18
- package/src/runtime/routes/approval-routes.ts +18 -47
- package/src/runtime/routes/attachment-routes.ts +15 -41
- package/src/runtime/routes/call-routes.ts +20 -20
- package/src/runtime/routes/channel-delivery-routes.ts +6 -5
- package/src/runtime/routes/contact-routes.ts +4 -9
- package/src/runtime/routes/conversation-attention-routes.ts +2 -1
- package/src/runtime/routes/conversation-routes.ts +26 -57
- package/src/runtime/routes/debug-routes.ts +71 -0
- package/src/runtime/routes/events-routes.ts +3 -2
- package/src/runtime/routes/guardian-approval-interception.ts +221 -0
- package/src/runtime/routes/identity-routes.ts +14 -10
- package/src/runtime/routes/inbound-conversation.ts +3 -2
- package/src/runtime/routes/inbound-message-handler.ts +527 -62
- package/src/runtime/routes/ingress-routes.ts +174 -0
- package/src/runtime/routes/integration-routes.ts +78 -16
- package/src/runtime/routes/pairing-routes.ts +11 -10
- package/src/runtime/routes/secret-routes.ts +10 -18
- package/src/runtime/verification-rate-limiter.ts +83 -0
- package/src/schedule/schedule-store.ts +13 -1
- package/src/schedule/scheduler.ts +1 -1
- package/src/security/secret-ingress.ts +5 -2
- package/src/security/secret-scanner.ts +72 -6
- package/src/subagent/manager.ts +6 -4
- package/src/swarm/plan-validator.ts +4 -1
- package/src/tasks/task-runner.ts +3 -1
- package/src/tools/browser/api-map.ts +9 -6
- package/src/tools/calls/call-start.ts +20 -0
- package/src/tools/executor.ts +50 -568
- package/src/tools/permission-checker.ts +271 -0
- package/src/tools/registry.ts +14 -6
- package/src/tools/reminder/reminder-store.ts +7 -7
- package/src/tools/reminder/reminder.ts +6 -3
- package/src/tools/secret-detection-handler.ts +301 -0
- package/src/tools/subagent/message.ts +1 -1
- package/src/tools/system/voice-config.ts +62 -0
- package/src/tools/tasks/index.ts +3 -3
- package/src/tools/tasks/work-item-list.ts +3 -3
- package/src/tools/tasks/work-item-update.ts +4 -5
- package/src/tools/tool-approval-handler.ts +192 -0
- package/src/tools/tool-manifest.ts +2 -0
- package/src/version.ts +29 -2
- package/src/watcher/watcher-store.ts +9 -9
- package/src/work-items/work-item-runner.ts +9 -6
- /package/src/memory/migrations/{026-embeddings-nullable-vector-json.ts → 026a-embeddings-nullable-vector-json.ts} +0 -0
- /package/src/memory/migrations/{027-guardian-bootstrap-token.ts → 027a-guardian-bootstrap-token.ts} +0 -0
|
@@ -25,6 +25,8 @@ export interface QueuedMessage {
|
|
|
25
25
|
isInteractive?: boolean;
|
|
26
26
|
/** Timestamp (ms) when the message was enqueued. */
|
|
27
27
|
queuedAt: number;
|
|
28
|
+
/** Original user message text to persist to DB when recording intent stripping produced a different `content`. */
|
|
29
|
+
displayContent?: string;
|
|
28
30
|
}
|
|
29
31
|
|
|
30
32
|
export const MAX_QUEUE_DEPTH = 10;
|
|
@@ -25,6 +25,10 @@ export interface ChannelCapabilities {
|
|
|
25
25
|
supportsDynamicUi: boolean;
|
|
26
26
|
/** Whether the channel supports voice/microphone input. */
|
|
27
27
|
supportsVoiceInput: boolean;
|
|
28
|
+
/** Push-to-talk activation key (e.g. 'fn', 'ctrl', 'fn_shift', 'none'). Only present on desktop clients. */
|
|
29
|
+
pttActivationKey?: string;
|
|
30
|
+
/** Whether the client has been granted microphone permission by the OS. */
|
|
31
|
+
microphonePermissionGranted?: boolean;
|
|
28
32
|
}
|
|
29
33
|
|
|
30
34
|
/** Guardian identity/trust context for external chat channels. */
|
|
@@ -39,10 +43,26 @@ export interface GuardianRuntimeContext {
|
|
|
39
43
|
denialReason?: 'no_binding' | 'no_identity';
|
|
40
44
|
}
|
|
41
45
|
|
|
46
|
+
/** Allowed push-to-talk activation key values. Used to validate client-provided keys before system-prompt injection. */
|
|
47
|
+
const PTT_KEY_ALLOWLIST = new Set(['fn', 'ctrl', 'fn_shift', 'none']);
|
|
48
|
+
|
|
49
|
+
/** Validate a PTT activation key against the allowlist. Returns the key if valid, 'unknown' otherwise. */
|
|
50
|
+
export function sanitizePttActivationKey(key: string | undefined | null): string | undefined {
|
|
51
|
+
if (key == null) return undefined;
|
|
52
|
+
return PTT_KEY_ALLOWLIST.has(key) ? key : 'unknown';
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Optional PTT metadata provided by the client alongside each message. */
|
|
56
|
+
export interface PttMetadata {
|
|
57
|
+
pttActivationKey?: string;
|
|
58
|
+
microphonePermissionGranted?: boolean;
|
|
59
|
+
}
|
|
60
|
+
|
|
42
61
|
/** Derive channel capabilities from source channel + interface identifiers. */
|
|
43
62
|
export function resolveChannelCapabilities(
|
|
44
63
|
sourceChannel?: string | null,
|
|
45
64
|
sourceInterface?: string | null,
|
|
65
|
+
pttMetadata?: PttMetadata | null,
|
|
46
66
|
): ChannelCapabilities {
|
|
47
67
|
// Normalise legacy pseudo-channel IDs to canonical ChannelId values.
|
|
48
68
|
let channel: string;
|
|
@@ -85,6 +105,8 @@ export function resolveChannelCapabilities(
|
|
|
85
105
|
dashboardCapable: supportsDesktopUi,
|
|
86
106
|
supportsDynamicUi: supportsDesktopUi,
|
|
87
107
|
supportsVoiceInput: supportsDesktopUi,
|
|
108
|
+
pttActivationKey: sanitizePttActivationKey(pttMetadata?.pttActivationKey),
|
|
109
|
+
microphonePermissionGranted: pttMetadata?.microphonePermissionGranted,
|
|
88
110
|
};
|
|
89
111
|
}
|
|
90
112
|
case 'telegram':
|
|
@@ -334,6 +356,23 @@ export function injectChannelCapabilityContext(message: Message, caps: ChannelCa
|
|
|
334
356
|
lines.push('- Do NOT ask the user to use voice or microphone input.');
|
|
335
357
|
}
|
|
336
358
|
|
|
359
|
+
// PTT state — only relevant on channels that support voice input
|
|
360
|
+
if (caps.supportsVoiceInput) {
|
|
361
|
+
if (caps.pttActivationKey && caps.pttActivationKey !== 'none') {
|
|
362
|
+
const keyLabel = caps.pttActivationKey === 'fn_shift' ? 'Fn+Shift' : caps.pttActivationKey === 'fn' ? 'Fn (Globe)' : caps.pttActivationKey;
|
|
363
|
+
lines.push(`ptt_activation_key: ${caps.pttActivationKey}`);
|
|
364
|
+
lines.push(`ptt_enabled: true`);
|
|
365
|
+
lines.push(`Push-to-talk is configured with the ${keyLabel} key. The user can hold ${keyLabel} to dictate text or start a voice conversation.`);
|
|
366
|
+
} else if (caps.pttActivationKey === 'none') {
|
|
367
|
+
lines.push(`ptt_activation_key: none`);
|
|
368
|
+
lines.push(`ptt_enabled: false`);
|
|
369
|
+
lines.push('Push-to-talk is disabled. You can offer to enable it for the user.');
|
|
370
|
+
}
|
|
371
|
+
if (caps.microphonePermissionGranted !== undefined) {
|
|
372
|
+
lines.push(`microphone_permission_granted: ${caps.microphonePermissionGranted}`);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
337
376
|
lines.push('</channel_capabilities>');
|
|
338
377
|
|
|
339
378
|
const block = lines.join('\n');
|
|
@@ -282,17 +282,18 @@ export function projectSkillTools(
|
|
|
282
282
|
);
|
|
283
283
|
|
|
284
284
|
if (tools.length > 0) {
|
|
285
|
+
let accepted = tools;
|
|
285
286
|
const prevHash = prevActive.get(skillId);
|
|
286
287
|
if (prevHash === undefined) {
|
|
287
288
|
// Newly active skill — register for the first time
|
|
288
|
-
registerSkillTools(tools);
|
|
289
|
+
accepted = registerSkillTools(tools);
|
|
289
290
|
} else if (prevHash !== currentHash) {
|
|
290
291
|
// Hash changed — unregister stale tools, then re-register with new definitions
|
|
291
292
|
log.info({ skillId, prevHash, currentHash }, 'Skill version changed, re-registering tools');
|
|
292
293
|
unregisterSkillTools(skillId);
|
|
293
294
|
alreadyUnregistered.add(skillId);
|
|
294
295
|
try {
|
|
295
|
-
registerSkillTools(tools);
|
|
296
|
+
accepted = registerSkillTools(tools);
|
|
296
297
|
} catch (err) {
|
|
297
298
|
log.error({ err, skillId }, 'Failed to re-register skill tools after version change');
|
|
298
299
|
// Don't add to successfulEntries — will be cleaned up as transiently-failed
|
|
@@ -306,12 +307,20 @@ export function projectSkillTools(
|
|
|
306
307
|
if (existing && existing.ownerSkillBundled !== (skill.bundled ?? undefined)) {
|
|
307
308
|
log.info({ skillId, bundled: skill.bundled }, 'Skill bundled status changed, re-registering tools');
|
|
308
309
|
unregisterSkillTools(skillId);
|
|
309
|
-
registerSkillTools(tools);
|
|
310
|
+
accepted = registerSkillTools(tools);
|
|
311
|
+
} else {
|
|
312
|
+
// Filter to only tools that are actually registered for this skill.
|
|
313
|
+
// Some tools may have been skipped during initial registration due
|
|
314
|
+
// to core-name collisions — don't let them leak back in.
|
|
315
|
+
accepted = tools.filter((t) => {
|
|
316
|
+
const reg = getTool(t.name);
|
|
317
|
+
return reg !== undefined && reg.origin === 'skill' && reg.ownerSkillId === skillId;
|
|
318
|
+
});
|
|
310
319
|
}
|
|
311
320
|
}
|
|
312
321
|
|
|
313
322
|
successfulEntries.set(skillId, currentHash);
|
|
314
|
-
for (const tool of
|
|
323
|
+
for (const tool of accepted) {
|
|
315
324
|
allToolDefinitions.push(tool.getDefinition());
|
|
316
325
|
allToolNames.add(tool.name);
|
|
317
326
|
}
|
|
@@ -117,12 +117,11 @@ export function createToolExecutor(
|
|
|
117
117
|
forcePromptSideEffects: ctx.memoryPolicy.strictSideEffects,
|
|
118
118
|
onToolLifecycleEvent: handleToolLifecycleEvent,
|
|
119
119
|
sendToClient: (msg) => {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
const s = serverMsg as unknown as UiSurfaceShow;
|
|
120
|
+
// Tool context's sendToClient uses a loose { type: string; [key: string]: unknown }
|
|
121
|
+
// signature, but at runtime these are always ServerMessage instances.
|
|
122
|
+
ctx.sendToClient(msg as ServerMessage);
|
|
123
|
+
if (msg.type === 'ui_surface_show') {
|
|
124
|
+
const s = msg as unknown as UiSurfaceShow;
|
|
126
125
|
ctx.currentTurnSurfaces.push({
|
|
127
126
|
surfaceId: s.surfaceId,
|
|
128
127
|
surfaceType: s.surfaceType,
|
package/src/daemon/session.ts
CHANGED
|
@@ -397,8 +397,9 @@ export class Session {
|
|
|
397
397
|
currentPage?: string,
|
|
398
398
|
metadata?: Record<string, unknown>,
|
|
399
399
|
options?: { isInteractive?: boolean },
|
|
400
|
+
displayContent?: string,
|
|
400
401
|
): { queued: boolean; rejected?: boolean; requestId: string } {
|
|
401
|
-
return enqueueMessageImpl(this, content, attachments, onEvent, requestId, activeSurfaceId, currentPage, metadata, options);
|
|
402
|
+
return enqueueMessageImpl(this, content, attachments, onEvent, requestId, activeSurfaceId, currentPage, metadata, options, displayContent);
|
|
402
403
|
}
|
|
403
404
|
|
|
404
405
|
getQueueDepth(): number {
|
|
@@ -425,6 +426,14 @@ export class Session {
|
|
|
425
426
|
return this.prompter.hasPendingRequest(requestId);
|
|
426
427
|
}
|
|
427
428
|
|
|
429
|
+
hasAnyPendingConfirmation(): boolean {
|
|
430
|
+
return this.prompter.hasPending;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
denyAllPendingConfirmations(): void {
|
|
434
|
+
this.prompter.denyAllPending();
|
|
435
|
+
}
|
|
436
|
+
|
|
428
437
|
hasPendingSecret(requestId: string): boolean {
|
|
429
438
|
return this.secretPrompter.hasPendingRequest(requestId);
|
|
430
439
|
}
|
|
@@ -489,13 +498,14 @@ export class Session {
|
|
|
489
498
|
return this.currentTurnInterfaceContext;
|
|
490
499
|
}
|
|
491
500
|
|
|
492
|
-
persistUserMessage(
|
|
501
|
+
async persistUserMessage(
|
|
493
502
|
content: string,
|
|
494
503
|
attachments: UserMessageAttachment[],
|
|
495
504
|
requestId?: string,
|
|
496
505
|
metadata?: Record<string, unknown>,
|
|
497
|
-
|
|
498
|
-
|
|
506
|
+
displayContent?: string,
|
|
507
|
+
): Promise<string> {
|
|
508
|
+
return persistUserMessageImpl(this, content, attachments, requestId, metadata, displayContent);
|
|
499
509
|
}
|
|
500
510
|
|
|
501
511
|
// ── Agent Loop ───────────────────────────────────────────────────
|
|
@@ -504,14 +514,14 @@ export class Session {
|
|
|
504
514
|
content: string,
|
|
505
515
|
userMessageId: string,
|
|
506
516
|
onEvent: (msg: ServerMessage) => void,
|
|
507
|
-
options?: { skipPreMessageRollback?: boolean; isInteractive?: boolean },
|
|
517
|
+
options?: { skipPreMessageRollback?: boolean; isInteractive?: boolean; titleText?: string },
|
|
508
518
|
): Promise<void> {
|
|
509
519
|
return runAgentLoopImpl(this, content, userMessageId, onEvent, options);
|
|
510
520
|
}
|
|
511
521
|
|
|
512
522
|
|
|
513
|
-
drainQueue(reason: QueueDrainReason = 'loop_complete'): void {
|
|
514
|
-
drainQueueImpl(this as ProcessSessionContext, reason);
|
|
523
|
+
drainQueue(reason: QueueDrainReason = 'loop_complete'): Promise<void> {
|
|
524
|
+
return drainQueueImpl(this as ProcessSessionContext, reason);
|
|
515
525
|
}
|
|
516
526
|
|
|
517
527
|
async processMessage(
|
|
@@ -522,8 +532,9 @@ export class Session {
|
|
|
522
532
|
activeSurfaceId?: string,
|
|
523
533
|
currentPage?: string,
|
|
524
534
|
options?: { isInteractive?: boolean },
|
|
535
|
+
displayContent?: string,
|
|
525
536
|
): Promise<string> {
|
|
526
|
-
return processMessageImpl(this as ProcessSessionContext, content, attachments, onEvent, requestId, activeSurfaceId, currentPage, options);
|
|
537
|
+
return processMessageImpl(this as ProcessSessionContext, content, attachments, onEvent, requestId, activeSurfaceId, currentPage, options, displayContent);
|
|
527
538
|
}
|
|
528
539
|
|
|
529
540
|
// ── History ──────────────────────────────────────────────────────
|
package/src/daemon/tls-certs.ts
CHANGED
|
@@ -42,6 +42,8 @@ function computeFingerprint(cert: X509Certificate): string {
|
|
|
42
42
|
return cert.fingerprint256.replace(/:/g, '').toLowerCase();
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
+
type CertStatus = 'valid' | 'approaching_expiry' | 'invalid';
|
|
46
|
+
|
|
45
47
|
/**
|
|
46
48
|
* Check whether an existing cert+key pair is valid:
|
|
47
49
|
* - All three files exist (cert, key, fingerprint)
|
|
@@ -49,8 +51,12 @@ function computeFingerprint(cert: X509Certificate): string {
|
|
|
49
51
|
* - Cert is not expired
|
|
50
52
|
* - Fingerprint file exists and matches the cert
|
|
51
53
|
* - Private key is valid and matches the certificate
|
|
54
|
+
*
|
|
55
|
+
* Returns 'valid' if the cert is good, 'approaching_expiry' if it's still usable
|
|
56
|
+
* but expires within 30 days (renewal should be attempted), or 'invalid' if the
|
|
57
|
+
* cert cannot be used at all.
|
|
52
58
|
*/
|
|
53
|
-
async function
|
|
59
|
+
async function checkCertStatus(): Promise<CertStatus> {
|
|
54
60
|
const certPath = getTlsCertPath();
|
|
55
61
|
const keyPath = getTlsKeyPath();
|
|
56
62
|
const fpPath = getTlsFingerprintPath();
|
|
@@ -63,7 +69,7 @@ async function isExistingCertValid(): Promise<boolean> {
|
|
|
63
69
|
]);
|
|
64
70
|
|
|
65
71
|
if (!certExists || !keyExists || !fpExists) {
|
|
66
|
-
return
|
|
72
|
+
return 'invalid';
|
|
67
73
|
}
|
|
68
74
|
|
|
69
75
|
try {
|
|
@@ -76,17 +82,18 @@ async function isExistingCertValid(): Promise<boolean> {
|
|
|
76
82
|
const x509 = new X509Certificate(certPem);
|
|
77
83
|
|
|
78
84
|
// Check expiration
|
|
85
|
+
const now = new Date();
|
|
79
86
|
const notAfter = new Date(x509.validTo);
|
|
80
|
-
if (notAfter <=
|
|
87
|
+
if (notAfter <= now) {
|
|
81
88
|
log.info('Existing TLS certificate has expired, will regenerate');
|
|
82
|
-
return
|
|
89
|
+
return 'invalid';
|
|
83
90
|
}
|
|
84
91
|
|
|
85
92
|
// Check fingerprint matches
|
|
86
93
|
const actualFp = computeFingerprint(x509);
|
|
87
94
|
if (actualFp !== storedFp.trim()) {
|
|
88
95
|
log.info('TLS fingerprint mismatch, will regenerate');
|
|
89
|
-
return
|
|
96
|
+
return 'invalid';
|
|
90
97
|
}
|
|
91
98
|
|
|
92
99
|
// Verify the private key is valid and matches the certificate's public key.
|
|
@@ -95,13 +102,20 @@ async function isExistingCertValid(): Promise<boolean> {
|
|
|
95
102
|
const privateKey = createPrivateKey(keyPem);
|
|
96
103
|
if (!x509.checkPrivateKey(privateKey)) {
|
|
97
104
|
log.info('TLS private key does not match certificate, will regenerate');
|
|
98
|
-
return
|
|
105
|
+
return 'invalid';
|
|
99
106
|
}
|
|
100
107
|
|
|
101
|
-
|
|
108
|
+
// Cert is structurally valid — check if it's approaching expiry
|
|
109
|
+
const thirtyDaysMs = 30 * 24 * 60 * 60 * 1000;
|
|
110
|
+
if (notAfter.getTime() - now.getTime() < thirtyDaysMs) {
|
|
111
|
+
log.info('TLS certificate approaching expiry, will attempt renewal');
|
|
112
|
+
return 'approaching_expiry';
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return 'valid';
|
|
102
116
|
} catch (err) {
|
|
103
117
|
log.warn({ err }, 'Failed to validate existing TLS certificate, will regenerate');
|
|
104
|
-
return
|
|
118
|
+
return 'invalid';
|
|
105
119
|
}
|
|
106
120
|
}
|
|
107
121
|
|
|
@@ -125,8 +139,9 @@ export async function ensureTlsCert(): Promise<{ cert: string; key: string; fing
|
|
|
125
139
|
const keyPath = getTlsKeyPath();
|
|
126
140
|
const fpPath = getTlsFingerprintPath();
|
|
127
141
|
|
|
128
|
-
|
|
129
|
-
|
|
142
|
+
const status = await checkCertStatus();
|
|
143
|
+
|
|
144
|
+
if (status === 'valid') {
|
|
130
145
|
const [cert, key, fingerprint] = await Promise.all([
|
|
131
146
|
readFile(certPath, 'utf-8'),
|
|
132
147
|
readFile(keyPath, 'utf-8'),
|
|
@@ -136,7 +151,39 @@ export async function ensureTlsCert(): Promise<{ cert: string; key: string; fing
|
|
|
136
151
|
return { cert, key, fingerprint: fingerprint.trim() };
|
|
137
152
|
}
|
|
138
153
|
|
|
139
|
-
|
|
154
|
+
if (status === 'approaching_expiry') {
|
|
155
|
+
try {
|
|
156
|
+
// Buffer existing cert/key/fingerprint before attempting renewal.
|
|
157
|
+
// generateNewCert() overwrites key.pem in-place, so if it fails mid-flight
|
|
158
|
+
// (e.g., key written but cert generation fails), reading from disk in the
|
|
159
|
+
// catch block would return a mismatched key/cert pair.
|
|
160
|
+
const [existingCert, existingKey, existingFp] = await Promise.all([
|
|
161
|
+
readFile(certPath, 'utf-8'),
|
|
162
|
+
readFile(keyPath, 'utf-8'),
|
|
163
|
+
readFile(fpPath, 'utf-8'),
|
|
164
|
+
]);
|
|
165
|
+
try {
|
|
166
|
+
return await generateNewCert(tlsDir, certPath, keyPath, fpPath);
|
|
167
|
+
} catch (err) {
|
|
168
|
+
log.warn({ err }, 'Proactive TLS renewal failed, continuing with existing certificate');
|
|
169
|
+
return { cert: existingCert, key: existingKey, fingerprint: existingFp.trim() };
|
|
170
|
+
}
|
|
171
|
+
} catch (err) {
|
|
172
|
+
log.warn({ err }, 'Failed to read existing TLS cert for buffering, attempting regeneration');
|
|
173
|
+
return await generateNewCert(tlsDir, certPath, keyPath, fpPath);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// status === 'invalid' — must regenerate, no fallback
|
|
178
|
+
return await generateNewCert(tlsDir, certPath, keyPath, fpPath);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
async function generateNewCert(
|
|
182
|
+
tlsDir: string,
|
|
183
|
+
certPath: string,
|
|
184
|
+
keyPath: string,
|
|
185
|
+
fpPath: string,
|
|
186
|
+
): Promise<{ cert: string; key: string; fingerprint: string }> {
|
|
140
187
|
log.info('Generating new self-signed TLS certificate');
|
|
141
188
|
await mkdir(tlsDir, { recursive: true });
|
|
142
189
|
|
|
@@ -151,13 +198,13 @@ export async function ensureTlsCert(): Promise<{ cert: string; key: string; fing
|
|
|
151
198
|
throw new Error(`Failed to generate TLS key: ${stderr}`);
|
|
152
199
|
}
|
|
153
200
|
|
|
154
|
-
// Generate self-signed cert (
|
|
201
|
+
// Generate self-signed cert (1-year validity)
|
|
155
202
|
const certProc = Bun.spawn(
|
|
156
203
|
[
|
|
157
204
|
'openssl', 'req', '-new', '-x509',
|
|
158
205
|
'-key', keyPath,
|
|
159
206
|
'-out', certPath,
|
|
160
|
-
'-days', '
|
|
207
|
+
'-days', '365',
|
|
161
208
|
'-subj', '/CN=Vellum Daemon',
|
|
162
209
|
],
|
|
163
210
|
{ stdout: 'pipe', stderr: 'pipe' },
|
|
@@ -11,6 +11,7 @@ import { updatePublishedAppDeployment } from '../services/published-app-updater.
|
|
|
11
11
|
import { openAppViaSurface } from '../tools/apps/open-proxy.js';
|
|
12
12
|
import type { ToolExecutionResult } from '../tools/types.js';
|
|
13
13
|
import { isDoordashCommand, updateDoordashProgress } from './doordash-steps.js';
|
|
14
|
+
import { normalizeActivationKey } from './handlers/config-voice.js';
|
|
14
15
|
import type { ServerMessage } from './ipc-protocol.js';
|
|
15
16
|
import {
|
|
16
17
|
refreshSurfacesForApp,
|
|
@@ -74,11 +75,6 @@ registerHook('app_update', (_name, input, _result, { ctx, broadcastToAllClients
|
|
|
74
75
|
}
|
|
75
76
|
});
|
|
76
77
|
|
|
77
|
-
// Tell the client to open/focus the tasks window when the model lists tasks
|
|
78
|
-
registerHook('task_list_show', (_name, _input, _result, { ctx }) => {
|
|
79
|
-
ctx.sendToClient({ type: 'open_tasks_window' });
|
|
80
|
-
});
|
|
81
|
-
|
|
82
78
|
// Broadcast tasks_changed so connected clients (e.g. macOS Tasks window)
|
|
83
79
|
// auto-refresh when the LLM mutates the task queue via tools
|
|
84
80
|
registerHook(
|
|
@@ -101,6 +97,18 @@ registerHook(
|
|
|
101
97
|
},
|
|
102
98
|
);
|
|
103
99
|
|
|
100
|
+
// Broadcast activation key change to all connected clients so every
|
|
101
|
+
// macOS/iOS instance picks up the new setting immediately.
|
|
102
|
+
registerHook('voice_config_update', (_name, input, _result, { broadcastToAllClients }) => {
|
|
103
|
+
const key = input.activation_key as string | undefined;
|
|
104
|
+
if (key) {
|
|
105
|
+
const normalized = normalizeActivationKey(key);
|
|
106
|
+
if (normalized.ok) {
|
|
107
|
+
broadcastToAllClients?.({ type: 'client_settings_update', key: 'activationKey', value: normalized.value });
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
|
|
104
112
|
// ── Runner ───────────────────────────────────────────────────────────
|
|
105
113
|
|
|
106
114
|
/**
|
|
@@ -107,8 +107,8 @@ const focusTimerHtml = `<!DOCTYPE html>
|
|
|
107
107
|
<div class="mode-label" id="modeLabel">Work Session</div>
|
|
108
108
|
<div class="timer-display" id="timerDisplay">25:00</div>
|
|
109
109
|
<div class="controls">
|
|
110
|
-
<button class="btn-primary" id="startBtn"
|
|
111
|
-
<button class="btn-secondary" id="resetBtn"
|
|
110
|
+
<button class="btn-primary" id="startBtn">Start</button>
|
|
111
|
+
<button class="btn-secondary" id="resetBtn">Reset</button>
|
|
112
112
|
</div>
|
|
113
113
|
<div class="stats">
|
|
114
114
|
<div class="stat-item">
|
|
@@ -184,6 +184,9 @@ const focusTimerHtml = `<!DOCTYPE html>
|
|
|
184
184
|
updateDisplay();
|
|
185
185
|
}
|
|
186
186
|
|
|
187
|
+
document.getElementById('startBtn').addEventListener('click', toggleTimer);
|
|
188
|
+
document.getElementById('resetBtn').addEventListener('click', resetTimer);
|
|
189
|
+
|
|
187
190
|
updateDisplay();
|
|
188
191
|
</script>
|
|
189
192
|
</body>
|
|
@@ -334,8 +337,8 @@ const habitTrackerHtml = `<!DOCTYPE html>
|
|
|
334
337
|
<h1>Habit Tracker</h1>
|
|
335
338
|
</div>
|
|
336
339
|
<div class="add-form">
|
|
337
|
-
<input type="text" id="habitInput" placeholder="Add a new habit..."
|
|
338
|
-
<button class="btn-primary"
|
|
340
|
+
<input type="text" id="habitInput" placeholder="Add a new habit...">
|
|
341
|
+
<button class="btn-primary" id="addHabitBtn">Add</button>
|
|
339
342
|
</div>
|
|
340
343
|
<div class="days-header">
|
|
341
344
|
<div></div>
|
|
@@ -387,12 +390,12 @@ const habitTrackerHtml = `<!DOCTYPE html>
|
|
|
387
390
|
html += '<div class="habit-row">';
|
|
388
391
|
html += '<div style="display:flex;align-items:center;gap:8px">';
|
|
389
392
|
html += '<span class="habit-name">' + escapeHtml(record.data.name) + '</span>';
|
|
390
|
-
html += '<button class="delete-btn"
|
|
393
|
+
html += '<button class="delete-btn" data-delete-habit="'+record.id+'">x</button>';
|
|
391
394
|
html += '</div>';
|
|
392
395
|
dates.forEach(function(date) {
|
|
393
396
|
var checked = completedDates.indexOf(date) !== -1;
|
|
394
397
|
html += '<div class="check-cell">';
|
|
395
|
-
html += '<button class="check-btn' + (checked ? ' checked' : '') + '"
|
|
398
|
+
html += '<button class="check-btn' + (checked ? ' checked' : '') + '" data-toggle-habit="'+record.id+'" data-toggle-date="'+date+'">';
|
|
396
399
|
html += checked ? '\\u2713' : '';
|
|
397
400
|
html += '</button></div>';
|
|
398
401
|
});
|
|
@@ -438,6 +441,17 @@ const habitTrackerHtml = `<!DOCTYPE html>
|
|
|
438
441
|
});
|
|
439
442
|
}
|
|
440
443
|
|
|
444
|
+
document.getElementById('habitInput').addEventListener('keydown', function(event) {
|
|
445
|
+
if (event.key === 'Enter') addHabit();
|
|
446
|
+
});
|
|
447
|
+
document.getElementById('addHabitBtn').addEventListener('click', addHabit);
|
|
448
|
+
document.getElementById('habitsList').addEventListener('click', function(event) {
|
|
449
|
+
var btn = event.target.closest('[data-delete-habit]');
|
|
450
|
+
if (btn) { deleteHabit(btn.getAttribute('data-delete-habit')); return; }
|
|
451
|
+
var toggle = event.target.closest('[data-toggle-habit]');
|
|
452
|
+
if (toggle) { toggleDate(toggle.getAttribute('data-toggle-habit'), toggle.getAttribute('data-toggle-date')); }
|
|
453
|
+
});
|
|
454
|
+
|
|
441
455
|
initDates();
|
|
442
456
|
loadHabits();
|
|
443
457
|
</script>
|
|
@@ -629,9 +643,9 @@ const expenseTrackerHtml = `<!DOCTYPE html>
|
|
|
629
643
|
<option value="entertainment">Entertainment</option>
|
|
630
644
|
<option value="other">Other</option>
|
|
631
645
|
</select>
|
|
632
|
-
<input type="text" class="input-desc" id="descInput" placeholder="Description..."
|
|
646
|
+
<input type="text" class="input-desc" id="descInput" placeholder="Description...">
|
|
633
647
|
<input type="date" class="input-date" id="dateInput">
|
|
634
|
-
<button class="btn-primary"
|
|
648
|
+
<button class="btn-primary" id="addExpenseBtn">Add</button>
|
|
635
649
|
</div>
|
|
636
650
|
<div class="section-title">By Category</div>
|
|
637
651
|
<div class="categories-grid" id="categoriesGrid"></div>
|
|
@@ -693,7 +707,7 @@ const expenseTrackerHtml = `<!DOCTYPE html>
|
|
|
693
707
|
listHtml += '<div class="expense-meta">' + escapeHtml(r.data.category || 'other') + ' \\u00B7 ' + escapeHtml(r.data.date || '') + '</div>';
|
|
694
708
|
listHtml += '</div>';
|
|
695
709
|
listHtml += '<div class="expense-amount">$' + amt.toFixed(2) + '</div>';
|
|
696
|
-
listHtml += '<button class="delete-btn"
|
|
710
|
+
listHtml += '<button class="delete-btn" data-delete-expense="'+r.id+'">x</button>';
|
|
697
711
|
listHtml += '</div>';
|
|
698
712
|
});
|
|
699
713
|
}
|
|
@@ -724,6 +738,15 @@ const expenseTrackerHtml = `<!DOCTYPE html>
|
|
|
724
738
|
});
|
|
725
739
|
}
|
|
726
740
|
|
|
741
|
+
document.getElementById('descInput').addEventListener('keydown', function(event) {
|
|
742
|
+
if (event.key === 'Enter') addExpense();
|
|
743
|
+
});
|
|
744
|
+
document.getElementById('addExpenseBtn').addEventListener('click', addExpense);
|
|
745
|
+
document.getElementById('expenseList').addEventListener('click', function(event) {
|
|
746
|
+
var btn = event.target.closest('[data-delete-expense]');
|
|
747
|
+
if (btn) { deleteExpense(btn.getAttribute('data-delete-expense')); }
|
|
748
|
+
});
|
|
749
|
+
|
|
727
750
|
loadExpenses();
|
|
728
751
|
</script>
|
|
729
752
|
</body>
|
package/src/influencer/client.ts
CHANGED
|
@@ -47,6 +47,7 @@
|
|
|
47
47
|
|
|
48
48
|
import type { ExtensionCommand, ExtensionResponse } from '../browser-extension-relay/protocol.js';
|
|
49
49
|
import { extensionRelayServer } from '../browser-extension-relay/server.js';
|
|
50
|
+
import { getGatewayInternalBaseUrl } from '../config/env.js';
|
|
50
51
|
import { readHttpToken } from '../util/platform.js';
|
|
51
52
|
|
|
52
53
|
// ---------------------------------------------------------------------------
|
|
@@ -133,7 +134,7 @@ async function sendRelayCommand(command: Record<string, unknown>): Promise<Exten
|
|
|
133
134
|
);
|
|
134
135
|
}
|
|
135
136
|
|
|
136
|
-
const resp = await fetch(
|
|
137
|
+
const resp = await fetch(`${getGatewayInternalBaseUrl()}/v1/browser-relay/command`, {
|
|
137
138
|
method: 'POST',
|
|
138
139
|
headers: {
|
|
139
140
|
'Content-Type': 'application/json',
|