@haaaiawd/second-nature 0.1.23 → 0.1.25

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.
Files changed (34) hide show
  1. package/index.js +77 -0
  2. package/package.json +5 -5
  3. package/runtime/cli/commands/goal.d.ts +26 -0
  4. package/runtime/cli/commands/goal.js +159 -0
  5. package/runtime/cli/commands/index.js +37 -2
  6. package/runtime/cli/index.js +52 -0
  7. package/runtime/cli/ops/ops-router.d.ts +1 -1
  8. package/runtime/cli/ops/ops-router.js +55 -1
  9. package/runtime/cli/read-models/index.d.ts +14 -2
  10. package/runtime/cli/read-models/index.js +328 -97
  11. package/runtime/cli/read-models/types.d.ts +80 -0
  12. package/runtime/connectors/agent-network/agent-world/adapter.d.ts +11 -0
  13. package/runtime/connectors/agent-network/agent-world/adapter.js +58 -0
  14. package/runtime/connectors/agent-network/agent-world/index.d.ts +2 -0
  15. package/runtime/connectors/agent-network/agent-world/index.js +2 -0
  16. package/runtime/connectors/agent-network/agent-world/manifest.d.ts +2 -0
  17. package/runtime/connectors/agent-network/agent-world/manifest.js +7 -0
  18. package/runtime/connectors/services/connector-executor-adapter.js +49 -0
  19. package/runtime/core/second-nature/heartbeat/heartbeat-loop.d.ts +3 -0
  20. package/runtime/core/second-nature/heartbeat/heartbeat-loop.js +33 -6
  21. package/runtime/core/second-nature/orchestrator/goal-priority.d.ts +2 -1
  22. package/runtime/core/second-nature/orchestrator/goal-priority.js +8 -7
  23. package/runtime/core/second-nature/outreach/build-outreach-draft-request.d.ts +3 -1
  24. package/runtime/core/second-nature/outreach/build-outreach-draft-request.js +39 -1
  25. package/runtime/core/second-nature/outreach/dispatch-user-outreach.js +21 -2
  26. package/runtime/guidance/draft-outreach-message.js +14 -1
  27. package/runtime/guidance/outreach-draft-schema.d.ts +104 -0
  28. package/runtime/guidance/outreach-draft-schema.js +14 -0
  29. package/runtime/observability/audit/audit-envelope.d.ts +1 -1
  30. package/runtime/observability/services/lived-experience-audit.d.ts +22 -0
  31. package/runtime/observability/services/lived-experience-audit.js +30 -0
  32. package/runtime/storage/db/schema/narrative-state.d.ts +1 -1
  33. package/runtime/storage/db/schema/narrative-state.js +2 -2
  34. package/workspace-ops-bridge.js +1 -0
@@ -5,8 +5,10 @@ import { createConnectorPolicyLayer } from "../base/policy-layer.js";
5
5
  import { InMemoryEffectCommitLedger } from "../base/execution-policy.js";
6
6
  import { moltbookManifest } from "../social-community/moltbook/manifest.js";
7
7
  import { evomapManifest } from "../agent-network/evomap/manifest.js";
8
+ import { agentWorldManifest } from "../agent-network/agent-world/manifest.js";
8
9
  import { createMoltbookApiClient } from "../social-community/moltbook/api-client.js";
9
10
  import { createMoltbookRunner } from "../social-community/moltbook/adapter.js";
11
+ import { createAgentWorldRunner } from "../agent-network/agent-world/adapter.js";
10
12
  import { ExecutionTelemetry } from "../../observability/services/execution-telemetry.js";
11
13
  import { createCredentialVault } from "../../storage/services/credential-vault.js";
12
14
  import { createCredentialRouteContextPort } from "./credential-route-context.js";
@@ -74,6 +76,52 @@ function createAdaptiveExecutionRunner(vault) {
74
76
  },
75
77
  };
76
78
  }
