@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.
Files changed (115) hide show
  1. package/index.js +102 -6
  2. package/openclaw.plugin.json +2 -5
  3. package/package.json +1 -1
  4. package/runtime/cli/commands/index.js +85 -11
  5. package/runtime/cli/host-capability/host-discovery-port.d.ts +85 -0
  6. package/runtime/cli/host-capability/host-discovery-port.js +137 -0
  7. package/runtime/cli/host-capability/probe-host-capability.js +1 -1
  8. package/runtime/cli/host-capability/types.d.ts +2 -7
  9. package/runtime/cli/host-capability/types.js +0 -5
  10. package/runtime/cli/ops/heartbeat-surface.d.ts +5 -3
  11. package/runtime/cli/ops/heartbeat-surface.js +38 -8
  12. package/runtime/cli/ops/ops-router.d.ts +6 -2
  13. package/runtime/cli/ops/ops-router.js +1275 -1147
  14. package/runtime/cli/ops/workspace-heartbeat-runner.js +2 -5
  15. package/runtime/connectors/base/normalized-evidence-content.d.ts +4 -0
  16. package/runtime/connectors/base/normalized-evidence-content.js +21 -2
  17. package/runtime/connectors/evidence-normalizer.js +32 -1
  18. package/runtime/connectors/manifest/manifest-schema.d.ts +2 -2
  19. package/runtime/connectors/registry/dynamic-connector-registry.js +16 -1
  20. package/runtime/connectors/services/connector-executor-adapter.js +54 -35
  21. package/runtime/core/second-nature/action/action-closure-recorder.d.ts +4 -0
  22. package/runtime/core/second-nature/action/action-closure-recorder.js +51 -38
  23. package/runtime/core/second-nature/action/action-proposal-builder.js +8 -34
  24. package/runtime/core/second-nature/action/policy-bound-dispatch.d.ts +2 -0
  25. package/runtime/core/second-nature/action/policy-bound-dispatch.js +10 -5
  26. package/runtime/core/second-nature/control-plane/accepted-projection-loader.js +1 -11
  27. package/runtime/core/second-nature/control-plane/cycle-finalizer.d.ts +82 -0
  28. package/runtime/core/second-nature/control-plane/cycle-finalizer.js +187 -0
  29. package/runtime/core/second-nature/control-plane/heartbeat-orchestrator.d.ts +1 -0
  30. package/runtime/core/second-nature/control-plane/heartbeat-orchestrator.js +23 -15
  31. package/runtime/core/second-nature/control-plane/real-runtime-spine.d.ts +2 -0
  32. package/runtime/core/second-nature/control-plane/real-runtime-spine.js +2 -1
  33. package/runtime/core/second-nature/guidance/guidance-proposal-consumer.d.ts +2 -1
  34. package/runtime/core/second-nature/guidance/guidance-proposal-consumer.js +4 -2
  35. package/runtime/core/second-nature/heartbeat/heartbeat-loop.js +4 -3
  36. package/runtime/core/second-nature/heartbeat/runtime-snapshot.d.ts +3 -2
  37. package/runtime/core/second-nature/heartbeat/snapshot-builder.d.ts +3 -2
  38. package/runtime/core/second-nature/orchestrator/intent-planner.js +4 -2
  39. package/runtime/core/second-nature/orchestrator/narrative-update.js +1 -2
  40. package/runtime/core/second-nature/orchestrator/platform-capability-router.d.ts +2 -2
  41. package/runtime/core/second-nature/orchestrator/platform-capability-router.js +1 -1
  42. package/runtime/core/second-nature/outreach/build-outreach-draft-request.js +2 -3
  43. package/runtime/core/second-nature/outreach/dispatch-user-outreach.d.ts +2 -2
  44. package/runtime/core/second-nature/outreach/dispatch-user-outreach.js +6 -1
  45. package/runtime/core/second-nature/outreach/judge-input-from-snapshot.js +3 -14
  46. package/runtime/core/second-nature/outreach/judge-outreach.d.ts +6 -5
  47. package/runtime/core/second-nature/perception/judgment-engine.js +10 -16
  48. package/runtime/core/second-nature/perception/perception-builder.js +15 -11
  49. package/runtime/core/second-nature/quiet/run-source-backed-quiet.js +13 -15
  50. package/runtime/core/second-nature/quiet-dream/daily-rhythm-scheduler.js +40 -16
  51. package/runtime/core/second-nature/quiet-dream/dream-consolidation-runner.d.ts +5 -1
  52. package/runtime/core/second-nature/quiet-dream/dream-consolidation-runner.js +68 -29
  53. package/runtime/core/second-nature/quiet-dream/dream-scheduler.js +2 -3
  54. package/runtime/core/second-nature/quiet-dream/memory-projection-lifecycle.js +2 -13
  55. package/runtime/core/second-nature/quiet-dream/quiet-daily-review-builder.d.ts +1 -0
  56. package/runtime/core/second-nature/quiet-dream/quiet-daily-review-builder.js +34 -11
  57. package/runtime/core/second-nature/types.d.ts +2 -9
  58. package/runtime/dream/dream-engine.js +11 -4
  59. package/runtime/guidance/outreach-draft-schema.d.ts +12 -12
  60. package/runtime/guidance/persona-selection.js +5 -0
  61. package/runtime/guidance/template-registry.js +6 -1
  62. package/runtime/guidance/types.d.ts +2 -2
  63. package/runtime/observability/causal-loop-health.d.ts +2 -1
  64. package/runtime/observability/causal-loop-health.js +7 -0
  65. package/runtime/observability/living-loop-health-gate.js +2 -8
  66. package/runtime/observability/loop-stage-event-sink.js +6 -2
  67. package/runtime/observability/loop-status.d.ts +2 -0
  68. package/runtime/observability/loop-status.js +14 -1
  69. package/runtime/observability/services/heartbeat-digest-assembler.d.ts +3 -0
  70. package/runtime/observability/services/heartbeat-digest-assembler.js +9 -0
  71. package/runtime/observability/services/lived-experience-audit.d.ts +7 -7
  72. package/runtime/shared/degraded-status-classifier.d.ts +16 -0
  73. package/runtime/shared/degraded-status-classifier.js +68 -0
  74. package/runtime/shared/evidence-level-classifier.d.ts +61 -0
  75. package/runtime/shared/evidence-level-classifier.js +116 -0
  76. package/runtime/shared/provenance-tier.d.ts +37 -0
  77. package/runtime/shared/provenance-tier.js +97 -0
  78. package/runtime/shared/serialization.d.ts +17 -0
  79. package/runtime/shared/serialization.js +27 -0
  80. package/runtime/shared/setup-ack.d.ts +54 -0
  81. package/runtime/shared/setup-ack.js +108 -0
  82. package/runtime/shared/source-ref-compat.d.ts +26 -0
  83. package/runtime/shared/source-ref-compat.js +64 -0
  84. package/runtime/shared/types/goal.d.ts +4 -4
  85. package/runtime/shared/types/goal.js +1 -1
  86. package/runtime/shared/types/index.d.ts +1 -0
  87. package/runtime/shared/types/index.js +1 -3
  88. package/runtime/shared/types/source-ref.d.ts +2 -2
  89. package/runtime/shared/types/v7-entities.d.ts +5 -5
  90. package/runtime/shared/types/v7-entities.js +1 -1
  91. package/runtime/shared/types/v8-contracts.d.ts +13 -2
  92. package/runtime/storage/db/index.js +60 -12
  93. package/runtime/storage/db/migrations/index.js +4 -0
  94. package/runtime/storage/db/migrations/v8-004-schema-closure.d.ts +19 -0
  95. package/runtime/storage/db/migrations/v8-004-schema-closure.js +74 -0
  96. package/runtime/storage/db/migrations/v8-005-single-status-schema.d.ts +11 -0
  97. package/runtime/storage/db/migrations/v8-005-single-status-schema.js +16 -0
  98. package/runtime/storage/db/migrations/v8-006-loop-stage-event-proof-trace-columns.d.ts +9 -0
  99. package/runtime/storage/db/migrations/v8-006-loop-stage-event-proof-trace-columns.js +15 -0
  100. package/runtime/storage/db/schema/v8-entities.d.ts +65 -84
  101. package/runtime/storage/db/schema/v8-entities.js +8 -7
  102. package/runtime/storage/delivery/types.d.ts +2 -2
  103. package/runtime/storage/fallback/load-operator-fallback.d.ts +2 -2
  104. package/runtime/storage/fallback/operator-fallback-types.d.ts +2 -2
  105. package/runtime/storage/fallback/operator-fallback-view.d.ts +2 -2
  106. package/runtime/storage/index.d.ts +1 -1
  107. package/runtime/storage/life-evidence/types.d.ts +5 -5
  108. package/runtime/storage/quiet/quiet-artifact-types.d.ts +4 -4
  109. package/runtime/storage/quiet/quiet-artifact-writer.d.ts +2 -2
  110. package/runtime/storage/services/write-validation-gate.d.ts +1 -1
  111. package/runtime/storage/services/write-validation-gate.js +15 -3
  112. package/runtime/storage/snapshots/types.d.ts +8 -8
  113. package/runtime/storage/user-interest/types.d.ts +3 -3
  114. package/runtime/storage/v8-state-stores.d.ts +15 -3
  115. 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: "degraded",
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: "degraded",
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: ControlPlaneSourceRef[];
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
- modelResult = await Promise.race([modelPromise, timeoutPromise]);
172
- mode = "hybrid_llm";
173
- llmCostUsd = modelResult.costUsd;
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: "platform_item" | "workspace_artifact" | "decision_record" | "user_anchor" | "connector_result" | "host_report" | "fallback_artifact";
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: "platform_item" | "workspace_artifact" | "decision_record" | "user_anchor" | "connector_result" | "host_report" | "fallback_artifact";
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: "platform_item" | "workspace_artifact" | "decision_record" | "user_anchor" | "connector_result" | "host_report" | "fallback_artifact";
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: "platform_item" | "workspace_artifact" | "decision_record" | "user_anchor" | "connector_result" | "host_report" | "fallback_artifact";
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 sourceRefsJson = closure.sourceRefsJson ?? "[]";
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: "degraded",
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 ("status" in health && health.status === "degraded") {
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) {