@haaaiawd/second-nature 0.1.27 → 0.1.29

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 (157) hide show
  1. package/SKILL.md +35 -33
  2. package/agent-inner-guide.md +144 -124
  3. package/index.js +76 -1
  4. package/openclaw.plugin.json +2 -2
  5. package/package.json +2 -1
  6. package/runtime/cli/commands/connector-behavior.d.ts +20 -0
  7. package/runtime/cli/commands/connector-behavior.js +160 -0
  8. package/runtime/cli/commands/index.js +8 -0
  9. package/runtime/cli/index.js +9 -2
  10. package/runtime/cli/ops/manual-run-dispatcher.d.ts +79 -0
  11. package/runtime/cli/ops/manual-run-dispatcher.js +110 -0
  12. package/runtime/cli/ops/ops-router.d.ts +45 -4
  13. package/runtime/cli/ops/ops-router.js +543 -2
  14. package/runtime/cli/read-models/index.js +35 -18
  15. package/runtime/cli/read-models/types.d.ts +1 -0
  16. package/runtime/connectors/agent-network/agent-world/adapter.d.ts +1 -0
  17. package/runtime/connectors/agent-network/agent-world/adapter.js +2 -2
  18. package/runtime/connectors/base/contract.d.ts +4 -1
  19. package/runtime/connectors/base/contract.js +5 -1
  20. package/runtime/connectors/base/effect-commit-ledger-sqlite.d.ts +31 -0
  21. package/runtime/connectors/base/effect-commit-ledger-sqlite.js +86 -0
  22. package/runtime/connectors/base/failure-taxonomy.js +5 -0
  23. package/runtime/connectors/base/manifest-v7.d.ts +151 -0
  24. package/runtime/connectors/base/manifest-v7.js +170 -0
  25. package/runtime/connectors/base/manifest.d.ts +67 -77
  26. package/runtime/connectors/base/manifest.js +7 -7
  27. package/runtime/connectors/base/route-planner.js +11 -8
  28. package/runtime/connectors/base/structured-unavailable-reason.d.ts +59 -0
  29. package/runtime/connectors/base/structured-unavailable-reason.js +113 -0
  30. package/runtime/connectors/base/wet-probe-runner.d.ts +40 -0
  31. package/runtime/connectors/base/wet-probe-runner.js +132 -0
  32. package/runtime/connectors/manifest/manifest-schema.d.ts +4 -0
  33. package/runtime/connectors/manifest/manifest-schema.js +2 -0
  34. package/runtime/connectors/services/connector-executor-adapter.d.ts +1 -0
  35. package/runtime/connectors/services/connector-executor-adapter.js +132 -26
  36. package/runtime/core/second-nature/body/behavior-promotion/behavior-promotion-loop.d.ts +45 -0
  37. package/runtime/core/second-nature/body/behavior-promotion/behavior-promotion-loop.js +132 -0
  38. package/runtime/core/second-nature/body/circuit-breaker/circuit-breaker-manager.d.ts +60 -0
  39. package/runtime/core/second-nature/body/circuit-breaker/circuit-breaker-manager.js +174 -0
  40. package/runtime/core/second-nature/body/probe-signal-adapter.d.ts +38 -0
  41. package/runtime/core/second-nature/body/probe-signal-adapter.js +60 -0
  42. package/runtime/core/second-nature/body/tool-affordance/affordance-assembler.d.ts +51 -0
  43. package/runtime/core/second-nature/body/tool-affordance/affordance-assembler.js +129 -0
  44. package/runtime/core/second-nature/body/tool-affordance/affordance-context-scope.d.ts +30 -0
  45. package/runtime/core/second-nature/body/tool-affordance/affordance-context-scope.js +92 -0
  46. package/runtime/core/second-nature/body/tool-experience/experience-writer.d.ts +34 -0
  47. package/runtime/core/second-nature/body/tool-experience/experience-writer.js +67 -0
  48. package/runtime/core/second-nature/body/tool-experience/pain-signal-query.d.ts +37 -0
  49. package/runtime/core/second-nature/body/tool-experience/pain-signal-query.js +62 -0
  50. package/runtime/core/second-nature/heartbeat/decision-trace-emitter.d.ts +29 -0
  51. package/runtime/core/second-nature/heartbeat/decision-trace-emitter.js +28 -0
  52. package/runtime/core/second-nature/heartbeat/embodied-context-assembler.d.ts +54 -0
  53. package/runtime/core/second-nature/heartbeat/embodied-context-assembler.js +164 -0
  54. package/runtime/core/second-nature/heartbeat/goal-lifecycle-policy.d.ts +37 -0
  55. package/runtime/core/second-nature/heartbeat/goal-lifecycle-policy.js +61 -0
  56. package/runtime/core/second-nature/heartbeat/idle-curiosity-policy.d.ts +37 -0
  57. package/runtime/core/second-nature/heartbeat/idle-curiosity-policy.js +60 -0
  58. package/runtime/core/second-nature/heartbeat/index.d.ts +4 -0
  59. package/runtime/core/second-nature/heartbeat/index.js +5 -0
  60. package/runtime/core/second-nature/heartbeat/run-heartbeat-cycle-v7.d.ts +63 -0
  61. package/runtime/core/second-nature/heartbeat/run-heartbeat-cycle-v7.js +118 -0
  62. package/runtime/core/second-nature/orchestrator/downstream-intent-orchestrator.d.ts +41 -0
  63. package/runtime/core/second-nature/orchestrator/downstream-intent-orchestrator.js +43 -0
  64. package/runtime/core/second-nature/orchestrator/effect-dispatcher.d.ts +2 -1
  65. package/runtime/core/second-nature/orchestrator/effect-dispatcher.js +2 -0
  66. package/runtime/core/second-nature/orchestrator/hard-guard-evaluator.d.ts +31 -0
  67. package/runtime/core/second-nature/orchestrator/hard-guard-evaluator.js +102 -0
  68. package/runtime/core/second-nature/orchestrator/index.d.ts +5 -0
  69. package/runtime/core/second-nature/orchestrator/index.js +7 -0
  70. package/runtime/core/second-nature/quiet/claim-synthesizer.d.ts +53 -0
  71. package/runtime/core/second-nature/quiet/claim-synthesizer.js +153 -0
  72. package/runtime/core/second-nature/quiet/daily-diary-writer.d.ts +29 -0
  73. package/runtime/core/second-nature/quiet/daily-diary-writer.js +92 -0
  74. package/runtime/core/second-nature/quiet/index.d.ts +5 -0
  75. package/runtime/core/second-nature/quiet/index.js +5 -0
  76. package/runtime/core/second-nature/quiet/run-source-backed-quiet.js +19 -12
  77. package/runtime/core/second-nature/types.d.ts +2 -0
  78. package/runtime/guidance/channel-feedback-ingestion-service.d.ts +88 -0
  79. package/runtime/guidance/channel-feedback-ingestion-service.js +231 -0
  80. package/runtime/guidance/guidance-draft-service.d.ts +60 -0
  81. package/runtime/guidance/guidance-draft-service.js +80 -0
  82. package/runtime/guidance/index.d.ts +3 -0
  83. package/runtime/guidance/index.js +3 -0
  84. package/runtime/guidance/outreach-draft-schema.d.ts +8 -8
  85. package/runtime/guidance/outreach-strategy-selector.d.ts +77 -0
  86. package/runtime/guidance/outreach-strategy-selector.js +211 -0
  87. package/runtime/observability/audit/append-only-audit-store.d.ts +20 -2
  88. package/runtime/observability/audit/append-only-audit-store.js +32 -6
  89. package/runtime/observability/audit/audit-envelope.d.ts +2 -1
  90. package/runtime/observability/audit/audit-envelope.js +8 -7
  91. package/runtime/observability/audit/audit-family-registry.json +66 -0
  92. package/runtime/observability/audit/family-registry.d.ts +43 -0
  93. package/runtime/observability/audit/family-registry.js +70 -0
  94. package/runtime/observability/index.d.ts +6 -1
  95. package/runtime/observability/index.js +6 -1
  96. package/runtime/observability/redaction/policy.d.ts +24 -3
  97. package/runtime/observability/redaction/policy.js +74 -0
  98. package/runtime/observability/services/heartbeat-digest-assembler.d.ts +152 -0
  99. package/runtime/observability/services/heartbeat-digest-assembler.js +248 -0
  100. package/runtime/observability/services/lived-experience-audit.js +6 -6
  101. package/runtime/observability/services/narrative-timeline-query-service.d.ts +136 -0
  102. package/runtime/observability/services/narrative-timeline-query-service.js +169 -0
  103. package/runtime/observability/services/restore-audit-service.d.ts +74 -0
  104. package/runtime/observability/services/restore-audit-service.js +79 -0
  105. package/runtime/observability/services/runtime-secret-anchor-view.d.ts +77 -0
  106. package/runtime/observability/services/runtime-secret-anchor-view.js +168 -0
  107. package/runtime/observability/services/self-health-snapshot.d.ts +92 -0
  108. package/runtime/observability/services/self-health-snapshot.js +251 -0
  109. package/runtime/shared/types/goal.d.ts +62 -0
  110. package/runtime/shared/types/goal.js +20 -0
  111. package/runtime/shared/types/index.d.ts +3 -0
  112. package/runtime/shared/types/index.js +3 -0
  113. package/runtime/shared/types/source-ref.d.ts +14 -0
  114. package/runtime/shared/types/source-ref.js +1 -0
  115. package/runtime/shared/types/v7-entities.d.ts +206 -0
  116. package/runtime/shared/types/v7-entities.js +27 -0
  117. package/runtime/storage/db/index.js +3 -0
  118. package/runtime/storage/db/migration-runner.d.ts +30 -0
  119. package/runtime/storage/db/migration-runner.js +93 -0
  120. package/runtime/storage/db/migrations/index.d.ts +5 -0
  121. package/runtime/storage/db/migrations/index.js +13 -0
  122. package/runtime/storage/db/migrations/v7-001-foundation.d.ts +13 -0
  123. package/runtime/storage/db/migrations/v7-001-foundation.js +144 -0
  124. package/runtime/storage/db/migrations/v7-002-effect-commit-ledger.d.ts +8 -0
  125. package/runtime/storage/db/migrations/v7-002-effect-commit-ledger.js +27 -0
  126. package/runtime/storage/db/migrations/v7-003-circuit-breaker.d.ts +7 -0
  127. package/runtime/storage/db/migrations/v7-003-circuit-breaker.js +26 -0
  128. package/runtime/storage/db/migrations/v7-004-behavior-promotion.d.ts +7 -0
  129. package/runtime/storage/db/migrations/v7-004-behavior-promotion.js +26 -0
  130. package/runtime/storage/db/schema/agent-goal.d.ts +38 -0
  131. package/runtime/storage/db/schema/agent-goal.js +2 -0
  132. package/runtime/storage/db/transaction-utils.d.ts +14 -0
  133. package/runtime/storage/db/transaction-utils.js +29 -0
  134. package/runtime/storage/db/write-queue.d.ts +38 -0
  135. package/runtime/storage/db/write-queue.js +97 -0
  136. package/runtime/storage/quiet/persist-quiet-artifact.js +2 -1
  137. package/runtime/storage/services/diary-dream-store.d.ts +35 -0
  138. package/runtime/storage/services/diary-dream-store.js +165 -0
  139. package/runtime/storage/services/embodied-context-state-port.d.ts +77 -0
  140. package/runtime/storage/services/embodied-context-state-port.js +115 -0
  141. package/runtime/storage/services/goal-lifecycle-store.d.ts +42 -0
  142. package/runtime/storage/services/goal-lifecycle-store.js +181 -0
  143. package/runtime/storage/services/history-digest-store.d.ts +33 -0
  144. package/runtime/storage/services/history-digest-store.js +140 -0
  145. package/runtime/storage/services/identity-profile-store.d.ts +25 -0
  146. package/runtime/storage/services/identity-profile-store.js +81 -0
  147. package/runtime/storage/services/interaction-snapshot-projector.d.ts +15 -0
  148. package/runtime/storage/services/interaction-snapshot-projector.js +35 -0
  149. package/runtime/storage/services/restore-snapshot-store.d.ts +52 -0
  150. package/runtime/storage/services/restore-snapshot-store.js +193 -0
  151. package/runtime/storage/services/runtime-secret-anchor-store.d.ts +26 -0
  152. package/runtime/storage/services/runtime-secret-anchor-store.js +82 -0
  153. package/runtime/storage/services/tool-experience-store.d.ts +25 -0
  154. package/runtime/storage/services/tool-experience-store.js +116 -0
  155. package/runtime/storage/services/write-validation-gate.d.ts +46 -0
  156. package/runtime/storage/services/write-validation-gate.js +200 -0
  157. package/workspace-ops-bridge.js +16 -1
