@mono-agent/agent-app 0.6.2 → 0.8.0
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/README.md +210 -19
- package/dist/adapter-send-proxy.d.ts +18 -0
- package/dist/adapter-send-proxy.d.ts.map +1 -0
- package/dist/adapter-send-proxy.js +229 -0
- package/dist/adapter-send-proxy.js.map +1 -0
- package/dist/adapter-send-tools-main.js +62 -4
- package/dist/adapter-send-tools-main.js.map +1 -1
- package/dist/adapter-send-tools.d.ts +33 -6
- package/dist/adapter-send-tools.d.ts.map +1 -1
- package/dist/adapter-send-tools.js +485 -87
- package/dist/adapter-send-tools.js.map +1 -1
- package/dist/app-config.d.ts.map +1 -1
- package/dist/app-config.js +8 -0
- package/dist/app-config.js.map +1 -1
- package/dist/app.d.ts +2 -0
- package/dist/app.d.ts.map +1 -1
- package/dist/app.js +528 -135
- package/dist/app.js.map +1 -1
- package/dist/broadcast-recorder.d.ts.map +1 -1
- package/dist/broadcast-recorder.js +24 -2
- package/dist/broadcast-recorder.js.map +1 -1
- package/dist/channels.d.ts.map +1 -1
- package/dist/channels.js +16 -6
- package/dist/channels.js.map +1 -1
- package/dist/cli.d.ts +76 -4
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +2102 -102
- package/dist/cli.js.map +1 -1
- package/dist/config-reference.d.ts.map +1 -1
- package/dist/config-reference.js +195 -6
- package/dist/config-reference.js.map +1 -1
- package/dist/configuration-proposal-main.d.ts +3 -0
- package/dist/configuration-proposal-main.d.ts.map +1 -0
- package/dist/configuration-proposal-main.js +13 -0
- package/dist/configuration-proposal-main.js.map +1 -0
- package/dist/configuration-proposal-tool.d.ts +26 -0
- package/dist/configuration-proposal-tool.d.ts.map +1 -0
- package/dist/configuration-proposal-tool.js +139 -0
- package/dist/configuration-proposal-tool.js.map +1 -0
- package/dist/configured-agent.d.ts +12 -0
- package/dist/configured-agent.d.ts.map +1 -1
- package/dist/configured-agent.js +150 -18
- package/dist/configured-agent.js.map +1 -1
- package/dist/consumer-contract.d.ts +3 -0
- package/dist/consumer-contract.d.ts.map +1 -1
- package/dist/consumer-contract.js +44 -1
- package/dist/consumer-contract.js.map +1 -1
- package/dist/doctor.d.ts +27 -5
- package/dist/doctor.d.ts.map +1 -1
- package/dist/doctor.js +1197 -101
- package/dist/doctor.js.map +1 -1
- package/dist/first-run-managed-memory.d.ts +39 -0
- package/dist/first-run-managed-memory.d.ts.map +1 -0
- package/dist/first-run-managed-memory.js +521 -0
- package/dist/first-run-managed-memory.js.map +1 -0
- package/dist/first-run-readiness.d.ts +111 -0
- package/dist/first-run-readiness.d.ts.map +1 -0
- package/dist/first-run-readiness.js +757 -0
- package/dist/first-run-readiness.js.map +1 -0
- package/dist/index.d.ts +8 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/init.d.ts +88 -3
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +1212 -23
- package/dist/init.js.map +1 -1
- package/dist/interaction-bridge.d.ts +25 -1
- package/dist/interaction-bridge.d.ts.map +1 -1
- package/dist/interaction-bridge.js +281 -19
- package/dist/interaction-bridge.js.map +1 -1
- package/dist/local-configuration.d.ts +56 -0
- package/dist/local-configuration.d.ts.map +1 -0
- package/dist/local-configuration.js +914 -0
- package/dist/local-configuration.js.map +1 -0
- package/dist/memory-command.d.ts +4 -0
- package/dist/memory-command.d.ts.map +1 -1
- package/dist/memory-command.js +751 -27
- package/dist/memory-command.js.map +1 -1
- package/dist/memory-recall.d.ts +38 -10
- package/dist/memory-recall.d.ts.map +1 -1
- package/dist/memory-recall.js +82 -25
- package/dist/memory-recall.js.map +1 -1
- package/dist/memory-retrieval.d.ts +66 -0
- package/dist/memory-retrieval.d.ts.map +1 -0
- package/dist/memory-retrieval.js +332 -0
- package/dist/memory-retrieval.js.map +1 -0
- package/dist/modules/base.d.ts +6 -5
- package/dist/modules/base.d.ts.map +1 -1
- package/dist/modules/base.js +11 -7
- package/dist/modules/base.js.map +1 -1
- package/dist/modules/catalog.d.ts.map +1 -1
- package/dist/modules/catalog.js +54 -20
- package/dist/modules/catalog.js.map +1 -1
- package/dist/modules/index.d.ts +2 -2
- package/dist/modules/index.d.ts.map +1 -1
- package/dist/modules/index.js +1 -1
- package/dist/modules/index.js.map +1 -1
- package/dist/modules/known-tools.d.ts +3 -0
- package/dist/modules/known-tools.d.ts.map +1 -1
- package/dist/modules/known-tools.js +11 -1
- package/dist/modules/known-tools.js.map +1 -1
- package/dist/modules/types.d.ts +20 -6
- package/dist/modules/types.d.ts.map +1 -1
- package/dist/modules/types.js.map +1 -1
- package/dist/package-version.d.ts +3 -0
- package/dist/package-version.d.ts.map +1 -0
- package/dist/package-version.js +14 -0
- package/dist/package-version.js.map +1 -0
- package/dist/pi-auth-store-inspection.d.ts +21 -0
- package/dist/pi-auth-store-inspection.d.ts.map +1 -0
- package/dist/pi-auth-store-inspection.js +120 -0
- package/dist/pi-auth-store-inspection.js.map +1 -0
- package/dist/pi-oauth-login-main.d.ts +3 -0
- package/dist/pi-oauth-login-main.d.ts.map +1 -0
- package/dist/pi-oauth-login-main.js +14 -0
- package/dist/pi-oauth-login-main.js.map +1 -0
- package/dist/pi-oauth-login.d.ts +22 -0
- package/dist/pi-oauth-login.d.ts.map +1 -0
- package/dist/pi-oauth-login.js +79 -0
- package/dist/pi-oauth-login.js.map +1 -0
- package/dist/project-skills.d.ts +40 -0
- package/dist/project-skills.d.ts.map +1 -0
- package/dist/project-skills.js +573 -0
- package/dist/project-skills.js.map +1 -0
- package/dist/provider-setup.d.ts +117 -2
- package/dist/provider-setup.d.ts.map +1 -1
- package/dist/provider-setup.js +1505 -49
- package/dist/provider-setup.js.map +1 -1
- package/dist/readiness-probe-worker.d.ts +12 -0
- package/dist/readiness-probe-worker.d.ts.map +1 -0
- package/dist/readiness-probe-worker.js +276 -0
- package/dist/readiness-probe-worker.js.map +1 -0
- package/dist/readiness-probe.d.ts +100 -0
- package/dist/readiness-probe.d.ts.map +1 -0
- package/dist/readiness-probe.js +780 -0
- package/dist/readiness-probe.js.map +1 -0
- package/dist/request-model-override.d.ts +43 -1
- package/dist/request-model-override.d.ts.map +1 -1
- package/dist/request-model-override.js +178 -19
- package/dist/request-model-override.js.map +1 -1
- package/dist/run-history.d.ts +32 -0
- package/dist/run-history.d.ts.map +1 -0
- package/dist/run-history.js +859 -0
- package/dist/run-history.js.map +1 -0
- package/dist/runs-health.d.ts.map +1 -1
- package/dist/runs-health.js +12 -4
- package/dist/runs-health.js.map +1 -1
- package/dist/runtime-option-extensions.d.ts +14 -0
- package/dist/runtime-option-extensions.d.ts.map +1 -0
- package/dist/runtime-option-extensions.js +98 -0
- package/dist/runtime-option-extensions.js.map +1 -0
- package/dist/runtime-routes.d.ts +9 -0
- package/dist/runtime-routes.d.ts.map +1 -0
- package/dist/runtime-routes.js +13 -0
- package/dist/runtime-routes.js.map +1 -0
- package/dist/sandbox-manager.d.ts +69 -0
- package/dist/sandbox-manager.d.ts.map +1 -0
- package/dist/sandbox-manager.js +856 -0
- package/dist/sandbox-manager.js.map +1 -0
- package/dist/supermemory-plugin.d.ts +54 -0
- package/dist/supermemory-plugin.d.ts.map +1 -0
- package/dist/supermemory-plugin.js +136 -0
- package/dist/supermemory-plugin.js.map +1 -0
- package/dist/tui-command.d.ts +15 -0
- package/dist/tui-command.d.ts.map +1 -1
- package/dist/tui-command.js +46 -6
- package/dist/tui-command.js.map +1 -1
- package/dist/web-command.d.ts +8 -0
- package/dist/web-command.d.ts.map +1 -1
- package/dist/web-command.js +174 -17
- package/dist/web-command.js.map +1 -1
- package/dist/wizard/answers.d.ts +31 -4
- package/dist/wizard/answers.d.ts.map +1 -1
- package/dist/wizard/answers.js +145 -16
- package/dist/wizard/answers.js.map +1 -1
- package/dist/wizard/from-flags.d.ts +7 -0
- package/dist/wizard/from-flags.d.ts.map +1 -1
- package/dist/wizard/from-flags.js +36 -2
- package/dist/wizard/from-flags.js.map +1 -1
- package/dist/wizard/index.d.ts +4 -4
- package/dist/wizard/index.d.ts.map +1 -1
- package/dist/wizard/index.js +2 -2
- package/dist/wizard/index.js.map +1 -1
- package/dist/wizard/model-discovery.d.ts +33 -2
- package/dist/wizard/model-discovery.d.ts.map +1 -1
- package/dist/wizard/model-discovery.js +826 -108
- package/dist/wizard/model-discovery.js.map +1 -1
- package/dist/wizard/presets.d.ts +1 -1
- package/dist/wizard/presets.d.ts.map +1 -1
- package/dist/wizard/presets.js +7 -12
- package/dist/wizard/presets.js.map +1 -1
- package/dist/wizard/prompts.d.ts +56 -9
- package/dist/wizard/prompts.d.ts.map +1 -1
- package/dist/wizard/prompts.js +172 -20
- package/dist/wizard/prompts.js.map +1 -1
- package/dist/wizard/run.d.ts +59 -3
- package/dist/wizard/run.d.ts.map +1 -1
- package/dist/wizard/run.js +1133 -191
- package/dist/wizard/run.js.map +1 -1
- package/package.json +21 -16
- package/resources/srt/package-lock.json +66 -0
- package/resources/srt/package.json +9 -0
- package/schema/mono-agent.config.schema.json +381 -17
- package/skills/mono-agent-composer/SKILL.md +4 -4
- package/skills/mono-agent-composer/references/config-blueprint.md +5 -4
- package/skills/mono-agent-composer/references/discovery-questions.md +8 -8
- package/skills/mono-agent-composer/references/feature-coverage.md +8 -6
- package/skills/mono-agent-composer/references/package-map.md +1 -1
- package/skills/mono-agent-composer/references/playbooks.md +8 -7
- package/skills/mono-agent-composer/references/validation.md +1 -1
package/dist/app.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { resolve } from "node:path";
|
|
2
|
+
import { loadToolPolicyFromJsonFileSync } from "@mono-agent/agent-harness";
|
|
2
3
|
import { createLiveEventBus } from "@mono-agent/agent-contracts";
|
|
3
4
|
import { pruneTraceSources, reconcileStaleRunArtifacts, registerTraceSource } from "@mono-agent/observability";
|
|
4
5
|
import { modelReferenceKey } from "@mono-agent/runtime-adapter";
|
|
@@ -11,10 +12,14 @@ import { adapterSendToolNames, createAdapterSendToolsRuntimeExtension, isAdapter
|
|
|
11
12
|
import { loadInteractionSettings, startInteractionBridge } from "./interaction-bridge.js";
|
|
12
13
|
import { startMemoryRituals } from "./memory-rituals.js";
|
|
13
14
|
import { startArtifactRetentionScheduler } from "./artifact-retention.js";
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
15
|
+
import { resolveMemoryRecallSettings, } from "./memory-recall.js";
|
|
16
|
+
import { isSharedRecallStore, MemoryRetrievalService, } from "./memory-retrieval.js";
|
|
17
|
+
import { composeRuntimeOptionExtensions, } from "./runtime-option-extensions.js";
|
|
18
|
+
import { createRunHistoryRuntimeExtension, isRunHistoryToolAllowed, } from "./run-history.js";
|
|
19
|
+
import { createRequestModelOverrideRuntimeExtension, requestModelOverrideTargetsDirectOpenCode, } from "./request-model-override.js";
|
|
16
20
|
import { resolveNotifyDestinations } from "./notify-destinations.js";
|
|
17
21
|
import { resolvePostedMessageIndexPath } from "./posted-message-index.js";
|
|
22
|
+
import { configuredRuntimeFallbackModels, configuredRuntimeModels, hasConfiguredRuntimeFallbacks, } from "./runtime-routes.js";
|
|
18
23
|
/**
|
|
19
24
|
* Starts a config-first mono-agent host in `cwd`: traceability first, then every
|
|
20
25
|
* configured channel in parallel. Channels with incomplete config report
|
|
@@ -41,7 +46,7 @@ export async function startMonoAgentApp(options = {}) {
|
|
|
41
46
|
await controller.startExporters("startup");
|
|
42
47
|
await Promise.all(drivers.map((driver) => controller?.startChannelIfConfigured(driver.id, "startup")));
|
|
43
48
|
await controller.startMemoryRitualsIfConfigured("startup");
|
|
44
|
-
await controller.
|
|
49
|
+
await controller.refreshMemoryHealthAfterLifecycle("startup-complete");
|
|
45
50
|
return controller;
|
|
46
51
|
}
|
|
47
52
|
const DEFAULT_SANDBOX_STATUS = sandboxStatusFromState({
|
|
@@ -54,6 +59,8 @@ const DEFAULT_SANDBOX_STATUS = sandboxStatusFromState({
|
|
|
54
59
|
fallbackActive: false,
|
|
55
60
|
unsafeAllowHostProcess: false,
|
|
56
61
|
});
|
|
62
|
+
/** Strict memory audits scan durable state; never run them at sub-second trace-heartbeat cadence. */
|
|
63
|
+
const MIN_MEMORY_HEALTH_REFRESH_INTERVAL_MS = 30_000;
|
|
57
64
|
class MonoAgentAppController {
|
|
58
65
|
configPath;
|
|
59
66
|
cwd;
|
|
@@ -80,12 +87,32 @@ class MonoAgentAppController {
|
|
|
80
87
|
reason: "No observability exporter configured.",
|
|
81
88
|
};
|
|
82
89
|
sandboxStatusValue = DEFAULT_SANDBOX_STATUS;
|
|
90
|
+
memoryHealthValue = {
|
|
91
|
+
backend: "none",
|
|
92
|
+
status: "unknown",
|
|
93
|
+
checkedAt: new Date().toISOString(),
|
|
94
|
+
};
|
|
95
|
+
memoryHealthRefreshInFlight;
|
|
96
|
+
memoryHealthRefreshTimer;
|
|
97
|
+
memoryHealthRefreshLoopActive = false;
|
|
98
|
+
memoryHealthRefreshIntervalMs = MIN_MEMORY_HEALTH_REFRESH_INTERVAL_MS;
|
|
99
|
+
/** Monotonic completion time for the current generation's cached full audit. */
|
|
100
|
+
memoryHealthLastCompletedAtMs;
|
|
101
|
+
/** One bounded forced refresh reserved by a due timer tick. */
|
|
102
|
+
memoryHealthRefreshDue = false;
|
|
103
|
+
memoryHealthGeneration = 0;
|
|
83
104
|
selectedSkillsValue;
|
|
84
105
|
sessionMetadataValue;
|
|
85
106
|
/** The exporter the responder threads into agent-host (first configured exporter). */
|
|
86
107
|
resolvedExporter;
|
|
87
108
|
traceSource;
|
|
88
|
-
|
|
109
|
+
/**
|
|
110
|
+
* Whole-publication single flight. A slow health probe must not let periodic
|
|
111
|
+
* heartbeat ticks build an unbounded promise tail behind it.
|
|
112
|
+
*/
|
|
113
|
+
traceRefreshInFlight;
|
|
114
|
+
traceRefreshDirty = false;
|
|
115
|
+
traceRefreshLatestReason;
|
|
89
116
|
// Best-effort mirror of `traceSource` into the machine-wide global registry,
|
|
90
117
|
// present only when `shouldMirrorTraceSourceGlobally` gates it on (see
|
|
91
118
|
// `startTraceability`). Kept in lockstep with `traceSource` on every
|
|
@@ -97,15 +124,16 @@ class MonoAgentAppController {
|
|
|
97
124
|
// One shared memory store across all channel responders + the ritual scheduler, so there is a single
|
|
98
125
|
// memory.db handle (not one per channel plus one for rituals). Rebuilt on config reload, closed on stop.
|
|
99
126
|
sharedMemory = undefined;
|
|
127
|
+
sharedMemoryRetrieval;
|
|
100
128
|
sharedMemoryBuilt = false;
|
|
101
129
|
sharedMemoryBuild;
|
|
102
130
|
configApplyTail = Promise.resolve();
|
|
103
131
|
stopped = false;
|
|
104
|
-
// Interaction bridge (AskUser + tool progress): lazily started once
|
|
105
|
-
// by every channel
|
|
132
|
+
// Interaction bridge (AskUser + tool progress): lazily started once and shared
|
|
133
|
+
// by every channel. Its master bearer stays app-owned and is never exported
|
|
134
|
+
// through the generic host/process environment.
|
|
106
135
|
interactionBridge;
|
|
107
136
|
interactionBridgeStart;
|
|
108
|
-
interactionBridgeEnvKeys = [];
|
|
109
137
|
// Shared in-process run-event bus: every run's recorder publishes to it (via the
|
|
110
138
|
// broadcast recorder threaded as `runEventSink`), and the `live` channel relays
|
|
111
139
|
// it over SSE. One instance for the app's lifetime — cheap, bounded ring buffer,
|
|
@@ -137,6 +165,9 @@ class MonoAgentAppController {
|
|
|
137
165
|
get sandboxStatus() {
|
|
138
166
|
return this.sandboxStatusValue;
|
|
139
167
|
}
|
|
168
|
+
get memoryHealth() {
|
|
169
|
+
return this.memoryHealthValue;
|
|
170
|
+
}
|
|
140
171
|
get selectedSkills() {
|
|
141
172
|
return this.selectedSkillsValue;
|
|
142
173
|
}
|
|
@@ -159,9 +190,17 @@ class MonoAgentAppController {
|
|
|
159
190
|
transports: [],
|
|
160
191
|
};
|
|
161
192
|
}
|
|
193
|
+
// Freeze periodic memory work before any channel/store teardown. A probe
|
|
194
|
+
// that already entered is generation-fenced and is deliberately not
|
|
195
|
+
// awaited, so config reload cannot hang behind native/filesystem work.
|
|
196
|
+
this.invalidateMemoryHealthRefresh();
|
|
162
197
|
for (const driver of this.drivers) {
|
|
163
198
|
await this.stopChannel(driver.id, `${reason}:reload`);
|
|
164
199
|
}
|
|
200
|
+
// Tool policy/runtime-family changes must re-evaluate implicit AskUser.
|
|
201
|
+
// Clearing the cached promise also prevents stale bridge env from a Pi
|
|
202
|
+
// config leaking into a reloaded direct-OpenCode responder.
|
|
203
|
+
await this.stopInteractionBridge();
|
|
165
204
|
this.stopMemoryRituals();
|
|
166
205
|
this.stopArtifactRetentionScheduler();
|
|
167
206
|
await this.resetSharedMemory();
|
|
@@ -171,7 +210,7 @@ class MonoAgentAppController {
|
|
|
171
210
|
await this.startExporters(reason);
|
|
172
211
|
await Promise.all(this.drivers.map((driver) => this.startChannelIfConfigured(driver.id, reason)));
|
|
173
212
|
await this.startMemoryRitualsIfConfigured(reason);
|
|
174
|
-
await this.
|
|
213
|
+
await this.refreshMemoryHealthAfterLifecycle(`${reason}:complete`);
|
|
175
214
|
return this.applyResult();
|
|
176
215
|
};
|
|
177
216
|
const next = this.configApplyTail.then(run, run);
|
|
@@ -207,6 +246,7 @@ class MonoAgentAppController {
|
|
|
207
246
|
try {
|
|
208
247
|
const input = { env: this.env, cwd: this.cwd, configPath: this.configPath };
|
|
209
248
|
await this.refreshSelectedSkillsSnapshot(reason);
|
|
249
|
+
await this.refreshMemoryHealthSnapshot(reason);
|
|
210
250
|
const [registryDir, artifactDir, sourceId, label, heartbeatMs, globalDiscovery] = await Promise.all([
|
|
211
251
|
resolveAppTraceRegistryDir(input),
|
|
212
252
|
resolveAppArtifactDir(input),
|
|
@@ -225,6 +265,7 @@ class MonoAgentAppController {
|
|
|
225
265
|
transports: this.activeTransports(),
|
|
226
266
|
configPath: this.configPath,
|
|
227
267
|
metadata: this.traceMetadata(reason),
|
|
268
|
+
memoryHealth: this.memoryHealthValue,
|
|
228
269
|
heartbeatMs,
|
|
229
270
|
};
|
|
230
271
|
this.traceSource = await registerTraceSource(registerOptions);
|
|
@@ -250,8 +291,10 @@ class MonoAgentAppController {
|
|
|
250
291
|
else {
|
|
251
292
|
this.globalTraceSource = undefined;
|
|
252
293
|
}
|
|
294
|
+
this.startMemoryHealthRefreshLoop(MIN_MEMORY_HEALTH_REFRESH_INTERVAL_MS);
|
|
253
295
|
}
|
|
254
296
|
catch (error) {
|
|
297
|
+
this.clearMemoryHealthRefreshTimer();
|
|
255
298
|
const failure = reasonOf(error);
|
|
256
299
|
this.traceabilityStatusValue = { kind: "failed", reason: failure };
|
|
257
300
|
this.logger?.error?.("Traceability source registration failed.", { reason: failure });
|
|
@@ -379,49 +422,271 @@ class MonoAgentAppController {
|
|
|
379
422
|
// (which reads the manifest, not this live object) can surface it too.
|
|
380
423
|
void this.refreshTraceSource("exporter-warning").catch(() => undefined);
|
|
381
424
|
}
|
|
382
|
-
|
|
383
|
-
const
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
425
|
+
refreshTraceSource(reason) {
|
|
426
|
+
const existing = this.traceRefreshInFlight;
|
|
427
|
+
if (existing !== undefined) {
|
|
428
|
+
// Keep one bounded replay slot. Concurrent explicit callers share the
|
|
429
|
+
// exact owner promise; the owner publishes the latest state/reason once
|
|
430
|
+
// more after its current write. Requests arriving during that replay set
|
|
431
|
+
// the same bit again, without growing a callback queue.
|
|
432
|
+
this.traceRefreshDirty = true;
|
|
433
|
+
this.traceRefreshLatestReason = reason;
|
|
434
|
+
return existing;
|
|
435
|
+
}
|
|
436
|
+
const traceSource = this.traceSource;
|
|
437
|
+
const globalTraceSource = this.globalTraceSource;
|
|
438
|
+
if (traceSource === undefined) {
|
|
439
|
+
return Promise.resolve();
|
|
440
|
+
}
|
|
441
|
+
const generation = this.memoryHealthGeneration;
|
|
442
|
+
let pending;
|
|
443
|
+
pending = (async () => {
|
|
444
|
+
let publishReason = reason;
|
|
445
|
+
for (;;) {
|
|
446
|
+
this.traceRefreshDirty = false;
|
|
447
|
+
this.traceRefreshLatestReason = undefined;
|
|
448
|
+
const health = await this.refreshMemoryHealthSnapshot(publishReason);
|
|
449
|
+
if (generation !== this.memoryHealthGeneration || this.traceSource !== traceSource) {
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
const patch = {
|
|
453
|
+
transports: this.activeTransports(),
|
|
454
|
+
metadata: this.traceMetadata(publishReason),
|
|
455
|
+
memoryHealth: health,
|
|
456
|
+
};
|
|
395
457
|
try {
|
|
396
|
-
await
|
|
458
|
+
await traceSource.update(patch);
|
|
397
459
|
}
|
|
398
460
|
catch (error) {
|
|
399
|
-
this.logger?.warn?.("
|
|
461
|
+
this.logger?.warn?.("Traceability source update failed.", { reason: reasonOf(error) });
|
|
462
|
+
}
|
|
463
|
+
if (globalTraceSource !== undefined && generation === this.memoryHealthGeneration
|
|
464
|
+
&& this.globalTraceSource === globalTraceSource) {
|
|
465
|
+
try {
|
|
466
|
+
await globalTraceSource.update(patch);
|
|
467
|
+
}
|
|
468
|
+
catch (error) {
|
|
469
|
+
this.logger?.warn?.("Global trace-source mirror update failed.", { reason: reasonOf(error) });
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
if (!this.traceRefreshDirty || generation !== this.memoryHealthGeneration
|
|
473
|
+
|| this.traceSource !== traceSource) {
|
|
474
|
+
return;
|
|
400
475
|
}
|
|
476
|
+
publishReason = this.traceRefreshLatestReason ?? publishReason;
|
|
401
477
|
}
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
478
|
+
})().catch(() => {
|
|
479
|
+
// Health computation and trace handles already sanitize/log their own
|
|
480
|
+
// failures. Keep this final boundary closed so background refreshes never
|
|
481
|
+
// become unhandled rejections or leak raw provider/native errors.
|
|
482
|
+
}).finally(() => {
|
|
483
|
+
if (this.traceRefreshInFlight === pending) {
|
|
484
|
+
this.traceRefreshInFlight = undefined;
|
|
485
|
+
const replayDue = this.memoryHealthRefreshDue
|
|
486
|
+
&& !this.stopped
|
|
487
|
+
&& generation === this.memoryHealthGeneration
|
|
488
|
+
&& this.traceSource === traceSource;
|
|
489
|
+
this.traceRefreshDirty = false;
|
|
490
|
+
this.traceRefreshLatestReason = undefined;
|
|
491
|
+
// A timer can fire after the loop's final dirty check but before this
|
|
492
|
+
// owner settles. Preserve exactly one due replay across that seam.
|
|
493
|
+
if (replayDue) {
|
|
494
|
+
void this.refreshTraceSource("memory-health-periodic").catch(() => undefined);
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
});
|
|
498
|
+
this.traceRefreshInFlight = pending;
|
|
499
|
+
return pending;
|
|
500
|
+
}
|
|
501
|
+
/**
|
|
502
|
+
* Refresh the content-free memory snapshot once for all concurrent trace
|
|
503
|
+
* publishers. The cached value is the only memory state read by synchronous
|
|
504
|
+
* manifest composition; provider/native errors collapse to a closed unknown
|
|
505
|
+
* shape and never escape into registry metadata.
|
|
506
|
+
*/
|
|
507
|
+
refreshMemoryHealthSnapshot(reason, lifecycleForce = false) {
|
|
508
|
+
if (this.stopped) {
|
|
509
|
+
return Promise.resolve(this.memoryHealthValue);
|
|
510
|
+
}
|
|
511
|
+
if (this.memoryHealthRefreshInFlight !== undefined) {
|
|
512
|
+
return this.memoryHealthRefreshInFlight;
|
|
513
|
+
}
|
|
514
|
+
const forced = lifecycleForce || this.memoryHealthRefreshDue;
|
|
515
|
+
if (!forced && this.memoryHealthLastCompletedAtMs !== undefined
|
|
516
|
+
&& performance.now() - this.memoryHealthLastCompletedAtMs < MIN_MEMORY_HEALTH_REFRESH_INTERVAL_MS) {
|
|
517
|
+
return Promise.resolve(this.memoryHealthValue);
|
|
518
|
+
}
|
|
519
|
+
const generation = this.memoryHealthGeneration;
|
|
520
|
+
let pending;
|
|
521
|
+
pending = this.computeMemoryHealth().then((health) => {
|
|
522
|
+
if (!this.stopped && generation === this.memoryHealthGeneration) {
|
|
523
|
+
this.memoryHealthValue = health;
|
|
524
|
+
this.recordMemoryHealthCompletion(generation);
|
|
525
|
+
return health;
|
|
526
|
+
}
|
|
527
|
+
return this.memoryHealthValue;
|
|
528
|
+
}).catch(() => {
|
|
529
|
+
const health = this.memoryHealthValue.backend === "bujo"
|
|
530
|
+
? unknownBujoMemoryHealth(this.memoryHealthValue.mode)
|
|
531
|
+
: this.memoryHealthValue.backend === "supermemory"
|
|
532
|
+
? {
|
|
533
|
+
backend: "supermemory",
|
|
534
|
+
status: "unknown",
|
|
535
|
+
checkedAt: new Date().toISOString(),
|
|
536
|
+
}
|
|
537
|
+
: unknownNoMemoryHealth();
|
|
538
|
+
if (!this.stopped && generation === this.memoryHealthGeneration) {
|
|
539
|
+
this.memoryHealthValue = health;
|
|
540
|
+
this.recordMemoryHealthCompletion(generation);
|
|
541
|
+
}
|
|
542
|
+
this.logger?.warn?.("Memory health refresh failed; publishing sanitized unknown health.", { reason });
|
|
543
|
+
return health;
|
|
544
|
+
}).finally(() => {
|
|
545
|
+
if (this.memoryHealthRefreshInFlight === pending) {
|
|
546
|
+
this.memoryHealthRefreshInFlight = undefined;
|
|
547
|
+
}
|
|
548
|
+
});
|
|
549
|
+
this.memoryHealthRefreshInFlight = pending;
|
|
550
|
+
return pending;
|
|
551
|
+
}
|
|
552
|
+
async computeMemoryHealth() {
|
|
553
|
+
let config;
|
|
554
|
+
try {
|
|
555
|
+
config = await loadAppCoreConfig({ env: this.env, cwd: this.cwd, configPath: this.configPath });
|
|
556
|
+
}
|
|
557
|
+
catch {
|
|
558
|
+
return unknownNoMemoryHealth();
|
|
559
|
+
}
|
|
560
|
+
const memory = config.memory;
|
|
561
|
+
if (memory === undefined) {
|
|
562
|
+
return {
|
|
563
|
+
backend: "none",
|
|
564
|
+
status: "not_configured",
|
|
565
|
+
checkedAt: new Date().toISOString(),
|
|
566
|
+
};
|
|
567
|
+
}
|
|
568
|
+
if ((memory.backend ?? "bujo") === "supermemory") {
|
|
569
|
+
return {
|
|
570
|
+
backend: "supermemory",
|
|
571
|
+
status: "unknown",
|
|
572
|
+
checkedAt: new Date().toISOString(),
|
|
573
|
+
};
|
|
574
|
+
}
|
|
575
|
+
try {
|
|
576
|
+
const { auditBujoMemoryHealth } = await import("@mono-agent/memory/bujo");
|
|
577
|
+
return traceMemoryHealthFromBujo(auditBujoMemoryHealth({
|
|
578
|
+
root: memory.path,
|
|
579
|
+
mode: memory.mode,
|
|
580
|
+
...(memory.embeddings === undefined
|
|
581
|
+
? {}
|
|
582
|
+
: {
|
|
583
|
+
configuredEmbeddingModel: `${memory.embeddings.provider}:${memory.embeddings.model}`,
|
|
584
|
+
configuredDimension: memory.embeddings.dim ?? 768,
|
|
585
|
+
}),
|
|
586
|
+
}));
|
|
587
|
+
}
|
|
588
|
+
catch {
|
|
589
|
+
return unknownBujoMemoryHealth(memory.mode);
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
startMemoryHealthRefreshLoop(intervalMs) {
|
|
593
|
+
this.memoryHealthRefreshLoopActive = true;
|
|
594
|
+
this.memoryHealthRefreshIntervalMs = intervalMs;
|
|
595
|
+
this.scheduleMemoryHealthRefresh();
|
|
596
|
+
}
|
|
597
|
+
scheduleMemoryHealthRefresh(delayOverrideMs) {
|
|
598
|
+
this.clearMemoryHealthRefreshTimer();
|
|
599
|
+
if (!this.memoryHealthRefreshLoopActive || this.stopped || this.traceSource === undefined)
|
|
600
|
+
return;
|
|
601
|
+
const elapsed = this.memoryHealthLastCompletedAtMs === undefined
|
|
602
|
+
? this.memoryHealthRefreshIntervalMs
|
|
603
|
+
: Math.max(0, performance.now() - this.memoryHealthLastCompletedAtMs);
|
|
604
|
+
const delay = delayOverrideMs ?? Math.max(0, this.memoryHealthRefreshIntervalMs - elapsed);
|
|
605
|
+
this.memoryHealthRefreshTimer = setTimeout(() => {
|
|
606
|
+
this.memoryHealthRefreshTimer = undefined;
|
|
607
|
+
this.refreshMemoryHealthOnTimer();
|
|
608
|
+
}, delay);
|
|
609
|
+
this.memoryHealthRefreshTimer.unref?.();
|
|
610
|
+
}
|
|
611
|
+
recordMemoryHealthCompletion(generation) {
|
|
612
|
+
if (this.stopped || generation !== this.memoryHealthGeneration)
|
|
613
|
+
return;
|
|
614
|
+
this.memoryHealthLastCompletedAtMs = performance.now();
|
|
615
|
+
// Any full audit that was already running when the timer became due
|
|
616
|
+
// satisfies that due tick; never queue an immediate second audit.
|
|
617
|
+
this.memoryHealthRefreshDue = false;
|
|
618
|
+
this.scheduleMemoryHealthRefresh(this.memoryHealthRefreshIntervalMs);
|
|
619
|
+
}
|
|
620
|
+
refreshMemoryHealthOnTimer() {
|
|
621
|
+
if (this.stopped)
|
|
622
|
+
return;
|
|
623
|
+
if (this.memoryHealthLastCompletedAtMs !== undefined) {
|
|
624
|
+
const elapsed = performance.now() - this.memoryHealthLastCompletedAtMs;
|
|
625
|
+
if (elapsed < this.memoryHealthRefreshIntervalMs) {
|
|
626
|
+
this.scheduleMemoryHealthRefresh();
|
|
627
|
+
return;
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
// Set the due bit before joining the trace single-flight. If a publication
|
|
631
|
+
// is already active it consumes one bounded trailing replay rather than
|
|
632
|
+
// dropping this tick and waiting another full interval.
|
|
633
|
+
this.memoryHealthRefreshDue = true;
|
|
634
|
+
void this.refreshTraceSource("memory-health-periodic").catch(() => undefined);
|
|
635
|
+
}
|
|
636
|
+
/**
|
|
637
|
+
* Re-audit after channel responders and the shared memory store have started.
|
|
638
|
+
* The trace source is registered first, so its registration audit can only
|
|
639
|
+
* observe the previous/stopped runtime snapshot. This one lifecycle force is
|
|
640
|
+
* generation-fenced and uses the same health/trace single-flights as timer
|
|
641
|
+
* work; ordinary session and trace events continue to reuse the 30s cache.
|
|
642
|
+
*/
|
|
643
|
+
async refreshMemoryHealthAfterLifecycle(reason) {
|
|
644
|
+
if (this.stopped)
|
|
645
|
+
return;
|
|
646
|
+
const generation = this.memoryHealthGeneration;
|
|
647
|
+
const joinedExistingAudit = this.memoryHealthRefreshInFlight !== undefined;
|
|
648
|
+
await this.refreshMemoryHealthSnapshot(reason, true);
|
|
649
|
+
if (joinedExistingAudit && !this.stopped && generation === this.memoryHealthGeneration) {
|
|
650
|
+
await this.refreshMemoryHealthSnapshot(reason, true);
|
|
651
|
+
}
|
|
652
|
+
if (this.stopped || generation !== this.memoryHealthGeneration)
|
|
653
|
+
return;
|
|
654
|
+
await this.refreshTraceSource(reason);
|
|
655
|
+
}
|
|
656
|
+
clearMemoryHealthRefreshTimer() {
|
|
657
|
+
if (this.memoryHealthRefreshTimer !== undefined) {
|
|
658
|
+
clearTimeout(this.memoryHealthRefreshTimer);
|
|
659
|
+
this.memoryHealthRefreshTimer = undefined;
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
invalidateMemoryHealthRefresh() {
|
|
663
|
+
this.memoryHealthRefreshLoopActive = false;
|
|
664
|
+
this.clearMemoryHealthRefreshTimer();
|
|
665
|
+
this.memoryHealthGeneration += 1;
|
|
666
|
+
this.memoryHealthRefreshInFlight = undefined;
|
|
667
|
+
this.memoryHealthLastCompletedAtMs = undefined;
|
|
668
|
+
this.memoryHealthRefreshDue = false;
|
|
406
669
|
}
|
|
407
670
|
/**
|
|
408
671
|
* Start the interaction bridge once, when a blocking ask tool is allowed by
|
|
409
|
-
* the tool policy
|
|
410
|
-
*
|
|
411
|
-
* spawned stdio tool children (which inherit process.env) can reach it.
|
|
672
|
+
* the tool policy, a request-context MCP needs scoped progress, or the operator
|
|
673
|
+
* configured the `interaction` block. The master bearer remains app-owned.
|
|
412
674
|
*/
|
|
413
675
|
ensureInteractionBridge(coreConfig) {
|
|
414
676
|
this.interactionBridgeStart ??= (async () => {
|
|
415
|
-
const
|
|
677
|
+
const directOpenCodeRoute = runtimeRouteContainsDirectOpenCode(coreConfig);
|
|
678
|
+
const settings = await loadInteractionSettings({ env: this.env, configPath: this.configPath });
|
|
679
|
+
const askUserAllowed = !directOpenCodeRoute && isAdapterSendToolAllowed("AskUser", {
|
|
416
680
|
allowedTools: coreConfig.tools.allowedTools,
|
|
417
681
|
disallowedTools: coreConfig.tools.disallowedTools,
|
|
418
682
|
});
|
|
419
|
-
const telegramAskAllowed = isAdapterSendToolAllowed("TelegramAskButtons", {
|
|
683
|
+
const telegramAskAllowed = !directOpenCodeRoute && isAdapterSendToolAllowed("TelegramAskButtons", {
|
|
420
684
|
allowedTools: coreConfig.tools.allowedTools,
|
|
421
685
|
disallowedTools: coreConfig.tools.disallowedTools,
|
|
422
686
|
});
|
|
423
|
-
const
|
|
424
|
-
|
|
687
|
+
const scopedProgressNeeded = settings.progressEnabled
|
|
688
|
+
&& (coreConfig.tools.mcpRequestContextServers?.length ?? 0) > 0;
|
|
689
|
+
if (!askUserAllowed && !telegramAskAllowed && !scopedProgressNeeded && !settings.configured) {
|
|
425
690
|
return undefined;
|
|
426
691
|
}
|
|
427
692
|
try {
|
|
@@ -431,12 +696,6 @@ class MonoAgentAppController {
|
|
|
431
696
|
askTimeoutMs: settings.askTimeoutMs,
|
|
432
697
|
...(this.logger === undefined ? {} : { logger: this.logger }),
|
|
433
698
|
});
|
|
434
|
-
const bridgeEnv = bridge.env();
|
|
435
|
-
this.interactionBridgeEnvKeys = Object.keys(bridgeEnv);
|
|
436
|
-
Object.assign(this.env, bridgeEnv);
|
|
437
|
-
if (this.env !== process.env) {
|
|
438
|
-
Object.assign(process.env, bridgeEnv);
|
|
439
|
-
}
|
|
440
699
|
this.interactionBridge = bridge;
|
|
441
700
|
this.logger?.info?.("Interaction bridge started.", { url: bridge.url });
|
|
442
701
|
return bridge;
|
|
@@ -454,13 +713,6 @@ class MonoAgentAppController {
|
|
|
454
713
|
const bridge = this.interactionBridge;
|
|
455
714
|
this.interactionBridge = undefined;
|
|
456
715
|
this.interactionBridgeStart = undefined;
|
|
457
|
-
for (const key of this.interactionBridgeEnvKeys) {
|
|
458
|
-
delete this.env[key];
|
|
459
|
-
if (this.env !== process.env) {
|
|
460
|
-
delete process.env[key];
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
this.interactionBridgeEnvKeys = [];
|
|
464
716
|
await bridge?.stop().catch(() => undefined);
|
|
465
717
|
}
|
|
466
718
|
async stop() {
|
|
@@ -468,6 +720,9 @@ class MonoAgentAppController {
|
|
|
468
720
|
return;
|
|
469
721
|
}
|
|
470
722
|
this.stopped = true;
|
|
723
|
+
// Stop the periodic audit before the first teardown await. Already-entered
|
|
724
|
+
// computation is generation-fenced and must never delay shutdown.
|
|
725
|
+
this.invalidateMemoryHealthRefresh();
|
|
471
726
|
for (const driver of this.drivers) {
|
|
472
727
|
await this.stopChannel(driver.id, "stop");
|
|
473
728
|
}
|
|
@@ -783,35 +1038,85 @@ class MonoAgentAppController {
|
|
|
783
1038
|
if (!this.activeRuntimes.includes(runtime)) {
|
|
784
1039
|
this.activeRuntimes.push(runtime);
|
|
785
1040
|
}
|
|
786
|
-
const
|
|
787
|
-
const
|
|
1041
|
+
const memoryBackend = await this.memoryStore(coreConfig);
|
|
1042
|
+
const memoryRetrieval = this.ensureSharedMemoryRetrieval(coreConfig, memoryBackend);
|
|
1043
|
+
const memory = memoryRetrieval ?? memoryBackend;
|
|
1044
|
+
const memoryRecallEnabled = this.reportMemoryRecallStatus(coreConfig, memoryRetrieval);
|
|
788
1045
|
const supermemoryMcp = this.supermemoryMcpRuntimeOptions(coreConfig);
|
|
789
1046
|
const adapterSendTools = await this.adapterSendToolsRuntimeOptions(coreConfig);
|
|
1047
|
+
const runHistoryBase = isRunHistoryToolAllowed(coreConfig.tools)
|
|
1048
|
+
&& !runtimeRouteContainsDirectOpenCode(coreConfig)
|
|
1049
|
+
? createRunHistoryRuntimeExtension({
|
|
1050
|
+
artifactDir: coreConfig.artifacts.dir,
|
|
1051
|
+
onUnavailable: (error) => {
|
|
1052
|
+
this.logger?.warn?.("RunHistory tool endpoint could not start; continuing without prior-run inspection.", {
|
|
1053
|
+
reason: reasonOf(error),
|
|
1054
|
+
});
|
|
1055
|
+
},
|
|
1056
|
+
})
|
|
1057
|
+
: undefined;
|
|
790
1058
|
// Always active: a no-op for interactive turns (which carry no cron/webhook
|
|
791
1059
|
// metadata), it applies the per-trigger model/effort override otherwise.
|
|
792
|
-
const
|
|
1060
|
+
const mcpSources = [];
|
|
1061
|
+
if (coreConfig.tools.mcpConfigPath !== undefined) {
|
|
1062
|
+
try {
|
|
1063
|
+
const names = Object.keys(loadToolPolicyFromJsonFileSync(coreConfig.tools.mcpConfigPath).mcpServers ?? {});
|
|
1064
|
+
if (names.length > 0)
|
|
1065
|
+
mcpSources.push(`tools.mcpConfigPath (${names.join(", ")})`);
|
|
1066
|
+
}
|
|
1067
|
+
catch {
|
|
1068
|
+
// Responder construction owns the missing/malformed policy error.
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
if (memoryRecallEnabled)
|
|
1072
|
+
mcpSources.push("memory.recallTool");
|
|
1073
|
+
if (supermemoryMcp !== undefined)
|
|
1074
|
+
mcpSources.push("memory.supermemory.exposeMcpServer");
|
|
1075
|
+
if (adapterSendTools.blockingToolNames.length > 0) {
|
|
1076
|
+
mcpSources.push(`adapter send tools (${adapterSendTools.blockingToolNames.join(", ")})`);
|
|
1077
|
+
}
|
|
1078
|
+
const requestModelOverride = this.requestModelOverrideRuntimeOptions(coreConfig, {
|
|
1079
|
+
mcpSources,
|
|
1080
|
+
indexSkillsActive: coreConfig.context.skillDisclosure === "index"
|
|
1081
|
+
&& coreConfig.context.skillsRoot !== undefined,
|
|
1082
|
+
});
|
|
1083
|
+
const adapterSendToolsExtension = adapterSendTools.createExtension?.(requestModelOverride.targetsDirectOpenCode);
|
|
1084
|
+
const runHistoryExtension = runHistoryBase === undefined
|
|
1085
|
+
? undefined
|
|
1086
|
+
: async (requestInput) => requestModelOverride.targetsDirectOpenCode(requestInput.request.metadata)
|
|
1087
|
+
? { runtimeOptions: {}, cleanup: async () => { } }
|
|
1088
|
+
: await runHistoryBase(requestInput);
|
|
793
1089
|
const runtimeOptionsForRequest = composeRuntimeOptionExtensions([
|
|
794
|
-
memoryRecall,
|
|
795
1090
|
supermemoryMcp,
|
|
796
|
-
|
|
797
|
-
|
|
1091
|
+
runHistoryExtension,
|
|
1092
|
+
adapterSendToolsExtension,
|
|
1093
|
+
requestModelOverride.extension,
|
|
798
1094
|
]);
|
|
799
1095
|
// The override factory is only needed when fallbacks are configured: the
|
|
800
1096
|
// fallback router freezes the model chain, so an override must run on a runtime
|
|
801
1097
|
// whose chain has it as primary. With no fallbacks the shared (plain) runtime
|
|
802
1098
|
// honors the per-run model directly, so building a separate runtime would be
|
|
803
1099
|
// redundant. Omit the factory there and the harness uses the shared runtime.
|
|
804
|
-
const runtimeForModel = (coreConfig.runtime
|
|
1100
|
+
const runtimeForModel = hasConfiguredRuntimeFallbacks(coreConfig.runtime)
|
|
805
1101
|
? this.buildRuntimeForModel(coreConfig)
|
|
806
1102
|
: undefined;
|
|
807
1103
|
const observabilityContext = await this.observabilityContext();
|
|
808
1104
|
const responder = await createConfiguredAgentResponder({
|
|
809
1105
|
config: coreConfig,
|
|
1106
|
+
cwd: this.cwd,
|
|
810
1107
|
runtime,
|
|
811
1108
|
...(runtimeForModel === undefined ? {} : { runtimeForModel }),
|
|
812
1109
|
...(this.sandboxEngine === undefined ? {} : { sandboxEngine: this.sandboxEngine }),
|
|
813
1110
|
...(memory !== undefined && { memory }),
|
|
1111
|
+
...(this.interactionBridge === undefined ? {} : { turnHistoryEnricher: this.interactionBridge }),
|
|
1112
|
+
...(this.interactionBridge === undefined ? {} : { progressCapabilityIssuer: this.interactionBridge }),
|
|
814
1113
|
...(runtimeOptionsForRequest === undefined ? {} : { runtimeOptionsForRequest }),
|
|
1114
|
+
onMemoryRecallUnavailable: (error) => {
|
|
1115
|
+
this.logger?.warn?.("MemoryRecall tool endpoint could not start; continuing without the explicit tool.", { error: reasonOf(error) });
|
|
1116
|
+
},
|
|
1117
|
+
onMemoryWarning: (message) => {
|
|
1118
|
+
this.logger?.warn?.(message);
|
|
1119
|
+
},
|
|
815
1120
|
// Thread run-identifying context onto exported spans and surface per-run
|
|
816
1121
|
// export warnings to `exporterStatus` (agent-host only builds the exporter
|
|
817
1122
|
// when config.observability.exporters is non-empty).
|
|
@@ -831,12 +1136,26 @@ class MonoAgentAppController {
|
|
|
831
1136
|
* block for the OVERRIDE model (see request-model-override doc). Composed
|
|
832
1137
|
* alongside the memory/adapter extensions.
|
|
833
1138
|
*/
|
|
834
|
-
requestModelOverrideRuntimeOptions(coreConfig) {
|
|
835
|
-
const
|
|
1139
|
+
requestModelOverrideRuntimeOptions(coreConfig, compatibility) {
|
|
1140
|
+
const options = {
|
|
836
1141
|
...(this.logger === undefined ? {} : { logger: this.logger }),
|
|
1142
|
+
baseModel: coreConfig.runtime.model,
|
|
1143
|
+
...(configuredRuntimeFallbackModels(coreConfig.runtime).length === 0
|
|
1144
|
+
? {}
|
|
1145
|
+
: { fallbackModels: configuredRuntimeFallbackModels(coreConfig.runtime) }),
|
|
1146
|
+
...(coreConfig.runtime.effort === undefined ? {} : { baseEffort: coreConfig.runtime.effort }),
|
|
1147
|
+
...(coreConfig.runtime.maxTurns === undefined ? {} : { baseMaxTurns: coreConfig.runtime.maxTurns }),
|
|
1148
|
+
...(compatibility.mcpSources.length === 0 ? {} : { mcpSources: compatibility.mcpSources }),
|
|
1149
|
+
...(compatibility.indexSkillsActive ? { indexSkillsActive: true } : {}),
|
|
1150
|
+
...(coreConfig.sandbox === undefined ? {} : { sandboxPolicy: coreConfig.sandbox }),
|
|
1151
|
+
toolPolicy: coreConfig.tools,
|
|
837
1152
|
...(coreConfig.providers?.local === undefined ? {} : { localProviders: coreConfig.providers.local }),
|
|
838
|
-
}
|
|
839
|
-
|
|
1153
|
+
};
|
|
1154
|
+
const extension = createRequestModelOverrideRuntimeExtension(options);
|
|
1155
|
+
return {
|
|
1156
|
+
extension: async (input) => extension({ request: input.request }),
|
|
1157
|
+
targetsDirectOpenCode: (metadata) => requestModelOverrideTargetsDirectOpenCode(metadata, options),
|
|
1158
|
+
};
|
|
840
1159
|
}
|
|
841
1160
|
/**
|
|
842
1161
|
* Memoized factory for runtimes bound to a per-request override model. Reuses
|
|
@@ -879,15 +1198,19 @@ class MonoAgentAppController {
|
|
|
879
1198
|
]);
|
|
880
1199
|
return { sourceId, sourceLabel, configPath: this.configPath };
|
|
881
1200
|
}
|
|
882
|
-
|
|
1201
|
+
reportMemoryRecallStatus(coreConfig, service) {
|
|
883
1202
|
const settings = resolveMemoryRecallSettings(coreConfig);
|
|
884
1203
|
if (settings === undefined) {
|
|
885
|
-
return
|
|
1204
|
+
return false;
|
|
1205
|
+
}
|
|
1206
|
+
if (service === undefined) {
|
|
1207
|
+
this.logger?.warn?.("MemoryRecall could not be enabled because the configured store has no recall surface.");
|
|
1208
|
+
return false;
|
|
886
1209
|
}
|
|
887
1210
|
this.logger?.info?.("Read-only MemoryRecall tool enabled.", {
|
|
888
1211
|
provider: "supermemory" in settings ? "supermemory" : settings.embeddings?.provider ?? "fts-only",
|
|
889
1212
|
});
|
|
890
|
-
return
|
|
1213
|
+
return true;
|
|
891
1214
|
}
|
|
892
1215
|
/**
|
|
893
1216
|
* Optional CLOUD-ONLY escape hatch: when `memory.supermemory.exposeMcpServer` is on, ALSO inject
|
|
@@ -918,21 +1241,47 @@ class MonoAgentAppController {
|
|
|
918
1241
|
}
|
|
919
1242
|
async adapterSendToolsRuntimeOptions(coreConfig) {
|
|
920
1243
|
const input = { env: this.env, cwd: this.cwd, configPath: this.configPath };
|
|
1244
|
+
const bridgeEnv = this.interactionBridge?.env();
|
|
1245
|
+
const appOwnedInteraction = this.interactionBridge === undefined || bridgeEnv === undefined
|
|
1246
|
+
? undefined
|
|
1247
|
+
: {
|
|
1248
|
+
bridgeUrl: this.interactionBridge.url,
|
|
1249
|
+
bridgeToken: this.interactionBridge.token,
|
|
1250
|
+
timeoutMs: Number(bridgeEnv.MONO_AGENT_ASK_USER_TIMEOUT_MS),
|
|
1251
|
+
};
|
|
921
1252
|
const settings = await resolveAdapterSendToolsSettings(input, {
|
|
922
1253
|
allowedTools: coreConfig.tools.allowedTools,
|
|
923
1254
|
disallowedTools: coreConfig.tools.disallowedTools,
|
|
924
1255
|
logger: this.logger,
|
|
1256
|
+
suppressInteractionTools: runtimeRouteContainsDirectOpenCode(coreConfig),
|
|
1257
|
+
...(appOwnedInteraction === undefined ? {} : { interaction: appOwnedInteraction }),
|
|
925
1258
|
});
|
|
926
1259
|
if (settings === undefined) {
|
|
927
|
-
return
|
|
1260
|
+
return { blockingToolNames: [] };
|
|
928
1261
|
}
|
|
929
1262
|
const toolNames = adapterSendToolNames(settings);
|
|
1263
|
+
const blockingToolNames = toolNames.filter((name) => !isInteractionToolName(name));
|
|
930
1264
|
this.logger?.info?.("Adapter send tools enabled.", { tools: toolNames });
|
|
931
1265
|
// Forward the posted-message index path so `SlackSendMessage` links each post
|
|
932
1266
|
// back to the producing conversation (so a later in-thread reply resumes it).
|
|
933
1267
|
const indexPath = resolvePostedMessageIndexPath(await resolveAppArtifactDir(input));
|
|
934
|
-
const
|
|
935
|
-
|
|
1268
|
+
const interactionForChild = settings.askUser ?? settings.telegram?.askBridge;
|
|
1269
|
+
const runOutputRoot = settings.telegram?.sendTools?.pathScope === "run-output"
|
|
1270
|
+
? resolve(coreConfig.artifacts.dir, "outbound")
|
|
1271
|
+
: undefined;
|
|
1272
|
+
const createExtension = (targetsDirectOpenCode) => async (requestInput) => {
|
|
1273
|
+
const effectiveToolNames = targetsDirectOpenCode(requestInput.request.metadata)
|
|
1274
|
+
? toolNames.filter((name) => !isInteractionToolName(name))
|
|
1275
|
+
: toolNames;
|
|
1276
|
+
if (effectiveToolNames.length === 0) {
|
|
1277
|
+
return { runtimeOptions: {}, cleanup: async () => { } };
|
|
1278
|
+
}
|
|
1279
|
+
const effectiveInteraction = effectiveToolNames.some(isInteractionToolName)
|
|
1280
|
+
? interactionForChild
|
|
1281
|
+
: undefined;
|
|
1282
|
+
return await createAdapterSendToolsRuntimeExtension(this.configPath, this.cwd, effectiveToolNames, indexPath, effectiveInteraction, runOutputRoot)(requestInput);
|
|
1283
|
+
};
|
|
1284
|
+
return { createExtension, blockingToolNames };
|
|
936
1285
|
}
|
|
937
1286
|
/** Build the configured memory store once and share it across responders + the ritual scheduler. */
|
|
938
1287
|
async memoryStore(coreConfig) {
|
|
@@ -964,9 +1313,11 @@ class MonoAgentAppController {
|
|
|
964
1313
|
runEventSink: this.liveEventBus,
|
|
965
1314
|
};
|
|
966
1315
|
this.sharedMemory = await createConfiguredMemory(coreConfig, {
|
|
1316
|
+
cwd: this.cwd,
|
|
967
1317
|
...(logger === undefined ? {} : { logger }),
|
|
968
1318
|
observability,
|
|
969
1319
|
});
|
|
1320
|
+
this.ensureSharedMemoryRetrieval(coreConfig, this.sharedMemory);
|
|
970
1321
|
this.sharedMemoryBuilt = true;
|
|
971
1322
|
return this.sharedMemory;
|
|
972
1323
|
})();
|
|
@@ -981,20 +1332,36 @@ class MonoAgentAppController {
|
|
|
981
1332
|
async resetSharedMemory() {
|
|
982
1333
|
const mem = this.sharedMemory;
|
|
983
1334
|
this.sharedMemory = undefined;
|
|
1335
|
+
this.sharedMemoryRetrieval?.releaseAllTurns();
|
|
1336
|
+
this.sharedMemoryRetrieval = undefined;
|
|
984
1337
|
this.sharedMemoryBuilt = false;
|
|
985
1338
|
this.sharedMemoryBuild = undefined;
|
|
986
|
-
if (mem?.flush !== undefined) {
|
|
987
|
-
await Promise.resolve(mem.flush()).catch(() => undefined);
|
|
988
|
-
}
|
|
989
1339
|
if (mem?.close !== undefined) {
|
|
990
1340
|
await Promise.resolve(mem.close()).catch(() => undefined);
|
|
991
1341
|
}
|
|
1342
|
+
else if (mem?.flush !== undefined) {
|
|
1343
|
+
// Stores without a lifecycle-aware close retain the legacy best-effort
|
|
1344
|
+
// drain. BuJo close owns its bounded shutdown deadline itself.
|
|
1345
|
+
await Promise.resolve(mem.flush()).catch(() => undefined);
|
|
1346
|
+
}
|
|
992
1347
|
}
|
|
993
1348
|
/** @internal Test-only seam: seed the shared memory store without going through config. */
|
|
994
1349
|
__setSharedMemoryForTest(store) {
|
|
995
1350
|
this.sharedMemory = store;
|
|
1351
|
+
this.sharedMemoryRetrieval = undefined;
|
|
996
1352
|
this.sharedMemoryBuilt = true;
|
|
997
1353
|
}
|
|
1354
|
+
ensureSharedMemoryRetrieval(coreConfig, store) {
|
|
1355
|
+
if (this.sharedMemoryRetrieval !== undefined)
|
|
1356
|
+
return this.sharedMemoryRetrieval;
|
|
1357
|
+
if (coreConfig.memory === undefined || !isSharedRecallStore(store))
|
|
1358
|
+
return undefined;
|
|
1359
|
+
this.sharedMemoryRetrieval = new MemoryRetrievalService(store, {
|
|
1360
|
+
maxBytes: coreConfig.memory.maxBytes,
|
|
1361
|
+
source: (coreConfig.memory.backend ?? "bujo") === "supermemory" ? "supermemory" : "memory-bujo",
|
|
1362
|
+
});
|
|
1363
|
+
return this.sharedMemoryRetrieval;
|
|
1364
|
+
}
|
|
998
1365
|
setStatus(id, status) {
|
|
999
1366
|
this.statuses.set(id, status);
|
|
1000
1367
|
return status;
|
|
@@ -1033,14 +1400,25 @@ class MonoAgentAppController {
|
|
|
1033
1400
|
};
|
|
1034
1401
|
}
|
|
1035
1402
|
async stopTraceSource(reason) {
|
|
1403
|
+
this.invalidateMemoryHealthRefresh();
|
|
1036
1404
|
const traceSource = this.traceSource;
|
|
1037
1405
|
const globalTraceSource = this.globalTraceSource;
|
|
1406
|
+
// Detach before stopping. A health probe may be stalled indefinitely; its
|
|
1407
|
+
// generation/handle checks fence any late result, while a reload is free to
|
|
1408
|
+
// register and refresh a new source without waiting for the old probe.
|
|
1409
|
+
this.traceSource = undefined;
|
|
1410
|
+
this.globalTraceSource = undefined;
|
|
1411
|
+
this.traceRefreshInFlight = undefined;
|
|
1412
|
+
this.traceRefreshDirty = false;
|
|
1413
|
+
this.traceRefreshLatestReason = undefined;
|
|
1038
1414
|
if (traceSource === undefined && globalTraceSource === undefined) {
|
|
1039
1415
|
return;
|
|
1040
1416
|
}
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1417
|
+
const patch = {
|
|
1418
|
+
metadata: this.traceMetadata(reason),
|
|
1419
|
+
transports: this.activeTransports(),
|
|
1420
|
+
memoryHealth: this.memoryHealthValue,
|
|
1421
|
+
};
|
|
1044
1422
|
await traceSource?.stop(patch).catch((error) => {
|
|
1045
1423
|
this.logger?.warn?.("Traceability source stop update failed.", { reason: reasonOf(error) });
|
|
1046
1424
|
});
|
|
@@ -1163,6 +1541,81 @@ class MonoAgentAppController {
|
|
|
1163
1541
|
};
|
|
1164
1542
|
}
|
|
1165
1543
|
}
|
|
1544
|
+
function traceMemoryHealthFromBujo(report) {
|
|
1545
|
+
return {
|
|
1546
|
+
backend: "bujo",
|
|
1547
|
+
mode: report.mode,
|
|
1548
|
+
status: traceMemoryStatus(report.status),
|
|
1549
|
+
checkedAt: report.checkedAt,
|
|
1550
|
+
issues: report.issues.map(traceMemoryIssue),
|
|
1551
|
+
counts: {
|
|
1552
|
+
pending: report.counts.pending,
|
|
1553
|
+
due: report.counts.due,
|
|
1554
|
+
dead: report.counts.dead,
|
|
1555
|
+
outbox: report.counts.outbox,
|
|
1556
|
+
temporary: report.counts.temporary,
|
|
1557
|
+
memories: report.counts.memories,
|
|
1558
|
+
vectors: report.counts.vectors,
|
|
1559
|
+
missingVectors: report.counts.missingVectors,
|
|
1560
|
+
},
|
|
1561
|
+
};
|
|
1562
|
+
}
|
|
1563
|
+
function traceMemoryStatus(status) {
|
|
1564
|
+
switch (status) {
|
|
1565
|
+
case "healthy":
|
|
1566
|
+
case "in_progress":
|
|
1567
|
+
case "degraded":
|
|
1568
|
+
case "unhealthy":
|
|
1569
|
+
case "unknown":
|
|
1570
|
+
return status;
|
|
1571
|
+
}
|
|
1572
|
+
}
|
|
1573
|
+
function traceMemoryIssue(issue) {
|
|
1574
|
+
switch (issue) {
|
|
1575
|
+
case "manifest_missing":
|
|
1576
|
+
case "manifest_invalid":
|
|
1577
|
+
case "configured_identity_mismatch":
|
|
1578
|
+
case "database_missing":
|
|
1579
|
+
case "database_unavailable":
|
|
1580
|
+
case "native_module_unavailable":
|
|
1581
|
+
case "health_check_failed":
|
|
1582
|
+
case "sqlite_integrity_failed":
|
|
1583
|
+
case "metadata_mismatch":
|
|
1584
|
+
case "fts_mismatch":
|
|
1585
|
+
case "vector_mismatch":
|
|
1586
|
+
case "orphaned_rows":
|
|
1587
|
+
case "canonical_mismatch":
|
|
1588
|
+
case "canonical_invalid":
|
|
1589
|
+
case "mutation_in_progress":
|
|
1590
|
+
case "intake_invalid":
|
|
1591
|
+
case "intake_pending":
|
|
1592
|
+
case "dead_letters":
|
|
1593
|
+
case "outbox_invalid":
|
|
1594
|
+
case "outbox_pending":
|
|
1595
|
+
case "work_stalled":
|
|
1596
|
+
case "temporary_artifacts":
|
|
1597
|
+
case "runtime_missing":
|
|
1598
|
+
case "runtime_stale":
|
|
1599
|
+
case "runtime_invalid":
|
|
1600
|
+
return issue;
|
|
1601
|
+
}
|
|
1602
|
+
}
|
|
1603
|
+
function unknownNoMemoryHealth() {
|
|
1604
|
+
return {
|
|
1605
|
+
backend: "none",
|
|
1606
|
+
status: "unknown",
|
|
1607
|
+
checkedAt: new Date().toISOString(),
|
|
1608
|
+
};
|
|
1609
|
+
}
|
|
1610
|
+
function unknownBujoMemoryHealth(mode) {
|
|
1611
|
+
return {
|
|
1612
|
+
backend: "bujo",
|
|
1613
|
+
mode,
|
|
1614
|
+
status: "unknown",
|
|
1615
|
+
checkedAt: new Date().toISOString(),
|
|
1616
|
+
issues: ["health_check_failed"],
|
|
1617
|
+
};
|
|
1618
|
+
}
|
|
1166
1619
|
function nextDailyRolloverAt(now, timezone) {
|
|
1167
1620
|
if (timezone === undefined || timezone.trim().length === 0) {
|
|
1168
1621
|
const next = new Date(now);
|
|
@@ -1229,71 +1682,11 @@ function sandboxStatusFromState(state) {
|
|
|
1229
1682
|
function reasonOf(error) {
|
|
1230
1683
|
return error instanceof Error ? error.message : String(error);
|
|
1231
1684
|
}
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
* `undefined` when no extension is active, so the host omits `runtimeOptionsForRequest` entirely.
|
|
1236
|
-
*/
|
|
1237
|
-
function composeRuntimeOptionExtensions(extensions) {
|
|
1238
|
-
const active = extensions.filter((extension) => extension !== undefined);
|
|
1239
|
-
if (active.length === 0) {
|
|
1240
|
-
return undefined;
|
|
1241
|
-
}
|
|
1242
|
-
if (active.length === 1) {
|
|
1243
|
-
return active[0];
|
|
1244
|
-
}
|
|
1245
|
-
return async (input) => {
|
|
1246
|
-
const results = await Promise.all(active.map((extension) => extension(input)));
|
|
1247
|
-
const runtimeOptions = {};
|
|
1248
|
-
for (const result of results) {
|
|
1249
|
-
mergeRuntimeOptions(runtimeOptions, result.runtimeOptions);
|
|
1250
|
-
}
|
|
1251
|
-
return {
|
|
1252
|
-
runtimeOptions,
|
|
1253
|
-
cleanup: async () => {
|
|
1254
|
-
// Chain every cleanup; run them all even if one rejects so no extension leaks resources.
|
|
1255
|
-
await Promise.all(results.map(async (result) => result.cleanup?.()));
|
|
1256
|
-
},
|
|
1257
|
-
};
|
|
1258
|
-
};
|
|
1259
|
-
}
|
|
1260
|
-
function mergeRuntimeOptions(target, next) {
|
|
1261
|
-
if (next === undefined) {
|
|
1262
|
-
return;
|
|
1263
|
-
}
|
|
1264
|
-
for (const [key, value] of Object.entries(next)) {
|
|
1265
|
-
if (value === undefined) {
|
|
1266
|
-
continue;
|
|
1267
|
-
}
|
|
1268
|
-
if (key === "allowedTools" || key === "disallowedTools") {
|
|
1269
|
-
target[key] = mergeStringLists(target[key], value);
|
|
1270
|
-
continue;
|
|
1271
|
-
}
|
|
1272
|
-
if (key === "mcpServers") {
|
|
1273
|
-
target[key] = {
|
|
1274
|
-
...(isRecord(target[key]) ? target[key] : {}),
|
|
1275
|
-
...(isRecord(value) ? value : {}),
|
|
1276
|
-
};
|
|
1277
|
-
continue;
|
|
1278
|
-
}
|
|
1279
|
-
target[key] = value;
|
|
1280
|
-
}
|
|
1281
|
-
}
|
|
1282
|
-
function mergeStringLists(current, next) {
|
|
1283
|
-
const out = [];
|
|
1284
|
-
for (const list of [current, next]) {
|
|
1285
|
-
if (!Array.isArray(list)) {
|
|
1286
|
-
continue;
|
|
1287
|
-
}
|
|
1288
|
-
for (const item of list) {
|
|
1289
|
-
if (typeof item === "string" && !out.includes(item)) {
|
|
1290
|
-
out.push(item);
|
|
1291
|
-
}
|
|
1292
|
-
}
|
|
1293
|
-
}
|
|
1294
|
-
return out;
|
|
1685
|
+
function runtimeRouteContainsDirectOpenCode(config) {
|
|
1686
|
+
return configuredRuntimeModels(config.runtime)
|
|
1687
|
+
.some((model) => model.sdk === "opencode");
|
|
1295
1688
|
}
|
|
1296
|
-
function
|
|
1297
|
-
return
|
|
1689
|
+
function isInteractionToolName(name) {
|
|
1690
|
+
return name === "AskUser" || name === "TelegramAskButtons";
|
|
1298
1691
|
}
|
|
1299
1692
|
//# sourceMappingURL=app.js.map
|