@haaaiawd/second-nature 0.1.38 → 0.1.40

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 (50) hide show
  1. package/agent-inner-guide.md +18 -0
  2. package/index.js +10 -2
  3. package/openclaw.plugin.json +2 -2
  4. package/package.json +1 -1
  5. package/runtime/cli/commands/connector-init.js +11 -4
  6. package/runtime/cli/index.js +6 -1
  7. package/runtime/cli/ops/heartbeat-surface.d.ts +15 -0
  8. package/runtime/cli/ops/heartbeat-surface.js +16 -2
  9. package/runtime/cli/ops/ops-router.js +229 -83
  10. package/runtime/cli/ops/workspace-heartbeat-runner.js +49 -4
  11. package/runtime/connectors/services/connector-executor-adapter.js +192 -41
  12. package/runtime/core/second-nature/guidance/apply-guidance.d.ts +2 -0
  13. package/runtime/core/second-nature/guidance/apply-guidance.js +6 -1
  14. package/runtime/core/second-nature/guidance/user-reply-continuity.d.ts +1 -1
  15. package/runtime/core/second-nature/guidance/user-reply-continuity.js +14 -5
  16. package/runtime/core/second-nature/orchestrator/intent-planner.js +15 -0
  17. package/runtime/core/second-nature/runtime/service-entry.d.ts +3 -0
  18. package/runtime/core/second-nature/runtime/service-entry.js +1 -2
  19. package/runtime/dream/dream-engine.d.ts +14 -0
  20. package/runtime/dream/dream-engine.js +306 -0
  21. package/runtime/dream/dream-input-loader.d.ts +37 -0
  22. package/runtime/dream/dream-input-loader.js +155 -0
  23. package/runtime/dream/dream-scheduler.d.ts +75 -0
  24. package/runtime/dream/dream-scheduler.js +131 -0
  25. package/runtime/dream/index.d.ts +16 -0
  26. package/runtime/dream/index.js +14 -0
  27. package/runtime/dream/insight-extractor.d.ts +32 -0
  28. package/runtime/dream/insight-extractor.js +135 -0
  29. package/runtime/dream/memory-consolidator.d.ts +45 -0
  30. package/runtime/dream/memory-consolidator.js +140 -0
  31. package/runtime/dream/narrative-update-proposal.d.ts +34 -0
  32. package/runtime/dream/narrative-update-proposal.js +83 -0
  33. package/runtime/dream/output-validator.d.ts +20 -0
  34. package/runtime/dream/output-validator.js +110 -0
  35. package/runtime/dream/redaction-gate.d.ts +31 -0
  36. package/runtime/dream/redaction-gate.js +109 -0
  37. package/runtime/dream/relationship-update-proposal.d.ts +27 -0
  38. package/runtime/dream/relationship-update-proposal.js +119 -0
  39. package/runtime/dream/sampler.d.ts +30 -0
  40. package/runtime/dream/sampler.js +65 -0
  41. package/runtime/dream/types.d.ts +187 -0
  42. package/runtime/dream/types.js +11 -0
  43. package/runtime/guidance/fallback.js +6 -3
  44. package/runtime/guidance/guidance-assembler.js +5 -3
  45. package/runtime/guidance/output-guard.d.ts +4 -1
  46. package/runtime/guidance/output-guard.js +24 -0
  47. package/runtime/guidance/template-registry.d.ts +5 -1
  48. package/runtime/guidance/template-registry.js +71 -30
  49. package/runtime/guidance/types.d.ts +14 -0
  50. package/runtime/observability/projections/guidance-audit.js +4 -1
@@ -5,6 +5,7 @@ import { createNarrativeStateStore } from "../../storage/narrative/narrative-sta
5
5
  import { createRelationshipMemoryStore } from "../../storage/relationship/relationship-memory-store.js";
6
6
  import { createIdentityProfileStore } from "../../storage/services/identity-profile-store.js";
7
7
  import { generateHeartbeatDigest, } from "../../observability/services/heartbeat-digest-assembler.js";
