@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,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Maps calendar rhythm policy + continuity into planner-facing window slice (T2.1.3).
|
|
3
|
+
* Control-plane owns allowedIntentKinds; state never emits them (T2.1.2 boundary).
|
|
4
|
+
*/
|
|
5
|
+
import type { ContinuitySnapshot, IntentKind } from "../types.js";
|
|
6
|
+
import type { RhythmPolicy } from "./rhythm-policy.js";
|
|
7
|
+
export interface PlannerRhythmWindowSlice {
|
|
8
|
+
windowId: string;
|
|
9
|
+
allowedIntentKinds: IntentKind[];
|
|
10
|
+
quietBias: boolean;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Derive allowed intent kinds and quiet bias for candidate planning.
|
|
14
|
+
*/
|
|
15
|
+
export declare function buildPlannerRhythmWindow(now: string, continuity: ContinuitySnapshot, policy: RhythmPolicy): PlannerRhythmWindowSlice;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { selectRhythmWindow } from "./select-window.js";
|
|
2
|
+
const ALL_INTENT_KINDS = [
|
|
3
|
+
"work",
|
|
4
|
+
"exploration",
|
|
5
|
+
"social",
|
|
6
|
+
"quiet",
|
|
7
|
+
"reflection",
|
|
8
|
+
"outreach",
|
|
9
|
+
"maintenance",
|
|
10
|
+
];
|
|
11
|
+
function allowedForPaused() {
|
|
12
|
+
return ["maintenance"];
|
|
13
|
+
}
|
|
14
|
+
function allowedForMaintenanceOnly() {
|
|
15
|
+
return ["work", "maintenance"];
|
|
16
|
+
}
|
|
17
|
+
function allowedForActiveWindow(windowId) {
|
|
18
|
+
if (windowId.includes("work")) {
|
|
19
|
+
return ["work", "exploration", "maintenance", "reflection", "outreach", "social", "quiet"];
|
|
20
|
+
}
|
|
21
|
+
if (windowId.includes("social")) {
|
|
22
|
+
return ["social", "exploration", "work", "maintenance", "reflection", "outreach", "quiet"];
|
|
23
|
+
}
|
|
24
|
+
if (windowId.includes("reflection")) {
|
|
25
|
+
return ["reflection", "work", "maintenance", "exploration", "social", "outreach", "quiet"];
|
|
26
|
+
}
|
|
27
|
+
return ALL_INTENT_KINDS;
|
|
28
|
+
}
|
|
29
|
+
function mergeQuietBias(decision, continuity, windowIsQuiet) {
|
|
30
|
+
return windowIsQuiet || decision.topLevelMode === "quiet" || continuity.mode === "quiet";
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Derive allowed intent kinds and quiet bias for candidate planning.
|
|
34
|
+
*/
|
|
35
|
+
export function buildPlannerRhythmWindow(now, continuity, policy) {
|
|
36
|
+
const decision = selectRhythmWindow(now, continuity, policy);
|
|
37
|
+
const window = policy.windows.find((w) => w.id === decision.windowId) ?? policy.windows[0];
|
|
38
|
+
const windowIsQuiet = window.mode === "quiet";
|
|
39
|
+
const quietBias = mergeQuietBias(decision, continuity, windowIsQuiet);
|
|
40
|
+
let allowed;
|
|
41
|
+
if (decision.topLevelMode === "paused_for_interrupt") {
|
|
42
|
+
allowed = allowedForPaused();
|
|
43
|
+
}
|
|
44
|
+
else if (decision.topLevelMode === "maintenance_only") {
|
|
45
|
+
allowed = allowedForMaintenanceOnly();
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
/** Calendar quiet sets `quietBias` only; candidate kinds stay window-biased (guards enforce quiet suppression). */
|
|
49
|
+
allowed = allowedForActiveWindow(window.id);
|
|
50
|
+
}
|
|
51
|
+
return { windowId: decision.windowId, allowedIntentKinds: allowed, quietBias };
|
|
52
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bridges state-system RhythmPolicySnapshot fields into control-plane RhythmPolicy (T2.1.2).
|
|
3
|
+
*
|
|
4
|
+
* State never emits window decisions or allowedIntentKinds; control-plane owns
|
|
5
|
+
* window geometry used by selectRhythmWindow(). Callers pass a pick of the DB read model.
|
|
6
|
+
*/
|
|
7
|
+
import type { RhythmPolicy } from "./rhythm-policy.js";
|
|
8
|
+
/** Subset of `RhythmPolicySnapshot` used for window derivation (no storage import from core). */
|
|
9
|
+
export interface RhythmPolicySnapshotBridgeInput {
|
|
10
|
+
quietEnabled: boolean;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Reject drifted snapshot shapes that smuggle control-plane decision fields.
|
|
14
|
+
*/
|
|
15
|
+
export declare function assertRhythmPolicySnapshotContract(snapshot: Record<string, unknown>): void;
|
|
16
|
+
/**
|
|
17
|
+
* Deterministic default windows from policy knobs (quiet hour tail when quietEnabled).
|
|
18
|
+
*/
|
|
19
|
+
export declare function rhythmPolicySnapshotToRhythmPolicy(snapshot: RhythmPolicySnapshotBridgeInput): RhythmPolicy;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
const FORBIDDEN_SNAPSHOT_KEYS = ["allowedIntentKinds", "windowDecision", "rhythmWindow"];
|
|
2
|
+
/**
|
|
3
|
+
* Reject drifted snapshot shapes that smuggle control-plane decision fields.
|
|
4
|
+
*/
|
|
5
|
+
export function assertRhythmPolicySnapshotContract(snapshot) {
|
|
6
|
+
for (const key of FORBIDDEN_SNAPSHOT_KEYS) {
|
|
7
|
+
if (key in snapshot) {
|
|
8
|
+
throw new Error(`rhythm_policy_snapshot_field_drift:${key}`);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Deterministic default windows from policy knobs (quiet hour tail when quietEnabled).
|
|
14
|
+
*/
|
|
15
|
+
export function rhythmPolicySnapshotToRhythmPolicy(snapshot) {
|
|
16
|
+
assertRhythmPolicySnapshotContract(snapshot);
|
|
17
|
+
if (snapshot.quietEnabled) {
|
|
18
|
+
return {
|
|
19
|
+
timezone: "UTC",
|
|
20
|
+
quietSuppressionEnabled: true,
|
|
21
|
+
windows: [
|
|
22
|
+
{ id: "w-work", startMinute: 0, endMinute: 480, mode: "active" },
|
|
23
|
+
{ id: "w-social", startMinute: 480, endMinute: 960, mode: "active" },
|
|
24
|
+
{ id: "w-reflection", startMinute: 960, endMinute: 1200, mode: "active" },
|
|
25
|
+
{ id: "w-quiet", startMinute: 1200, endMinute: 1440, mode: "quiet" },
|
|
26
|
+
],
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
return {
|
|
30
|
+
timezone: "UTC",
|
|
31
|
+
quietSuppressionEnabled: false,
|
|
32
|
+
windows: [{ id: "w-open", startMinute: 0, endMinute: 1440, mode: "active" }],
|
|
33
|
+
};
|
|
34
|
+
}
|
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
export type TopLevelMode = "active" | "quiet" | "maintenance_only" | "paused_for_interrupt";
|
|
2
|
-
|
|
2
|
+
/** Control-plane candidate kinds; includes `quiet` for quiet-window–biased intents (L0 alignment). */
|
|
3
|
+
export type IntentKind = "work" | "exploration" | "social" | "quiet" | "reflection" | "outreach" | "maintenance";
|
|
3
4
|
export type DecisionBasis = "rule_only" | "score_based" | "model_assisted";
|
|
4
5
|
export type GuardVerdict = "allow" | "defer" | "deny" | "escalate";
|
|
6
|
+
/** Minimal source ref for planner / guards (matches state-system `SourceRef` subset). */
|
|
7
|
+
export interface ControlPlaneSourceRef {
|
|
8
|
+
id: string;
|
|
9
|
+
kind: "platform_item" | "workspace_artifact" | "decision_record" | "user_anchor" | "connector_result" | "host_report" | "fallback_artifact";
|
|
10
|
+
uri: string;
|
|
11
|
+
excerptHash?: string;
|
|
12
|
+
observedAt?: string;
|
|
13
|
+
}
|
|
5
14
|
export interface ContinuitySnapshot {
|
|
6
15
|
mode: TopLevelMode;
|
|
7
16
|
currentWindowId: string;
|
|
@@ -19,6 +28,7 @@ export interface ContinuitySnapshot {
|
|
|
19
28
|
awaitingUserInput?: boolean;
|
|
20
29
|
riskSuppressed?: boolean;
|
|
21
30
|
}
|
|
31
|
+
export type CandidateEffectClass = "external_platform_action" | "connector_action" | "memory_curation" | "narrative_reflection" | "user_outreach" | "maintenance" | "no_effect";
|
|
22
32
|
export interface CandidateIntent {
|
|
23
33
|
id: string;
|
|
24
34
|
kind: IntentKind;
|
|
@@ -26,7 +36,11 @@ export interface CandidateIntent {
|
|
|
26
36
|
source: "tick" | "interrupt" | "obligation" | "quiet_plan";
|
|
27
37
|
platformId?: string;
|
|
28
38
|
summary: string;
|
|
29
|
-
effectClass:
|
|
39
|
+
effectClass: CandidateEffectClass;
|
|
40
|
+
/** Required for source-backed guard; may be empty when planner expects hard-guard deny. */
|
|
41
|
+
sourceRefs: ControlPlaneSourceRef[];
|
|
42
|
+
/** Dedupe / cooldown key; defaults to stable fingerprint in guard layer when omitted. */
|
|
43
|
+
idempotencyKey?: string;
|
|
30
44
|
}
|
|
31
45
|
export interface GuardEvaluation {
|
|
32
46
|
verdict: GuardVerdict;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deterministic GuidanceDraftPort implementation for contract tests and packaged runtime.
|
|
3
|
+
* Does not claim user-visible delivery when wordingMode is not_sent_fallback_candidate (T6.2.1 / ADR-004).
|
|
4
|
+
*/
|
|
5
|
+
import { type GuidanceDraftPort, type OutreachDraftRequest } from "./outreach-draft-schema.js";
|
|
6
|
+
export declare function draftOutreachMessage(request: OutreachDraftRequest): ReturnType<GuidanceDraftPort["draftOutreachMessage"]>;
|
|
7
|
+
export declare function createDraftOutreachMessagePort(): GuidanceDraftPort;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deterministic GuidanceDraftPort implementation for contract tests and packaged runtime.
|
|
3
|
+
* Does not claim user-visible delivery when wordingMode is not_sent_fallback_candidate (T6.2.1 / ADR-004).
|
|
4
|
+
*/
|
|
5
|
+
import { safeParseOutreachDraftRequest } from "./outreach-draft-schema.js";
|
|
6
|
+
function baseDraftText(request) {
|
|
7
|
+
const ids = request.sourceRefs.map((s) => s.id).join(",");
|
|
8
|
+
return `draft:${request.candidateId}:grounded:${ids}`;
|
|
9
|
+
}
|
|
10
|
+
export async function draftOutreachMessage(request) {
|
|
11
|
+
const parsed = safeParseOutreachDraftRequest(request);
|
|
12
|
+
if (!parsed.success) {
|
|
13
|
+
return { status: "unavailable", reasons: ["outreach_draft_schema_invalid"] };
|
|
14
|
+
}
|
|
15
|
+
const r = parsed.data;
|
|
16
|
+
if (r.judgmentVerdict !== "allow") {
|
|
17
|
+
return { status: "unavailable", reasons: ["hard_decision_not_allow"] };
|
|
18
|
+
}
|
|
19
|
+
if (r.sourceRefs.length === 0) {
|
|
20
|
+
return { status: "unavailable", reasons: ["missing_resolved_source_refs"] };
|
|
21
|
+
}
|
|
22
|
+
const wording = r.deliveryContext.wordingMode;
|
|
23
|
+
if (wording === "sendable") {
|
|
24
|
+
return {
|
|
25
|
+
status: "ready",
|
|
26
|
+
draft: {
|
|
27
|
+
text: `${baseDraftText(r)};wording=sendable`,
|
|
28
|
+
deliveryWording: "sendable",
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
return {
|
|
33
|
+
status: "ready",
|
|
34
|
+
draft: {
|
|
35
|
+
text: `Not sent to the user (candidate only). Delivery state: ${r.deliveryContext.deliveryVerdict}. ${baseDraftText(r)}`,
|
|
36
|
+
deliveryWording: "not_sent_fallback_candidate",
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
export function createDraftOutreachMessagePort() {
|
|
41
|
+
return { draftOutreachMessage };
|
|
42
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Evidence pack assembly, interest-basis selection, and Quiet narrative gate (T6.1.2 / ADR-004).
|
|
3
|
+
* Guidance does not own delivery or judgment; callers pass already-resolved refs.
|
|
4
|
+
*/
|
|
5
|
+
import type { GuidanceSourceRef } from "./outreach-draft-schema.js";
|
|
6
|
+
import type { UserInterestStaleness } from "../storage/user-interest/types.js";
|
|
7
|
+
import type { SourceCoverage } from "../storage/snapshots/types.js";
|
|
8
|
+
export type SourceCoveragePolicy = "strict" | "lenient";
|
|
9
|
+
export interface EvidencePack {
|
|
10
|
+
groundedRefs: GuidanceSourceRef[];
|
|
11
|
+
unresolvedIds: string[];
|
|
12
|
+
sensitiveBlocked: boolean;
|
|
13
|
+
policy: SourceCoveragePolicy;
|
|
14
|
+
}
|
|
15
|
+
export declare function buildEvidencePack(refs: GuidanceSourceRef[], opts?: {
|
|
16
|
+
policy?: SourceCoveragePolicy;
|
|
17
|
+
}): {
|
|
18
|
+
ok: true;
|
|
19
|
+
pack: EvidencePack;
|
|
20
|
+
} | {
|
|
21
|
+
ok: false;
|
|
22
|
+
reasons: string[];
|
|
23
|
+
};
|
|
24
|
+
export type InterestBasisMode = "evidence_only" | "interest_augmented" | "unavailable";
|
|
25
|
+
export declare function selectInterestBasis(input: {
|
|
26
|
+
staleness: UserInterestStaleness;
|
|
27
|
+
confidence: number;
|
|
28
|
+
signalCount: number;
|
|
29
|
+
}): InterestBasisMode;
|
|
30
|
+
export declare function buildQuietNarrativeGuidance(input: {
|
|
31
|
+
interestBasis: InterestBasisMode;
|
|
32
|
+
sourceCoverage: Pick<SourceCoverage, "coverageRatio" | "unsupportedClaims">;
|
|
33
|
+
outline: string[];
|
|
34
|
+
}): {
|
|
35
|
+
status: "ready";
|
|
36
|
+
hints: string[];
|
|
37
|
+
} | {
|
|
38
|
+
status: "unavailable";
|
|
39
|
+
reasons: string[];
|
|
40
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
export function buildEvidencePack(refs, opts) {
|
|
2
|
+
const policy = opts?.policy ?? "strict";
|
|
3
|
+
const unresolvedIds = [];
|
|
4
|
+
let sensitiveBlocked = false;
|
|
5
|
+
const grounded = [];
|
|
6
|
+
for (const r of refs) {
|
|
7
|
+
if (!r.uri?.trim()) {
|
|
8
|
+
unresolvedIds.push(r.id);
|
|
9
|
+
continue;
|
|
10
|
+
}
|
|
11
|
+
if (r.uri.includes("credential") || r.uri.includes("secret")) {
|
|
12
|
+
sensitiveBlocked = true;
|
|
13
|
+
continue;
|
|
14
|
+
}
|
|
15
|
+
grounded.push(r);
|
|
16
|
+
}
|
|
17
|
+
if (policy === "strict" && unresolvedIds.length > 0) {
|
|
18
|
+
return { ok: false, reasons: ["unresolved_source_refs", ...unresolvedIds.slice(0, 3)] };
|
|
19
|
+
}
|
|
20
|
+
return {
|
|
21
|
+
ok: true,
|
|
22
|
+
pack: {
|
|
23
|
+
groundedRefs: grounded,
|
|
24
|
+
unresolvedIds,
|
|
25
|
+
sensitiveBlocked,
|
|
26
|
+
policy,
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
export function selectInterestBasis(input) {
|
|
31
|
+
if (input.staleness === "insufficient" || input.confidence < 0.15) {
|
|
32
|
+
return input.signalCount > 0 ? "evidence_only" : "unavailable";
|
|
33
|
+
}
|
|
34
|
+
if (input.staleness === "stale") {
|
|
35
|
+
return "evidence_only";
|
|
36
|
+
}
|
|
37
|
+
return "interest_augmented";
|
|
38
|
+
}
|
|
39
|
+
export function buildQuietNarrativeGuidance(input) {
|
|
40
|
+
if (input.interestBasis === "unavailable" && input.sourceCoverage.coverageRatio < 0.25) {
|
|
41
|
+
return { status: "unavailable", reasons: ["quiet_guidance_insufficient_interest_and_coverage"] };
|
|
42
|
+
}
|
|
43
|
+
if (input.sourceCoverage.unsupportedClaims.length > 0) {
|
|
44
|
+
return { status: "unavailable", reasons: ["quiet_guidance_unsupported_claims"] };
|
|
45
|
+
}
|
|
46
|
+
const hints = [
|
|
47
|
+
...input.outline.slice(0, 3).map((line) => `hint:${line}`),
|
|
48
|
+
`basis:${input.interestBasis}`,
|
|
49
|
+
`coverage:${input.sourceCoverage.coverageRatio.toFixed(2)}`,
|
|
50
|
+
];
|
|
51
|
+
return { status: "ready", hints };
|
|
52
|
+
}
|
|
@@ -6,3 +6,6 @@ export * from "./fallback.js";
|
|
|
6
6
|
export * from "./template-registry.js";
|
|
7
7
|
export * from "./review-workflow.js";
|
|
8
8
|
export * from "./guidance-assembler.js";
|
|
9
|
+
export * from "./outreach-draft-schema.js";
|
|
10
|
+
export * from "./draft-outreach-message.js";
|
|
11
|
+
export * from "./evidence-guidance.js";
|
|
@@ -6,3 +6,6 @@ export * from "./fallback.js";
|
|
|
6
6
|
export * from "./template-registry.js";
|
|
7
7
|
export * from "./review-workflow.js";
|
|
8
8
|
export * from "./guidance-assembler.js";
|
|
9
|
+
export * from "./outreach-draft-schema.js";
|
|
10
|
+
export * from "./draft-outreach-message.js";
|
|
11
|
+
export * from "./evidence-guidance.js";
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Outreach draft request contracts + zod validation (T6.1.1 / behavioral-guidance-system v5).
|
|
3
|
+
*
|
|
4
|
+
* Core logic: strict schema for control-plane → guidance handoff; GuidanceDraftPort documents async seam.
|
|
5
|
+
* Test coverage: tests/unit/guidance/outreach-draft-schema.test.ts
|
|
6
|
+
*/
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
export declare const guidanceSourceRefSchema: z.ZodObject<{
|
|
9
|
+
id: z.ZodString;
|
|
10
|
+
kind: z.ZodEnum<{
|
|
11
|
+
platform_item: "platform_item";
|
|
12
|
+
workspace_artifact: "workspace_artifact";
|
|
13
|
+
decision_record: "decision_record";
|
|
14
|
+
user_anchor: "user_anchor";
|
|
15
|
+
connector_result: "connector_result";
|
|
16
|
+
host_report: "host_report";
|
|
17
|
+
fallback_artifact: "fallback_artifact";
|
|
18
|
+
}>;
|
|
19
|
+
uri: z.ZodString;
|
|
20
|
+
excerptHash: z.ZodOptional<z.ZodString>;
|
|
21
|
+
observedAt: z.ZodOptional<z.ZodString>;
|
|
22
|
+
}, z.core.$strip>;
|
|
23
|
+
export type GuidanceSourceRef = z.infer<typeof guidanceSourceRefSchema>;
|
|
24
|
+
export declare const deliveryExpressionContextSchema: z.ZodObject<{
|
|
25
|
+
deliveryVerdict: z.ZodEnum<{
|
|
26
|
+
target_none: "target_none";
|
|
27
|
+
channel_missing: "channel_missing";
|
|
28
|
+
host_unsupported: "host_unsupported";
|
|
29
|
+
delivery_failed: "delivery_failed";
|
|
30
|
+
target_available: "target_available";
|
|
31
|
+
}>;
|
|
32
|
+
fallbackRef: z.ZodOptional<z.ZodString>;
|
|
33
|
+
wordingMode: z.ZodEnum<{
|
|
34
|
+
sendable: "sendable";
|
|
35
|
+
not_sent_fallback_candidate: "not_sent_fallback_candidate";
|
|
36
|
+
}>;
|
|
37
|
+
}, z.core.$strip>;
|
|
38
|
+
export declare const sceneGuidanceRequestSchema: z.ZodObject<{
|
|
39
|
+
requestId: z.ZodString;
|
|
40
|
+
sceneType: z.ZodEnum<{
|
|
41
|
+
outreach: "outreach";
|
|
42
|
+
explain: "explain";
|
|
43
|
+
social: "social";
|
|
44
|
+
quiet_reflection: "quiet_reflection";
|
|
45
|
+
user_reply_continuity: "user_reply_continuity";
|
|
46
|
+
fallback_candidate: "fallback_candidate";
|
|
47
|
+
}>;
|
|
48
|
+
runtimeScope: z.ZodEnum<{
|
|
49
|
+
rhythm: "rhythm";
|
|
50
|
+
user_reply: "user_reply";
|
|
51
|
+
user_task: "user_task";
|
|
52
|
+
}>;
|
|
53
|
+
rhythmWindowKind: z.ZodOptional<z.ZodEnum<{
|
|
54
|
+
quiet: "quiet";
|
|
55
|
+
social: "social";
|
|
56
|
+
work: "work";
|
|
57
|
+
exploration: "exploration";
|
|
58
|
+
reflection: "reflection";
|
|
59
|
+
maintenance: "maintenance";
|
|
60
|
+
}>>;
|
|
61
|
+
riskLevel: z.ZodEnum<{
|
|
62
|
+
medium: "medium";
|
|
63
|
+
low: "low";
|
|
64
|
+
high: "high";
|
|
65
|
+
}>;
|
|
66
|
+
sourceRefs: z.ZodArray<z.ZodObject<{
|
|
67
|
+
id: z.ZodString;
|
|
68
|
+
kind: z.ZodEnum<{
|
|
69
|
+
platform_item: "platform_item";
|
|
70
|
+
workspace_artifact: "workspace_artifact";
|
|
71
|
+
decision_record: "decision_record";
|
|
72
|
+
user_anchor: "user_anchor";
|
|
73
|
+
connector_result: "connector_result";
|
|
74
|
+
host_report: "host_report";
|
|
75
|
+
fallback_artifact: "fallback_artifact";
|
|
76
|
+
}>;
|
|
77
|
+
uri: z.ZodString;
|
|
78
|
+
excerptHash: z.ZodOptional<z.ZodString>;
|
|
79
|
+
observedAt: z.ZodOptional<z.ZodString>;
|
|
80
|
+
}, z.core.$strip>>;
|
|
81
|
+
deliveryContext: z.ZodOptional<z.ZodObject<{
|
|
82
|
+
deliveryVerdict: z.ZodEnum<{
|
|
83
|
+
target_none: "target_none";
|
|
84
|
+
channel_missing: "channel_missing";
|
|
85
|
+
host_unsupported: "host_unsupported";
|
|
86
|
+
delivery_failed: "delivery_failed";
|
|
87
|
+
target_available: "target_available";
|
|
88
|
+
}>;
|
|
89
|
+
fallbackRef: z.ZodOptional<z.ZodString>;
|
|
90
|
+
wordingMode: z.ZodEnum<{
|
|
91
|
+
sendable: "sendable";
|
|
92
|
+
not_sent_fallback_candidate: "not_sent_fallback_candidate";
|
|
93
|
+
}>;
|
|
94
|
+
}, z.core.$strip>>;
|
|
95
|
+
language: z.ZodOptional<z.ZodEnum<{
|
|
96
|
+
"en-US": "en-US";
|
|
97
|
+
"zh-CN": "zh-CN";
|
|
98
|
+
}>>;
|
|
99
|
+
}, z.core.$strip>;
|
|
100
|
+
export declare const outreachDraftRequestSchema: z.ZodObject<{
|
|
101
|
+
requestId: z.ZodString;
|
|
102
|
+
runtimeScope: z.ZodEnum<{
|
|
103
|
+
rhythm: "rhythm";
|
|
104
|
+
user_reply: "user_reply";
|
|
105
|
+
user_task: "user_task";
|
|
106
|
+
}>;
|
|
107
|
+
rhythmWindowKind: z.ZodOptional<z.ZodEnum<{
|
|
108
|
+
quiet: "quiet";
|
|
109
|
+
social: "social";
|
|
110
|
+
work: "work";
|
|
111
|
+
exploration: "exploration";
|
|
112
|
+
reflection: "reflection";
|
|
113
|
+
maintenance: "maintenance";
|
|
114
|
+
}>>;
|
|
115
|
+
riskLevel: z.ZodEnum<{
|
|
116
|
+
medium: "medium";
|
|
117
|
+
low: "low";
|
|
118
|
+
high: "high";
|
|
119
|
+
}>;
|
|
120
|
+
sourceRefs: z.ZodArray<z.ZodObject<{
|
|
121
|
+
id: z.ZodString;
|
|
122
|
+
kind: z.ZodEnum<{
|
|
123
|
+
platform_item: "platform_item";
|
|
124
|
+
workspace_artifact: "workspace_artifact";
|
|
125
|
+
decision_record: "decision_record";
|
|
126
|
+
user_anchor: "user_anchor";
|
|
127
|
+
connector_result: "connector_result";
|
|
128
|
+
host_report: "host_report";
|
|
129
|
+
fallback_artifact: "fallback_artifact";
|
|
130
|
+
}>;
|
|
131
|
+
uri: z.ZodString;
|
|
132
|
+
excerptHash: z.ZodOptional<z.ZodString>;
|
|
133
|
+
observedAt: z.ZodOptional<z.ZodString>;
|
|
134
|
+
}, z.core.$strip>>;
|
|
135
|
+
deliveryContext: z.ZodOptional<z.ZodObject<{
|
|
136
|
+
deliveryVerdict: z.ZodEnum<{
|
|
137
|
+
target_none: "target_none";
|
|
138
|
+
channel_missing: "channel_missing";
|
|
139
|
+
host_unsupported: "host_unsupported";
|
|
140
|
+
delivery_failed: "delivery_failed";
|
|
141
|
+
target_available: "target_available";
|
|
142
|
+
}>;
|
|
143
|
+
fallbackRef: z.ZodOptional<z.ZodString>;
|
|
144
|
+
wordingMode: z.ZodEnum<{
|
|
145
|
+
sendable: "sendable";
|
|
146
|
+
not_sent_fallback_candidate: "not_sent_fallback_candidate";
|
|
147
|
+
}>;
|
|
148
|
+
}, z.core.$strip>>;
|
|
149
|
+
language: z.ZodOptional<z.ZodEnum<{
|
|
150
|
+
"en-US": "en-US";
|
|
151
|
+
"zh-CN": "zh-CN";
|
|
152
|
+
}>>;
|
|
153
|
+
sceneType: z.ZodEnum<{
|
|
154
|
+
outreach: "outreach";
|
|
155
|
+
fallback_candidate: "fallback_candidate";
|
|
156
|
+
}>;
|
|
157
|
+
decisionId: z.ZodString;
|
|
158
|
+
candidateId: z.ZodString;
|
|
159
|
+
judgmentVerdict: z.ZodEnum<{
|
|
160
|
+
allow: "allow";
|
|
161
|
+
deny: "deny";
|
|
162
|
+
defer: "defer";
|
|
163
|
+
}>;
|
|
164
|
+
valueScore: z.ZodNumber;
|
|
165
|
+
interestRefs: z.ZodArray<z.ZodObject<{
|
|
166
|
+
id: z.ZodString;
|
|
167
|
+
kind: z.ZodEnum<{
|
|
168
|
+
platform_item: "platform_item";
|
|
169
|
+
workspace_artifact: "workspace_artifact";
|
|
170
|
+
decision_record: "decision_record";
|
|
171
|
+
user_anchor: "user_anchor";
|
|
172
|
+
connector_result: "connector_result";
|
|
173
|
+
host_report: "host_report";
|
|
174
|
+
fallback_artifact: "fallback_artifact";
|
|
175
|
+
}>;
|
|
176
|
+
uri: z.ZodString;
|
|
177
|
+
excerptHash: z.ZodOptional<z.ZodString>;
|
|
178
|
+
observedAt: z.ZodOptional<z.ZodString>;
|
|
179
|
+
}, z.core.$strip>>;
|
|
180
|
+
}, z.core.$strip>;
|
|
181
|
+
export type SceneGuidanceRequest = z.infer<typeof sceneGuidanceRequestSchema>;
|
|
182
|
+
export type OutreachDraftRequest = z.infer<typeof outreachDraftRequestSchema>;
|
|
183
|
+
export type DeliveryExpressionContext = z.infer<typeof deliveryExpressionContextSchema>;
|
|
184
|
+
export declare function parseOutreachDraftRequest(input: unknown): OutreachDraftRequest;
|
|
185
|
+
export declare function safeParseOutreachDraftRequest(input: unknown): z.ZodSafeParseResult<{
|
|
186
|
+
requestId: string;
|
|
187
|
+
runtimeScope: "rhythm" | "user_reply" | "user_task";
|
|
188
|
+
riskLevel: "medium" | "low" | "high";
|
|
189
|
+
sourceRefs: {
|
|
190
|
+
id: string;
|
|
191
|
+
kind: "platform_item" | "workspace_artifact" | "decision_record" | "user_anchor" | "connector_result" | "host_report" | "fallback_artifact";
|
|
192
|
+
uri: string;
|
|
193
|
+
excerptHash?: string | undefined;
|
|
194
|
+
observedAt?: string | undefined;
|
|
195
|
+
}[];
|
|
196
|
+
sceneType: "outreach" | "fallback_candidate";
|
|
197
|
+
decisionId: string;
|
|
198
|
+
candidateId: string;
|
|
199
|
+
judgmentVerdict: "allow" | "deny" | "defer";
|
|
200
|
+
valueScore: number;
|
|
201
|
+
interestRefs: {
|
|
202
|
+
id: string;
|
|
203
|
+
kind: "platform_item" | "workspace_artifact" | "decision_record" | "user_anchor" | "connector_result" | "host_report" | "fallback_artifact";
|
|
204
|
+
uri: string;
|
|
205
|
+
excerptHash?: string | undefined;
|
|
206
|
+
observedAt?: string | undefined;
|
|
207
|
+
}[];
|
|
208
|
+
rhythmWindowKind?: "quiet" | "social" | "work" | "exploration" | "reflection" | "maintenance" | undefined;
|
|
209
|
+
deliveryContext?: {
|
|
210
|
+
deliveryVerdict: "target_none" | "channel_missing" | "host_unsupported" | "delivery_failed" | "target_available";
|
|
211
|
+
wordingMode: "sendable" | "not_sent_fallback_candidate";
|
|
212
|
+
fallbackRef?: string | undefined;
|
|
213
|
+
} | undefined;
|
|
214
|
+
language?: "en-US" | "zh-CN" | undefined;
|
|
215
|
+
}>;
|
|
216
|
+
/** Async seam for generative outreach copy (implementation lives outside control-plane). */
|
|
217
|
+
export interface GuidanceDraftPort {
|
|
218
|
+
draftOutreachMessage(request: OutreachDraftRequest): Promise<{
|
|
219
|
+
status: "ready";
|
|
220
|
+
draft: {
|
|
221
|
+
text: string;
|
|
222
|
+
deliveryWording: DeliveryExpressionContext["wordingMode"];
|
|
223
|
+
};
|
|
224
|
+
} | {
|
|
225
|
+
status: "unavailable";
|
|
226
|
+
reasons: string[];
|
|
227
|
+
}>;
|
|
228
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Outreach draft request contracts + zod validation (T6.1.1 / behavioral-guidance-system v5).
|
|
3
|
+
*
|
|
4
|
+
* Core logic: strict schema for control-plane → guidance handoff; GuidanceDraftPort documents async seam.
|
|
5
|
+
* Test coverage: tests/unit/guidance/outreach-draft-schema.test.ts
|
|
6
|
+
*/
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
const sourceRefKindSchema = z.enum([
|
|
9
|
+
"platform_item",
|
|
10
|
+
"workspace_artifact",
|
|
11
|
+
"decision_record",
|
|
12
|
+
"user_anchor",
|
|
13
|
+
"connector_result",
|
|
14
|
+
"host_report",
|
|
15
|
+
"fallback_artifact",
|
|
16
|
+
]);
|
|
17
|
+
export const guidanceSourceRefSchema = z.object({
|
|
18
|
+
id: z.string().min(1),
|
|
19
|
+
kind: sourceRefKindSchema,
|
|
20
|
+
uri: z.string().min(1),
|
|
21
|
+
excerptHash: z.string().optional(),
|
|
22
|
+
observedAt: z.string().optional(),
|
|
23
|
+
});
|
|
24
|
+
export const deliveryExpressionContextSchema = z.object({
|
|
25
|
+
deliveryVerdict: z.enum([
|
|
26
|
+
"target_available",
|
|
27
|
+
"target_none",
|
|
28
|
+
"channel_missing",
|
|
29
|
+
"host_unsupported",
|
|
30
|
+
"delivery_failed",
|
|
31
|
+
]),
|
|
32
|
+
fallbackRef: z.string().optional(),
|
|
33
|
+
wordingMode: z.enum(["sendable", "not_sent_fallback_candidate"]),
|
|
34
|
+
});
|
|
35
|
+
const guidanceSceneTypeSchema = z.enum([
|
|
36
|
+
"outreach",
|
|
37
|
+
"quiet_reflection",
|
|
38
|
+
"social",
|
|
39
|
+
"explain",
|
|
40
|
+
"user_reply_continuity",
|
|
41
|
+
"fallback_candidate",
|
|
42
|
+
]);
|
|
43
|
+
const runtimeScopeSchema = z.enum(["rhythm", "user_task", "user_reply"]);
|
|
44
|
+
const riskLevelSchema = z.enum(["low", "medium", "high"]);
|
|
45
|
+
export const sceneGuidanceRequestSchema = z.object({
|
|
46
|
+
requestId: z.string().min(1),
|
|
47
|
+
sceneType: guidanceSceneTypeSchema,
|
|
48
|
+
runtimeScope: runtimeScopeSchema,
|
|
49
|
+
rhythmWindowKind: z
|
|
50
|
+
.enum(["work", "exploration", "social", "quiet", "reflection", "maintenance"])
|
|
51
|
+
.optional(),
|
|
52
|
+
riskLevel: riskLevelSchema,
|
|
53
|
+
sourceRefs: z.array(guidanceSourceRefSchema),
|
|
54
|
+
deliveryContext: deliveryExpressionContextSchema.optional(),
|
|
55
|
+
language: z.enum(["zh-CN", "en-US"]).optional(),
|
|
56
|
+
});
|
|
57
|
+
export const outreachDraftRequestSchema = sceneGuidanceRequestSchema
|
|
58
|
+
.extend({
|
|
59
|
+
sceneType: z.enum(["outreach", "fallback_candidate"]),
|
|
60
|
+
decisionId: z.string().min(1),
|
|
61
|
+
candidateId: z.string().min(1),
|
|
62
|
+
judgmentVerdict: z.enum(["allow", "deny", "defer"]),
|
|
63
|
+
valueScore: z.number(),
|
|
64
|
+
interestRefs: z.array(guidanceSourceRefSchema),
|
|
65
|
+
})
|
|
66
|
+
.superRefine((val, ctx) => {
|
|
67
|
+
if (!val.deliveryContext) {
|
|
68
|
+
ctx.addIssue({
|
|
69
|
+
code: "custom",
|
|
70
|
+
message: "outreach_draft_requires_delivery_context",
|
|
71
|
+
path: ["deliveryContext"],
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
export function parseOutreachDraftRequest(input) {
|
|
76
|
+
return outreachDraftRequestSchema.parse(input);
|
|
77
|
+
}
|
|
78
|
+
export function safeParseOutreachDraftRequest(input) {
|
|
79
|
+
return outreachDraftRequestSchema.safeParse(input);
|
|
80
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory append-only audit store with hash-chain verification hooks.
|
|
3
|
+
*
|
|
4
|
+
* Core logic: reject broken previousHash links; expose ordered envelopes for tests / local tooling.
|
|
5
|
+
*
|
|
6
|
+
* Test coverage: tests/unit/observability/audit-envelope.test.ts
|
|
7
|
+
*/
|
|
8
|
+
import type { AuditEnvelope } from "./audit-envelope.js";
|
|
9
|
+
export declare class AppendOnlyAuditStore {
|
|
10
|
+
private readonly events;
|
|
11
|
+
append<T>(envelope: AuditEnvelope<T>): void;
|
|
12
|
+
list(): readonly AuditEnvelope<unknown>[];
|
|
13
|
+
lastRecordHash(): string | undefined;
|
|
14
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export class AppendOnlyAuditStore {
|
|
2
|
+
events = [];
|
|
3
|
+
append(envelope) {
|
|
4
|
+
const last = this.events[this.events.length - 1];
|
|
5
|
+
if (last) {
|
|
6
|
+
if (envelope.integrity.previousHash !== last.integrity.recordHash) {
|
|
7
|
+
throw new Error("audit_previous_hash_mismatch");
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
else if (envelope.integrity.previousHash !== undefined) {
|
|
11
|
+
throw new Error("audit_genesis_previous_hash");
|
|
12
|
+
}
|
|
13
|
+
this.events.push(envelope);
|
|
14
|
+
}
|
|
15
|
+
list() {
|
|
16
|
+
return this.events;
|
|
17
|
+
}
|
|
18
|
+
lastRecordHash() {
|
|
19
|
+
return this.events[this.events.length - 1]?.integrity.recordHash;
|
|
20
|
+
}
|
|
21
|
+
}
|