@vellumai/assistant 0.11.1 → 0.11.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/Dockerfile +1 -3
- package/README.md +1 -1
- package/eslint.config.mjs +28 -8
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/__tests__/stripe-currency.test.ts +37 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/stripe-currency.ts +55 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/stripe-currency.test.ts +37 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/stripe-currency.ts +55 -0
- package/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/service-contracts/src/__tests__/stripe-currency.test.ts +37 -0
- package/node_modules/@vellumai/service-contracts/src/stripe-currency.ts +55 -0
- package/node_modules/@vellumai/slack-text/src/index.test.ts +48 -0
- package/node_modules/@vellumai/slack-text/src/index.ts +25 -6
- package/openapi.yaml +292 -5
- package/package.json +1 -1
- package/src/__tests__/agent-loop-resume-interrupted.test.ts +223 -0
- package/src/__tests__/byok-default-profile-ensure.test.ts +43 -0
- package/src/__tests__/cli-logger-boundary-guard.test.ts +87 -0
- package/src/__tests__/compaction-events.test.ts +139 -2
- package/src/__tests__/config-loader-backfill.test.ts +35 -3
- package/src/__tests__/config-schema.test.ts +1 -0
- package/src/__tests__/conversation-agent-loop-fatal-cleanup.test.ts +30 -0
- package/src/__tests__/conversation-agent-loop.test.ts +180 -7
- package/src/__tests__/conversation-error.test.ts +211 -4
- package/src/__tests__/conversation-options-turn-scoped-transport.test.ts +64 -0
- package/src/__tests__/conversation-queue.test.ts +126 -0
- package/src/__tests__/conversation-retry-route.test.ts +46 -7
- package/src/__tests__/conversation-slash.test.ts +4 -2
- package/src/__tests__/conversation-summarize-route.test.ts +7 -1
- package/src/__tests__/conversation-summarize-up-to.test.ts +71 -5
- package/src/__tests__/document-create-dedupe.test.ts +2 -1
- package/src/__tests__/document-find-replace.test.ts +2 -1
- package/src/__tests__/document-tool-security.test.ts +2 -1
- package/src/__tests__/document-update-default-surface.test.ts +2 -1
- package/src/__tests__/document-workspace-file.test.ts +467 -0
- package/src/__tests__/emit-signal-routing-intent.test.ts +290 -49
- package/src/__tests__/guardian-card-withdrawal.test.ts +91 -3
- package/src/__tests__/http-user-message-parity.test.ts +66 -0
- package/src/__tests__/list-messages-attachments.test.ts +87 -0
- package/src/__tests__/list-messages-provider-error.test.ts +143 -0
- package/src/__tests__/list-messages-system-card.test.ts +100 -0
- package/src/__tests__/llm-resolver.test.ts +19 -9
- package/src/__tests__/managed-profile-guard.test.ts +23 -0
- package/src/__tests__/notification-platform-adapter.test.ts +130 -2
- package/src/__tests__/notification-telegram-adapter.test.ts +6 -0
- package/src/__tests__/notification-vellum-adapter.test.ts +45 -0
- package/src/__tests__/plugin-api-model-profiles.test.ts +10 -1
- package/src/__tests__/plugin-import-boundary-guard.test.ts +3 -0
- package/src/__tests__/provider-error-scenarios.test.ts +140 -0
- package/src/__tests__/provider-send-message-override-profile.test.ts +95 -0
- package/src/__tests__/run-conversation-turn-persistence.test.ts +66 -3
- package/src/__tests__/scripted-turn-metadata-persistence.test.ts +209 -0
- package/src/__tests__/skills.test.ts +27 -0
- package/src/__tests__/slack-channels-routes.test.ts +0 -2
- package/src/__tests__/slack-share-routes.test.ts +0 -3
- package/src/__tests__/slack-users-routes.test.ts +0 -2
- package/src/__tests__/subagent-tools.test.ts +11 -0
- package/src/__tests__/tool-preview-lifecycle.test.ts +58 -0
- package/src/__tests__/tool-result-spool.test.ts +5 -4
- package/src/__tests__/turn-boundary-resolution.test.ts +53 -0
- package/src/__tests__/turn-events-store.test.ts +26 -0
- package/src/__tests__/ui-visual-surface.test.ts +695 -0
- package/src/__tests__/unified-turn-context-visible-app.test.ts +99 -0
- package/src/__tests__/visible-app-context.test.ts +189 -0
- package/src/__tests__/workspace-git-service.test.ts +173 -33
- package/src/__tests__/workspace-migration-137-repair-retired-fireworks-minimax-model-id.test.ts +157 -0
- package/src/__tests__/workspace-migration-138-backfill-home-feed-titles.test.ts +373 -0
- package/src/__tests__/workspace-migration-139-clear-renamed-cost-profile-label.test.ts +137 -0
- package/src/agent/loop.ts +36 -7
- package/src/api/events/context-window-usage.ts +31 -0
- package/src/api/events/notification-intent.ts +8 -0
- package/src/api/events/ui-surface-pending.ts +35 -0
- package/src/api/index.ts +14 -0
- package/src/api/responses/conversation-message.ts +25 -4
- package/src/api/surfaces.ts +90 -1
- package/src/approvals/guardian-card-withdrawal.ts +66 -31
- package/src/approvals/guardian-decision-primitive.ts +4 -0
- package/src/calls/__tests__/call-setup-router.test.ts +156 -26
- package/src/calls/__tests__/voice-session-bridge.test.ts +201 -12
- package/src/calls/call-setup-router.ts +80 -36
- package/src/calls/voice-session-bridge.ts +173 -26
- package/src/cli/commands/inference.help.ts +3 -3
- package/src/cli/commands/notifications.help.ts +3 -2
- package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +42 -128
- package/src/cli/commands/platform/__tests__/credits.test.ts +9 -78
- package/src/cli/commands/platform/__tests__/helpers.ts +90 -0
- package/src/cli/commands/platform/__tests__/invoices.test.ts +238 -0
- package/src/cli/commands/platform/__tests__/plans.test.ts +9 -88
- package/src/cli/commands/platform/__tests__/status.test.ts +12 -87
- package/src/cli/commands/platform/__tests__/subscription.test.ts +9 -86
- package/src/cli/commands/platform/index.help.ts +92 -0
- package/src/cli/commands/platform/index.ts +7 -0
- package/src/cli/commands/platform/invoices.ts +132 -0
- package/src/cli/commands/usage.help.ts +1 -1
- package/src/cli/lib/list-installed-plugins.ts +2 -1
- package/src/config/__tests__/default-profile-catalog.test.ts +7 -10
- package/src/config/__tests__/deployment-context-defaults.test.ts +27 -5
- package/src/config/assistant-feature-flags.ts +7 -2
- package/src/config/bundled-skills/app-builder/SKILL.md +3 -2
- package/src/config/bundled-skills/subagent/SKILL.md +3 -1
- package/src/config/bundled-skills/subagent/TOOLS.json +3 -3
- package/src/config/bundled-skills/visualize/SKILL.md +163 -0
- package/src/config/call-site-defaults.ts +3 -2
- package/src/config/default-profile-catalog.ts +75 -70
- package/src/config/default-profile-names.ts +13 -28
- package/src/config/env-registry.ts +1 -0
- package/src/config/feature-flag-registry.json +16 -0
- package/src/config/llm-resolver.ts +21 -5
- package/src/config/loader.ts +18 -11
- package/src/config/schemas/llm.ts +1 -9
- package/src/config/schemas/memory-retrospective.ts +9 -0
- package/src/config/schemas/monitoring.ts +28 -2
- package/src/config/schemas/workspace-git.ts +15 -0
- package/src/config/seed-inference-profiles.ts +19 -5
- package/src/context/post-turn-tool-result-truncation.ts +2 -2
- package/src/conversations/__tests__/message-consolidation.test.ts +54 -0
- package/src/conversations/message-consolidation.ts +17 -15
- package/src/daemon/__tests__/turn-tail-assistant-reply-notify.test.ts +182 -0
- package/src/daemon/__tests__/turn-tail-deleted-conversation.test.ts +181 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +90 -7
- package/src/daemon/conversation-agent-loop.ts +107 -22
- package/src/daemon/conversation-error.ts +169 -48
- package/src/daemon/conversation-messaging.ts +87 -4
- package/src/daemon/conversation-process.ts +57 -45
- package/src/daemon/conversation-runtime-assembly.ts +57 -0
- package/src/daemon/conversation-store.ts +36 -1
- package/src/daemon/conversation-surfaces.ts +68 -6
- package/src/daemon/conversation-turn-finalize.ts +76 -18
- package/src/daemon/conversation.ts +122 -27
- package/src/daemon/lifecycle.ts +9 -0
- package/src/daemon/message-types/conversations.ts +8 -0
- package/src/daemon/message-types/surfaces.ts +3 -0
- package/src/documents/document-store.ts +247 -10
- package/src/home/__tests__/feed-types.test.ts +43 -8
- package/src/home/__tests__/feed-writer.test.ts +59 -0
- package/src/home/feed-types.ts +34 -7
- package/src/home/feed-writer.ts +15 -6
- package/src/live-voice/__tests__/activity-label.test.ts +95 -0
- package/src/live-voice/__tests__/live-activity-reporter.test.ts +86 -5
- package/src/live-voice/__tests__/live-voice-agent-turn.test.ts +310 -25
- package/src/live-voice/__tests__/live-voice-events.test.ts +4 -4
- package/src/live-voice/__tests__/live-voice-triage-escalate.test.ts +8 -4
- package/src/live-voice/__tests__/live-voice-vad.test.ts +9 -1
- package/src/live-voice/activity-label.ts +169 -0
- package/src/live-voice/live-activity-reporter.ts +35 -6
- package/src/live-voice/live-voice-session.ts +282 -41
- package/src/live-voice/protocol.ts +35 -0
- package/src/messaging/providers/slack/__tests__/adapter-mention-rendering.test.ts +84 -2
- package/src/messaging/providers/slack/adapter.ts +86 -19
- package/src/messaging/providers/slack/api.test.ts +85 -1
- package/src/messaging/providers/slack/api.ts +121 -288
- package/src/messaging/providers/slack/client.ts +20 -251
- package/src/messaging/providers/slack/send.test.ts +4 -9
- package/src/messaging/providers/slack/send.ts +1 -1
- package/src/messaging/providers/slack/types.ts +5 -0
- package/src/messaging/providers/slack/web-api-transport.test.ts +181 -0
- package/src/messaging/providers/slack/web-api-transport.ts +367 -0
- package/src/messaging/providers/slack/withdraw.ts +6 -14
- package/src/messaging/providers/telegram-bot/send.test.ts +31 -1
- package/src/messaging/providers/telegram-bot/send.ts +23 -2
- package/src/messaging/providers/telegram-bot/withdraw.test.ts +152 -0
- package/src/messaging/providers/telegram-bot/withdraw.ts +166 -0
- package/src/monitoring/__tests__/db-integrity-sample.test.ts +18 -4
- package/src/monitoring/__tests__/file-descriptors.test.ts +144 -0
- package/src/monitoring/file-descriptors.ts +262 -0
- package/src/monitoring/process-memory.ts +4 -11
- package/src/monitoring/resource-sampler.ts +5 -0
- package/src/monitoring/worker.ts +12 -0
- package/src/notifications/__tests__/assistant-reply-producer.test.ts +740 -0
- package/src/notifications/__tests__/broadcaster.test.ts +347 -7
- package/src/notifications/__tests__/copy-composer.test.ts +53 -3
- package/src/notifications/__tests__/decision-engine.test.ts +349 -29
- package/src/notifications/__tests__/deterministic-checks.test.ts +21 -0
- package/src/notifications/__tests__/edit-notification.test.ts +330 -0
- package/src/notifications/__tests__/guardian-delivery-recorder.test.ts +71 -0
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +170 -12
- package/src/notifications/adapters/macos.ts +3 -0
- package/src/notifications/adapters/platform.ts +90 -14
- package/src/notifications/adapters/telegram.ts +6 -4
- package/src/notifications/assistant-reply-producer.ts +219 -0
- package/src/notifications/broadcaster.ts +523 -317
- package/src/notifications/copy-composer.ts +27 -6
- package/src/notifications/decision-engine.ts +154 -77
- package/src/notifications/deterministic-checks.ts +6 -7
- package/src/notifications/edit-notification.ts +8 -4
- package/src/notifications/emit-signal.ts +49 -16
- package/src/notifications/guardian-delivery-recorder.ts +8 -2
- package/src/notifications/home-feed-side-effect.ts +43 -6
- package/src/notifications/notification-utils.ts +39 -4
- package/src/notifications/signal.ts +10 -0
- package/src/notifications/types.ts +17 -0
- package/src/persistence/bookmark-crud.ts +18 -9
- package/src/persistence/conversation-attention-store.ts +26 -0
- package/src/persistence/conversation-crud.ts +79 -32
- package/src/persistence/conversation-queries.ts +30 -15
- package/src/persistence/conversation-title-service.ts +5 -170
- package/src/persistence/conversation-types.ts +192 -1
- package/src/persistence/db-init.ts +14 -1
- package/src/persistence/db-maintenance.ts +5 -4
- package/src/persistence/embeddings/__tests__/plugin-index-qdrant-init.test.ts +219 -0
- package/src/persistence/embeddings/__tests__/plugin-index.test.ts +22 -0
- package/src/persistence/embeddings/__tests__/worker-script-version.test.ts +76 -0
- package/src/persistence/embeddings/embedding-local.ts +2 -1
- package/src/persistence/embeddings/embedding-runtime-manager.ts +29 -5
- package/src/persistence/embeddings/plugin-index.ts +84 -12
- package/src/persistence/migrations/360-add-document-workspace-path.test.ts +110 -0
- package/src/persistence/migrations/360-add-document-workspace-path.ts +39 -0
- package/src/persistence/planner-statistics.ts +14 -0
- package/src/persistence/schema/documents.ts +26 -11
- package/src/persistence/steps.ts +2 -0
- package/src/platform/client.test.ts +172 -2
- package/src/platform/client.ts +178 -55
- package/src/plugin-api/conversation-turn.ts +13 -0
- package/src/plugin-api/model-profiles.test.ts +6 -2
- package/src/plugins/defaults/memory/__tests__/bookmark-crud.test.ts +34 -0
- package/src/plugins/defaults/memory/__tests__/conversation-queries.test.ts +22 -0
- package/src/plugins/defaults/memory/__tests__/jobs-store-enqueue-gate.test.ts +11 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-accounting.test.ts +199 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-enqueue.test.ts +98 -1
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +105 -1
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-sweep.test.ts +24 -2
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-wake-chain.test.ts +524 -0
- package/src/plugins/defaults/memory/__tests__/memory-tier-boundary-guard.test.ts +1 -1
- package/src/plugins/defaults/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +9 -4
- package/src/plugins/defaults/memory/host-utils.ts +5 -0
- package/src/plugins/defaults/memory/memory-retrospective-accounting.ts +105 -1
- package/src/plugins/defaults/memory/memory-retrospective-enqueue.ts +64 -5
- package/src/plugins/defaults/memory/memory-retrospective-job.ts +36 -1
- package/src/plugins/defaults/memory/memory-retrospective-sweep.ts +12 -3
- package/src/plugins/defaults/memory/src/__tests__/memory-v2-simulate-route.test.ts +1 -0
- package/src/plugins/defaults/memory/substrate/__tests__/skill-store.test.ts +195 -1
- package/src/plugins/defaults/memory/substrate/consolidation-job.ts +1 -1
- package/src/plugins/defaults/memory/substrate/skill-store.ts +167 -29
- package/src/plugins/defaults/memory/v2/__tests__/injection.test.ts +131 -0
- package/src/plugins/defaults/memory/v2/__tests__/router.test.ts +1 -0
- package/src/plugins/defaults/memory/v2/activation-log-store.ts +4 -0
- package/src/plugins/defaults/memory/v2/injection.ts +48 -3
- package/src/plugins/defaults/memory/v2/rerank-local.ts +6 -2
- package/src/plugins/defaults/memory/v3/__tests__/pool-select.test.ts +1 -1
- package/src/plugins/defaults/platform-hosted/routes/reengage.ts +1 -1
- package/src/plugins/defaults/turn-context/injectors.ts +1 -0
- package/src/plugins/defaults/turn-context/unified-turn-context.ts +26 -0
- package/src/plugins/types.ts +17 -0
- package/src/prompts/templates/system-sections.ts +2 -2
- package/src/providers/__tests__/dispatch-connection-routing.test.ts +43 -2
- package/src/providers/__tests__/vellum-mismatch-routing.test.ts +8 -0
- package/src/providers/call-site-routing.ts +77 -14
- package/src/providers/connection-resolution.ts +42 -2
- package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +127 -1
- package/src/providers/inference/adapter-factory.ts +70 -12
- package/src/providers/model-catalog.ts +0 -11
- package/src/providers/model-intents.ts +11 -0
- package/src/providers/registry.ts +8 -0
- package/src/providers/retry.ts +85 -17
- package/src/providers/types.ts +8 -1
- package/src/runtime/__tests__/agent-wake.test.ts +181 -0
- package/src/runtime/agent-wake.ts +132 -9
- package/src/runtime/channel-approval-types.ts +25 -0
- package/src/runtime/routes/__tests__/connection-routes-vs-cli-parity.test.ts +2 -2
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +11 -0
- package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +121 -9
- package/src/runtime/routes/__tests__/platform-invoice-routes.test.ts +392 -0
- package/src/runtime/routes/__tests__/slack-channel-routes.test.ts +7 -0
- package/src/runtime/routes/__tests__/workspace-commit-routes.test.ts +63 -0
- package/src/runtime/routes/consolidation-routes.ts +1 -1
- package/src/runtime/routes/conversation-management-routes.ts +18 -6
- package/src/runtime/routes/conversation-query-routes.ts +14 -23
- package/src/runtime/routes/conversation-routes.ts +113 -8
- package/src/runtime/routes/credential-routes.ts +1 -5
- package/src/runtime/routes/documents-routes.ts +207 -12
- package/src/runtime/routes/identity-routes.ts +3 -93
- package/src/runtime/routes/inbound-stages/admission-policy.test.ts +31 -1
- package/src/runtime/routes/inbound-stages/admission-policy.ts +18 -0
- package/src/runtime/routes/inference-provider-connection-routes.ts +52 -3
- package/src/runtime/routes/notification-routes.ts +4 -1
- package/src/runtime/routes/platform-routes.ts +238 -3
- package/src/runtime/routes/playground/guard.ts +1 -2
- package/src/runtime/routes/slack-channel-routes.ts +2 -4
- package/src/runtime/routes/workspace-commit-routes.ts +49 -2
- package/src/runtime/routes/workspace-routes.ts +11 -12
- package/src/runtime/routes/workspace-utils.ts +49 -2
- package/src/runtime/services/conversation-serializer.ts +2 -4
- package/src/subagent/__tests__/consult-context-gating.test.ts +98 -0
- package/src/subagent/__tests__/consult-context-skills.test.ts +82 -0
- package/src/subagent/__tests__/consult-context.test.ts +84 -0
- package/src/subagent/__tests__/consult-prompt.test.ts +36 -0
- package/src/subagent/consult-context.ts +410 -0
- package/src/subagent/consult-prompt.ts +38 -5
- package/src/subagent/manager.ts +9 -4
- package/src/subagent/types.ts +8 -0
- package/src/telemetry/telemetry-event-sources.ts +7 -0
- package/src/telemetry/telemetry-wire-source.json +1 -1
- package/src/telemetry/telemetry-wire.generated.ts +1 -0
- package/src/telemetry/turn-events-store.ts +30 -1
- package/src/telemetry/types.ts +24 -0
- package/src/telemetry/usage-telemetry-reporter.test.ts +48 -0
- package/src/tools/browser/pinned-tabs.ts +3 -1
- package/src/tools/subagent/spawn.ts +23 -1
- package/src/tools/terminal/safe-env.ts +1 -0
- package/src/tools/ui-surface/definitions.ts +39 -1
- package/src/tools/ui-surface/surface-shape-docs.ts +38 -4
- package/src/tools/ui-surface/visual-validation.ts +787 -0
- package/src/tools/workflows/run-workflow.test.ts +1 -0
- package/src/util/__tests__/short-title.test.ts +229 -0
- package/src/util/__tests__/worker-compute.test.ts +65 -0
- package/src/util/cgroup-cpu.ts +93 -0
- package/src/util/errors.ts +27 -0
- package/src/util/process-tree.ts +19 -0
- package/src/util/short-title.ts +189 -0
- package/src/util/worker-compute.ts +83 -0
- package/src/workspace/byok-default-profile-ensure.ts +25 -15
- package/src/workspace/git-service.ts +79 -30
- package/src/workspace/migrations/137-repair-retired-fireworks-minimax-model-id.ts +131 -0
- package/src/workspace/migrations/138-backfill-home-feed-titles.ts +179 -0
- package/src/workspace/migrations/139-clear-renamed-cost-profile-label.ts +92 -0
- package/src/workspace/migrations/registry.ts +6 -0
- package/src/plugins/defaults/memory/substrate/constants.ts +0 -8
|
@@ -20,6 +20,7 @@ import { DAEMON_INTERNAL_ASSISTANT_ID } from "../runtime/assistant-scope.js";
|
|
|
20
20
|
import {
|
|
21
21
|
type AdmissionPolicyResult,
|
|
22
22
|
enforceAdmissionPolicy,
|
|
23
|
+
trustedContactPromotionClearsFloor,
|
|
23
24
|
} from "../runtime/routes/inbound-stages/admission-policy.js";
|
|
24
25
|
import {
|
|
25
26
|
actorTrustContextFromVerdict,
|
|
@@ -260,13 +261,20 @@ export async function routeSetup(ctx: SetupContext): Promise<{
|
|
|
260
261
|
: await getPendingSession("phone");
|
|
261
262
|
|
|
262
263
|
// An admission floor is "active" only when a policy applies and no pending
|
|
263
|
-
// verification challenge is in flight. While active,
|
|
264
|
-
//
|
|
265
|
-
//
|
|
266
|
-
//
|
|
267
|
-
// legacy flows are preserved unchanged.
|
|
264
|
+
// verification challenge is in flight. While active, an admitted caller
|
|
265
|
+
// bypasses the legacy identity flows (unverified_caller / name_capture) and
|
|
266
|
+
// connects directly. When inactive (null policy, flag off, exempt channel,
|
|
267
|
+
// or a pending challenge), those legacy flows are preserved unchanged.
|
|
268
268
|
const floorActive = ctx.admissionPolicy != null && !pendingChallenge;
|
|
269
269
|
|
|
270
|
+
// Whether a guardian approval could lift a below-floor caller past this
|
|
271
|
+
// floor. On floors it can clear, a floor deny routes the caller into the
|
|
272
|
+
// guardian access-request flow instead of hanging up on them (see the
|
|
273
|
+
// unknown-caller branch below).
|
|
274
|
+
const approvalCouldClearFloor =
|
|
275
|
+
ctx.admissionPolicy != null &&
|
|
276
|
+
trustedContactPromotionClearsFloor(ctx.admissionPolicy);
|
|
277
|
+
|
|
270
278
|
// Inbound admission floor verdict; defaults to admitted when inactive.
|
|
271
279
|
const floorVerdict = floorActive
|
|
272
280
|
? enforceAdmissionPolicy({
|
|
@@ -303,6 +311,36 @@ export async function routeSetup(ctx: SetupContext): Promise<{
|
|
|
303
311
|
};
|
|
304
312
|
};
|
|
305
313
|
|
|
314
|
+
// Members whose channel the guardian set to `policy: 'deny'`. This gate is
|
|
315
|
+
// trust-class independent and runs ahead of every inbound flow below: the
|
|
316
|
+
// gateway derives trust class from channel STATUS alone, so a denied channel
|
|
317
|
+
// still resolves to `unverified_contact` (status `unverified`/`pending`) or
|
|
318
|
+
// `trusted_contact` (status `active`). Gating per class would leave the deny
|
|
319
|
+
// unenforced for whichever classes the branch order happened to exclude.
|
|
320
|
+
//
|
|
321
|
+
// It also outranks an active voice invite: an explicit channel-level deny is
|
|
322
|
+
// the guardian's standing ruling, and an invite must not redeem past it into
|
|
323
|
+
// a trusted contact.
|
|
324
|
+
if (actorTrust.memberRecord?.policy === "deny") {
|
|
325
|
+
log.info(
|
|
326
|
+
{
|
|
327
|
+
callSessionId: ctx.callSessionId,
|
|
328
|
+
from: ctx.from,
|
|
329
|
+
channelId: actorTrust.memberRecord.channel.id,
|
|
330
|
+
trustClass: actorTrust.trustClass,
|
|
331
|
+
},
|
|
332
|
+
"Inbound voice ACL: member policy deny",
|
|
333
|
+
);
|
|
334
|
+
return {
|
|
335
|
+
outcome: {
|
|
336
|
+
action: "deny",
|
|
337
|
+
message: "This number is not authorized to use this assistant.",
|
|
338
|
+
logReason: "Inbound voice ACL: member policy deny",
|
|
339
|
+
},
|
|
340
|
+
resolved,
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
|
|
306
344
|
if (
|
|
307
345
|
(actorTrust.trustClass === "unknown" ||
|
|
308
346
|
actorTrust.trustClass === "unverified_contact") &&
|
|
@@ -350,19 +388,46 @@ export async function routeSetup(ctx: SetupContext): Promise<{
|
|
|
350
388
|
};
|
|
351
389
|
}
|
|
352
390
|
|
|
353
|
-
//
|
|
354
|
-
//
|
|
355
|
-
//
|
|
356
|
-
//
|
|
357
|
-
//
|
|
391
|
+
// A caller who clears the floor connects directly, skipping
|
|
392
|
+
// unverified_caller / name_capture. Invites (handled above) bypass the
|
|
393
|
+
// floor as an explicit grant.
|
|
394
|
+
//
|
|
395
|
+
// A caller BELOW the floor is not simply hung up on: when a guardian
|
|
396
|
+
// approval could admit them, fall through to the legacy identity flows
|
|
397
|
+
// below, which capture the caller's name, raise a guardian access request,
|
|
398
|
+
// and hold the line for the decision. The floor still governs the outcome,
|
|
399
|
+
// because the flow only ever admits a caller the guardian approved into a
|
|
400
|
+
// trusted contact.
|
|
401
|
+
//
|
|
402
|
+
// Two cases keep the hard deny. Floors a trusted-contact promotion could
|
|
403
|
+
// not clear (`guardian_only`, `no_one`), where an approval flow would
|
|
404
|
+
// promise a decision that cannot admit the caller. And `blocked` /
|
|
405
|
+
// `revoked` members, who should hear the denial rather than an invitation
|
|
406
|
+
// to identify themselves. (The third governance signal, a channel
|
|
407
|
+
// `policy` of `deny`, is gated ahead of this whole branch.)
|
|
358
408
|
if (floorActive) {
|
|
359
|
-
if (
|
|
409
|
+
if (floorVerdict.admitted) {
|
|
410
|
+
return {
|
|
411
|
+
outcome: { action: "normal_call" as const, isInbound: true },
|
|
412
|
+
resolved,
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
if (
|
|
416
|
+
!approvalCouldClearFloor ||
|
|
417
|
+
floorVerdict.reason === "member_blocked" ||
|
|
418
|
+
floorVerdict.reason === "member_revoked"
|
|
419
|
+
) {
|
|
360
420
|
return floorDeny(floorVerdict);
|
|
361
421
|
}
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
422
|
+
log.info(
|
|
423
|
+
{
|
|
424
|
+
callSessionId: ctx.callSessionId,
|
|
425
|
+
from: ctx.from,
|
|
426
|
+
trustClass: actorTrust.trustClass,
|
|
427
|
+
effectivePolicy: floorVerdict.effectivePolicy,
|
|
428
|
+
},
|
|
429
|
+
"Inbound voice ACL: caller below admission floor, routing to guardian access request",
|
|
430
|
+
);
|
|
366
431
|
}
|
|
367
432
|
|
|
368
433
|
// Known caller whose channel hasn't passed verification yet —
|
|
@@ -413,27 +478,6 @@ export async function routeSetup(ctx: SetupContext): Promise<{
|
|
|
413
478
|
};
|
|
414
479
|
}
|
|
415
480
|
|
|
416
|
-
// Members with policy: 'deny'
|
|
417
|
-
if (actorTrust.memberRecord?.policy === "deny") {
|
|
418
|
-
log.info(
|
|
419
|
-
{
|
|
420
|
-
callSessionId: ctx.callSessionId,
|
|
421
|
-
from: ctx.from,
|
|
422
|
-
channelId: actorTrust.memberRecord.channel.id,
|
|
423
|
-
trustClass: actorTrust.trustClass,
|
|
424
|
-
},
|
|
425
|
-
"Inbound voice ACL: member policy deny",
|
|
426
|
-
);
|
|
427
|
-
return {
|
|
428
|
-
outcome: {
|
|
429
|
-
action: "deny",
|
|
430
|
-
message: "This number is not authorized to use this assistant.",
|
|
431
|
-
logReason: "Inbound voice ACL: member policy deny",
|
|
432
|
-
},
|
|
433
|
-
resolved,
|
|
434
|
-
};
|
|
435
|
-
}
|
|
436
|
-
|
|
437
481
|
// Guardian verification challenge
|
|
438
482
|
if (pendingChallenge) {
|
|
439
483
|
return {
|
|
@@ -40,6 +40,7 @@ import { getCurrentSeq } from "../runtime/assistant-stream-state.js";
|
|
|
40
40
|
import { publishConversationMessagesChanged } from "../runtime/sync/resource-sync-events.js";
|
|
41
41
|
import { computeToolApprovalDigest } from "../security/tool-approval-digest.js";
|
|
42
42
|
import { getAllTools } from "../tools/registry.js";
|
|
43
|
+
import { sensitiveToolReach } from "../tools/tool-approval-handler.js";
|
|
43
44
|
import { createAbortReason } from "../util/abort-reasons.js";
|
|
44
45
|
import { getLogger } from "../util/logger.js";
|
|
45
46
|
import { truncate } from "../util/truncate.js";
|
|
@@ -286,6 +287,28 @@ export interface VoiceTurnOptions {
|
|
|
286
287
|
onError?: (message: string) => void;
|
|
287
288
|
/** Event-name callbacks used by non-phone voice clients. */
|
|
288
289
|
callbacks?: VoiceTurnCallbacks;
|
|
290
|
+
/**
|
|
291
|
+
* Called when this turn leaves a confirmation for the user to answer instead
|
|
292
|
+
* of deciding it, so the client can put the prompt where they can see it.
|
|
293
|
+
*
|
|
294
|
+
* A voice client renders its call as something that covers the app (the
|
|
295
|
+
* live-voice room is a full-screen overlay), which is fine while the call is
|
|
296
|
+
* the only thing happening and wrong the moment the turn is waiting on a
|
|
297
|
+
* decision: the card is on screen, behind the call. The bridge cannot reach
|
|
298
|
+
* a client's own surfaces, so it says *that a decision is waiting* and the
|
|
299
|
+
* client decides what to do about it.
|
|
300
|
+
*/
|
|
301
|
+
onApprovalPending?: (requestId: string) => void;
|
|
302
|
+
/**
|
|
303
|
+
* Called when the last pending confirmation this turn was waiting on is
|
|
304
|
+
* decided, however it was decided (the user answered, it timed out, a newer
|
|
305
|
+
* message superseded it). Paired with {@link onApprovalPending} so a client
|
|
306
|
+
* that changed its presentation for the wait can change it back.
|
|
307
|
+
*
|
|
308
|
+
* Fires on the *last* one, not each: a turn waiting on two decisions is
|
|
309
|
+
* still waiting after the first is answered.
|
|
310
|
+
*/
|
|
311
|
+
onApprovalsResolved?: () => void;
|
|
289
312
|
/** Optional AbortSignal for external cancellation (e.g. barge-in). */
|
|
290
313
|
signal?: AbortSignal;
|
|
291
314
|
/**
|
|
@@ -367,12 +390,27 @@ export interface VoiceTurnHandle {
|
|
|
367
390
|
* provides assistant identity) and guardian context (injected separately).
|
|
368
391
|
*/
|
|
369
392
|
/**
|
|
370
|
-
* Steering shared by every voice channel.
|
|
371
|
-
*
|
|
372
|
-
*
|
|
373
|
-
*
|
|
374
|
-
* the
|
|
393
|
+
* Steering shared by every voice channel. A sign-in flow opens a browser
|
|
394
|
+
* window mid-call that the caller may be unable to see or complete, whether it
|
|
395
|
+
* is reached through a ui-surface tool or through shell and CLI tools (e.g.
|
|
396
|
+
* `assistant oauth connect`). Tell the model to speak the limitation and defer
|
|
397
|
+
* the flow to text chat instead.
|
|
398
|
+
*
|
|
399
|
+
* This outlives the ui-surface restriction it was written alongside: a
|
|
400
|
+
* live-voice call can now show surfaces, but a browser window handing control
|
|
401
|
+
* to a third party mid-call is a different problem, and one a minimized room
|
|
402
|
+
* does not solve.
|
|
375
403
|
*/
|
|
404
|
+
/**
|
|
405
|
+
* How long a live-voice call waits on an approval before deciding it itself.
|
|
406
|
+
*
|
|
407
|
+
* Long enough to pick the phone up and read the card, short enough that a turn
|
|
408
|
+
* cannot sit blocked for the rest of the call. The fallback is the decision
|
|
409
|
+
* this channel made before it prompted at all, so the worst case of nobody
|
|
410
|
+
* answering is exactly the old behavior, arrived at late.
|
|
411
|
+
*/
|
|
412
|
+
const VOICE_APPROVAL_TIMEOUT_MS = 45_000;
|
|
413
|
+
|
|
376
414
|
export const VOICE_NO_SETUP_FLOWS_RULE =
|
|
377
415
|
"Never start account connections, OAuth or sign-in flows, or any other action that opens a browser window or needs the user's screen during this call — not even through shell or CLI tools. If the task needs one, say so briefly and offer to finish it in text chat after the call.";
|
|
378
416
|
|
|
@@ -851,7 +889,37 @@ export async function startVoiceTurn(
|
|
|
851
889
|
// (tracked via `clientCallbackInstalled`); otherwise cleanup would detach
|
|
852
890
|
// an active sender installed by a prior turn.
|
|
853
891
|
let clientCallbackInstalled = false;
|
|
892
|
+
/**
|
|
893
|
+
* Confirmations this voice turn left pending for the user to answer, and the
|
|
894
|
+
* timers that stop them waiting forever.
|
|
895
|
+
*
|
|
896
|
+
* A call is a poor place to block: the user may have put the phone in a
|
|
897
|
+
* pocket, and a turn that waits indefinitely is a session that looks wedged.
|
|
898
|
+
* Each pending request therefore carries a deadline, after which it resolves
|
|
899
|
+
* the way this channel resolved everything before it prompted at all.
|
|
900
|
+
*/
|
|
901
|
+
const pendingVoiceApprovals = new Map<
|
|
902
|
+
string,
|
|
903
|
+
ReturnType<typeof setTimeout>
|
|
904
|
+
>();
|
|
905
|
+
const settleVoiceApproval = (requestId: string): void => {
|
|
906
|
+
const timer = pendingVoiceApprovals.get(requestId);
|
|
907
|
+
if (timer === undefined) {
|
|
908
|
+
// Not one of ours: other interactions (secrets, host-proxy requests)
|
|
909
|
+
// resolve through the same event.
|
|
910
|
+
return;
|
|
911
|
+
}
|
|
912
|
+
clearTimeout(timer);
|
|
913
|
+
pendingVoiceApprovals.delete(requestId);
|
|
914
|
+
if (pendingVoiceApprovals.size === 0) {
|
|
915
|
+
opts.onApprovalsResolved?.();
|
|
916
|
+
}
|
|
917
|
+
};
|
|
854
918
|
const cleanup = () => {
|
|
919
|
+
for (const timer of pendingVoiceApprovals.values()) {
|
|
920
|
+
clearTimeout(timer);
|
|
921
|
+
}
|
|
922
|
+
pendingVoiceApprovals.clear();
|
|
855
923
|
conversation.setChannelCapabilities(null);
|
|
856
924
|
conversation.setTrustContext(null);
|
|
857
925
|
conversation.setCommandIntent(null);
|
|
@@ -872,7 +940,12 @@ export async function startVoiceTurn(
|
|
|
872
940
|
const persistResult = await conversation.persistUserMessage({
|
|
873
941
|
content: persistedContent,
|
|
874
942
|
requestId,
|
|
875
|
-
|
|
943
|
+
metadata: {
|
|
944
|
+
// Durable "this turn came from an open voice session" marker; see
|
|
945
|
+
// `isVoiceSessionUserMessage` for why the channel fields cannot carry it.
|
|
946
|
+
voiceSessionTurn: true,
|
|
947
|
+
...(isHiddenSyntheticPrompt ? { hidden: true } : {}),
|
|
948
|
+
},
|
|
876
949
|
});
|
|
877
950
|
return persistResult.id;
|
|
878
951
|
};
|
|
@@ -892,21 +965,22 @@ export async function startVoiceTurn(
|
|
|
892
965
|
trustContext: opts.trustContext ?? null,
|
|
893
966
|
turnChannelContext,
|
|
894
967
|
turnInterfaceContext,
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
968
|
+
// Resolved from the channel, with no voice-specific override.
|
|
969
|
+
//
|
|
970
|
+
// Whether a call can show a surface is a property of the call's channel,
|
|
971
|
+
// not of calls in general. A phone call has no screen at all and resolves
|
|
972
|
+
// to `supportsDynamicUi: false` on its own; a live-voice call is a screen
|
|
973
|
+
// the user is holding, temporarily covered by the room overlay, and the
|
|
974
|
+
// session minimizes that overlay when a surface is shown (see the ui-tool
|
|
975
|
+
// branch of the live-voice session's `tool_use_start`).
|
|
976
|
+
//
|
|
977
|
+
// This one flag also drives the runtime-context `supports_dynamic_ui`
|
|
978
|
+
// line, the secret-prompter's dynamic-UI branch, and the
|
|
979
|
+
// task-progress-nudge hook, so a live-voice turn now reaches all of them.
|
|
980
|
+
channelCapabilities: resolveChannelCapabilities(
|
|
981
|
+
turnChannelContext.userMessageChannel,
|
|
982
|
+
turnInterfaceContext.userMessageInterface,
|
|
983
|
+
),
|
|
910
984
|
voiceCallControlPrompt,
|
|
911
985
|
};
|
|
912
986
|
const installVoiceTurnState = () => {
|
|
@@ -1105,6 +1179,11 @@ export async function startVoiceTurn(
|
|
|
1105
1179
|
// Voice auto-denies/auto-allows/auto-resolves these since there's no interactive UI.
|
|
1106
1180
|
let lastError: string | null = null;
|
|
1107
1181
|
conversation.updateClient(async (msg: AssistantEvent) => {
|
|
1182
|
+
// The user (or anything else) answered: stop the fallback from firing on a
|
|
1183
|
+
// request that is already decided.
|
|
1184
|
+
if (msg.type === "interaction_resolved") {
|
|
1185
|
+
settleVoiceApproval(msg.requestId);
|
|
1186
|
+
}
|
|
1108
1187
|
if (msg.type === "confirmation_request") {
|
|
1109
1188
|
// Broadcast the request BEFORE resolving it: resolution synchronously
|
|
1110
1189
|
// broadcasts `interaction_resolved` (handleConfirmationResponse →
|
|
@@ -1181,6 +1260,71 @@ export async function startVoiceTurn(
|
|
|
1181
1260
|
});
|
|
1182
1261
|
return;
|
|
1183
1262
|
}
|
|
1263
|
+
// A live-voice call has a screen, so a consequential action can be put
|
|
1264
|
+
// to the user instead of decided for them.
|
|
1265
|
+
//
|
|
1266
|
+
// Everything used to be allowed here on the strength of the caller being
|
|
1267
|
+
// a guardian, which made a voice call the one surface where a tool that
|
|
1268
|
+
// writes to the workspace or reaches the host never had to ask. Only
|
|
1269
|
+
// *sensitive* reach prompts: gating every confirmation would interrupt a
|
|
1270
|
+
// conversation constantly, and the tools that read or render were never
|
|
1271
|
+
// the reason approval exists.
|
|
1272
|
+
//
|
|
1273
|
+
// Phone keeps the old behavior in full. There is no screen on that
|
|
1274
|
+
// channel, so a prompt there is a question nobody can answer.
|
|
1275
|
+
// The workspace root is what makes an escape visible. A sandbox file
|
|
1276
|
+
// tool pointed outside the workspace reaches the host filesystem on a
|
|
1277
|
+
// non-containerized install, and `sensitiveToolReach` can only see that
|
|
1278
|
+
// when it is given the boundary to compare against: without it,
|
|
1279
|
+
// `file_read { path: "/etc/hosts" }` classifies as `none` and would fall
|
|
1280
|
+
// through to the auto-allow this branch exists to prevent.
|
|
1281
|
+
const workspaceRoot = conversation.workingDir;
|
|
1282
|
+
const reach = sensitiveToolReach(
|
|
1283
|
+
msg.toolName,
|
|
1284
|
+
// Absent on requests that do not come from the tool pipeline (proxy
|
|
1285
|
+
// and network prompters). Unknown reads as the more consequential of
|
|
1286
|
+
// the two: the cost of being wrong is a prompt the user did not need,
|
|
1287
|
+
// against an unreviewed action on their machine.
|
|
1288
|
+
msg.executionTarget ?? "host",
|
|
1289
|
+
msg.input,
|
|
1290
|
+
workspaceRoot,
|
|
1291
|
+
);
|
|
1292
|
+
const canPrompt = turnChannelContext.userMessageChannel === "vellum";
|
|
1293
|
+
// Fail closed on a missing boundary for the same reason: with no
|
|
1294
|
+
// workspace root there is no way to tell an ordinary write from an
|
|
1295
|
+
// escape, and the safe reading of "cannot tell" is "ask". A real
|
|
1296
|
+
// conversation always has one, so this is a guard rather than a path.
|
|
1297
|
+
if (canPrompt && (reach !== "none" || !workspaceRoot)) {
|
|
1298
|
+
log.info(
|
|
1299
|
+
{ turnId, toolName: msg.toolName, reach },
|
|
1300
|
+
"Prompting guardian voice caller for a sensitive tool",
|
|
1301
|
+
);
|
|
1302
|
+
// Left pending: the request is already broadcast, so the approval card
|
|
1303
|
+
// an attached client renders is now answerable rather than cleared a
|
|
1304
|
+
// moment later by this handler.
|
|
1305
|
+
//
|
|
1306
|
+
// Announced separately, because "a card exists" and "the user can see
|
|
1307
|
+
// it" are different things on a channel whose call covers the app.
|
|
1308
|
+
opts.onApprovalPending?.(msg.requestId);
|
|
1309
|
+
const timer = setTimeout(() => {
|
|
1310
|
+
pendingVoiceApprovals.delete(msg.requestId);
|
|
1311
|
+
if (pendingVoiceApprovals.size === 0) {
|
|
1312
|
+
opts.onApprovalsResolved?.();
|
|
1313
|
+
}
|
|
1314
|
+
log.info(
|
|
1315
|
+
{ turnId, toolName: msg.toolName },
|
|
1316
|
+
"Voice approval timed out — falling back to the guardian allow",
|
|
1317
|
+
);
|
|
1318
|
+
conversation.handleConfirmationResponse(msg.requestId, "allow", {
|
|
1319
|
+
decisionContext: `Permission approved for "${msg.toolName}": this is a verified guardian voice call and the approval prompt went unanswered.`,
|
|
1320
|
+
});
|
|
1321
|
+
}, VOICE_APPROVAL_TIMEOUT_MS);
|
|
1322
|
+
// Never hold the process open for a prompt nobody is looking at.
|
|
1323
|
+
timer.unref?.();
|
|
1324
|
+
pendingVoiceApprovals.set(msg.requestId, timer);
|
|
1325
|
+
return;
|
|
1326
|
+
}
|
|
1327
|
+
|
|
1184
1328
|
log.info(
|
|
1185
1329
|
{ turnId, toolName: msg.toolName },
|
|
1186
1330
|
"Auto-approving confirmation request for guardian voice turn",
|
|
@@ -1191,11 +1335,14 @@ export async function startVoiceTurn(
|
|
|
1191
1335
|
return;
|
|
1192
1336
|
}
|
|
1193
1337
|
if (msg.type === "secret_request") {
|
|
1194
|
-
//
|
|
1195
|
-
//
|
|
1196
|
-
//
|
|
1197
|
-
//
|
|
1198
|
-
//
|
|
1338
|
+
// Auto-resolved rather than prompted, on every voice channel.
|
|
1339
|
+
//
|
|
1340
|
+
// A phone call cannot render a secret field at all. A live-voice call
|
|
1341
|
+
// now can (its channel resolves `supportsDynamicUi` true), so this is
|
|
1342
|
+
// where the prompt would surface, and it deliberately does not yet:
|
|
1343
|
+
// typing a credential into a screen you reached by minimizing a call is
|
|
1344
|
+
// a flow that needs designing, not a flag flip. Resolving with no secret
|
|
1345
|
+
// leaves the tool to fail the way it does today.
|
|
1199
1346
|
log.info(
|
|
1200
1347
|
{ turnId, service: msg.service, field: msg.field },
|
|
1201
1348
|
"Auto-resolving secret request for voice turn (no secret-entry UI)",
|
|
@@ -81,7 +81,7 @@ Examples:
|
|
|
81
81
|
$ echo "Summarize this" | assistant inference send
|
|
82
82
|
$ assistant llm send --system-prompt "You are a poet" "Write a haiku"
|
|
83
83
|
$ assistant inference send --timeout-seconds 300 "Draft a long memo"
|
|
84
|
-
$ assistant inference send --model claude-sonnet-
|
|
84
|
+
$ assistant inference send --model claude-sonnet-5 --json "Hello"
|
|
85
85
|
$ assistant inference send --profile balanced "Explain RFC 1149"`,
|
|
86
86
|
};
|
|
87
87
|
|
|
@@ -96,7 +96,7 @@ Examples:
|
|
|
96
96
|
$ assistant inference send "What is the capital of France?"
|
|
97
97
|
$ echo "Explain quantum computing" | assistant inference send
|
|
98
98
|
$ assistant llm send --system-prompt "Be concise" "What is TCP?"
|
|
99
|
-
$ assistant inference send --model claude-sonnet-
|
|
99
|
+
$ assistant inference send --model claude-sonnet-5 --json "Hello"
|
|
100
100
|
$ assistant inference send --profile balanced "Explain RFC 1149"`,
|
|
101
101
|
subcommands: [
|
|
102
102
|
sendSubcommandHelp,
|
|
@@ -551,7 +551,7 @@ Examples:
|
|
|
551
551
|
$ assistant llm send "What is the capital of France?"
|
|
552
552
|
$ echo "Explain quantum computing" | assistant llm send
|
|
553
553
|
$ assistant llm send --system-prompt "Be concise" "What is TCP?"
|
|
554
|
-
$ assistant llm send --model claude-sonnet-
|
|
554
|
+
$ assistant llm send --model claude-sonnet-5 --json "Hello"
|
|
555
555
|
$ assistant llm send --profile balanced "Explain RFC 1149"`,
|
|
556
556
|
subcommands: [sendSubcommandHelp],
|
|
557
557
|
};
|
|
@@ -86,7 +86,7 @@ Examples:
|
|
|
86
86
|
{
|
|
87
87
|
flags: "--title <title>",
|
|
88
88
|
description:
|
|
89
|
-
"Short headline
|
|
89
|
+
"Short headline. Titles over 40 characters are trimmed to fit, and one over 40 characters that also runs past 7 words is cut to its first 5. A title that reads like prose (a reasoning opener, or a full sentence ending in terminal punctuation) is discarded in favor of a headline derived from --message.",
|
|
90
90
|
},
|
|
91
91
|
{
|
|
92
92
|
flags: "--urgency <urgency>",
|
|
@@ -263,7 +263,8 @@ Examples:
|
|
|
263
263
|
},
|
|
264
264
|
{
|
|
265
265
|
flags: "--title <title>",
|
|
266
|
-
description:
|
|
266
|
+
description:
|
|
267
|
+
"New short headline (≤ 8 words). An empty value is ignored, so the existing title stays put.",
|
|
267
268
|
},
|
|
268
269
|
{
|
|
269
270
|
flags: "--urgency <urgency>",
|