@haaaiawd/second-nature 0.1.18 → 0.1.20

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 (190) hide show
  1. package/index.js +911 -855
  2. package/openclaw.plugin.json +29 -29
  3. package/package.json +52 -52
  4. package/runtime/cli/commands/index.d.ts +14 -14
  5. package/runtime/cli/commands/index.js +224 -193
  6. package/runtime/cli/explain/explain-surface-subject.d.ts +8 -8
  7. package/runtime/cli/explain/explain-surface-subject.js +9 -9
  8. package/runtime/cli/explain/format-explanation.d.ts +12 -12
  9. package/runtime/cli/explain/format-explanation.js +12 -12
  10. package/runtime/cli/explain/resolve-subject.js +41 -41
  11. package/runtime/cli/host-capability/classify-delivery.d.ts +14 -14
  12. package/runtime/cli/host-capability/classify-delivery.js +20 -20
  13. package/runtime/cli/host-capability/probe-host-capability.d.ts +2 -2
  14. package/runtime/cli/host-capability/probe-host-capability.js +58 -58
  15. package/runtime/cli/host-capability/record-host-capability.d.ts +6 -6
  16. package/runtime/cli/host-capability/record-host-capability.js +14 -14
  17. package/runtime/cli/host-capability/types.d.ts +71 -71
  18. package/runtime/cli/host-capability/types.js +6 -6
  19. package/runtime/cli/host-smoke/run-host-smoke.d.ts +2 -2
  20. package/runtime/cli/host-smoke/run-host-smoke.js +40 -40
  21. package/runtime/cli/host-smoke/types.d.ts +35 -35
  22. package/runtime/cli/host-smoke/types.js +6 -6
  23. package/runtime/cli/index.js +67 -58
  24. package/runtime/cli/ops/heartbeat-surface.d.ts +45 -38
  25. package/runtime/cli/ops/heartbeat-surface.js +79 -73
  26. package/runtime/cli/ops/ops-router.d.ts +32 -19
  27. package/runtime/cli/ops/ops-router.js +188 -89
  28. package/runtime/cli/ops/show-operator-fallback.d.ts +13 -13
  29. package/runtime/cli/ops/show-operator-fallback.js +22 -22
  30. package/runtime/cli/ops/workspace-heartbeat-runner.d.ts +40 -19
  31. package/runtime/cli/ops/workspace-heartbeat-runner.js +93 -39
  32. package/runtime/cli/read-models/index.d.ts +46 -29
  33. package/runtime/cli/read-models/index.js +391 -256
  34. package/runtime/cli/read-models/operator-explain-map.d.ts +6 -6
  35. package/runtime/cli/read-models/operator-explain-map.js +10 -10
  36. package/runtime/cli/read-models/types.d.ts +129 -79
  37. package/runtime/cli/runtime/runtime-artifact-boundary.d.ts +28 -28
  38. package/runtime/cli/runtime/runtime-artifact-boundary.js +94 -94
  39. package/runtime/connectors/base/contract.d.ts +87 -87
  40. package/runtime/connectors/base/execution-policy.d.ts +47 -47
  41. package/runtime/connectors/base/execution-policy.js +82 -82
  42. package/runtime/connectors/base/index.d.ts +8 -8
  43. package/runtime/connectors/base/index.js +8 -8
  44. package/runtime/connectors/base/manifest.d.ts +64 -64
  45. package/runtime/connectors/base/manifest.js +86 -86
  46. package/runtime/connectors/base/map-life-evidence.d.ts +16 -16
  47. package/runtime/connectors/base/map-life-evidence.js +79 -79
  48. package/runtime/connectors/base/policy-layer.d.ts +29 -29
  49. package/runtime/connectors/base/policy-layer.js +198 -198
  50. package/runtime/connectors/base/route-planner.js +99 -99
  51. package/runtime/connectors/index.d.ts +5 -5
  52. package/runtime/connectors/index.js +5 -5
  53. package/runtime/connectors/near-real/near-real-connector-smoke.d.ts +19 -19
  54. package/runtime/connectors/near-real/near-real-connector-smoke.js +152 -152
  55. package/runtime/core/second-nature/heartbeat/heartbeat-executor.js +114 -114
  56. package/runtime/core/second-nature/heartbeat/heartbeat-loop.d.ts +63 -63
  57. package/runtime/core/second-nature/heartbeat/heartbeat-loop.js +162 -139
  58. package/runtime/core/second-nature/heartbeat/index.d.ts +8 -8
  59. package/runtime/core/second-nature/heartbeat/index.js +7 -7
  60. package/runtime/core/second-nature/heartbeat/run-heartbeat-cycle.d.ts +21 -21
  61. package/runtime/core/second-nature/heartbeat/run-heartbeat-cycle.js +35 -35
  62. package/runtime/core/second-nature/heartbeat/runtime-snapshot.d.ts +28 -28
  63. package/runtime/core/second-nature/heartbeat/runtime-snapshot.js +35 -35
  64. package/runtime/core/second-nature/heartbeat/signal.d.ts +42 -42
  65. package/runtime/core/second-nature/heartbeat/snapshot-builder.d.ts +51 -51
  66. package/runtime/core/second-nature/index.d.ts +22 -22
  67. package/runtime/core/second-nature/index.js +22 -22
  68. package/runtime/core/second-nature/orchestrator/effect-dispatcher.d.ts +100 -100
  69. package/runtime/core/second-nature/orchestrator/effect-dispatcher.js +144 -144
  70. package/runtime/core/second-nature/orchestrator/guard-layer.d.ts +8 -8
  71. package/runtime/core/second-nature/orchestrator/guard-layer.js +110 -110
  72. package/runtime/core/second-nature/orchestrator/intent-planner.d.ts +13 -13
  73. package/runtime/core/second-nature/orchestrator/intent-planner.js +199 -199
  74. package/runtime/core/second-nature/orchestrator/lease-manager.d.ts +14 -14
  75. package/runtime/core/second-nature/orchestrator/lease-manager.js +58 -58
  76. package/runtime/core/second-nature/outreach/build-outreach-draft-request.d.ts +6 -6
  77. package/runtime/core/second-nature/outreach/build-outreach-draft-request.js +63 -63
  78. package/runtime/core/second-nature/outreach/delivery-target.d.ts +26 -26
  79. package/runtime/core/second-nature/outreach/delivery-target.js +70 -70
  80. package/runtime/core/second-nature/outreach/dispatch-user-outreach.d.ts +38 -38
  81. package/runtime/core/second-nature/outreach/dispatch-user-outreach.js +119 -119
  82. package/runtime/core/second-nature/outreach/judge-input-from-snapshot.d.ts +7 -7
  83. package/runtime/core/second-nature/outreach/judge-input-from-snapshot.js +45 -45
  84. package/runtime/core/second-nature/outreach/judge-outreach.d.ts +40 -40
  85. package/runtime/core/second-nature/outreach/judge-outreach.js +121 -121
  86. package/runtime/core/second-nature/quiet/run-source-backed-quiet.d.ts +21 -21
  87. package/runtime/core/second-nature/quiet/run-source-backed-quiet.js +123 -123
  88. package/runtime/core/second-nature/rhythm/planner-rhythm-window.d.ts +15 -15
  89. package/runtime/core/second-nature/rhythm/planner-rhythm-window.js +52 -52
  90. package/runtime/core/second-nature/rhythm/policy-bridge.d.ts +19 -19
  91. package/runtime/core/second-nature/rhythm/policy-bridge.js +34 -34
  92. package/runtime/core/second-nature/runtime/service-entry.js +45 -45
  93. package/runtime/core/second-nature/types.d.ts +51 -51
  94. package/runtime/guidance/draft-outreach-message.d.ts +7 -7
  95. package/runtime/guidance/draft-outreach-message.js +42 -42
  96. package/runtime/guidance/evidence-guidance.d.ts +40 -40
  97. package/runtime/guidance/evidence-guidance.js +52 -52
  98. package/runtime/guidance/index.d.ts +11 -11
  99. package/runtime/guidance/index.js +11 -11
  100. package/runtime/guidance/outreach-draft-schema.d.ts +228 -228
  101. package/runtime/guidance/outreach-draft-schema.js +80 -80
  102. package/runtime/observability/audit/append-only-audit-store.d.ts +14 -14
  103. package/runtime/observability/audit/append-only-audit-store.js +21 -21
  104. package/runtime/observability/audit/audit-envelope.d.ts +51 -51
  105. package/runtime/observability/audit/audit-envelope.js +130 -130
  106. package/runtime/observability/audit/verify-audit-hash-chain.d.ts +23 -23
  107. package/runtime/observability/audit/verify-audit-hash-chain.js +83 -83
  108. package/runtime/observability/db/index.js +47 -47
  109. package/runtime/observability/db/schema/host-capability-reports.d.ts +180 -180
  110. package/runtime/observability/db/schema/host-capability-reports.js +12 -12
  111. package/runtime/observability/db/schema/index.d.ts +947 -947
  112. package/runtime/observability/db/schema/index.js +71 -71
  113. package/runtime/observability/index.d.ts +20 -20
  114. package/runtime/observability/index.js +19 -19
  115. package/runtime/observability/query/explain-query.d.ts +48 -48
  116. package/runtime/observability/query/explain-query.js +114 -114
  117. package/runtime/observability/query/export-audit-bundle.d.ts +22 -22
  118. package/runtime/observability/query/export-audit-bundle.js +27 -27
  119. package/runtime/observability/services/decision-ledger.d.ts +46 -46
  120. package/runtime/observability/services/decision-ledger.js +161 -161
  121. package/runtime/observability/services/governance-audit.d.ts +41 -41
  122. package/runtime/observability/services/governance-audit.js +163 -163
  123. package/runtime/observability/services/governance-plane-recorder.d.ts +47 -47
  124. package/runtime/observability/services/governance-plane-recorder.js +55 -55
  125. package/runtime/observability/services/lived-experience-audit.d.ts +97 -97
  126. package/runtime/observability/services/lived-experience-audit.js +162 -162
  127. package/runtime/observability/services/observability-retention.d.ts +10 -0
  128. package/runtime/observability/services/observability-retention.js +37 -0
  129. package/runtime/observability/services/runtime-decision-recorder.d.ts +29 -29
  130. package/runtime/observability/services/runtime-decision-recorder.js +94 -94
  131. package/runtime/storage/bootstrap/native-sqlite-probe.d.ts +7 -7
  132. package/runtime/storage/bootstrap/native-sqlite-probe.js +28 -28
  133. package/runtime/storage/bootstrap/repair-gate.d.ts +17 -17
  134. package/runtime/storage/bootstrap/repair-gate.js +71 -71
  135. package/runtime/storage/bootstrap/storage-mode-smoke.d.ts +38 -38
  136. package/runtime/storage/bootstrap/storage-mode-smoke.js +85 -85
  137. package/runtime/storage/db/index.js +61 -61
  138. package/runtime/storage/db/schema/delivery-attempts.d.ts +199 -199
  139. package/runtime/storage/db/schema/delivery-attempts.js +13 -13
  140. package/runtime/storage/db/schema/index.d.ts +9 -9
  141. package/runtime/storage/db/schema/index.js +9 -9
  142. package/runtime/storage/db/schema/life-evidence-index.d.ts +161 -161
  143. package/runtime/storage/db/schema/life-evidence-index.js +11 -11
  144. package/runtime/storage/db/schema/operator-fallback-artifacts.d.ts +161 -161
  145. package/runtime/storage/db/schema/operator-fallback-artifacts.js +11 -11
  146. package/runtime/storage/db/schema/policies.d.ts +98 -98
  147. package/runtime/storage/db/schema/policies.js +8 -8
  148. package/runtime/storage/delivery/query-delivery-attempts.d.ts +3 -3
  149. package/runtime/storage/delivery/query-delivery-attempts.js +32 -32
  150. package/runtime/storage/delivery/types.d.ts +27 -27
  151. package/runtime/storage/delivery/types.js +1 -1
  152. package/runtime/storage/delivery/write-delivery-attempt.d.ts +6 -6
  153. package/runtime/storage/delivery/write-delivery-attempt.js +36 -36
  154. package/runtime/storage/fallback/load-operator-fallback.d.ts +14 -14
  155. package/runtime/storage/fallback/load-operator-fallback.js +47 -47
  156. package/runtime/storage/fallback/operator-fallback-types.d.ts +9 -9
  157. package/runtime/storage/fallback/operator-fallback-types.js +1 -1
  158. package/runtime/storage/fallback/operator-fallback-view.d.ts +11 -11
  159. package/runtime/storage/fallback/operator-fallback-view.js +1 -1
  160. package/runtime/storage/fallback/write-operator-fallback.d.ts +6 -6
  161. package/runtime/storage/fallback/write-operator-fallback.js +21 -21
  162. package/runtime/storage/index.d.ts +37 -37
  163. package/runtime/storage/index.js +30 -30
  164. package/runtime/storage/life-evidence/append-life-evidence.d.ts +7 -7
  165. package/runtime/storage/life-evidence/append-life-evidence.js +64 -64
  166. package/runtime/storage/life-evidence/types.d.ts +45 -45
  167. package/runtime/storage/life-evidence/types.js +6 -6
  168. package/runtime/storage/quiet/persist-quiet-artifact.d.ts +7 -7
  169. package/runtime/storage/quiet/persist-quiet-artifact.js +22 -22
  170. package/runtime/storage/quiet/quiet-artifact-types.d.ts +18 -18
  171. package/runtime/storage/quiet/quiet-artifact-types.js +1 -1
  172. package/runtime/storage/quiet/quiet-artifact-writer.d.ts +15 -15
  173. package/runtime/storage/quiet/quiet-artifact-writer.js +56 -56
  174. package/runtime/storage/repositories/credential-repository.js +30 -30
  175. package/runtime/storage/rhythm/rhythm-policy-snapshot.d.ts +10 -10
  176. package/runtime/storage/rhythm/rhythm-policy-snapshot.js +34 -34
  177. package/runtime/storage/services/credential-vault.d.ts +13 -13
  178. package/runtime/storage/services/credential-vault.js +116 -116
  179. package/runtime/storage/snapshots/continuity-snapshot.d.ts +9 -9
  180. package/runtime/storage/snapshots/continuity-snapshot.js +41 -41
  181. package/runtime/storage/snapshots/life-evidence-snapshot.d.ts +6 -6
  182. package/runtime/storage/snapshots/life-evidence-snapshot.js +114 -114
  183. package/runtime/storage/snapshots/types.d.ts +58 -58
  184. package/runtime/storage/snapshots/types.js +1 -1
  185. package/runtime/storage/state-api.js +104 -104
  186. package/runtime/storage/user-interest/load-user-interest-snapshot.d.ts +2 -2
  187. package/runtime/storage/user-interest/load-user-interest-snapshot.js +150 -150
  188. package/runtime/storage/user-interest/types.d.ts +25 -25
  189. package/runtime/storage/user-interest/types.js +1 -1
  190. package/workspace-ops-bridge.js +90 -81
