@vellumai/assistant 0.8.3 → 0.8.4
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/docker-entrypoint.sh +0 -1
- package/node_modules/@vellumai/gateway-client/src/types.ts +2 -0
- package/openapi.yaml +610 -16
- package/package.json +1 -1
- package/src/__tests__/agent-loop-exit-reason.test.ts +4 -5
- package/src/__tests__/agent-loop-override-profile.test.ts +1 -1
- package/src/__tests__/agent-loop.test.ts +88 -3
- package/src/__tests__/anthropic-provider.test.ts +272 -0
- package/src/__tests__/approval-cascade.test.ts +1 -1
- package/src/__tests__/background-workers-disk-pressure.test.ts +2 -1
- package/src/__tests__/channel-delivery-store.test.ts +193 -0
- package/src/__tests__/channel-reply-delivery.test.ts +284 -5
- package/src/__tests__/channel-retry-sweep.test.ts +274 -1
- package/src/__tests__/compaction-events.test.ts +1 -1
- package/src/__tests__/compactor-preserved-tail-count.test.ts +110 -0
- package/src/__tests__/config-watcher.test.ts +1 -1
- package/src/__tests__/context-token-estimator.test.ts +91 -1
- package/src/__tests__/conversation-abort-tool-results.test.ts +1 -1
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +54 -3
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +31 -6
- package/src/__tests__/conversation-agent-loop.test.ts +25 -7
- package/src/__tests__/conversation-app-control-lifecycle.test.ts +1 -1
- package/src/__tests__/conversation-clean-command.test.ts +137 -0
- package/src/__tests__/conversation-confirmation-signals.test.ts +1 -1
- package/src/__tests__/conversation-fork-crud.test.ts +161 -0
- package/src/__tests__/conversation-lifecycle.test.ts +1 -1
- package/src/__tests__/conversation-load-cleaned-at.test.ts +279 -0
- package/src/__tests__/conversation-load-history-repair.test.ts +1 -1
- package/src/__tests__/conversation-pairing.test.ts +2 -2
- package/src/__tests__/conversation-process-callsite.test.ts +1 -1
- package/src/__tests__/conversation-provider-retry-repair.test.ts +1 -1
- package/src/__tests__/conversation-queue.test.ts +1 -1
- package/src/__tests__/conversation-runtime-assembly.test.ts +264 -81
- package/src/__tests__/conversation-seed-composer.test.ts +66 -4
- package/src/__tests__/conversation-slash-commands.test.ts +36 -8
- package/src/__tests__/conversation-slash-queue.test.ts +1 -1
- package/src/__tests__/conversation-slash-unknown.test.ts +1 -1
- package/src/__tests__/conversation-speed-override.test.ts +1 -1
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +220 -0
- package/src/__tests__/conversation-workspace-cache-state.test.ts +1 -1
- package/src/__tests__/conversation-workspace-injection.test.ts +5 -1
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +5 -1
- package/src/__tests__/credential-security-invariants.test.ts +6 -0
- package/src/__tests__/cu-unified-flow.test.ts +10 -1
- package/src/__tests__/dm-backfill.test.ts +64 -0
- package/src/__tests__/dm-persistence.test.ts +33 -0
- package/src/__tests__/document-find-replace.test.ts +501 -0
- package/src/__tests__/first-greeting.test.ts +23 -2
- package/src/__tests__/headless-browser-navigate.test.ts +172 -0
- package/src/__tests__/host-bash-proxy.test.ts +6 -0
- package/src/__tests__/host-browser-proxy.test.ts +10 -0
- package/src/__tests__/host-cu-proxy.test.ts +8 -1
- package/src/__tests__/host-file-proxy.test.ts +8 -1
- package/src/__tests__/host-transfer-proxy.test.ts +8 -1
- package/src/__tests__/identity-routes.test.ts +57 -0
- package/src/__tests__/inbound-slack-persistence.test.ts +3 -0
- package/src/__tests__/injector-chain.test.ts +2 -0
- package/src/__tests__/injector-document-comments.test.ts +378 -0
- package/src/__tests__/injector-pkb-v2-silenced.test.ts +4 -25
- package/src/__tests__/list-messages-attachments.test.ts +21 -17
- package/src/__tests__/list-messages-hidden-metadata.test.ts +217 -0
- package/src/__tests__/list-messages-page-latest.test.ts +130 -14
- package/src/__tests__/list-messages-tool-merge.test.ts +17 -16
- package/src/__tests__/llm-context-normalization.test.ts +0 -2
- package/src/__tests__/llm-resolver.test.ts +85 -1
- package/src/__tests__/log-export-routes.test.ts +99 -2
- package/src/__tests__/message-queue-steer.test.ts +114 -0
- package/src/__tests__/openai-provider.test.ts +105 -0
- package/src/__tests__/openai-responses-provider.test.ts +4 -4
- package/src/__tests__/outbound-slack-persistence.test.ts +187 -20
- package/src/__tests__/pending-interactions-resolved-event.test.ts +190 -0
- package/src/__tests__/platform.test.ts +0 -3
- package/src/__tests__/plugin-source-watcher.test.ts +302 -0
- package/src/__tests__/process-message-background-slack.test.ts +1 -51
- package/src/__tests__/process-message-display-content.test.ts +21 -16
- package/src/__tests__/server-history-render.test.ts +83 -4
- package/src/__tests__/steer-tool-repair.test.ts +249 -0
- package/src/__tests__/system-prompt.test.ts +51 -28
- package/src/__tests__/terminal-tools.test.ts +11 -1
- package/src/__tests__/thinking-block-replay.test.ts +113 -0
- package/src/__tests__/thread-backfill.test.ts +370 -22
- package/src/__tests__/tool-executor.test.ts +90 -1
- package/src/__tests__/tool-result-metadata-plumbing.test.ts +167 -0
- package/src/__tests__/twilio-routes.test.ts +1 -1
- package/src/__tests__/web-fetch.test.ts +2 -2
- package/src/__tests__/workspace-git-service.test.ts +88 -5
- package/src/__tests__/workspace-migration-088-deprecate-background-conversation-override.test.ts +158 -0
- package/src/agent/attachments.ts +1 -0
- package/src/agent/loop.ts +57 -20
- package/src/background-wake/next-wake.test.ts +289 -0
- package/src/background-wake/next-wake.ts +172 -0
- package/src/browser/operations.ts +15 -0
- package/src/cli/commands/__tests__/conversations-slack.test.ts +572 -0
- package/src/cli/commands/__tests__/memory-v2.test.ts +9 -12
- package/src/cli/commands/conversations.ts +128 -1
- package/src/cli/commands/inference-providers.ts +147 -1
- package/src/cli/commands/memory-v2.ts +308 -0
- package/src/cli/commands/notifications.ts +24 -2
- package/src/cli/utils/conversation-id.ts +17 -5
- package/src/config/bundled-skills/app-builder/SKILL.md +2 -2
- package/src/config/bundled-skills/document-editor/SKILL.md +115 -0
- package/src/config/bundled-skills/document-editor/TOOLS.json +240 -0
- package/src/config/bundled-skills/document-editor/tools/comment-list.ts +12 -0
- package/src/config/bundled-skills/document-editor/tools/comment-reply.ts +12 -0
- package/src/config/bundled-skills/document-editor/tools/comment-resolve.ts +12 -0
- package/src/config/bundled-skills/document-editor/tools/document-find.ts +12 -0
- package/src/config/bundled-skills/document-editor/tools/document-replace-text.ts +12 -0
- package/src/config/bundled-skills/media-processing/SKILL.md +8 -0
- package/src/config/bundled-skills/schedule/SKILL.md +8 -0
- package/src/config/bundled-tool-registry.ts +22 -12
- package/src/config/call-site-defaults.ts +19 -0
- package/src/config/feature-flag-registry.json +99 -3
- package/src/config/llm-resolver.ts +16 -2
- package/src/config/schemas/__tests__/memory-v2.test.ts +4 -0
- package/src/config/schemas/call-site-catalog.ts +21 -0
- package/src/config/schemas/llm.ts +3 -0
- package/src/config/schemas/memory-v2.ts +48 -1
- package/src/context/compactor.ts +8 -1
- package/src/context/token-estimator.ts +47 -4
- package/src/context/window-manager.ts +25 -0
- package/src/credential-health/credential-health-service.ts +34 -19
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +66 -6
- package/src/daemon/__tests__/native-web-search-metadata.test.ts +357 -0
- package/src/daemon/__tests__/web-search-status-text.test.ts +287 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +153 -23
- package/src/daemon/conversation-agent-loop.ts +223 -54
- package/src/daemon/conversation-lifecycle.ts +142 -116
- package/src/daemon/conversation-messaging.ts +3 -0
- package/src/daemon/conversation-process.ts +273 -0
- package/src/daemon/conversation-queue-manager.ts +14 -0
- package/src/daemon/conversation-runtime-assembly.ts +135 -75
- package/src/daemon/conversation-slash.ts +37 -5
- package/src/daemon/conversation-surfaces.ts +45 -2
- package/src/daemon/conversation-tool-setup.ts +7 -0
- package/src/daemon/conversation.ts +42 -5
- package/src/daemon/first-greeting.ts +10 -0
- package/src/daemon/handlers/__tests__/config-a2a-accept.test.ts +498 -0
- package/src/daemon/handlers/config-a2a.ts +160 -0
- package/src/daemon/handlers/config-model.test.ts +1 -0
- package/src/daemon/handlers/conversations.ts +79 -0
- package/src/daemon/handlers/shared.ts +92 -29
- package/src/daemon/host-bash-proxy.ts +1 -1
- package/src/daemon/host-cu-proxy.ts +1 -1
- package/src/daemon/host-file-proxy.ts +1 -1
- package/src/daemon/host-transfer-proxy.ts +1 -1
- package/src/daemon/lifecycle.ts +18 -4
- package/src/daemon/message-protocol.ts +4 -0
- package/src/daemon/message-types/conversations.ts +8 -0
- package/src/daemon/message-types/document-comments.ts +50 -0
- package/src/daemon/message-types/messages.ts +68 -1
- package/src/daemon/message-types/surfaces.ts +3 -1
- package/src/daemon/message-types/web-activity.ts +57 -0
- package/src/daemon/plugin-source-watcher.ts +135 -3
- package/src/daemon/process-message.ts +69 -12
- package/src/daemon/query-complexity-router.ts +75 -0
- package/src/daemon/trust-context.ts +6 -0
- package/src/documents/document-comments-store.test.ts +338 -0
- package/src/documents/document-comments-store.ts +237 -0
- package/src/documents/document-store.ts +202 -0
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +0 -1
- package/src/heartbeat/heartbeat-service.ts +1 -0
- package/src/home/__tests__/suggested-prompts.test.ts +33 -2
- package/src/home/feed-types.ts +6 -1
- package/src/home/home-content-refresh.ts +52 -0
- package/src/home/home-greeting-cache.ts +69 -0
- package/src/home/home-greeting.ts +94 -0
- package/src/home/suggested-prompts.ts +177 -9
- package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +135 -2
- package/src/memory/__tests__/memory-retrospective-job.test.ts +320 -6
- package/src/memory/conversation-crud.ts +133 -43
- package/src/memory/db-init.ts +16 -0
- package/src/memory/delivery-crud.ts +41 -0
- package/src/memory/delivery-status.ts +141 -15
- package/src/memory/external-conversation-store.ts +32 -1
- package/src/memory/jobs-worker.ts +21 -1
- package/src/memory/memory-retrospective-constants.ts +28 -0
- package/src/memory/memory-retrospective-enqueue.ts +3 -2
- package/src/memory/memory-retrospective-job.ts +408 -18
- package/src/memory/memory-retrospective-startup-cleanup.ts +3 -3
- package/src/memory/memory-v2-activation-log-store.ts +26 -8
- package/src/memory/migrations/100-core-tables.ts +1 -0
- package/src/memory/migrations/109-external-conversation-bindings.ts +1 -0
- package/src/memory/migrations/253-conversation-last-notified-profile.ts +15 -0
- package/src/memory/migrations/253-document-comments.ts +47 -0
- package/src/memory/migrations/254-external-conversation-binding-chat-name.ts +43 -0
- package/src/memory/migrations/255-channel-inbound-delivery-attempts.ts +24 -0
- package/src/memory/migrations/256-memory-v2-injection-events.ts +113 -0
- package/src/memory/migrations/257-strip-base-url-non-openai-compatible.ts +22 -0
- package/src/memory/migrations/258-onboarding-events-prior-assistants.ts +13 -0
- package/src/memory/migrations/259-conversation-cleaned-at.ts +33 -0
- package/src/memory/migrations/index.ts +17 -0
- package/src/memory/migrations/registry.ts +25 -0
- package/src/memory/onboarding-events-store.ts +7 -0
- package/src/memory/schema/calls.ts +1 -0
- package/src/memory/schema/conversations.ts +3 -0
- package/src/memory/schema/infrastructure.ts +1 -0
- package/src/memory/v2/__tests__/injection-events.test.ts +318 -0
- package/src/memory/v2/__tests__/injection.test.ts +31 -14
- package/src/memory/v2/__tests__/page-index.test.ts +365 -1
- package/src/memory/v2/__tests__/router.test.ts +489 -1
- package/src/memory/v2/consolidation-job.ts +14 -0
- package/src/memory/v2/injection-events.ts +101 -0
- package/src/memory/v2/injection.ts +21 -10
- package/src/memory/v2/page-index.ts +209 -7
- package/src/memory/v2/page-store.ts +18 -0
- package/src/memory/v2/router.ts +209 -55
- package/src/messaging/providers/index.ts +7 -1
- package/src/messaging/providers/slack/__tests__/adapter-mention-rendering.test.ts +329 -3
- package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +34 -1
- package/src/messaging/providers/slack/adapter.ts +178 -25
- package/src/messaging/providers/slack/api.test.ts +54 -0
- package/src/messaging/providers/slack/api.ts +119 -3
- package/src/messaging/providers/slack/client.ts +12 -0
- package/src/messaging/providers/slack/deep-link.ts +20 -1
- package/src/messaging/providers/slack/message-metadata.test.ts +48 -0
- package/src/messaging/providers/slack/message-metadata.ts +156 -0
- package/src/messaging/providers/slack/render-transcript.test.ts +107 -75
- package/src/messaging/providers/slack/render-transcript.ts +176 -49
- package/src/messaging/providers/slack/send.test.ts +77 -0
- package/src/messaging/providers/slack/send.ts +8 -2
- package/src/messaging/providers/slack/types.ts +14 -0
- package/src/notifications/__tests__/emit-signal-home-feed.test.ts +4 -1
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +116 -54
- package/src/notifications/conversation-seed-composer.ts +14 -2
- package/src/notifications/deferred-emit.ts +135 -0
- package/src/notifications/emit-signal.ts +9 -1
- package/src/notifications/home-feed-side-effect.ts +60 -30
- package/src/oauth/connect-orchestrator.ts +3 -0
- package/src/oauth/credential-token-resolver.ts +2 -0
- package/src/oauth/manual-token-connection.ts +19 -0
- package/src/oauth/oauth-store.ts +12 -0
- package/src/oauth/seed-providers.ts +22 -0
- package/src/permissions/prompter.ts +5 -2
- package/src/permissions/secret-prompter.ts +4 -1
- package/src/plugins/defaults/injectors.ts +82 -9
- package/src/prompts/__tests__/system-prompt.test.ts +46 -2
- package/src/prompts/normalize-onboarding.ts +40 -0
- package/src/prompts/sections.ts +32 -14
- package/src/prompts/system-prompt.ts +105 -68
- package/src/prompts/template-detection.ts +37 -0
- package/src/prompts/templates/BOOTSTRAP-CONTENT-AUTOMATION.md +141 -0
- package/src/prompts/templates/BOOTSTRAP.md +8 -0
- package/src/prompts/templates/VOICE.md +3 -0
- package/src/prompts/templates/system-sections.ts +53 -3
- package/src/providers/anthropic/client.ts +132 -5
- package/src/providers/fireworks/client.ts +20 -2
- package/src/providers/inference/__tests__/base-url-route-validation.test.ts +342 -0
- package/src/providers/inference/__tests__/base-url-security.test.ts +189 -0
- package/src/providers/inference/__tests__/codex-token-refresh.test.ts +254 -0
- package/src/providers/inference/adapter-factory.ts +15 -1
- package/src/providers/inference/auth.ts +3 -3
- package/src/providers/inference/codex-token-refresh.ts +128 -0
- package/src/providers/inference/resolve-auth.ts +49 -6
- package/src/providers/model-catalog.ts +48 -1
- package/src/providers/openai/chat-completions-provider.ts +57 -20
- package/src/providers/openai/responses-provider.ts +9 -3
- package/src/providers/openrouter/client.ts +5 -1
- package/src/providers/types.ts +25 -0
- package/src/runtime/__tests__/agent-wake.test.ts +214 -0
- package/src/runtime/__tests__/background-job-runner.test.ts +128 -0
- package/src/runtime/agent-wake.ts +151 -56
- package/src/runtime/auth/route-policy.ts +7 -3
- package/src/runtime/background-job-runner.ts +26 -0
- package/src/runtime/channel-reply-delivery.ts +182 -47
- package/src/runtime/channel-retry-sweep.ts +141 -16
- package/src/runtime/http-types.ts +7 -4
- package/src/runtime/pending-interactions.ts +51 -8
- package/src/runtime/routes/__tests__/content-source-routes.test.ts +162 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +55 -1
- package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +14 -0
- package/src/runtime/routes/__tests__/memory-v2-simulate-route.test.ts +271 -0
- package/src/runtime/routes/__tests__/sanity-routes.test.ts +280 -0
- package/src/runtime/routes/__tests__/slack-channel-routes.test.ts +266 -0
- package/src/runtime/routes/approval-routes.ts +4 -1
- package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +246 -0
- package/src/runtime/routes/content-source-routes.ts +78 -0
- package/src/runtime/routes/conversation-cli-routes.ts +146 -1
- package/src/runtime/routes/conversation-query-routes.ts +60 -1
- package/src/runtime/routes/conversation-routes.ts +281 -76
- package/src/runtime/routes/document-comments-routes.ts +287 -0
- package/src/runtime/routes/documents-routes.ts +33 -0
- package/src/runtime/routes/home-feed-routes.ts +6 -3
- package/src/runtime/routes/host-app-control-routes.ts +1 -1
- package/src/runtime/routes/host-browser-routes.ts +8 -1
- package/src/runtime/routes/identity-routes.ts +21 -0
- package/src/runtime/routes/inbound-message-handler.ts +288 -58
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +365 -6
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +283 -82
- package/src/runtime/routes/index.ts +12 -4
- package/src/runtime/routes/inference-provider-connection-routes.ts +63 -7
- package/src/runtime/routes/integrations/a2a.ts +60 -1
- package/src/runtime/routes/log-export-routes.ts +39 -0
- package/src/runtime/routes/memory-v2-routes.ts +217 -0
- package/src/runtime/routes/notification-routes.ts +19 -2
- package/src/runtime/routes/question-routes.ts +4 -1
- package/src/runtime/routes/sanity-routes.ts +159 -0
- package/src/runtime/routes/slack-channel-routes.ts +187 -0
- package/src/runtime/services/conversation-serializer.ts +30 -4
- package/src/schedule/integration-status.ts +3 -1
- package/src/security/__tests__/oauth2-device-code.test.ts +479 -0
- package/src/security/oauth2-device-code.ts +307 -0
- package/src/security/oauth2.ts +26 -9
- package/src/security/secure-keys.ts +5 -0
- package/src/skills/catalog-install.ts +6 -2
- package/src/tools/browser/__tests__/pinned-tabs.test.ts +80 -0
- package/src/tools/browser/browser-execution.ts +93 -0
- package/src/tools/browser/cdp-client/__tests__/factory.test.ts +28 -0
- package/src/tools/browser/cdp-client/__tests__/types.test.ts +1 -0
- package/src/tools/browser/cdp-client/cdp-inspect-client.ts +10 -0
- package/src/tools/browser/cdp-client/extension-cdp-client.ts +15 -1
- package/src/tools/browser/cdp-client/factory.ts +87 -3
- package/src/tools/browser/cdp-client/local-cdp-client.ts +9 -0
- package/src/tools/browser/cdp-client/types.ts +36 -0
- package/src/tools/browser/pinned-tabs.ts +90 -0
- package/src/tools/document/document-comment-tool.test.ts +379 -0
- package/src/tools/document/document-comment-tool.ts +156 -0
- package/src/tools/document/document-tool.ts +128 -2
- package/src/tools/network/__tests__/web-fetch-metadata.test.ts +229 -0
- package/src/tools/network/__tests__/web-search-metadata.test.ts +346 -0
- package/src/tools/network/domain-normalize.ts +17 -0
- package/src/tools/network/web-fetch.ts +213 -64
- package/src/tools/network/web-search.ts +191 -66
- package/src/tools/terminal/safe-env.ts +3 -2
- package/src/tools/tool-approval-handler.ts +19 -12
- package/src/tools/types.ts +4 -0
- package/src/tools/ui-surface/definitions.ts +3 -1
- package/src/types/onboarding-context.ts +4 -0
- package/src/util/__tests__/favicon.test.ts +84 -0
- package/src/util/favicon.ts +40 -0
- package/src/util/platform.ts +0 -5
- package/src/workspace/git-service.ts +75 -4
- package/src/workspace/migrations/088-deprecate-background-conversation-override.ts +103 -0
- package/src/workspace/migrations/registry.ts +2 -0
- package/src/config/bundled-skills/document/SKILL.md +0 -54
- package/src/config/bundled-skills/document/TOOLS.json +0 -106
- package/src/daemon/seed-files.ts +0 -18
- package/src/runtime/routes/interface-routes.ts +0 -43
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-create.ts +0 -0
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-delete.ts +0 -0
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-list.ts +0 -0
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-read.ts +0 -0
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-update.ts +0 -0
|
@@ -2,10 +2,15 @@ import { existsSync, readFileSync } from "node:fs";
|
|
|
2
2
|
|
|
3
3
|
import type { Command } from "commander";
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
cliIpcCall,
|
|
7
|
+
exitCodeFromIpcResult,
|
|
8
|
+
exitFromIpcResult,
|
|
9
|
+
} from "../../ipc/cli-client.js";
|
|
6
10
|
import { registerCommand } from "../lib/register-command.js";
|
|
7
11
|
import { timeAgo } from "../lib/time-ago.js";
|
|
8
12
|
import { log } from "../logger.js";
|
|
13
|
+
import { tryResolveConversationId } from "../utils/conversation-id.js";
|
|
9
14
|
import { registerConversationsDeferCommand } from "./conversations-defer.js";
|
|
10
15
|
import { registerConversationsImportCommand } from "./conversations-import.js";
|
|
11
16
|
|
|
@@ -14,6 +19,23 @@ type ConversationSeedMessage = {
|
|
|
14
19
|
content: string;
|
|
15
20
|
};
|
|
16
21
|
|
|
22
|
+
type SlackDetachCliResult = {
|
|
23
|
+
detached: boolean;
|
|
24
|
+
channelId: string;
|
|
25
|
+
threadTs: string;
|
|
26
|
+
source: "explicit" | "conversation_binding";
|
|
27
|
+
conversationId?: string;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
function outputSlackDetachError(message: string, json?: boolean): void {
|
|
31
|
+
if (json) {
|
|
32
|
+
process.stdout.write(JSON.stringify({ ok: false, error: message }) + "\n");
|
|
33
|
+
} else {
|
|
34
|
+
log.error(`Error: ${message}`);
|
|
35
|
+
}
|
|
36
|
+
process.exitCode = 1;
|
|
37
|
+
}
|
|
38
|
+
|
|
17
39
|
function readSeedMessages(
|
|
18
40
|
contentFile?: string,
|
|
19
41
|
): ConversationSeedMessage[] | undefined {
|
|
@@ -318,6 +340,111 @@ Examples:
|
|
|
318
340
|
},
|
|
319
341
|
);
|
|
320
342
|
|
|
343
|
+
// -------------------------------------------------------------------
|
|
344
|
+
// slack
|
|
345
|
+
// -------------------------------------------------------------------
|
|
346
|
+
|
|
347
|
+
const slack = conversations
|
|
348
|
+
.command("slack")
|
|
349
|
+
.description("Manage Slack conversation bindings");
|
|
350
|
+
|
|
351
|
+
slack
|
|
352
|
+
.command("detach [conversationId]")
|
|
353
|
+
.alias("mute")
|
|
354
|
+
.description("Detach the assistant from a Slack thread")
|
|
355
|
+
.option("--channel <id>", "Slack channel ID")
|
|
356
|
+
.option("--thread <ts>", "Slack thread timestamp")
|
|
357
|
+
.option("--json", "Output result as JSON")
|
|
358
|
+
.addHelpText(
|
|
359
|
+
"after",
|
|
360
|
+
`
|
|
361
|
+
Arguments:
|
|
362
|
+
conversationId Optional conversation ID. Defaults to the current skill or
|
|
363
|
+
tool conversation when available.
|
|
364
|
+
|
|
365
|
+
Detaches the assistant from Socket Mode listening for the Slack thread bound
|
|
366
|
+
to a conversation, or for explicit --channel and --thread identifiers.
|
|
367
|
+
|
|
368
|
+
Examples:
|
|
369
|
+
$ assistant conversations slack detach
|
|
370
|
+
$ assistant conversations slack detach conv-123
|
|
371
|
+
$ assistant conversations slack mute --channel C123 --thread 1700000000.000100
|
|
372
|
+
$ assistant conversations slack detach --json`,
|
|
373
|
+
)
|
|
374
|
+
.action(
|
|
375
|
+
async (
|
|
376
|
+
conversationIdArg: string | undefined,
|
|
377
|
+
opts: { channel?: string; thread?: string; json?: boolean },
|
|
378
|
+
) => {
|
|
379
|
+
const channelId = opts.channel?.trim();
|
|
380
|
+
const threadTs = opts.thread?.trim();
|
|
381
|
+
const hasExplicitSlackTarget =
|
|
382
|
+
opts.channel !== undefined || opts.thread !== undefined;
|
|
383
|
+
const body: Record<string, string> = {};
|
|
384
|
+
|
|
385
|
+
if (hasExplicitSlackTarget) {
|
|
386
|
+
if (!channelId || !threadTs) {
|
|
387
|
+
outputSlackDetachError(
|
|
388
|
+
"Both --channel and --thread are required when using explicit Slack identifiers.",
|
|
389
|
+
opts.json,
|
|
390
|
+
);
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
body.channelId = channelId;
|
|
394
|
+
body.threadTs = threadTs;
|
|
395
|
+
} else {
|
|
396
|
+
const conversationId = tryResolveConversationId({
|
|
397
|
+
explicit: conversationIdArg,
|
|
398
|
+
});
|
|
399
|
+
if (!conversationId) {
|
|
400
|
+
outputSlackDetachError(
|
|
401
|
+
"No conversation ID available. Pass a conversation ID, provide --channel and --thread, or run this command from a skill or bash tool context.",
|
|
402
|
+
opts.json,
|
|
403
|
+
);
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
body.conversationId = conversationId;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
const result = await cliIpcCall<SlackDetachCliResult>(
|
|
410
|
+
"conversation_slack_detach_cli",
|
|
411
|
+
{ body },
|
|
412
|
+
);
|
|
413
|
+
|
|
414
|
+
if (!result.ok) {
|
|
415
|
+
if (opts.json) {
|
|
416
|
+
process.stdout.write(
|
|
417
|
+
JSON.stringify({
|
|
418
|
+
ok: false,
|
|
419
|
+
error: result.error ?? "Failed to detach Slack thread",
|
|
420
|
+
}) + "\n",
|
|
421
|
+
);
|
|
422
|
+
process.exitCode = exitCodeFromIpcResult(result);
|
|
423
|
+
return;
|
|
424
|
+
}
|
|
425
|
+
return exitFromIpcResult(result);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
const detach = result.result!;
|
|
429
|
+
if (opts.json) {
|
|
430
|
+
process.stdout.write(
|
|
431
|
+
JSON.stringify({ ok: true, result: detach }) + "\n",
|
|
432
|
+
);
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
if (detach.detached) {
|
|
437
|
+
log.info(
|
|
438
|
+
`Detached Slack thread ${detach.threadTs} in channel ${detach.channelId} from assistant listening.`,
|
|
439
|
+
);
|
|
440
|
+
} else {
|
|
441
|
+
log.info(
|
|
442
|
+
`Slack thread ${detach.threadTs} in channel ${detach.channelId} was already detached from assistant listening.`,
|
|
443
|
+
);
|
|
444
|
+
}
|
|
445
|
+
},
|
|
446
|
+
);
|
|
447
|
+
|
|
321
448
|
// -------------------------------------------------------------------
|
|
322
449
|
// clear
|
|
323
450
|
// -------------------------------------------------------------------
|
|
@@ -16,7 +16,12 @@
|
|
|
16
16
|
|
|
17
17
|
import type { Command } from "commander";
|
|
18
18
|
|
|
19
|
+
import { isAssistantFeatureFlagEnabled } from "../../config/assistant-feature-flags.js";
|
|
20
|
+
import { getConfig } from "../../config/loader.js";
|
|
19
21
|
import { cliIpcCall } from "../../ipc/cli-client.js";
|
|
22
|
+
import type { OAuth2Config } from "../../security/oauth2.js";
|
|
23
|
+
import { startOAuth2Flow } from "../../security/oauth2.js";
|
|
24
|
+
import { setSecureKeyAsync } from "../../security/secure-keys.js";
|
|
20
25
|
import { log } from "../logger.js";
|
|
21
26
|
|
|
22
27
|
// ---------------------------------------------------------------------------
|
|
@@ -165,7 +170,11 @@ function buildAuthInput(
|
|
|
165
170
|
if (credential) return "--credential is not accepted with --auth none";
|
|
166
171
|
return { type: "none" };
|
|
167
172
|
}
|
|
168
|
-
|
|
173
|
+
if (authType === "oauth_subscription") {
|
|
174
|
+
if (!credential) return "--credential is required when --auth oauth_subscription";
|
|
175
|
+
return { type: "oauth_subscription", credential };
|
|
176
|
+
}
|
|
177
|
+
return `Unknown auth type "${authType}". Use: api_key, platform, none, oauth_subscription`;
|
|
169
178
|
}
|
|
170
179
|
|
|
171
180
|
function writeCliError(msg: string, json?: boolean): void {
|
|
@@ -311,6 +320,141 @@ function attachDeleteSubcommand(connections: Command): void {
|
|
|
311
320
|
});
|
|
312
321
|
}
|
|
313
322
|
|
|
323
|
+
// ---------------------------------------------------------------------------
|
|
324
|
+
// OpenAI Codex OAuth config (PKCE, no client secret)
|
|
325
|
+
// ---------------------------------------------------------------------------
|
|
326
|
+
|
|
327
|
+
const OPENAI_CODEX_OAUTH_CONFIG: OAuth2Config = {
|
|
328
|
+
authorizeUrl: "https://auth.openai.com/oauth/authorize",
|
|
329
|
+
tokenExchangeUrl: "https://auth.openai.com/oauth/token",
|
|
330
|
+
clientId: "app_EMoamEEZ73f0CkXaXp7hrann",
|
|
331
|
+
scopes: ["openid", "profile", "email", "offline_access"],
|
|
332
|
+
scopeSeparator: " ",
|
|
333
|
+
authorizeParams: { id_token_add_organizations: "true" },
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
// ---------------------------------------------------------------------------
|
|
337
|
+
// Subcommand: login-chatgpt
|
|
338
|
+
// ---------------------------------------------------------------------------
|
|
339
|
+
|
|
340
|
+
function attachLoginChatgptSubcommand(providers: Command): void {
|
|
341
|
+
providers
|
|
342
|
+
.command("login-chatgpt")
|
|
343
|
+
.description("Authenticate with ChatGPT via browser OAuth flow")
|
|
344
|
+
.option("--json", "Output as JSON")
|
|
345
|
+
.action(async (opts: { json?: boolean }) => {
|
|
346
|
+
const config = getConfig();
|
|
347
|
+
if (!isAssistantFeatureFlagEnabled("chatgpt-subscription-auth", config)) {
|
|
348
|
+
writeCliError("This feature is not yet available", opts.json);
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
try {
|
|
353
|
+
// Step 1: Run browser-based PKCE OAuth flow
|
|
354
|
+
process.stdout.write("Opening browser for ChatGPT authentication...\n");
|
|
355
|
+
const result = await startOAuth2Flow(
|
|
356
|
+
OPENAI_CODEX_OAUTH_CONFIG,
|
|
357
|
+
{
|
|
358
|
+
openUrl: (url) => {
|
|
359
|
+
Bun.spawn(["open", url]);
|
|
360
|
+
},
|
|
361
|
+
},
|
|
362
|
+
{
|
|
363
|
+
callbackTransport: "loopback",
|
|
364
|
+
loopbackPort: 1455,
|
|
365
|
+
loopbackCallbackPath: "/auth/callback",
|
|
366
|
+
},
|
|
367
|
+
);
|
|
368
|
+
const tokens = result.tokens;
|
|
369
|
+
|
|
370
|
+
// Step 2: Store tokens in CES
|
|
371
|
+
const accessStored = await setSecureKeyAsync(
|
|
372
|
+
"credential/chatgpt/access_token",
|
|
373
|
+
tokens.accessToken,
|
|
374
|
+
);
|
|
375
|
+
if (!accessStored) {
|
|
376
|
+
writeCliError("Failed to store access token", opts.json);
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
if (tokens.refreshToken) {
|
|
381
|
+
const refreshStored = await setSecureKeyAsync(
|
|
382
|
+
"credential/chatgpt/refresh_token",
|
|
383
|
+
tokens.refreshToken,
|
|
384
|
+
);
|
|
385
|
+
if (!refreshStored) {
|
|
386
|
+
writeCliError("Failed to store refresh token", opts.json);
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
if (tokens.expiresIn) {
|
|
392
|
+
const expiresAt = Math.floor(Date.now() / 1000 + tokens.expiresIn);
|
|
393
|
+
await setSecureKeyAsync(
|
|
394
|
+
"credential/chatgpt/expires_at",
|
|
395
|
+
String(expiresAt),
|
|
396
|
+
);
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// Step 3: Create (or update) provider connection via IPC
|
|
400
|
+
const connectionName = "chatgpt-subscription";
|
|
401
|
+
const authInput = {
|
|
402
|
+
type: "oauth_subscription",
|
|
403
|
+
credential: "credential/chatgpt/access_token",
|
|
404
|
+
};
|
|
405
|
+
|
|
406
|
+
// Try to update first; if the connection doesn't exist, create it.
|
|
407
|
+
const updateResult = await cliIpcCall<ProviderConnection>(
|
|
408
|
+
"inference_provider_connections_update",
|
|
409
|
+
{
|
|
410
|
+
pathParams: { name: connectionName },
|
|
411
|
+
body: { auth: authInput },
|
|
412
|
+
},
|
|
413
|
+
);
|
|
414
|
+
|
|
415
|
+
if (!updateResult.ok) {
|
|
416
|
+
// Connection doesn't exist yet — create it
|
|
417
|
+
const createResult = await cliIpcCall<ProviderConnection>(
|
|
418
|
+
"inference_provider_connections_create",
|
|
419
|
+
{
|
|
420
|
+
body: {
|
|
421
|
+
name: connectionName,
|
|
422
|
+
provider: "openai",
|
|
423
|
+
auth: authInput,
|
|
424
|
+
},
|
|
425
|
+
},
|
|
426
|
+
);
|
|
427
|
+
|
|
428
|
+
if (!createResult.ok) {
|
|
429
|
+
writeCliError(
|
|
430
|
+
createResult.error ?? "Failed to create provider connection",
|
|
431
|
+
opts.json,
|
|
432
|
+
);
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
if (opts.json) {
|
|
438
|
+
process.stdout.write(
|
|
439
|
+
JSON.stringify({
|
|
440
|
+
ok: true,
|
|
441
|
+
connection: connectionName,
|
|
442
|
+
message: "ChatGPT subscription auth configured successfully",
|
|
443
|
+
}) + "\n",
|
|
444
|
+
);
|
|
445
|
+
} else {
|
|
446
|
+
process.stdout.write(
|
|
447
|
+
`ChatGPT subscription auth configured successfully.\n` +
|
|
448
|
+
`Connection "${connectionName}" is ready (provider=openai, auth=oauth_subscription).\n`,
|
|
449
|
+
);
|
|
450
|
+
}
|
|
451
|
+
} catch (err) {
|
|
452
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
453
|
+
writeCliError(message, opts.json);
|
|
454
|
+
}
|
|
455
|
+
});
|
|
456
|
+
}
|
|
457
|
+
|
|
314
458
|
// ---------------------------------------------------------------------------
|
|
315
459
|
// Registration
|
|
316
460
|
// ---------------------------------------------------------------------------
|
|
@@ -349,4 +493,6 @@ Examples:
|
|
|
349
493
|
attachCreateSubcommand(connections);
|
|
350
494
|
attachUpdateSubcommand(connections);
|
|
351
495
|
attachDeleteSubcommand(connections);
|
|
496
|
+
|
|
497
|
+
attachLoginChatgptSubcommand(providers);
|
|
352
498
|
}
|
|
@@ -22,7 +22,9 @@ import { cliIpcCall } from "../../ipc/cli-client.js";
|
|
|
22
22
|
import type {
|
|
23
23
|
MemoryV2BackfillOp,
|
|
24
24
|
MemoryV2BackfillResult,
|
|
25
|
+
MemoryV2EmaScoresResult,
|
|
25
26
|
MemoryV2ReembedSkillsResult,
|
|
27
|
+
MemoryV2SimulateRouterResult,
|
|
26
28
|
MemoryV2ValidateResult,
|
|
27
29
|
} from "../../runtime/routes/memory-v2-routes.js";
|
|
28
30
|
import { registerCommand } from "../lib/register-command.js";
|
|
@@ -252,6 +254,312 @@ Examples:
|
|
|
252
254
|
process.exitCode = 1;
|
|
253
255
|
}
|
|
254
256
|
});
|
|
257
|
+
|
|
258
|
+
// ── ema ───────────────────────────────────────────────────────────────
|
|
259
|
+
|
|
260
|
+
v2.command("ema")
|
|
261
|
+
.description(
|
|
262
|
+
"List concept pages by injection-frequency EMA score (read-only)",
|
|
263
|
+
)
|
|
264
|
+
.option(
|
|
265
|
+
"-n, --limit <count>",
|
|
266
|
+
"Maximum rows to print (default 25; ignored with --all)",
|
|
267
|
+
"25",
|
|
268
|
+
)
|
|
269
|
+
.option("--all", "Print every page, including zero-score pages")
|
|
270
|
+
.option(
|
|
271
|
+
"--include-zeros",
|
|
272
|
+
"Include pages with score 0 in the default-limited view",
|
|
273
|
+
)
|
|
274
|
+
.option("--json", "Emit raw JSON instead of a formatted table")
|
|
275
|
+
.addHelpText(
|
|
276
|
+
"after",
|
|
277
|
+
`
|
|
278
|
+
EMA score is the time-decayed sum Σ exp(-λ × (now - tᵢ)) with a 3-day
|
|
279
|
+
half-life, computed from memory_v2_injection_events. A score of 1.0 means
|
|
280
|
+
roughly one router selection in the last few minutes; 0.5 means a single
|
|
281
|
+
selection ~3 days ago. Pages that have never been router-selected since
|
|
282
|
+
EMA tracking began report 0.
|
|
283
|
+
|
|
284
|
+
Examples:
|
|
285
|
+
$ assistant memory v2 ema
|
|
286
|
+
$ assistant memory v2 ema -n 100
|
|
287
|
+
$ assistant memory v2 ema --all --json | jq '.entries | length'`,
|
|
288
|
+
)
|
|
289
|
+
.action(
|
|
290
|
+
async (opts: {
|
|
291
|
+
limit: string;
|
|
292
|
+
all?: boolean;
|
|
293
|
+
includeZeros?: boolean;
|
|
294
|
+
json?: boolean;
|
|
295
|
+
}) => {
|
|
296
|
+
const result = await cliIpcCall<MemoryV2EmaScoresResult>(
|
|
297
|
+
"memory_v2_ema_scores",
|
|
298
|
+
{ body: {} },
|
|
299
|
+
);
|
|
300
|
+
|
|
301
|
+
if (!result.ok) {
|
|
302
|
+
log.error(result.error ?? "Failed to fetch EMA scores");
|
|
303
|
+
process.exitCode = 1;
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
const allEntries = result.result!.entries;
|
|
308
|
+
const includeZeros =
|
|
309
|
+
opts.all === true || opts.includeZeros === true;
|
|
310
|
+
const visible = includeZeros
|
|
311
|
+
? allEntries
|
|
312
|
+
: allEntries.filter((e) => e.score > 0);
|
|
313
|
+
|
|
314
|
+
const limit =
|
|
315
|
+
opts.all === true ? visible.length : Number(opts.limit);
|
|
316
|
+
if (!opts.all && (!Number.isFinite(limit) || limit < 1)) {
|
|
317
|
+
log.error(
|
|
318
|
+
`--limit must be a positive integer (got "${opts.limit}")`,
|
|
319
|
+
);
|
|
320
|
+
process.exitCode = 1;
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
const rows = visible.slice(0, limit);
|
|
324
|
+
|
|
325
|
+
if (opts.json === true) {
|
|
326
|
+
log.info(
|
|
327
|
+
JSON.stringify(
|
|
328
|
+
{
|
|
329
|
+
entries: rows,
|
|
330
|
+
totalScored: allEntries.filter((e) => e.score > 0).length,
|
|
331
|
+
totalPages: allEntries.length,
|
|
332
|
+
},
|
|
333
|
+
null,
|
|
334
|
+
2,
|
|
335
|
+
),
|
|
336
|
+
);
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
if (rows.length === 0) {
|
|
341
|
+
log.info(
|
|
342
|
+
"No concept pages have any EMA signal yet. Send a few turns through the router and try again.",
|
|
343
|
+
);
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
const slugWidth = Math.min(
|
|
348
|
+
60,
|
|
349
|
+
Math.max(...rows.map((r) => r.slug.length)),
|
|
350
|
+
);
|
|
351
|
+
const header = `${"slug".padEnd(slugWidth)} ${"score".padStart(8)} modified`;
|
|
352
|
+
log.info(header);
|
|
353
|
+
log.info("-".repeat(header.length));
|
|
354
|
+
for (const row of rows) {
|
|
355
|
+
const slug =
|
|
356
|
+
row.slug.length > slugWidth
|
|
357
|
+
? row.slug.slice(0, slugWidth - 1) + "…"
|
|
358
|
+
: row.slug.padEnd(slugWidth);
|
|
359
|
+
const score = row.score.toFixed(3).padStart(8);
|
|
360
|
+
const modified =
|
|
361
|
+
row.modifiedAt > 0
|
|
362
|
+
? new Date(row.modifiedAt).toISOString().slice(0, 10)
|
|
363
|
+
: "—";
|
|
364
|
+
log.info(`${slug} ${score} ${modified}`);
|
|
365
|
+
}
|
|
366
|
+
const totalScored = allEntries.filter((e) => e.score > 0).length;
|
|
367
|
+
log.info(
|
|
368
|
+
`\n${rows.length} of ${visible.length} shown (${totalScored} total with score > 0, ${allEntries.length} pages indexed).`,
|
|
369
|
+
);
|
|
370
|
+
},
|
|
371
|
+
);
|
|
372
|
+
|
|
373
|
+
// ── simulate ──────────────────────────────────────────────────────────
|
|
374
|
+
|
|
375
|
+
v2.command("simulate")
|
|
376
|
+
.description(
|
|
377
|
+
"Dry-run the v4 router against a synthetic query (read-only)",
|
|
378
|
+
)
|
|
379
|
+
.requiredOption(
|
|
380
|
+
"-q, --query <text>",
|
|
381
|
+
"User query to route the simulated turn against",
|
|
382
|
+
)
|
|
383
|
+
.option(
|
|
384
|
+
"--tier1-size <n>",
|
|
385
|
+
"Override memory.v2.router.tier1_size for this run (number or 'null')",
|
|
386
|
+
)
|
|
387
|
+
.option(
|
|
388
|
+
"--tier2-size <n>",
|
|
389
|
+
"Override memory.v2.router.tier2_size for this run (number or 'null')",
|
|
390
|
+
)
|
|
391
|
+
.option(
|
|
392
|
+
"--batch-size <n>",
|
|
393
|
+
"Override memory.v2.router.batch_size for this run (number or 'null')",
|
|
394
|
+
)
|
|
395
|
+
.option("--json", "Emit raw JSON instead of a grouped report")
|
|
396
|
+
.addHelpText(
|
|
397
|
+
"after",
|
|
398
|
+
`
|
|
399
|
+
Runs the v4 router read-only against the live page index + EMA scores, with
|
|
400
|
+
optional tier/batch overrides applied on top of the live config. NO writes:
|
|
401
|
+
no row is appended to memory_v2_injection_events or memory_v2_activation_logs,
|
|
402
|
+
and no activation state is mutated. Use this to preview the effect of a
|
|
403
|
+
config knob change before flipping it in workspace config.json.
|
|
404
|
+
|
|
405
|
+
Limitations:
|
|
406
|
+
- priorEverInjected is empty (single-turn simulation; live router dedups
|
|
407
|
+
against pages already in context).
|
|
408
|
+
- NOW.md is read at simulate-time, not historical-turn time.
|
|
409
|
+
- assistantMessage is empty.
|
|
410
|
+
|
|
411
|
+
Pass 'null' to an override flag to explicitly disable that tier for this run
|
|
412
|
+
(e.g. --tier2-size null reverts to tier1 → tier3). Omitting an override
|
|
413
|
+
inherits the live config value.
|
|
414
|
+
|
|
415
|
+
Examples:
|
|
416
|
+
$ assistant memory v2 simulate -q "what should we ship next"
|
|
417
|
+
$ assistant memory v2 simulate -q "..." --tier1-size 100 --tier2-size 200 --batch-size 50
|
|
418
|
+
$ assistant memory v2 simulate -q "..." --json | jq '.selectedSlugs'`,
|
|
419
|
+
)
|
|
420
|
+
.action(
|
|
421
|
+
async (opts: {
|
|
422
|
+
query: string;
|
|
423
|
+
tier1Size?: string;
|
|
424
|
+
tier2Size?: string;
|
|
425
|
+
batchSize?: string;
|
|
426
|
+
json?: boolean;
|
|
427
|
+
}) => {
|
|
428
|
+
const parseOverride = (
|
|
429
|
+
flag: string,
|
|
430
|
+
raw: string | undefined,
|
|
431
|
+
): number | null | undefined => {
|
|
432
|
+
if (raw === undefined) return undefined;
|
|
433
|
+
if (raw === "null") return null;
|
|
434
|
+
const parsed = Number(raw);
|
|
435
|
+
if (!Number.isInteger(parsed) || parsed < 1) {
|
|
436
|
+
log.error(
|
|
437
|
+
`${flag} must be a positive integer or 'null' (got "${raw}")`,
|
|
438
|
+
);
|
|
439
|
+
process.exitCode = 1;
|
|
440
|
+
throw new Error("invalid-override");
|
|
441
|
+
}
|
|
442
|
+
return parsed;
|
|
443
|
+
};
|
|
444
|
+
|
|
445
|
+
let configOverrides:
|
|
446
|
+
| {
|
|
447
|
+
tier1_size?: number | null;
|
|
448
|
+
tier2_size?: number | null;
|
|
449
|
+
batch_size?: number | null;
|
|
450
|
+
}
|
|
451
|
+
| undefined;
|
|
452
|
+
try {
|
|
453
|
+
const t1 = parseOverride("--tier1-size", opts.tier1Size);
|
|
454
|
+
const t2 = parseOverride("--tier2-size", opts.tier2Size);
|
|
455
|
+
const bs = parseOverride("--batch-size", opts.batchSize);
|
|
456
|
+
configOverrides = {
|
|
457
|
+
...(t1 !== undefined ? { tier1_size: t1 } : {}),
|
|
458
|
+
...(t2 !== undefined ? { tier2_size: t2 } : {}),
|
|
459
|
+
...(bs !== undefined ? { batch_size: bs } : {}),
|
|
460
|
+
};
|
|
461
|
+
if (Object.keys(configOverrides).length === 0) {
|
|
462
|
+
configOverrides = undefined;
|
|
463
|
+
}
|
|
464
|
+
} catch {
|
|
465
|
+
return;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
const result = await cliIpcCall<MemoryV2SimulateRouterResult>(
|
|
469
|
+
"memory_v2_simulate_router",
|
|
470
|
+
{
|
|
471
|
+
body: {
|
|
472
|
+
query: opts.query,
|
|
473
|
+
...(configOverrides ? { configOverrides } : {}),
|
|
474
|
+
},
|
|
475
|
+
},
|
|
476
|
+
);
|
|
477
|
+
|
|
478
|
+
if (!result.ok) {
|
|
479
|
+
log.error(result.error ?? "Failed to simulate router");
|
|
480
|
+
process.exitCode = 1;
|
|
481
|
+
return;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
const payload = result.result!;
|
|
485
|
+
|
|
486
|
+
if (opts.json === true) {
|
|
487
|
+
log.info(JSON.stringify(payload, null, 2));
|
|
488
|
+
return;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
log.info("Memory Router Simulation");
|
|
492
|
+
log.info("========================");
|
|
493
|
+
log.info(`Query: ${JSON.stringify(opts.query)}`);
|
|
494
|
+
log.info("");
|
|
495
|
+
log.info("Config (effective):");
|
|
496
|
+
const formatKnob = (
|
|
497
|
+
key: keyof MemoryV2SimulateRouterResult["effectiveConfig"],
|
|
498
|
+
): string => {
|
|
499
|
+
const eff = payload.effectiveConfig[key];
|
|
500
|
+
const override = (
|
|
501
|
+
payload.overrides as Record<string, number | null | undefined>
|
|
502
|
+
)[key];
|
|
503
|
+
const effStr = eff === null ? "null" : String(eff);
|
|
504
|
+
if (override === undefined) {
|
|
505
|
+
return ` ${key}: ${effStr}`;
|
|
506
|
+
}
|
|
507
|
+
return ` ${key}: ${effStr} (override)`;
|
|
508
|
+
};
|
|
509
|
+
log.info(formatKnob("tier1_size"));
|
|
510
|
+
log.info(formatKnob("tier2_size"));
|
|
511
|
+
log.info(formatKnob("batch_size"));
|
|
512
|
+
log.info(` max_page_ids: ${payload.effectiveConfig.max_page_ids}`);
|
|
513
|
+
log.info("");
|
|
514
|
+
log.info(`Total candidate pages: ${payload.totalCandidatePages}`);
|
|
515
|
+
log.info(
|
|
516
|
+
`Selected: ${payload.selectedSlugs.length} / ${payload.effectiveConfig.max_page_ids} pages`,
|
|
517
|
+
);
|
|
518
|
+
if (payload.failureReason) {
|
|
519
|
+
log.info(`Failure: ${payload.failureReason}`);
|
|
520
|
+
}
|
|
521
|
+
log.info("");
|
|
522
|
+
|
|
523
|
+
const grouped = new Map<string, string[]>();
|
|
524
|
+
for (const slug of payload.selectedSlugs) {
|
|
525
|
+
const source = payload.sourceBySlug[slug] ?? "unknown";
|
|
526
|
+
const bucket = grouped.get(source) ?? [];
|
|
527
|
+
bucket.push(slug);
|
|
528
|
+
grouped.set(source, bucket);
|
|
529
|
+
}
|
|
530
|
+
const sortedKeys = [...grouped.keys()].sort((a, b) => {
|
|
531
|
+
const order = (s: string) => {
|
|
532
|
+
if (s === "tier1") return 0;
|
|
533
|
+
if (s === "tier2") return 1;
|
|
534
|
+
if (s.startsWith("tier3:")) {
|
|
535
|
+
return 2 + Number(s.slice("tier3:".length));
|
|
536
|
+
}
|
|
537
|
+
return Number.MAX_SAFE_INTEGER;
|
|
538
|
+
};
|
|
539
|
+
return order(a) - order(b);
|
|
540
|
+
});
|
|
541
|
+
|
|
542
|
+
for (const key of sortedKeys) {
|
|
543
|
+
const label = key.startsWith("tier3:")
|
|
544
|
+
? `tier 3 · b${key.slice("tier3:".length)}`
|
|
545
|
+
: key === "tier1"
|
|
546
|
+
? "tier 1"
|
|
547
|
+
: key === "tier2"
|
|
548
|
+
? "tier 2"
|
|
549
|
+
: key;
|
|
550
|
+
log.info(label);
|
|
551
|
+
for (const slug of grouped.get(key)!) {
|
|
552
|
+
if (key === "tier2") {
|
|
553
|
+
const score = payload.scores[slug] ?? 0;
|
|
554
|
+
log.info(` - ${slug} (EMA ${score.toFixed(3)})`);
|
|
555
|
+
} else {
|
|
556
|
+
log.info(` - ${slug}`);
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
log.info("");
|
|
560
|
+
}
|
|
561
|
+
},
|
|
562
|
+
);
|
|
255
563
|
},
|
|
256
564
|
});
|
|
257
565
|
}
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
import { registerCommand } from "../lib/register-command.js";
|
|
9
9
|
import { log } from "../logger.js";
|
|
10
10
|
import { shouldOutputJson, writeOutput } from "../output.js";
|
|
11
|
+
import { tryResolveConversationId } from "../utils/conversation-id.js";
|
|
11
12
|
|
|
12
13
|
// ---------------------------------------------------------------------------
|
|
13
14
|
// Command registration
|
|
@@ -248,8 +249,6 @@ Examples:
|
|
|
248
249
|
}
|
|
249
250
|
}
|
|
250
251
|
|
|
251
|
-
const sourceContextId = opts.sessionId ?? `cli-${Date.now()}`;
|
|
252
|
-
|
|
253
252
|
// Validate --conversation-id if provided
|
|
254
253
|
const conversationId = opts.conversationId?.trim();
|
|
255
254
|
if (opts.conversationId != null && !conversationId) {
|
|
@@ -261,6 +260,26 @@ Examples:
|
|
|
261
260
|
return;
|
|
262
261
|
}
|
|
263
262
|
|
|
263
|
+
// Picks up __CONVERSATION_ID / __SKILL_CONTEXT_JSON env vars
|
|
264
|
+
// so deferred-emit can buffer notifications when called from a
|
|
265
|
+
// background job that hasn't confirmed success yet.
|
|
266
|
+
const originatingConversationId = tryResolveConversationId();
|
|
267
|
+
|
|
268
|
+
// The signal's `sourceContextId` doubles as the home-feed's
|
|
269
|
+
// navigation target — `resolveHomeFeedMirror` looks it up via
|
|
270
|
+
// `getConversation()` and only renders a "Go to Convo" button
|
|
271
|
+
// when it resolves to a real row. Prefer the conversation the
|
|
272
|
+
// CLI was invoked from (env-derived) so notifications emitted
|
|
273
|
+
// by background jobs and skills link back to their producing
|
|
274
|
+
// convo; an explicit --session-id still wins to preserve
|
|
275
|
+
// caller intent, and --conversation-id is the last resort
|
|
276
|
+
// before the unresolvable `cli-<ts>` sentinel.
|
|
277
|
+
const sourceContextId =
|
|
278
|
+
opts.sessionId ??
|
|
279
|
+
originatingConversationId ??
|
|
280
|
+
conversationId ??
|
|
281
|
+
`cli-${Date.now()}`;
|
|
282
|
+
|
|
264
283
|
const result = await cliIpcCall<{
|
|
265
284
|
signalId: string;
|
|
266
285
|
dispatched: boolean;
|
|
@@ -289,6 +308,9 @@ Examples:
|
|
|
289
308
|
...(conversationId
|
|
290
309
|
? { conversationAffinityHint: { vellum: conversationId } }
|
|
291
310
|
: {}),
|
|
311
|
+
...(originatingConversationId
|
|
312
|
+
? { originatingConversationId }
|
|
313
|
+
: {}),
|
|
292
314
|
throwOnError: true,
|
|
293
315
|
},
|
|
294
316
|
});
|