@haaaiawd/second-nature 0.1.34 → 0.1.39
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/agent-inner-guide.md +43 -0
- package/index.js +1270 -1262
- package/openclaw.plugin.json +29 -29
- package/package.json +55 -55
- package/runtime/cli/commands/goal.d.ts +1 -0
- package/runtime/cli/commands/goal.js +1 -0
- package/runtime/cli/index.js +3 -3
- package/runtime/cli/ops/heartbeat-surface.d.ts +75 -54
- package/runtime/cli/ops/heartbeat-surface.js +97 -81
- package/runtime/cli/ops/ops-router.js +1428 -1182
- package/runtime/cli/ops/workspace-heartbeat-runner.d.ts +24 -0
- package/runtime/cli/ops/workspace-heartbeat-runner.js +236 -150
- package/runtime/connectors/base/contract.d.ts +10 -0
- package/runtime/core/second-nature/guidance/apply-guidance.d.ts +12 -10
- package/runtime/core/second-nature/guidance/apply-guidance.js +15 -10
- package/runtime/core/second-nature/guidance/user-reply-continuity.d.ts +50 -50
- package/runtime/core/second-nature/guidance/user-reply-continuity.js +89 -80
- package/runtime/core/second-nature/heartbeat/heartbeat-loop.d.ts +7 -1
- package/runtime/core/second-nature/heartbeat/heartbeat-loop.js +25 -0
- package/runtime/core/second-nature/heartbeat/runtime-snapshot.d.ts +5 -0
- package/runtime/core/second-nature/heartbeat/runtime-snapshot.js +10 -1
- package/runtime/core/second-nature/heartbeat/snapshot-builder.d.ts +5 -0
- package/runtime/core/second-nature/orchestrator/guard-layer.js +24 -1
- package/runtime/core/second-nature/quiet/run-source-backed-quiet.d.ts +20 -0
- package/runtime/core/second-nature/quiet/run-source-backed-quiet.js +32 -2
- package/runtime/core/second-nature/runtime/service-entry.d.ts +39 -36
- package/runtime/core/second-nature/runtime/service-entry.js +44 -45
- package/runtime/dream/dream-engine.d.ts +14 -0
- package/runtime/dream/dream-engine.js +306 -0
- package/runtime/dream/dream-input-loader.d.ts +37 -0
- package/runtime/dream/dream-input-loader.js +155 -0
- package/runtime/dream/dream-scheduler.d.ts +75 -0
- package/runtime/dream/dream-scheduler.js +131 -0
- package/runtime/dream/index.d.ts +16 -0
- package/runtime/dream/index.js +14 -0
- package/runtime/dream/insight-extractor.d.ts +32 -0
- package/runtime/dream/insight-extractor.js +135 -0
- package/runtime/dream/memory-consolidator.d.ts +45 -0
- package/runtime/dream/memory-consolidator.js +140 -0
- package/runtime/dream/narrative-update-proposal.d.ts +34 -0
- package/runtime/dream/narrative-update-proposal.js +83 -0
- package/runtime/dream/output-validator.d.ts +20 -0
- package/runtime/dream/output-validator.js +110 -0
- package/runtime/dream/redaction-gate.d.ts +31 -0
- package/runtime/dream/redaction-gate.js +109 -0
- package/runtime/dream/relationship-update-proposal.d.ts +27 -0
- package/runtime/dream/relationship-update-proposal.js +119 -0
- package/runtime/dream/sampler.d.ts +30 -0
- package/runtime/dream/sampler.js +65 -0
- package/runtime/dream/types.d.ts +187 -0
- package/runtime/dream/types.js +11 -0
- package/runtime/guidance/capability-class.d.ts +38 -0
- package/runtime/guidance/capability-class.js +65 -0
- package/runtime/guidance/fallback.js +20 -17
- package/runtime/guidance/guidance-assembler.d.ts +2 -0
- package/runtime/guidance/guidance-assembler.js +76 -62
- package/runtime/guidance/guidance-draft-service.js +5 -5
- package/runtime/guidance/impulse-assembler.d.ts +71 -0
- package/runtime/guidance/impulse-assembler.js +103 -0
- package/runtime/guidance/index.d.ts +2 -0
- package/runtime/guidance/index.js +2 -0
- package/runtime/guidance/output-guard.d.ts +13 -10
- package/runtime/guidance/output-guard.js +53 -29
- package/runtime/guidance/outreach-strategy-selector.d.ts +13 -0
- package/runtime/guidance/outreach-strategy-selector.js +2 -2
- package/runtime/guidance/template-registry.d.ts +20 -3
- package/runtime/guidance/template-registry.js +123 -45
- package/runtime/guidance/types.d.ts +98 -72
- package/runtime/observability/projections/guidance-audit.js +38 -35
- package/runtime/storage/goal/agent-goal-store.d.ts +2 -0
- package/runtime/storage/goal/agent-goal-store.js +28 -1
- package/runtime/storage/services/tool-experience-store.js +11 -11
|
@@ -1,80 +1,89 @@
|
|
|
1
|
-
import { buildMinimalGuidanceFallback } from "../../../guidance/fallback.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
*
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* -
|
|
15
|
-
* -
|
|
16
|
-
* -
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
"
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
1
|
+
import { buildMinimalGuidanceFallback } from "../../../guidance/fallback.js";
|
|
2
|
+
import { buildExpressionBoundary } from "../../../guidance/output-guard.js";
|
|
3
|
+
import { selectPersonaSnippets } from "../../../guidance/persona-selection.js";
|
|
4
|
+
import { getShortAtmosphereTemplate } from "../../../guidance/template-registry.js";
|
|
5
|
+
/**
|
|
6
|
+
* Scene context for user reply - uses a distinct scene type
|
|
7
|
+
* to avoid confusion with platform reply scene.
|
|
8
|
+
*/
|
|
9
|
+
export const USER_REPLY_SCENE_TYPE = "user_reply";
|
|
10
|
+
/**
|
|
11
|
+
* Build very light continuity guidance for direct user replies.
|
|
12
|
+
*
|
|
13
|
+
* Returns a minimal guidance payload with:
|
|
14
|
+
* - Light atmosphere (continuity-focused)
|
|
15
|
+
* - NO impulses (unlike platform reply scene)
|
|
16
|
+
* - Optional persona reinforcement (1-2 snippets max)
|
|
17
|
+
* - Minimal expression boundary (tone consistency only)
|
|
18
|
+
*/
|
|
19
|
+
export async function buildLightReplyContinuity(input) {
|
|
20
|
+
const sceneContext = {
|
|
21
|
+
sceneType: "user_reply",
|
|
22
|
+
mode: "active",
|
|
23
|
+
riskLevel: "low",
|
|
24
|
+
sceneSummary: "direct user reply continuity",
|
|
25
|
+
};
|
|
26
|
+
try {
|
|
27
|
+
// Light atmosphere - continuity focused (T-V7C.C.7: short constraint style)
|
|
28
|
+
const atmosphereTemplate = getShortAtmosphereTemplate(sceneContext.mode, sceneContext.riskLevel);
|
|
29
|
+
const atmosphere = {
|
|
30
|
+
kind: "atmosphere",
|
|
31
|
+
text: `保持同一个人的语气。${input.replyContext.recentTone ? `最近语气参考:${input.replyContext.recentTone}` : "延续既有连续感。"}`,
|
|
32
|
+
openness: "open",
|
|
33
|
+
pressureLabels: ["user_reply", "continuity"],
|
|
34
|
+
reviewStatus: atmosphereTemplate.reviewStatus,
|
|
35
|
+
};
|
|
36
|
+
// NO impulses for user reply - this is the key difference from platform reply
|
|
37
|
+
const impulses = [];
|
|
38
|
+
// Minimal persona reinforcement - only if candidates available
|
|
39
|
+
let personaReinforcement = [];
|
|
40
|
+
if (input.personaCandidates && input.personaCandidates.length > 0) {
|
|
41
|
+
const personaDecision = selectPersonaSnippets({
|
|
42
|
+
sceneContext,
|
|
43
|
+
candidates: input.personaCandidates.slice(0, 2), // Max 2 snippets for light continuity
|
|
44
|
+
});
|
|
45
|
+
personaReinforcement = personaDecision.snippets;
|
|
46
|
+
}
|
|
47
|
+
// Minimal expression boundary - tone consistency only (T-V7C.C.7)
|
|
48
|
+
const outputGuard = {
|
|
49
|
+
kind: "output_guard",
|
|
50
|
+
constraints: [
|
|
51
|
+
"保持对话语气,不要用帖子回复腔",
|
|
52
|
+
"延续同一个人格连续性",
|
|
53
|
+
],
|
|
54
|
+
hardGuardPriority: true,
|
|
55
|
+
_semanticNote: "output_guard_only_shapes_expression",
|
|
56
|
+
};
|
|
57
|
+
const expressionBoundary = buildExpressionBoundary(sceneContext.sceneType);
|
|
58
|
+
// Override with user-reply-specific constraints
|
|
59
|
+
expressionBoundary.constraints = [
|
|
60
|
+
"保持对话语气,不要用帖子回复腔",
|
|
61
|
+
"延续同一个人格连续性",
|
|
62
|
+
];
|
|
63
|
+
return {
|
|
64
|
+
scene: sceneContext,
|
|
65
|
+
atmosphere,
|
|
66
|
+
impulses,
|
|
67
|
+
personaReinforcement,
|
|
68
|
+
outputGuard,
|
|
69
|
+
expressionBoundary,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
// Fallback to minimal guidance
|
|
74
|
+
return buildMinimalGuidanceFallback(sceneContext);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Check if an input should be classified as direct user reply.
|
|
79
|
+
*
|
|
80
|
+
* Classification criteria:
|
|
81
|
+
* - Trigger source is user_reply
|
|
82
|
+
* - Not a platform comment/reply
|
|
83
|
+
* - Not an explicit task delegation
|
|
84
|
+
*/
|
|
85
|
+
export function isDirectUserReply(input) {
|
|
86
|
+
return (input.triggerSource === "user_reply" &&
|
|
87
|
+
!input.isPlatformReply &&
|
|
88
|
+
!input.isExplicitTask);
|
|
89
|
+
}
|
|
@@ -23,6 +23,8 @@ import type { ConnectorExecutor } from "../../../connectors/base/contract.js";
|
|
|
23
23
|
import type { CapabilityContractRegistry } from "../../../connectors/base/manifest.js";
|
|
24
24
|
import type { NarrativeStateStore } from "../../../storage/narrative/narrative-state-store.js";
|
|
25
25
|
import type { NarrativeTracePayload } from "../../../observability/services/lived-experience-audit.js";
|
|
26
|
+
import type { ExperienceWriter } from "../body/tool-experience/experience-writer.js";
|
|
27
|
+
import type { QuietDreamSchedulePort } from "../quiet/run-source-backed-quiet.js";
|
|
26
28
|
export interface HeartbeatDecisionTracePayload {
|
|
27
29
|
scope: RuntimeScope;
|
|
28
30
|
status: HeartbeatCycleStatus;
|
|
@@ -43,12 +45,14 @@ export interface HeartbeatOutreachDispatchDeps {
|
|
|
43
45
|
/** Optional Quiet orchestration: when set, quiet/reflection allows run source-backed Quiet writer (T2.3.3). */
|
|
44
46
|
export interface HeartbeatQuietWorkflowDeps {
|
|
45
47
|
workspaceRoot: string;
|
|
48
|
+
/** v7 T-V7C.C.3: when present, a successful Quiet write auto-triggers Dream scheduling. */
|
|
49
|
+
dreamSchedulePort?: QuietDreamSchedulePort;
|
|
46
50
|
}
|
|
47
51
|
/**
|
|
48
52
|
* Resolves the heartbeat outcome for a guard-allowed intent (outreach dispatch, quiet orchestration, or default).
|
|
49
53
|
* Exported for unit tests (CR-M1 wiring).
|
|
50
54
|
*/
|
|
51
|
-
export declare function resolveAllowedIntentResult(intent: CandidateIntent, runtime: HeartbeatRuntimeSnapshot, inputs: SnapshotInputs, signal: HeartbeatSignal, deps: Pick<HeartbeatDeps, "outreachDispatch" | "quietWorkflow" | "connectorExecutor" | "state" | "workspaceRoot">): Promise<HeartbeatCycleResult>;
|
|
55
|
+
export declare function resolveAllowedIntentResult(intent: CandidateIntent, runtime: HeartbeatRuntimeSnapshot, inputs: SnapshotInputs, signal: HeartbeatSignal, deps: Pick<HeartbeatDeps, "outreachDispatch" | "quietWorkflow" | "connectorExecutor" | "state" | "workspaceRoot" | "experienceWriter">): Promise<HeartbeatCycleResult>;
|
|
52
56
|
export interface HeartbeatDeps {
|
|
53
57
|
/** Load snapshot inputs from state-system */
|
|
54
58
|
loadSnapshotInputs: () => Promise<SnapshotInputs>;
|
|
@@ -71,6 +75,8 @@ export interface HeartbeatDeps {
|
|
|
71
75
|
workspaceRoot?: string;
|
|
72
76
|
/** T2.4.1: when present, planner resolves platform-specific intents. */
|
|
73
77
|
connectorRegistry?: CapabilityContractRegistry;
|
|
78
|
+
/** v7 T-V7C.C.2: when present, connector attempts write ToolExperience with triggerSource="heartbeat". */
|
|
79
|
+
experienceWriter?: ExperienceWriter;
|
|
74
80
|
}
|
|
75
81
|
/**
|
|
76
82
|
* Ingest a heartbeat rhythm signal and drive one full decision round.
|
|
@@ -38,6 +38,8 @@ export async function resolveAllowedIntentResult(intent, runtime, inputs, signal
|
|
|
38
38
|
day,
|
|
39
39
|
userInterestSnapshot: inputs.userInterestSnapshot,
|
|
40
40
|
workspaceRoot: deps.quietWorkflow.workspaceRoot,
|
|
41
|
+
// v7 T-V7C.C.3: pass Dream schedule port so Quiet completion triggers Dream.
|
|
42
|
+
dreamSchedulePort: deps.quietWorkflow.dreamSchedulePort,
|
|
41
43
|
});
|
|
42
44
|
return quietRun.result;
|
|
43
45
|
}
|
|
@@ -64,6 +66,8 @@ export async function resolveAllowedIntentResult(intent, runtime, inputs, signal
|
|
|
64
66
|
};
|
|
65
67
|
}
|
|
66
68
|
const decisionId = `decision:${intent.id}:${Date.now()}`;
|
|
69
|
+
// T-V7C.C.4: inject identity from EmbodiedContext into connector request (readable, no credential)
|
|
70
|
+
const platformHandle = runtime.identity?.platformHandles.find((h) => h.platformId === intent.platformId)?.handle;
|
|
67
71
|
const result = await deps.connectorExecutor.executeEffect({
|
|
68
72
|
platformId: intent.platformId,
|
|
69
73
|
intent: toCapabilityIntent(intent),
|
|
@@ -71,6 +75,12 @@ export async function resolveAllowedIntentResult(intent, runtime, inputs, signal
|
|
|
71
75
|
decisionId,
|
|
72
76
|
intentId: intent.id,
|
|
73
77
|
idempotencyKey: `idem:${intent.id}:${Date.now()}`,
|
|
78
|
+
identity: platformHandle || runtime.identity?.canonicalName
|
|
79
|
+
? {
|
|
80
|
+
platformHandle,
|
|
81
|
+
canonicalName: runtime.identity?.canonicalName,
|
|
82
|
+
}
|
|
83
|
+
: undefined,
|
|
74
84
|
});
|
|
75
85
|
// T3.3.1: on success, map connector result to life evidence and append.
|
|
76
86
|
// On failure or empty result, no evidence is fabricated — attempt audit
|
|
@@ -96,6 +106,21 @@ export async function resolveAllowedIntentResult(intent, runtime, inputs, signal
|
|
|
96
106
|
console.warn(`[heartbeat] evidence append failed for ${intent.platformId ?? "unknown"}: ${errorMessage}`);
|
|
97
107
|
}
|
|
98
108
|
}
|
|
109
|
+
// v7 T-V7C.C.2: record ToolExperience for all connector attempts in heartbeat.
|
|
110
|
+
if (deps.experienceWriter) {
|
|
111
|
+
try {
|
|
112
|
+
await deps.experienceWriter.recordExperience({
|
|
113
|
+
connectorId: intent.platformId,
|
|
114
|
+
capabilityId: toCapabilityIntent(intent),
|
|
115
|
+
result,
|
|
116
|
+
triggerSource: "heartbeat",
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
catch (err) {
|
|
120
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
121
|
+
console.warn(`[heartbeat] ToolExperience record failed for ${intent.platformId ?? "unknown"}: ${errorMessage}`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
99
124
|
const base = {
|
|
100
125
|
scope: "rhythm",
|
|
101
126
|
status: "intent_selected",
|
|
@@ -5,6 +5,7 @@ import type { ContinuitySnapshot, ControlPlaneSourceRef } from "../types.js";
|
|
|
5
5
|
import type { RhythmPolicy } from "../rhythm/rhythm-policy.js";
|
|
6
6
|
import { type PlannerRhythmWindowSlice } from "../rhythm/planner-rhythm-window.js";
|
|
7
7
|
import type { SnapshotInputs } from "./snapshot-builder.js";
|
|
8
|
+
import type { AffordanceMap } from "../../../shared/types/v7-entities.js";
|
|
8
9
|
export interface PlannerLifeEvidenceSlice {
|
|
9
10
|
evidenceRefs: ControlPlaneSourceRef[];
|
|
10
11
|
platformEventCount: number;
|
|
@@ -23,6 +24,10 @@ export interface HeartbeatRuntimeSnapshot {
|
|
|
23
24
|
hardGuards: HardGuardDeps;
|
|
24
25
|
narrativeState?: import("../../../storage/narrative/narrative-state-store.js").NarrativeState;
|
|
25
26
|
relationshipMemory?: import("../../../storage/relationship/relationship-memory-store.js").RelationshipMemory;
|
|
27
|
+
/** v7: affordance map for breaker-aware guard evaluation (T-V7C.C.2). */
|
|
28
|
+
affordanceMap?: AffordanceMap;
|
|
29
|
+
/** T-V7C.C.4: identity profile for connector request identity injection. */
|
|
30
|
+
identity?: import("../../../shared/types/v7-entities.js").IdentityProfile;
|
|
26
31
|
}
|
|
27
32
|
export declare function buildLifeEvidenceSliceFromInputs(inputs: SnapshotInputs): PlannerLifeEvidenceSlice;
|
|
28
33
|
export declare function buildHardGuardDeps(continuity: ContinuitySnapshot, inputs: SnapshotInputs): HardGuardDeps;
|
|
@@ -31,5 +31,14 @@ export function buildHeartbeatRuntimeSnapshot(timestamp, inputs, continuity) {
|
|
|
31
31
|
const rhythmWindow = buildPlannerRhythmWindow(timestamp, continuity, policy);
|
|
32
32
|
const lifeEvidence = buildLifeEvidenceSliceFromInputs(inputs);
|
|
33
33
|
const hardGuards = buildHardGuardDeps(continuity, inputs);
|
|
34
|
-
return {
|
|
34
|
+
return {
|
|
35
|
+
continuity,
|
|
36
|
+
lifeEvidence,
|
|
37
|
+
rhythmWindow,
|
|
38
|
+
hardGuards,
|
|
39
|
+
narrativeState: inputs.narrativeState,
|
|
40
|
+
relationshipMemory: inputs.relationshipMemory,
|
|
41
|
+
affordanceMap: inputs.affordanceMap,
|
|
42
|
+
identity: inputs.identity,
|
|
43
|
+
};
|
|
35
44
|
}
|
|
@@ -12,6 +12,7 @@ import type { DeliveryCapabilitySnapshot } from "../outreach/delivery-target.js"
|
|
|
12
12
|
import type { UserInterestSnapshot } from "../../../storage/user-interest/types.js";
|
|
13
13
|
import type { NarrativeState } from "../../../storage/narrative/narrative-state-store.js";
|
|
14
14
|
import type { RelationshipMemory } from "../../../storage/relationship/relationship-memory-store.js";
|
|
15
|
+
import type { AffordanceMap, IdentityProfile } from "../../../shared/types/v7-entities.js";
|
|
15
16
|
export interface SnapshotInputs {
|
|
16
17
|
mode: TopLevelMode;
|
|
17
18
|
currentWindowId: string;
|
|
@@ -58,6 +59,10 @@ export interface SnapshotInputs {
|
|
|
58
59
|
narrativeState?: NarrativeState;
|
|
59
60
|
/** When present, planner uses relationship memory to influence outreach timing. */
|
|
60
61
|
relationshipMemory?: RelationshipMemory;
|
|
62
|
+
/** v7: affordance map for breaker-aware guard evaluation (T-V7C.C.2). */
|
|
63
|
+
affordanceMap?: AffordanceMap;
|
|
64
|
+
/** T-V7C.C.4: identity profile for connector request identity injection. */
|
|
65
|
+
identity?: IdentityProfile;
|
|
61
66
|
}
|
|
62
67
|
/**
|
|
63
68
|
* Build a ContinuitySnapshot from loaded inputs.
|
|
@@ -42,6 +42,27 @@ export function evaluateHardGuards(intent, runtime) {
|
|
|
42
42
|
if (!isSourceBacked(intent)) {
|
|
43
43
|
reasons.push("missing_source_refs");
|
|
44
44
|
}
|
|
45
|
+
// v7: Affordance / breaker guard (T-V7C.C.2)
|
|
46
|
+
if ((intent.effectClass === "connector_action" ||
|
|
47
|
+
intent.effectClass === "external_platform_action") &&
|
|
48
|
+
runtime.affordanceMap &&
|
|
49
|
+
intent.platformId) {
|
|
50
|
+
const platformItems = runtime.affordanceMap[intent.platformId] ?? [];
|
|
51
|
+
const match = intent.capabilityIntent
|
|
52
|
+
? platformItems.find((i) => i.capabilityId === intent.capabilityIntent)
|
|
53
|
+
: platformItems.find((i) => i.intent === intent.summary);
|
|
54
|
+
if (match) {
|
|
55
|
+
if (match.status === "painful") {
|
|
56
|
+
reasons.push("connector_circuit_open");
|
|
57
|
+
}
|
|
58
|
+
else if (match.status === "unavailable") {
|
|
59
|
+
reasons.push("affordance_unavailable");
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
reasons.push("affordance_unavailable");
|
|
64
|
+
}
|
|
65
|
+
}
|
|
45
66
|
const key = intentFingerprint(intent);
|
|
46
67
|
if (runtime.hardGuards.hasDuplicateIntent(key)) {
|
|
47
68
|
reasons.push("duplicate_intent");
|
|
@@ -74,7 +95,9 @@ export function evaluateHardGuards(intent, runtime) {
|
|
|
74
95
|
}
|
|
75
96
|
const duplicate = reasons.includes("duplicate_intent");
|
|
76
97
|
const cooldown = reasons.includes("outreach_cooldown");
|
|
77
|
-
|
|
98
|
+
const circuitOpen = reasons.includes("connector_circuit_open");
|
|
99
|
+
const affordanceUnavailable = reasons.includes("affordance_unavailable");
|
|
100
|
+
if (duplicate || cooldown || circuitOpen || affordanceUnavailable) {
|
|
78
101
|
return {
|
|
79
102
|
verdict: "defer",
|
|
80
103
|
reasons,
|
|
@@ -1,17 +1,37 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Quiet / reflection orchestration: empty evidence → empty_state; otherwise coverage-gated artifact (T2.3.3).
|
|
3
|
+
*
|
|
4
|
+
* v7 T-V7C.C.3: After a successful Quiet artifact write, if a DreamSchedulePort is provided,
|
|
5
|
+
* automatically trigger scheduleDream(quiet_completion). Skip reason is embedded in HeartbeatCycleResult
|
|
6
|
+
* reasons when the scheduler returns "skipped" (e.g. lock held).
|
|
3
7
|
*/
|
|
4
8
|
import type { CandidateIntent } from "../types.js";
|
|
5
9
|
import type { HeartbeatRuntimeSnapshot } from "../heartbeat/runtime-snapshot.js";
|
|
6
10
|
import type { HeartbeatCycleResult } from "../heartbeat/signal.js";
|
|
7
11
|
import { type QuietArtifactAck } from "../../../storage/quiet/quiet-artifact-writer.js";
|
|
8
12
|
import type { UserInterestSnapshot } from "../../../storage/user-interest/types.js";
|
|
13
|
+
/**
|
|
14
|
+
* Minimal port for triggering Dream after Quiet completion (T-V7C.C.3).
|
|
15
|
+
* Kept narrow so run-source-backed-quiet does not take a hard dependency on dream-scheduler.
|
|
16
|
+
*/
|
|
17
|
+
export interface QuietDreamSchedulePort {
|
|
18
|
+
scheduleDream(params: {
|
|
19
|
+
triggerKind: "quiet_completion";
|
|
20
|
+
runId: string;
|
|
21
|
+
traceId: string;
|
|
22
|
+
}): Promise<{
|
|
23
|
+
status: "started" | "skipped" | "queued";
|
|
24
|
+
reason?: string;
|
|
25
|
+
}>;
|
|
26
|
+
}
|
|
9
27
|
export interface RunSourceBackedQuietParams {
|
|
10
28
|
candidate: CandidateIntent;
|
|
11
29
|
runtime: HeartbeatRuntimeSnapshot;
|
|
12
30
|
day: string;
|
|
13
31
|
userInterestSnapshot?: UserInterestSnapshot;
|
|
14
32
|
workspaceRoot?: string;
|
|
33
|
+
/** v7 T-V7C.C.3: when present, a successful Quiet artifact write auto-triggers Dream scheduling. */
|
|
34
|
+
dreamSchedulePort?: QuietDreamSchedulePort;
|
|
15
35
|
}
|
|
16
36
|
export interface RunSourceBackedQuietResult {
|
|
17
37
|
result: HeartbeatCycleResult;
|
|
@@ -11,8 +11,33 @@ function toGuidanceRef(r) {
|
|
|
11
11
|
observedAt: r.observedAt,
|
|
12
12
|
};
|
|
13
13
|
}
|
|
14
|
+
/**
|
|
15
|
+
* v7 T-V7C.C.3: Fire-and-forget Dream schedule after successful Quiet write.
|
|
16
|
+
* Returns the schedule status reason string to embed in HeartbeatCycleResult reasons.
|
|
17
|
+
* Never throws — Dream scheduling failure must not break the Quiet cycle result.
|
|
18
|
+
*/
|
|
19
|
+
async function maybeScheduleDreamAfterQuiet(dreamSchedulePort, day) {
|
|
20
|
+
if (!dreamSchedulePort)
|
|
21
|
+
return undefined;
|
|
22
|
+
try {
|
|
23
|
+
const result = await dreamSchedulePort.scheduleDream({
|
|
24
|
+
triggerKind: "quiet_completion",
|
|
25
|
+
runId: `dream:quiet_completion:${day}:${Date.now()}`,
|
|
26
|
+
traceId: `trace:quiet_completion:${day}:${Date.now()}`,
|
|
27
|
+
});
|
|
28
|
+
if (result.status === "skipped") {
|
|
29
|
+
return `quiet_dream_skip:${result.reason ?? "lock_held"}`;
|
|
30
|
+
}
|
|
31
|
+
return "quiet_dream_scheduled";
|
|
32
|
+
}
|
|
33
|
+
catch (err) {
|
|
34
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
35
|
+
console.warn(`[run-source-backed-quiet] Dream schedule failed: ${msg}`);
|
|
36
|
+
return `quiet_dream_schedule_error:${msg.slice(0, 60)}`;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
14
39
|
export async function runSourceBackedQuiet(params) {
|
|
15
|
-
const { candidate, runtime, day, userInterestSnapshot, workspaceRoot } = params;
|
|
40
|
+
const { candidate, runtime, day, userInterestSnapshot, workspaceRoot, dreamSchedulePort } = params;
|
|
16
41
|
const empty = isLifeEvidenceSliceEmpty(runtime.lifeEvidence);
|
|
17
42
|
if (empty) {
|
|
18
43
|
const input = {
|
|
@@ -117,12 +142,17 @@ export async function runSourceBackedQuiet(params) {
|
|
|
117
142
|
const p = await persistQuietArtifactToWorkspace(workspaceRoot, ack, reportWrite);
|
|
118
143
|
persistedRelativePath = p.relativePath;
|
|
119
144
|
}
|
|
145
|
+
// v7 T-V7C.C.3: After a successful source-backed Quiet write, auto-trigger Dream scheduling.
|
|
146
|
+
const dreamReason = await maybeScheduleDreamAfterQuiet(dreamSchedulePort, day);
|
|
147
|
+
const reasons = ["quiet_artifact_written", ...gq.hints.slice(0, 2)];
|
|
148
|
+
if (dreamReason)
|
|
149
|
+
reasons.push(dreamReason);
|
|
120
150
|
return {
|
|
121
151
|
result: {
|
|
122
152
|
scope: "rhythm",
|
|
123
153
|
status: "intent_selected",
|
|
124
154
|
selectedIntentId: candidate.id,
|
|
125
|
-
reasons
|
|
155
|
+
reasons,
|
|
126
156
|
},
|
|
127
157
|
artifactAck: ack,
|
|
128
158
|
persistedRelativePath,
|
|
@@ -1,36 +1,39 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Second Nature Runtime Service Entry
|
|
3
|
-
*
|
|
4
|
-
* This module provides the actual implementation for the `second-nature-runtime` service.
|
|
5
|
-
* It serves as the heartbeat host bridge candidate carrier and initializes the
|
|
6
|
-
* minimal runtime state needed for the plugin to function.
|
|
7
|
-
*
|
|
8
|
-
* Per ADR-005: heartbeat is the free-rhythm main entry; this service provides
|
|
9
|
-
* the stable runtime state that heartbeat rounds will interact with.
|
|
10
|
-
*/
|
|
11
|
-
export interface RuntimeServiceContext {
|
|
12
|
-
/** Workspace root path for state/observability databases */
|
|
13
|
-
workspaceRoot?: string;
|
|
14
|
-
/** Plugin configuration overrides */
|
|
15
|
-
config?: Record<string, unknown>;
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Second Nature Runtime Service Entry
|
|
3
|
+
*
|
|
4
|
+
* This module provides the actual implementation for the `second-nature-runtime` service.
|
|
5
|
+
* It serves as the heartbeat host bridge candidate carrier and initializes the
|
|
6
|
+
* minimal runtime state needed for the plugin to function.
|
|
7
|
+
*
|
|
8
|
+
* Per ADR-005: heartbeat is the free-rhythm main entry; this service provides
|
|
9
|
+
* the stable runtime state that heartbeat rounds will interact with.
|
|
10
|
+
*/
|
|
11
|
+
export interface RuntimeServiceContext {
|
|
12
|
+
/** Workspace root path for state/observability databases */
|
|
13
|
+
workspaceRoot?: string;
|
|
14
|
+
/** Plugin configuration overrides */
|
|
15
|
+
config?: Record<string, unknown>;
|
|
16
|
+
/** Runtime version — supplied by the plugin entry from its package manifest.
|
|
17
|
+
* Eliminates hard-coded version drift (previously `const version = "0.1.38"`). */
|
|
18
|
+
version?: string;
|
|
19
|
+
}
|
|
20
|
+
export interface RuntimeServiceHandle {
|
|
21
|
+
/** Service is ready and accepting requests */
|
|
22
|
+
ready: boolean;
|
|
23
|
+
/** Runtime version string */
|
|
24
|
+
version: string;
|
|
25
|
+
/** Close the runtime handle and release resources */
|
|
26
|
+
close: () => void;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Start the Second Nature runtime service.
|
|
30
|
+
*
|
|
31
|
+
* This is the non-empty implementation that replaces the previous `start() { return; }` shell.
|
|
32
|
+
* It initializes the minimal runtime state and returns a handle that can be used
|
|
33
|
+
* by the heartbeat host bridge.
|
|
34
|
+
*/
|
|
35
|
+
export declare function startRuntimeService(ctx?: RuntimeServiceContext): RuntimeServiceHandle;
|
|
36
|
+
/**
|
|
37
|
+
* Get the current runtime service handle, or null if not started.
|
|
38
|
+
*/
|
|
39
|
+
export declare function getRuntimeHandle(): RuntimeServiceHandle | null;
|
|
@@ -1,45 +1,44 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Second Nature Runtime Service Entry
|
|
3
|
-
*
|
|
4
|
-
* This module provides the actual implementation for the `second-nature-runtime` service.
|
|
5
|
-
* It serves as the heartbeat host bridge candidate carrier and initializes the
|
|
6
|
-
* minimal runtime state needed for the plugin to function.
|
|
7
|
-
*
|
|
8
|
-
* Per ADR-005: heartbeat is the free-rhythm main entry; this service provides
|
|
9
|
-
* the stable runtime state that heartbeat rounds will interact with.
|
|
10
|
-
*/
|
|
11
|
-
let activeHandle = null;
|
|
12
|
-
/**
|
|
13
|
-
* Start the Second Nature runtime service.
|
|
14
|
-
*
|
|
15
|
-
* This is the non-empty implementation that replaces the previous `start() { return; }` shell.
|
|
16
|
-
* It initializes the minimal runtime state and returns a handle that can be used
|
|
17
|
-
* by the heartbeat host bridge.
|
|
18
|
-
*/
|
|
19
|
-
export function startRuntimeService(ctx) {
|
|
20
|
-
if (activeHandle?.ready) {
|
|
21
|
-
return activeHandle;
|
|
22
|
-
}
|
|
23
|
-
// Initialize minimal runtime state
|
|
24
|
-
// In future iterations, this will connect to:
|
|
25
|
-
// - state-system (SQLite database initialization)
|
|
26
|
-
// - observability-system (event store setup)
|
|
27
|
-
// - control-plane-system (heartbeat bridge preparation)
|
|
28
|
-
const workspaceRoot = ctx?.workspaceRoot ?? process.cwd();
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Second Nature Runtime Service Entry
|
|
3
|
+
*
|
|
4
|
+
* This module provides the actual implementation for the `second-nature-runtime` service.
|
|
5
|
+
* It serves as the heartbeat host bridge candidate carrier and initializes the
|
|
6
|
+
* minimal runtime state needed for the plugin to function.
|
|
7
|
+
*
|
|
8
|
+
* Per ADR-005: heartbeat is the free-rhythm main entry; this service provides
|
|
9
|
+
* the stable runtime state that heartbeat rounds will interact with.
|
|
10
|
+
*/
|
|
11
|
+
let activeHandle = null;
|
|
12
|
+
/**
|
|
13
|
+
* Start the Second Nature runtime service.
|
|
14
|
+
*
|
|
15
|
+
* This is the non-empty implementation that replaces the previous `start() { return; }` shell.
|
|
16
|
+
* It initializes the minimal runtime state and returns a handle that can be used
|
|
17
|
+
* by the heartbeat host bridge.
|
|
18
|
+
*/
|
|
19
|
+
export function startRuntimeService(ctx) {
|
|
20
|
+
if (activeHandle?.ready) {
|
|
21
|
+
return activeHandle;
|
|
22
|
+
}
|
|
23
|
+
// Initialize minimal runtime state
|
|
24
|
+
// In future iterations, this will connect to:
|
|
25
|
+
// - state-system (SQLite database initialization)
|
|
26
|
+
// - observability-system (event store setup)
|
|
27
|
+
// - control-plane-system (heartbeat bridge preparation)
|
|
28
|
+
const workspaceRoot = ctx?.workspaceRoot ?? process.cwd();
|
|
29
|
+
const version = ctx?.version ?? "unknown";
|
|
30
|
+
activeHandle = {
|
|
31
|
+
ready: true,
|
|
32
|
+
version,
|
|
33
|
+
close() {
|
|
34
|
+
activeHandle = null;
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
return activeHandle;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Get the current runtime service handle, or null if not started.
|
|
41
|
+
*/
|
|
42
|
+
export function getRuntimeHandle() {
|
|
43
|
+
return activeHandle;
|
|
44
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dream Engine — orchestrates the hybrid memory consolidation pipeline.
|
|
3
|
+
*
|
|
4
|
+
* Pipeline: load inputs → consolidate (rules) → sample → redact →
|
|
5
|
+
* optional model insights → merge → validate → write output + trace.
|
|
6
|
+
*
|
|
7
|
+
* Contract:
|
|
8
|
+
* - Input store is never modified.
|
|
9
|
+
* - Output is always candidate until validation passes and lifecycle port accepts it.
|
|
10
|
+
* - Budget/redaction/timeout failures degrade gracefully with trace.
|
|
11
|
+
* Test coverage: tests/integration/dream/t7-1-1-dream-pipeline.test.ts
|
|
12
|
+
*/
|
|
13
|
+
import type { DreamEngineInput, DreamRunResult } from "./types.js";
|
|
14
|
+
export declare function runDream(input: DreamEngineInput): Promise<DreamRunResult>;
|