@@ -1,63 +1,63 @@
1
- /**
2
- * Heartbeat Decision Loop
3
- *
4
- * Main entry point for the heartbeat runtime. Accepts a HeartbeatSignal,
5
- * builds runtime snapshot, plans candidate intents, evaluates hard guards,
6
- * and returns a HeartbeatCycleResult.
7
- *
8
- * Per design doc §4.3: heartbeat round follows the sequence:
9
- * signal → snapshot → plan → guard → result (HEARTBEAT_OK or intent_selected)
10
- *
11
- * Per ADR-005: heartbeat is the free-rhythm main entry; this loop
12
- * implements the default conservative path where HEARTBEAT_OK is
13
- * the first-class result when no action is warranted.
14
- */
15
- import type { HeartbeatSignal, HeartbeatCycleResult, HeartbeatCycleStatus, RuntimeScope, RuntimeTrigger } from "./signal.js";
16
- import type { CandidateIntent, ContinuitySnapshot, IntentKind } from "../types.js";
17
- import { type SnapshotInputs } from "./snapshot-builder.js";
18
- import { type HeartbeatRuntimeSnapshot } from "./runtime-snapshot.js";
19
- import type { GuidanceDraftPort } from "../../../guidance/outreach-draft-schema.js";
20
- import type { StateDatabase } from "../../../storage/db/index.js";
21
- import { type OpenClawDeliveryPort } from "../outreach/dispatch-user-outreach.js";
22
- export interface HeartbeatDecisionTracePayload {
23
- scope: RuntimeScope;
24
- status: HeartbeatCycleStatus;
25
- reasons: string[];
26
- selectedIntentId?: string;
27
- rhythmWindowId: string;
28
- allowedIntentKinds: IntentKind[];
29
- candidateCount: number;
30
- lifeEvidenceEmpty: boolean;
31
- trigger: RuntimeTrigger;
32
- }
33
- /** Optional outreach delivery chain: when set, first allowed `user_outreach` runs dispatch (CR-M1). */
34
- export interface HeartbeatOutreachDispatchDeps {
35
- state: StateDatabase;
36
- guidance: GuidanceDraftPort;
37
- delivery: OpenClawDeliveryPort;
38
- }
39
- /** Optional Quiet orchestration: when set, quiet/reflection allows run source-backed Quiet writer (T2.3.3). */
40
- export interface HeartbeatQuietWorkflowDeps {
41
- workspaceRoot: string;
42
- }
43
- /**
44
- * Resolves the heartbeat outcome for a guard-allowed intent (outreach dispatch, quiet orchestration, or default).
45
- * Exported for unit tests (CR-M1 wiring).
46
- */
47
- export declare function resolveAllowedIntentResult(intent: CandidateIntent, runtime: HeartbeatRuntimeSnapshot, inputs: SnapshotInputs, signal: HeartbeatSignal, deps: Pick<HeartbeatDeps, "outreachDispatch" | "quietWorkflow">): Promise<HeartbeatCycleResult>;
48
- export interface HeartbeatDeps {
49
- /** Load snapshot inputs from state-system */
50
- loadSnapshotInputs: () => Promise<SnapshotInputs>;
51
- /** Optional observability hook (T2.2.1): one record per completed cycle. */
52
- recordDecisionTrace?: (payload: HeartbeatDecisionTracePayload) => Promise<void>;
53
- outreachDispatch?: HeartbeatOutreachDispatchDeps;
54
- quietWorkflow?: HeartbeatQuietWorkflowDeps;
55
- }
56
- /**
57
- * Ingest a heartbeat rhythm signal and drive one full decision round.
58
- */
59
- export declare function ingestRhythmSignal(signal: HeartbeatSignal, deps: HeartbeatDeps): Promise<HeartbeatCycleResult>;
60
- /**
61
- * Build a snapshot directly from inputs (for testing or when state-system is unavailable).
62
- */
63
- export declare function buildSnapshotFromInputs(inputs: SnapshotInputs): ContinuitySnapshot;
1
+ /**
2
+ * Heartbeat Decision Loop
3
+ *
4
+ * Main entry point for the heartbeat runtime. Accepts a HeartbeatSignal,
5
+ * builds runtime snapshot, plans candidate intents, evaluates hard guards,
6
+ * and returns a HeartbeatCycleResult.
7
+ *
8
+ * Per design doc §4.3: heartbeat round follows the sequence:
9
+ * signal → snapshot → plan → guard → result (HEARTBEAT_OK or intent_selected)
10
+ *
11
+ * Per ADR-005: heartbeat is the free-rhythm main entry; this loop
12
+ * implements the default conservative path where HEARTBEAT_OK is
13
+ * the first-class result when no action is warranted.
14
+ */
15
+ import type { HeartbeatSignal, HeartbeatCycleResult, HeartbeatCycleStatus, RuntimeScope, RuntimeTrigger } from "./signal.js";
16
+ import type { CandidateIntent, ContinuitySnapshot, IntentKind } from "../types.js";
17
+ import { type SnapshotInputs } from "./snapshot-builder.js";
18
+ import { type HeartbeatRuntimeSnapshot } from "./runtime-snapshot.js";
19
+ import type { GuidanceDraftPort } from "../../../guidance/outreach-draft-schema.js";
20
+ import type { StateDatabase } from "../../../storage/db/index.js";
21
+ import { type OpenClawDeliveryPort } from "../outreach/dispatch-user-outreach.js";
22
+ export interface HeartbeatDecisionTracePayload {
23
+ scope: RuntimeScope;
24
+ status: HeartbeatCycleStatus;
25
+ reasons: string[];
26
+ selectedIntentId?: string;
27
+ rhythmWindowId: string;
28
+ allowedIntentKinds: IntentKind[];
29
+ candidateCount: number;
30
+ lifeEvidenceEmpty: boolean;
31
+ trigger: RuntimeTrigger;
32
+ }
33
+ /** Optional outreach delivery chain: when set, first allowed `user_outreach` runs dispatch (CR-M1). */
34
+ export interface HeartbeatOutreachDispatchDeps {
35
+ state: StateDatabase;
36
+ guidance: GuidanceDraftPort;
37
+ delivery: OpenClawDeliveryPort;
38
+ }
39
+ /** Optional Quiet orchestration: when set, quiet/reflection allows run source-backed Quiet writer (T2.3.3). */
40
+ export interface HeartbeatQuietWorkflowDeps {
41
+ workspaceRoot: string;
42
+ }
43
+ /**
44
+ * Resolves the heartbeat outcome for a guard-allowed intent (outreach dispatch, quiet orchestration, or default).
45
+ * Exported for unit tests (CR-M1 wiring).
46
+ */
47
+ export declare function resolveAllowedIntentResult(intent: CandidateIntent, runtime: HeartbeatRuntimeSnapshot, inputs: SnapshotInputs, signal: HeartbeatSignal, deps: Pick<HeartbeatDeps, "outreachDispatch" | "quietWorkflow">): Promise<HeartbeatCycleResult>;
48
+ export interface HeartbeatDeps {
49
+ /** Load snapshot inputs from state-system */
50
+ loadSnapshotInputs: () => Promise<SnapshotInputs>;
51
+ /** Optional observability hook (T2.2.1): one record per completed cycle. */
52
+ recordDecisionTrace?: (payload: HeartbeatDecisionTracePayload) => Promise<void>;
53
+ outreachDispatch?: HeartbeatOutreachDispatchDeps;
54
+ quietWorkflow?: HeartbeatQuietWorkflowDeps;
55
+ }
56
+ /**
57
+ * Ingest a heartbeat rhythm signal and drive one full decision round.
58
+ */
59
+ export declare function ingestRhythmSignal(signal: HeartbeatSignal, deps: HeartbeatDeps): Promise<HeartbeatCycleResult>;
60
+ /**
61
+ * Build a snapshot directly from inputs (for testing or when state-system is unavailable).
62
+ */
63
+ export declare function buildSnapshotFromInputs(inputs: SnapshotInputs): ContinuitySnapshot;
@@ -1,139 +1,162 @@
1
- import { buildContinuitySnapshot } from "./snapshot-builder.js";
2
- import { buildHeartbeatRuntimeSnapshot } from "./runtime-snapshot.js";
3
- import { planCandidateIntents } from "../orchestrator/intent-planner.js";
4
- import { evaluateHardGuards } from "../orchestrator/guard-layer.js";
5
- import { dispatchUserOutreachIntent } from "../outreach/dispatch-user-outreach.js";
6
- import { buildJudgeOutreachInputFromSnapshot } from "../outreach/judge-input-from-snapshot.js";
7
- import { runSourceBackedQuiet } from "../quiet/run-source-backed-quiet.js";
8
- /**
9
- * Resolves the heartbeat outcome for a guard-allowed intent (outreach dispatch, quiet orchestration, or default).
10
- * Exported for unit tests (CR-M1 wiring).
11
- */
12
- export async function resolveAllowedIntentResult(intent, runtime, inputs, signal, deps) {
13
- const day = typeof signal.payload.timestamp === "string" ? signal.payload.timestamp.slice(0, 10) : "1970-01-01";
14
- if (intent.effectClass === "user_outreach" && deps.outreachDispatch) {
15
- return dispatchUserOutreachIntent({
16
- candidate: intent,
17
- snapshot: runtime,
18
- judgeInput: buildJudgeOutreachInputFromSnapshot(intent, runtime, inputs),
19
- guidance: deps.outreachDispatch.guidance,
20
- delivery: deps.outreachDispatch.delivery,
21
- state: deps.outreachDispatch.state,
22
- });
23
- }
24
- if (deps.quietWorkflow &&
25
- (intent.kind === "quiet" || (intent.kind === "reflection" && intent.effectClass === "narrative_reflection"))) {
26
- const quietRun = await runSourceBackedQuiet({
27
- candidate: intent,
28
- runtime,
29
- day,
30
- userInterestSnapshot: inputs.userInterestSnapshot,
31
- workspaceRoot: deps.quietWorkflow.workspaceRoot,
32
- });
33
- return quietRun.result;
34
- }
35
- return {
36
- scope: "rhythm",
37
- status: "intent_selected",
38
- selectedIntentId: intent.id,
39
- reasons: [],
40
- };
41
- }
42
- /**
43
- * Ingest a heartbeat rhythm signal and drive one full decision round.
44
- */
45
- export async function ingestRhythmSignal(signal, deps) {
46
- const inputs = await deps.loadSnapshotInputs();
47
- const snapshot = buildContinuitySnapshot(inputs);
48
- const timestamp = signal.payload.timestamp;
49
- const runtime = buildHeartbeatRuntimeSnapshot(timestamp, inputs, snapshot);
50
- const candidates = planCandidateIntents(runtime);
51
- const emitTrace = async (result) => {
52
- if (!deps.recordDecisionTrace)
53
- return;
54
- await deps.recordDecisionTrace({
55
- scope: result.scope,
56
- status: result.status,
57
- reasons: result.reasons,
58
- selectedIntentId: result.selectedIntentId,
59
- rhythmWindowId: runtime.rhythmWindow.windowId,
60
- allowedIntentKinds: [...runtime.rhythmWindow.allowedIntentKinds],
61
- candidateCount: candidates.length,
62
- lifeEvidenceEmpty: runtime.lifeEvidence.evidenceRefs.length === 0 &&
63
- runtime.lifeEvidence.platformEventCount === 0 &&
64
- runtime.lifeEvidence.workEventCount === 0,
65
- trigger: signal.trigger,
66
- });
67
- };
68
- let hasCandidates = false;
69
- let anyAllow = false;
70
- let anyDefer = false;
71
- let anyDeny = false;
72
- const denyReasons = [];
73
- for (const intent of candidates) {
74
- hasCandidates = true;
75
- const evaluation = evaluateHardGuards(intent, runtime);
76
- if (evaluation.verdict === "allow") {
77
- anyAllow = true;
78
- const base = {
79
- scope: "rhythm",
80
- status: "intent_selected",
81
- selectedIntentId: intent.id,
82
- reasons: evaluation.reasons,
83
- };
84
- const resolved = await resolveAllowedIntentResult(intent, runtime, inputs, signal, deps);
85
- const result = resolved.status === "intent_selected" && resolved.reasons.length === 0 && evaluation.reasons.length > 0
86
- ? { ...resolved, reasons: evaluation.reasons }
87
- : resolved;
88
- await emitTrace(result);
89
- return result;
90
- }
91
- if (evaluation.verdict === "defer") {
92
- anyDefer = true;
93
- denyReasons.push(`${intent.id}:${evaluation.verdict}(${evaluation.reasons.join(",")})`);
94
- continue;
95
- }
96
- anyDeny = true;
97
- denyReasons.push(`${intent.id}:${evaluation.verdict}(${evaluation.reasons.join(",")})`);
98
- }
99
- if (!hasCandidates) {
100
- const result = {
101
- scope: "rhythm",
102
- status: "heartbeat_ok",
103
- reasons: ["silent_no_candidates"],
104
- };
105
- await emitTrace(result);
106
- return result;
107
- }
108
- if (!anyAllow && anyDefer && !anyDeny) {
109
- const result = {
110
- scope: "rhythm",
111
- status: "deferred",
112
- reasons: denyReasons.length > 0 ? denyReasons : ["all_candidates_deferred"],
113
- };
114
- await emitTrace(result);
115
- return result;
116
- }
117
- if (!anyAllow && denyReasons.length > 0) {
118
- const result = {
119
- scope: "rhythm",
120
- status: "denied",
121
- reasons: denyReasons,
122
- };
123
- await emitTrace(result);
124
- return result;
125
- }
126
- const result = {
127
- scope: "rhythm",
128
- status: "heartbeat_ok",
129
- reasons: ["no_allow_verdict"],
130
- };
131
- await emitTrace(result);
132
- return result;
133
- }
134
- /**
135
- * Build a snapshot directly from inputs (for testing or when state-system is unavailable).
136
- */
137
- export function buildSnapshotFromInputs(inputs) {
138
- return buildContinuitySnapshot(inputs);
139
- }
1
+ import { buildContinuitySnapshot, } from "./snapshot-builder.js";
2
+ import { buildHeartbeatRuntimeSnapshot, } from "./runtime-snapshot.js";
3
+ import { planCandidateIntents } from "../orchestrator/intent-planner.js";
4
+ import { evaluateHardGuards } from "../orchestrator/guard-layer.js";
5
+ import { dispatchUserOutreachIntent, } from "../outreach/dispatch-user-outreach.js";
6
+ import { buildJudgeOutreachInputFromSnapshot } from "../outreach/judge-input-from-snapshot.js";
7
+ import { runSourceBackedQuiet } from "../quiet/run-source-backed-quiet.js";
8
+ /**
9
+ * Resolves the heartbeat outcome for a guard-allowed intent (outreach dispatch, quiet orchestration, or default).
10
+ * Exported for unit tests (CR-M1 wiring).
11
+ */
12
+ export async function resolveAllowedIntentResult(intent, runtime, inputs, signal, deps) {
13
+ const day = typeof signal.payload.timestamp === "string"
14
+ ? signal.payload.timestamp.slice(0, 10)
15
+ : "1970-01-01";
16
+ if (intent.effectClass === "user_outreach" && deps.outreachDispatch) {
17
+ return dispatchUserOutreachIntent({
18
+ candidate: intent,
19
+ snapshot: runtime,
20
+ judgeInput: buildJudgeOutreachInputFromSnapshot(intent, runtime, inputs),
21
+ guidance: deps.outreachDispatch.guidance,
22
+ delivery: deps.outreachDispatch.delivery,
23
+ state: deps.outreachDispatch.state,
24
+ });
25
+ }
26
+ if (deps.quietWorkflow &&
27
+ (intent.kind === "quiet" ||
28
+ (intent.kind === "reflection" &&
29
+ intent.effectClass === "narrative_reflection"))) {
30
+ const quietRun = await runSourceBackedQuiet({
31
+ candidate: intent,
32
+ runtime,
33
+ day,
34
+ userInterestSnapshot: inputs.userInterestSnapshot,
35
+ workspaceRoot: deps.quietWorkflow.workspaceRoot,
36
+ });
37
+ return quietRun.result;
38
+ }
39
+ // T2.2.3 (CH-14-02/03 / CH-15-01): all intent_selected results must carry at least one
40
+ // machine-readable reason so operators can distinguish between effect classes:
41
+ // - maintenance / no_effect → "internal_tick" (no external side-effects)
42
+ // - connector_action without dispatch wired → "connector_dispatch_unwired"
43
+ // - external_platform_action / memory_curation not generated by intent-planner today;
44
+ // if a future path produces them, they will reach the fallback [] branch below and
45
+ // should have dedicated reason codes added (e.g. "external_platform_action_unwired").
46
+ // - other (outreach / quiet) → caught by the early-return branches above
47
+ const noExternalEffect = intent.effectClass === "maintenance" ||
48
+ intent.effectClass === "no_effect" ||
49
+ intent.kind === "maintenance";
50
+ const connectorUnwired = intent.effectClass === "connector_action";
51
+ const reasons = noExternalEffect
52
+ ? ["internal_tick"]
53
+ : connectorUnwired
54
+ ? ["connector_dispatch_unwired"]
55
+ : [];
56
+ return {
57
+ scope: "rhythm",
58
+ status: "intent_selected",
59
+ selectedIntentId: intent.id,
60
+ reasons,
61
+ };
62
+ }
63
+ /**
64
+ * Ingest a heartbeat rhythm signal and drive one full decision round.
65
+ */
66
+ export async function ingestRhythmSignal(signal, deps) {
67
+ const inputs = await deps.loadSnapshotInputs();
68
+ const snapshot = buildContinuitySnapshot(inputs);
69
+ const timestamp = signal.payload.timestamp;
70
+ const runtime = buildHeartbeatRuntimeSnapshot(timestamp, inputs, snapshot);
71
+ const candidates = planCandidateIntents(runtime);
72
+ const emitTrace = async (result) => {
73
+ if (!deps.recordDecisionTrace)
74
+ return;
75
+ await deps.recordDecisionTrace({
76
+ scope: result.scope,
77
+ status: result.status,
78
+ reasons: result.reasons,
79
+ selectedIntentId: result.selectedIntentId,
80
+ rhythmWindowId: runtime.rhythmWindow.windowId,
81
+ allowedIntentKinds: [...runtime.rhythmWindow.allowedIntentKinds],
82
+ candidateCount: candidates.length,
83
+ lifeEvidenceEmpty: runtime.lifeEvidence.evidenceRefs.length === 0 &&
84
+ runtime.lifeEvidence.platformEventCount === 0 &&
85
+ runtime.lifeEvidence.workEventCount === 0,
86
+ trigger: signal.trigger,
87
+ });
88
+ };
89
+ let hasCandidates = false;
90
+ let anyAllow = false;
91
+ let anyDefer = false;
92
+ let anyDeny = false;
93
+ const denyReasons = [];
94
+ for (const intent of candidates) {
95
+ hasCandidates = true;
96
+ const evaluation = evaluateHardGuards(intent, runtime);
97
+ if (evaluation.verdict === "allow") {
98
+ anyAllow = true;
99
+ const base = {
100
+ scope: "rhythm",
101
+ status: "intent_selected",
102
+ selectedIntentId: intent.id,
103
+ reasons: evaluation.reasons,
104
+ };
105
+ const resolved = await resolveAllowedIntentResult(intent, runtime, inputs, signal, deps);
106
+ const result = resolved.status === "intent_selected" &&
107
+ resolved.reasons.length === 0 &&
108
+ evaluation.reasons.length > 0
109
+ ? { ...resolved, reasons: evaluation.reasons }
110
+ : resolved;
111
+ await emitTrace(result);
112
+ return result;
113
+ }
114
+ if (evaluation.verdict === "defer") {
115
+ anyDefer = true;
116
+ denyReasons.push(`${intent.id}:${evaluation.verdict}(${evaluation.reasons.join(",")})`);
117
+ continue;
118
+ }
119
+ anyDeny = true;
120
+ denyReasons.push(`${intent.id}:${evaluation.verdict}(${evaluation.reasons.join(",")})`);
121
+ }
122
+ if (!hasCandidates) {
123
+ const result = {
124
+ scope: "rhythm",
125
+ status: "heartbeat_ok",
126
+ reasons: ["silent_no_candidates"],
127
+ };
128
+ await emitTrace(result);
129
+ return result;
130
+ }
131
+ if (!anyAllow && anyDefer && !anyDeny) {
132
+ const result = {
133
+ scope: "rhythm",
134
+ status: "deferred",
135
+ reasons: denyReasons.length > 0 ? denyReasons : ["all_candidates_deferred"],
136
+ };
137
+ await emitTrace(result);
138
+ return result;
139
+ }
140
+ if (!anyAllow && denyReasons.length > 0) {
141
+ const result = {
142
+ scope: "rhythm",
143
+ status: "denied",
144
+ reasons: denyReasons,
145
+ };
146
+ await emitTrace(result);
147
+ return result;
148
+ }
149
+ const result = {
150
+ scope: "rhythm",
151
+ status: "heartbeat_ok",
152
+ reasons: ["no_allow_verdict"],
153
+ };
154
+ await emitTrace(result);
155
+ return result;
156
+ }
157
+ /**
158
+ * Build a snapshot directly from inputs (for testing or when state-system is unavailable).
159
+ */
160
+ export function buildSnapshotFromInputs(inputs) {
161
+ return buildContinuitySnapshot(inputs);
162
+ }
@@ -1,8 +1,8 @@
1
- export { type RuntimeScope, type RuntimeTrigger, type HeartbeatCycleStatus, type HeartbeatSignal, type ScopedRuntimeInput, type HeartbeatCycleResult, type ScopeRouteResult, } from "./signal.js";
2
- export { buildContinuitySnapshot, type SnapshotInputs, } from "./snapshot-builder.js";
3
- export { ingestRhythmSignal, resolveAllowedIntentResult, type HeartbeatDeps, type HeartbeatOutreachDispatchDeps, type HeartbeatQuietWorkflowDeps, type HeartbeatDecisionTracePayload, buildSnapshotFromInputs, } from "./heartbeat-loop.js";
4
- export { buildHeartbeatRuntimeSnapshot, buildLifeEvidenceSliceFromInputs, buildHardGuardDeps, resolveRhythmPolicyForHeartbeat, isLifeEvidenceSliceEmpty, type HeartbeatRuntimeSnapshot, type PlannerLifeEvidenceSlice, type HardGuardDeps, } from "./runtime-snapshot.js";
5
- export { buildPlannerRhythmWindow, type PlannerRhythmWindowSlice } from "../rhythm/planner-rhythm-window.js";
6
- export { runHeartbeatCycle, type RunHeartbeatCycleInput } from "./run-heartbeat-cycle.js";
7
- export { routeScopedInput, type ScopeRouterDeps, } from "./scope-router.js";
8
- export { requestGuidanceForIntent, dispatchAllowedEffect, executeHeartbeatCycle, type GuidanceBridgeDeps, type EffectDispatchDeps, type HeartbeatExecutorDeps, type GuidanceBridgeResult, type HeartbeatExecutionResult, } from "./heartbeat-executor.js";
1
+ export { type RuntimeScope, type RuntimeTrigger, type HeartbeatCycleStatus, type HeartbeatSignal, type ScopedRuntimeInput, type HeartbeatCycleResult, type ScopeRouteResult, } from "./signal.js";
2
+ export { buildContinuitySnapshot, type SnapshotInputs, } from "./snapshot-builder.js";
3
+ export { ingestRhythmSignal, resolveAllowedIntentResult, type HeartbeatDeps, type HeartbeatOutreachDispatchDeps, type HeartbeatQuietWorkflowDeps, type HeartbeatDecisionTracePayload, buildSnapshotFromInputs, } from "./heartbeat-loop.js";
4
+ export { buildHeartbeatRuntimeSnapshot, buildLifeEvidenceSliceFromInputs, buildHardGuardDeps, resolveRhythmPolicyForHeartbeat, isLifeEvidenceSliceEmpty, type HeartbeatRuntimeSnapshot, type PlannerLifeEvidenceSlice, type HardGuardDeps, } from "./runtime-snapshot.js";
5
+ export { buildPlannerRhythmWindow, type PlannerRhythmWindowSlice } from "../rhythm/planner-rhythm-window.js";
6
+ export { runHeartbeatCycle, type RunHeartbeatCycleInput } from "./run-heartbeat-cycle.js";
7
+ export { routeScopedInput, type ScopeRouterDeps, } from "./scope-router.js";
8
+ export { requestGuidanceForIntent, dispatchAllowedEffect, executeHeartbeatCycle, type GuidanceBridgeDeps, type EffectDispatchDeps, type HeartbeatExecutorDeps, type GuidanceBridgeResult, type HeartbeatExecutionResult, } from "./heartbeat-executor.js";
@@ -1,7 +1,7 @@
1
- export { buildContinuitySnapshot, } from "./snapshot-builder.js";
2
- export { ingestRhythmSignal, resolveAllowedIntentResult, buildSnapshotFromInputs, } from "./heartbeat-loop.js";
3
- export { buildHeartbeatRuntimeSnapshot, buildLifeEvidenceSliceFromInputs, buildHardGuardDeps, resolveRhythmPolicyForHeartbeat, isLifeEvidenceSliceEmpty, } from "./runtime-snapshot.js";
4
- export { buildPlannerRhythmWindow } from "../rhythm/planner-rhythm-window.js";
5
- export { runHeartbeatCycle } from "./run-heartbeat-cycle.js";
6
- export { routeScopedInput, } from "./scope-router.js";
7
- export { requestGuidanceForIntent, dispatchAllowedEffect, executeHeartbeatCycle, } from "./heartbeat-executor.js";
1
+ export { buildContinuitySnapshot, } from "./snapshot-builder.js";
2
+ export { ingestRhythmSignal, resolveAllowedIntentResult, buildSnapshotFromInputs, } from "./heartbeat-loop.js";
3
+ export { buildHeartbeatRuntimeSnapshot, buildLifeEvidenceSliceFromInputs, buildHardGuardDeps, resolveRhythmPolicyForHeartbeat, isLifeEvidenceSliceEmpty, } from "./runtime-snapshot.js";
4
+ export { buildPlannerRhythmWindow } from "../rhythm/planner-rhythm-window.js";
5
+ export { runHeartbeatCycle } from "./run-heartbeat-cycle.js";
6
+ export { routeScopedInput, } from "./scope-router.js";
7
+ export { requestGuidanceForIntent, dispatchAllowedEffect, executeHeartbeatCycle, } from "./heartbeat-executor.js";
@@ -1,21 +1,21 @@
1
- /**
2
- * Control-plane heartbeat cycle entry (T2.1.1).
3
- *
4
- * Core logic: runtime availability gate → scope routing (user_task bypasses rhythm) →
5
- * rhythm path delegates to ingestRhythmSignal. Mirrors L0 control-plane-system §4.3.
6
- *
7
- * Boundaries: does not claim lived-experience completion when runtime is unavailable;
8
- * user_task / user_reply do not enter the rhythm candidate planner.
9
- */
10
- import type { HeartbeatSignal, HeartbeatCycleResult } from "./signal.js";
11
- import type { HeartbeatDeps } from "./heartbeat-loop.js";
12
- export interface RunHeartbeatCycleInput {
13
- signal: HeartbeatSignal;
14
- /** When false, return runtime_carrier_only without loading snapshots (host-safe carrier). */
15
- runtimeAvailable: boolean;
16
- deps: HeartbeatDeps;
17
- }
18
- /**
19
- * Single entry for one heartbeat turn: scope routing, runtime gate, then rhythm loop if applicable.
20
- */
21
- export declare function runHeartbeatCycle(input: RunHeartbeatCycleInput): Promise<HeartbeatCycleResult>;
1
+ /**
2
+ * Control-plane heartbeat cycle entry (T2.1.1).
3
+ *
4
+ * Core logic: runtime availability gate → scope routing (user_task bypasses rhythm) →
5
+ * rhythm path delegates to ingestRhythmSignal. Mirrors L0 control-plane-system §4.3.
6
+ *
7
+ * Boundaries: does not claim lived-experience completion when runtime is unavailable;
8
+ * user_task / user_reply do not enter the rhythm candidate planner.
9
+ */
10
+ import type { HeartbeatSignal, HeartbeatCycleResult } from "./signal.js";
11
+ import type { HeartbeatDeps } from "./heartbeat-loop.js";
12
+ export interface RunHeartbeatCycleInput {
13
+ signal: HeartbeatSignal;
14
+ /** When false, return runtime_carrier_only without loading snapshots (host-safe carrier). */
15
+ runtimeAvailable: boolean;
16
+ deps: HeartbeatDeps;
17
+ }
18
+ /**
19
+ * Single entry for one heartbeat turn: scope routing, runtime gate, then rhythm loop if applicable.
20
+ */
21
+ export declare function runHeartbeatCycle(input: RunHeartbeatCycleInput): Promise<HeartbeatCycleResult>;
@@ -1,35 +1,35 @@
1
- import { ingestRhythmSignal } from "./heartbeat-loop.js";
2
- import { routeScopedInput } from "./scope-router.js";
3
- /**
4
- * Single entry for one heartbeat turn: scope routing, runtime gate, then rhythm loop if applicable.
5
- */
6
- export async function runHeartbeatCycle(input) {
7
- const scoped = {
8
- trigger: input.signal.trigger,
9
- scopeHint: input.signal.scopeHint,
10
- payload: input.signal.payload,
11
- };
12
- const route = routeScopedInput(scoped);
13
- if (!input.runtimeAvailable) {
14
- return {
15
- scope: route.scope,
16
- status: "runtime_carrier_only",
17
- reasons: ["runtime_unavailable_no_lived_experience_loop"],
18
- };
19
- }
20
- if (route.scope === "user_task") {
21
- return {
22
- scope: "user_task",
23
- status: "heartbeat_ok",
24
- reasons: ["rhythm_gate_bypass_user_task"],
25
- };
26
- }
27
- if (route.scope === "user_reply") {
28
- return {
29
- scope: "user_reply",
30
- status: "heartbeat_ok",
31
- reasons: ["user_reply_light_continuity_skeleton"],
32
- };
33
- }
34
- return ingestRhythmSignal(input.signal, input.deps);
35
- }
1
+ import { ingestRhythmSignal } from "./heartbeat-loop.js";
2
+ import { routeScopedInput } from "./scope-router.js";
3
+ /**
4
+ * Single entry for one heartbeat turn: scope routing, runtime gate, then rhythm loop if applicable.
5
+ */
6
+ export async function runHeartbeatCycle(input) {
7
+ const scoped = {
8
+ trigger: input.signal.trigger,
9
+ scopeHint: input.signal.scopeHint,
10
+ payload: input.signal.payload,
11
+ };
12
+ const route = routeScopedInput(scoped);
13
+ if (!input.runtimeAvailable) {
14
+ return {
15
+ scope: route.scope,
16
+ status: "runtime_carrier_only",
17
+ reasons: ["runtime_unavailable_no_lived_experience_loop"],
18
+ };
19
+ }
20
+ if (route.scope === "user_task") {
21
+ return {
22
+ scope: "user_task",
23
+ status: "heartbeat_ok",
24
+ reasons: ["rhythm_gate_bypass_user_task"],
25
+ };
26
+ }
27
+ if (route.scope === "user_reply") {
28
+ return {
29
+ scope: "user_reply",
30
+ status: "heartbeat_ok",
31
+ reasons: ["user_reply_light_continuity_skeleton"],
32
+ };
33
+ }
34
+ return ingestRhythmSignal(input.signal, input.deps);
35
+ }