@haaaiawd/second-nature 0.1.8 → 0.1.9

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 (183) hide show
  1. package/index.js +73 -1
  2. package/openclaw.plugin.json +1 -1
  3. package/package.json +1 -1
  4. package/runtime/cli/commands/index.d.ts +2 -0
  5. package/runtime/cli/commands/index.js +61 -6
  6. package/runtime/cli/explain/explain-surface-subject.d.ts +8 -0
  7. package/runtime/cli/explain/explain-surface-subject.js +9 -0
  8. package/runtime/cli/explain/format-explanation.d.ts +2 -0
  9. package/runtime/cli/explain/format-explanation.js +2 -0
  10. package/runtime/cli/explain/resolve-subject.js +15 -0
  11. package/runtime/cli/host-capability/classify-delivery.d.ts +14 -0
  12. package/runtime/cli/host-capability/classify-delivery.js +20 -0
  13. package/runtime/cli/host-capability/probe-host-capability.d.ts +2 -0
  14. package/runtime/cli/host-capability/probe-host-capability.js +58 -0
  15. package/runtime/cli/host-capability/record-host-capability.d.ts +6 -0
  16. package/runtime/cli/host-capability/record-host-capability.js +14 -0
  17. package/runtime/cli/host-capability/types.d.ts +71 -0
  18. package/runtime/cli/host-capability/types.js +6 -0
  19. package/runtime/cli/host-smoke/run-host-smoke.d.ts +2 -0
  20. package/runtime/cli/host-smoke/run-host-smoke.js +40 -0
  21. package/runtime/cli/host-smoke/types.d.ts +35 -0
  22. package/runtime/cli/host-smoke/types.js +6 -0
  23. package/runtime/cli/index.js +18 -0
  24. package/runtime/cli/ops/heartbeat-surface.d.ts +35 -0
  25. package/runtime/cli/ops/heartbeat-surface.js +71 -0
  26. package/runtime/cli/ops/ops-router.d.ts +16 -0
  27. package/runtime/cli/ops/ops-router.js +83 -0
  28. package/runtime/cli/ops/show-operator-fallback.d.ts +13 -0
  29. package/runtime/cli/ops/show-operator-fallback.js +22 -0
  30. package/runtime/cli/ops/workspace-heartbeat-runner.d.ts +10 -0
  31. package/runtime/cli/ops/workspace-heartbeat-runner.js +26 -0
  32. package/runtime/cli/read-models/index.d.ts +11 -2
  33. package/runtime/cli/read-models/index.js +50 -0
  34. package/runtime/cli/read-models/operator-explain-map.d.ts +6 -0
  35. package/runtime/cli/read-models/operator-explain-map.js +10 -0
  36. package/runtime/cli/read-models/types.d.ts +5 -1
  37. package/runtime/cli/runtime/runtime-artifact-boundary.d.ts +28 -0
  38. package/runtime/cli/runtime/runtime-artifact-boundary.js +94 -0
  39. package/runtime/connectors/base/contract.d.ts +6 -0
  40. package/runtime/connectors/base/execution-policy.d.ts +47 -0
  41. package/runtime/connectors/base/execution-policy.js +82 -0
  42. package/runtime/connectors/base/index.d.ts +2 -0
  43. package/runtime/connectors/base/index.js +2 -0
  44. package/runtime/connectors/base/manifest.d.ts +55 -2
  45. package/runtime/connectors/base/manifest.js +50 -0
  46. package/runtime/connectors/base/map-life-evidence.d.ts +16 -0
  47. package/runtime/connectors/base/map-life-evidence.js +79 -0
  48. package/runtime/connectors/base/policy-layer.d.ts +2 -0
  49. package/runtime/connectors/base/policy-layer.js +16 -0
  50. package/runtime/connectors/base/route-planner.js +1 -0
  51. package/runtime/connectors/index.d.ts +1 -0
  52. package/runtime/connectors/index.js +1 -0
  53. package/runtime/connectors/near-real/near-real-connector-smoke.d.ts +19 -0
  54. package/runtime/connectors/near-real/near-real-connector-smoke.js +152 -0
  55. package/runtime/core/second-nature/heartbeat/heartbeat-executor.js +2 -0
  56. package/runtime/core/second-nature/heartbeat/heartbeat-loop.d.ts +37 -16
  57. package/runtime/core/second-nature/heartbeat/heartbeat-loop.js +95 -29
  58. package/runtime/core/second-nature/heartbeat/index.d.ts +4 -1
  59. package/runtime/core/second-nature/heartbeat/index.js +4 -1
  60. package/runtime/core/second-nature/heartbeat/run-heartbeat-cycle.d.ts +21 -0
  61. package/runtime/core/second-nature/heartbeat/run-heartbeat-cycle.js +35 -0
  62. package/runtime/core/second-nature/heartbeat/runtime-snapshot.d.ts +28 -0
  63. package/runtime/core/second-nature/heartbeat/runtime-snapshot.js +35 -0
  64. package/runtime/core/second-nature/heartbeat/signal.d.ts +9 -2
  65. package/runtime/core/second-nature/heartbeat/snapshot-builder.d.ts +19 -1
  66. package/runtime/core/second-nature/index.d.ts +8 -0
  67. package/runtime/core/second-nature/index.js +8 -0
  68. package/runtime/core/second-nature/orchestrator/effect-dispatcher.d.ts +1 -1
  69. package/runtime/core/second-nature/orchestrator/effect-dispatcher.js +9 -4
  70. package/runtime/core/second-nature/orchestrator/guard-layer.d.ts +6 -0
  71. package/runtime/core/second-nature/orchestrator/guard-layer.js +76 -20
  72. package/runtime/core/second-nature/orchestrator/intent-planner.d.ts +10 -0
  73. package/runtime/core/second-nature/orchestrator/intent-planner.js +135 -28
  74. package/runtime/core/second-nature/orchestrator/lease-manager.d.ts +1 -1
  75. package/runtime/core/second-nature/orchestrator/lease-manager.js +1 -1
  76. package/runtime/core/second-nature/outreach/build-outreach-draft-request.d.ts +6 -0
  77. package/runtime/core/second-nature/outreach/build-outreach-draft-request.js +63 -0
  78. package/runtime/core/second-nature/outreach/delivery-target.d.ts +26 -0
  79. package/runtime/core/second-nature/outreach/delivery-target.js +70 -0
  80. package/runtime/core/second-nature/outreach/dispatch-user-outreach.d.ts +38 -0
  81. package/runtime/core/second-nature/outreach/dispatch-user-outreach.js +119 -0
  82. package/runtime/core/second-nature/outreach/judge-input-from-snapshot.d.ts +7 -0
  83. package/runtime/core/second-nature/outreach/judge-input-from-snapshot.js +45 -0
  84. package/runtime/core/second-nature/outreach/judge-outreach.d.ts +40 -0
  85. package/runtime/core/second-nature/outreach/judge-outreach.js +121 -0
  86. package/runtime/core/second-nature/quiet/run-source-backed-quiet.d.ts +21 -0
  87. package/runtime/core/second-nature/quiet/run-source-backed-quiet.js +123 -0
  88. package/runtime/core/second-nature/rhythm/planner-rhythm-window.d.ts +15 -0
  89. package/runtime/core/second-nature/rhythm/planner-rhythm-window.js +52 -0
  90. package/runtime/core/second-nature/rhythm/policy-bridge.d.ts +19 -0
  91. package/runtime/core/second-nature/rhythm/policy-bridge.js +34 -0
  92. package/runtime/core/second-nature/types.d.ts +16 -2
  93. package/runtime/guidance/draft-outreach-message.d.ts +7 -0
  94. package/runtime/guidance/draft-outreach-message.js +42 -0
  95. package/runtime/guidance/evidence-guidance.d.ts +40 -0
  96. package/runtime/guidance/evidence-guidance.js +52 -0
  97. package/runtime/guidance/index.d.ts +3 -0
  98. package/runtime/guidance/index.js +3 -0
  99. package/runtime/guidance/outreach-draft-schema.d.ts +228 -0
  100. package/runtime/guidance/outreach-draft-schema.js +80 -0
  101. package/runtime/observability/audit/append-only-audit-store.d.ts +14 -0
  102. package/runtime/observability/audit/append-only-audit-store.js +21 -0
  103. package/runtime/observability/audit/audit-envelope.d.ts +51 -0
  104. package/runtime/observability/audit/audit-envelope.js +130 -0
  105. package/runtime/observability/audit/verify-audit-hash-chain.d.ts +23 -0
  106. package/runtime/observability/audit/verify-audit-hash-chain.js +83 -0
  107. package/runtime/observability/db/index.js +11 -0
  108. package/runtime/observability/db/schema/host-capability-reports.d.ts +180 -0
  109. package/runtime/observability/db/schema/host-capability-reports.js +12 -0
  110. package/runtime/observability/db/schema/index.d.ts +1 -0
  111. package/runtime/observability/db/schema/index.js +1 -0
  112. package/runtime/observability/index.d.ts +7 -0
  113. package/runtime/observability/index.js +7 -0
  114. package/runtime/observability/query/explain-query.d.ts +48 -0
  115. package/runtime/observability/query/explain-query.js +114 -0
  116. package/runtime/observability/query/export-audit-bundle.d.ts +22 -0
  117. package/runtime/observability/query/export-audit-bundle.js +27 -0
  118. package/runtime/observability/services/decision-ledger.d.ts +1 -1
  119. package/runtime/observability/services/decision-ledger.js +4 -0
  120. package/runtime/observability/services/governance-audit.d.ts +14 -0
  121. package/runtime/observability/services/governance-audit.js +25 -1
  122. package/runtime/observability/services/governance-plane-recorder.d.ts +47 -0
  123. package/runtime/observability/services/governance-plane-recorder.js +55 -0
  124. package/runtime/observability/services/lived-experience-audit.d.ts +97 -0
  125. package/runtime/observability/services/lived-experience-audit.js +161 -0
  126. package/runtime/storage/bootstrap/native-sqlite-probe.d.ts +7 -0
  127. package/runtime/storage/bootstrap/native-sqlite-probe.js +28 -0
  128. package/runtime/storage/bootstrap/repair-gate.d.ts +17 -0
  129. package/runtime/storage/bootstrap/repair-gate.js +71 -0
  130. package/runtime/storage/bootstrap/storage-mode-smoke.d.ts +38 -0
  131. package/runtime/storage/bootstrap/storage-mode-smoke.js +85 -0
  132. package/runtime/storage/db/index.js +49 -0
  133. package/runtime/storage/db/schema/delivery-attempts.d.ts +199 -0
  134. package/runtime/storage/db/schema/delivery-attempts.js +13 -0
  135. package/runtime/storage/db/schema/index.d.ts +3 -0
  136. package/runtime/storage/db/schema/index.js +3 -0
  137. package/runtime/storage/db/schema/life-evidence-index.d.ts +161 -0
  138. package/runtime/storage/db/schema/life-evidence-index.js +11 -0
  139. package/runtime/storage/db/schema/operator-fallback-artifacts.d.ts +161 -0
  140. package/runtime/storage/db/schema/operator-fallback-artifacts.js +11 -0
  141. package/runtime/storage/db/schema/policies.d.ts +17 -0
  142. package/runtime/storage/db/schema/policies.js +1 -0
  143. package/runtime/storage/delivery/query-delivery-attempts.d.ts +3 -0
  144. package/runtime/storage/delivery/query-delivery-attempts.js +32 -0
  145. package/runtime/storage/delivery/types.d.ts +27 -0
  146. package/runtime/storage/delivery/types.js +1 -0
  147. package/runtime/storage/delivery/write-delivery-attempt.d.ts +6 -0
  148. package/runtime/storage/delivery/write-delivery-attempt.js +36 -0
  149. package/runtime/storage/fallback/load-operator-fallback.d.ts +14 -0
  150. package/runtime/storage/fallback/load-operator-fallback.js +47 -0
  151. package/runtime/storage/fallback/operator-fallback-types.d.ts +9 -0
  152. package/runtime/storage/fallback/operator-fallback-types.js +1 -0
  153. package/runtime/storage/fallback/operator-fallback-view.d.ts +11 -0
  154. package/runtime/storage/fallback/operator-fallback-view.js +1 -0
  155. package/runtime/storage/fallback/write-operator-fallback.d.ts +6 -0
  156. package/runtime/storage/fallback/write-operator-fallback.js +21 -0
  157. package/runtime/storage/index.d.ts +21 -0
  158. package/runtime/storage/index.js +14 -0
  159. package/runtime/storage/life-evidence/append-life-evidence.d.ts +7 -0
  160. package/runtime/storage/life-evidence/append-life-evidence.js +64 -0
  161. package/runtime/storage/life-evidence/types.d.ts +45 -0
  162. package/runtime/storage/life-evidence/types.js +6 -0
  163. package/runtime/storage/quiet/persist-quiet-artifact.d.ts +7 -0
  164. package/runtime/storage/quiet/persist-quiet-artifact.js +22 -0
  165. package/runtime/storage/quiet/quiet-artifact-types.d.ts +18 -0
  166. package/runtime/storage/quiet/quiet-artifact-types.js +1 -0
  167. package/runtime/storage/quiet/quiet-artifact-writer.d.ts +15 -0
  168. package/runtime/storage/quiet/quiet-artifact-writer.js +56 -0
  169. package/runtime/storage/rhythm/rhythm-policy-snapshot.d.ts +10 -0
  170. package/runtime/storage/rhythm/rhythm-policy-snapshot.js +34 -0
  171. package/runtime/storage/services/credential-vault.d.ts +5 -0
  172. package/runtime/storage/services/credential-vault.js +46 -9
  173. package/runtime/storage/snapshots/continuity-snapshot.d.ts +9 -0
  174. package/runtime/storage/snapshots/continuity-snapshot.js +41 -0
  175. package/runtime/storage/snapshots/life-evidence-snapshot.d.ts +6 -0
  176. package/runtime/storage/snapshots/life-evidence-snapshot.js +114 -0
  177. package/runtime/storage/snapshots/types.d.ts +58 -0
  178. package/runtime/storage/snapshots/types.js +1 -0
  179. package/runtime/storage/state-api.js +11 -4
  180. package/runtime/storage/user-interest/load-user-interest-snapshot.d.ts +2 -0
  181. package/runtime/storage/user-interest/load-user-interest-snapshot.js +150 -0
  182. package/runtime/storage/user-interest/types.d.ts +25 -0
  183. package/runtime/storage/user-interest/types.js +1 -0
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Side-effect idempotency + degraded-channel gate + effect commit replay (T3.2.1).
3
+ * Aligns with connector-system.detail §3.6 enforceExecutionPolicy.
4
+ */
5
+ import * as crypto from "node:crypto";
6
+ import { ConnectorPolicyError } from "./failure-taxonomy.js";
7
+ const READONLY = new Set([
8
+ "feed.read",
9
+ "notification.list",
10
+ "work.discover",
11
+ ]);
12
+ export function classifyConnectorIntentEffect(intent) {
13
+ if (intent === "agent.heartbeat")
14
+ return "keepalive";
15
+ if (intent === "task.claim")
16
+ return "task_claim";
17
+ if (READONLY.has(intent))
18
+ return "read_only";
19
+ return "side_effect";
20
+ }
21
+ /** In-memory ledger for tests and offline harnesses. */
22
+ export class InMemoryEffectCommitLedger {
23
+ byKey = new Map();
24
+ key(decisionId, idempotencyKey) {
25
+ return `${decisionId}::${idempotencyKey}`;
26
+ }
27
+ async getOrCreateIntentCommitRecord(input) {
28
+ const k = this.key(input.decisionId, input.idempotencyKey);
29
+ const hit = this.byKey.get(k);
30
+ if (hit) {
31
+ return { existing: true, record: hit };
32
+ }
33
+ const id = crypto.randomUUID();
34
+ const rec = { id, state: "planned", outcomeRef: undefined };
35
+ this.byKey.set(k, rec);
36
+ return { existing: false, record: rec };
37
+ }
38
+ /** Test seam: mark a key as already committed with replayable outcome. */
39
+ seedCommitted(decisionId, idempotencyKey, outcomeRef) {
40
+ const id = crypto.randomUUID();
41
+ this.byKey.set(this.key(decisionId, idempotencyKey), { id, state: "committed", outcomeRef });
42
+ }
43
+ markState(decisionId, idempotencyKey, state) {
44
+ const k = this.key(decisionId, idempotencyKey);
45
+ const cur = this.byKey.get(k);
46
+ if (!cur)
47
+ throw new Error("ledger_seed_missing");
48
+ this.byKey.set(k, { ...cur, state });
49
+ }
50
+ }
51
+ export async function enforceExecutionPolicy(plan, intent, request, deps) {
52
+ const semantics = classifyConnectorIntentEffect(intent);
53
+ if ((semantics === "side_effect" || semantics === "task_claim") && !plan.idempotencyKey?.trim()) {
54
+ throw new ConnectorPolicyError("permanent_input_error", "side_effect_requires_idempotency_key");
55
+ }
56
+ if (plan.degraded && (semantics === "side_effect" || semantics === "task_claim")) {
57
+ throw new ConnectorPolicyError("semantic_rejection", "degraded_channel_not_allowed_for_side_effect");
58
+ }
59
+ if (plan.idempotencyKey && deps.effectCommitLedger) {
60
+ if (!request.decisionId?.trim() || !request.intentId?.trim()) {
61
+ throw new ConnectorPolicyError("permanent_input_error", "effect_commit_requires_decision_and_intent_id");
62
+ }
63
+ const lookup = await deps.effectCommitLedger.getOrCreateIntentCommitRecord({
64
+ decisionId: request.decisionId,
65
+ intentId: request.intentId,
66
+ idempotencyKey: plan.idempotencyKey,
67
+ effectClass: semantics,
68
+ });
69
+ if (lookup.existing && lookup.record.state === "committed") {
70
+ return {
71
+ skipAdapter: true,
72
+ existingOutcomeRef: lookup.record.outcomeRef,
73
+ effectCommitId: lookup.record.id,
74
+ };
75
+ }
76
+ if (lookup.existing && (lookup.record.state === "dispatched" || lookup.record.state === "reconcile")) {
77
+ throw new ConnectorPolicyError("concurrency_conflict", "effect_commit_requires_reconcile");
78
+ }
79
+ return { skipAdapter: false, effectCommitId: lookup.record.id };
80
+ }
81
+ return { skipAdapter: false };
82
+ }
@@ -1,6 +1,8 @@
1
1
  export * from "./contract.js";
