@vellumai/assistant 0.12.1 → 0.12.2-staging.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 +20 -21
- package/docs/architecture/turn-actor.md +15 -8
- package/docs/guardian-request-flow.md +16 -0
- package/node_modules/@vellumai/slack-text/src/index.ts +13 -8
- package/node_modules/@vellumai/slack-text/src/label-resolution-entities.test.ts +95 -0
- package/openapi.yaml +360 -51
- package/package.json +3 -1
- package/scripts/smoke-desktop-window-theme.ts +186 -0
- package/src/__tests__/always-loaded-tools-guard.test.ts +18 -10
- package/src/__tests__/anthropic-provider.test.ts +0 -63
- package/src/__tests__/app-compiler.test.ts +6 -0
- package/src/__tests__/app-control-tool-schemas.test.ts +5 -2
- package/src/__tests__/app-dir-path-guard.test.ts +1 -3
- package/src/__tests__/assistant-event-hub-machine-name.test.ts +6 -7
- package/src/__tests__/assistant-event-hub.test.ts +44 -0
- package/src/__tests__/computer-use-skill-manifest-regression.test.ts +1 -1
- package/src/__tests__/computer-use-skill-proxy-bridge.test.ts +5 -2
- package/src/__tests__/computer-use-tools.test.ts +19 -7
- package/src/__tests__/config-schema-cmd.test.ts +2 -2
- package/src/__tests__/config-schema.test.ts +28 -29
- package/src/__tests__/config-watcher.test.ts +23 -8
- package/src/__tests__/connection-model-compat.test.ts +2 -2
- package/src/__tests__/conversation-key-store-disk-view.test.ts +1 -1
- package/src/__tests__/conversation-pairing.test.ts +26 -0
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +4 -10
- package/src/__tests__/conversation-queue.test.ts +48 -207
- package/src/__tests__/conversation-retry-discard.test.ts +54 -1
- package/src/__tests__/conversation-runtime-assembly.test.ts +28 -0
- package/src/__tests__/conversation-skill-tools.test.ts +27 -0
- package/src/__tests__/conversation-surfaces-app-control.test.ts +8 -3
- package/src/__tests__/conversation-surfaces-point-at-budget.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-point-at-capability.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +67 -0
- package/src/__tests__/conversation-title-service.test.ts +14 -10
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +3 -5
- package/src/__tests__/conversation-tool-setup-attribution.test.ts +51 -0
- package/src/__tests__/credential-record-retire.test.ts +171 -1
- package/src/__tests__/credential-routes.test.ts +250 -3
- package/src/__tests__/credential-security-invariants.test.ts +5 -3
- package/src/__tests__/cu-unified-flow.test.ts +14 -4
- package/src/__tests__/document-sync-tags.test.ts +25 -0
- package/src/__tests__/drain-kick-guard.test.ts +0 -2
- package/src/__tests__/drain-requeue-on-contention.test.ts +0 -6
- package/src/__tests__/evict-conversations-for-reload.test.ts +1 -1
- package/src/__tests__/git-porcelain.test.ts +47 -0
- package/src/__tests__/heartbeat-service.test.ts +56 -40
- package/src/__tests__/helpers/set-workspace-mcp.ts +11 -0
- package/src/__tests__/host-bash-proxy.test.ts +26 -45
- package/src/__tests__/host-browser-proxy.test.ts +6 -17
- package/src/__tests__/host-cu-proxy.test.ts +596 -0
- package/src/__tests__/host-file-proxy-targeted.test.ts +23 -0
- package/src/__tests__/host-file-proxy.test.ts +11 -23
- package/src/__tests__/host-transfer-proxy-targeted.test.ts +30 -9
- package/src/__tests__/list-messages-camera-frame.test.ts +121 -0
- package/src/__tests__/llm-catalog-parity.test.ts +25 -0
- package/src/__tests__/mcp-auth-routes.test.ts +6 -9
- package/src/__tests__/mcp-config-secret-boundary.test.ts +78 -136
- package/src/__tests__/mcp-health-check.test.ts +8 -11
- package/src/__tests__/mcp-list-plugin-servers.test.ts +8 -10
- package/src/__tests__/message-deleted-hook-dispatch.test.ts +119 -0
- package/src/__tests__/messages-after-tiebreaker.test.ts +91 -0
- package/src/__tests__/messages-read-boundary-guard.test.ts +1 -1
- package/src/__tests__/messaging-send-tool.test.ts +355 -548
- package/src/__tests__/model-intents.test.ts +3 -0
- package/src/__tests__/notification-vellum-adapter.test.ts +85 -2
- package/src/__tests__/oauth-commands-routes.test.ts +168 -0
- package/src/__tests__/oauth-provider-profiles.test.ts +18 -0
- package/src/__tests__/oauth-provider-serializer.test.ts +22 -0
- package/src/__tests__/oauth-providers-routes.test.ts +1 -0
- package/src/__tests__/openai-provider.test.ts +1 -0
- package/src/__tests__/outlook-messaging-provider.test.ts +6 -6
- package/src/__tests__/platform-callback-registration.test.ts +8 -16
- package/src/__tests__/platform-client-verify-credential.test.ts +2 -0
- package/src/__tests__/plugin-import-boundary-guard.test.ts +14 -0
- package/src/__tests__/qdrant-manager.test.ts +132 -0
- package/src/__tests__/resolve-app.test.ts +135 -0
- package/src/__tests__/scaffold-managed-skill-tool.test.ts +229 -6
- package/src/__tests__/script-proxy-certs.test.ts +35 -0
- package/src/__tests__/secret-routes-acp-guard.test.ts +59 -1
- package/src/__tests__/service-account-token.test.ts +247 -0
- package/src/__tests__/skill-feature-flags-integration.test.ts +1 -1
- package/src/__tests__/skill-feature-flags.test.ts +2 -2
- package/src/__tests__/skills.test.ts +9 -4
- package/src/__tests__/source-fingerprint.test.ts +184 -0
- package/src/__tests__/subagent-tool-gate-mode.test.ts +70 -3
- package/src/__tests__/subagent-tools.test.ts +3 -2
- package/src/__tests__/telephony-tts-guard.test.ts +38 -3
- package/src/__tests__/test-support/computer-use-skill-harness.ts +3 -2
- package/src/__tests__/ui-channel-variants.test.ts +1 -56
- package/src/__tests__/unicode.test.ts +13 -0
- package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +4 -0
- package/src/__tests__/watch-retro-tool-availability.test.ts +8 -17
- package/src/__tests__/workspace-git-service.test.ts +26 -0
- package/src/__tests__/workspace-migration-154-repair-retired-codex-gpt-5-4-model-ids.test.ts +336 -0
- package/src/acp/__tests__/acp-claude-oauth.test.ts +257 -6
- package/src/acp/__tests__/acp-credentials.test.ts +13 -0
- package/src/acp/__tests__/agent-process.test.ts +258 -4
- package/src/acp/__tests__/auth-required.test.ts +96 -0
- package/src/acp/__tests__/claude-token-refresh.test.ts +257 -0
- package/src/acp/__tests__/client-handler.test.ts +90 -2
- package/src/acp/__tests__/helpers/acp-model-option.ts +51 -0
- package/src/acp/__tests__/prepare-agent-env.test.ts +60 -1
- package/src/acp/__tests__/session-manager-persistence.test.ts +5 -1
- package/src/acp/__tests__/session-manager-resume.test.ts +270 -6
- package/src/acp/__tests__/session-manager-usage.test.ts +4 -2
- package/src/acp/__tests__/session-manager.test.ts +895 -4
- package/src/acp/acp-claude-oauth.ts +328 -14
- package/src/acp/acp-credentials.ts +19 -0
- package/src/acp/agent-process.test.ts +4 -1
- package/src/acp/agent-process.ts +96 -10
- package/src/acp/auth-required.ts +50 -1
- package/src/acp/auto-install.test.ts +9 -7
- package/src/acp/claude-token-refresh.ts +150 -0
- package/src/acp/client-handler.ts +16 -3
- package/src/acp/model-config.test.ts +166 -0
- package/src/acp/model-config.ts +138 -0
- package/src/acp/prepare-agent-env.ts +21 -6
- package/src/acp/resolve-agent.test.ts +220 -6
- package/src/acp/resolve-agent.ts +88 -23
- package/src/acp/session-manager.test.ts +196 -3
- package/src/acp/session-manager.ts +441 -18
- package/src/acp/types.ts +50 -5
- package/src/agent/loop.ts +1 -0
- package/src/api/events/acp-session-model-update.test.ts +88 -0
- package/src/api/events/acp-session-model-update.ts +46 -0
- package/src/api/events/app-files-changed.ts +4 -3
- package/src/api/events/notification-intent.ts +2 -0
- package/src/api/events/user-message-echo.ts +7 -1
- package/src/api/index.ts +6 -0
- package/src/api/responses/conversation-message.ts +5 -0
- package/src/apps/app-store.ts +1 -1
- package/src/apps/resolve-app.ts +81 -0
- package/src/apps/source-fingerprint.ts +266 -0
- package/src/avatar/identity-avatar.ts +7 -2
- package/src/bundler/app-compiler.ts +15 -0
- package/src/calls/__tests__/barge-in-guard.test.ts +81 -0
- package/src/{__tests__ → calls/__tests__}/call-controller.test.ts +503 -82
- package/src/{__tests__ → calls/__tests__}/media-stream-audio-transcode.test.ts +1 -1
- package/src/{__tests__ → calls/__tests__}/media-stream-output.test.ts +37 -8
- package/src/{__tests__ → calls/__tests__}/media-stream-parser.test.ts +1 -1
- package/src/{__tests__ → calls/__tests__}/media-stream-server-integration.test.ts +150 -57
- package/src/{__tests__ → calls/__tests__}/media-stream-stt-session.test.ts +5 -5
- package/src/{__tests__ → calls/__tests__}/media-turn-detector.test.ts +1 -1
- package/src/{live-voice → calls}/__tests__/progress-narration.test.ts +4 -4
- package/src/{live-voice → calls}/__tests__/progress-phrases.test.ts +1 -1
- package/src/calls/__tests__/voice-control-protocol.test.ts +120 -25
- package/src/calls/__tests__/voice-leg-coordinator.test.ts +229 -0
- package/src/calls/__tests__/voice-progress-cadence.test.ts +274 -0
- package/src/{__tests__/voice-session-bridge.test.ts → calls/__tests__/voice-session-bridge-turn-flow.test.ts} +20 -12
- package/src/calls/__tests__/voice-session-bridge.test.ts +56 -1
- package/src/calls/__tests__/voice-triage-escalate.test.ts +105 -0
- package/src/calls/barge-in-guard.ts +118 -0
- package/src/calls/call-constants.ts +24 -4
- package/src/calls/call-controller.ts +499 -199
- package/src/calls/call-transport.ts +16 -0
- package/src/calls/media-stream-output.ts +32 -0
- package/src/calls/media-stream-server.ts +82 -30
- package/src/calls/media-stream-stt-session.ts +15 -0
- package/src/{live-voice → calls}/progress-narration.ts +2 -2
- package/src/calls/resolve-call-tts-provider.ts +33 -2
- package/src/calls/tts-call-strategy.ts +9 -5
- package/src/calls/types.ts +2 -0
- package/src/calls/voice-control-protocol.ts +155 -21
- package/src/calls/voice-leg-coordinator.ts +189 -0
- package/src/calls/voice-progress-cadence.ts +423 -0
- package/src/calls/voice-session-bridge.ts +92 -72
- package/src/calls/voice-triage-escalate.ts +178 -59
- package/src/cli/commands/__tests__/apps.test.ts +245 -0
- package/src/cli/commands/__tests__/bash.test.ts +53 -0
- package/src/cli/commands/__tests__/roadmap.help.test.ts +19 -0
- package/src/cli/commands/apps.help.ts +67 -13
- package/src/cli/commands/apps.ts +183 -0
- package/src/cli/commands/bash.help.ts +8 -13
- package/src/cli/commands/bash.ts +7 -25
- package/src/cli/commands/channels/__tests__/send.test.ts +218 -0
- package/src/cli/commands/channels/index.help.ts +70 -2
- package/src/cli/commands/channels/index.ts +16 -3
- package/src/cli/commands/channels/send.ts +128 -0
- package/src/cli/commands/credentials.help.ts +6 -2
- package/src/cli/commands/credentials.ts +2 -2
- package/src/cli/commands/mcp.help.ts +8 -8
- package/src/cli/commands/mcp.ts +1 -1
- package/src/cli/commands/oauth/status.ts +5 -0
- package/src/cli/commands/roadmap.help.ts +5 -1
- package/src/cli/commands/roadmap.ts +1 -1
- package/src/cli/lib/bundled-marketplace.json +103 -90
- package/src/config/__tests__/default-profile-catalog.test.ts +22 -0
- package/src/config/__tests__/memory-retrospective-schema.test.ts +15 -1
- package/src/config/__tests__/platform-identity.test.ts +255 -0
- package/src/config/__tests__/platform-rehydration.test.ts +99 -9
- package/src/config/acp-agent-write.ts +43 -0
- package/src/config/acp-defaults.test.ts +13 -3
- package/src/config/acp-defaults.ts +23 -6
- package/src/config/acp-schema.test.ts +81 -0
- package/src/config/acp-schema.ts +30 -2
- package/src/config/bundled-skills/acp/SKILL.md +22 -14
- package/src/config/bundled-skills/acp/TOOLS.json +5 -1
- package/src/config/bundled-skills/app-builder/SKILL.md +12 -9
- package/src/config/bundled-skills/app-builder/references/CUSTOM_ROUTES.md +1 -1
- package/src/config/bundled-skills/computer-use/SKILL.md +29 -2
- package/src/config/bundled-skills/computer-use/TOOLS.json +99 -0
- package/src/config/bundled-skills/computer-use/tools/computer-use-sequence.ts +12 -0
- package/src/config/bundled-skills/media-processing/services/preprocess.ts +2 -1
- package/src/config/bundled-skills/media-processing/services/processing-pipeline.ts +2 -1
- package/src/config/bundled-skills/media-processing/tools/generate-clip.ts +2 -1
- package/src/config/bundled-skills/messaging/SKILL.md +3 -3
- package/src/config/bundled-skills/messaging/TOOLS.json +3 -3
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +67 -139
- package/src/config/bundled-skills/phone-calls/references/TRANSCRIPTS.md +1 -1
- package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +2 -1
- package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +2 -1
- package/src/config/bundled-skills/schedule/SKILL.md +3 -4
- package/src/config/bundled-skills/screen-annotation/SKILL.md +8 -1
- package/src/config/bundled-skills/screen-annotation/tools/screen-annotation.test.ts +4 -1
- package/src/config/bundled-skills/skill-management/TOOLS.json +6 -2
- package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +5 -2
- package/src/config/default-profile-catalog.ts +1 -1
- package/src/config/env.ts +2 -2
- package/src/config/feature-flag-registry.json +33 -13
- package/src/config/loader.ts +2 -0
- package/src/config/memory-v3-gate.test.ts +31 -0
- package/src/config/memory-v3-gate.ts +20 -16
- package/src/config/platform-identity.ts +201 -0
- package/src/config/platform-rehydration.ts +11 -27
- package/src/config/schema.ts +2 -2
- package/src/config/schemas/__tests__/live-voice.test.ts +8 -158
- package/src/config/schemas/__tests__/voice.test.ts +148 -0
- package/src/config/schemas/calls.ts +1 -28
- package/src/config/schemas/live-voice.ts +1 -173
- package/src/config/schemas/mcp.ts +1 -1
- package/src/config/schemas/memory-retrospective.ts +10 -0
- package/src/config/schemas/services.ts +18 -0
- package/src/config/schemas/skills.ts +0 -14
- package/src/config/schemas/voice.ts +150 -0
- package/src/credential-execution/process-manager.test.ts +18 -19
- package/src/credential-health/credential-health-service.ts +11 -17
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +66 -121
- package/src/daemon/__tests__/plugin-mcp-reconcile.test.ts +4 -1
- package/src/daemon/__tests__/skill-memory-refresh-mtime.test.ts +133 -0
- package/src/daemon/app-change-notify.ts +45 -0
- package/src/daemon/config-watcher.ts +13 -194
- package/src/daemon/conversation-error.ts +3 -2
- package/src/daemon/conversation-media-retry.ts +2 -1
- package/src/daemon/conversation-process.ts +35 -64
- package/src/daemon/conversation-runtime-assembly.ts +20 -7
- package/src/daemon/conversation-skill-tools.ts +13 -31
- package/src/daemon/conversation-store.ts +1 -1
- package/src/daemon/conversation-surfaces.ts +21 -10
- package/src/daemon/conversation-tool-setup.ts +45 -59
- package/src/daemon/conversation.ts +0 -3
- package/src/daemon/dictation-profile-store.ts +2 -1
- package/src/daemon/handlers/shared.ts +2 -1
- package/src/daemon/handlers/skills.ts +6 -3
- package/src/daemon/host-bash-proxy.ts +8 -12
- package/src/daemon/host-browser-proxy.ts +15 -1
- package/src/daemon/host-cu-proxy.ts +224 -19
- package/src/daemon/host-file-proxy.ts +8 -10
- package/src/daemon/host-proxy-preactivation.ts +2 -1
- package/src/daemon/host-transfer-proxy.ts +14 -18
- package/src/daemon/lifecycle.ts +0 -5
- package/src/daemon/mcp-reload-service.ts +5 -5
- package/src/daemon/message-types/sync.ts +1 -0
- package/src/daemon/orphan-reaper.ts +1 -1
- package/src/daemon/providers-setup.ts +1 -1
- package/src/daemon/reaction-history-render.ts +2 -1
- package/src/daemon/shutdown-handlers.ts +1 -9
- package/src/daemon/skill-memory-refresh.ts +64 -0
- package/src/daemon/tool-side-effects.ts +7 -18
- package/src/desktop/__tests__/fake-desktop.ts +122 -0
- package/src/desktop/desktop-chrome-policy.test.ts +79 -0
- package/src/desktop/desktop-chrome-policy.ts +47 -0
- package/src/desktop/desktop-chrome-session.ts +37 -0
- package/src/desktop/desktop-dependencies.test.ts +102 -0
- package/src/desktop/desktop-dependencies.ts +308 -0
- package/src/desktop/desktop-feature.test.ts +18 -0
- package/src/desktop/desktop-feature.ts +19 -0
- package/src/desktop/desktop-panel-config.test.ts +135 -0
- package/src/desktop/desktop-panel-config.ts +149 -0
- package/src/desktop/desktop-session-manager.test.ts +730 -0
- package/src/desktop/desktop-session-manager.ts +850 -0
- package/src/desktop/desktop-stream-bridge.test.ts +395 -0
- package/src/desktop/desktop-stream-bridge.ts +214 -0
- package/src/desktop/desktop-wallpaper.test.ts +191 -0
- package/src/desktop/desktop-wallpaper.ts +113 -0
- package/src/desktop/desktop-window-drag.ts +177 -0
- package/src/desktop/desktop-window-manager-config.test.ts +230 -0
- package/src/desktop/desktop-window-manager-config.ts +238 -0
- package/src/desktop/desktop-window-theme.ts +170 -0
- package/src/heartbeat/heartbeat-service.ts +21 -3
- package/src/home/__tests__/feed-writer.test.ts +22 -0
- package/src/home/feed-writer.ts +11 -2
- package/src/hooks/types.ts +33 -0
- package/src/i18n/AGENTS.md +22 -0
- package/src/i18n/__tests__/i18n.test.ts +108 -0
- package/src/i18n/index.ts +16 -0
- package/src/i18n/locales.ts +93 -0
- package/src/i18n/messages.ts +61 -0
- package/src/i18n/title.ts +25 -0
- package/src/inbound/platform-callback-registration.ts +4 -5
- package/src/live-voice/__tests__/live-voice-agent-turn.test.ts +121 -0
- package/src/live-voice/__tests__/live-voice-events.test.ts +8 -7
- package/src/live-voice/__tests__/live-voice-metrics.test.ts +0 -3
- package/src/live-voice/__tests__/live-voice-progress.test.ts +94 -15
- package/src/live-voice/__tests__/live-voice-sight-frame-echo.test.ts +238 -0
- package/src/live-voice/__tests__/live-voice-vad.test.ts +2 -2
- package/src/live-voice/__tests__/protocol.test.ts +39 -0
- package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +13 -69
- package/src/live-voice/__tests__/session-controls.test.ts +79 -0
- package/src/live-voice/live-voice-photo.ts +25 -6
- package/src/live-voice/live-voice-session.ts +297 -699
- package/src/live-voice/protocol.ts +60 -2
- package/src/live-voice/session-controls.ts +111 -0
- package/src/mcp/__tests__/effective-config.test.ts +2 -2
- package/src/mcp/__tests__/workspace-mcp-config.test.ts +135 -0
- package/src/mcp/effective-config.ts +3 -3
- package/src/mcp/mcp-header-store.ts +29 -45
- package/src/mcp/spec-schema.ts +111 -0
- package/src/mcp/startup.ts +8 -4
- package/src/mcp/workspace-mcp-config.ts +188 -0
- package/src/messaging/provider.ts +10 -3
- package/src/messaging/providers/__tests__/proactive-address.test.ts +95 -0
- package/src/messaging/providers/callback-routing.ts +18 -0
- package/src/messaging/providers/channel-transport.ts +56 -0
- package/src/messaging/providers/discord/transport.ts +17 -0
- package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +6 -17
- package/src/messaging/providers/slack/adapter.ts +7 -44
- package/src/messaging/providers/slack/transport.ts +16 -0
- package/src/messaging/providers/telegram-bot/adapter.ts +1 -48
- package/src/messaging/providers/telegram-bot/client.ts +0 -26
- package/src/messaging/providers/telegram-bot/transport.ts +20 -0
- package/src/messaging/providers/whatsapp/adapter.ts +1 -48
- package/src/messaging/providers/whatsapp/transport.ts +18 -0
- package/src/monitoring/__tests__/workspace-git-heartbeat.test.ts +8 -0
- package/src/notifications/__tests__/assistant-reply-producer.test.ts +55 -0
- package/src/notifications/__tests__/guardian-feed-projection.test.ts +158 -2
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +153 -3
- package/src/notifications/__tests__/schedule-result-producer.test.ts +99 -3
- package/src/notifications/adapters/macos.ts +3 -0
- package/src/notifications/assistant-reply-producer.ts +8 -3
- package/src/notifications/conversation-pairing.ts +36 -16
- package/src/notifications/delivered-post-record.ts +11 -1
- package/src/notifications/guardian-feed-projection.ts +125 -5
- package/src/notifications/home-feed-side-effect.ts +55 -7
- package/src/notifications/schedule-result-producer.ts +66 -22
- package/src/oauth/AGENTS.md +2 -0
- package/src/oauth/__tests__/seed-providers-managed.test.ts +149 -0
- package/src/oauth/byo-connection.test.ts +105 -0
- package/src/oauth/byo-connection.ts +36 -6
- package/src/oauth/connection-resolver.test.ts +100 -0
- package/src/oauth/connection-resolver.ts +120 -3
- package/src/oauth/provider-serializer.ts +9 -0
- package/src/oauth/scope-utils.ts +24 -0
- package/src/oauth/seed-providers.ts +314 -27
- package/src/outbound-proxy/certs.ts +3 -5
- package/src/permissions/checker.test.ts +11 -6
- package/src/persistence/compaction-log-store-clickhouse.ts +3 -3
- package/src/persistence/conversation-crud.ts +71 -63
- package/src/persistence/conversation-lineage.ts +12 -4
- package/src/persistence/conversation-queries.ts +2 -2
- package/src/persistence/conversation-title-placeholders.ts +23 -0
- package/src/persistence/conversation-title-service.ts +9 -26
- package/src/persistence/embeddings/qdrant-manager.ts +163 -18
- package/src/persistence/llm-request-log-sink-clickhouse.ts +3 -3
- package/src/persistence/llm-request-log-source-clickhouse.ts +3 -3
- package/src/persistence/message-cursor.ts +78 -0
- package/src/persistence/message-reads.ts +30 -0
- package/src/persistence/migrations/157-invite-contact-id.ts +18 -6
- package/src/persistence/migrations/__tests__/157-invite-contact-id.test.ts +149 -0
- package/src/persistence/schema/memory-core.ts +5 -0
- package/src/platform/client.test.ts +4 -2
- package/src/platform/client.ts +2 -10
- package/src/plugin-api/constants.ts +2 -0
- package/src/plugin-api/index.ts +1 -0
- package/src/plugin-api/types.ts +2 -0
- package/src/plugins/defaults/index.ts +2 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-accounting.test.ts +46 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-cursor-backfill.test.ts +118 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-cursor-preserve.test.ts +83 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-enqueue.test.ts +9 -5
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +20 -10
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-prompt.test.ts +5 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-state.test.ts +219 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-sweep.test.ts +31 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-trigger-check.test.ts +63 -0
- package/src/plugins/defaults/memory/__tests__/shutdown-hook.test.ts +4 -0
- package/src/plugins/defaults/memory/context-search/agent-runner.ts +3 -2
- package/src/plugins/defaults/memory/context-search/format.ts +3 -2
- package/src/plugins/defaults/memory/context-search/sources/memory-v2.ts +6 -3
- package/src/plugins/defaults/memory/context-search/sources/workspace.ts +3 -2
- package/src/plugins/defaults/memory/graph/capability-seed.ts +6 -7
- package/src/plugins/defaults/memory/hooks/init.ts +10 -0
- package/src/plugins/defaults/memory/hooks/message-deleted.ts +21 -0
- package/src/plugins/defaults/memory/hooks/shutdown.ts +2 -0
- package/src/plugins/defaults/memory/host-utils.ts +10 -0
- package/src/plugins/defaults/memory/jobs-worker.ts +13 -0
- package/src/plugins/defaults/memory/memory-retrospective-accounting.ts +34 -60
- package/src/plugins/defaults/memory/memory-retrospective-cursor-backfill.ts +126 -0
- package/src/plugins/defaults/memory/memory-retrospective-cursor-preserve.ts +79 -0
- package/src/plugins/defaults/memory/memory-retrospective-cursor.ts +28 -0
- package/src/plugins/defaults/memory/memory-retrospective-enqueue.ts +3 -5
- package/src/plugins/defaults/memory/memory-retrospective-job.ts +12 -3
- package/src/plugins/defaults/memory/memory-retrospective-prompt.ts +1 -1
- package/src/plugins/defaults/memory/memory-retrospective-state.ts +312 -71
- package/src/plugins/defaults/memory/memory-retrospective-sweep.ts +2 -1
- package/src/plugins/defaults/memory/memory-retrospective-trigger-check.ts +5 -3
- package/src/plugins/defaults/memory/skill-md-mtime-poll.ts +9 -0
- package/src/plugins/defaults/memory/startup.ts +28 -2
- package/src/plugins/defaults/memory/substrate/__tests__/skill-content.test.ts +7 -9
- package/src/plugins/defaults/memory/substrate/page-index.ts +6 -1
- package/src/plugins/defaults/memory/substrate/skill-content.ts +2 -2
- package/src/plugins/defaults/memory/v1/graph/consolidation.ts +4 -2
- package/src/plugins/defaults/memory/v1/graph/extraction.ts +4 -2
- package/src/plugins/defaults/memory/v1/graph/retriever.ts +4 -1
- package/src/plugins/defaults/memory/v2/reranker.ts +6 -1
- package/src/plugins/defaults/memory/v3/__tests__/card.test.ts +24 -0
- package/src/plugins/defaults/memory/v3/__tests__/pool-select.test.ts +125 -0
- package/src/plugins/defaults/memory/v3/__tests__/sections.test.ts +26 -0
- package/src/plugins/defaults/memory/v3/__tests__/shadow-plugin.test.ts +30 -0
- package/src/plugins/defaults/memory/v3/card.ts +3 -2
- package/src/plugins/defaults/memory/v3/pool-select.ts +5 -3
- package/src/plugins/defaults/memory/v3/sections.ts +9 -3
- package/src/plugins/defaults/memory/v3/shadow-plugin.ts +5 -2
- package/src/plugins/defaults/memory/worker.ts +6 -0
- package/src/plugins/mcp-servers.ts +15 -75
- package/src/providers/__tests__/connection-model-compat.test.ts +3 -3
- package/src/providers/__tests__/retry-callsite.test.ts +41 -0
- package/src/providers/__tests__/retry-outbound-sanitize.test.ts +176 -0
- package/src/providers/anthropic/client.ts +0 -47
- package/src/providers/gemini/client.ts +9 -23
- package/src/providers/gemini-thought-signature.test.ts +63 -0
- package/src/providers/gemini-thought-signature.ts +44 -0
- package/src/providers/inference/__tests__/endpoint-probe.test.ts +42 -3
- package/src/providers/inference/auth.ts +2 -3
- package/src/providers/inference/connection-availability.ts +27 -8
- package/src/providers/inference/endpoint-probe.ts +10 -1
- package/src/providers/inference/resolve-auth.ts +22 -4
- package/src/providers/inference/service-account-token.ts +248 -0
- package/src/providers/model-catalog.ts +34 -0
- package/src/providers/model-intents.ts +10 -5
- package/src/providers/openai/__tests__/chat-completions-output-token-limit.test.ts +141 -0
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +146 -18
- package/src/providers/openai/__tests__/chat-completions-provider-thought-signature.test.ts +613 -0
- package/src/providers/openai/__tests__/google-thought-signature.test.ts +235 -0
- package/src/providers/openai/__tests__/tool-choice-mapping.test.ts +83 -1
- package/src/providers/openai/chat-completions-provider.ts +212 -79
- package/src/providers/openai/codex-models.ts +0 -4
- package/src/providers/openai/google-thought-signature.ts +320 -0
- package/src/providers/opencode/client.test.ts +73 -4
- package/src/providers/opencode/client.ts +60 -3
- package/src/providers/openrouter/client.ts +20 -0
- package/src/providers/outbound-request-sanitize.ts +96 -0
- package/src/providers/retry.ts +30 -19
- package/src/providers/unparseable-tool-args.ts +5 -1
- package/src/runtime/AGENTS.md +3 -1
- package/src/runtime/__tests__/channel-send.test.ts +443 -0
- package/src/runtime/__tests__/desktop-stream-upgrade.test.ts +89 -0
- package/src/runtime/__tests__/host-observe.test.ts +40 -1
- package/src/runtime/__tests__/live-voice-upgrade.test.ts +58 -0
- package/src/runtime/__tests__/runtime-ws-test-utils.ts +72 -0
- package/src/runtime/__tests__/watch-stream-upgrade.test.ts +59 -0
- package/src/runtime/agent-wake.ts +2 -1
- package/src/runtime/assistant-event-hub.ts +21 -3
- package/src/runtime/auth/same-actor.ts +38 -3
- package/src/runtime/channel-send.ts +316 -0
- package/src/runtime/host-observe.ts +11 -1
- package/src/runtime/http-server.ts +160 -151
- package/src/runtime/routes/__tests__/acp-claude-auth-routes.test.ts +16 -5
- package/src/runtime/routes/__tests__/acp-routes.test.ts +159 -4
- package/src/runtime/routes/__tests__/apps-refresh-route.test.ts +275 -0
- package/src/runtime/routes/__tests__/channel-send-routes.test.ts +127 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +108 -1
- package/src/runtime/routes/__tests__/default-provider-routes.test.ts +2 -2
- package/src/runtime/routes/__tests__/dictation-cleanup-guard.test.ts +107 -0
- package/src/runtime/routes/__tests__/mcp-add-default-risk.test.ts +16 -12
- package/src/runtime/routes/acp-claude-auth-routes.ts +11 -3
- package/src/runtime/routes/acp-routes.test.ts +15 -0
- package/src/runtime/routes/acp-routes.ts +72 -9
- package/src/runtime/routes/app-management-routes.ts +90 -2
- package/src/runtime/routes/app-routes.ts +1 -1
- package/src/runtime/routes/channel-send-routes.ts +85 -0
- package/src/runtime/routes/conversation-list-routes.ts +14 -3
- package/src/runtime/routes/conversation-query-routes.ts +40 -0
- package/src/runtime/routes/conversation-routes.ts +16 -3
- package/src/runtime/routes/credential-routes.ts +20 -14
- package/src/runtime/routes/desktop-setup-routes.test.ts +74 -0
- package/src/runtime/routes/desktop-setup-routes.ts +35 -0
- package/src/runtime/routes/diagnostics-routes.ts +61 -4
- package/src/runtime/routes/events-routes.ts +4 -0
- package/src/runtime/routes/host-cu-routes.ts +6 -0
- package/src/runtime/routes/index.ts +4 -2
- package/src/runtime/routes/log-export-routes.ts +2 -0
- package/src/runtime/routes/mcp-auth-routes.ts +39 -47
- package/src/runtime/routes/migration-routes.ts +6 -20
- package/src/runtime/routes/oauth-commands-routes.ts +50 -0
- package/src/runtime/routes/oauth-providers.ts +7 -0
- package/src/runtime/routes/platform-routes.ts +18 -15
- package/src/runtime/routes/workspace-routes.test.ts +55 -26
- package/src/runtime/routes/workspace-routes.ts +46 -63
- package/src/runtime/routes/workspace-tree-walk.test.ts +244 -0
- package/src/runtime/routes/workspace-tree-walk.ts +240 -0
- package/src/runtime/routes/workspace-utils.ts +17 -0
- package/src/runtime/services/__tests__/conversation-serializer.test.ts +28 -0
- package/src/runtime/services/conversation-serializer.ts +16 -3
- package/src/runtime/sync/resource-sync-events.ts +2 -1
- package/src/schedule/run-script.ts +2 -1
- package/src/schedule/schedule-store.ts +9 -2
- package/src/schedule/worker.ts +1 -1
- package/src/security/__tests__/ces-http-record-backend.test.ts +137 -0
- package/src/security/ces-http-record-backend.ts +120 -0
- package/src/security/ces-rpc-record-backend.ts +7 -0
- package/src/security/untrusted-content.ts +3 -1
- package/src/skills/__tests__/workspace-skill-md-signature.test.ts +60 -0
- package/src/skills/workspace-skill-md-signature.ts +50 -0
- package/src/subagent/consult-context.ts +12 -25
- package/src/tools/__tests__/side-effect-abort-guard.test.ts +1 -0
- package/src/tools/acp/list-agents.test.ts +3 -1
- package/src/tools/acp/spawn.test.ts +146 -11
- package/src/tools/acp/spawn.ts +21 -3
- package/src/tools/acp/status.test.ts +94 -0
- package/src/tools/acp/status.ts +40 -3
- package/src/tools/app-control/skill-proxy-bridge.ts +2 -1
- package/src/tools/browser/browser-execution.ts +1 -1
- package/src/tools/browser/cdp-client/accessibility-snapshot.ts +9 -3
- package/src/tools/browser/cdp-client/cdp-inspect/discovery.ts +2 -1
- package/src/tools/browser/cdp-client/extension-cdp-client.ts +3 -2
- package/src/tools/browser/network-recorder.ts +2 -1
- package/src/tools/capability-offer.test.ts +22 -2
- package/src/tools/capability-offer.ts +16 -1
- package/src/tools/computer-use/definitions.ts +121 -2
- package/src/tools/computer-use/skill-proxy-bridge.ts +2 -1
- package/src/tools/credentials/metadata-store.ts +100 -0
- package/src/tools/filesystem/search.ts +2 -1
- package/src/tools/host-filesystem/edit.test.ts +2 -1
- package/src/tools/host-filesystem/edit.ts +2 -2
- package/src/tools/host-filesystem/read.test.ts +2 -1
- package/src/tools/host-filesystem/read.ts +2 -2
- package/src/tools/host-filesystem/transfer.test.ts +2 -1
- package/src/tools/host-filesystem/transfer.ts +2 -2
- package/src/tools/host-filesystem/write.test.ts +2 -1
- package/src/tools/host-filesystem/write.ts +2 -2
- package/src/tools/host-terminal/host-shell.test.ts +16 -0
- package/src/tools/host-terminal/host-shell.ts +4 -20
- package/src/tools/network/web-search.ts +6 -1
- package/src/tools/policy-context.ts +6 -7
- package/src/tools/skills/scaffold-managed.ts +61 -3
- package/src/tools/subagent/spawn.ts +5 -8
- package/src/tools/terminal/__tests__/sanitized-bash.test.ts +57 -0
- package/src/tools/terminal/sanitized-bash.ts +105 -0
- package/src/tools/ui-surface/channel-variants.ts +10 -59
- package/src/tools/watch/watch-retro-report.ts +0 -8
- package/src/types/host-capabilities.ts +5 -3
- package/src/util/__tests__/language-subtag.test.ts +23 -1
- package/src/util/ca-bundle.ts +13 -0
- package/src/util/language-subtag.ts +16 -0
- package/src/util/platform.ts +8 -0
- package/src/util/unicode.ts +15 -1
- package/src/watch/watch-retro.ts +6 -8
- package/src/watch/watch-timeline.ts +2 -2
- package/src/workflows/run-manager.ts +2 -1
- package/src/workspace/git-porcelain.ts +66 -0
- package/src/workspace/git-service.ts +6 -55
- package/src/workspace/migrations/154-repair-retired-codex-gpt-5-4-model-ids.ts +258 -0
- package/src/workspace/migrations/155-move-front-model-config-to-voice.ts +78 -0
- package/src/workspace/migrations/156-extract-workspace-mcp-json.ts +214 -0
- package/src/workspace/migrations/__tests__/155-move-front-model-config-to-voice.test.ts +102 -0
- package/src/workspace/migrations/__tests__/156-extract-workspace-mcp-json.test.ts +199 -0
- package/src/workspace/migrations/registry.ts +6 -0
- package/src/__tests__/app-source-watcher.test.ts +0 -218
- package/src/__tests__/config-watcher-skill-reseed.test.ts +0 -359
- package/src/__tests__/speaker-identification.test.ts +0 -56
- package/src/calls/speaker-identification.ts +0 -233
- package/src/daemon/app-source-watcher.ts +0 -218
- package/src/messaging/providers/whatsapp/client.ts +0 -29
- package/src/runtime/routes/debug-bash-routes.ts +0 -173
- package/src/util/fs-watcher-error.ts +0 -36
- /package/src/{live-voice → calls}/progress-phrases.ts +0 -0
|
@@ -5,6 +5,7 @@ import type { SkillSource } from "../../config/skills.js";
|
|
|
5
5
|
import { loadSkillCatalog } from "../../config/skills.js";
|
|
6
6
|
import { refreshSkillCapabilityMemories } from "../../daemon/skill-memory-refresh.js";
|
|
7
7
|
import { emitNotificationSignal } from "../../notifications/emit-signal.js";
|
|
8
|
+
import { sanitizeMultilineMessagePreview } from "../../notifications/notification-utils.js";
|
|
8
9
|
import { getConversation } from "../../persistence/conversation-crud.js";
|
|
9
10
|
import { upsertSkillCardInsertJob } from "../../persistence/jobs-store.js";
|
|
10
11
|
import { MEMORY_RETROSPECTIVE_ORIGIN } from "../../plugins/defaults/memory/memory-retrospective-constants.js";
|
|
@@ -37,6 +38,14 @@ function sanitizeFrontmatterValue(value: string): string {
|
|
|
37
38
|
const MISSING_ACTIVATION_HINTS =
|
|
38
39
|
'activation_hints is required: pass 1-4 short trigger phrases stating the intent this skill serves (for example "user asks to deploy staging") so it can be found later by intent, not just by name.';
|
|
39
40
|
|
|
41
|
+
/**
|
|
42
|
+
* Self-correcting error for a background overwrite of an existing skill with
|
|
43
|
+
* no change summary. The schema cannot express the condition (a create and a
|
|
44
|
+
* user-directed edit need none), so the executor is the enforcement point.
|
|
45
|
+
*/
|
|
46
|
+
const MISSING_CHANGE_SUMMARY =
|
|
47
|
+
'change_summary is required when updating an existing skill: pass one or two short sentences (under 200 characters) for the person who reads the "Skill updated" notice, naming what you changed and what in the trace prompted it (for example "Added the retry after an expired session and the export endpoint that held steady.").';
|
|
48
|
+
|
|
40
49
|
/**
|
|
41
50
|
* Validate + normalize a string-array input (sanitize, drop blanks, dedupe).
|
|
42
51
|
* Returns `{ error }` on the first invalid element, or `{ value }` holding
|
|
@@ -90,6 +99,12 @@ function normalizeOptionalStringArray(
|
|
|
90
99
|
* scheduled jobs, heartbeat), where a user already looks to see what the
|
|
91
100
|
* assistant did on its own.
|
|
92
101
|
*
|
|
102
|
+
* `changeSummary` is the pass's own account of what it changed and is the
|
|
103
|
+
* notice's body: the feed item is the only place the change surfaces, and a
|
|
104
|
+
* reader triaging it there has no diff to look at, so a notice that only
|
|
105
|
+
* names the skill sends them into the skill to find out what happened. The
|
|
106
|
+
* executor refuses the overwrite without one, so this never runs without it.
|
|
107
|
+
*
|
|
93
108
|
* `sourceContextId` is a conversation id so the feed item's "Go to Convo"
|
|
94
109
|
* target resolves (see `home-feed-side-effect.ts`, which looks it up via
|
|
95
110
|
* `getConversation`): the source conversation when lineage resolved, else the
|
|
@@ -102,6 +117,7 @@ function notifyBackgroundSkillUpdate(args: {
|
|
|
102
117
|
skillId: string;
|
|
103
118
|
name: string;
|
|
104
119
|
conversationId: string;
|
|
120
|
+
changeSummary: string;
|
|
105
121
|
}): void {
|
|
106
122
|
const day = new Date().toISOString().slice(0, 10);
|
|
107
123
|
void emitNotificationSignal({
|
|
@@ -121,13 +137,13 @@ function notifyBackgroundSkillUpdate(args: {
|
|
|
121
137
|
// background, no interruption) leaves the feed writer with no summary
|
|
122
138
|
// and it skips the item entirely, so the quiet case would surface
|
|
123
139
|
// nothing at all.
|
|
124
|
-
summary:
|
|
140
|
+
summary: args.changeSummary,
|
|
125
141
|
// Named, not just "Skill updated": the feed sits several rows deep and
|
|
126
142
|
// a generic title is unscannable next to entries that name their
|
|
127
143
|
// subject (`Background job failed: memory.v2.sweep`). The word "Skill"
|
|
128
144
|
// stays because a bare skill name does not always read as one.
|
|
129
145
|
title: `Skill updated: ${args.name}`,
|
|
130
|
-
body:
|
|
146
|
+
body: args.changeSummary,
|
|
131
147
|
skillId: args.skillId,
|
|
132
148
|
},
|
|
133
149
|
attentionHints: {
|
|
@@ -313,6 +329,24 @@ export async function executeScaffoldManagedSkill(
|
|
|
313
329
|
}
|
|
314
330
|
}
|
|
315
331
|
|
|
332
|
+
// The update notice's body. Model-authored text bound for a notification
|
|
333
|
+
// surface, so it gets the same control-character strip and preview clamp as
|
|
334
|
+
// any other producer-supplied body; blank collapses to absent so the
|
|
335
|
+
// requirement below treats it as missing rather than posting an empty notice.
|
|
336
|
+
let changeSummary: string | undefined;
|
|
337
|
+
if (input.change_summary !== undefined) {
|
|
338
|
+
if (typeof input.change_summary !== "string") {
|
|
339
|
+
return {
|
|
340
|
+
content: "Error: change_summary must be a string",
|
|
341
|
+
isError: true,
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
const sanitized = sanitizeMultilineMessagePreview(input.change_summary);
|
|
345
|
+
if (sanitized) {
|
|
346
|
+
changeSummary = sanitized;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
316
350
|
const id = skillId.trim();
|
|
317
351
|
const fromRetrospective =
|
|
318
352
|
context.requestOrigin === MEMORY_RETROSPECTIVE_ORIGIN;
|
|
@@ -366,6 +400,24 @@ export async function executeScaffoldManagedSkill(
|
|
|
366
400
|
}
|
|
367
401
|
}
|
|
368
402
|
|
|
403
|
+
// A background overwrite announces itself through the notice built in
|
|
404
|
+
// notifyBackgroundSkillUpdate, whose body is this summary, so the write is
|
|
405
|
+
// refused without one. The error returns to the pass in the same turn and
|
|
406
|
+
// it retries with the field, the way a missing activation_hints does.
|
|
407
|
+
// Checked after the ownership backstop so a pass that may not touch the
|
|
408
|
+
// skill hears that first, and only for a call that asked to overwrite: a
|
|
409
|
+
// call without the flag is blocked by the managed store's own overwrite
|
|
410
|
+
// error, and hearing about the summary instead would cost it a retry that
|
|
411
|
+
// still cannot succeed. Before any write, so nothing is lost.
|
|
412
|
+
if (
|
|
413
|
+
fromRetrospective &&
|
|
414
|
+
managedSkillExistedBefore &&
|
|
415
|
+
input.overwrite === true &&
|
|
416
|
+
!changeSummary
|
|
417
|
+
) {
|
|
418
|
+
return { content: `Error: ${MISSING_CHANGE_SUMMARY}`, isError: true };
|
|
419
|
+
}
|
|
420
|
+
|
|
369
421
|
// Conversation lineage (retrospective origin only). The retrospective runs
|
|
370
422
|
// in a background fork of the conversation it distilled the procedure from,
|
|
371
423
|
// so the fork's parent is this skill's durable source conversation.
|
|
@@ -450,11 +502,17 @@ export async function executeScaffoldManagedSkill(
|
|
|
450
502
|
// skill.
|
|
451
503
|
const notifyConversationId =
|
|
452
504
|
sourceConversationId ?? retrospectiveConversationId;
|
|
453
|
-
if (
|
|
505
|
+
if (
|
|
506
|
+
fromRetrospective &&
|
|
507
|
+
managedSkillExistedBefore &&
|
|
508
|
+
notifyConversationId &&
|
|
509
|
+
changeSummary
|
|
510
|
+
) {
|
|
454
511
|
notifyBackgroundSkillUpdate({
|
|
455
512
|
skillId: id,
|
|
456
513
|
name: normalizedName,
|
|
457
514
|
conversationId: notifyConversationId,
|
|
515
|
+
changeSummary,
|
|
458
516
|
});
|
|
459
517
|
}
|
|
460
518
|
|
|
@@ -484,9 +484,9 @@ function spawnCancelledResult(label: string): ToolExecutionResult {
|
|
|
484
484
|
* repeat-spawn guard folds, so a consult has to record it the same way every
|
|
485
485
|
* other spawn does or the guard could never match one consult against another.
|
|
486
486
|
*
|
|
487
|
-
* The context pack is best effort:
|
|
488
|
-
*
|
|
489
|
-
*
|
|
487
|
+
* The context pack is best effort: each section independently falls back to
|
|
488
|
+
* null on failure, so a missing workspace or empty catalog costs only that
|
|
489
|
+
* section, never the consult.
|
|
490
490
|
*
|
|
491
491
|
* The budget is the advisor's alone. It is the only spawn the agent may issue
|
|
492
492
|
* unprompted and the only one that runs on the premium profile, so it is the
|
|
@@ -502,21 +502,18 @@ async function buildAdvisorFields(
|
|
|
502
502
|
maxRuntimeMs: number;
|
|
503
503
|
maxToolCalls: number;
|
|
504
504
|
}> {
|
|
505
|
-
const parentConversation = findConversation(context.conversationId);
|
|
506
505
|
// Situational awareness for the advisor: the parent's live tool set, the
|
|
507
506
|
// full skill catalog, and its workspace. Assembled off the per-turn
|
|
508
507
|
// ToolContext snapshot (trust, channel) so the personal-memory sections are
|
|
509
508
|
// gated exactly like the runtime injectors. A null pack just means the
|
|
510
|
-
// consult runs on the brief alone.
|
|
509
|
+
// consult runs on the brief alone. Skills come from a fresh
|
|
510
|
+
// `loadSkillCatalog()` inside the context pack, not a conversation snapshot.
|
|
511
511
|
const situationalContext = await buildAdvisorContext({
|
|
512
512
|
conversationId: context.conversationId,
|
|
513
513
|
workingDir: context.workingDir,
|
|
514
514
|
allowedToolNames: context.allowedToolNames,
|
|
515
515
|
trustClass: context.trustClass,
|
|
516
516
|
enabledPluginSet: context.enabledPluginSet,
|
|
517
|
-
// The parent's warm per-turn catalog keeps the synchronous on-disk catalog
|
|
518
|
-
// scan out of the spawn path.
|
|
519
|
-
skillCatalog: parentConversation?.skillProjectionCache?.catalog,
|
|
520
517
|
});
|
|
521
518
|
return {
|
|
522
519
|
requestText: advisorRequestText(objective, situationalContext),
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { afterEach, describe, expect, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
import { runSanitizedBash } from "../sanitized-bash.js";
|
|
4
|
+
|
|
5
|
+
describe("runSanitizedBash", () => {
|
|
6
|
+
const priorToken = process.env.CES_SERVICE_TOKEN;
|
|
7
|
+
const priorSocket = process.env.CES_LOCAL_SOCKET;
|
|
8
|
+
|
|
9
|
+
afterEach(() => {
|
|
10
|
+
if (priorToken == null) {
|
|
11
|
+
delete process.env.CES_SERVICE_TOKEN;
|
|
12
|
+
} else {
|
|
13
|
+
process.env.CES_SERVICE_TOKEN = priorToken;
|
|
14
|
+
}
|
|
15
|
+
if (priorSocket == null) {
|
|
16
|
+
delete process.env.CES_LOCAL_SOCKET;
|
|
17
|
+
} else {
|
|
18
|
+
process.env.CES_LOCAL_SOCKET = priorSocket;
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
test("runs a command and returns stdout", async () => {
|
|
23
|
+
const result = await runSanitizedBash("echo hello", 5_000);
|
|
24
|
+
expect(result.error).toBeUndefined();
|
|
25
|
+
expect(result.timedOut).toBe(false);
|
|
26
|
+
expect(result.exitCode).toBe(0);
|
|
27
|
+
expect(result.stdout.trim()).toBe("hello");
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test("forwards CES_SERVICE_TOKEN to the child", async () => {
|
|
31
|
+
process.env.CES_SERVICE_TOKEN = "vault-bearer";
|
|
32
|
+
const result = await runSanitizedBash("printenv CES_SERVICE_TOKEN", 5_000);
|
|
33
|
+
expect(result.error).toBeUndefined();
|
|
34
|
+
expect(result.exitCode).toBe(0);
|
|
35
|
+
expect(result.stdout.trim()).toBe("vault-bearer");
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test("forwards CES_LOCAL_SOCKET to the child", async () => {
|
|
39
|
+
process.env.CES_LOCAL_SOCKET = "/tmp/ces.sock";
|
|
40
|
+
const result = await runSanitizedBash("printenv CES_LOCAL_SOCKET", 5_000);
|
|
41
|
+
expect(result.error).toBeUndefined();
|
|
42
|
+
expect(result.exitCode).toBe(0);
|
|
43
|
+
expect(result.stdout.trim()).toBe("/tmp/ces.sock");
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test("times out a hung command", async () => {
|
|
47
|
+
const result = await runSanitizedBash("sleep 5", 200);
|
|
48
|
+
expect(result.error).toBeUndefined();
|
|
49
|
+
expect(result.timedOut).toBe(true);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test("rejects an empty command", async () => {
|
|
53
|
+
const result = await runSanitizedBash("");
|
|
54
|
+
expect(result.error).toBe("command is required");
|
|
55
|
+
expect(result.exitCode).toBeNull();
|
|
56
|
+
});
|
|
57
|
+
});
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spawn a shell command with the same sanitized environment the bash tool
|
|
3
|
+
* forwards to tool-call subprocesses.
|
|
4
|
+
*
|
|
5
|
+
* Used by `assistant bash` so a CLI invocation sees the same allowlisted
|
|
6
|
+
* env, workspace cwd, and platform shell as a bash tool call, without
|
|
7
|
+
* round-tripping through the assistant HTTP or IPC API.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { spawn } from "node:child_process";
|
|
11
|
+
|
|
12
|
+
import {
|
|
13
|
+
buildShellInvocation,
|
|
14
|
+
terminateProcessTree,
|
|
15
|
+
} from "../../util/host-process.js";
|
|
16
|
+
import { getWorkspaceDir } from "../../util/platform.js";
|
|
17
|
+
import { buildSanitizedEnv } from "./safe-env.js";
|
|
18
|
+
|
|
19
|
+
export const DEFAULT_SANITIZED_BASH_TIMEOUT_MS = 30_000;
|
|
20
|
+
|
|
21
|
+
export interface SanitizedBashResult {
|
|
22
|
+
stdout: string;
|
|
23
|
+
stderr: string;
|
|
24
|
+
exitCode: number | null;
|
|
25
|
+
timedOut: boolean;
|
|
26
|
+
error?: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function runSanitizedBash(
|
|
30
|
+
command: string,
|
|
31
|
+
timeoutMs: number = DEFAULT_SANITIZED_BASH_TIMEOUT_MS,
|
|
32
|
+
): Promise<SanitizedBashResult> {
|
|
33
|
+
if (!command) {
|
|
34
|
+
return Promise.resolve({
|
|
35
|
+
stdout: "",
|
|
36
|
+
stderr: "",
|
|
37
|
+
exitCode: null,
|
|
38
|
+
timedOut: false,
|
|
39
|
+
error: "command is required",
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const effectiveTimeout =
|
|
44
|
+
typeof timeoutMs === "number" && timeoutMs > 0
|
|
45
|
+
? timeoutMs
|
|
46
|
+
: DEFAULT_SANITIZED_BASH_TIMEOUT_MS;
|
|
47
|
+
|
|
48
|
+
return new Promise<SanitizedBashResult>((resolve) => {
|
|
49
|
+
const stdoutChunks: Buffer[] = [];
|
|
50
|
+
const stderrChunks: Buffer[] = [];
|
|
51
|
+
let timedOut = false;
|
|
52
|
+
let settled = false;
|
|
53
|
+
|
|
54
|
+
const finish = (result: SanitizedBashResult) => {
|
|
55
|
+
if (settled) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
settled = true;
|
|
59
|
+
resolve(result);
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const wrapped = buildShellInvocation(command);
|
|
63
|
+
const child = spawn(wrapped.command, wrapped.args, {
|
|
64
|
+
cwd: getWorkspaceDir(),
|
|
65
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
66
|
+
detached: true,
|
|
67
|
+
env: buildSanitizedEnv(),
|
|
68
|
+
windowsHide: true,
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
const timer = setTimeout(() => {
|
|
72
|
+
timedOut = true;
|
|
73
|
+
terminateProcessTree(child);
|
|
74
|
+
}, effectiveTimeout);
|
|
75
|
+
|
|
76
|
+
child.stdout.on("data", (data: Buffer) => {
|
|
77
|
+
stdoutChunks.push(data);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
child.stderr.on("data", (data: Buffer) => {
|
|
81
|
+
stderrChunks.push(data);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
child.on("close", (code) => {
|
|
85
|
+
clearTimeout(timer);
|
|
86
|
+
finish({
|
|
87
|
+
stdout: Buffer.concat(stdoutChunks).toString(),
|
|
88
|
+
stderr: Buffer.concat(stderrChunks).toString(),
|
|
89
|
+
exitCode: code,
|
|
90
|
+
timedOut,
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
child.on("error", (err) => {
|
|
95
|
+
clearTimeout(timer);
|
|
96
|
+
finish({
|
|
97
|
+
stdout: "",
|
|
98
|
+
stderr: "",
|
|
99
|
+
exitCode: null,
|
|
100
|
+
timedOut: false,
|
|
101
|
+
error: err.message,
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
}
|
|
@@ -1,68 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Conversation-stable wire projections of the UI surface tool definitions.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* Activation-rail conversations additionally get the `activation_moment`
|
|
9
|
-
* telemetry param, which no other conversation pays schema tokens for (the
|
|
10
|
-
* emit path is gated on `isActivationSession` regardless).
|
|
4
|
+
* `ui_show`, `ui_update`, and `ui_dismiss` keep one definition across every
|
|
5
|
+
* channel and across live vs clientless turns. Channel renderers enforce the
|
|
6
|
+
* surfaces they can display at execution. Clientless calls persist surface
|
|
7
|
+
* content for the next capable client that opens the conversation.
|
|
11
8
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
9
|
+
* `activation_moment` is the remaining schema projection. It applies only to
|
|
10
|
+
* activation-rail conversations, whose marker is written before the first
|
|
11
|
+
* tool resolution, so the extra property is stable for that conversation's
|
|
12
|
+
* lifetime. It must not vary by channel or turn presence.
|
|
16
13
|
*/
|
|
17
14
|
|
|
18
15
|
import type { ToolDefinition } from "../../providers/types.js";
|
|
19
16
|
import { ACTIVATION_MOMENT_PARAMS } from "../../telemetry/activation-funnel.js";
|
|
20
|
-
import { TASK_PROGRESS_TEMPLATE_SHAPE } from "./surface-shape-docs.js";
|
|
21
|
-
|
|
22
|
-
const SLACK_UI_SHOW_DESCRIPTION = `Show a live task-progress card for the current work — the only surface this channel renders. data: { template: "task_progress", templateData: ${TASK_PROGRESS_TEMPLATE_SHAPE} }. Show it early on multi-step turns and advance step statuses via ui_update as work progresses.`;
|
|
23
|
-
|
|
24
|
-
const SLACK_UI_SHOW_INPUT_SCHEMA = {
|
|
25
|
-
type: "object",
|
|
26
|
-
properties: {
|
|
27
|
-
surface_type: {
|
|
28
|
-
type: "string",
|
|
29
|
-
enum: ["card"],
|
|
30
|
-
description: "Always card on this channel",
|
|
31
|
-
},
|
|
32
|
-
title: {
|
|
33
|
-
type: "string",
|
|
34
|
-
description: "Optional surface title",
|
|
35
|
-
},
|
|
36
|
-
data: {
|
|
37
|
-
type: "object",
|
|
38
|
-
description:
|
|
39
|
-
'Card data: { template: "task_progress", templateData: { title, status, steps } }',
|
|
40
|
-
},
|
|
41
|
-
},
|
|
42
|
-
required: ["surface_type", "data"],
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Swap UI surface tool definitions for channel-appropriate variants. Returns
|
|
47
|
-
* the input array unchanged (same identity) for channels without a variant.
|
|
48
|
-
*/
|
|
49
|
-
export function projectUiToolsForChannel(
|
|
50
|
-
definitions: ToolDefinition[],
|
|
51
|
-
channel: string | undefined,
|
|
52
|
-
): ToolDefinition[] {
|
|
53
|
-
if (channel !== "slack") {
|
|
54
|
-
return definitions;
|
|
55
|
-
}
|
|
56
|
-
return definitions.map((def) =>
|
|
57
|
-
def.name === "ui_show"
|
|
58
|
-
? {
|
|
59
|
-
...def,
|
|
60
|
-
description: SLACK_UI_SHOW_DESCRIPTION,
|
|
61
|
-
input_schema: SLACK_UI_SHOW_INPUT_SCHEMA,
|
|
62
|
-
}
|
|
63
|
-
: def,
|
|
64
|
-
);
|
|
65
|
-
}
|
|
66
17
|
|
|
67
18
|
const ACTIVATION_MOMENT_PROPERTY = {
|
|
68
19
|
type: "string",
|
|
@@ -73,7 +24,7 @@ const ACTIVATION_MOMENT_PROPERTY = {
|
|
|
73
24
|
|
|
74
25
|
/**
|
|
75
26
|
* Add the optional `activation_moment` param to ui_show's schema. Applied
|
|
76
|
-
* only for activation-rail conversations (`isActivationSession`)
|
|
27
|
+
* only for activation-rail conversations (`isActivationSession`). The rail
|
|
77
28
|
* bootstrap prompt instructs the tagging, and the daemon's emit path reads
|
|
78
29
|
* the tag from tool input independently of this schema.
|
|
79
30
|
*/
|
|
@@ -14,14 +14,6 @@ import type {
|
|
|
14
14
|
/**
|
|
15
15
|
* How a watch retrospective hands its report to the daemon.
|
|
16
16
|
*
|
|
17
|
-
* **The card cannot come from `ui_show`.** A retrospective runs as a
|
|
18
|
-
* `clientless` wake, which pins the turn non-interactive, and
|
|
19
|
-
* `conversation-tool-setup` gates the whole `ui_surface` tool family on a
|
|
20
|
-
* client being present (`return channelCapabilities?.supportsDynamicUi ??
|
|
21
|
-
* !hasNoClient`). So `ui_show` is not merely denied in this turn, it is absent
|
|
22
|
-
* from the tool set, and a retrospective told to call it can only report that
|
|
23
|
-
* it cannot. This tool is an ordinary one and passes that gate untouched.
|
|
24
|
-
*
|
|
25
17
|
* **It records; it does not render.** The executor validates the payload and
|
|
26
18
|
* returns, and nothing here writes to the conversation. What makes the card is
|
|
27
19
|
* `watch-retro.ts` reading this call back out of the turn's own history once
|
|
@@ -16,6 +16,7 @@ export const HOST_PROXY_CAPABILITIES = [
|
|
|
16
16
|
"host_file",
|
|
17
17
|
"host_cu",
|
|
18
18
|
"host_cu_window_capture",
|
|
19
|
+
"host_cu_sequence",
|
|
19
20
|
"host_cu_annotate",
|
|
20
21
|
"host_browser",
|
|
21
22
|
"host_app_control",
|
|
@@ -24,11 +25,12 @@ export const HOST_PROXY_CAPABILITIES = [
|
|
|
24
25
|
|
|
25
26
|
export type HostProxyCapability = (typeof HOST_PROXY_CAPABILITIES)[number];
|
|
26
27
|
|
|
27
|
-
/** Every host capability except the
|
|
28
|
+
/** Every host capability except the macOS-only ones. */
|
|
28
29
|
const DESKTOP_SHARED_CAPABILITIES = HOST_PROXY_CAPABILITIES.filter(
|
|
29
30
|
(capability) =>
|
|
30
31
|
capability !== "host_app_control" &&
|
|
31
32
|
capability !== "host_cu_window_capture" &&
|
|
33
|
+
capability !== "host_cu_sequence" &&
|
|
32
34
|
capability !== "host_cu_annotate",
|
|
33
35
|
);
|
|
34
36
|
|
|
@@ -47,8 +49,8 @@ export type HostProxyInterfaceId = "macos" | "windows" | "linux";
|
|
|
47
49
|
* (web, ios, android, and every messaging transport) provide none.
|
|
48
50
|
*
|
|
49
51
|
* Windows and Linux run the same host proxy as macOS minus app control. The
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
+
* negotiated CU capabilities ride the host_cu transport and are claimed per
|
|
53
|
+
* connection; only the macOS native helper answers them, so a
|
|
52
54
|
* request elsewhere would reach that helper as an unknown action.
|
|
53
55
|
*
|
|
54
56
|
* macOS additionally provisions its `host_browser` proxy via the assistant
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { describe, expect, test } from "bun:test";
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
baseLanguageSubtag,
|
|
5
|
+
fixedPhraseLanguage,
|
|
6
|
+
localizedOrDefault,
|
|
7
|
+
} from "../language-subtag.js";
|
|
4
8
|
|
|
5
9
|
describe("baseLanguageSubtag", () => {
|
|
6
10
|
test("lowercases a bare tag", () => {
|
|
@@ -52,3 +56,21 @@ describe("localizedOrDefault", () => {
|
|
|
52
56
|
expect(localizedOrDefault(table, "__proto__", "fallback")).toBe("fallback");
|
|
53
57
|
});
|
|
54
58
|
});
|
|
59
|
+
|
|
60
|
+
describe("fixedPhraseLanguage", () => {
|
|
61
|
+
const table = { en: "one", es: "uno" };
|
|
62
|
+
|
|
63
|
+
test("pins English when the speaker's language has no table entry", () => {
|
|
64
|
+
expect(fixedPhraseLanguage(table, "ko")).toBe("en");
|
|
65
|
+
expect(fixedPhraseLanguage(table, "ar-EG")).toBe("en");
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test("rides the speaker's language when the table covers it", () => {
|
|
69
|
+
expect(fixedPhraseLanguage(table, "es")).toBeUndefined();
|
|
70
|
+
expect(fixedPhraseLanguage(table, "es-419")).toBeUndefined();
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test("carries no hint when the language is unknown", () => {
|
|
74
|
+
expect(fixedPhraseLanguage(table, undefined)).toBeUndefined();
|
|
75
|
+
});
|
|
76
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
|
|
3
|
+
export async function writeCombinedCABundle(
|
|
4
|
+
systemBundlePath: string,
|
|
5
|
+
extraCAPath: string,
|
|
6
|
+
outputPath: string,
|
|
7
|
+
): Promise<void> {
|
|
8
|
+
const [systemCAs, extraCAs] = await Promise.all([
|
|
9
|
+
readFile(systemBundlePath, "utf-8"),
|
|
10
|
+
readFile(extraCAPath, "utf-8"),
|
|
11
|
+
]);
|
|
12
|
+
await writeFile(outputPath, `${systemCAs}\n${extraCAs}`, { mode: 0o644 });
|
|
13
|
+
}
|
|
@@ -41,3 +41,19 @@ export function localizedOrDefault<T>(
|
|
|
41
41
|
? (table[baseLanguageSubtag(language)!] as T)
|
|
42
42
|
: fallback;
|
|
43
43
|
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* The TTS language hint for a fixed phrase picked from a localized table:
|
|
47
|
+
* "en" when the speaker has a language the table does not cover (the
|
|
48
|
+
* picker fell back to English text, which must not be synthesized under
|
|
49
|
+
* an ar/ko/ta hint), undefined otherwise (the phrase rides the speaker's
|
|
50
|
+
* language, or carries no hint at all when the language is unknown).
|
|
51
|
+
*/
|
|
52
|
+
export function fixedPhraseLanguage(
|
|
53
|
+
table: Readonly<Record<string, unknown>>,
|
|
54
|
+
language: string | undefined,
|
|
55
|
+
): string | undefined {
|
|
56
|
+
return language !== undefined && !hasLocalizedEntry(table, language)
|
|
57
|
+
? "en"
|
|
58
|
+
: undefined;
|
|
59
|
+
}
|
package/src/util/platform.ts
CHANGED
|
@@ -470,6 +470,14 @@ export function getWorkspaceConfigPath(): string {
|
|
|
470
470
|
return join(getWorkspaceDir(), "config.json");
|
|
471
471
|
}
|
|
472
472
|
|
|
473
|
+
/** Filename for the user-owned Agent Plugins MCP document. */
|
|
474
|
+
export const WORKSPACE_MCP_FILENAME = "mcp.json";
|
|
475
|
+
|
|
476
|
+
/** Returns $VELLUM_WORKSPACE_DIR/mcp.json */
|
|
477
|
+
export function getWorkspaceMcpConfigPath(): string {
|
|
478
|
+
return join(getWorkspaceDir(), WORKSPACE_MCP_FILENAME);
|
|
479
|
+
}
|
|
480
|
+
|
|
473
481
|
/** Returns $VELLUM_WORKSPACE_DIR/skills */
|
|
474
482
|
export function getWorkspaceSkillsDir(): string {
|
|
475
483
|
return join(getWorkspaceDir(), "skills");
|
package/src/util/unicode.ts
CHANGED
|
@@ -126,6 +126,15 @@ export interface DeepSanitizeResult<T> {
|
|
|
126
126
|
fixedStringCount: number;
|
|
127
127
|
}
|
|
128
128
|
|
|
129
|
+
export interface DeepSanitizeOptions {
|
|
130
|
+
/**
|
|
131
|
+
* Skip a plain-object property entirely: its value is neither scanned nor
|
|
132
|
+
* copied. For fields known to hold ASCII (base64 payloads), scanning is
|
|
133
|
+
* pure cost.
|
|
134
|
+
*/
|
|
135
|
+
skipKey?: (key: string, parent: Record<string, unknown>) => boolean;
|
|
136
|
+
}
|
|
137
|
+
|
|
129
138
|
/**
|
|
130
139
|
* Recursively walk arrays and plain objects, replacing orphaned surrogates in
|
|
131
140
|
* every string value. Non-plain objects (class instances, Date, Buffer, Map,
|
|
@@ -136,8 +145,10 @@ export interface DeepSanitizeResult<T> {
|
|
|
136
145
|
*/
|
|
137
146
|
export function stripOrphanedSurrogatesDeep<T>(
|
|
138
147
|
input: T,
|
|
148
|
+
options: DeepSanitizeOptions = {},
|
|
139
149
|
): DeepSanitizeResult<T> {
|
|
140
150
|
let fixedStringCount = 0;
|
|
151
|
+
const { skipKey } = options;
|
|
141
152
|
|
|
142
153
|
const walk = (value: unknown): { value: unknown; changed: boolean } => {
|
|
143
154
|
if (typeof value === "string") {
|
|
@@ -178,7 +189,10 @@ export function stripOrphanedSurrogatesDeep<T>(
|
|
|
178
189
|
let next: Record<string, unknown> | null = null;
|
|
179
190
|
for (let i = 0; i < keys.length; i++) {
|
|
180
191
|
const key = keys[i]!;
|
|
181
|
-
const result =
|
|
192
|
+
const result =
|
|
193
|
+
skipKey?.(key, source) === true
|
|
194
|
+
? { value: source[key], changed: false }
|
|
195
|
+
: walk(source[key]);
|
|
182
196
|
if (result.changed && next === null) {
|
|
183
197
|
next = {};
|
|
184
198
|
for (let j = 0; j < i; j++) {
|
package/src/watch/watch-retro.ts
CHANGED
|
@@ -61,14 +61,12 @@ const WATCH_RETRO_WAKE_SOURCE = "watch-retro";
|
|
|
61
61
|
* under the `watch_retro` template, which the client draws as a paged card: the
|
|
62
62
|
* record on the first page, one question per page after it.
|
|
63
63
|
*
|
|
64
|
-
* **The daemon appends the card; the model
|
|
65
|
-
* is `clientless
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
* retrospective's `skill_card` is appended by the daemon rather than requested
|
|
71
|
-
* by the model.
|
|
64
|
+
* **The daemon appends the card; the model calls `watch_retro_report`.** This
|
|
65
|
+
* wake is `clientless`. `ui_show` stays on the wire so background surfaces can
|
|
66
|
+
* persist, but this flow still reports through `watch_retro_report` so the
|
|
67
|
+
* daemon can append the card after the turn ends. The memory retrospective's
|
|
68
|
+
* `skill_card` is appended the same way: the daemon writes the surface, the
|
|
69
|
+
* model does not request it.
|
|
72
70
|
*
|
|
73
71
|
* **The append waits for the turn to end.** A `ui_surface` row written mid-turn
|
|
74
72
|
* can land between a persisted `tool_use` and its `tool_result`, an ordering
|
|
@@ -366,8 +366,8 @@ export function appendNarration(
|
|
|
366
366
|
* timeline of a session where observation stalled is simply a sparser one.
|
|
367
367
|
*
|
|
368
368
|
* The screenshot is stored only when `attachScreenshot` asks for it, and
|
|
369
|
-
* carrying one is not asking.
|
|
370
|
-
*
|
|
369
|
+
* carrying one is not asking. Watch requests a screenshot on every observe, so
|
|
370
|
+
* an observation normally has pixels available and a policy
|
|
371
371
|
* of "store what arrives" is a policy of storing every frame. Which frames are
|
|
372
372
|
* worth an image is a cadence decision, and it belongs to the caller driving
|
|
373
373
|
* the session rather than to the row writer.
|
|
@@ -33,6 +33,7 @@ import type { TrustContext } from "../daemon/trust-context-types.js";
|
|
|
33
33
|
import { wakeAgentForOpportunity } from "../runtime/agent-wake.js";
|
|
34
34
|
import { broadcastMessage } from "../runtime/assistant-event-hub.js";
|
|
35
35
|
import { getLogger } from "../util/logger.js";
|
|
36
|
+
import { safeStringSlice } from "../util/unicode.js";
|
|
36
37
|
import type { CapabilityManifest } from "./capabilities.js";
|
|
37
38
|
import {
|
|
38
39
|
normalizeCapabilityManifest,
|
|
@@ -611,7 +612,7 @@ function truncateForSummary(s: string, runId: string): string {
|
|
|
611
612
|
}
|
|
612
613
|
const omitted = s.length - MAX_SUMMARY_RESULT_CHARS;
|
|
613
614
|
return (
|
|
614
|
-
`${s
|
|
615
|
+
`${safeStringSlice(s, 0, MAX_SUMMARY_RESULT_CHARS)}… ` +
|
|
615
616
|
`[truncated ${omitted} chars — fetch the full result with manage_workflows ` +
|
|
616
617
|
`{ action: "get_result", run_id: "${runId}" }]`
|
|
617
618
|
);
|