@haaaiawd/second-nature 0.2.9 → 0.2.12

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 (84) hide show
  1. package/index.js +6 -0
  2. package/openclaw.plugin.json +2 -5
  3. package/package.json +1 -1
  4. package/runtime/cli/host-capability/probe-host-capability.js +1 -1
  5. package/runtime/cli/host-capability/types.d.ts +2 -7
  6. package/runtime/cli/host-capability/types.js +0 -5
  7. package/runtime/cli/ops/heartbeat-surface.d.ts +2 -0
  8. package/runtime/cli/ops/heartbeat-surface.js +32 -3
  9. package/runtime/cli/ops/ops-router.js +3 -3
  10. package/runtime/cli/ops/workspace-heartbeat-runner.js +2 -5
  11. package/runtime/connectors/manifest/manifest-schema.d.ts +2 -2
  12. package/runtime/connectors/registry/dynamic-connector-registry.js +16 -1
  13. package/runtime/connectors/services/connector-executor-adapter.js +54 -35
  14. package/runtime/core/second-nature/action/action-closure-recorder.d.ts +2 -0
  15. package/runtime/core/second-nature/action/action-closure-recorder.js +2 -4
  16. package/runtime/core/second-nature/action/action-proposal-builder.js +5 -32
  17. package/runtime/core/second-nature/action/policy-bound-dispatch.js +4 -3
  18. package/runtime/core/second-nature/control-plane/accepted-projection-loader.js +1 -11
  19. package/runtime/core/second-nature/control-plane/heartbeat-orchestrator.d.ts +1 -0
  20. package/runtime/core/second-nature/control-plane/heartbeat-orchestrator.js +10 -6
  21. package/runtime/core/second-nature/control-plane/real-runtime-spine.d.ts +2 -0
  22. package/runtime/core/second-nature/control-plane/real-runtime-spine.js +1 -0
  23. package/runtime/core/second-nature/heartbeat/heartbeat-loop.js +4 -3
  24. package/runtime/core/second-nature/heartbeat/runtime-snapshot.d.ts +3 -2
  25. package/runtime/core/second-nature/heartbeat/snapshot-builder.d.ts +3 -2
  26. package/runtime/core/second-nature/orchestrator/intent-planner.js +4 -2
  27. package/runtime/core/second-nature/orchestrator/narrative-update.js +1 -2
  28. package/runtime/core/second-nature/orchestrator/platform-capability-router.d.ts +2 -2
  29. package/runtime/core/second-nature/orchestrator/platform-capability-router.js +1 -1
  30. package/runtime/core/second-nature/outreach/build-outreach-draft-request.js +2 -3
  31. package/runtime/core/second-nature/outreach/dispatch-user-outreach.d.ts +2 -2
  32. package/runtime/core/second-nature/outreach/dispatch-user-outreach.js +6 -1
  33. package/runtime/core/second-nature/outreach/judge-input-from-snapshot.js +3 -14
  34. package/runtime/core/second-nature/outreach/judge-outreach.d.ts +6 -5
  35. package/runtime/core/second-nature/perception/judgment-engine.js +2 -12
  36. package/runtime/core/second-nature/perception/perception-builder.js +1 -9
  37. package/runtime/core/second-nature/quiet/run-source-backed-quiet.js +13 -15
  38. package/runtime/core/second-nature/quiet-dream/daily-rhythm-scheduler.js +10 -13
  39. package/runtime/core/second-nature/quiet-dream/dream-scheduler.js +0 -2
  40. package/runtime/core/second-nature/quiet-dream/memory-projection-lifecycle.js +0 -12
  41. package/runtime/core/second-nature/quiet-dream/quiet-daily-review-builder.js +1 -11
  42. package/runtime/core/second-nature/types.d.ts +2 -9
  43. package/runtime/dream/dream-engine.js +11 -4
  44. package/runtime/guidance/outreach-draft-schema.d.ts +12 -12
  45. package/runtime/guidance/persona-selection.js +5 -0
  46. package/runtime/guidance/template-registry.js +6 -1
  47. package/runtime/guidance/types.d.ts +2 -2
  48. package/runtime/observability/living-loop-health-gate.js +2 -8
  49. package/runtime/observability/loop-stage-event-sink.js +0 -1
  50. package/runtime/observability/services/lived-experience-audit.d.ts +7 -7
  51. package/runtime/shared/serialization.d.ts +17 -0
  52. package/runtime/shared/serialization.js +27 -0
  53. package/runtime/shared/source-ref-compat.d.ts +26 -0
  54. package/runtime/shared/source-ref-compat.js +61 -0
  55. package/runtime/shared/types/goal.d.ts +4 -4
  56. package/runtime/shared/types/goal.js +1 -1
  57. package/runtime/shared/types/index.d.ts +1 -0
  58. package/runtime/shared/types/index.js +1 -3
  59. package/runtime/shared/types/source-ref.d.ts +2 -2
  60. package/runtime/shared/types/v7-entities.d.ts +5 -5
  61. package/runtime/shared/types/v7-entities.js +1 -1
  62. package/runtime/shared/types/v8-contracts.d.ts +1 -1
  63. package/runtime/storage/db/index.js +31 -26
  64. package/runtime/storage/db/migrations/index.js +4 -0
  65. package/runtime/storage/db/migrations/v8-004-schema-closure.d.ts +19 -0
  66. package/runtime/storage/db/migrations/v8-004-schema-closure.js +74 -0
  67. package/runtime/storage/db/migrations/v8-005-single-status-schema.d.ts +11 -0
  68. package/runtime/storage/db/migrations/v8-005-single-status-schema.js +16 -0
  69. package/runtime/storage/db/schema/v8-entities.d.ts +0 -95
  70. package/runtime/storage/db/schema/v8-entities.js +4 -7
  71. package/runtime/storage/delivery/types.d.ts +2 -2
  72. package/runtime/storage/fallback/load-operator-fallback.d.ts +2 -2
  73. package/runtime/storage/fallback/operator-fallback-types.d.ts +2 -2
  74. package/runtime/storage/fallback/operator-fallback-view.d.ts +2 -2
  75. package/runtime/storage/index.d.ts +1 -1
  76. package/runtime/storage/life-evidence/types.d.ts +5 -5
  77. package/runtime/storage/quiet/quiet-artifact-types.d.ts +4 -4
  78. package/runtime/storage/quiet/quiet-artifact-writer.d.ts +2 -2
  79. package/runtime/storage/services/write-validation-gate.d.ts +1 -1
  80. package/runtime/storage/services/write-validation-gate.js +16 -4
  81. package/runtime/storage/snapshots/types.d.ts +8 -8
  82. package/runtime/storage/user-interest/types.d.ts +3 -3
  83. package/runtime/storage/v8-state-stores.d.ts +8 -1
  84. package/runtime/storage/v8-state-stores.js +23 -20
