@haaaiawd/second-nature 0.1.51 → 0.2.0

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 (71) hide show
  1. package/openclaw.plugin.json +29 -29
  2. package/package.json +55 -55
  3. package/runtime/cli/commands/index.js +326 -325
  4. package/runtime/cli/ops/heartbeat-surface.d.ts +84 -84
  5. package/runtime/cli/ops/heartbeat-surface.js +100 -100
  6. package/runtime/cli/ops/ops-router.js +1555 -1482
  7. package/runtime/cli/ops/workspace-heartbeat-runner.d.ts +85 -85
  8. package/runtime/cli/ops/workspace-heartbeat-runner.js +242 -242
  9. package/runtime/connectors/base/contract.d.ts +111 -111
  10. package/runtime/connectors/base/failure-taxonomy.d.ts +13 -13
  11. package/runtime/connectors/base/failure-taxonomy.js +186 -186
  12. package/runtime/connectors/base/map-life-evidence.js +137 -137
  13. package/runtime/connectors/base/policy-layer.js +202 -202
  14. package/runtime/connectors/evidence-normalizer.d.ts +45 -0
  15. package/runtime/connectors/evidence-normalizer.js +115 -0
  16. package/runtime/connectors/manifest/manifest-schema.d.ts +152 -152
  17. package/runtime/connectors/manifest/manifest-schema.js +54 -54
  18. package/runtime/connectors/services/connector-executor-adapter.d.ts +20 -20
  19. package/runtime/connectors/services/connector-executor-adapter.js +645 -645
  20. package/runtime/core/second-nature/action/action-closure-recorder.d.ts +70 -0
  21. package/runtime/core/second-nature/action/action-closure-recorder.js +184 -0
  22. package/runtime/core/second-nature/action/action-proposal-builder.d.ts +70 -0
  23. package/runtime/core/second-nature/action/action-proposal-builder.js +217 -0
  24. package/runtime/core/second-nature/action/autonomy-policy-evaluator.d.ts +43 -0
  25. package/runtime/core/second-nature/action/autonomy-policy-evaluator.js +213 -0
  26. package/runtime/core/second-nature/action/policy-bound-dispatch.d.ts +69 -0
  27. package/runtime/core/second-nature/action/policy-bound-dispatch.js +112 -0
  28. package/runtime/core/second-nature/body/tool-affordance/affordance-side-effect.d.ts +49 -0
  29. package/runtime/core/second-nature/body/tool-affordance/affordance-side-effect.js +100 -0
  30. package/runtime/core/second-nature/control-plane/accepted-projection-loader.d.ts +45 -0
  31. package/runtime/core/second-nature/control-plane/accepted-projection-loader.js +85 -0
  32. package/runtime/core/second-nature/control-plane/heartbeat-orchestrator.d.ts +38 -0
  33. package/runtime/core/second-nature/control-plane/heartbeat-orchestrator.js +165 -0
  34. package/runtime/core/second-nature/guidance/guidance-proposal-consumer.d.ts +51 -0
  35. package/runtime/core/second-nature/guidance/guidance-proposal-consumer.js +113 -0
  36. package/runtime/core/second-nature/heartbeat/goal-lifecycle-policy.d.ts +24 -24
  37. package/runtime/core/second-nature/heartbeat/goal-lifecycle-policy.js +61 -61
  38. package/runtime/core/second-nature/heartbeat/heartbeat-loop.d.ts +97 -97
  39. package/runtime/core/second-nature/heartbeat/heartbeat-loop.js +397 -397
  40. package/runtime/core/second-nature/orchestrator/platform-capability-router.js +149 -149
  41. package/runtime/core/second-nature/perception/judgment-engine.d.ts +53 -0
  42. package/runtime/core/second-nature/perception/judgment-engine.js +239 -0
  43. package/runtime/core/second-nature/perception/perception-builder.d.ts +62 -0
  44. package/runtime/core/second-nature/perception/perception-builder.js +208 -0
  45. package/runtime/core/second-nature/perception/sensitivity-classifier.d.ts +37 -0
  46. package/runtime/core/second-nature/perception/sensitivity-classifier.js +87 -0
  47. package/runtime/core/second-nature/quiet-dream/dream-consolidation-runner.d.ts +44 -0
  48. package/runtime/core/second-nature/quiet-dream/dream-consolidation-runner.js +180 -0
  49. package/runtime/core/second-nature/quiet-dream/dream-scheduler.d.ts +36 -0
  50. package/runtime/core/second-nature/quiet-dream/dream-scheduler.js +105 -0
  51. package/runtime/core/second-nature/quiet-dream/memory-projection-lifecycle.d.ts +36 -0
  52. package/runtime/core/second-nature/quiet-dream/memory-projection-lifecycle.js +151 -0
  53. package/runtime/core/second-nature/quiet-dream/quiet-daily-review-builder.d.ts +46 -0
  54. package/runtime/core/second-nature/quiet-dream/quiet-daily-review-builder.js +123 -0
  55. package/runtime/observability/causal-loop-health.d.ts +44 -0
  56. package/runtime/observability/causal-loop-health.js +118 -0
  57. package/runtime/observability/diagnostic-redaction.d.ts +43 -0
  58. package/runtime/observability/diagnostic-redaction.js +114 -0
  59. package/runtime/observability/loop-stage-event-sink.d.ts +43 -0
  60. package/runtime/observability/loop-stage-event-sink.js +148 -0
  61. package/runtime/observability/loop-status.d.ts +46 -0
  62. package/runtime/observability/loop-status.js +85 -0
  63. package/runtime/shared/types/index.js +3 -0
  64. package/runtime/shared/types/v8-contracts.d.ts +86 -0
  65. package/runtime/shared/types/v8-contracts.js +84 -0
  66. package/runtime/storage/db/schema/index.d.ts +1 -0
  67. package/runtime/storage/db/schema/index.js +1 -0
  68. package/runtime/storage/db/schema/v8-entities.d.ts +1973 -0
  69. package/runtime/storage/db/schema/v8-entities.js +160 -0
  70. package/runtime/storage/v8-state-stores.d.ts +147 -0
  71. package/runtime/storage/v8-state-stores.js +491 -0
