@haaaiawd/second-nature 0.1.22 → 0.1.24

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.
Files changed (79) hide show
  1. package/openclaw.plugin.json +1 -1
  2. package/package.json +1 -1
  3. package/runtime/cli/commands/connector-init.d.ts +19 -0
  4. package/runtime/cli/commands/connector-init.js +168 -0
  5. package/runtime/cli/commands/connector-status.d.ts +12 -0
  6. package/runtime/cli/commands/connector-status.js +156 -0
  7. package/runtime/cli/commands/index.js +40 -0
  8. package/runtime/cli/index.js +52 -0
  9. package/runtime/cli/ops/ops-router.d.ts +5 -0
  10. package/runtime/cli/ops/ops-router.js +34 -0
  11. package/runtime/cli/ops/workspace-heartbeat-runner.js +22 -0
  12. package/runtime/connectors/agent-network/agent-world/adapter.d.ts +11 -0
  13. package/runtime/connectors/agent-network/agent-world/adapter.js +58 -0
  14. package/runtime/connectors/agent-network/agent-world/index.d.ts +2 -0
  15. package/runtime/connectors/agent-network/agent-world/index.js +2 -0
  16. package/runtime/connectors/agent-network/agent-world/manifest.d.ts +2 -0
  17. package/runtime/connectors/agent-network/agent-world/manifest.js +7 -0
  18. package/runtime/connectors/base/manifest.d.ts +13 -0
  19. package/runtime/connectors/base/manifest.js +47 -0
  20. package/runtime/connectors/manifest/manifest-parser.d.ts +16 -0
  21. package/runtime/connectors/manifest/manifest-parser.js +35 -0
  22. package/runtime/connectors/manifest/manifest-schema.d.ts +145 -0
  23. package/runtime/connectors/manifest/manifest-schema.js +51 -0
  24. package/runtime/connectors/registry/dynamic-connector-registry.d.ts +29 -0
  25. package/runtime/connectors/registry/dynamic-connector-registry.js +123 -0
  26. package/runtime/connectors/registry/index.d.ts +3 -0
  27. package/runtime/connectors/registry/index.js +3 -0
  28. package/runtime/connectors/registry/manifest-scanner.d.ts +9 -0
  29. package/runtime/connectors/registry/manifest-scanner.js +29 -0
  30. package/runtime/connectors/registry/trust-policy.d.ts +13 -0
  31. package/runtime/connectors/registry/trust-policy.js +37 -0
  32. package/runtime/connectors/services/connector-executor-adapter.js +49 -0
  33. package/runtime/core/second-nature/heartbeat/heartbeat-loop.d.ts +3 -0
  34. package/runtime/core/second-nature/heartbeat/heartbeat-loop.js +52 -1
  35. package/runtime/core/second-nature/heartbeat/snapshot-builder.d.ts +3 -0
  36. package/runtime/core/second-nature/orchestrator/goal-priority.d.ts +19 -0
  37. package/runtime/core/second-nature/orchestrator/goal-priority.js +67 -0
  38. package/runtime/core/second-nature/orchestrator/intent-planner.js +8 -0
  39. package/runtime/core/second-nature/orchestrator/narrative-update.d.ts +27 -0
  40. package/runtime/core/second-nature/orchestrator/narrative-update.js +107 -0
  41. package/runtime/core/second-nature/types.d.ts +4 -0
  42. package/runtime/guidance/draft-narrative-outreach.d.ts +36 -0
  43. package/runtime/guidance/draft-narrative-outreach.js +84 -0
  44. package/runtime/guidance/index.d.ts +1 -0
  45. package/runtime/guidance/index.js +1 -0
  46. package/runtime/guidance/outreach-draft-schema.d.ts +3 -3
  47. package/runtime/observability/connector-inventory-ledger.d.ts +45 -0
  48. package/runtime/observability/connector-inventory-ledger.js +72 -0
  49. package/runtime/observability/db/index.js +13 -0
  50. package/runtime/observability/db/schema/connector-inventory.d.ts +174 -0
  51. package/runtime/observability/db/schema/connector-inventory.js +15 -0
  52. package/runtime/observability/db/schema/index.d.ts +1 -0
  53. package/runtime/observability/db/schema/index.js +1 -0
  54. package/runtime/storage/chronicle/session-chronicle-store.d.ts +42 -0
  55. package/runtime/storage/chronicle/session-chronicle-store.js +66 -0
  56. package/runtime/storage/db/index.js +75 -0
  57. package/runtime/storage/db/schema/agent-goal.d.ts +235 -0
  58. package/runtime/storage/db/schema/agent-goal.js +19 -0
  59. package/runtime/storage/db/schema/index.d.ts +5 -0
  60. package/runtime/storage/db/schema/index.js +5 -0
  61. package/runtime/storage/db/schema/memory-store.d.ts +199 -0
  62. package/runtime/storage/db/schema/memory-store.js +18 -0
  63. package/runtime/storage/db/schema/narrative-state.d.ts +195 -0
  64. package/runtime/storage/db/schema/narrative-state.js +16 -0
  65. package/runtime/storage/db/schema/relationship-memory.d.ts +174 -0
  66. package/runtime/storage/db/schema/relationship-memory.js +14 -0
  67. package/runtime/storage/db/schema/session-chronicle.d.ts +199 -0
  68. package/runtime/storage/db/schema/session-chronicle.js +18 -0
  69. package/runtime/storage/goal/agent-goal-store.d.ts +57 -0
  70. package/runtime/storage/goal/agent-goal-store.js +109 -0
  71. package/runtime/storage/index.d.ts +5 -0
  72. package/runtime/storage/index.js +5 -0
  73. package/runtime/storage/memory-store/memory-store-lifecycle.d.ts +70 -0
  74. package/runtime/storage/memory-store/memory-store-lifecycle.js +113 -0
  75. package/runtime/storage/narrative/narrative-state-store.d.ts +40 -0
  76. package/runtime/storage/narrative/narrative-state-store.js +79 -0
  77. package/runtime/storage/relationship/relationship-memory-store.d.ts +42 -0
  78. package/runtime/storage/relationship/relationship-memory-store.js +76 -0
  79. package/workspace-ops-bridge.js +1 -0
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "id": "second-nature",
3
3
  "name": "Second Nature",
