@haaaiawd/second-nature 0.1.26 → 0.1.29
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/SKILL.md +35 -0
- package/agent-inner-guide.md +144 -0
- package/index.js +280 -2
- package/openclaw.plugin.json +2 -2
- package/package.json +4 -1
- package/runtime/cli/commands/connector-behavior.d.ts +20 -0
- package/runtime/cli/commands/connector-behavior.js +160 -0
- package/runtime/cli/commands/index.js +8 -0
- package/runtime/cli/index.js +9 -2
- package/runtime/cli/ops/manual-run-dispatcher.d.ts +79 -0
- package/runtime/cli/ops/manual-run-dispatcher.js +110 -0
- package/runtime/cli/ops/ops-router.d.ts +45 -4
- package/runtime/cli/ops/ops-router.js +543 -2
- package/runtime/cli/read-models/index.js +35 -18
- package/runtime/cli/read-models/types.d.ts +1 -0
- package/runtime/connectors/agent-network/agent-world/adapter.d.ts +1 -0
- package/runtime/connectors/agent-network/agent-world/adapter.js +2 -2
- package/runtime/connectors/base/contract.d.ts +4 -1
- package/runtime/connectors/base/contract.js +5 -1
- package/runtime/connectors/base/effect-commit-ledger-sqlite.d.ts +31 -0
- package/runtime/connectors/base/effect-commit-ledger-sqlite.js +86 -0
- package/runtime/connectors/base/failure-taxonomy.js +5 -0
- package/runtime/connectors/base/manifest-v7.d.ts +151 -0
- package/runtime/connectors/base/manifest-v7.js +170 -0
- package/runtime/connectors/base/manifest.d.ts +3 -13
- package/runtime/connectors/base/manifest.js +7 -7
- package/runtime/connectors/base/route-planner.js +11 -8
- package/runtime/connectors/base/structured-unavailable-reason.d.ts +59 -0
- package/runtime/connectors/base/structured-unavailable-reason.js +113 -0
- package/runtime/connectors/base/wet-probe-runner.d.ts +40 -0
- package/runtime/connectors/base/wet-probe-runner.js +132 -0
- package/runtime/connectors/manifest/manifest-schema.d.ts +4 -0
- package/runtime/connectors/manifest/manifest-schema.js +2 -0
- package/runtime/connectors/services/connector-executor-adapter.d.ts +1 -0
- package/runtime/connectors/services/connector-executor-adapter.js +132 -26
- package/runtime/core/second-nature/body/behavior-promotion/behavior-promotion-loop.d.ts +45 -0
- package/runtime/core/second-nature/body/behavior-promotion/behavior-promotion-loop.js +132 -0
- package/runtime/core/second-nature/body/circuit-breaker/circuit-breaker-manager.d.ts +60 -0
- package/runtime/core/second-nature/body/circuit-breaker/circuit-breaker-manager.js +174 -0
- package/runtime/core/second-nature/body/probe-signal-adapter.d.ts +38 -0
- package/runtime/core/second-nature/body/probe-signal-adapter.js +60 -0
- package/runtime/core/second-nature/body/tool-affordance/affordance-assembler.d.ts +51 -0
- package/runtime/core/second-nature/body/tool-affordance/affordance-assembler.js +129 -0
- package/runtime/core/second-nature/body/tool-affordance/affordance-context-scope.d.ts +30 -0
- package/runtime/core/second-nature/body/tool-affordance/affordance-context-scope.js +92 -0
- package/runtime/core/second-nature/body/tool-experience/experience-writer.d.ts +34 -0
- package/runtime/core/second-nature/body/tool-experience/experience-writer.js +67 -0
- package/runtime/core/second-nature/body/tool-experience/pain-signal-query.d.ts +37 -0
- package/runtime/core/second-nature/body/tool-experience/pain-signal-query.js +62 -0
- package/runtime/core/second-nature/heartbeat/decision-trace-emitter.d.ts +29 -0
- package/runtime/core/second-nature/heartbeat/decision-trace-emitter.js +28 -0
- package/runtime/core/second-nature/heartbeat/embodied-context-assembler.d.ts +54 -0
- package/runtime/core/second-nature/heartbeat/embodied-context-assembler.js +164 -0
- package/runtime/core/second-nature/heartbeat/goal-lifecycle-policy.d.ts +37 -0
- package/runtime/core/second-nature/heartbeat/goal-lifecycle-policy.js +61 -0
- package/runtime/core/second-nature/heartbeat/idle-curiosity-policy.d.ts +37 -0
- package/runtime/core/second-nature/heartbeat/idle-curiosity-policy.js +60 -0
- package/runtime/core/second-nature/heartbeat/index.d.ts +4 -0
- package/runtime/core/second-nature/heartbeat/index.js +5 -0
- package/runtime/core/second-nature/heartbeat/run-heartbeat-cycle-v7.d.ts +63 -0
- package/runtime/core/second-nature/heartbeat/run-heartbeat-cycle-v7.js +118 -0
- package/runtime/core/second-nature/orchestrator/downstream-intent-orchestrator.d.ts +41 -0
- package/runtime/core/second-nature/orchestrator/downstream-intent-orchestrator.js +43 -0
- package/runtime/core/second-nature/orchestrator/effect-dispatcher.d.ts +2 -1
- package/runtime/core/second-nature/orchestrator/effect-dispatcher.js +2 -0
- package/runtime/core/second-nature/orchestrator/hard-guard-evaluator.d.ts +31 -0
- package/runtime/core/second-nature/orchestrator/hard-guard-evaluator.js +102 -0
- package/runtime/core/second-nature/orchestrator/index.d.ts +5 -0
- package/runtime/core/second-nature/orchestrator/index.js +7 -0
- package/runtime/core/second-nature/quiet/claim-synthesizer.d.ts +53 -0
- package/runtime/core/second-nature/quiet/claim-synthesizer.js +153 -0
- package/runtime/core/second-nature/quiet/daily-diary-writer.d.ts +29 -0
- package/runtime/core/second-nature/quiet/daily-diary-writer.js +92 -0
- package/runtime/core/second-nature/quiet/index.d.ts +5 -0
- package/runtime/core/second-nature/quiet/index.js +5 -0
- package/runtime/core/second-nature/quiet/run-source-backed-quiet.js +19 -12
- package/runtime/core/second-nature/types.d.ts +2 -0
- package/runtime/guidance/channel-feedback-ingestion-service.d.ts +88 -0
- package/runtime/guidance/channel-feedback-ingestion-service.js +231 -0
- package/runtime/guidance/guidance-draft-service.d.ts +60 -0
- package/runtime/guidance/guidance-draft-service.js +80 -0
- package/runtime/guidance/index.d.ts +3 -0
- package/runtime/guidance/index.js +3 -0
- package/runtime/guidance/outreach-draft-schema.d.ts +8 -8
- package/runtime/guidance/outreach-strategy-selector.d.ts +77 -0
- package/runtime/guidance/outreach-strategy-selector.js +211 -0
- package/runtime/observability/audit/append-only-audit-store.d.ts +20 -2
- package/runtime/observability/audit/append-only-audit-store.js +32 -6
- package/runtime/observability/audit/audit-envelope.d.ts +2 -1
- package/runtime/observability/audit/audit-envelope.js +8 -7
- package/runtime/observability/audit/audit-family-registry.json +66 -0
- package/runtime/observability/audit/family-registry.d.ts +43 -0
- package/runtime/observability/audit/family-registry.js +70 -0
- package/runtime/observability/index.d.ts +6 -1
- package/runtime/observability/index.js +6 -1
- package/runtime/observability/redaction/policy.d.ts +24 -3
- package/runtime/observability/redaction/policy.js +74 -0
- package/runtime/observability/services/heartbeat-digest-assembler.d.ts +152 -0
- package/runtime/observability/services/heartbeat-digest-assembler.js +248 -0
- package/runtime/observability/services/lived-experience-audit.js +6 -6
- package/runtime/observability/services/narrative-timeline-query-service.d.ts +136 -0
- package/runtime/observability/services/narrative-timeline-query-service.js +169 -0
- package/runtime/observability/services/restore-audit-service.d.ts +74 -0
- package/runtime/observability/services/restore-audit-service.js +79 -0
- package/runtime/observability/services/runtime-secret-anchor-view.d.ts +77 -0
- package/runtime/observability/services/runtime-secret-anchor-view.js +168 -0
- package/runtime/observability/services/self-health-snapshot.d.ts +92 -0
- package/runtime/observability/services/self-health-snapshot.js +251 -0
- package/runtime/shared/types/goal.d.ts +62 -0
- package/runtime/shared/types/goal.js +20 -0
- package/runtime/shared/types/index.d.ts +3 -0
- package/runtime/shared/types/index.js +3 -0
- package/runtime/shared/types/source-ref.d.ts +14 -0
- package/runtime/shared/types/source-ref.js +1 -0
- package/runtime/shared/types/v7-entities.d.ts +206 -0
- package/runtime/shared/types/v7-entities.js +27 -0
- package/runtime/storage/db/index.js +3 -0
- package/runtime/storage/db/migration-runner.d.ts +30 -0
- package/runtime/storage/db/migration-runner.js +93 -0
- package/runtime/storage/db/migrations/index.d.ts +5 -0
- package/runtime/storage/db/migrations/index.js +13 -0
- package/runtime/storage/db/migrations/v7-001-foundation.d.ts +13 -0
- package/runtime/storage/db/migrations/v7-001-foundation.js +144 -0
- package/runtime/storage/db/migrations/v7-002-effect-commit-ledger.d.ts +8 -0
- package/runtime/storage/db/migrations/v7-002-effect-commit-ledger.js +27 -0
- package/runtime/storage/db/migrations/v7-003-circuit-breaker.d.ts +7 -0
- package/runtime/storage/db/migrations/v7-003-circuit-breaker.js +26 -0
- package/runtime/storage/db/migrations/v7-004-behavior-promotion.d.ts +7 -0
- package/runtime/storage/db/migrations/v7-004-behavior-promotion.js +26 -0
- package/runtime/storage/db/schema/agent-goal.d.ts +38 -0
- package/runtime/storage/db/schema/agent-goal.js +2 -0
- package/runtime/storage/db/transaction-utils.d.ts +14 -0
- package/runtime/storage/db/transaction-utils.js +29 -0
- package/runtime/storage/db/write-queue.d.ts +38 -0
- package/runtime/storage/db/write-queue.js +97 -0
- package/runtime/storage/quiet/persist-quiet-artifact.js +2 -1
- package/runtime/storage/services/credential-vault.js +31 -17
- package/runtime/storage/services/diary-dream-store.d.ts +35 -0
- package/runtime/storage/services/diary-dream-store.js +165 -0
- package/runtime/storage/services/embodied-context-state-port.d.ts +77 -0
- package/runtime/storage/services/embodied-context-state-port.js +115 -0
- package/runtime/storage/services/goal-lifecycle-store.d.ts +42 -0
- package/runtime/storage/services/goal-lifecycle-store.js +181 -0
- package/runtime/storage/services/history-digest-store.d.ts +33 -0
- package/runtime/storage/services/history-digest-store.js +140 -0
- package/runtime/storage/services/identity-profile-store.d.ts +25 -0
- package/runtime/storage/services/identity-profile-store.js +81 -0
- package/runtime/storage/services/interaction-snapshot-projector.d.ts +15 -0
- package/runtime/storage/services/interaction-snapshot-projector.js +35 -0
- package/runtime/storage/services/restore-snapshot-store.d.ts +52 -0
- package/runtime/storage/services/restore-snapshot-store.js +193 -0
- package/runtime/storage/services/runtime-secret-anchor-store.d.ts +26 -0
- package/runtime/storage/services/runtime-secret-anchor-store.js +82 -0
- package/runtime/storage/services/tool-experience-store.d.ts +25 -0
- package/runtime/storage/services/tool-experience-store.js +116 -0
- package/runtime/storage/services/write-validation-gate.d.ts +46 -0
- package/runtime/storage/services/write-validation-gate.js +200 -0
- package/workspace-ops-bridge.js +16 -1
|
@@ -12,6 +12,8 @@ function isConnectorEffect(effectClass) {
|
|
|
12
12
|
effectClass === "connector_action");
|
|
13
13
|
}
|
|
14
14
|
export function toCapabilityIntent(intent) {
|
|
15
|
+
if (intent.capabilityIntent?.trim())
|
|
16
|
+
return intent.capabilityIntent.trim();
|
|
15
17
|
if (intent.kind === "work")
|
|
16
18
|
return "work.discover";
|
|
17
19
|
if (intent.kind === "exploration")
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HardGuardEvaluator — T-CP.C.2
|
|
3
|
+
*
|
|
4
|
+
* Core logic: Applies source, affordance, circuit-breaker, dedupe, cooldown,
|
|
5
|
+
* quiet, budget, risk, and privacy guards to candidate intents.
|
|
6
|
+
*
|
|
7
|
+
* v7 extensions over guard-layer.ts:
|
|
8
|
+
* - Affordance guard: connector_action intents must map to a healthy affordance
|
|
9
|
+
* item (safe/exploratory/needs_auth). painful → connector_circuit_open defer;
|
|
10
|
+
* unavailable → affordance_unavailable defer.
|
|
11
|
+
* - Source refs guard remains mandatory for all non-maintenance intents.
|
|
12
|
+
*
|
|
13
|
+
* Boundary:
|
|
14
|
+
* - Consumes AffordanceMap from EmbodiedContext; does NOT read breaker state
|
|
15
|
+
* directly (DR-002: breaker posture flows through affordance).
|
|
16
|
+
* - Guard result is final for control-plane.
|
|
17
|
+
*
|
|
18
|
+
* Test coverage: tests/unit/control-plane/hard-guard-evaluator.test.ts
|
|
19
|
+
*/
|
|
20
|
+
import type { CandidateIntent, GuardEvaluation } from "../types.js";
|
|
21
|
+
import type { AffordanceMap } from "../../../shared/types/v7-entities.js";
|
|
22
|
+
export interface HardGuardEvaluatorDeps {
|
|
23
|
+
hasDuplicateIntent: (idempotencyKey: string) => boolean;
|
|
24
|
+
isOutreachCooldownClear: (idempotencyKey: string) => boolean;
|
|
25
|
+
affordanceMap?: AffordanceMap;
|
|
26
|
+
quietBias?: boolean;
|
|
27
|
+
budgetExceeded?: boolean;
|
|
28
|
+
awaitingUser?: boolean;
|
|
29
|
+
riskSuppressed?: boolean;
|
|
30
|
+
}
|
|
31
|
+
export declare function evaluateHardGuards(intent: CandidateIntent, deps: HardGuardEvaluatorDeps): GuardEvaluation;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
const QUIET_DENY_KINDS = ["outreach", "social"];
|
|
2
|
+
export function evaluateHardGuards(intent, deps) {
|
|
3
|
+
const reasons = [];
|
|
4
|
+
// 1. Source refs guard
|
|
5
|
+
if (intent.sourceRefs.length === 0 &&
|
|
6
|
+
intent.effectClass !== "maintenance" &&
|
|
7
|
+
intent.effectClass !== "no_effect") {
|
|
8
|
+
reasons.push("missing_source_refs");
|
|
9
|
+
}
|
|
10
|
+
// 2. Affordance / breaker guard (v7)
|
|
11
|
+
if ((intent.effectClass === "connector_action" ||
|
|
12
|
+
intent.effectClass === "external_platform_action") &&
|
|
13
|
+
deps.affordanceMap &&
|
|
14
|
+
intent.platformId) {
|
|
15
|
+
const platformItems = deps.affordanceMap[intent.platformId] ?? [];
|
|
16
|
+
const match = intent.capabilityIntent
|
|
17
|
+
? platformItems.find((i) => i.capabilityId === intent.capabilityIntent)
|
|
18
|
+
: platformItems.find((i) => i.intent === intent.summary);
|
|
19
|
+
if (match) {
|
|
20
|
+
if (match.status === "painful") {
|
|
21
|
+
reasons.push("connector_circuit_open");
|
|
22
|
+
}
|
|
23
|
+
else if (match.status === "unavailable") {
|
|
24
|
+
reasons.push("affordance_unavailable");
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
reasons.push("affordance_unavailable");
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
// 3. Dedupe guard
|
|
32
|
+
const key = intent.idempotencyKey ?? `${intent.kind}:${intent.summary}`;
|
|
33
|
+
if (deps.hasDuplicateIntent(key)) {
|
|
34
|
+
reasons.push("duplicate_intent");
|
|
35
|
+
}
|
|
36
|
+
// 4. Outreach cooldown
|
|
37
|
+
if (intent.effectClass === "user_outreach" &&
|
|
38
|
+
!deps.isOutreachCooldownClear(key)) {
|
|
39
|
+
reasons.push("outreach_cooldown");
|
|
40
|
+
}
|
|
41
|
+
// 5. Quiet suppression
|
|
42
|
+
if (deps.quietBias) {
|
|
43
|
+
if (QUIET_DENY_KINDS.includes(intent.kind)) {
|
|
44
|
+
reasons.push("quiet_window_suppression");
|
|
45
|
+
}
|
|
46
|
+
if (intent.effectClass === "connector_action" ||
|
|
47
|
+
intent.effectClass === "external_platform_action") {
|
|
48
|
+
reasons.push("quiet_window_suppression");
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
// 6. Budget
|
|
52
|
+
if (deps.budgetExceeded) {
|
|
53
|
+
reasons.push("budget_exceeded");
|
|
54
|
+
}
|
|
55
|
+
// 7. Awaiting user
|
|
56
|
+
if (deps.awaitingUser) {
|
|
57
|
+
reasons.push("awaiting_user");
|
|
58
|
+
}
|
|
59
|
+
// 8. Risk
|
|
60
|
+
if (deps.riskSuppressed) {
|
|
61
|
+
if (intent.kind === "exploration" ||
|
|
62
|
+
intent.kind === "social" ||
|
|
63
|
+
intent.kind === "outreach") {
|
|
64
|
+
reasons.push("risk_suppressed");
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
// Verdict resolution
|
|
68
|
+
if (reasons.length === 0) {
|
|
69
|
+
return {
|
|
70
|
+
verdict: "allow",
|
|
71
|
+
reasons: [],
|
|
72
|
+
quietSuppressed: false,
|
|
73
|
+
leaseRequired: intent.effectClass === "external_platform_action" ||
|
|
74
|
+
intent.effectClass === "connector_action" ||
|
|
75
|
+
intent.effectClass === "user_outreach",
|
|
76
|
+
requiresCheckpoint: intent.effectClass !== "maintenance" &&
|
|
77
|
+
intent.effectClass !== "no_effect",
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
const deferOnlyReasons = reasons.every((r) => r === "duplicate_intent" ||
|
|
81
|
+
r === "outreach_cooldown" ||
|
|
82
|
+
r === "connector_circuit_open" ||
|
|
83
|
+
r === "quiet_window_suppression" ||
|
|
84
|
+
r === "affordance_unavailable");
|
|
85
|
+
if (deferOnlyReasons) {
|
|
86
|
+
return {
|
|
87
|
+
verdict: "defer",
|
|
88
|
+
reasons,
|
|
89
|
+
quietSuppressed: reasons.includes("quiet_window_suppression"),
|
|
90
|
+
leaseRequired: false,
|
|
91
|
+
requiresCheckpoint: false,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
const escalated = reasons.includes("awaiting_user") && intent.kind === "outreach";
|
|
95
|
+
return {
|
|
96
|
+
verdict: escalated ? "escalate" : "deny",
|
|
97
|
+
reasons,
|
|
98
|
+
quietSuppressed: reasons.includes("quiet_window_suppression"),
|
|
99
|
+
leaseRequired: false,
|
|
100
|
+
requiresCheckpoint: false,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { evaluateHardGuards, type HardGuardEvaluatorDeps, } from "./hard-guard-evaluator.js";
|
|
2
|
+
export { createDownstreamIntentOrchestrator, type DownstreamIntentOrchestrator, type DownstreamRequest, type ConnectorIntentRequest, type QuietRunRequest, type DreamScheduleRequest, type GuidanceDraftRequest, } from "./downstream-intent-orchestrator.js";
|
|
3
|
+
export { planCandidateIntents, planIntent, planIntentWithKind, decideDecisionBasis } from "./intent-planner.js";
|
|
4
|
+
export { applyGoalPriority } from "./goal-priority.js";
|
|
5
|
+
export { evaluateGuards } from "./guard-layer.js";
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/* Wave 57 — v7 control-plane orchestration components */
|
|
2
|
+
export { evaluateHardGuards, } from "./hard-guard-evaluator.js";
|
|
3
|
+
export { createDownstreamIntentOrchestrator, } from "./downstream-intent-orchestrator.js";
|
|
4
|
+
/* v6 legacy exports (preserved for backward compatibility) */
|
|
5
|
+
export { planCandidateIntents, planIntent, planIntentWithKind, decideDecisionBasis } from "./intent-planner.js";
|
|
6
|
+
export { applyGoalPriority } from "./goal-priority.js";
|
|
7
|
+
export { evaluateGuards } from "./guard-layer.js";
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ClaimSynthesizer — T-DQS.C.1
|
|
3
|
+
*
|
|
4
|
+
* Core logic: Transform aggregated life evidence into source-backed QuietClaims.
|
|
5
|
+
* - EvidenceAggregator: group and summarize raw evidence candidates
|
|
6
|
+
* - ClaimDeduplicator: remove duplicate claims by sourceRef key
|
|
7
|
+
* - ClaimSynthesizer: map evidence -> claim kind (observation/fact/pattern)
|
|
8
|
+
* - SourceValidator: reject fact claims with empty sourceRefs (DR-025)
|
|
9
|
+
*
|
|
10
|
+
* Rules:
|
|
11
|
+
* - Single weak evidence (confidence < 0.5, count == 1) → observation only
|
|
12
|
+
* - Multiple evidence or confidence >= 0.5 → fact
|
|
13
|
+
* - Pattern requires >= 3 related evidence with confidence >= 0.7
|
|
14
|
+
* - Fact claim sourceRefs must be non-empty tuple (DR-025)
|
|
15
|
+
* - SourceValidator returns claim_source_missing for empty sourceRefs
|
|
16
|
+
*
|
|
17
|
+
* Dependencies:
|
|
18
|
+
* - `QuietClaim`, `QuietClaimKind` from `../../../shared/types/v7-entities.js`
|
|
19
|
+
* - `LifeEvidenceCandidate` from `../../../storage/life-evidence/types.js`
|
|
20
|
+
*
|
|
21
|
+
* Test coverage: tests/unit/quiet/claim-synthesizer.test.ts
|
|
22
|
+
*/
|
|
23
|
+
import type { QuietClaim } from "../../../shared/types/v7-entities.js";
|
|
24
|
+
import type { LifeEvidenceCandidate } from "../../../storage/life-evidence/types.js";
|
|
25
|
+
export interface EvidenceSlice {
|
|
26
|
+
items: LifeEvidenceCandidate[];
|
|
27
|
+
summary: string;
|
|
28
|
+
}
|
|
29
|
+
export interface ClaimSynthesisResult {
|
|
30
|
+
claims: QuietClaim[];
|
|
31
|
+
errors: string[];
|
|
32
|
+
}
|
|
33
|
+
export interface EvidenceAggregator {
|
|
34
|
+
aggregate(candidates: LifeEvidenceCandidate[]): EvidenceSlice;
|
|
35
|
+
}
|
|
36
|
+
export interface ClaimDeduplicator {
|
|
37
|
+
deduplicate(claims: QuietClaim[]): QuietClaim[];
|
|
38
|
+
}
|
|
39
|
+
export interface ClaimSynthesizer {
|
|
40
|
+
synthesize(slice: EvidenceSlice): ClaimSynthesisResult;
|
|
41
|
+
}
|
|
42
|
+
export interface SourceValidator {
|
|
43
|
+
validate(claim: QuietClaim): {
|
|
44
|
+
ok: true;
|
|
45
|
+
} | {
|
|
46
|
+
ok: false;
|
|
47
|
+
reason: string;
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
export declare function createEvidenceAggregator(): EvidenceAggregator;
|
|
51
|
+
export declare function createClaimDeduplicator(): ClaimDeduplicator;
|
|
52
|
+
export declare function createClaimSynthesizer(): ClaimSynthesizer;
|
|
53
|
+
export declare function createSourceValidator(): SourceValidator;
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ClaimSynthesizer — T-DQS.C.1
|
|
3
|
+
*
|
|
4
|
+
* Core logic: Transform aggregated life evidence into source-backed QuietClaims.
|
|
5
|
+
* - EvidenceAggregator: group and summarize raw evidence candidates
|
|
6
|
+
* - ClaimDeduplicator: remove duplicate claims by sourceRef key
|
|
7
|
+
* - ClaimSynthesizer: map evidence -> claim kind (observation/fact/pattern)
|
|
8
|
+
* - SourceValidator: reject fact claims with empty sourceRefs (DR-025)
|
|
9
|
+
*
|
|
10
|
+
* Rules:
|
|
11
|
+
* - Single weak evidence (confidence < 0.5, count == 1) → observation only
|
|
12
|
+
* - Multiple evidence or confidence >= 0.5 → fact
|
|
13
|
+
* - Pattern requires >= 3 related evidence with confidence >= 0.7
|
|
14
|
+
* - Fact claim sourceRefs must be non-empty tuple (DR-025)
|
|
15
|
+
* - SourceValidator returns claim_source_missing for empty sourceRefs
|
|
16
|
+
*
|
|
17
|
+
* Dependencies:
|
|
18
|
+
* - `QuietClaim`, `QuietClaimKind` from `../../../shared/types/v7-entities.js`
|
|
19
|
+
* - `LifeEvidenceCandidate` from `../../../storage/life-evidence/types.js`
|
|
20
|
+
*
|
|
21
|
+
* Test coverage: tests/unit/quiet/claim-synthesizer.test.ts
|
|
22
|
+
*/
|
|
23
|
+
// ── EvidenceAggregator ───────────────────────────────────────
|
|
24
|
+
export function createEvidenceAggregator() {
|
|
25
|
+
return {
|
|
26
|
+
aggregate(candidates) {
|
|
27
|
+
// Group by evidenceType for summary
|
|
28
|
+
const byType = new Map();
|
|
29
|
+
for (const c of candidates) {
|
|
30
|
+
const list = byType.get(c.evidenceType) ?? [];
|
|
31
|
+
list.push(c);
|
|
32
|
+
byType.set(c.evidenceType, list);
|
|
33
|
+
}
|
|
34
|
+
const parts = [];
|
|
35
|
+
for (const [type, list] of byType) {
|
|
36
|
+
parts.push(`${list.length} ${type}`);
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
items: candidates,
|
|
40
|
+
summary: parts.join("; ") || "empty",
|
|
41
|
+
};
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
// ── ClaimDeduplicator ────────────────────────────────────────
|
|
46
|
+
function sourceRefKey(claim) {
|
|
47
|
+
return [...claim.sourceRefs].sort().join("|");
|
|
48
|
+
}
|
|
49
|
+
export function createClaimDeduplicator() {
|
|
50
|
+
return {
|
|
51
|
+
deduplicate(claims) {
|
|
52
|
+
const seen = new Set();
|
|
53
|
+
const result = [];
|
|
54
|
+
for (const claim of claims) {
|
|
55
|
+
const key = sourceRefKey(claim);
|
|
56
|
+
if (seen.has(key))
|
|
57
|
+
continue;
|
|
58
|
+
seen.add(key);
|
|
59
|
+
result.push(claim);
|
|
60
|
+
}
|
|
61
|
+
return result;
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
// ── ClaimSynthesizer ─────────────────────────────────────────
|
|
66
|
+
function determineKind(items, avgConfidence) {
|
|
67
|
+
if (items.length >= 3 && avgConfidence >= 0.7) {
|
|
68
|
+
return "pattern";
|
|
69
|
+
}
|
|
70
|
+
return "fact";
|
|
71
|
+
}
|
|
72
|
+
function buildText(kind, summary, count) {
|
|
73
|
+
switch (kind) {
|
|
74
|
+
case "observation":
|
|
75
|
+
return `Observed: ${summary}`;
|
|
76
|
+
case "fact":
|
|
77
|
+
return `Noted ${count} evidence(s): ${summary}`;
|
|
78
|
+
case "pattern":
|
|
79
|
+
return `Pattern detected across ${count} sources: ${summary}`;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
function toSourceRefTuple(refs) {
|
|
83
|
+
const ids = refs.map((r) => r.id).filter((id) => id && id.trim().length > 0);
|
|
84
|
+
if (ids.length === 0) {
|
|
85
|
+
// This should not happen if validation is correct, but type-safety requires it
|
|
86
|
+
return ["synthetic://missing"];
|
|
87
|
+
}
|
|
88
|
+
return ids;
|
|
89
|
+
}
|
|
90
|
+
export function createClaimSynthesizer() {
|
|
91
|
+
return {
|
|
92
|
+
synthesize(slice) {
|
|
93
|
+
const claims = [];
|
|
94
|
+
const errors = [];
|
|
95
|
+
const now = new Date().toISOString();
|
|
96
|
+
if (slice.items.length === 0) {
|
|
97
|
+
return { claims: [], errors: [] };
|
|
98
|
+
}
|
|
99
|
+
// Group by evidenceType for per-group synthesis
|
|
100
|
+
const byType = new Map();
|
|
101
|
+
for (const item of slice.items) {
|
|
102
|
+
const list = byType.get(item.evidenceType) ?? [];
|
|
103
|
+
list.push(item);
|
|
104
|
+
byType.set(item.evidenceType, list);
|
|
105
|
+
}
|
|
106
|
+
for (const [evidenceType, items] of byType) {
|
|
107
|
+
const avgConfidence = items.reduce((sum, i) => sum + (i.confidence ?? 0), 0) /
|
|
108
|
+
items.length;
|
|
109
|
+
let kind = determineKind(items, avgConfidence);
|
|
110
|
+
// Single weak evidence → observation only (no fact/pattern)
|
|
111
|
+
if (items.length === 1 && (items[0].confidence ?? 0) < 0.5) {
|
|
112
|
+
if (kind !== "observation") {
|
|
113
|
+
errors.push(`weak_evidence_downgrade:${evidenceType}:${kind}_to_observation`);
|
|
114
|
+
}
|
|
115
|
+
kind = "observation";
|
|
116
|
+
}
|
|
117
|
+
const summary = items.map((i) => i.summary).join("; ");
|
|
118
|
+
const text = buildText(kind, summary, items.length);
|
|
119
|
+
const sourceRefs = toSourceRefTuple(items.map((i) => ({ id: i.id ?? i.summary, uri: i.sourceRefs[0]?.uri ?? "" })));
|
|
120
|
+
claims.push({
|
|
121
|
+
claimId: `claim:${evidenceType}:${Date.now()}:${items.length}`,
|
|
122
|
+
kind,
|
|
123
|
+
text,
|
|
124
|
+
sourceRefs,
|
|
125
|
+
confidence: Math.min(avgConfidence, 1),
|
|
126
|
+
createdAt: now,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
return { claims, errors };
|
|
130
|
+
},
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
// ── SourceValidator ──────────────────────────────────────────
|
|
134
|
+
export function createSourceValidator() {
|
|
135
|
+
return {
|
|
136
|
+
validate(claim) {
|
|
137
|
+
// All claim kinds require non-empty sourceRefs (DR-025)
|
|
138
|
+
if (claim.sourceRefs.length === 0) {
|
|
139
|
+
return { ok: false, reason: "claim_source_missing" };
|
|
140
|
+
}
|
|
141
|
+
for (const ref of claim.sourceRefs) {
|
|
142
|
+
if (!ref || ref.trim().length === 0) {
|
|
143
|
+
return { ok: false, reason: "claim_source_missing" };
|
|
144
|
+
}
|
|
145
|
+
// Reject synthetic fallback refs (defensive: should not reach here)
|
|
146
|
+
if (ref.startsWith("synthetic://")) {
|
|
147
|
+
return { ok: false, reason: "claim_source_missing" };
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return { ok: true };
|
|
151
|
+
},
|
|
152
|
+
};
|
|
153
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DailyDiaryWriter — T-DQS.C.1
|
|
3
|
+
*
|
|
4
|
+
* Core logic: Convert QuietClaims into a three-section DailyDiary artifact.
|
|
5
|
+
* - observedToday: from observation claims (what was seen)
|
|
6
|
+
* - notableSignals: from fact claims (what matters)
|
|
7
|
+
* - tomorrowDirection: from pattern claims or best-guess from facts
|
|
8
|
+
*
|
|
9
|
+
* Rules:
|
|
10
|
+
* - Each section gets at most 5 entries (bounded, non-bloat)
|
|
11
|
+
* - Empty claims → empty sections (no fabricated content)
|
|
12
|
+
* - sourceRefs aggregated from all contributing claims
|
|
13
|
+
* - sensitive refs filtered by WriteValidationGate upstream (not here)
|
|
14
|
+
*
|
|
15
|
+
* Dependencies:
|
|
16
|
+
* - `DailyDiary` from `../../../shared/types/v7-entities.js`
|
|
17
|
+
* - `QuietClaim` from `../../../shared/types/v7-entities.js`
|
|
18
|
+
*
|
|
19
|
+
* Test coverage: tests/unit/quiet/daily-diary-writer.test.ts
|
|
20
|
+
*/
|
|
21
|
+
import type { DailyDiary, QuietClaim } from "../../../shared/types/v7-entities.js";
|
|
22
|
+
export interface DailyDiaryResult {
|
|
23
|
+
diary: DailyDiary;
|
|
24
|
+
errors: string[];
|
|
25
|
+
}
|
|
26
|
+
export interface DailyDiaryWriter {
|
|
27
|
+
write(claims: QuietClaim[], day: string): DailyDiaryResult;
|
|
28
|
+
}
|
|
29
|
+
export declare function createDailyDiaryWriter(): DailyDiaryWriter;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DailyDiaryWriter — T-DQS.C.1
|
|
3
|
+
*
|
|
4
|
+
* Core logic: Convert QuietClaims into a three-section DailyDiary artifact.
|
|
5
|
+
* - observedToday: from observation claims (what was seen)
|
|
6
|
+
* - notableSignals: from fact claims (what matters)
|
|
7
|
+
* - tomorrowDirection: from pattern claims or best-guess from facts
|
|
8
|
+
*
|
|
9
|
+
* Rules:
|
|
10
|
+
* - Each section gets at most 5 entries (bounded, non-bloat)
|
|
11
|
+
* - Empty claims → empty sections (no fabricated content)
|
|
12
|
+
* - sourceRefs aggregated from all contributing claims
|
|
13
|
+
* - sensitive refs filtered by WriteValidationGate upstream (not here)
|
|
14
|
+
*
|
|
15
|
+
* Dependencies:
|
|
16
|
+
* - `DailyDiary` from `../../../shared/types/v7-entities.js`
|
|
17
|
+
* - `QuietClaim` from `../../../shared/types/v7-entities.js`
|
|
18
|
+
*
|
|
19
|
+
* Test coverage: tests/unit/quiet/daily-diary-writer.test.ts
|
|
20
|
+
*/
|
|
21
|
+
const SECTION_LIMIT = 5;
|
|
22
|
+
function pickObservations(claims) {
|
|
23
|
+
return claims
|
|
24
|
+
.filter((c) => c.kind === "observation")
|
|
25
|
+
.map((c) => c.text)
|
|
26
|
+
.slice(0, SECTION_LIMIT);
|
|
27
|
+
}
|
|
28
|
+
function pickNotableSignals(claims) {
|
|
29
|
+
return claims
|
|
30
|
+
.filter((c) => c.kind === "fact")
|
|
31
|
+
.map((c) => c.text)
|
|
32
|
+
.slice(0, SECTION_LIMIT);
|
|
33
|
+
}
|
|
34
|
+
function pickTomorrowDirection(claims) {
|
|
35
|
+
const patterns = claims.filter((c) => c.kind === "pattern");
|
|
36
|
+
if (patterns.length > 0) {
|
|
37
|
+
return `Continue watching: ${patterns
|
|
38
|
+
.map((p) => p.text)
|
|
39
|
+
.join("; ")
|
|
40
|
+
.slice(0, 200)}`;
|
|
41
|
+
}
|
|
42
|
+
const facts = claims.filter((c) => c.kind === "fact");
|
|
43
|
+
if (facts.length > 0) {
|
|
44
|
+
return `Follow up on ${facts.length} note(s) tomorrow.`;
|
|
45
|
+
}
|
|
46
|
+
const observations = claims.filter((c) => c.kind === "observation");
|
|
47
|
+
if (observations.length > 0) {
|
|
48
|
+
return `Keep observing; no strong signals yet.`;
|
|
49
|
+
}
|
|
50
|
+
return "Nothing significant today.";
|
|
51
|
+
}
|
|
52
|
+
function aggregateSourceRefs(claims) {
|
|
53
|
+
const seen = new Set();
|
|
54
|
+
for (const claim of claims) {
|
|
55
|
+
for (const ref of claim.sourceRefs) {
|
|
56
|
+
if (!ref.startsWith("synthetic://")) {
|
|
57
|
+
seen.add(ref);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
const arr = [...seen];
|
|
62
|
+
if (arr.length === 0) {
|
|
63
|
+
return ["synthetic://empty"];
|
|
64
|
+
}
|
|
65
|
+
return arr;
|
|
66
|
+
}
|
|
67
|
+
export function createDailyDiaryWriter() {
|
|
68
|
+
return {
|
|
69
|
+
write(claims, day) {
|
|
70
|
+
const errors = [];
|
|
71
|
+
const now = new Date().toISOString();
|
|
72
|
+
const observedToday = pickObservations(claims);
|
|
73
|
+
const notableSignals = pickNotableSignals(claims);
|
|
74
|
+
const tomorrowDirection = pickTomorrowDirection(claims);
|
|
75
|
+
if (observedToday.length === 0 &&
|
|
76
|
+
notableSignals.length === 0 &&
|
|
77
|
+
tomorrowDirection === "Nothing significant today.") {
|
|
78
|
+
errors.push("diary_empty:no_claims");
|
|
79
|
+
}
|
|
80
|
+
const diary = {
|
|
81
|
+
diaryId: `diary:${day}:${Date.now()}`,
|
|
82
|
+
day,
|
|
83
|
+
observedToday,
|
|
84
|
+
notableSignals,
|
|
85
|
+
tomorrowDirection,
|
|
86
|
+
sourceRefs: aggregateSourceRefs(claims),
|
|
87
|
+
createdAt: now,
|
|
88
|
+
};
|
|
89
|
+
return { diary, errors };
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Quiet system barrel — T-DQS.C.1 exports
|
|
3
|
+
*/
|
|
4
|
+
export { createEvidenceAggregator, createClaimDeduplicator, createClaimSynthesizer, createSourceValidator, type EvidenceSlice, type ClaimSynthesisResult, type EvidenceAggregator, type ClaimDeduplicator, type ClaimSynthesizer, type SourceValidator, } from "./claim-synthesizer.js";
|
|
5
|
+
export { createDailyDiaryWriter, type DailyDiaryResult, type DailyDiaryWriter, } from "./daily-diary-writer.js";
|
|
@@ -2,15 +2,6 @@ import { isLifeEvidenceSliceEmpty } from "../heartbeat/runtime-snapshot.js";
|
|
|
2
2
|
import { writeQuietArtifact } from "../../../storage/quiet/quiet-artifact-writer.js";
|
|
3
3
|
import { persistQuietArtifactToWorkspace } from "../../../storage/quiet/persist-quiet-artifact.js";
|
|
4
4
|
import { buildEvidencePack, buildQuietNarrativeGuidance, selectInterestBasis } from "../../../guidance/evidence-guidance.js";
|
|
5
|
-
function toSourceRefFromControlPlane(r) {
|
|
6
|
-
return {
|
|
7
|
-
id: r.id,
|
|
8
|
-
kind: r.kind,
|
|
9
|
-
uri: r.uri,
|
|
10
|
-
excerptHash: r.excerptHash,
|
|
11
|
-
observedAt: r.observedAt,
|
|
12
|
-
};
|
|
13
|
-
}
|
|
14
5
|
function toGuidanceRef(r) {
|
|
15
6
|
return {
|
|
16
7
|
id: r.id,
|
|
@@ -49,7 +40,6 @@ export async function runSourceBackedQuiet(params) {
|
|
|
49
40
|
persistedRelativePath,
|
|
50
41
|
};
|
|
51
42
|
}
|
|
52
|
-
const bundleRefs = runtime.lifeEvidence.evidenceRefs.map(toSourceRefFromControlPlane);
|
|
53
43
|
const guidanceRefs = runtime.lifeEvidence.evidenceRefs.map(toGuidanceRef);
|
|
54
44
|
const ep = buildEvidencePack(guidanceRefs);
|
|
55
45
|
if (!ep.ok) {
|
|
@@ -62,11 +52,28 @@ export async function runSourceBackedQuiet(params) {
|
|
|
62
52
|
},
|
|
63
53
|
};
|
|
64
54
|
}
|
|
55
|
+
if (ep.pack.sensitiveBlocked) {
|
|
56
|
+
return {
|
|
57
|
+
result: {
|
|
58
|
+
scope: "rhythm",
|
|
59
|
+
status: "denied",
|
|
60
|
+
selectedIntentId: candidate.id,
|
|
61
|
+
reasons: ["quiet_guidance_sensitive_source_blocked"],
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
65
|
const basis = selectInterestBasis({
|
|
66
66
|
staleness: userInterestSnapshot?.staleness ?? "insufficient",
|
|
67
67
|
confidence: userInterestSnapshot?.confidence ?? 0,
|
|
68
68
|
signalCount: userInterestSnapshot?.signals.length ?? 0,
|
|
69
69
|
});
|
|
70
|
+
const groundedSourceRefs = ep.pack.groundedRefs.map((g) => ({
|
|
71
|
+
id: g.id,
|
|
72
|
+
kind: g.kind,
|
|
73
|
+
uri: g.uri,
|
|
74
|
+
excerptHash: g.excerptHash,
|
|
75
|
+
observedAt: g.observedAt,
|
|
76
|
+
}));
|
|
70
77
|
const claims = ep.pack.groundedRefs.map((g, i) => ({
|
|
71
78
|
id: `fact:${g.id}`,
|
|
72
79
|
text: `Evidence-backed note ${i + 1}`,
|
|
@@ -85,9 +92,9 @@ export async function runSourceBackedQuiet(params) {
|
|
|
85
92
|
day,
|
|
86
93
|
kind: "daily_report",
|
|
87
94
|
title: "Quiet daily report",
|
|
88
|
-
body: `Source-backed quiet summary (${
|
|
95
|
+
body: `Source-backed quiet summary (${groundedSourceRefs.length} refs).`,
|
|
89
96
|
claims,
|
|
90
|
-
sourceRefs:
|
|
97
|
+
sourceRefs: groundedSourceRefs,
|
|
91
98
|
};
|
|
92
99
|
const ack = writeQuietArtifact(reportWrite);
|
|
93
100
|
const gq = buildQuietNarrativeGuidance({
|
|
@@ -32,6 +32,8 @@ export type CandidateEffectClass = "external_platform_action" | "connector_actio
|
|
|
32
32
|
export interface CandidateIntent {
|
|
33
33
|
id: string;
|
|
34
34
|
kind: IntentKind;
|
|
35
|
+
/** Optional connector capability override, including workspace-defined behavior IDs. */
|
|
36
|
+
capabilityIntent?: string;
|
|
35
37
|
priority: number;
|
|
36
38
|
source: "tick" | "interrupt" | "obligation" | "quiet_plan";
|
|
37
39
|
platformId?: string;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ChannelFeedbackIngestionService — T-GVS.C.2
|
|
3
|
+
*
|
|
4
|
+
* Core logic: process delivery result and owner reaction into RelationshipMemory;
|
|
5
|
+
* retry with exponential backoff on persistence failure; audit on exhaustion.
|
|
6
|
+
* Implements DR-029 (retry + audit; no silent loss) and ADR-006 (delivery truth).
|
|
7
|
+
*
|
|
8
|
+
* Boundary:
|
|
9
|
+
* - Consumes ChannelFeedback from runtime-ops-system.
|
|
10
|
+
* - Writes to state-memory-system via port (no direct DB access).
|
|
11
|
+
* - On 3 failed retries, emits observability audit event (family: guidance.feedback_ingestion_failed)
|
|
12
|
+
* with feedback summary hash — never raw reaction content.
|
|
13
|
+
* - Missing deliveryProof → deliveryResult coerced to "not_sent".
|
|
14
|
+
*
|
|
15
|
+
* Test coverage: tests/unit/guidance/channel-feedback-ingestion.test.ts
|
|
16
|
+
*/
|
|
17
|
+
export type DeliveryResult = "sent" | "failed" | "not_sent";
|
|
18
|
+
export type OwnerReaction = "reply" | "ignore" | "block" | "react";
|
|
19
|
+
export interface DeliveryProof {
|
|
20
|
+
messageId?: string;
|
|
21
|
+
hostProofRef?: string;
|
|
22
|
+
}
|
|
23
|
+
export interface ChannelFeedback {
|
|
24
|
+
messageId?: string;
|
|
25
|
+
deliveryResult: DeliveryResult;
|
|
26
|
+
deliveryProof?: DeliveryProof;
|
|
27
|
+
ownerReaction: OwnerReaction;
|
|
28
|
+
reactionContent?: string;
|
|
29
|
+
timestamp: string;
|
|
30
|
+
channelId: string;
|
|
31
|
+
}
|
|
32
|
+
export interface RelationshipUpdate {
|
|
33
|
+
channelId: string;
|
|
34
|
+
timestamp: string;
|
|
35
|
+
trustDelta: number;
|
|
36
|
+
responsePattern: {
|
|
37
|
+
reaction: OwnerReaction;
|
|
38
|
+
timing: "immediate" | "delayed" | "very_delayed";
|
|
39
|
+
tone: "positive" | "neutral" | "negative";
|
|
40
|
+
};
|
|
41
|
+
deliverySuccess: boolean;
|
|
42
|
+
}
|
|
43
|
+
export interface ResponsePatternEntry {
|
|
44
|
+
reaction: OwnerReaction;
|
|
45
|
+
timing: "immediate" | "delayed" | "very_delayed";
|
|
46
|
+
tone: "positive" | "neutral" | "negative";
|
|
47
|
+
observedAt: string;
|
|
48
|
+
}
|
|
49
|
+
export interface ChannelPreference {
|
|
50
|
+
channelId: string;
|
|
51
|
+
successRate: number;
|
|
52
|
+
lastUsedAt?: string;
|
|
53
|
+
}
|
|
54
|
+
export interface RelationshipMemory {
|
|
55
|
+
channelPreferences: ChannelPreference[];
|
|
56
|
+
responsePatterns: ResponsePatternEntry[];
|
|
57
|
+
trustDelta: number;
|
|
58
|
+
lastUpdated?: string;
|
|
59
|
+
}
|
|
60
|
+
export interface StrategyAdjustment {
|
|
61
|
+
type: "frequency" | "tone" | "timing";
|
|
62
|
+
adjustment: string;
|
|
63
|
+
reason: string;
|
|
64
|
+
value?: number;
|
|
65
|
+
}
|
|
66
|
+
export interface FeedbackIngestionResult {
|
|
67
|
+
status: "ingested" | "rejected" | "failed_after_retries";
|
|
68
|
+
errors?: string[];
|
|
69
|
+
relationshipUpdate?: RelationshipUpdate;
|
|
70
|
+
strategyAdjustments?: StrategyAdjustment[];
|
|
71
|
+
updatedTrust?: number;
|
|
72
|
+
}
|
|
73
|
+
export interface RelationshipMemoryPort {
|
|
74
|
+
loadRelationshipMemory(): Promise<RelationshipMemory>;
|
|
75
|
+
updateRelationshipMemory(update: RelationshipMemory): Promise<void>;
|
|
76
|
+
}
|
|
77
|
+
export interface FeedbackAuditPort {
|
|
78
|
+
recordFeedbackIngestionFailed(summary: {
|
|
79
|
+
feedbackId: string;
|
|
80
|
+
channelId: string;
|
|
81
|
+
summaryHash: string;
|
|
82
|
+
retryCount: number;
|
|
83
|
+
}): Promise<void>;
|
|
84
|
+
}
|
|
85
|
+
export declare function ingestChannelFeedback(feedback: ChannelFeedback, deps: {
|
|
86
|
+
relationshipPort: RelationshipMemoryPort;
|
|
87
|
+
auditPort: FeedbackAuditPort;
|
|
88
|
+
}): Promise<FeedbackIngestionResult>;
|