@@ -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 {
@@ -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;
@@ -124,7 +124,6 @@ export async function recordLoopStageEvent(db, event, options) {
124
124
  occurredAt: event.occurredAt,
125
125
  expectedDownstreamByCycle: event.expectedDownstreamByCycle,
126
126
  payloadJson: event.payloadJson ?? null,
127
- lifecycleStatus: "completed",
128
127
  };
129
128
  const result = await writeLoopStageEvent(db, record);
130
129
  if ("id" in result) {
@@ -1,5 +1,5 @@
1
1
  import { AppendOnlyAuditStore } from "../audit/append-only-audit-store.js";
2
- import type { SourceRef } from "../../storage/life-evidence/types.js";
2
+ import type { LifeEvidenceSourceRef } from "../../storage/life-evidence/types.js";
3
3
  import type { DreamTrace } from "../../dream/types.js";
4
4
  export type RuntimeScope = "rhythm" | "user_task" | "user_reply";
5
5
  export type HeartbeatOutcome = "heartbeat_ok" | "intent_selected" | "denied" | "deferred" | "runtime_carrier_only" | "delivery_unavailable";
@@ -18,8 +18,8 @@ export interface DecisionTracePayload {
18
18
  outreachVerdict?: "allow" | "deny" | "defer";
19
19
  deliveryAuditId?: string;
20
20
  reasonCodes: string[];
21
- sourceRefs: SourceRef[];
22
- snapshotRef?: SourceRef;
21
+ sourceRefs: LifeEvidenceSourceRef[];
22
+ snapshotRef?: LifeEvidenceSourceRef;
23
23
  createdAt: string;
24
24
  }
25
25
  export interface DeliveryAuditPayload {
@@ -31,7 +31,7 @@ export interface DeliveryAuditPayload {
31
31
  recipientRef?: string;
32
32
  status: DeliveryAuditStatus;
33
33
  messageId?: string;
34
- hostProofRef?: SourceRef;
34
+ hostProofRef?: LifeEvidenceSourceRef;
35
35
  fallbackRef?: string;
36
36
  ackDropMatched?: boolean;
37
37
  hostVersion?: string;
@@ -45,8 +45,8 @@ export interface SourceCoverageAuditPayload {
45
45
  decisionId?: string;
46
46
  subjectType: "quiet_artifact" | "outreach_draft" | "guidance_payload" | "decision_trace" | "host_report";
47
47
  subjectRef: string;
48
- usedSourceRefs: SourceRef[];
49
- unresolvedRefs: SourceRef[];
48
+ usedSourceRefs: LifeEvidenceSourceRef[];
49
+ unresolvedRefs: LifeEvidenceSourceRef[];
50
50
  coverageRatio: number;
51
51
  unsupportedClaims: string[];
52
52
  status: GroundingStatus;
@@ -61,7 +61,7 @@ export interface GuidanceGroundingAuditPayload {
61
61
  draftId?: string;
62
62
  sceneType: "outreach" | "quiet_reflection" | "social" | "explain" | "user_reply_continuity" | "fallback_candidate";
63
63
  groundingStatus: GroundingStatus;
64
- usedSourceRefs: SourceRef[];
64
+ usedSourceRefs: LifeEvidenceSourceRef[];
65
65
  unsupportedClaims: string[];
66
66
  guardViolations: string[];
67
67
  deliveryWording?: "sendable" | "not_sent_fallback_candidate";
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Shared serialization helpers for cross-system value types.
3
+ *
4
+ * Core logic:
5
+ * - `parseSourceRefs` / `serializeSourceRefs` provide the single round-trip
6
+ * implementation for `sourceRefsJson` / `source_refs_json` columns.
7
+ * - Failures are silent on read (return empty array) because malformed JSON
8
+ * in persisted state must not crash downstream consumers; callers that need
9
+ * to distinguish malformed rows should validate separately.
10
+ *
11
+ * Dependencies: `src/shared/types/v8-contracts.js` (SourceRef)
12
+ * Boundary: Pure functions; no storage or business logic.
13
+ * Test coverage: `tests/unit/shared/source-ref-serialization.test.ts`
14
+ */
15
+ import type { SourceRef } from "./types/v8-contracts.js";
16
+ export declare function serializeSourceRefs(refs: SourceRef[]): string;
17
+ export declare function parseSourceRefs(json: string | null | undefined): SourceRef[];
@@ -0,0 +1,27 @@
1
+ export function serializeSourceRefs(refs) {
2
+ return JSON.stringify(refs);
3
+ }
4
+ function isSourceRef(value) {
5
+ if (!value || typeof value !== "object" || Array.isArray(value))
6
+ return false;
7
+ const candidate = value;
8
+ return (typeof candidate.uri === "string" &&
9
+ typeof candidate.family === "string" &&
10
+ typeof candidate.id === "string" &&
11
+ typeof candidate.redactionClass === "string" &&
12
+ (candidate.sensitivityClass === undefined ||
13
+ typeof candidate.sensitivityClass === "string"));
14
+ }
15
+ export function parseSourceRefs(json) {
16
+ if (!json)
17
+ return [];
18
+ try {
19
+ const parsed = JSON.parse(json);
20
+ if (Array.isArray(parsed) && parsed.every(isSourceRef))
21
+ return parsed;
22
+ return [];
23
+ }
24
+ catch {
25
+ return [];
26
+ }
27
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * SourceRef compatibility adapters.
3
+ *
4
+ * Core logic: convert legacy v5/v7 source-ref shapes that used `kind` into
5
+ * the v8 canonical `SourceRef` shape (`family` + `redactionClass`).
6
+ * Dependencies: shared v8 contracts only.
7
+ * Boundary: compatibility at old/new system seams; canonical v8 modules should
8
+ * pass `SourceRef` directly without reintroducing local clones.
9
+ * Test coverage: Wave 113 typecheck and affected control-plane/host/life-evidence tests.
10
+ */
11
+ import type { SourceRef, SourceRefFamily } from "./types/v8-contracts.js";
12
+ export type LegacySourceRefKind = "platform_item" | "workspace_artifact" | "decision_record" | "user_anchor" | "connector_result" | "host_report" | "fallback_artifact";
13
+ export interface LegacySourceRefLike {
14
+ id: string;
15
+ kind?: LegacySourceRefKind | string;
16
+ uri: string;
17
+ }
18
+ export declare function sourceRefFamilyFromLegacyKind(kind: LegacySourceRefKind | string | undefined): SourceRefFamily;
19
+ export declare function legacyKindFromSourceRef(ref: SourceRef): LegacySourceRefKind;
20
+ export declare function toCanonicalSourceRef(ref: LegacySourceRefLike): SourceRef;
21
+ export declare function makeCanonicalSourceRef(input: {
22
+ id: string;
23
+ family: SourceRefFamily;
24
+ uri: string;
25
+ redactionClass?: SourceRef["redactionClass"];
26
+ }): SourceRef;
@@ -0,0 +1,61 @@
1
+ export function sourceRefFamilyFromLegacyKind(kind) {
2
+ switch (kind) {
3
+ case "connector_result":
4
+ return "connector_result";
5
+ case "decision_record":
6
+ return "judgment";
7
+ case "platform_item":
8
+ case "user_anchor":
9
+ return "evidence";
10
+ case "workspace_artifact":
11
+ case "host_report":
12
+ case "fallback_artifact":
13
+ default:
14
+ return "audit";
15
+ }
16
+ }
17
+ export function legacyKindFromSourceRef(ref) {
18
+ if (ref.uri.startsWith("platform://")) {
19
+ return "platform_item";
20
+ }
21
+ if (ref.uri.startsWith("goal://") || ref.uri.startsWith("workspace://")) {
22
+ return "workspace_artifact";
23
+ }
24
+ if (ref.id.startsWith("anchor:") || ref.id.startsWith("curated:") || /(?:USER|MEMORY)\.md$/i.test(ref.uri)) {
25
+ return "user_anchor";
26
+ }
27
+ switch (ref.family) {
28
+ case "connector_result":
29
+ return "connector_result";
30
+ case "judgment":
31
+ return "decision_record";
32
+ case "evidence":
33
+ case "perception":
34
+ return "platform_item";
35
+ case "audit":
36
+ case "action_closure":
37
+ case "quiet_review":
38
+ case "dream_run":
39
+ case "memory_projection":
40
+ case "projection":
41
+ case "tool_experience":
42
+ default:
43
+ return "workspace_artifact";
44
+ }
45
+ }
46
+ export function toCanonicalSourceRef(ref) {
47
+ return {
48
+ id: ref.id,
49
+ uri: ref.uri,
50
+ family: sourceRefFamilyFromLegacyKind(ref.kind),
51
+ redactionClass: "none",
52
+ };
53
+ }
54
+ export function makeCanonicalSourceRef(input) {
55
+ return {
56
+ id: input.id,
57
+ family: input.family,
58
+ uri: input.uri,
59
+ redactionClass: input.redactionClass ?? "none",
60
+ };
61
+ }
@@ -7,7 +7,7 @@
7
7
  * - `status` supports full v7 lifecycle including paused → expired/replaced.
8
8
  *
9
9
  * Dependencies:
10
- * - `SourceRef` from `./source-ref.js` for grounding.
10
+ * - `SourceRefTuple` from `./source-ref.js` for grounding.
11
11
  *
12
12
  * Boundary:
13
13
  * - Used by state-memory (GoalLifecycleStore), control-plane
@@ -17,7 +17,7 @@
17
17
  * Test coverage: tests/unit/shared/v7-entities.test.ts (invalid kind
18
18
  * `@ts-expect-error` compile guard).
19
19
  */
20
- import type { SourceRef } from "./source-ref.js";
20
+ import type { SourceRefTuple } from "./source-ref.js";
21
21
  export type AgentGoalKind = "short_term" | "long_term" | "habit" | "maintenance" | "passive_sensing" | "outreach" | "exploration";
22
22
  export type AgentGoalStatus = "proposal" | "accepted" | "rejected" | "completed" | "paused" | "expired" | "replaced";
23
23
  export type AgentGoalOrigin = "owner_set" | "agent_proposed" | "policy_seeded";
@@ -32,7 +32,7 @@ export interface AgentGoal {
32
32
  completionCriteria: string;
33
33
  risk: "low" | "medium" | "high";
34
34
  priorityHint: number;
35
- sourceRefs: SourceRef;
35
+ sourceRefs: SourceRefTuple;
36
36
  acceptedBy?: "owner" | "policy_allowlist";
37
37
  expiresAt?: string;
38
38
  createdAt: string;
@@ -48,7 +48,7 @@ export interface AgentGoalWrite {
48
48
  completionCriteria: string;
49
49
  risk: "low" | "medium" | "high";
50
50
  priorityHint: number;
51
- sourceRefs: SourceRef;
51
+ sourceRefs: SourceRefTuple;
52
52
  acceptedBy?: "owner" | "policy_allowlist";
53
53
  expiresAt?: string;
54
54
  createdAt: string;
@@ -7,7 +7,7 @@
7
7
  * - `status` supports full v7 lifecycle including paused → expired/replaced.
8
8
  *
9
9
  * Dependencies:
10
- * - `SourceRef` from `./source-ref.js` for grounding.
10
+ * - `SourceRefTuple` from `./source-ref.js` for grounding.
11
11
  *
12
12
  * Boundary:
13
13
  * - Used by state-memory (GoalLifecycleStore), control-plane
@@ -4,3 +4,4 @@ export * from "./outreach.js";
4
4
  export * from "./source-ref.js";
5
5
  export * from "./goal.js";
6
6
  export * from "./v7-entities.js";
7
+ export * from "./v8-contracts.js";
@@ -4,6 +4,4 @@ export * from "./outreach.js";
4
4
  export * from "./source-ref.js";
5
5
  export * from "./goal.js";
6
6
  export * from "./v7-entities.js";
7
- // v8-contracts intentionally NOT re-exported from index to avoid SourceRef
8
- // name collision with v7 tuple type. v8 consumers import directly from
9
- // `./v8-contracts.js`.
7
+ export * from "./v8-contracts.js";
@@ -1,5 +1,5 @@
1
1
  /**
2
- * SourceRef — v7 non-empty tuple for source grounding.
2
+ * SourceRefTuple — v7 non-empty tuple for source grounding.
3
3
  *
4
4
  * Core logic: Every fact claim must carry at least one source reference.
5
5
  * DR-025 enforces non-empty at compile time. Empty array assignments are
@@ -11,4 +11,4 @@
11
11
  * Test coverage: tests/unit/shared/v7-entities.test.ts (compile-time
12
12
  * `@ts-expect-error` guard for empty tuple).
13
13
  */
14
- export type SourceRef = readonly [string, ...string[]];
14
+ export type SourceRefTuple = readonly [string, ...string[]];
@@ -12,7 +12,7 @@
12
12
  * - ADR-002/003/007/008 — entity semantics
13
13
  *
14
14
  * Dependencies:
15
- * - `SourceRef` from `./source-ref.js`
15
+ * - `SourceRefTuple` from `./source-ref.js`
16
16
  * - `AgentGoal` from `./goal.js`
17
17
  *
18
18
  * Boundary:
@@ -24,7 +24,7 @@
24
24
  *
25
25
  * Test coverage: tests/unit/shared/v7-entities.test.ts
26
26
  */
27
- import type { SourceRef } from "./source-ref.js";
27
+ import type { SourceRefTuple } from "./source-ref.js";
28
28
  import type { AgentGoal } from "./goal.js";
29
29
  export interface PlatformHandle {
30
30
  platformId: string;
@@ -60,7 +60,7 @@ export interface ToolExperience {
60
60
  failureClass?: string;
61
61
  latencyMs: number;
62
62
  evidenceQuality: number;
63
- sourceRefs: SourceRef;
63
+ sourceRefs: SourceRefTuple;
64
64
  triggerSource: ToolExperienceTriggerSource;
65
65
  createdAt: string;
66
66
  }
@@ -97,7 +97,7 @@ export interface QuietClaim {
97
97
  claimId: string;
98
98
  kind: QuietClaimKind;
99
99
  text: string;
100
- sourceRefs: SourceRef;
100
+ sourceRefs: SourceRefTuple;
101
101
  confidence: number;
102
102
  createdAt: string;
103
103
  }
@@ -107,7 +107,7 @@ export interface DailyDiary {
107
107
  observedToday: string[];
108
108
  notableSignals: string[];
109
109
  tomorrowDirection: string;
110
- sourceRefs: SourceRef;
110
+ sourceRefs: SourceRefTuple;
111
111
  createdAt: string;
112
112
  }
113
113
  export type DreamOutputStatus = "candidate" | "accepted" | "archived" | "partial";
@@ -12,7 +12,7 @@
12
12
  * - ADR-002/003/007/008 — entity semantics
13
13
  *
14
14
  * Dependencies:
15
- * - `SourceRef` from `./source-ref.js`
15
+ * - `SourceRefTuple` from `./source-ref.js`
16
16
  * - `AgentGoal` from `./goal.js`
17
17
  *
18
18
  * Boundary:
@@ -82,5 +82,5 @@ export interface DegradedOperationResult {
82
82
  operatorNextAction: string;
83
83
  retryable: boolean;
84
84
  }
85
- export type V8ReasonCode = "quiet_completed" | "quiet_empty_input" | "quiet_state_unreadable" | "quiet_validation_failed" | "quiet_redaction_blocked" | "dream_scheduled" | "dream_scheduled_stalled" | "dream_scheduler_unavailable" | "dream_started" | "dream_completed" | "dream_failed" | "dream_blocked_redaction" | "dream_rules_only" | "dream_model_timeout" | "projection_candidate_created" | "projection_accepted" | "projection_rejected" | "projection_superseded" | "projection_topic_matched" | "proposal_created" | "proposal_no_action" | "proposal_missing_source_refs" | "proposal_risk_blocked" | "policy_allowed" | "policy_deferred_owner_confirmation" | "policy_downgraded_to_draft" | "policy_denied_missing_permission" | "policy_denied_high_risk" | "policy_denied_breaker_open" | "guidance_unavailable" | "closure_completed" | "closure_no_action" | "closure_denied" | "closure_deferred" | "closure_downgraded" | "closure_downgraded_without_draft" | "closure_failed" | "perception_rules_only" | "perception_contract_drift" | "evidence_batch_empty" | "evidence_batch_truncated" | "evidence_content_missing" | "judgment_low_confidence" | "judgment_missing_source_refs" | "source_refs_unresolved" | "state_unreadable" | "stage_event_missing" | "ingestion_no_data" | "ingestion_empty" | "ingestion_state_unreadable" | "ingestion_connector_failed" | "execution_completed" | "execution_failed" | "execution_timeout" | "execution_unavailable";
85
+ export type V8ReasonCode = "quiet_completed" | "quiet_empty_input" | "quiet_state_unreadable" | "quiet_validation_failed" | "quiet_redaction_blocked" | "dream_scheduled" | "dream_scheduled_stalled" | "dream_scheduler_unavailable" | "dream_started" | "dream_completed" | "dream_failed" | "dream_blocked_redaction" | "dream_interval_active" | "dream_rules_only" | "dream_model_timeout" | "projection_candidate_created" | "projection_accepted" | "projection_rejected" | "projection_superseded" | "projection_topic_matched" | "proposal_created" | "proposal_no_action" | "proposal_missing_source_refs" | "proposal_risk_blocked" | "policy_allowed" | "policy_deferred_owner_confirmation" | "policy_downgraded_to_draft" | "policy_denied_missing_permission" | "policy_denied_high_risk" | "policy_denied_breaker_open" | "guidance_unavailable" | "closure_completed" | "closure_no_action" | "closure_denied" | "closure_deferred" | "closure_downgraded" | "closure_downgraded_without_draft" | "closure_failed" | "perception_rules_only" | "perception_contract_drift" | "evidence_batch_empty" | "evidence_batch_truncated" | "evidence_content_missing" | "judgment_low_confidence" | "judgment_missing_source_refs" | "source_refs_unresolved" | "state_unreadable" | "stage_event_missing" | "ingestion_no_data" | "ingestion_empty" | "ingestion_state_unreadable" | "ingestion_connector_failed" | "execution_completed" | "execution_failed" | "execution_timeout" | "execution_unavailable";
86
86
  export declare const ACTION_KIND_REGISTRY: Readonly<Record<PlatformNeutralActionKind, ActionKindMetadata>>;
@@ -232,11 +232,10 @@ const STATE_SCHEMA_SQL = `
232
232
  status TEXT NOT NULL,
233
233
  reason TEXT,
234
234
  next_state TEXT,
235
- source_refs_json TEXT NOT NULL,
236
- redaction_class TEXT NOT NULL DEFAULT 'none',
237
- payload_json TEXT,
238
- lifecycle_status TEXT NOT NULL DEFAULT 'closed'
239
- );
235
+ source_refs_json TEXT NOT NULL,
236
+ redaction_class TEXT NOT NULL DEFAULT 'none',
237
+ payload_json TEXT
238
+ );
240
239
  CREATE TABLE IF NOT EXISTS quiet_daily_review (
241
240
  id TEXT PRIMARY KEY,
242
241
  created_at TEXT NOT NULL,
@@ -255,22 +254,20 @@ const STATE_SCHEMA_SQL = `
255
254
  quiet_review_id TEXT NOT NULL,
256
255
  status TEXT NOT NULL,
257
256
  reason TEXT,
258
- source_refs_json TEXT NOT NULL,
259
- redaction_class TEXT NOT NULL DEFAULT 'none',
260
- payload_json TEXT,
261
- lifecycle_status TEXT NOT NULL DEFAULT 'pending'
262
- );
257
+ source_refs_json TEXT NOT NULL,
258
+ redaction_class TEXT NOT NULL DEFAULT 'none',
259
+ payload_json TEXT
260
+ );
263
261
  CREATE TABLE IF NOT EXISTS long_term_memory_projection (
264
262
  id TEXT PRIMARY KEY,
265
263
  created_at TEXT NOT NULL,
266
264
  candidate_id TEXT NOT NULL,
267
265
  topic_key TEXT NOT NULL,
268
266
  status TEXT NOT NULL DEFAULT 'candidate',
269
- source_refs_json TEXT NOT NULL,
270
- redaction_class TEXT NOT NULL DEFAULT 'none',
271
- payload_json TEXT,
272
- lifecycle_status TEXT NOT NULL DEFAULT 'candidate'
273
- );
267
+ source_refs_json TEXT NOT NULL,
268
+ redaction_class TEXT NOT NULL DEFAULT 'none',
269
+ payload_json TEXT
270
+ );
274
271
  CREATE TABLE IF NOT EXISTS heartbeat_cycle_trace (
275
272
  id TEXT PRIMARY KEY,
276
273
  cycle_sequence INTEGER NOT NULL,
@@ -279,12 +276,11 @@ const STATE_SCHEMA_SQL = `
279
276
  input_count INTEGER NOT NULL DEFAULT 0,
280
277
  output_count INTEGER NOT NULL DEFAULT 0,
281
278
  expected_downstream_by_cycle INTEGER,
282
- status TEXT NOT NULL,
283
- source_refs_json TEXT,
284
- redaction_class TEXT NOT NULL DEFAULT 'none',
285
- payload_json TEXT,
286
- lifecycle_status TEXT NOT NULL DEFAULT 'started'
287
- );
279
+ status TEXT NOT NULL,
280
+ source_refs_json TEXT,
281
+ redaction_class TEXT NOT NULL DEFAULT 'none',
282
+ payload_json TEXT
283
+ );
288
284
  CREATE TABLE IF NOT EXISTS loop_stage_event (
289
285
  id TEXT PRIMARY KEY,
290
286
  cycle_id TEXT NOT NULL,
@@ -294,11 +290,10 @@ const STATE_SCHEMA_SQL = `
294
290
  reason TEXT,
295
291
  source_refs_json TEXT NOT NULL,
296
292
  redaction_class TEXT NOT NULL DEFAULT 'none',
297
- occurred_at TEXT NOT NULL,
298
- expected_downstream_by_cycle INTEGER,
299
- payload_json TEXT,
300
- lifecycle_status TEXT NOT NULL DEFAULT 'started'
301
- );
293
+ occurred_at TEXT NOT NULL,
294
+ expected_downstream_by_cycle INTEGER,
295
+ payload_json TEXT
296
+ );
302
297
  CREATE TABLE IF NOT EXISTS impulse_context_artifact (
303
298
  id TEXT PRIMARY KEY,
304
299
  created_at TEXT NOT NULL,
@@ -366,12 +361,22 @@ function bootstrapStateSchema(sqlite) {
366
361
  runMigrations(sqlite, ALL_MIGRATIONS);
367
362
  }
368
363
  function applyStateSchemaMigrations(sqlite) {
364
+ // Defensive column/index additions for DBs that were initialized before
365
+ // v8-004-schema-closure. Fresh DBs already have these from bootstrap SQL.
366
+ // Each statement is wrapped individually so duplicate-column errors are
367
+ // harmless and do not block startup.
369
368
  const migrations = [
370
369
  "ALTER TABLE policy_records ADD COLUMN outreach_daily_budget INTEGER NOT NULL DEFAULT 2",
371
370
  "ALTER TABLE action_closure_record ADD COLUMN platform_id TEXT",
372
371
  "ALTER TABLE action_closure_record ADD COLUMN capability_id TEXT",
372
+ "ALTER TABLE quiet_daily_review ADD COLUMN closure_refs_json TEXT",
373
373
  "ALTER TABLE connector_cooldown_state ADD COLUMN terminal_count INTEGER NOT NULL DEFAULT 0",
374
374
  "CREATE INDEX IF NOT EXISTS connector_cooldown_state_platform_capability_idx ON connector_cooldown_state(platform_id, capability_id)",
375
+ "ALTER TABLE action_closure_record DROP COLUMN lifecycle_status",
376
+ "ALTER TABLE dream_consolidation_run DROP COLUMN lifecycle_status",
377
+ "ALTER TABLE long_term_memory_projection DROP COLUMN lifecycle_status",
378
+ "ALTER TABLE heartbeat_cycle_trace DROP COLUMN lifecycle_status",
379
+ "ALTER TABLE loop_stage_event DROP COLUMN lifecycle_status",
375
380
  ];
376
381
  for (const sql of migrations) {
377
382
  try {