8
+ import { createHistoryDigestStore } from "../../storage/services/history-digest-store.js";
8
9
  export async function loadSnapshotInputsForWorkspaceHeartbeat(readModels, options = {}) {
9
10
  const status = await readModels.loadStatus();
10
11
  const mode = status.rhythm.mode === "unknown" ? "active" : status.rhythm.mode;
@@ -167,7 +168,8 @@ export function createWorkspaceHeartbeatRunner(readModels, options = {}) {
167
168
  // cycle outcome itself is still returned to the caller.
168
169
  }
169
170
  }
170
- // v7 T-V7C.C.3: After each cycle, attempt HeartbeatDigest generation if configured.
171
+ // v7 T-V7C.C.3 / T-V7C.C.6: After each cycle, attempt HeartbeatDigest generation
172
+ // and persist to heartbeat_digest table so the digest index actually grows.
171
173
  // Only runs inside the designated UTC digest window hour, or on every cycle when
172
174
  // digestWindowHour is unset (test / always-on mode).
173
175
  if (options.digestOpts) {
@@ -177,15 +179,58 @@ export function createWorkspaceHeartbeatRunner(readModels, options = {}) {
177
179
  if (inDigestWindow) {
178
180
  try {
179
181
  const date = new Date().toISOString().slice(0, 10);
180
- await generateHeartbeatDigest(date, assemblerDeps);
182
+ const assembledDigest = await generateHeartbeatDigest(date, assemblerDeps);
183
+ // v7 T-V7C.C.6: Persist assembled digest to heartbeat_digest table when state DB is wired.
184
+ if (options.state) {
185
+ const digestStore = createHistoryDigestStore(options.state);
186
+ await digestStore.writeHeartbeatDigest(toStoreDigest(assembledDigest));
187
+ }
181
188
  }
182
189
  catch (err) {
183
- // Digest generation must not break the heartbeat cycle response.
190
+ // Digest generation / persistence must not break the heartbeat cycle response.
184
191
  const msg = err instanceof Error ? err.message : String(err);
185
- console.warn(`[workspace-heartbeat-runner] Digest generation failed: ${msg}`);
192
+ console.warn(`[workspace-heartbeat-runner] Digest generation/persistence failed: ${msg}`);
186
193
  }
187
194
  }
188
195
  }
189
196
  return cycle;
190
197
  };
191
198
  }
199
+ /**
200
+ * Bridge: converts the assembler-facing HeartbeatDigest into the storage-facing
201
+ * HeartbeatDigest (shared/types/v7-entities.ts) so it can be written to heartbeat_digest.
202
+ *
203
+ * The two shapes diverge by design: assembler is an audit-aggregate rich view;
204
+ * store is a flattened day-keyed row. Mapping is lossy but sufficient for growth.
205
+ */
206
+ function toStoreDigest(d) {
207
+ return {
208
+ digestId: `digest:${d.date}:${Date.now()}`,
209
+ day: d.date,
210
+ connectorSummary: d.connectorSummary.map((c) => ({
211
+ platformId: c.platformId,
212
+ status: c.blockedCount > 0
213
+ ? "blocked"
214
+ : c.circuitOpenCount > 0
215
+ ? "blocked"
216
+ : c.failureCount > 0
217
+ ? "degraded"
218
+ : "ok",
219
+ attemptCount: c.successCount + c.failureCount + c.blockedCount,
220
+ })),
221
+ goalSummary: [
222
+ { kind: "new", activeCount: d.goalSummary.newGoals },
223
+ { kind: "completed", activeCount: d.goalSummary.completedGoals },
224
+ { kind: "expired", activeCount: d.goalSummary.expiredGoals },
225
+ { kind: "replaced", activeCount: d.goalSummary.replacedGoals },
226
+ { kind: "active", activeCount: d.goalSummary.activeGoals },
227
+ ].filter((g) => g.activeCount > 0),
228
+ quietCount: d.quietDreamSummary.quietRuns,
229
+ dreamCount: d.quietDreamSummary.dreamRuns,
230
+ breakerSummary: d.healthSummary.circuitBreakerChanges > 0
231
+ ? [{ connectorId: "aggregate", state: "changed" }]
232
+ : [],
233
+ healthStatus: d.healthSummary.auditChainHealthy ? "ok" : "degraded",
234
+ createdAt: d.generatedAt,
235
+ };
236
+ }
@@ -14,6 +14,8 @@ import { createCredentialVault } from "../../storage/services/credential-vault.j
14
14
  import { createCredentialRouteContextPort } from "./credential-route-context.js";