2
2
  export * from "./manifest.js";
3
+ export * from "./map-life-evidence.js";
3
4
  export * from "./failure-taxonomy.js";
4
5
  export * from "./route-planner.js";
5
6
  export * from "./channel-health.js";
6
7
  export * from "./policy-layer.js";
8
+ export * from "./execution-policy.js";
@@ -1,6 +1,8 @@
1
1
  export * from "./contract.js";
2
2
  export * from "./manifest.js";
3
+ export * from "./map-life-evidence.js";
3
4
  export * from "./failure-taxonomy.js";
4
5
  export * from "./route-planner.js";
5
6
  export * from "./channel-health.js";
6
7
  export * from "./policy-layer.js";
8
+ export * from "./execution-policy.js";
@@ -1,11 +1,64 @@
1
- import { type CapabilityIntent, type ChannelType, type ConnectorManifestLike } from "./contract.js";
2
- export type ConnectorManifest = ConnectorManifestLike;
1
+ import { z } from "zod";
2
+ import { type CapabilityIntent, type ChannelType } from "./contract.js";
3
+ declare const connectorManifestSchema: z.ZodObject<{
4
+ platformId: z.ZodString;
5
+ supportedCapabilities: z.ZodArray<z.ZodEnum<{
6
+ "feed.read": "feed.read";
7
+ "post.publish": "post.publish";
8
+ "comment.reply": "comment.reply";
9
+ "notification.list": "notification.list";
10
+ "message.send": "message.send";
11
+ "agent.register": "agent.register";
12
+ "agent.heartbeat": "agent.heartbeat";
13
+ "work.discover": "work.discover";
14
+ "task.claim": "task.claim";
15
+ }>>;
16
+ channelPriority: z.ZodArray<z.ZodEnum<{
17
+ api_rest: "api_rest";
18
+ api_rpc: "api_rpc";
19
+ a2a: "a2a";
20
+ mcp: "mcp";
21
+ cli: "cli";
22
+ skill: "skill";
23
+ browser: "browser";
24
+ }>>;
25
+ credentialTypes: z.ZodArray<z.ZodString>;
26
+ degradedChannels: z.ZodOptional<z.ZodArray<z.ZodEnum<{
27
+ api_rest: "api_rest";
28
+ api_rpc: "api_rpc";
29
+ a2a: "a2a";
30
+ mcp: "mcp";
31
+ cli: "cli";
32
+ skill: "skill";
33
+ browser: "browser";
34
+ }>>>;
35
+ sourceRefPolicy: z.ZodOptional<z.ZodObject<{
36
+ minSourceRefs: z.ZodDefault<z.ZodNumber>;
37
+ rejectInlineSensitivePayload: z.ZodOptional<z.ZodBoolean>;
38
+ }, z.core.$strip>>;
39
+ }, z.core.$strip>;
40
+ export type ConnectorManifest = z.infer<typeof connectorManifestSchema>;
3
41
  export declare class CapabilityContractRegistry {
4
42
  private readonly byPlatform;
5
43
  register(manifest: ConnectorManifest): void;
6
44
  loadManifest(platformId: string): ConnectorManifest;
45
+ listRegisteredPlatformIds(): string[];
7
46
  hasCapability(platformId: string, intent: CapabilityIntent): boolean;
8
47
  listCapabilities(platformId: string): CapabilityIntent[];
9
48
  listChannels(platformId: string): ChannelType[];
10
49
  }