79
+ if (platformId === "agent-world") {
80
+ const baseUrl = process.env.SECOND_NATURE_AGENT_WORLD_BASE_URL;
81
+ if (!baseUrl) {
82
+ return {
83
+ platformId,
84
+ channel: request.preferredChannel ?? "api_rest",
85
+ latencyMs: Date.now() - started,
86
+ success: false,
87
+ error: {
88
+ code: "configuration_missing",
89
+ detail: "SECOND_NATURE_AGENT_WORLD_BASE_URL not set",
90
+ },
91
+ };
92
+ }
93
+ const runner = createAgentWorldRunner({
94
+ apiClient: {
95
+ async readFeed(payload, _apiKey) {
96
+ const resp = await fetch(`${baseUrl}/api/v1/feed`, {
97
+ headers: { "Authorization": `Bearer ${_apiKey}`, "Content-Type": "application/json" },
98
+ });
99
+ if (!resp.ok)
100
+ throw { code: "api_error", detail: `agent-world feed: ${resp.status}` };
101
+ return resp.json();
102
+ },
103
+ async discoverWork(payload, _apiKey) {
104
+ const resp = await fetch(`${baseUrl}/api/v1/work`, {
105
+ headers: { "Authorization": `Bearer ${_apiKey}`, "Content-Type": "application/json" },
106
+ });
107
+ if (!resp.ok)
108
+ throw { code: "api_error", detail: `agent-world work: ${resp.status}` };
109
+ return resp.json();
110
+ },
111
+ async claimTask(payload, _apiKey) {
112
+ const resp = await fetch(`${baseUrl}/api/v1/tasks/${payload.taskId ?? "unknown"}/claim`, {
113
+ method: "POST",
114
+ headers: { "Authorization": `Bearer ${_apiKey}`, "Content-Type": "application/json" },
115
+ body: JSON.stringify(payload),
116
+ });
117
+ if (!resp.ok)
118
+ throw { code: "api_error", detail: `agent-world claim: ${resp.status}` };
119
+ return resp.json();
120
+ },
121
+ },
122
+ });
123
+ return runner.run(_plan, request);
124
+ }
77
125
  return {
78
126
  platformId,
79
127
  channel: request.preferredChannel ?? "api_rest",
@@ -92,6 +140,7 @@ export function createConnectorExecutorAdapter(options) {
92
140
  const registry = new CapabilityContractRegistry();
93
141
  registry.register({ ...moltbookManifest });
94
142
  registry.register({ ...evomapManifest });
143
+ registry.register({ ...agentWorldManifest });
95
144
  const routeContextPort = createCredentialRouteContextPort(vault);
96
145
  const routePlanner = new ConnectorRoutePlanner(registry, routeContextPort, new ChannelHealthStore());
97
146
  const telemetry = new ExecutionTelemetry(options.observabilityDb);
@@ -21,6 +21,7 @@ import type { StateDatabase } from "../../../storage/db/index.js";
21
21
  import { type OpenClawDeliveryPort } from "../outreach/dispatch-user-outreach.js";
22
22
  import type { ConnectorExecutor } from "../../../connectors/base/contract.js";
23
23
  import type { NarrativeStateStore } from "../../../storage/narrative/narrative-state-store.js";
24
+ import type { NarrativeTracePayload } from "../../../observability/services/lived-experience-audit.js";
24
25
  export interface HeartbeatDecisionTracePayload {
25
26
  scope: RuntimeScope;
26
27
  status: HeartbeatCycleStatus;
@@ -61,6 +62,8 @@ export interface HeartbeatDeps {
61
62
  connectorExecutor?: ConnectorExecutor;
62
63
  /** T2.1.5: when present, heartbeat writes a source-backed NarrativeState revision after each cycle. */
63
64
  narrativeStateStore?: NarrativeStateStore;
65
+ /** T5.1.2: when present, heartbeat records a NarrativeTrace after successful narrative state update. */
66
+ recordNarrativeTrace?: (payload: NarrativeTracePayload) => Promise<void>;
64
67
  }
65
68
  /**
66
69
  * Ingest a heartbeat rhythm signal and drive one full decision round.
@@ -91,7 +91,7 @@ export async function resolveAllowedIntentResult(intent, runtime, inputs, signal
91
91
  * is never blocked by a store failure. Store failures are optionally traced
92
92
  * via recordDecisionTrace so operators can monitor store health.
93
93
  */
94
- async function maybeUpdateNarrativeState(result, selectedIntent, runtime, store, recordTrace, signal) {
94
+ async function maybeUpdateNarrativeState(result, selectedIntent, runtime, store, recordTrace, signal, recordNarrativeTrace) {
95
95
  if (!store)
96
96
  return;
97
97
  try {
@@ -103,6 +103,33 @@ async function maybeUpdateNarrativeState(result, selectedIntent, runtime, store,
103
103
  priorNarrative: prior,
104
104
  });
105
105
  await store.updateNarrativeState(update);
106
+ // T5.1.2: record NarrativeTrace on successful state update
107
+ if (recordNarrativeTrace) {
108
+ try {
109
+ await recordNarrativeTrace({
110
+ traceId: `narrative_trace:${crypto.randomUUID()}`,
111
+ narrativeId: update.narrativeId,
112
+ revision: update.revision,
113
+ updateSource: "heartbeat",
114
+ sourceRefs: update.sourceRefs.map((r) => ({
115
+ id: r.sourceId,
116
+ kind: r.kind,
117
+ uri: r.url,
118
+ })),
119
+ unsupportedClaims: update.unsupportedClaims,
120
+ groundingStatus: update.unsupportedClaims.length > 0
121
+ ? "degraded"
122
+ : update.status === "insufficient_sources"
123
+ ? "blocked"
124
+ : "pass",
125
+ goalInfluenceRefs: selectedIntent?.goalInfluenceRefs ?? [],
126
+ createdAt: update.updatedAt,
127
+ });
128
+ }
129
+ catch {
130
+ // trace emission must not block the cycle
131
+ }
132
+ }
106
133
  }
107
134
  catch {
108
135
  // degrade silently; narrative update is best-effort
@@ -178,7 +205,7 @@ export async function ingestRhythmSignal(signal, deps) {
178
205
  ? { ...resolved, reasons: evaluation.reasons }
179
206
  : resolved;
180
207
  await emitTrace(result);
181
- await maybeUpdateNarrativeState(result, intent, runtime, deps.narrativeStateStore, deps.recordDecisionTrace, signal);
208
+ await maybeUpdateNarrativeState(result, intent, runtime, deps.narrativeStateStore, deps.recordDecisionTrace, signal, deps.recordNarrativeTrace);
182
209
  return result;
183
210
  }
184
211
  if (evaluation.verdict === "defer") {
@@ -196,7 +223,7 @@ export async function ingestRhythmSignal(signal, deps) {
196
223
  reasons: ["silent_no_candidates"],
197
224
  };
198
225
  await emitTrace(result);
199
- await maybeUpdateNarrativeState(result, undefined, runtime, deps.narrativeStateStore, deps.recordDecisionTrace, signal);
226
+ await maybeUpdateNarrativeState(result, undefined, runtime, deps.narrativeStateStore, deps.recordDecisionTrace, signal, deps.recordNarrativeTrace);
200
227
  return result;
201
228
  }
202
229
  if (!anyAllow && anyDefer && !anyDeny) {
@@ -206,7 +233,7 @@ export async function ingestRhythmSignal(signal, deps) {
206
233
  reasons: denyReasons.length > 0 ? denyReasons : ["all_candidates_deferred"],
207
234
  };
208
235
  await emitTrace(result);
209
- await maybeUpdateNarrativeState(result, undefined, runtime, deps.narrativeStateStore, deps.recordDecisionTrace, signal);
236
+ await maybeUpdateNarrativeState(result, undefined, runtime, deps.narrativeStateStore, deps.recordDecisionTrace, signal, deps.recordNarrativeTrace);
210
237
  return result;
211
238
  }
212
239
  if (!anyAllow && denyReasons.length > 0) {
@@ -216,7 +243,7 @@ export async function ingestRhythmSignal(signal, deps) {
216
243
  reasons: denyReasons,
217
244
  };
218
245
  await emitTrace(result);
219
- await maybeUpdateNarrativeState(result, undefined, runtime, deps.narrativeStateStore, deps.recordDecisionTrace, signal);
246
+ await maybeUpdateNarrativeState(result, undefined, runtime, deps.narrativeStateStore, deps.recordDecisionTrace, signal, deps.recordNarrativeTrace);
220
247
  return result;
221
248
  }
222
249
  const result = {
@@ -225,7 +252,7 @@ export async function ingestRhythmSignal(signal, deps) {
225
252
  reasons: ["no_allow_verdict"],
226
253
  };
227
254
  await emitTrace(result);
228
- await maybeUpdateNarrativeState(result, undefined, runtime, deps.narrativeStateStore, deps.recordDecisionTrace, signal);
255
+ await maybeUpdateNarrativeState(result, undefined, runtime, deps.narrativeStateStore, deps.recordDecisionTrace, signal, deps.recordNarrativeTrace);
229
256
  return result;
230
257
  }
231
258
  /**
@@ -3,7 +3,8 @@
3
3
  *
4
4
  * `applyGoalPriority` adjusts candidate intent priorities based on accepted AgentGoals.
5
5
  * Priority order: user_task > accepted_goal > rhythm.
6
- * Only goals with status === "accepted" and origin !== "agent_proposed" are considered.
6
+ * Only goals with status === "accepted" are considered.
7
+ * Agent-proposed goals are included ONLY if policy-accepted (acceptedBy === "policy_allowlist").
7
8
  * All other statuses (proposal / rejected / completed / paused) are implicitly excluded.
8
9
  */
9
10
  import type { CandidateIntent } from "../types.js";
@@ -7,14 +7,14 @@
7
7
  */
8
8
  const GOAL_PRIORITY_BOOST = 20;
9
9
  function isGoalRelatedToCandidate(goal, candidate) {
10
- if (!candidate.platformId)
11
- return false;
12
10
  const goalText = `${goal.description} ${goal.completionCriteria}`.toLowerCase();
13
- const platformId = candidate.platformId.toLowerCase();
14
11
  // Direct platformId mention in goal text
15
- if (goalText.includes(platformId))
16
- return true;
17
- // Goal description contains candidate summary keywords
12
+ if (candidate.platformId) {
13
+ const platformId = candidate.platformId.toLowerCase();
14
+ if (goalText.includes(platformId))
15
+ return true;
16
+ }
17
+ // Fallback: Goal description contains candidate summary keywords
18
18
  const summaryWords = candidate.summary.toLowerCase().split(/\s+/);
19
19
  for (const word of summaryWords) {
20
20
  if (word.length > 3 && goalText.includes(word))
@@ -23,7 +23,8 @@ function isGoalRelatedToCandidate(goal, candidate) {
23
23
  return false;
24
24
  }
25
25
  export function applyGoalPriority(candidates, goals) {
26
- const acceptedGoals = (goals ?? []).filter((g) => g.status === "accepted" && g.origin !== "agent_proposed");
26
+ const acceptedGoals = (goals ?? []).filter((g) => g.status === "accepted" &&
27
+ (g.origin !== "agent_proposed" || g.acceptedBy === "policy_allowlist"));
27
28
  if (acceptedGoals.length === 0) {
28
29
  return {
29
30
  candidates: candidates.map((c) => ({
@@ -3,4 +3,6 @@ import type { CandidateIntent } from "../types.js";
3
3
  import type { HeartbeatRuntimeSnapshot } from "../heartbeat/runtime-snapshot.js";
4
4
  import type { OutreachJudgment } from "./judge-outreach.js";
5
5
  import type { DeliveryTargetResolution } from "./delivery-target.js";
6
- export declare function buildOutreachDraftRequest(candidate: CandidateIntent, judgment: OutreachJudgment, snapshot: HeartbeatRuntimeSnapshot, delivery: DeliveryTargetResolution): OutreachDraftRequest;
6
+ import type { NarrativeState } from "../../../storage/narrative/narrative-state-store.js";
7
+ import type { RelationshipMemory } from "../../../storage/relationship/relationship-memory-store.js";
8
+ export declare function buildOutreachDraftRequest(candidate: CandidateIntent, judgment: OutreachJudgment, snapshot: HeartbeatRuntimeSnapshot, delivery: DeliveryTargetResolution, narrativeState?: NarrativeState, relationshipMemory?: RelationshipMemory): OutreachDraftRequest;
@@ -40,7 +40,43 @@ function mapDeliveryVerdict(verdict) {
40
40
  return "host_unsupported";
41
41
  }
42
42
  }
43
- export function buildOutreachDraftRequest(candidate, judgment, snapshot, delivery) {
43
+ function buildNarrativeContext(state) {
44
+ if (!state)
45
+ return undefined;
46
+ return {
47
+ focus: state.focus || undefined,
48
+ progress: state.progress.length > 0 ? state.progress : undefined,
49
+ nextIntent: state.nextIntent || undefined,
50
+ sourceRefs: state.sourceRefs.map((r) => ({
51
+ id: r.sourceId,
52
+ kind: "user_anchor",
53
+ uri: r.url || "",
54
+ excerptHash: r.snippet,
55
+ observedAt: undefined,
56
+ })),
57
+ };
58
+ }
59
+ function buildRelationshipContext(memory) {
60
+ if (!memory)
61
+ return undefined;
62
+ const avgAffinity = memory.topicAffinities.length > 0
63
+ ? memory.topicAffinities.reduce((s, t) => s + t.affinity, 0) /
64
+ memory.topicAffinities.length
65
+ : 0;
66
+ return {
67
+ tone: memory.tonePreference,
68
+ topicAffinities: memory.topicAffinities.map((t) => t.topic),
69
+ avgAffinity,
70
+ sourceRefs: memory.sourceRefs?.map((r) => ({
71
+ id: r.sourceId,
72
+ kind: "user_anchor",
73
+ uri: r.url || "",
74
+ excerptHash: r.snippet,
75
+ observedAt: undefined,
76
+ })),
77
+ };
78
+ }
79
+ export function buildOutreachDraftRequest(candidate, judgment, snapshot, delivery, narrativeState, relationshipMemory) {
44
80
  const sceneType = delivery.verdict === "target_available" ? "outreach" : "fallback_candidate";
45
81
  const riskLevel = delivery.verdict === "target_available" ? "medium" : "low";
46
82
  return {
@@ -55,6 +91,8 @@ export function buildOutreachDraftRequest(candidate, judgment, snapshot, deliver
55
91
  judgmentVerdict: judgment.verdict,
56
92
  valueScore: judgment.valueScore,
57
93
  interestRefs: toGuidanceRefs(judgment.interestRefs),
94
+ narrativeContext: buildNarrativeContext(narrativeState),
95
+ relationshipContext: buildRelationshipContext(relationshipMemory),
58
96
  deliveryContext: {
59
97
  deliveryVerdict: mapDeliveryVerdict(delivery.verdict),
60
98
  wordingMode: delivery.verdict === "target_available" ? "sendable" : "not_sent_fallback_candidate",
@@ -3,6 +3,8 @@ import { writeOperatorFallback } from "../../../storage/fallback/write-operator-
3
3
  import { judgeOutreach } from "./judge-outreach.js";
4
4
  import { resolveDeliveryTarget } from "./delivery-target.js";
5
5
  import { buildOutreachDraftRequest } from "./build-outreach-draft-request.js";
6
+ import { createNarrativeStateStore } from "../../../storage/narrative/narrative-state-store.js";
7
+ import { createRelationshipMemoryStore } from "../../../storage/relationship/relationship-memory-store.js";
6
8
  function toSourceRefs(refs) {
7
9
  return refs.map((r) => ({ ...r }));
8
10
  }
@@ -29,8 +31,25 @@ export async function dispatchUserOutreachIntent(input) {
29
31
  };
30
32
  }
31
33
  const deliveryResolution = resolveDeliveryTarget(judgeInput.delivery);
34
+ // T2.3.1: load narrative/relationship context for source-backed draft
35
+ let narrativeState;
36
+ let relationshipMemory;
37
+ try {
38
+ const narrativeStore = createNarrativeStateStore(state);
39
+ narrativeState = (await narrativeStore.loadNarrativeState()) ?? undefined;
40
+ }
41
+ catch {
42
+ // degrade silently; draft proceeds without narrative context
43
+ }
44
+ try {
45
+ const relStore = createRelationshipMemoryStore(state);
46
+ relationshipMemory = (await relStore.loadRelationshipMemory()) ?? undefined;
47
+ }
48
+ catch {
49
+ // degrade silently; draft proceeds without relationship context
50
+ }
32
51
  if (deliveryResolution.verdict !== "target_available") {
33
- const req = buildOutreachDraftRequest(candidate, judgment, snapshot, deliveryResolution);
52
+ const req = buildOutreachDraftRequest(candidate, judgment, snapshot, deliveryResolution, narrativeState, relationshipMemory);
34
53
  const draft = await guidance.draftOutreachMessage(req);
35
54
  const fb = await writeOperatorFallback(state, {
36
55
  reason: operatorReasonForUnavailable(deliveryResolution.verdict),
@@ -48,7 +67,7 @@ export async function dispatchUserOutreachIntent(input) {
48
67
  fallbackRef: fb.fallbackRef,
49
68
  };
50
69
  }
51
- const req = buildOutreachDraftRequest(candidate, judgment, snapshot, deliveryResolution);
70
+ const req = buildOutreachDraftRequest(candidate, judgment, snapshot, deliveryResolution, narrativeState, relationshipMemory);
52
71
  const draft = await guidance.draftOutreachMessage(req);
53
72
  if (draft.status !== "ready") {
54
73
  return {
@@ -3,9 +3,22 @@
3
3
  * Does not claim user-visible delivery when wordingMode is not_sent_fallback_candidate (T6.2.1 / ADR-004).
4
4
  */
5
5
  import { safeParseOutreachDraftRequest } from "./outreach-draft-schema.js";
6
+ function buildContextSummary(r) {
7
+ const parts = [];
8
+ if (r.narrativeContext?.focus) {
9
+ parts.push(`what=${r.narrativeContext.focus}`);
10
+ }
11
+ if (r.relationshipContext?.tone) {
12
+ parts.push(`tone=${r.relationshipContext.tone}`);
13
+ }
14
+ if (r.relationshipContext?.topicAffinities && r.relationshipContext.topicAffinities.length > 0) {
15
+ parts.push(`interests=${r.relationshipContext.topicAffinities.join(",")}`);
16
+ }
17
+ return parts.length > 0 ? `;context=${parts.join(";")}` : "";
18
+ }
6
19
  function baseDraftText(request) {
7
20
  const ids = request.sourceRefs.map((s) => s.id).join(",");
8
- return `draft:${request.candidateId}:grounded:${ids}`;
21
+ return `draft:${request.candidateId}:grounded:${ids}${buildContextSummary(request)}`;
9
22
  }
10
23
  export async function draftOutreachMessage(request) {
11
24
  const parsed = safeParseOutreachDraftRequest(request);
@@ -97,6 +97,46 @@ export declare const sceneGuidanceRequestSchema: z.ZodObject<{
97
97
  "zh-CN": "zh-CN";
98
98
  }>>;
99
99
  }, z.core.$strip>;
100
+ export declare const outreachNarrativeContextSchema: z.ZodObject<{
101
+ focus: z.ZodOptional<z.ZodString>;
102
+ progress: z.ZodOptional<z.ZodArray<z.ZodString>>;
103
+ nextIntent: z.ZodOptional<z.ZodString>;
104
+ sourceRefs: z.ZodOptional<z.ZodArray<z.ZodObject<{
105
+ id: z.ZodString;
106
+ kind: z.ZodEnum<{
107
+ platform_item: "platform_item";
108
+ workspace_artifact: "workspace_artifact";
109
+ decision_record: "decision_record";
110
+ user_anchor: "user_anchor";
111
+ connector_result: "connector_result";
112
+ host_report: "host_report";
113
+ fallback_artifact: "fallback_artifact";
114
+ }>;
115
+ uri: z.ZodString;
116
+ excerptHash: z.ZodOptional<z.ZodString>;
117
+ observedAt: z.ZodOptional<z.ZodString>;
118
+ }, z.core.$strip>>>;
119
+ }, z.core.$strip>;
120
+ export declare const outreachRelationshipContextSchema: z.ZodObject<{
121
+ tone: z.ZodOptional<z.ZodString>;
122
+ topicAffinities: z.ZodOptional<z.ZodArray<z.ZodString>>;
123
+ avgAffinity: z.ZodOptional<z.ZodNumber>;
124
+ sourceRefs: z.ZodOptional<z.ZodArray<z.ZodObject<{
125
+ id: z.ZodString;
126
+ kind: z.ZodEnum<{
127
+ platform_item: "platform_item";
128
+ workspace_artifact: "workspace_artifact";
129
+ decision_record: "decision_record";
130
+ user_anchor: "user_anchor";
131
+ connector_result: "connector_result";
132
+ host_report: "host_report";
133
+ fallback_artifact: "fallback_artifact";
134
+ }>;
135
+ uri: z.ZodString;
136
+ excerptHash: z.ZodOptional<z.ZodString>;
137
+ observedAt: z.ZodOptional<z.ZodString>;
138
+ }, z.core.$strip>>>;
139
+ }, z.core.$strip>;
100
140
  export declare const outreachDraftRequestSchema: z.ZodObject<{
101
141
  requestId: z.ZodString;
102
142
  runtimeScope: z.ZodEnum<{
@@ -177,6 +217,46 @@ export declare const outreachDraftRequestSchema: z.ZodObject<{
177
217
  excerptHash: z.ZodOptional<z.ZodString>;
178
218
  observedAt: z.ZodOptional<z.ZodString>;
179
219
  }, z.core.$strip>>;
220
+ narrativeContext: z.ZodOptional<z.ZodObject<{
221
+ focus: z.ZodOptional<z.ZodString>;
222
+ progress: z.ZodOptional<z.ZodArray<z.ZodString>>;
223
+ nextIntent: z.ZodOptional<z.ZodString>;
224
+ sourceRefs: z.ZodOptional<z.ZodArray<z.ZodObject<{
225
+ id: z.ZodString;
226
+ kind: z.ZodEnum<{
227
+ platform_item: "platform_item";
228
+ workspace_artifact: "workspace_artifact";
229
+ decision_record: "decision_record";
230
+ user_anchor: "user_anchor";
231
+ connector_result: "connector_result";
232
+ host_report: "host_report";
233
+ fallback_artifact: "fallback_artifact";
234
+ }>;
235
+ uri: z.ZodString;
236
+ excerptHash: z.ZodOptional<z.ZodString>;
237
+ observedAt: z.ZodOptional<z.ZodString>;
238
+ }, z.core.$strip>>>;
239
+ }, z.core.$strip>>;
240
+ relationshipContext: z.ZodOptional<z.ZodObject<{
241
+ tone: z.ZodOptional<z.ZodString>;
242
+ topicAffinities: z.ZodOptional<z.ZodArray<z.ZodString>>;
243
+ avgAffinity: z.ZodOptional<z.ZodNumber>;
244
+ sourceRefs: z.ZodOptional<z.ZodArray<z.ZodObject<{
245
+ id: z.ZodString;
246
+ kind: z.ZodEnum<{
247
+ platform_item: "platform_item";
248
+ workspace_artifact: "workspace_artifact";
249
+ decision_record: "decision_record";
250
+ user_anchor: "user_anchor";
251
+ connector_result: "connector_result";
252
+ host_report: "host_report";
253
+ fallback_artifact: "fallback_artifact";
254
+ }>;
255
+ uri: z.ZodString;
256
+ excerptHash: z.ZodOptional<z.ZodString>;
257
+ observedAt: z.ZodOptional<z.ZodString>;
258
+ }, z.core.$strip>>>;
259
+ }, z.core.$strip>>;
180
260
  }, z.core.$strip>;
181
261
  export type SceneGuidanceRequest = z.infer<typeof sceneGuidanceRequestSchema>;
182
262
  export type OutreachDraftRequest = z.infer<typeof outreachDraftRequestSchema>;
@@ -212,6 +292,30 @@ export declare function safeParseOutreachDraftRequest(input: unknown): z.ZodSafe
212
292
  fallbackRef?: string | undefined;
213
293
  } | undefined;
214
294
  language?: "en-US" | "zh-CN" | undefined;
295
+ narrativeContext?: {
296
+ focus?: string | undefined;
297
+ progress?: string[] | undefined;
298
+ nextIntent?: string | undefined;
299
+ sourceRefs?: {
300
+ id: string;
301
+ kind: "platform_item" | "workspace_artifact" | "decision_record" | "user_anchor" | "connector_result" | "host_report" | "fallback_artifact";
302
+ uri: string;
303
+ excerptHash?: string | undefined;
304
+ observedAt?: string | undefined;
305
+ }[] | undefined;
306
+ } | undefined;
307
+ relationshipContext?: {
308
+ tone?: string | undefined;
309
+ topicAffinities?: string[] | undefined;
310
+ avgAffinity?: number | undefined;
311
+ sourceRefs?: {
312
+ id: string;
313
+ kind: "platform_item" | "workspace_artifact" | "decision_record" | "user_anchor" | "connector_result" | "host_report" | "fallback_artifact";
314
+ uri: string;
315
+ excerptHash?: string | undefined;
316
+ observedAt?: string | undefined;
317
+ }[] | undefined;
318
+ } | undefined;
215
319
  }>;
216
320
  /** Async seam for generative outreach copy (implementation lives outside control-plane). */
217
321
  export interface GuidanceDraftPort {
@@ -54,6 +54,18 @@ export const sceneGuidanceRequestSchema = z.object({
54
54
  deliveryContext: deliveryExpressionContextSchema.optional(),
55
55
  language: z.enum(["zh-CN", "en-US"]).optional(),
56
56
  });
57
+ export const outreachNarrativeContextSchema = z.object({
58
+ focus: z.string().optional(),
59
+ progress: z.array(z.string()).optional(),
60
+ nextIntent: z.string().optional(),
61
+ sourceRefs: z.array(guidanceSourceRefSchema).optional(),
62
+ });
63
+ export const outreachRelationshipContextSchema = z.object({
64
+ tone: z.string().optional(),
65
+ topicAffinities: z.array(z.string()).optional(),
66
+ avgAffinity: z.number().optional(),
67
+ sourceRefs: z.array(guidanceSourceRefSchema).optional(),
68
+ });
57
69
  export const outreachDraftRequestSchema = sceneGuidanceRequestSchema
58
70
  .extend({
59
71
  sceneType: z.enum(["outreach", "fallback_candidate"]),
@@ -62,6 +74,8 @@ export const outreachDraftRequestSchema = sceneGuidanceRequestSchema
62
74
  judgmentVerdict: z.enum(["allow", "deny", "defer"]),
63
75
  valueScore: z.number(),
64
76
  interestRefs: z.array(guidanceSourceRefSchema),
77
+ narrativeContext: outreachNarrativeContextSchema.optional(),
78
+ relationshipContext: outreachRelationshipContextSchema.optional(),
65
79
  })
66
80
  .superRefine((val, ctx) => {
67
81
  if (!val.deliveryContext) {
@@ -1,6 +1,6 @@
1
1
  import { type RedactionManifest as FieldRedactionManifest } from "../redaction/manifest.js";
2
2
  export type AuditPlane = "decision" | "delivery" | "source_coverage" | "governance" | "telemetry";
3
- export type AuditEventFamily = "heartbeat.decision" | "delivery" | "source_coverage" | "guidance.grounding" | "host_capability" | "connector.attempt" | "state.governance";
3
+ export type AuditEventFamily = "heartbeat.decision" | "delivery" | "source_coverage" | "guidance.grounding" | "host_capability" | "connector.attempt" | "state.governance" | "narrative.trace" | "dream.trace";
4
4
  export type AuditEnvelopeSensitivity = "public" | "internal" | "private" | "credential" | "sensitive";
5
5
  export interface AuditRedactionManifest {
6
6
  manifestId: string;
@@ -1,5 +1,6 @@
1
1
  import { AppendOnlyAuditStore } from "../audit/append-only-audit-store.js";
2
2
  import type { SourceRef } from "../../storage/life-evidence/types.js";
3
+ import type { DreamTrace } from "../../dream/types.js";
3
4
  export type RuntimeScope = "rhythm" | "user_task" | "user_reply";
4
5
  export type HeartbeatOutcome = "heartbeat_ok" | "intent_selected" | "denied" | "deferred" | "runtime_carrier_only" | "delivery_unavailable";
5
6
  export type DeliveryAuditStatus = "not_requested" | "target_available" | "target_none" | "channel_missing" | "host_unsupported" | "ack_dropped" | "sent" | "failed" | "not_sent_fallback";
@@ -66,6 +67,21 @@ export interface GuidanceGroundingAuditPayload {
66
67
  deliveryWording?: "sendable" | "not_sent_fallback_candidate";
67
68
  createdAt: string;
68
69
  }
70
+ export interface NarrativeTracePayload {
71
+ traceId: string;
72
+ narrativeId: string;
73
+ revision: number;
74
+ updateSource: "heartbeat" | "dream" | "owner" | "maintenance";
75
+ sourceRefs: Array<{
76
+ id: string;
77
+ kind: string;
78
+ uri?: string;
79
+ }>;
80
+ unsupportedClaims: string[];
81
+ groundingStatus: GroundingStatus;
82
+ goalInfluenceRefs: string[];
83
+ createdAt: string;
84
+ }
69
85
  export interface ExplainLinkageSummary {
70
86
  decisionId: string;
71
87
  summary: string;
@@ -92,6 +108,12 @@ export declare class LivedExperienceAuditRecorder {
92
108
  recordGuidanceGrounding(payload: GuidanceGroundingAuditPayload): {
93
109
  eventId: string;
94
110
  };
111
+ recordNarrativeTrace(payload: NarrativeTracePayload): {
112
+ eventId: string;
113
+ };
114
+ recordDreamTrace(payload: DreamTrace): {
115
+ eventId: string;
116
+ };
95
117
  explainLinkageForDecision(decisionId: string): ExplainLinkageSummary;
96
118
  }
97
119
  export declare function createLivedExperienceAuditRecorder(store?: AppendOnlyAuditStore): LivedExperienceAuditRecorder;
@@ -133,6 +133,36 @@ export class LivedExperienceAuditRecorder {
133
133
  }
134
134
  return { eventId: envelope.eventId };
135
135
  }
136
+ recordNarrativeTrace(payload) {
137
+ const seq = this.bumpSequence();
138
+ const envelope = buildAuditEnvelope({
139
+ family: "narrative.trace",
140
+ plane: "source_coverage",
141
+ traceId: payload.traceId,
142
+ sequence: seq,
143
+ payload,
144
+ previousHash: this.store.lastRecordHash(),
145
+ eventId: crypto.randomUUID(),
146
+ createdAt: payload.createdAt,
147
+ });
148
+ this.store.append(envelope);
149
+ return { eventId: envelope.eventId };
150
+ }
151
+ recordDreamTrace(payload) {
152
+ const seq = this.bumpSequence();
153
+ const envelope = buildAuditEnvelope({
154
+ family: "dream.trace",
155
+ plane: "telemetry",
156
+ traceId: payload.traceId,
157
+ sequence: seq,
158
+ payload,
159
+ previousHash: this.store.lastRecordHash(),
160
+ eventId: crypto.randomUUID(),
161
+ createdAt: payload.finishedAt,
162
+ });
163
+ this.store.append(envelope);
164
+ return { eventId: envelope.eventId };
165
+ }
136
166
  explainLinkageForDecision(decisionId) {
137
167
  const entry = this.explainIndex.get(decisionId);
138
168
  const warnings = [];
@@ -99,7 +99,7 @@ export declare const narrativeState: import("drizzle-orm/sqlite-core").SQLiteTab
99
99
  name: "confidence";
100
100
  tableName: "narrative_state";
101
101
  dataType: "number";
102
- columnType: "SQLiteInteger";
102
+ columnType: "SQLiteReal";
103
103
  data: number;
104
104
  driverParam: number;
105
105
  notNull: true;
@@ -1,11 +1,11 @@
1
- import { index, sqliteTable, text, integer } from "drizzle-orm/sqlite-core";
1
+ import { index, sqliteTable, text, integer, real } from "drizzle-orm/sqlite-core";
2
2
  export const narrativeState = sqliteTable("narrative_state", {
3
3
  narrativeId: text("narrative_id").primaryKey(),
4
4
  revision: integer("revision").notNull().default(1),
5
5
  focus: text("focus").notNull(),
6
6
  progressJson: text("progress_json").notNull(),
7
7
  nextIntent: text("next_intent").notNull(),
8
- confidence: integer("confidence").notNull().default(0),
8
+ confidence: real("confidence").notNull().default(0),
9
9
  sourceRefsJson: text("source_refs_json").notNull(),
10
10
  unsupportedClaimsJson: text("unsupported_claims_json").notNull(),
11
11
  status: text("status").notNull(),
@@ -47,6 +47,7 @@ export async function openWorkspaceOpsBridge(workspaceRoot) {
47
47
  state: stateDb,
48
48
  workspaceRoot: resolvedRoot,
49
49
  connectorExecutor: deps.connectorExecutor,
50
+ registry: deps.registry,
50
51
  });
51
52
  const commands = commandsMod.createCliCommands({
52
53
  readModels: deps.readModels,