vellum 0.2.1 → 0.2.7
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/README.md +15 -2
- package/bun.lock +71 -100
- package/package.json +5 -3
- package/scripts/capture-x-graphql.ts +562 -0
- package/scripts/ipc/check-swift-decoder-drift.ts +2 -1
- package/scripts/test.sh +5 -0
- package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +133 -34
- package/src/__tests__/account-registry.test.ts +2 -1
- package/src/__tests__/agent-heartbeat-service.test.ts +250 -0
- package/src/__tests__/asset-materialize-tool.test.ts +16 -15
- package/src/__tests__/asset-search-tool.test.ts +23 -22
- package/src/__tests__/attachments-store.test.ts +56 -127
- package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +5 -4
- package/src/__tests__/browser-skill-endstate.test.ts +4 -3
- package/src/__tests__/call-bridge.test.ts +385 -0
- package/src/__tests__/call-constants.test.ts +40 -0
- package/src/__tests__/call-orchestrator.test.ts +130 -4
- package/src/__tests__/call-recovery.test.ts +518 -0
- package/src/__tests__/call-routes-http.test.ts +459 -0
- package/src/__tests__/call-state-machine.test.ts +143 -0
- package/src/__tests__/call-store.test.ts +216 -1
- package/src/__tests__/cli-discover.test.ts +1 -1
- package/src/__tests__/commit-message-enrichment-service.test.ts +148 -7
- package/src/__tests__/compaction.benchmark.test.ts +176 -0
- package/src/__tests__/computer-use-tools.test.ts +250 -0
- package/src/__tests__/config-schema.test.ts +305 -3
- package/src/__tests__/conflict-store.test.ts +2 -1
- package/src/__tests__/contacts-tools.test.ts +331 -0
- package/src/__tests__/conversation-store.test.ts +30 -32
- package/src/__tests__/credential-security-invariants.test.ts +4 -0
- package/src/__tests__/date-context.test.ts +373 -0
- package/src/__tests__/db-schedule-syntax-migration.test.ts +129 -0
- package/src/__tests__/fixtures/media-reuse-fixtures.ts +3 -3
- package/src/__tests__/followup-tools.test.ts +303 -0
- package/src/__tests__/handlers-twilio-config.test.ts +221 -0
- package/src/__tests__/handlers-twitter-config.test.ts +718 -0
- package/src/__tests__/intent-routing.test.ts +64 -57
- package/src/__tests__/ipc-roundtrip.benchmark.test.ts +237 -0
- package/src/__tests__/ipc-snapshot.test.ts +71 -28
- package/src/__tests__/llm-usage-store.test.ts +3 -8
- package/src/__tests__/media-generate-image.test.ts +1 -1
- package/src/__tests__/media-reuse-story.e2e.test.ts +7 -7
- package/src/__tests__/memory-regressions.test.ts +100 -2
- package/src/__tests__/memory-retrieval.benchmark.test.ts +430 -0
- package/src/__tests__/parallel-tool.benchmark.test.ts +294 -0
- package/src/__tests__/playbook-tools.test.ts +342 -0
- package/src/__tests__/profile-compiler.test.ts +2 -1
- package/src/__tests__/provider-commit-message-generator.test.ts +303 -0
- package/src/__tests__/provider-streaming.benchmark.test.ts +773 -0
- package/src/__tests__/recurrence-engine-rruleset.test.ts +78 -0
- package/src/__tests__/recurrence-engine.test.ts +69 -0
- package/src/__tests__/recurrence-types.test.ts +71 -0
- package/src/__tests__/registry.test.ts +5 -3
- package/src/__tests__/relay-server.test.ts +633 -0
- package/src/__tests__/reminder-store.test.ts +6 -3
- package/src/__tests__/reminder.test.ts +43 -77
- package/src/__tests__/run-orchestrator-assistant-events.test.ts +8 -4
- package/src/__tests__/run-orchestrator.test.ts +4 -4
- package/src/__tests__/runtime-attachment-metadata.test.ts +7 -6
- package/src/__tests__/runtime-runs-http.test.ts +4 -4
- package/src/__tests__/runtime-runs.test.ts +4 -4
- package/src/__tests__/schedule-store.test.ts +482 -0
- package/src/__tests__/schedule-tools.test.ts +700 -0
- package/src/__tests__/scheduler-recurrence.test.ts +329 -0
- package/src/__tests__/server-history-render.test.ts +14 -13
- package/src/__tests__/session-conflict-gate.test.ts +28 -25
- package/src/__tests__/session-error.test.ts +28 -0
- package/src/__tests__/session-init.benchmark.test.ts +462 -0
- package/src/__tests__/session-queue.test.ts +71 -48
- package/src/__tests__/session-runtime-assembly.test.ts +161 -0
- package/src/__tests__/session-surfaces-task-progress.test.ts +104 -0
- package/src/__tests__/signup-e2e.test.ts +2 -1
- package/src/__tests__/skill-projection.benchmark.test.ts +328 -0
- package/src/__tests__/skill-script-runner.test.ts +159 -0
- package/src/__tests__/speaker-identification.test.ts +52 -0
- package/src/__tests__/subagent-manager-notify.test.ts +42 -10
- package/src/__tests__/subagent-tools.test.ts +141 -41
- package/src/__tests__/task-compiler.test.ts +2 -1
- package/src/__tests__/task-runner.test.ts +2 -1
- package/src/__tests__/task-scheduler.test.ts +2 -1
- package/src/__tests__/task-tools.test.ts +49 -56
- package/src/__tests__/tool-audit-listener.test.ts +1 -0
- package/src/__tests__/tool-domain-event-publisher.test.ts +2 -0
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +500 -0
- package/src/__tests__/tool-executor.test.ts +13 -17
- package/src/__tests__/turn-commit.test.ts +218 -3
- package/src/__tests__/twilio-provider.test.ts +143 -0
- package/src/__tests__/twilio-routes.test.ts +789 -0
- package/src/__tests__/twitter-auth-handler.test.ts +581 -0
- package/src/__tests__/view-image-tool.test.ts +217 -0
- package/src/__tests__/workspace-git-service.test.ts +186 -0
- package/src/__tests__/workspace-heartbeat-service.test.ts +13 -3
- package/src/agent-heartbeat/agent-heartbeat-service.ts +155 -0
- package/src/bundler/app-bundler.ts +12 -8
- package/src/calls/__tests__/twilio-webhook-urls.test.ts +162 -0
- package/src/calls/call-bridge.ts +95 -0
- package/src/calls/call-constants.ts +43 -5
- package/src/calls/call-domain.ts +276 -0
- package/src/calls/call-orchestrator.ts +43 -17
- package/src/calls/call-recovery.ts +207 -0
- package/src/calls/call-state-machine.ts +68 -0
- package/src/calls/call-store.ts +192 -5
- package/src/calls/relay-server.ts +41 -4
- package/src/calls/speaker-identification.ts +213 -0
- package/src/calls/twilio-config.ts +8 -8
- package/src/calls/twilio-provider.ts +13 -9
- package/src/calls/twilio-routes.ts +90 -76
- package/src/calls/twilio-webhook-urls.ts +50 -0
- package/src/calls/types.ts +1 -1
- package/src/cli/config-commands.ts +334 -0
- package/src/cli/core-commands.ts +776 -0
- package/src/cli/doordash.ts +251 -1
- package/src/cli/ipc-client.ts +82 -0
- package/src/cli/map.ts +270 -0
- package/src/cli/twitter.ts +575 -0
- package/src/cli.ts +7 -5
- package/src/commands/__tests__/cc-command-registry.test.ts +319 -0
- package/src/commands/cc-command-registry.ts +209 -0
- package/src/config/bundled-skills/contacts/SKILL.md +39 -0
- package/src/config/bundled-skills/contacts/TOOLS.json +122 -0
- package/src/config/bundled-skills/contacts/tools/contact-merge.ts +9 -0
- package/src/config/bundled-skills/contacts/tools/contact-search.ts +9 -0
- package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +9 -0
- package/src/config/bundled-skills/document/SKILL.md +18 -0
- package/src/config/bundled-skills/document/TOOLS.json +53 -0
- package/src/config/bundled-skills/document/tools/document-create.ts +9 -0
- package/src/config/bundled-skills/document/tools/document-update.ts +9 -0
- package/src/config/bundled-skills/doordash/SKILL.md +82 -23
- package/src/config/bundled-skills/followups/SKILL.md +32 -0
- package/src/config/bundled-skills/followups/TOOLS.json +100 -0
- package/src/config/bundled-skills/followups/tools/followup-create.ts +9 -0
- package/src/config/bundled-skills/followups/tools/followup-list.ts +9 -0
- package/src/config/bundled-skills/followups/tools/followup-resolve.ts +9 -0
- package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +1 -23
- package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +2 -1
- package/src/config/bundled-skills/playbooks/SKILL.md +31 -0
- package/src/config/bundled-skills/playbooks/TOOLS.json +126 -0
- package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +9 -0
- package/src/config/bundled-skills/playbooks/tools/playbook-delete.ts +9 -0
- package/src/config/bundled-skills/playbooks/tools/playbook-list.ts +9 -0
- package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +9 -0
- package/src/config/bundled-skills/reminder/SKILL.md +20 -0
- package/src/config/bundled-skills/reminder/TOOLS.json +67 -0
- package/src/config/bundled-skills/reminder/tools/reminder-cancel.ts +9 -0
- package/src/config/bundled-skills/reminder/tools/reminder-create.ts +9 -0
- package/src/config/bundled-skills/reminder/tools/reminder-list.ts +9 -0
- package/src/config/bundled-skills/schedule/SKILL.md +74 -0
- package/src/config/bundled-skills/schedule/TOOLS.json +135 -0
- package/src/config/bundled-skills/schedule/tools/schedule-create.ts +9 -0
- package/src/config/bundled-skills/schedule/tools/schedule-delete.ts +9 -0
- package/src/config/bundled-skills/schedule/tools/schedule-list.ts +9 -0
- package/src/config/bundled-skills/schedule/tools/schedule-update.ts +9 -0
- package/src/config/bundled-skills/subagent/SKILL.md +25 -0
- package/src/config/bundled-skills/subagent/TOOLS.json +107 -0
- package/src/config/bundled-skills/subagent/tools/subagent-abort.ts +9 -0
- package/src/config/bundled-skills/subagent/tools/subagent-message.ts +9 -0
- package/src/config/bundled-skills/subagent/tools/subagent-read.ts +9 -0
- package/src/config/bundled-skills/subagent/tools/subagent-spawn.ts +9 -0
- package/src/config/bundled-skills/subagent/tools/subagent-status.ts +9 -0
- package/src/config/bundled-skills/tasks/SKILL.md +28 -0
- package/src/config/bundled-skills/tasks/TOOLS.json +256 -0
- package/src/config/bundled-skills/tasks/tools/task-delete.ts +9 -0
- package/src/config/bundled-skills/tasks/tools/task-list-add.ts +9 -0
- package/src/config/bundled-skills/tasks/tools/task-list-remove.ts +9 -0
- package/src/config/bundled-skills/tasks/tools/task-list-show.ts +9 -0
- package/src/config/bundled-skills/tasks/tools/task-list-update.ts +9 -0
- package/src/config/bundled-skills/tasks/tools/task-list.ts +9 -0
- package/src/config/bundled-skills/tasks/tools/task-run.ts +9 -0
- package/src/config/bundled-skills/tasks/tools/task-save.ts +9 -0
- package/src/config/bundled-skills/twitter/SKILL.md +134 -0
- package/src/config/bundled-skills/watcher/SKILL.md +27 -0
- package/src/config/bundled-skills/watcher/TOOLS.json +147 -0
- package/src/config/bundled-skills/watcher/tools/watcher-create.ts +9 -0
- package/src/config/bundled-skills/watcher/tools/watcher-delete.ts +9 -0
- package/src/config/bundled-skills/watcher/tools/watcher-digest.ts +9 -0
- package/src/config/bundled-skills/watcher/tools/watcher-list.ts +9 -0
- package/src/config/bundled-skills/watcher/tools/watcher-update.ts +9 -0
- package/src/config/defaults.ts +34 -0
- package/src/config/loader.ts +4 -1
- package/src/config/schema.ts +165 -1
- package/src/config/system-prompt.ts +61 -16
- package/src/config/templates/IDENTITY.md +7 -0
- package/src/config/types.ts +4 -0
- package/src/config/vellum-skills/telegram-setup/SKILL.md +1 -5
- package/src/contacts/contact-store.ts +4 -4
- package/src/daemon/assistant-attachments.ts +10 -0
- package/src/daemon/classifier.ts +3 -1
- package/src/daemon/computer-use-session.ts +3 -1
- package/src/daemon/date-context.ts +136 -0
- package/src/daemon/handlers/apps.ts +16 -1
- package/src/daemon/handlers/browser.ts +54 -0
- package/src/daemon/handlers/computer-use.ts +7 -1
- package/src/daemon/handlers/config.ts +205 -5
- package/src/daemon/handlers/diagnostics.ts +5 -1
- package/src/daemon/handlers/documents.ts +18 -29
- package/src/daemon/handlers/home-base.ts +5 -1
- package/src/daemon/handlers/index.ts +40 -277
- package/src/daemon/handlers/misc.ts +9 -1
- package/src/daemon/handlers/publish.ts +6 -1
- package/src/daemon/handlers/sessions.ts +65 -12
- package/src/daemon/handlers/shared.ts +36 -1
- package/src/daemon/handlers/signing.ts +37 -0
- package/src/daemon/handlers/skills.ts +20 -6
- package/src/daemon/handlers/subagents.ts +8 -3
- package/src/daemon/handlers/twitter-auth.ts +169 -0
- package/src/daemon/handlers/work-items.ts +384 -68
- package/src/daemon/ipc-contract-inventory.json +32 -4
- package/src/daemon/ipc-contract.ts +156 -37
- package/src/daemon/ipc-protocol.ts +7 -2
- package/src/daemon/lifecycle.ts +21 -0
- package/src/daemon/main.ts +10 -4
- package/src/daemon/ride-shotgun-handler.ts +75 -10
- package/src/daemon/server.ts +143 -26
- package/src/daemon/session-agent-loop.ts +922 -0
- package/src/daemon/session-attachments.ts +28 -5
- package/src/daemon/session-conflict-gate.ts +18 -109
- package/src/daemon/session-error.ts +24 -3
- package/src/daemon/session-lifecycle.ts +147 -0
- package/src/daemon/session-media-retry.ts +147 -0
- package/src/daemon/session-messaging.ts +145 -0
- package/src/daemon/session-notifiers.ts +164 -0
- package/src/daemon/session-process.ts +2 -2
- package/src/daemon/session-queue-manager.ts +1 -0
- package/src/daemon/session-runtime-assembly.ts +52 -0
- package/src/daemon/session-skill-tools.ts +124 -5
- package/src/daemon/session-slash.ts +3 -0
- package/src/daemon/session-surfaces.ts +77 -2
- package/src/daemon/session-tool-setup.ts +216 -2
- package/src/daemon/session-usage.ts +0 -2
- package/src/daemon/session.ts +114 -1404
- package/src/daemon/video-thumbnail.ts +60 -0
- package/src/doordash/client.ts +121 -27
- package/src/doordash/queries.ts +1 -2
- package/src/export/formatter.ts +3 -1
- package/src/followups/followup-store.ts +4 -2
- package/src/followups/types.ts +6 -0
- package/src/hooks/templates.ts +1 -1
- package/src/index.ts +32 -1153
- package/src/memory/attachments-store.ts +28 -83
- package/src/memory/channel-delivery-store.ts +7 -21
- package/src/memory/clarification-resolver.ts +6 -5
- package/src/memory/conflict-intent.ts +114 -0
- package/src/memory/contradiction-checker.ts +3 -2
- package/src/memory/conversation-key-store.ts +10 -29
- package/src/memory/conversation-store.ts +2 -1
- package/src/memory/db.ts +96 -2
- package/src/memory/entity-extractor.ts +6 -3
- package/src/memory/items-extractor.ts +5 -4
- package/src/memory/job-handlers/conflict.ts +23 -1
- package/src/memory/jobs-store.ts +3 -2
- package/src/memory/llm-usage-store.ts +1 -2
- package/src/memory/runs-store.ts +1 -2
- package/src/memory/schema.ts +23 -2
- package/src/messaging/style-analyzer.ts +3 -2
- package/src/messaging/thread-summarizer.ts +8 -12
- package/src/messaging/triage-engine.ts +4 -2
- package/src/providers/openrouter/client.ts +20 -0
- package/src/providers/registry.ts +8 -0
- package/src/runtime/gateway-client.ts +36 -0
- package/src/runtime/http-server.ts +166 -22
- package/src/runtime/routes/attachment-routes.ts +2 -3
- package/src/runtime/routes/call-routes.ts +140 -0
- package/src/runtime/routes/channel-routes.ts +125 -88
- package/src/runtime/routes/conversation-routes.ts +5 -5
- package/src/runtime/routes/run-routes.ts +2 -2
- package/src/runtime/run-orchestrator.ts +9 -3
- package/src/schedule/recurrence-engine.ts +138 -0
- package/src/schedule/recurrence-types.ts +67 -0
- package/src/schedule/schedule-store.ts +102 -57
- package/src/schedule/scheduler.ts +9 -6
- package/src/security/oauth2.ts +29 -4
- package/src/security/secret-allowlist.ts +46 -0
- package/src/skills/clawhub.ts +1 -1
- package/src/subagent/manager.ts +40 -8
- package/src/swarm/backend-claude-code.ts +64 -9
- package/src/swarm/worker-prompts.ts +2 -1
- package/src/tasks/SPEC.md +34 -28
- package/src/tasks/ephemeral-permissions.ts +16 -7
- package/src/tasks/task-compiler.ts +5 -4
- package/src/tasks/task-runner.ts +10 -5
- package/src/tasks/task-scheduler.ts +1 -1
- package/src/tasks/tool-sanitizer.ts +36 -0
- package/src/tools/assets/search.ts +4 -4
- package/src/tools/browser/api-map.ts +293 -0
- package/src/tools/browser/auto-navigate.ts +270 -0
- package/src/tools/browser/browser-execution.ts +2 -1
- package/src/tools/browser/browser-manager.ts +2 -2
- package/src/tools/browser/network-recorder.ts +5 -4
- package/src/tools/browser/x-auto-navigate.ts +207 -0
- package/src/tools/calls/call-end.ts +17 -67
- package/src/tools/calls/call-start.ts +24 -85
- package/src/tools/calls/call-status.ts +35 -51
- package/src/tools/claude-code/claude-code.ts +207 -11
- package/src/tools/contacts/contact-merge.ts +46 -78
- package/src/tools/contacts/contact-search.ts +35 -79
- package/src/tools/contacts/contact-upsert.ts +35 -108
- package/src/tools/credentials/vault.ts +20 -4
- package/src/tools/document/document-tool.ts +71 -144
- package/src/tools/executor.ts +129 -10
- package/src/tools/followups/followup_create.ts +46 -88
- package/src/tools/followups/followup_list.ts +34 -74
- package/src/tools/followups/followup_resolve.ts +31 -66
- package/src/tools/host-terminal/cli-discover.ts +2 -1
- package/src/tools/host-terminal/host-shell.ts +10 -0
- package/src/tools/memory/handlers.ts +5 -4
- package/src/tools/network/__tests__/web-search.test.ts +427 -0
- package/src/tools/network/script-proxy/__tests__/logging.test.ts +248 -0
- package/src/tools/network/script-proxy/__tests__/policy.test.ts +234 -0
- package/src/tools/network/script-proxy/__tests__/router.test.ts +76 -0
- package/src/tools/network/web-fetch.ts +18 -6
- package/src/tools/playbooks/index.ts +4 -5
- package/src/tools/playbooks/playbook-create.ts +3 -47
- package/src/tools/playbooks/playbook-delete.ts +1 -25
- package/src/tools/playbooks/playbook-list.ts +1 -28
- package/src/tools/playbooks/playbook-update.ts +3 -51
- package/src/tools/reminder/reminder.ts +5 -78
- package/src/tools/schedule/create.ts +69 -74
- package/src/tools/schedule/delete.ts +21 -47
- package/src/tools/schedule/list.ts +55 -74
- package/src/tools/schedule/update.ts +77 -84
- package/src/tools/subagent/abort.ts +29 -58
- package/src/tools/subagent/message.ts +30 -63
- package/src/tools/subagent/read.ts +53 -84
- package/src/tools/subagent/spawn.ts +43 -82
- package/src/tools/subagent/status.ts +42 -71
- package/src/tools/swarm/delegate.ts +2 -1
- package/src/tools/tasks/index.ts +8 -8
- package/src/tools/tasks/task-delete.ts +60 -88
- package/src/tools/tasks/task-list.ts +31 -52
- package/src/tools/tasks/task-run.ts +72 -108
- package/src/tools/tasks/task-save.ts +33 -65
- package/src/tools/tasks/work-item-enqueue.ts +183 -215
- package/src/tools/tasks/work-item-list.ts +33 -63
- package/src/tools/tasks/work-item-remove.ts +45 -97
- package/src/tools/tasks/work-item-update.ts +91 -163
- package/src/tools/terminal/backends/native.ts +3 -1
- package/src/tools/tool-manifest.ts +0 -62
- package/src/tools/types.ts +6 -0
- package/src/tools/ui-surface/definitions.ts +3 -1
- package/src/tools/watch/screen-watch.ts +3 -1
- package/src/tools/watcher/create.ts +52 -98
- package/src/tools/watcher/delete.ts +20 -46
- package/src/tools/watcher/digest.ts +36 -70
- package/src/tools/watcher/list.ts +49 -79
- package/src/tools/watcher/update.ts +45 -91
- package/src/twitter/client.ts +690 -0
- package/src/twitter/session.ts +91 -0
- package/src/usage/types.ts +0 -1
- package/src/util/truncate.ts +6 -0
- package/src/watcher/providers/slack.ts +2 -1
- package/src/watcher/watcher-store.ts +3 -2
- package/src/work-items/work-item-store.ts +27 -2
- package/src/workspace/commit-message-enrichment-service.ts +31 -7
- package/src/workspace/git-service.ts +87 -22
- package/src/workspace/provider-commit-message-generator.ts +269 -0
- package/src/workspace/turn-commit.ts +62 -3
- package/src/tools/contacts/index.ts +0 -4
- package/src/tools/document/index.ts +0 -5
- package/src/tools/followups/index.ts +0 -3
- package/src/tools/subagent/index.ts +0 -5
- /package/src/__tests__/{memory-context-benchmark.test.ts → memory-context-benchmark.benchmark.test.ts} +0 -0
|
@@ -98,8 +98,4 @@ Summarize what was done:
|
|
|
98
98
|
- Bot commands registered: /new
|
|
99
99
|
- Credentials stored securely in the vault
|
|
100
100
|
|
|
101
|
-
|
|
102
|
-
- `TELEGRAM_BOT_TOKEN` — the bot token
|
|
103
|
-
- `TELEGRAM_WEBHOOK_SECRET` — the generated secret
|
|
104
|
-
|
|
105
|
-
The values are stored in the credential vault and can be retrieved for gateway configuration.
|
|
101
|
+
The gateway automatically detects credentials from the vault and will begin accepting Telegram webhooks shortly. No manual environment variable configuration is needed.
|
|
@@ -70,7 +70,7 @@ export function upsertContact(params: {
|
|
|
70
70
|
responseExpectation?: string | null;
|
|
71
71
|
preferredTone?: string | null;
|
|
72
72
|
channels?: Array<{ type: string; address: string; isPrimary?: boolean }>;
|
|
73
|
-
}): ContactWithChannels {
|
|
73
|
+
}): ContactWithChannels & { created: boolean } {
|
|
74
74
|
const db = getDb();
|
|
75
75
|
const now = Date.now();
|
|
76
76
|
|
|
@@ -96,7 +96,7 @@ export function upsertContact(params: {
|
|
|
96
96
|
syncChannels(contactId, params.channels, now);
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
-
return getContact(contactId)
|
|
99
|
+
return { ...getContact(contactId)!, created: false };
|
|
100
100
|
}
|
|
101
101
|
}
|
|
102
102
|
|
|
@@ -124,7 +124,7 @@ export function upsertContact(params: {
|
|
|
124
124
|
.run();
|
|
125
125
|
|
|
126
126
|
syncChannels(contactId, params.channels, now);
|
|
127
|
-
return getContact(contactId)
|
|
127
|
+
return { ...getContact(contactId)!, created: false };
|
|
128
128
|
}
|
|
129
129
|
}
|
|
130
130
|
}
|
|
@@ -148,7 +148,7 @@ export function upsertContact(params: {
|
|
|
148
148
|
syncChannels(contactId, params.channels, now);
|
|
149
149
|
}
|
|
150
150
|
|
|
151
|
-
return getContact(contactId)
|
|
151
|
+
return { ...getContact(contactId)!, created: true };
|
|
152
152
|
}
|
|
153
153
|
|
|
154
154
|
/**
|
|
@@ -313,6 +313,16 @@ export function drainDirectiveDisplayBuffer(buffer: string): DirectiveDisplayDra
|
|
|
313
313
|
|
|
314
314
|
if (!isValidDirective) {
|
|
315
315
|
emitText += tag;
|
|
316
|
+
} else {
|
|
317
|
+
// Only trim the trailing newline when the directive occupied its own
|
|
318
|
+
// line (preceded by \n and followed by \n or \r\n). We intentionally
|
|
319
|
+
// do NOT trim when nextChar is undefined (end-of-buffer) because in
|
|
320
|
+
// streaming mode more data may arrive in the next chunk — eagerly
|
|
321
|
+
// trimming would merge words across the directive boundary.
|
|
322
|
+
const nextChar = buffer[end + 2];
|
|
323
|
+
if (emitText.endsWith('\n') && (nextChar === '\n' || nextChar === '\r')) {
|
|
324
|
+
emitText = emitText.slice(0, -1);
|
|
325
|
+
}
|
|
316
326
|
}
|
|
317
327
|
|
|
318
328
|
cursor = end + 2;
|
package/src/daemon/classifier.ts
CHANGED
|
@@ -4,6 +4,8 @@ import { getLogger } from '../util/logger.js';
|
|
|
4
4
|
|
|
5
5
|
const log = getLogger('classifier');
|
|
6
6
|
|
|
7
|
+
const CLASSIFICATION_TIMEOUT_MS = 5000;
|
|
8
|
+
|
|
7
9
|
export type InteractionType = 'computer_use' | 'text_qa';
|
|
8
10
|
|
|
9
11
|
/**
|
|
@@ -53,7 +55,7 @@ export async function classifyInteraction(task: string, source?: 'voice' | 'text
|
|
|
53
55
|
messages: [{ role: 'user' as const, content: task }],
|
|
54
56
|
}),
|
|
55
57
|
new Promise<never>((_, reject) =>
|
|
56
|
-
setTimeout(() => reject(new Error('Classification timeout')),
|
|
58
|
+
setTimeout(() => reject(new Error('Classification timeout')), CLASSIFICATION_TIMEOUT_MS),
|
|
57
59
|
),
|
|
58
60
|
]);
|
|
59
61
|
|
|
@@ -21,7 +21,7 @@ import { registerSkillTools } from '../tools/registry.js';
|
|
|
21
21
|
import { buildComputerUseSystemPrompt } from '../config/computer-use-prompt.js';
|
|
22
22
|
import { getSandboxWorkingDir } from '../util/platform.js';
|
|
23
23
|
import { getConfig } from '../config/loader.js';
|
|
24
|
-
import { projectSkillTools, resetSkillToolProjection } from './session-skill-tools.js';
|
|
24
|
+
import { projectSkillTools, resetSkillToolProjection, type SkillProjectionCache } from './session-skill-tools.js';
|
|
25
25
|
import { getLogger } from '../util/logger.js';
|
|
26
26
|
|
|
27
27
|
const log = getLogger('computer-use-session');
|
|
@@ -60,6 +60,7 @@ export class ComputerUseSession {
|
|
|
60
60
|
private readonly onTerminal?: (sessionId: string) => void;
|
|
61
61
|
private readonly preactivatedSkillIds: string[];
|
|
62
62
|
private readonly skillProjectionState = new Map<string, string>();
|
|
63
|
+
private readonly skillProjectionCache: SkillProjectionCache = {};
|
|
63
64
|
|
|
64
65
|
private state: SessionState = 'idle';
|
|
65
66
|
private stepCount = 0;
|
|
@@ -235,6 +236,7 @@ export class ComputerUseSession {
|
|
|
235
236
|
const projection = projectSkillTools([], {
|
|
236
237
|
preactivatedSkillIds: this.preactivatedSkillIds,
|
|
237
238
|
previouslyActiveSkillIds: this.skillProjectionState,
|
|
239
|
+
cache: this.skillProjectionCache,
|
|
238
240
|
});
|
|
239
241
|
|
|
240
242
|
if (projection.toolDefinitions.length === 0) {
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Temporal context formatter for future weekday/weekend grounding.
|
|
3
|
+
*
|
|
4
|
+
* Produces a compact, deterministic payload describing the current date,
|
|
5
|
+
* upcoming weekend/work-week windows, and a short horizon of labelled
|
|
6
|
+
* future dates. Intended for runtime injection into the model context.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export interface TemporalContextOptions {
|
|
10
|
+
/** Override current time (epoch ms) for deterministic tests. */
|
|
11
|
+
nowMs?: number;
|
|
12
|
+
/** IANA timezone (e.g. "America/New_York"). Defaults to host timezone. */
|
|
13
|
+
timeZone?: string;
|
|
14
|
+
/** Number of future days to list (default 14, hard-capped at 14). */
|
|
15
|
+
horizonDays?: number;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const MAX_OUTPUT_CHARS = 1500;
|
|
19
|
+
const MAX_HORIZON_ENTRIES = 14;
|
|
20
|
+
|
|
21
|
+
const WEEKDAY_NAMES = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'] as const;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Get the local date parts for a given instant in the specified timezone.
|
|
25
|
+
*/
|
|
26
|
+
function localDateParts(date: Date, timeZone: string): { year: number; month: number; day: number; weekday: number } {
|
|
27
|
+
const fmt = new Intl.DateTimeFormat('en-US', {
|
|
28
|
+
timeZone,
|
|
29
|
+
year: 'numeric',
|
|
30
|
+
month: '2-digit',
|
|
31
|
+
day: '2-digit',
|
|
32
|
+
weekday: 'short',
|
|
33
|
+
});
|
|
34
|
+
const parts = fmt.formatToParts(date);
|
|
35
|
+
const get = (t: string) => parts.find((p) => p.type === t)?.value ?? '';
|
|
36
|
+
// Weekday as 0-6 (Sun-Sat)
|
|
37
|
+
const weekdayShort = get('weekday');
|
|
38
|
+
const weekdayMap: Record<string, number> = { Sun: 0, Mon: 1, Tue: 2, Wed: 3, Thu: 4, Fri: 5, Sat: 6 };
|
|
39
|
+
return {
|
|
40
|
+
year: parseInt(get('year'), 10),
|
|
41
|
+
month: parseInt(get('month'), 10),
|
|
42
|
+
day: parseInt(get('day'), 10),
|
|
43
|
+
weekday: weekdayMap[weekdayShort] ?? 0,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Format a Date as YYYY-MM-DD in the given timezone.
|
|
49
|
+
*/
|
|
50
|
+
function formatLocalDate(date: Date, timeZone: string): string {
|
|
51
|
+
const p = localDateParts(date, timeZone);
|
|
52
|
+
return `${p.year}-${String(p.month).padStart(2, '0')}-${String(p.day).padStart(2, '0')}`;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Advance a date by `days` calendar days in the given timezone.
|
|
57
|
+
*
|
|
58
|
+
* Computes the local date, adds days to the day component, then anchors
|
|
59
|
+
* the result at noon local time to avoid DST-transition edge cases.
|
|
60
|
+
*/
|
|
61
|
+
function addDays(date: Date, days: number, timeZone: string): Date {
|
|
62
|
+
const parts = localDateParts(date, timeZone);
|
|
63
|
+
// Use Date.UTC for calendar overflow (e.g. Jan 32 → Feb 1).
|
|
64
|
+
const ref = new Date(Date.UTC(parts.year, parts.month - 1, parts.day + days));
|
|
65
|
+
const tY = ref.getUTCFullYear();
|
|
66
|
+
const tM = ref.getUTCMonth() + 1;
|
|
67
|
+
const tD = ref.getUTCDate();
|
|
68
|
+
// Noon UTC covers UTC-12 through ~UTC+11. For far-east timezones
|
|
69
|
+
// (UTC+12/+13/+14) noon UTC is already the next local day, so fall
|
|
70
|
+
// back to midnight UTC which resolves correctly there.
|
|
71
|
+
const noonUTC = new Date(Date.UTC(tY, tM - 1, tD, 12, 0, 0));
|
|
72
|
+
const r = localDateParts(noonUTC, timeZone);
|
|
73
|
+
if (r.year === tY && r.month === tM && r.day === tD) {
|
|
74
|
+
return noonUTC;
|
|
75
|
+
}
|
|
76
|
+
return new Date(Date.UTC(tY, tM - 1, tD, 0, 0, 0));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Build a compact temporal context string for model injection.
|
|
81
|
+
*
|
|
82
|
+
* Output is hard-capped at {@link MAX_OUTPUT_CHARS} characters and
|
|
83
|
+
* {@link MAX_HORIZON_ENTRIES} horizon entries.
|
|
84
|
+
*/
|
|
85
|
+
export function buildTemporalContext(options: TemporalContextOptions = {}): string {
|
|
86
|
+
const now = new Date(options.nowMs ?? Date.now());
|
|
87
|
+
const timeZone = options.timeZone ?? Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
88
|
+
const horizonDays = Math.min(options.horizonDays ?? MAX_HORIZON_ENTRIES, MAX_HORIZON_ENTRIES);
|
|
89
|
+
|
|
90
|
+
const todayParts = localDateParts(now, timeZone);
|
|
91
|
+
const todayStr = formatLocalDate(now, timeZone);
|
|
92
|
+
const todayWeekday = WEEKDAY_NAMES[todayParts.weekday];
|
|
93
|
+
|
|
94
|
+
// ── Next weekend (Saturday-Sunday) ──
|
|
95
|
+
const daysUntilSaturday = (6 - todayParts.weekday + 7) % 7 || 7;
|
|
96
|
+
const nextSaturday = addDays(now, daysUntilSaturday, timeZone);
|
|
97
|
+
const nextSunday = addDays(now, daysUntilSaturday + 1, timeZone);
|
|
98
|
+
|
|
99
|
+
// ── Next work week (Monday-Friday) ──
|
|
100
|
+
const daysUntilMonday = (1 - todayParts.weekday + 7) % 7 || 7;
|
|
101
|
+
const nextMonday = addDays(now, daysUntilMonday, timeZone);
|
|
102
|
+
const nextFriday = addDays(now, daysUntilMonday + 4, timeZone);
|
|
103
|
+
|
|
104
|
+
// ── Horizon list ──
|
|
105
|
+
const horizonLines: string[] = [];
|
|
106
|
+
for (let i = 1; i <= horizonDays; i++) {
|
|
107
|
+
const futureDate = addDays(now, i, timeZone);
|
|
108
|
+
const futureParts = localDateParts(futureDate, timeZone);
|
|
109
|
+
const label = WEEKDAY_NAMES[futureParts.weekday];
|
|
110
|
+
horizonLines.push(` ${formatLocalDate(futureDate, timeZone)} ${label}`);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const lines = [
|
|
114
|
+
`<temporal_context>`,
|
|
115
|
+
`Today: ${todayStr} (${todayWeekday})`,
|
|
116
|
+
`Timezone: ${timeZone}`,
|
|
117
|
+
``,
|
|
118
|
+
`Week definitions: work week = Monday–Friday, weekend = Saturday–Sunday`,
|
|
119
|
+
``,
|
|
120
|
+
`Next weekend: ${formatLocalDate(nextSaturday, timeZone)} – ${formatLocalDate(nextSunday, timeZone)}`,
|
|
121
|
+
`Next work week: ${formatLocalDate(nextMonday, timeZone)} – ${formatLocalDate(nextFriday, timeZone)}`,
|
|
122
|
+
``,
|
|
123
|
+
`Upcoming dates:`,
|
|
124
|
+
...horizonLines,
|
|
125
|
+
`</temporal_context>`,
|
|
126
|
+
];
|
|
127
|
+
|
|
128
|
+
let output = lines.join('\n');
|
|
129
|
+
|
|
130
|
+
// Hard cap: truncate if somehow over budget (shouldn't happen with 14 entries).
|
|
131
|
+
if (output.length > MAX_OUTPUT_CHARS) {
|
|
132
|
+
output = output.slice(0, MAX_OUTPUT_CHARS - 25) + '\n</temporal_context>';
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return output;
|
|
136
|
+
}
|
|
@@ -18,7 +18,7 @@ import type {
|
|
|
18
18
|
AppUpdatePreviewRequest,
|
|
19
19
|
UiSurfaceShow,
|
|
20
20
|
} from '../ipc-protocol.js';
|
|
21
|
-
import { log, compareSemver, createSigningCallback, type HandlerContext } from './shared.js';
|
|
21
|
+
import { log, compareSemver, createSigningCallback, defineHandlers, type HandlerContext } from './shared.js';
|
|
22
22
|
|
|
23
23
|
export function handleAppDataRequest(
|
|
24
24
|
msg: AppDataRequest,
|
|
@@ -444,3 +444,18 @@ export function handleGalleryInstall(
|
|
|
444
444
|
});
|
|
445
445
|
}
|
|
446
446
|
}
|
|
447
|
+
|
|
448
|
+
export const appHandlers = defineHandlers({
|
|
449
|
+
app_data_request: handleAppDataRequest,
|
|
450
|
+
app_open_request: handleAppOpenRequest,
|
|
451
|
+
app_update_preview: handleAppUpdatePreview,
|
|
452
|
+
app_preview_request: handleAppPreview,
|
|
453
|
+
apps_list: (_msg, socket, ctx) => handleAppsList(socket, ctx),
|
|
454
|
+
shared_apps_list: (_msg, socket, ctx) => handleSharedAppsList(socket, ctx),
|
|
455
|
+
shared_app_delete: handleSharedAppDelete,
|
|
456
|
+
fork_shared_app: handleForkSharedApp,
|
|
457
|
+
share_app_cloud: handleShareAppCloud,
|
|
458
|
+
bundle_app: handleBundleApp,
|
|
459
|
+
gallery_list: (_msg, socket, ctx) => handleGalleryList(socket, ctx),
|
|
460
|
+
gallery_install: handleGalleryInstall,
|
|
461
|
+
});
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { browserManager } from '../../tools/browser/browser-manager.js';
|
|
2
|
+
import { log, defineHandlers } from './shared.js';
|
|
3
|
+
|
|
4
|
+
export const browserHandlers = defineHandlers({
|
|
5
|
+
browser_cdp_response: (msg) => {
|
|
6
|
+
browserManager.resolveCDPResponse(msg.sessionId, msg.success, msg.declined);
|
|
7
|
+
},
|
|
8
|
+
|
|
9
|
+
browser_user_click: async (msg) => {
|
|
10
|
+
try {
|
|
11
|
+
const page = await browserManager.getOrCreateSessionPage(msg.sessionId);
|
|
12
|
+
const viewport = await page.evaluate('(() => ({ vw: window.innerWidth, vh: window.innerHeight }))()') as { vw: number; vh: number };
|
|
13
|
+
const scale = Math.min(1280 / viewport.vw, 960 / viewport.vh);
|
|
14
|
+
const pageX = msg.x / scale;
|
|
15
|
+
const pageY = msg.y / scale;
|
|
16
|
+
const options: Record<string, unknown> = {};
|
|
17
|
+
if (msg.button === 'right') options.button = 'right';
|
|
18
|
+
if (msg.doubleClick) options.clickCount = 2;
|
|
19
|
+
await page.mouse.click(pageX, pageY, options);
|
|
20
|
+
} catch (err) {
|
|
21
|
+
log.warn({ err, sessionId: msg.sessionId }, 'Failed to forward user click');
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
|
|
25
|
+
browser_user_scroll: async (msg) => {
|
|
26
|
+
try {
|
|
27
|
+
const page = await browserManager.getOrCreateSessionPage(msg.sessionId);
|
|
28
|
+
await page.mouse.wheel(msg.deltaX, msg.deltaY);
|
|
29
|
+
} catch (err) {
|
|
30
|
+
log.warn({ err, sessionId: msg.sessionId }, 'Failed to forward user scroll');
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
|
|
34
|
+
browser_user_keypress: async (msg) => {
|
|
35
|
+
try {
|
|
36
|
+
const page = await browserManager.getOrCreateSessionPage(msg.sessionId);
|
|
37
|
+
const combo = msg.modifiers?.length ? [...msg.modifiers, msg.key].join('+') : msg.key;
|
|
38
|
+
await page.keyboard.press(combo);
|
|
39
|
+
} catch (err) {
|
|
40
|
+
log.warn({ err, sessionId: msg.sessionId }, 'Failed to forward user keypress');
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
|
|
44
|
+
browser_interactive_mode: (msg, socket, ctx) => {
|
|
45
|
+
log.info({ sessionId: msg.sessionId, enabled: msg.enabled }, 'Interactive mode toggled');
|
|
46
|
+
browserManager.setInteractiveMode(msg.sessionId, msg.enabled);
|
|
47
|
+
ctx.send(socket, {
|
|
48
|
+
type: 'browser_interactive_mode_changed',
|
|
49
|
+
sessionId: msg.sessionId,
|
|
50
|
+
surfaceId: msg.surfaceId,
|
|
51
|
+
enabled: msg.enabled,
|
|
52
|
+
});
|
|
53
|
+
},
|
|
54
|
+
});
|
|
@@ -10,7 +10,7 @@ import type {
|
|
|
10
10
|
CuObservation,
|
|
11
11
|
ServerMessage,
|
|
12
12
|
} from '../ipc-protocol.js';
|
|
13
|
-
import { log, type HandlerContext } from './shared.js';
|
|
13
|
+
import { log, defineHandlers, type HandlerContext } from './shared.js';
|
|
14
14
|
|
|
15
15
|
const cuObservationSequenceBySession = new Map<string, number>();
|
|
16
16
|
|
|
@@ -179,3 +179,9 @@ export async function handleCuObservation(
|
|
|
179
179
|
log.error({ err, sessionId: msg.sessionId }, 'Error handling CU observation');
|
|
180
180
|
});
|
|
181
181
|
}
|
|
182
|
+
|
|
183
|
+
export const computerUseHandlers = defineHandlers({
|
|
184
|
+
cu_session_create: handleCuSessionCreate,
|
|
185
|
+
cu_session_abort: handleCuSessionAbort,
|
|
186
|
+
cu_observation: handleCuObservation,
|
|
187
|
+
});
|
|
@@ -5,7 +5,7 @@ import { addRule, removeRule, updateRule, getAllRules, acceptStarterBundle } fro
|
|
|
5
5
|
import { listSchedules, updateSchedule, deleteSchedule, describeCronExpression } from '../../schedule/schedule-store.js';
|
|
6
6
|
import { listReminders, cancelReminder } from '../../tools/reminder/reminder-store.js';
|
|
7
7
|
import { getSecureKey, setSecureKey, deleteSecureKey } from '../../security/secure-keys.js';
|
|
8
|
-
import { upsertCredentialMetadata, deleteCredentialMetadata } from '../../tools/credentials/metadata-store.js';
|
|
8
|
+
import { upsertCredentialMetadata, deleteCredentialMetadata, getCredentialMetadata } from '../../tools/credentials/metadata-store.js';
|
|
9
9
|
import { postToSlackWebhook } from '../../slack/slack-webhook.js';
|
|
10
10
|
import { getApp } from '../../memory/app-store.js';
|
|
11
11
|
import type {
|
|
@@ -19,9 +19,11 @@ import type {
|
|
|
19
19
|
ReminderCancel,
|
|
20
20
|
ShareToSlackRequest,
|
|
21
21
|
SlackWebhookConfigRequest,
|
|
22
|
+
TwilioWebhookConfigRequest,
|
|
22
23
|
VercelApiConfigRequest,
|
|
24
|
+
TwitterIntegrationConfigRequest,
|
|
23
25
|
} from '../ipc-protocol.js';
|
|
24
|
-
import { log, type HandlerContext } from './shared.js';
|
|
26
|
+
import { log, CONFIG_RELOAD_DEBOUNCE_MS, defineHandlers, type HandlerContext } from './shared.js';
|
|
25
27
|
import { MODEL_TO_PROVIDER } from '../session-slash.js';
|
|
26
28
|
|
|
27
29
|
export function handleModelGet(socket: net.Socket, ctx: HandlerContext): void {
|
|
@@ -96,7 +98,7 @@ export function handleModelSet(
|
|
|
96
98
|
}
|
|
97
99
|
const existingSuppressTimer = ctx.debounceTimers.get('__suppress_reset__');
|
|
98
100
|
if (existingSuppressTimer) clearTimeout(existingSuppressTimer);
|
|
99
|
-
const resetTimer = setTimeout(() => { ctx.setSuppressConfigReload(false); },
|
|
101
|
+
const resetTimer = setTimeout(() => { ctx.setSuppressConfigReload(false); }, CONFIG_RELOAD_DEBOUNCE_MS);
|
|
100
102
|
ctx.debounceTimers.set('__suppress_reset__', resetTimer);
|
|
101
103
|
|
|
102
104
|
// Re-initialize provider with the new model so LLM calls use it
|
|
@@ -145,7 +147,7 @@ export function handleImageGenModelSet(
|
|
|
145
147
|
}
|
|
146
148
|
const existingSuppressTimer = ctx.debounceTimers.get('__suppress_reset__');
|
|
147
149
|
if (existingSuppressTimer) clearTimeout(existingSuppressTimer);
|
|
148
|
-
const resetTimer = setTimeout(() => { ctx.setSuppressConfigReload(false); },
|
|
150
|
+
const resetTimer = setTimeout(() => { ctx.setSuppressConfigReload(false); }, CONFIG_RELOAD_DEBOUNCE_MS);
|
|
149
151
|
ctx.debounceTimers.set('__suppress_reset__', resetTimer);
|
|
150
152
|
|
|
151
153
|
ctx.updateConfigFingerprint();
|
|
@@ -237,13 +239,15 @@ export function handleSchedulesList(socket: net.Socket, ctx: HandlerContext): vo
|
|
|
237
239
|
id: j.id,
|
|
238
240
|
name: j.name,
|
|
239
241
|
enabled: j.enabled,
|
|
242
|
+
syntax: j.syntax,
|
|
243
|
+
expression: j.expression,
|
|
240
244
|
cronExpression: j.cronExpression,
|
|
241
245
|
timezone: j.timezone,
|
|
242
246
|
message: j.message,
|
|
243
247
|
nextRunAt: j.nextRunAt,
|
|
244
248
|
lastRunAt: j.lastRunAt,
|
|
245
249
|
lastStatus: j.lastStatus,
|
|
246
|
-
description: describeCronExpression(j.cronExpression),
|
|
250
|
+
description: j.syntax === 'cron' ? describeCronExpression(j.cronExpression) : j.expression,
|
|
247
251
|
})),
|
|
248
252
|
});
|
|
249
253
|
}
|
|
@@ -393,6 +397,41 @@ export function handleSlackWebhookConfig(
|
|
|
393
397
|
}
|
|
394
398
|
}
|
|
395
399
|
|
|
400
|
+
export function handleTwilioWebhookConfig(
|
|
401
|
+
msg: TwilioWebhookConfigRequest,
|
|
402
|
+
socket: net.Socket,
|
|
403
|
+
ctx: HandlerContext,
|
|
404
|
+
): void {
|
|
405
|
+
try {
|
|
406
|
+
if (msg.action === 'get') {
|
|
407
|
+
const raw = loadRawConfig();
|
|
408
|
+
const webhookBaseUrl = (raw?.calls as Record<string, unknown>)?.webhookBaseUrl as string ?? '';
|
|
409
|
+
ctx.send(socket, { type: 'twilio_webhook_config_response', webhookBaseUrl, success: true });
|
|
410
|
+
} else if (msg.action === 'set') {
|
|
411
|
+
const value = (msg.webhookBaseUrl ?? '').trim().replace(/\/+$/, '');
|
|
412
|
+
const raw = loadRawConfig();
|
|
413
|
+
const calls = (raw?.calls ?? {}) as Record<string, unknown>;
|
|
414
|
+
calls.webhookBaseUrl = value || undefined;
|
|
415
|
+
const wasSuppressed = ctx.suppressConfigReload;
|
|
416
|
+
ctx.setSuppressConfigReload(true);
|
|
417
|
+
try {
|
|
418
|
+
saveRawConfig({ ...raw, calls });
|
|
419
|
+
} catch (err) {
|
|
420
|
+
ctx.setSuppressConfigReload(wasSuppressed);
|
|
421
|
+
throw err;
|
|
422
|
+
}
|
|
423
|
+
const existingSuppressTimer = ctx.debounceTimers.get('__suppress_reset__');
|
|
424
|
+
if (existingSuppressTimer) clearTimeout(existingSuppressTimer);
|
|
425
|
+
const resetTimer = setTimeout(() => { ctx.setSuppressConfigReload(false); }, CONFIG_RELOAD_DEBOUNCE_MS);
|
|
426
|
+
ctx.debounceTimers.set('__suppress_reset__', resetTimer);
|
|
427
|
+
ctx.send(socket, { type: 'twilio_webhook_config_response', webhookBaseUrl: value, success: true });
|
|
428
|
+
}
|
|
429
|
+
} catch (err) {
|
|
430
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
431
|
+
ctx.send(socket, { type: 'twilio_webhook_config_response', webhookBaseUrl: '', success: false, error: message });
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
396
435
|
export function handleVercelApiConfig(
|
|
397
436
|
msg: VercelApiConfigRequest,
|
|
398
437
|
socket: net.Socket,
|
|
@@ -455,6 +494,145 @@ export function handleVercelApiConfig(
|
|
|
455
494
|
}
|
|
456
495
|
}
|
|
457
496
|
|
|
497
|
+
export function handleTwitterIntegrationConfig(
|
|
498
|
+
msg: TwitterIntegrationConfigRequest,
|
|
499
|
+
socket: net.Socket,
|
|
500
|
+
ctx: HandlerContext,
|
|
501
|
+
): void {
|
|
502
|
+
try {
|
|
503
|
+
if (msg.action === 'get') {
|
|
504
|
+
const raw = loadRawConfig();
|
|
505
|
+
const mode = (raw.twitterIntegrationMode as 'local_byo' | 'managed' | undefined) ?? 'local_byo';
|
|
506
|
+
const localClientConfigured = !!getSecureKey('credential:integration:twitter:oauth_client_id');
|
|
507
|
+
const connected = !!getSecureKey('credential:integration:twitter:access_token');
|
|
508
|
+
const meta = getCredentialMetadata('integration:twitter', 'access_token');
|
|
509
|
+
ctx.send(socket, {
|
|
510
|
+
type: 'twitter_integration_config_response',
|
|
511
|
+
success: true,
|
|
512
|
+
mode,
|
|
513
|
+
managedAvailable: false,
|
|
514
|
+
localClientConfigured,
|
|
515
|
+
connected,
|
|
516
|
+
accountInfo: meta?.accountInfo ?? undefined,
|
|
517
|
+
});
|
|
518
|
+
} else if (msg.action === 'set_mode') {
|
|
519
|
+
const raw = loadRawConfig();
|
|
520
|
+
raw.twitterIntegrationMode = msg.mode ?? 'local_byo';
|
|
521
|
+
saveRawConfig(raw);
|
|
522
|
+
ctx.send(socket, {
|
|
523
|
+
type: 'twitter_integration_config_response',
|
|
524
|
+
success: true,
|
|
525
|
+
mode: msg.mode ?? 'local_byo',
|
|
526
|
+
managedAvailable: false,
|
|
527
|
+
localClientConfigured: !!getSecureKey('credential:integration:twitter:oauth_client_id'),
|
|
528
|
+
connected: !!getSecureKey('credential:integration:twitter:access_token'),
|
|
529
|
+
});
|
|
530
|
+
} else if (msg.action === 'set_local_client') {
|
|
531
|
+
if (!msg.clientId) {
|
|
532
|
+
ctx.send(socket, {
|
|
533
|
+
type: 'twitter_integration_config_response',
|
|
534
|
+
success: false,
|
|
535
|
+
managedAvailable: false,
|
|
536
|
+
localClientConfigured: false,
|
|
537
|
+
connected: false,
|
|
538
|
+
error: 'clientId is required for set_local_client action',
|
|
539
|
+
});
|
|
540
|
+
return;
|
|
541
|
+
}
|
|
542
|
+
const previousClientId = getSecureKey('credential:integration:twitter:oauth_client_id');
|
|
543
|
+
const storedId = setSecureKey('credential:integration:twitter:oauth_client_id', msg.clientId);
|
|
544
|
+
if (!storedId) {
|
|
545
|
+
ctx.send(socket, {
|
|
546
|
+
type: 'twitter_integration_config_response',
|
|
547
|
+
success: false,
|
|
548
|
+
managedAvailable: false,
|
|
549
|
+
localClientConfigured: false,
|
|
550
|
+
connected: false,
|
|
551
|
+
error: 'Failed to store client ID in secure storage',
|
|
552
|
+
});
|
|
553
|
+
return;
|
|
554
|
+
}
|
|
555
|
+
if (msg.clientSecret) {
|
|
556
|
+
const storedSecret = setSecureKey('credential:integration:twitter:oauth_client_secret', msg.clientSecret);
|
|
557
|
+
if (!storedSecret) {
|
|
558
|
+
// Roll back the client ID to its previous value to avoid inconsistent OAuth state
|
|
559
|
+
if (previousClientId) {
|
|
560
|
+
setSecureKey('credential:integration:twitter:oauth_client_id', previousClientId);
|
|
561
|
+
} else {
|
|
562
|
+
deleteSecureKey('credential:integration:twitter:oauth_client_id');
|
|
563
|
+
}
|
|
564
|
+
ctx.send(socket, {
|
|
565
|
+
type: 'twitter_integration_config_response',
|
|
566
|
+
success: false,
|
|
567
|
+
managedAvailable: false,
|
|
568
|
+
localClientConfigured: false,
|
|
569
|
+
connected: false,
|
|
570
|
+
error: 'Failed to store client secret in secure storage',
|
|
571
|
+
});
|
|
572
|
+
return;
|
|
573
|
+
}
|
|
574
|
+
} else {
|
|
575
|
+
// Clear any stale secret when updating client without a secret (e.g. switching to PKCE)
|
|
576
|
+
deleteSecureKey('credential:integration:twitter:oauth_client_secret');
|
|
577
|
+
}
|
|
578
|
+
ctx.send(socket, {
|
|
579
|
+
type: 'twitter_integration_config_response',
|
|
580
|
+
success: true,
|
|
581
|
+
managedAvailable: false,
|
|
582
|
+
localClientConfigured: true,
|
|
583
|
+
connected: !!getSecureKey('credential:integration:twitter:access_token'),
|
|
584
|
+
});
|
|
585
|
+
} else if (msg.action === 'clear_local_client') {
|
|
586
|
+
// If connected, disconnect first
|
|
587
|
+
if (getSecureKey('credential:integration:twitter:access_token')) {
|
|
588
|
+
deleteSecureKey('credential:integration:twitter:access_token');
|
|
589
|
+
deleteSecureKey('credential:integration:twitter:refresh_token');
|
|
590
|
+
deleteCredentialMetadata('integration:twitter', 'access_token');
|
|
591
|
+
}
|
|
592
|
+
deleteSecureKey('credential:integration:twitter:oauth_client_id');
|
|
593
|
+
deleteSecureKey('credential:integration:twitter:oauth_client_secret');
|
|
594
|
+
ctx.send(socket, {
|
|
595
|
+
type: 'twitter_integration_config_response',
|
|
596
|
+
success: true,
|
|
597
|
+
managedAvailable: false,
|
|
598
|
+
localClientConfigured: false,
|
|
599
|
+
connected: false,
|
|
600
|
+
});
|
|
601
|
+
} else if (msg.action === 'disconnect') {
|
|
602
|
+
deleteSecureKey('credential:integration:twitter:access_token');
|
|
603
|
+
deleteSecureKey('credential:integration:twitter:refresh_token');
|
|
604
|
+
deleteCredentialMetadata('integration:twitter', 'access_token');
|
|
605
|
+
ctx.send(socket, {
|
|
606
|
+
type: 'twitter_integration_config_response',
|
|
607
|
+
success: true,
|
|
608
|
+
managedAvailable: false,
|
|
609
|
+
localClientConfigured: !!getSecureKey('credential:integration:twitter:oauth_client_id'),
|
|
610
|
+
connected: false,
|
|
611
|
+
});
|
|
612
|
+
} else {
|
|
613
|
+
ctx.send(socket, {
|
|
614
|
+
type: 'twitter_integration_config_response',
|
|
615
|
+
success: false,
|
|
616
|
+
managedAvailable: false,
|
|
617
|
+
localClientConfigured: false,
|
|
618
|
+
connected: false,
|
|
619
|
+
error: `Unknown action: ${String((msg as unknown as Record<string, unknown>).action)}`,
|
|
620
|
+
});
|
|
621
|
+
}
|
|
622
|
+
} catch (err) {
|
|
623
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
624
|
+
log.error({ err }, 'Failed to handle Twitter integration config');
|
|
625
|
+
ctx.send(socket, {
|
|
626
|
+
type: 'twitter_integration_config_response',
|
|
627
|
+
success: false,
|
|
628
|
+
managedAvailable: false,
|
|
629
|
+
localClientConfigured: false,
|
|
630
|
+
connected: false,
|
|
631
|
+
error: message,
|
|
632
|
+
});
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
|
|
458
636
|
export function handleEnvVarsRequest(socket: net.Socket, ctx: HandlerContext): void {
|
|
459
637
|
const vars: Record<string, string> = {};
|
|
460
638
|
for (const [key, value] of Object.entries(process.env)) {
|
|
@@ -462,3 +640,25 @@ export function handleEnvVarsRequest(socket: net.Socket, ctx: HandlerContext): v
|
|
|
462
640
|
}
|
|
463
641
|
ctx.send(socket, { type: 'env_vars_response', vars });
|
|
464
642
|
}
|
|
643
|
+
|
|
644
|
+
export const configHandlers = defineHandlers({
|
|
645
|
+
model_get: (_msg, socket, ctx) => handleModelGet(socket, ctx),
|
|
646
|
+
model_set: handleModelSet,
|
|
647
|
+
image_gen_model_set: handleImageGenModelSet,
|
|
648
|
+
add_trust_rule: handleAddTrustRule,
|
|
649
|
+
trust_rules_list: (_msg, socket, ctx) => handleTrustRulesList(socket, ctx),
|
|
650
|
+
remove_trust_rule: handleRemoveTrustRule,
|
|
651
|
+
update_trust_rule: handleUpdateTrustRule,
|
|
652
|
+
accept_starter_bundle: (_msg, socket, ctx) => handleAcceptStarterBundle(socket, ctx),
|
|
653
|
+
schedules_list: (_msg, socket, ctx) => handleSchedulesList(socket, ctx),
|
|
654
|
+
schedule_toggle: handleScheduleToggle,
|
|
655
|
+
schedule_remove: handleScheduleRemove,
|
|
656
|
+
reminders_list: (_msg, socket, ctx) => handleRemindersList(socket, ctx),
|
|
657
|
+
reminder_cancel: handleReminderCancel,
|
|
658
|
+
share_to_slack: handleShareToSlack,
|
|
659
|
+
slack_webhook_config: handleSlackWebhookConfig,
|
|
660
|
+
twilio_webhook_config: handleTwilioWebhookConfig,
|
|
661
|
+
vercel_api_config: handleVercelApiConfig,
|
|
662
|
+
twitter_integration_config: handleTwitterIntegrationConfig,
|
|
663
|
+
env_vars_request: (_msg, socket, ctx) => handleEnvVarsRequest(socket, ctx),
|
|
664
|
+
});
|
|
@@ -9,7 +9,7 @@ import archiver from 'archiver';
|
|
|
9
9
|
import { getDb } from '../../memory/db.js';
|
|
10
10
|
import { messages, toolInvocations, llmUsageEvents, llmRequestLogs } from '../../memory/schema.js';
|
|
11
11
|
import type { DiagnosticsExportRequest } from '../ipc-protocol.js';
|
|
12
|
-
import { log, type HandlerContext } from './shared.js';
|
|
12
|
+
import { log, defineHandlers, type HandlerContext } from './shared.js';
|
|
13
13
|
|
|
14
14
|
const MAX_CONTENT_LENGTH = 500;
|
|
15
15
|
|
|
@@ -332,3 +332,7 @@ export async function handleDiagnosticsExport(
|
|
|
332
332
|
});
|
|
333
333
|
}
|
|
334
334
|
}
|
|
335
|
+
|
|
336
|
+
export const diagnosticsHandlers = defineHandlers({
|
|
337
|
+
diagnostics_export_request: handleDiagnosticsExport,
|
|
338
|
+
});
|