@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.
- package/index.js +73 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -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 +161 -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/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 +46 -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
|
@@ -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 {
|
|
2
|
-
|
|
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}`;
|
|
@@ -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
|
/**
|