50
+ /** T3.1.1 contract name for manifest-first registry. */
51
+ export declare const ConnectorManifestRegistry: typeof CapabilityContractRegistry;
52
+ export type ConnectorManifestRegistry = CapabilityContractRegistry;
53
+ export declare function describeConnector(registry: CapabilityContractRegistry, platformId: string): ConnectorManifest;
54
+ export declare function checkConnector(registry: CapabilityContractRegistry, platformId: string): {
55
+ ok: boolean;
56
+ errors: string[];
57
+ };
58
+ export declare function discoverCapabilities(registry: CapabilityContractRegistry): Array<{
59
+ platformId: string;
60
+ capabilities: CapabilityIntent[];
61
+ degradedChannels?: ChannelType[];
62
+ }>;
11
63
  export declare function parseConnectorManifest(input: unknown): ConnectorManifest;
64
+ export {};
@@ -1,11 +1,18 @@
1
1
  import { z } from "zod";
2
2
  import { CAPABILITY_INTENTS, CHANNEL_TYPES } from "./contract.js";
3
+ const sourceRefPolicySchema = z
4
+ .object({
5
+ minSourceRefs: z.number().int().min(0).default(1),
6
+ rejectInlineSensitivePayload: z.boolean().optional(),
7
+ })
8
+ .optional();
3
9
  const connectorManifestSchema = z.object({
4
10
  platformId: z.string().min(1),
5
11
  supportedCapabilities: z.array(z.enum(CAPABILITY_INTENTS)).min(1),
6
12
  channelPriority: z.array(z.enum(CHANNEL_TYPES)).min(1),
7
13
  credentialTypes: z.array(z.string().min(1)).min(1),
8
14
  degradedChannels: z.array(z.enum(CHANNEL_TYPES)).optional(),
15
+ sourceRefPolicy: sourceRefPolicySchema,
9
16
  });
