@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
- * Maps control-plane judgment + delivery resolution into guidance OutreachDraftRequest (T6.2.1).
3
- * Aligns with control-plane-system.detail §3.9 buildOutreachDraftRequest.
4
- */
5
- import * as crypto from "node:crypto";
6
- function inferRhythmWindowKind(windowId) {
7
- const id = windowId.toLowerCase();
8
- if (id.includes("work"))
9
- return "work";
10
- if (id.includes("social"))
11
- return "social";
12
- if (id.includes("quiet"))
13
- return "quiet";
14
- if (id.includes("reflect"))
15
- return "reflection";
16
- if (id.includes("explore"))
17
- return "exploration";
18
- return undefined;
19
- }
20
- function toGuidanceRefs(refs) {
21
- return refs.map((r) => ({
22
- id: r.id,
23
- kind: r.kind,
24
- uri: r.uri,
25
- excerptHash: r.excerptHash,
26
- observedAt: r.observedAt,
27
- }));
28
- }
29
- function mapDeliveryVerdict(verdict) {
30
- switch (verdict) {
31
- case "target_available":
32
- return "target_available";
33
- case "target_none":
34
- return "target_none";
35
- case "channel_missing":
36
- return "channel_missing";
37
- case "host_unsupported":
38
- return "host_unsupported";
39
- default:
40
- return "host_unsupported";
41
- }
42
- }
43
- export function buildOutreachDraftRequest(candidate, judgment, snapshot, delivery) {
44
- const sceneType = delivery.verdict === "target_available" ? "outreach" : "fallback_candidate";
45
- const riskLevel = delivery.verdict === "target_available" ? "medium" : "low";
46
- return {
47
- requestId: `outreach_draft_request:${crypto.randomUUID()}`,
48
- sceneType,
49
- runtimeScope: "rhythm",
50
- rhythmWindowKind: inferRhythmWindowKind(snapshot.rhythmWindow.windowId),
51
- riskLevel,
52
- sourceRefs: toGuidanceRefs(judgment.sourceRefs),
53
- decisionId: judgment.decisionId,
54
- candidateId: candidate.id,
55
- judgmentVerdict: judgment.verdict,
56
- valueScore: judgment.valueScore,
57
- interestRefs: toGuidanceRefs(judgment.interestRefs),
58
- deliveryContext: {
59
- deliveryVerdict: mapDeliveryVerdict(delivery.verdict),
60
- wordingMode: delivery.verdict === "target_available" ? "sendable" : "not_sent_fallback_candidate",
61
- },
62
- };
63
- }
1
+ /**
2
+ * Maps control-plane judgment + delivery resolution into guidance OutreachDraftRequest (T6.2.1).
3
+ * Aligns with control-plane-system.detail §3.9 buildOutreachDraftRequest.
4
+ */
5
+ import * as crypto from "node:crypto";
6
+ function inferRhythmWindowKind(windowId) {
7
+ const id = windowId.toLowerCase();
8
+ if (id.includes("work"))
9
+ return "work";
10
+ if (id.includes("social"))
11
+ return "social";
12
+ if (id.includes("quiet"))
13
+ return "quiet";
14
+ if (id.includes("reflect"))
15
+ return "reflection";
16
+ if (id.includes("explore"))
17
+ return "exploration";
18
+ return undefined;
19
+ }
20
+ function toGuidanceRefs(refs) {
21
+ return refs.map((r) => ({
22
+ id: r.id,
23
+ kind: r.kind,
24
+ uri: r.uri,
25
+ excerptHash: r.excerptHash,
26
+ observedAt: r.observedAt,
27
+ }));
28
+ }
29
+ function mapDeliveryVerdict(verdict) {
30
+ switch (verdict) {
31
+ case "target_available":
32
+ return "target_available";
33
+ case "target_none":
34
+ return "target_none";
35
+ case "channel_missing":
36
+ return "channel_missing";
37
+ case "host_unsupported":
38
+ return "host_unsupported";
39
+ default:
40
+ return "host_unsupported";
41
+ }
42
+ }
43
+ export function buildOutreachDraftRequest(candidate, judgment, snapshot, delivery) {
44
+ const sceneType = delivery.verdict === "target_available" ? "outreach" : "fallback_candidate";
45
+ const riskLevel = delivery.verdict === "target_available" ? "medium" : "low";
46
+ return {
47
+ requestId: `outreach_draft_request:${crypto.randomUUID()}`,
48
+ sceneType,
49
+ runtimeScope: "rhythm",
50
+ rhythmWindowKind: inferRhythmWindowKind(snapshot.rhythmWindow.windowId),
51
+ riskLevel,
52
+ sourceRefs: toGuidanceRefs(judgment.sourceRefs),
53
+ decisionId: judgment.decisionId,
54
+ candidateId: candidate.id,
55
+ judgmentVerdict: judgment.verdict,
56
+ valueScore: judgment.valueScore,
57
+ interestRefs: toGuidanceRefs(judgment.interestRefs),
58
+ deliveryContext: {
59
+ deliveryVerdict: mapDeliveryVerdict(delivery.verdict),
60
+ wordingMode: delivery.verdict === "target_available" ? "sendable" : "not_sent_fallback_candidate",
61
+ },
62
+ };
63
+ }
@@ -1,26 +1,26 @@
1
- /**
2
- * Resolves OpenClaw-visible delivery target from host capability snapshot (T2.3.1 / ADR-007).
3
- *
4
- * Core logic: explicit/last targets require channel materialization; `none` is a first-class verdict.
5
- * Test coverage: tests/unit/core/outreach-judgment.test.ts
6
- */
7
- export type DeliveryHostTarget = "none" | "last" | "explicit";
8
- export interface DeliveryCapabilitySnapshot {
9
- /** Raw host value; may be empty string before normalization. */
10
- target?: DeliveryHostTarget | string | null;
11
- channel?: string | null;
12
- recipient?: string | null;
13
- lastKnownVisibleChannel?: string | null;
14
- /** When true, host reports unsupported delivery surface (maps to host_unsupported verdict). */
15
- hostUnsupported?: boolean;
16
- }
17
- export type DeliveryTargetVerdict = "target_none" | "channel_missing" | "target_available" | "host_unsupported";
18
- export interface DeliveryTargetResolution {
19
- verdict: DeliveryTargetVerdict;
20
- target?: DeliveryHostTarget;
21
- channel?: string;
22
- recipient?: string;
23
- reason: string;
24
- }
25
- export declare function resolveDeliveryTarget(snapshot: DeliveryCapabilitySnapshot): DeliveryTargetResolution;
26
- export declare function isDeliveryUnavailableReason(reason: string): boolean;
1
+ /**
2
+ * Resolves OpenClaw-visible delivery target from host capability snapshot (T2.3.1 / ADR-007).
3
+ *
4
+ * Core logic: explicit/last targets require channel materialization; `none` is a first-class verdict.
5
+ * Test coverage: tests/unit/core/outreach-judgment.test.ts
6
+ */
7
+ export type DeliveryHostTarget = "none" | "last" | "explicit";
8
+ export interface DeliveryCapabilitySnapshot {
9
+ /** Raw host value; may be empty string before normalization. */
10
+ target?: DeliveryHostTarget | string | null;
11
+ channel?: string | null;
12
+ recipient?: string | null;
13
+ lastKnownVisibleChannel?: string | null;
14
+ /** When true, host reports unsupported delivery surface (maps to host_unsupported verdict). */
15
+ hostUnsupported?: boolean;
16
+ }
17
+ export type DeliveryTargetVerdict = "target_none" | "channel_missing" | "target_available" | "host_unsupported";
18
+ export interface DeliveryTargetResolution {
19
+ verdict: DeliveryTargetVerdict;
20
+ target?: DeliveryHostTarget;
21
+ channel?: string;
22
+ recipient?: string;
23
+ reason: string;
24
+ }
25
+ export declare function resolveDeliveryTarget(snapshot: DeliveryCapabilitySnapshot): DeliveryTargetResolution;
26
+ export declare function isDeliveryUnavailableReason(reason: string): boolean;
@@ -1,70 +1,70 @@
1
- /**
2
- * Resolves OpenClaw-visible delivery target from host capability snapshot (T2.3.1 / ADR-007).
3
- *
4
- * Core logic: explicit/last targets require channel materialization; `none` is a first-class verdict.
5
- * Test coverage: tests/unit/core/outreach-judgment.test.ts
6
- */
7
- export function resolveDeliveryTarget(snapshot) {
8
- if (snapshot.hostUnsupported) {
9
- return {
10
- verdict: "host_unsupported",
11
- target: snapshot.target && String(snapshot.target).trim() !== "" && snapshot.target !== "none"
12
- ? snapshot.target
13
- : "none",
14
- reason: "host_delivery_surface_unsupported",
15
- };
16
- }
17
- const raw = snapshot.target;
18
- if (raw === undefined || raw === null || String(raw).trim() === "" || raw === "none") {
19
- return {
20
- verdict: "target_none",
21
- target: "none",
22
- reason: "heartbeat_run_without_user_visible_delivery",
23
- };
24
- }
25
- if (raw === "explicit") {
26
- const ch = (snapshot.channel ?? "").trim();
27
- const rec = (snapshot.recipient ?? "").trim();
28
- if (!ch || !rec) {
29
- return {
30
- verdict: "channel_missing",
31
- target: "explicit",
32
- reason: "explicit_delivery_requires_channel_and_recipient",
33
- };
34
- }
35
- return {
36
- verdict: "target_available",
37
- target: "explicit",
38
- channel: ch,
39
- recipient: rec,
40
- reason: "delivery_target_available",
41
- };
42
- }
43
- if (raw === "last") {
44
- const lastCh = (snapshot.lastKnownVisibleChannel ?? "").trim();
45
- if (!lastCh) {
46
- return {
47
- verdict: "channel_missing",
48
- target: "last",
49
- reason: "last_target_has_no_known_visible_channel",
50
- };
51
- }
52
- return {
53
- verdict: "target_available",
54
- target: "last",
55
- channel: lastCh,
56
- recipient: snapshot.recipient?.trim() || undefined,
57
- reason: "delivery_target_available",
58
- };
59
- }
60
- return {
61
- verdict: "target_available",
62
- target: raw,
63
- channel: snapshot.channel?.trim() || snapshot.lastKnownVisibleChannel?.trim() || undefined,
64
- recipient: snapshot.recipient?.trim() || undefined,
65
- reason: "delivery_target_available",
66
- };
67
- }
68
- export function isDeliveryUnavailableReason(reason) {
69
- return reason === "target_none" || reason === "channel_missing" || reason === "host_unsupported";
70
- }
1
+ /**
2
+ * Resolves OpenClaw-visible delivery target from host capability snapshot (T2.3.1 / ADR-007).
3
+ *
4
+ * Core logic: explicit/last targets require channel materialization; `none` is a first-class verdict.
5
+ * Test coverage: tests/unit/core/outreach-judgment.test.ts
6
+ */
7
+ export function resolveDeliveryTarget(snapshot) {
8
+ if (snapshot.hostUnsupported) {
9
+ return {
10
+ verdict: "host_unsupported",
11
+ target: snapshot.target && String(snapshot.target).trim() !== "" && snapshot.target !== "none"
12
+ ? snapshot.target
13
+ : "none",
14
+ reason: "host_delivery_surface_unsupported",
15
+ };
16
+ }
17
+ const raw = snapshot.target;
18
+ if (raw === undefined || raw === null || String(raw).trim() === "" || raw === "none") {
19
+ return {
20
+ verdict: "target_none",
21
+ target: "none",
22
+ reason: "heartbeat_run_without_user_visible_delivery",
23
+ };
24
+ }
25
+ if (raw === "explicit") {
26
+ const ch = (snapshot.channel ?? "").trim();
27
+ const rec = (snapshot.recipient ?? "").trim();
28
+ if (!ch || !rec) {
29
+ return {
30
+ verdict: "channel_missing",
31
+ target: "explicit",
32
+ reason: "explicit_delivery_requires_channel_and_recipient",
33
+ };
34
+ }
35
+ return {
36
+ verdict: "target_available",
37
+ target: "explicit",
38
+ channel: ch,
39
+ recipient: rec,
40
+ reason: "delivery_target_available",
41
+ };
42
+ }
43
+ if (raw === "last") {
44
+ const lastCh = (snapshot.lastKnownVisibleChannel ?? "").trim();
45
+ if (!lastCh) {
46
+ return {
47
+ verdict: "channel_missing",
48
+ target: "last",
49
+ reason: "last_target_has_no_known_visible_channel",
50
+ };
51
+ }
52
+ return {
53
+ verdict: "target_available",
54
+ target: "last",
55
+ channel: lastCh,
56
+ recipient: snapshot.recipient?.trim() || undefined,
57
+ reason: "delivery_target_available",
58
+ };
59
+ }
60
+ return {
61
+ verdict: "target_available",
62
+ target: raw,
63
+ channel: snapshot.channel?.trim() || snapshot.lastKnownVisibleChannel?.trim() || undefined,
64
+ recipient: snapshot.recipient?.trim() || undefined,
65
+ reason: "delivery_target_available",
66
+ };
67
+ }
68
+ export function isDeliveryUnavailableReason(reason) {
69
+ return reason === "target_none" || reason === "channel_missing" || reason === "host_unsupported";
70
+ }
@@ -1,38 +1,38 @@
1
- /**
2
- * User outreach dispatch path: judgment → draft → host delivery → attempt + operator fallback (T2.3.2).
3
- * Mirrors control-plane-system.detail §3.9 dispatchAllowedIntent user_outreach branch.
4
- */
5
- import type { GuidanceDraftPort } from "../../../guidance/outreach-draft-schema.js";
6
- import type { CandidateIntent } from "../types.js";
7
- import type { HeartbeatRuntimeSnapshot } from "../heartbeat/runtime-snapshot.js";
8
- import type { HeartbeatCycleResult } from "../heartbeat/signal.js";
9
- import type { StateDatabase } from "../../../storage/db/index.js";
10
- import type { SourceRef } from "../../../storage/life-evidence/types.js";
11
- import { type JudgeOutreachInput } from "./judge-outreach.js";
12
- import { type DeliveryTargetResolution } from "./delivery-target.js";
13
- export interface OpenClawDeliverySendResult {
14
- id: string;
15
- status: "sent" | "failed" | "dropped_by_host_policy";
16
- errorClass?: string;
17
- messageId?: string;
18
- /** Host-reported delivery proof when messageId is absent (T4.3.1). */
19
- hostProofRef?: SourceRef;
20
- }
21
- export interface OpenClawDeliveryPort {
22
- sendDeliveryRequest(input: {
23
- decisionId: string;
24
- target: NonNullable<DeliveryTargetResolution["target"]>;
25
- channel: string;
26
- recipient?: string;
27
- message: string;
28
- sourceRefs: CandidateIntent["sourceRefs"];
29
- }): Promise<OpenClawDeliverySendResult>;
30
- }
31
- export declare function dispatchUserOutreachIntent(input: {
32
- candidate: CandidateIntent;
33
- snapshot: HeartbeatRuntimeSnapshot;
34
- judgeInput: Omit<JudgeOutreachInput, "candidate">;
35
- guidance: GuidanceDraftPort;
36
- delivery: OpenClawDeliveryPort;
37
- state: StateDatabase;
38
- }): Promise<HeartbeatCycleResult>;
1
+ /**
2
+ * User outreach dispatch path: judgment → draft → host delivery → attempt + operator fallback (T2.3.2).
3
+ * Mirrors control-plane-system.detail §3.9 dispatchAllowedIntent user_outreach branch.
4
+ */
5
+ import type { GuidanceDraftPort } from "../../../guidance/outreach-draft-schema.js";
6
+ import type { CandidateIntent } from "../types.js";
7
+ import type { HeartbeatRuntimeSnapshot } from "../heartbeat/runtime-snapshot.js";
8
+ import type { HeartbeatCycleResult } from "../heartbeat/signal.js";
9
+ import type { StateDatabase } from "../../../storage/db/index.js";
10
+ import type { SourceRef } from "../../../storage/life-evidence/types.js";
11
+ import { type JudgeOutreachInput } from "./judge-outreach.js";
12
+ import { type DeliveryTargetResolution } from "./delivery-target.js";
13
+ export interface OpenClawDeliverySendResult {
14
+ id: string;
15
+ status: "sent" | "failed" | "dropped_by_host_policy";
16
+ errorClass?: string;
17
+ messageId?: string;
18
+ /** Host-reported delivery proof when messageId is absent (T4.3.1). */
19
+ hostProofRef?: SourceRef;
20
+ }
21
+ export interface OpenClawDeliveryPort {
22
+ sendDeliveryRequest(input: {
23
+ decisionId: string;
24
+ target: NonNullable<DeliveryTargetResolution["target"]>;
25
+ channel: string;
26
+ recipient?: string;
27
+ message: string;
28
+ sourceRefs: CandidateIntent["sourceRefs"];
29
+ }): Promise<OpenClawDeliverySendResult>;
30
+ }
31
+ export declare function dispatchUserOutreachIntent(input: {
32
+ candidate: CandidateIntent;
33
+ snapshot: HeartbeatRuntimeSnapshot;
34
+ judgeInput: Omit<JudgeOutreachInput, "candidate">;
35
+ guidance: GuidanceDraftPort;
36
+ delivery: OpenClawDeliveryPort;
37
+ state: StateDatabase;
38
+ }): Promise<HeartbeatCycleResult>;
@@ -1,119 +1,119 @@
1
- import { writeDeliveryAttempt } from "../../../storage/delivery/write-delivery-attempt.js";
2
- import { writeOperatorFallback } from "../../../storage/fallback/write-operator-fallback.js";
3
- import { judgeOutreach } from "./judge-outreach.js";
4
- import { resolveDeliveryTarget } from "./delivery-target.js";
5
- import { buildOutreachDraftRequest } from "./build-outreach-draft-request.js";
6
- function toSourceRefs(refs) {
7
- return refs.map((r) => ({ ...r }));
8
- }
9
- function hasDeliveryProof(attempt) {
10
- return Boolean(attempt.messageId?.trim()) || Boolean(attempt.hostProofRef);
11
- }
12
- function operatorReasonForUnavailable(verdict) {
13
- if (verdict === "target_none")
14
- return "target_none";
15
- if (verdict === "channel_missing")
16
- return "channel_missing";
17
- return "host_unsupported";
18
- }
19
- export async function dispatchUserOutreachIntent(input) {
20
- const { candidate, snapshot, judgeInput, guidance, delivery, state } = input;
21
- const judgment = judgeOutreach({ ...judgeInput, candidate });
22
- if (judgment.verdict !== "allow") {
23
- return {
24
- scope: "rhythm",
25
- status: judgment.verdict === "defer" ? "deferred" : "denied",
26
- selectedIntentId: candidate.id,
27
- reasons: judgment.reasons,
28
- decisionId: judgment.decisionId,
29
- };
30
- }
31
- const deliveryResolution = resolveDeliveryTarget(judgeInput.delivery);
32
- if (deliveryResolution.verdict !== "target_available") {
33
- const req = buildOutreachDraftRequest(candidate, judgment, snapshot, deliveryResolution);
34
- const draft = await guidance.draftOutreachMessage(req);
35
- const fb = await writeOperatorFallback(state, {
36
- reason: operatorReasonForUnavailable(deliveryResolution.verdict),
37
- decisionId: judgment.decisionId,
38
- sourceRefs: toSourceRefs(judgment.sourceRefs),
39
- candidateMessage: draft.status === "ready" ? draft.draft.text : undefined,
40
- nextStep: "resolve_delivery_target_or_retry_after_host_update",
41
- });
42
- return {
43
- scope: "rhythm",
44
- status: "delivery_unavailable",
45
- selectedIntentId: candidate.id,
46
- reasons: [deliveryResolution.reason],
47
- decisionId: judgment.decisionId,
48
- fallbackRef: fb.fallbackRef,
49
- };
50
- }
51
- const req = buildOutreachDraftRequest(candidate, judgment, snapshot, deliveryResolution);
52
- const draft = await guidance.draftOutreachMessage(req);
53
- if (draft.status !== "ready") {
54
- return {
55
- scope: "rhythm",
56
- status: "denied",
57
- selectedIntentId: candidate.id,
58
- reasons: draft.reasons,
59
- decisionId: judgment.decisionId,
60
- };
61
- }
62
- const attempt = await delivery.sendDeliveryRequest({
63
- decisionId: judgment.decisionId,
64
- target: deliveryResolution.target,
65
- channel: deliveryResolution.channel,
66
- recipient: deliveryResolution.recipient,
67
- message: draft.draft.text,
68
- sourceRefs: judgment.sourceRefs,
69
- });
70
- if (attempt.status !== "sent" || !hasDeliveryProof(attempt)) {
71
- const fb = await writeOperatorFallback(state, {
72
- reason: "delivery_failed",
73
- decisionId: judgment.decisionId,
74
- sourceRefs: toSourceRefs(judgment.sourceRefs),
75
- candidateMessage: draft.draft.text,
76
- nextStep: "review_delivery_audit_and_host_capability",
77
- });
78
- const hostReportedSentWithoutProof = attempt.status === "sent" && !hasDeliveryProof(attempt);
79
- await writeDeliveryAttempt(state, {
80
- attemptId: attempt.id,
81
- decisionId: judgment.decisionId,
82
- target: deliveryResolution.target,
83
- channel: deliveryResolution.channel,
84
- status: attempt.status === "dropped_by_host_policy" ? "dropped_by_host_policy" : "failed",
85
- errorClass: hostReportedSentWithoutProof
86
- ? "delivery_proof_missing"
87
- : attempt.errorClass ?? attempt.status,
88
- fallbackRef: fb.fallbackRef,
89
- });
90
- return {
91
- scope: "rhythm",
92
- status: "delivery_unavailable",
93
- selectedIntentId: candidate.id,
94
- reasons: hostReportedSentWithoutProof
95
- ? ["delivery_failed", "delivery_proof_missing"]
96
- : ["delivery_failed", attempt.status],
97
- decisionId: judgment.decisionId,
98
- deliveryAttemptId: attempt.id,
99
- fallbackRef: fb.fallbackRef,
100
- };
101
- }
102
- await writeDeliveryAttempt(state, {
103
- attemptId: attempt.id,
104
- decisionId: judgment.decisionId,
105
- target: deliveryResolution.target,
106
- channel: deliveryResolution.channel,
107
- status: "sent",
108
- messageId: attempt.messageId?.trim(),
109
- hostProofRef: attempt.hostProofRef,
110
- });
111
- return {
112
- scope: "rhythm",
113
- status: "intent_selected",
114
- selectedIntentId: candidate.id,
115
- reasons: ["outreach_sent"],
116
- decisionId: judgment.decisionId,
117
- deliveryAttemptId: attempt.id,
118
- };
119
- }
1
+ import { writeDeliveryAttempt } from "../../../storage/delivery/write-delivery-attempt.js";
2
+ import { writeOperatorFallback } from "../../../storage/fallback/write-operator-fallback.js";
3
+ import { judgeOutreach } from "./judge-outreach.js";
4
+ import { resolveDeliveryTarget } from "./delivery-target.js";
5
+ import { buildOutreachDraftRequest } from "./build-outreach-draft-request.js";
6
+ function toSourceRefs(refs) {
7
+ return refs.map((r) => ({ ...r }));
8
+ }
9
+ function hasDeliveryProof(attempt) {
10
+ return Boolean(attempt.messageId?.trim()) || Boolean(attempt.hostProofRef);
11
+ }
12
+ function operatorReasonForUnavailable(verdict) {
13
+ if (verdict === "target_none")
14
+ return "target_none";
15
+ if (verdict === "channel_missing")
16
+ return "channel_missing";
17
+ return "host_unsupported";
18
+ }
19
+ export async function dispatchUserOutreachIntent(input) {
20
+ const { candidate, snapshot, judgeInput, guidance, delivery, state } = input;
21
+ const judgment = judgeOutreach({ ...judgeInput, candidate });
22
+ if (judgment.verdict !== "allow") {
23
+ return {
24
+ scope: "rhythm",
25
+ status: judgment.verdict === "defer" ? "deferred" : "denied",
26
+ selectedIntentId: candidate.id,
27
+ reasons: judgment.reasons,
28
+ decisionId: judgment.decisionId,
29
+ };
30
+ }
31
+ const deliveryResolution = resolveDeliveryTarget(judgeInput.delivery);
32
+ if (deliveryResolution.verdict !== "target_available") {
33
+ const req = buildOutreachDraftRequest(candidate, judgment, snapshot, deliveryResolution);
34
+ const draft = await guidance.draftOutreachMessage(req);
35
+ const fb = await writeOperatorFallback(state, {
36
+ reason: operatorReasonForUnavailable(deliveryResolution.verdict),
37
+ decisionId: judgment.decisionId,
38
+ sourceRefs: toSourceRefs(judgment.sourceRefs),
39
+ candidateMessage: draft.status === "ready" ? draft.draft.text : undefined,
40
+ nextStep: "resolve_delivery_target_or_retry_after_host_update",
41
+ });
42
+ return {
43
+ scope: "rhythm",
44
+ status: "delivery_unavailable",
45
+ selectedIntentId: candidate.id,
46
+ reasons: [deliveryResolution.reason],
47
+ decisionId: judgment.decisionId,
48
+ fallbackRef: fb.fallbackRef,
49
+ };
50
+ }
51
+ const req = buildOutreachDraftRequest(candidate, judgment, snapshot, deliveryResolution);
52
+ const draft = await guidance.draftOutreachMessage(req);
53
+ if (draft.status !== "ready") {
54
+ return {
55
+ scope: "rhythm",
56
+ status: "denied",
57
+ selectedIntentId: candidate.id,
58
+ reasons: draft.reasons,
59
+ decisionId: judgment.decisionId,
60
+ };
61
+ }
62
+ const attempt = await delivery.sendDeliveryRequest({
63
+ decisionId: judgment.decisionId,
64
+ target: deliveryResolution.target,
65
+ channel: deliveryResolution.channel,
66
+ recipient: deliveryResolution.recipient,
67
+ message: draft.draft.text,
68
+ sourceRefs: judgment.sourceRefs,
69
+ });
70
+ if (attempt.status !== "sent" || !hasDeliveryProof(attempt)) {
71
+ const fb = await writeOperatorFallback(state, {
72
+ reason: "delivery_failed",
73
+ decisionId: judgment.decisionId,
74
+ sourceRefs: toSourceRefs(judgment.sourceRefs),
75
+ candidateMessage: draft.draft.text,
76
+ nextStep: "review_delivery_audit_and_host_capability",
77
+ });
78
+ const hostReportedSentWithoutProof = attempt.status === "sent" && !hasDeliveryProof(attempt);
79
+ await writeDeliveryAttempt(state, {
80
+ attemptId: attempt.id,
81
+ decisionId: judgment.decisionId,
82
+ target: deliveryResolution.target,
83
+ channel: deliveryResolution.channel,
84
+ status: attempt.status === "dropped_by_host_policy" ? "dropped_by_host_policy" : "failed",
85
+ errorClass: hostReportedSentWithoutProof
86
+ ? "delivery_proof_missing"
87
+ : attempt.errorClass ?? attempt.status,
88
+ fallbackRef: fb.fallbackRef,
89
+ });
90
+ return {
91
+ scope: "rhythm",
92
+ status: "delivery_unavailable",
93
+ selectedIntentId: candidate.id,
94
+ reasons: hostReportedSentWithoutProof
95
+ ? ["delivery_failed", "delivery_proof_missing"]
96
+ : ["delivery_failed", attempt.status],
97
+ decisionId: judgment.decisionId,
98
+ deliveryAttemptId: attempt.id,
99
+ fallbackRef: fb.fallbackRef,
100
+ };
101
+ }
102
+ await writeDeliveryAttempt(state, {
103
+ attemptId: attempt.id,
104
+ decisionId: judgment.decisionId,
105
+ target: deliveryResolution.target,
106
+ channel: deliveryResolution.channel,
107
+ status: "sent",
108
+ messageId: attempt.messageId?.trim(),
109
+ hostProofRef: attempt.hostProofRef,
110
+ });
111
+ return {
112
+ scope: "rhythm",
113
+ status: "intent_selected",
114
+ selectedIntentId: candidate.id,
115
+ reasons: ["outreach_sent"],
116
+ decisionId: judgment.decisionId,
117
+ deliveryAttemptId: attempt.id,
118
+ };
119
+ }
@@ -1,7 +1,7 @@
1
- import type { SnapshotInputs } from "../heartbeat/snapshot-builder.js";
2
- import type { HeartbeatRuntimeSnapshot } from "../heartbeat/runtime-snapshot.js";
3
- import type { CandidateIntent } from "../types.js";
4
- import type { JudgeOutreachInput, JudgeOutreachUserInterest } from "./judge-outreach.js";
5
- import type { UserInterestSnapshot } from "../../../storage/user-interest/types.js";
6
- export declare function userInterestSnapshotToJudge(snapshot?: UserInterestSnapshot): JudgeOutreachUserInterest;
7
- export declare function buildJudgeOutreachInputFromSnapshot(intent: CandidateIntent, runtime: HeartbeatRuntimeSnapshot, inputs: SnapshotInputs): Omit<JudgeOutreachInput, "candidate">;
1
+ import type { SnapshotInputs } from "../heartbeat/snapshot-builder.js";
2
+ import type { HeartbeatRuntimeSnapshot } from "../heartbeat/runtime-snapshot.js";
3
+ import type { CandidateIntent } from "../types.js";
4
+ import type { JudgeOutreachInput, JudgeOutreachUserInterest } from "./judge-outreach.js";
5
+ import type { UserInterestSnapshot } from "../../../storage/user-interest/types.js";
6
+ export declare function userInterestSnapshotToJudge(snapshot?: UserInterestSnapshot): JudgeOutreachUserInterest;
7
+ export declare function buildJudgeOutreachInputFromSnapshot(intent: CandidateIntent, runtime: HeartbeatRuntimeSnapshot, inputs: SnapshotInputs): Omit<JudgeOutreachInput, "candidate">;