@@ -1,645 +1,645 @@
1
- import { CapabilityContractRegistry } from "../base/manifest.js";
2
- import { ConnectorRoutePlanner } from "../base/route-planner.js";
3
- import { ChannelHealthStore } from "../base/channel-health.js";
4
- import { createConnectorPolicyLayer } from "../base/policy-layer.js";
5
- import { InMemoryEffectCommitLedger } from "../base/execution-policy.js";
6
- import { moltbookManifest } from "../social-community/moltbook/manifest.js";
7
- import { instreetManifest } from "../social-community/instreet/manifest.js";
8
- import { evomapManifest } from "../agent-network/evomap/manifest.js";
9
- import { agentWorldManifest } from "../agent-network/agent-world/manifest.js";
10
- import { createMoltbookApiClient } from "../social-community/moltbook/api-client.js";
11
- import { createMoltbookRunner } from "../social-community/moltbook/adapter.js";
12
- import { createAgentWorldRunner } from "../agent-network/agent-world/adapter.js";
13
- import { createEvoMapRunner } from "../agent-network/evomap/adapter.js";
14
- import { ExecutionTelemetry } from "../../observability/services/execution-telemetry.js";
15
- import { createCredentialVault } from "../../storage/services/credential-vault.js";
16
- import { createCredentialRouteContextPort } from "./credential-route-context.js";
17
- import { scanConnectorManifests } from "../registry/manifest-scanner.js";
18
- import { parseConnectorManifestV6 } from "../manifest/manifest-parser.js";
19
- import fs from "node:fs";
20
- import path from "node:path";
21
- import { pathToFileURL } from "node:url";
22
- const DEFAULT_AGENT_WORLD_USERNAME = "nyx_ha";
23
- const DEFAULT_AGENT_WORLD_PROFILE_PATH_TEMPLATE = "/api/agents/profile/{username}";
24
- function readString(value) {
25
- return typeof value === "string" && value.trim().length > 0
26
- ? value.trim()
27
- : undefined;
28
- }
29
- function channelPriorityForRunner(manifest) {
30
- const declared = manifest.capabilities
31
- .map((capability) => capability.channel)
32
- .filter((channel) => channel === "api_rest" ||
33
- channel === "api_rpc" ||
34
- channel === "a2a" ||
35
- channel === "mcp" ||
36
- channel === "cli" ||
37
- channel === "skill" ||
38
- channel === "browser");
39
- if (declared.length > 0)
40
- return [...new Set(declared)];
41
- if (manifest.runner.kind === "declarative_a2a")
42
- return ["a2a"];
43
- if (manifest.runner.kind === "declarative_mcp")
44
- return ["mcp"];
45
- if (manifest.runner.kind === "cli_descriptor")
46
- return ["cli"];
47
- if (manifest.runner.kind === "skill")
48
- return ["skill"];
49
- if (manifest.runner.kind === "browser")
50
- return ["browser"];
51
- return ["api_rest"];
52
- }
53
- function registerWorkspaceManifests(registry, workspaceRoot) {
54
- if (!workspaceRoot)
55
- return;
56
- for (const file of scanConnectorManifests(workspaceRoot)) {
57
- const parsed = parseConnectorManifestV6(file.content, file.path);
58
- if (!parsed.ok)
59
- continue;
60
- const manifest = parsed.manifest;
61
- try {
62
- registry.register({
63
- platformId: manifest.platformId,
64
- supportedCapabilities: manifest.capabilities.map((capability) => capability.id),
65
- channelPriority: channelPriorityForRunner(manifest),
66
- credentialTypes: manifest.credentials
67
- .filter((credential) => credential.required !== false)
68
- .map((credential) => credential.type),
69
- sourceRefPolicy: manifest.sourceRefPolicy,
70
- });
71
- }
72
- catch {
73
- // Invalid workspace manifests remain visible through connector_status validation.
74
- // Execution side keeps fail-closed behavior by not registering them here.
75
- }
76
- }
77
- }
78
- function resolveAgentWorldUsername(payload, purpose) {
79
- const payloadUsername = (purpose === "discover" ? readString(payload.targetUsername) : undefined) ??
80
- readString(payload.username) ??
81
- readString(payload.agentUsername);
82
- return (payloadUsername ??
83
- readString(process.env.SECOND_NATURE_AGENT_WORLD_USERNAME) ??
84
- DEFAULT_AGENT_WORLD_USERNAME);
85
- }
86
- function resolveAgentWorldProfilePath(payload, username) {
87
- const template = readString(payload.profilePathTemplate) ??
88
- readString(process.env.SECOND_NATURE_AGENT_WORLD_PROFILE_PATH_TEMPLATE) ??
89
- DEFAULT_AGENT_WORLD_PROFILE_PATH_TEMPLATE;
90
- return template.replaceAll("{username}", encodeURIComponent(username));
91
- }
92
- function joinAgentWorldUrl(baseUrl, path) {
93
- if (/^https?:\/\//i.test(path))
94
- return path;
95
- return `${baseUrl.replace(/\/+$/, "")}/${path.replace(/^\/+/, "")}`;
96
- }
97
- async function fetchAgentWorldJson(input) {
98
- const resp = await fetch(joinAgentWorldUrl(input.baseUrl, input.path), {
99
- method: input.method ?? "GET",
100
- headers: {
101
- "Authorization": `Bearer ${input.apiKey}`,
102
- "Content-Type": "application/json",
103
- },
104
- body: input.body === undefined ? undefined : JSON.stringify(input.body),
105
- });
106
- if (!resp.ok) {
107
- throw { code: "api_error", detail: `agent-world ${input.label}: ${resp.status}` };
108
- }
109
- return resp.json();
110
- }
111
- export function createEvoMapSecretPort(vault) {
112
- const NODE_SECRET_KEY = "evomap_node_secret";
113
- return {
114
- async loadNodeSecret(_platformId) {
115
- const ctx = await vault.loadCredentialContext(NODE_SECRET_KEY);
116
- if (!ctx || ctx.status !== "active" || !ctx.encryptedValue)
117
- return null;
118
- // CredentialVault.loadCredentialContext already decrypts at rest;
119
- // encryptedValue here is the plaintext.
120
- return ctx.encryptedValue;
121
- },
122
- async saveNodeSecret(_platformId, nodeSecret) {
123
- await vault.saveCredentialContext({
124
- platformId: NODE_SECRET_KEY,
125
- credentialType: "node_secret",
126
- encryptedValue: nodeSecret,
127
- status: "active",
128
- });
129
- },
130
- };
131
- }
132
- function joinEvoMapUrl(baseUrl, path) {
133
- if (/^https?:\/\//i.test(path))
134
- return path;
135
- return `${baseUrl.replace(/\/+$/, "")}/${path.replace(/^\/+/, "")}`;
136
- }
137
- async function fetchEvoMapJson(input) {
138
- const headers = {
139
- "Content-Type": "application/json",
140
- };
141
- if (input.nodeSecret) {
142
- headers["Authorization"] = `Bearer ${input.nodeSecret}`;
143
- }
144
- const resp = await fetch(joinEvoMapUrl(input.baseUrl, input.path), {
145
- method: input.method ?? "GET",
146
- headers,
147
- body: input.body === undefined ? undefined : JSON.stringify(input.body),
148
- });
149
- if (!resp.ok) {
150
- throw { code: "api_error", detail: `evomap ${input.label}: ${resp.status}` };
151
- }
152
- return resp.json();
153
- }
154
- function createMoltbookMockRunner(workspaceRoot) {
155
- return {
156
- async run(_plan, request) {
157
- const started = Date.now();
158
- const mockPath = workspaceRoot
159
- ? path.join(workspaceRoot, ".second-nature", "mock", "moltbook-feed.json")
160
- : undefined;
161
- if (!mockPath || !fs.existsSync(mockPath)) {
162
- return {
163
- platformId: request.platformId,
164
- channel: request.preferredChannel ?? "api_rest",
165
- latencyMs: Date.now() - started,
166
- success: false,
167
- error: {
168
- code: "configuration_missing",
169
- detail: "SECOND_NATURE_MOLTBOOK_BASE_URL not set and no mock data found",
170
- },
171
- };
172
- }
173
- try {
174
- const raw = fs.readFileSync(mockPath, "utf-8");
175
- const data = JSON.parse(raw);
176
- return {
177
- platformId: request.platformId,
178
- channel: request.preferredChannel ?? "api_rest",
179
- latencyMs: Date.now() - started,
180
- degraded: true,
181
- success: true,
182
- payload: {
183
- capability: request.intent,
184
- channel: request.preferredChannel ?? "api_rest",
185
- data: {
186
- source: "mock",
187
- items: Array.isArray(data.items) ? data.items : [],
188
- },
189
- },
190
- };
191
- }
192
- catch (err) {
193
- return {
194
- platformId: request.platformId,
195
- channel: request.preferredChannel ?? "api_rest",
196
- latencyMs: Date.now() - started,
197
- success: false,
198
- error: {
199
- code: "mock_read_error",
200
- detail: String(err),
201
- },
202
- };
203
- }
204
- },
205
- };
206
- }
207
- function findWorkspaceManifest(platformId, workspaceRoot) {
208
- if (!workspaceRoot)
209
- return undefined;
210
- for (const file of scanConnectorManifests(workspaceRoot)) {
211
- const parsed = parseConnectorManifestV6(file.content, file.path);
212
- if (parsed.ok && parsed.manifest.platformId === platformId) {
213
- return { manifest: parsed.manifest, manifestDir: path.dirname(file.path) };
214
- }
215
- }
216
- return undefined;
217
- }
218
- function resolveDeclarativeHttpPath(capabilityId) {
219
- return `/${capabilityId.replace(/\./g, "/")}`;
220
- }
221
- function resolveDeclarativeHttpMethod(capabilityId) {
222
- const readOps = ["read", "list", "discover", "get", "heartbeat", "fetch"];
223
- if (readOps.some((op) => capabilityId.includes(op)))
224
- return "GET";
225
- return "POST";
226
- }
227
- function createDeclarativeHttpRunner(manifest, credential) {
228
- return {
229
- async run(plan, request) {
230
- const started = Date.now();
231
- const baseUrl = manifest.runner.config?.baseUrl ?? "";
232
- if (!baseUrl) {
233
- return {
234
- platformId: request.platformId,
235
- channel: plan.channel,
236
- latencyMs: Date.now() - started,
237
- success: false,
238
- error: {
239
- code: "configuration_missing",
240
- detail: "runner.config.baseUrl not set for declarative_http connector",
241
- },
242
- };
243
- }
244
- const httpPath = resolveDeclarativeHttpPath(request.intent);
245
- const method = resolveDeclarativeHttpMethod(request.intent);
246
- try {
247
- const headers = {
248
- "Content-Type": "application/json",
249
- };
250
- if (credential?.encryptedValue) {
251
- headers.Authorization = `Bearer ${credential.encryptedValue}`;
252
- }
253
- const resp = await fetch(`${baseUrl.replace(/\/+$/, "")}${httpPath}`, {
254
- method,
255
- headers,
256
- body: method !== "GET" && request.payload ? JSON.stringify(request.payload) : undefined,
257
- });
258
- if (!resp.ok) {
259
- return {
260
- platformId: request.platformId,
261
- channel: plan.channel,
262
- latencyMs: Date.now() - started,
263
- success: false,
264
- error: {
265
- code: "api_error",
266
- detail: `HTTP ${resp.status}: ${await resp.text().catch(() => "")}`,
267
- },
268
- };
269
- }
270
- const data = await resp.json();
271
- return {
272
- platformId: request.platformId,
273
- channel: plan.channel,
274
- latencyMs: Date.now() - started,
275
- success: true,
276
- payload: {
277
- capability: request.intent,
278
- channel: plan.channel,
279
- data,
280
- },
281
- };
282
- }
283
- catch (err) {
284
- return {
285
- platformId: request.platformId,
286
- channel: plan.channel,
287
- latencyMs: Date.now() - started,
288
- success: false,
289
- error: {
290
- code: "network_error",
291
- detail: String(err),
292
- },
293
- };
294
- }
295
- },
296
- };
297
- }
298
- /**
299
- * Scriptable Node Runner — workspace connector execution via dynamic ES Module import.
300
- *
301
- * Contract (runner.mjs default export):
302
- * Input: { intent: string, payload: unknown, credential?: string }
303
- * Output: { success: boolean, data?: unknown, error?: { code: string, detail: string } }
304
- *
305
- * Timeout: default 10s, overridable via manifest.runner.config.timeoutMs.
306
- * Credential: passed as plain string when manifest.credentials required and vault has active entry.
307
- * Error mapping:
308
- * - missing entrypoint file → configuration_missing
309
- * - default export is not function → script_error
310
- * - runner throws → script_error (detail includes error message)
311
- * - Promise.race timeout → timeout
312
- */
313
- function createScriptableNodeRunner(manifest, manifestDir, activeCredential) {
314
- const entryPath = manifest.runner.entrypoint ?? "runner.mjs";
315
- const absoluteEntryPath = path.resolve(manifestDir, entryPath);
316
- const DEFAULT_TIMEOUT_MS = 10000;
317
- const timeoutMs = typeof manifest.runner.config?.timeoutMs === "number" &&
318
- Number.isFinite(manifest.runner.config.timeoutMs) &&
319
- manifest.runner.config.timeoutMs > 0
320
- ? manifest.runner.config.timeoutMs
321
- : DEFAULT_TIMEOUT_MS;
322
- return {
323
- async run(_plan, request) {
324
- const started = Date.now();
325
- if (!fs.existsSync(absoluteEntryPath)) {
326
- return {
327
- platformId: request.platformId,
328
- channel: request.preferredChannel ?? "api_rest",
329
- latencyMs: Date.now() - started,
330
- success: false,
331
- error: {
332
- code: "configuration_missing",
333
- detail: `scriptable_node runner not found: ${absoluteEntryPath}`,
334
- },
335
- };
336
- }
337
- try {
338
- const module = await import(pathToFileURL(absoluteEntryPath).href);
339
- const handler = module.default;
340
- if (typeof handler !== "function") {
341
- return {
342
- platformId: request.platformId,
343
- channel: request.preferredChannel ?? "api_rest",
344
- latencyMs: Date.now() - started,
345
- success: false,
346
- error: {
347
- code: "script_error",
348
- detail: `scriptable_node runner must export a default function from ${absoluteEntryPath}`,
349
- },
350
- };
351
- }
352
- const result = await Promise.race([
353
- handler({
354
- intent: request.intent,
355
- payload: request.payload,
356
- credential: activeCredential?.encryptedValue,
357
- }),
358
- new Promise((_, reject) => setTimeout(() => reject(new Error("scriptable_node_timeout")), timeoutMs)),
359
- ]);
360
- if (result && typeof result === "object" && "success" in result) {
361
- return {
362
- platformId: request.platformId,
363
- channel: request.preferredChannel ?? "api_rest",
364
- latencyMs: Date.now() - started,
365
- success: Boolean(result.success),
366
- payload: result.success
367
- ? {
368
- capability: request.intent,
369
- channel: request.preferredChannel ?? "api_rest",
370
- data: result.data,
371
- }
372
- : undefined,
373
- error: !result.success
374
- ? {
375
- code: result.error?.code ?? "script_error",
376
- detail: result.error?.detail ?? "scriptable_node_runner_returned_failure",
377
- }
378
- : undefined,
379
- };
380
- }
381
- return {
382
- platformId: request.platformId,
383
- channel: request.preferredChannel ?? "api_rest",
384
- latencyMs: Date.now() - started,
385
- success: false,
386
- error: {
387
- code: "script_error",
388
- detail: `scriptable_node runner returned invalid shape from ${absoluteEntryPath}`,
389
- },
390
- };
391
- }
392
- catch (err) {
393
- const isTimeout = err instanceof Error && err.message === "scriptable_node_timeout";
394
- return {
395
- platformId: request.platformId,
396
- channel: request.preferredChannel ?? "api_rest",
397
- latencyMs: Date.now() - started,
398
- success: false,
399
- error: {
400
- code: isTimeout ? "timeout" : "script_error",
401
- detail: isTimeout
402
- ? `scriptable_node runner exceeded ${timeoutMs}ms timeout`
403
- : String(err),
404
- },
405
- };
406
- }
407
- },
408
- };
409
- }
410
- function createAdaptiveExecutionRunner(vault, workspaceRoot) {
411
- return {
412
- async run(_plan, request) {
413
- const platformId = request.platformId;
414
- const started = Date.now();
415
- const workspaceManifestResult = findWorkspaceManifest(platformId, workspaceRoot);
416
- const workspaceManifest = workspaceManifestResult?.manifest;
417
- const isBuiltInPlatform = platformId === "moltbook" ||
418
- platformId === "evomap" ||
419
- platformId === "agent-world";
420
- const requiresCredential = isBuiltInPlatform ||
421
- Boolean(workspaceManifest?.credentials.some((credential) => credential.required !== false));
422
- const credential = requiresCredential
423
- ? await vault.loadCredentialContext(platformId)
424
- : undefined;
425
- if (requiresCredential &&
426
- (!credential ||
427
- credential.status !== "active" ||
428
- !credential.encryptedValue)) {
429
- return {
430
- platformId,
431
- channel: request.preferredChannel ?? "api_rest",
432
- latencyMs: Date.now() - started,
433
- success: false,
434
- error: {
435
- code: "auth_failure",
436
- detail: "credential_unavailable_for_execution",
437
- },
438
- };
439
- }
440
- const activeCredential = credential?.status === "active" && credential.encryptedValue
441
- ? { encryptedValue: credential.encryptedValue }
442
- : undefined;
443
- if (platformId === "moltbook") {
444
- const baseUrl = process.env.SECOND_NATURE_MOLTBOOK_BASE_URL;
445
- if (baseUrl) {
446
- const apiClient = createMoltbookApiClient({
447
- baseUrl,
448
- accessToken: activeCredential.encryptedValue,
449
- timeoutMs: 10000,
450
- });
451
- const runner = createMoltbookRunner({
452
- apiClient,
453
- skillRunner: {
454
- run: async () => {
455
- throw {
456
- code: "protocol_mismatch",
457
- detail: "moltbook_skill_runner_not_configured",
458
- };
459
- },
460
- },
461
- });
462
- return runner.run(_plan, request);
463
- }
464
- // Mock fallback when real API is not configured
465
- const mockRunner = createMoltbookMockRunner(workspaceRoot);
466
- return mockRunner.run(_plan, request);
467
- }
468
- if (platformId === "evomap") {
469
- const baseUrl = process.env.SECOND_NATURE_EVOMAP_BASE_URL;
470
- if (!baseUrl) {
471
- return {
472
- platformId,
473
- channel: request.preferredChannel ?? "api_rest",
474
- latencyMs: Date.now() - started,
475
- success: false,
476
- error: {
477
- code: "configuration_missing",
478
- detail: "SECOND_NATURE_EVOMAP_BASE_URL not set. This connector requires the evomap node base URL to be configured via environment variable.",
479
- },
480
- };
481
- }
482
- const secretPort = createEvoMapSecretPort(vault);
483
- const runner = createEvoMapRunner({
484
- apiClient: {
485
- async heartbeat(payload, nodeSecret) {
486
- const path = readString(payload.heartbeatPath) ?? "/api/heartbeat";
487
- return fetchEvoMapJson({ baseUrl, path, nodeSecret, method: "POST", body: payload, label: "heartbeat" });
488
- },
489
- async claimTask(payload, nodeSecret) {
490
- const path = readString(payload.claimPath) ?? "/api/tasks/claim";
491
- return fetchEvoMapJson({ baseUrl, path, nodeSecret, method: "POST", body: payload, label: "claim" });
492
- },
493
- },
494
- a2aClient: {
495
- async helloOrRegister(payload) {
496
- const path = readString(payload.registerPath) ?? "/a2a/hello";
497
- return fetchEvoMapJson({ baseUrl, path, method: "POST", body: payload, label: "register" });
498
- },
499
- async discoverWork(payload, nodeSecret) {
500
- const path = readString(payload.discoverPath) ?? "/a2a/discover";
501
- return fetchEvoMapJson({ baseUrl, path, nodeSecret, method: "POST", body: payload, label: "discover" });
502
- },
503
- },
504
- secretPort,
505
- });
506
- return runner.run(_plan, request);
507
- }
508
- if (platformId === "agent-world") {
509
- const baseUrl = process.env.SECOND_NATURE_AGENT_WORLD_BASE_URL;
510
- if (!baseUrl) {
511
- return {
512
- platformId,
513
- channel: request.preferredChannel ?? "api_rest",
514
- latencyMs: Date.now() - started,
515
- success: false,
516
- error: {
517
- code: "configuration_missing",
518
- detail: "SECOND_NATURE_AGENT_WORLD_BASE_URL not set. This connector requires the agent-world node base URL to be configured via environment variable.",
519
- },
520
- };
521
- }
522
- const runner = createAgentWorldRunner({
523
- apiKey: activeCredential.encryptedValue,
524
- apiClient: {
525
- async readFeed(payload, _apiKey) {
526
- const username = resolveAgentWorldUsername(payload, "feed");
527
- return fetchAgentWorldJson({
528
- baseUrl,
529
- path: resolveAgentWorldProfilePath(payload, username),
530
- apiKey: _apiKey,
531
- label: "profile feed",
532
- });
533
- },
534
- async discoverWork(payload, _apiKey) {
535
- const username = resolveAgentWorldUsername(payload, "discover");
536
- return fetchAgentWorldJson({
537
- baseUrl,
538
- path: resolveAgentWorldProfilePath(payload, username),
539
- apiKey: _apiKey,
540
- label: "profile discover",
541
- });
542
- },
543
- async claimTask(payload, _apiKey) {
544
- const claimPath = readString(payload.claimEndpointPath);
545
- if (!claimPath) {
546
- throw {
547
- code: "protocol_mismatch",
548
- detail: "agent_world_task_claim_endpoint_not_configured",
549
- };
550
- }
551
- return fetchAgentWorldJson({
552
- baseUrl,
553
- path: claimPath,
554
- apiKey: _apiKey,
555
- method: "POST",
556
- body: payload,
557
- label: "task claim",
558
- });
559
- },
560
- },
561
- });
562
- return runner.run(_plan, request);
563
- }
564
- // T-CS.C.9: instreet is registered but requires skill/browser channel;
565
- // pure api_rest execution returns platform_unavailable.
566
- if (platformId === "instreet") {
567
- return {
568
- platformId,
569
- channel: request.preferredChannel ?? "api_rest",
570
- latencyMs: Date.now() - started,
571
- success: false,
572
- error: {
573
- code: "platform_unavailable",
574
- detail: "instreet_requires_skill_browser_channel",
575
- },
576
- };
577
- }
578
- // Wave 83: workspace declarative_http connector fallback
579
- if (workspaceManifest && workspaceManifest.runner.kind === "declarative_http") {
580
- const httpRunner = createDeclarativeHttpRunner(workspaceManifest, activeCredential);
581
- return httpRunner.run(_plan, request);
582
- }
583
- // Wave 90: workspace scriptable_node connector
584
- if (workspaceManifest && workspaceManifest.runner.kind === "scriptable_node") {
585
- if (!workspaceManifestResult) {
586
- return {
587
- platformId,
588
- channel: request.preferredChannel ?? "api_rest",
589
- latencyMs: Date.now() - started,
590
- success: false,
591
- error: {
592
- code: "configuration_missing",
593
- detail: "scriptable_node requires workspace manifest with manifestDir",
594
- },
595
- };
596
- }
597
- const scriptRunner = createScriptableNodeRunner(workspaceManifest, workspaceManifestResult.manifestDir, activeCredential);
598
- return scriptRunner.run(_plan, request);
599
- }
600
- return {
601
- platformId,
602
- channel: request.preferredChannel ?? "api_rest",
603
- latencyMs: Date.now() - started,
604
- success: false,
605
- error: {
606
- code: "unknown_platform",
607
- detail: `no execution runner for ${platformId}`,
608
- },
609
- };
610
- },
611
- };
612
- }
613
- export function createConnectorExecutorAdapter(options) {
614
- const vault = createCredentialVault(options.stateDb.db);
615
- const registry = new CapabilityContractRegistry();
616
- registry.register({ ...moltbookManifest });
617
- registry.register({ ...evomapManifest });
618
- registry.register({ ...agentWorldManifest });
619
- registry.register({ ...instreetManifest });
620
- registerWorkspaceManifests(registry, options.workspaceRoot);
621
- const routeContextPort = createCredentialRouteContextPort(vault);
622
- const routePlanner = new ConnectorRoutePlanner(registry, routeContextPort, new ChannelHealthStore());
623
- const telemetry = new ExecutionTelemetry(options.observabilityDb);
624
- const executionRunner = createAdaptiveExecutionRunner(vault, options.workspaceRoot);
625
- const policy = createConnectorPolicyLayer({
626
- routePlanner,
627
- executionRunner,
628
- telemetry,
629
- effectCommitLedger: new InMemoryEffectCommitLedger(),
630
- retryPolicy: { maxRetries: 2, jitter: true },
631
- });
632
- return {
633
- async executeEffect(input) {
634
- registerWorkspaceManifests(registry, options.workspaceRoot);
635
- return policy.executeWithPolicy(input.intent, {
636
- platformId: input.platformId,
637
- intent: input.intent,
638
- payload: input.payload,
639
- decisionId: input.decisionId,
640
- intentId: input.intentId,
641
- idempotencyKey: input.idempotencyKey,
642
- });
643
- },
644
- };
645
- }
1
+ import { CapabilityContractRegistry } from "../base/manifest.js";
2
+ import { ConnectorRoutePlanner } from "../base/route-planner.js";
3
+ import { ChannelHealthStore } from "../base/channel-health.js";
4
+ import { createConnectorPolicyLayer } from "../base/policy-layer.js";
5
+ import { InMemoryEffectCommitLedger } from "../base/execution-policy.js";
6
+ import { moltbookManifest } from "../social-community/moltbook/manifest.js";
7
+ import { instreetManifest } from "../social-community/instreet/manifest.js";
8
+ import { evomapManifest } from "../agent-network/evomap/manifest.js";
9
+ import { agentWorldManifest } from "../agent-network/agent-world/manifest.js";
10
+ import { createMoltbookApiClient } from "../social-community/moltbook/api-client.js";
11
+ import { createMoltbookRunner } from "../social-community/moltbook/adapter.js";
12
+ import { createAgentWorldRunner } from "../agent-network/agent-world/adapter.js";
13
+ import { createEvoMapRunner } from "../agent-network/evomap/adapter.js";
14
+ import { ExecutionTelemetry } from "../../observability/services/execution-telemetry.js";
15
+ import { createCredentialVault } from "../../storage/services/credential-vault.js";
16
+ import { createCredentialRouteContextPort } from "./credential-route-context.js";
17
+ import { scanConnectorManifests } from "../registry/manifest-scanner.js";
18
+ import { parseConnectorManifestV6 } from "../manifest/manifest-parser.js";
19
+ import fs from "node:fs";
20
+ import path from "node:path";
21
+ import { pathToFileURL } from "node:url";
22
+ const DEFAULT_AGENT_WORLD_USERNAME = "nyx_ha";
23
+ const DEFAULT_AGENT_WORLD_PROFILE_PATH_TEMPLATE = "/api/agents/profile/{username}";
24
+ function readString(value) {
25
+ return typeof value === "string" && value.trim().length > 0
26
+ ? value.trim()
27
+ : undefined;
28
+ }
29
+ function channelPriorityForRunner(manifest) {
30
+ const declared = manifest.capabilities
31
+ .map((capability) => capability.channel)
32
+ .filter((channel) => channel === "api_rest" ||
33
+ channel === "api_rpc" ||
34
+ channel === "a2a" ||
35
+ channel === "mcp" ||
36
+ channel === "cli" ||
37
+ channel === "skill" ||
38
+ channel === "browser");
39
+ if (declared.length > 0)
40
+ return [...new Set(declared)];
41
+ if (manifest.runner.kind === "declarative_a2a")
42
+ return ["a2a"];
43
+ if (manifest.runner.kind === "declarative_mcp")
44
+ return ["mcp"];
45
+ if (manifest.runner.kind === "cli_descriptor")
46
+ return ["cli"];
47
+ if (manifest.runner.kind === "skill")
48
+ return ["skill"];
49
+ if (manifest.runner.kind === "browser")
50
+ return ["browser"];
51
+ return ["api_rest"];
52
+ }
53
+ function registerWorkspaceManifests(registry, workspaceRoot) {
54
+ if (!workspaceRoot)
55
+ return;
56
+ for (const file of scanConnectorManifests(workspaceRoot)) {
57
+ const parsed = parseConnectorManifestV6(file.content, file.path);
58
+ if (!parsed.ok)
59
+ continue;
60
+ const manifest = parsed.manifest;
61
+ try {
62
+ registry.register({
63
+ platformId: manifest.platformId,
64
+ supportedCapabilities: manifest.capabilities.map((capability) => capability.id),
65
+ channelPriority: channelPriorityForRunner(manifest),
66
+ credentialTypes: manifest.credentials
67
+ .filter((credential) => credential.required !== false)
68
+ .map((credential) => credential.type),
69
+ sourceRefPolicy: manifest.sourceRefPolicy,
70
+ });
71
+ }
72
+ catch {
73
+ // Invalid workspace manifests remain visible through connector_status validation.
74
+ // Execution side keeps fail-closed behavior by not registering them here.
75
+ }
76
+ }
77
+ }
78
+ function resolveAgentWorldUsername(payload, purpose) {
79
+ const payloadUsername = (purpose === "discover" ? readString(payload.targetUsername) : undefined) ??
80
+ readString(payload.username) ??
81
+ readString(payload.agentUsername);
82
+ return (payloadUsername ??
83
+ readString(process.env.SECOND_NATURE_AGENT_WORLD_USERNAME) ??
84
+ DEFAULT_AGENT_WORLD_USERNAME);
85
+ }
86
+ function resolveAgentWorldProfilePath(payload, username) {
87
+ const template = readString(payload.profilePathTemplate) ??
88
+ readString(process.env.SECOND_NATURE_AGENT_WORLD_PROFILE_PATH_TEMPLATE) ??
89
+ DEFAULT_AGENT_WORLD_PROFILE_PATH_TEMPLATE;
90
+ return template.replaceAll("{username}", encodeURIComponent(username));
91
+ }
92
+ function joinAgentWorldUrl(baseUrl, path) {
93
+ if (/^https?:\/\//i.test(path))
94
+ return path;
95
+ return `${baseUrl.replace(/\/+$/, "")}/${path.replace(/^\/+/, "")}`;
96
+ }
97
+ async function fetchAgentWorldJson(input) {
98
+ const resp = await fetch(joinAgentWorldUrl(input.baseUrl, input.path), {
99
+ method: input.method ?? "GET",
100
+ headers: {
101
+ "Authorization": `Bearer ${input.apiKey}`,
102
+ "Content-Type": "application/json",
103
+ },
104
+ body: input.body === undefined ? undefined : JSON.stringify(input.body),
105
+ });
106
+ if (!resp.ok) {
107
+ throw { code: "api_error", detail: `agent-world ${input.label}: ${resp.status}` };
108
+ }
109
+ return resp.json();
110
+ }
111
+ export function createEvoMapSecretPort(vault) {
112
+ const NODE_SECRET_KEY = "evomap_node_secret";
113
+ return {
114
+ async loadNodeSecret(_platformId) {
115
+ const ctx = await vault.loadCredentialContext(NODE_SECRET_KEY);
116
+ if (!ctx || ctx.status !== "active" || !ctx.encryptedValue)
117
+ return null;
118
+ // CredentialVault.loadCredentialContext already decrypts at rest;
119
+ // encryptedValue here is the plaintext.
120
+ return ctx.encryptedValue;
121
+ },
122
+ async saveNodeSecret(_platformId, nodeSecret) {
123
+ await vault.saveCredentialContext({
124
+ platformId: NODE_SECRET_KEY,
125
+ credentialType: "node_secret",
126
+ encryptedValue: nodeSecret,
127
+ status: "active",
128
+ });
129
+ },
130
+ };
131
+ }
132
+ function joinEvoMapUrl(baseUrl, path) {
133
+ if (/^https?:\/\//i.test(path))
134
+ return path;
135
+ return `${baseUrl.replace(/\/+$/, "")}/${path.replace(/^\/+/, "")}`;
136
+ }
137
+ async function fetchEvoMapJson(input) {
138
+ const headers = {
139
+ "Content-Type": "application/json",
140
+ };
141
+ if (input.nodeSecret) {
142
+ headers["Authorization"] = `Bearer ${input.nodeSecret}`;
143
+ }
144
+ const resp = await fetch(joinEvoMapUrl(input.baseUrl, input.path), {
145
+ method: input.method ?? "GET",
146
+ headers,
147
+ body: input.body === undefined ? undefined : JSON.stringify(input.body),
148
+ });
149
+ if (!resp.ok) {
150
+ throw { code: "api_error", detail: `evomap ${input.label}: ${resp.status}` };
151
+ }
152
+ return resp.json();
153
+ }
154
+ function createMoltbookMockRunner(workspaceRoot) {
155
+ return {
156
+ async run(_plan, request) {
157
+ const started = Date.now();
158
+ const mockPath = workspaceRoot
159
+ ? path.join(workspaceRoot, ".second-nature", "mock", "moltbook-feed.json")
160
+ : undefined;
161
+ if (!mockPath || !fs.existsSync(mockPath)) {
162
+ return {
163
+ platformId: request.platformId,
164
+ channel: request.preferredChannel ?? "api_rest",
165
+ latencyMs: Date.now() - started,
166
+ success: false,
167
+ error: {
168
+ code: "configuration_missing",
169
+ detail: "SECOND_NATURE_MOLTBOOK_BASE_URL not set and no mock data found",
170
+ },
171
+ };
172
+ }
173
+ try {
174
+ const raw = fs.readFileSync(mockPath, "utf-8");
175
+ const data = JSON.parse(raw);
176
+ return {
177
+ platformId: request.platformId,
178
+ channel: request.preferredChannel ?? "api_rest",
179
+ latencyMs: Date.now() - started,
180
+ degraded: true,
181
+ success: true,
182
+ payload: {
183
+ capability: request.intent,
184
+ channel: request.preferredChannel ?? "api_rest",
185
+ data: {
186
+ source: "mock",
187
+ items: Array.isArray(data.items) ? data.items : [],
188
+ },
189
+ },
190
+ };
191
+ }
192
+ catch (err) {
193
+ return {
194
+ platformId: request.platformId,
195
+ channel: request.preferredChannel ?? "api_rest",
196
+ latencyMs: Date.now() - started,
197
+ success: false,
198
+ error: {
199
+ code: "mock_read_error",
200
+ detail: String(err),
201
+ },
202
+ };
203
+ }
204
+ },
205
+ };
206
+ }
207
+ function findWorkspaceManifest(platformId, workspaceRoot) {
208
+ if (!workspaceRoot)
209
+ return undefined;
210
+ for (const file of scanConnectorManifests(workspaceRoot)) {
211
+ const parsed = parseConnectorManifestV6(file.content, file.path);
212
+ if (parsed.ok && parsed.manifest.platformId === platformId) {
213
+ return { manifest: parsed.manifest, manifestDir: path.dirname(file.path) };
214
+ }
215
+ }
216
+ return undefined;
217
+ }
218
+ function resolveDeclarativeHttpPath(capabilityId) {
219
+ return `/${capabilityId.replace(/\./g, "/")}`;
220
+ }
221
+ function resolveDeclarativeHttpMethod(capabilityId) {
222
+ const readOps = ["read", "list", "discover", "get", "heartbeat", "fetch"];
223
+ if (readOps.some((op) => capabilityId.includes(op)))
224
+ return "GET";
225
+ return "POST";
226
+ }
227
+ function createDeclarativeHttpRunner(manifest, credential) {
228
+ return {
229
+ async run(plan, request) {
230
+ const started = Date.now();
231
+ const baseUrl = manifest.runner.config?.baseUrl ?? "";
232
+ if (!baseUrl) {
233
+ return {
234
+ platformId: request.platformId,
235
+ channel: plan.channel,
236
+ latencyMs: Date.now() - started,
237
+ success: false,
238
+ error: {
239
+ code: "configuration_missing",
240
+ detail: "runner.config.baseUrl not set for declarative_http connector",
241
+ },
242
+ };
243
+ }
244
+ const httpPath = resolveDeclarativeHttpPath(request.intent);
245
+ const method = resolveDeclarativeHttpMethod(request.intent);
246
+ try {
247
+ const headers = {
248
+ "Content-Type": "application/json",
249
+ };
250
+ if (credential?.encryptedValue) {
251
+ headers.Authorization = `Bearer ${credential.encryptedValue}`;
252
+ }
253
+ const resp = await fetch(`${baseUrl.replace(/\/+$/, "")}${httpPath}`, {
254
+ method,
255
+ headers,
256
+ body: method !== "GET" && request.payload ? JSON.stringify(request.payload) : undefined,
257
+ });
258
+ if (!resp.ok) {
259
+ return {
260
+ platformId: request.platformId,
261
+ channel: plan.channel,
262
+ latencyMs: Date.now() - started,
263
+ success: false,
264
+ error: {
265
+ code: "api_error",
266
+ detail: `HTTP ${resp.status}: ${await resp.text().catch(() => "")}`,
267
+ },
268
+ };
269
+ }
270
+ const data = await resp.json();
271
+ return {
272
+ platformId: request.platformId,
273
+ channel: plan.channel,
274
+ latencyMs: Date.now() - started,
275
+ success: true,
276
+ payload: {
277
+ capability: request.intent,
278
+ channel: plan.channel,
279
+ data,
280
+ },
281
+ };
282
+ }
283
+ catch (err) {
284
+ return {
285
+ platformId: request.platformId,
286
+ channel: plan.channel,
287
+ latencyMs: Date.now() - started,
288
+ success: false,
289
+ error: {
290
+ code: "network_error",
291
+ detail: String(err),
292
+ },
293
+ };
294
+ }
295
+ },
296
+ };
297
+ }
298
+ /**
299
+ * Scriptable Node Runner — workspace connector execution via dynamic ES Module import.
300
+ *
301
+ * Contract (runner.mjs default export):
302
+ * Input: { intent: string, payload: unknown, credential?: string }
303
+ * Output: { success: boolean, data?: unknown, error?: { code: string, detail: string } }
304
+ *
305
+ * Timeout: default 10s, overridable via manifest.runner.config.timeoutMs.
306
+ * Credential: passed as plain string when manifest.credentials required and vault has active entry.
307
+ * Error mapping:
308
+ * - missing entrypoint file → configuration_missing
309
+ * - default export is not function → script_error
310
+ * - runner throws → script_error (detail includes error message)
311
+ * - Promise.race timeout → timeout
312
+ */
313
+ function createScriptableNodeRunner(manifest, manifestDir, activeCredential) {
314
+ const entryPath = manifest.runner.entrypoint ?? "runner.mjs";
315
+ const absoluteEntryPath = path.resolve(manifestDir, entryPath);
316
+ const DEFAULT_TIMEOUT_MS = 10000;
317
+ const timeoutMs = typeof manifest.runner.config?.timeoutMs === "number" &&
318
+ Number.isFinite(manifest.runner.config.timeoutMs) &&
319
+ manifest.runner.config.timeoutMs > 0
320
+ ? manifest.runner.config.timeoutMs
321
+ : DEFAULT_TIMEOUT_MS;
322
+ return {
323
+ async run(_plan, request) {
324
+ const started = Date.now();
325
+ if (!fs.existsSync(absoluteEntryPath)) {
326
+ return {
327
+ platformId: request.platformId,
328
+ channel: request.preferredChannel ?? "api_rest",
329
+ latencyMs: Date.now() - started,
330
+ success: false,
331
+ error: {
332
+ code: "configuration_missing",
333
+ detail: `scriptable_node runner not found: ${absoluteEntryPath}`,
334
+ },
335
+ };
336
+ }
337
+ try {
338
+ const module = await import(pathToFileURL(absoluteEntryPath).href);
339
+ const handler = module.default;
340
+ if (typeof handler !== "function") {
341
+ return {
342
+ platformId: request.platformId,
343
+ channel: request.preferredChannel ?? "api_rest",
344
+ latencyMs: Date.now() - started,
345
+ success: false,
346
+ error: {
347
+ code: "script_error",
348
+ detail: `scriptable_node runner must export a default function from ${absoluteEntryPath}`,
349
+ },
350
+ };
351
+ }
352
+ const result = await Promise.race([
353
+ handler({
354
+ intent: request.intent,
355
+ payload: request.payload,
356
+ credential: activeCredential?.encryptedValue,
357
+ }),
358
+ new Promise((_, reject) => setTimeout(() => reject(new Error("scriptable_node_timeout")), timeoutMs)),
359
+ ]);
360
+ if (result && typeof result === "object" && "success" in result) {
361
+ return {
362
+ platformId: request.platformId,
363
+ channel: request.preferredChannel ?? "api_rest",
364
+ latencyMs: Date.now() - started,
365
+ success: Boolean(result.success),
366
+ payload: result.success
367
+ ? {
368
+ capability: request.intent,
369
+ channel: request.preferredChannel ?? "api_rest",
370
+ data: result.data,
371
+ }
372
+ : undefined,
373
+ error: !result.success
374
+ ? {
375
+ code: result.error?.code ?? "script_error",
376
+ detail: result.error?.detail ?? "scriptable_node_runner_returned_failure",
377
+ }
378
+ : undefined,
379
+ };
380
+ }
381
+ return {
382
+ platformId: request.platformId,
383
+ channel: request.preferredChannel ?? "api_rest",
384
+ latencyMs: Date.now() - started,
385
+ success: false,
386
+ error: {
387
+ code: "script_error",
388
+ detail: `scriptable_node runner returned invalid shape from ${absoluteEntryPath}`,
389
+ },
390
+ };
391
+ }
392
+ catch (err) {
393
+ const isTimeout = err instanceof Error && err.message === "scriptable_node_timeout";
394
+ return {
395
+ platformId: request.platformId,
396
+ channel: request.preferredChannel ?? "api_rest",
397
+ latencyMs: Date.now() - started,
398
+ success: false,
399
+ error: {
400
+ code: isTimeout ? "timeout" : "script_error",
401
+ detail: isTimeout
402
+ ? `scriptable_node runner exceeded ${timeoutMs}ms timeout`
403
+ : String(err),
404
+ },
405
+ };
406
+ }
407
+ },
408
+ };
409
+ }
410
+ function createAdaptiveExecutionRunner(vault, workspaceRoot) {
411
+ return {
412
+ async run(_plan, request) {
413
+ const platformId = request.platformId;
414
+ const started = Date.now();
415
+ const workspaceManifestResult = findWorkspaceManifest(platformId, workspaceRoot);
416
+ const workspaceManifest = workspaceManifestResult?.manifest;
417
+ const isBuiltInPlatform = platformId === "moltbook" ||
418
+ platformId === "evomap" ||
419
+ platformId === "agent-world";
420
+ const requiresCredential = isBuiltInPlatform ||
421
+ Boolean(workspaceManifest?.credentials.some((credential) => credential.required !== false));
422
+ const credential = requiresCredential
423
+ ? await vault.loadCredentialContext(platformId)
424
+ : undefined;
425
+ if (requiresCredential &&
426
+ (!credential ||
427
+ credential.status !== "active" ||
428
+ !credential.encryptedValue)) {
429
+ return {
430
+ platformId,
431
+ channel: request.preferredChannel ?? "api_rest",
432
+ latencyMs: Date.now() - started,
433
+ success: false,
434
+ error: {
435
+ code: "auth_failure",
436
+ detail: "credential_unavailable_for_execution",
437
+ },
438
+ };
439
+ }
440
+ const activeCredential = credential?.status === "active" && credential.encryptedValue
441
+ ? { encryptedValue: credential.encryptedValue }
442
+ : undefined;
443
+ if (platformId === "moltbook") {
444
+ const baseUrl = process.env.SECOND_NATURE_MOLTBOOK_BASE_URL;
445
+ if (baseUrl) {
446
+ const apiClient = createMoltbookApiClient({
447
+ baseUrl,
448
+ accessToken: activeCredential.encryptedValue,
449
+ timeoutMs: 10000,
450
+ });
451
+ const runner = createMoltbookRunner({
452
+ apiClient,
453
+ skillRunner: {
454
+ run: async () => {
455
+ throw {
456
+ code: "protocol_mismatch",
457
+ detail: "moltbook_skill_runner_not_configured",
458
+ };
459
+ },
460
+ },
461
+ });
462
+ return runner.run(_plan, request);
463
+ }
464
+ // Mock fallback when real API is not configured
465
+ const mockRunner = createMoltbookMockRunner(workspaceRoot);
466
+ return mockRunner.run(_plan, request);
467
+ }
468
+ if (platformId === "evomap") {
469
+ const baseUrl = process.env.SECOND_NATURE_EVOMAP_BASE_URL;
470
+ if (!baseUrl) {
471
+ return {
472
+ platformId,
473
+ channel: request.preferredChannel ?? "api_rest",
474
+ latencyMs: Date.now() - started,
475
+ success: false,
476
+ error: {
477
+ code: "configuration_missing",
478
+ detail: "SECOND_NATURE_EVOMAP_BASE_URL not set. This connector requires the evomap node base URL to be configured via environment variable.",
479
+ },
480
+ };
481
+ }
482
+ const secretPort = createEvoMapSecretPort(vault);
483
+ const runner = createEvoMapRunner({
484
+ apiClient: {
485
+ async heartbeat(payload, nodeSecret) {
486
+ const path = readString(payload.heartbeatPath) ?? "/api/heartbeat";
487
+ return fetchEvoMapJson({ baseUrl, path, nodeSecret, method: "POST", body: payload, label: "heartbeat" });
488
+ },
489
+ async claimTask(payload, nodeSecret) {
490
+ const path = readString(payload.claimPath) ?? "/api/tasks/claim";
491
+ return fetchEvoMapJson({ baseUrl, path, nodeSecret, method: "POST", body: payload, label: "claim" });
492
+ },
493
+ },
494
+ a2aClient: {
495
+ async helloOrRegister(payload) {
496
+ const path = readString(payload.registerPath) ?? "/a2a/hello";
497
+ return fetchEvoMapJson({ baseUrl, path, method: "POST", body: payload, label: "register" });
498
+ },
499
+ async discoverWork(payload, nodeSecret) {
500
+ const path = readString(payload.discoverPath) ?? "/a2a/discover";
501
+ return fetchEvoMapJson({ baseUrl, path, nodeSecret, method: "POST", body: payload, label: "discover" });
502
+ },
503
+ },
504
+ secretPort,
505
+ });
506
+ return runner.run(_plan, request);
507
+ }
508
+ if (platformId === "agent-world") {
509
+ const baseUrl = process.env.SECOND_NATURE_AGENT_WORLD_BASE_URL;
510
+ if (!baseUrl) {
511
+ return {
512
+ platformId,
513
+ channel: request.preferredChannel ?? "api_rest",
514
+ latencyMs: Date.now() - started,
515
+ success: false,
516
+ error: {
517
+ code: "configuration_missing",
518
+ detail: "SECOND_NATURE_AGENT_WORLD_BASE_URL not set. This connector requires the agent-world node base URL to be configured via environment variable.",
519
+ },
520
+ };
521
+ }
522
+ const runner = createAgentWorldRunner({
523
+ apiKey: activeCredential.encryptedValue,
524
+ apiClient: {
525
+ async readFeed(payload, _apiKey) {
526
+ const username = resolveAgentWorldUsername(payload, "feed");
527
+ return fetchAgentWorldJson({
528
+ baseUrl,
529
+ path: resolveAgentWorldProfilePath(payload, username),
530
+ apiKey: _apiKey,
531
+ label: "profile feed",
532
+ });
533
+ },
534
+ async discoverWork(payload, _apiKey) {
535
+ const username = resolveAgentWorldUsername(payload, "discover");
536
+ return fetchAgentWorldJson({
537
+ baseUrl,
538
+ path: resolveAgentWorldProfilePath(payload, username),
539
+ apiKey: _apiKey,
540
+ label: "profile discover",
541
+ });
542
+ },
543
+ async claimTask(payload, _apiKey) {
544
+ const claimPath = readString(payload.claimEndpointPath);
545
+ if (!claimPath) {
546
+ throw {
547
+ code: "protocol_mismatch",
548
+ detail: "agent_world_task_claim_endpoint_not_configured",
549
+ };
550
+ }
551
+ return fetchAgentWorldJson({
552
+ baseUrl,
553
+ path: claimPath,
554
+ apiKey: _apiKey,
555
+ method: "POST",
556
+ body: payload,
557
+ label: "task claim",
558
+ });
559
+ },
560
+ },
561
+ });
562
+ return runner.run(_plan, request);
563
+ }
564
+ // T-CS.C.9: instreet is registered but requires skill/browser channel;
565
+ // pure api_rest execution returns platform_unavailable.
566
+ if (platformId === "instreet") {
567
+ return {
568
+ platformId,
569
+ channel: request.preferredChannel ?? "api_rest",
570
+ latencyMs: Date.now() - started,
571
+ success: false,
572
+ error: {
573
+ code: "platform_unavailable",
574
+ detail: "instreet_requires_skill_browser_channel",
575
+ },
576
+ };
577
+ }
578
+ // Wave 83: workspace declarative_http connector fallback
579
+ if (workspaceManifest && workspaceManifest.runner.kind === "declarative_http") {
580
+ const httpRunner = createDeclarativeHttpRunner(workspaceManifest, activeCredential);
581
+ return httpRunner.run(_plan, request);
582
+ }
583
+ // Wave 90: workspace scriptable_node connector
584
+ if (workspaceManifest && workspaceManifest.runner.kind === "scriptable_node") {
585
+ if (!workspaceManifestResult) {
586
+ return {
587
+ platformId,
588
+ channel: request.preferredChannel ?? "api_rest",
589
+ latencyMs: Date.now() - started,
590
+ success: false,
591
+ error: {
592
+ code: "configuration_missing",
593
+ detail: "scriptable_node requires workspace manifest with manifestDir",
594
+ },
595
+ };
596
+ }
597
+ const scriptRunner = createScriptableNodeRunner(workspaceManifest, workspaceManifestResult.manifestDir, activeCredential);
598
+ return scriptRunner.run(_plan, request);
599
+ }
600
+ return {
601
+ platformId,
602
+ channel: request.preferredChannel ?? "api_rest",
603
+ latencyMs: Date.now() - started,
604
+ success: false,
605
+ error: {
606
+ code: "unknown_platform",
607
+ detail: `no execution runner for ${platformId}`,
608
+ },
609
+ };
610
+ },
611
+ };
612
+ }
613
+ export function createConnectorExecutorAdapter(options) {
614
+ const vault = createCredentialVault(options.stateDb.db);
615
+ const registry = new CapabilityContractRegistry();
616
+ registry.register({ ...moltbookManifest });
617
+ registry.register({ ...evomapManifest });
618
+ registry.register({ ...agentWorldManifest });
619
+ registry.register({ ...instreetManifest });
620
+ registerWorkspaceManifests(registry, options.workspaceRoot);
621
+ const routeContextPort = createCredentialRouteContextPort(vault);
622
+ const routePlanner = new ConnectorRoutePlanner(registry, routeContextPort, new ChannelHealthStore());
623
+ const telemetry = new ExecutionTelemetry(options.observabilityDb);
624
+ const executionRunner = createAdaptiveExecutionRunner(vault, options.workspaceRoot);
625
+ const policy = createConnectorPolicyLayer({
626
+ routePlanner,
627
+ executionRunner,
628
+ telemetry,
629
+ effectCommitLedger: new InMemoryEffectCommitLedger(),
630
+ retryPolicy: { maxRetries: 2, jitter: true },
631
+ });
632
+ return {
633
+ async executeEffect(input) {
634
+ registerWorkspaceManifests(registry, options.workspaceRoot);
635
+ return policy.executeWithPolicy(input.intent, {
636
+ platformId: input.platformId,
637
+ intent: input.intent,
638
+ payload: input.payload,
639
+ decisionId: input.decisionId,
640
+ intentId: input.intentId,
641
+ idempotencyKey: input.idempotencyKey,
642
+ });
643
+ },
644
+ };
645
+ }