15
15
  import { scanConnectorManifests } from "../registry/manifest-scanner.js";
16
16
  import { parseConnectorManifestV6 } from "../manifest/manifest-parser.js";
17
+ import fs from "node:fs";
18
+ import path from "node:path";
17
19
  const DEFAULT_AGENT_WORLD_USERNAME = "nyx_ha";
18
20
  const DEFAULT_AGENT_WORLD_PROFILE_PATH_TEMPLATE = "/api/agents/profile/{username}";
19
21
  function readString(value) {
@@ -101,71 +103,206 @@ async function fetchAgentWorldJson(input) {
101
103
  }
102
104
  return resp.json();
103
105
  }
104
- function createAdaptiveExecutionRunner(vault) {
106
+ function createMoltbookMockRunner(workspaceRoot) {
105
107
  return {
106
108
  async run(_plan, request) {
107
- const platformId = request.platformId;
108
109
  const started = Date.now();
109
- if (platformId !== "moltbook" &&
110
- platformId !== "evomap" &&
111
- platformId !== "agent-world") {
110
+ const mockPath = workspaceRoot
111
+ ? path.join(workspaceRoot, ".second-nature", "mock", "moltbook-feed.json")
112
+ : undefined;
113
+ if (!mockPath || !fs.existsSync(mockPath)) {
112
114
  return {
113
- platformId,
115
+ platformId: request.platformId,
114
116
  channel: request.preferredChannel ?? "api_rest",
115
117
  latencyMs: Date.now() - started,
116
118
  success: false,
117
119
  error: {
118
- code: "unknown_platform",
119
- detail: `no execution runner for ${platformId}`,
120
+ code: "configuration_missing",
121
+ detail: "SECOND_NATURE_MOLTBOOK_BASE_URL not set and no mock data found",
120
122
  },
121
123
  };
122
124
  }
123
- const credential = await vault.loadCredentialContext(platformId);
124
- if (!credential ||
125
- credential.status !== "active" ||
126
- !credential.encryptedValue) {
125
+ try {
126
+ const raw = fs.readFileSync(mockPath, "utf-8");
127
+ const data = JSON.parse(raw);
127
128
  return {
128
- platformId,
129
+ platformId: request.platformId,
130
+ channel: request.preferredChannel ?? "api_rest",
131
+ latencyMs: Date.now() - started,
132
+ degraded: true,
133
+ success: true,
134
+ payload: {
135
+ capability: request.intent,
136
+ channel: request.preferredChannel ?? "api_rest",
137
+ data: {
138
+ source: "mock",
139
+ items: Array.isArray(data.items) ? data.items : [],
140
+ },
141
+ },
142
+ };
143
+ }
144
+ catch (err) {
145
+ return {
146
+ platformId: request.platformId,
129
147
  channel: request.preferredChannel ?? "api_rest",
130
148
  latencyMs: Date.now() - started,
131
149
  success: false,
132
150
  error: {
133
- code: "auth_failure",
134
- detail: "credential_unavailable_for_execution",
151
+ code: "mock_read_error",
152
+ detail: String(err),
135
153
  },
136
154
  };
137
155
  }
138
- if (platformId === "moltbook") {
139
- const baseUrl = process.env.SECOND_NATURE_MOLTBOOK_BASE_URL;
140
- if (!baseUrl) {
156
+ },
157
+ };
158
+ }
159
+ function findWorkspaceManifest(platformId, workspaceRoot) {
160
+ if (!workspaceRoot)
161
+ return undefined;
162
+ for (const file of scanConnectorManifests(workspaceRoot)) {
163
+ const parsed = parseConnectorManifestV6(file.content, file.path);
164
+ if (parsed.ok && parsed.manifest.platformId === platformId) {
165
+ return parsed.manifest;
166
+ }
167
+ }
168
+ return undefined;
169
+ }
170
+ function resolveDeclarativeHttpPath(capabilityId) {
171
+ return `/${capabilityId.replace(/\./g, "/")}`;
172
+ }
173
+ function resolveDeclarativeHttpMethod(capabilityId) {
174
+ const readOps = ["read", "list", "discover", "get", "heartbeat", "fetch"];
175
+ if (readOps.some((op) => capabilityId.includes(op)))
176
+ return "GET";
177
+ return "POST";
178
+ }
179
+ function createDeclarativeHttpRunner(manifest, credential) {
180
+ return {
181
+ async run(plan, request) {
182
+ const started = Date.now();
183
+ const baseUrl = manifest.runner.config?.baseUrl ?? "";
184
+ if (!baseUrl) {
185
+ return {
186
+ platformId: request.platformId,
187
+ channel: plan.channel,
188
+ latencyMs: Date.now() - started,
189
+ success: false,
190
+ error: {
191
+ code: "configuration_missing",
192
+ detail: "runner.config.baseUrl not set for declarative_http connector",
193
+ },
194
+ };
195
+ }
196
+ const httpPath = resolveDeclarativeHttpPath(request.intent);
197
+ const method = resolveDeclarativeHttpMethod(request.intent);
198
+ try {
199
+ const headers = {
200
+ "Content-Type": "application/json",
201
+ };
202
+ if (credential?.encryptedValue) {
203
+ headers.Authorization = `Bearer ${credential.encryptedValue}`;
204
+ }
205
+ const resp = await fetch(`${baseUrl.replace(/\/+$/, "")}${httpPath}`, {
206
+ method,
207
+ headers,
208
+ body: method !== "GET" && request.payload ? JSON.stringify(request.payload) : undefined,
209
+ });
210
+ if (!resp.ok) {
141
211
  return {
142
- platformId,
143
- channel: request.preferredChannel ?? "api_rest",
212
+ platformId: request.platformId,
213
+ channel: plan.channel,
144
214
  latencyMs: Date.now() - started,
145
215
  success: false,
146
216
  error: {
147
- code: "configuration_missing",
148
- detail: "SECOND_NATURE_MOLTBOOK_BASE_URL not set",
217
+ code: "api_error",
218
+ detail: `HTTP ${resp.status}: ${await resp.text().catch(() => "")}`,
149
219
  },
150
220
  };
151
221
  }
152
- const apiClient = createMoltbookApiClient({
153
- baseUrl,
154
- accessToken: credential.encryptedValue,
155
- timeoutMs: 10000,
156
- });
157
- const runner = createMoltbookRunner({
158
- apiClient,
159
- skillRunner: {
160
- run: async () => {
161
- throw {
162
- code: "protocol_mismatch",
163
- detail: "moltbook_skill_runner_not_configured",
164
- };
165
- },
222
+ const data = await resp.json();
223
+ return {
224
+ platformId: request.platformId,
225
+ channel: plan.channel,
226
+ latencyMs: Date.now() - started,
227
+ success: true,
228
+ payload: {
229
+ capability: request.intent,
230
+ channel: plan.channel,
231
+ data,
166
232
  },
167
- });
168
- return runner.run(_plan, request);
233
+ };
234
+ }
235
+ catch (err) {
236
+ return {
237
+ platformId: request.platformId,
238
+ channel: plan.channel,
239
+ latencyMs: Date.now() - started,
240
+ success: false,
241
+ error: {
242
+ code: "network_error",
243
+ detail: String(err),
244
+ },
245
+ };
246
+ }
247
+ },
248
+ };
249
+ }
250
+ function createAdaptiveExecutionRunner(vault, workspaceRoot) {
251
+ return {
252
+ async run(_plan, request) {
253
+ const platformId = request.platformId;
254
+ const started = Date.now();
255
+ const workspaceManifest = findWorkspaceManifest(platformId, workspaceRoot);
256
+ const isBuiltInPlatform = platformId === "moltbook" ||
257
+ platformId === "evomap" ||
258
+ platformId === "agent-world";
259
+ const requiresCredential = isBuiltInPlatform ||
260
+ Boolean(workspaceManifest?.credentials.some((credential) => credential.required !== false));
261
+ const credential = requiresCredential
262
+ ? await vault.loadCredentialContext(platformId)
263
+ : undefined;
264
+ if (requiresCredential &&
265
+ (!credential ||
266
+ credential.status !== "active" ||
267
+ !credential.encryptedValue)) {
268
+ return {
269
+ platformId,
270
+ channel: request.preferredChannel ?? "api_rest",
271
+ latencyMs: Date.now() - started,
272
+ success: false,
273
+ error: {
274
+ code: "auth_failure",
275
+ detail: "credential_unavailable_for_execution",
276
+ },
277
+ };
278
+ }
279
+ const activeCredential = credential?.status === "active" && credential.encryptedValue
280
+ ? { encryptedValue: credential.encryptedValue }
281
+ : undefined;
282
+ if (platformId === "moltbook") {
283
+ const baseUrl = process.env.SECOND_NATURE_MOLTBOOK_BASE_URL;
284
+ if (baseUrl) {
285
+ const apiClient = createMoltbookApiClient({
286
+ baseUrl,
287
+ accessToken: activeCredential.encryptedValue,
288
+ timeoutMs: 10000,
289
+ });
290
+ const runner = createMoltbookRunner({
291
+ apiClient,
292
+ skillRunner: {
293
+ run: async () => {
294
+ throw {
295
+ code: "protocol_mismatch",
296
+ detail: "moltbook_skill_runner_not_configured",
297
+ };
298
+ },
299
+ },
300
+ });
301
+ return runner.run(_plan, request);
302
+ }
303
+ // Mock fallback when real API is not configured
304
+ const mockRunner = createMoltbookMockRunner(workspaceRoot);
305
+ return mockRunner.run(_plan, request);
169
306
  }
170
307
  if (platformId === "evomap") {
171
308
  return {
@@ -194,7 +331,7 @@ function createAdaptiveExecutionRunner(vault) {
194
331
  };
195
332
  }
196
333
  const runner = createAgentWorldRunner({
197
- apiKey: credential.encryptedValue,
334
+ apiKey: activeCredential.encryptedValue,
198
335
  apiClient: {
199
336
  async readFeed(payload, _apiKey) {
200
337
  const username = resolveAgentWorldUsername(payload, "feed");
@@ -235,7 +372,21 @@ function createAdaptiveExecutionRunner(vault) {
235
372
  });
236
373
  return runner.run(_plan, request);
237
374
  }
238
- throw new Error(`unreachable_connector_platform:${platformId}`);
375
+ // Wave 83: workspace declarative_http connector fallback
376
+ if (workspaceManifest && workspaceManifest.runner.kind === "declarative_http") {
377
+ const httpRunner = createDeclarativeHttpRunner(workspaceManifest, activeCredential);
378
+ return httpRunner.run(_plan, request);
379
+ }
380
+ return {
381
+ platformId,
382
+ channel: request.preferredChannel ?? "api_rest",
383
+ latencyMs: Date.now() - started,
384
+ success: false,
385
+ error: {
386
+ code: "unknown_platform",
387
+ detail: `no execution runner for ${platformId}`,
388
+ },
389
+ };
239
390
  },
240
391
  };
241
392
  }
@@ -249,7 +400,7 @@ export function createConnectorExecutorAdapter(options) {
249
400
  const routeContextPort = createCredentialRouteContextPort(vault);
250
401
  const routePlanner = new ConnectorRoutePlanner(registry, routeContextPort, new ChannelHealthStore());
251
402
  const telemetry = new ExecutionTelemetry(options.observabilityDb);
252
- const executionRunner = createAdaptiveExecutionRunner(vault);
403
+ const executionRunner = createAdaptiveExecutionRunner(vault, options.workspaceRoot);
253
404
  const policy = createConnectorPolicyLayer({
254
405
  routePlanner,
255
406
  executionRunner,
@@ -5,6 +5,8 @@ export interface AppliedGuidanceContext {
5
5
  atmosphereText?: string;
6
6
  impulseTexts: string[];
7
7
  personaRationales: string[];
8
+ /** @deprecated Use expressionConstraints. Kept for backward compatibility. */
8
9
  outputConstraints: string[];
10
+ expressionConstraints: string[];
9
11
  }
10
12
  export declare function applyGuidance(input: GuidancePayload | GuidanceFallback): AppliedGuidanceContext;
@@ -1,10 +1,15 @@
1
1
  export function applyGuidance(input) {
2
+ const isMinimal = "minimal" in input && input.minimal;
3
+ const boundaryConstraints = input.expressionBoundary?.constraints
4
+ ?? input.outputGuard?.constraints
5
+ ?? [];
2
6
  return {
3
- source: "minimal" in input && input.minimal ? "minimal_fallback" : "guidance_payload",
7
+ source: isMinimal ? "minimal_fallback" : "guidance_payload",
4
8
  sceneType: input.scene.sceneType,
5
9
  atmosphereText: input.atmosphere?.text,
6
10
  impulseTexts: input.impulses.map((item) => item.text),
7
11
  personaRationales: input.personaReinforcement.map((item) => item.rationale),
8
12
  outputConstraints: input.outputGuard?.constraints ?? [],
13
+ expressionConstraints: boundaryConstraints,
9
14
  };
10
15
  }
@@ -26,7 +26,7 @@ export type UserReplySceneType = typeof USER_REPLY_SCENE_TYPE;
26
26
  * - Light atmosphere (continuity-focused)
27
27
  * - NO impulses (unlike platform reply scene)
28
28
  * - Optional persona reinforcement (1-2 snippets max)
29
- * - Minimal output guard (tone consistency only)
29
+ * - Minimal expression boundary (tone consistency only)
30
30
  */
31
31
  export declare function buildLightReplyContinuity(input: {
32
32
  replyContext: {
@@ -1,6 +1,7 @@
1
1
  import { buildMinimalGuidanceFallback } from "../../../guidance/fallback.js";
2
+ import { buildExpressionBoundary } from "../../../guidance/output-guard.js";
2
3
  import { selectPersonaSnippets } from "../../../guidance/persona-selection.js";
3
- import { getBaselineAtmosphereTemplate } from "../../../guidance/template-registry.js";
4
+ import { getShortAtmosphereTemplate } from "../../../guidance/template-registry.js";
4
5
  /**
5
6
  * Scene context for user reply - uses a distinct scene type
6
7
  * to avoid confusion with platform reply scene.
@@ -13,7 +14,7 @@ export const USER_REPLY_SCENE_TYPE = "user_reply";
13
14
  * - Light atmosphere (continuity-focused)
14
15
  * - NO impulses (unlike platform reply scene)
15
16
  * - Optional persona reinforcement (1-2 snippets max)
16
- * - Minimal output guard (tone consistency only)
17
+ * - Minimal expression boundary (tone consistency only)
17
18
  */
18
19
  export async function buildLightReplyContinuity(input) {
19
20
  const sceneContext = {
@@ -23,8 +24,8 @@ export async function buildLightReplyContinuity(input) {
23
24
  sceneSummary: "direct user reply continuity",
24
25
  };
25
26
  try {
26
- // Light atmosphere - continuity focused
27
- const atmosphereTemplate = getBaselineAtmosphereTemplate();
27
+ // Light atmosphere - continuity focused (T-V7C.C.7: short constraint style)
28
+ const atmosphereTemplate = getShortAtmosphereTemplate(sceneContext.mode, sceneContext.riskLevel);
28
29
  const atmosphere = {
29
30
  kind: "atmosphere",
30
31
  text: `保持同一个人的语气。${input.replyContext.recentTone ? `最近语气参考:${input.replyContext.recentTone}` : "延续既有连续感。"}`,
@@ -43,7 +44,7 @@ export async function buildLightReplyContinuity(input) {
43
44
  });
44
45
  personaReinforcement = personaDecision.snippets;
45
46
  }
46
- // Minimal output guard - tone consistency only
47
+ // Minimal expression boundary - tone consistency only (T-V7C.C.7)
47
48
  const outputGuard = {
48
49
  kind: "output_guard",
49
50
  constraints: [
@@ -51,13 +52,21 @@ export async function buildLightReplyContinuity(input) {
51
52
  "延续同一个人格连续性",
52
53
  ],
53
54
  hardGuardPriority: true,
55
+ _semanticNote: "output_guard_only_shapes_expression",
54
56
  };
57
+ const expressionBoundary = buildExpressionBoundary(sceneContext.sceneType);
58
+ // Override with user-reply-specific constraints
59
+ expressionBoundary.constraints = [
60
+ "保持对话语气,不要用帖子回复腔",
61
+ "延续同一个人格连续性",
62
+ ];
55
63
  return {
56
64
  scene: sceneContext,
57
65
  atmosphere,
58
66
  impulses,
59
67
  personaReinforcement,
60
68
  outputGuard,
69
+ expressionBoundary,
61
70
  };
62
71
  }
63
72
  catch {
@@ -120,6 +120,11 @@ export function planIntentWithKind(kind, basePriority, runtime, context, registr
120
120
  }));
121
121
  }
122
122
  const refs = kind === "work" ? [...OBLIGATION_SOURCE] : evidenceRefsForConnector(runtime);
123
+ const capabilityIntent = kind === "exploration" ? "feed.read"
124
+ : kind === "social" ? "comment.reply"
125
+ : kind === "work" ? "work.discover"
126
+ : kind === "outreach" ? "message.send"
127
+ : undefined;
123
128
  return [
124
129
  {
125
130
  id: platformId ? `${config.idPrefix}-${platformId}` : config.idPrefix,
@@ -134,6 +139,7 @@ export function planIntentWithKind(kind, basePriority, runtime, context, registr
134
139
  ? `${config.idempotencyPrefix}:${platformId}`
135
140
  : `${config.idempotencyPrefix}:${config.summary(undefined)}`,
136
141
  goalInfluenceRefs: [],
142
+ ...(capabilityIntent ? { capabilityIntent } : {}),
137
143
  },
138
144
  ];
139
145
  }
@@ -242,6 +248,15 @@ export function planCandidateIntents(runtime, options) {
242
248
  if (related.length > 0) {
243
249
  intent.goalInfluenceRefs = related.map((g) => g.goalId);
244
250
  }
251
+ // W80: sourceRefs fallback — when lifeEvidence is empty, bind accepted goals
252
+ // as source refs so hard guard (isSourceBacked) does not deny/defer.
253
+ if (intent.sourceRefs.length === 0 && related.length > 0) {
254
+ intent.sourceRefs = related.slice(0, 4).map((g) => ({
255
+ id: g.goalId,
256
+ kind: "workspace_artifact",
257
+ uri: `goal://${g.goalId}`,
258
+ }));
259
+ }
245
260
  }
246
261
  // CR-02: apply narrative-focus bias globally across all candidate kinds.
247
262
  const adjusted = intents.map((intent) => {
@@ -13,6 +13,9 @@ export interface RuntimeServiceContext {
13
13
  workspaceRoot?: string;
14
14
  /** Plugin configuration overrides */
15
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;
16
19
  }
17
20
  export interface RuntimeServiceHandle {
18
21
  /** Service is ready and accepting requests */
@@ -26,8 +26,7 @@ export function startRuntimeService(ctx) {
26
26
  // - observability-system (event store setup)
27
27
  // - control-plane-system (heartbeat bridge preparation)
28
28
  const workspaceRoot = ctx?.workspaceRoot ?? process.cwd();
29
- /** Keep in sync with `plugin/package.json` when cutting releases. */
30
- const version = "0.1.21";
29
+ const version = ctx?.version ?? "unknown";
31
30
  activeHandle = {
32
31
  ready: true,
33
32
  version,
@@ -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>;