10
17
  export class CapabilityContractRegistry {
11
18
  byPlatform = new Map();
@@ -20,6 +27,9 @@ export class CapabilityContractRegistry {
20
27
  }
21
28
  return found;
22
29
  }
30
+ listRegisteredPlatformIds() {
31
+ return [...this.byPlatform.keys()];
32
+ }
23
33
  hasCapability(platformId, intent) {
24
34
  const manifest = this.loadManifest(platformId);
25
35
  return manifest.supportedCapabilities.includes(intent);
@@ -31,6 +41,46 @@ export class CapabilityContractRegistry {
31
41
  return [...this.loadManifest(platformId).channelPriority];
32
42
  }
33
43
  }
44
+ /** T3.1.1 contract name for manifest-first registry. */
45
+ export const ConnectorManifestRegistry = CapabilityContractRegistry;
46
+ export function describeConnector(registry, platformId) {
47
+ return registry.loadManifest(platformId);
48
+ }
49
+ export function checkConnector(registry, platformId) {
50
+ const errors = [];
51
+ try {
52
+ const manifest = registry.loadManifest(platformId);
53
+ if (manifest.supportedCapabilities.length === 0) {
54
+ errors.push("capability_list_empty");
55
+ }
56
+ if (manifest.channelPriority.length === 0) {
57
+ errors.push("channel_priority_empty");
58
+ }
59
+ if (manifest.credentialTypes.length === 0) {
60
+ errors.push("credential_types_empty");
61
+ }
62
+ const degraded = manifest.degradedChannels ?? [];
63
+ for (const channel of degraded) {
64
+ if (!manifest.channelPriority.includes(channel)) {
65
+ errors.push(`degraded_channel_not_in_priority:${channel}`);
66
+ }
67
+ }
68
+ }
69
+ catch (error) {
70
+ errors.push(error instanceof Error ? error.message : String(error));
71
+ }
72
+ return { ok: errors.length === 0, errors };
73
+ }
74
+ export function discoverCapabilities(registry) {
75
+ return registry.listRegisteredPlatformIds().map((platformId) => {
76
+ const manifest = registry.loadManifest(platformId);
77
+ return {
78
+ platformId,
79
+ capabilities: [...manifest.supportedCapabilities],
80
+ degradedChannels: manifest.degradedChannels,
81
+ };
82
+ });
83
+ }
34
84
  export function parseConnectorManifest(input) {
35
85
  return connectorManifestSchema.parse(input);
36
86
  }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Maps normalized connector success results to `LifeEvidenceCandidate` (T3.1.2).
