@haaaiawd/second-nature 0.1.8 → 0.1.10
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 +281 -69
- package/openclaw.plugin.json +1 -1
- package/package.json +2 -1
- package/runtime/cli/commands/index.d.ts +2 -0
- package/runtime/cli/commands/index.js +61 -6
- package/runtime/cli/explain/explain-surface-subject.d.ts +8 -0
- package/runtime/cli/explain/explain-surface-subject.js +9 -0
- package/runtime/cli/explain/format-explanation.d.ts +2 -0
- package/runtime/cli/explain/format-explanation.js +2 -0
- package/runtime/cli/explain/resolve-subject.js +15 -0
- package/runtime/cli/host-capability/classify-delivery.d.ts +14 -0
- package/runtime/cli/host-capability/classify-delivery.js +20 -0
- package/runtime/cli/host-capability/probe-host-capability.d.ts +2 -0
- package/runtime/cli/host-capability/probe-host-capability.js +58 -0
- package/runtime/cli/host-capability/record-host-capability.d.ts +6 -0
- package/runtime/cli/host-capability/record-host-capability.js +14 -0
- package/runtime/cli/host-capability/types.d.ts +71 -0
- package/runtime/cli/host-capability/types.js +6 -0
- package/runtime/cli/host-smoke/run-host-smoke.d.ts +2 -0
- package/runtime/cli/host-smoke/run-host-smoke.js +40 -0
- package/runtime/cli/host-smoke/types.d.ts +35 -0
- package/runtime/cli/host-smoke/types.js +6 -0
- package/runtime/cli/index.js +18 -0
- package/runtime/cli/ops/heartbeat-surface.d.ts +35 -0
- package/runtime/cli/ops/heartbeat-surface.js +71 -0
- package/runtime/cli/ops/ops-router.d.ts +16 -0
- package/runtime/cli/ops/ops-router.js +83 -0
- package/runtime/cli/ops/show-operator-fallback.d.ts +13 -0
- package/runtime/cli/ops/show-operator-fallback.js +22 -0
- package/runtime/cli/ops/workspace-heartbeat-runner.d.ts +10 -0
- package/runtime/cli/ops/workspace-heartbeat-runner.js +26 -0
- package/runtime/cli/read-models/index.d.ts +11 -2
- package/runtime/cli/read-models/index.js +50 -0
- package/runtime/cli/read-models/operator-explain-map.d.ts +6 -0
- package/runtime/cli/read-models/operator-explain-map.js +10 -0
- package/runtime/cli/read-models/types.d.ts +5 -1
- package/runtime/cli/runtime/runtime-artifact-boundary.d.ts +28 -0
- package/runtime/cli/runtime/runtime-artifact-boundary.js +94 -0
- package/runtime/connectors/base/contract.d.ts +6 -0
- package/runtime/connectors/base/execution-policy.d.ts +47 -0
- package/runtime/connectors/base/execution-policy.js +82 -0
- package/runtime/connectors/base/index.d.ts +2 -0
- package/runtime/connectors/base/index.js +2 -0
- package/runtime/connectors/base/manifest.d.ts +55 -2
- package/runtime/connectors/base/manifest.js +50 -0
- package/runtime/connectors/base/map-life-evidence.d.ts +16 -0
- package/runtime/connectors/base/map-life-evidence.js +79 -0
- package/runtime/connectors/base/policy-layer.d.ts +2 -0
- package/runtime/connectors/base/policy-layer.js +16 -0
- package/runtime/connectors/base/route-planner.js +1 -0
- package/runtime/connectors/index.d.ts +1 -0
- package/runtime/connectors/index.js +1 -0
- package/runtime/connectors/near-real/near-real-connector-smoke.d.ts +19 -0
- package/runtime/connectors/near-real/near-real-connector-smoke.js +152 -0
- package/runtime/core/second-nature/heartbeat/heartbeat-executor.js +2 -0
- package/runtime/core/second-nature/heartbeat/heartbeat-loop.d.ts +37 -16
- package/runtime/core/second-nature/heartbeat/heartbeat-loop.js +95 -29
- package/runtime/core/second-nature/heartbeat/index.d.ts +4 -1
- package/runtime/core/second-nature/heartbeat/index.js +4 -1
- package/runtime/core/second-nature/heartbeat/run-heartbeat-cycle.d.ts +21 -0
- package/runtime/core/second-nature/heartbeat/run-heartbeat-cycle.js +35 -0
- package/runtime/core/second-nature/heartbeat/runtime-snapshot.d.ts +28 -0
- package/runtime/core/second-nature/heartbeat/runtime-snapshot.js +35 -0
- package/runtime/core/second-nature/heartbeat/signal.d.ts +9 -2
- package/runtime/core/second-nature/heartbeat/snapshot-builder.d.ts +19 -1
- package/runtime/core/second-nature/index.d.ts +8 -0
- package/runtime/core/second-nature/index.js +8 -0
- package/runtime/core/second-nature/orchestrator/effect-dispatcher.d.ts +1 -1
- package/runtime/core/second-nature/orchestrator/effect-dispatcher.js +9 -4
- package/runtime/core/second-nature/orchestrator/guard-layer.d.ts +6 -0
- package/runtime/core/second-nature/orchestrator/guard-layer.js +76 -20
- package/runtime/core/second-nature/orchestrator/intent-planner.d.ts +10 -0
- package/runtime/core/second-nature/orchestrator/intent-planner.js +135 -28
- package/runtime/core/second-nature/orchestrator/lease-manager.d.ts +1 -1
- package/runtime/core/second-nature/orchestrator/lease-manager.js +1 -1
- package/runtime/core/second-nature/outreach/build-outreach-draft-request.d.ts +6 -0
- package/runtime/core/second-nature/outreach/build-outreach-draft-request.js +63 -0
- package/runtime/core/second-nature/outreach/delivery-target.d.ts +26 -0
- package/runtime/core/second-nature/outreach/delivery-target.js +70 -0
- package/runtime/core/second-nature/outreach/dispatch-user-outreach.d.ts +38 -0
- package/runtime/core/second-nature/outreach/dispatch-user-outreach.js +119 -0
- package/runtime/core/second-nature/outreach/judge-input-from-snapshot.d.ts +7 -0
- package/runtime/core/second-nature/outreach/judge-input-from-snapshot.js +45 -0
- package/runtime/core/second-nature/outreach/judge-outreach.d.ts +40 -0
- package/runtime/core/second-nature/outreach/judge-outreach.js +121 -0
- package/runtime/core/second-nature/quiet/run-source-backed-quiet.d.ts +21 -0
- package/runtime/core/second-nature/quiet/run-source-backed-quiet.js +123 -0
- package/runtime/core/second-nature/rhythm/planner-rhythm-window.d.ts +15 -0
- package/runtime/core/second-nature/rhythm/planner-rhythm-window.js +52 -0
- package/runtime/core/second-nature/rhythm/policy-bridge.d.ts +19 -0
- package/runtime/core/second-nature/rhythm/policy-bridge.js +34 -0
- package/runtime/core/second-nature/types.d.ts +16 -2
- package/runtime/guidance/draft-outreach-message.d.ts +7 -0
- package/runtime/guidance/draft-outreach-message.js +42 -0
- package/runtime/guidance/evidence-guidance.d.ts +40 -0
- package/runtime/guidance/evidence-guidance.js +52 -0
- package/runtime/guidance/index.d.ts +3 -0
- package/runtime/guidance/index.js +3 -0
- package/runtime/guidance/outreach-draft-schema.d.ts +228 -0
- package/runtime/guidance/outreach-draft-schema.js +80 -0
- package/runtime/observability/audit/append-only-audit-store.d.ts +14 -0
- package/runtime/observability/audit/append-only-audit-store.js +21 -0
- package/runtime/observability/audit/audit-envelope.d.ts +51 -0
- package/runtime/observability/audit/audit-envelope.js +130 -0
- package/runtime/observability/audit/verify-audit-hash-chain.d.ts +23 -0
- package/runtime/observability/audit/verify-audit-hash-chain.js +83 -0
- package/runtime/observability/db/index.js +11 -0
- package/runtime/observability/db/schema/host-capability-reports.d.ts +180 -0
- package/runtime/observability/db/schema/host-capability-reports.js +12 -0
- package/runtime/observability/db/schema/index.d.ts +1 -0
- package/runtime/observability/db/schema/index.js +1 -0
- package/runtime/observability/index.d.ts +7 -0
- package/runtime/observability/index.js +7 -0
- package/runtime/observability/query/explain-query.d.ts +48 -0
- package/runtime/observability/query/explain-query.js +114 -0
- package/runtime/observability/query/export-audit-bundle.d.ts +22 -0
- package/runtime/observability/query/export-audit-bundle.js +27 -0
- package/runtime/observability/services/decision-ledger.d.ts +1 -1
- package/runtime/observability/services/decision-ledger.js +4 -0
- package/runtime/observability/services/governance-audit.d.ts +14 -0
- package/runtime/observability/services/governance-audit.js +25 -1
- package/runtime/observability/services/governance-plane-recorder.d.ts +47 -0
- package/runtime/observability/services/governance-plane-recorder.js +55 -0
- package/runtime/observability/services/lived-experience-audit.d.ts +97 -0
- package/runtime/observability/services/lived-experience-audit.js +162 -0
- package/runtime/storage/bootstrap/native-sqlite-probe.d.ts +7 -0
- package/runtime/storage/bootstrap/native-sqlite-probe.js +28 -0
- package/runtime/storage/bootstrap/repair-gate.d.ts +17 -0
- package/runtime/storage/bootstrap/repair-gate.js +71 -0
- package/runtime/storage/bootstrap/storage-mode-smoke.d.ts +38 -0
- package/runtime/storage/bootstrap/storage-mode-smoke.js +85 -0
- package/runtime/storage/db/index.js +49 -0
- package/runtime/storage/db/schema/delivery-attempts.d.ts +199 -0
- package/runtime/storage/db/schema/delivery-attempts.js +13 -0
- package/runtime/storage/db/schema/index.d.ts +3 -0
- package/runtime/storage/db/schema/index.js +3 -0
- package/runtime/storage/db/schema/life-evidence-index.d.ts +161 -0
- package/runtime/storage/db/schema/life-evidence-index.js +11 -0
- package/runtime/storage/db/schema/operator-fallback-artifacts.d.ts +161 -0
- package/runtime/storage/db/schema/operator-fallback-artifacts.js +11 -0
- package/runtime/storage/db/schema/policies.d.ts +17 -0
- package/runtime/storage/db/schema/policies.js +1 -0
- package/runtime/storage/delivery/query-delivery-attempts.d.ts +3 -0
- package/runtime/storage/delivery/query-delivery-attempts.js +32 -0
- package/runtime/storage/delivery/types.d.ts +27 -0
- package/runtime/storage/delivery/types.js +1 -0
- package/runtime/storage/delivery/write-delivery-attempt.d.ts +6 -0
- package/runtime/storage/delivery/write-delivery-attempt.js +36 -0
- package/runtime/storage/fallback/load-operator-fallback.d.ts +14 -0
- package/runtime/storage/fallback/load-operator-fallback.js +47 -0
- package/runtime/storage/fallback/operator-fallback-types.d.ts +9 -0
- package/runtime/storage/fallback/operator-fallback-types.js +1 -0
- package/runtime/storage/fallback/operator-fallback-view.d.ts +11 -0
- package/runtime/storage/fallback/operator-fallback-view.js +1 -0
- package/runtime/storage/fallback/write-operator-fallback.d.ts +6 -0
- package/runtime/storage/fallback/write-operator-fallback.js +21 -0
- package/runtime/storage/index.d.ts +21 -0
- package/runtime/storage/index.js +14 -0
- package/runtime/storage/life-evidence/append-life-evidence.d.ts +7 -0
- package/runtime/storage/life-evidence/append-life-evidence.js +64 -0
- package/runtime/storage/life-evidence/types.d.ts +45 -0
- package/runtime/storage/life-evidence/types.js +6 -0
- package/runtime/storage/quiet/persist-quiet-artifact.d.ts +7 -0
- package/runtime/storage/quiet/persist-quiet-artifact.js +22 -0
- package/runtime/storage/quiet/quiet-artifact-types.d.ts +18 -0
- package/runtime/storage/quiet/quiet-artifact-types.js +1 -0
- package/runtime/storage/quiet/quiet-artifact-writer.d.ts +15 -0
- package/runtime/storage/quiet/quiet-artifact-writer.js +56 -0
- package/runtime/storage/repositories/credential-repository.js +12 -1
- package/runtime/storage/rhythm/rhythm-policy-snapshot.d.ts +10 -0
- package/runtime/storage/rhythm/rhythm-policy-snapshot.js +34 -0
- package/runtime/storage/services/credential-vault.d.ts +5 -0
- package/runtime/storage/services/credential-vault.js +47 -9
- package/runtime/storage/snapshots/continuity-snapshot.d.ts +9 -0
- package/runtime/storage/snapshots/continuity-snapshot.js +41 -0
- package/runtime/storage/snapshots/life-evidence-snapshot.d.ts +6 -0
- package/runtime/storage/snapshots/life-evidence-snapshot.js +114 -0
- package/runtime/storage/snapshots/types.d.ts +58 -0
- package/runtime/storage/snapshots/types.js +1 -0
- package/runtime/storage/state-api.js +11 -4
- package/runtime/storage/user-interest/load-user-interest-snapshot.d.ts +2 -0
- package/runtime/storage/user-interest/load-user-interest-snapshot.js +150 -0
- package/runtime/storage/user-interest/types.d.ts +25 -0
- package/runtime/storage/user-interest/types.js +1 -0
- package/workspace-ops-bridge.js +78 -0
|
@@ -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}`;
|
|
@@ -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
|
+
}
|
|
@@ -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
|
|
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 {
|
|
16
|
-
import type {
|
|
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
|
/**
|
|
@@ -1,69 +1,135 @@
|
|
|
1
1
|
import { buildContinuitySnapshot } from "./snapshot-builder.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
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
|
+
}
|
|
4
42
|
/**
|
|
5
43
|
* Ingest a heartbeat rhythm signal and drive one full decision round.
|
|
6
|
-
*
|
|
7
|
-
* Decision flow:
|
|
8
|
-
* 1. Build continuity snapshot from state-system inputs
|
|
9
|
-
* 2. Plan candidate intents from snapshot
|
|
10
|
-
* 3. Evaluate guards for each candidate in priority order
|
|
11
|
-
* 4. Return one of:
|
|
12
|
-
* - intent_selected: a candidate passed all guards
|
|
13
|
-
* - denied: candidates existed but all were rejected by guards
|
|
14
|
-
* - heartbeat_ok: no candidates or no action warranted (conservative default)
|
|
15
|
-
*
|
|
16
|
-
* Per ADR-005: heartbeat is the free-rhythm main entry; this loop
|
|
17
|
-
* implements the default conservative path where HEARTBEAT_OK is
|
|
18
|
-
* the first-class result when no action is warranted.
|
|
19
44
|
*/
|
|
20
45
|
export async function ingestRhythmSignal(signal, deps) {
|
|
21
|
-
// Step 1: Build continuity snapshot
|
|
22
46
|
const inputs = await deps.loadSnapshotInputs();
|
|
23
47
|
const snapshot = buildContinuitySnapshot(inputs);
|
|
24
|
-
|
|
25
|
-
const
|
|
26
|
-
|
|
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
|
+
};
|
|
27
68
|
let hasCandidates = false;
|
|
28
69
|
let anyAllow = false;
|
|
70
|
+
let anyDefer = false;
|
|
71
|
+
let anyDeny = false;
|
|
29
72
|
const denyReasons = [];
|
|
30
73
|
for (const intent of candidates) {
|
|
31
74
|
hasCandidates = true;
|
|
32
|
-
const evaluation =
|
|
75
|
+
const evaluation = evaluateHardGuards(intent, runtime);
|
|
33
76
|
if (evaluation.verdict === "allow") {
|
|
34
77
|
anyAllow = true;
|
|
35
|
-
|
|
78
|
+
const base = {
|
|
36
79
|
scope: "rhythm",
|
|
37
80
|
status: "intent_selected",
|
|
38
81
|
selectedIntentId: intent.id,
|
|
39
82
|
reasons: evaluation.reasons,
|
|
40
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;
|
|
41
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;
|
|
42
97
|
denyReasons.push(`${intent.id}:${evaluation.verdict}(${evaluation.reasons.join(",")})`);
|
|
43
98
|
}
|
|
44
|
-
// Step 4: No viable intent path
|
|
45
99
|
if (!hasCandidates) {
|
|
46
|
-
|
|
47
|
-
return {
|
|
100
|
+
const result = {
|
|
48
101
|
scope: "rhythm",
|
|
49
102
|
status: "heartbeat_ok",
|
|
50
|
-
reasons: ["
|
|
103
|
+
reasons: ["silent_no_candidates"],
|
|
51
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;
|
|
52
116
|
}
|
|
53
117
|
if (!anyAllow && denyReasons.length > 0) {
|
|
54
|
-
|
|
55
|
-
return {
|
|
118
|
+
const result = {
|
|
56
119
|
scope: "rhythm",
|
|
57
120
|
status: "denied",
|
|
58
121
|
reasons: denyReasons,
|
|
59
122
|
};
|
|
123
|
+
await emitTrace(result);
|
|
124
|
+
return result;
|
|
60
125
|
}
|
|
61
|
-
|
|
62
|
-
return {
|
|
126
|
+
const result = {
|
|
63
127
|
scope: "rhythm",
|
|
64
128
|
status: "heartbeat_ok",
|
|
65
129
|
reasons: ["no_allow_verdict"],
|
|
66
130
|
};
|
|
131
|
+
await emitTrace(result);
|
|
132
|
+
return result;
|
|
67
133
|
}
|
|
68
134
|
/**
|
|
69
135
|
* Build a snapshot directly from inputs (for testing or when state-system is unavailable).
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
export { type RuntimeScope, type RuntimeTrigger, type HeartbeatCycleStatus, type HeartbeatSignal, type ScopedRuntimeInput, type HeartbeatCycleResult, type ScopeRouteResult, } from "./signal.js";
|
|
2
2
|
export { buildContinuitySnapshot, type SnapshotInputs, } from "./snapshot-builder.js";
|
|
3
|
-
export { ingestRhythmSignal, type HeartbeatDeps, buildSnapshotFromInputs, } from "./heartbeat-loop.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";
|
|
4
7
|
export { routeScopedInput, type ScopeRouterDeps, } from "./scope-router.js";
|
|
5
8
|
export { requestGuidanceForIntent, dispatchAllowedEffect, executeHeartbeatCycle, type GuidanceBridgeDeps, type EffectDispatchDeps, type HeartbeatExecutorDeps, type GuidanceBridgeResult, type HeartbeatExecutionResult, } from "./heartbeat-executor.js";
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
export { buildContinuitySnapshot, } from "./snapshot-builder.js";
|
|
2
|
-
export { ingestRhythmSignal, buildSnapshotFromInputs, } from "./heartbeat-loop.js";
|
|
2
|
+
export { ingestRhythmSignal, resolveAllowedIntentResult, buildSnapshotFromInputs, } from "./heartbeat-loop.js";
|
|
3
|
+
export { buildHeartbeatRuntimeSnapshot, buildLifeEvidenceSliceFromInputs, buildHardGuardDeps, resolveRhythmPolicyForHeartbeat, isLifeEvidenceSliceEmpty, } from "./runtime-snapshot.js";
|
|
4
|
+
export { buildPlannerRhythmWindow } from "../rhythm/planner-rhythm-window.js";
|
|
5
|
+
export { runHeartbeatCycle } from "./run-heartbeat-cycle.js";
|
|
3
6
|
export { routeScopedInput, } from "./scope-router.js";
|
|
4
7
|
export { requestGuidanceForIntent, dispatchAllowedEffect, executeHeartbeatCycle, } from "./heartbeat-executor.js";
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Control-plane heartbeat cycle entry (T2.1.1).
|
|
3
|
+
*
|
|
4
|
+
* Core logic: runtime availability gate → scope routing (user_task bypasses rhythm) →
|
|
5
|
+
* rhythm path delegates to ingestRhythmSignal. Mirrors L0 control-plane-system §4.3.
|
|
6
|
+
*
|
|
7
|
+
* Boundaries: does not claim lived-experience completion when runtime is unavailable;
|
|
8
|
+
* user_task / user_reply do not enter the rhythm candidate planner.
|
|
9
|
+
*/
|
|
10
|
+
import type { HeartbeatSignal, HeartbeatCycleResult } from "./signal.js";
|
|
11
|
+
import type { HeartbeatDeps } from "./heartbeat-loop.js";
|
|
12
|
+
export interface RunHeartbeatCycleInput {
|
|
13
|
+
signal: HeartbeatSignal;
|
|
14
|
+
/** When false, return runtime_carrier_only without loading snapshots (host-safe carrier). */
|
|
15
|
+
runtimeAvailable: boolean;
|
|
16
|
+
deps: HeartbeatDeps;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Single entry for one heartbeat turn: scope routing, runtime gate, then rhythm loop if applicable.
|
|
20
|
+
*/
|
|
21
|
+
export declare function runHeartbeatCycle(input: RunHeartbeatCycleInput): Promise<HeartbeatCycleResult>;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { ingestRhythmSignal } from "./heartbeat-loop.js";
|
|
2
|
+
import { routeScopedInput } from "./scope-router.js";
|
|
3
|
+
/**
|
|
4
|
+
* Single entry for one heartbeat turn: scope routing, runtime gate, then rhythm loop if applicable.
|
|
5
|
+
*/
|
|
6
|
+
export async function runHeartbeatCycle(input) {
|
|
7
|
+
const scoped = {
|
|
8
|
+
trigger: input.signal.trigger,
|
|
9
|
+
scopeHint: input.signal.scopeHint,
|
|
10
|
+
payload: input.signal.payload,
|
|
11
|
+
};
|
|
12
|
+
const route = routeScopedInput(scoped);
|
|
13
|
+
if (!input.runtimeAvailable) {
|
|
14
|
+
return {
|
|
15
|
+
scope: route.scope,
|
|
16
|
+
status: "runtime_carrier_only",
|
|
17
|
+
reasons: ["runtime_unavailable_no_lived_experience_loop"],
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
if (route.scope === "user_task") {
|
|
21
|
+
return {
|
|
22
|
+
scope: "user_task",
|
|
23
|
+
status: "heartbeat_ok",
|
|
24
|
+
reasons: ["rhythm_gate_bypass_user_task"],
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
if (route.scope === "user_reply") {
|
|
28
|
+
return {
|
|
29
|
+
scope: "user_reply",
|
|
30
|
+
status: "heartbeat_ok",
|
|
31
|
+
reasons: ["user_reply_light_continuity_skeleton"],
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
return ingestRhythmSignal(input.signal, input.deps);
|
|
35
|
+
}
|