@haaaiawd/second-nature 0.1.30 → 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 CHANGED
@@ -71,7 +71,7 @@ process.stderr.write("[second-nature] module evaluated\n");
71
71
  const INTERNAL_RUNTIME_TRACE_PREFIX = "sn-runtime-";
72
72
  const HOST_SAFE_LIMITATION_MESSAGE = "Host-safe plugin package keeps synchronous register/load semantics, but mutating workspace runtime flows remain unavailable here.";
73
73
  const SETUP_MARKER_RELATIVE_PATH = path.join(".second-nature", "setup", "agent-inner-guide-ack.json");
74
- const SETUP_GUIDE_VERSION = "0.1.28";
74
+ const SETUP_GUIDE_VERSION = "0.1.31";
75
75
  const SETUP_COMMANDS = new Set(["setup_hint", "setup_ack"]);
76
76
  let activationSpine = null;
77
77
  /** T1.1.4 — lazily opened full read bridge; closed when workspace root / resolution changes. */
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "id": "second-nature",
3
3
  "name": "Second Nature",
4
- "version": "0.1.30",
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@haaaiawd/second-nature",
3
- "version": "0.1.30",
3
+ "version": "0.1.31",
4
4
  "description": "OpenClaw native plugin with synchronous registration, a packaged runtime artifact, and operator-facing status/explain flows.",
5
5
  "keywords": [
6
6
  "openclaw",
@@ -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: best-effort return the single candidate (backward compat)
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
  }
@@ -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 resolvedRoot = path.resolve(workspaceRoot);
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.