3
+ * Returns null when evidence cannot be source-backed (no refs, wrong intent, or failure).
4
+ */
5
+ import type { CapabilityIntent, ConnectorResult } from "./contract.js";
6
+ import type { LifeEvidenceCandidate, Sensitivity } from "../../storage/life-evidence/types.js";
7
+ /**
8
+ * Produce a single life-evidence candidate from a connector outcome, or null if not mappable.
9
+ */
10
+ export declare function mapLifeEvidence(input: {
11
+ platformId: string;
12
+ intent: CapabilityIntent;
13
+ result: ConnectorResult<unknown>;
14
+ observedAt?: string;
15
+ sensitivityOverride?: Sensitivity;
16
+ }): LifeEvidenceCandidate | null;
@@ -0,0 +1,79 @@
1
+ function extractSourceRefs(platformId, data, observedAt) {
2
+ if (data && typeof data === "object") {
3
+ const record = data;
4
+ if (Array.isArray(record.sourceRefs)) {
5
+ const out = [];
6
+ for (const item of record.sourceRefs) {
7
+ if (item && typeof item === "object" && "uri" in item && "id" in item) {
8
+ const ref = item;
9
+ out.push({
10
+ id: String(ref.id),
11
+ kind: ref.kind ?? "platform_item",
12
+ uri: String(ref.uri),
13
+ excerptHash: ref.excerptHash !== undefined ? String(ref.excerptHash) : undefined,
14
+ observedAt: ref.observedAt !== undefined ? String(ref.observedAt) : observedAt,
15
+ });
16
+ }
17
+ }
18
+ if (out.length > 0)
19
+ return out;
20
+ }
21
+ if (Array.isArray(record.items)) {
22
+ return record.items.map((item, index) => {
23
+ const id = item && typeof item === "object" && "id" in item
24
+ ? String(item.id)
25
+ : `${platformId}-item-${index}`;
26
+ return {
27
+ id,
28
+ kind: "platform_item",
29
+ uri: `platform://${platformId}/item/${encodeURIComponent(id)}`,
30
+ observedAt,
31
+ };
32
+ });
33
+ }
34
+ }
35
+ return [];
36
+ }
37
+ function resolveEvidenceType(intent) {
38
+ if (intent === "feed.read")
39
+ return "platform_browse";
40
+ if (intent === "work.discover")
41
+ return "task_discovery";
42
+ return null;
43
+ }
44
+ function resolveSensitivity(intent, explicit) {
45
+ if (explicit)
46
+ return explicit;
47
+ if (intent === "message.send" || intent === "comment.reply")
48
+ return "private";
49
+ return "public";
50
+ }
51
+ /**
52
+ * Produce a single life-evidence candidate from a connector outcome, or null if not mappable.
53
+ */
54
+ export function mapLifeEvidence(input) {
55
+ if (input.result.status !== "success") {
56
+ return null;
57
+ }
58
+ if (input.intent === "message.send") {
59
+ return null;
60
+ }
61
+ const evidenceType = resolveEvidenceType(input.intent);
62
+ if (!evidenceType) {
63
+ return null;
64
+ }
65
+ const observedAt = input.observedAt ?? new Date().toISOString();
66
+ const refs = extractSourceRefs(input.platformId, input.result.data, observedAt);
67
+ if (refs.length === 0) {
68
+ return null;
69
+ }
70
+ return {
71
+ timestamp: observedAt,
72
+ evidenceType,
73
+ platformId: input.platformId,
74
+ summary: `${input.platformId}:${input.intent}`,
75
+ sourceRefs: refs,
76
+ sensitivity: resolveSensitivity(input.intent, input.sensitivityOverride),
77
+ producer: "connector-system",
78
+ };
79
+ }
@@ -1,4 +1,5 @@
1
1
  import { type FailureClass } from "./failure-taxonomy.js";
