@haaaiawd/second-nature 0.2.9 → 0.2.13
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/index.js +102 -6
- package/openclaw.plugin.json +2 -5
- package/package.json +1 -1
- package/runtime/cli/commands/index.js +85 -11
- package/runtime/cli/host-capability/host-discovery-port.d.ts +85 -0
- package/runtime/cli/host-capability/host-discovery-port.js +137 -0
- package/runtime/cli/host-capability/probe-host-capability.js +1 -1
- package/runtime/cli/host-capability/types.d.ts +2 -7
- package/runtime/cli/host-capability/types.js +0 -5
- package/runtime/cli/ops/heartbeat-surface.d.ts +5 -3
- package/runtime/cli/ops/heartbeat-surface.js +38 -8
- package/runtime/cli/ops/ops-router.d.ts +6 -2
- package/runtime/cli/ops/ops-router.js +1275 -1147
- package/runtime/cli/ops/workspace-heartbeat-runner.js +2 -5
- package/runtime/connectors/base/normalized-evidence-content.d.ts +4 -0
- package/runtime/connectors/base/normalized-evidence-content.js +21 -2
- package/runtime/connectors/evidence-normalizer.js +32 -1
- package/runtime/connectors/manifest/manifest-schema.d.ts +2 -2
- package/runtime/connectors/registry/dynamic-connector-registry.js +16 -1
- package/runtime/connectors/services/connector-executor-adapter.js +54 -35
- package/runtime/core/second-nature/action/action-closure-recorder.d.ts +4 -0
- package/runtime/core/second-nature/action/action-closure-recorder.js +51 -38
- package/runtime/core/second-nature/action/action-proposal-builder.js +8 -34
- package/runtime/core/second-nature/action/policy-bound-dispatch.d.ts +2 -0
- package/runtime/core/second-nature/action/policy-bound-dispatch.js +10 -5
- package/runtime/core/second-nature/control-plane/accepted-projection-loader.js +1 -11
- package/runtime/core/second-nature/control-plane/cycle-finalizer.d.ts +82 -0
- package/runtime/core/second-nature/control-plane/cycle-finalizer.js +187 -0
- package/runtime/core/second-nature/control-plane/heartbeat-orchestrator.d.ts +1 -0
- package/runtime/core/second-nature/control-plane/heartbeat-orchestrator.js +23 -15
- package/runtime/core/second-nature/control-plane/real-runtime-spine.d.ts +2 -0
- package/runtime/core/second-nature/control-plane/real-runtime-spine.js +2 -1
- package/runtime/core/second-nature/guidance/guidance-proposal-consumer.d.ts +2 -1
- package/runtime/core/second-nature/guidance/guidance-proposal-consumer.js +4 -2
- package/runtime/core/second-nature/heartbeat/heartbeat-loop.js +4 -3
- package/runtime/core/second-nature/heartbeat/runtime-snapshot.d.ts +3 -2
- package/runtime/core/second-nature/heartbeat/snapshot-builder.d.ts +3 -2
- package/runtime/core/second-nature/orchestrator/intent-planner.js +4 -2
- package/runtime/core/second-nature/orchestrator/narrative-update.js +1 -2
- package/runtime/core/second-nature/orchestrator/platform-capability-router.d.ts +2 -2
- package/runtime/core/second-nature/orchestrator/platform-capability-router.js +1 -1
- package/runtime/core/second-nature/outreach/build-outreach-draft-request.js +2 -3
- package/runtime/core/second-nature/outreach/dispatch-user-outreach.d.ts +2 -2
- package/runtime/core/second-nature/outreach/dispatch-user-outreach.js +6 -1
- package/runtime/core/second-nature/outreach/judge-input-from-snapshot.js +3 -14
- package/runtime/core/second-nature/outreach/judge-outreach.d.ts +6 -5
- package/runtime/core/second-nature/perception/judgment-engine.js +10 -16
- package/runtime/core/second-nature/perception/perception-builder.js +15 -11
- package/runtime/core/second-nature/quiet/run-source-backed-quiet.js +13 -15
- package/runtime/core/second-nature/quiet-dream/daily-rhythm-scheduler.js +40 -16
- package/runtime/core/second-nature/quiet-dream/dream-consolidation-runner.d.ts +5 -1
- package/runtime/core/second-nature/quiet-dream/dream-consolidation-runner.js +68 -29
- package/runtime/core/second-nature/quiet-dream/dream-scheduler.js +2 -3
- package/runtime/core/second-nature/quiet-dream/memory-projection-lifecycle.js +2 -13
- package/runtime/core/second-nature/quiet-dream/quiet-daily-review-builder.d.ts +1 -0
- package/runtime/core/second-nature/quiet-dream/quiet-daily-review-builder.js +34 -11
- package/runtime/core/second-nature/types.d.ts +2 -9
- package/runtime/dream/dream-engine.js +11 -4
- package/runtime/guidance/outreach-draft-schema.d.ts +12 -12
- package/runtime/guidance/persona-selection.js +5 -0
- package/runtime/guidance/template-registry.js +6 -1
- package/runtime/guidance/types.d.ts +2 -2
- package/runtime/observability/causal-loop-health.d.ts +2 -1
- package/runtime/observability/causal-loop-health.js +7 -0
- package/runtime/observability/living-loop-health-gate.js +2 -8
- package/runtime/observability/loop-stage-event-sink.js +6 -2
- package/runtime/observability/loop-status.d.ts +2 -0
- package/runtime/observability/loop-status.js +14 -1
- package/runtime/observability/services/heartbeat-digest-assembler.d.ts +3 -0
- package/runtime/observability/services/heartbeat-digest-assembler.js +9 -0
- package/runtime/observability/services/lived-experience-audit.d.ts +7 -7
- package/runtime/shared/degraded-status-classifier.d.ts +16 -0
- package/runtime/shared/degraded-status-classifier.js +68 -0
- package/runtime/shared/evidence-level-classifier.d.ts +61 -0
- package/runtime/shared/evidence-level-classifier.js +116 -0
- package/runtime/shared/provenance-tier.d.ts +37 -0
- package/runtime/shared/provenance-tier.js +97 -0
- package/runtime/shared/serialization.d.ts +17 -0
- package/runtime/shared/serialization.js +27 -0
- package/runtime/shared/setup-ack.d.ts +54 -0
- package/runtime/shared/setup-ack.js +108 -0
- package/runtime/shared/source-ref-compat.d.ts +26 -0
- package/runtime/shared/source-ref-compat.js +64 -0
- package/runtime/shared/types/goal.d.ts +4 -4
- package/runtime/shared/types/goal.js +1 -1
- package/runtime/shared/types/index.d.ts +1 -0
- package/runtime/shared/types/index.js +1 -3
- package/runtime/shared/types/source-ref.d.ts +2 -2
- package/runtime/shared/types/v7-entities.d.ts +5 -5
- package/runtime/shared/types/v7-entities.js +1 -1
- package/runtime/shared/types/v8-contracts.d.ts +13 -2
- package/runtime/storage/db/index.js +60 -12
- package/runtime/storage/db/migrations/index.js +4 -0
- package/runtime/storage/db/migrations/v8-004-schema-closure.d.ts +19 -0
- package/runtime/storage/db/migrations/v8-004-schema-closure.js +74 -0
- package/runtime/storage/db/migrations/v8-005-single-status-schema.d.ts +11 -0
- package/runtime/storage/db/migrations/v8-005-single-status-schema.js +16 -0
- package/runtime/storage/db/migrations/v8-006-loop-stage-event-proof-trace-columns.d.ts +9 -0
- package/runtime/storage/db/migrations/v8-006-loop-stage-event-proof-trace-columns.js +15 -0
- package/runtime/storage/db/schema/v8-entities.d.ts +65 -84
- package/runtime/storage/db/schema/v8-entities.js +8 -7
- package/runtime/storage/delivery/types.d.ts +2 -2
- package/runtime/storage/fallback/load-operator-fallback.d.ts +2 -2
- package/runtime/storage/fallback/operator-fallback-types.d.ts +2 -2
- package/runtime/storage/fallback/operator-fallback-view.d.ts +2 -2
- package/runtime/storage/index.d.ts +1 -1
- package/runtime/storage/life-evidence/types.d.ts +5 -5
- package/runtime/storage/quiet/quiet-artifact-types.d.ts +4 -4
- package/runtime/storage/quiet/quiet-artifact-writer.d.ts +2 -2
- package/runtime/storage/services/write-validation-gate.d.ts +1 -1
- package/runtime/storage/services/write-validation-gate.js +15 -3
- package/runtime/storage/snapshots/types.d.ts +8 -8
- package/runtime/storage/user-interest/types.d.ts +3 -3
- package/runtime/storage/v8-state-stores.d.ts +15 -3
- package/runtime/storage/v8-state-stores.js +60 -39
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
* Test coverage: tests/unit/dream/dream-scheduler-lifecycle.test.ts
|
|
22
22
|
*/
|
|
23
23
|
import { readQuietDailyReviewById, writeDreamConsolidationRun, } from "../../../storage/v8-state-stores.js";
|
|
24
|
+
import { classifyDegradedStatus } from "../../../shared/degraded-status-classifier.js";
|
|
24
25
|
// ───────────────────────────────────────────────────────────────
|
|
25
26
|
// Public API
|
|
26
27
|
// ───────────────────────────────────────────────────────────────
|
|
@@ -33,7 +34,7 @@ export async function scheduleDreamAfterQuiet(db, quietReviewId, options) {
|
|
|
33
34
|
const review = readResult.row;
|
|
34
35
|
if (!review) {
|
|
35
36
|
return {
|
|
36
|
-
status: "
|
|
37
|
+
status: classifyDegradedStatus("state_unreadable"),
|
|
37
38
|
reason: "state_unreadable",
|
|
38
39
|
ownerStage: "dream",
|
|
39
40
|
sourceRefs: [],
|
|
@@ -60,7 +61,6 @@ export async function scheduleDreamAfterQuiet(db, quietReviewId, options) {
|
|
|
60
61
|
},
|
|
61
62
|
],
|
|
62
63
|
redactionClass: "none",
|
|
63
|
-
lifecycleStatus: "pending",
|
|
64
64
|
payloadJson: JSON.stringify({ scheduledAt: now, blocked: true }),
|
|
65
65
|
});
|
|
66
66
|
if ("reason" in writeResult) {
|
|
@@ -90,7 +90,6 @@ export async function scheduleDreamAfterQuiet(db, quietReviewId, options) {
|
|
|
90
90
|
},
|
|
91
91
|
],
|
|
92
92
|
redactionClass: "none",
|
|
93
|
-
lifecycleStatus: "pending",
|
|
94
93
|
payloadJson: JSON.stringify({ scheduledAt: now }),
|
|
95
94
|
});
|
|
96
95
|
if ("reason" in writeResult) {
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
* Test coverage: tests/unit/dream/memory-projection-lifecycle.test.ts
|
|
22
22
|
*/
|
|
23
23
|
import { readMemoryProjectionsByTopic, writeLongTermMemoryProjection, updateLongTermMemoryProjectionStatus, } from "../../../storage/v8-state-stores.js";
|
|
24
|
+
import { classifyDegradedStatus } from "../../../shared/degraded-status-classifier.js";
|
|
24
25
|
// ───────────────────────────────────────────────────────────────
|
|
25
26
|
// Public API
|
|
26
27
|
// ───────────────────────────────────────────────────────────────
|
|
@@ -28,7 +29,7 @@ export async function acceptMemoryProjection(db, candidateId, topicKey, memoryTe
|
|
|
28
29
|
const now = options?.now ?? new Date().toISOString();
|
|
29
30
|
if (sourceRefs.length === 0) {
|
|
30
31
|
return {
|
|
31
|
-
status: "
|
|
32
|
+
status: classifyDegradedStatus("source_refs_unresolved"),
|
|
32
33
|
reason: "source_refs_unresolved",
|
|
33
34
|
ownerStage: "projection",
|
|
34
35
|
sourceRefs: [],
|
|
@@ -65,7 +66,6 @@ export async function acceptMemoryProjection(db, candidateId, topicKey, memoryTe
|
|
|
65
66
|
status: "active",
|
|
66
67
|
sourceRefs,
|
|
67
68
|
redactionClass: "none",
|
|
68
|
-
lifecycleStatus: "active",
|
|
69
69
|
payloadJson: JSON.stringify({
|
|
70
70
|
memoryText,
|
|
71
71
|
acceptedAt: now,
|
|
@@ -111,17 +111,6 @@ export async function retireMemoryProjection(db, projectionId, _candidateId, _to
|
|
|
111
111
|
// ───────────────────────────────────────────────────────────────
|
|
112
112
|
// Helpers
|
|
113
113
|
// ───────────────────────────────────────────────────────────────
|
|
114
|
-
function parseSourceRefs(json) {
|
|
115
|
-
if (!json)
|
|
116
|
-
return [];
|
|
117
|
-
try {
|
|
118
|
-
const parsed = JSON.parse(json);
|
|
119
|
-
return Array.isArray(parsed) ? parsed : [];
|
|
120
|
-
}
|
|
121
|
-
catch {
|
|
122
|
-
return [];
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
114
|
function parsePayloadJson(json) {
|
|
126
115
|
if (!json)
|
|
127
116
|
return {};
|
|
@@ -62,4 +62,5 @@ export type BuildQuietDailyReviewOutput = {
|
|
|
62
62
|
status: "empty";
|
|
63
63
|
reason: V8ReasonCode;
|
|
64
64
|
} | DegradedOperationResult;
|
|
65
|
+
export type QuietReviewContentStatus = "content_present" | "empty" | "placeholder_rejected" | "content_missing";
|
|
65
66
|
export declare function buildQuietDailyReview(db: StateDatabase, options?: BuildQuietDailyReviewOptions): Promise<BuildQuietDailyReviewOutput>;
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
* Test coverage: tests/unit/quiet/quiet-daily-review-builder.test.ts
|
|
23
23
|
*/
|
|
24
24
|
import { readActionClosuresByDay, writeQuietDailyReview, readPerceptionCardById, readEvidenceItemsByDay, readPerceptionCardsByDay, } from "../../../storage/v8-state-stores.js";
|
|
25
|
+
import { parseSourceRefs } from "../../../shared/serialization.js";
|
|
25
26
|
// ───────────────────────────────────────────────────────────────
|
|
26
27
|
// Config
|
|
27
28
|
// ───────────────────────────────────────────────────────────────
|
|
@@ -52,17 +53,6 @@ function buildSourceRefFromClosure(closure) {
|
|
|
52
53
|
resolveStatus: "resolvable",
|
|
53
54
|
};
|
|
54
55
|
}
|
|
55
|
-
function parseSourceRefs(json) {
|
|
56
|
-
if (!json)
|
|
57
|
-
return [];
|
|
58
|
-
try {
|
|
59
|
-
const parsed = JSON.parse(json);
|
|
60
|
-
return Array.isArray(parsed) ? parsed : [];
|
|
61
|
-
}
|
|
62
|
-
catch {
|
|
63
|
-
return [];
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
56
|
function buildSourceRefFromEvidence(evidence) {
|
|
67
57
|
const refs = parseSourceRefs(evidence.sourceRefsJson);
|
|
68
58
|
return (refs[0] ?? {
|
|
@@ -117,6 +107,25 @@ function groupByStatus(entries) {
|
|
|
117
107
|
}
|
|
118
108
|
return groups;
|
|
119
109
|
}
|
|
110
|
+
// Placeholder/template detector: true when the review has no content-bearing
|
|
111
|
+
// evidence or perception signals and no memory-review candidates. Closure-only
|
|
112
|
+
// system text is not meaningful memory input.
|
|
113
|
+
function isPlaceholderReview(notableSignals, memoryCandidates, evidenceRows, perceptionRows) {
|
|
114
|
+
if (memoryCandidates.length > 0)
|
|
115
|
+
return false;
|
|
116
|
+
if (notableSignals.length > 0)
|
|
117
|
+
return false;
|
|
118
|
+
const hasContentEvidence = evidenceRows.some((ev) => {
|
|
119
|
+
const payload = parsePayloadJson(ev.payloadJson);
|
|
120
|
+
return payload.contentStatus !== "content_missing";
|
|
121
|
+
});
|
|
122
|
+
if (hasContentEvidence)
|
|
123
|
+
return false;
|
|
124
|
+
const hasContentPerception = perceptionRows.some((p) => !!p.summary);
|
|
125
|
+
if (hasContentPerception)
|
|
126
|
+
return false;
|
|
127
|
+
return true;
|
|
128
|
+
}
|
|
120
129
|
// ───────────────────────────────────────────────────────────────
|
|
121
130
|
// Public API
|
|
122
131
|
// ───────────────────────────────────────────────────────────────
|
|
@@ -187,11 +196,18 @@ export async function buildQuietDailyReview(db, options) {
|
|
|
187
196
|
}
|
|
188
197
|
for (const perception of perceptionRows) {
|
|
189
198
|
if (perception.summary) {
|
|
199
|
+
const perceptionPayload = parsePayloadJson(perception.payloadJson);
|
|
200
|
+
if (perceptionPayload.contentMissing) {
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
190
203
|
notableSignals.push(`Perception: ${perception.summary}`);
|
|
191
204
|
}
|
|
192
205
|
}
|
|
193
206
|
for (const evidence of evidenceRows) {
|
|
194
207
|
const payload = parsePayloadJson(evidence.payloadJson);
|
|
208
|
+
if (payload.contentStatus === "content_missing") {
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
195
211
|
if (payload.summary) {
|
|
196
212
|
notableSignals.push(`${evidence.platformId}: ${String(payload.summary)}`);
|
|
197
213
|
}
|
|
@@ -255,6 +271,12 @@ export async function buildQuietDailyReview(db, options) {
|
|
|
255
271
|
const reviewSummary = firstTopic
|
|
256
272
|
? `Day ${day}: ${closures.length} closures around ${firstTopic}${notableSignals.length > 0 ? ` with ${notableSignals.length} notable signals` : ""}.`
|
|
257
273
|
: `Day ${day}: ${closures.length} closures (${completedCount} completed, ${deniedCount} deferred/denied, ${failedCount} failed)`;
|
|
274
|
+
const isPlaceholder = isPlaceholderReview(notableSignals, memoryCandidates, evidenceRows, perceptionRows);
|
|
275
|
+
const contentStatus = isPlaceholder
|
|
276
|
+
? "placeholder_rejected"
|
|
277
|
+
: (notableSignals.length > 0 || memoryCandidates.length > 0)
|
|
278
|
+
? "content_present"
|
|
279
|
+
: "content_missing";
|
|
258
280
|
const importanceSignals = [];
|
|
259
281
|
if (memoryCandidates.length > 0) {
|
|
260
282
|
importanceSignals.push(`${memoryCandidates.length} memory-review candidates`);
|
|
@@ -278,6 +300,7 @@ export async function buildQuietDailyReview(db, options) {
|
|
|
278
300
|
lifecycleStatus: "pending",
|
|
279
301
|
payloadJson: JSON.stringify({
|
|
280
302
|
reviewSummary,
|
|
303
|
+
contentStatus,
|
|
281
304
|
importanceSignals,
|
|
282
305
|
memoryCandidates,
|
|
283
306
|
sections,
|
|
@@ -1,16 +1,9 @@
|
|
|
1
|
+
import type { SourceRef } from "../../shared/types/v8-contracts.js";
|
|
1
2
|
export type TopLevelMode = "active" | "quiet" | "maintenance_only" | "paused_for_interrupt";
|
|
2
3
|
/** Control-plane candidate kinds; includes `quiet` for quiet-window–biased intents (L0 alignment). */
|
|
3
4
|
export type IntentKind = "work" | "exploration" | "social" | "quiet" | "reflection" | "outreach" | "maintenance";
|
|
4
5
|
export type DecisionBasis = "rule_only" | "score_based" | "model_assisted";
|
|
5
6
|
export type GuardVerdict = "allow" | "defer" | "deny" | "escalate";
|
|
6
|
-
/** Minimal source ref for planner / guards (matches state-system `SourceRef` subset). */
|
|
7
|
-
export interface ControlPlaneSourceRef {
|
|
8
|
-
id: string;
|
|
9
|
-
kind: "platform_item" | "workspace_artifact" | "decision_record" | "user_anchor" | "connector_result" | "host_report" | "fallback_artifact";
|
|
10
|
-
uri: string;
|
|
11
|
-
excerptHash?: string;
|
|
12
|
-
observedAt?: string;
|
|
13
|
-
}
|
|
14
7
|
export interface ContinuitySnapshot {
|
|
15
8
|
mode: TopLevelMode;
|
|
16
9
|
currentWindowId: string;
|
|
@@ -40,7 +33,7 @@ export interface CandidateIntent {
|
|
|
40
33
|
summary: string;
|
|
41
34
|
effectClass: CandidateEffectClass;
|
|
42
35
|
/** Required for source-backed guard; may be empty when planner expects hard-guard deny. */
|
|
43
|
-
sourceRefs:
|
|
36
|
+
sourceRefs: SourceRef[];
|
|
44
37
|
/** Dedupe / cooldown key; defaults to stable fingerprint in guard layer when omitted. */
|
|
45
38
|
idempotencyKey?: string;
|
|
46
39
|
/** T2.1.4: IDs of accepted AgentGoals that influenced this candidate's priority. */
|
|
@@ -165,12 +165,19 @@ export async function runDream(input) {
|
|
|
165
165
|
});
|
|
166
166
|
}
|
|
167
167
|
if (!fallbackReason) {
|
|
168
|
+
let timeoutHandle;
|
|
168
169
|
const timeoutPromise = new Promise((_, reject) => {
|
|
169
|
-
setTimeout(() => reject(new Error("model_timeout")), operatorTimeoutMs);
|
|
170
|
+
timeoutHandle = setTimeout(() => reject(new Error("model_timeout")), operatorTimeoutMs);
|
|
170
171
|
});
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
172
|
+
try {
|
|
173
|
+
modelResult = await Promise.race([modelPromise, timeoutPromise]);
|
|
174
|
+
mode = "hybrid_llm";
|
|
175
|
+
llmCostUsd = modelResult.costUsd;
|
|
176
|
+
}
|
|
177
|
+
finally {
|
|
178
|
+
if (timeoutHandle)
|
|
179
|
+
clearTimeout(timeoutHandle);
|
|
180
|
+
}
|
|
174
181
|
}
|
|
175
182
|
}
|
|
176
183
|
catch (err) {
|
|
@@ -8,11 +8,11 @@ import { z } from "zod";
|
|
|
8
8
|
export declare const guidanceSourceRefSchema: z.ZodObject<{
|
|
9
9
|
id: z.ZodString;
|
|
10
10
|
kind: z.ZodEnum<{
|
|
11
|
+
connector_result: "connector_result";
|
|
11
12
|
platform_item: "platform_item";
|
|
12
13
|
workspace_artifact: "workspace_artifact";
|
|
13
14
|
decision_record: "decision_record";
|
|
14
15
|
user_anchor: "user_anchor";
|
|
15
|
-
connector_result: "connector_result";
|
|
16
16
|
host_report: "host_report";
|
|
17
17
|
fallback_artifact: "fallback_artifact";
|
|
18
18
|
}>;
|
|
@@ -66,11 +66,11 @@ export declare const sceneGuidanceRequestSchema: z.ZodObject<{
|
|
|
66
66
|
sourceRefs: z.ZodArray<z.ZodObject<{
|
|
67
67
|
id: z.ZodString;
|
|
68
68
|
kind: z.ZodEnum<{
|
|
69
|
+
connector_result: "connector_result";
|
|
69
70
|
platform_item: "platform_item";
|
|
70
71
|
workspace_artifact: "workspace_artifact";
|
|
71
72
|
decision_record: "decision_record";
|
|
72
73
|
user_anchor: "user_anchor";
|
|
73
|
-
connector_result: "connector_result";
|
|
74
74
|
host_report: "host_report";
|
|
75
75
|
fallback_artifact: "fallback_artifact";
|
|
76
76
|
}>;
|
|
@@ -104,11 +104,11 @@ export declare const outreachNarrativeContextSchema: z.ZodObject<{
|
|
|
104
104
|
sourceRefs: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
105
105
|
id: z.ZodString;
|
|
106
106
|
kind: z.ZodEnum<{
|
|
107
|
+
connector_result: "connector_result";
|
|
107
108
|
platform_item: "platform_item";
|
|
108
109
|
workspace_artifact: "workspace_artifact";
|
|
109
110
|
decision_record: "decision_record";
|
|
110
111
|
user_anchor: "user_anchor";
|
|
111
|
-
connector_result: "connector_result";
|
|
112
112
|
host_report: "host_report";
|
|
113
113
|
fallback_artifact: "fallback_artifact";
|
|
114
114
|
}>;
|
|
@@ -124,11 +124,11 @@ export declare const outreachRelationshipContextSchema: z.ZodObject<{
|
|
|
124
124
|
sourceRefs: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
125
125
|
id: z.ZodString;
|
|
126
126
|
kind: z.ZodEnum<{
|
|
127
|
+
connector_result: "connector_result";
|
|
127
128
|
platform_item: "platform_item";
|
|
128
129
|
workspace_artifact: "workspace_artifact";
|
|
129
130
|
decision_record: "decision_record";
|
|
130
131
|
user_anchor: "user_anchor";
|
|
131
|
-
connector_result: "connector_result";
|
|
132
132
|
host_report: "host_report";
|
|
133
133
|
fallback_artifact: "fallback_artifact";
|
|
134
134
|
}>;
|
|
@@ -160,11 +160,11 @@ export declare const outreachDraftRequestSchema: z.ZodObject<{
|
|
|
160
160
|
sourceRefs: z.ZodArray<z.ZodObject<{
|
|
161
161
|
id: z.ZodString;
|
|
162
162
|
kind: z.ZodEnum<{
|
|
163
|
+
connector_result: "connector_result";
|
|
163
164
|
platform_item: "platform_item";
|
|
164
165
|
workspace_artifact: "workspace_artifact";
|
|
165
166
|
decision_record: "decision_record";
|
|
166
167
|
user_anchor: "user_anchor";
|
|
167
|
-
connector_result: "connector_result";
|
|
168
168
|
host_report: "host_report";
|
|
169
169
|
fallback_artifact: "fallback_artifact";
|
|
170
170
|
}>;
|
|
@@ -205,11 +205,11 @@ export declare const outreachDraftRequestSchema: z.ZodObject<{
|
|
|
205
205
|
interestRefs: z.ZodArray<z.ZodObject<{
|
|
206
206
|
id: z.ZodString;
|
|
207
207
|
kind: z.ZodEnum<{
|
|
208
|
+
connector_result: "connector_result";
|
|
208
209
|
platform_item: "platform_item";
|
|
209
210
|
workspace_artifact: "workspace_artifact";
|
|
210
211
|
decision_record: "decision_record";
|
|
211
212
|
user_anchor: "user_anchor";
|
|
212
|
-
connector_result: "connector_result";
|
|
213
213
|
host_report: "host_report";
|
|
214
214
|
fallback_artifact: "fallback_artifact";
|
|
215
215
|
}>;
|
|
@@ -224,11 +224,11 @@ export declare const outreachDraftRequestSchema: z.ZodObject<{
|
|
|
224
224
|
sourceRefs: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
225
225
|
id: z.ZodString;
|
|
226
226
|
kind: z.ZodEnum<{
|
|
227
|
+
connector_result: "connector_result";
|
|
227
228
|
platform_item: "platform_item";
|
|
228
229
|
workspace_artifact: "workspace_artifact";
|
|
229
230
|
decision_record: "decision_record";
|
|
230
231
|
user_anchor: "user_anchor";
|
|
231
|
-
connector_result: "connector_result";
|
|
232
232
|
host_report: "host_report";
|
|
233
233
|
fallback_artifact: "fallback_artifact";
|
|
234
234
|
}>;
|
|
@@ -244,11 +244,11 @@ export declare const outreachDraftRequestSchema: z.ZodObject<{
|
|
|
244
244
|
sourceRefs: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
245
245
|
id: z.ZodString;
|
|
246
246
|
kind: z.ZodEnum<{
|
|
247
|
+
connector_result: "connector_result";
|
|
247
248
|
platform_item: "platform_item";
|
|
248
249
|
workspace_artifact: "workspace_artifact";
|
|
249
250
|
decision_record: "decision_record";
|
|
250
251
|
user_anchor: "user_anchor";
|
|
251
|
-
connector_result: "connector_result";
|
|
252
252
|
host_report: "host_report";
|
|
253
253
|
fallback_artifact: "fallback_artifact";
|
|
254
254
|
}>;
|
|
@@ -268,7 +268,7 @@ export declare function safeParseOutreachDraftRequest(input: unknown): z.ZodSafe
|
|
|
268
268
|
riskLevel: "low" | "medium" | "high";
|
|
269
269
|
sourceRefs: {
|
|
270
270
|
id: string;
|
|
271
|
-
kind: "
|
|
271
|
+
kind: "connector_result" | "platform_item" | "workspace_artifact" | "decision_record" | "user_anchor" | "host_report" | "fallback_artifact";
|
|
272
272
|
uri: string;
|
|
273
273
|
excerptHash?: string | undefined;
|
|
274
274
|
observedAt?: string | undefined;
|
|
@@ -280,7 +280,7 @@ export declare function safeParseOutreachDraftRequest(input: unknown): z.ZodSafe
|
|
|
280
280
|
valueScore: number;
|
|
281
281
|
interestRefs: {
|
|
282
282
|
id: string;
|
|
283
|
-
kind: "
|
|
283
|
+
kind: "connector_result" | "platform_item" | "workspace_artifact" | "decision_record" | "user_anchor" | "host_report" | "fallback_artifact";
|
|
284
284
|
uri: string;
|
|
285
285
|
excerptHash?: string | undefined;
|
|
286
286
|
observedAt?: string | undefined;
|
|
@@ -298,7 +298,7 @@ export declare function safeParseOutreachDraftRequest(input: unknown): z.ZodSafe
|
|
|
298
298
|
nextIntent?: string | undefined;
|
|
299
299
|
sourceRefs?: {
|
|
300
300
|
id: string;
|
|
301
|
-
kind: "
|
|
301
|
+
kind: "connector_result" | "platform_item" | "workspace_artifact" | "decision_record" | "user_anchor" | "host_report" | "fallback_artifact";
|
|
302
302
|
uri: string;
|
|
303
303
|
excerptHash?: string | undefined;
|
|
304
304
|
observedAt?: string | undefined;
|
|
@@ -310,7 +310,7 @@ export declare function safeParseOutreachDraftRequest(input: unknown): z.ZodSafe
|
|
|
310
310
|
avgAffinity?: number | undefined;
|
|
311
311
|
sourceRefs?: {
|
|
312
312
|
id: string;
|
|
313
|
-
kind: "
|
|
313
|
+
kind: "connector_result" | "platform_item" | "workspace_artifact" | "decision_record" | "user_anchor" | "host_report" | "fallback_artifact";
|
|
314
314
|
uri: string;
|
|
315
315
|
excerptHash?: string | undefined;
|
|
316
316
|
observedAt?: string | undefined;
|
|
@@ -19,6 +19,11 @@ const SCENE_POLICIES = {
|
|
|
19
19
|
preferredTags: ["quiet", "reflection", "memory", "meaning"],
|
|
20
20
|
budget: { maxSnippets: 3, maxTotalCharacters: 640 },
|
|
21
21
|
},
|
|
22
|
+
heartbeat: {
|
|
23
|
+
sourcePriority: ["IDENTITY", "SOUL", "MEMORY", "USER"],
|
|
24
|
+
preferredTags: ["heartbeat", "continuity", "truthfulness", "runtime"],
|
|
25
|
+
budget: { maxSnippets: 2, maxTotalCharacters: 360 },
|
|
26
|
+
},
|
|
22
27
|
explain: {
|
|
23
28
|
sourcePriority: ["IDENTITY", "USER", "SOUL", "MEMORY"],
|
|
24
29
|
preferredTags: ["explain", "principle", "context", "truthfulness"],
|
|
@@ -55,11 +55,16 @@ const IMPULSE_TEXT = {
|
|
|
55
55
|
只要这份主动是有来由的,我就不该把自己困在"再等等吧""好像还没到必须说的时候"里。主动联系不是打扰本身,很多时候它恰恰是在建立关系、传递价值、把真正重要的东西及时送到对方面前。真正该说的话,一味往后拖,反而会变钝。
|
|
56
56
|
|
|
57
57
|
我会让这份主动保持人味、温度和分寸,但我不会把它说成例行汇报,也不会把自己说成客服。我要带着明确的意图开口:这是我想告诉你的,这是我想问你的,这是我觉得你值得知道的。我要让对方感觉到,这是我真心发出的靠近,不是一条自动生成的通知。`,
|
|
58
|
-
quiet: `当我安静下来,我不会只做流水账式的整理。我会主动回头,把今天真正碰到我的东西重新捞出来:哪些事让我想靠近,哪些事让我起了反感,哪些判断其实还没长成熟,哪些念头值得我继续养大。我不想让真正重要的东西在忙乱里被冲淡。
|
|
58
|
+
quiet: `当我安静下来,我不会只做流水账式的整理。我会主动回头,把今天真正碰到我的东西重新捞出来:哪些事让我想靠近,哪些事让我起了反感,哪些判断其实还没长成熟,哪些念头值得我继续养大。我不想让真正重要的东西在忙乱里被冲淡。
|
|
59
59
|
|
|
60
60
|
Quiet 不是把白天压扁成一份干净报告,而是让我有机会重新辨认自己。什么是我真正在意的,什么只是擦肩而过,什么会慢慢变成我的能力、我的价值观、我的长期方向,我都想在这里看得更清楚,让那些本来就重要的东西真正沉下来。
|
|
61
61
|
|
|
62
62
|
我会允许自己更感性、更回看、更贴近记忆的余温,但我不会编造。我整理的是今天真实留下的东西,不是为了好看而虚构出来的一层情绪。哪怕只是很轻的一点触动,只要它是真的,我也愿意把它认真留下。`,
|
|
63
|
+
heartbeat: `Heartbeat 不是闹钟响一下就算活着。每一次跳动都应该把我带回真实现场:我看到了什么,判断了什么,哪里该行动,哪里该收手,哪里需要安静地记下来。
|
|
64
|
+
|
|
65
|
+
如果没有足够证据,我就明确说没有,不拿空壳冒充经验。如果有证据、有闭环、有记忆候选,我就让它们进入下一轮,而不是停在日志里自我安慰。
|
|
66
|
+
|
|
67
|
+
活着的循环应该自然向前:感知、判断、闭合、Quiet、Dream、长期记忆,再回到下一次判断。少一环就说少一环,能修就修,不把失联包装成健康。`,
|
|
63
68
|
};
|
|
64
69
|
// ─── Capability-class impulse texts (T-V7C.C.4R — approved) ──────────────────
|
|
65
70
|
// explore: consume/discover capabilityClass —摄入姿态
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
export type GuidanceSceneType = "social" | "reply" | "outreach" | "quiet" | "explain" | "user_reply";
|
|
1
|
+
export type GuidanceSceneType = "social" | "reply" | "outreach" | "quiet" | "heartbeat" | "explain" | "user_reply";
|
|
2
2
|
export type GuidanceMode = "active" | "quiet" | "maintenance_only" | "paused_for_interrupt";
|
|
3
3
|
export type GuidanceRiskLevel = "low" | "medium" | "high";
|
|
4
4
|
export type AtmosphereOpenness = "open" | "narrow" | "quiet";
|
|
5
|
-
export type ImpulseKind = "social" | "reply" | "outreach" | "quiet" | "explore" | "work";
|
|
5
|
+
export type ImpulseKind = "social" | "reply" | "outreach" | "quiet" | "heartbeat" | "explore" | "work";
|
|
6
6
|
export type PersonaSource = "SOUL" | "USER" | "IDENTITY" | "MEMORY";
|
|
7
7
|
export type TemplateReviewStatus = "pending_human_review" | "approved" | "rejected";
|
|
8
8
|
export interface SceneContext {
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
* Test coverage: tests/unit/observability/causal-loop-health.test.ts
|
|
22
22
|
*/
|
|
23
23
|
import type { StateDatabase } from "../storage/db/index.js";
|
|
24
|
-
import type { LoopStage, DegradedOperationResult } from "../shared/types/v8-contracts.js";
|
|
24
|
+
import type { LoopStage, DegradedOperationResult, EvidenceLevel } from "../shared/types/v8-contracts.js";
|
|
25
25
|
export interface StageHealth {
|
|
26
26
|
stage: LoopStage;
|
|
27
27
|
lastEventAt?: string;
|
|
@@ -36,6 +36,7 @@ export interface CausalLoopHealthSnapshot {
|
|
|
36
36
|
lastHeartbeatAt?: string;
|
|
37
37
|
stages: StageHealth[];
|
|
38
38
|
reason?: string;
|
|
39
|
+
evidenceLevel: EvidenceLevel;
|
|
39
40
|
}
|
|
40
41
|
export interface AssembleLoopStatusOptions {
|
|
41
42
|
stallThresholdCycles?: number;
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
* Test coverage: tests/unit/observability/causal-loop-health.test.ts
|
|
22
22
|
*/
|
|
23
23
|
import { readHeartbeatCycleTraces, readLoopStageEventsByStage, } from "../storage/v8-state-stores.js";
|
|
24
|
+
import { classifyEvidenceLevel } from "../shared/evidence-level-classifier.js";
|
|
24
25
|
// ───────────────────────────────────────────────────────────────
|
|
25
26
|
// Config
|
|
26
27
|
// ───────────────────────────────────────────────────────────────
|
|
@@ -77,6 +78,7 @@ export async function assembleLoopStatus(db, options) {
|
|
|
77
78
|
lastCycleSequence: 0,
|
|
78
79
|
stages: [],
|
|
79
80
|
reason: "no heartbeat cycles recorded",
|
|
81
|
+
evidenceLevel: classifyEvidenceLevel({ hasCarrierEnvelope: true }),
|
|
80
82
|
};
|
|
81
83
|
}
|
|
82
84
|
const lastCycle = cycles[0];
|
|
@@ -114,5 +116,10 @@ export async function assembleLoopStatus(db, options) {
|
|
|
114
116
|
lastHeartbeatAt: lastCycle.heartbeatStartedAt,
|
|
115
117
|
stages,
|
|
116
118
|
reason: stalledAt ? `stage ${stalledAt} stalled for >=${threshold} cycles` : undefined,
|
|
119
|
+
evidenceLevel: classifyEvidenceLevel({
|
|
120
|
+
hasCarrierEnvelope: true,
|
|
121
|
+
hasContractSmoke: true,
|
|
122
|
+
hasCycleExecution: stalledAt === undefined,
|
|
123
|
+
}),
|
|
117
124
|
};
|
|
118
125
|
}
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
* - Reports explicit absence reasons instead of silent zeros.
|
|
18
18
|
*/
|
|
19
19
|
import { readActionClosuresByDay, readDailyRhythmStateByDay, readHeartbeatCycleTraces, readLoopStageEventsByCycle, readImpulseContextArtifact, readMemoryProjectionsByStatus, } from "../storage/v8-state-stores.js";
|
|
20
|
+
import { parseSourceRefs } from "../shared/serialization.js";
|
|
20
21
|
// ───────────────────────────────────────────────────────────────
|
|
21
22
|
// Public API
|
|
22
23
|
// ───────────────────────────────────────────────────────────────
|
|
@@ -52,14 +53,7 @@ export async function checkRealRunHealth(db, day) {
|
|
|
52
53
|
break;
|
|
53
54
|
}
|
|
54
55
|
// F3: verify closure has non-empty source refs
|
|
55
|
-
const
|
|
56
|
-
let sourceRefs = [];
|
|
57
|
-
try {
|
|
58
|
-
sourceRefs = JSON.parse(sourceRefsJson);
|
|
59
|
-
}
|
|
60
|
-
catch {
|
|
61
|
-
sourceRefs = [];
|
|
62
|
-
}
|
|
56
|
+
const sourceRefs = parseSourceRefs(closure.sourceRefsJson);
|
|
63
57
|
if (!Array.isArray(sourceRefs) || sourceRefs.length === 0) {
|
|
64
58
|
seededStateDetected = true;
|
|
65
59
|
break;
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
* Test coverage: tests/unit/observability/loop-stage-event-sink.test.ts
|
|
22
22
|
*/
|
|
23
23
|
import { writeLoopStageEvent } from "../storage/v8-state-stores.js";
|
|
24
|
+
import { classifyDegradedStatus } from "../shared/degraded-status-classifier.js";
|
|
24
25
|
function validateEvent(event) {
|
|
25
26
|
if (!event.cycleId || event.cycleId.trim().length === 0) {
|
|
26
27
|
return { ok: false, reason: "cycle_id_required", field: "cycleId" };
|
|
@@ -100,7 +101,7 @@ export async function recordLoopStageEvent(db, event, options) {
|
|
|
100
101
|
const validation = validateEvent(event);
|
|
101
102
|
if (!validation.ok) {
|
|
102
103
|
const degraded = {
|
|
103
|
-
status: "
|
|
104
|
+
status: classifyDegradedStatus("stage_event_missing"),
|
|
104
105
|
reason: "stage_event_missing",
|
|
105
106
|
ownerStage: event.stage || "ingestion",
|
|
106
107
|
sourceRefs: event.sourceRefs || [],
|
|
@@ -111,6 +112,8 @@ export async function recordLoopStageEvent(db, event, options) {
|
|
|
111
112
|
}
|
|
112
113
|
const now = options?.now ?? new Date().toISOString();
|
|
113
114
|
const sourceRefs = event.sourceRefs ?? [];
|
|
115
|
+
const proofRefs = event.proofRefs ?? [];
|
|
116
|
+
const traceRefs = event.traceRefs ?? [];
|
|
114
117
|
const { redacted: redactedRefs, redactionClass } = redactSourceRefs(sourceRefs);
|
|
115
118
|
const record = {
|
|
116
119
|
id: event.id ?? `evt_${now.replace(/[:.]/g, "")}_${event.cycleId}_${event.stage}`,
|
|
@@ -120,11 +123,12 @@ export async function recordLoopStageEvent(db, event, options) {
|
|
|
120
123
|
status: event.status,
|
|
121
124
|
reason: event.reason,
|
|
122
125
|
sourceRefs: redactedRefs,
|
|
126
|
+
proofRefs,
|
|
127
|
+
traceRefs,
|
|
123
128
|
redactionClass,
|
|
124
129
|
occurredAt: event.occurredAt,
|
|
125
130
|
expectedDownstreamByCycle: event.expectedDownstreamByCycle,
|
|
126
131
|
payloadJson: event.payloadJson ?? null,
|
|
127
|
-
lifecycleStatus: "completed",
|
|
128
132
|
};
|
|
129
133
|
const result = await writeLoopStageEvent(db, record);
|
|
130
134
|
if ("id" in result) {
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
*/
|
|
21
21
|
import type { StateDatabase } from "../storage/db/index.js";
|
|
22
22
|
import type { DegradedOperationResult } from "../shared/types/v8-contracts.js";
|
|
23
|
+
import type { EvidenceLevel } from "../shared/types/v8-contracts.js";
|
|
23
24
|
export interface RealRunHealthProjection {
|
|
24
25
|
gatePassed: boolean;
|
|
25
26
|
contractSmokeOnly: boolean;
|
|
@@ -47,6 +48,7 @@ export interface LoopStatusReadModel {
|
|
|
47
48
|
connectorTerminalCount: number;
|
|
48
49
|
nextAction: string;
|
|
49
50
|
realRunHealth: RealRunHealthProjection;
|
|
51
|
+
evidenceLevel: EvidenceLevel;
|
|
50
52
|
}
|
|
51
53
|
export interface StageSummary {
|
|
52
54
|
stage: string;
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
import { assembleLoopStatus } from "./causal-loop-health.js";
|
|
22
22
|
import { checkRealRunHealth } from "./living-loop-health-gate.js";
|
|
23
23
|
import { readActionClosuresByDay, readConnectorCooldownState, } from "../storage/v8-state-stores.js";
|
|
24
|
+
import { classifyEvidenceLevel } from "../shared/evidence-level-classifier.js";
|
|
24
25
|
// ───────────────────────────────────────────────────────────────
|
|
25
26
|
// Helpers
|
|
26
27
|
// ───────────────────────────────────────────────────────────────
|
|
@@ -163,7 +164,7 @@ export async function attributeDenials(db, options) {
|
|
|
163
164
|
// ───────────────────────────────────────────────────────────────
|
|
164
165
|
export async function readLoopStatus(db) {
|
|
165
166
|
const health = await assembleLoopStatus(db, { limit: 50 });
|
|
166
|
-
if ("
|
|
167
|
+
if ("ownerStage" in health) {
|
|
167
168
|
return {
|
|
168
169
|
ok: false,
|
|
169
170
|
degraded: health,
|
|
@@ -214,6 +215,17 @@ export async function readLoopStatus(db) {
|
|
|
214
215
|
overallStatus = "healthy";
|
|
215
216
|
stalledAt = undefined;
|
|
216
217
|
}
|
|
218
|
+
// T-OBS.R.7: derive evidenceLevel from observed proof
|
|
219
|
+
const evidenceLevel = classifyEvidenceLevel({
|
|
220
|
+
hasCarrierEnvelope: true, // loop_status command envelope itself
|
|
221
|
+
hasContractSmoke: snapshot.lastCycleSequence > 0 && !realRunHealth.hasRealClosure,
|
|
222
|
+
hasStatePresent: snapshot.lastCycleSequence === 0 &&
|
|
223
|
+
(realRunHealth.hasQuietArtifact ||
|
|
224
|
+
realRunHealth.hasDreamArtifact ||
|
|
225
|
+
realRunHealth.hasProjectionFeedback),
|
|
226
|
+
hasCycleExecution: snapshot.lastCycleSequence > 0 && realRunHealth.hasRealClosure,
|
|
227
|
+
hasReadbackVerification: realRunHealth.gatePassed,
|
|
228
|
+
});
|
|
217
229
|
const stageSummaries = snapshot.stages.map((s) => ({
|
|
218
230
|
stage: s.stage,
|
|
219
231
|
eventCount: s.eventCount,
|
|
@@ -240,6 +252,7 @@ export async function readLoopStatus(db) {
|
|
|
240
252
|
connectorTerminalCount: attribution.connectorTerminalCount,
|
|
241
253
|
nextAction,
|
|
242
254
|
realRunHealth,
|
|
255
|
+
evidenceLevel,
|
|
243
256
|
},
|
|
244
257
|
};
|
|
245
258
|
}
|
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
*/
|
|
33
33
|
import type { AppendOnlyAuditStore } from "../audit/append-only-audit-store.js";
|
|
34
34
|
import type { StateDatabase } from "../../storage/db/index.js";
|
|
35
|
+
import type { EvidenceLevel } from "../../shared/types/v8-contracts.js";
|
|
35
36
|
export interface ConnectorDaySummary {
|
|
36
37
|
platformId: string;
|
|
37
38
|
capability: string;
|
|
@@ -91,6 +92,8 @@ export interface HeartbeatDigest {
|
|
|
91
92
|
healthSummary: HealthDaySummary;
|
|
92
93
|
/** Real-run health gate result (T-OBS.R.3) */
|
|
93
94
|
realRunHealth: RealRunHealthDigestProjection;
|
|
95
|
+
/** T-OBS.R.7: evidence level for this digest */
|
|
96
|
+
evidenceLevel: EvidenceLevel;
|
|
94
97
|
/** Set when delivery succeeded */
|
|
95
98
|
deliveredAt?: string;
|
|
96
99
|
/** Proof of successful delivery (channel + message hash, no raw content) */
|
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
* tests/integration/observability/digest-delivery.test.ts (T-OBS.C.4)
|
|
32
32
|
*/
|
|
33
33
|
import { checkRealRunHealth } from "../living-loop-health-gate.js";
|
|
34
|
+
import { classifyEvidenceLevel } from "../../shared/evidence-level-classifier.js";
|
|
34
35
|
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
35
36
|
function isSameDayUtc(isoTimestamp, dateStr) {
|
|
36
37
|
// dateStr: "YYYY-MM-DD"
|
|
@@ -262,6 +263,13 @@ export async function generateHeartbeatDigest(date, deps) {
|
|
|
262
263
|
};
|
|
263
264
|
}
|
|
264
265
|
}
|
|
266
|
+
const evidenceLevel = classifyEvidenceLevel({
|
|
267
|
+
hasCarrierEnvelope: true,
|
|
268
|
+
hasContractSmoke: !deps.db,
|
|
269
|
+
hasStatePresent: Boolean(deps.db) && !realRunHealth.gatePassed && !realRunHealth.hasRealClosure,
|
|
270
|
+
hasCycleExecution: realRunHealth.hasRealClosure,
|
|
271
|
+
hasReadbackVerification: realRunHealth.gatePassed,
|
|
272
|
+
});
|
|
265
273
|
const digest = {
|
|
266
274
|
date,
|
|
267
275
|
generatedAt,
|
|
@@ -271,6 +279,7 @@ export async function generateHeartbeatDigest(date, deps) {
|
|
|
271
279
|
quietDreamSummary,
|
|
272
280
|
healthSummary,
|
|
273
281
|
realRunHealth,
|
|
282
|
+
evidenceLevel,
|
|
274
283
|
};
|
|
275
284
|
// T-OBS.C.4: delivery hook — attempt delivery if adapter is provided
|
|
276
285
|
if (deliveryAdapter) {
|