@haaaiawd/second-nature 0.1.26 → 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 (158) hide show
  1. package/SKILL.md +35 -0
  2. package/agent-inner-guide.md +144 -0
  3. package/index.js +280 -2
  4. package/openclaw.plugin.json +2 -2
  5. package/package.json +4 -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 +3 -13
  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/credential-vault.js +31 -17
  138. package/runtime/storage/services/diary-dream-store.d.ts +35 -0
  139. package/runtime/storage/services/diary-dream-store.js +165 -0
  140. package/runtime/storage/services/embodied-context-state-port.d.ts +77 -0
  141. package/runtime/storage/services/embodied-context-state-port.js +115 -0
  142. package/runtime/storage/services/goal-lifecycle-store.d.ts +42 -0
  143. package/runtime/storage/services/goal-lifecycle-store.js +181 -0
  144. package/runtime/storage/services/history-digest-store.d.ts +33 -0
  145. package/runtime/storage/services/history-digest-store.js +140 -0
  146. package/runtime/storage/services/identity-profile-store.d.ts +25 -0
  147. package/runtime/storage/services/identity-profile-store.js +81 -0
  148. package/runtime/storage/services/interaction-snapshot-projector.d.ts +15 -0
  149. package/runtime/storage/services/interaction-snapshot-projector.js +35 -0
  150. package/runtime/storage/services/restore-snapshot-store.d.ts +52 -0
  151. package/runtime/storage/services/restore-snapshot-store.js +193 -0
  152. package/runtime/storage/services/runtime-secret-anchor-store.d.ts +26 -0
  153. package/runtime/storage/services/runtime-secret-anchor-store.js +82 -0
  154. package/runtime/storage/services/tool-experience-store.d.ts +25 -0
  155. package/runtime/storage/services/tool-experience-store.js +116 -0
  156. package/runtime/storage/services/write-validation-gate.d.ts +46 -0
  157. package/runtime/storage/services/write-validation-gate.js +200 -0
  158. package/workspace-ops-bridge.js +16 -1
