@haaaiawd/second-nature 0.1.43 → 0.1.51
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/openclaw.plugin.json +29 -29
- package/package.json +55 -55
- package/runtime/cli/commands/index.js +325 -325
- package/runtime/cli/ops/heartbeat-surface.d.ts +84 -75
- package/runtime/cli/ops/heartbeat-surface.js +100 -97
- package/runtime/cli/ops/ops-router.js +1482 -1454
- package/runtime/cli/ops/workspace-heartbeat-runner.d.ts +85 -76
- package/runtime/cli/ops/workspace-heartbeat-runner.js +242 -236
- package/runtime/connectors/base/contract.d.ts +111 -111
- package/runtime/connectors/base/failure-taxonomy.d.ts +13 -13
- package/runtime/connectors/base/failure-taxonomy.js +186 -186
- package/runtime/connectors/base/map-life-evidence.js +137 -137
- package/runtime/connectors/base/policy-layer.js +202 -202
- package/runtime/connectors/manifest/manifest-schema.d.ts +152 -152
- package/runtime/connectors/manifest/manifest-schema.js +54 -54
- package/runtime/connectors/services/connector-executor-adapter.d.ts +20 -20
- package/runtime/connectors/services/connector-executor-adapter.js +645 -645
- package/runtime/core/second-nature/heartbeat/goal-lifecycle-policy.d.ts +24 -37
- package/runtime/core/second-nature/heartbeat/goal-lifecycle-policy.js +61 -61
- package/runtime/core/second-nature/heartbeat/heartbeat-loop.d.ts +97 -88
- package/runtime/core/second-nature/heartbeat/heartbeat-loop.js +397 -329
- package/runtime/core/second-nature/orchestrator/platform-capability-router.js +149 -131
|
@@ -1,131 +1,149 @@
|
|
|
1
|
-
function kindToCapability(kind) {
|
|
2
|
-
if (kind === "exploration")
|
|
3
|
-
return "feed.read";
|
|
4
|
-
if (kind === "social")
|
|
5
|
-
return "comment.reply";
|
|
6
|
-
if (kind === "work")
|
|
7
|
-
return "work.discover";
|
|
8
|
-
if (kind === "outreach")
|
|
9
|
-
return "message.send";
|
|
10
|
-
return null;
|
|
11
|
-
}
|
|
12
|
-
function getPlatformIds(registry) {
|
|
13
|
-
if (registry) {
|
|
14
|
-
return registry.listRegisteredPlatformIds();
|
|
15
|
-
}
|
|
16
|
-
// Fallback: built-in platforms when registry is absent (backward compat)
|
|
17
|
-
return ["moltbook", "instreet", "evomap", "agent-world"];
|
|
18
|
-
}
|
|
19
|
-
const FALLBACK_PLATFORM_CAPABILITIES = {
|
|
20
|
-
"moltbook": ["feed.read", "post.publish", "comment.reply", "message.send"],
|
|
21
|
-
"instreet": ["notification.list", "message.send", "comment.reply", "agent.heartbeat"],
|
|
22
|
-
"evomap": ["agent.register", "agent.heartbeat", "work.discover", "task.claim"],
|
|
23
|
-
"agent-world": ["feed.read", "work.discover", "task.claim"],
|
|
24
|
-
};
|
|
25
|
-
function fallbackPlatformSupportsCapability(platformId, kind) {
|
|
26
|
-
const capability = kindToCapability(kind);
|
|
27
|
-
if (!capability)
|
|
28
|
-
return false;
|
|
29
|
-
return (FALLBACK_PLATFORM_CAPABILITIES[platformId] ?? []).includes(capability);
|
|
30
|
-
}
|
|
31
|
-
function extractPlatformIdsFromGoals(goals, kind, platformIds) {
|
|
32
|
-
const capability = kindToCapability(kind);
|
|
33
|
-
const results = new Set();
|
|
34
|
-
for (const goal of goals) {
|
|
35
|
-
const text = `${goal.description} ${goal.completionCriteria ?? ""}`.toLowerCase();
|
|
36
|
-
for (const pid of platformIds) {
|
|
37
|
-
if (text.includes(pid)) {
|
|
38
|
-
results.add(pid);
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
// Also match if goal text contains the capability name (e.g. "feed.read")
|
|
42
|
-
if (capability && text.includes(capability.toLowerCase())) {
|
|
43
|
-
// capability alone doesn't tell us platform; keep for later
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
return [...results];
|
|
47
|
-
}
|
|
48
|
-
function extractPlatformIdsFromEvidence(refs, platformIds) {
|
|
49
|
-
const results = new Set();
|
|
50
|
-
for (const ref of refs) {
|
|
51
|
-
if (ref.kind === "connector_result" && ref.id) {
|
|
52
|
-
for (const pid of platformIds) {
|
|
53
|
-
if (ref.id.includes(pid)) {
|
|
54
|
-
results.add(pid);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
// Parse platform:// URIs (e.g. platform://moltbook/feed.read)
|
|
59
|
-
if (ref.uri && ref.uri.startsWith("platform://")) {
|
|
60
|
-
const afterScheme = ref.uri.slice("platform://".length);
|
|
61
|
-
const platformPart = afterScheme.split("/")[0];
|
|
62
|
-
if (platformPart && platformIds.includes(platformPart)) {
|
|
63
|
-
results.add(platformPart);
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
// L-02: Also support namespace format moltbook:feed.read (connector-system §5.3)
|
|
67
|
-
if (ref.uri && !ref.uri.includes("://") && ref.uri.includes(":")) {
|
|
68
|
-
const nsPart = ref.uri.split(":")[0];
|
|
69
|
-
if (nsPart && platformIds.includes(nsPart)) {
|
|
70
|
-
results.add(nsPart);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
return [...results];
|
|
75
|
-
}
|
|
76
|
-
function validatePlatformCapability(platformId, kind, registry) {
|
|
77
|
-
const capability = kindToCapability(kind);
|
|
78
|
-
if (!capability)
|
|
79
|
-
return false;
|
|
80
|
-
try {
|
|
81
|
-
return registry.hasCapability(platformId, capability);
|
|
82
|
-
}
|
|
83
|
-
catch (err) {
|
|
84
|
-
// H-08: Log registry validation failures for observability.
|
|
85
|
-
console.warn(`[platform-capability-router] Registry validation failed for ${platformId}:${capability}`, err);
|
|
86
|
-
return false;
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
/**
|
|
90
|
-
* Resolve an explicit platformId for a candidate intent kind.
|
|
91
|
-
* Returns `undefined` when no unambiguous platform can be inferred.
|
|
92
|
-
*/
|
|
93
|
-
export function resolvePlatformForIntent(kind, context, registry) {
|
|
94
|
-
const capability = kindToCapability(kind);
|
|
95
|
-
if (!capability) {
|
|
96
|
-
// Quiet, reflection, maintenance have no connector capability mapping.
|
|
97
|
-
return undefined;
|
|
98
|
-
}
|
|
99
|
-
const platformIds = getPlatformIds(registry);
|
|
100
|
-
const candidates = [];
|
|
101
|
-
if (context.acceptedGoals && context.acceptedGoals.length > 0) {
|
|
102
|
-
candidates.push(...extractPlatformIdsFromGoals(context.acceptedGoals, kind, platformIds));
|
|
103
|
-
}
|
|
104
|
-
if (context.evidenceRefs && context.evidenceRefs.length > 0) {
|
|
105
|
-
candidates.push(...extractPlatformIdsFromEvidence(context.evidenceRefs, platformIds));
|
|
106
|
-
}
|
|
107
|
-
// Deduplicate while preserving order
|
|
108
|
-
const ordered = [...new Set(candidates)];
|
|
109
|
-
if (ordered.length
|
|
110
|
-
return undefined
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
return
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
|
|
1
|
+
function kindToCapability(kind) {
|
|
2
|
+
if (kind === "exploration")
|
|
3
|
+
return "feed.read";
|
|
4
|
+
if (kind === "social")
|
|
5
|
+
return "comment.reply";
|
|
6
|
+
if (kind === "work")
|
|
7
|
+
return "work.discover";
|
|
8
|
+
if (kind === "outreach")
|
|
9
|
+
return "message.send";
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
function getPlatformIds(registry) {
|
|
13
|
+
if (registry) {
|
|
14
|
+
return registry.listRegisteredPlatformIds();
|
|
15
|
+
}
|
|
16
|
+
// Fallback: built-in platforms when registry is absent (backward compat)
|
|
17
|
+
return ["moltbook", "instreet", "evomap", "agent-world"];
|
|
18
|
+
}
|
|
19
|
+
const FALLBACK_PLATFORM_CAPABILITIES = {
|
|
20
|
+
"moltbook": ["feed.read", "post.publish", "comment.reply", "message.send"],
|
|
21
|
+
"instreet": ["notification.list", "message.send", "comment.reply", "agent.heartbeat"],
|
|
22
|
+
"evomap": ["agent.register", "agent.heartbeat", "work.discover", "task.claim"],
|
|
23
|
+
"agent-world": ["feed.read", "work.discover", "task.claim"],
|
|
24
|
+
};
|
|
25
|
+
function fallbackPlatformSupportsCapability(platformId, kind) {
|
|
26
|
+
const capability = kindToCapability(kind);
|
|
27
|
+
if (!capability)
|
|
28
|
+
return false;
|
|
29
|
+
return (FALLBACK_PLATFORM_CAPABILITIES[platformId] ?? []).includes(capability);
|
|
30
|
+
}
|
|
31
|
+
function extractPlatformIdsFromGoals(goals, kind, platformIds) {
|
|
32
|
+
const capability = kindToCapability(kind);
|
|
33
|
+
const results = new Set();
|
|
34
|
+
for (const goal of goals) {
|
|
35
|
+
const text = `${goal.description} ${goal.completionCriteria ?? ""}`.toLowerCase();
|
|
36
|
+
for (const pid of platformIds) {
|
|
37
|
+
if (text.includes(pid)) {
|
|
38
|
+
results.add(pid);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
// Also match if goal text contains the capability name (e.g. "feed.read")
|
|
42
|
+
if (capability && text.includes(capability.toLowerCase())) {
|
|
43
|
+
// capability alone doesn't tell us platform; keep for later
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return [...results];
|
|
47
|
+
}
|
|
48
|
+
function extractPlatformIdsFromEvidence(refs, platformIds) {
|
|
49
|
+
const results = new Set();
|
|
50
|
+
for (const ref of refs) {
|
|
51
|
+
if (ref.kind === "connector_result" && ref.id) {
|
|
52
|
+
for (const pid of platformIds) {
|
|
53
|
+
if (ref.id.includes(pid)) {
|
|
54
|
+
results.add(pid);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
// Parse platform:// URIs (e.g. platform://moltbook/feed.read)
|
|
59
|
+
if (ref.uri && ref.uri.startsWith("platform://")) {
|
|
60
|
+
const afterScheme = ref.uri.slice("platform://".length);
|
|
61
|
+
const platformPart = afterScheme.split("/")[0];
|
|
62
|
+
if (platformPart && platformIds.includes(platformPart)) {
|
|
63
|
+
results.add(platformPart);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
// L-02: Also support namespace format moltbook:feed.read (connector-system §5.3)
|
|
67
|
+
if (ref.uri && !ref.uri.includes("://") && ref.uri.includes(":")) {
|
|
68
|
+
const nsPart = ref.uri.split(":")[0];
|
|
69
|
+
if (nsPart && platformIds.includes(nsPart)) {
|
|
70
|
+
results.add(nsPart);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return [...results];
|
|
75
|
+
}
|
|
76
|
+
function validatePlatformCapability(platformId, kind, registry) {
|
|
77
|
+
const capability = kindToCapability(kind);
|
|
78
|
+
if (!capability)
|
|
79
|
+
return false;
|
|
80
|
+
try {
|
|
81
|
+
return registry.hasCapability(platformId, capability);
|
|
82
|
+
}
|
|
83
|
+
catch (err) {
|
|
84
|
+
// H-08: Log registry validation failures for observability.
|
|
85
|
+
console.warn(`[platform-capability-router] Registry validation failed for ${platformId}:${capability}`, err);
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Resolve an explicit platformId for a candidate intent kind.
|
|
91
|
+
* Returns `undefined` when no unambiguous platform can be inferred.
|
|
92
|
+
*/
|
|
93
|
+
export function resolvePlatformForIntent(kind, context, registry) {
|
|
94
|
+
const capability = kindToCapability(kind);
|
|
95
|
+
if (!capability) {
|
|
96
|
+
// Quiet, reflection, maintenance have no connector capability mapping.
|
|
97
|
+
return undefined;
|
|
98
|
+
}
|
|
99
|
+
const platformIds = getPlatformIds(registry);
|
|
100
|
+
const candidates = [];
|
|
101
|
+
if (context.acceptedGoals && context.acceptedGoals.length > 0) {
|
|
102
|
+
candidates.push(...extractPlatformIdsFromGoals(context.acceptedGoals, kind, platformIds));
|
|
103
|
+
}
|
|
104
|
+
if (context.evidenceRefs && context.evidenceRefs.length > 0) {
|
|
105
|
+
candidates.push(...extractPlatformIdsFromEvidence(context.evidenceRefs, platformIds));
|
|
106
|
+
}
|
|
107
|
+
// Deduplicate while preserving order
|
|
108
|
+
const ordered = [...new Set(candidates)];
|
|
109
|
+
if (ordered.length > 1) {
|
|
110
|
+
// Ambiguous: multiple platforms inferred → do not guess, return undefined.
|
|
111
|
+
// Guard layer will deny with "ambiguous_platform" reason.
|
|
112
|
+
return undefined;
|
|
113
|
+
}
|
|
114
|
+
if (ordered.length === 1) {
|
|
115
|
+
const single = ordered[0];
|
|
116
|
+
if (registry) {
|
|
117
|
+
if (validatePlatformCapability(single, kind, registry)) {
|
|
118
|
+
return single;
|
|
119
|
+
}
|
|
120
|
+
// Registry says unsupported → undefined (guard layer will deny)
|
|
121
|
+
return undefined;
|
|
122
|
+
}
|
|
123
|
+
// No registry: keep legacy platform-name fallback, but do not invent an
|
|
124
|
+
// unsupported platform/capability pair that later fails as protocol_mismatch.
|
|
125
|
+
if (!fallbackPlatformSupportsCapability(single, kind)) {
|
|
126
|
+
return undefined;
|
|
127
|
+
}
|
|
128
|
+
return single;
|
|
129
|
+
}
|
|
130
|
+
// No candidates inferred from goals/evidence → fallback to a supported platform.
|
|
131
|
+
// Sort alphabetically so different capabilities map to different platforms over time,
|
|
132
|
+
// preventing a single platform (e.g. moltbook) from monopolising all connector traffic.
|
|
133
|
+
if (registry) {
|
|
134
|
+
const supported = platformIds
|
|
135
|
+
.filter((pid) => validatePlatformCapability(pid, kind, registry))
|
|
136
|
+
.sort();
|
|
137
|
+
if (supported.length > 0) {
|
|
138
|
+
return supported[0];
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
// No registry: use fallback capability mapping
|
|
142
|
+
const supportedFallback = platformIds
|
|
143
|
+
.filter((pid) => fallbackPlatformSupportsCapability(pid, kind))
|
|
144
|
+
.sort();
|
|
145
|
+
if (supportedFallback.length > 0) {
|
|
146
|
+
return supportedFallback[0];
|
|
147
|
+
}
|
|
148
|
+
return undefined;
|
|
149
|
+
}
|