@vellumai/assistant 0.12.5 → 0.12.6-dev.202609292116.41f7caa
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +2 -0
- package/ARCHITECTURE.md +26 -3
- package/docs/automatic-done.md +32 -0
- package/docs/desktop-browser-cli.md +3 -2
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/package.json +2 -1
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/__tests__/mcp-oauth.test.ts +53 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/mcp-oauth.ts +60 -0
- package/node_modules/@vellumai/environments/src/shell.test.ts +13 -1
- package/node_modules/@vellumai/environments/src/shell.ts +22 -1
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/package.json +2 -1
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/mcp-oauth.test.ts +53 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/mcp-oauth.ts +60 -0
- package/node_modules/@vellumai/gateway-client/src/__tests__/status-contracts.test.ts +39 -0
- package/node_modules/@vellumai/gateway-client/src/gateway-ipc-contracts.ts +25 -0
- package/node_modules/@vellumai/service-contracts/package.json +2 -1
- package/node_modules/@vellumai/service-contracts/src/__tests__/mcp-oauth.test.ts +53 -0
- package/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/node_modules/@vellumai/service-contracts/src/mcp-oauth.ts +60 -0
- package/openapi.yaml +270 -10
- package/package.json +1 -1
- package/src/__tests__/agent-loop.test.ts +76 -0
- package/src/__tests__/anthropic-provider.test.ts +2 -0
- package/src/__tests__/byok-default-profile-ensure.test.ts +7 -0
- package/src/__tests__/config-get-vision-flag.test.ts +1 -1
- package/src/__tests__/config-loader-validation-reset-notice.test.ts +2 -2
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +160 -154
- package/src/__tests__/conversation-evictor.test.ts +54 -0
- package/src/__tests__/conversation-pairing.test.ts +112 -0
- package/src/__tests__/document-append-idempotency.test.ts +13 -0
- package/src/__tests__/document-create-dedupe.test.ts +13 -0
- package/src/__tests__/document-find-replace.test.ts +13 -0
- package/src/__tests__/document-revision-history.test.ts +390 -0
- package/src/__tests__/document-revision.test.ts +425 -0
- package/src/__tests__/document-tool-security.test.ts +13 -0
- package/src/__tests__/document-update-default-surface.test.ts +13 -0
- package/src/__tests__/emit-signal-routing-intent.test.ts +270 -0
- package/src/__tests__/gateway-flag-listener.test.ts +59 -6
- package/src/__tests__/headless-browser-navigate.test.ts +20 -30
- package/src/__tests__/host-cu-proxy.test.ts +79 -0
- package/src/__tests__/host-cu-routes-targeted.test.ts +14 -5
- package/src/__tests__/host-shell-tool.test.ts +6 -2
- package/src/__tests__/identity-routes.test.ts +4 -3
- package/src/__tests__/list-messages-client-message-id.test.ts +43 -0
- package/src/__tests__/llm-catalog-parity.test.ts +4 -4
- package/src/__tests__/llm-schema.test.ts +1 -1
- package/src/__tests__/notification-platform-adapter.test.ts +83 -1
- package/src/__tests__/oauth2-gateway-transport.test.ts +2 -0
- package/src/__tests__/openai-provider.test.ts +38 -11
- package/src/__tests__/pricing.test.ts +11 -0
- package/src/__tests__/public-ingress-urls.test.ts +26 -0
- package/src/__tests__/retry-thinking-tool-choice.test.ts +47 -0
- package/src/__tests__/skill-execute-input.test.ts +11 -0
- package/src/__tests__/tool-approval-handler.test.ts +4 -0
- package/src/__tests__/tool-preview-lifecycle.test.ts +26 -0
- package/src/__tests__/usage-attribution.test.ts +31 -0
- package/src/__tests__/workspace-migration-133-collapse-provider-connections.test.ts +3 -3
- package/src/__tests__/workspace-migration-160-repair-retired-fireworks-model-ids.test.ts +262 -0
- package/src/__tests__/workspace-migration-161-repair-retired-fireworks-glm-5p2-model-id.test.ts +233 -0
- package/src/acp/agent-process.ts +6 -1
- package/src/acp/client-handler.ts +6 -1
- package/src/agent/loop-unparseable-tool-retry.test.ts +333 -0
- package/src/agent/loop.ts +161 -17
- package/src/api/events/document-editor-show.ts +5 -0
- package/src/api/events/document-editor-update.ts +10 -0
- package/src/api/events/tool-result.ts +1 -0
- package/src/calls/__tests__/call-controller.test.ts +273 -1
- package/src/calls/__tests__/media-stream-server-integration.test.ts +40 -0
- package/src/calls/__tests__/media-stream-stt-session.test.ts +33 -0
- package/src/calls/__tests__/voice-session-bridge.test.ts +54 -0
- package/src/calls/call-constants.ts +10 -0
- package/src/calls/call-controller.ts +359 -25
- package/src/calls/channel-admission-reader.ts +15 -9
- package/src/calls/media-stream-server.ts +18 -0
- package/src/calls/media-stream-stt-session.ts +30 -9
- package/src/calls/twilio-provider.ts +14 -31
- package/src/calls/voice-session-bridge.ts +18 -1
- package/src/cli/commands/browser.ts +1 -2
- package/src/cli/lib/__tests__/plugin-artifact.test.ts +23 -0
- package/src/cli/lib/__tests__/plugin-catalog-visibility.test.ts +2 -0
- package/src/cli/lib/__tests__/plugin-details.test.ts +16 -0
- package/src/cli/lib/bundled-marketplace.json +54 -2
- package/src/cli/lib/inspect-plugin.ts +15 -15
- package/src/cli/lib/list-installed-plugins.ts +29 -28
- package/src/cli/lib/plugin-artifact.ts +74 -63
- package/src/cli/lib/plugin-catalog-visibility.ts +1 -0
- package/src/cli/lib/plugin-details.ts +27 -42
- package/src/cli/lib/plugin-marketplace.ts +6 -8
- package/src/cli/lib/plugin-surfaces.ts +8 -7
- package/src/cli.ts +1 -3
- package/src/config/__tests__/chat-settings.test.ts +121 -0
- package/src/config/assistant-feature-flags.ts +28 -23
- package/src/config/bundled-skills/computer-use/SKILL.md +20 -0
- package/src/config/bundled-skills/computer-use/TOOLS.json +11 -11
- package/src/config/chat-settings.ts +100 -0
- package/src/config/default-profile-catalog.ts +1 -1
- package/src/config/feature-flag-cache.ts +7 -25
- package/src/config/feature-flag-registry.json +9 -9
- package/src/config/loader.ts +5 -1
- package/src/config/profile-text-generation.ts +25 -21
- package/src/config/schemas/conversations.ts +14 -0
- package/src/config/schemas/llm.ts +1 -0
- package/src/config/schemas/notifications.ts +8 -1
- package/src/contacts/gateway-channel-read.ts +1 -1
- package/src/conversations/__tests__/auto-archive-activity.test.ts +136 -0
- package/src/conversations/__tests__/auto-archive.test.ts +304 -0
- package/src/conversations/auto-archive-activity.ts +61 -0
- package/src/conversations/auto-archive.ts +201 -0
- package/src/daemon/computer-use-observation.test.ts +81 -0
- package/src/daemon/computer-use-observation.ts +23 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +18 -2
- package/src/daemon/conversation-agent-loop.ts +14 -6
- package/src/daemon/conversation-evictor.ts +50 -16
- package/src/daemon/conversation-messaging.ts +4 -0
- package/src/daemon/conversation-process.ts +15 -0
- package/src/daemon/conversation-queue-manager.ts +2 -0
- package/src/daemon/conversation.ts +2 -0
- package/src/daemon/doordash-steps.ts +1 -3
- package/src/daemon/handlers/__tests__/config-a2a-accept.test.ts +43 -1
- package/src/daemon/handlers/__tests__/config-channels.test.ts +1 -1
- package/src/daemon/handlers/config-a2a.ts +75 -2
- package/src/daemon/handlers/shared.ts +13 -2
- package/src/daemon/host-cu-proxy.ts +34 -3
- package/src/daemon/lifecycle.ts +15 -2
- package/src/daemon/process-message.ts +5 -0
- package/src/daemon/shutdown-handlers.ts +3 -0
- package/src/daemon/turn-finalization.ts +4 -0
- package/src/desktop/desktop-browser-endpoint.ts +11 -2
- package/src/desktop/desktop-chrome-policy.test.ts +1 -0
- package/src/desktop/desktop-chrome-policy.ts +7 -1
- package/src/desktop/desktop-computer-use.test.ts +14 -4
- package/src/desktop/desktop-session-manager.ts +5 -3
- package/src/documents/document-revisions-store.ts +153 -0
- package/src/documents/document-store.ts +471 -133
- package/src/followups/followup-store.ts +0 -4
- package/src/i18n/__tests__/i18n.test.ts +25 -0
- package/src/i18n/assistant-text.ts +18 -0
- package/src/i18n/index.ts +1 -0
- package/src/i18n/messages.ts +11 -0
- package/src/inbound/__tests__/oauth-callback-url.test.ts +25 -2
- package/src/inbound/oauth-callback-url.ts +30 -1
- package/src/inbound/public-ingress-urls.ts +12 -2
- package/src/ipc/gateway-client.test.ts +40 -0
- package/src/ipc/gateway-client.ts +12 -29
- package/src/ipc/gateway-flag-listener.ts +7 -10
- package/src/live-voice/__tests__/live-voice-agent-turn.test.ts +71 -0
- package/src/live-voice/__tests__/live-voice-flux-turn-end.test.ts +83 -0
- package/src/live-voice/__tests__/protocol.test.ts +77 -0
- package/src/live-voice/__tests__/share-targets.test.ts +163 -0
- package/src/live-voice/live-voice-session.ts +23 -6
- package/src/live-voice/protocol.ts +22 -0
- package/src/live-voice/share-targets.ts +225 -0
- package/src/mcp/__tests__/mcp-oauth-client-registration.test.ts +144 -3
- package/src/mcp/__tests__/mcp-oauth-provider.test.ts +2 -0
- package/src/mcp/client.ts +1 -19
- package/src/mcp/mcp-oauth-provider.ts +66 -49
- package/src/media/openrouter-image-service.ts +39 -22
- package/src/monitoring/__tests__/oom-inheritance-sweep.test.ts +86 -0
- package/src/monitoring/__tests__/oom-kill-reporter.test.ts +374 -0
- package/src/monitoring/control.ts +2 -1
- package/src/monitoring/oom-inheritance-sweep.ts +96 -0
- package/src/monitoring/oom-kill-reporter.ts +469 -0
- package/src/monitoring/resource-sampler.ts +30 -0
- package/src/notifications/README.md +53 -4
- package/src/notifications/__tests__/assistant-initiated-thread.test.ts +254 -0
- package/src/notifications/__tests__/broadcaster.test.ts +558 -6
- package/src/notifications/__tests__/completion-policy.test.ts +37 -0
- package/src/notifications/__tests__/from-me-alert-integration.test.ts +192 -0
- package/src/notifications/adapters/platform.ts +14 -0
- package/src/notifications/assistant-initiated-thread.ts +101 -0
- package/src/notifications/broadcaster.ts +94 -16
- package/src/notifications/chat-reply-policy.ts +11 -0
- package/src/notifications/completion-policy.ts +12 -2
- package/src/notifications/conversation-pairing.ts +15 -70
- package/src/notifications/emit-signal.ts +34 -3
- package/src/notifications/types.ts +2 -0
- package/src/oauth/connect-orchestrator.ts +1 -2
- package/src/persistence/__tests__/conversation-auto-archive.test.ts +330 -0
- package/src/persistence/conversation-auto-archive.ts +170 -0
- package/src/persistence/conversation-crud.ts +2 -1
- package/src/persistence/conversation-queries.ts +15 -8
- package/src/persistence/embeddings/embed.test.ts +96 -0
- package/src/persistence/embeddings/embed.ts +20 -2
- package/src/persistence/embeddings/embedding-gemini.test.ts +39 -3
- package/src/persistence/embeddings/embedding-gemini.ts +39 -1
- package/src/persistence/embeddings/embedding-local.ts +6 -0
- package/src/persistence/embeddings/qdrant-manager.ts +5 -0
- package/src/persistence/job-utils.ts +10 -0
- package/src/persistence/migrations/384-add-document-revision.test.ts +83 -0
- package/src/persistence/migrations/384-add-document-revision.ts +21 -0
- package/src/persistence/migrations/385-create-document-revisions.test.ts +67 -0
- package/src/persistence/migrations/385-create-document-revisions.ts +28 -0
- package/src/persistence/migrations/386-conversations-last-reopened-at.ts +10 -0
- package/src/persistence/migrations/__tests__/386-conversations-last-reopened-at.test.ts +32 -0
- package/src/persistence/schema/conversations.ts +1 -0
- package/src/persistence/schema/documents.ts +20 -0
- package/src/persistence/steps.ts +6 -0
- package/src/platform/managed-speech.ts +42 -46
- package/src/plugin-api/vision-support.test.ts +21 -21
- package/src/plugins/defaults/empty-response/hooks/post-model-call.ts +11 -6
- package/src/providers/__tests__/dispatch-connection-routing.test.ts +1 -1
- package/src/providers/__tests__/search-provider-catalog.test.ts +8 -0
- package/src/providers/__tests__/unparseable-tool-args.test.ts +25 -0
- package/src/providers/anthropic/client.ts +1 -1
- package/src/providers/fireworks/client.test.ts +168 -0
- package/src/providers/fireworks/client.ts +24 -0
- package/src/providers/inference/adapter-factory.ts +6 -0
- package/src/providers/ionet/client.test.ts +143 -0
- package/src/providers/ionet/client.ts +37 -0
- package/src/providers/model-catalog.ts +141 -84
- package/src/providers/model-intents.ts +4 -4
- package/src/providers/openai/chat-completions-provider.ts +33 -2
- package/src/providers/retry.ts +56 -4
- package/src/providers/search-provider-catalog.ts +10 -0
- package/src/providers/types.ts +10 -2
- package/src/providers/unparseable-tool-args.ts +29 -5
- package/src/providers/usage-tracking.ts +5 -3
- package/src/runtime/assistant-event-hub.ts +23 -0
- package/src/runtime/auth/__tests__/guard-tests.test.ts +0 -7
- package/src/runtime/auth/__tests__/route-policy.test.ts +1 -1
- package/src/runtime/channel-reply-delivery.ts +2 -0
- package/src/runtime/channel-retry-sweep.ts +1 -2
- package/src/runtime/http-types.ts +3 -0
- package/src/runtime/migrations/__tests__/vbundle-streaming-validator.test.ts +35 -10
- package/src/runtime/migrations/__tests__/vbundle-validator-v1-schema.test.ts +48 -0
- package/src/runtime/migrations/vbundle-builder.ts +19 -3
- package/src/runtime/migrations/vbundle-streaming-importer.ts +14 -19
- package/src/runtime/migrations/vbundle-streaming-validator.ts +14 -7
- package/src/runtime/migrations/vbundle-validator.ts +54 -1
- package/src/runtime/pending-interactions.ts +20 -2
- package/src/runtime/routes/__tests__/contact-routes.test.ts +5 -3
- package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +36 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +375 -2
- package/src/runtime/routes/__tests__/inference-send-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +12 -0
- package/src/runtime/routes/contact-routes.ts +2 -2
- package/src/runtime/routes/conversation-list-routes.ts +10 -2
- package/src/runtime/routes/conversation-query-routes.ts +48 -10
- package/src/runtime/routes/conversation-routes.ts +7 -0
- package/src/runtime/routes/documents-routes.ts +185 -47
- package/src/runtime/routes/events-routes.ts +1 -3
- package/src/runtime/routes/host-app-control-routes.ts +1 -0
- package/src/runtime/routes/host-bash-routes.ts +1 -0
- package/src/runtime/routes/host-browser-routes.ts +15 -0
- package/src/runtime/routes/host-cu-routes.ts +4 -1
- package/src/runtime/routes/host-file-routes.ts +1 -0
- package/src/runtime/routes/host-proxy-result-binding.test.ts +83 -0
- package/src/runtime/routes/host-proxy-result-binding.ts +25 -0
- package/src/runtime/routes/host-transfer-routes.ts +1 -0
- package/src/runtime/routes/identity-routes.ts +3 -3
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +4 -0
- package/src/runtime/routes/integrations/a2a.ts +4 -1
- package/src/runtime/routes/migration-routes.ts +3 -1
- package/src/runtime/routes/notification-routes.ts +1 -2
- package/src/runtime/routes/ui-snapshot-routes.test.ts +1 -0
- package/src/runtime/routes/user-route-dispatcher.ts +5 -1
- package/src/runtime/verification-templates.ts +1 -2
- package/src/sequence/analytics.ts +0 -4
- package/src/sequence/engine.ts +1 -11
- package/src/sequence/guardrails.ts +1 -6
- package/src/sequence/importer.ts +0 -8
- package/src/sequence/store.ts +0 -10
- package/src/signals/user-message.ts +0 -6
- package/src/skills/catalog-install.ts +52 -23
- package/src/tools/browser/browser-execution.ts +20 -23
- package/src/tools/browser/cdp-client/cdp-inspect/ws-transport.ts +1 -3
- package/src/tools/document/document-tool.ts +13 -11
- package/src/tools/executor.ts +1 -2
- package/src/tools/host-terminal/host-shell.ts +4 -2
- package/src/tools/network/__tests__/web-search.test.ts +105 -0
- package/src/tools/network/web-fetch.ts +2 -4
- package/src/tools/network/web-search.ts +239 -12
- package/src/tools/shared/filesystem/format-diff.ts +1 -2
- package/src/tools/shared/shell-output.test.ts +42 -0
- package/src/tools/shared/shell-output.ts +27 -4
- package/src/tools/skills/execute.ts +10 -7
- package/src/tools/terminal/shell.ts +3 -3
- package/src/tools/tool-approval-handler.ts +9 -1
- package/src/tools/ui-surface/visual-validation.ts +1 -2
- package/src/usage/attribution.ts +18 -1
- package/src/util/__tests__/oom-priority.test.ts +55 -0
- package/src/util/__tests__/worker-memory.test.ts +16 -0
- package/src/util/debounce.ts +0 -4
- package/src/util/oom-priority.ts +79 -0
- package/src/util/platform.ts +0 -2
- package/src/util/plugin-manifest.ts +5 -7
- package/src/util/pricing.ts +25 -0
- package/src/util/worker-memory.ts +9 -3
- package/src/util/worker-process.ts +8 -0
- package/src/workspace/byok-default-profile-ensure.ts +11 -0
- package/src/workspace/hatched-date.ts +11 -4
- package/src/workspace/migrations/160-repair-retired-fireworks-model-ids.ts +209 -0
- package/src/workspace/migrations/161-repair-retired-fireworks-glm-5p2-model-id.ts +197 -0
- package/src/workspace/migrations/registry.ts +4 -0
|
@@ -69,7 +69,10 @@ import {
|
|
|
69
69
|
verifySymlinkEntry,
|
|
70
70
|
} from "./vbundle-streaming-validator.js";
|
|
71
71
|
import { parseVBundleStream } from "./vbundle-tar-stream.js";
|
|
72
|
-
import
|
|
72
|
+
import {
|
|
73
|
+
MANIFEST_MAX_ENTRIES,
|
|
74
|
+
type ManifestType,
|
|
75
|
+
} from "./vbundle-validator.js";
|
|
73
76
|
|
|
74
77
|
const log = getLogger("vbundle-streaming-importer");
|
|
75
78
|
|
|
@@ -91,11 +94,11 @@ const log = getLogger("vbundle-streaming-importer");
|
|
|
91
94
|
const DEFAULT_MAX_BUNDLE_BYTES = 16 * 1024 * 1024 * 1024;
|
|
92
95
|
|
|
93
96
|
/**
|
|
94
|
-
* Entry-count ceiling for the bundle
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
+
* Entry-count ceiling for the bundle, shared with manifest validation so
|
|
98
|
+
* preflight and import agree. Also applied to tar-level entries so padding
|
|
99
|
+
* extras cannot exceed what the manifest declared.
|
|
97
100
|
*/
|
|
98
|
-
const DEFAULT_MAX_BUNDLE_ENTRIES =
|
|
101
|
+
const DEFAULT_MAX_BUNDLE_ENTRIES = MANIFEST_MAX_ENTRIES;
|
|
99
102
|
|
|
100
103
|
/**
|
|
101
104
|
* Prefixes used for scratch dirs the streaming importer creates INSIDE the
|
|
@@ -153,7 +156,7 @@ export interface StreamCommitArgs {
|
|
|
153
156
|
maxBundleBytes?: number;
|
|
154
157
|
/**
|
|
155
158
|
* Test-only override for the entry-count ceiling. Production callers
|
|
156
|
-
* should omit this and rely on the
|
|
159
|
+
* should omit this and rely on the `MANIFEST_MAX_ENTRIES` default.
|
|
157
160
|
*/
|
|
158
161
|
maxBundleEntries?: number;
|
|
159
162
|
}
|
|
@@ -313,7 +316,9 @@ export async function streamCommitImport(
|
|
|
313
316
|
if (entryIndex === 0) {
|
|
314
317
|
// First entry MUST be manifest.json — readAndValidateManifest
|
|
315
318
|
// enforces that and throws StreamingValidationError otherwise.
|
|
316
|
-
const manifestResult = await readAndValidateManifest(entry
|
|
319
|
+
const manifestResult = await readAndValidateManifest(entry, {
|
|
320
|
+
maxEntries: bundleEntryCap,
|
|
321
|
+
});
|
|
317
322
|
manifest = manifestResult.manifest;
|
|
318
323
|
expected = manifestResult.expected;
|
|
319
324
|
|
|
@@ -340,18 +345,8 @@ export async function streamCommitImport(
|
|
|
340
345
|
);
|
|
341
346
|
}
|
|
342
347
|
|
|
343
|
-
//
|
|
344
|
-
//
|
|
345
|
-
// importer is willing to do for this bundle.
|
|
346
|
-
if (manifest.contents.length > bundleEntryCap) {
|
|
347
|
-
throw new StreamingValidationError(
|
|
348
|
-
"bundle_too_many_entries",
|
|
349
|
-
`bundle contains more than ${bundleEntryCap} entries (declared: ${manifest.contents.length})`,
|
|
350
|
-
);
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
// Only NOW — after the manifest is parsed, the version gate passes,
|
|
354
|
-
// and the entry-count ceiling is enforced — do we materialize the
|
|
348
|
+
// Only NOW, after the manifest is parsed (including its entry-count
|
|
349
|
+
// ceiling) and the version gate passes, do we materialize the
|
|
355
350
|
// temp staging dir on disk. Doing this lazily preserves the plan
|
|
356
351
|
// invariant that importers gate on runtime-version compat BEFORE
|
|
357
352
|
// any state mutation. If this throws, the outer catch runs
|
|
@@ -28,6 +28,8 @@ import {
|
|
|
28
28
|
computeLegacyManifestSha256,
|
|
29
29
|
computeManifestChecksum,
|
|
30
30
|
LegacyManifestSchema,
|
|
31
|
+
MANIFEST_MAX_BYTES,
|
|
32
|
+
MANIFEST_MAX_ENTRIES,
|
|
31
33
|
ManifestSchema,
|
|
32
34
|
type ManifestType,
|
|
33
35
|
translateLegacyManifest,
|
|
@@ -74,21 +76,17 @@ export class StreamingValidationError extends Error {
|
|
|
74
76
|
// Manifest validation
|
|
75
77
|
// ---------------------------------------------------------------------------
|
|
76
78
|
|
|
77
|
-
// Manifests are metadata only — typically tens to hundreds of KB even for
|
|
78
|
-
// huge bundles. A 1 MiB cap is comfortably above realistic sizes and
|
|
79
|
-
// protects against a malicious archive whose "manifest" is actually a
|
|
80
|
-
// multi-GB stream intended to OOM the validator.
|
|
81
|
-
const MANIFEST_MAX_BYTES = 1 * 1024 * 1024;
|
|
82
|
-
|
|
83
79
|
/**
|
|
84
80
|
* Drain the first tar entry — which MUST be `manifest.json` — and run the
|
|
85
81
|
* full manifest-level validation pipeline:
|
|
86
82
|
* 1. Entry name check.
|
|
87
|
-
* 2. Size cap (
|
|
83
|
+
* 2. Size cap (`MANIFEST_MAX_BYTES`).
|
|
88
84
|
* 3. JSON parse.
|
|
89
85
|
* 4. Zod schema validation.
|
|
90
86
|
* 5. Self-referencing `checksum` verification against the
|
|
91
87
|
* canonicalized JSON (minus that field).
|
|
88
|
+
* 6. Entry-count ceiling on `contents` (`maxEntries`, default
|
|
89
|
+
* `MANIFEST_MAX_ENTRIES`).
|
|
92
90
|
*
|
|
93
91
|
* On success, returns the parsed manifest plus a `Map` keyed by archive
|
|
94
92
|
* path that callers consult as each subsequent entry streams past.
|
|
@@ -98,7 +96,9 @@ const MANIFEST_MAX_BYTES = 1 * 1024 * 1024;
|
|
|
98
96
|
*/
|
|
99
97
|
export async function readAndValidateManifest(
|
|
100
98
|
first: StreamedTarEntry,
|
|
99
|
+
options: { maxEntries?: number } = {},
|
|
101
100
|
): Promise<ManifestReadResult> {
|
|
101
|
+
const maxEntries = options.maxEntries ?? MANIFEST_MAX_ENTRIES;
|
|
102
102
|
if (first.header.name !== "manifest.json") {
|
|
103
103
|
// Drain the body so the underlying tar extractor isn't left dangling
|
|
104
104
|
// on backpressure before the caller reports the error.
|
|
@@ -186,6 +186,13 @@ export async function readAndValidateManifest(
|
|
|
186
186
|
manifest = translateLegacyManifest(legacy);
|
|
187
187
|
}
|
|
188
188
|
|
|
189
|
+
if (manifest.contents.length > maxEntries) {
|
|
190
|
+
throw new StreamingValidationError(
|
|
191
|
+
"bundle_too_many_entries",
|
|
192
|
+
`bundle contains more than ${maxEntries} entries (declared: ${manifest.contents.length})`,
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
|
|
189
196
|
const expected = new Map<
|
|
190
197
|
string,
|
|
191
198
|
{ sha256: string; size: number; linkTarget: string | null }
|
|
@@ -381,6 +381,33 @@ export function computeManifestChecksum(manifest: unknown): string {
|
|
|
381
381
|
// Core validation
|
|
382
382
|
// ---------------------------------------------------------------------------
|
|
383
383
|
|
|
384
|
+
/**
|
|
385
|
+
* Hard caps on `manifest.json`, shared by the buffered and streaming
|
|
386
|
+
* validators so preflight and import agree on what is acceptable. The two
|
|
387
|
+
* are sized together: the manifest carries one `contents` entry (path,
|
|
388
|
+
* sha256, size) per bundled file at roughly 150 bytes each, so 200,000
|
|
389
|
+
* entries serialize to about 32 MiB.
|
|
390
|
+
*
|
|
391
|
+
* That absorbs a workspace with a stray dependency install or checked-out
|
|
392
|
+
* repo while still bounding the transient memory a hostile "manifest" can
|
|
393
|
+
* force the importer to hold (buffer, decoded string, parsed object,
|
|
394
|
+
* canonical re-serialization for the self-checksum) and the per-entry work
|
|
395
|
+
* it commits to, on a memory-constrained pod.
|
|
396
|
+
*/
|
|
397
|
+
export const MANIFEST_MAX_BYTES = 32 * 1024 * 1024;
|
|
398
|
+
export const MANIFEST_MAX_ENTRIES = 200_000;
|
|
399
|
+
|
|
400
|
+
export interface ValidateVBundleOptions {
|
|
401
|
+
/**
|
|
402
|
+
* Reject manifests over `MANIFEST_MAX_BYTES` or `MANIFEST_MAX_ENTRIES`.
|
|
403
|
+
* Preflights that precede a streaming import set this so they never
|
|
404
|
+
* approve a bundle the import will refuse. The buffered import path
|
|
405
|
+
* (`commitImport`) has no such ceilings, so it stays off there to keep
|
|
406
|
+
* existing large backups restorable.
|
|
407
|
+
*/
|
|
408
|
+
enforceStreamingLimits?: boolean;
|
|
409
|
+
}
|
|
410
|
+
|
|
384
411
|
// Only manifest.json is structurally required. The DB and config live under
|
|
385
412
|
// workspace/ (new format) or data/db/ + config/ (old format) — both are valid.
|
|
386
413
|
const REQUIRED_ENTRIES = ["manifest.json"];
|
|
@@ -397,7 +424,10 @@ const MAX_DECOMPRESSED_SIZE = 2 * 1024 * 1024 * 1024;
|
|
|
397
424
|
* 3. Manifest checksum (SHA-256 of canonicalized JSON with the `checksum` field set to empty string)
|
|
398
425
|
* 4. Per-file content integrity (SHA-256 of each file vs manifest declaration)
|
|
399
426
|
*/
|
|
400
|
-
export function validateVBundle(
|
|
427
|
+
export function validateVBundle(
|
|
428
|
+
data: Uint8Array,
|
|
429
|
+
{ enforceStreamingLimits = false }: ValidateVBundleOptions = {},
|
|
430
|
+
): VBundleValidationResult {
|
|
401
431
|
const errors: ValidationError[] = [];
|
|
402
432
|
|
|
403
433
|
// Step 1: Decompress gzip with size cap to prevent zip-bomb DoS
|
|
@@ -455,6 +485,17 @@ export function validateVBundle(data: Uint8Array): VBundleValidationResult {
|
|
|
455
485
|
}
|
|
456
486
|
|
|
457
487
|
// Step 4: Parse and validate manifest schema
|
|
488
|
+
if (
|
|
489
|
+
enforceStreamingLimits &&
|
|
490
|
+
manifestEntry.data.length > MANIFEST_MAX_BYTES
|
|
491
|
+
) {
|
|
492
|
+
errors.push({
|
|
493
|
+
code: "MANIFEST_TOO_LARGE",
|
|
494
|
+
message: `manifest.json exceeds ${MANIFEST_MAX_BYTES} byte limit (${manifestEntry.data.length} bytes)`,
|
|
495
|
+
path: "manifest.json",
|
|
496
|
+
});
|
|
497
|
+
return { is_valid: false, errors };
|
|
498
|
+
}
|
|
458
499
|
let manifestRaw: unknown;
|
|
459
500
|
try {
|
|
460
501
|
manifestRaw = JSON.parse(new TextDecoder().decode(manifestEntry.data));
|
|
@@ -523,6 +564,18 @@ export function validateVBundle(data: Uint8Array): VBundleValidationResult {
|
|
|
523
564
|
manifest = translateLegacyManifest(legacy);
|
|
524
565
|
}
|
|
525
566
|
|
|
567
|
+
if (
|
|
568
|
+
enforceStreamingLimits &&
|
|
569
|
+
manifest.contents.length > MANIFEST_MAX_ENTRIES
|
|
570
|
+
) {
|
|
571
|
+
errors.push({
|
|
572
|
+
code: "MANIFEST_TOO_MANY_ENTRIES",
|
|
573
|
+
message: `manifest.json declares more than ${MANIFEST_MAX_ENTRIES} entries (${manifest.contents.length})`,
|
|
574
|
+
path: "manifest.json",
|
|
575
|
+
});
|
|
576
|
+
return { is_valid: false, errors };
|
|
577
|
+
}
|
|
578
|
+
|
|
526
579
|
// Step 6: Verify per-file content integrity
|
|
527
580
|
const manifestFilePaths = new Set(manifest.contents.map((f) => f.path));
|
|
528
581
|
|
|
@@ -23,7 +23,10 @@ import type { QuestionEntry } from "../api/events/question-request.js";
|
|
|
23
23
|
import { findConversationOrSubagent } from "../daemon/conversation-registry.js";
|
|
24
24
|
import type { UserDecision } from "../permissions/types.js";
|
|
25
25
|
import { getLogger } from "../util/logger.js";
|
|
26
|
-
import {
|
|
26
|
+
import {
|
|
27
|
+
assistantEventHub,
|
|
28
|
+
broadcastMessage,
|
|
29
|
+
} from "./assistant-event-hub.js";
|
|
27
30
|
|
|
28
31
|
const log = getLogger("pending-interactions");
|
|
29
32
|
|
|
@@ -110,6 +113,12 @@ export interface PendingInteraction {
|
|
|
110
113
|
directResolve?: (decision: UserDecision) => void;
|
|
111
114
|
/** When set, the host_bash request should be routed to this specific client. */
|
|
112
115
|
targetClientId?: string;
|
|
116
|
+
/**
|
|
117
|
+
* Hub connection that was active for `targetClientId` at dispatch. Results
|
|
118
|
+
* must arrive while this connection is still subscribed so a reconnect
|
|
119
|
+
* cannot submit under the same client id.
|
|
120
|
+
*/
|
|
121
|
+
targetConnectionId?: string;
|
|
113
122
|
/**
|
|
114
123
|
* Actor principal captured at registration. Targeted requests snapshot the
|
|
115
124
|
* target client's actor so a brief SSE reconnect does not 403 a legitimate
|
|
@@ -167,7 +176,16 @@ export function register(
|
|
|
167
176
|
requestId: string,
|
|
168
177
|
interaction: PendingInteraction,
|
|
169
178
|
): void {
|
|
170
|
-
|
|
179
|
+
const targetConnectionId =
|
|
180
|
+
interaction.targetConnectionId ??
|
|
181
|
+
(interaction.targetClientId
|
|
182
|
+
? assistantEventHub.getClientById?.(interaction.targetClientId)
|
|
183
|
+
?.connectionId
|
|
184
|
+
: undefined);
|
|
185
|
+
pending.set(requestId, {
|
|
186
|
+
...interaction,
|
|
187
|
+
...(targetConnectionId != null ? { targetConnectionId } : {}),
|
|
188
|
+
});
|
|
171
189
|
}
|
|
172
190
|
|
|
173
191
|
/**
|
|
@@ -275,7 +275,7 @@ describe("contacts read API relays from the gateway", () => {
|
|
|
275
275
|
});
|
|
276
276
|
|
|
277
277
|
test("get surfaces a clean gateway not-found as a 404", async () => {
|
|
278
|
-
ipcResult =
|
|
278
|
+
ipcResult = null;
|
|
279
279
|
|
|
280
280
|
await expect(handleGetContact("missing")).rejects.toMatchObject({
|
|
281
281
|
statusCode: 404,
|
|
@@ -391,7 +391,8 @@ describe("filtered/native contact reads: daemon filters, gateway hydrates teleme
|
|
|
391
391
|
expect(contact.interactionCount).toBe(11);
|
|
392
392
|
expect(contact.lastInteraction).toBe(9200);
|
|
393
393
|
expect(
|
|
394
|
-
(contact as { autoApproveThreshold?: string | null })
|
|
394
|
+
(contact as { autoApproveThreshold?: string | null })
|
|
395
|
+
.autoApproveThreshold,
|
|
395
396
|
).toBe("high");
|
|
396
397
|
const channel = contact.channels[0] as Record<string, unknown>;
|
|
397
398
|
expect(channel.interactionCount).toBe(11);
|
|
@@ -422,7 +423,8 @@ describe("filtered/native contact reads: daemon filters, gateway hydrates teleme
|
|
|
422
423
|
expect(contact.interactionCount).toBe(0);
|
|
423
424
|
expect(contact.lastInteraction).toBeNull();
|
|
424
425
|
expect(
|
|
425
|
-
(contact as { autoApproveThreshold?: string | null })
|
|
426
|
+
(contact as { autoApproveThreshold?: string | null })
|
|
427
|
+
.autoApproveThreshold,
|
|
426
428
|
).toBeNull();
|
|
427
429
|
const channel = contact.channels[0] as Record<string, unknown>;
|
|
428
430
|
expect(channel.interactionCount).toBe(0);
|
|
@@ -71,6 +71,7 @@ interface ListResponse {
|
|
|
71
71
|
nextOffset: number;
|
|
72
72
|
hasMore: boolean;
|
|
73
73
|
groups?: unknown[];
|
|
74
|
+
orderedBy: string;
|
|
74
75
|
}
|
|
75
76
|
|
|
76
77
|
function findHandler(routes: RouteDefinition[], operationId: string) {
|
|
@@ -1380,6 +1381,41 @@ describe("GET /v1/conversations, conversationType=all", () => {
|
|
|
1380
1381
|
]);
|
|
1381
1382
|
});
|
|
1382
1383
|
|
|
1384
|
+
test("files a done row by when it was marked done when that is later than its last message", async () => {
|
|
1385
|
+
const recent = createConversation("recent-foreground");
|
|
1386
|
+
const doneLater = createConversation("old-chat-marked-done-later");
|
|
1387
|
+
const doneEarlier = createConversation("old-chat-done-before-a-reply");
|
|
1388
|
+
setLastMessageAt(recent.id, 2000);
|
|
1389
|
+
setLastMessageAt(doneLater.id, 1000);
|
|
1390
|
+
setLastMessageAt(doneEarlier.id, 1500);
|
|
1391
|
+
rawRun(
|
|
1392
|
+
"test:markDone",
|
|
1393
|
+
"UPDATE conversations SET archived_at = ? WHERE id = ?",
|
|
1394
|
+
3000,
|
|
1395
|
+
doneLater.id,
|
|
1396
|
+
);
|
|
1397
|
+
rawRun(
|
|
1398
|
+
"test:markDone",
|
|
1399
|
+
"UPDATE conversations SET archived_at = ? WHERE id = ?",
|
|
1400
|
+
500,
|
|
1401
|
+
doneEarlier.id,
|
|
1402
|
+
);
|
|
1403
|
+
|
|
1404
|
+
const result = (await invoke({
|
|
1405
|
+
conversationType: "all",
|
|
1406
|
+
archiveStatus: "all",
|
|
1407
|
+
})) as ListResponse;
|
|
1408
|
+
|
|
1409
|
+
expect(result.conversations.map((c) => c.title)).toEqual([
|
|
1410
|
+
"old-chat-marked-done-later",
|
|
1411
|
+
"recent-foreground",
|
|
1412
|
+
"old-chat-done-before-a-reply",
|
|
1413
|
+
]);
|
|
1414
|
+
// A client paging done rows relies on this to know the order is not
|
|
1415
|
+
// message recency.
|
|
1416
|
+
expect(result.orderedBy).toBe("lastActivity");
|
|
1417
|
+
});
|
|
1418
|
+
|
|
1383
1419
|
test("excludes legacy private rows and subagent runs", async () => {
|
|
1384
1420
|
createConversation("foreground-1");
|
|
1385
1421
|
const legacyPrivate = createConversation("legacy-private");
|