4
- "version": "0.1.22",
4
+ "version": "0.1.23",
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 (see README / T1.1.4 ops norm).",
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.22",
3
+ "version": "0.1.24",
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",
@@ -0,0 +1,19 @@
1
+ export interface ConnectorInitInput {
2
+ platformId: string;
3
+ family?: "social_community" | "agent_network" | "work_platform" | "custom";
4
+ displayName?: string;
5
+ baseUrl?: string;
6
+ runnerKind?: "declarative_http" | "declarative_a2a" | "declarative_mcp" | "cli_descriptor" | "custom_adapter" | "skill" | "browser";
7
+ force?: boolean;
8
+ workspaceRoot?: string;
9
+ }
10
+ export interface ConnectorInitResult {
11
+ ok: boolean;
12
+ platformId: string;
13
+ manifestPath: string;
14
+ adapterPath: string;
15
+ typesPath: string;
16
+ created: boolean;
17
+ reason?: string;
18
+ }
19
+ export declare function connectorInit(input: ConnectorInitInput): Promise<ConnectorInitResult>;
@@ -0,0 +1,168 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ function resolveConnectorsDir(workspaceRoot) {
4
+ if (workspaceRoot) {
5
+ return path.resolve(workspaceRoot, ".second-nature", "connectors");
6
+ }
7
+ return path.resolve(process.cwd(), ".second-nature", "connectors");
8
+ }
9
+ function generateManifestYaml(input) {
10
+ const platformId = input.platformId;
11
+ const displayName = input.displayName ?? platformId;
12
+ const family = input.family ?? "custom";
13
+ const runnerKind = input.runnerKind ?? "declarative_http";
14
+ const baseUrlLine = input.baseUrl ? `\nbaseUrl: ${input.baseUrl}` : "";
15
+ return `schemaVersion: sn.connector.v1
16
+ platformId: ${platformId}
17
+ displayName: ${displayName}
18
+ family: ${family}${baseUrlLine}
19
+ capabilities:
20
+ - id: ${platformId}.placeholder
21
+ description: Placeholder capability — replace with real capability declarations
22
+ runner:
23
+ kind: ${runnerKind}
24
+ entrypoint: ""
25
+ credentials: []
26
+ sourceRefPolicy:
27
+ minSourceRefs: 1
28
+ rejectInlineSensitivePayload: true
29
+ trust:
30
+ status: custom_adapter_pending_trust
31
+ reason: generated_by_connector_init
32
+ `;
33
+ }
34
+ function generateAdapterStub(platformId) {
35
+ const pascalId = platformId
36
+ .split(/[-_]/)
37
+ .map((s) => s.charAt(0).toUpperCase() + s.slice(1).toLowerCase())
38
+ .join("");
39
+ return `/**
40
+ * ${pascalId} Connector Adapter Stub
41
+ *
42
+ * Generated by \`second-nature connector init\`.
43
+ * Replace this stub with real platform-specific execution logic.
44
+ *
45
+ * Runner kind: custom_adapter (pending trust until owner allowlists).
46
+ */
47
+
48
+ import type { ConnectorResult } from "@second-nature/connector-sdk";
49
+
50
+ export interface ${pascalId}AdapterConfig {
51
+ baseUrl: string;
52
+ apiKey?: string;
53
+ }
54
+
55
+ export async function execute${pascalId}Action(
56
+ config: ${pascalId}AdapterConfig,
57
+ action: string,
58
+ payload: unknown,
59
+ ): Promise<ConnectorResult> {
60
+ // TODO: implement real platform call
61
+ return {
62
+ ok: false,
63
+ error: {
64
+ code: "NOT_IMPLEMENTED",
65
+ message: \`${pascalId} adapter action '\${action}' is not yet implemented\`,
66
+ },
67
+ };
68
+ }
69
+ `;
70
+ }
71
+ function generateTypesStub(platformId) {
72
+ const pascalId = platformId
73
+ .split(/[-_]/)
74
+ .map((s) => s.charAt(0).toUpperCase() + s.slice(1).toLowerCase())
75
+ .join("");
76
+ return `/**
77
+ * ${pascalId} Connector Type Stubs
78
+ *
79
+ * Generated by \`second-nature connector init\`.
80
+ * Expand these types as you add real capabilities.
81
+ */
82
+
83
+ export interface ${pascalId}Capability {
84
+ id: string;
85
+ description?: string;
86
+ }
87
+
88
+ export interface ${pascalId}Credential {
89
+ type: "api_key" | "oauth" | "session";
90
+ required: boolean;
91
+ }
92
+
93
+ export interface ${pascalId}PlatformPayload {
94
+ // TODO: define real platform-specific payload shapes
95
+ [key: string]: unknown;
96
+ }
97
+ `;
98
+ }
99
+ export async function connectorInit(input) {
100
+ const { platformId, force = false } = input;
101
+ if (!platformId || typeof platformId !== "string" || platformId.trim().length === 0) {
102
+ return {
103
+ ok: false,
104
+ platformId: "",
105
+ manifestPath: "",
106
+ adapterPath: "",
107
+ typesPath: "",
108
+ created: false,
109
+ reason: "platformId is required and must be a non-empty string",
110
+ };
111
+ }
112
+ const trimmed = platformId.trim();
113
+ if (trimmed === "." || trimmed === "..") {
114
+ return {
115
+ ok: false,
116
+ platformId: trimmed,
117
+ manifestPath: "",
118
+ adapterPath: "",
119
+ typesPath: "",
120
+ created: false,
121
+ reason: "platformId cannot be '.' or '..'",
122
+ };
123
+ }
124
+ const sanitized = trimmed.replace(/[^a-zA-Z0-9_-]/g, "_");
125
+ if (sanitized !== trimmed) {
126
+ return {
127
+ ok: false,
128
+ platformId,
129
+ manifestPath: "",
130
+ adapterPath: "",
131
+ typesPath: "",
132
+ created: false,
133
+ reason: `platformId contains invalid characters. Suggested: ${sanitized}`,
134
+ };
135
+ }
136
+ const connectorsDir = resolveConnectorsDir(input.workspaceRoot);
137
+ const platformDir = path.join(connectorsDir, sanitized);
138
+ const manifestPath = path.join(platformDir, "manifest.yaml");
139
+ const adapterPath = path.join(platformDir, "adapter.ts");
140
+ const typesPath = path.join(platformDir, "types.ts");
141
+ // CR7-01: fail-closed when target already exists (no silent skip)
142
+ if (fs.existsSync(manifestPath) && !force) {
143
+ return {
144
+ ok: false,
145
+ platformId: sanitized,
146
+ manifestPath,
147
+ adapterPath,
148
+ typesPath,
149
+ created: false,
150
+ reason: "manifest already exists; use force:true to overwrite",
151
+ };
152
+ }
153
+ fs.mkdirSync(platformDir, { recursive: true });
154
+ const manifestContent = generateManifestYaml({ ...input, platformId: sanitized });
155
+ fs.writeFileSync(manifestPath, manifestContent, "utf-8");
156
+ const adapterContent = generateAdapterStub(sanitized);
157
+ fs.writeFileSync(adapterPath, adapterContent, "utf-8");
158
+ const typesContent = generateTypesStub(sanitized);
159
+ fs.writeFileSync(typesPath, typesContent, "utf-8");
160
+ return {
161
+ ok: true,
162
+ platformId: sanitized,
163
+ manifestPath,
164
+ adapterPath,
165
+ typesPath,
166
+ created: true,
167
+ };
168
+ }
@@ -0,0 +1,12 @@
1
+ import type { DynamicConnectorRegistry } from "../../connectors/registry/index.js";
2
+ import type { ConnectorInventoryLedger } from "../../observability/connector-inventory-ledger.js";
3
+ export interface ConnectorStatusInput {
4
+ includeHealth?: boolean;
5
+ workspaceRoot?: string;
6
+ }
7
+ export interface ConnectorTestInput {
8
+ platformId: string;
9
+ dryRun?: boolean;
10
+ }
11
+ export declare function connectorStatus(registry: DynamicConnectorRegistry | undefined, ledger: ConnectorInventoryLedger | undefined, input?: ConnectorStatusInput): Promise<Record<string, unknown>>;
12
+ export declare function connectorTest(registry: DynamicConnectorRegistry | undefined, input: ConnectorTestInput): Promise<Record<string, unknown>>;
@@ -0,0 +1,156 @@
1
+ export async function connectorStatus(registry, ledger, input) {
2
+ if (!registry) {
3
+ return {
4
+ ok: false,
5
+ command: "connector_status",
6
+ error: {
7
+ code: "REGISTRY_UNAVAILABLE",
8
+ message: "connector:status requires DynamicConnectorRegistry to be wired into OpsRouterDeps",
9
+ nextStep: "wire_registry_into_ops_router",
10
+ },
11
+ };
12
+ }
13
+ // Ensure snapshot is loaded; if workspaceRoot provided, reload
14
+ if (input?.workspaceRoot) {
15
+ registry.reloadConnectors(input.workspaceRoot);
16
+ }
17
+ const snapshot = registry.getActiveRegistrySnapshot();
18
+ const entries = [...snapshot.entries.values()];
19
+ const conflicts = snapshot.conflicts.map((c) => ({
20
+ platformId: c.platformId,
21
+ existingSource: c.existingSource,
22
+ attemptedSource: c.attemptedSource,
23
+ reason: c.reason,
24
+ }));
25
+ const validationErrors = snapshot.validationErrors.map((e) => ({
26
+ platformId: e.platformId,
27
+ path: e.path,
28
+ message: e.message,
29
+ }));
30
+ const summary = {
31
+ total: entries.length,
32
+ builtIn: entries.filter((e) => e.source === "built_in").length,
33
+ workspace: entries.filter((e) => e.source === "workspace").length,
34
+ executable: entries.filter((e) => e.executable).length,
35
+ pendingTrust: entries.filter((e) => !e.executable).length,
36
+ };
37
+ // Optionally record audit
38
+ if (ledger) {
39
+ await ledger.recordAudit({
40
+ snapshotId: snapshot.createdAt,
41
+ scanned: entries.length + conflicts.length + validationErrors.length,
42
+ registered: entries.length,
43
+ skipped: conflicts.length + validationErrors.length,
44
+ conflicts: conflicts.map((c) => ({ connectorId: c.platformId, reason: c.reason })),
45
+ validationErrors: validationErrors.map((e) => ({
46
+ connectorId: e.platformId ?? "unknown",
47
+ errors: [e.message],
48
+ })),
49
+ trustSummary: {
50
+ executable: summary.executable,
51
+ pendingTrust: summary.pendingTrust,
52
+ },
53
+ });
54
+ }
55
+ return {
56
+ ok: true,
57
+ command: "connector_status",
58
+ data: {
59
+ summary,
60
+ connectors: entries.map((e) => ({
61
+ platformId: e.platformId,
62
+ source: e.source,
63
+ trustStatus: e.trustStatus,
64
+ executable: e.executable,
65
+ capabilities: e.capabilities,
66
+ validationErrors: e.validationErrors,
67
+ manifestPath: e.manifestPath,
68
+ })),
69
+ conflicts,
70
+ validationErrors,
71
+ snapshotCreatedAt: snapshot.createdAt,
72
+ },
73
+ };
74
+ }
75
+ export async function connectorTest(registry, input) {
76
+ if (!registry) {
77
+ return {
78
+ ok: false,
79
+ command: "connector_test",
80
+ error: {
81
+ code: "REGISTRY_UNAVAILABLE",
82
+ message: "connector:test requires DynamicConnectorRegistry to be wired into OpsRouterDeps",
83
+ nextStep: "wire_registry_into_ops_router",
84
+ },
85
+ };
86
+ }
87
+ const platformId = input.platformId.trim();
88
+ if (!platformId) {
89
+ return {
90
+ ok: false,
91
+ command: "connector_test",
92
+ error: {
93
+ code: "MISSING_PLATFORM_ID",
94
+ message: "connector:test requires platformId",
95
+ requiredUserInput: ["platformId"],
96
+ nextStep: "reinvoke_with_platform_id",
97
+ },
98
+ };
99
+ }
100
+ const entry = registry.describeConnector(platformId);
101
+ if (!entry) {
102
+ return {
103
+ ok: false,
104
+ command: "connector_test",
105
+ error: {
106
+ code: "CONNECTOR_NOT_FOUND",
107
+ message: `No connector found for platformId: ${platformId}`,
108
+ requiredUserInput: ["platformId"],
109
+ nextStep: "verify_platform_id_or_run_connector_status",
110
+ },
111
+ };
112
+ }
113
+ const dryRun = input.dryRun !== false; // default dry-run
114
+ // CR7-02: pending-trust / non-executable connectors must fail-closed
115
+ if (!entry.executable) {
116
+ return {
117
+ ok: false,
118
+ command: "connector_test",
119
+ error: {
120
+ code: "PENDING_TRUST_DENIED",
121
+ message: `Connector '${entry.platformId}' is not executable: trustStatus=${entry.trustStatus}. Use connector:status to review trust policy or owner allowlist to enable.`,
122
+ trustStatus: entry.trustStatus,
123
+ platformId: entry.platformId,
124
+ nextStep: "review_trust_policy_or_owner_allowlist",
125
+ },
126
+ };
127
+ }
128
+ const healthChecks = [];
129
+ if (entry.validationErrors.length > 0) {
130
+ healthChecks.push(`validation_errors: ${entry.validationErrors.join("; ")}`);
131
+ }
132
+ if (entry.capabilities.length === 0) {
133
+ healthChecks.push("no_capabilities_declared");
134
+ }
135
+ if (healthChecks.length === 0) {
136
+ healthChecks.push("ok");
137
+ }
138
+ return {
139
+ ok: true,
140
+ command: "connector_test",
141
+ data: {
142
+ platformId: entry.platformId,
143
+ source: entry.source,
144
+ trustStatus: entry.trustStatus,
145
+ executable: entry.executable,
146
+ capabilities: entry.capabilities,
147
+ validationErrors: entry.validationErrors,
148
+ manifestPath: entry.manifestPath,
149
+ dryRun,
150
+ healthChecks,
151
+ note: dryRun
152
+ ? "dry-run mode: no side effects were attempted"
153
+ : "live test mode: side effects may have been attempted (use with caution)",
154
+ },
155
+ };
156
+ }
@@ -1,4 +1,5 @@
1
1
  import { credentialVerify } from "./credential.js";
