@haaaiawd/second-nature 0.1.39 → 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/index.js +1270 -1270
- package/openclaw.plugin.json +29 -29
- package/package.json +55 -55
- package/runtime/cli/commands/connector-init.js +11 -4
- package/runtime/cli/index.js +6 -1
- package/runtime/cli/ops/heartbeat-surface.d.ts +75 -75
- package/runtime/cli/ops/heartbeat-surface.js +97 -97
- package/runtime/cli/ops/ops-router.js +1428 -1428
- package/runtime/cli/ops/workspace-heartbeat-runner.js +236 -236
- package/runtime/connectors/services/connector-executor-adapter.js +192 -41
- package/runtime/core/second-nature/guidance/apply-guidance.d.ts +12 -12
- package/runtime/core/second-nature/guidance/apply-guidance.js +15 -15
- package/runtime/core/second-nature/guidance/user-reply-continuity.d.ts +50 -50
- package/runtime/core/second-nature/guidance/user-reply-continuity.js +89 -89
- package/runtime/core/second-nature/orchestrator/intent-planner.js +15 -0
- package/runtime/core/second-nature/runtime/service-entry.d.ts +39 -39
- package/runtime/core/second-nature/runtime/service-entry.js +44 -44
- package/runtime/dream/dream-engine.d.ts +14 -14
- package/runtime/dream/dream-engine.js +306 -306
- package/runtime/dream/dream-input-loader.d.ts +37 -37
- package/runtime/dream/dream-input-loader.js +150 -150
- package/runtime/dream/dream-scheduler.d.ts +75 -75
- package/runtime/dream/dream-scheduler.js +131 -131
- package/runtime/dream/index.d.ts +16 -16
- package/runtime/dream/index.js +14 -14
- package/runtime/dream/insight-extractor.d.ts +32 -32
- package/runtime/dream/insight-extractor.js +135 -135
- package/runtime/dream/memory-consolidator.d.ts +45 -45
- package/runtime/dream/memory-consolidator.js +140 -140
- package/runtime/dream/narrative-update-proposal.d.ts +34 -34
- package/runtime/dream/narrative-update-proposal.js +83 -83
- package/runtime/dream/output-validator.d.ts +20 -20
- package/runtime/dream/output-validator.js +110 -110
- package/runtime/dream/redaction-gate.d.ts +31 -31
- package/runtime/dream/redaction-gate.js +109 -109
- package/runtime/dream/relationship-update-proposal.d.ts +27 -27
- package/runtime/dream/relationship-update-proposal.js +119 -119
- package/runtime/dream/sampler.d.ts +30 -30
- package/runtime/dream/sampler.js +65 -65
- package/runtime/dream/types.d.ts +187 -187
- package/runtime/dream/types.js +11 -11
- package/runtime/guidance/fallback.js +20 -20
- package/runtime/guidance/guidance-assembler.js +76 -76
- package/runtime/guidance/output-guard.d.ts +13 -13
- package/runtime/guidance/output-guard.js +53 -53
- package/runtime/guidance/template-registry.d.ts +20 -20
- package/runtime/guidance/template-registry.js +93 -93
- package/runtime/guidance/types.d.ts +98 -98
- package/runtime/observability/projections/guidance-audit.js +38 -38
|
@@ -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,
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import type { GuidanceFallback, GuidancePayload } from "../../../guidance/index.js";
|
|
2
|
-
export interface AppliedGuidanceContext {
|
|
3
|
-
source: "guidance_payload" | "minimal_fallback";
|
|
4
|
-
sceneType: string;
|
|
5
|
-
atmosphereText?: string;
|
|
6
|
-
impulseTexts: string[];
|
|
7
|
-
personaRationales: string[];
|
|
8
|
-
/** @deprecated Use expressionConstraints. Kept for backward compatibility. */
|
|
9
|
-
outputConstraints: string[];
|
|
10
|
-
expressionConstraints: string[];
|
|
11
|
-
}
|
|
12
|
-
export declare function applyGuidance(input: GuidancePayload | GuidanceFallback): AppliedGuidanceContext;
|
|
1
|
+
import type { GuidanceFallback, GuidancePayload } from "../../../guidance/index.js";
|
|
2
|
+
export interface AppliedGuidanceContext {
|
|
3
|
+
source: "guidance_payload" | "minimal_fallback";
|
|
4
|
+
sceneType: string;
|
|
5
|
+
atmosphereText?: string;
|
|
6
|
+
impulseTexts: string[];
|
|
7
|
+
personaRationales: string[];
|
|
8
|
+
/** @deprecated Use expressionConstraints. Kept for backward compatibility. */
|
|
9
|
+
outputConstraints: string[];
|
|
10
|
+
expressionConstraints: string[];
|
|
11
|
+
}
|
|
12
|
+
export declare function applyGuidance(input: GuidancePayload | GuidanceFallback): AppliedGuidanceContext;
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
export function applyGuidance(input) {
|
|
2
|
-
const isMinimal = "minimal" in input && input.minimal;
|
|
3
|
-
const boundaryConstraints = input.expressionBoundary?.constraints
|
|
4
|
-
?? input.outputGuard?.constraints
|
|
5
|
-
?? [];
|
|
6
|
-
return {
|
|
7
|
-
source: isMinimal ? "minimal_fallback" : "guidance_payload",
|
|
8
|
-
sceneType: input.scene.sceneType,
|
|
9
|
-
atmosphereText: input.atmosphere?.text,
|
|
10
|
-
impulseTexts: input.impulses.map((item) => item.text),
|
|
11
|
-
personaRationales: input.personaReinforcement.map((item) => item.rationale),
|
|
12
|
-
outputConstraints: input.outputGuard?.constraints ?? [],
|
|
13
|
-
expressionConstraints: boundaryConstraints,
|
|
14
|
-
};
|
|
15
|
-
}
|
|
1
|
+
export function applyGuidance(input) {
|
|
2
|
+
const isMinimal = "minimal" in input && input.minimal;
|
|
3
|
+
const boundaryConstraints = input.expressionBoundary?.constraints
|
|
4
|
+
?? input.outputGuard?.constraints
|
|
5
|
+
?? [];
|
|
6
|
+
return {
|
|
7
|
+
source: isMinimal ? "minimal_fallback" : "guidance_payload",
|
|
8
|
+
sceneType: input.scene.sceneType,
|
|
9
|
+
atmosphereText: input.atmosphere?.text,
|
|
10
|
+
impulseTexts: input.impulses.map((item) => item.text),
|
|
11
|
+
personaRationales: input.personaReinforcement.map((item) => item.rationale),
|
|
12
|
+
outputConstraints: input.outputGuard?.constraints ?? [],
|
|
13
|
+
expressionConstraints: boundaryConstraints,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
@@ -1,50 +1,50 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* User Reply Light Continuity Contract
|
|
3
|
-
*
|
|
4
|
-
* Per T6.1.1: Provides very light continuity guidance for direct user replies.
|
|
5
|
-
* This is separate from the platform `reply` scene - it only provides
|
|
6
|
-
* lightweight persona continuity and tone consistency for user-facing chat.
|
|
7
|
-
*
|
|
8
|
-
* Key differences from platform `reply` scene:
|
|
9
|
-
* - No platform-specific impulses
|
|
10
|
-
* - No comment/reply formatting constraints
|
|
11
|
-
* - Only persona continuity and minimal tone guidance
|
|
12
|
-
* - Does not enter the reply scene impulse system
|
|
13
|
-
*/
|
|
14
|
-
import type { GuidanceFallback, GuidancePayload } from "../../../guidance/index.js";
|
|
15
|
-
import type { PersonaCandidate } from "../../../guidance/index.js";
|
|
16
|
-
/**
|
|
17
|
-
* Scene context for user reply - uses a distinct scene type
|
|
18
|
-
* to avoid confusion with platform reply scene.
|
|
19
|
-
*/
|
|
20
|
-
export declare const USER_REPLY_SCENE_TYPE: "user_reply";
|
|
21
|
-
export type UserReplySceneType = typeof USER_REPLY_SCENE_TYPE;
|
|
22
|
-
/**
|
|
23
|
-
* Build very light continuity guidance for direct user replies.
|
|
24
|
-
*
|
|
25
|
-
* Returns a minimal guidance payload with:
|
|
26
|
-
* - Light atmosphere (continuity-focused)
|
|
27
|
-
* - NO impulses (unlike platform reply scene)
|
|
28
|
-
* - Optional persona reinforcement (1-2 snippets max)
|
|
29
|
-
* - Minimal expression boundary (tone consistency only)
|
|
30
|
-
*/
|
|
31
|
-
export declare function buildLightReplyContinuity(input: {
|
|
32
|
-
replyContext: {
|
|
33
|
-
recentTone?: string;
|
|
34
|
-
lastInteractionSummary?: string;
|
|
35
|
-
};
|
|
36
|
-
personaCandidates?: PersonaCandidate[];
|
|
37
|
-
}): Promise<GuidancePayload | GuidanceFallback>;
|
|
38
|
-
/**
|
|
39
|
-
* Check if an input should be classified as direct user reply.
|
|
40
|
-
*
|
|
41
|
-
* Classification criteria:
|
|
42
|
-
* - Trigger source is user_reply
|
|
43
|
-
* - Not a platform comment/reply
|
|
44
|
-
* - Not an explicit task delegation
|
|
45
|
-
*/
|
|
46
|
-
export declare function isDirectUserReply(input: {
|
|
47
|
-
triggerSource: string;
|
|
48
|
-
isPlatformReply: boolean;
|
|
49
|
-
isExplicitTask: boolean;
|
|
50
|
-
}): boolean;
|
|
1
|
+
/**
|
|
2
|
+
* User Reply Light Continuity Contract
|
|
3
|
+
*
|
|
4
|
+
* Per T6.1.1: Provides very light continuity guidance for direct user replies.
|
|
5
|
+
* This is separate from the platform `reply` scene - it only provides
|
|
6
|
+
* lightweight persona continuity and tone consistency for user-facing chat.
|
|
7
|
+
*
|
|
8
|
+
* Key differences from platform `reply` scene:
|
|
9
|
+
* - No platform-specific impulses
|
|
10
|
+
* - No comment/reply formatting constraints
|
|
11
|
+
* - Only persona continuity and minimal tone guidance
|
|
12
|
+
* - Does not enter the reply scene impulse system
|
|
13
|
+
*/
|
|
14
|
+
import type { GuidanceFallback, GuidancePayload } from "../../../guidance/index.js";
|
|
15
|
+
import type { PersonaCandidate } from "../../../guidance/index.js";
|
|
16
|
+
/**
|
|
17
|
+
* Scene context for user reply - uses a distinct scene type
|
|
18
|
+
* to avoid confusion with platform reply scene.
|
|
19
|
+
*/
|
|
20
|
+
export declare const USER_REPLY_SCENE_TYPE: "user_reply";
|
|
21
|
+
export type UserReplySceneType = typeof USER_REPLY_SCENE_TYPE;
|
|
22
|
+
/**
|
|
23
|
+
* Build very light continuity guidance for direct user replies.
|
|
24
|
+
*
|
|
25
|
+
* Returns a minimal guidance payload with:
|
|
26
|
+
* - Light atmosphere (continuity-focused)
|
|
27
|
+
* - NO impulses (unlike platform reply scene)
|
|
28
|
+
* - Optional persona reinforcement (1-2 snippets max)
|
|
29
|
+
* - Minimal expression boundary (tone consistency only)
|
|
30
|
+
*/
|
|
31
|
+
export declare function buildLightReplyContinuity(input: {
|
|
32
|
+
replyContext: {
|
|
33
|
+
recentTone?: string;
|
|
34
|
+
lastInteractionSummary?: string;
|
|
35
|
+
};
|
|
36
|
+
personaCandidates?: PersonaCandidate[];
|
|
37
|
+
}): Promise<GuidancePayload | GuidanceFallback>;
|
|
38
|
+
/**
|
|
39
|
+
* Check if an input should be classified as direct user reply.
|
|
40
|
+
*
|
|
41
|
+
* Classification criteria:
|
|
42
|
+
* - Trigger source is user_reply
|
|
43
|
+
* - Not a platform comment/reply
|
|
44
|
+
* - Not an explicit task delegation
|
|
45
|
+
*/
|
|
46
|
+
export declare function isDirectUserReply(input: {
|
|
47
|
+
triggerSource: string;
|
|
48
|
+
isPlatformReply: boolean;
|
|
49
|
+
isExplicitTask: boolean;
|
|
50
|
+
}): boolean;
|
|
@@ -1,89 +1,89 @@
|
|
|
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
|
-
}
|
|
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
|
+
}
|