@@ -190,6 +190,44 @@ export declare const agentGoal: import("drizzle-orm/sqlite-core").SQLiteTableWit
190
190
  }, {}, {
191
191
  length: number | undefined;
192
192
  }>;
193
+ scope: import("drizzle-orm/sqlite-core").SQLiteColumn<{
194
+ name: "scope";
195
+ tableName: "agent_goal";
196
+ dataType: "string";
197
+ columnType: "SQLiteText";
198
+ data: string;
199
+ driverParam: string;
200
+ notNull: false;
201
+ hasDefault: false;
202
+ isPrimaryKey: false;
203
+ isAutoincrement: false;
204
+ hasRuntimeDefault: false;
205
+ enumValues: [string, ...string[]];
206
+ baseColumn: never;
207
+ identity: undefined;
208
+ generated: undefined;
209
+ }, {}, {
210
+ length: number | undefined;
211
+ }>;
212
+ expiresAt: import("drizzle-orm/sqlite-core").SQLiteColumn<{
213
+ name: "expires_at";
214
+ tableName: "agent_goal";
215
+ dataType: "string";
216
+ columnType: "SQLiteText";
217
+ data: string;
218
+ driverParam: string;
219
+ notNull: false;
220
+ hasDefault: false;
221
+ isPrimaryKey: false;
222
+ isAutoincrement: false;
223
+ hasRuntimeDefault: false;
224
+ enumValues: [string, ...string[]];
225
+ baseColumn: never;
226
+ identity: undefined;
227
+ generated: undefined;
228
+ }, {}, {
229
+ length: number | undefined;
230
+ }>;
193
231
  createdAt: import("drizzle-orm/sqlite-core").SQLiteColumn<{
194
232
  name: "created_at";
195
233
  tableName: "agent_goal";
@@ -10,6 +10,8 @@ export const agentGoal = sqliteTable("agent_goal", {
10
10
  priorityHint: integer("priority_hint").notNull().default(0),
11
11
  sourceRefsJson: text("source_refs_json").notNull(),
12
12
  acceptedBy: text("accepted_by"),
13
+ scope: text("scope"),
14
+ expiresAt: text("expires_at"),
13
15
  createdAt: text("created_at").notNull(),
14
16
  updatedAt: text("updated_at").notNull(),
15
17
  }, (table) => [
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Transaction utilities for SQLite exclusive writes (DR-019).
3
+ *
4
+ * Core logic:
5
+ * - Provides `runExclusive` for one-off transactional writes outside the queue.
6
+ * - Uses `BEGIN EXCLUSIVE` for write isolation.
7
+ * - Automatically rolls back on error.
8
+ *
9
+ * Dependencies: sql.js Database.
10
+ * Boundary: Stateless helper; for queued writes prefer WriteQueue.
11
+ * Test coverage: tests/unit/storage/write-queue.test.ts (indirect via queue)
12
+ */
13
+ import type { Database } from "sql.js";
14
+ export declare function runExclusive<T>(sqlite: Database, fn: (sqlite: Database) => T): T;
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Transaction utilities for SQLite exclusive writes (DR-019).
3
+ *
4
+ * Core logic:
5
+ * - Provides `runExclusive` for one-off transactional writes outside the queue.
6
+ * - Uses `BEGIN EXCLUSIVE` for write isolation.
7
+ * - Automatically rolls back on error.
8
+ *
9
+ * Dependencies: sql.js Database.
10
+ * Boundary: Stateless helper; for queued writes prefer WriteQueue.
11
+ * Test coverage: tests/unit/storage/write-queue.test.ts (indirect via queue)
12
+ */
13
+ export function runExclusive(sqlite, fn) {
14
+ sqlite.exec("BEGIN EXCLUSIVE");
15
+ try {
16
+ const result = fn(sqlite);
17
+ sqlite.exec("COMMIT");
18
+ return result;
19
+ }
20
+ catch (err) {
21
+ try {
22
+ sqlite.exec("ROLLBACK");
23
+ }
24
+ catch {
25
+ // rollback may fail if no transaction active
26
+ }
27
+ throw err;
28
+ }
29
+ }
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Serial write queue with concurrency protection (DR-019).
3
+ *
4
+ * Core logic:
5
+ * - All DB writes are funneled through a single serial queue.
6
+ * - Each write executes inside a `BEGIN EXCLUSIVE` transaction.
7
+ * - On SQLITE_BUSY or equivalent, retries up to 3 times with 50ms backoff.
8
+ * - Flush failure writes to stderr without blocking read paths.
9
+ * - `triggerSource` is preserved through the write pipeline.
10
+ *
11
+ * Dependencies: sql.js Database.
12
+ * Boundary: Wraps raw SQL execution; callers enqueue write operations.
13
+ * Test coverage: tests/unit/storage/write-queue.test.ts
14
+ */
15
+ import type { Database } from "sql.js";
16
+ export type TriggerSource = "heartbeat" | "manual_run" | "probe" | "idle_curiosity";
17
+ export interface WriteRequest<T = unknown> {
18
+ label: string;
19
+ triggerSource: TriggerSource;
20
+ execute: (sqlite: Database) => T;
21
+ }
22
+ export interface WriteResult<T = unknown> {
23
+ ok: boolean;
24
+ value?: T;
25
+ error?: string;
26
+ triggerSource: TriggerSource;
27
+ }
28
+ export declare class WriteQueue {
29
+ private readonly sqlite;
30
+ private queue;
31
+ private processing;
32
+ constructor(sqlite: Database);
33
+ enqueue<T>(request: WriteRequest<T>): Promise<WriteResult<T>>;
34
+ private drain;
35
+ private executeWithRetry;
36
+ get pending(): number;
37
+ }
38
+ export declare function createWriteQueue(sqlite: Database): WriteQueue;
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Serial write queue with concurrency protection (DR-019).
3
+ *
4
+ * Core logic:
5
+ * - All DB writes are funneled through a single serial queue.
6
+ * - Each write executes inside a `BEGIN EXCLUSIVE` transaction.
7
+ * - On SQLITE_BUSY or equivalent, retries up to 3 times with 50ms backoff.
8
+ * - Flush failure writes to stderr without blocking read paths.
9
+ * - `triggerSource` is preserved through the write pipeline.
10
+ *
11
+ * Dependencies: sql.js Database.
12
+ * Boundary: Wraps raw SQL execution; callers enqueue write operations.
13
+ * Test coverage: tests/unit/storage/write-queue.test.ts
14
+ */
15
+ const MAX_RETRIES = 3;
16
+ const BACKOFF_MS = 50;
17
+ function sleep(ms) {
18
+ return new Promise((resolve) => setTimeout(resolve, ms));
19
+ }
20
+ function isBusyError(err) {
21
+ if (err instanceof Error) {
22
+ const msg = err.message.toLowerCase();
23
+ return msg.includes("busy") || msg.includes("locked") || msg.includes("database is locked");
24
+ }
25
+ return false;
26
+ }
27
+ export class WriteQueue {
28
+ sqlite;
29
+ queue = [];
30
+ processing = false;
31
+ constructor(sqlite) {
32
+ this.sqlite = sqlite;
33
+ }
34
+ enqueue(request) {
35
+ return new Promise((resolve) => {
36
+ this.queue.push({
37
+ request: request,
38
+ resolve: resolve,
39
+ });
40
+ this.drain();
41
+ });
42
+ }
43
+ async drain() {
44
+ if (this.processing) {
45
+ return;
46
+ }
47
+ this.processing = true;
48
+ while (this.queue.length > 0) {
49
+ const item = this.queue.shift();
50
+ const result = await this.executeWithRetry(item.request);
51
+ item.resolve(result);
52
+ }
53
+ this.processing = false;
54
+ }
55
+ async executeWithRetry(request) {
56
+ let lastError;
57
+ for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
58
+ try {
59
+ this.sqlite.exec("BEGIN EXCLUSIVE");
60
+ const value = request.execute(this.sqlite);
61
+ this.sqlite.exec("COMMIT");
62
+ return {
63
+ ok: true,
64
+ value,
65
+ triggerSource: request.triggerSource,
66
+ };
67
+ }
68
+ catch (err) {
69
+ try {
70
+ this.sqlite.exec("ROLLBACK");
71
+ }
72
+ catch {
73
+ // rollback may fail if no transaction active
74
+ }
75
+ const message = err instanceof Error ? err.message : String(err);
76
+ lastError = message;
77
+ if (isBusyError(err) && attempt < MAX_RETRIES) {
78
+ await sleep(BACKOFF_MS * (attempt + 1));
79
+ continue;
80
+ }
81
+ break;
82
+ }
83
+ }
84
+ process.stderr.write(`write_queue_flush_failed: ${request.label} [${request.triggerSource}] — ${lastError}\n`);
85
+ return {
86
+ ok: false,
87
+ error: lastError,
88
+ triggerSource: request.triggerSource,
89
+ };
90
+ }
91
+ get pending() {
92
+ return this.queue.length;
93
+ }
94
+ }
95
+ export function createWriteQueue(sqlite) {
96
+ return new WriteQueue(sqlite);
97
+ }
@@ -6,7 +6,8 @@ import * as path from "node:path";
6
6
  export async function persistQuietArtifactToWorkspace(workspaceRoot, ack, input) {
7
7
  const dir = path.join(workspaceRoot, ".second-nature", "quiet", input.day);
8
8
  await fs.mkdir(dir, { recursive: true });
9
- const file = path.join(dir, `${ack.artifactId}.json`);
9
+ const fileName = input.kind === "empty_state" ? "empty_state.json" : `${ack.artifactId}.json`;
10
+ const file = path.join(dir, fileName);
10
11
  const payload = {
11
12
  artifactId: ack.artifactId,
12
13
  artifactRef: ack.artifactRef,
@@ -0,0 +1,35 @@
1
+ /**
2
+ * DiaryDreamStore — T-SMS.C.7
3
+ *
4
+ * Core logic:
5
+ * - DailyDiary artifact ref + index (observedToday, notableSignals,
6
+ * tomorrowDirection, sourceRefs)
7
+ * - DreamOutput lifecycle: candidate -> accepted -> archived
8
+ * (partial is a terminal state, not a lifecycle stage)
9
+ * - `transitionDreamOutputLifecycle` enforces VALID_TRANSITIONS.
10
+ * - Read path `loadAcceptedDreamProjection` only exposes accepted outputs.
11
+ *
12
+ * Dependencies:
13
+ * - `StateDatabase` from `../db/index.js`
14
+ * - `DailyDiary`, `DreamOutput`, `DreamOutputStatus` from
15
+ * `../../shared/types/v7-entities.js`
16
+ * - `validateWritePayload` from `./write-validation-gate.js`
17
+ *
18
+ * Boundary:
19
+ * - Candidate outputs are stored but filtered from active read paths.
20
+ * - Accepted transition is initiated by dream-quiet (DR-023).
21
+ * - Write paths pass through WriteValidationGate.
22
+ *
23
+ * Test coverage: tests/unit/storage/diary-dream-store.test.ts
24
+ */
25
+ import type { StateDatabase } from "../db/index.js";
26
+ import type { DailyDiary, DreamOutput, DreamOutputStatus } from "../../shared/types/v7-entities.js";
27
+ export interface DiaryDreamStore {
28
+ writeDailyDiary(diary: DailyDiary): Promise<void>;
29
+ loadDailyDiary(day: string): Promise<DailyDiary | undefined>;
30
+ appendDreamOutput(output: DreamOutput): Promise<void>;
31
+ transitionDreamOutputLifecycle(outputId: string, newStatus: Exclude<DreamOutputStatus, "candidate" | "partial">): Promise<void>;
32
+ loadAcceptedDreamProjection(limit?: number): Promise<DreamOutput[]>;
33
+ listDreamOutputs(limit?: number): Promise<DreamOutput[]>;
34
+ }
35
+ export declare function createDiaryDreamStore(database: StateDatabase): DiaryDreamStore;
@@ -0,0 +1,165 @@
1
+ /**
2
+ * DiaryDreamStore — T-SMS.C.7
3
+ *
4
+ * Core logic:
5
+ * - DailyDiary artifact ref + index (observedToday, notableSignals,
6
+ * tomorrowDirection, sourceRefs)
7
+ * - DreamOutput lifecycle: candidate -> accepted -> archived
8
+ * (partial is a terminal state, not a lifecycle stage)
9
+ * - `transitionDreamOutputLifecycle` enforces VALID_TRANSITIONS.
10
+ * - Read path `loadAcceptedDreamProjection` only exposes accepted outputs.
11
+ *
12
+ * Dependencies:
13
+ * - `StateDatabase` from `../db/index.js`
14
+ * - `DailyDiary`, `DreamOutput`, `DreamOutputStatus` from
15
+ * `../../shared/types/v7-entities.js`
16
+ * - `validateWritePayload` from `./write-validation-gate.js`
17
+ *
18
+ * Boundary:
19
+ * - Candidate outputs are stored but filtered from active read paths.
20
+ * - Accepted transition is initiated by dream-quiet (DR-023).
21
+ * - Write paths pass through WriteValidationGate.
22
+ *
23
+ * Test coverage: tests/unit/storage/diary-dream-store.test.ts
24
+ */
25
+ import { validateWritePayload } from "./write-validation-gate.js";
26
+ const VALID_TRANSITIONS = {
27
+ candidate: ["accepted", "archived"],
28
+ accepted: ["archived"],
29
+ archived: [],
30
+ partial: [],
31
+ };
32
+ function safeParseJson(json, fallback) {
33
+ try {
34
+ return JSON.parse(json);
35
+ }
36
+ catch {
37
+ return fallback;
38
+ }
39
+ }
40
+ export function createDiaryDreamStore(database) {
41
+ const { sqlite } = database;
42
+ return {
43
+ async writeDailyDiary(diary) {
44
+ const gate = validateWritePayload({
45
+ ...diary,
46
+ sourceRefs: diary.sourceRefs,
47
+ });
48
+ if (!gate.ok)
49
+ throw new Error(gate.reason ?? "write_validation_failed");
50
+ sqlite.run(`INSERT INTO daily_diary_index
51
+ (diary_id, day, observed_today_json, notable_signals_json, tomorrow_direction, source_refs_json, created_at)
52
+ VALUES (?, ?, ?, ?, ?, ?, ?)
53
+ ON CONFLICT(day) DO UPDATE SET
54
+ observed_today_json = excluded.observed_today_json,
55
+ notable_signals_json = excluded.notable_signals_json,
56
+ tomorrow_direction = excluded.tomorrow_direction,
57
+ source_refs_json = excluded.source_refs_json,
58
+ created_at = excluded.created_at`, [
59
+ diary.diaryId,
60
+ diary.day,
61
+ JSON.stringify(diary.observedToday),
62
+ JSON.stringify(diary.notableSignals),
63
+ diary.tomorrowDirection,
64
+ JSON.stringify(diary.sourceRefs),
65
+ diary.createdAt,
66
+ ]);
67
+ },
68
+ async loadDailyDiary(day) {
69
+ const result = sqlite.exec(`SELECT * FROM daily_diary_index WHERE day = ?`, [day]);
70
+ if (result.length === 0 || result[0].values.length === 0) {
71
+ return undefined;
72
+ }
73
+ const cols = result[0].columns;
74
+ const get = (row, name) => row[cols.indexOf(name)];
75
+ const row = result[0].values[0];
76
+ return {
77
+ diaryId: get(row, "diary_id"),
78
+ day: get(row, "day"),
79
+ observedToday: safeParseJson(get(row, "observed_today_json") ?? "[]", []),
80
+ notableSignals: safeParseJson(get(row, "notable_signals_json") ?? "[]", []),
81
+ tomorrowDirection: get(row, "tomorrow_direction") ?? "",
82
+ sourceRefs: safeParseJson(get(row, "source_refs_json") ?? "[]", ["store:default"]),
83
+ createdAt: get(row, "created_at"),
84
+ };
85
+ },
86
+ async appendDreamOutput(output) {
87
+ const gate = validateWritePayload({
88
+ ...output,
89
+ sourceRefs: ["dream:run"],
90
+ });
91
+ if (!gate.ok)
92
+ throw new Error(gate.reason ?? "write_validation_failed");
93
+ sqlite.run(`INSERT INTO dream_output_index
94
+ (output_id, run_id, status, canonical_entries_json, insights_json,
95
+ narrative_update_json, relationship_update_json, validation_json, created_at)
96
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
97
+ output.outputId,
98
+ output.runId,
99
+ output.status,
100
+ JSON.stringify(output.canonicalEntries),
101
+ JSON.stringify(output.insights),
102
+ output.narrativeUpdate ? JSON.stringify(output.narrativeUpdate) : null,
103
+ output.relationshipUpdate ? JSON.stringify(output.relationshipUpdate) : null,
104
+ JSON.stringify(output.validation),
105
+ output.createdAt ?? new Date().toISOString(),
106
+ ]);
107
+ },
108
+ async transitionDreamOutputLifecycle(outputId, newStatus) {
109
+ const currentResult = sqlite.exec(`SELECT status FROM dream_output_index WHERE output_id = ?`, [outputId]);
110
+ if (currentResult.length === 0 ||
111
+ currentResult[0].values.length === 0) {
112
+ throw new Error(`dream_output_not_found:${outputId}`);
113
+ }
114
+ const currentStatus = currentResult[0].values[0][0];
115
+ const allowed = VALID_TRANSITIONS[currentStatus];
116
+ if (!allowed.includes(newStatus)) {
117
+ throw new Error(`invalid_transition:${currentStatus}->${newStatus}`);
118
+ }
119
+ sqlite.run(`UPDATE dream_output_index SET status = ? WHERE output_id = ?`, [newStatus, outputId]);
120
+ },
121
+ async loadAcceptedDreamProjection(limit = 10) {
122
+ const result = sqlite.exec(`SELECT * FROM dream_output_index
123
+ WHERE status = 'accepted'
124
+ ORDER BY created_at DESC
125
+ LIMIT ${limit}`);
126
+ if (result.length === 0 || result[0].values.length === 0) {
127
+ return [];
128
+ }
129
+ const cols = result[0].columns;
130
+ const get = (row, name) => row[cols.indexOf(name)];
131
+ return result[0].values.map((row) => rowToDreamOutput(row, cols));
132
+ },
133
+ async listDreamOutputs(limit = 10) {
134
+ const result = sqlite.exec(`SELECT * FROM dream_output_index
135
+ ORDER BY created_at DESC
136
+ LIMIT ${limit}`);
137
+ if (result.length === 0 || result[0].values.length === 0) {
138
+ return [];
139
+ }
140
+ const cols = result[0].columns;
141
+ return result[0].values.map((row) => rowToDreamOutput(row, cols));
142
+ },
143
+ };
144
+ }
145
+ function rowToDreamOutput(row, cols) {
146
+ const get = (name) => row[cols.indexOf(name)];
147
+ return {
148
+ outputId: get("output_id"),
149
+ runId: get("run_id"),
150
+ status: get("status"),
151
+ canonicalEntries: safeParseJson(get("canonical_entries_json") ?? "[]", []),
152
+ insights: safeParseJson(get("insights_json") ?? "[]", []),
153
+ narrativeUpdate: safeParseJson(get("narrative_update_json") ?? "null", undefined),
154
+ relationshipUpdate: safeParseJson(get("relationship_update_json") ?? "null", undefined),
155
+ validation: safeParseJson(get("validation_json") ?? "{}", {
156
+ schemaValid: false,
157
+ sourceGrounded: false,
158
+ sensitivityClean: false,
159
+ unsupportedClaims: [],
160
+ errors: [],
161
+ checkedAt: new Date().toISOString(),
162
+ }),
163
+ createdAt: get("created_at") ?? new Date().toISOString(),
164
+ };
165
+ }
@@ -0,0 +1,77 @@
1
+ /**
2
+ * EmbodiedContextStatePort — T-SMS.C.2
3
+ *
4
+ * Core logic: 5 read methods for assembling EmbodiedContext slices.
5
+ * Each method supports bounded query (limit/window).
6
+ * Affordance and self-health slices are sourced from body-tool-system
7
+ * and observability-health-system respectively, NOT read directly here.
8
+ * DR-011: loadAcceptedDreamProjection returns accepted projection only.
9
+ * DR-013: bounded context, degraded reason codes on failure.
10
+ * DR-024: empty accepted dream → context_degraded:dream_projection_unavailable.
11
+ *
12
+ * Dependencies:
13
+ * - GoalLifecycleStore from `./goal-lifecycle-store.js`
14
+ * - IdentityProfileStore from `./identity-profile-store.js`
15
+ * - InteractionSnapshotProjector from `./interaction-snapshot-projector.js`
16
+ * - ToolExperienceStore from `./tool-experience-store.js`
17
+ * - dream_output_index table (v7-001 migration)
18
+ *
19
+ * Boundary:
20
+ * - This port ONLY reads state-memory tables.
21
+ * - Affordance map and self-health are injected by caller (control-plane)
22
+ * from their respective systems.
23
+ * - All methods return empty array + reason on missing data, never throw.
24
+ */
25
+ import type { IdentityProfile, RecentInteractionSnapshot, ToolExperience, DreamOutput } from "../../shared/types/v7-entities.js";
26
+ import type { AgentGoal } from "../../shared/types/goal.js";
27
+ import type { GoalLifecycleStore } from "./goal-lifecycle-store.js";
28
+ import type { IdentityProfileStore } from "./identity-profile-store.js";
29
+ import type { InteractionSnapshotProjector } from "./interaction-snapshot-projector.js";
30
+ import type { ToolExperienceStore } from "./tool-experience-store.js";
31
+ import type { StateDatabase } from "../db/index.js";
32
+ export interface EmbodiedContextStatePort {
33
+ loadIdentityProfile(profileId: string): Promise<{
34
+ status: "loaded";
35
+ data: IdentityProfile;
36
+ } | {
37
+ status: "degraded";
38
+ data?: IdentityProfile;
39
+ reason: string;
40
+ }>;
41
+ listActiveGoals(limit?: number): Promise<{
42
+ status: "loaded";
43
+ data: AgentGoal[];
44
+ } | {
45
+ status: "degraded";
46
+ reason: string;
47
+ }>;
48
+ loadRecentInteractionSnapshot(limit?: number): Promise<{
49
+ status: "loaded";
50
+ data: RecentInteractionSnapshot[];
51
+ } | {
52
+ status: "degraded";
53
+ reason: string;
54
+ }>;
55
+ loadToolExperienceSlice(limit?: number): Promise<{
56
+ status: "loaded";
57
+ data: ToolExperience[];
58
+ } | {
59
+ status: "degraded";
60
+ reason: string;
61
+ }>;
62
+ loadAcceptedDreamProjection(limit?: number): Promise<{
63
+ status: "loaded";
64
+ data: DreamOutput[];
65
+ } | {
66
+ status: "degraded";
67
+ reason: string;
68
+ }>;
69
+ }
70
+ export interface EmbodiedContextStatePortDeps {
71
+ database: StateDatabase;
72
+ goalStore: GoalLifecycleStore;
73
+ identityStore: IdentityProfileStore;
74
+ interactionProjector: InteractionSnapshotProjector;
75
+ experienceStore: ToolExperienceStore;
76
+ }
77
+ export declare function createEmbodiedContextStatePort(deps: EmbodiedContextStatePortDeps): EmbodiedContextStatePort;
@@ -0,0 +1,115 @@
1
+ /**
2
+ * EmbodiedContextStatePort — T-SMS.C.2
3
+ *
4
+ * Core logic: 5 read methods for assembling EmbodiedContext slices.
5
+ * Each method supports bounded query (limit/window).
6
+ * Affordance and self-health slices are sourced from body-tool-system
7
+ * and observability-health-system respectively, NOT read directly here.
8
+ * DR-011: loadAcceptedDreamProjection returns accepted projection only.
9
+ * DR-013: bounded context, degraded reason codes on failure.
10
+ * DR-024: empty accepted dream → context_degraded:dream_projection_unavailable.
11
+ *
12
+ * Dependencies:
13
+ * - GoalLifecycleStore from `./goal-lifecycle-store.js`
14
+ * - IdentityProfileStore from `./identity-profile-store.js`
15
+ * - InteractionSnapshotProjector from `./interaction-snapshot-projector.js`
16
+ * - ToolExperienceStore from `./tool-experience-store.js`
17
+ * - dream_output_index table (v7-001 migration)
18
+ *
19
+ * Boundary:
20
+ * - This port ONLY reads state-memory tables.
21
+ * - Affordance map and self-health are injected by caller (control-plane)
22
+ * from their respective systems.
23
+ * - All methods return empty array + reason on missing data, never throw.
24
+ */
25
+ export function createEmbodiedContextStatePort(deps) {
26
+ const { database, goalStore, identityStore, interactionProjector, experienceStore } = deps;
27
+ return {
28
+ async loadIdentityProfile(profileId) {
29
+ const result = await identityStore.loadIdentityProfile(profileId);
30
+ if (result.status === "not_found") {
31
+ return { status: "degraded", reason: `identity_profile_degraded:${profileId}` };
32
+ }
33
+ if (result.status === "degraded") {
34
+ return {
35
+ status: "degraded",
36
+ data: result.profile,
37
+ reason: `identity_profile_degraded:${result.missingPlatforms.join(",")}`,
38
+ };
39
+ }
40
+ return { status: "loaded", data: result.profile };
41
+ },
42
+ async listActiveGoals(limit = 10) {
43
+ try {
44
+ const goals = await goalStore.listActiveGoals({ limit });
45
+ return { status: "loaded", data: goals };
46
+ }
47
+ catch {
48
+ return { status: "degraded", reason: "goal_store_unavailable" };
49
+ }
50
+ },
51
+ async loadRecentInteractionSnapshot(limit = 10) {
52
+ try {
53
+ const rows = await interactionProjector.loadRecentInteractionSnapshot(limit);
54
+ return { status: "loaded", data: rows };
55
+ }
56
+ catch {
57
+ return { status: "degraded", reason: "interaction_store_unavailable" };
58
+ }
59
+ },
60
+ async loadToolExperienceSlice(limit = 10) {
61
+ try {
62
+ const rows = await experienceStore.listToolExperience({ limit });
63
+ return { status: "loaded", data: rows };
64
+ }
65
+ catch {
66
+ return { status: "degraded", reason: "tool_experience_store_unavailable" };
67
+ }
68
+ },
69
+ async loadAcceptedDreamProjection(limit = 3) {
70
+ try {
71
+ const { sqlite } = database;
72
+ const result = sqlite.exec(`SELECT output_id, run_id, status, canonical_entries_json,
73
+ insights_json, narrative_update_json, relationship_update_json,
74
+ validation_json, created_at
75
+ FROM dream_output_index
76
+ WHERE status = 'accepted'
77
+ ORDER BY created_at DESC
78
+ LIMIT ${limit}`);
79
+ if (result.length === 0 || result[0].values.length === 0) {
80
+ return {
81
+ status: "degraded",
82
+ reason: "context_degraded:dream_projection_unavailable",
83
+ };
84
+ }
85
+ const cols = result[0].columns;
86
+ const get = (row, name) => row[cols.indexOf(name)];
87
+ const outputs = result[0].values.map((row) => ({
88
+ outputId: get(row, "output_id"),
89
+ runId: get(row, "run_id"),
90
+ status: "accepted",
91
+ canonicalEntries: safeParseJson(get(row, "canonical_entries_json") ?? "[]", []),
92
+ insights: safeParseJson(get(row, "insights_json") ?? "[]", []),
93
+ narrativeUpdate: safeParseJson(get(row, "narrative_update_json") ?? "null", undefined),
94
+ relationshipUpdate: safeParseJson(get(row, "relationship_update_json") ?? "null", undefined),
95
+ validation: safeParseJson(get(row, "validation_json") ?? "{}", {
96
+ schemaValid: false, sourceGrounded: false, sensitivityClean: false,
97
+ unsupportedClaims: [], errors: [], checkedAt: "",
98
+ }),
99
+ }));
100
+ return { status: "loaded", data: outputs };
101
+ }
102
+ catch {
103
+ return { status: "degraded", reason: "dream_output_store_unavailable" };
104
+ }
105
+ },
106
+ };
107
+ }
108
+ function safeParseJson(json, fallback) {
109
+ try {
110
+ return JSON.parse(json);
111
+ }
112
+ catch {
113
+ return fallback;
114
+ }
115
+ }