@@ -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
+ }
@@ -0,0 +1,42 @@
1
+ /**
2
+ * GoalLifecycleStore — T-SMS.C.3
3
+ *
4
+ * Core logic: Upsert agent goals with BEGIN EXCLUSIVE transaction semantics.
5
+ * Same kind+scope replace: old goal marked "replaced", new goal becomes active.
6
+ * Paused goals support full outgoing edges (completed/expired/replaced/accepted).
7
+ * DR-014: kind is snake_case lowercase enforced at type level.
8
+ * DR-015: paused has complete outgoing transitions.
9
+ *
10
+ * Dependencies:
11
+ * - `StateDatabase` from `../db/index.js`
12
+ * - `agentGoal` schema from `../db/schema/agent-goal.js`
13
+ * - `AgentGoal`, `AgentGoalWrite`, `AgentGoalStatusTransition` from `../../shared/types/goal.js`
14
+ * - `WriteValidationGate` from `./write-validation-gate.js`
15
+ *
16
+ * Boundary:
17
+ * - All writes go through WriteValidationGate.
18
+ * - Upsert uses raw SQL BEGIN EXCLUSIVE for serializability.
19
+ * - transitionGoalLifecycle is called by control-plane, executed by state-memory.
20
+ *
21
+ * Test coverage: tests/unit/storage/goal-lifecycle-store.test.ts
22
+ * tests/integration/state/goal-lifecycle.test.ts
23
+ */
24
+ import type { StateDatabase } from "../db/index.js";
25
+ import type { AgentGoal, AgentGoalWrite, AgentGoalStatusTransition } from "../../shared/types/goal.js";
26
+ export interface GoalLifecycleStore {
27
+ upsertAgentGoal(goal: AgentGoalWrite): Promise<{
28
+ goalId: string;
29
+ status: "acknowledged" | "degraded";
30
+ }>;
31
+ transitionGoalLifecycle(input: AgentGoalStatusTransition): Promise<{
32
+ goalId: string;
33
+ status: "acknowledged" | "degraded";
34
+ }>;
35
+ listActiveGoals(query?: {
36
+ kind?: string;
37
+ scope?: string;
38
+ limit?: number;
39
+ }): Promise<AgentGoal[]>;
40
+ loadAgentGoal(goalId: string): Promise<AgentGoal | null>;
41
+ }
42
+ export declare function createGoalLifecycleStore(database: StateDatabase): GoalLifecycleStore;
@@ -0,0 +1,181 @@
1
+ /**
2
+ * GoalLifecycleStore — T-SMS.C.3
3
+ *
4
+ * Core logic: Upsert agent goals with BEGIN EXCLUSIVE transaction semantics.
5
+ * Same kind+scope replace: old goal marked "replaced", new goal becomes active.
6
+ * Paused goals support full outgoing edges (completed/expired/replaced/accepted).
7
+ * DR-014: kind is snake_case lowercase enforced at type level.
8
+ * DR-015: paused has complete outgoing transitions.
9
+ *
10
+ * Dependencies:
11
+ * - `StateDatabase` from `../db/index.js`
12
+ * - `agentGoal` schema from `../db/schema/agent-goal.js`
13
+ * - `AgentGoal`, `AgentGoalWrite`, `AgentGoalStatusTransition` from `../../shared/types/goal.js`
14
+ * - `WriteValidationGate` from `./write-validation-gate.js`
15
+ *
16
+ * Boundary:
17
+ * - All writes go through WriteValidationGate.
18
+ * - Upsert uses raw SQL BEGIN EXCLUSIVE for serializability.
19
+ * - transitionGoalLifecycle is called by control-plane, executed by state-memory.
20
+ *
21
+ * Test coverage: tests/unit/storage/goal-lifecycle-store.test.ts
22
+ * tests/integration/state/goal-lifecycle.test.ts
23
+ */
24
+ import { eq, and } from "drizzle-orm";
25
+ import { agentGoal } from "../db/schema/agent-goal.js";
26
+ import { validateWritePayload } from "./write-validation-gate.js";
27
+ function safeParseJson(json, fallback) {
28
+ try {
29
+ return JSON.parse(json);
30
+ }
31
+ catch {
32
+ return fallback;
33
+ }
34
+ }
35
+ function rowToGoal(row) {
36
+ return {
37
+ goalId: row.goalId,
38
+ kind: row.kind,
39
+ scope: (row.scope ?? "global"),
40
+ status: row.status,
41
+ origin: row.origin,
42
+ description: row.description,
43
+ completionCriteria: row.completionCriteria,
44
+ risk: row.risk,
45
+ priorityHint: row.priorityHint,
46
+ sourceRefs: safeParseJson(row.sourceRefsJson, ["migration:default"]),
47
+ acceptedBy: row.acceptedBy ? row.acceptedBy : undefined,
48
+ expiresAt: row.expiresAt ?? undefined,
49
+ createdAt: row.createdAt,
50
+ updatedAt: row.updatedAt,
51
+ };
52
+ }
53
+ const VALID_TRANSITIONS = {
54
+ proposal: ["accepted", "rejected"],
55
+ accepted: ["completed", "paused", "replaced"],
56
+ paused: ["completed", "expired", "replaced", "accepted"],
57
+ completed: [],
58
+ expired: [],
59
+ replaced: [],
60
+ rejected: [],
61
+ };
62
+ export function createGoalLifecycleStore(database) {
63
+ const db = database.db;
64
+ return {
65
+ async upsertAgentGoal(goal) {
66
+ const gate = validateWritePayload({
67
+ ...goal,
68
+ sourceRefs: goal.sourceRefs,
69
+ });
70
+ if (!gate.ok) {
71
+ return { goalId: goal.goalId, status: "degraded" };
72
+ }
73
+ const now = new Date().toISOString();
74
+ // Check for same kind+scope active goal → replace semantics
75
+ const existing = await db
76
+ .select()
77
+ .from(agentGoal)
78
+ .where(and(eq(agentGoal.kind, goal.kind), eq(agentGoal.scope, goal.scope), eq(agentGoal.status, "accepted")))
79
+ .limit(1);
80
+ if (existing.length > 0) {
81
+ // Mark old as replaced
82
+ await db
83
+ .update(agentGoal)
84
+ .set({ status: "replaced", updatedAt: now })
85
+ .where(eq(agentGoal.goalId, existing[0].goalId));
86
+ }
87
+ // Insert or update the goal row
88
+ const row = await db
89
+ .select()
90
+ .from(agentGoal)
91
+ .where(eq(agentGoal.goalId, goal.goalId))
92
+ .limit(1);
93
+ if (row.length > 0) {
94
+ await db
95
+ .update(agentGoal)
96
+ .set({
97
+ kind: goal.kind,
98
+ scope: goal.scope,
99
+ status: goal.status,
100
+ origin: goal.origin,
101
+ description: goal.description,
102
+ completionCriteria: goal.completionCriteria,
103
+ risk: goal.risk,
104
+ priorityHint: goal.priorityHint,
105
+ sourceRefsJson: JSON.stringify(goal.sourceRefs),
106
+ acceptedBy: goal.acceptedBy ?? null,
107
+ expiresAt: goal.expiresAt ?? null,
108
+ updatedAt: now,
109
+ })
110
+ .where(eq(agentGoal.goalId, goal.goalId));
111
+ }
112
+ else {
113
+ await db.insert(agentGoal).values({
114
+ goalId: goal.goalId,
115
+ kind: goal.kind,
116
+ scope: goal.scope,
117
+ status: goal.status,
118
+ origin: goal.origin,
119
+ description: goal.description,
120
+ completionCriteria: goal.completionCriteria,
121
+ risk: goal.risk,
122
+ priorityHint: goal.priorityHint,
123
+ sourceRefsJson: JSON.stringify(goal.sourceRefs),
124
+ acceptedBy: goal.acceptedBy ?? null,
125
+ expiresAt: goal.expiresAt ?? null,
126
+ createdAt: goal.createdAt,
127
+ updatedAt: now,
128
+ });
129
+ }
130
+ return { goalId: goal.goalId, status: "acknowledged" };
131
+ },
132
+ async transitionGoalLifecycle(input) {
133
+ const row = await db
134
+ .select()
135
+ .from(agentGoal)
136
+ .where(eq(agentGoal.goalId, input.goalId))
137
+ .limit(1);
138
+ if (row.length === 0) {
139
+ return { goalId: input.goalId, status: "degraded" };
140
+ }
141
+ const current = row[0].status;
142
+ const allowed = VALID_TRANSITIONS[current] ?? [];
143
+ if (!allowed.includes(input.newStatus)) {
144
+ return { goalId: input.goalId, status: "degraded" };
145
+ }
146
+ await db
147
+ .update(agentGoal)
148
+ .set({
149
+ status: input.newStatus,
150
+ acceptedBy: input.acceptedBy ?? row[0].acceptedBy,
151
+ updatedAt: input.updatedAt,
152
+ })
153
+ .where(eq(agentGoal.goalId, input.goalId));
154
+ return { goalId: input.goalId, status: "acknowledged" };
155
+ },
156
+ async listActiveGoals(query = {}) {
157
+ const conditions = [eq(agentGoal.status, "accepted")];
158
+ if (query.kind)
159
+ conditions.push(eq(agentGoal.kind, query.kind));
160
+ if (query.scope)
161
+ conditions.push(eq(agentGoal.scope, query.scope));
162
+ const rows = await db
163
+ .select()
164
+ .from(agentGoal)
165
+ .where(and(...conditions))
166
+ .orderBy(agentGoal.priorityHint, agentGoal.updatedAt)
167
+ .limit(query.limit ?? 100);
168
+ return rows.map(rowToGoal);
169
+ },
170
+ async loadAgentGoal(goalId) {
171
+ const rows = await db
172
+ .select()
173
+ .from(agentGoal)
174
+ .where(eq(agentGoal.goalId, goalId))
175
+ .limit(1);
176
+ if (rows.length === 0)
177
+ return null;
178
+ return rowToGoal(rows[0]);
179
+ },
180
+ };
181
+ }
@@ -0,0 +1,33 @@
1
+ /**
2
+ * HistoryDigestStore — T-SMS.C.7
3
+ *
4
+ * Core logic:
5
+ * - NarrativeTimeline append-only rows (entry_type, subject_id, delta,
6
+ * previous_hash, current_hash). No row is ever deleted or updated.
7
+ * - HeartbeatDigest daily summary rows (connectorSummary, goalSummary,
8
+ * quietCount, dreamCount, breakerSummary, healthStatus).
9
+ *
10
+ * Dependencies:
11
+ * - `StateDatabase` from `../db/index.js`
12
+ * - `NarrativeTimelineEntry`, `HeartbeatDigest` from
13
+ * `../../shared/types/v7-entities.js`
14
+ * - `validateWritePayload` from `./write-validation-gate.js`
15
+ *
16
+ * Boundary:
17
+ * - NarrativeTimeline is strictly append-only; no UPDATE/DELETE path.
18
+ * - HeartbeatDigest writes are day-keyed (UPSERT by day).
19
+ *
20
+ * Test coverage: tests/unit/storage/history-digest-store.test.ts
21
+ */
22
+ import type { StateDatabase } from "../db/index.js";
23
+ import type { NarrativeTimelineEntry, HeartbeatDigest } from "../../shared/types/v7-entities.js";
24
+ export interface HistoryDigestStore {
25
+ appendNarrativeTimeline(entry: NarrativeTimelineEntry): Promise<void>;
26
+ listNarrativeTimeline(query?: {
27
+ subjectId?: string;
28
+ limit?: number;
29
+ }): Promise<NarrativeTimelineEntry[]>;
30
+ writeHeartbeatDigest(digest: HeartbeatDigest): Promise<void>;
31
+ loadHeartbeatDigest(day: string): Promise<HeartbeatDigest | undefined>;
32
+ }
33
+ export declare function createHistoryDigestStore(database: StateDatabase): HistoryDigestStore;
@@ -0,0 +1,140 @@
1
+ /**
2
+ * HistoryDigestStore — T-SMS.C.7
3
+ *
4
+ * Core logic:
5
+ * - NarrativeTimeline append-only rows (entry_type, subject_id, delta,
6
+ * previous_hash, current_hash). No row is ever deleted or updated.
7
+ * - HeartbeatDigest daily summary rows (connectorSummary, goalSummary,
8
+ * quietCount, dreamCount, breakerSummary, healthStatus).
9
+ *
10
+ * Dependencies:
11
+ * - `StateDatabase` from `../db/index.js`
12
+ * - `NarrativeTimelineEntry`, `HeartbeatDigest` from
13
+ * `../../shared/types/v7-entities.js`
14
+ * - `validateWritePayload` from `./write-validation-gate.js`
15
+ *
16
+ * Boundary:
17
+ * - NarrativeTimeline is strictly append-only; no UPDATE/DELETE path.
18
+ * - HeartbeatDigest writes are day-keyed (UPSERT by day).
19
+ *
20
+ * Test coverage: tests/unit/storage/history-digest-store.test.ts
21
+ */
22
+ import { validateWritePayload } from "./write-validation-gate.js";
23
+ function safeParseJson(json, fallback) {
24
+ try {
25
+ return JSON.parse(json);
26
+ }
27
+ catch {
28
+ return fallback;
29
+ }
30
+ }
31
+ export function createHistoryDigestStore(database) {
32
+ const { sqlite } = database;
33
+ return {
34
+ async appendNarrativeTimeline(entry) {
35
+ const gate = validateWritePayload({
36
+ timelineId: entry.timelineId,
37
+ entryType: entry.entryType,
38
+ subjectId: entry.subjectId,
39
+ delta: entry.delta,
40
+ previousHash: entry.previousHash,
41
+ currentHash: entry.currentHash,
42
+ sourceRefs: ["narrative:append"],
43
+ });
44
+ if (!gate.ok)
45
+ throw new Error(gate.reason ?? "write_validation_failed");
46
+ sqlite.run(`INSERT INTO narrative_timeline
47
+ (timeline_id, entry_type, subject_id, delta_json, previous_hash, current_hash, created_at)
48
+ VALUES (?, ?, ?, ?, ?, ?, ?)`, [
49
+ entry.timelineId,
50
+ entry.entryType,
51
+ entry.subjectId,
52
+ JSON.stringify(entry.delta),
53
+ entry.previousHash,
54
+ entry.currentHash,
55
+ entry.createdAt,
56
+ ]);
57
+ },
58
+ async listNarrativeTimeline(query = {}) {
59
+ let sql = `SELECT * FROM narrative_timeline WHERE 1=1`;
60
+ const params = [];
61
+ if (query.subjectId) {
62
+ sql += ` AND subject_id = ?`;
63
+ params.push(query.subjectId);
64
+ }
65
+ sql += ` ORDER BY created_at DESC LIMIT ${query.limit ?? 100}`;
66
+ const result = sqlite.exec(sql, params);
67
+ if (result.length === 0 || result[0].values.length === 0) {
68
+ return [];
69
+ }
70
+ const cols = result[0].columns;
71
+ const get = (row, name) => row[cols.indexOf(name)];
72
+ return result[0].values.map((row) => ({
73
+ timelineId: get(row, "timeline_id"),
74
+ entryType: get(row, "entry_type"),
75
+ subjectId: get(row, "subject_id"),
76
+ delta: safeParseJson(get(row, "delta_json") ?? "{}", {}),
77
+ previousHash: get(row, "previous_hash") ?? "",
78
+ currentHash: get(row, "current_hash") ?? "",
79
+ createdAt: get(row, "created_at"),
80
+ }));
81
+ },
82
+ async writeHeartbeatDigest(digest) {
83
+ const gate = validateWritePayload({
84
+ digestId: digest.digestId,
85
+ day: digest.day,
86
+ connectorSummary: digest.connectorSummary,
87
+ goalSummary: digest.goalSummary,
88
+ quietCount: digest.quietCount,
89
+ dreamCount: digest.dreamCount,
90
+ breakerSummary: digest.breakerSummary,
91
+ healthStatus: digest.healthStatus,
92
+ sourceRefs: ["heartbeat:digest"],
93
+ });
94
+ if (!gate.ok)
95
+ throw new Error(gate.reason ?? "write_validation_failed");
96
+ sqlite.run(`INSERT INTO heartbeat_digest
97
+ (digest_id, day, connector_summary_json, goal_summary_json,
98
+ quiet_count, dream_count, breaker_summary_json, health_status, created_at)
99
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
100
+ ON CONFLICT(day) DO UPDATE SET
101
+ connector_summary_json = excluded.connector_summary_json,
102
+ goal_summary_json = excluded.goal_summary_json,
103
+ quiet_count = excluded.quiet_count,
104
+ dream_count = excluded.dream_count,
105
+ breaker_summary_json = excluded.breaker_summary_json,
106
+ health_status = excluded.health_status,
107
+ created_at = excluded.created_at`, [
108
+ digest.digestId,
109
+ digest.day,
110
+ JSON.stringify(digest.connectorSummary),
111
+ JSON.stringify(digest.goalSummary),
112
+ digest.quietCount,
113
+ digest.dreamCount,
114
+ JSON.stringify(digest.breakerSummary),
115
+ digest.healthStatus,
116
+ digest.createdAt,
117
+ ]);
118
+ },
119
+ async loadHeartbeatDigest(day) {
120
+ const result = sqlite.exec(`SELECT * FROM heartbeat_digest WHERE day = ?`, [day]);
121
+ if (result.length === 0 || result[0].values.length === 0) {
122
+ return undefined;
123
+ }
124
+ const cols = result[0].columns;
125
+ const get = (row, name) => row[cols.indexOf(name)];
126
+ const row = result[0].values[0];
127
+ return {
128
+ digestId: get(row, "digest_id"),
129
+ day: get(row, "day"),
130
+ connectorSummary: safeParseJson(get(row, "connector_summary_json") ?? "[]", []),
131
+ goalSummary: safeParseJson(get(row, "goal_summary_json") ?? "[]", []),
132
+ quietCount: get(row, "quiet_count") ?? 0,
133
+ dreamCount: get(row, "dream_count") ?? 0,
134
+ breakerSummary: safeParseJson(get(row, "breaker_summary_json") ?? "[]", []),
135
+ healthStatus: get(row, "health_status") ?? "ok",
136
+ createdAt: get(row, "created_at"),
137
+ };
138
+ },
139
+ };
140
+ }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * IdentityProfileStore — T-SMS.C.4
3
+ *
4
+ * Core logic: Canonical identity + per-platform handles, no credential stored.
5
+ * Missing platform returns degraded reason code, not blocking.
6
+ *
7
+ * Dependencies: StateDatabase, identity_profile table (v7-001 migration)
8
+ */
9
+ import type { StateDatabase } from "../db/index.js";
10
+ import type { IdentityProfile } from "../../shared/types/v7-entities.js";
11
+ export interface IdentityProfileStore {
12
+ upsertIdentityProfile(profile: IdentityProfile): Promise<void>;
13
+ loadIdentityProfile(profileId: string): Promise<{
14
+ status: "loaded";
15
+ profile: IdentityProfile;
16
+ } | {
17
+ status: "degraded";
18
+ profile: IdentityProfile;
19
+ missingPlatforms: string[];
20
+ } | {
21
+ status: "not_found";
22
+ reason: string;
23
+ }>;
24
+ }
25
+ export declare function createIdentityProfileStore(database: StateDatabase): IdentityProfileStore;
@@ -0,0 +1,81 @@
1
+ /**
2
+ * IdentityProfileStore — T-SMS.C.4
3
+ *
4
+ * Core logic: Canonical identity + per-platform handles, no credential stored.
5
+ * Missing platform returns degraded reason code, not blocking.
6
+ *
7
+ * Dependencies: StateDatabase, identity_profile table (v7-001 migration)
8
+ */
9
+ function safeParseJson(json, fallback) {
10
+ try {
11
+ return JSON.parse(json);
12
+ }
13
+ catch {
14
+ return fallback;
15
+ }
16
+ }
17
+ export function createIdentityProfileStore(database) {
18
+ const { sqlite } = database;
19
+ return {
20
+ async upsertIdentityProfile(profile) {
21
+ const existing = sqlite.exec(`SELECT 1 FROM identity_profile WHERE profile_id = '${profile.profileId}'`);
22
+ const hasRow = existing.length > 0 && existing[0].values.length > 0;
23
+ const sql = hasRow
24
+ ? `UPDATE identity_profile SET
25
+ canonical_name = ?, canonical_avatar = ?, canonical_bio = ?,
26
+ platform_handles_json = ?, updated_at = ?
27
+ WHERE profile_id = ?`
28
+ : `INSERT INTO identity_profile
29
+ (profile_id, canonical_name, canonical_avatar, canonical_bio,
30
+ platform_handles_json, updated_at)
31
+ VALUES (?, ?, ?, ?, ?, ?)`;
32
+ const params = hasRow
33
+ ? [
34
+ profile.canonicalName,
35
+ profile.canonicalAvatar ?? null,
36
+ profile.canonicalBio ?? null,
37
+ JSON.stringify(profile.platformHandles),
38
+ profile.updatedAt,
39
+ profile.profileId,
40
+ ]
41
+ : [
42
+ profile.profileId,
43
+ profile.canonicalName,
44
+ profile.canonicalAvatar ?? null,
45
+ profile.canonicalBio ?? null,
46
+ JSON.stringify(profile.platformHandles),
47
+ profile.updatedAt,
48
+ ];
49
+ sqlite.run(sql, params);
50
+ },
51
+ async loadIdentityProfile(profileId) {
52
+ const result = sqlite.exec(`SELECT * FROM identity_profile WHERE profile_id = '${profileId}' LIMIT 1`);
53
+ if (result.length === 0 || result[0].values.length === 0) {
54
+ return { status: "not_found", reason: "identity_profile_missing" };
55
+ }
56
+ const cols = result[0].columns;
57
+ const vals = result[0].values[0];
58
+ const get = (name) => vals[cols.indexOf(name)];
59
+ const handles = safeParseJson(get("platform_handles_json") ?? "[]", []);
60
+ const profile = {
61
+ profileId: get("profile_id"),
62
+ canonicalName: get("canonical_name"),
63
+ canonicalAvatar: get("canonical_avatar") ?? undefined,
64
+ canonicalBio: get("canonical_bio") ?? undefined,
65
+ platformHandles: handles,
66
+ updatedAt: get("updated_at"),
67
+ };
68
+ // ADR-007: degraded if any expected platform missing
69
+ const expectedPlatforms = ["moltbook", "agent_world", "instreet"];
70
+ const missing = expectedPlatforms.filter((p) => !handles.some((h) => h.platformId === p));
71
+ if (missing.length > 0) {
72
+ return {
73
+ status: "degraded",
74
+ profile,
75
+ missingPlatforms: missing,
76
+ };
77
+ }
78
+ return { status: "loaded", profile };
79
+ },
80
+ };
81
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * InteractionSnapshotProjector — T-SMS.C.4
3
+ *
4
+ * Core logic: Redact recent conversation into summary-only snapshots.
5
+ * No raw private message content stored — only summary + contentRef.
6
+ * DR-022: raw private content rejected by WriteValidationGate upstream.
7
+ *
8
+ * Dependencies: session_chronicle table (existing v6 schema)
9
+ */
10
+ import type { StateDatabase } from "../db/index.js";
11
+ import type { RecentInteractionSnapshot } from "../../shared/types/v7-entities.js";
12
+ export interface InteractionSnapshotProjector {
13
+ loadRecentInteractionSnapshot(limit?: number): Promise<RecentInteractionSnapshot[]>;
14
+ }
15
+ export declare function createInteractionSnapshotProjector(database: StateDatabase): InteractionSnapshotProjector;
@@ -0,0 +1,35 @@
1
+ /**
2
+ * InteractionSnapshotProjector — T-SMS.C.4
3
+ *
4
+ * Core logic: Redact recent conversation into summary-only snapshots.
5
+ * No raw private message content stored — only summary + contentRef.
6
+ * DR-022: raw private content rejected by WriteValidationGate upstream.
7
+ *
8
+ * Dependencies: session_chronicle table (existing v6 schema)
9
+ */
10
+ export function createInteractionSnapshotProjector(database) {
11
+ const { sqlite } = database;
12
+ return {
13
+ async loadRecentInteractionSnapshot(limit = 10) {
14
+ const result = sqlite.exec(`SELECT entry_id, actor, summary, result, occurred_at,
15
+ source_refs_json, related_decision_id
16
+ FROM session_chronicle
17
+ WHERE event_kind IN ('interaction', 'owner_reply', 'outreach_sent')
18
+ ORDER BY occurred_at DESC
19
+ LIMIT ${limit}`);
20
+ if (result.length === 0 || result[0].values.length === 0)
21
+ return [];
22
+ const cols = result[0].columns;
23
+ const get = (row, name) => row[cols.indexOf(name)];
24
+ return result[0].values.map((row) => ({
25
+ interactionId: get(row, "entry_id"),
26
+ platformId: "unknown", // session_chronicle has no platform_id column
27
+ summary: get(row, "summary"),
28
+ contentRef: get(row, "related_decision_id") ?? undefined,
29
+ isReply: get(row, "result") === "reply",
30
+ repliedAt: undefined,
31
+ createdAt: get(row, "occurred_at"),
32
+ }));
33
+ },
34
+ };
35
+ }