@vellumai/assistant 0.4.49 → 0.4.51
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 +24 -33
- package/README.md +3 -3
- package/docs/architecture/integrations.md +2 -2
- package/docs/architecture/keychain-broker.md +6 -6
- package/docs/architecture/memory.md +180 -119
- package/knip.json +32 -0
- package/package.json +3 -2
- package/src/__tests__/agent-loop.test.ts +3 -1
- package/src/__tests__/anthropic-provider.test.ts +114 -23
- package/src/__tests__/approval-cascade.test.ts +1 -15
- package/src/__tests__/approval-routes-http.test.ts +2 -0
- package/src/__tests__/assistant-feature-flag-guard.test.ts +0 -23
- package/src/__tests__/btw-routes.test.ts +61 -5
- package/src/__tests__/canonical-guardian-store.test.ts +95 -0
- package/src/__tests__/checker.test.ts +13 -0
- package/src/__tests__/config-schema.test.ts +1 -68
- package/src/__tests__/config-watcher.test.ts +8 -0
- package/src/__tests__/context-memory-e2e.test.ts +11 -100
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +8 -0
- package/src/__tests__/conversation-routes-slash-commands.test.ts +1 -0
- package/src/__tests__/credential-security-e2e.test.ts +1 -0
- package/src/__tests__/credential-security-invariants.test.ts +8 -7
- package/src/__tests__/credential-vault-unit.test.ts +23 -18
- package/src/__tests__/credential-vault.test.ts +30 -18
- package/src/__tests__/credentials-cli.test.ts +257 -82
- package/src/__tests__/cu-unified-flow.test.ts +532 -0
- package/src/__tests__/date-context.test.ts +93 -77
- package/src/__tests__/deterministic-verification-control-plane.test.ts +64 -0
- package/src/__tests__/guardian-routing-invariants.test.ts +93 -0
- package/src/__tests__/history-repair.test.ts +245 -0
- package/src/__tests__/host-cu-proxy.test.ts +165 -3
- package/src/__tests__/http-user-message-parity.test.ts +1 -0
- package/src/__tests__/inbound-invite-redemption.test.ts +36 -7
- package/src/__tests__/integration-status.test.ts +31 -30
- package/src/__tests__/invite-redemption-service.test.ts +166 -13
- package/src/__tests__/invite-routes-http.test.ts +166 -5
- package/src/__tests__/keychain-broker-client.test.ts +4 -4
- package/src/__tests__/list-messages-attachments.test.ts +193 -0
- package/src/__tests__/memory-context-benchmark.benchmark.test.ts +56 -18
- package/src/__tests__/memory-lifecycle-e2e.test.ts +244 -387
- package/src/__tests__/memory-recall-quality.test.ts +244 -407
- package/src/__tests__/memory-regressions.experimental.test.ts +126 -101
- package/src/__tests__/memory-regressions.test.ts +477 -2841
- package/src/__tests__/memory-retrieval.benchmark.test.ts +33 -150
- package/src/__tests__/memory-upsert-concurrency.test.ts +5 -244
- package/src/__tests__/mime-builder.test.ts +28 -0
- package/src/__tests__/native-web-search.test.ts +1 -0
- package/src/__tests__/oauth-cli.test.ts +824 -31
- package/src/__tests__/oauth-provider-profiles.test.ts +1 -1
- package/src/__tests__/oauth-store.test.ts +363 -17
- package/src/__tests__/qdrant-collection-migration.test.ts +53 -8
- package/src/__tests__/registry.test.ts +0 -1
- package/src/__tests__/relay-server.test.ts +55 -1
- package/src/__tests__/schedule-tools.test.ts +32 -0
- package/src/__tests__/script-proxy-certs.test.ts +1 -1
- package/src/__tests__/secret-onetime-send.test.ts +1 -0
- package/src/__tests__/secret-routes-managed-proxy.test.ts +183 -0
- package/src/__tests__/secure-keys.test.ts +78 -18
- package/src/__tests__/send-endpoint-busy.test.ts +3 -0
- package/src/__tests__/server-history-render.test.ts +2 -2
- package/src/__tests__/session-abort-tool-results.test.ts +1 -14
- package/src/__tests__/session-agent-loop-overflow.test.ts +1583 -0
- package/src/__tests__/session-agent-loop.test.ts +19 -15
- package/src/__tests__/session-confirmation-signals.test.ts +1 -15
- package/src/__tests__/session-error.test.ts +124 -2
- package/src/__tests__/session-history-web-search.test.ts +918 -0
- package/src/__tests__/session-pre-run-repair.test.ts +1 -14
- package/src/__tests__/session-provider-retry-repair.test.ts +25 -28
- package/src/__tests__/session-queue.test.ts +37 -27
- package/src/__tests__/session-runtime-assembly.test.ts +54 -0
- package/src/__tests__/session-slash-known.test.ts +1 -15
- package/src/__tests__/session-slash-queue.test.ts +1 -15
- package/src/__tests__/session-slash-unknown.test.ts +1 -15
- package/src/__tests__/session-workspace-cache-state.test.ts +3 -33
- package/src/__tests__/session-workspace-injection.test.ts +3 -37
- package/src/__tests__/session-workspace-tool-tracking.test.ts +3 -37
- package/src/__tests__/skills-install-extract.test.ts +93 -0
- package/src/__tests__/skills.test.ts +2 -2
- package/src/__tests__/skillssh-registry.test.ts +451 -0
- package/src/__tests__/slack-channel-config.test.ts +10 -8
- package/src/__tests__/trust-store.test.ts +15 -0
- package/src/__tests__/twilio-config.test.ts +11 -10
- package/src/__tests__/twilio-provider.test.ts +9 -4
- package/src/__tests__/voice-invite-redemption.test.ts +85 -5
- package/src/agent/ax-tree-compaction.test.ts +51 -0
- package/src/agent/loop.ts +39 -12
- package/src/approvals/AGENTS.md +1 -1
- package/src/approvals/guardian-request-resolvers.ts +14 -2
- package/src/bundler/compiler-tools.ts +66 -2
- package/src/calls/call-domain.ts +134 -3
- package/src/calls/call-store.ts +6 -0
- package/src/calls/relay-server.ts +44 -6
- package/src/calls/relay-setup-router.ts +17 -1
- package/src/calls/twilio-config.ts +5 -4
- package/src/calls/twilio-provider.ts +14 -9
- package/src/calls/twilio-rest.ts +10 -7
- package/src/calls/types.ts +3 -1
- package/src/cli/commands/config.ts +14 -9
- package/src/cli/commands/contacts.ts +3 -0
- package/src/cli/commands/credentials.ts +170 -174
- package/src/cli/commands/doctor.ts +11 -8
- package/src/cli/commands/keys.ts +9 -9
- package/src/cli/commands/mcp.ts +46 -59
- package/src/cli/commands/memory.ts +16 -165
- package/src/cli/commands/oauth/apps.ts +68 -10
- package/src/cli/commands/oauth/connections.ts +475 -105
- package/src/cli/commands/oauth/index.ts +3 -3
- package/src/cli/commands/oauth/providers.ts +18 -4
- package/src/cli/commands/sessions.ts +5 -2
- package/src/cli/commands/skills.ts +173 -1
- package/src/cli/http-client.ts +0 -20
- package/src/cli/main-screen.tsx +2 -2
- package/src/cli/program.ts +5 -6
- package/src/cli.ts +20 -22
- package/src/config/__tests__/feature-flag-registry-bundled.test.ts +39 -0
- package/src/config/bundled-skills/computer-use/TOOLS.json +1 -1
- package/src/config/bundled-skills/computer-use/tools/computer-use-observe.ts +12 -0
- package/src/config/bundled-skills/contacts/SKILL.md +35 -11
- package/src/config/bundled-skills/contacts/tools/google-contacts.ts +1 -1
- package/src/config/bundled-skills/gmail/SKILL.md +1 -1
- package/src/config/bundled-skills/gmail/TOOLS.json +52 -0
- package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +13 -3
- package/src/config/bundled-skills/gmail/tools/gmail-attachments.ts +9 -2
- package/src/config/bundled-skills/gmail/tools/gmail-draft.ts +5 -1
- package/src/config/bundled-skills/gmail/tools/gmail-filters.ts +5 -1
- package/src/config/bundled-skills/gmail/tools/gmail-follow-up.ts +5 -1
- package/src/config/bundled-skills/gmail/tools/gmail-forward.ts +5 -1
- package/src/config/bundled-skills/gmail/tools/gmail-label.ts +9 -2
- package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +5 -1
- package/src/config/bundled-skills/gmail/tools/gmail-send-draft.ts +5 -1
- package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +5 -1
- package/src/config/bundled-skills/gmail/tools/gmail-trash.ts +5 -1
- package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +5 -1
- package/src/config/bundled-skills/gmail/tools/gmail-vacation.ts +5 -1
- package/src/config/bundled-skills/google-calendar/TOOLS.json +20 -0
- package/src/config/bundled-skills/google-calendar/tools/calendar-check-availability.ts +2 -1
- package/src/config/bundled-skills/google-calendar/tools/calendar-create-event.ts +2 -1
- package/src/config/bundled-skills/google-calendar/tools/calendar-get-event.ts +2 -1
- package/src/config/bundled-skills/google-calendar/tools/calendar-list-events.ts +2 -1
- package/src/config/bundled-skills/google-calendar/tools/calendar-rsvp.ts +2 -1
- package/src/config/bundled-skills/google-calendar/tools/shared.ts +8 -2
- package/src/config/bundled-skills/messaging/SKILL.md +1 -1
- package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +2 -2
- package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +2 -2
- package/src/config/bundled-skills/messaging/tools/messaging-auth-test.ts +2 -2
- package/src/config/bundled-skills/messaging/tools/messaging-list-conversations.ts +2 -2
- package/src/config/bundled-skills/messaging/tools/messaging-mark-read.ts +2 -2
- package/src/config/bundled-skills/messaging/tools/messaging-read.ts +2 -2
- package/src/config/bundled-skills/messaging/tools/messaging-search.ts +2 -2
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +2 -2
- package/src/config/bundled-skills/messaging/tools/messaging-sender-digest.ts +2 -2
- package/src/config/bundled-skills/messaging/tools/shared.ts +7 -5
- package/src/config/bundled-skills/slack/tools/shared.ts +1 -1
- package/src/config/bundled-skills/slack/tools/slack-add-reaction.ts +1 -1
- package/src/config/bundled-skills/slack/tools/slack-channel-details.ts +1 -1
- package/src/config/bundled-skills/slack/tools/slack-delete-message.ts +1 -1
- package/src/config/bundled-skills/slack/tools/slack-edit-message.ts +1 -1
- package/src/config/bundled-skills/slack/tools/slack-leave-channel.ts +1 -1
- package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +1 -1
- package/src/config/bundled-tool-registry.ts +2 -5
- package/src/config/loader.ts +6 -42
- package/src/config/schema.ts +1 -12
- package/src/config/schemas/memory-lifecycle.ts +0 -9
- package/src/config/schemas/memory-processing.ts +0 -180
- package/src/config/schemas/memory-retrieval.ts +32 -104
- package/src/config/schemas/memory.ts +0 -10
- package/src/config/types.ts +0 -4
- package/src/contacts/contact-store.ts +39 -2
- package/src/contacts/contacts-write.ts +9 -0
- package/src/context/window-manager.ts +4 -1
- package/src/daemon/config-watcher.ts +55 -2
- package/src/daemon/daemon-control.ts +1 -1
- package/src/daemon/date-context.ts +114 -31
- package/src/daemon/handlers/config-ingress.ts +2 -2
- package/src/daemon/handlers/config-slack-channel.ts +59 -39
- package/src/daemon/handlers/config-telegram.ts +23 -14
- package/src/daemon/handlers/session-history.ts +1 -358
- package/src/daemon/handlers/sessions.ts +18 -13
- package/src/daemon/handlers/shared.ts +3 -17
- package/src/daemon/handlers/skills.ts +20 -1
- package/src/daemon/history-repair.ts +72 -8
- package/src/daemon/host-cu-proxy.ts +55 -26
- package/src/daemon/lifecycle.ts +39 -4
- package/src/daemon/mcp-reload-service.ts +2 -2
- package/src/daemon/message-types/computer-use.ts +1 -12
- package/src/daemon/message-types/memory.ts +4 -16
- package/src/daemon/message-types/messages.ts +1 -0
- package/src/daemon/message-types/sessions.ts +4 -42
- package/src/daemon/server.ts +6 -1
- package/src/daemon/session-agent-loop-handlers.ts +38 -0
- package/src/daemon/session-agent-loop.ts +334 -48
- package/src/daemon/session-error.ts +89 -6
- package/src/daemon/session-history.ts +17 -7
- package/src/daemon/session-media-retry.ts +6 -2
- package/src/daemon/session-memory.ts +69 -149
- package/src/daemon/session-process.ts +10 -1
- package/src/daemon/session-runtime-assembly.ts +49 -19
- package/src/daemon/session-slash.ts +3 -5
- package/src/daemon/session-surfaces.ts +4 -1
- package/src/daemon/session-tool-setup.ts +7 -1
- package/src/daemon/session.ts +12 -2
- package/src/email/providers/index.ts +2 -2
- package/src/instrument.ts +61 -1
- package/src/media/avatar-router.ts +1 -1
- package/src/memory/admin.ts +2 -191
- package/src/memory/canonical-guardian-store.ts +38 -2
- package/src/memory/conversation-crud.ts +0 -33
- package/src/memory/conversation-queries.ts +25 -83
- package/src/memory/db-init.ts +32 -0
- package/src/memory/embedding-backend.ts +84 -8
- package/src/memory/embedding-types.ts +9 -1
- package/src/memory/indexer.ts +7 -46
- package/src/memory/invite-store.ts +19 -0
- package/src/memory/items-extractor.ts +274 -76
- package/src/memory/job-handlers/backfill.ts +2 -127
- package/src/memory/job-handlers/cleanup.ts +2 -16
- package/src/memory/job-handlers/extraction.ts +2 -138
- package/src/memory/job-handlers/index-maintenance.ts +1 -6
- package/src/memory/job-handlers/summarization.ts +3 -148
- package/src/memory/job-utils.ts +21 -59
- package/src/memory/jobs-store.ts +1 -159
- package/src/memory/jobs-worker.ts +9 -52
- package/src/memory/migrations/104-core-indexes.ts +3 -3
- package/src/memory/migrations/149-oauth-tables.ts +2 -0
- package/src/memory/migrations/150-oauth-apps-client-secret-path.ts +98 -0
- package/src/memory/migrations/151-oauth-providers-ping-url.ts +11 -0
- package/src/memory/migrations/152-memory-item-supersession.ts +44 -0
- package/src/memory/migrations/153-drop-entity-tables.ts +15 -0
- package/src/memory/migrations/154-drop-fts.ts +20 -0
- package/src/memory/migrations/155-drop-conflicts.ts +7 -0
- package/src/memory/migrations/156-call-session-invite-metadata.ts +24 -0
- package/src/memory/migrations/157-invite-contact-id.ts +104 -0
- package/src/memory/migrations/index.ts +8 -0
- package/src/memory/migrations/registry.ts +6 -0
- package/src/memory/qdrant-client.ts +148 -51
- package/src/memory/raw-query.ts +1 -1
- package/src/memory/retriever.test.ts +294 -273
- package/src/memory/retriever.ts +421 -645
- package/src/memory/schema/calls.ts +2 -0
- package/src/memory/schema/contacts.ts +1 -0
- package/src/memory/schema/memory-core.ts +3 -48
- package/src/memory/schema/oauth.ts +2 -0
- package/src/memory/search/formatting.ts +263 -176
- package/src/memory/search/lexical.ts +1 -254
- package/src/memory/search/ranking.ts +0 -455
- package/src/memory/search/semantic.ts +100 -14
- package/src/memory/search/staleness.ts +47 -0
- package/src/memory/search/tier-classifier.ts +21 -0
- package/src/memory/search/types.ts +15 -77
- package/src/memory/task-memory-cleanup.ts +4 -6
- package/src/messaging/provider.ts +1 -1
- package/src/messaging/providers/gmail/adapter.ts +1 -1
- package/src/messaging/providers/gmail/mime-builder.ts +17 -7
- package/src/messaging/providers/telegram-bot/adapter.ts +17 -8
- package/src/messaging/providers/whatsapp/adapter.ts +13 -9
- package/src/messaging/registry.ts +9 -5
- package/src/oauth/byo-connection.test.ts +40 -25
- package/src/oauth/connect-orchestrator.ts +4 -10
- package/src/oauth/connection-resolver.ts +20 -6
- package/src/oauth/manual-token-connection.ts +5 -5
- package/src/oauth/oauth-store.ts +183 -31
- package/src/oauth/platform-connection.test.ts +1 -1
- package/src/oauth/provider-behaviors.ts +503 -4
- package/src/oauth/seed-providers.ts +214 -8
- package/src/oauth/token-persistence.ts +31 -16
- package/src/permissions/defaults.ts +1 -0
- package/src/permissions/trust-store.ts +23 -1
- package/src/playbooks/playbook-compiler.ts +1 -1
- package/src/prompts/system-prompt.ts +18 -2
- package/src/providers/anthropic/client.ts +56 -126
- package/src/providers/types.ts +7 -1
- package/src/runtime/AGENTS.md +9 -0
- package/src/runtime/auth/route-policy.ts +6 -3
- package/src/runtime/channel-readiness-service.ts +48 -40
- package/src/runtime/guardian-reply-router.ts +24 -22
- package/src/runtime/http-server.ts +2 -2
- package/src/runtime/http-types.ts +2 -0
- package/src/runtime/invite-redemption-service.ts +72 -12
- package/src/runtime/invite-service.ts +43 -0
- package/src/runtime/middleware/twilio-validation.ts +1 -1
- package/src/runtime/pending-interactions.ts +2 -2
- package/src/runtime/routes/brain-graph-routes.ts +10 -90
- package/src/runtime/routes/btw-routes.ts +10 -5
- package/src/runtime/routes/conversation-routes.ts +56 -11
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +21 -12
- package/src/runtime/routes/integrations/slack/channel.ts +2 -2
- package/src/runtime/routes/integrations/telegram.ts +2 -2
- package/src/runtime/routes/integrations/twilio.ts +17 -17
- package/src/runtime/routes/invite-routes.ts +29 -4
- package/src/runtime/routes/memory-item-routes.test.ts +754 -0
- package/src/runtime/routes/memory-item-routes.ts +503 -0
- package/src/runtime/routes/secret-routes.ts +17 -0
- package/src/runtime/routes/session-management-routes.ts +3 -3
- package/src/runtime/routes/settings-routes.ts +3 -3
- package/src/runtime/routes/trust-rules-routes.ts +14 -0
- package/src/runtime/routes/workspace-routes.ts +9 -4
- package/src/runtime/routes/workspace-utils.ts +8 -2
- package/src/schedule/integration-status.ts +26 -19
- package/src/security/keychain-broker-client.ts +17 -4
- package/src/security/oauth2.ts +6 -7
- package/src/security/secure-keys.ts +44 -19
- package/src/security/token-manager.ts +46 -39
- package/src/services/vercel-deploy.ts +0 -24
- package/src/signals/confirm.ts +78 -0
- package/src/signals/mcp-reload.ts +18 -0
- package/src/skills/catalog-install.ts +74 -18
- package/src/skills/skillssh-registry.ts +503 -0
- package/src/tools/assets/search.ts +5 -1
- package/src/tools/computer-use/definitions.ts +0 -10
- package/src/tools/computer-use/registry.ts +1 -1
- package/src/tools/credentials/vault.ts +22 -7
- package/src/tools/memory/definitions.ts +4 -13
- package/src/tools/memory/handlers.test.ts +83 -103
- package/src/tools/memory/handlers.ts +50 -85
- package/src/tools/network/script-proxy/session-manager.ts +8 -8
- package/src/tools/schedule/create.ts +10 -3
- package/src/tools/schedule/update.ts +8 -1
- package/src/tools/skills/load.ts +25 -2
- package/src/watcher/provider-types.ts +1 -1
- package/src/watcher/providers/github.ts +1 -1
- package/src/watcher/providers/gmail.ts +3 -3
- package/src/watcher/providers/google-calendar.ts +3 -3
- package/src/watcher/providers/linear.ts +1 -1
- package/src/__tests__/clarification-resolver.test.ts +0 -193
- package/src/__tests__/conflict-intent-tokenization.test.ts +0 -160
- package/src/__tests__/conflict-policy.test.ts +0 -269
- package/src/__tests__/conflict-store.test.ts +0 -372
- package/src/__tests__/contradiction-checker.test.ts +0 -361
- package/src/__tests__/entity-extractor.test.ts +0 -211
- package/src/__tests__/entity-search.test.ts +0 -1117
- package/src/__tests__/profile-compiler.test.ts +0 -392
- package/src/__tests__/session-conflict-gate.test.ts +0 -1228
- package/src/__tests__/session-profile-injection.test.ts +0 -557
- package/src/config/bundled-skills/knowledge-graph/SKILL.md +0 -25
- package/src/config/bundled-skills/knowledge-graph/TOOLS.json +0 -66
- package/src/config/bundled-skills/knowledge-graph/tools/graph-query.ts +0 -211
- package/src/daemon/session-conflict-gate.ts +0 -167
- package/src/daemon/session-dynamic-profile.ts +0 -77
- package/src/memory/clarification-resolver.ts +0 -417
- package/src/memory/conflict-intent.ts +0 -205
- package/src/memory/conflict-policy.ts +0 -127
- package/src/memory/conflict-store.ts +0 -410
- package/src/memory/contradiction-checker.ts +0 -508
- package/src/memory/entity-extractor.ts +0 -535
- package/src/memory/format-recall.ts +0 -47
- package/src/memory/fts-reconciler.ts +0 -165
- package/src/memory/job-handlers/conflict.ts +0 -200
- package/src/memory/profile-compiler.ts +0 -195
- package/src/memory/recall-cache.ts +0 -117
- package/src/memory/search/entity.ts +0 -535
- package/src/memory/search/query-expansion.test.ts +0 -70
- package/src/memory/search/query-expansion.ts +0 -118
- package/src/runtime/routes/mcp-routes.ts +0 -20
|
@@ -6,11 +6,17 @@ describe("HostCuProxy", () => {
|
|
|
6
6
|
let proxy: InstanceType<typeof HostCuProxy>;
|
|
7
7
|
let sentMessages: unknown[];
|
|
8
8
|
let sendToClient: (msg: unknown) => void;
|
|
9
|
+
let resolvedRequestIds: string[];
|
|
9
10
|
|
|
10
11
|
function setup(maxSteps?: number) {
|
|
11
12
|
sentMessages = [];
|
|
13
|
+
resolvedRequestIds = [];
|
|
12
14
|
sendToClient = (msg: unknown) => sentMessages.push(msg);
|
|
13
|
-
proxy = new HostCuProxy(
|
|
15
|
+
proxy = new HostCuProxy(
|
|
16
|
+
sendToClient as never,
|
|
17
|
+
(requestId: string) => resolvedRequestIds.push(requestId),
|
|
18
|
+
maxSteps,
|
|
19
|
+
);
|
|
14
20
|
}
|
|
15
21
|
|
|
16
22
|
afterEach(() => {
|
|
@@ -364,6 +370,60 @@ describe("HostCuProxy", () => {
|
|
|
364
370
|
);
|
|
365
371
|
});
|
|
366
372
|
|
|
373
|
+
test("does not emit spurious warning on first observation", async () => {
|
|
374
|
+
setup();
|
|
375
|
+
|
|
376
|
+
// First ever request — no previous AX tree exists
|
|
377
|
+
proxy.recordAction("computer_use_click", { element_id: 1 });
|
|
378
|
+
const p1 = proxy.request(
|
|
379
|
+
"computer_use_click",
|
|
380
|
+
{ element_id: 1 },
|
|
381
|
+
"session-1",
|
|
382
|
+
1,
|
|
383
|
+
);
|
|
384
|
+
const sent1 = sentMessages[0] as Record<string, unknown>;
|
|
385
|
+
proxy.resolve(sent1.requestId as string, {
|
|
386
|
+
axTree: "Button [1]",
|
|
387
|
+
// No axDiff on first observation — this is normal, not unchanged
|
|
388
|
+
});
|
|
389
|
+
const result1 = await p1;
|
|
390
|
+
expect(result1.content).not.toContain("NO VISIBLE EFFECT");
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
test("skips unchanged warning after computer_use_wait", async () => {
|
|
394
|
+
setup();
|
|
395
|
+
|
|
396
|
+
// Establish previous AX tree
|
|
397
|
+
const p1 = proxy.request(
|
|
398
|
+
"computer_use_click",
|
|
399
|
+
{ element_id: 1 },
|
|
400
|
+
"session-1",
|
|
401
|
+
1,
|
|
402
|
+
);
|
|
403
|
+
proxy.recordAction("computer_use_click", { element_id: 1 });
|
|
404
|
+
const sent1 = sentMessages[0] as Record<string, unknown>;
|
|
405
|
+
proxy.resolve(sent1.requestId as string, {
|
|
406
|
+
axTree: "Button [1]",
|
|
407
|
+
});
|
|
408
|
+
await p1;
|
|
409
|
+
|
|
410
|
+
// Wait action with unchanged screen — should NOT warn
|
|
411
|
+
const p2 = proxy.request(
|
|
412
|
+
"computer_use_wait",
|
|
413
|
+
{ duration_ms: 2000 },
|
|
414
|
+
"session-1",
|
|
415
|
+
2,
|
|
416
|
+
);
|
|
417
|
+
proxy.recordAction("computer_use_wait", { duration_ms: 2000 });
|
|
418
|
+
const sent2 = sentMessages[1] as Record<string, unknown>;
|
|
419
|
+
proxy.resolve(sent2.requestId as string, {
|
|
420
|
+
axTree: "Button [1]",
|
|
421
|
+
// No axDiff — screen unchanged, but that's expected after wait
|
|
422
|
+
});
|
|
423
|
+
const result2 = await p2;
|
|
424
|
+
expect(result2.content).not.toContain("NO VISIBLE EFFECT");
|
|
425
|
+
});
|
|
426
|
+
|
|
367
427
|
test("resets consecutive count when diff is present", async () => {
|
|
368
428
|
setup();
|
|
369
429
|
|
|
@@ -507,6 +567,35 @@ describe("HostCuProxy", () => {
|
|
|
507
567
|
expect(result.content).toMatch(/<\/ax-tree>$/m);
|
|
508
568
|
});
|
|
509
569
|
|
|
570
|
+
test("includes secondaryWindows after AX tree with cross-window note", () => {
|
|
571
|
+
setup();
|
|
572
|
+
|
|
573
|
+
const result = proxy.formatObservation({
|
|
574
|
+
axTree: "Button [1]\nLabel [2]",
|
|
575
|
+
secondaryWindows: "Safari — Window [10]\n Link [11]",
|
|
576
|
+
});
|
|
577
|
+
|
|
578
|
+
expect(result.content).toContain("Safari — Window [10]");
|
|
579
|
+
expect(result.content).toContain("Link [11]");
|
|
580
|
+
expect(result.content).toContain(
|
|
581
|
+
"Note: The element [ID]s above are from other windows",
|
|
582
|
+
);
|
|
583
|
+
// secondaryWindows should appear after the AX tree
|
|
584
|
+
const axTreeEnd = result.content.indexOf("</ax-tree>");
|
|
585
|
+
const secondaryIdx = result.content.indexOf("Safari — Window [10]");
|
|
586
|
+
expect(axTreeEnd).toBeLessThan(secondaryIdx);
|
|
587
|
+
});
|
|
588
|
+
|
|
589
|
+
test("omits secondaryWindows section when field is absent", () => {
|
|
590
|
+
setup();
|
|
591
|
+
|
|
592
|
+
const result = proxy.formatObservation({
|
|
593
|
+
axTree: "Button [1]",
|
|
594
|
+
});
|
|
595
|
+
|
|
596
|
+
expect(result.content).not.toContain("other windows");
|
|
597
|
+
});
|
|
598
|
+
|
|
510
599
|
test("includes diff when present", () => {
|
|
511
600
|
setup();
|
|
512
601
|
|
|
@@ -576,7 +665,7 @@ describe("HostCuProxy", () => {
|
|
|
576
665
|
// -------------------------------------------------------------------------
|
|
577
666
|
|
|
578
667
|
describe("dispose", () => {
|
|
579
|
-
test("rejects all pending requests", () => {
|
|
668
|
+
test("rejects all pending requests", async () => {
|
|
580
669
|
setup();
|
|
581
670
|
|
|
582
671
|
const resultPromise = proxy.request(
|
|
@@ -593,7 +682,80 @@ describe("HostCuProxy", () => {
|
|
|
593
682
|
proxy.dispose();
|
|
594
683
|
|
|
595
684
|
expect(proxy.hasPendingRequest(requestId)).toBe(false);
|
|
596
|
-
expect(resultPromise).rejects.toThrow("Host CU proxy disposed");
|
|
685
|
+
await expect(resultPromise).rejects.toThrow("Host CU proxy disposed");
|
|
686
|
+
});
|
|
687
|
+
});
|
|
688
|
+
|
|
689
|
+
// -------------------------------------------------------------------------
|
|
690
|
+
// onInternalResolve callback
|
|
691
|
+
// -------------------------------------------------------------------------
|
|
692
|
+
|
|
693
|
+
describe("onInternalResolve", () => {
|
|
694
|
+
test("calls onInternalResolve when abort signal fires", async () => {
|
|
695
|
+
setup();
|
|
696
|
+
|
|
697
|
+
const controller = new AbortController();
|
|
698
|
+
const resultPromise = proxy.request(
|
|
699
|
+
"computer_use_click",
|
|
700
|
+
{ element_id: 1 },
|
|
701
|
+
"session-1",
|
|
702
|
+
1,
|
|
703
|
+
undefined,
|
|
704
|
+
controller.signal,
|
|
705
|
+
);
|
|
706
|
+
|
|
707
|
+
const sent = sentMessages[0] as Record<string, unknown>;
|
|
708
|
+
const requestId = sent.requestId as string;
|
|
709
|
+
|
|
710
|
+
controller.abort();
|
|
711
|
+
|
|
712
|
+
await resultPromise;
|
|
713
|
+
expect(resolvedRequestIds).toContain(requestId);
|
|
714
|
+
});
|
|
715
|
+
|
|
716
|
+
test("calls onInternalResolve on dispose", async () => {
|
|
717
|
+
setup();
|
|
718
|
+
|
|
719
|
+
const resultPromise = proxy.request(
|
|
720
|
+
"computer_use_click",
|
|
721
|
+
{ element_id: 1 },
|
|
722
|
+
"session-1",
|
|
723
|
+
1,
|
|
724
|
+
);
|
|
725
|
+
|
|
726
|
+
const sent = sentMessages[0] as Record<string, unknown>;
|
|
727
|
+
const requestId = sent.requestId as string;
|
|
728
|
+
|
|
729
|
+
proxy.dispose();
|
|
730
|
+
|
|
731
|
+
// dispose rejects pending requests — catch to avoid unhandled rejection
|
|
732
|
+
await resultPromise.catch(() => {});
|
|
733
|
+
|
|
734
|
+
expect(resolvedRequestIds).toContain(requestId);
|
|
735
|
+
});
|
|
736
|
+
});
|
|
737
|
+
|
|
738
|
+
// -------------------------------------------------------------------------
|
|
739
|
+
// isAvailable
|
|
740
|
+
// -------------------------------------------------------------------------
|
|
741
|
+
|
|
742
|
+
describe("isAvailable", () => {
|
|
743
|
+
test("returns false by default", () => {
|
|
744
|
+
setup();
|
|
745
|
+
expect(proxy.isAvailable()).toBe(false);
|
|
746
|
+
});
|
|
747
|
+
|
|
748
|
+
test("returns true after updateSender with clientConnected=true", () => {
|
|
749
|
+
setup();
|
|
750
|
+
proxy.updateSender(sendToClient as never, true);
|
|
751
|
+
expect(proxy.isAvailable()).toBe(true);
|
|
752
|
+
});
|
|
753
|
+
|
|
754
|
+
test("returns false after updateSender with clientConnected=false", () => {
|
|
755
|
+
setup();
|
|
756
|
+
proxy.updateSender(sendToClient as never, true);
|
|
757
|
+
proxy.updateSender(sendToClient as never, false);
|
|
758
|
+
expect(proxy.isAvailable()).toBe(false);
|
|
597
759
|
});
|
|
598
760
|
});
|
|
599
761
|
|
|
@@ -170,6 +170,7 @@ function makeSession(overrides: Record<string, unknown> = {}) {
|
|
|
170
170
|
setHostBashProxy: () => {},
|
|
171
171
|
setHostFileProxy: () => {},
|
|
172
172
|
setHostCuProxy: () => {},
|
|
173
|
+
addPreactivatedSkillId: () => {},
|
|
173
174
|
emitConfirmationStateChanged: () => {},
|
|
174
175
|
emitActivityState: () => {},
|
|
175
176
|
setTurnChannelContext: () => {},
|
|
@@ -85,7 +85,10 @@ mock.module("../runtime/approval-message-composer.js", () => ({
|
|
|
85
85
|
composeApprovalMessageGenerative: async () => "mock generative message",
|
|
86
86
|
}));
|
|
87
87
|
|
|
88
|
-
import {
|
|
88
|
+
import {
|
|
89
|
+
findContactChannel,
|
|
90
|
+
upsertContact,
|
|
91
|
+
} from "../contacts/contact-store.js";
|
|
89
92
|
import { upsertContactChannel } from "../contacts/contacts-write.js";
|
|
90
93
|
import { getDb, initializeDb, resetDb } from "../memory/db.js";
|
|
91
94
|
import { createInvite, revokeInvite } from "../memory/invite-store.js";
|
|
@@ -106,6 +109,11 @@ afterAll(() => {
|
|
|
106
109
|
// Helpers
|
|
107
110
|
// ---------------------------------------------------------------------------
|
|
108
111
|
|
|
112
|
+
/** Create a throwaway contact and return its ID, for use as the invite's contactId. */
|
|
113
|
+
function createTargetContact(displayName = "Test Contact"): string {
|
|
114
|
+
return upsertContact({ displayName, role: "contact" }).id;
|
|
115
|
+
}
|
|
116
|
+
|
|
109
117
|
const TEST_BEARER_TOKEN = "test-token";
|
|
110
118
|
let msgCounter = 0;
|
|
111
119
|
|
|
@@ -178,6 +186,7 @@ describe("inbound invite redemption intercept", () => {
|
|
|
178
186
|
test("non-member with valid invite token becomes active member without guardian approval", async () => {
|
|
179
187
|
const { rawToken } = createInvite({
|
|
180
188
|
sourceChannel: "telegram",
|
|
189
|
+
contactId: createTargetContact(),
|
|
181
190
|
maxUses: 5,
|
|
182
191
|
});
|
|
183
192
|
|
|
@@ -231,6 +240,7 @@ describe("inbound invite redemption intercept", () => {
|
|
|
231
240
|
test("non-member with expired token gets appropriate message", async () => {
|
|
232
241
|
const { rawToken } = createInvite({
|
|
233
242
|
sourceChannel: "telegram",
|
|
243
|
+
contactId: createTargetContact(),
|
|
234
244
|
maxUses: 1,
|
|
235
245
|
expiresInMs: -1, // already expired
|
|
236
246
|
});
|
|
@@ -252,6 +262,7 @@ describe("inbound invite redemption intercept", () => {
|
|
|
252
262
|
test("non-member with revoked token gets refusal text", async () => {
|
|
253
263
|
const { rawToken, invite } = createInvite({
|
|
254
264
|
sourceChannel: "telegram",
|
|
265
|
+
contactId: createTargetContact(),
|
|
255
266
|
maxUses: 5,
|
|
256
267
|
});
|
|
257
268
|
revokeInvite(invite.id);
|
|
@@ -292,6 +303,7 @@ describe("inbound invite redemption intercept", () => {
|
|
|
292
303
|
test("duplicate Telegram webhook deliveries do not double-redeem", async () => {
|
|
293
304
|
const { rawToken } = createInvite({
|
|
294
305
|
sourceChannel: "telegram",
|
|
306
|
+
contactId: createTargetContact(),
|
|
295
307
|
maxUses: 5,
|
|
296
308
|
});
|
|
297
309
|
|
|
@@ -353,7 +365,11 @@ describe("inbound invite redemption intercept", () => {
|
|
|
353
365
|
|
|
354
366
|
test("channel mismatch returns appropriate message", async () => {
|
|
355
367
|
// Create an invite for voice, but try to redeem via Telegram
|
|
356
|
-
const { rawToken } = createInvite({
|
|
368
|
+
const { rawToken } = createInvite({
|
|
369
|
+
sourceChannel: "phone",
|
|
370
|
+
contactId: createTargetContact(),
|
|
371
|
+
maxUses: 5,
|
|
372
|
+
});
|
|
357
373
|
|
|
358
374
|
const req = buildInviteRequest(rawToken);
|
|
359
375
|
const resp = await handleChannelInbound(req, undefined, TEST_BEARER_TOKEN);
|
|
@@ -372,6 +388,7 @@ describe("inbound invite redemption intercept", () => {
|
|
|
372
388
|
test("already-active member with invite token gets acknowledgement", async () => {
|
|
373
389
|
const { rawToken } = createInvite({
|
|
374
390
|
sourceChannel: "telegram",
|
|
391
|
+
contactId: createTargetContact(),
|
|
375
392
|
maxUses: 5,
|
|
376
393
|
});
|
|
377
394
|
|
|
@@ -397,11 +414,8 @@ describe("inbound invite redemption intercept", () => {
|
|
|
397
414
|
});
|
|
398
415
|
|
|
399
416
|
test("reactivation via invite preserves existing guardian-managed member display name", async () => {
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
maxUses: 5,
|
|
403
|
-
});
|
|
404
|
-
|
|
417
|
+
// Pre-create a revoked member named "Jeff" — the invite should preserve
|
|
418
|
+
// that guardian-assigned name rather than overwriting with the Telegram name.
|
|
405
419
|
upsertContactChannel({
|
|
406
420
|
sourceChannel: "telegram",
|
|
407
421
|
externalUserId: "user-invite-123",
|
|
@@ -411,6 +425,21 @@ describe("inbound invite redemption intercept", () => {
|
|
|
411
425
|
displayName: "Jeff",
|
|
412
426
|
});
|
|
413
427
|
|
|
428
|
+
// Look up the contact that upsertContactChannel created so we can use
|
|
429
|
+
// its ID as the invite's contactId (satisfies the FK constraint).
|
|
430
|
+
const existing = findContactChannel({
|
|
431
|
+
channelType: "telegram",
|
|
432
|
+
externalUserId: "user-invite-123",
|
|
433
|
+
externalChatId: "chat-invite-test",
|
|
434
|
+
});
|
|
435
|
+
const targetContactId = existing!.contact.id;
|
|
436
|
+
|
|
437
|
+
const { rawToken } = createInvite({
|
|
438
|
+
sourceChannel: "telegram",
|
|
439
|
+
contactId: targetContactId,
|
|
440
|
+
maxUses: 5,
|
|
441
|
+
});
|
|
442
|
+
|
|
414
443
|
const req = buildInviteRequest(rawToken, {
|
|
415
444
|
actorDisplayName: "Noa Flaherty",
|
|
416
445
|
});
|
|
@@ -10,6 +10,7 @@ const connectedProviders = new Set<string>();
|
|
|
10
10
|
|
|
11
11
|
mock.module("../security/secure-keys.js", () => ({
|
|
12
12
|
getSecureKey: (account: string) => secureKeyValues.get(account),
|
|
13
|
+
getSecureKeyAsync: async (account: string) => secureKeyValues.get(account),
|
|
13
14
|
}));
|
|
14
15
|
|
|
15
16
|
mock.module("../config/loader.js", () => ({
|
|
@@ -45,8 +46,8 @@ describe("integration-status", () => {
|
|
|
45
46
|
});
|
|
46
47
|
|
|
47
48
|
describe("getIntegrationSummary", () => {
|
|
48
|
-
test("returns all disconnected when no keys are set", () => {
|
|
49
|
-
const summary = getIntegrationSummary();
|
|
49
|
+
test("returns all disconnected when no keys are set", async () => {
|
|
50
|
+
const summary = await getIntegrationSummary();
|
|
50
51
|
expect(summary).toEqual([
|
|
51
52
|
{ name: "Gmail", category: "email", connected: false },
|
|
52
53
|
{ name: "Slack", category: "messaging", connected: false },
|
|
@@ -55,25 +56,25 @@ describe("integration-status", () => {
|
|
|
55
56
|
]);
|
|
56
57
|
});
|
|
57
58
|
|
|
58
|
-
test("returns all connected when all keys are set", () => {
|
|
59
|
-
setOAuthConnected("integration:
|
|
59
|
+
test("returns all connected when all keys are set", async () => {
|
|
60
|
+
setOAuthConnected("integration:google");
|
|
60
61
|
setOAuthConnected("integration:slack");
|
|
61
62
|
mockTwilioAccountSid = "sid";
|
|
62
63
|
secureKeyValues.set(credentialKey("twilio", "auth_token"), "auth");
|
|
63
64
|
setOAuthConnected("telegram");
|
|
64
65
|
|
|
65
|
-
const summary = getIntegrationSummary();
|
|
66
|
+
const summary = await getIntegrationSummary();
|
|
66
67
|
expect(summary.every((s: { connected: boolean }) => s.connected)).toBe(
|
|
67
68
|
true,
|
|
68
69
|
);
|
|
69
70
|
});
|
|
70
71
|
|
|
71
|
-
test("returns mixed status", () => {
|
|
72
|
+
test("returns mixed status", async () => {
|
|
72
73
|
mockTwilioAccountSid = "sid";
|
|
73
74
|
secureKeyValues.set(credentialKey("twilio", "auth_token"), "auth");
|
|
74
75
|
setOAuthConnected("telegram");
|
|
75
76
|
|
|
76
|
-
const summary = getIntegrationSummary();
|
|
77
|
+
const summary = await getIntegrationSummary();
|
|
77
78
|
const connected = summary.filter(
|
|
78
79
|
(s: { connected: boolean }) => s.connected,
|
|
79
80
|
);
|
|
@@ -91,17 +92,17 @@ describe("integration-status", () => {
|
|
|
91
92
|
]);
|
|
92
93
|
});
|
|
93
94
|
|
|
94
|
-
test("Twilio disconnected when only account_sid is set (missing auth_token)", () => {
|
|
95
|
+
test("Twilio disconnected when only account_sid is set (missing auth_token)", async () => {
|
|
95
96
|
mockTwilioAccountSid = "sid";
|
|
96
97
|
|
|
97
|
-
const summary = getIntegrationSummary();
|
|
98
|
+
const summary = await getIntegrationSummary();
|
|
98
99
|
const twilio = summary.find((s: { name: string }) => s.name === "Twilio");
|
|
99
100
|
expect(twilio?.connected).toBe(false);
|
|
100
101
|
});
|
|
101
102
|
|
|
102
|
-
test("Telegram disconnected when no connection record exists", () => {
|
|
103
|
+
test("Telegram disconnected when no connection record exists", async () => {
|
|
103
104
|
// No oauth_connection record for telegram — should be disconnected
|
|
104
|
-
const summary = getIntegrationSummary();
|
|
105
|
+
const summary = await getIntegrationSummary();
|
|
105
106
|
const telegram = summary.find(
|
|
106
107
|
(s: { name: string }) => s.name === "Telegram",
|
|
107
108
|
);
|
|
@@ -110,32 +111,32 @@ describe("integration-status", () => {
|
|
|
110
111
|
});
|
|
111
112
|
|
|
112
113
|
describe("formatIntegrationSummary", () => {
|
|
113
|
-
test("shows checkmarks and crosses", () => {
|
|
114
|
+
test("shows checkmarks and crosses", async () => {
|
|
114
115
|
mockTwilioAccountSid = "sid";
|
|
115
116
|
secureKeyValues.set(credentialKey("twilio", "auth_token"), "auth");
|
|
116
117
|
setOAuthConnected("telegram");
|
|
117
118
|
|
|
118
|
-
const result = formatIntegrationSummary();
|
|
119
|
+
const result = await formatIntegrationSummary();
|
|
119
120
|
expect(result).toBe(
|
|
120
121
|
"Gmail \u2717 | Slack \u2717 | Twilio \u2713 | Telegram \u2713",
|
|
121
122
|
);
|
|
122
123
|
});
|
|
123
124
|
|
|
124
|
-
test("all disconnected", () => {
|
|
125
|
-
const result = formatIntegrationSummary();
|
|
125
|
+
test("all disconnected", async () => {
|
|
126
|
+
const result = await formatIntegrationSummary();
|
|
126
127
|
expect(result).toBe(
|
|
127
128
|
"Gmail \u2717 | Slack \u2717 | Twilio \u2717 | Telegram \u2717",
|
|
128
129
|
);
|
|
129
130
|
});
|
|
130
131
|
|
|
131
|
-
test("all connected", () => {
|
|
132
|
-
setOAuthConnected("integration:
|
|
132
|
+
test("all connected", async () => {
|
|
133
|
+
setOAuthConnected("integration:google");
|
|
133
134
|
setOAuthConnected("integration:slack");
|
|
134
135
|
mockTwilioAccountSid = "sid";
|
|
135
136
|
secureKeyValues.set(credentialKey("twilio", "auth_token"), "auth");
|
|
136
137
|
setOAuthConnected("telegram");
|
|
137
138
|
|
|
138
|
-
const result = formatIntegrationSummary();
|
|
139
|
+
const result = await formatIntegrationSummary();
|
|
139
140
|
expect(result).toBe(
|
|
140
141
|
"Gmail \u2713 | Slack \u2713 | Twilio \u2713 | Telegram \u2713",
|
|
141
142
|
);
|
|
@@ -143,28 +144,28 @@ describe("integration-status", () => {
|
|
|
143
144
|
});
|
|
144
145
|
|
|
145
146
|
describe("hasCapability", () => {
|
|
146
|
-
test("returns false when no integrations in category are connected", () => {
|
|
147
|
-
expect(hasCapability("email")).toBe(false);
|
|
148
|
-
expect(hasCapability("messaging")).toBe(false);
|
|
147
|
+
test("returns false when no integrations in category are connected", async () => {
|
|
148
|
+
expect(await hasCapability("email")).toBe(false);
|
|
149
|
+
expect(await hasCapability("messaging")).toBe(false);
|
|
149
150
|
});
|
|
150
151
|
|
|
151
|
-
test("returns true when any integration in category is connected", () => {
|
|
152
|
+
test("returns true when any integration in category is connected", async () => {
|
|
152
153
|
setOAuthConnected("telegram");
|
|
153
|
-
expect(hasCapability("messaging")).toBe(true);
|
|
154
|
+
expect(await hasCapability("messaging")).toBe(true);
|
|
154
155
|
});
|
|
155
156
|
|
|
156
|
-
test("returns false when no connection record exists for category integrations", () => {
|
|
157
|
+
test("returns false when no connection record exists for category integrations", async () => {
|
|
157
158
|
// No oauth_connection record for telegram — should not count as connected
|
|
158
|
-
expect(hasCapability("messaging")).toBe(false);
|
|
159
|
+
expect(await hasCapability("messaging")).toBe(false);
|
|
159
160
|
});
|
|
160
161
|
|
|
161
|
-
test("returns false for unknown categories", () => {
|
|
162
|
-
expect(hasCapability("nonexistent")).toBe(false);
|
|
162
|
+
test("returns false for unknown categories", async () => {
|
|
163
|
+
expect(await hasCapability("nonexistent")).toBe(false);
|
|
163
164
|
});
|
|
164
165
|
|
|
165
|
-
test("email category checks Gmail", () => {
|
|
166
|
-
setOAuthConnected("integration:
|
|
167
|
-
expect(hasCapability("email")).toBe(true);
|
|
166
|
+
test("email category checks Gmail", async () => {
|
|
167
|
+
setOAuthConnected("integration:google");
|
|
168
|
+
expect(await hasCapability("email")).toBe(true);
|
|
168
169
|
});
|
|
169
170
|
});
|
|
170
171
|
});
|