nemoris 0.1.0 → 0.1.2
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/.env.example +49 -49
- package/LICENSE +21 -21
- package/README.md +209 -209
- package/SECURITY.md +59 -119
- package/bin/nemoris +46 -46
- package/config/agents/agent.toml.example +28 -28
- package/config/agents/content.toml +23 -0
- package/config/agents/default.toml +22 -22
- package/config/agents/heartbeat.toml +35 -0
- package/config/agents/iris.toml +23 -0
- package/config/agents/lab.toml +23 -0
- package/config/agents/main.toml +45 -0
- package/config/agents/nemo.toml +21 -0
- package/config/agents/ops.toml +38 -0
- package/config/agents/orchestrator.toml +18 -18
- package/config/agents/revenue.toml +23 -0
- package/config/agents/testyboo.toml +19 -0
- package/config/delivery.toml +73 -73
- package/config/embeddings.toml +5 -5
- package/config/identity/content-purpose.md +11 -0
- package/config/identity/content-soul.md +45 -0
- package/config/identity/default-purpose.md +1 -1
- package/config/identity/default-soul.md +3 -3
- package/config/identity/heartbeat-purpose.md +9 -0
- package/config/identity/heartbeat-soul.md +16 -0
- package/config/identity/iris-purpose.md +17 -0
- package/config/identity/iris-soul.md +68 -0
- package/config/identity/lab-purpose.md +10 -0
- package/config/identity/lab-soul.md +38 -0
- package/config/identity/main-purpose.md +17 -0
- package/config/identity/main-soul.md +66 -0
- package/config/identity/main-user.md +22 -0
- package/config/identity/ops-purpose.md +9 -0
- package/config/identity/ops-soul.md +16 -0
- package/config/identity/orchestrator-purpose.md +1 -1
- package/config/identity/orchestrator-soul.md +1 -1
- package/config/identity/revenue-purpose.md +9 -0
- package/config/identity/revenue-soul.md +41 -0
- package/config/identity/testyboo-purpose.md +13 -0
- package/config/identity/testyboo-soul.md +20 -0
- package/config/improvement-targets.toml +15 -15
- package/config/jobs/heartbeat-check.toml +30 -30
- package/config/jobs/memory-rollup.toml +46 -46
- package/config/jobs/workspace-health.toml +63 -63
- package/config/mcp.toml +16 -16
- package/config/output-contracts.toml +17 -17
- package/config/peers.toml +32 -32
- package/config/peers.toml.example +32 -32
- package/config/policies/memory-default.toml +10 -10
- package/config/policies/memory-heartbeat.toml +5 -5
- package/config/policies/memory-ops.toml +10 -10
- package/config/policies/tools-heartbeat-minimal.toml +8 -8
- package/config/policies/tools-interactive-safe.toml +8 -8
- package/config/policies/tools-ops-bounded.toml +8 -8
- package/config/policies/tools-orchestrator.toml +7 -7
- package/config/providers/anthropic.toml +15 -15
- package/config/providers/ollama.toml +5 -5
- package/config/providers/openai-codex.toml +9 -9
- package/config/providers/openrouter.toml +5 -5
- package/config/router.toml +22 -22
- package/config/runtime.toml +114 -114
- package/config/skills/self-improvement.toml +15 -15
- package/config/skills/telegram-onboarding-spec.md +240 -240
- package/config/skills/workspace-monitor.toml +15 -15
- package/config/task-router.toml +42 -42
- package/install.sh +50 -50
- package/package.json +91 -90
- package/src/auth/auth-profiles.js +169 -169
- package/src/auth/openai-codex-oauth.js +285 -285
- package/src/battle.js +449 -449
- package/src/cli/help.js +265 -265
- package/src/cli/output-filter.js +49 -49
- package/src/cli/runtime-control.js +704 -704
- package/src/cli-main.js +2763 -2763
- package/src/cli.js +78 -78
- package/src/config/loader.js +332 -332
- package/src/config/schema-validator.js +214 -214
- package/src/config/toml-lite.js +8 -8
- package/src/daemon/action-handlers.js +71 -71
- package/src/daemon/healing-tick.js +87 -87
- package/src/daemon/health-probes.js +90 -90
- package/src/daemon/notifier.js +57 -57
- package/src/daemon/nurse.js +218 -218
- package/src/daemon/repair-log.js +106 -106
- package/src/daemon/rule-staging.js +90 -90
- package/src/daemon/rules.js +29 -29
- package/src/daemon/telegram-commands.js +54 -54
- package/src/daemon/updater.js +85 -85
- package/src/jobs/job-runner.js +78 -78
- package/src/mcp/consumer.js +129 -129
- package/src/memory/active-recall.js +171 -171
- package/src/memory/backend-manager.js +97 -97
- package/src/memory/backends/file-backend.js +38 -38
- package/src/memory/backends/qmd-backend.js +219 -219
- package/src/memory/embedding-guards.js +24 -24
- package/src/memory/embedding-index.js +118 -118
- package/src/memory/embedding-service.js +179 -179
- package/src/memory/file-index.js +177 -177
- package/src/memory/memory-signature.js +5 -5
- package/src/memory/memory-store.js +648 -648
- package/src/memory/retrieval-planner.js +66 -66
- package/src/memory/scoring.js +145 -145
- package/src/memory/simhash.js +78 -78
- package/src/memory/sqlite-active-store.js +824 -824
- package/src/memory/write-policy.js +36 -36
- package/src/onboarding/aliases.js +33 -33
- package/src/onboarding/auth/api-key.js +224 -224
- package/src/onboarding/auth/ollama-detect.js +42 -42
- package/src/onboarding/clack-prompter.js +77 -77
- package/src/onboarding/doctor.js +530 -530
- package/src/onboarding/lock.js +42 -42
- package/src/onboarding/model-catalog.js +344 -344
- package/src/onboarding/phases/auth.js +576 -589
- package/src/onboarding/phases/build.js +130 -130
- package/src/onboarding/phases/choose.js +82 -82
- package/src/onboarding/phases/detect.js +98 -98
- package/src/onboarding/phases/hatch.js +216 -216
- package/src/onboarding/phases/identity.js +79 -79
- package/src/onboarding/phases/ollama.js +345 -345
- package/src/onboarding/phases/scaffold.js +99 -99
- package/src/onboarding/phases/telegram.js +377 -377
- package/src/onboarding/phases/validate.js +204 -204
- package/src/onboarding/phases/verify.js +206 -206
- package/src/onboarding/platform.js +482 -482
- package/src/onboarding/status-bar.js +95 -95
- package/src/onboarding/templates.js +794 -794
- package/src/onboarding/toml-writer.js +38 -38
- package/src/onboarding/tui.js +250 -250
- package/src/onboarding/uninstall.js +153 -153
- package/src/onboarding/wizard.js +516 -499
- package/src/providers/anthropic.js +168 -168
- package/src/providers/base.js +247 -247
- package/src/providers/circuit-breaker.js +136 -136
- package/src/providers/ollama.js +163 -163
- package/src/providers/openai-codex.js +149 -149
- package/src/providers/openrouter.js +136 -136
- package/src/providers/registry.js +36 -36
- package/src/providers/router.js +16 -16
- package/src/runtime/bootstrap-cache.js +47 -47
- package/src/runtime/capabilities-prompt.js +25 -25
- package/src/runtime/completion-ping.js +99 -99
- package/src/runtime/config-validator.js +121 -121
- package/src/runtime/context-ledger.js +360 -360
- package/src/runtime/cutover-readiness.js +42 -42
- package/src/runtime/daemon.js +729 -729
- package/src/runtime/delivery-ack.js +195 -195
- package/src/runtime/delivery-adapters/local-file.js +41 -41
- package/src/runtime/delivery-adapters/openclaw-cli.js +94 -94
- package/src/runtime/delivery-adapters/openclaw-peer.js +98 -98
- package/src/runtime/delivery-adapters/shadow.js +13 -13
- package/src/runtime/delivery-adapters/standalone-http.js +98 -98
- package/src/runtime/delivery-adapters/telegram.js +104 -104
- package/src/runtime/delivery-adapters/tui.js +128 -128
- package/src/runtime/delivery-manager.js +807 -807
- package/src/runtime/delivery-store.js +168 -168
- package/src/runtime/dependency-health.js +118 -118
- package/src/runtime/envelope.js +114 -114
- package/src/runtime/evaluation.js +1089 -1089
- package/src/runtime/exec-approvals.js +216 -216
- package/src/runtime/executor.js +500 -500
- package/src/runtime/failure-ping.js +67 -67
- package/src/runtime/flows.js +83 -83
- package/src/runtime/guards.js +45 -45
- package/src/runtime/handoff.js +51 -51
- package/src/runtime/identity-cache.js +28 -28
- package/src/runtime/improvement-engine.js +109 -109
- package/src/runtime/improvement-harness.js +581 -581
- package/src/runtime/input-sanitiser.js +72 -72
- package/src/runtime/interaction-contract.js +347 -347
- package/src/runtime/lane-readiness.js +226 -226
- package/src/runtime/migration.js +323 -323
- package/src/runtime/model-resolution.js +78 -78
- package/src/runtime/network.js +64 -64
- package/src/runtime/notification-store.js +97 -97
- package/src/runtime/notifier.js +256 -256
- package/src/runtime/orchestrator.js +53 -53
- package/src/runtime/orphan-reaper.js +41 -41
- package/src/runtime/output-contract-schema.js +139 -139
- package/src/runtime/output-contract-validator.js +439 -439
- package/src/runtime/peer-readiness.js +69 -69
- package/src/runtime/peer-registry.js +133 -133
- package/src/runtime/pilot-status.js +108 -108
- package/src/runtime/prompt-builder.js +261 -261
- package/src/runtime/provider-attempt.js +582 -582
- package/src/runtime/report-fallback.js +71 -71
- package/src/runtime/result-normalizer.js +183 -183
- package/src/runtime/retention.js +74 -74
- package/src/runtime/review.js +244 -244
- package/src/runtime/route-job.js +15 -15
- package/src/runtime/run-store.js +38 -38
- package/src/runtime/schedule.js +88 -88
- package/src/runtime/scheduler-state.js +434 -434
- package/src/runtime/scheduler.js +656 -656
- package/src/runtime/session-compactor.js +182 -182
- package/src/runtime/session-search.js +155 -155
- package/src/runtime/slack-inbound.js +249 -249
- package/src/runtime/ssrf.js +102 -102
- package/src/runtime/status-aggregator.js +330 -330
- package/src/runtime/task-contract.js +140 -140
- package/src/runtime/task-packet.js +107 -107
- package/src/runtime/task-router.js +140 -140
- package/src/runtime/telegram-inbound.js +1565 -1565
- package/src/runtime/token-counter.js +134 -134
- package/src/runtime/token-estimator.js +59 -59
- package/src/runtime/tool-loop.js +200 -200
- package/src/runtime/transport-server.js +311 -311
- package/src/runtime/tui-server.js +411 -411
- package/src/runtime/ulid.js +44 -44
- package/src/security/ssrf-check.js +197 -197
- package/src/setup.js +369 -369
- package/src/shadow/bridge.js +303 -303
- package/src/skills/loader.js +84 -84
- package/src/tools/catalog.json +49 -49
- package/src/tools/cli-delegate.js +44 -44
- package/src/tools/mcp-client.js +106 -106
- package/src/tools/micro/cancel-task.js +6 -6
- package/src/tools/micro/complete-task.js +6 -6
- package/src/tools/micro/fail-task.js +6 -6
- package/src/tools/micro/http-fetch.js +74 -74
- package/src/tools/micro/index.js +36 -36
- package/src/tools/micro/lcm-recall.js +60 -60
- package/src/tools/micro/list-dir.js +17 -17
- package/src/tools/micro/list-skills.js +46 -46
- package/src/tools/micro/load-skill.js +38 -38
- package/src/tools/micro/memory-search.js +45 -45
- package/src/tools/micro/read-file.js +11 -11
- package/src/tools/micro/session-search.js +54 -54
- package/src/tools/micro/shell-exec.js +43 -43
- package/src/tools/micro/trigger-job.js +79 -79
- package/src/tools/micro/web-search.js +58 -58
- package/src/tools/micro/workspace-paths.js +39 -39
- package/src/tools/micro/write-file.js +14 -14
- package/src/tools/micro/write-memory.js +41 -41
- package/src/tools/registry.js +348 -348
- package/src/tools/tool-result-contract.js +36 -36
- package/src/tui/chat.js +835 -835
- package/src/tui/renderer.js +175 -175
- package/src/tui/socket-client.js +217 -217
- package/src/utils/canonical-json.js +29 -29
- package/src/utils/compaction.js +30 -30
- package/src/utils/env-loader.js +5 -5
- package/src/utils/errors.js +80 -80
- package/src/utils/fs.js +101 -101
- package/src/utils/ids.js +5 -5
- package/src/utils/model-context-limits.js +30 -30
- package/src/utils/token-budget.js +74 -74
- package/src/utils/usage-cost.js +25 -25
- package/src/utils/usage-metrics.js +14 -14
|
@@ -1,226 +1,226 @@
|
|
|
1
|
-
function truthy(value) {
|
|
2
|
-
return value === true;
|
|
3
|
-
}
|
|
4
|
-
|
|
5
|
-
function hasBlockingInteractionIssues(interaction = null) {
|
|
6
|
-
if (!interaction) return false;
|
|
7
|
-
return Boolean(
|
|
8
|
-
(interaction.deliveryEvidenceRequired && !interaction.deliveryEvidenceHealthy) ||
|
|
9
|
-
(interaction.yielded && interaction.followUpQueued && !interaction.followUpConsumed) ||
|
|
10
|
-
interaction.followUpExpired ||
|
|
11
|
-
interaction.handoffExpired ||
|
|
12
|
-
interaction.handoffBlocked ||
|
|
13
|
-
(interaction.handoffRequired && interaction.handoffQueued && !interaction.handoffChosen && !interaction.handoffPendingChoice) ||
|
|
14
|
-
interaction.deliveryUncertain
|
|
15
|
-
);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
function summarizeFallback(reportFallback = null) {
|
|
19
|
-
if (!reportFallback) {
|
|
20
|
-
return {
|
|
21
|
-
configured: false,
|
|
22
|
-
ready: true,
|
|
23
|
-
blockedReasons: []
|
|
24
|
-
};
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
return {
|
|
28
|
-
configured: Boolean(reportFallback.jobConfigured),
|
|
29
|
-
ready: Boolean(!reportFallback.jobConfigured || reportFallback.ready),
|
|
30
|
-
blockedReasons: reportFallback.blockedReasons || []
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
function isStaleError(health) {
|
|
35
|
-
if (!health.lastError) return false;
|
|
36
|
-
if (!health.lastSuccessAt) return false;
|
|
37
|
-
const successMs = new Date(health.lastSuccessAt).getTime();
|
|
38
|
-
const errorMs = health.lastErrorAt ? new Date(health.lastErrorAt).getTime() : 0;
|
|
39
|
-
return successMs > errorMs;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function summarizeEmbeddings(embeddingHealth = null) {
|
|
43
|
-
const health = embeddingHealth?.embeddingHealth || embeddingHealth || null;
|
|
44
|
-
if (!health) {
|
|
45
|
-
return {
|
|
46
|
-
available: false,
|
|
47
|
-
healthy: false,
|
|
48
|
-
degraded: true,
|
|
49
|
-
blockedReasons: ["embedding_health_missing"]
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const blockedReasons = [];
|
|
54
|
-
if (health.disabled) blockedReasons.push("embeddings_disabled");
|
|
55
|
-
if (health.lastError && !isStaleError(health)) blockedReasons.push("embedding_error");
|
|
56
|
-
if (health.missingCount > 0 && health.freshCount === 0) blockedReasons.push("embeddings_missing");
|
|
57
|
-
if (health.failedCount > 0) blockedReasons.push("embedding_failures");
|
|
58
|
-
|
|
59
|
-
return {
|
|
60
|
-
available: !health.disabled,
|
|
61
|
-
healthy: blockedReasons.length === 0,
|
|
62
|
-
degraded: blockedReasons.length > 0,
|
|
63
|
-
blockedReasons,
|
|
64
|
-
health
|
|
65
|
-
};
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
function reconcileEmbeddingsWithRetrieval(embeddings, retrieval) {
|
|
69
|
-
if (!embeddings) return embeddings;
|
|
70
|
-
if (!retrieval) return embeddings;
|
|
71
|
-
|
|
72
|
-
const retrievalShowsHealthyEmbeddingQuery =
|
|
73
|
-
retrieval.embeddingQueryMode === "embedding_query" &&
|
|
74
|
-
Number(retrieval.semanticCount || 0) > 0 &&
|
|
75
|
-
!retrieval.embeddingError &&
|
|
76
|
-
Number(retrieval.failedEmbeddingCount || 0) === 0;
|
|
77
|
-
|
|
78
|
-
if (!retrievalShowsHealthyEmbeddingQuery) {
|
|
79
|
-
return embeddings;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
const blockedReasons = (embeddings.blockedReasons || []).filter((reason) => reason !== "embedding_error");
|
|
83
|
-
return {
|
|
84
|
-
...embeddings,
|
|
85
|
-
healthy: blockedReasons.length === 0,
|
|
86
|
-
degraded: blockedReasons.length > 0,
|
|
87
|
-
blockedReasons
|
|
88
|
-
};
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
function buildEffectiveRetrieval(retrieval = null, embeddings = null) {
|
|
92
|
-
if (!retrieval) return null;
|
|
93
|
-
|
|
94
|
-
const effective = { ...retrieval };
|
|
95
|
-
const embeddingHealth = embeddings?.health || null;
|
|
96
|
-
|
|
97
|
-
// Only adopt the embedding health's lastQueryMode when the evaluation's
|
|
98
|
-
// own retrieval doesn't already show a healthy semantic result. This
|
|
99
|
-
// prevents a stale "lexical_fallback" from overwriting a genuinely
|
|
100
|
-
// healthy "embedding_query" captured during the most recent run.
|
|
101
|
-
const evalAlreadyHealthy =
|
|
102
|
-
retrieval.embeddingQueryMode === "embedding_query" &&
|
|
103
|
-
Number(retrieval.semanticCount || 0) > 0 &&
|
|
104
|
-
!retrieval.embeddingError;
|
|
105
|
-
|
|
106
|
-
if (embeddingHealth?.lastQueryMode && !evalAlreadyHealthy) {
|
|
107
|
-
effective.embeddingQueryMode = embeddingHealth.lastQueryMode;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
if (embeddings?.healthy && !embeddings.degraded) {
|
|
111
|
-
effective.embeddingError = null;
|
|
112
|
-
effective.failedEmbeddingCount = 0;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
return effective;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
export function buildLaneReadiness({
|
|
119
|
-
jobId,
|
|
120
|
-
job = null,
|
|
121
|
-
evaluation = null,
|
|
122
|
-
embeddingHealth = null,
|
|
123
|
-
reportFallback = null,
|
|
124
|
-
maintenance = null,
|
|
125
|
-
dependencyHealth = null
|
|
126
|
-
} = {}) {
|
|
127
|
-
const interaction = evaluation?.interaction || null;
|
|
128
|
-
const retrieval = evaluation?.retrieval || null;
|
|
129
|
-
const contractCheck = evaluation?.contractCheck || null;
|
|
130
|
-
const rubric = evaluation?.rubric || null;
|
|
131
|
-
const embeddingSummary = summarizeEmbeddings(embeddingHealth);
|
|
132
|
-
const effectiveRetrieval = buildEffectiveRetrieval(retrieval, embeddingSummary);
|
|
133
|
-
const embeddings = reconcileEmbeddingsWithRetrieval(embeddingSummary, effectiveRetrieval);
|
|
134
|
-
const fallback = summarizeFallback(reportFallback);
|
|
135
|
-
|
|
136
|
-
const checks = {
|
|
137
|
-
contractHealthy:
|
|
138
|
-
contractCheck == null
|
|
139
|
-
? true
|
|
140
|
-
: Number(contractCheck.satisfiedRatio ?? 0) >= 0.99 && (contractCheck.missingFromV2 || []).length === 0,
|
|
141
|
-
interactionHealthy: !hasBlockingInteractionIssues(interaction),
|
|
142
|
-
retrievalHealthy:
|
|
143
|
-
!effectiveRetrieval ||
|
|
144
|
-
(!effectiveRetrieval.embeddingError &&
|
|
145
|
-
effectiveRetrieval.embeddingQueryMode !== "lexical_fallback" &&
|
|
146
|
-
Number(effectiveRetrieval.failedEmbeddingCount || 0) === 0),
|
|
147
|
-
daemonHealthy:
|
|
148
|
-
!maintenance ||
|
|
149
|
-
((maintenance.handoffs?.expiredCount || 0) === 0 && (maintenance.followUps?.expiredCount || 0) === 0),
|
|
150
|
-
providerReachable:
|
|
151
|
-
!dependencyHealth || dependencyHealth.provider?.reachable !== false,
|
|
152
|
-
deliveryReachable:
|
|
153
|
-
!dependencyHealth || dependencyHealth.delivery?.reachable !== false,
|
|
154
|
-
fallbackReady: fallback.ready
|
|
155
|
-
};
|
|
156
|
-
|
|
157
|
-
const blockers = [];
|
|
158
|
-
if (!checks.contractHealthy) blockers.push("output_contract_not_consistently_satisfied");
|
|
159
|
-
if (!checks.interactionHealthy) blockers.push("interaction_lifecycle_has_blocking_issues");
|
|
160
|
-
if (!checks.retrievalHealthy) blockers.push("retrieval_is_degraded_or_falling_back");
|
|
161
|
-
if (!checks.daemonHealthy) blockers.push("daemon_maintenance_detected_expired_items");
|
|
162
|
-
if (!checks.providerReachable) blockers.push("provider_unreachable");
|
|
163
|
-
if (embeddings.degraded) blockers.push(...embeddings.blockedReasons.map((item) => `embeddings:${item}`));
|
|
164
|
-
|
|
165
|
-
const warnings = [];
|
|
166
|
-
if (!checks.deliveryReachable) warnings.push("delivery_not_reachable");
|
|
167
|
-
|
|
168
|
-
const suggestions = [];
|
|
169
|
-
if (!checks.retrievalHealthy) {
|
|
170
|
-
suggestions.push("Repair embeddings or accept lexical-only mode before treating this lane as daily-driver ready.");
|
|
171
|
-
}
|
|
172
|
-
if (!checks.contractHealthy) {
|
|
173
|
-
suggestions.push("Keep contract adherence at 100% before cutover.");
|
|
174
|
-
}
|
|
175
|
-
if (!checks.interactionHealthy) {
|
|
176
|
-
suggestions.push("Resolve follow-up / handoff lifecycle issues before using this lane unattended.");
|
|
177
|
-
}
|
|
178
|
-
if (!checks.providerReachable) {
|
|
179
|
-
suggestions.push("Check provider health before treating this lane as trustworthy.");
|
|
180
|
-
}
|
|
181
|
-
if (!checks.deliveryReachable) {
|
|
182
|
-
suggestions.push("Delivery transport is not reachable. This does not block pilot readiness but should be resolved before daily-driver cutover.");
|
|
183
|
-
}
|
|
184
|
-
if (!checks.fallbackReady && fallback.configured) {
|
|
185
|
-
suggestions.push("Remote fallback is optional for this lane. Enable and validate it only if you want recovery from local failures.");
|
|
186
|
-
}
|
|
187
|
-
if (checks.contractHealthy && checks.interactionHealthy && checks.retrievalHealthy) {
|
|
188
|
-
suggestions.push("This lane is technically close to cutover; focus next on repeat-run validation and operator trust.");
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
let tier = "not_ready";
|
|
192
|
-
if (blockers.length === 0 && Number(rubric?.overallScore || 0) >= 0.95) {
|
|
193
|
-
tier = "ready_for_pilot";
|
|
194
|
-
} else if (blockers.length <= 2 && Number(rubric?.overallScore || 0) >= 0.85) {
|
|
195
|
-
tier = "close";
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
return {
|
|
199
|
-
jobId,
|
|
200
|
-
taskType: job?.taskType || null,
|
|
201
|
-
primaryLane: job?.modelLane || null,
|
|
202
|
-
readinessTier: tier,
|
|
203
|
-
readyForPilot: tier === "ready_for_pilot",
|
|
204
|
-
checks,
|
|
205
|
-
blockers: [...new Set(blockers)],
|
|
206
|
-
warnings,
|
|
207
|
-
suggestions,
|
|
208
|
-
summary: {
|
|
209
|
-
overallScore: rubric?.overallScore ?? null,
|
|
210
|
-
contractSatisfiedRatio: contractCheck?.satisfiedRatio ?? null,
|
|
211
|
-
yielded: truthy(interaction?.yielded),
|
|
212
|
-
followUpConsumed: truthy(interaction?.followUpConsumed),
|
|
213
|
-
handoffDelivered: truthy(interaction?.handoffDelivered),
|
|
214
|
-
embeddingQueryMode: effectiveRetrieval?.embeddingQueryMode || null,
|
|
215
|
-
semanticCount: effectiveRetrieval?.semanticCount ?? 0,
|
|
216
|
-
fallbackConfigured: fallback.configured,
|
|
217
|
-
fallbackReady: fallback.ready
|
|
218
|
-
},
|
|
219
|
-
interaction,
|
|
220
|
-
retrieval: effectiveRetrieval,
|
|
221
|
-
embeddings,
|
|
222
|
-
fallback,
|
|
223
|
-
maintenance,
|
|
224
|
-
dependencyHealth
|
|
225
|
-
};
|
|
226
|
-
}
|
|
1
|
+
function truthy(value) {
|
|
2
|
+
return value === true;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
function hasBlockingInteractionIssues(interaction = null) {
|
|
6
|
+
if (!interaction) return false;
|
|
7
|
+
return Boolean(
|
|
8
|
+
(interaction.deliveryEvidenceRequired && !interaction.deliveryEvidenceHealthy) ||
|
|
9
|
+
(interaction.yielded && interaction.followUpQueued && !interaction.followUpConsumed) ||
|
|
10
|
+
interaction.followUpExpired ||
|
|
11
|
+
interaction.handoffExpired ||
|
|
12
|
+
interaction.handoffBlocked ||
|
|
13
|
+
(interaction.handoffRequired && interaction.handoffQueued && !interaction.handoffChosen && !interaction.handoffPendingChoice) ||
|
|
14
|
+
interaction.deliveryUncertain
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function summarizeFallback(reportFallback = null) {
|
|
19
|
+
if (!reportFallback) {
|
|
20
|
+
return {
|
|
21
|
+
configured: false,
|
|
22
|
+
ready: true,
|
|
23
|
+
blockedReasons: []
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return {
|
|
28
|
+
configured: Boolean(reportFallback.jobConfigured),
|
|
29
|
+
ready: Boolean(!reportFallback.jobConfigured || reportFallback.ready),
|
|
30
|
+
blockedReasons: reportFallback.blockedReasons || []
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function isStaleError(health) {
|
|
35
|
+
if (!health.lastError) return false;
|
|
36
|
+
if (!health.lastSuccessAt) return false;
|
|
37
|
+
const successMs = new Date(health.lastSuccessAt).getTime();
|
|
38
|
+
const errorMs = health.lastErrorAt ? new Date(health.lastErrorAt).getTime() : 0;
|
|
39
|
+
return successMs > errorMs;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function summarizeEmbeddings(embeddingHealth = null) {
|
|
43
|
+
const health = embeddingHealth?.embeddingHealth || embeddingHealth || null;
|
|
44
|
+
if (!health) {
|
|
45
|
+
return {
|
|
46
|
+
available: false,
|
|
47
|
+
healthy: false,
|
|
48
|
+
degraded: true,
|
|
49
|
+
blockedReasons: ["embedding_health_missing"]
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const blockedReasons = [];
|
|
54
|
+
if (health.disabled) blockedReasons.push("embeddings_disabled");
|
|
55
|
+
if (health.lastError && !isStaleError(health)) blockedReasons.push("embedding_error");
|
|
56
|
+
if (health.missingCount > 0 && health.freshCount === 0) blockedReasons.push("embeddings_missing");
|
|
57
|
+
if (health.failedCount > 0) blockedReasons.push("embedding_failures");
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
available: !health.disabled,
|
|
61
|
+
healthy: blockedReasons.length === 0,
|
|
62
|
+
degraded: blockedReasons.length > 0,
|
|
63
|
+
blockedReasons,
|
|
64
|
+
health
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function reconcileEmbeddingsWithRetrieval(embeddings, retrieval) {
|
|
69
|
+
if (!embeddings) return embeddings;
|
|
70
|
+
if (!retrieval) return embeddings;
|
|
71
|
+
|
|
72
|
+
const retrievalShowsHealthyEmbeddingQuery =
|
|
73
|
+
retrieval.embeddingQueryMode === "embedding_query" &&
|
|
74
|
+
Number(retrieval.semanticCount || 0) > 0 &&
|
|
75
|
+
!retrieval.embeddingError &&
|
|
76
|
+
Number(retrieval.failedEmbeddingCount || 0) === 0;
|
|
77
|
+
|
|
78
|
+
if (!retrievalShowsHealthyEmbeddingQuery) {
|
|
79
|
+
return embeddings;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const blockedReasons = (embeddings.blockedReasons || []).filter((reason) => reason !== "embedding_error");
|
|
83
|
+
return {
|
|
84
|
+
...embeddings,
|
|
85
|
+
healthy: blockedReasons.length === 0,
|
|
86
|
+
degraded: blockedReasons.length > 0,
|
|
87
|
+
blockedReasons
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function buildEffectiveRetrieval(retrieval = null, embeddings = null) {
|
|
92
|
+
if (!retrieval) return null;
|
|
93
|
+
|
|
94
|
+
const effective = { ...retrieval };
|
|
95
|
+
const embeddingHealth = embeddings?.health || null;
|
|
96
|
+
|
|
97
|
+
// Only adopt the embedding health's lastQueryMode when the evaluation's
|
|
98
|
+
// own retrieval doesn't already show a healthy semantic result. This
|
|
99
|
+
// prevents a stale "lexical_fallback" from overwriting a genuinely
|
|
100
|
+
// healthy "embedding_query" captured during the most recent run.
|
|
101
|
+
const evalAlreadyHealthy =
|
|
102
|
+
retrieval.embeddingQueryMode === "embedding_query" &&
|
|
103
|
+
Number(retrieval.semanticCount || 0) > 0 &&
|
|
104
|
+
!retrieval.embeddingError;
|
|
105
|
+
|
|
106
|
+
if (embeddingHealth?.lastQueryMode && !evalAlreadyHealthy) {
|
|
107
|
+
effective.embeddingQueryMode = embeddingHealth.lastQueryMode;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (embeddings?.healthy && !embeddings.degraded) {
|
|
111
|
+
effective.embeddingError = null;
|
|
112
|
+
effective.failedEmbeddingCount = 0;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return effective;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export function buildLaneReadiness({
|
|
119
|
+
jobId,
|
|
120
|
+
job = null,
|
|
121
|
+
evaluation = null,
|
|
122
|
+
embeddingHealth = null,
|
|
123
|
+
reportFallback = null,
|
|
124
|
+
maintenance = null,
|
|
125
|
+
dependencyHealth = null
|
|
126
|
+
} = {}) {
|
|
127
|
+
const interaction = evaluation?.interaction || null;
|
|
128
|
+
const retrieval = evaluation?.retrieval || null;
|
|
129
|
+
const contractCheck = evaluation?.contractCheck || null;
|
|
130
|
+
const rubric = evaluation?.rubric || null;
|
|
131
|
+
const embeddingSummary = summarizeEmbeddings(embeddingHealth);
|
|
132
|
+
const effectiveRetrieval = buildEffectiveRetrieval(retrieval, embeddingSummary);
|
|
133
|
+
const embeddings = reconcileEmbeddingsWithRetrieval(embeddingSummary, effectiveRetrieval);
|
|
134
|
+
const fallback = summarizeFallback(reportFallback);
|
|
135
|
+
|
|
136
|
+
const checks = {
|
|
137
|
+
contractHealthy:
|
|
138
|
+
contractCheck == null
|
|
139
|
+
? true
|
|
140
|
+
: Number(contractCheck.satisfiedRatio ?? 0) >= 0.99 && (contractCheck.missingFromV2 || []).length === 0,
|
|
141
|
+
interactionHealthy: !hasBlockingInteractionIssues(interaction),
|
|
142
|
+
retrievalHealthy:
|
|
143
|
+
!effectiveRetrieval ||
|
|
144
|
+
(!effectiveRetrieval.embeddingError &&
|
|
145
|
+
effectiveRetrieval.embeddingQueryMode !== "lexical_fallback" &&
|
|
146
|
+
Number(effectiveRetrieval.failedEmbeddingCount || 0) === 0),
|
|
147
|
+
daemonHealthy:
|
|
148
|
+
!maintenance ||
|
|
149
|
+
((maintenance.handoffs?.expiredCount || 0) === 0 && (maintenance.followUps?.expiredCount || 0) === 0),
|
|
150
|
+
providerReachable:
|
|
151
|
+
!dependencyHealth || dependencyHealth.provider?.reachable !== false,
|
|
152
|
+
deliveryReachable:
|
|
153
|
+
!dependencyHealth || dependencyHealth.delivery?.reachable !== false,
|
|
154
|
+
fallbackReady: fallback.ready
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
const blockers = [];
|
|
158
|
+
if (!checks.contractHealthy) blockers.push("output_contract_not_consistently_satisfied");
|
|
159
|
+
if (!checks.interactionHealthy) blockers.push("interaction_lifecycle_has_blocking_issues");
|
|
160
|
+
if (!checks.retrievalHealthy) blockers.push("retrieval_is_degraded_or_falling_back");
|
|
161
|
+
if (!checks.daemonHealthy) blockers.push("daemon_maintenance_detected_expired_items");
|
|
162
|
+
if (!checks.providerReachable) blockers.push("provider_unreachable");
|
|
163
|
+
if (embeddings.degraded) blockers.push(...embeddings.blockedReasons.map((item) => `embeddings:${item}`));
|
|
164
|
+
|
|
165
|
+
const warnings = [];
|
|
166
|
+
if (!checks.deliveryReachable) warnings.push("delivery_not_reachable");
|
|
167
|
+
|
|
168
|
+
const suggestions = [];
|
|
169
|
+
if (!checks.retrievalHealthy) {
|
|
170
|
+
suggestions.push("Repair embeddings or accept lexical-only mode before treating this lane as daily-driver ready.");
|
|
171
|
+
}
|
|
172
|
+
if (!checks.contractHealthy) {
|
|
173
|
+
suggestions.push("Keep contract adherence at 100% before cutover.");
|
|
174
|
+
}
|
|
175
|
+
if (!checks.interactionHealthy) {
|
|
176
|
+
suggestions.push("Resolve follow-up / handoff lifecycle issues before using this lane unattended.");
|
|
177
|
+
}
|
|
178
|
+
if (!checks.providerReachable) {
|
|
179
|
+
suggestions.push("Check provider health before treating this lane as trustworthy.");
|
|
180
|
+
}
|
|
181
|
+
if (!checks.deliveryReachable) {
|
|
182
|
+
suggestions.push("Delivery transport is not reachable. This does not block pilot readiness but should be resolved before daily-driver cutover.");
|
|
183
|
+
}
|
|
184
|
+
if (!checks.fallbackReady && fallback.configured) {
|
|
185
|
+
suggestions.push("Remote fallback is optional for this lane. Enable and validate it only if you want recovery from local failures.");
|
|
186
|
+
}
|
|
187
|
+
if (checks.contractHealthy && checks.interactionHealthy && checks.retrievalHealthy) {
|
|
188
|
+
suggestions.push("This lane is technically close to cutover; focus next on repeat-run validation and operator trust.");
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
let tier = "not_ready";
|
|
192
|
+
if (blockers.length === 0 && Number(rubric?.overallScore || 0) >= 0.95) {
|
|
193
|
+
tier = "ready_for_pilot";
|
|
194
|
+
} else if (blockers.length <= 2 && Number(rubric?.overallScore || 0) >= 0.85) {
|
|
195
|
+
tier = "close";
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return {
|
|
199
|
+
jobId,
|
|
200
|
+
taskType: job?.taskType || null,
|
|
201
|
+
primaryLane: job?.modelLane || null,
|
|
202
|
+
readinessTier: tier,
|
|
203
|
+
readyForPilot: tier === "ready_for_pilot",
|
|
204
|
+
checks,
|
|
205
|
+
blockers: [...new Set(blockers)],
|
|
206
|
+
warnings,
|
|
207
|
+
suggestions,
|
|
208
|
+
summary: {
|
|
209
|
+
overallScore: rubric?.overallScore ?? null,
|
|
210
|
+
contractSatisfiedRatio: contractCheck?.satisfiedRatio ?? null,
|
|
211
|
+
yielded: truthy(interaction?.yielded),
|
|
212
|
+
followUpConsumed: truthy(interaction?.followUpConsumed),
|
|
213
|
+
handoffDelivered: truthy(interaction?.handoffDelivered),
|
|
214
|
+
embeddingQueryMode: effectiveRetrieval?.embeddingQueryMode || null,
|
|
215
|
+
semanticCount: effectiveRetrieval?.semanticCount ?? 0,
|
|
216
|
+
fallbackConfigured: fallback.configured,
|
|
217
|
+
fallbackReady: fallback.ready
|
|
218
|
+
},
|
|
219
|
+
interaction,
|
|
220
|
+
retrieval: effectiveRetrieval,
|
|
221
|
+
embeddings,
|
|
222
|
+
fallback,
|
|
223
|
+
maintenance,
|
|
224
|
+
dependencyHealth
|
|
225
|
+
};
|
|
226
|
+
}
|