@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.
- package/agent-inner-guide.md +18 -0
- package/index.js +10 -2
- package/openclaw.plugin.json +2 -2
- package/package.json +1 -1
- package/runtime/cli/commands/connector-init.js +11 -4
- package/runtime/cli/index.js +6 -1
- package/runtime/cli/ops/heartbeat-surface.d.ts +15 -0
- package/runtime/cli/ops/heartbeat-surface.js +16 -2
- package/runtime/cli/ops/ops-router.js +229 -83
- package/runtime/cli/ops/workspace-heartbeat-runner.js +49 -4
- package/runtime/connectors/services/connector-executor-adapter.js +192 -41
- package/runtime/core/second-nature/guidance/apply-guidance.d.ts +2 -0
- package/runtime/core/second-nature/guidance/apply-guidance.js +6 -1
- package/runtime/core/second-nature/guidance/user-reply-continuity.d.ts +1 -1
- package/runtime/core/second-nature/guidance/user-reply-continuity.js +14 -5
- package/runtime/core/second-nature/orchestrator/intent-planner.js +15 -0
- package/runtime/core/second-nature/runtime/service-entry.d.ts +3 -0
- package/runtime/core/second-nature/runtime/service-entry.js +1 -2
- 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/fallback.js +6 -3
- package/runtime/guidance/guidance-assembler.js +5 -3
- package/runtime/guidance/output-guard.d.ts +4 -1
- package/runtime/guidance/output-guard.js +24 -0
- package/runtime/guidance/template-registry.d.ts +5 -1
- package/runtime/guidance/template-registry.js +71 -30
- package/runtime/guidance/types.d.ts +14 -0
- 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
|
|
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
|
|
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
|
-
|
|
110
|
-
|
|
111
|
-
|
|
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: "
|
|
119
|
-
detail:
|
|
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
|
-
|
|
124
|
-
|
|
125
|
-
|
|
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: "
|
|
134
|
-
detail:
|
|
151
|
+
code: "mock_read_error",
|
|
152
|
+
detail: String(err),
|
|
135
153
|
},
|
|
136
154
|
};
|
|
137
155
|
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
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:
|
|
212
|
+
platformId: request.platformId,
|
|
213
|
+
channel: plan.channel,
|
|
144
214
|
latencyMs: Date.now() - started,
|
|
145
215
|
success: false,
|
|
146
216
|
error: {
|
|
147
|
-
code: "
|
|
148
|
-
detail:
|
|
217
|
+
code: "api_error",
|
|
218
|
+
detail: `HTTP ${resp.status}: ${await resp.text().catch(() => "")}`,
|
|
149
219
|
},
|
|
150
220
|
};
|
|
151
221
|
}
|
|
152
|
-
const
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
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:
|
|
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
|
|
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 {
|
|
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
|
|
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 =
|
|
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
|
|
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
|
-
|
|
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>;
|