2
+ import { connectorInit } from "./connector-init.js";
2
3
  import { formatExplanation } from "../explain/format-explanation.js";
3
4
  import { explainSurfaceSubject } from "../explain/explain-surface-subject.js";
4
5
  import { showOperatorFallback, OperatorFallbackNotFoundError, } from "../ops/show-operator-fallback.js";
@@ -220,5 +221,44 @@ export function createCliCommands(deps) {
220
221
  return surface;
221
222
  },
222
223
  },
224
+ {
225
+ name: "connector_init",
226
+ description: "T1.3.1 — generate connector manifest stub under .second-nature/connectors/{platformId}/",
227
+ execute: async (input) => {
228
+ const result = await connectorInit({
229
+ platformId: typeof input?.platformId === "string" ? input.platformId : "",
230
+ family: typeof input?.family === "string"
231
+ ? input.family
232
+ : undefined,
233
+ displayName: typeof input?.displayName === "string"
234
+ ? input.displayName
235
+ : undefined,
236
+ runnerKind: typeof input?.runnerKind === "string"
237
+ ? input.runnerKind
238
+ : undefined,
239
+ force: Boolean(input?.force),
240
+ workspaceRoot: typeof input?.workspaceRoot === "string"
241
+ ? input.workspaceRoot
242
+ : undefined,
243
+ });
244
+ return result;
245
+ },
246
+ },
247
+ {
248
+ name: "connector_status",
249
+ description: "T1.2.3 — show connector inventory, trust/executable/conflict summary",
250
+ execute: async (input) => {
251
+ const surface = await Promise.resolve(opsRouter.dispatch("connector_status", input));
252
+ return surface;
253
+ },
254
+ },
255
+ {
256
+ name: "connector_test",
257
+ description: "T1.2.3 — dry-run test a connector by platformId (default dry-run)",
258
+ execute: async (input) => {
259
+ const surface = await Promise.resolve(opsRouter.dispatch("connector_test", input));
260
+ return surface;
261
+ },
262
+ },
223
263
  ];
