@haaaiawd/second-nature 0.1.16 → 0.1.18
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.
- package/index.js +855 -851
- package/openclaw.plugin.json +29 -29
- package/package.json +52 -52
- package/runtime/cli/commands/index.d.ts +14 -14
- package/runtime/cli/commands/index.js +193 -193
- package/runtime/cli/explain/explain-surface-subject.d.ts +8 -8
- package/runtime/cli/explain/explain-surface-subject.js +9 -9
- package/runtime/cli/explain/format-explanation.d.ts +12 -12
- package/runtime/cli/explain/format-explanation.js +12 -12
- package/runtime/cli/explain/resolve-subject.js +41 -41
- package/runtime/cli/host-capability/classify-delivery.d.ts +14 -14
- package/runtime/cli/host-capability/classify-delivery.js +20 -20
- package/runtime/cli/host-capability/probe-host-capability.d.ts +2 -2
- package/runtime/cli/host-capability/probe-host-capability.js +58 -58
- package/runtime/cli/host-capability/record-host-capability.d.ts +6 -6
- package/runtime/cli/host-capability/record-host-capability.js +14 -14
- package/runtime/cli/host-capability/types.d.ts +71 -71
- package/runtime/cli/host-capability/types.js +6 -6
- package/runtime/cli/host-smoke/run-host-smoke.d.ts +2 -2
- package/runtime/cli/host-smoke/run-host-smoke.js +40 -40
- package/runtime/cli/host-smoke/types.d.ts +35 -35
- package/runtime/cli/host-smoke/types.js +6 -6
- package/runtime/cli/index.js +58 -54
- package/runtime/cli/ops/heartbeat-surface.d.ts +38 -35
- package/runtime/cli/ops/heartbeat-surface.js +73 -71
- package/runtime/cli/ops/ops-router.d.ts +19 -16
- package/runtime/cli/ops/ops-router.js +89 -87
- package/runtime/cli/ops/show-operator-fallback.d.ts +13 -13
- package/runtime/cli/ops/show-operator-fallback.js +22 -22
- package/runtime/cli/ops/workspace-heartbeat-runner.d.ts +19 -10
- package/runtime/cli/ops/workspace-heartbeat-runner.js +39 -26
- package/runtime/cli/read-models/index.d.ts +29 -29
- package/runtime/cli/read-models/index.js +256 -256
- package/runtime/cli/read-models/operator-explain-map.d.ts +6 -6
- package/runtime/cli/read-models/operator-explain-map.js +10 -10
- package/runtime/cli/read-models/types.d.ts +79 -79
- package/runtime/cli/runtime/runtime-artifact-boundary.d.ts +28 -28
- package/runtime/cli/runtime/runtime-artifact-boundary.js +94 -94
- package/runtime/connectors/base/contract.d.ts +87 -87
- package/runtime/connectors/base/execution-policy.d.ts +47 -47
- package/runtime/connectors/base/execution-policy.js +82 -82
- package/runtime/connectors/base/index.d.ts +8 -8
- package/runtime/connectors/base/index.js +8 -8
- package/runtime/connectors/base/manifest.d.ts +64 -64
- package/runtime/connectors/base/manifest.js +86 -86
- package/runtime/connectors/base/map-life-evidence.d.ts +16 -16
- package/runtime/connectors/base/map-life-evidence.js +79 -79
- package/runtime/connectors/base/policy-layer.d.ts +29 -29
- package/runtime/connectors/base/policy-layer.js +198 -198
- package/runtime/connectors/base/route-planner.js +99 -99
- package/runtime/connectors/index.d.ts +5 -5
- package/runtime/connectors/index.js +5 -5
- package/runtime/connectors/near-real/near-real-connector-smoke.d.ts +19 -19
- package/runtime/connectors/near-real/near-real-connector-smoke.js +152 -152
- package/runtime/core/second-nature/heartbeat/heartbeat-executor.js +114 -114
- package/runtime/core/second-nature/heartbeat/heartbeat-loop.d.ts +63 -63
- package/runtime/core/second-nature/heartbeat/heartbeat-loop.js +139 -139
- package/runtime/core/second-nature/heartbeat/index.d.ts +8 -8
- package/runtime/core/second-nature/heartbeat/index.js +7 -7
- package/runtime/core/second-nature/heartbeat/run-heartbeat-cycle.d.ts +21 -21
- package/runtime/core/second-nature/heartbeat/run-heartbeat-cycle.js +35 -35
- package/runtime/core/second-nature/heartbeat/runtime-snapshot.d.ts +28 -28
- package/runtime/core/second-nature/heartbeat/runtime-snapshot.js +35 -35
- package/runtime/core/second-nature/heartbeat/signal.d.ts +42 -42
- package/runtime/core/second-nature/heartbeat/snapshot-builder.d.ts +51 -51
- package/runtime/core/second-nature/index.d.ts +22 -22
- package/runtime/core/second-nature/index.js +22 -22
- package/runtime/core/second-nature/orchestrator/effect-dispatcher.d.ts +100 -100
- package/runtime/core/second-nature/orchestrator/effect-dispatcher.js +144 -144
- package/runtime/core/second-nature/orchestrator/guard-layer.d.ts +8 -8
- package/runtime/core/second-nature/orchestrator/guard-layer.js +110 -110
- package/runtime/core/second-nature/orchestrator/intent-planner.d.ts +13 -13
- package/runtime/core/second-nature/orchestrator/intent-planner.js +199 -199
- package/runtime/core/second-nature/orchestrator/lease-manager.d.ts +14 -14
- package/runtime/core/second-nature/orchestrator/lease-manager.js +58 -58
- package/runtime/core/second-nature/outreach/build-outreach-draft-request.d.ts +6 -6
- package/runtime/core/second-nature/outreach/build-outreach-draft-request.js +63 -63
- package/runtime/core/second-nature/outreach/delivery-target.d.ts +26 -26
- package/runtime/core/second-nature/outreach/delivery-target.js +70 -70
- package/runtime/core/second-nature/outreach/dispatch-user-outreach.d.ts +38 -38
- package/runtime/core/second-nature/outreach/dispatch-user-outreach.js +119 -119
- package/runtime/core/second-nature/outreach/judge-input-from-snapshot.d.ts +7 -7
- package/runtime/core/second-nature/outreach/judge-input-from-snapshot.js +45 -45
- package/runtime/core/second-nature/outreach/judge-outreach.d.ts +40 -40
- package/runtime/core/second-nature/outreach/judge-outreach.js +121 -121
- package/runtime/core/second-nature/quiet/run-source-backed-quiet.d.ts +21 -21
- package/runtime/core/second-nature/quiet/run-source-backed-quiet.js +123 -123
- package/runtime/core/second-nature/rhythm/planner-rhythm-window.d.ts +15 -15
- package/runtime/core/second-nature/rhythm/planner-rhythm-window.js +52 -52
- package/runtime/core/second-nature/rhythm/policy-bridge.d.ts +19 -19
- package/runtime/core/second-nature/rhythm/policy-bridge.js +34 -34
- package/runtime/core/second-nature/runtime/service-entry.js +45 -45
- package/runtime/core/second-nature/types.d.ts +51 -51
- package/runtime/guidance/draft-outreach-message.d.ts +7 -7
- package/runtime/guidance/draft-outreach-message.js +42 -42
- package/runtime/guidance/evidence-guidance.d.ts +40 -40
- package/runtime/guidance/evidence-guidance.js +52 -52
- package/runtime/guidance/index.d.ts +11 -11
- package/runtime/guidance/index.js +11 -11
- package/runtime/guidance/outreach-draft-schema.d.ts +228 -228
- package/runtime/guidance/outreach-draft-schema.js +80 -80
- package/runtime/observability/audit/append-only-audit-store.d.ts +14 -14
- package/runtime/observability/audit/append-only-audit-store.js +21 -21
- package/runtime/observability/audit/audit-envelope.d.ts +51 -51
- package/runtime/observability/audit/audit-envelope.js +130 -130
- package/runtime/observability/audit/verify-audit-hash-chain.d.ts +23 -23
- package/runtime/observability/audit/verify-audit-hash-chain.js +83 -83
- package/runtime/observability/db/index.js +124 -124
- package/runtime/observability/db/schema/host-capability-reports.d.ts +180 -180
- package/runtime/observability/db/schema/host-capability-reports.js +12 -12
- package/runtime/observability/db/schema/index.d.ts +947 -947
- package/runtime/observability/db/schema/index.js +71 -71
- package/runtime/observability/index.d.ts +20 -19
- package/runtime/observability/index.js +19 -18
- package/runtime/observability/query/explain-query.d.ts +48 -48
- package/runtime/observability/query/explain-query.js +114 -114
- package/runtime/observability/query/export-audit-bundle.d.ts +22 -22
- package/runtime/observability/query/export-audit-bundle.js +27 -27
- package/runtime/observability/services/decision-ledger.d.ts +46 -46
- package/runtime/observability/services/decision-ledger.js +161 -161
- package/runtime/observability/services/governance-audit.d.ts +41 -41
- package/runtime/observability/services/governance-audit.js +163 -163
- package/runtime/observability/services/governance-plane-recorder.d.ts +47 -47
- package/runtime/observability/services/governance-plane-recorder.js +55 -55
- package/runtime/observability/services/lived-experience-audit.d.ts +97 -97
- package/runtime/observability/services/lived-experience-audit.js +162 -162
- package/runtime/observability/services/runtime-decision-recorder.d.ts +29 -0
- package/runtime/observability/services/runtime-decision-recorder.js +94 -0
- package/runtime/storage/bootstrap/native-sqlite-probe.d.ts +7 -7
- package/runtime/storage/bootstrap/native-sqlite-probe.js +28 -28
- package/runtime/storage/bootstrap/repair-gate.d.ts +17 -17
- package/runtime/storage/bootstrap/repair-gate.js +71 -71
- package/runtime/storage/bootstrap/storage-mode-smoke.d.ts +38 -38
- package/runtime/storage/bootstrap/storage-mode-smoke.js +85 -85
- package/runtime/storage/db/index.js +154 -154
- package/runtime/storage/db/schema/delivery-attempts.d.ts +199 -199
- package/runtime/storage/db/schema/delivery-attempts.js +13 -13
- package/runtime/storage/db/schema/index.d.ts +9 -9
- package/runtime/storage/db/schema/index.js +9 -9
- package/runtime/storage/db/schema/life-evidence-index.d.ts +161 -161
- package/runtime/storage/db/schema/life-evidence-index.js +11 -11
- package/runtime/storage/db/schema/operator-fallback-artifacts.d.ts +161 -161
- package/runtime/storage/db/schema/operator-fallback-artifacts.js +11 -11
- package/runtime/storage/db/schema/policies.d.ts +98 -98
- package/runtime/storage/db/schema/policies.js +8 -8
- package/runtime/storage/delivery/query-delivery-attempts.d.ts +3 -3
- package/runtime/storage/delivery/query-delivery-attempts.js +32 -32
- package/runtime/storage/delivery/types.d.ts +27 -27
- package/runtime/storage/delivery/types.js +1 -1
- package/runtime/storage/delivery/write-delivery-attempt.d.ts +6 -6
- package/runtime/storage/delivery/write-delivery-attempt.js +36 -36
- package/runtime/storage/fallback/load-operator-fallback.d.ts +14 -14
- package/runtime/storage/fallback/load-operator-fallback.js +47 -47
- package/runtime/storage/fallback/operator-fallback-types.d.ts +9 -9
- package/runtime/storage/fallback/operator-fallback-types.js +1 -1
- package/runtime/storage/fallback/operator-fallback-view.d.ts +11 -11
- package/runtime/storage/fallback/operator-fallback-view.js +1 -1
- package/runtime/storage/fallback/write-operator-fallback.d.ts +6 -6
- package/runtime/storage/fallback/write-operator-fallback.js +21 -21
- package/runtime/storage/index.d.ts +37 -37
- package/runtime/storage/index.js +30 -30
- package/runtime/storage/life-evidence/append-life-evidence.d.ts +7 -7
- package/runtime/storage/life-evidence/append-life-evidence.js +64 -64
- package/runtime/storage/life-evidence/types.d.ts +45 -45
- package/runtime/storage/life-evidence/types.js +6 -6
- package/runtime/storage/quiet/persist-quiet-artifact.d.ts +7 -7
- package/runtime/storage/quiet/persist-quiet-artifact.js +22 -22
- package/runtime/storage/quiet/quiet-artifact-types.d.ts +18 -18
- package/runtime/storage/quiet/quiet-artifact-types.js +1 -1
- package/runtime/storage/quiet/quiet-artifact-writer.d.ts +15 -15
- package/runtime/storage/quiet/quiet-artifact-writer.js +56 -56
- package/runtime/storage/repositories/credential-repository.js +30 -30
- package/runtime/storage/rhythm/rhythm-policy-snapshot.d.ts +10 -10
- package/runtime/storage/rhythm/rhythm-policy-snapshot.js +34 -34
- package/runtime/storage/services/credential-vault.d.ts +13 -13
- package/runtime/storage/services/credential-vault.js +116 -116
- package/runtime/storage/snapshots/continuity-snapshot.d.ts +9 -9
- package/runtime/storage/snapshots/continuity-snapshot.js +41 -41
- package/runtime/storage/snapshots/life-evidence-snapshot.d.ts +6 -6
- package/runtime/storage/snapshots/life-evidence-snapshot.js +114 -114
- package/runtime/storage/snapshots/types.d.ts +58 -58
- package/runtime/storage/snapshots/types.js +1 -1
- package/runtime/storage/state-api.js +104 -104
- package/runtime/storage/user-interest/load-user-interest-snapshot.d.ts +2 -2
- package/runtime/storage/user-interest/load-user-interest-snapshot.js +150 -150
- package/runtime/storage/user-interest/types.d.ts +25 -25
- package/runtime/storage/user-interest/types.js +1 -1
- package/workspace-ops-bridge.js +81 -80
|
@@ -1,114 +1,114 @@
|
|
|
1
|
-
import { EffectDispatcher, buildDecisionContext } from "../orchestrator/effect-dispatcher.js";
|
|
2
|
-
/**
|
|
3
|
-
* Map an intent kind to its guidance scene type.
|
|
4
|
-
* Only generative scenes (social, outreach, explain) request guidance.
|
|
5
|
-
* Maintenance, reflection, and work do not request guidance.
|
|
6
|
-
*/
|
|
7
|
-
function intentKindToScene(kind) {
|
|
8
|
-
switch (kind) {
|
|
9
|
-
case "social":
|
|
10
|
-
return "social";
|
|
11
|
-
case "outreach":
|
|
12
|
-
return "outreach";
|
|
13
|
-
case "exploration":
|
|
14
|
-
return "explain";
|
|
15
|
-
case "quiet":
|
|
16
|
-
return null;
|
|
17
|
-
default:
|
|
18
|
-
return null;
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
/**
|
|
22
|
-
* Build a scene context from an allowed intent and runtime mode.
|
|
23
|
-
*
|
|
24
|
-
* Mode comes from the actual runtime context (active/quiet/maintenance_only/paused_for_interrupt),
|
|
25
|
-
* not hardcoded.
|
|
26
|
-
*/
|
|
27
|
-
function buildSceneContext(intent, mode) {
|
|
28
|
-
return {
|
|
29
|
-
sceneType: intentKindToScene(intent.kind) ?? "explain",
|
|
30
|
-
mode,
|
|
31
|
-
sceneSummary: intent.summary,
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* Request guidance for a selected intent.
|
|
36
|
-
*
|
|
37
|
-
* Guidance is only requested when:
|
|
38
|
-
* - The intent kind maps to a generative scene (social, outreach, explain)
|
|
39
|
-
* - Maintenance, reflection, and work do not request guidance
|
|
40
|
-
*
|
|
41
|
-
* The guidance payload is used for context assembly within control-plane.
|
|
42
|
-
* It does NOT cross the connector execution boundary.
|
|
43
|
-
*/
|
|
44
|
-
export async function requestGuidanceForIntent(intent, mode, deps) {
|
|
45
|
-
const sceneType = intentKindToScene(intent.kind);
|
|
46
|
-
if (!sceneType) {
|
|
47
|
-
// Non-generative intents don't request guidance
|
|
48
|
-
return { intent };
|
|
49
|
-
}
|
|
50
|
-
const sceneContext = buildSceneContext(intent, mode);
|
|
51
|
-
const guidanceResult = await deps.requestGuidance({ sceneContext });
|
|
52
|
-
const appliedContext = deps.applyGuidance(guidanceResult.guidance);
|
|
53
|
-
return {
|
|
54
|
-
intent,
|
|
55
|
-
guidanceResult,
|
|
56
|
-
appliedContext,
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
/**
|
|
60
|
-
* Dispatch effects for an allowed intent.
|
|
61
|
-
*
|
|
62
|
-
* This function enforces the allow-only boundary:
|
|
63
|
-
* - Only called when guard verdict is "allow"
|
|
64
|
-
* - Creates a decision context and dispatches through EffectDispatcher
|
|
65
|
-
* - Guidance context stays within control-plane and is NOT passed to connector executor
|
|
66
|
-
*
|
|
67
|
-
* The connector executor receives the original intent payload without any
|
|
68
|
-
* guidance-derived fields. Guidance participates in control-plane context
|
|
69
|
-
* assembly but does not leak into the connector execution boundary.
|
|
70
|
-
*/
|
|
71
|
-
export async function dispatchAllowedEffect(intent, deps) {
|
|
72
|
-
const dispatcher = new EffectDispatcher(deps.leaseManager, deps.commitPort, deps.connectorExecutor, deps.checkpointPort, deps.memoryPort, deps.reflectionPort);
|
|
73
|
-
const decisionContext = buildDecisionContext({
|
|
74
|
-
tickId: `tick:${Date.now()}`,
|
|
75
|
-
intentId: intent.id,
|
|
76
|
-
});
|
|
77
|
-
// Dispatch with the original intent payload.
|
|
78
|
-
// Guidance context (if any) remains within control-plane boundary
|
|
79
|
-
// and is NOT embedded in the connector payload.
|
|
80
|
-
return dispatcher.dispatchEffect(intent, decisionContext);
|
|
81
|
-
}
|
|
82
|
-
/**
|
|
83
|
-
* Full heartbeat execution: guidance bridge + allow-only effect dispatch.
|
|
84
|
-
*
|
|
85
|
-
* Flow:
|
|
86
|
-
* 1. Check guard verdict — non-allow paths skip guidance entirely
|
|
87
|
-
* 2. For allow verdict: request guidance (if generative scene), then dispatch effect
|
|
88
|
-
* 3. Return execution result with guidance and dispatch info
|
|
89
|
-
*
|
|
90
|
-
* Per T2.2.2 boundaries:
|
|
91
|
-
* - Guidance is NOT requested for deny/defer verdicts
|
|
92
|
-
* - Guidance payload does NOT cross into connector execution boundary
|
|
93
|
-
* - External effects only occur under allow verdict
|
|
94
|
-
*/
|
|
95
|
-
export async function executeHeartbeatCycle(intent, guardVerdict, mode, deps) {
|
|
96
|
-
// Non-allow verdicts: skip guidance entirely, no effect dispatch
|
|
97
|
-
if (guardVerdict !== "allow") {
|
|
98
|
-
return {
|
|
99
|
-
decisionId: `decision:${intent.id}:${Date.now()}`,
|
|
100
|
-
intentId: intent.id,
|
|
101
|
-
guardVerdict,
|
|
102
|
-
};
|
|
103
|
-
}
|
|
104
|
-
// Allow verdict: request guidance for generative scenes, then dispatch
|
|
105
|
-
const guidance = await requestGuidanceForIntent(intent, mode, deps.guidance);
|
|
106
|
-
const dispatch = await dispatchAllowedEffect(intent, deps.effects);
|
|
107
|
-
return {
|
|
108
|
-
decisionId: `decision:${intent.id}:${Date.now()}`,
|
|
109
|
-
intentId: intent.id,
|
|
110
|
-
guardVerdict,
|
|
111
|
-
guidance,
|
|
112
|
-
dispatch,
|
|
113
|
-
};
|
|
114
|
-
}
|
|
1
|
+
import { EffectDispatcher, buildDecisionContext } from "../orchestrator/effect-dispatcher.js";
|
|
2
|
+
/**
|
|
3
|
+
* Map an intent kind to its guidance scene type.
|
|
4
|
+
* Only generative scenes (social, outreach, explain) request guidance.
|
|
5
|
+
* Maintenance, reflection, and work do not request guidance.
|
|
6
|
+
*/
|
|
7
|
+
function intentKindToScene(kind) {
|
|
8
|
+
switch (kind) {
|
|
9
|
+
case "social":
|
|
10
|
+
return "social";
|
|
11
|
+
case "outreach":
|
|
12
|
+
return "outreach";
|
|
13
|
+
case "exploration":
|
|
14
|
+
return "explain";
|
|
15
|
+
case "quiet":
|
|
16
|
+
return null;
|
|
17
|
+
default:
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Build a scene context from an allowed intent and runtime mode.
|
|
23
|
+
*
|
|
24
|
+
* Mode comes from the actual runtime context (active/quiet/maintenance_only/paused_for_interrupt),
|
|
25
|
+
* not hardcoded.
|
|
26
|
+
*/
|
|
27
|
+
function buildSceneContext(intent, mode) {
|
|
28
|
+
return {
|
|
29
|
+
sceneType: intentKindToScene(intent.kind) ?? "explain",
|
|
30
|
+
mode,
|
|
31
|
+
sceneSummary: intent.summary,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Request guidance for a selected intent.
|
|
36
|
+
*
|
|
37
|
+
* Guidance is only requested when:
|
|
38
|
+
* - The intent kind maps to a generative scene (social, outreach, explain)
|
|
39
|
+
* - Maintenance, reflection, and work do not request guidance
|
|
40
|
+
*
|
|
41
|
+
* The guidance payload is used for context assembly within control-plane.
|
|
42
|
+
* It does NOT cross the connector execution boundary.
|
|
43
|
+
*/
|
|
44
|
+
export async function requestGuidanceForIntent(intent, mode, deps) {
|
|
45
|
+
const sceneType = intentKindToScene(intent.kind);
|
|
46
|
+
if (!sceneType) {
|
|
47
|
+
// Non-generative intents don't request guidance
|
|
48
|
+
return { intent };
|
|
49
|
+
}
|
|
50
|
+
const sceneContext = buildSceneContext(intent, mode);
|
|
51
|
+
const guidanceResult = await deps.requestGuidance({ sceneContext });
|
|
52
|
+
const appliedContext = deps.applyGuidance(guidanceResult.guidance);
|
|
53
|
+
return {
|
|
54
|
+
intent,
|
|
55
|
+
guidanceResult,
|
|
56
|
+
appliedContext,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Dispatch effects for an allowed intent.
|
|
61
|
+
*
|
|
62
|
+
* This function enforces the allow-only boundary:
|
|
63
|
+
* - Only called when guard verdict is "allow"
|
|
64
|
+
* - Creates a decision context and dispatches through EffectDispatcher
|
|
65
|
+
* - Guidance context stays within control-plane and is NOT passed to connector executor
|
|
66
|
+
*
|
|
67
|
+
* The connector executor receives the original intent payload without any
|
|
68
|
+
* guidance-derived fields. Guidance participates in control-plane context
|
|
69
|
+
* assembly but does not leak into the connector execution boundary.
|
|
70
|
+
*/
|
|
71
|
+
export async function dispatchAllowedEffect(intent, deps) {
|
|
72
|
+
const dispatcher = new EffectDispatcher(deps.leaseManager, deps.commitPort, deps.connectorExecutor, deps.checkpointPort, deps.memoryPort, deps.reflectionPort);
|
|
73
|
+
const decisionContext = buildDecisionContext({
|
|
74
|
+
tickId: `tick:${Date.now()}`,
|
|
75
|
+
intentId: intent.id,
|
|
76
|
+
});
|
|
77
|
+
// Dispatch with the original intent payload.
|
|
78
|
+
// Guidance context (if any) remains within control-plane boundary
|
|
79
|
+
// and is NOT embedded in the connector payload.
|
|
80
|
+
return dispatcher.dispatchEffect(intent, decisionContext);
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Full heartbeat execution: guidance bridge + allow-only effect dispatch.
|
|
84
|
+
*
|
|
85
|
+
* Flow:
|
|
86
|
+
* 1. Check guard verdict — non-allow paths skip guidance entirely
|
|
87
|
+
* 2. For allow verdict: request guidance (if generative scene), then dispatch effect
|
|
88
|
+
* 3. Return execution result with guidance and dispatch info
|
|
89
|
+
*
|
|
90
|
+
* Per T2.2.2 boundaries:
|
|
91
|
+
* - Guidance is NOT requested for deny/defer verdicts
|
|
92
|
+
* - Guidance payload does NOT cross into connector execution boundary
|
|
93
|
+
* - External effects only occur under allow verdict
|
|
94
|
+
*/
|
|
95
|
+
export async function executeHeartbeatCycle(intent, guardVerdict, mode, deps) {
|
|
96
|
+
// Non-allow verdicts: skip guidance entirely, no effect dispatch
|
|
97
|
+
if (guardVerdict !== "allow") {
|
|
98
|
+
return {
|
|
99
|
+
decisionId: `decision:${intent.id}:${Date.now()}`,
|
|
100
|
+
intentId: intent.id,
|
|
101
|
+
guardVerdict,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
// Allow verdict: request guidance for generative scenes, then dispatch
|
|
105
|
+
const guidance = await requestGuidanceForIntent(intent, mode, deps.guidance);
|
|
106
|
+
const dispatch = await dispatchAllowedEffect(intent, deps.effects);
|
|
107
|
+
return {
|
|
108
|
+
decisionId: `decision:${intent.id}:${Date.now()}`,
|
|
109
|
+
intentId: intent.id,
|
|
110
|
+
guardVerdict,
|
|
111
|
+
guidance,
|
|
112
|
+
dispatch,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
@@ -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,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" ? 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" ? 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,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";
|