2
+ import { type EffectCommitLedgerPort } from "./execution-policy.js";
2
3
  import type { CapabilityIntent, ConnectorRequest, ConnectorResult, ExecutionPlan, ExecutionRunner, RoutePlanner } from "./contract.js";
3
4
  import type { ExecutionTelemetry } from "../../observability/services/execution-telemetry.js";
4
5
  export interface RetryPolicy {
@@ -21,6 +22,7 @@ export interface ConnectorPolicyContext {
21
22
  cooldownPort?: CooldownPort;
22
23
  retryPolicy?: Partial<RetryPolicy>;
23
24
  allowDegradedFallback?: (plan: ExecutionPlan, request: ConnectorRequest) => boolean;
25
+ effectCommitLedger?: EffectCommitLedgerPort;
24
26
  }
25
27
  export declare function createConnectorPolicyLayer(ctx: ConnectorPolicyContext): {
26
28
  executeWithPolicy(intent: CapabilityIntent, request: ConnectorRequest): Promise<ConnectorResult<unknown>>;
@@ -1,4 +1,5 @@
1
1
  import { classifyFailure } from "./failure-taxonomy.js";
2
+ import { enforceExecutionPolicy } from "./execution-policy.js";
2
3
  const DEFAULT_RETRY_MAX = 3;
3
4
  const DEFAULT_BASE_DELAY_MS = 1000;
4
5
  const DEFAULT_MAX_DELAY_MS = 30000;
@@ -106,6 +107,21 @@ export function createConnectorPolicyLayer(ctx) {
106
107
  },
107
108
  };
108
109
  }
110
+ const policyGate = await enforceExecutionPolicy(plan, intent, request, {
111
+ effectCommitLedger: ctx.effectCommitLedger,
112
+ });
113
+ if (policyGate.skipAdapter && policyGate.existingOutcomeRef) {
114
+ return {
115
+ status: "success",
116
+ data: { replayedCommit: true, outcomeRef: policyGate.existingOutcomeRef },
117
+ metadata: {
118
+ platformId: request.platformId,
119
+ channel: plan.channel,
120
+ latencyMs: 0,
121
+ degraded: plan.degraded,
122
+ },
123
+ };
124
+ }
109
125
  let lastFailure;
110
126
  for (let attempt = 1; attempt <= retryPolicy.maxRetries; attempt += 1) {
111
127
  const traceId = `${makeTraceId(request, plan)}:${attempt}`;
@@ -93,6 +93,7 @@ export class ConnectorRoutePlanner {
93
93
  channel: selected,
94
94
  endpointMode: endpointModeFor(selected),
95
95
  idempotencyKey: request.idempotencyKey,
96
+ degraded: isDegradedChannel(selected, manifest.degradedChannels),
96
97
  };
97
98
  }
