@vellumai/assistant 0.9.0 → 0.9.1-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/ARCHITECTURE.md +18 -34
- package/bun.lock +7 -8
- package/docs/activation-funnel-telemetry.md +4 -4
- package/docs/architecture/security.md +29 -28
- package/docs/stt-provider-onboarding.md +3 -5
- package/docs/workflows-testing.md +13 -44
- package/docs/workflows.md +3 -5
- package/node_modules/@vellumai/ces-client/src/__tests__/ces-client.test.ts +47 -0
- package/node_modules/@vellumai/ces-client/src/rpc-client.ts +28 -5
- package/node_modules/@vellumai/environments/src/seeds.ts +2 -5
- package/node_modules/@vellumai/gateway-client/src/index.ts +17 -6
- package/node_modules/@vellumai/gateway-client/src/outbound-contract.ts +119 -0
- package/node_modules/@vellumai/gateway-client/src/types.ts +15 -84
- package/openapi.yaml +135 -59
- package/package.json +2 -1
- package/scripts/sync-llm-catalog.ts +6 -15
- package/scripts/sync-web-search-catalog.ts +3 -11
- package/src/__tests__/actor-trust-resolver-address-fallback.test.ts +14 -26
- package/src/__tests__/agent-loop-compaction-strip.test.ts +240 -0
- package/src/__tests__/agent-loop-output-hooks.test.ts +69 -0
- package/src/__tests__/agent-loop-override-profile.test.ts +25 -0
- package/src/__tests__/always-loaded-tools-guard.test.ts +2 -3
- package/src/__tests__/app-dir-path-guard.test.ts +0 -1
- package/src/__tests__/assistant-feature-flag-guard.test.ts +1 -4
- package/src/__tests__/assistant-feature-flag-guardrails.test.ts +0 -2
- package/src/__tests__/avatar-identity-sync.test.ts +2 -27
- package/src/__tests__/btw-routes.test.ts +6 -8
- package/src/__tests__/checker.test.ts +0 -3
- package/src/__tests__/config-loader-backfill.test.ts +103 -6
- package/src/__tests__/config-watcher.test.ts +0 -18
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +22 -0
- package/src/__tests__/credential-broker.test.ts +449 -1
- package/src/__tests__/credential-execution-tools.test.ts +0 -1
- package/src/__tests__/credential-prompt-route.test.ts +3 -4
- package/src/__tests__/credential-routes.test.ts +360 -0
- package/src/__tests__/credential-security-invariants.test.ts +2 -13
- package/src/__tests__/dynamic-page-surface.test.ts +101 -1
- package/src/__tests__/fixtures/credential-security-fixtures.ts +2 -33
- package/src/__tests__/gateway-only-guard.test.ts +3 -7
- package/src/__tests__/identity-routes.test.ts +0 -189
- package/src/__tests__/inbound-invite-redemption.test.ts +4 -4
- package/src/__tests__/invite-redemption-service.test.ts +4 -4
- package/src/__tests__/llm-callsite-catalog.test.ts +5 -6
- package/src/__tests__/llm-catalog-parity.test.ts +0 -22
- package/src/__tests__/llm-resolver.test.ts +49 -24
- package/src/__tests__/oauth-provider-seed-logos.test.ts +4 -6
- package/src/__tests__/onboarding-persona-write.test.ts +1 -1
- package/src/__tests__/persona-resolver.test.ts +11 -14
- package/src/__tests__/plugin-api-model-profiles.test.ts +178 -0
- package/src/__tests__/registry.test.ts +2 -7
- package/src/__tests__/schedule-routes-workflow-validation.test.ts +1 -10
- package/src/__tests__/schedule-routes.test.ts +0 -30
- package/src/__tests__/schedule-tools.test.ts +2 -18
- package/src/__tests__/skill-execute-input.test.ts +46 -1
- package/src/__tests__/skill-runtime-path.test.ts +2 -3
- package/src/__tests__/subagent-tools.test.ts +116 -0
- package/src/__tests__/surface-completion-nudge-hook.test.ts +367 -0
- package/src/__tests__/token-estimator-accuracy.benchmark.test.ts +1 -29
- package/src/__tests__/token-manager.test.ts +519 -0
- package/src/__tests__/tool-executor.test.ts +0 -79
- package/src/__tests__/trusted-contact-multichannel.test.ts +3 -3
- package/src/__tests__/trusted-contact-verification.test.ts +6 -6
- package/src/__tests__/voice-invite-redemption.test.ts +2 -2
- package/src/__tests__/web-search-catalog-parity.test.ts +6 -25
- package/src/__tests__/workspace-greetings.test.ts +152 -0
- package/src/agent/loop.ts +25 -5
- package/src/api/README.md +6 -6
- package/src/api/responses/conversation-message.ts +2 -4
- package/src/api/responses/home.ts +0 -4
- package/src/approvals/guardian-request-resolvers.ts +2 -2
- package/src/calls/relay-access-wait.ts +1 -1
- package/src/calls/voice-session-bridge.ts +2 -2
- package/src/cli/commands/plugins.ts +143 -2
- package/src/cli/lib/__tests__/diff-plugin.test.ts +443 -0
- package/src/cli/lib/__tests__/merge-plugin-tree.test.ts +313 -0
- package/src/cli/lib/__tests__/upgrade-plugin.test.ts +253 -2
- package/src/cli/lib/diff-plugin.ts +346 -0
- package/src/cli/lib/install-from-github.ts +105 -17
- package/src/cli/lib/merge-plugin-tree.ts +228 -0
- package/src/cli/lib/plugin-fingerprint.ts +14 -0
- package/src/cli/lib/upgrade-plugin.ts +270 -10
- package/src/cli/program.ts +0 -2
- package/src/config/bundled-skills/subagent/SKILL.md +4 -0
- package/src/config/bundled-skills/subagent/TOOLS.json +4 -0
- package/src/config/bundled-skills/workflows/SKILL.md +0 -1
- package/src/config/bundled-tool-registry.ts +2 -7
- package/src/config/call-site-defaults.ts +12 -2
- package/src/config/feature-flag-registry.json +1 -17
- package/src/config/inference-profile-validation.ts +26 -0
- package/src/config/loader.ts +4 -0
- package/src/config/profile-order.ts +28 -0
- package/src/config/schemas/elevenlabs.ts +0 -1
- package/src/config/schemas/platform.ts +0 -8
- package/src/config/seed-inference-profiles.ts +25 -20
- package/src/contacts/contact-store.ts +87 -96
- package/src/contacts/contacts-write.ts +5 -21
- package/src/context/compactor.ts +2 -2
- package/src/credential-execution/process-manager.ts +55 -14
- package/src/credential-execution/prompted-credential.ts +2 -3
- package/src/daemon/config-watcher.ts +0 -4
- package/src/daemon/conversation-agent-loop.ts +15 -4
- package/src/daemon/conversation-slash.ts +2 -23
- package/src/daemon/conversation-tool-setup.ts +11 -3
- package/src/daemon/conversation.ts +2 -0
- package/src/daemon/handlers/config-channels.ts +20 -16
- package/src/daemon/handlers/config-slack-channel.ts +2 -3
- package/src/daemon/lifecycle.ts +0 -7
- package/src/daemon/message-types/conversations.ts +3 -3
- package/src/daemon/message-types/sync.ts +0 -1
- package/src/daemon/orphan-reaper.test.ts +0 -19
- package/src/daemon/orphan-reaper.ts +2 -24
- package/src/daemon/server.ts +0 -10
- package/src/home/relationship-state.ts +2 -4
- package/src/memory/__tests__/memory-retrospective-job.test.ts +195 -401
- package/src/memory/bookmark-crud.ts +1 -2
- package/src/memory/db-init.ts +7 -17
- package/src/memory/embedding-backend.ts +23 -0
- package/src/memory/embedding-billing-breaker.ts +96 -0
- package/src/memory/jobs-store.ts +25 -13
- package/src/memory/jobs-worker.ts +52 -0
- package/src/memory/memory-retrospective-constants.ts +4 -4
- package/src/memory/memory-retrospective-job.ts +19 -227
- package/src/memory/migrations/291-contact-channels-renormalize-addresses.ts +62 -0
- package/src/memory/migrations/__tests__/291-contact-channels-renormalize-addresses.test.ts +311 -0
- package/src/memory/migrations/__tests__/run-migrations.test.ts +52 -0
- package/src/memory/migrations/index.ts +1 -0
- package/src/memory/migrations/run-migrations.ts +41 -0
- package/src/memory/migrations/validate-migration-state.ts +1 -1
- package/src/memory/schema/contacts.ts +0 -4
- package/src/messaging/providers/slack/adapter.ts +1 -1
- package/src/notifications/adapters/shared.ts +29 -0
- package/src/notifications/adapters/slack.ts +5 -32
- package/src/notifications/adapters/telegram.ts +2 -20
- package/src/notifications/broadcaster.ts +10 -1
- package/src/notifications/home-feed-side-effect.ts +4 -3
- package/src/notifications/notification-utils.ts +17 -19
- package/src/notifications/types.ts +7 -0
- package/src/oauth/AGENTS.md +5 -24
- package/src/plugin-api/constants.ts +1 -1
- package/src/plugin-api/index.ts +6 -1
- package/src/plugin-api/model-profiles.ts +33 -0
- package/src/plugin-api/types.ts +50 -2
- package/src/plugins/defaults/index.ts +25 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/maintain-job.test.ts +54 -2
- package/src/plugins/defaults/memory-v3-shadow/maintain-job.ts +107 -7
- package/src/plugins/defaults/surface-completion-nudge/hooks/post-model-call.ts +276 -0
- package/src/plugins/defaults/surface-completion-nudge/hooks/stop.ts +22 -0
- package/src/plugins/defaults/surface-completion-nudge/nudge-state-store.ts +46 -0
- package/src/plugins/defaults/surface-completion-nudge/package.json +14 -0
- package/src/plugins/defaults/task-progress-nudge/hooks/post-tool-use.ts +1 -1
- package/src/prompts/persona-resolver.ts +2 -2
- package/src/runtime/AGENTS.md +0 -1
- package/src/runtime/actor-trust-resolver.ts +12 -44
- package/src/runtime/btw-sidechain.ts +3 -6
- package/src/runtime/channel-approval-types.ts +18 -45
- package/src/runtime/channel-invite-transports/telegram.ts +4 -4
- package/src/runtime/channel-verification-service.ts +4 -3
- package/src/runtime/invite-redemption-service.ts +3 -3
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +218 -1
- package/src/runtime/routes/app-routes.ts +1 -1
- package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +2 -2
- package/src/runtime/routes/assets/vellum-design-system.css +1959 -0
- package/src/runtime/routes/btw-routes.ts +1 -27
- package/src/runtime/routes/conversation-compaction-routes.ts +1 -1
- package/src/runtime/routes/conversation-routes.ts +2 -2
- package/src/runtime/routes/credential-routes.ts +40 -16
- package/src/runtime/routes/empty-state-greeting-cache.ts +1 -2
- package/src/runtime/routes/identity-routes.ts +1 -296
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +1 -1
- package/src/runtime/routes/plugins-routes.ts +171 -5
- package/src/runtime/routes/schedule-routes.ts +0 -22
- package/src/runtime/routes/workflow-routes.test.ts +4 -43
- package/src/runtime/routes/workflow-routes.ts +0 -28
- package/src/runtime/routes/workspace-greetings.ts +55 -0
- package/src/runtime/sync/resource-sync-events.ts +1 -11
- package/src/schedule/inference-profile.ts +2 -14
- package/src/subagent/manager.ts +6 -0
- package/src/subagent/types.ts +6 -0
- package/src/tools/AGENTS.md +3 -3
- package/src/tools/browser/browser-execution.ts +1 -1
- package/src/tools/network/web-search-error.ts +1 -1
- package/src/tools/permission-checker.ts +1 -1
- package/src/tools/schedule/create.ts +3 -9
- package/src/tools/schedule/update.ts +2 -10
- package/src/tools/side-effects.ts +2 -17
- package/src/tools/skills/execute.ts +34 -0
- package/src/tools/subagent/spawn.ts +34 -9
- package/src/tools/tool-approval-handler.ts +1 -3
- package/src/tools/tool-manifest.ts +0 -2
- package/src/tools/ui-surface/definitions.ts +39 -1
- package/src/tools/workflows/run-workflow.test.ts +8 -18
- package/src/util/platform.ts +2 -2
- package/src/workflows/capabilities.ts +2 -3
- package/src/workflows/run-manager.test.ts +0 -25
- package/src/workflows/run-manager.ts +2 -24
- package/src/__tests__/app-control-no-global-cgevent.test.ts +0 -98
- package/src/__tests__/credential-security-e2e.test.ts +0 -362
- package/src/__tests__/credential-vault-unit.test.ts +0 -1528
- package/src/__tests__/credential-vault.test.ts +0 -1706
- package/src/__tests__/identity-intro-cache.test.ts +0 -315
- package/src/__tests__/secret-onetime-send.test.ts +0 -182
- package/src/cli/commands/__tests__/task.test.ts +0 -914
- package/src/cli/commands/task.ts +0 -771
- package/src/config/bundled-skills/personal-page/SKILL.md +0 -57
- package/src/config/bundled-skills/personal-page/TOOLS.json +0 -27
- package/src/config/bundled-skills/personal-page/tools/app-refresh.ts +0 -17
- package/src/config/preloaded-apps/personal-page/src/components/About.tsx +0 -22
- package/src/config/preloaded-apps/personal-page/src/components/App.tsx +0 -16
- package/src/config/preloaded-apps/personal-page/src/components/Features.tsx +0 -77
- package/src/config/preloaded-apps/personal-page/src/components/Hero.tsx +0 -57
- package/src/config/preloaded-apps/personal-page/src/components/Pending.tsx +0 -28
- package/src/config/preloaded-apps/personal-page/src/components/animations.tsx +0 -234
- package/src/config/preloaded-apps/personal-page/src/components/icons.tsx +0 -48
- package/src/config/preloaded-apps/personal-page/src/components/media.ts +0 -16
- package/src/config/preloaded-apps/personal-page/src/index.html +0 -20
- package/src/config/preloaded-apps/personal-page/src/main.tsx +0 -7
- package/src/config/preloaded-apps/personal-page/src/profile-data.ts +0 -82
- package/src/config/preloaded-apps/personal-page/src/styles.css +0 -759
- package/src/memory/__tests__/preloaded-apps.test.ts +0 -85
- package/src/memory/preloaded-apps.ts +0 -116
- package/src/runtime/routes/identity-intro-cache.ts +0 -172
- package/src/tools/credentials/vault.ts +0 -712
|
@@ -6,8 +6,6 @@
|
|
|
6
6
|
* identity and access-control state.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import type { ChannelId } from "../channels/types.js";
|
|
10
|
-
import { canonicalizeInboundIdentity } from "../util/canonicalize-identity.js";
|
|
11
9
|
import { emitContactChange } from "./contact-events.js";
|
|
12
10
|
import {
|
|
13
11
|
findContactChannel,
|
|
@@ -25,7 +23,6 @@ import type {
|
|
|
25
23
|
ContactWriteResult,
|
|
26
24
|
} from "./types.js";
|
|
27
25
|
|
|
28
|
-
|
|
29
26
|
// ── Guardian operations ──────────────────────────────────────────────
|
|
30
27
|
|
|
31
28
|
/**
|
|
@@ -68,12 +65,7 @@ export function upsertContactChannel(params: {
|
|
|
68
65
|
let address: string;
|
|
69
66
|
|
|
70
67
|
if (params.externalUserId) {
|
|
71
|
-
|
|
72
|
-
canonicalizeInboundIdentity(
|
|
73
|
-
params.sourceChannel as ChannelId,
|
|
74
|
-
params.externalUserId,
|
|
75
|
-
) ?? params.externalUserId;
|
|
76
|
-
address = canonical;
|
|
68
|
+
address = params.externalUserId;
|
|
77
69
|
} else if (params.externalChatId) {
|
|
78
70
|
address = params.externalChatId;
|
|
79
71
|
} else {
|
|
@@ -84,8 +76,8 @@ export function upsertContactChannel(params: {
|
|
|
84
76
|
let displayName = params.displayName ?? params.externalUserId ?? "Unknown";
|
|
85
77
|
|
|
86
78
|
// When binding a channel to a specific contact (invite redemption), preserve
|
|
87
|
-
// the target contact's curated displayName
|
|
88
|
-
//
|
|
79
|
+
// the target contact's curated displayName instead of overwriting it
|
|
80
|
+
// with the raw platform identity.
|
|
89
81
|
if (params.contactId) {
|
|
90
82
|
const targetContact = getContact(params.contactId);
|
|
91
83
|
if (targetContact?.displayName?.trim().length) {
|
|
@@ -93,13 +85,6 @@ export function upsertContactChannel(params: {
|
|
|
93
85
|
}
|
|
94
86
|
}
|
|
95
87
|
|
|
96
|
-
const canonicalId = params.externalUserId
|
|
97
|
-
? (canonicalizeInboundIdentity(
|
|
98
|
-
params.sourceChannel as ChannelId,
|
|
99
|
-
params.externalUserId,
|
|
100
|
-
) ?? params.externalUserId)
|
|
101
|
-
: null;
|
|
102
|
-
|
|
103
88
|
upsertContact({
|
|
104
89
|
id: params.contactId,
|
|
105
90
|
displayName,
|
|
@@ -108,7 +93,7 @@ export function upsertContactChannel(params: {
|
|
|
108
93
|
{
|
|
109
94
|
type: params.sourceChannel,
|
|
110
95
|
address,
|
|
111
|
-
externalUserId:
|
|
96
|
+
externalUserId: params.externalUserId ? address : null,
|
|
112
97
|
externalChatId: params.externalChatId ?? null,
|
|
113
98
|
status: (params.status as ChannelStatus) ?? undefined,
|
|
114
99
|
policy: (params.policy as ChannelPolicy) ?? undefined,
|
|
@@ -133,7 +118,7 @@ export function upsertContactChannel(params: {
|
|
|
133
118
|
|
|
134
119
|
const contactResult = findContactChannel({
|
|
135
120
|
channelType: params.sourceChannel,
|
|
136
|
-
|
|
121
|
+
address,
|
|
137
122
|
externalChatId: params.externalChatId,
|
|
138
123
|
});
|
|
139
124
|
|
|
@@ -174,4 +159,3 @@ export function revokeMember(
|
|
|
174
159
|
emitContactChange();
|
|
175
160
|
return { contact, channel: updatedChannel };
|
|
176
161
|
}
|
|
177
|
-
|
package/src/context/compactor.ts
CHANGED
|
@@ -1132,8 +1132,8 @@ export async function runAssistantDrivenCompaction(
|
|
|
1132
1132
|
// from the moment of capture — keeping them would (a) waste tokens on
|
|
1133
1133
|
// outdated content, (b) duplicate against the freshly re-injected blocks
|
|
1134
1134
|
// the next turn produces, and (c) leak `<system_reminder>` text the model
|
|
1135
|
-
// is not supposed to see in history. `<turn_context>`
|
|
1136
|
-
//
|
|
1135
|
+
// is not supposed to see in history. `<turn_context>` is intentionally
|
|
1136
|
+
// preserved by `RUNTIME_INJECTION_PREFIXES`.
|
|
1137
1137
|
const tailMessages = stripInjectionsForCompaction(
|
|
1138
1138
|
args.messages.slice(tailIndex),
|
|
1139
1139
|
);
|
|
@@ -312,6 +312,43 @@ function buildLocalCesEnv(): Record<string, string | undefined> {
|
|
|
312
312
|
};
|
|
313
313
|
}
|
|
314
314
|
|
|
315
|
+
// ---------------------------------------------------------------------------
|
|
316
|
+
// Close notification (shared by both transports)
|
|
317
|
+
// ---------------------------------------------------------------------------
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Tracks the transport `alive` state and notifies registered handlers exactly
|
|
321
|
+
* once when the transport dies, so the RPC client can fail-fast any in-flight
|
|
322
|
+
* calls instead of waiting out their timeouts.
|
|
323
|
+
*/
|
|
324
|
+
function createCloseNotifier(): {
|
|
325
|
+
isAlive: () => boolean;
|
|
326
|
+
markDead: () => void;
|
|
327
|
+
onClose: (handler: () => void) => void;
|
|
328
|
+
} {
|
|
329
|
+
let alive = true;
|
|
330
|
+
let notified = false;
|
|
331
|
+
const handlers: Array<() => void> = [];
|
|
332
|
+
return {
|
|
333
|
+
isAlive: () => alive,
|
|
334
|
+
markDead() {
|
|
335
|
+
alive = false;
|
|
336
|
+
if (notified) return;
|
|
337
|
+
notified = true;
|
|
338
|
+
for (const handler of handlers) {
|
|
339
|
+
try {
|
|
340
|
+
handler();
|
|
341
|
+
} catch {
|
|
342
|
+
// a close handler must never throw back into the transport
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
},
|
|
346
|
+
onClose(handler) {
|
|
347
|
+
handlers.push(handler);
|
|
348
|
+
},
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
|
|
315
352
|
// ---------------------------------------------------------------------------
|
|
316
353
|
// Stdio transport (local mode)
|
|
317
354
|
// ---------------------------------------------------------------------------
|
|
@@ -319,7 +356,7 @@ function buildLocalCesEnv(): Record<string, string | undefined> {
|
|
|
319
356
|
function createStdioTransport(proc: Subprocess): CesTransport {
|
|
320
357
|
const messageHandlers: Array<(message: string) => void> = [];
|
|
321
358
|
let buffer = "";
|
|
322
|
-
|
|
359
|
+
const death = createCloseNotifier();
|
|
323
360
|
|
|
324
361
|
// Read stdout line by line — narrow past `number` union arm from Subprocess type
|
|
325
362
|
if (proc.stdout && typeof proc.stdout !== "number") {
|
|
@@ -350,9 +387,9 @@ function createStdioTransport(proc: Subprocess): CesTransport {
|
|
|
350
387
|
endReason = "error";
|
|
351
388
|
readError = err;
|
|
352
389
|
} finally {
|
|
353
|
-
//
|
|
354
|
-
// the
|
|
355
|
-
|
|
390
|
+
// Report dead the moment the stdout stream ends (regardless of why),
|
|
391
|
+
// and notify the client so it can fail-fast any in-flight calls.
|
|
392
|
+
death.markDead();
|
|
356
393
|
|
|
357
394
|
// DIAGNOSTIC (observation only). The reconnection path (secure-keys.ts)
|
|
358
395
|
// bounces CES whenever isAlive() goes false. This classifies WHY the
|
|
@@ -384,7 +421,7 @@ function createStdioTransport(proc: Subprocess): CesTransport {
|
|
|
384
421
|
|
|
385
422
|
// Track process exit
|
|
386
423
|
void proc.exited.then((exitCode) => {
|
|
387
|
-
|
|
424
|
+
death.markDead();
|
|
388
425
|
log.info(
|
|
389
426
|
{ pid: proc.pid, exitCode },
|
|
390
427
|
"CES stdio transport: process exited",
|
|
@@ -393,7 +430,7 @@ function createStdioTransport(proc: Subprocess): CesTransport {
|
|
|
393
430
|
|
|
394
431
|
return {
|
|
395
432
|
write(line: string): void {
|
|
396
|
-
if (!
|
|
433
|
+
if (!death.isAlive() || !proc.stdin || typeof proc.stdin === "number") {
|
|
397
434
|
throw new Error("CES stdio transport is not alive");
|
|
398
435
|
}
|
|
399
436
|
proc.stdin.write(line + "\n");
|
|
@@ -404,11 +441,13 @@ function createStdioTransport(proc: Subprocess): CesTransport {
|
|
|
404
441
|
},
|
|
405
442
|
|
|
406
443
|
isAlive(): boolean {
|
|
407
|
-
return
|
|
444
|
+
return death.isAlive();
|
|
408
445
|
},
|
|
409
446
|
|
|
447
|
+
onClose: death.onClose,
|
|
448
|
+
|
|
410
449
|
close(): void {
|
|
411
|
-
|
|
450
|
+
death.markDead();
|
|
412
451
|
if (proc.stdin && typeof proc.stdin !== "number") {
|
|
413
452
|
proc.stdin.end();
|
|
414
453
|
}
|
|
@@ -423,7 +462,7 @@ function createStdioTransport(proc: Subprocess): CesTransport {
|
|
|
423
462
|
function createSocketTransport(socket: Socket): CesTransport {
|
|
424
463
|
const messageHandlers: Array<(message: string) => void> = [];
|
|
425
464
|
let buffer = "";
|
|
426
|
-
|
|
465
|
+
const death = createCloseNotifier();
|
|
427
466
|
|
|
428
467
|
const decoder = new StringDecoder("utf8");
|
|
429
468
|
|
|
@@ -442,17 +481,17 @@ function createSocketTransport(socket: Socket): CesTransport {
|
|
|
442
481
|
});
|
|
443
482
|
|
|
444
483
|
socket.on("close", () => {
|
|
445
|
-
|
|
484
|
+
death.markDead();
|
|
446
485
|
});
|
|
447
486
|
|
|
448
487
|
socket.on("error", (err) => {
|
|
449
488
|
log.warn({ err }, "CES socket transport error");
|
|
450
|
-
|
|
489
|
+
death.markDead();
|
|
451
490
|
});
|
|
452
491
|
|
|
453
492
|
return {
|
|
454
493
|
write(line: string): void {
|
|
455
|
-
if (!
|
|
494
|
+
if (!death.isAlive() || socket.destroyed) {
|
|
456
495
|
throw new Error("CES socket transport is not alive");
|
|
457
496
|
}
|
|
458
497
|
socket.write(line + "\n");
|
|
@@ -463,11 +502,13 @@ function createSocketTransport(socket: Socket): CesTransport {
|
|
|
463
502
|
},
|
|
464
503
|
|
|
465
504
|
isAlive(): boolean {
|
|
466
|
-
return
|
|
505
|
+
return death.isAlive() && !socket.destroyed;
|
|
467
506
|
},
|
|
468
507
|
|
|
508
|
+
onClose: death.onClose,
|
|
509
|
+
|
|
469
510
|
close(): void {
|
|
470
|
-
|
|
511
|
+
death.markDead();
|
|
471
512
|
socket.destroy();
|
|
472
513
|
},
|
|
473
514
|
};
|
|
@@ -8,9 +8,8 @@
|
|
|
8
8
|
* channel actually connects, not just stored;
|
|
9
9
|
* - any other credential: written to secure storage.
|
|
10
10
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* identically.
|
|
11
|
+
* The CLI `credentials prompt` route uses this logic to persist a credential
|
|
12
|
+
* collected through a secure prompt.
|
|
14
13
|
*/
|
|
15
14
|
|
|
16
15
|
import { getConfig } from "../config/loader.js";
|
|
@@ -178,8 +178,6 @@ export class ConfigWatcher {
|
|
|
178
178
|
* Start all file watchers. `onConversationEvict` is called when watched
|
|
179
179
|
* files change and conversations need to be evicted for reload.
|
|
180
180
|
* `onIdentityChanged` is called when IDENTITY.md changes on disk.
|
|
181
|
-
* `onIdentityIntroChanged` is called when SOUL.md changes and identity
|
|
182
|
-
* intro subscribers should refetch.
|
|
183
181
|
* `onSkillsChanged` is called after skill directory changes evict
|
|
184
182
|
* conversations.
|
|
185
183
|
*/
|
|
@@ -190,7 +188,6 @@ export class ConfigWatcher {
|
|
|
190
188
|
onAvatarChanged?: () => void,
|
|
191
189
|
onConfigChanged?: () => void,
|
|
192
190
|
onSkillsChanged?: () => void,
|
|
193
|
-
onIdentityIntroChanged?: () => void,
|
|
194
191
|
): void {
|
|
195
192
|
// Reset the stopped flag so a stop()→start() cycle on the same
|
|
196
193
|
// instance resumes hot-reload instead of silently bailing in every
|
|
@@ -226,7 +223,6 @@ export class ConfigWatcher {
|
|
|
226
223
|
},
|
|
227
224
|
"SOUL.md": () => {
|
|
228
225
|
onConversationEvict();
|
|
229
|
-
onIdentityIntroChanged?.();
|
|
230
226
|
},
|
|
231
227
|
"IDENTITY.md": () => {
|
|
232
228
|
onConversationEvict();
|
|
@@ -215,6 +215,11 @@ export async function runAgentLoopImpl(
|
|
|
215
215
|
* spawns).
|
|
216
216
|
*/
|
|
217
217
|
overrideProfile?: string;
|
|
218
|
+
/**
|
|
219
|
+
* Float `overrideProfile` above call-site layers for non-main-agent call
|
|
220
|
+
* sites. Used when a caller explicitly pins a background run to a profile.
|
|
221
|
+
*/
|
|
222
|
+
forceOverrideProfile?: boolean;
|
|
218
223
|
},
|
|
219
224
|
): Promise<void> {
|
|
220
225
|
if (!ctx.abortController) {
|
|
@@ -290,6 +295,7 @@ export async function runAgentLoopImpl(
|
|
|
290
295
|
ctx.toolRoutedProfile = undefined;
|
|
291
296
|
|
|
292
297
|
const turnOverrideProfile = userExplicitOverride;
|
|
298
|
+
const forceOverrideProfile = options?.forceOverrideProfile === true;
|
|
293
299
|
|
|
294
300
|
const readCurrentOverrideProfile = (): string | undefined =>
|
|
295
301
|
options?.overrideProfile ??
|
|
@@ -300,6 +306,7 @@ export async function runAgentLoopImpl(
|
|
|
300
306
|
llm: config.llm,
|
|
301
307
|
callSite: turnCallSite,
|
|
302
308
|
overrideProfile: turnOverrideProfile ?? undefined,
|
|
309
|
+
forceOverrideProfile,
|
|
303
310
|
selectionSeed: ctx.conversationId,
|
|
304
311
|
});
|
|
305
312
|
let currentEffectiveContextWindow: EffectiveContextWindow =
|
|
@@ -307,6 +314,7 @@ export async function runAgentLoopImpl(
|
|
|
307
314
|
let currentContextWindowConfig = contextWindowConfigFromEffective(
|
|
308
315
|
resolveCallSiteConfig(turnCallSite, config.llm, {
|
|
309
316
|
overrideProfile: turnOverrideProfile ?? undefined,
|
|
317
|
+
forceOverrideProfile,
|
|
310
318
|
selectionSeed: ctx.conversationId,
|
|
311
319
|
}).contextWindow,
|
|
312
320
|
currentEffectiveContextWindow,
|
|
@@ -322,11 +330,13 @@ export async function runAgentLoopImpl(
|
|
|
322
330
|
llm: config.llm,
|
|
323
331
|
callSite: turnCallSite,
|
|
324
332
|
overrideProfile: currentOverrideProfile,
|
|
333
|
+
forceOverrideProfile,
|
|
325
334
|
selectionSeed: ctx.conversationId,
|
|
326
335
|
});
|
|
327
336
|
currentContextWindowConfig = contextWindowConfigFromEffective(
|
|
328
337
|
resolveCallSiteConfig(turnCallSite, config.llm, {
|
|
329
338
|
overrideProfile: currentOverrideProfile,
|
|
339
|
+
forceOverrideProfile,
|
|
330
340
|
selectionSeed: ctx.conversationId,
|
|
331
341
|
}).contextWindow,
|
|
332
342
|
currentEffectiveContextWindow,
|
|
@@ -901,6 +911,7 @@ export async function runAgentLoopImpl(
|
|
|
901
911
|
callSite: turnCallSite,
|
|
902
912
|
trust: loopTrust,
|
|
903
913
|
overrideProfile: turnOverrideProfile,
|
|
914
|
+
...(forceOverrideProfile ? { forceOverrideProfile: true } : {}),
|
|
904
915
|
resolveOverrideProfile: resolveCurrentOverrideProfile,
|
|
905
916
|
resolveContextWindow,
|
|
906
917
|
compactInPlace,
|
|
@@ -1657,10 +1668,10 @@ function collapseRawResponses(rawResponses?: unknown[]): unknown | undefined {
|
|
|
1657
1668
|
|
|
1658
1669
|
/**
|
|
1659
1670
|
* Matches any runtime-injection tag that should never appear inside a
|
|
1660
|
-
* generated summary.
|
|
1661
|
-
*
|
|
1662
|
-
*
|
|
1663
|
-
*
|
|
1671
|
+
* generated summary. A hit means the summary echoed an injection tag —
|
|
1672
|
+
* either parroted from history the summarizer read or invented outright.
|
|
1673
|
+
* The durable summary should be clean prose, so the match is surfaced via
|
|
1674
|
+
* telemetry.
|
|
1664
1675
|
*/
|
|
1665
1676
|
const SUMMARY_MEMORY_ECHO_PATTERN =
|
|
1666
1677
|
/<(?:memory|memory_context|memory_image|turn_context|workspace|workspace_top_level|knowledge_base|pkb|system_reminder|now_scratchpad|NOW\.md|active_thread|active_subagents|active_workspace|active_dynamic_page|channel_capabilities|transport_hints|system_notice|non_interactive_context|temporal_context|guardian_context|inbound_actor_context|channel_turn_context|interface_turn_context|channel_command_context|voice_call_control)\b/i;
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
loadRawConfig,
|
|
8
8
|
saveRawConfig,
|
|
9
9
|
} from "../config/loader.js";
|
|
10
|
+
import { orderProfileKeys } from "../config/profile-order.js";
|
|
10
11
|
import { getConversationOverrideProfile } from "../memory/conversation-crud.js";
|
|
11
12
|
import { getConfiguredProviders } from "../providers/provider-availability.js";
|
|
12
13
|
import { getVisibleProviderCatalog } from "../providers/provider-catalog-visibility.js";
|
|
@@ -130,28 +131,6 @@ function parseModelCommand(trimmed: string): ModelCommandParse | null {
|
|
|
130
131
|
return { kind: "switch", profileName: rest };
|
|
131
132
|
}
|
|
132
133
|
|
|
133
|
-
function orderedProfileNames(
|
|
134
|
-
profiles: Record<
|
|
135
|
-
string,
|
|
136
|
-
{ label?: string; description?: string; status?: "active" | "disabled" }
|
|
137
|
-
>,
|
|
138
|
-
profileOrder: readonly string[] | undefined,
|
|
139
|
-
): string[] {
|
|
140
|
-
const order = profileOrder ?? [];
|
|
141
|
-
const seen = new Set<string>();
|
|
142
|
-
const ordered: string[] = [];
|
|
143
|
-
for (const name of order) {
|
|
144
|
-
if (profiles[name] != null && !seen.has(name)) {
|
|
145
|
-
ordered.push(name);
|
|
146
|
-
seen.add(name);
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
const tail = Object.keys(profiles)
|
|
150
|
-
.filter((n) => !seen.has(n))
|
|
151
|
-
.sort();
|
|
152
|
-
return [...ordered, ...tail];
|
|
153
|
-
}
|
|
154
|
-
|
|
155
134
|
async function resolveModelCommand(
|
|
156
135
|
parse: ModelCommandParse,
|
|
157
136
|
): Promise<SlashResolution> {
|
|
@@ -160,7 +139,7 @@ async function resolveModelCommand(
|
|
|
160
139
|
string,
|
|
161
140
|
{ label?: string; description?: string; status?: "active" | "disabled" }
|
|
162
141
|
>;
|
|
163
|
-
const profileNames =
|
|
142
|
+
const profileNames = orderProfileKeys(profiles, config.llm.profileOrder);
|
|
164
143
|
const activeProfile = config.llm.activeProfile;
|
|
165
144
|
|
|
166
145
|
if (parse.kind === "list") {
|
|
@@ -26,7 +26,10 @@ import {
|
|
|
26
26
|
ACTIVITY_SKIP_SET,
|
|
27
27
|
injectActivityField,
|
|
28
28
|
} from "../tools/schema-transforms.js";
|
|
29
|
-
import {
|
|
29
|
+
import {
|
|
30
|
+
augmentSkillExecuteError,
|
|
31
|
+
resolveSkillExecuteInput,
|
|
32
|
+
} from "../tools/skills/execute.js";
|
|
30
33
|
import { resolveToolInvocationAlias } from "../tools/tool-name-aliases.js";
|
|
31
34
|
import {
|
|
32
35
|
isDiskPressureCleanupToolName,
|
|
@@ -320,7 +323,12 @@ export function createToolExecutor(
|
|
|
320
323
|
const innerRejection = rejectNonAllowlistedTool(toolName);
|
|
321
324
|
if (innerRejection) return innerRejection;
|
|
322
325
|
|
|
323
|
-
const
|
|
326
|
+
const rawResult = await executor.execute(
|
|
327
|
+
toolName,
|
|
328
|
+
toolInput,
|
|
329
|
+
toolContext,
|
|
330
|
+
);
|
|
331
|
+
const result = augmentSkillExecuteError(toolName, toolInput, rawResult);
|
|
324
332
|
if (toolContext.approvedViaPrompt) {
|
|
325
333
|
ctx.approvedViaPromptThisTurn = true;
|
|
326
334
|
}
|
|
@@ -371,7 +379,7 @@ export function createProxyApprovalCallback(
|
|
|
371
379
|
* history or explicit preactivation. Without this, their tools are
|
|
372
380
|
* unavailable in fresh conversations until `skill_load` is called.
|
|
373
381
|
*/
|
|
374
|
-
const DEFAULT_PREACTIVATED_SKILL_IDS = ["
|
|
382
|
+
const DEFAULT_PREACTIVATED_SKILL_IDS = ["notifications", "subagent"];
|
|
375
383
|
|
|
376
384
|
/**
|
|
377
385
|
* Subset of Conversation state that the resolveTools callback reads at each
|
|
@@ -1847,6 +1847,8 @@ export class Conversation {
|
|
|
1847
1847
|
* this value via {@link SubagentManager.spawn}.
|
|
1848
1848
|
*/
|
|
1849
1849
|
overrideProfile?: string;
|
|
1850
|
+
/** Float `overrideProfile` above call-site layers for this run. */
|
|
1851
|
+
forceOverrideProfile?: boolean;
|
|
1850
1852
|
},
|
|
1851
1853
|
): Promise<void> {
|
|
1852
1854
|
const { onEvent, ...rest } = options ?? {};
|
|
@@ -204,7 +204,7 @@ export function revokeVerificationForChannel(
|
|
|
204
204
|
// revocation becomes a no-op (wrong reason or skipped entirely).
|
|
205
205
|
const contactResult = findContactChannel({
|
|
206
206
|
channelType: resolvedChannel,
|
|
207
|
-
|
|
207
|
+
address: bindingBeforeRevoke.guardianExternalUserId,
|
|
208
208
|
externalChatId: bindingBeforeRevoke.guardianDeliveryChatId,
|
|
209
209
|
});
|
|
210
210
|
|
|
@@ -331,7 +331,12 @@ export async function verifyTrustedContact(
|
|
|
331
331
|
const sessionResult = createOutboundSession({
|
|
332
332
|
channel: verificationChannel,
|
|
333
333
|
expectedChatId: channel.externalChatId,
|
|
334
|
-
expectedExternalUserId:
|
|
334
|
+
expectedExternalUserId:
|
|
335
|
+
channel.externalUserId ??
|
|
336
|
+
(channel.address !== channel.externalChatId
|
|
337
|
+
? channel.address
|
|
338
|
+
: undefined) ??
|
|
339
|
+
undefined,
|
|
335
340
|
identityBindingStatus: "bound",
|
|
336
341
|
destinationAddress: effectiveDestination,
|
|
337
342
|
verificationPurpose: "trusted_contact",
|
|
@@ -404,22 +409,17 @@ export async function verifyTrustedContact(
|
|
|
404
409
|
|
|
405
410
|
// --- Slack verification ---
|
|
406
411
|
if (verificationChannel === "slack") {
|
|
407
|
-
const slackUserId =
|
|
408
|
-
|
|
409
|
-
const hasIdentityBinding = Boolean(
|
|
410
|
-
channel.externalUserId || channel.externalChatId,
|
|
411
|
-
);
|
|
412
|
-
if (!hasIdentityBinding) {
|
|
413
|
-
return {
|
|
414
|
-
success: false,
|
|
415
|
-
error:
|
|
416
|
-
"Slack verification requires an externalUserId or externalChatId for identity binding",
|
|
417
|
-
};
|
|
418
|
-
}
|
|
412
|
+
const slackUserId =
|
|
413
|
+
channel.externalUserId ?? channel.address ?? destination;
|
|
419
414
|
|
|
420
415
|
const sessionResult = createOutboundSession({
|
|
421
416
|
channel: verificationChannel,
|
|
422
|
-
expectedExternalUserId:
|
|
417
|
+
expectedExternalUserId:
|
|
418
|
+
channel.externalUserId ??
|
|
419
|
+
(channel.address !== channel.externalChatId
|
|
420
|
+
? channel.address
|
|
421
|
+
: undefined) ??
|
|
422
|
+
undefined,
|
|
423
423
|
expectedChatId: channel.externalChatId ?? undefined,
|
|
424
424
|
identityBindingStatus: "bound",
|
|
425
425
|
destinationAddress: slackUserId,
|
|
@@ -560,7 +560,11 @@ export async function handleChannelVerificationSession(
|
|
|
560
560
|
const { to, text, subject, assistantId: aid } = result._pendingEmail;
|
|
561
561
|
deliverVerificationEmail(to, text, subject, aid);
|
|
562
562
|
}
|
|
563
|
-
const {
|
|
563
|
+
const {
|
|
564
|
+
_pendingSlackDm: _,
|
|
565
|
+
_pendingEmail: __,
|
|
566
|
+
...publicResult
|
|
567
|
+
} = result;
|
|
564
568
|
broadcastMessage({
|
|
565
569
|
type: "channel_verification_session_response",
|
|
566
570
|
...publicResult,
|
|
@@ -440,9 +440,8 @@ export async function setSlackChannelConfig(
|
|
|
440
440
|
* the bot_token, app_token, oauth_connection row, and Slack config metadata
|
|
441
441
|
* untouched so the Socket Mode connection stays up. Returns a
|
|
442
442
|
* `SlackChannelConfigResult` reflecting the remaining state. A `not-found`
|
|
443
|
-
* delete outcome is reported as a failure to match the
|
|
444
|
-
*
|
|
445
|
-
* detection).
|
|
443
|
+
* delete outcome is reported as a failure to match the credential delete
|
|
444
|
+
* semantics (callers and automation rely on missing-credential detection).
|
|
446
445
|
*/
|
|
447
446
|
export async function clearSlackUserToken(): Promise<SlackChannelConfigResult> {
|
|
448
447
|
const result = await deleteSecureKeyAsync(
|
package/src/daemon/lifecycle.ts
CHANGED
|
@@ -1281,13 +1281,6 @@ export async function runDaemon(): Promise<void> {
|
|
|
1281
1281
|
log.warn({ err }, "Assistant symlink installation failed — continuing");
|
|
1282
1282
|
}
|
|
1283
1283
|
|
|
1284
|
-
// Seed preloaded apps (personal landing page) in background (non-blocking).
|
|
1285
|
-
void import("../memory/preloaded-apps.js")
|
|
1286
|
-
.then(({ seedPreloadedApps }) => seedPreloadedApps(config))
|
|
1287
|
-
.catch((err) =>
|
|
1288
|
-
log.warn({ err }, "Preloaded app seeding failed — continuing"),
|
|
1289
|
-
);
|
|
1290
|
-
|
|
1291
1284
|
// Download embedding runtime in background (non-blocking).
|
|
1292
1285
|
// If download fails, local embeddings gracefully fall back to cloud backends.
|
|
1293
1286
|
void (async () => {
|
|
@@ -462,9 +462,9 @@ export interface ContextCompacted {
|
|
|
462
462
|
* - `summaryCharCount`: length of the produced summary text.
|
|
463
463
|
* - `summaryHeaderCount`: number of `## ` section headers in the summary.
|
|
464
464
|
* - `summaryHadMemoryEcho`: `true` if the summary contains any runtime
|
|
465
|
-
* injection tag (e.g. `<memory`, `<turn_context>`, `<workspace>`).
|
|
466
|
-
*
|
|
467
|
-
*
|
|
465
|
+
* injection tag (e.g. `<memory`, `<turn_context>`, `<workspace>`). The
|
|
466
|
+
* durable summary should be clean prose, so `true` flags an echoed or
|
|
467
|
+
* invented tag worth investigating.
|
|
468
468
|
*/
|
|
469
469
|
summaryCharCount?: number;
|
|
470
470
|
summaryHeaderCount?: number;
|
|
@@ -10,7 +10,6 @@ export { type SyncChangedEvent, SyncChangedEventSchema };
|
|
|
10
10
|
export const SYNC_TAGS = {
|
|
11
11
|
assistantAvatar: "assistant:self:avatar",
|
|
12
12
|
assistantIdentity: "assistant:self:identity",
|
|
13
|
-
assistantIdentityIntro: "assistant:self:identity-intro",
|
|
14
13
|
assistantConfig: "assistant:self:config",
|
|
15
14
|
assistantSounds: "assistant:self:sounds",
|
|
16
15
|
assistantSchedules: "assistant:self:schedules",
|
|
@@ -21,7 +21,6 @@ mock.module("../util/logger.js", () => ({
|
|
|
21
21
|
}));
|
|
22
22
|
|
|
23
23
|
const { parseProcStat, selectReapable } = await import("./orphan-reaper.js");
|
|
24
|
-
const { DaemonConfigSchema } = await import("../config/schemas/platform.js");
|
|
25
24
|
|
|
26
25
|
describe("parseProcStat", () => {
|
|
27
26
|
test("parses a normal stat line", () => {
|
|
@@ -89,24 +88,6 @@ describe("selectReapable", () => {
|
|
|
89
88
|
});
|
|
90
89
|
});
|
|
91
90
|
|
|
92
|
-
describe("daemon.reapOrphanedSubprocesses gate", () => {
|
|
93
|
-
test("defaults to off so the reaper is opt-in", () => {
|
|
94
|
-
// GIVEN a daemon config with the reaper flag unspecified
|
|
95
|
-
// WHEN it is parsed with schema defaults
|
|
96
|
-
const parsed = DaemonConfigSchema.parse({});
|
|
97
|
-
// THEN the reaper is disabled unless explicitly turned on
|
|
98
|
-
expect(parsed.reapOrphanedSubprocesses).toBe(false);
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
test("honors an explicit opt-in", () => {
|
|
102
|
-
// GIVEN a daemon config that explicitly enables the reaper
|
|
103
|
-
// WHEN it is parsed
|
|
104
|
-
const parsed = DaemonConfigSchema.parse({ reapOrphanedSubprocesses: true });
|
|
105
|
-
// THEN the flag is respected
|
|
106
|
-
expect(parsed.reapOrphanedSubprocesses).toBe(true);
|
|
107
|
-
});
|
|
108
|
-
});
|
|
109
|
-
|
|
110
91
|
// ── Integration: real reparented orphan on Linux ────────────────────────────
|
|
111
92
|
const itLinux = process.platform === "linux" ? test : test.skip;
|
|
112
93
|
|
|
@@ -26,9 +26,7 @@
|
|
|
26
26
|
* macOS dev, or if an init such as tini is ever placed above the daemon),
|
|
27
27
|
* orphans reparent to that init and are reaped there, so there is nothing for
|
|
28
28
|
* this to do. Because the daemon is PID 1, orphans already reparent to it and
|
|
29
|
-
* `PR_SET_CHILD_SUBREAPER` is unnecessary.
|
|
30
|
-
* `daemon.reapOrphanedSubprocesses` config flag (default off) so the behavior
|
|
31
|
-
* can be enabled per workspace for validation before becoming the default.
|
|
29
|
+
* `PR_SET_CHILD_SUBREAPER` is unnecessary.
|
|
32
30
|
*
|
|
33
31
|
* References:
|
|
34
32
|
* - libuv reaps its own children by pid on SIGCHLD (`uv__wait_children`):
|
|
@@ -42,7 +40,6 @@
|
|
|
42
40
|
import { readdirSync, readFileSync } from "node:fs";
|
|
43
41
|
import { dlopen, FFIType, ptr } from "bun:ffi";
|
|
44
42
|
|
|
45
|
-
import { getConfigReadOnly } from "../config/loader.js";
|
|
46
43
|
import { getLogger } from "../util/logger.js";
|
|
47
44
|
|
|
48
45
|
const log = getLogger("orphan-reaper");
|
|
@@ -192,22 +189,9 @@ function reapScan(): void {
|
|
|
192
189
|
}
|
|
193
190
|
}
|
|
194
191
|
|
|
195
|
-
/**
|
|
196
|
-
* Read the opt-in gate from workspace config (`daemon.reapOrphanedSubprocesses`),
|
|
197
|
-
* tolerating a missing or malformed config so startup never fails on it.
|
|
198
|
-
*/
|
|
199
|
-
function isReaperEnabled(): boolean {
|
|
200
|
-
try {
|
|
201
|
-
return getConfigReadOnly().daemon.reapOrphanedSubprocesses;
|
|
202
|
-
} catch {
|
|
203
|
-
return false;
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
|
|
207
192
|
/**
|
|
208
193
|
* Start the periodic orphan reaper. No-op unless the daemon is PID 1 on Linux
|
|
209
|
-
* (otherwise reparented orphans are reaped by the real init)
|
|
210
|
-
* `daemon.reapOrphanedSubprocesses` config gate is enabled.
|
|
194
|
+
* (otherwise reparented orphans are reaped by the real init).
|
|
211
195
|
*/
|
|
212
196
|
export function startOrphanReaper(): void {
|
|
213
197
|
if (scanTimer) return;
|
|
@@ -218,12 +202,6 @@ export function startOrphanReaper(): void {
|
|
|
218
202
|
);
|
|
219
203
|
return;
|
|
220
204
|
}
|
|
221
|
-
if (!isReaperEnabled()) {
|
|
222
|
-
log.info(
|
|
223
|
-
"Orphan reaper not started: daemon.reapOrphanedSubprocesses is disabled",
|
|
224
|
-
);
|
|
225
|
-
return;
|
|
226
|
-
}
|
|
227
205
|
if (!initWaitpid()) return;
|
|
228
206
|
seenLastScan = new Set();
|
|
229
207
|
scanTimer = setInterval(reapScan, SCAN_INTERVAL_MS);
|
package/src/daemon/server.ts
CHANGED
|
@@ -19,7 +19,6 @@ import {
|
|
|
19
19
|
publishAvatarChanged,
|
|
20
20
|
publishConfigChanged,
|
|
21
21
|
publishIdentityChanged,
|
|
22
|
-
publishIdentityIntroChanged,
|
|
23
22
|
publishSoundsConfigUpdated,
|
|
24
23
|
} from "../runtime/sync/resource-sync-events.js";
|
|
25
24
|
import { updatePublishedAppDeployment } from "../services/published-app-updater.js";
|
|
@@ -193,14 +192,6 @@ export class DaemonServer {
|
|
|
193
192
|
}
|
|
194
193
|
}
|
|
195
194
|
|
|
196
|
-
private broadcastIdentityIntroChanged(): void {
|
|
197
|
-
try {
|
|
198
|
-
publishIdentityIntroChanged();
|
|
199
|
-
} catch (err) {
|
|
200
|
-
log.error({ err }, "Failed to broadcast identity intro change");
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
|
|
204
195
|
/** Best-effort sync of the IDENTITY.md name to the platform record. */
|
|
205
196
|
private syncIdentityToPlatform(): void {
|
|
206
197
|
try {
|
|
@@ -313,7 +304,6 @@ export class DaemonServer {
|
|
|
313
304
|
() => this.broadcastAvatarUpdated(),
|
|
314
305
|
() => this.broadcastConfigChanged(),
|
|
315
306
|
() => refreshSkillCapabilityMemories(getConfig()),
|
|
316
|
-
() => this.broadcastIdentityIntroChanged(),
|
|
317
307
|
);
|
|
318
308
|
|
|
319
309
|
this.syncIdentityToPlatform();
|