@vellumai/assistant 0.6.0 → 0.6.2
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/AGENTS.md +4 -0
- package/ARCHITECTURE.md +68 -15
- package/Dockerfile +2 -2
- package/bun.lock +6 -2
- package/docker-entrypoint.sh +42 -1
- package/docs/architecture/integrations.md +1 -1
- package/docs/architecture/memory.md +21 -24
- package/node_modules/@vellumai/ces-contracts/src/handles.ts +7 -9
- package/openapi.yaml +539 -4
- package/package.json +5 -1
- package/src/__tests__/anthropic-provider.test.ts +160 -95
- package/src/__tests__/app-dir-path-guard.test.ts +1 -0
- package/src/__tests__/app-executors.test.ts +47 -1
- package/src/__tests__/app-source-watcher.test.ts +159 -0
- package/src/__tests__/assistant-event-hub.test.ts +30 -0
- package/src/__tests__/checker.test.ts +138 -172
- package/src/__tests__/cli-command-risk-guard.test.ts +1 -1
- package/src/__tests__/config-schema.test.ts +5 -0
- package/src/__tests__/context-overflow-approval.test.ts +5 -5
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +4 -6
- package/src/__tests__/conversation-agent-loop.test.ts +4 -51
- package/src/__tests__/conversation-analysis-routes.test.ts +169 -0
- package/src/__tests__/conversation-directories-parse.test.ts +105 -0
- package/src/__tests__/conversation-history-web-search.test.ts +1 -1
- package/src/__tests__/conversation-runtime-assembly.test.ts +653 -832
- package/src/__tests__/conversation-runtime-workspace.test.ts +1 -93
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +17 -4
- package/src/__tests__/conversation-wipe.test.ts +2 -6
- package/src/__tests__/conversation-workspace-cache-state.test.ts +6 -12
- package/src/__tests__/conversation-workspace-injection.test.ts +25 -26
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +1 -1
- package/src/__tests__/copy-composer-tc-templates.test.ts +335 -0
- package/src/__tests__/credential-execution-approval-bridge.test.ts +0 -2
- package/src/__tests__/date-context.test.ts +76 -210
- package/src/__tests__/db-schedule-syntax-migration.test.ts +16 -1
- package/src/__tests__/file-list-tool.test.ts +219 -0
- package/src/__tests__/first-greeting.test.ts +1 -1
- package/src/__tests__/heartbeat-service.test.ts +180 -3
- package/src/__tests__/identity-routes.test.ts +328 -0
- package/src/__tests__/init-feature-flag-overrides.test.ts +167 -0
- package/src/__tests__/injection-block.test.ts +24 -0
- package/src/__tests__/inline-command-runner.test.ts +7 -5
- package/src/__tests__/install-skill-routing.test.ts +7 -6
- package/src/__tests__/jobs-store-qdrant-breaker.test.ts +15 -14
- package/src/__tests__/list-messages-tool-merge.test.ts +300 -0
- package/src/__tests__/llm-context-normalization.test.ts +18 -18
- package/src/__tests__/llm-context-route-provider.test.ts +101 -0
- package/src/__tests__/llm-request-log-turn-query.test.ts +162 -0
- package/src/__tests__/log-export-workspace.test.ts +257 -100
- package/src/__tests__/managed-credential-catalog-cli.test.ts +12 -14
- package/src/__tests__/mcp-abort-signal.test.ts +5 -0
- package/src/__tests__/mcp-client-auth.test.ts +5 -0
- package/src/__tests__/memory-recall-log-store.test.ts +132 -0
- package/src/__tests__/migration-export-streaming.test.ts +304 -0
- package/src/__tests__/migration-import-commit-http.test.ts +11 -10
- package/src/__tests__/mock-fetch.ts +87 -0
- package/src/__tests__/navigate-settings-tab.test.ts +14 -1
- package/src/__tests__/notification-broadcaster.test.ts +65 -0
- package/src/__tests__/notification-decision-recipient-context.test.ts +282 -0
- package/src/__tests__/onboarding-template-contract.test.ts +63 -14
- package/src/__tests__/parser.test.ts +32 -0
- package/src/__tests__/permission-checker-host-gate.test.ts +452 -0
- package/src/__tests__/permission-controls-v2-flag.test.ts +55 -0
- package/src/__tests__/permission-mode-sse.test.ts +418 -0
- package/src/__tests__/permission-mode-store.test.ts +277 -0
- package/src/__tests__/permission-mode.test.ts +101 -0
- package/src/__tests__/pkb-autoinject.test.ts +96 -0
- package/src/__tests__/platform-bash-auto-approve.test.ts +359 -0
- package/src/__tests__/profiler-routes.test.ts +502 -0
- package/src/__tests__/profiler-run-store.test.ts +441 -0
- package/src/__tests__/proxy-approval-callback.test.ts +4 -75
- package/src/__tests__/registry.test.ts +1 -1
- package/src/__tests__/require-fresh-approval.test.ts +0 -2
- package/src/__tests__/sandbox-diagnostics.test.ts +1 -32
- package/src/__tests__/sandbox-host-parity.test.ts +5 -4
- package/src/__tests__/scheduler-reuse-conversation.test.ts +368 -0
- package/src/__tests__/scrub-corrupted-image-attachments.test.ts +278 -0
- package/src/__tests__/search-skills-unified.test.ts +4 -3
- package/src/__tests__/send-endpoint-busy.test.ts +42 -3
- package/src/__tests__/set-permission-mode.test.ts +274 -0
- package/src/__tests__/skill-load-feature-flag.test.ts +12 -0
- package/src/__tests__/skill-memory.test.ts +2 -783
- package/src/__tests__/strip-memory-injections.test.ts +187 -0
- package/src/__tests__/subagent-detail.test.ts +84 -0
- package/src/__tests__/subagent-disposal.test.ts +308 -0
- package/src/__tests__/subagent-manager-notify.test.ts +19 -10
- package/src/__tests__/subagent-notify-parent.test.ts +390 -0
- package/src/__tests__/subagent-role-registry.test.ts +108 -0
- package/src/__tests__/subagent-tool-filtering.test.ts +71 -0
- package/src/__tests__/subagent-tools.test.ts +464 -4
- package/src/__tests__/system-prompt-ask-mode.test.ts +139 -0
- package/src/__tests__/task-memory-cleanup.test.ts +12 -12
- package/src/__tests__/terminal-sandbox.test.ts +1 -1
- package/src/__tests__/terminal-tools.test.ts +16 -29
- package/src/__tests__/test-preload.ts +18 -0
- package/src/__tests__/tool-domain-event-publisher.test.ts +0 -1
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +1 -8
- package/src/__tests__/tool-executor.test.ts +4 -27
- package/src/__tests__/tool-side-effects-slack-dm.test.ts +1 -0
- package/src/__tests__/top-level-renderer.test.ts +10 -13
- package/src/__tests__/transport-hints-queue.test.ts +77 -0
- package/src/__tests__/trust-store.test.ts +4 -4
- package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +116 -2
- package/src/__tests__/workspace-migration-028-recover-conversations-from-disk-view.test.ts +387 -0
- package/src/__tests__/workspace-migration-030-seed-pkb-autoinject.test.ts +168 -0
- package/src/__tests__/workspace-policy.test.ts +2 -7
- package/src/agent/loop.ts +6 -29
- package/src/approvals/guardian-request-resolvers.ts +24 -0
- package/src/avatar/traits-png-sync.ts +3 -3
- package/src/channels/types.ts +5 -0
- package/src/cli/__tests__/run-assistant-command.ts +56 -0
- package/src/cli/__tests__/unknown-command.test.ts +33 -0
- package/src/cli/commands/__tests__/email-download.test.ts +245 -0
- package/src/cli/commands/__tests__/email-list.test.ts +192 -0
- package/src/cli/commands/__tests__/email-register.test.ts +186 -0
- package/src/cli/commands/__tests__/email-send.test.ts +291 -0
- package/src/cli/commands/__tests__/email-status.test.ts +181 -0
- package/src/cli/commands/__tests__/email-unregister.test.ts +139 -0
- package/src/cli/commands/__tests__/routes.test.ts +562 -0
- package/src/cli/commands/conversations.ts +1 -8
- package/src/cli/commands/default-action.ts +68 -1
- package/src/cli/commands/email.ts +584 -835
- package/src/cli/commands/memory.ts +1 -34
- package/src/cli/commands/notifications.ts +7 -2
- package/src/cli/commands/oauth/__tests__/connect.test.ts +27 -0
- package/src/cli/commands/oauth/connect.ts +25 -5
- package/src/cli/commands/platform/__tests__/connect.test.ts +1 -1
- package/src/cli/commands/platform/__tests__/disconnect.test.ts +1 -1
- package/src/cli/commands/platform/__tests__/status.test.ts +1 -1
- package/src/cli/commands/routes.ts +396 -0
- package/src/cli/commands/skills.ts +130 -20
- package/src/cli/program.ts +11 -2
- package/src/cli.ts +1 -120
- package/src/config/assistant-feature-flags.ts +59 -55
- package/src/config/bundled-skills/app-builder/SKILL.md +91 -5
- package/src/config/bundled-skills/gmail/SKILL.md +13 -8
- package/src/config/bundled-skills/gmail/TOOLS.json +1 -1
- package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +2 -1
- package/src/config/bundled-skills/messaging/SKILL.md +7 -0
- package/src/config/bundled-skills/schedule/SKILL.md +22 -2
- package/src/config/bundled-skills/schedule/TOOLS.json +8 -0
- package/src/config/bundled-skills/settings/TOOLS.json +1 -1
- package/src/config/bundled-skills/settings/tools/avatar-get.ts +3 -13
- package/src/config/bundled-skills/settings/tools/avatar-remove.ts +2 -4
- package/src/config/bundled-skills/settings/tools/avatar-update.ts +5 -2
- package/src/config/bundled-skills/settings/tools/navigate-settings-tab.ts +8 -3
- package/src/config/bundled-skills/slack/SKILL.md +2 -0
- package/src/config/bundled-skills/subagent/SKILL.md +43 -3
- package/src/config/bundled-skills/subagent/TOOLS.json +29 -4
- package/src/config/env-registry.ts +63 -0
- package/src/config/feature-flag-registry.json +17 -1
- package/src/config/schema.ts +8 -0
- package/src/config/schemas/filing.ts +51 -0
- package/src/config/schemas/heartbeat.ts +15 -12
- package/src/config/schemas/memory-lifecycle.ts +12 -0
- package/src/config/schemas/security.ts +14 -0
- package/src/config/schemas/services.ts +8 -0
- package/src/credential-execution/approval-bridge.ts +0 -1
- package/src/credential-execution/managed-catalog.ts +3 -7
- package/src/daemon/app-source-watcher.ts +93 -0
- package/src/daemon/config-watcher.ts +85 -3
- package/src/daemon/context-overflow-approval.ts +0 -1
- package/src/daemon/conversation-agent-loop-handlers.ts +20 -0
- package/src/daemon/conversation-agent-loop.ts +179 -65
- package/src/daemon/conversation-attachments.ts +0 -1
- package/src/daemon/conversation-history.ts +4 -19
- package/src/daemon/conversation-lifecycle.ts +8 -14
- package/src/daemon/conversation-messaging.ts +3 -0
- package/src/daemon/conversation-process.ts +30 -8
- package/src/daemon/conversation-queue-manager.ts +8 -0
- package/src/daemon/conversation-runtime-assembly.ts +359 -308
- package/src/daemon/conversation-surfaces.ts +65 -0
- package/src/daemon/conversation-tool-setup.ts +44 -17
- package/src/daemon/conversation-workspace.ts +1 -2
- package/src/daemon/conversation.ts +19 -3
- package/src/daemon/date-context.ts +26 -53
- package/src/daemon/first-greeting.ts +1 -1
- package/src/daemon/handlers/conversations.ts +5 -7
- package/src/daemon/handlers/shared.test.ts +143 -0
- package/src/daemon/handlers/shared.ts +70 -5
- package/src/daemon/handlers/skills.ts +11 -18
- package/src/daemon/lifecycle.ts +220 -158
- package/src/daemon/message-types/conversations.ts +29 -6
- package/src/daemon/message-types/messages.ts +9 -2
- package/src/daemon/message-types/notifications.ts +12 -0
- package/src/daemon/message-types/schedules.ts +1 -0
- package/src/daemon/message-types/settings.ts +18 -0
- package/src/daemon/profiler-run-store.ts +557 -0
- package/src/daemon/server.ts +87 -10
- package/src/daemon/shutdown-handlers.ts +5 -0
- package/src/daemon/tool-side-effects.ts +23 -3
- package/src/daemon/transport-hints.ts +33 -0
- package/src/export/transcript-formatter.ts +148 -0
- package/src/filing/filing-service.ts +228 -0
- package/src/heartbeat/heartbeat-service.ts +96 -7
- package/src/index.ts +1 -1
- package/src/mcp/client.ts +6 -0
- package/src/mcp/mcp-oauth-provider.ts +149 -27
- package/src/memory/admin.ts +33 -32
- package/src/memory/app-store.ts +69 -0
- package/src/memory/conversation-bootstrap.ts +1 -1
- package/src/memory/conversation-crud.ts +151 -117
- package/src/memory/conversation-directories.ts +39 -0
- package/src/memory/conversation-group-migration.ts +66 -6
- package/src/memory/conversation-queries.ts +58 -12
- package/src/memory/conversation-title-service.ts +1 -0
- package/src/memory/db-init.ts +182 -376
- package/src/memory/embedding-local.ts +1 -1
- package/src/memory/graph/bootstrap.ts +75 -66
- package/src/memory/graph/capability-seed.ts +167 -17
- package/src/memory/graph/consolidation.ts +38 -4
- package/src/memory/graph/conversation-graph-memory.ts +133 -104
- package/src/memory/graph/extraction-job.ts +9 -4
- package/src/memory/graph/extraction.ts +66 -23
- package/src/memory/graph/graph-memory-state-store.ts +37 -0
- package/src/memory/graph/graph-search.ts +29 -15
- package/src/memory/graph/injection.ts +38 -8
- package/src/memory/graph/inspect.ts +12 -3
- package/src/memory/graph/retriever.ts +365 -262
- package/src/memory/graph/store.test.ts +48 -0
- package/src/memory/graph/store.ts +150 -11
- package/src/memory/graph/tool-handlers.ts +84 -209
- package/src/memory/graph/tools.ts +8 -52
- package/src/memory/graph/types.ts +24 -0
- package/src/memory/group-crud.ts +25 -9
- package/src/memory/job-handlers/cleanup.ts +44 -1
- package/src/memory/jobs-store.ts +70 -60
- package/src/memory/jobs-worker.ts +44 -28
- package/src/memory/llm-request-log-store.ts +96 -12
- package/src/memory/memory-recall-log-store.ts +49 -5
- package/src/memory/migrations/203-drop-memory-items-tables.ts +33 -1
- package/src/memory/migrations/206-memory-graph-node-edits.ts +19 -0
- package/src/memory/migrations/206-scrub-corrupted-image-attachments.ts +131 -0
- package/src/memory/migrations/207-conversation-graph-memory-state.ts +20 -0
- package/src/memory/migrations/208-conversations-last-message-at.ts +35 -0
- package/src/memory/migrations/209-strip-thinking-from-consolidated.ts +85 -0
- package/src/memory/migrations/210-schedule-reuse-conversation.ts +13 -0
- package/src/memory/migrations/211-memory-recall-logs-query-context.ts +21 -0
- package/src/memory/migrations/212-llm-request-logs-created-at-index.ts +19 -0
- package/src/memory/migrations/index.ts +8 -0
- package/src/memory/migrations/registry.ts +8 -0
- package/src/memory/schema/conversations.ts +14 -0
- package/src/memory/schema/infrastructure.ts +8 -1
- package/src/memory/schema/memory-core.ts +0 -51
- package/src/memory/schema/memory-graph.ts +15 -0
- package/src/memory/task-memory-cleanup.ts +30 -11
- package/src/messaging/provider.ts +1 -1
- package/src/notifications/broadcaster.ts +6 -0
- package/src/notifications/conversation-pairing.ts +12 -4
- package/src/notifications/copy-composer.ts +86 -0
- package/src/notifications/decision-engine.ts +35 -0
- package/src/notifications/emit-signal.ts +14 -0
- package/src/notifications/signal.ts +11 -0
- package/src/oauth/platform-connection.test.ts +2 -2
- package/src/oauth/seed-providers.ts +1 -0
- package/src/permissions/checker.ts +15 -4
- package/src/permissions/defaults.ts +7 -8
- package/src/permissions/permission-mode-store.ts +180 -0
- package/src/permissions/permission-mode.ts +31 -0
- package/src/permissions/prompter.ts +0 -2
- package/src/permissions/workspace-policy.ts +9 -0
- package/src/platform/client.ts +1 -1
- package/src/prompts/system-prompt.ts +59 -7
- package/src/prompts/templates/BOOTSTRAP-REFERENCE.md +100 -0
- package/src/prompts/templates/BOOTSTRAP.md +76 -162
- package/src/prompts/templates/HEARTBEAT.md +3 -1
- package/src/prompts/templates/SOUL.md +30 -9
- package/src/prompts/templates/UPDATES.md +8 -0
- package/src/providers/anthropic/client.ts +107 -219
- package/src/runtime/assistant-event-hub.ts +22 -0
- package/src/runtime/auth/route-policy.ts +23 -0
- package/src/runtime/auth/token-service.ts +8 -0
- package/src/runtime/http-server.ts +32 -2
- package/src/runtime/http-types.ts +12 -1
- package/src/runtime/migrations/vbundle-builder.ts +389 -3
- package/src/runtime/migrations/vbundle-importer.ts +8 -6
- package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +378 -0
- package/src/runtime/routes/app-management-routes.ts +1 -11
- package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +26 -0
- package/src/runtime/routes/archive-utils.ts +29 -0
- package/src/runtime/routes/avatar-routes.ts +2 -9
- package/src/runtime/routes/btw-routes.ts +14 -1
- package/src/runtime/routes/conversation-analysis-routes.ts +185 -0
- package/src/runtime/routes/conversation-management-routes.ts +1 -14
- package/src/runtime/routes/conversation-query-routes.ts +49 -3
- package/src/runtime/routes/conversation-routes.ts +270 -44
- package/src/runtime/routes/group-routes.ts +22 -8
- package/src/runtime/routes/heartbeat-routes.ts +4 -10
- package/src/runtime/routes/identity-routes.ts +53 -18
- package/src/runtime/routes/llm-context-normalization.ts +14 -10
- package/src/runtime/routes/log-export/AGENTS.md +104 -0
- package/src/runtime/routes/log-export/__tests__/workspace-allowlist-error-contract.test.ts +103 -0
- package/src/runtime/routes/log-export/__tests__/workspace-allowlist.test.ts +716 -0
- package/src/runtime/routes/log-export/workspace-allowlist.ts +458 -0
- package/src/runtime/routes/log-export-routes.ts +41 -278
- package/src/runtime/routes/memory-item-routes.test.ts +168 -233
- package/src/runtime/routes/migration-routes.ts +18 -7
- package/src/runtime/routes/profiler-routes.ts +350 -0
- package/src/runtime/routes/schedule-routes.ts +27 -12
- package/src/runtime/routes/settings-routes.ts +95 -8
- package/src/runtime/routes/subagents-routes.ts +28 -7
- package/src/runtime/routes/user-route-dispatcher.ts +223 -0
- package/src/runtime/routes/user-routes.ts +41 -0
- package/src/runtime/routes/workspace-routes.ts +0 -1
- package/src/schedule/schedule-store.ts +30 -0
- package/src/schedule/scheduler.ts +45 -18
- package/src/skills/catalog-install.ts +10 -2
- package/src/skills/inline-command-runner.ts +12 -14
- package/src/skills/managed-store.ts +2 -2
- package/src/skills/skill-memory.ts +1 -293
- package/src/subagent/index.ts +13 -3
- package/src/subagent/manager.ts +308 -29
- package/src/subagent/types.ts +68 -0
- package/src/tasks/task-runner.ts +4 -4
- package/src/tools/apps/executors.ts +29 -4
- package/src/tools/filesystem/list.ts +93 -0
- package/src/tools/permission-checker.ts +78 -18
- package/src/tools/registry.ts +4 -0
- package/src/tools/schedule/create.ts +3 -0
- package/src/tools/schedule/list.ts +1 -0
- package/src/tools/schedule/update.ts +6 -0
- package/src/tools/secret-detection-handler.ts +0 -1
- package/src/tools/shared/filesystem/errors.ts +5 -0
- package/src/tools/shared/filesystem/file-ops-service.ts +90 -2
- package/src/tools/shared/filesystem/types.ts +17 -0
- package/src/tools/shared/shell-output.ts +31 -2
- package/src/tools/skills/sandbox-runner.ts +3 -6
- package/src/tools/subagent/abort.ts +12 -2
- package/src/tools/subagent/message.ts +9 -2
- package/src/tools/subagent/notify-parent.ts +79 -0
- package/src/tools/subagent/read.ts +29 -8
- package/src/tools/subagent/resolve.ts +21 -0
- package/src/tools/subagent/spawn.ts +2 -0
- package/src/tools/subagent/status.ts +11 -1
- package/src/tools/system/avatar-generator.ts +3 -3
- package/src/tools/system/register.ts +23 -0
- package/src/tools/system/set-permission-mode.ts +103 -0
- package/src/tools/terminal/parser.ts +30 -5
- package/src/tools/terminal/safe-env.ts +16 -1
- package/src/tools/terminal/sandbox-diagnostics.ts +4 -4
- package/src/tools/terminal/sandbox.ts +4 -1
- package/src/tools/terminal/shell.ts +3 -5
- package/src/tools/tool-manifest.ts +6 -0
- package/src/tools/types.ts +2 -3
- package/src/util/logger.ts +1 -1
- package/src/util/platform.ts +50 -17
- package/src/watcher/provider-types.ts +1 -1
- package/src/workspace/migrations/023-move-config-files-to-workspace.ts +2 -2
- package/src/workspace/migrations/024-move-runtime-files-to-workspace.ts +2 -2
- package/src/workspace/migrations/028-recover-conversations-from-disk-view.ts +270 -0
- package/src/workspace/migrations/029-seed-pkb.ts +85 -0
- package/src/workspace/migrations/030-seed-pkb-autoinject.ts +73 -0
- package/src/workspace/migrations/registry.ts +6 -0
- package/src/workspace/top-level-renderer.ts +5 -9
- package/src/__tests__/cli-memory.test.ts +0 -377
- package/src/__tests__/clipboard.test.ts +0 -88
- package/src/cli/cli-memory.ts +0 -179
- package/src/util/clipboard.ts +0 -34
|
@@ -9,6 +9,10 @@ import {
|
|
|
9
9
|
resolveAppDir,
|
|
10
10
|
updateApp,
|
|
11
11
|
} from "../memory/app-store.js";
|
|
12
|
+
import {
|
|
13
|
+
getMessages,
|
|
14
|
+
updateMessageContent,
|
|
15
|
+
} from "../memory/conversation-crud.js";
|
|
12
16
|
import type { ToolExecutionResult } from "../tools/types.js";
|
|
13
17
|
import { getLogger } from "../util/logger.js";
|
|
14
18
|
import { isPlainObject } from "../util/object.js";
|
|
@@ -26,11 +30,69 @@ import type {
|
|
|
26
30
|
UiSurfaceShow,
|
|
27
31
|
} from "./message-protocol.js";
|
|
28
32
|
import { INTERACTIVE_SURFACE_TYPES } from "./message-protocol.js";
|
|
33
|
+
import type { ConversationTransportMetadata } from "./message-types/conversations.js";
|
|
29
34
|
import type { UserMessageAttachment } from "./message-types/shared.js";
|
|
30
35
|
|
|
31
36
|
const log = getLogger("conversation-surfaces");
|
|
32
37
|
|
|
33
38
|
const MAX_UNDO_DEPTH = 10;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Mark a `ui_surface` content block as completed in the database so that
|
|
42
|
+
* history reconstruction preserves the completion state. Also updates
|
|
43
|
+
* in-memory messages when available.
|
|
44
|
+
*/
|
|
45
|
+
export function markSurfaceCompleted(
|
|
46
|
+
ctx: { conversationId: string; messages?: Array<{ content: unknown }> },
|
|
47
|
+
surfaceId: string,
|
|
48
|
+
summary: string,
|
|
49
|
+
): void {
|
|
50
|
+
// Update in-memory messages when available so subsequent reads within
|
|
51
|
+
// this session see the change without waiting for DB.
|
|
52
|
+
if (ctx.messages) {
|
|
53
|
+
for (let i = ctx.messages.length - 1; i >= 0; i--) {
|
|
54
|
+
const msg = ctx.messages[i];
|
|
55
|
+
if (!Array.isArray(msg.content)) continue;
|
|
56
|
+
for (const block of msg.content) {
|
|
57
|
+
const b = block as Record<string, unknown>;
|
|
58
|
+
if (b.type === "ui_surface" && b.surfaceId === surfaceId) {
|
|
59
|
+
b.completed = true;
|
|
60
|
+
b.completionSummary = summary;
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Persist to DB.
|
|
68
|
+
const rows = getMessages(ctx.conversationId);
|
|
69
|
+
for (let r = rows.length - 1; r >= 0; r--) {
|
|
70
|
+
let parsed: unknown[];
|
|
71
|
+
try {
|
|
72
|
+
const result = JSON.parse(rows[r].content);
|
|
73
|
+
if (!Array.isArray(result)) continue;
|
|
74
|
+
parsed = result;
|
|
75
|
+
} catch {
|
|
76
|
+
// Some rows store plain text content (e.g. notification seeding) —
|
|
77
|
+
// skip them and keep scanning.
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
let found = false;
|
|
81
|
+
for (const pb of parsed) {
|
|
82
|
+
const rb = pb as Record<string, unknown>;
|
|
83
|
+
if (rb.type === "ui_surface" && rb.surfaceId === surfaceId) {
|
|
84
|
+
rb.completed = true;
|
|
85
|
+
rb.completionSummary = summary;
|
|
86
|
+
found = true;
|
|
87
|
+
break;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
if (found) {
|
|
91
|
+
updateMessageContent(rows[r].id, JSON.stringify(parsed));
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
34
96
|
const TASK_PROGRESS_TEMPLATE_FIELDS = ["title", "status", "steps"] as const;
|
|
35
97
|
|
|
36
98
|
/**
|
|
@@ -226,6 +288,7 @@ export interface SurfaceConversationContext {
|
|
|
226
288
|
metadata?: Record<string, unknown>,
|
|
227
289
|
options?: { isInteractive?: boolean },
|
|
228
290
|
displayContent?: string,
|
|
291
|
+
transport?: ConversationTransportMetadata,
|
|
229
292
|
): { queued: boolean; requestId: string; rejected?: boolean };
|
|
230
293
|
getQueueDepth(): number;
|
|
231
294
|
processMessage(
|
|
@@ -857,6 +920,7 @@ export function handleSurfaceAction(
|
|
|
857
920
|
summary,
|
|
858
921
|
submittedData: mergedData,
|
|
859
922
|
});
|
|
923
|
+
markSurfaceCompleted(ctx, surfaceId, summary);
|
|
860
924
|
}
|
|
861
925
|
|
|
862
926
|
// Extract file attachments from action data so they are sent as proper
|
|
@@ -1442,6 +1506,7 @@ export async function surfaceProxyResolver(
|
|
|
1442
1506
|
summary,
|
|
1443
1507
|
submittedData: lastAction.data,
|
|
1444
1508
|
});
|
|
1509
|
+
markSurfaceCompleted(ctx, surfaceId, summary);
|
|
1445
1510
|
} else {
|
|
1446
1511
|
ctx.sendToClient({
|
|
1447
1512
|
type: "ui_surface_dismiss",
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import { isHttpAuthDisabled } from "../config/env.js";
|
|
10
|
+
import { getIsPlatform } from "../config/env-registry.js";
|
|
10
11
|
import type { CesClient } from "../credential-execution/client.js";
|
|
11
12
|
import { getBindingByConversation } from "../memory/external-conversation-store.js";
|
|
12
13
|
import {
|
|
@@ -88,7 +89,6 @@ export interface ToolSetupContext extends SurfaceConversationContext {
|
|
|
88
89
|
assistantId?: string;
|
|
89
90
|
currentRequestId?: string;
|
|
90
91
|
workingDir: string;
|
|
91
|
-
sandboxOverride?: boolean;
|
|
92
92
|
abortController: AbortController | null;
|
|
93
93
|
/** When set, only tools in this set may execute during the current turn. */
|
|
94
94
|
allowedToolNames?: Set<string>;
|
|
@@ -185,13 +185,13 @@ export function createToolExecutor(
|
|
|
185
185
|
: undefined,
|
|
186
186
|
onOutput,
|
|
187
187
|
signal: ctx.abortController?.signal,
|
|
188
|
-
sandboxOverride: ctx.sandboxOverride,
|
|
189
188
|
allowedToolNames: ctx.allowedToolNames,
|
|
190
189
|
memoryScopeId: ctx.memoryPolicy.scopeId,
|
|
191
190
|
forcePromptSideEffects: ctx.memoryPolicy.strictSideEffects,
|
|
192
191
|
toolUseId,
|
|
193
192
|
hostBashProxy: ctx.hostBashProxy,
|
|
194
193
|
hostFileProxy: ctx.hostFileProxy,
|
|
194
|
+
isPlatformHosted: getIsPlatform(),
|
|
195
195
|
cesClient: ctx.cesClient,
|
|
196
196
|
onToolLifecycleEvent: handleToolLifecycleEvent,
|
|
197
197
|
sendToClient: (msg) => {
|
|
@@ -320,8 +320,7 @@ export function createProxyApprovalCallback(
|
|
|
320
320
|
input.matching_patterns = decision.matchingPatterns;
|
|
321
321
|
}
|
|
322
322
|
|
|
323
|
-
const riskLevel =
|
|
324
|
-
decision.kind === "ask_missing_credential" ? "high" : "medium";
|
|
323
|
+
const riskLevel: string = "medium";
|
|
325
324
|
|
|
326
325
|
// Check trust store before prompting — build candidates that mirror
|
|
327
326
|
// buildCommandCandidates() in checker.ts for network_request.
|
|
@@ -342,10 +341,7 @@ export function createProxyApprovalCallback(
|
|
|
342
341
|
);
|
|
343
342
|
if (existingRule && existingRule.decision !== "ask") {
|
|
344
343
|
if (existingRule.decision === "deny") return false;
|
|
345
|
-
|
|
346
|
-
// must fall through to prompting — mirroring the checker's behavior.
|
|
347
|
-
if (riskLevel !== "high" || existingRule.allowHighRisk === true)
|
|
348
|
-
return true;
|
|
344
|
+
return true;
|
|
349
345
|
}
|
|
350
346
|
|
|
351
347
|
// Use the checker's built-in allowlist generation for network_request
|
|
@@ -373,7 +369,6 @@ export function createProxyApprovalCallback(
|
|
|
373
369
|
allowlistOptions,
|
|
374
370
|
scopeOptions,
|
|
375
371
|
undefined,
|
|
376
|
-
undefined,
|
|
377
372
|
ctx.conversationId,
|
|
378
373
|
);
|
|
379
374
|
|
|
@@ -384,12 +379,13 @@ export function createProxyApprovalCallback(
|
|
|
384
379
|
response.selectedPattern &&
|
|
385
380
|
response.selectedScope
|
|
386
381
|
) {
|
|
382
|
+
const allowHighRisk = response.decision === "always_allow_high_risk";
|
|
387
383
|
log.info(
|
|
388
384
|
{
|
|
389
385
|
toolName,
|
|
390
386
|
pattern: response.selectedPattern,
|
|
391
387
|
scope: response.selectedScope,
|
|
392
|
-
|
|
388
|
+
allowHighRisk,
|
|
393
389
|
},
|
|
394
390
|
"Persisting always-allow trust rule (proxy)",
|
|
395
391
|
);
|
|
@@ -399,9 +395,7 @@ export function createProxyApprovalCallback(
|
|
|
399
395
|
response.selectedScope,
|
|
400
396
|
"allow",
|
|
401
397
|
100,
|
|
402
|
-
|
|
403
|
-
? { allowHighRisk: true }
|
|
404
|
-
: undefined,
|
|
398
|
+
allowHighRisk ? { allowHighRisk: true } : undefined,
|
|
405
399
|
);
|
|
406
400
|
}
|
|
407
401
|
if (
|
|
@@ -436,7 +430,7 @@ export function createProxyApprovalCallback(
|
|
|
436
430
|
* history or explicit preactivation. Without this, their tools are
|
|
437
431
|
* unavailable in fresh conversations until `skill_load` is called.
|
|
438
432
|
*/
|
|
439
|
-
const DEFAULT_PREACTIVATED_SKILL_IDS = ["tasks", "notifications"];
|
|
433
|
+
const DEFAULT_PREACTIVATED_SKILL_IDS = ["tasks", "notifications", "subagent"];
|
|
440
434
|
|
|
441
435
|
/**
|
|
442
436
|
* Subset of Conversation state that the resolveTools callback reads at each
|
|
@@ -454,9 +448,14 @@ export interface SkillProjectionContext {
|
|
|
454
448
|
readonly channelCapabilities?: {
|
|
455
449
|
channel: string;
|
|
456
450
|
supportsDynamicUi: boolean;
|
|
451
|
+
clientOS?: string;
|
|
457
452
|
};
|
|
458
453
|
/** True when no client is connected (HTTP-only). */
|
|
459
454
|
readonly hasNoClient?: boolean;
|
|
455
|
+
/** When set, only tools in this set are included in the resolved tool list (subagent delegation). */
|
|
456
|
+
subagentAllowedTools?: Set<string>;
|
|
457
|
+
/** True when this conversation belongs to a subagent spawned by SubagentManager. */
|
|
458
|
+
readonly isSubagent?: boolean;
|
|
460
459
|
}
|
|
461
460
|
|
|
462
461
|
// ── Conditional tool sets ────────────────────────────────────────────
|
|
@@ -471,6 +470,16 @@ const HOST_TOOL_NAMES = new Set([
|
|
|
471
470
|
const CLIENT_CAPABILITY_TOOL_NAMES = new Set(["app_open"]);
|
|
472
471
|
const PLATFORM_TOOL_NAMES = new Set(["request_system_permission"]);
|
|
473
472
|
|
|
473
|
+
/**
|
|
474
|
+
* Tools that should only be visible to subagent conversations. Main (parent)
|
|
475
|
+
* conversations never see these in the LLM tool definitions. Subsequent PRs
|
|
476
|
+
* will populate this set; it starts empty so there is no behavioral change.
|
|
477
|
+
*/
|
|
478
|
+
export const SUBAGENT_ONLY_TOOL_NAMES = new Set<string>([
|
|
479
|
+
"file_list",
|
|
480
|
+
"notify_parent",
|
|
481
|
+
]);
|
|
482
|
+
|
|
474
483
|
/**
|
|
475
484
|
* Determine whether a tool should be included in the LLM tool definitions
|
|
476
485
|
* for the current turn based on conversation context. Tools not active for the
|
|
@@ -495,7 +504,12 @@ export function isToolActiveForContext(
|
|
|
495
504
|
return !ctx.hasNoClient;
|
|
496
505
|
}
|
|
497
506
|
if (PLATFORM_TOOL_NAMES.has(name)) {
|
|
498
|
-
|
|
507
|
+
// Check the *client's* platform, not the daemon's process.platform.
|
|
508
|
+
// In Docker the daemon runs on Linux but the connected client may be macOS.
|
|
509
|
+
return ctx.channelCapabilities?.clientOS === "macos" && !ctx.hasNoClient;
|
|
510
|
+
}
|
|
511
|
+
if (SUBAGENT_ONLY_TOOL_NAMES.has(name)) {
|
|
512
|
+
return ctx.isSubagent === true;
|
|
499
513
|
}
|
|
500
514
|
return true;
|
|
501
515
|
}
|
|
@@ -548,18 +562,27 @@ export function createResolveToolsCallback(
|
|
|
548
562
|
isToolActiveForContext(d.name, ctx),
|
|
549
563
|
);
|
|
550
564
|
|
|
565
|
+
// When the conversation is acting as a subagent, restrict core tools to
|
|
566
|
+
// only those explicitly allowed by the parent orchestrator.
|
|
567
|
+
const scopedCoreDefs = ctx.subagentAllowedTools
|
|
568
|
+
? filteredCoreDefs.filter((d) => ctx.subagentAllowedTools!.has(d.name))
|
|
569
|
+
: filteredCoreDefs;
|
|
570
|
+
|
|
551
571
|
// Re-read MCP tool definitions from the registry each turn so conversations
|
|
552
572
|
// automatically pick up tools added/removed by `vellum mcp reload`.
|
|
553
573
|
const currentMcpDefs = getMcpToolDefinitions();
|
|
554
574
|
log.debug(
|
|
555
575
|
{
|
|
556
|
-
coreCount:
|
|
576
|
+
coreCount: scopedCoreDefs.length,
|
|
557
577
|
mcpCount: currentMcpDefs.length,
|
|
558
578
|
mcpTools: currentMcpDefs.map((d) => d.name),
|
|
559
579
|
},
|
|
560
580
|
"MCP tools resolved for turn",
|
|
561
581
|
);
|
|
562
|
-
const
|
|
582
|
+
const scopedMcpDefs = ctx.subagentAllowedTools
|
|
583
|
+
? currentMcpDefs.filter((d) => ctx.subagentAllowedTools!.has(d.name))
|
|
584
|
+
: currentMcpDefs;
|
|
585
|
+
const allBaseDefs = [...scopedCoreDefs, ...scopedMcpDefs];
|
|
563
586
|
|
|
564
587
|
const effectivePreactivated = [
|
|
565
588
|
...DEFAULT_PREACTIVATED_SKILL_IDS,
|
|
@@ -572,6 +595,10 @@ export function createResolveToolsCallback(
|
|
|
572
595
|
});
|
|
573
596
|
const turnAllowed = new Set(allBaseDefs.map((d) => d.name));
|
|
574
597
|
for (const name of projection.allowedToolNames) {
|
|
598
|
+
// When a subagent allowlist is active, exclude skill tools not on it.
|
|
599
|
+
if (ctx.subagentAllowedTools && !ctx.subagentAllowedTools.has(name)) {
|
|
600
|
+
continue;
|
|
601
|
+
}
|
|
575
602
|
turnAllowed.add(name);
|
|
576
603
|
}
|
|
577
604
|
ctx.allowedToolNames = turnAllowed;
|
|
@@ -33,8 +33,7 @@ export function refreshWorkspaceTopLevelContextIfNeeded(
|
|
|
33
33
|
currentConversationPath = `conversations/${resolvedDirName}/`;
|
|
34
34
|
}
|
|
35
35
|
ctx.workspaceTopLevelContext = renderWorkspaceTopLevelContext(snapshot, {
|
|
36
|
-
currentConversationPath
|
|
37
|
-
currentConversationAttachmentsPath: currentConversationPath
|
|
36
|
+
conversationAttachmentsPath: currentConversationPath
|
|
38
37
|
? `${currentConversationPath}attachments/`
|
|
39
38
|
: null,
|
|
40
39
|
});
|
|
@@ -114,6 +114,7 @@ import type {
|
|
|
114
114
|
UsageStats,
|
|
115
115
|
UserMessageAttachment,
|
|
116
116
|
} from "./message-protocol.js";
|
|
117
|
+
import type { ConversationTransportMetadata } from "./message-types/conversations.js";
|
|
117
118
|
import type {
|
|
118
119
|
AssistantActivityState,
|
|
119
120
|
ConfirmationStateChanged,
|
|
@@ -156,10 +157,10 @@ export class Conversation {
|
|
|
156
157
|
/** @internal */ sendToClient: (msg: ServerMessage) => void;
|
|
157
158
|
/** @internal */ eventBus = new EventBus<AssistantDomainEvents>();
|
|
158
159
|
/** @internal */ workingDir: string;
|
|
159
|
-
/** @internal */ sandboxOverride?: boolean;
|
|
160
160
|
/** @internal */ allowedToolNames?: Set<string>;
|
|
161
161
|
/** @internal */ toolsDisabledDepth = 0;
|
|
162
162
|
/** @internal */ preactivatedSkillIds?: string[];
|
|
163
|
+
/** @internal */ subagentAllowedTools?: Set<string>;
|
|
163
164
|
/** @internal */ coreToolNames: Set<string>;
|
|
164
165
|
/** @internal */ readonly skillProjectionState = new Map<string, string>();
|
|
165
166
|
/** @internal */ readonly skillProjectionCache: SkillProjectionCache = {};
|
|
@@ -174,6 +175,7 @@ export class Conversation {
|
|
|
174
175
|
/** @internal */ contextCompactedAt: number | null = null;
|
|
175
176
|
/** @internal */ currentRequestId?: string;
|
|
176
177
|
/** @internal */ hasNoClient = false;
|
|
178
|
+
/** @internal */ isSubagent = false;
|
|
177
179
|
/** @internal */ headlessLock = false;
|
|
178
180
|
/** @internal */ taskRunId?: string;
|
|
179
181
|
/** @internal */ callSessionId?: string;
|
|
@@ -248,6 +250,8 @@ export class Conversation {
|
|
|
248
250
|
public readonly hasSystemPromptOverride: boolean;
|
|
249
251
|
public memoryPolicy: ConversationMemoryPolicy;
|
|
250
252
|
/** @internal */ readonly graphMemory: ConversationGraphMemory;
|
|
253
|
+
/** @internal */ activeContextNodeIds?: string[];
|
|
254
|
+
/** @internal */ memoryScopeId?: string;
|
|
251
255
|
/** @internal */ streamThinking: boolean;
|
|
252
256
|
/** @internal */ turnCount = 0;
|
|
253
257
|
public lastAssistantAttachments: AssistantAttachmentDraft[] = [];
|
|
@@ -277,6 +281,7 @@ export class Conversation {
|
|
|
277
281
|
memoryPolicy?: ConversationMemoryPolicy,
|
|
278
282
|
sharedCesClient?: CesClient,
|
|
279
283
|
speedOverride?: Speed,
|
|
284
|
+
cacheTtl?: "5m" | "1h",
|
|
280
285
|
) {
|
|
281
286
|
this.conversationId = conversationId;
|
|
282
287
|
this.systemPrompt = systemPrompt;
|
|
@@ -415,6 +420,7 @@ export class Conversation {
|
|
|
415
420
|
...(fastModeEnabled && resolvedSpeed === "fast"
|
|
416
421
|
? { speed: resolvedSpeed }
|
|
417
422
|
: {}),
|
|
423
|
+
...(cacheTtl ? { cacheTtl } : {}),
|
|
418
424
|
},
|
|
419
425
|
toolDefs.length > 0 ? toolDefs : undefined,
|
|
420
426
|
toolDefs.length > 0 ? toolExecutor : undefined,
|
|
@@ -439,6 +445,7 @@ export class Conversation {
|
|
|
439
445
|
async loadFromDb(): Promise<void> {
|
|
440
446
|
await loadFromDbImpl(this);
|
|
441
447
|
this.restoreSurfaceStateFromHistory();
|
|
448
|
+
this.graphMemory.restoreState();
|
|
442
449
|
}
|
|
443
450
|
|
|
444
451
|
/**
|
|
@@ -533,8 +540,12 @@ export class Conversation {
|
|
|
533
540
|
}
|
|
534
541
|
}
|
|
535
542
|
|
|
536
|
-
|
|
537
|
-
this.
|
|
543
|
+
setSubagentAllowedTools(tools: Set<string> | undefined): void {
|
|
544
|
+
this.subagentAllowedTools = tools;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
setIsSubagent(value: boolean): void {
|
|
548
|
+
this.isSubagent = value;
|
|
538
549
|
}
|
|
539
550
|
|
|
540
551
|
isProcessing(): boolean {
|
|
@@ -564,6 +575,9 @@ export class Conversation {
|
|
|
564
575
|
// CES client is owned by DaemonServer — just drop the reference.
|
|
565
576
|
// Do NOT close it here; the server manages the CES lifecycle.
|
|
566
577
|
this.cesClient = undefined;
|
|
578
|
+
this.activeContextNodeIds = this.graphMemory.tracker.getActiveNodeIds();
|
|
579
|
+
this.memoryScopeId = this.memoryPolicy.scopeId;
|
|
580
|
+
this.graphMemory.persistState();
|
|
567
581
|
disposeConversation(this);
|
|
568
582
|
}
|
|
569
583
|
|
|
@@ -591,6 +605,7 @@ export class Conversation {
|
|
|
591
605
|
metadata?: Record<string, unknown>,
|
|
592
606
|
options?: { isInteractive?: boolean },
|
|
593
607
|
displayContent?: string,
|
|
608
|
+
transport?: ConversationTransportMetadata,
|
|
594
609
|
): { queued: boolean; requestId: string; rejected?: boolean } {
|
|
595
610
|
return enqueueMessageImpl(
|
|
596
611
|
this,
|
|
@@ -603,6 +618,7 @@ export class Conversation {
|
|
|
603
618
|
metadata,
|
|
604
619
|
options,
|
|
605
620
|
displayContent,
|
|
621
|
+
transport,
|
|
606
622
|
);
|
|
607
623
|
}
|
|
608
624
|
|
|
@@ -290,31 +290,14 @@ function formatLocalDate(date: Date, timeZone: string): string {
|
|
|
290
290
|
}
|
|
291
291
|
|
|
292
292
|
/**
|
|
293
|
-
* Format HH:MM
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
const fmt = new Intl.DateTimeFormat("en-US", {
|
|
300
|
-
timeZone,
|
|
301
|
-
hour: "2-digit",
|
|
302
|
-
minute: "2-digit",
|
|
303
|
-
hourCycle: "h23",
|
|
304
|
-
timeZoneName: "shortOffset",
|
|
305
|
-
});
|
|
306
|
-
const parts = fmt.formatToParts(date);
|
|
307
|
-
const get = (t: string) => parts.find((p) => p.type === t)?.value ?? "";
|
|
308
|
-
const hour = get("hour");
|
|
309
|
-
const minute = get("minute");
|
|
310
|
-
const offset = normalizeOffsetToken(get("timeZoneName"));
|
|
311
|
-
return { time: `${hour}:${minute}`, offset };
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
/**
|
|
315
|
-
* Build a compact temporal context string for model injection.
|
|
293
|
+
* Format time as HH:MM:SS with UTC offset and timezone name.
|
|
294
|
+
*
|
|
295
|
+
* Uses the timezone resolution cascade:
|
|
296
|
+
* explicit override → configured user tz → profile user tz → host fallback.
|
|
297
|
+
*
|
|
298
|
+
* Returns format: `2026-04-02 (Thu) 01:52:33 -05:00 (America/Chicago)`
|
|
316
299
|
*/
|
|
317
|
-
export function
|
|
300
|
+
export function formatTurnTimestamp(
|
|
318
301
|
options: TemporalContextOptions = {},
|
|
319
302
|
): string {
|
|
320
303
|
const now = new Date(options.nowMs ?? Date.now());
|
|
@@ -336,36 +319,26 @@ export function buildTemporalContext(
|
|
|
336
319
|
resolvedConfiguredUserTimeZone ??
|
|
337
320
|
resolvedUserTimeZone ??
|
|
338
321
|
resolvedHostTimeZone;
|
|
339
|
-
const userTimeZone = resolvedConfiguredUserTimeZone ?? resolvedUserTimeZone;
|
|
340
|
-
const timeZoneSource = resolvedTimeZone
|
|
341
|
-
? "explicit_override"
|
|
342
|
-
: resolvedConfiguredUserTimeZone
|
|
343
|
-
? "user_settings"
|
|
344
|
-
: resolvedUserTimeZone
|
|
345
|
-
? "user_profile_memory"
|
|
346
|
-
: "assistant_host_fallback";
|
|
347
|
-
const todayParts = localDateParts(now, timeZone);
|
|
348
|
-
const todayStr = formatLocalDate(now, timeZone);
|
|
349
|
-
const todayWeekday = WEEKDAY_SHORT[todayParts.weekday];
|
|
350
|
-
const { time, offset } = formatCompactTimeAndOffset(now, timeZone);
|
|
351
|
-
|
|
352
|
-
const tzSuffix =
|
|
353
|
-
timeZoneSource === "assistant_host_fallback" ? " (host fallback)" : "";
|
|
354
322
|
|
|
355
|
-
const
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
`TZ: ${timeZone}${tzSuffix}`,
|
|
359
|
-
];
|
|
360
|
-
|
|
361
|
-
if (userTimeZone && userTimeZone !== timeZone) {
|
|
362
|
-
lines.push(`User TZ: ${userTimeZone}`);
|
|
363
|
-
}
|
|
364
|
-
if (resolvedHostTimeZone !== timeZone) {
|
|
365
|
-
lines.push(`Host TZ: ${resolvedHostTimeZone}`);
|
|
366
|
-
}
|
|
323
|
+
const dateStr = formatLocalDate(now, timeZone);
|
|
324
|
+
const todayParts = localDateParts(now, timeZone);
|
|
325
|
+
const dayName = WEEKDAY_SHORT[todayParts.weekday];
|
|
367
326
|
|
|
368
|
-
|
|
327
|
+
const fmt = new Intl.DateTimeFormat("en-US", {
|
|
328
|
+
timeZone,
|
|
329
|
+
hour: "2-digit",
|
|
330
|
+
minute: "2-digit",
|
|
331
|
+
second: "2-digit",
|
|
332
|
+
hourCycle: "h23",
|
|
333
|
+
timeZoneName: "shortOffset",
|
|
334
|
+
});
|
|
335
|
+
const parts = fmt.formatToParts(now);
|
|
336
|
+
const get = (t: string) => parts.find((p) => p.type === t)?.value ?? "";
|
|
337
|
+
const hour = get("hour");
|
|
338
|
+
const minute = get("minute");
|
|
339
|
+
const second = get("second");
|
|
340
|
+
const offset = normalizeOffsetToken(get("timeZoneName"));
|
|
369
341
|
|
|
370
|
-
return
|
|
342
|
+
return `${dateStr} (${dayName}) ${hour}:${minute}:${second} ${offset} (${timeZone})`;
|
|
371
343
|
}
|
|
344
|
+
|
|
@@ -8,7 +8,7 @@ import { getWorkspacePromptPath } from "../util/platform.js";
|
|
|
8
8
|
* time," "I'm ready to be useful," and "you're in control."
|
|
9
9
|
*/
|
|
10
10
|
export const CANNED_FIRST_GREETING =
|
|
11
|
-
"Hey
|
|
11
|
+
"Hey, I'm brand new. No name, no memories, nothing yet. Think of me like a new colleague on their first day: I'll get better the more we work together. First things first, let's figure out how we work best. What should I call you?";
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* Returns `true` when all of the following are true:
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
type InterfaceId,
|
|
5
5
|
parseChannelId,
|
|
6
6
|
parseInterfaceId,
|
|
7
|
+
supportsHostProxy,
|
|
7
8
|
} from "../../channels/types.js";
|
|
8
9
|
import { getConfig } from "../../config/loader.js";
|
|
9
10
|
import {
|
|
@@ -103,13 +104,10 @@ export function makeEventSender(params: {
|
|
|
103
104
|
guardianPrincipalId: trustContext?.guardianPrincipalId ?? undefined,
|
|
104
105
|
toolName: event.toolName,
|
|
105
106
|
commandPreview:
|
|
106
|
-
redactSecrets(
|
|
107
|
-
|
|
108
|
-
) || undefined,
|
|
107
|
+
redactSecrets(summarizeToolInput(event.toolName, inputRecord)) ||
|
|
108
|
+
undefined,
|
|
109
109
|
riskLevel: event.riskLevel,
|
|
110
|
-
activityText: activityRaw
|
|
111
|
-
? redactSecrets(activityRaw)
|
|
112
|
-
: undefined,
|
|
110
|
+
activityText: activityRaw ? redactSecrets(activityRaw) : undefined,
|
|
113
111
|
executionTarget: event.executionTarget,
|
|
114
112
|
status: "pending",
|
|
115
113
|
requestCode: generateCanonicalRequestCode(),
|
|
@@ -304,7 +302,7 @@ export async function handleConversationCreate(
|
|
|
304
302
|
// Only create the host bash proxy for desktop client interfaces that can
|
|
305
303
|
// execute commands on the user's machine. Set before updateClient so
|
|
306
304
|
// updateClient's call to hostBashProxy.updateSender targets the new proxy.
|
|
307
|
-
if (transportInterface
|
|
305
|
+
if (supportsHostProxy(transportInterface)) {
|
|
308
306
|
const proxy = new HostBashProxy(sendEvent, (requestId) => {
|
|
309
307
|
pendingInteractions.resolve(requestId);
|
|
310
308
|
});
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
import { compareSemver } from "./shared.js";
|
|
4
|
+
|
|
5
|
+
describe("compareSemver", () => {
|
|
6
|
+
// ── Basic numeric comparison ──────────────────────────────────────
|
|
7
|
+
test("equal versions return 0", () => {
|
|
8
|
+
expect(compareSemver("1.2.3", "1.2.3")).toBe(0);
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
test("higher major returns positive", () => {
|
|
12
|
+
expect(compareSemver("2.0.0", "1.0.0")).toBeGreaterThan(0);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
test("lower major returns negative", () => {
|
|
16
|
+
expect(compareSemver("1.0.0", "2.0.0")).toBeLessThan(0);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test("higher minor returns positive", () => {
|
|
20
|
+
expect(compareSemver("1.3.0", "1.2.0")).toBeGreaterThan(0);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
test("higher patch returns positive", () => {
|
|
24
|
+
expect(compareSemver("1.2.4", "1.2.3")).toBeGreaterThan(0);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
// ── v/V prefix handling ───────────────────────────────────────────
|
|
28
|
+
test("strips v prefix", () => {
|
|
29
|
+
expect(compareSemver("v1.2.3", "1.2.3")).toBe(0);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test("strips V prefix", () => {
|
|
33
|
+
expect(compareSemver("V1.2.3", "1.2.3")).toBe(0);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test("compares with mixed v prefix", () => {
|
|
37
|
+
expect(compareSemver("v2.0.0", "1.0.0")).toBeGreaterThan(0);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
// ── Pre-release vs release ────────────────────────────────────────
|
|
41
|
+
test("pre-release sorts lower than release", () => {
|
|
42
|
+
expect(compareSemver("0.6.0-staging.1", "0.6.0")).toBeLessThan(0);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
test("release sorts higher than pre-release", () => {
|
|
46
|
+
expect(compareSemver("0.6.0", "0.6.0-staging.1")).toBeGreaterThan(0);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test("both without pre-release and same version return 0", () => {
|
|
50
|
+
expect(compareSemver("0.6.0", "0.6.0")).toBe(0);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// ── Pre-release numeric comparison ────────────────────────────────
|
|
54
|
+
test("staging.1 < staging.2", () => {
|
|
55
|
+
expect(compareSemver("0.6.0-staging.1", "0.6.0-staging.2")).toBeLessThan(0);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test("staging.10 > staging.2 (numeric, not lexical)", () => {
|
|
59
|
+
expect(
|
|
60
|
+
compareSemver("0.6.0-staging.10", "0.6.0-staging.2"),
|
|
61
|
+
).toBeGreaterThan(0);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test("equal pre-release returns 0", () => {
|
|
65
|
+
expect(compareSemver("0.6.0-staging.5", "0.6.0-staging.5")).toBe(0);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// ── Pre-release lexical comparison ────────────────────────────────
|
|
69
|
+
test("alpha < beta (lexical)", () => {
|
|
70
|
+
expect(compareSemver("1.0.0-alpha", "1.0.0-beta")).toBeLessThan(0);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test("rc < staging (lexical)", () => {
|
|
74
|
+
expect(compareSemver("1.0.0-rc", "1.0.0-staging")).toBeLessThan(0);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
// ── Pre-release fewer identifiers sorts earlier ───────────────────
|
|
78
|
+
test("fewer pre-release identifiers sorts earlier", () => {
|
|
79
|
+
expect(compareSemver("1.0.0-alpha", "1.0.0-alpha.1")).toBeLessThan(0);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
// ── Mixed numeric vs non-numeric per §11.4.4 ─────────────────────
|
|
83
|
+
test("numeric identifier sorts lower than non-numeric", () => {
|
|
84
|
+
expect(compareSemver("1.0.0-1", "1.0.0-alpha")).toBeLessThan(0);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test("non-numeric identifier sorts higher than numeric", () => {
|
|
88
|
+
expect(compareSemver("1.0.0-alpha", "1.0.0-1")).toBeGreaterThan(0);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
// ── Multi-segment pre-release ─────────────────────────────────────
|
|
92
|
+
test("multi-segment pre-release comparison", () => {
|
|
93
|
+
expect(
|
|
94
|
+
compareSemver("1.0.0-alpha.beta.1", "1.0.0-alpha.beta.2"),
|
|
95
|
+
).toBeLessThan(0);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// ── Hyphenated pre-release identifiers ────────────────────────────
|
|
99
|
+
test("pre-release with multiple hyphens", () => {
|
|
100
|
+
expect(
|
|
101
|
+
compareSemver("1.0.0-pre-release-1", "1.0.0-pre-release-2"),
|
|
102
|
+
).toBeLessThan(0);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
// ── Different major.minor.patch trumps pre-release ────────────────
|
|
106
|
+
test("higher patch wins regardless of pre-release", () => {
|
|
107
|
+
expect(compareSemver("0.6.1", "0.6.0-staging.99")).toBeGreaterThan(0);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
test("lower patch loses regardless of pre-release absence", () => {
|
|
111
|
+
expect(compareSemver("0.5.9", "0.6.0-staging.1")).toBeLessThan(0);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
// ── Edge cases ────────────────────────────────────────────────────
|
|
115
|
+
test("missing segments default to 0", () => {
|
|
116
|
+
expect(compareSemver("1", "1.0.0")).toBe(0);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
test("two-segment version", () => {
|
|
120
|
+
expect(compareSemver("1.2", "1.2.0")).toBe(0);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
// ── Sort integration ──────────────────────────────────────────────
|
|
124
|
+
test("Array.sort produces correct semver order", () => {
|
|
125
|
+
const versions = [
|
|
126
|
+
"0.6.0",
|
|
127
|
+
"0.6.0-staging.2",
|
|
128
|
+
"0.5.9",
|
|
129
|
+
"0.6.0-staging.10",
|
|
130
|
+
"v0.6.0-staging.1",
|
|
131
|
+
"0.6.1",
|
|
132
|
+
];
|
|
133
|
+
const sorted = [...versions].sort(compareSemver);
|
|
134
|
+
expect(sorted).toEqual([
|
|
135
|
+
"0.5.9",
|
|
136
|
+
"v0.6.0-staging.1",
|
|
137
|
+
"0.6.0-staging.2",
|
|
138
|
+
"0.6.0-staging.10",
|
|
139
|
+
"0.6.0",
|
|
140
|
+
"0.6.1",
|
|
141
|
+
]);
|
|
142
|
+
});
|
|
143
|
+
});
|