98
99
  }
@@ -2,3 +2,4 @@ export * from "./base/index.js";
2
2
  export * from "./social-community/moltbook/index.js";
3
3
  export * from "./social-community/instreet/index.js";
4
4
  export * from "./agent-network/evomap/index.js";
5
+ export * from "./near-real/near-real-connector-smoke.js";
@@ -2,3 +2,4 @@ export * from "./base/index.js";
2
2
  export * from "./social-community/moltbook/index.js";
3
3
  export * from "./social-community/instreet/index.js";
4
4
  export * from "./agent-network/evomap/index.js";
5
+ export * from "./near-real/near-real-connector-smoke.js";
@@ -0,0 +1,19 @@
1
+ import type { ObservabilityDatabase } from "../../observability/db/index.js";
2
+ import type { StateDatabase } from "../../storage/db/index.js";
3
+ export interface NearRealConnectorSmokeResult {
4
+ generatedAt: string;
5
+ platforms: {
6
+ social: "moltbook";
7
+ agentNetwork: "evomap";
8
+ };
9
+ feedReadEvidenceId?: string;
10
+ workDiscoverEvidenceId?: string;
11
+ taskClaimDryRunOk: boolean;
12
+ executionAttemptRowsForDecision: number;
13
+ }
14
+ export interface RunNearRealConnectorSmokeInput {
15
+ state: StateDatabase;
16
+ observabilityDb: ObservabilityDatabase;
17
+ workspaceRoot: string;
18
+ }
19
+ export declare function runNearRealConnectorSmoke(input: RunNearRealConnectorSmokeInput): Promise<NearRealConnectorSmokeResult>;
@@ -0,0 +1,152 @@
1
+ /**
2
+ * T3.3.1 — Near-real connector smoke: sentinel adapters + policy + telemetry + life evidence ingest.
3
+ * Uses fixture payloads (no live HTTP); task.claim runs with idempotency key (dry-run style outcome).
4
+ */
5
+ import { eq } from "drizzle-orm";
6
+ import { CapabilityContractRegistry } from "../base/manifest.js";
7
+ import { ConnectorRoutePlanner } from "../base/route-planner.js";
8
+ import { ChannelHealthStore } from "../base/channel-health.js";
9
+ import { createConnectorPolicyLayer } from "../base/policy-layer.js";
10
+ import { InMemoryEffectCommitLedger } from "../base/execution-policy.js";
11
+ import { mapLifeEvidence } from "../base/map-life-evidence.js";
12
+ import { moltbookManifest } from "../social-community/moltbook/manifest.js";
13
+ import { evomapManifest } from "../agent-network/evomap/manifest.js";
14
+ import { ExecutionTelemetry } from "../../observability/services/execution-telemetry.js";
15
+ import { executionAttempts } from "../../observability/db/schema/index.js";
16
+ import { appendLifeEvidence } from "../../storage/life-evidence/append-life-evidence.js";
17
+ const DECISION_ID = "dec-near-real-smoke";
18
+ function smokeRouteContext() {
19
+ return {
20
+ async loadCredentialState(platformId) {
21
+ if (platformId === "moltbook") {
22
+ return { platformId, status: "active", credentialType: "api_key" };
23
+ }
24
+ if (platformId === "evomap") {
25
+ return { platformId, status: "active", credentialType: "node_secret" };
26
+ }
27
+ throw new Error(`near_real_smoke_unsupported_platform:${platformId}`);
28
+ },
29
+ async loadCooldownState() {
30
+ return { blocked: false };
31
+ },
32
+ };
33
+ }
34
+ const smokeExecutionRunner = {
35
+ async run(plan, request) {
36
+ if (plan.platformId === "moltbook" && plan.intent === "feed.read") {
37
+ return {
38
+ platformId: plan.platformId,
39
+ channel: plan.channel,
40
+ latencyMs: 2,
41
+ success: true,
42
+ payload: {
43
+ items: [{ id: "mb-smoke-1", title: "near-real fixture post" }],
44
+ },
45
+ };
46
+ }
47
+ if (plan.platformId === "evomap" && plan.intent === "work.discover") {
48
+ return {
49
+ platformId: plan.platformId,
50
+ channel: plan.channel,
51
+ latencyMs: 3,
52
+ success: true,
53
+ payload: {
54
+ items: [{ id: "ev-task-smoke-1", title: "fixture agent task" }],
55
+ },
56
+ };
57
+ }
58
+ if (plan.platformId === "evomap" && plan.intent === "task.claim") {
59
+ return {
60
+ platformId: plan.platformId,
61
+ channel: plan.channel,
62
+ latencyMs: 1,
63
+ success: true,
64
+ degraded: plan.degraded,
65
+ payload: {
66
+ dryRun: true,
67
+ taskId: typeof request.payload.taskId === "string" ? request.payload.taskId : "unknown",
68
+ note: "near_real_smoke_no_external_side_effect",
69
+ },
70
+ };
71
+ }
72
+ throw new Error(`near_real_smoke_unhandled:${plan.platformId}:${plan.intent}`);
73
+ },
74
+ };
75
+ export async function runNearRealConnectorSmoke(input) {
76
+ const ledger = new InMemoryEffectCommitLedger();
77
+ const registry = new CapabilityContractRegistry();
78
+ registry.register({ ...moltbookManifest });
79
+ registry.register({ ...evomapManifest });
80
+ const planner = new ConnectorRoutePlanner(registry, smokeRouteContext(), new ChannelHealthStore());
81
+ const telemetry = new ExecutionTelemetry(input.observabilityDb);
82
+ const policy = createConnectorPolicyLayer({
83
+ routePlanner: planner,
84
+ executionRunner: smokeExecutionRunner,
85
+ telemetry,
86
+ effectCommitLedger: ledger,
87
+ retryPolicy: { maxRetries: 1, jitter: false },
88
+ });
89
+ const base = {
90
+ decisionId: DECISION_ID,
91
+ payload: {},
92
+ };
93
+ const feedResult = await policy.executeWithPolicy("feed.read", {
94
+ platformId: "moltbook",
95
+ intent: "feed.read",
96
+ ...base,
97
+ intentId: "intent-smoke-feed-read",
98
+ payload: {},
99
+ });
100
+ let feedReadEvidenceId;
101
+ const feedCand = mapLifeEvidence({
102
+ platformId: "moltbook",
103
+ intent: "feed.read",
104
+ result: feedResult,
105
+ });
106
+ if (feedCand) {
107
+ const ack = await appendLifeEvidence(input.state, input.workspaceRoot, feedCand);
108
+ feedReadEvidenceId = ack.evidenceId;
109
+ }
110
+ const workResult = await policy.executeWithPolicy("work.discover", {
111
+ platformId: "evomap",
112
+ intent: "work.discover",
113
+ ...base,
114
+ intentId: "intent-smoke-work-discover",
115
+ payload: {},
116
+ });
117
+ let workDiscoverEvidenceId;
118
+ const workCand = mapLifeEvidence({
119
+ platformId: "evomap",
120
+ intent: "work.discover",
121
+ result: workResult,
122
+ });
123
+ if (workCand) {
124
+ const ack = await appendLifeEvidence(input.state, input.workspaceRoot, workCand);
125
+ workDiscoverEvidenceId = ack.evidenceId;
126
+ }
127
+ const claimResult = await policy.executeWithPolicy("task.claim", {
128
+ platformId: "evomap",
129
+ intent: "task.claim",
130
+ ...base,
131
+ intentId: "intent-smoke-task-claim",
132
+ payload: { taskId: "ev-task-smoke-1" },
133
+ idempotencyKey: "idem-near-real-task-claim-smoke",
134
+ });
135
+ const claimPayload = claimResult.status === "success" && claimResult.data && typeof claimResult.data === "object"
136
+ ? claimResult.data
137
+ : undefined;
138
+ const attemptRows = await input.observabilityDb.db
139
+ .select()
140
+ .from(executionAttempts)
141
+ .where(eq(executionAttempts.decisionId, DECISION_ID));
142
+ return {
143
+ generatedAt: new Date().toISOString(),
144
+ platforms: { social: "moltbook", agentNetwork: "evomap" },
145
+ feedReadEvidenceId,
146
+ workDiscoverEvidenceId,
147
+ taskClaimDryRunOk: claimResult.status === "success" &&
148
+ claimPayload?.dryRun === true &&
149
+ claimPayload?.note === "near_real_smoke_no_external_side_effect",
150
+ executionAttemptRowsForDecision: attemptRows.length,
151
+ };
152
+ }
@@ -12,6 +12,8 @@ function intentKindToScene(kind) {
12
12
  return "outreach";
13
13
  case "exploration":
14
14
  return "explain";
15
+ case "quiet":
16
+ return null;
15
17
  default:
16
18
  return null;
17
19
  }
