@vellumai/assistant 0.10.3-staging.2 → 0.10.4-staging.1
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/openapi.yaml +73 -56
- package/package.json +1 -1
- package/src/__tests__/actor-trust-resolver-address-fallback.test.ts +83 -31
- package/src/__tests__/assistant-stream-state.test.ts +3 -76
- package/src/__tests__/background-workers-disk-pressure.test.ts +4 -2
- package/src/__tests__/channel-approval-routes.test.ts +21 -26
- package/src/__tests__/channel-delivery-store.test.ts +28 -0
- package/src/__tests__/channel-guardian.test.ts +82 -32
- package/src/__tests__/channel-inbound-disk-pressure.test.ts +11 -19
- package/src/__tests__/channel-reply-delivery.test.ts +6 -2
- package/src/__tests__/compaction-ledger-store.test.ts +128 -0
- package/src/__tests__/config-loader-backfill.test.ts +148 -0
- package/src/__tests__/consult-deadline.test.ts +60 -0
- package/src/__tests__/contact-store-interaction-info.test.ts +156 -0
- package/src/__tests__/contact-store-user-file.test.ts +7 -10
- package/src/__tests__/contacts-relay-reads.test.ts +6 -9
- package/src/__tests__/contacts-write.test.ts +0 -2
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +4 -2
- package/src/__tests__/conversation-agent-loop.test.ts +98 -7
- package/src/__tests__/conversation-attention-telegram.test.ts +9 -11
- package/src/__tests__/conversation-error.test.ts +18 -0
- package/src/__tests__/conversation-fork-crud.test.ts +354 -24
- package/src/__tests__/conversation-title-service.test.ts +222 -201
- package/src/__tests__/db-compaction-events-migration.test.ts +129 -0
- package/src/__tests__/delete-propagation.test.ts +5 -3
- package/src/__tests__/dm-backfill.test.ts +6 -4
- package/src/__tests__/emit-signal-routing-intent.test.ts +2 -6
- package/src/__tests__/guardian-binding-drift-heal.test.ts +43 -23
- package/src/__tests__/guardian-dispatch.test.ts +50 -5
- package/src/__tests__/guardian-routing-state.test.ts +6 -10
- package/src/__tests__/helpers/channel-test-adapter.ts +45 -12
- package/src/__tests__/helpers/create-guardian-binding.ts +15 -23
- package/src/__tests__/helpers/mock-logger.ts +1 -0
- package/src/__tests__/helpers/seed-contact-channel.ts +96 -0
- package/src/__tests__/inbound-invite-redemption.test.ts +87 -10
- package/src/__tests__/invite-redemption-service.test.ts +273 -53
- package/src/__tests__/invite-routes-http.test.ts +34 -0
- package/src/__tests__/invite-service-ipc.test.ts +65 -2
- package/src/__tests__/list-messages-page-latest.test.ts +173 -4
- package/src/__tests__/mcp-config-secret-boundary.test.ts +3 -0
- package/src/__tests__/non-member-access-request.test.ts +15 -13
- package/src/__tests__/onboarding-persona-write.test.ts +52 -22
- package/src/__tests__/persist-onboarding-artifacts.test.ts +1 -0
- package/src/__tests__/persona-resolver.test.ts +75 -45
- package/src/__tests__/plugin-bootstrap.test.ts +13 -5
- package/src/__tests__/plugin-disabled-state.test.ts +190 -0
- package/src/__tests__/provider-usage-tracking.test.ts +1 -1
- package/src/__tests__/reaction-intercept-cold-cache-warm.test.ts +135 -0
- package/src/__tests__/reaction-intercept-member-verdict-warm.test.ts +158 -0
- package/src/__tests__/reaction-persistence.test.ts +51 -4
- package/src/__tests__/relay-server.test.ts +88 -31
- package/src/__tests__/runtime-attachment-metadata.test.ts +9 -11
- package/src/__tests__/settings-routes.test.ts +32 -0
- package/src/__tests__/slack-block-formatting.test.ts +1 -38
- package/src/__tests__/sse-actor-principal-guardian-source.test.ts +13 -36
- package/src/__tests__/stt-hints.test.ts +6 -3
- package/src/__tests__/subagent-fork-prompt-role.test.ts +195 -0
- package/src/__tests__/subagent-fork-spawn.test.ts +6 -7
- package/src/__tests__/subagent-role-registry.test.ts +17 -4
- package/src/__tests__/subagent-spawn-and-await.test.ts +546 -0
- package/src/__tests__/subagent-tools.test.ts +398 -3
- package/src/__tests__/thread-backfill.test.ts +3 -3
- package/src/__tests__/tool-preview-lifecycle.test.ts +26 -10
- package/src/__tests__/tool-start-timestamp.test.ts +4 -3
- package/src/__tests__/trusted-contact-approval-notifier.test.ts +37 -51
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +2 -2
- package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +9 -7
- package/src/__tests__/trusted-contact-multichannel.test.ts +16 -7
- package/src/__tests__/trusted-contact-verification.test.ts +79 -54
- package/src/__tests__/voice-guardian-cold-cache-warm.test.ts +137 -0
- package/src/__tests__/voice-invite-redemption.test.ts +183 -20
- package/src/__tests__/workspace-migration-102-preserve-heartbeat-enabled-for-existing-workspaces.test.ts +3 -3
- package/src/__tests__/workspace-migration-111-prune-seeded-callsite-defaults.test.ts +2 -2
- package/src/__tests__/workspace-migration-112-remove-advisor-callsite-override.test.ts +170 -0
- package/src/__tests__/workspace-migration-drop-user-md.test.ts +196 -238
- package/src/a2a/__tests__/e2e-a2a-channel.test.ts +35 -47
- package/src/agent/loop-exclusive-tool.test.ts +19 -15
- package/src/agent/loop-native-web-search.test.ts +200 -0
- package/src/agent/loop.ts +108 -1
- package/src/api/responses/conversation-message.ts +9 -0
- package/src/approvals/guardian-request-resolvers.ts +16 -4
- package/src/calls/__tests__/relay-setup-router.test.ts +10 -18
- package/src/calls/guardian-dispatch.ts +14 -11
- package/src/calls/inbound-trust-reader.ts +7 -1
- package/src/calls/relay-access-wait.ts +6 -6
- package/src/calls/relay-server.ts +22 -2
- package/src/calls/relay-setup-router.ts +10 -10
- package/src/cli/commands/__tests__/conversations-slack.test.ts +1 -0
- package/src/cli/commands/contacts.ts +10 -7
- package/src/cli/commands/memory/__tests__/worker.test.ts +147 -17
- package/src/cli/commands/memory/worker.ts +97 -30
- package/src/cli/commands/plugins.ts +3 -146
- package/src/cli/lib/__tests__/list-installed-plugins.test.ts +17 -17
- package/src/cli/lib/__tests__/publish-plugin.test.ts +98 -0
- package/src/cli/lib/publish-plugin.ts +231 -1
- package/src/config/__tests__/sync-gated-profiles.test.ts +5 -7
- package/src/config/bundled-skills/subagent/SKILL.md +16 -1
- package/src/config/bundled-skills/subagent/TOOLS.json +5 -4
- package/src/config/call-site-defaults.ts +0 -6
- package/src/config/llm-resolver.ts +0 -3
- package/src/config/schemas/call-site-catalog.ts +0 -7
- package/src/config/schemas/heartbeat.ts +2 -5
- package/src/config/schemas/llm.ts +3 -12
- package/src/config/schemas/memory-lifecycle.ts +1 -1
- package/src/config/seed-inference-profiles.ts +76 -35
- package/src/config/sync-gated-profiles.ts +0 -3
- package/src/contacts/__tests__/contacts-write-revoke-relay.test.ts +7 -8
- package/src/contacts/__tests__/member-write-relay.test.ts +35 -11
- package/src/contacts/contact-store.ts +27 -237
- package/src/contacts/contacts-write.ts +18 -58
- package/src/contacts/gateway-channel-read.ts +51 -0
- package/src/contacts/member-write-relay.ts +25 -31
- package/src/contacts/types.ts +3 -15
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +0 -44
- package/src/daemon/conversation-agent-loop-handlers.ts +29 -10
- package/src/daemon/conversation-agent-loop.ts +68 -61
- package/src/daemon/conversation-error.ts +7 -10
- package/src/daemon/conversation-tool-setup.ts +0 -10
- package/src/daemon/conversation.ts +10 -0
- package/src/daemon/external-plugins-bootstrap.ts +8 -2
- package/src/daemon/handlers/__tests__/config-a2a-accept.test.ts +0 -1
- package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +0 -2
- package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +0 -2
- package/src/daemon/handlers/__tests__/config-channels.test.ts +9 -14
- package/src/daemon/handlers/config-channels.ts +14 -29
- package/src/daemon/lifecycle.ts +16 -4
- package/src/daemon/message-types/surfaces.ts +2 -0
- package/src/heartbeat/heartbeat-service.ts +5 -0
- package/src/home/relationship-state-writer.ts +5 -0
- package/src/memory/__tests__/embedding-cache.test.ts +136 -0
- package/src/memory/compaction-ledger-store.ts +107 -0
- package/src/memory/conversation-crud.ts +136 -61
- package/src/memory/conversation-title-service.ts +173 -24
- package/src/memory/embedding-backend.ts +8 -1
- package/src/memory/embedding-cache.ts +139 -0
- package/src/memory/jobs-worker.ts +75 -29
- package/src/memory/memory-retrospective-job.ts +5 -0
- package/src/memory/migrations/209-strip-thinking-from-consolidated.ts +27 -5
- package/src/memory/migrations/302-create-compaction-events.ts +107 -0
- package/src/memory/migrations/303-add-conversation-creation-seq.ts +33 -0
- package/src/memory/migrations/__tests__/209-strip-thinking-from-consolidated.test.ts +79 -6
- package/src/memory/schema/contacts.ts +6 -2
- package/src/memory/schema/conversations.ts +39 -0
- package/src/memory/steps.ts +1090 -367
- package/src/memory/worker-control.ts +104 -18
- package/src/memory/worker-process.ts +17 -0
- package/src/messaging/channel-binding-metadata.ts +31 -0
- package/src/messaging/channel-binding-schema.ts +51 -0
- package/src/messaging/providers/__tests__/callback-routing.test.ts +45 -0
- package/src/messaging/providers/__tests__/transport-dispatch.test.ts +195 -0
- package/src/messaging/providers/a2a/__tests__/deliver.test.ts +11 -0
- package/src/messaging/providers/a2a/deliver.ts +5 -1
- package/src/messaging/providers/a2a/transport.ts +10 -0
- package/src/messaging/providers/callback-routing.ts +48 -0
- package/src/messaging/providers/channel-transport.ts +55 -0
- package/src/messaging/providers/index.ts +65 -241
- package/src/messaging/providers/slack/binding-metadata.ts +62 -0
- package/src/messaging/providers/slack/transport.ts +92 -0
- package/src/messaging/providers/telegram-bot/transport.ts +51 -0
- package/src/messaging/providers/whatsapp/transport.ts +38 -0
- package/src/notifications/__tests__/broadcaster.test.ts +0 -8
- package/src/notifications/__tests__/connected-channels.test.ts +8 -36
- package/src/notifications/__tests__/destination-resolver.test.ts +12 -117
- package/src/notifications/destination-resolver.ts +7 -23
- package/src/notifications/emit-signal.ts +5 -11
- package/src/plugins/defaults/index.ts +0 -35
- package/src/plugins/defaults/memory-v3-shadow/__tests__/dense.test.ts +11 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/section-dense-store.test.ts +243 -2
- package/src/plugins/defaults/memory-v3-shadow/section-dense-store.ts +167 -14
- package/src/plugins/disabled-state.ts +31 -0
- package/src/plugins/registry.ts +55 -12
- package/src/prompts/persona-resolver.ts +43 -11
- package/src/providers/call-site-routing.ts +41 -0
- package/src/providers/provider-send-message.ts +6 -0
- package/src/providers/ratelimit.ts +6 -0
- package/src/providers/registry.ts +1 -1
- package/src/providers/retry.ts +6 -0
- package/src/providers/types.ts +13 -0
- package/src/providers/usage-tracking.ts +6 -0
- package/src/runtime/__tests__/guardian-vellum-migration.test.ts +30 -27
- package/src/runtime/__tests__/local-principal-trust.test.ts +16 -18
- package/src/runtime/__tests__/member-verdict-cache.test.ts +119 -0
- package/src/runtime/__tests__/trust-verdict-consumer.test.ts +115 -168
- package/src/runtime/access-request-helper.ts +1 -2
- package/src/runtime/actor-trust-resolver.ts +44 -17
- package/src/runtime/anchored-guardian.test.ts +7 -54
- package/src/runtime/anchored-guardian.ts +4 -53
- package/src/runtime/assistant-stream-state.ts +12 -74
- package/src/runtime/channel-reply-delivery.ts +3 -8
- package/src/runtime/guardian-vellum-migration.ts +18 -16
- package/src/runtime/invite-redemption-service.ts +25 -10
- package/src/runtime/local-actor-identity.test.ts +108 -0
- package/src/runtime/local-actor-identity.ts +27 -20
- package/src/runtime/member-verdict-cache.ts +0 -0
- package/src/runtime/routes/__tests__/contact-routes.test.ts +100 -7
- package/src/runtime/routes/__tests__/global-search-routes.test.ts +1 -2
- package/src/runtime/routes/__tests__/surface-action-routes.test.ts +2 -1
- package/src/runtime/routes/contact-routes.ts +40 -25
- package/src/runtime/routes/conversation-list-routes.ts +1 -29
- package/src/runtime/routes/conversation-routes.ts +27 -7
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +0 -10
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +4 -8
- package/src/runtime/routes/inbound-stages/reaction-intercept.ts +19 -0
- package/src/runtime/routes/settings-routes.ts +8 -3
- package/src/runtime/services/conversation-serializer.ts +6 -49
- package/src/runtime/slack-block-formatting.ts +0 -15
- package/src/runtime/trust-verdict-consumer.ts +36 -41
- package/src/subagent/__tests__/consult-prompt.test.ts +35 -0
- package/src/{plugins/defaults/advisor/__tests__/transcript.test.ts → subagent/__tests__/consult-transcript.test.ts} +47 -10
- package/src/{plugins/defaults/advisor/steering.ts → subagent/consult-prompt.ts} +17 -39
- package/src/{plugins/defaults/advisor/transcript.ts → subagent/consult-transcript.ts} +18 -8
- package/src/subagent/index.ts +1 -1
- package/src/subagent/manager.ts +245 -33
- package/src/subagent/types.ts +8 -1
- package/src/tools/registry.ts +10 -3
- package/src/tools/subagent/consult-deadline.ts +49 -0
- package/src/tools/subagent/spawn.ts +234 -5
- package/src/util/logger.ts +9 -0
- package/src/util/platform.ts +14 -0
- package/src/workspace/migrations/031-drop-user-md.ts +232 -148
- package/src/workspace/migrations/112-remove-advisor-callsite-override.ts +64 -0
- package/src/workspace/migrations/registry.ts +2 -0
- package/src/plugins/defaults/advisor/__tests__/advisor-gate.test.ts +0 -56
- package/src/plugins/defaults/advisor/__tests__/advisor-state-store.test.ts +0 -43
- package/src/plugins/defaults/advisor/__tests__/agent-loop-integration.test.ts +0 -137
- package/src/plugins/defaults/advisor/__tests__/consult.test.ts +0 -314
- package/src/plugins/defaults/advisor/__tests__/context-pack-gating.test.ts +0 -106
- package/src/plugins/defaults/advisor/__tests__/context-pack.test.ts +0 -60
- package/src/plugins/defaults/advisor/__tests__/hooks.test.ts +0 -138
- package/src/plugins/defaults/advisor/advisor-gate.ts +0 -29
- package/src/plugins/defaults/advisor/advisor-state-store.ts +0 -94
- package/src/plugins/defaults/advisor/config.ts +0 -21
- package/src/plugins/defaults/advisor/consult.ts +0 -197
- package/src/plugins/defaults/advisor/context-pack.ts +0 -288
- package/src/plugins/defaults/advisor/hooks/post-model-call.ts +0 -34
- package/src/plugins/defaults/advisor/hooks/pre-model-call.ts +0 -30
- package/src/plugins/defaults/advisor/hooks/user-prompt-submit.ts +0 -19
- package/src/plugins/defaults/advisor/package.json +0 -14
- package/src/plugins/defaults/advisor/tools/advisor.ts +0 -92
|
@@ -2,11 +2,45 @@ import { validateInferenceProfileKey } from "../../config/inference-profile-vali
|
|
|
2
2
|
import { resolveDefaultProfileKey } from "../../config/llm-resolver.js";
|
|
3
3
|
import { getConfig } from "../../config/loader.js";
|
|
4
4
|
import { findConversation } from "../../daemon/conversation-registry.js";
|
|
5
|
-
import {
|
|
6
|
-
import
|
|
7
|
-
|
|
5
|
+
import type { ServerMessage } from "../../daemon/message-protocol.js";
|
|
6
|
+
import {
|
|
7
|
+
getConversationOverrideProfile,
|
|
8
|
+
getMessages,
|
|
9
|
+
} from "../../memory/conversation-crud.js";
|
|
10
|
+
import type { ContentBlock, Message } from "../../providers/types.js";
|
|
11
|
+
import {
|
|
12
|
+
advisorRequestText,
|
|
13
|
+
buildAdvisorSystem,
|
|
14
|
+
} from "../../subagent/consult-prompt.js";
|
|
15
|
+
import { sanitizeConsultTranscript } from "../../subagent/consult-transcript.js";
|
|
16
|
+
import {
|
|
17
|
+
getSubagentManager,
|
|
18
|
+
SubagentAbortedError,
|
|
19
|
+
} from "../../subagent/index.js";
|
|
8
20
|
import type { SubagentRole } from "../../subagent/types.js";
|
|
21
|
+
import { getLogger } from "../../util/logger.js";
|
|
9
22
|
import type { ToolContext, ToolExecutionResult } from "../types.js";
|
|
23
|
+
import { createConsultDeadline } from "./consult-deadline.js";
|
|
24
|
+
|
|
25
|
+
const log = getLogger("subagent-spawn");
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Idle ceiling on a single advisor consult: abort only after this much time
|
|
29
|
+
* passes with NO streamed token (thinking or text). A reasoning advisor profile
|
|
30
|
+
* streams its reasoning while it works, so a fixed wall-clock ceiling would kill
|
|
31
|
+
* it mid-thought; an idle window instead fires only when the consult is
|
|
32
|
+
* genuinely stalled (or never starts). Generous enough to also span
|
|
33
|
+
* time-to-first-token over a large inherited transcript.
|
|
34
|
+
*/
|
|
35
|
+
const ADVISOR_IDLE_TIMEOUT_MS = 60_000;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Absolute backstop on a single advisor consult regardless of streaming
|
|
39
|
+
* progress, so a runaway or looping stream can't block the parent forever.
|
|
40
|
+
* Either ceiling still yields the partial guidance (recovered in the
|
|
41
|
+
* `SubagentAbortedError` branch below), not a discard.
|
|
42
|
+
*/
|
|
43
|
+
const ADVISOR_MAX_TIMEOUT_MS = 300_000;
|
|
10
44
|
|
|
11
45
|
export async function executeSubagentSpawn(
|
|
12
46
|
input: Record<string, unknown>,
|
|
@@ -63,6 +97,19 @@ export async function executeSubagentSpawn(
|
|
|
63
97
|
};
|
|
64
98
|
}
|
|
65
99
|
|
|
100
|
+
// ── Advisor role: synchronous, tool-less, stronger-model consult ──
|
|
101
|
+
// Branch before the fire-and-forget path: the advisor blocks on the run and
|
|
102
|
+
// returns its guidance as the tool result in the same turn.
|
|
103
|
+
if (role === "advisor") {
|
|
104
|
+
return runAdvisorConsult({
|
|
105
|
+
context,
|
|
106
|
+
label,
|
|
107
|
+
objective,
|
|
108
|
+
sendToClient: sendToClient as (msg: ServerMessage) => void,
|
|
109
|
+
requestedOverrideProfile,
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
66
113
|
// ── Fork mode: resolve parent context ────────────────────────────
|
|
67
114
|
let forkFields:
|
|
68
115
|
| {
|
|
@@ -138,8 +185,8 @@ export async function executeSubagentSpawn(
|
|
|
138
185
|
objective,
|
|
139
186
|
context: extraContext,
|
|
140
187
|
sendResultToUser,
|
|
141
|
-
//
|
|
142
|
-
//
|
|
188
|
+
// Regular forks omit the role so they default to general; the advisor
|
|
189
|
+
// role is special-cased earlier via runAdvisorConsult, not here.
|
|
143
190
|
...(!fork && role ? { role: role as SubagentRole } : {}),
|
|
144
191
|
...(inheritedOverrideProfile
|
|
145
192
|
? { overrideProfile: inheritedOverrideProfile }
|
|
@@ -168,3 +215,185 @@ export async function executeSubagentSpawn(
|
|
|
168
215
|
return { content: `Failed to spawn subagent: ${msg}`, isError: true };
|
|
169
216
|
}
|
|
170
217
|
}
|
|
218
|
+
|
|
219
|
+
// ── Advisor consult ──────────────────────────────────────────────────
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Run the `advisor` role as a synchronous, context-inheriting, stronger-model
|
|
223
|
+
* consult and return its guidance as the tool result.
|
|
224
|
+
*
|
|
225
|
+
* Inherits the parent transcript (sanitized), frames it as advice via
|
|
226
|
+
* `buildAdvisorSystem`, runs tool-less on `llm.advisorProfile` (unless the
|
|
227
|
+
* caller passed an explicit `inference_profile`), and is bounded by a
|
|
228
|
+
* progress-aware deadline: an idle window (`ADVISOR_IDLE_TIMEOUT_MS`) reset on
|
|
229
|
+
* every streamed token so a reasoning model isn't killed mid-thought, plus an
|
|
230
|
+
* absolute `ADVISOR_MAX_TIMEOUT_MS` backstop. If either ceiling is hit, the
|
|
231
|
+
* partial guidance produced so far is recovered and returned with a "may be cut
|
|
232
|
+
* off" note rather than discarded. Degrades to a benign non-error notice on any
|
|
233
|
+
* other failure (including the depth-limit rejection when a subagent itself
|
|
234
|
+
* calls the advisor).
|
|
235
|
+
*/
|
|
236
|
+
async function runAdvisorConsult(args: {
|
|
237
|
+
context: ToolContext;
|
|
238
|
+
label: string;
|
|
239
|
+
/** The agent's own `objective` — its framing of what it wants advised on. */
|
|
240
|
+
objective: string;
|
|
241
|
+
sendToClient: (msg: ServerMessage) => void;
|
|
242
|
+
requestedOverrideProfile: string | undefined;
|
|
243
|
+
}): Promise<ToolExecutionResult> {
|
|
244
|
+
const { context, label, objective, sendToClient, requestedOverrideProfile } =
|
|
245
|
+
args;
|
|
246
|
+
|
|
247
|
+
try {
|
|
248
|
+
const parentConversation = findConversation(context.conversationId);
|
|
249
|
+
if (!parentConversation) {
|
|
250
|
+
return {
|
|
251
|
+
content:
|
|
252
|
+
"(advisor unavailable: parent conversation could not be resolved)",
|
|
253
|
+
isError: false,
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Snapshot the parent's in-memory transcript and system prompt, then append
|
|
258
|
+
// the in-flight assistant turn (the plan/text the model wrote THIS turn,
|
|
259
|
+
// before calling the advisor). The in-memory array does not yet hold that
|
|
260
|
+
// turn — the agent loop only writes it back to `conversation.messages` after
|
|
261
|
+
// the turn settles — but it is already persisted to the DB (the assistant
|
|
262
|
+
// row is finalized at `message_complete`, which fires before tool execution).
|
|
263
|
+
// `sanitizeConsultTranscript` then strips the dangling advisor `tool_use`
|
|
264
|
+
// off that final assistant turn so the inherited transcript is provider-safe.
|
|
265
|
+
const parentSystemPrompt = parentConversation.getCurrentSystemPrompt();
|
|
266
|
+
const withInFlight = appendInFlightAssistantTurn(
|
|
267
|
+
[...parentConversation.messages],
|
|
268
|
+
context.conversationId,
|
|
269
|
+
);
|
|
270
|
+
const sanitizedMessages = sanitizeConsultTranscript(withInFlight);
|
|
271
|
+
|
|
272
|
+
// Default to the stronger advisor profile when the caller did not pin one;
|
|
273
|
+
// an explicit `inference_profile` wins (already forced upstream).
|
|
274
|
+
const advisorProfile = getConfig().llm.advisorProfile;
|
|
275
|
+
const overrideProfile = requestedOverrideProfile ?? advisorProfile;
|
|
276
|
+
const forceOverrideProfile = overrideProfile !== undefined;
|
|
277
|
+
|
|
278
|
+
// Progress-aware deadline: reset on every streamed token so the consult
|
|
279
|
+
// isn't killed mid-thought, with an absolute backstop. Combine it with the
|
|
280
|
+
// caller's own signal.
|
|
281
|
+
const deadline = createConsultDeadline({
|
|
282
|
+
idleMs: ADVISOR_IDLE_TIMEOUT_MS,
|
|
283
|
+
maxMs: ADVISOR_MAX_TIMEOUT_MS,
|
|
284
|
+
});
|
|
285
|
+
const signal = context.signal
|
|
286
|
+
? AbortSignal.any([context.signal, deadline.signal])
|
|
287
|
+
: deadline.signal;
|
|
288
|
+
// Every streamed chunk (thinking or text) counts as progress and resets the
|
|
289
|
+
// idle window, then forwards to the caller's stream sink if one is present.
|
|
290
|
+
const onText = (chunk: string): void => {
|
|
291
|
+
deadline.recordProgress();
|
|
292
|
+
context.onOutput?.(chunk);
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
try {
|
|
296
|
+
const advice = await getSubagentManager().spawnAndAwait(
|
|
297
|
+
{
|
|
298
|
+
parentConversationId: context.conversationId,
|
|
299
|
+
label,
|
|
300
|
+
// Carry the agent's own objective into the consult request — the
|
|
301
|
+
// agent states the task here, and the inherited transcript can be thin.
|
|
302
|
+
objective: advisorRequestText(objective),
|
|
303
|
+
sendResultToUser: false,
|
|
304
|
+
role: "advisor",
|
|
305
|
+
fork: true,
|
|
306
|
+
parentMessages: sanitizedMessages,
|
|
307
|
+
systemPromptOverride: buildAdvisorSystem(parentSystemPrompt),
|
|
308
|
+
...(overrideProfile ? { overrideProfile } : {}),
|
|
309
|
+
...(forceOverrideProfile ? { forceOverrideProfile: true } : {}),
|
|
310
|
+
...(context.toolUseId ? { parentToolUseId: context.toolUseId } : {}),
|
|
311
|
+
},
|
|
312
|
+
sendToClient,
|
|
313
|
+
{ signal, onText },
|
|
314
|
+
);
|
|
315
|
+
|
|
316
|
+
const trimmed = advice.trim();
|
|
317
|
+
return {
|
|
318
|
+
content:
|
|
319
|
+
trimmed.length > 0 ? trimmed : "(advisor returned no guidance)",
|
|
320
|
+
isError: false,
|
|
321
|
+
};
|
|
322
|
+
} finally {
|
|
323
|
+
deadline.dispose();
|
|
324
|
+
}
|
|
325
|
+
} catch (err) {
|
|
326
|
+
// Timed out mid-generation: salvage whatever guidance the advisor had
|
|
327
|
+
// written rather than throwing it away. Partial strategic advice is far
|
|
328
|
+
// more useful to the agent than an "unavailable" notice — especially on a
|
|
329
|
+
// slow reasoning profile that needs most of the window to think.
|
|
330
|
+
if (err instanceof SubagentAbortedError) {
|
|
331
|
+
const partial = err.partialText.trim();
|
|
332
|
+
if (partial.length > 0) {
|
|
333
|
+
log.warn(
|
|
334
|
+
{ conversationId: context.conversationId },
|
|
335
|
+
"Advisor consult timed out; returning partial guidance",
|
|
336
|
+
);
|
|
337
|
+
return {
|
|
338
|
+
content: `${partial}\n\n_(The advisor reached its time limit while still writing — the guidance above may be cut off.)_`,
|
|
339
|
+
isError: false,
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
344
|
+
log.warn(
|
|
345
|
+
{ err, conversationId: context.conversationId },
|
|
346
|
+
"Advisor consult failed",
|
|
347
|
+
);
|
|
348
|
+
// Never fail the turn — the advisor is advice, not a blocker.
|
|
349
|
+
return { content: `(advisor unavailable: ${reason})`, isError: false };
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* Append the in-flight assistant turn (persisted this turn before the advisor
|
|
355
|
+
* tool ran) to an in-memory message snapshot, unless the snapshot already ends
|
|
356
|
+
* with it. The latest persisted assistant row carries the plan/text the model
|
|
357
|
+
* wrote immediately before calling the advisor plus the dangling advisor
|
|
358
|
+
* `tool_use`; `sanitizeConsultTranscript` strips the dangling call.
|
|
359
|
+
*
|
|
360
|
+
* Best-effort: a malformed or missing row leaves the snapshot unchanged so the
|
|
361
|
+
* consult still runs over the in-memory history.
|
|
362
|
+
*/
|
|
363
|
+
function appendInFlightAssistantTurn(
|
|
364
|
+
messages: Message[],
|
|
365
|
+
conversationId: string,
|
|
366
|
+
): Message[] {
|
|
367
|
+
// When the snapshot already ends on an assistant turn, the in-flight turn is
|
|
368
|
+
// present (or there is none to add) — appending the latest row would duplicate it.
|
|
369
|
+
if (messages[messages.length - 1]?.role === "assistant") return messages;
|
|
370
|
+
|
|
371
|
+
let rows;
|
|
372
|
+
try {
|
|
373
|
+
rows = getMessages(conversationId);
|
|
374
|
+
} catch {
|
|
375
|
+
return messages;
|
|
376
|
+
}
|
|
377
|
+
if (!rows || rows.length === 0) return messages;
|
|
378
|
+
|
|
379
|
+
const lastRow = rows[rows.length - 1];
|
|
380
|
+
if (lastRow.role !== "assistant") return messages;
|
|
381
|
+
|
|
382
|
+
let blocks: ContentBlock[];
|
|
383
|
+
try {
|
|
384
|
+
const parsed = JSON.parse(lastRow.content);
|
|
385
|
+
if (Array.isArray(parsed)) {
|
|
386
|
+
blocks = parsed as ContentBlock[];
|
|
387
|
+
} else if (typeof parsed === "string") {
|
|
388
|
+
blocks = [{ type: "text", text: parsed }];
|
|
389
|
+
} else {
|
|
390
|
+
return messages;
|
|
391
|
+
}
|
|
392
|
+
} catch {
|
|
393
|
+
// Plain-text content (no JSON envelope).
|
|
394
|
+
blocks = [{ type: "text", text: lastRow.content }];
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
if (blocks.length === 0) return messages;
|
|
398
|
+
return [...messages, { role: "assistant", content: blocks }];
|
|
399
|
+
}
|
package/src/util/logger.ts
CHANGED
|
@@ -49,6 +49,15 @@ function logFilePathForDate(dir: string, date: Date): string {
|
|
|
49
49
|
return join(dir, `${LOG_FILE_PREFIX}${formatDate(date)}${LOG_FILE_SUFFIX}`);
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
+
/**
|
|
53
|
+
* Returns the path to today's log file (`<logsDir>/assistant-YYYY-MM-DD.log`).
|
|
54
|
+
* Used by callers that need to open the same file the logger writes to, e.g.
|
|
55
|
+
* the memory worker spawner piping the child's stderr into the log file.
|
|
56
|
+
*/
|
|
57
|
+
export function getCurrentLogFilePath(): string {
|
|
58
|
+
return logFilePathForDate(getLogsDir(), new Date());
|
|
59
|
+
}
|
|
60
|
+
|
|
52
61
|
export function pruneOldLogFiles(dir: string, retentionDays: number): number {
|
|
53
62
|
if (!existsSync(dir)) return 0;
|
|
54
63
|
|
package/src/util/platform.ts
CHANGED
|
@@ -247,6 +247,20 @@ export function getMemoryWorkerPidPath(): string {
|
|
|
247
247
|
return join(getWorkspaceDir(), "memory-worker.pid");
|
|
248
248
|
}
|
|
249
249
|
|
|
250
|
+
/**
|
|
251
|
+
* Returns the path to the memory sync-runner marker file
|
|
252
|
+
* ($VELLUM_WORKSPACE_DIR/memory-sync-runner.pid).
|
|
253
|
+
*
|
|
254
|
+
* The daemon's in-process memory-jobs supervisor writes this marker (with its
|
|
255
|
+
* own PID) while the synchronous in-process runner is actively draining the
|
|
256
|
+
* queue, and removes it when it stands down for an out-of-process worker. It
|
|
257
|
+
* lets `assistant memory worker status` report whether the synchronous runner
|
|
258
|
+
* is still going without an IPC round-trip to the daemon.
|
|
259
|
+
*/
|
|
260
|
+
export function getMemorySyncRunnerMarkerPath(): string {
|
|
261
|
+
return join(getWorkspaceDir(), "memory-sync-runner.pid");
|
|
262
|
+
}
|
|
263
|
+
|
|
250
264
|
/**
|
|
251
265
|
* Returns the workspace root for user-facing state.
|
|
252
266
|
*
|