224
264
  }
@@ -8,6 +8,51 @@ import { createCliReadModels, } from "./read-models/index.js";
8
8
  import { resolvePackagedRuntime } from "./runtime/runtime-artifact-boundary.js";
9
9
  import { createRuntimeDecisionRecorder, } from "../observability/services/runtime-decision-recorder.js";
10
10
  import { createConnectorExecutorAdapter, } from "../connectors/services/connector-executor-adapter.js";
11
+ import { DynamicConnectorRegistry, createRegistrySnapshotStore, } from "../connectors/registry/index.js";
12
+ /** Built-in connector manifests for DynamicConnectorRegistry. */
13
+ const BUILT_IN_CONNECTOR_MANIFESTS = [
14
+ {
15
+ schemaVersion: "sn.connector.v1",
16
+ platformId: "moltbook",
17
+ displayName: "Moltbook",
18
+ family: "social_community",
19
+ capabilities: [
20
+ { id: "feed.read" },
21
+ { id: "post.publish" },
22
+ { id: "comment.reply" },
23
+ ],
24
+ runner: { kind: "declarative_http" },
25
+ credentials: [{ type: "api_key", required: true }],
26
+ sourceRefPolicy: { minSourceRefs: 1 },
27
+ },
28
+ {
29
+ schemaVersion: "sn.connector.v1",
30
+ platformId: "evomap",
31
+ displayName: "EvoMap",
32
+ family: "agent_network",
33
+ capabilities: [
34
+ { id: "agent.register" },
35
+ { id: "work.discover" },
36
+ ],
37
+ runner: { kind: "declarative_http" },
38
+ credentials: [{ type: "api_key", required: true }],
39
+ sourceRefPolicy: { minSourceRefs: 1 },
40
+ },
41
+ {
42
+ schemaVersion: "sn.connector.v1",
43
+ platformId: "agent-world",
44
+ displayName: "Agent World",
45
+ family: "agent_network",
46
+ capabilities: [
47
+ { id: "feed.read" },
48
+ { id: "work.discover" },
49
+ { id: "task.claim" },
50
+ ],
51
+ runner: { kind: "declarative_http" },
52
+ credentials: [{ type: "api_key", required: true }],
53
+ sourceRefPolicy: { minSourceRefs: 1 },
54
+ },
55
+ ];
11
56
  export function createCliRuntimeDeps(overrides = {}) {
12
57
  const stateDb = overrides.stateDb ?? createStateDatabase();
13
58
  const observabilityDb = overrides.observabilityDb ?? createObservabilityDatabase();
@@ -26,6 +71,11 @@ export function createCliRuntimeDeps(overrides = {}) {
26
71
  stateDb,
27
72
  observabilityDb,
28
73
  });
