@haaaiawd/second-nature 0.1.29 → 0.1.31
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 +8 -20
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/runtime/cli/commands/connector-status.d.ts +1 -0
- package/runtime/cli/commands/connector-status.js +5 -0
- package/runtime/cli/commands/index.js +16 -0
- package/runtime/cli/ops/ops-router.js +3 -0
- package/runtime/core/second-nature/orchestrator/intent-planner.js +3 -0
- package/runtime/core/second-nature/orchestrator/platform-capability-router.js +18 -2
- package/workspace-ops-bridge.js +13 -1
package/index.js
CHANGED
|
@@ -58,23 +58,11 @@ import { fileURLToPath } from "node:url";
|
|
|
58
58
|
import { startRuntimeService, } from "./runtime/core/second-nature/runtime/service-entry.js";
|
|
59
59
|
import { getLifecycleState, recordRegistration, } from "./runtime/core/second-nature/runtime/lifecycle-service.js";
|
|
60
60
|
import { openWorkspaceOpsBridge } from "./workspace-ops-bridge.js";
|
|
61
|
-
//
|
|
62
|
-
//
|
|
63
|
-
//
|
|
64
|
-
//
|
|
65
|
-
//
|
|
66
|
-
// use the factory because it is the documented, supported entry shape, and
|
|
67
|
-
// keeping it future-proof against SDK option-processing changes.
|
|
68
|
-
//
|
|
69
|
-
// IMPORTANT — keep this a STATIC import. The packaged runtime is loaded inside
|
|
70
|
-
// OpenClaw's vm sandbox, which rejects top-level await (manifests as
|
|
71
|
-
// "SyntaxError: Unexpected identifier 'Promise'" at host load time). The same
|
|
72
|
-
// constraint applies to the sql.js async bootstrap noted in the file header.
|
|
73
|
-
// In production the host always provides `openclaw` as a sibling module under
|
|
74
|
-
// ~/.openclaw/npm/node_modules/, so this resolves synchronously. Locally,
|
|
75
|
-
// `openclaw` is declared as a devDependency so build and tests resolve via
|
|
76
|
-
// the same import path.
|
|
77
|
-
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
|
|
61
|
+
// Keep the entry as a plain object instead of importing OpenClaw's
|
|
62
|
+
// SDK entry helper. Upload/package validators may import this module
|
|
63
|
+
// before the host SDK is installed; a static SDK import turns a valid package
|
|
64
|
+
// into ERR_MODULE_NOT_FOUND. OpenClaw classifies this plugin from the manifest
|
|
65
|
+
// fields, and the helper returns this same object shape at runtime.
|
|
78
66
|
// Stderr sentinels make daemon load-path observable in `gateway.log`. Three
|
|
79
67
|
// lines should appear at startup: "module evaluated", "register() entered ...",
|
|
80
68
|
// "register() completed". Their absence after `openclaw gateway run` proves
|
|
@@ -83,7 +71,7 @@ process.stderr.write("[second-nature] module evaluated\n");
|
|
|
83
71
|
const INTERNAL_RUNTIME_TRACE_PREFIX = "sn-runtime-";
|
|
84
72
|
const HOST_SAFE_LIMITATION_MESSAGE = "Host-safe plugin package keeps synchronous register/load semantics, but mutating workspace runtime flows remain unavailable here.";
|
|
85
73
|
const SETUP_MARKER_RELATIVE_PATH = path.join(".second-nature", "setup", "agent-inner-guide-ack.json");
|
|
86
|
-
const SETUP_GUIDE_VERSION = "0.1.
|
|
74
|
+
const SETUP_GUIDE_VERSION = "0.1.31";
|
|
87
75
|
const SETUP_COMMANDS = new Set(["setup_hint", "setup_ack"]);
|
|
88
76
|
let activationSpine = null;
|
|
89
77
|
/** T1.1.4 — lazily opened full read bridge; closed when workspace root / resolution changes. */
|
|
@@ -1189,7 +1177,7 @@ const SECOND_NATURE_TOOL_SCHEMA = {
|
|
|
1189
1177
|
},
|
|
1190
1178
|
required: ["command"],
|
|
1191
1179
|
};
|
|
1192
|
-
export default
|
|
1180
|
+
export default {
|
|
1193
1181
|
id: "second-nature",
|
|
1194
1182
|
name: "Second Nature",
|
|
1195
1183
|
description: "Registers command/tool/service surface with load-reload lifecycle semantics.",
|
|
@@ -1264,4 +1252,4 @@ export default definePluginEntry({
|
|
|
1264
1252
|
});
|
|
1265
1253
|
process.stderr.write("[second-nature] register() completed\n");
|
|
1266
1254
|
},
|
|
1267
|
-
}
|
|
1255
|
+
};
|
package/openclaw.plugin.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "second-nature",
|
|
3
3
|
"name": "Second Nature",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.31",
|
|
5
5
|
"description": "OpenClaw native plugin with synchronous surface registration and bundled runtime spine. Set SECOND_NATURE_WORKSPACE_ROOT or tool workspaceRoot to the same path as the agent workspace. Agent inner guide is packaged as agent-inner-guide.md. v7 ops surface: self_health, tool_affordance, heartbeat_digest, narrative:diff, timeline, restore, runtime_secret_bootstrap.",
|
|
6
6
|
"activation": {
|
|
7
7
|
"onStartup": true,
|
package/package.json
CHANGED
|
@@ -7,6 +7,7 @@ export interface ConnectorStatusInput {
|
|
|
7
7
|
export interface ConnectorTestInput {
|
|
8
8
|
platformId: string;
|
|
9
9
|
dryRun?: boolean;
|
|
10
|
+
workspaceRoot?: string;
|
|
10
11
|
}
|
|
11
12
|
export declare function connectorStatus(registry: DynamicConnectorRegistry | undefined, ledger: ConnectorInventoryLedger | undefined, input?: ConnectorStatusInput): Promise<Record<string, unknown>>;
|
|
12
13
|
export declare function connectorTest(registry: DynamicConnectorRegistry | undefined, input: ConnectorTestInput): Promise<Record<string, unknown>>;
|
|
@@ -97,6 +97,11 @@ export async function connectorTest(registry, input) {
|
|
|
97
97
|
},
|
|
98
98
|
};
|
|
99
99
|
}
|
|
100
|
+
// Workspace bridge calls are often process-isolated per tool invocation.
|
|
101
|
+
// Reload here as well as in connector_status so dry-run tests see the same inventory.
|
|
102
|
+
if (input.workspaceRoot) {
|
|
103
|
+
registry.reloadConnectors(input.workspaceRoot);
|
|
104
|
+
}
|
|
100
105
|
const entry = registry.describeConnector(platformId);
|
|
101
106
|
if (!entry) {
|
|
102
107
|
return {
|
|
@@ -23,6 +23,14 @@ function explainSubjectError(code, message) {
|
|
|
23
23
|
}
|
|
24
24
|
export function createCliCommands(deps) {
|
|
25
25
|
const { readModels, actionBridge, opsRouter } = deps;
|
|
26
|
+
const opsCommand = (name, description) => ({
|
|
27
|
+
name,
|
|
28
|
+
description,
|
|
29
|
+
execute: async (input) => {
|
|
30
|
+
const surface = await Promise.resolve(opsRouter.dispatch(name, input));
|
|
31
|
+
return surface;
|
|
32
|
+
},
|
|
33
|
+
});
|
|
26
34
|
return [
|
|
27
35
|
{
|
|
28
36
|
name: "status",
|
|
@@ -268,6 +276,14 @@ export function createCliCommands(deps) {
|
|
|
268
276
|
return surface;
|
|
269
277
|
},
|
|
270
278
|
},
|
|
279
|
+
opsCommand("connector:run", "T-ROS.C.3 — manually execute a connector capability outside heartbeat cadence"),
|
|
280
|
+
opsCommand("self_health", "T-ROS.C.1 — show v7 self-health snapshot and degraded dimensions"),
|
|
281
|
+
opsCommand("tool_affordance", "T-ROS.C.1 — show v7 tool affordance map or explicit unavailable state"),
|
|
282
|
+
opsCommand("heartbeat_digest", "T-ROS.C.1 — assemble v7 heartbeat digest for a day"),
|
|
283
|
+
opsCommand("narrative:diff", "T-ROS.C.1 — compare two narrative timeline versions"),
|
|
284
|
+
opsCommand("timeline", "T-ROS.C.1 — query v7 narrative timeline with cursor pagination"),
|
|
285
|
+
opsCommand("restore", "T-ROS.C.1 — apply bounded restore and write restore audit"),
|
|
286
|
+
opsCommand("runtime_secret_bootstrap", "T-ROS.C.1 — inspect runtime secret anchor health without exposing plaintext"),
|
|
271
287
|
{
|
|
272
288
|
name: "goal",
|
|
273
289
|
description: "T1.2.4 — owner-governed goal operations: set, list, accept, reject",
|
|
@@ -249,6 +249,9 @@ export function createOpsRouter(deps) {
|
|
|
249
249
|
const result = await connectorTest(deps.registry, {
|
|
250
250
|
platformId: typeof input?.platformId === "string" ? input.platformId : "",
|
|
251
251
|
dryRun: isWet ? false : (input?.dryRun === false ? false : true),
|
|
252
|
+
workspaceRoot: typeof input?.workspaceRoot === "string"
|
|
253
|
+
? input.workspaceRoot
|
|
254
|
+
: deps.workspaceRoot,
|
|
252
255
|
});
|
|
253
256
|
if (isWet && result.ok) {
|
|
254
257
|
// Annotate result with manual trigger context (DR-038 / T-ROS.C.3)
|
|
@@ -79,6 +79,9 @@ export function planIntentWithKind(kind, basePriority, runtime, context, registr
|
|
|
79
79
|
return [];
|
|
80
80
|
const config = INTENT_CONFIGS[kind];
|
|
81
81
|
const platformId = resolvePlatformForIntent(kind, context ?? {}, registry);
|
|
82
|
+
if (kind === "work" && !options?.multiSource && !platformId) {
|
|
83
|
+
return [];
|
|
84
|
+
}
|
|
82
85
|
let priority = basePriority;
|
|
83
86
|
// Social budget exhaustion → cap priority.
|
|
84
87
|
if (kind === "social" &&
|
|
@@ -14,7 +14,19 @@ function getPlatformIds(registry) {
|
|
|
14
14
|
return registry.listRegisteredPlatformIds();
|
|
15
15
|
}
|
|
16
16
|
// Fallback: built-in platforms when registry is absent (backward compat)
|
|
17
|
-
return ["moltbook", "instreet", "evomap"];
|
|
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);
|
|
18
30
|
}
|
|
19
31
|
function extractPlatformIdsFromGoals(goals, kind, platformIds) {
|
|
20
32
|
const capability = kindToCapability(kind);
|
|
@@ -110,6 +122,10 @@ export function resolvePlatformForIntent(kind, context, registry) {
|
|
|
110
122
|
// Registry says unsupported → undefined (guard layer will deny)
|
|
111
123
|
return undefined;
|
|
112
124
|
}
|
|
113
|
-
// No registry:
|
|
125
|
+
// No registry: keep legacy platform-name fallback, but do not invent an
|
|
126
|
+
// unsupported platform/capability pair that later fails as protocol_mismatch.
|
|
127
|
+
if (!fallbackPlatformSupportsCapability(single, kind)) {
|
|
128
|
+
return undefined;
|
|
129
|
+
}
|
|
114
130
|
return single;
|
|
115
131
|
}
|
package/workspace-ops-bridge.js
CHANGED
|
@@ -23,7 +23,19 @@ import fs from "node:fs";
|
|
|
23
23
|
import path from "node:path";
|
|
24
24
|
import { fileURLToPath } from "node:url";
|
|
25
25
|
export async function openWorkspaceOpsBridge(workspaceRoot) {
|
|
26
|
-
const
|
|
26
|
+
const declaredRoot = typeof workspaceRoot === "string" ? workspaceRoot.trim() : "";
|
|
27
|
+
if (!declaredRoot) {
|
|
28
|
+
return {
|
|
29
|
+
ok: false,
|
|
30
|
+
error: {
|
|
31
|
+
code: "WORKSPACE_ROOT_REQUIRED",
|
|
32
|
+
message: "openWorkspaceOpsBridge requires a workspaceRoot path. Set SECOND_NATURE_WORKSPACE_ROOT or pass tool workspaceRoot.",
|
|
33
|
+
nextStep: "reinvoke_with_workspaceRoot_or_set_SECOND_NATURE_WORKSPACE_ROOT",
|
|
34
|
+
requiredUserInput: ["workspaceRoot"],
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
const resolvedRoot = path.resolve(declaredRoot);
|
|
27
39
|
try {
|
|
28
40
|
const pluginPackageRoot = path.dirname(fileURLToPath(import.meta.url));
|
|
29
41
|
// Packaged `plugin/runtime` is emitted JS without sibling `.d.ts` in this repo layout.
|