@vellumai/assistant 0.11.4-staging.2 → 0.11.4-staging.3
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 +8 -2
- package/ARCHITECTURE.md +2 -0
- package/docs/architecture/memory.md +15 -0
- package/docs/browser-use-architecture-phase2.md +128 -56
- package/docs/flux-turn-detection-spike.md +11 -6
- package/knip.json +3 -0
- package/openapi.yaml +136 -76
- package/package.json +1 -1
- package/scripts/write-plugin-api-shim.ts +10 -0
- package/src/__tests__/app-control-flow.test.ts +1 -0
- package/src/__tests__/approval-routes-http.test.ts +2 -2
- package/src/__tests__/assistant-feature-flag-guard.test.ts +25 -3
- package/src/__tests__/channel-setup-panel-ack.test.ts +1 -1
- package/src/__tests__/compaction-events.test.ts +8 -10
- package/src/__tests__/conversation-agent-loop.test.ts +4 -1
- package/src/__tests__/conversation-confirmation-signals.test.ts +112 -0
- package/src/__tests__/conversation-notifiers-provenance.test.ts +1 -1
- package/src/__tests__/conversation-queue.test.ts +39 -62
- package/src/__tests__/conversation-routes-disk-view.test.ts +1 -1
- package/src/__tests__/conversation-routes-enabled-plugins.test.ts +1 -1
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +9 -9
- package/src/__tests__/conversation-routes-hidden-queue.test.ts +1 -1
- package/src/__tests__/conversation-routes-slash-commands.test.ts +1 -1
- package/src/__tests__/conversation-slash-queue.test.ts +3 -0
- package/src/__tests__/conversation-surfaces-action-delivery.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-activation-emit.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-app-control.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-app-open.test.ts +1 -1
- package/src/__tests__/conversation-surfaces-data-persist.test.ts +1 -1
- package/src/__tests__/conversation-surfaces-history-restored-completion.test.ts +21 -14
- package/src/__tests__/conversation-surfaces-queued-emit.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-standalone.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-state-update.test.ts +1 -1
- package/src/__tests__/conversation-surfaces-table-action.test.ts +1 -1
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +1 -1
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +1 -1
- package/src/__tests__/conversation-tool-setup-attribution.test.ts +1 -1
- package/src/__tests__/cu-unified-flow.test.ts +1 -0
- package/src/__tests__/document-sync-tags.test.ts +0 -75
- package/src/__tests__/gateway-only-guard.test.ts +2 -5
- package/src/__tests__/http-user-message-parity.test.ts +1 -1
- package/src/__tests__/init-feature-flag-overrides.test.ts +49 -0
- package/src/__tests__/managed-skill-lifecycle.test.ts +7 -0
- package/src/__tests__/media-generate-image.test.ts +131 -21
- package/src/__tests__/memory-retrieval-hook.test.ts +94 -2
- package/src/__tests__/plugin-api-webhook-url.test.ts +10 -7
- package/src/__tests__/plugin-import-boundary-reverse-guard.test.ts +7 -3
- package/src/__tests__/proxy-approval-callback.test.ts +1 -0
- package/src/__tests__/qdrant-manager.test.ts +14 -1
- package/src/__tests__/run-due-schedules.test.ts +21 -0
- package/src/__tests__/scaffold-managed-skill-tool.test.ts +187 -18
- package/src/__tests__/schedule-routes.test.ts +23 -0
- package/src/__tests__/schedule-store.test.ts +17 -0
- package/src/__tests__/secret-ingress-http.test.ts +1 -1
- package/src/__tests__/send-endpoint-busy.test.ts +3 -3
- package/src/__tests__/starter-task-flow.test.ts +5 -4
- package/src/__tests__/subagent-fork-prompt-role.test.ts +1 -1
- package/src/__tests__/subagent-spawn-and-await.test.ts +4 -7
- package/src/__tests__/subagent-tool-gate-mode.test.ts +1 -1
- package/src/__tests__/subagent-tools.test.ts +81 -101
- package/src/__tests__/surface-completion-in-flight-snapshot.test.ts +1 -0
- package/src/__tests__/ui-choice-copy-surfaces.test.ts +1 -1
- package/src/__tests__/ui-visual-surface.test.ts +1 -1
- package/src/__tests__/ui-voice-picker-surface.test.ts +1 -1
- package/src/__tests__/ui-work-result-surface.test.ts +1 -1
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +5 -3
- package/src/__tests__/voice-session-bridge.test.ts +85 -29
- package/src/acp/session-manager.ts +8 -1
- package/src/api/surfaces.ts +5 -0
- package/src/calls/__tests__/voice-session-bridge.test.ts +21 -10
- package/src/calls/__tests__/voice-triage-escalate.test.ts +8 -0
- package/src/calls/voice-session-bridge.ts +44 -25
- package/src/calls/voice-triage-escalate.ts +1 -0
- package/src/cli/bundled-modules.ts +29 -0
- package/src/cli/commands/db/repair.ts +4 -8
- package/src/cli/commands/domain.ts +6 -3
- package/src/cli/commands/email.ts +6 -3
- package/src/cli/commands/keys.ts +8 -3
- package/src/cli/commands/plugins.ts +85 -36
- package/src/cli/commands/schedules.ts +35 -1
- package/src/cli/lib/bundled-marketplace.json +1 -1
- package/src/config/__tests__/balanced-model-experiment.test.ts +278 -0
- package/src/config/assistant-feature-flags.ts +36 -15
- package/src/config/balanced-model-experiment.ts +35 -0
- package/src/config/bundled-skills/image-studio/SKILL.md +5 -4
- package/src/config/bundled-skills/image-studio/TOOLS.json +1 -1
- package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +101 -0
- package/src/config/bundled-skills/skill-management/TOOLS.json +9 -3
- package/src/config/bundled-skills/subagent/SKILL.md +17 -12
- package/src/config/bundled-skills/subagent/TOOLS.json +4 -4
- package/src/config/call-site-defaults.ts +7 -0
- package/src/config/default-profile-catalog.ts +96 -4
- package/src/config/feature-flag-registry.json +11 -11
- package/src/config/skills.ts +9 -2
- package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +1 -1
- package/src/daemon/conversation-agent-loop.ts +14 -13
- package/src/daemon/conversation-notifiers.ts +11 -9
- package/src/daemon/conversation-process.ts +0 -27
- package/src/daemon/conversation-store.ts +4 -4
- package/src/daemon/conversation-surfaces.ts +27 -13
- package/src/daemon/conversation-tool-setup.ts +2 -4
- package/src/daemon/conversation.ts +81 -44
- package/src/daemon/doordash-steps.ts +2 -2
- package/src/daemon/lifecycle.ts +14 -1
- package/src/daemon/process-message.ts +0 -13
- package/src/daemon/windows-compiled-entry.ts +4 -0
- package/src/documents/document-store.ts +5 -235
- package/src/hooks/types.ts +5 -0
- package/src/ipc/gateway-flag-listener.ts +17 -3
- package/src/live-voice/__tests__/live-voice-flux-turn-end.test.ts +118 -0
- package/src/live-voice/live-voice-manager.ts +16 -3
- package/src/live-voice/live-voice-session.ts +63 -9
- package/src/live-voice/windows-compiled-live-voice.ts +4 -0
- package/src/monitoring/control.ts +1 -0
- package/src/monitoring/db-integrity-sample.ts +4 -5
- package/src/permissions/prompter.ts +1 -5
- package/src/persistence/conversation-queries.ts +66 -16
- package/src/persistence/embeddings/qdrant-manager.ts +84 -49
- package/src/persistence/migrations/360-add-document-workspace-path.ts +5 -14
- package/src/persistence/schema/documents.ts +4 -4
- package/src/plugin-api/constants.ts +8 -0
- package/src/plugin-api/index.ts +5 -1
- package/src/plugin-api/webhook-url.ts +13 -11
- package/src/plugins/defaults/main.ts +6 -7
- package/src/plugins/defaults/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +77 -0
- package/src/plugins/defaults/memory/graph/conversation-graph-memory.ts +24 -6
- package/src/plugins/defaults/memory/hooks/user-prompt-submit.ts +53 -5
- package/src/plugins/defaults/memory/memory-retrospective-job.ts +4 -4
- package/src/plugins/defaults/memory/v3/__tests__/injection.test.ts +18 -0
- package/src/plugins/defaults/memory/v3/__tests__/shadow-plugin.test.ts +66 -1
- package/src/plugins/defaults/memory/v3/injector.ts +8 -0
- package/src/plugins/defaults/memory/v3/shadow-plugin.ts +23 -9
- package/src/plugins/defaults/memory/worker-control.ts +1 -0
- package/src/plugins/defaults/worker-entrypoints.ts +3 -0
- package/src/plugins/mtime-cache.ts +17 -0
- package/src/prompts/templates/system-sections.ts +0 -7
- package/src/providers/__tests__/context-overflow-error.test.ts +24 -0
- package/src/providers/__tests__/retry-callsite.test.ts +20 -0
- package/src/providers/openai/chat-completions-provider.ts +11 -1
- package/src/providers/speech-to-text/__tests__/deepgram-flux-realtime.test.ts +11 -6
- package/src/providers/speech-to-text/deepgram-flux-realtime.ts +9 -49
- package/src/routes/control.ts +1 -0
- package/src/routes/route-host-client.ts +1 -0
- package/src/runtime/AGENTS.md +16 -17
- package/src/runtime/agent-wake.ts +15 -12
- package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +170 -1
- package/src/runtime/routes/__tests__/schedule-routes-disarm-reason.test.ts +215 -0
- package/src/runtime/routes/conversation-list-routes.ts +54 -22
- package/src/runtime/routes/conversation-management-routes.ts +2 -3
- package/src/runtime/routes/conversation-routes.ts +11 -13
- package/src/runtime/routes/documents-routes.ts +3 -222
- package/src/runtime/routes/playground/__tests__/inject-failures.test.ts +2 -0
- package/src/runtime/routes/playground/__tests__/reset-circuit.test.ts +3 -0
- package/src/runtime/routes/playground/inject-failures.ts +2 -2
- package/src/runtime/routes/playground/reset-circuit.ts +1 -1
- package/src/runtime/routes/schedule-routes.ts +94 -7
- package/src/runtime/routes/workspace-routes.ts +0 -9
- package/src/runtime/routes/workspace-utils.ts +3 -13
- package/src/runtime/services/conversation-serializer.ts +7 -2
- package/src/schedule/__tests__/plugin-schedule-declarations.test.ts +68 -5
- package/src/schedule/__tests__/plugin-schedule-reconciler.test.ts +81 -0
- package/src/schedule/plugin-schedule-availability.ts +58 -0
- package/src/schedule/plugin-schedule-declarations.ts +23 -27
- package/src/schedule/plugin-schedule-reconciler.ts +12 -3
- package/src/schedule/schedule-store.ts +5 -1
- package/src/schedule/scheduler.ts +9 -4
- package/src/schedule/worker-control.ts +1 -0
- package/src/subagent/__tests__/consult-prompt.test.ts +26 -15
- package/src/subagent/consult-context.ts +11 -11
- package/src/subagent/consult-prompt.ts +26 -35
- package/src/subagent/manager.ts +20 -37
- package/src/subagent/notify.ts +7 -1
- package/src/subagent/types.ts +8 -6
- package/src/tools/acp/spawn.ts +6 -4
- package/src/tools/skills/scaffold-managed.ts +25 -7
- package/src/tools/subagent/spawn.ts +28 -88
- package/src/tools/ui-surface/surface-shape-docs.ts +1 -1
- package/src/util/__tests__/worker-process-command.test.ts +37 -0
- package/src/util/logger.ts +16 -0
- package/src/util/worker-process.ts +37 -4
- package/src/windows-compiled-cli.ts +32 -0
- package/src/windows-compiled-entry.ts +4 -0
- package/src/windows-compiled-logger.ts +6 -0
- package/src/windows-compiled-worker-entry.ts +29 -0
- package/src/__tests__/document-workspace-file.test.ts +0 -467
- package/src/daemon/interactive-turn-sender.ts +0 -59
- package/src/subagent/__tests__/consult-transcript.test.ts +0 -184
- package/src/subagent/consult-transcript.ts +0 -90
|
@@ -5,20 +5,18 @@ import { validateInferenceProfileKey } from "../../config/inference-profile-vali
|
|
|
5
5
|
import { getConfig } from "../../config/loader.js";
|
|
6
6
|
import { profileSupportsTools } from "../../config/profile-tool-support.js";
|
|
7
7
|
import { findConversation } from "../../daemon/conversation-registry.js";
|
|
8
|
-
import { getMessages } from "../../persistence/conversation-crud.js";
|
|
9
8
|
import {
|
|
10
9
|
countRecentSimilarSpawns,
|
|
11
10
|
normalizeSpawnObjective,
|
|
12
11
|
type RecentSimilarSpawns,
|
|
13
12
|
type SimilarSpawnTally,
|
|
14
13
|
} from "../../persistence/subagent-store.js";
|
|
15
|
-
import type {
|
|
14
|
+
import type { Message } from "../../providers/types.js";
|
|
16
15
|
import { buildAdvisorContext } from "../../subagent/consult-context.js";
|
|
17
16
|
import {
|
|
18
17
|
advisorRequestText,
|
|
19
18
|
buildAdvisorSystem,
|
|
20
19
|
} from "../../subagent/consult-prompt.js";
|
|
21
|
-
import { sanitizeConsultTranscript } from "../../subagent/consult-transcript.js";
|
|
22
20
|
import {
|
|
23
21
|
getSubagentManager,
|
|
24
22
|
SubagentAbortedError,
|
|
@@ -49,7 +47,7 @@ const log = getLogger("subagent-spawn");
|
|
|
49
47
|
* reasoning while it works, so a fixed wall-clock ceiling would kill it
|
|
50
48
|
* mid-thought; an idle window instead fires only when the consult is genuinely
|
|
51
49
|
* stalled (or never starts). Generous enough to also span time-to-first-token
|
|
52
|
-
*
|
|
50
|
+
* on a slow reasoning profile.
|
|
53
51
|
*/
|
|
54
52
|
const ADVISOR_IDLE_TIMEOUT_MS = 60_000;
|
|
55
53
|
|
|
@@ -540,13 +538,19 @@ function inFlightGuardResult(
|
|
|
540
538
|
// ── Advisor consult ──────────────────────────────────────────────────
|
|
541
539
|
|
|
542
540
|
/**
|
|
543
|
-
* Run the `advisor` role as a synchronous,
|
|
544
|
-
*
|
|
541
|
+
* Run the `advisor` role as a synchronous, stronger-model consult and return
|
|
542
|
+
* its guidance as the tool result.
|
|
545
543
|
*
|
|
546
|
-
*
|
|
547
|
-
*
|
|
548
|
-
*
|
|
549
|
-
*
|
|
544
|
+
* The consult sees only what it is handed: the spawning agent's own `objective`
|
|
545
|
+
* as a written brief, plus the situational context pack from
|
|
546
|
+
* `buildAdvisorContext`. Nothing of the parent conversation's transcript or
|
|
547
|
+
* system prompt travels with it, so a consult costs the brief rather than a
|
|
548
|
+
* re-prefill of the whole chat at premium rates.
|
|
549
|
+
*
|
|
550
|
+
* It is framed as advice via `buildAdvisorSystem` and runs on
|
|
551
|
+
* `llm.advisorProfile` (unless the caller passed an explicit
|
|
552
|
+
* `inference_profile`) under both the advisor role allowlist and
|
|
553
|
+
* `denySideEffectTools`, so the only tools it can reach are the first-party
|
|
550
554
|
* built-in readers. It is bounded on two axes, because the consult holds up the
|
|
551
555
|
* user-facing turn while it runs: a progress-aware deadline (an idle window,
|
|
552
556
|
* `ADVISOR_IDLE_TIMEOUT_MS`, reset on every streamed token and every tool event
|
|
@@ -562,7 +566,7 @@ function inFlightGuardResult(
|
|
|
562
566
|
async function runAdvisorConsult(args: {
|
|
563
567
|
context: ToolContext;
|
|
564
568
|
label: string;
|
|
565
|
-
/** The agent's own `objective
|
|
569
|
+
/** The agent's own `objective`: the brief the advisor advises off. */
|
|
566
570
|
objective: string;
|
|
567
571
|
sendToClient: (msg: AssistantEvent) => void;
|
|
568
572
|
requestedOverrideProfile: string | undefined;
|
|
@@ -576,35 +580,16 @@ async function runAdvisorConsult(args: {
|
|
|
576
580
|
let profileNote: string | undefined;
|
|
577
581
|
|
|
578
582
|
try {
|
|
583
|
+
// The parent conversation is looked up only for its warm skill catalog, so
|
|
584
|
+
// an unresolvable one (e.g. evicted) costs the skills section of the pack
|
|
585
|
+
// and nothing else: the consult itself runs off the brief.
|
|
579
586
|
const parentConversation = findConversation(context.conversationId);
|
|
580
|
-
if (!parentConversation) {
|
|
581
|
-
return {
|
|
582
|
-
content:
|
|
583
|
-
"(advisor unavailable: parent conversation could not be resolved)",
|
|
584
|
-
isError: false,
|
|
585
|
-
};
|
|
586
|
-
}
|
|
587
|
-
|
|
588
|
-
// Snapshot the parent's in-memory transcript and system prompt, then append
|
|
589
|
-
// the in-flight assistant turn (the plan/text the model wrote THIS turn,
|
|
590
|
-
// before calling the advisor). The in-memory array does not yet hold that
|
|
591
|
-
// turn — the agent loop only writes it back to `conversation.messages` after
|
|
592
|
-
// the turn settles — but it is already persisted to the DB (the assistant
|
|
593
|
-
// row is finalized at `message_complete`, which fires before tool execution).
|
|
594
|
-
// `sanitizeConsultTranscript` then strips the dangling advisor `tool_use`
|
|
595
|
-
// off that final assistant turn so the inherited transcript is provider-safe.
|
|
596
|
-
const parentSystemPrompt = parentConversation.getCurrentSystemPrompt();
|
|
597
|
-
const withInFlight = appendInFlightAssistantTurn(
|
|
598
|
-
[...parentConversation.messages],
|
|
599
|
-
context.conversationId,
|
|
600
|
-
);
|
|
601
|
-
const sanitizedMessages = sanitizeConsultTranscript(withInFlight);
|
|
602
587
|
|
|
603
588
|
// Situational awareness for the advisor: the parent's live tool set, the
|
|
604
589
|
// full skill catalog, and its workspace. Assembled off the per-turn
|
|
605
590
|
// ToolContext snapshot (trust, channel) so the personal-memory sections
|
|
606
591
|
// are gated exactly like the runtime injectors. Best-effort: a null pack
|
|
607
|
-
// just means the consult runs on
|
|
592
|
+
// just means the consult runs on the brief alone.
|
|
608
593
|
const situationalContext = await buildAdvisorContext({
|
|
609
594
|
conversationId: context.conversationId,
|
|
610
595
|
workingDir: context.workingDir,
|
|
@@ -614,7 +599,7 @@ async function runAdvisorConsult(args: {
|
|
|
614
599
|
enabledPluginSet: context.enabledPluginSet,
|
|
615
600
|
// The parent's warm per-turn catalog keeps the synchronous on-disk
|
|
616
601
|
// catalog scan out of the consult path.
|
|
617
|
-
skillCatalog: parentConversation
|
|
602
|
+
skillCatalog: parentConversation?.skillProjectionCache?.catalog,
|
|
618
603
|
});
|
|
619
604
|
|
|
620
605
|
// Default to the stronger advisor profile when the caller did not pin one;
|
|
@@ -623,7 +608,7 @@ async function runAdvisorConsult(args: {
|
|
|
623
608
|
let overrideProfile = requestedOverrideProfile ?? config.llm.advisorProfile;
|
|
624
609
|
// The advisor carries read tools, so a profile the catalog states cannot
|
|
625
610
|
// call them is handed a surface it can never use and answers from the
|
|
626
|
-
//
|
|
611
|
+
// brief alone. Fall back to the call site's own default and say so
|
|
627
612
|
// alongside the guidance, the way a regular spawn reports it. The check is
|
|
628
613
|
// unconditional, matching the tools it protects, and only a catalog `false`
|
|
629
614
|
// redirects, so a model the catalog has never heard of is left alone.
|
|
@@ -697,16 +682,15 @@ async function runAdvisorConsult(args: {
|
|
|
697
682
|
{
|
|
698
683
|
parentConversationId: context.conversationId,
|
|
699
684
|
label,
|
|
700
|
-
//
|
|
701
|
-
//
|
|
702
|
-
//
|
|
703
|
-
//
|
|
704
|
-
//
|
|
685
|
+
// The agent's own objective IS the brief the consult runs on, so it
|
|
686
|
+
// carries into the request verbatim. The situational pack rides in
|
|
687
|
+
// the model request only (`requestText`), keeping the system prompt
|
|
688
|
+
// minimal and the display-facing `objective` free of bulky internal
|
|
689
|
+
// context.
|
|
705
690
|
objective: advisorRequestText(objective),
|
|
706
691
|
requestText: advisorRequestText(objective, situationalContext),
|
|
707
692
|
sendResultToUser: false,
|
|
708
693
|
role: "advisor",
|
|
709
|
-
fork: true,
|
|
710
694
|
// The advisor's read-only guarantee cannot rest on tool NAMES. A
|
|
711
695
|
// workspace tool may register under `file_read` (registerWorkspaceTools
|
|
712
696
|
// stashes the built-in and installs its own implementation), and a
|
|
@@ -718,10 +702,9 @@ async function runAdvisorConsult(args: {
|
|
|
718
702
|
//
|
|
719
703
|
// The advisor is a ROLE, not an `LLMCallSiteEnum` value, so its usage
|
|
720
704
|
// lands under `subagentSpawn` like any other subagent. This is what
|
|
721
|
-
// makes advisor consults separable from regular
|
|
705
|
+
// makes advisor consults separable from regular spawns in telemetry.
|
|
722
706
|
spawnMode: "advisor_consult",
|
|
723
|
-
|
|
724
|
-
systemPromptOverride: buildAdvisorSystem(parentSystemPrompt),
|
|
707
|
+
systemPromptOverride: buildAdvisorSystem(),
|
|
725
708
|
...(overrideProfile ? { overrideProfile } : {}),
|
|
726
709
|
...(forceOverrideProfile ? { forceOverrideProfile: true } : {}),
|
|
727
710
|
// A consult is delegated work of the invoking turn, so its spend
|
|
@@ -817,46 +800,3 @@ function withAdvisorNote(
|
|
|
817
800
|
}
|
|
818
801
|
return `${guidance}\n\n${present.map((note) => `_(${note})_`).join("\n")}`;
|
|
819
802
|
}
|
|
820
|
-
|
|
821
|
-
/**
|
|
822
|
-
* Append the in-flight assistant turn (persisted this turn before the advisor
|
|
823
|
-
* tool ran) to an in-memory message snapshot, unless the snapshot already ends
|
|
824
|
-
* with it. The latest persisted assistant row carries the plan/text the model
|
|
825
|
-
* wrote immediately before calling the advisor plus the dangling advisor
|
|
826
|
-
* `tool_use`; `sanitizeConsultTranscript` strips the dangling call.
|
|
827
|
-
*
|
|
828
|
-
* Best-effort: a malformed or missing row leaves the snapshot unchanged so the
|
|
829
|
-
* consult still runs over the in-memory history.
|
|
830
|
-
*/
|
|
831
|
-
function appendInFlightAssistantTurn(
|
|
832
|
-
messages: Message[],
|
|
833
|
-
conversationId: string,
|
|
834
|
-
): Message[] {
|
|
835
|
-
// When the snapshot already ends on an assistant turn, the in-flight turn is
|
|
836
|
-
// present (or there is none to add) — appending the latest row would duplicate it.
|
|
837
|
-
if (messages[messages.length - 1]?.role === "assistant") {
|
|
838
|
-
return messages;
|
|
839
|
-
}
|
|
840
|
-
|
|
841
|
-
let rows;
|
|
842
|
-
try {
|
|
843
|
-
rows = getMessages(conversationId);
|
|
844
|
-
} catch {
|
|
845
|
-
return messages;
|
|
846
|
-
}
|
|
847
|
-
if (!rows || rows.length === 0) {
|
|
848
|
-
return messages;
|
|
849
|
-
}
|
|
850
|
-
|
|
851
|
-
const lastRow = rows[rows.length - 1];
|
|
852
|
-
if (lastRow.role !== "assistant") {
|
|
853
|
-
return messages;
|
|
854
|
-
}
|
|
855
|
-
|
|
856
|
-
const blocks: ContentBlock[] = lastRow.content;
|
|
857
|
-
|
|
858
|
-
if (blocks.length === 0) {
|
|
859
|
-
return messages;
|
|
860
|
-
}
|
|
861
|
-
return [...messages, { role: "assistant", content: blocks }];
|
|
862
|
-
}
|
|
@@ -123,7 +123,7 @@ export const SURFACE_SHAPE_DOCS: Record<string, SurfaceShapeDoc> = {
|
|
|
123
123
|
work_result: {
|
|
124
124
|
purpose: "structured receipt after completed work",
|
|
125
125
|
shape:
|
|
126
|
-
'{ eyebrow?, status?: "completed"|"partial"|"failed"|"in_progress", summary?, metrics?: [{ label, value, detail?, tone?: "neutral"|"positive"|"warning"|"negative" }], sections?: [{ id?, title, description?, type?: "items"|"timeline"|"diff"|"artifacts"|"warnings", items?: [{ id?, title, description?, status?, tone?, metadata?: [{ label, value }], href? }], diffs?: [{ label?, before?, after? }] }] }
|
|
126
|
+
'{ eyebrow?, status?: "completed"|"partial"|"failed"|"in_progress", summary?, metrics?: [{ label, value, detail?, tone?: "neutral"|"positive"|"warning"|"negative" }], sections?: [{ id?, title, description?, type?: "items"|"timeline"|"diff"|"artifacts"|"warnings", items?: [{ id?, title, description?, status?, tone?, metadata?: [{ label, value }], href? }], diffs?: [{ label?, before?, after? }] }] }: structured receipt after real work; keep display-only unless follow-up buttons are needed. An item `href` makes the row a link: an in-app path (e.g. "/assistant/skills/<skillId>?tab=history") opens in place, an https URL opens externally; other schemes are ignored',
|
|
127
127
|
missingContent: (data) =>
|
|
128
128
|
hasContent(data)
|
|
129
129
|
? null
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
import { resolveWorkerCommand } from "../worker-process.js";
|
|
4
|
+
|
|
5
|
+
const entry = new URL("file:///source/monitoring/worker.ts");
|
|
6
|
+
|
|
7
|
+
describe("resolveWorkerCommand", () => {
|
|
8
|
+
test("uses the packaged Windows worker executable when present", () => {
|
|
9
|
+
expect(
|
|
10
|
+
resolveWorkerCommand(entry, "monitoring", {
|
|
11
|
+
platform: "win32",
|
|
12
|
+
execPath: "/runtime/vellum-daemon.exe",
|
|
13
|
+
executableExists: () => true,
|
|
14
|
+
}),
|
|
15
|
+
).toEqual(["/runtime/vellum-worker.exe", "monitoring"]);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test("routes integrity checks through the packaged worker", () => {
|
|
19
|
+
expect(
|
|
20
|
+
resolveWorkerCommand(entry, "db-integrity", {
|
|
21
|
+
platform: "win32",
|
|
22
|
+
execPath: "/runtime/vellum-worker.exe",
|
|
23
|
+
executableExists: () => true,
|
|
24
|
+
}),
|
|
25
|
+
).toEqual(["/runtime/vellum-worker.exe", "db-integrity"]);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test("falls back to the source entry outside a packaged runtime", () => {
|
|
29
|
+
expect(
|
|
30
|
+
resolveWorkerCommand(entry, "monitoring", {
|
|
31
|
+
platform: "win32",
|
|
32
|
+
execPath: "/runtime/vellum-daemon.exe",
|
|
33
|
+
executableExists: () => false,
|
|
34
|
+
}),
|
|
35
|
+
).toEqual(["bun", "--smol", "run", "/source/monitoring/worker.ts"]);
|
|
36
|
+
});
|
|
37
|
+
});
|
package/src/util/logger.ts
CHANGED
|
@@ -19,6 +19,16 @@ import { logSerializers } from "./log-redact.js";
|
|
|
19
19
|
import { getLogsDir } from "./platform.js";
|
|
20
20
|
|
|
21
21
|
const loadModule = createRequire(import.meta.url);
|
|
22
|
+
let bundledPino: typeof import("pino") | undefined;
|
|
23
|
+
let bundledPinoPretty: typeof import("pino-pretty") | undefined;
|
|
24
|
+
|
|
25
|
+
export function setBundledLoggerModules(
|
|
26
|
+
pino: typeof import("pino"),
|
|
27
|
+
pinoPretty: typeof import("pino-pretty"),
|
|
28
|
+
): void {
|
|
29
|
+
bundledPino = pino;
|
|
30
|
+
bundledPinoPretty = pinoPretty;
|
|
31
|
+
}
|
|
22
32
|
|
|
23
33
|
/**
|
|
24
34
|
* pino loads on first logger construction, not import, so CLI processes that
|
|
@@ -26,11 +36,17 @@ const loadModule = createRequire(import.meta.url);
|
|
|
26
36
|
* the real dual-export and ESM-shaped test mocks.
|
|
27
37
|
*/
|
|
28
38
|
function loadPino(): typeof import("pino") {
|
|
39
|
+
if (bundledPino) {
|
|
40
|
+
return bundledPino;
|
|
41
|
+
}
|
|
29
42
|
const mod = loadModule("pino") as { default?: unknown };
|
|
30
43
|
return (mod.default ?? mod) as typeof import("pino");
|
|
31
44
|
}
|
|
32
45
|
|
|
33
46
|
function loadPinoPretty(): typeof import("pino-pretty") {
|
|
47
|
+
if (bundledPinoPretty) {
|
|
48
|
+
return bundledPinoPretty;
|
|
49
|
+
}
|
|
34
50
|
const mod = loadModule("pino-pretty") as { default?: unknown };
|
|
35
51
|
return (mod.default ?? mod) as typeof import("pino-pretty");
|
|
36
52
|
}
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
readFileSync,
|
|
17
17
|
unlinkSync,
|
|
18
18
|
} from "node:fs";
|
|
19
|
-
import { dirname } from "node:path";
|
|
19
|
+
import { dirname, join } from "node:path";
|
|
20
20
|
import { fileURLToPath } from "node:url";
|
|
21
21
|
|
|
22
22
|
import { getCurrentLogFilePath } from "./logger.js";
|
|
@@ -122,6 +122,37 @@ export interface SpawnWorkerProcessOptions {
|
|
|
122
122
|
detached?: boolean;
|
|
123
123
|
}
|
|
124
124
|
|
|
125
|
+
export type PackagedWorkerEntry =
|
|
126
|
+
| "monitoring"
|
|
127
|
+
| "schedule"
|
|
128
|
+
| "memory"
|
|
129
|
+
| "routes"
|
|
130
|
+
| "db-integrity";
|
|
131
|
+
|
|
132
|
+
interface WorkerCommandRuntime {
|
|
133
|
+
platform: NodeJS.Platform;
|
|
134
|
+
execPath: string;
|
|
135
|
+
executableExists: (path: string) => boolean;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export function resolveWorkerCommand(
|
|
139
|
+
entry: URL,
|
|
140
|
+
packagedEntry: PackagedWorkerEntry | undefined,
|
|
141
|
+
runtime: WorkerCommandRuntime = {
|
|
142
|
+
platform: process.platform,
|
|
143
|
+
execPath: process.execPath,
|
|
144
|
+
executableExists: existsSync,
|
|
145
|
+
},
|
|
146
|
+
): string[] {
|
|
147
|
+
if (runtime.platform === "win32" && packagedEntry) {
|
|
148
|
+
const executable = join(dirname(runtime.execPath), "vellum-worker.exe");
|
|
149
|
+
if (runtime.executableExists(executable)) {
|
|
150
|
+
return [executable, packagedEntry];
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return ["bun", "--smol", "run", fileURLToPath(entry)];
|
|
154
|
+
}
|
|
155
|
+
|
|
125
156
|
type WorkerReadyOutcome = "ready" | "exited" | "timeout";
|
|
126
157
|
|
|
127
158
|
/**
|
|
@@ -178,6 +209,8 @@ export async function spawnWorkerProcess(args: {
|
|
|
178
209
|
pidPath: string;
|
|
179
210
|
/** Worker entry script, e.g. `new URL("./worker.ts", import.meta.url)`. */
|
|
180
211
|
entry: URL;
|
|
212
|
+
/** Entry bundled into vellum-worker.exe for packaged Windows runtimes. */
|
|
213
|
+
packagedEntry?: PackagedWorkerEntry;
|
|
181
214
|
/** Human-readable name used in spawn-failure messages, e.g. "Memory worker". */
|
|
182
215
|
workerLabel: string;
|
|
183
216
|
options?: SpawnWorkerProcessOptions;
|
|
@@ -208,15 +241,15 @@ export async function spawnWorkerProcess(args: {
|
|
|
208
241
|
// output is at least visible to the spawning process.
|
|
209
242
|
}
|
|
210
243
|
|
|
211
|
-
//
|
|
212
|
-
//
|
|
244
|
+
// Source workers use bun's small-heap mode. Packaged Windows workers use the
|
|
245
|
+
// compiled worker executable. Both receive the RAM hint from worker-memory.
|
|
213
246
|
//
|
|
214
247
|
// `fileURLToPath`, not `.pathname`: a URL's pathname is percent-encoded, so
|
|
215
248
|
// an install path containing a space (every macOS desktop install lives
|
|
216
249
|
// under "Application Support") would reach bun as "Application%20Support"
|
|
217
250
|
// and the entry would not be found.
|
|
218
251
|
const child = Bun.spawn({
|
|
219
|
-
cmd:
|
|
252
|
+
cmd: resolveWorkerCommand(args.entry, args.packagedEntry),
|
|
220
253
|
env: workerMemoryEnv(),
|
|
221
254
|
stdio: ["ignore", "ignore", stderrFd],
|
|
222
255
|
detached,
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { setBundledCliModules } from "./cli/bundled-modules.js";
|
|
2
|
+
import * as pluginDiff from "./cli/lib/diff-plugin.js";
|
|
3
|
+
import * as pluginInspect from "./cli/lib/inspect-plugin.js";
|
|
4
|
+
import * as pluginInstallGitHub from "./cli/lib/install-from-github.js";
|
|
5
|
+
import * as pluginInstallPlatform from "./cli/lib/install-from-platform.js";
|
|
6
|
+
import * as pluginInstalled from "./cli/lib/list-installed-plugins.js";
|
|
7
|
+
import * as pluginCatalogCache from "./cli/lib/plugin-catalog-cache.js";
|
|
8
|
+
import * as pluginCatalogLocal from "./cli/lib/plugin-catalog-local.js";
|
|
9
|
+
import * as pluginPinHistory from "./cli/lib/plugin-pin-history.js";
|
|
10
|
+
import * as pluginSurfaces from "./cli/lib/plugin-surfaces.js";
|
|
11
|
+
import * as pluginSearch from "./cli/lib/search-plugins.js";
|
|
12
|
+
import * as pluginUninstall from "./cli/lib/uninstall-plugin.js";
|
|
13
|
+
import * as pluginUpgrade from "./cli/lib/upgrade-plugin.js";
|
|
14
|
+
import * as configEnv from "./config/env.js";
|
|
15
|
+
import * as providerSecretCatalog from "./providers/provider-secret-catalog.js";
|
|
16
|
+
|
|
17
|
+
setBundledCliModules({
|
|
18
|
+
configEnv,
|
|
19
|
+
providerSecretCatalog,
|
|
20
|
+
pluginCatalogCache,
|
|
21
|
+
pluginCatalogLocal,
|
|
22
|
+
pluginDiff,
|
|
23
|
+
pluginInspect,
|
|
24
|
+
pluginInstallGitHub,
|
|
25
|
+
pluginInstallPlatform,
|
|
26
|
+
pluginInstalled,
|
|
27
|
+
pluginPinHistory,
|
|
28
|
+
pluginSearch,
|
|
29
|
+
pluginSurfaces,
|
|
30
|
+
pluginUninstall,
|
|
31
|
+
pluginUpgrade,
|
|
32
|
+
});
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import "./windows-compiled-logger.js";
|
|
2
|
+
|
|
3
|
+
const worker = process.argv[2];
|
|
4
|
+
|
|
5
|
+
switch (worker) {
|
|
6
|
+
case "monitoring":
|
|
7
|
+
await import("./monitoring/worker.js");
|
|
8
|
+
break;
|
|
9
|
+
case "schedule":
|
|
10
|
+
await import("./schedule/worker.js");
|
|
11
|
+
break;
|
|
12
|
+
case "memory":
|
|
13
|
+
await (
|
|
14
|
+
await import("./plugins/defaults/worker-entrypoints.js")
|
|
15
|
+
).loadDefaultMemoryWorker();
|
|
16
|
+
break;
|
|
17
|
+
case "routes":
|
|
18
|
+
await import("./embedded/plugin-api.js");
|
|
19
|
+
await import("./routes/worker.js");
|
|
20
|
+
break;
|
|
21
|
+
case "db-integrity": {
|
|
22
|
+
const { runIntegrityCheck } =
|
|
23
|
+
await import("./monitoring/db-integrity-check.js");
|
|
24
|
+
console.log(JSON.stringify(runIntegrityCheck(process.argv[3] ?? "")));
|
|
25
|
+
break;
|
|
26
|
+
}
|
|
27
|
+
default:
|
|
28
|
+
throw new Error(`Unknown Windows worker entry: ${worker ?? "missing"}`);
|
|
29
|
+
}
|