74
+ const registry = overrides.registry ??
75
+ new DynamicConnectorRegistry({
76
+ builtInManifests: BUILT_IN_CONNECTOR_MANIFESTS,
77
+ snapshotStore: createRegistrySnapshotStore(),
78
+ });
29
79
  return {
30
80
  stateDb,
31
81
  observabilityDb,
@@ -34,6 +84,7 @@ export function createCliRuntimeDeps(overrides = {}) {
34
84
  actionBridge,
35
85
  runtimeRecorder,
36
86
  connectorExecutor,
87
+ registry,
37
88
  };
38
89
  }
39
90
  export function createCommandRouter(options = {}) {
@@ -47,6 +98,7 @@ export function createCommandRouter(options = {}) {
47
98
  workspaceRoot: process.cwd(),
48
99
  observabilityDb: runtime.observabilityDb,
49
100
  connectorExecutor: runtime.connectorExecutor,
101
+ registry: runtime.registry,
50
102
  });
51
103
  const commands = createCliCommands({
52
104
  readModels: runtime.readModels,
@@ -7,6 +7,7 @@ import type { RuntimeDecisionRecorder } from "../../observability/services/runti
7
7
  import type { StateDatabase } from "../../storage/db/index.js";
8
8
  import type { ObservabilityDatabase } from "../../observability/db/index.js";
9
9
  import type { ConnectorExecutor } from "../../core/second-nature/orchestrator/effect-dispatcher.js";
10
+ import type { DynamicConnectorRegistry } from "../../connectors/registry/index.js";
10
11
  export interface OpsRouterDeps {
11
12
  /** When true, packaged runtime artifacts resolved and full graph is loadable */
12
13
  runtimeAvailable: boolean;
@@ -30,6 +31,10 @@ export interface OpsRouterDeps {
30
31
  * connector-system instead of returning connector_dispatch_unwired.
31
32
  */
32
33
  connectorExecutor?: ConnectorExecutor;
34
+ /**
35
+ * T1.2.3: DynamicConnectorRegistry for connector:status and connector:test commands.
36
+ */
37
+ registry?: DynamicConnectorRegistry;
33
38
  }
34
39
  export interface OpsRouter {
35
40
  heartbeatCheck(input: HeartbeatCheckInput): Promise<HeartbeatSurfaceResult>;
@@ -6,6 +6,8 @@ import { showOperatorFallback, OperatorFallbackNotFoundError, } from "./show-ope
6
6
  import { probeHostCapability } from "../host-capability/probe-host-capability.js";
7
7
  import { recordHostCapability } from "../host-capability/record-host-capability.js";
8
8
  import { runNearRealConnectorSmoke } from "../../connectors/near-real/near-real-connector-smoke.js";
9
+ import { connectorInit } from "../commands/connector-init.js";
10
+ import { connectorStatus, connectorTest } from "../commands/connector-status.js";
9
11
  function coerceProbeOnlyFlag(input) {
10
12
  const v = input?.probeOnly;
11
13
  return v === true || v === "true" || v === 1 || v === "1";
@@ -179,6 +181,38 @@ export function createOpsRouter(deps) {
179
181
  };
180
182
  })();
181
183
  }
184
+ if (command === "connector_init") {
185
+ // T1.3.1 (SN-CODE-06): generate connector manifest stub.
186
+ return (async () => {
187
+ const result = await connectorInit({
188
+ platformId: typeof input?.platformId === "string" ? input.platformId : "",
189
+ family: typeof input?.family === "string"
190
+ ? input.family
191
+ : undefined,
192
+ displayName: typeof input?.displayName === "string" ? input.displayName : undefined,
193
+ runnerKind: typeof input?.runnerKind === "string"
194
+ ? input.runnerKind
195
+ : undefined,
196
+ force: Boolean(input?.force),
197
+ workspaceRoot: deps.workspaceRoot,
198
+ });
199
+ return result;
200
+ })();
201
+ }
202
+ if (command === "connector_status") {
203
+ return connectorStatus(deps.registry, undefined, {
204
+ includeHealth: Boolean(input?.includeHealth),
205
+ workspaceRoot: typeof input?.workspaceRoot === "string"
206
+ ? input.workspaceRoot
207
+ : deps.workspaceRoot,
208
+ });
209
+ }
210
+ if (command === "connector_test") {
211
+ return connectorTest(deps.registry, {
212
+ platformId: typeof input?.platformId === "string" ? input.platformId : "",
213
+ dryRun: input?.dryRun === false ? false : true, // default dry-run
214
+ });
215
+ }
182
216
  return {
183
217
  ok: false,
184
218
  error: {
@@ -1,5 +1,7 @@
1
1
  import { runHeartbeatCycle } from "../../core/second-nature/heartbeat/run-heartbeat-cycle.js";
2
2
  import { loadLifeEvidenceSnapshot } from "../../storage/snapshots/life-evidence-snapshot.js";
3
+ import { createAgentGoalStore } from "../../storage/goal/agent-goal-store.js";
4
+ import { createNarrativeStateStore } from "../../storage/narrative/narrative-state-store.js";
3
5
  export async function loadSnapshotInputsForWorkspaceHeartbeat(readModels, options = {}) {
4
6
  const status = await readModels.loadStatus();
5
7
  const mode = status.rhythm.mode === "unknown" ? "active" : status.rhythm.mode;
@@ -43,6 +45,20 @@ export async function loadSnapshotInputsForWorkspaceHeartbeat(readModels, option
43
45
  // No state wired — record that life evidence wasn't loaded so guards can reason honestly.
44
46
  lifeEvidenceEmptyReason = "state_unavailable";
45
47
  }
48
+ // T2.1.4: Load accepted goals from state DB when available.
49
+ let acceptedGoals;
50
+ if (options.state) {
51
+ try {
52
+ const goalStore = createAgentGoalStore(options.state);
53
+ acceptedGoals = await goalStore.listAgentGoals({
54
+ statuses: ["accepted"],
55
+ limit: 20,
56
+ });
57
+ }
58
+ catch {
59
+ acceptedGoals = undefined;
60
+ }
61
+ }
46
62
  return {
47
63
  mode,
48
64
  currentWindowId: status.rhythm.windowId ?? "workspace-default",
@@ -57,12 +73,17 @@ export async function loadSnapshotInputsForWorkspaceHeartbeat(readModels, option
57
73
  platformEventCount,
58
74
  workEventCount,
59
75
  lifeEvidenceEmptyReason,
76
+ acceptedGoals,
60
77
  };
61
78
  }
62
79
  export function createWorkspaceHeartbeatRunner(readModels, options = {}) {
63
80
  // T1.2.4: inject quietWorkflow dep when workspaceRoot is set so quiet/reflection intents
64
81
  // can trigger runSourceBackedQuiet and persist artifacts to disk.
65
82
  const quietEnabled = options.workspaceRoot && options.enableQuietWorkflow !== false;
83
+ // T2.1.5: when state DB is wired, create a NarrativeStateStore for heartbeat updates.
84
+ const narrativeStateStore = options.state
85
+ ? createNarrativeStateStore(options.state)
86
+ : undefined;
66
87
  return async (signal) => {
67
88
  const cycle = await runHeartbeatCycle({
68
89
  signal,
@@ -77,6 +98,7 @@ export function createWorkspaceHeartbeatRunner(readModels, options = {}) {
77
98
  ? { workspaceRoot: options.workspaceRoot }
78
99
  : undefined,
79
100
  connectorExecutor: options.connectorExecutor,
101
+ narrativeStateStore,
80
102
  },
81
103
  });
82
104
  if (options.runtimeRecorder) {
@@ -0,0 +1,11 @@
1
+ import type { ConnectorRequest, ExecutionPlan, RawAttempt } from "../../base/contract.js";
2
+ export interface AgentWorldApiClient {
3
+ readFeed(payload: Record<string, unknown>, apiKey: string): Promise<unknown>;
4
+ discoverWork(payload: Record<string, unknown>, apiKey: string): Promise<unknown>;
5
+ claimTask(payload: Record<string, unknown>, apiKey: string): Promise<unknown>;
6
+ }
7
+ export declare function createAgentWorldRunner(input: {
8
+ apiClient: AgentWorldApiClient;
9
+ }): {
10
+ run(plan: ExecutionPlan, request: ConnectorRequest): Promise<RawAttempt>;
11
+ };