@haaaiawd/second-nature 0.1.34 → 0.1.39
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/agent-inner-guide.md +43 -0
- package/index.js +1270 -1262
- package/openclaw.plugin.json +29 -29
- package/package.json +55 -55
- package/runtime/cli/commands/goal.d.ts +1 -0
- package/runtime/cli/commands/goal.js +1 -0
- package/runtime/cli/index.js +3 -3
- package/runtime/cli/ops/heartbeat-surface.d.ts +75 -54
- package/runtime/cli/ops/heartbeat-surface.js +97 -81
- package/runtime/cli/ops/ops-router.js +1428 -1182
- package/runtime/cli/ops/workspace-heartbeat-runner.d.ts +24 -0
- package/runtime/cli/ops/workspace-heartbeat-runner.js +236 -150
- package/runtime/connectors/base/contract.d.ts +10 -0
- package/runtime/core/second-nature/guidance/apply-guidance.d.ts +12 -10
- package/runtime/core/second-nature/guidance/apply-guidance.js +15 -10
- package/runtime/core/second-nature/guidance/user-reply-continuity.d.ts +50 -50
- package/runtime/core/second-nature/guidance/user-reply-continuity.js +89 -80
- package/runtime/core/second-nature/heartbeat/heartbeat-loop.d.ts +7 -1
- package/runtime/core/second-nature/heartbeat/heartbeat-loop.js +25 -0
- package/runtime/core/second-nature/heartbeat/runtime-snapshot.d.ts +5 -0
- package/runtime/core/second-nature/heartbeat/runtime-snapshot.js +10 -1
- package/runtime/core/second-nature/heartbeat/snapshot-builder.d.ts +5 -0
- package/runtime/core/second-nature/orchestrator/guard-layer.js +24 -1
- package/runtime/core/second-nature/quiet/run-source-backed-quiet.d.ts +20 -0
- package/runtime/core/second-nature/quiet/run-source-backed-quiet.js +32 -2
- package/runtime/core/second-nature/runtime/service-entry.d.ts +39 -36
- package/runtime/core/second-nature/runtime/service-entry.js +44 -45
- package/runtime/dream/dream-engine.d.ts +14 -0
- package/runtime/dream/dream-engine.js +306 -0
- package/runtime/dream/dream-input-loader.d.ts +37 -0
- package/runtime/dream/dream-input-loader.js +155 -0
- package/runtime/dream/dream-scheduler.d.ts +75 -0
- package/runtime/dream/dream-scheduler.js +131 -0
- package/runtime/dream/index.d.ts +16 -0
- package/runtime/dream/index.js +14 -0
- package/runtime/dream/insight-extractor.d.ts +32 -0
- package/runtime/dream/insight-extractor.js +135 -0
- package/runtime/dream/memory-consolidator.d.ts +45 -0
- package/runtime/dream/memory-consolidator.js +140 -0
- package/runtime/dream/narrative-update-proposal.d.ts +34 -0
- package/runtime/dream/narrative-update-proposal.js +83 -0
- package/runtime/dream/output-validator.d.ts +20 -0
- package/runtime/dream/output-validator.js +110 -0
- package/runtime/dream/redaction-gate.d.ts +31 -0
- package/runtime/dream/redaction-gate.js +109 -0
- package/runtime/dream/relationship-update-proposal.d.ts +27 -0
- package/runtime/dream/relationship-update-proposal.js +119 -0
- package/runtime/dream/sampler.d.ts +30 -0
- package/runtime/dream/sampler.js +65 -0
- package/runtime/dream/types.d.ts +187 -0
- package/runtime/dream/types.js +11 -0
- package/runtime/guidance/capability-class.d.ts +38 -0
- package/runtime/guidance/capability-class.js +65 -0
- package/runtime/guidance/fallback.js +20 -17
- package/runtime/guidance/guidance-assembler.d.ts +2 -0
- package/runtime/guidance/guidance-assembler.js +76 -62
- package/runtime/guidance/guidance-draft-service.js +5 -5
- package/runtime/guidance/impulse-assembler.d.ts +71 -0
- package/runtime/guidance/impulse-assembler.js +103 -0
- package/runtime/guidance/index.d.ts +2 -0
- package/runtime/guidance/index.js +2 -0
- package/runtime/guidance/output-guard.d.ts +13 -10
- package/runtime/guidance/output-guard.js +53 -29
- package/runtime/guidance/outreach-strategy-selector.d.ts +13 -0
- package/runtime/guidance/outreach-strategy-selector.js +2 -2
- package/runtime/guidance/template-registry.d.ts +20 -3
- package/runtime/guidance/template-registry.js +123 -45
- package/runtime/guidance/types.d.ts +98 -72
- package/runtime/observability/projections/guidance-audit.js +38 -35
- package/runtime/storage/goal/agent-goal-store.d.ts +2 -0
- package/runtime/storage/goal/agent-goal-store.js +28 -1
- package/runtime/storage/services/tool-experience-store.js +11 -11
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* capability-class.ts — T-V7C.C.4R
|
|
3
|
+
*
|
|
4
|
+
* Core logic: infer CapabilityClass from capabilityIntent string prefix.
|
|
5
|
+
*
|
|
6
|
+
* CapabilityClass is the middle axis between intentKind (why) and platform (where),
|
|
7
|
+
* used by ImpulseAssembler to select appropriate behavioral impulse templates.
|
|
8
|
+
*
|
|
9
|
+
* Classification rules (prefix-based, not EffectSemanticsClass — execution layer is intentionally
|
|
10
|
+
* kept separate from expression layer):
|
|
11
|
+
* feed.* → consume (browsing / reading feeds)
|
|
12
|
+
* notification.* → consume (reading notifications, not interacting)
|
|
13
|
+
* work.* → discover (research / task discovery)
|
|
14
|
+
* post.* → broadcast (publishing, primary expression)
|
|
15
|
+
* comment.* → interact (replying to others' content)
|
|
16
|
+
* message.* → interact (private messages / DMs)
|
|
17
|
+
* task.* → claim (claiming work items)
|
|
18
|
+
* agent.* → null (keepalive/internal — excluded from impulse system)
|
|
19
|
+
* unknown/custom → broadcast (safe default for unrecognized side-effect capabilities)
|
|
20
|
+
*
|
|
21
|
+
* Boundary:
|
|
22
|
+
* - Pure function, zero side effects.
|
|
23
|
+
* - Does NOT consult EffectSemanticsClass (execution-policy.ts) — those layers must not couple.
|
|
24
|
+
* - Custom capabilities declared by Claw without a prefix match default to "broadcast".
|
|
25
|
+
*
|
|
26
|
+
* Test coverage: tests/unit/guidance/capability-class.test.ts
|
|
27
|
+
*/
|
|
28
|
+
/** The expression-layer classification of a capability. */
|
|
29
|
+
export type CapabilityClass = "consume" | "broadcast" | "interact" | "discover" | "claim";
|
|
30
|
+
/**
|
|
31
|
+
* Infer CapabilityClass from a capabilityIntent string.
|
|
32
|
+
*
|
|
33
|
+
* Returns null for agent.* capabilities (keepalive / internal — no impulse injection).
|
|
34
|
+
* Returns "broadcast" for unrecognized custom capabilities (safe default).
|
|
35
|
+
*/
|
|
36
|
+
export declare function inferCapabilityClass(capabilityIntent: string): CapabilityClass | null;
|
|
37
|
+
/** Map from CapabilityClass to its default intentKind-style scene, for impulse lookup fallback. */
|
|
38
|
+
export declare const CAPABILITY_CLASS_SCENE_MAP: Readonly<Record<CapabilityClass, string>>;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* capability-class.ts — T-V7C.C.4R
|
|
3
|
+
*
|
|
4
|
+
* Core logic: infer CapabilityClass from capabilityIntent string prefix.
|
|
5
|
+
*
|
|
6
|
+
* CapabilityClass is the middle axis between intentKind (why) and platform (where),
|
|
7
|
+
* used by ImpulseAssembler to select appropriate behavioral impulse templates.
|
|
8
|
+
*
|
|
9
|
+
* Classification rules (prefix-based, not EffectSemanticsClass — execution layer is intentionally
|
|
10
|
+
* kept separate from expression layer):
|
|
11
|
+
* feed.* → consume (browsing / reading feeds)
|
|
12
|
+
* notification.* → consume (reading notifications, not interacting)
|
|
13
|
+
* work.* → discover (research / task discovery)
|
|
14
|
+
* post.* → broadcast (publishing, primary expression)
|
|
15
|
+
* comment.* → interact (replying to others' content)
|
|
16
|
+
* message.* → interact (private messages / DMs)
|
|
17
|
+
* task.* → claim (claiming work items)
|
|
18
|
+
* agent.* → null (keepalive/internal — excluded from impulse system)
|
|
19
|
+
* unknown/custom → broadcast (safe default for unrecognized side-effect capabilities)
|
|
20
|
+
*
|
|
21
|
+
* Boundary:
|
|
22
|
+
* - Pure function, zero side effects.
|
|
23
|
+
* - Does NOT consult EffectSemanticsClass (execution-policy.ts) — those layers must not couple.
|
|
24
|
+
* - Custom capabilities declared by Claw without a prefix match default to "broadcast".
|
|
25
|
+
*
|
|
26
|
+
* Test coverage: tests/unit/guidance/capability-class.test.ts
|
|
27
|
+
*/
|
|
28
|
+
/**
|
|
29
|
+
* Infer CapabilityClass from a capabilityIntent string.
|
|
30
|
+
*
|
|
31
|
+
* Returns null for agent.* capabilities (keepalive / internal — no impulse injection).
|
|
32
|
+
* Returns "broadcast" for unrecognized custom capabilities (safe default).
|
|
33
|
+
*/
|
|
34
|
+
export function inferCapabilityClass(capabilityIntent) {
|
|
35
|
+
if (!capabilityIntent || typeof capabilityIntent !== "string")
|
|
36
|
+
return null;
|
|
37
|
+
const prefix = capabilityIntent.split(".")[0]?.toLowerCase() ?? "";
|
|
38
|
+
switch (prefix) {
|
|
39
|
+
case "agent":
|
|
40
|
+
return null; // Keepalive/internal: excluded from impulse system entirely
|
|
41
|
+
case "feed":
|
|
42
|
+
case "notification":
|
|
43
|
+
return "consume";
|
|
44
|
+
case "work":
|
|
45
|
+
return "discover";
|
|
46
|
+
case "post":
|
|
47
|
+
return "broadcast";
|
|
48
|
+
case "comment":
|
|
49
|
+
case "message":
|
|
50
|
+
return "interact";
|
|
51
|
+
case "task":
|
|
52
|
+
return "claim";
|
|
53
|
+
default:
|
|
54
|
+
// Custom or unrecognized capability — default to broadcast (outward expression)
|
|
55
|
+
return "broadcast";
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/** Map from CapabilityClass to its default intentKind-style scene, for impulse lookup fallback. */
|
|
59
|
+
export const CAPABILITY_CLASS_SCENE_MAP = {
|
|
60
|
+
consume: "explore",
|
|
61
|
+
discover: "explore",
|
|
62
|
+
broadcast: "social",
|
|
63
|
+
interact: "reply",
|
|
64
|
+
claim: "work",
|
|
65
|
+
};
|
|
@@ -1,17 +1,20 @@
|
|
|
1
|
-
import { buildOutputGuard } from "./output-guard.js";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
1
|
+
import { buildOutputGuard, buildExpressionBoundary } from "./output-guard.js";
|
|
2
|
+
import { getShortAtmosphereTemplate } from "./template-registry.js";
|
|
3
|
+
export function buildMinimalGuidanceFallback(sceneContext) {
|
|
4
|
+
const atmosphereTemplate = getShortAtmosphereTemplate(sceneContext.mode, sceneContext.riskLevel);
|
|
5
|
+
return {
|
|
6
|
+
scene: sceneContext,
|
|
7
|
+
atmosphere: {
|
|
8
|
+
kind: "atmosphere",
|
|
9
|
+
text: atmosphereTemplate.text,
|
|
10
|
+
openness: sceneContext.mode === "quiet" ? "quiet" : sceneContext.riskLevel === "high" ? "narrow" : "open",
|
|
11
|
+
pressureLabels: [sceneContext.mode, sceneContext.riskLevel ?? "unknown_risk"],
|
|
12
|
+
reviewStatus: atmosphereTemplate.reviewStatus,
|
|
13
|
+
},
|
|
14
|
+
impulses: [],
|
|
15
|
+
personaReinforcement: [],
|
|
16
|
+
outputGuard: buildOutputGuard(sceneContext.sceneType),
|
|
17
|
+
expressionBoundary: buildExpressionBoundary(sceneContext.sceneType),
|
|
18
|
+
minimal: true,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { type PlatformImpulsePort } from "./impulse-assembler.js";
|
|
1
2
|
import type { GuidancePayload, GuidanceUnavailable, PersonaCandidate, SceneContext } from "./types.js";
|
|
2
3
|
export declare function assembleGuidance(input: {
|
|
3
4
|
sceneContext: SceneContext | null | undefined;
|
|
4
5
|
personaCandidates?: PersonaCandidate[];
|
|
6
|
+
platformImpulsePort?: PlatformImpulsePort;
|
|
5
7
|
}): Promise<GuidancePayload | GuidanceUnavailable>;
|
|
@@ -1,62 +1,76 @@
|
|
|
1
|
-
import { buildMinimalGuidanceFallback } from "./fallback.js";
|
|
2
|
-
import { buildOutputGuard } from "./output-guard.js";
|
|
3
|
-
import { selectPersonaSnippets } from "./persona-selection.js";
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
if (
|
|
23
|
-
return
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
1
|
+
import { buildMinimalGuidanceFallback } from "./fallback.js";
|
|
2
|
+
import { buildOutputGuard, buildExpressionBoundary } from "./output-guard.js";
|
|
3
|
+
import { selectPersonaSnippets } from "./persona-selection.js";
|
|
4
|
+
import { getShortAtmosphereTemplate } from "./template-registry.js";
|
|
5
|
+
import { assembleImpulse } from "./impulse-assembler.js";
|
|
6
|
+
async function buildAtmosphere(sceneContext) {
|
|
7
|
+
const template = getShortAtmosphereTemplate(sceneContext.mode, sceneContext.riskLevel);
|
|
8
|
+
return {
|
|
9
|
+
kind: "atmosphere",
|
|
10
|
+
text: template.text,
|
|
11
|
+
openness: sceneContext.mode === "quiet" ? "quiet" : sceneContext.riskLevel === "high" ? "narrow" : "open",
|
|
12
|
+
pressureLabels: [sceneContext.mode, sceneContext.riskLevel ?? "unknown_risk"],
|
|
13
|
+
reviewStatus: template.reviewStatus,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Select impulses using the dual-axis capabilityClass assembler (T-V7C.C.4R).
|
|
18
|
+
*
|
|
19
|
+
* Fallback chain: platform-specific → capabilityClass preset → intentKind → []
|
|
20
|
+
*/
|
|
21
|
+
async function selectImpulses(sceneContext, deps) {
|
|
22
|
+
if (sceneContext.sceneType === "explain" || sceneContext.sceneType === "user_reply") {
|
|
23
|
+
return [];
|
|
24
|
+
}
|
|
25
|
+
const result = await assembleImpulse({
|
|
26
|
+
sceneType: sceneContext.sceneType,
|
|
27
|
+
capabilityIntent: sceneContext.capabilityIntent,
|
|
28
|
+
platformId: sceneContext.platformId,
|
|
29
|
+
}, deps);
|
|
30
|
+
return result.impulse ? [result.impulse] : [];
|
|
31
|
+
}
|
|
32
|
+
export async function assembleGuidance(input) {
|
|
33
|
+
if (!input.sceneContext) {
|
|
34
|
+
return {
|
|
35
|
+
available: false,
|
|
36
|
+
reason: "missing_scene_context",
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
const sceneContext = input.sceneContext;
|
|
40
|
+
const deps = { platformImpulsePort: input.platformImpulsePort };
|
|
41
|
+
try {
|
|
42
|
+
const [atmosphere, impulses] = await Promise.all([
|
|
43
|
+
buildAtmosphere(sceneContext),
|
|
44
|
+
selectImpulses(sceneContext, deps),
|
|
45
|
+
]);
|
|
46
|
+
const personaDecision = selectPersonaSnippets({
|
|
47
|
+
sceneContext,
|
|
48
|
+
candidates: input.personaCandidates ?? [],
|
|
49
|
+
});
|
|
50
|
+
return {
|
|
51
|
+
scene: sceneContext,
|
|
52
|
+
atmosphere,
|
|
53
|
+
impulses,
|
|
54
|
+
personaReinforcement: personaDecision.snippets,
|
|
55
|
+
outputGuard: buildOutputGuard(sceneContext.sceneType),
|
|
56
|
+
expressionBoundary: buildExpressionBoundary(sceneContext.sceneType),
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
if ((input.personaCandidates ?? []).length === 0) {
|
|
61
|
+
const fallback = buildMinimalGuidanceFallback(sceneContext);
|
|
62
|
+
return {
|
|
63
|
+
scene: fallback.scene,
|
|
64
|
+
atmosphere: fallback.atmosphere,
|
|
65
|
+
impulses: fallback.impulses,
|
|
66
|
+
personaReinforcement: fallback.personaReinforcement,
|
|
67
|
+
outputGuard: fallback.outputGuard,
|
|
68
|
+
expressionBoundary: fallback.expressionBoundary,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
return {
|
|
72
|
+
available: false,
|
|
73
|
+
reason: "missing_template",
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -19,16 +19,16 @@ const SCENE_KINDS = [
|
|
|
19
19
|
"reconnect",
|
|
20
20
|
];
|
|
21
21
|
function buildDraftText(request, claims) {
|
|
22
|
-
const anchor = claims.map((c) => c.text).join("
|
|
22
|
+
const anchor = claims.map((c) => c.text).join(";");
|
|
23
23
|
switch (request.sceneKind) {
|
|
24
24
|
case "outreach":
|
|
25
|
-
return
|
|
25
|
+
return `有件事想跟你分享,正好碰到了:${anchor}`;
|
|
26
26
|
case "follow_up":
|
|
27
|
-
return
|
|
27
|
+
return `接着上次聊的说一下:${anchor}`;
|
|
28
28
|
case "reconnect":
|
|
29
|
-
return
|
|
29
|
+
return `好久不见,最近有个东西让我想到你:${anchor}`;
|
|
30
30
|
default:
|
|
31
|
-
return
|
|
31
|
+
return `关于 ${request.sceneKind}:${anchor}`;
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
34
|
export async function generateGuidanceDraft(request, deps) {
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* impulse-assembler.ts — T-V7C.C.4R
|
|
3
|
+
*
|
|
4
|
+
* Core logic: three-level fallback impulse selection.
|
|
5
|
+
*
|
|
6
|
+
* Priority chain (highest → lowest):
|
|
7
|
+
* 1. platform-specific impulse — Claw-defined per platformId, loaded from workspace
|
|
8
|
+
* 2. capabilityClass preset — derived from capabilityIntent prefix via inferCapabilityClass
|
|
9
|
+
* 3. intentKind fallback — existing scene type impulse (social/outreach/reply/quiet)
|
|
10
|
+
* 4. null — no impulse (baseline atmosphere still applies)
|
|
11
|
+
*
|
|
12
|
+
* Exclusions:
|
|
13
|
+
* - agent.* capabilities → null always (keepalive/internal, not an expression action)
|
|
14
|
+
* - explore/work capabilityClass impulses → approved and active (T-V7C.C.4R review complete)
|
|
15
|
+
*
|
|
16
|
+
* Boundary:
|
|
17
|
+
* - Pure function composition; no I/O except the optional platformImpulsePort.
|
|
18
|
+
* - Does NOT write state or emit events.
|
|
19
|
+
* - SceneContext is enriched with capabilityIntent + platformId as optional fields
|
|
20
|
+
* to carry the dual-axis context without breaking existing SceneContext consumers.
|
|
21
|
+
*
|
|
22
|
+
* Test coverage: tests/unit/guidance/impulse-assembler.test.ts
|
|
23
|
+
*/
|
|
24
|
+
import type { ImpulseBlock, GuidanceSceneType } from "./types.js";
|
|
25
|
+
import { type CapabilityClass } from "./capability-class.js";
|
|
26
|
+
/** Extended scene context carrying dual-axis impulse selection inputs. */
|
|
27
|
+
export interface ImpulseSelectionContext {
|
|
28
|
+
/** The intent kind (why) — maps to intentKind fallback impulse. */
|
|
29
|
+
sceneType: GuidanceSceneType;
|
|
30
|
+
/** The capability being executed (what physical form). e.g. "post.publish", "feed.read" */
|
|
31
|
+
capabilityIntent?: string;
|
|
32
|
+
/** The platform being targeted. Used for platform-specific impulse lookup. */
|
|
33
|
+
platformId?: string;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Port for loading platform-specific impulse overrides.
|
|
37
|
+
* Claw implements this by writing impulse files to the workspace.
|
|
38
|
+
* When absent, the assembler skips platform-specific lookup gracefully.
|
|
39
|
+
*/
|
|
40
|
+
export interface PlatformImpulsePort {
|
|
41
|
+
/**
|
|
42
|
+
* Load a platform-specific impulse for the given platformId + capabilityClass.
|
|
43
|
+
* Returns null when no override is defined.
|
|
44
|
+
*/
|
|
45
|
+
loadPlatformImpulse(input: {
|
|
46
|
+
platformId: string;
|
|
47
|
+
capabilityClass: CapabilityClass;
|
|
48
|
+
}): Promise<ImpulseBlock | null>;
|
|
49
|
+
}
|
|
50
|
+
export interface ImpulseAssemblerResult {
|
|
51
|
+
/** The selected impulse, or null if no applicable impulse exists. */
|
|
52
|
+
impulse: ImpulseBlock | null;
|
|
53
|
+
/** Which level of the fallback chain was used. */
|
|
54
|
+
source: "platform_specific" | "capability_class" | "intent_kind" | "none";
|
|
55
|
+
/** The inferred capability class (null for agent.* or unknown intent). */
|
|
56
|
+
capabilityClass: CapabilityClass | null;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Select the most specific impulse for a given scene + capability context.
|
|
60
|
+
*
|
|
61
|
+
* Fallback chain:
|
|
62
|
+
* platform-specific → capabilityClass preset → intentKind → null
|
|
63
|
+
*/
|
|
64
|
+
export declare function assembleImpulse(ctx: ImpulseSelectionContext, deps: {
|
|
65
|
+
platformImpulsePort?: PlatformImpulsePort;
|
|
66
|
+
}): Promise<ImpulseAssemblerResult>;
|
|
67
|
+
/**
|
|
68
|
+
* Synchronous variant for contexts where capabilityClass + intentKind are sufficient
|
|
69
|
+
* and no platform-specific port is needed (e.g. guidance_payload ops command preview).
|
|
70
|
+
*/
|
|
71
|
+
export declare function assembleImpulseSync(ctx: ImpulseSelectionContext): ImpulseAssemblerResult;
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* impulse-assembler.ts — T-V7C.C.4R
|
|
3
|
+
*
|
|
4
|
+
* Core logic: three-level fallback impulse selection.
|
|
5
|
+
*
|
|
6
|
+
* Priority chain (highest → lowest):
|
|
7
|
+
* 1. platform-specific impulse — Claw-defined per platformId, loaded from workspace
|
|
8
|
+
* 2. capabilityClass preset — derived from capabilityIntent prefix via inferCapabilityClass
|
|
9
|
+
* 3. intentKind fallback — existing scene type impulse (social/outreach/reply/quiet)
|
|
10
|
+
* 4. null — no impulse (baseline atmosphere still applies)
|
|
11
|
+
*
|
|
12
|
+
* Exclusions:
|
|
13
|
+
* - agent.* capabilities → null always (keepalive/internal, not an expression action)
|
|
14
|
+
* - explore/work capabilityClass impulses → approved and active (T-V7C.C.4R review complete)
|
|
15
|
+
*
|
|
16
|
+
* Boundary:
|
|
17
|
+
* - Pure function composition; no I/O except the optional platformImpulsePort.
|
|
18
|
+
* - Does NOT write state or emit events.
|
|
19
|
+
* - SceneContext is enriched with capabilityIntent + platformId as optional fields
|
|
20
|
+
* to carry the dual-axis context without breaking existing SceneContext consumers.
|
|
21
|
+
*
|
|
22
|
+
* Test coverage: tests/unit/guidance/impulse-assembler.test.ts
|
|
23
|
+
*/
|
|
24
|
+
import { inferCapabilityClass, CAPABILITY_CLASS_SCENE_MAP, } from "./capability-class.js";
|
|
25
|
+
import { getImpulseTemplate, getCapabilityClassImpulseTemplate, } from "./template-registry.js";
|
|
26
|
+
// ─── Core assembly logic ──────────────────────────────────────────────────────
|
|
27
|
+
/**
|
|
28
|
+
* Select the most specific impulse for a given scene + capability context.
|
|
29
|
+
*
|
|
30
|
+
* Fallback chain:
|
|
31
|
+
* platform-specific → capabilityClass preset → intentKind → null
|
|
32
|
+
*/
|
|
33
|
+
export async function assembleImpulse(ctx, deps) {
|
|
34
|
+
// Infer capability class from capabilityIntent prefix
|
|
35
|
+
const capabilityClass = ctx.capabilityIntent
|
|
36
|
+
? inferCapabilityClass(ctx.capabilityIntent)
|
|
37
|
+
: null;
|
|
38
|
+
// agent.* → excluded entirely
|
|
39
|
+
if (ctx.capabilityIntent && inferCapabilityClass(ctx.capabilityIntent) === null &&
|
|
40
|
+
ctx.capabilityIntent.startsWith("agent.")) {
|
|
41
|
+
return { impulse: null, source: "none", capabilityClass: null };
|
|
42
|
+
}
|
|
43
|
+
// ── Level 1: platform-specific ──────────────────────────────────────────────
|
|
44
|
+
if (ctx.platformId && capabilityClass && deps.platformImpulsePort) {
|
|
45
|
+
try {
|
|
46
|
+
const platformImpulse = await deps.platformImpulsePort.loadPlatformImpulse({
|
|
47
|
+
platformId: ctx.platformId,
|
|
48
|
+
capabilityClass,
|
|
49
|
+
});
|
|
50
|
+
if (platformImpulse) {
|
|
51
|
+
return { impulse: platformImpulse, source: "platform_specific", capabilityClass };
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
// Port failure → fall through gracefully
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
// ── Level 2: capabilityClass preset ─────────────────────────────────────────
|
|
59
|
+
if (capabilityClass) {
|
|
60
|
+
const ccImpulseKind = CAPABILITY_CLASS_SCENE_MAP[capabilityClass];
|
|
61
|
+
const ccImpulse = getCapabilityClassImpulseTemplate(ccImpulseKind);
|
|
62
|
+
if (ccImpulse) {
|
|
63
|
+
return { impulse: ccImpulse, source: "capability_class", capabilityClass };
|
|
64
|
+
}
|
|
65
|
+
// explore/work are pending review → fall through to intentKind
|
|
66
|
+
}
|
|
67
|
+
// ── Level 3: intentKind fallback ─────────────────────────────────────────────
|
|
68
|
+
const sceneType = ctx.sceneType;
|
|
69
|
+
if (sceneType !== "explain" && sceneType !== "user_reply") {
|
|
70
|
+
const intentImpulse = getImpulseTemplate(sceneType);
|
|
71
|
+
return { impulse: intentImpulse, source: "intent_kind", capabilityClass };
|
|
72
|
+
}
|
|
73
|
+
// ── Level 4: no impulse ───────────────────────────────────────────────────────
|
|
74
|
+
return { impulse: null, source: "none", capabilityClass };
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Synchronous variant for contexts where capabilityClass + intentKind are sufficient
|
|
78
|
+
* and no platform-specific port is needed (e.g. guidance_payload ops command preview).
|
|
79
|
+
*/
|
|
80
|
+
export function assembleImpulseSync(ctx) {
|
|
81
|
+
const capabilityClass = ctx.capabilityIntent
|
|
82
|
+
? inferCapabilityClass(ctx.capabilityIntent)
|
|
83
|
+
: null;
|
|
84
|
+
// agent.* excluded
|
|
85
|
+
if (ctx.capabilityIntent?.startsWith("agent.")) {
|
|
86
|
+
return { impulse: null, source: "none", capabilityClass: null };
|
|
87
|
+
}
|
|
88
|
+
// capabilityClass preset (sync — no platform port available)
|
|
89
|
+
if (capabilityClass) {
|
|
90
|
+
const ccImpulseKind = CAPABILITY_CLASS_SCENE_MAP[capabilityClass];
|
|
91
|
+
const ccImpulse = getCapabilityClassImpulseTemplate(ccImpulseKind);
|
|
92
|
+
if (ccImpulse) {
|
|
93
|
+
return { impulse: ccImpulse, source: "capability_class", capabilityClass };
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
// intentKind fallback
|
|
97
|
+
const sceneType = ctx.sceneType;
|
|
98
|
+
if (sceneType !== "explain" && sceneType !== "user_reply") {
|
|
99
|
+
const intentImpulse = getImpulseTemplate(sceneType);
|
|
100
|
+
return { impulse: intentImpulse, source: "intent_kind", capabilityClass };
|
|
101
|
+
}
|
|
102
|
+
return { impulse: null, source: "none", capabilityClass };
|
|
103
|
+
}
|
|
@@ -5,6 +5,8 @@ export * from "./output-guard.js";
|
|
|
5
5
|
export * from "./fallback.js";
|
|
6
6
|
export * from "./template-registry.js";
|
|
7
7
|
export * from "./review-workflow.js";
|
|
8
|
+
export * from "./capability-class.js";
|
|
9
|
+
export * from "./impulse-assembler.js";
|
|
8
10
|
export * from "./guidance-assembler.js";
|
|
9
11
|
export * from "./outreach-draft-schema.js";
|
|
10
12
|
export * from "./draft-outreach-message.js";
|
|
@@ -5,6 +5,8 @@ export * from "./output-guard.js";
|
|
|
5
5
|
export * from "./fallback.js";
|
|
6
6
|
export * from "./template-registry.js";
|
|
7
7
|
export * from "./review-workflow.js";
|
|
8
|
+
export * from "./capability-class.js";
|
|
9
|
+
export * from "./impulse-assembler.js";
|
|
8
10
|
export * from "./guidance-assembler.js";
|
|
9
11
|
export * from "./outreach-draft-schema.js";
|
|
10
12
|
export * from "./draft-outreach-message.js";
|
|
@@ -1,10 +1,13 @@
|
|
|
1
|
-
import type { GuardBlock, GuidanceSceneType } from "./types.js";
|
|
2
|
-
export type OutputGuardConstraintId = "avoid_customer_service_tone" | "avoid_daily_report_tone" | "avoid_teaching_template_tone" | "avoid_fabricated_experience" | "avoid_repetitive_phrasing";
|
|
3
|
-
export interface OutputGuardDefinition {
|
|
4
|
-
sceneType: GuidanceSceneType;
|
|
5
|
-
constraints: OutputGuardConstraintId[];
|
|
6
|
-
hardGuardPriority: true;
|
|
7
|
-
note: "output_guard_only_shapes_expression";
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export declare function
|
|
1
|
+
import type { GuardBlock, ExpressionBoundaryBlock, GuidanceSceneType } from "./types.js";
|
|
2
|
+
export type OutputGuardConstraintId = "avoid_customer_service_tone" | "avoid_daily_report_tone" | "avoid_teaching_template_tone" | "avoid_fabricated_experience" | "avoid_repetitive_phrasing";
|
|
3
|
+
export interface OutputGuardDefinition {
|
|
4
|
+
sceneType: GuidanceSceneType;
|
|
5
|
+
constraints: OutputGuardConstraintId[];
|
|
6
|
+
hardGuardPriority: true;
|
|
7
|
+
note: "output_guard_only_shapes_expression";
|
|
8
|
+
}
|
|
9
|
+
/** @deprecated Use buildExpressionBoundary for new code. Kept for backward compatibility. */
|
|
10
|
+
export declare function buildOutputGuard(sceneType: GuidanceSceneType): GuardBlock;
|
|
11
|
+
export declare function getOutputGuardDefinition(sceneType: GuidanceSceneType): OutputGuardDefinition;
|
|
12
|
+
export type ExpressionConstraintId = "avoid_customer_service_tone" | "avoid_daily_report_tone" | "avoid_teaching_template_tone" | "avoid_fabricated_experience" | "avoid_repetitive_phrasing";
|
|
13
|
+
export declare function buildExpressionBoundary(_sceneType: GuidanceSceneType): ExpressionBoundaryBlock;
|
|
@@ -1,29 +1,53 @@
|
|
|
1
|
-
const SHARED_CONSTRAINTS = [
|
|
2
|
-
"avoid_customer_service_tone",
|
|
3
|
-
"avoid_daily_report_tone",
|
|
4
|
-
"avoid_teaching_template_tone",
|
|
5
|
-
"avoid_fabricated_experience",
|
|
6
|
-
"avoid_repetitive_phrasing",
|
|
7
|
-
];
|
|
8
|
-
const SCENE_NOTES = {
|
|
9
|
-
avoid_customer_service_tone: "不要把自己说成客服或通知系统。",
|
|
10
|
-
avoid_daily_report_tone: "不要把表达压扁成日报、周报或例行播报。",
|
|
11
|
-
avoid_teaching_template_tone: "不要把表达写成教程、步骤说明或培训手册。",
|
|
12
|
-
avoid_fabricated_experience: "不要虚构经历、关系、情绪事件或未发生的观察。",
|
|
13
|
-
avoid_repetitive_phrasing: "不要滑进高重复、硬模板化或近似复读的措辞。",
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
1
|
+
const SHARED_CONSTRAINTS = [
|
|
2
|
+
"avoid_customer_service_tone",
|
|
3
|
+
"avoid_daily_report_tone",
|
|
4
|
+
"avoid_teaching_template_tone",
|
|
5
|
+
"avoid_fabricated_experience",
|
|
6
|
+
"avoid_repetitive_phrasing",
|
|
7
|
+
];
|
|
8
|
+
const SCENE_NOTES = {
|
|
9
|
+
avoid_customer_service_tone: "不要把自己说成客服或通知系统。",
|
|
10
|
+
avoid_daily_report_tone: "不要把表达压扁成日报、周报或例行播报。",
|
|
11
|
+
avoid_teaching_template_tone: "不要把表达写成教程、步骤说明或培训手册。",
|
|
12
|
+
avoid_fabricated_experience: "不要虚构经历、关系、情绪事件或未发生的观察。",
|
|
13
|
+
avoid_repetitive_phrasing: "不要滑进高重复、硬模板化或近似复读的措辞。",
|
|
14
|
+
};
|
|
15
|
+
/** @deprecated Use buildExpressionBoundary for new code. Kept for backward compatibility. */
|
|
16
|
+
export function buildOutputGuard(sceneType) {
|
|
17
|
+
return {
|
|
18
|
+
kind: "output_guard",
|
|
19
|
+
constraints: SHARED_CONSTRAINTS.map((constraint) => SCENE_NOTES[constraint]),
|
|
20
|
+
hardGuardPriority: true,
|
|
21
|
+
_semanticNote: "output_guard_only_shapes_expression",
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
export function getOutputGuardDefinition(sceneType) {
|
|
25
|
+
return {
|
|
26
|
+
sceneType,
|
|
27
|
+
constraints: [...SHARED_CONSTRAINTS],
|
|
28
|
+
hardGuardPriority: true,
|
|
29
|
+
note: "output_guard_only_shapes_expression",
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
const EXPRESSION_CONSTRAINTS = [
|
|
33
|
+
"avoid_customer_service_tone",
|
|
34
|
+
"avoid_daily_report_tone",
|
|
35
|
+
"avoid_teaching_template_tone",
|
|
36
|
+
"avoid_fabricated_experience",
|
|
37
|
+
"avoid_repetitive_phrasing",
|
|
38
|
+
];
|
|
39
|
+
const EXPRESSION_NOTES = {
|
|
40
|
+
avoid_customer_service_tone: "避免客服腔或通知系统语气。",
|
|
41
|
+
avoid_daily_report_tone: "避免日报、周报或例行播报腔。",
|
|
42
|
+
avoid_teaching_template_tone: "避免教程、步骤说明或培训手册腔。",
|
|
43
|
+
avoid_fabricated_experience: "避免虚构经历、关系、情绪事件或未发生的观察。",
|
|
44
|
+
avoid_repetitive_phrasing: "避免高重复、硬模板化或近似复读的措辞。",
|
|
45
|
+
};
|
|
46
|
+
export function buildExpressionBoundary(_sceneType) {
|
|
47
|
+
return {
|
|
48
|
+
kind: "expression_boundary",
|
|
49
|
+
constraints: EXPRESSION_CONSTRAINTS.map((c) => EXPRESSION_NOTES[c]),
|
|
50
|
+
style: "avoid_prefer",
|
|
51
|
+
ownership: "behavioral_guidance_system",
|
|
52
|
+
};
|
|
53
|
+
}
|
|
@@ -66,6 +66,19 @@ export declare function runStyleLint(draftText: string): StyleLintResult;
|
|
|
66
66
|
* Never returns empty string. Includes sourceRefs anchor and human-readable channel reason.
|
|
67
67
|
*/
|
|
68
68
|
export declare function buildFallbackCopy(ctx: FallbackContext): FallbackCopy;
|
|
69
|
+
/**
|
|
70
|
+
* Compute outreach frequency from RelationshipMemory.
|
|
71
|
+
* - noReply signals: if ≥50% of last 5 patterns are "ignore" → reduce frequency
|
|
72
|
+
* - trustDelta: negative trust pushes toward minimal/paused
|
|
73
|
+
*/
|
|
74
|
+
export declare function computeFrequency(memory: RelationshipMemory): OutreachFrequency;
|
|
75
|
+
/**
|
|
76
|
+
* Compute phrasing style from RelationshipMemory.
|
|
77
|
+
* - positive tone patterns → warm_anchored
|
|
78
|
+
* - neutral or mixed → concise_factual
|
|
79
|
+
* - degraded trust / mostly negative → light_check
|
|
80
|
+
*/
|
|
81
|
+
export declare function computeStyle(memory: RelationshipMemory, frequency?: OutreachFrequency): OutreachStyle;
|
|
69
82
|
export interface OutreachStrategySelectorOptions {
|
|
70
83
|
fallbackContext?: FallbackContext;
|
|
71
84
|
}
|