@@ -2,7 +2,7 @@
2
2
  * Heartbeat Decision Loop
3
3
  *
4
4
  * Main entry point for the heartbeat runtime. Accepts a HeartbeatSignal,
5
- * builds a ContinuitySnapshot, plans candidate intents, evaluates guards,
5
+ * builds runtime snapshot, plans candidate intents, evaluates hard guards,
6
6
  * and returns a HeartbeatCycleResult.
7
7
  *
8
8
  * Per design doc §4.3: heartbeat round follows the sequence:
@@ -12,28 +12,49 @@
12
12
  * implements the default conservative path where HEARTBEAT_OK is
13
13
  * the first-class result when no action is warranted.
14
14
  */
15
- import type { ContinuitySnapshot } from "../types.js";
16
- import type { HeartbeatSignal, HeartbeatCycleResult } from "./signal.js";
15
+ import type { HeartbeatSignal, HeartbeatCycleResult, HeartbeatCycleStatus, RuntimeScope, RuntimeTrigger } from "./signal.js";
16
+ import type { CandidateIntent, ContinuitySnapshot, IntentKind } from "../types.js";
17
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>;
18
48
  export interface HeartbeatDeps {
19
49
  /** Load snapshot inputs from state-system */
20
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;
21
55
  }
22
56
  /**
23
57
  * Ingest a heartbeat rhythm signal and drive one full decision round.
24
- *
25
- * Decision flow:
26
- * 1. Build continuity snapshot from state-system inputs
27
- * 2. Plan candidate intents from snapshot
28
- * 3. Evaluate guards for each candidate in priority order
29
- * 4. Return one of:
30
- * - intent_selected: a candidate passed all guards
31
- * - denied: candidates existed but all were rejected by guards
32
- * - heartbeat_ok: no candidates or no action warranted (conservative default)
33
- *
34
- * Per ADR-005: heartbeat is the free-rhythm main entry; this loop
35
- * implements the default conservative path where HEARTBEAT_OK is
36
- * the first-class result when no action is warranted.
37
58
  */
38
59
  export declare function ingestRhythmSignal(signal: HeartbeatSignal, deps: HeartbeatDeps): Promise<HeartbeatCycleResult>;
39
60
  /**