@haaaiawd/second-nature 0.1.26 → 0.1.29

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 (158) hide show
  1. package/SKILL.md +35 -0
  2. package/agent-inner-guide.md +144 -0
  3. package/index.js +280 -2
  4. package/openclaw.plugin.json +2 -2
  5. package/package.json +4 -1
  6. package/runtime/cli/commands/connector-behavior.d.ts +20 -0
  7. package/runtime/cli/commands/connector-behavior.js +160 -0
  8. package/runtime/cli/commands/index.js +8 -0
  9. package/runtime/cli/index.js +9 -2
  10. package/runtime/cli/ops/manual-run-dispatcher.d.ts +79 -0
  11. package/runtime/cli/ops/manual-run-dispatcher.js +110 -0
  12. package/runtime/cli/ops/ops-router.d.ts +45 -4
  13. package/runtime/cli/ops/ops-router.js +543 -2
  14. package/runtime/cli/read-models/index.js +35 -18
  15. package/runtime/cli/read-models/types.d.ts +1 -0
  16. package/runtime/connectors/agent-network/agent-world/adapter.d.ts +1 -0
  17. package/runtime/connectors/agent-network/agent-world/adapter.js +2 -2
  18. package/runtime/connectors/base/contract.d.ts +4 -1
  19. package/runtime/connectors/base/contract.js +5 -1
  20. package/runtime/connectors/base/effect-commit-ledger-sqlite.d.ts +31 -0
  21. package/runtime/connectors/base/effect-commit-ledger-sqlite.js +86 -0
  22. package/runtime/connectors/base/failure-taxonomy.js +5 -0
  23. package/runtime/connectors/base/manifest-v7.d.ts +151 -0
  24. package/runtime/connectors/base/manifest-v7.js +170 -0
  25. package/runtime/connectors/base/manifest.d.ts +3 -13
  26. package/runtime/connectors/base/manifest.js +7 -7
  27. package/runtime/connectors/base/route-planner.js +11 -8
  28. package/runtime/connectors/base/structured-unavailable-reason.d.ts +59 -0
  29. package/runtime/connectors/base/structured-unavailable-reason.js +113 -0
  30. package/runtime/connectors/base/wet-probe-runner.d.ts +40 -0
  31. package/runtime/connectors/base/wet-probe-runner.js +132 -0
  32. package/runtime/connectors/manifest/manifest-schema.d.ts +4 -0
  33. package/runtime/connectors/manifest/manifest-schema.js +2 -0
  34. package/runtime/connectors/services/connector-executor-adapter.d.ts +1 -0
  35. package/runtime/connectors/services/connector-executor-adapter.js +132 -26
  36. package/runtime/core/second-nature/body/behavior-promotion/behavior-promotion-loop.d.ts +45 -0
  37. package/runtime/core/second-nature/body/behavior-promotion/behavior-promotion-loop.js +132 -0
  38. package/runtime/core/second-nature/body/circuit-breaker/circuit-breaker-manager.d.ts +60 -0
  39. package/runtime/core/second-nature/body/circuit-breaker/circuit-breaker-manager.js +174 -0
  40. package/runtime/core/second-nature/body/probe-signal-adapter.d.ts +38 -0
  41. package/runtime/core/second-nature/body/probe-signal-adapter.js +60 -0
  42. package/runtime/core/second-nature/body/tool-affordance/affordance-assembler.d.ts +51 -0
  43. package/runtime/core/second-nature/body/tool-affordance/affordance-assembler.js +129 -0
  44. package/runtime/core/second-nature/body/tool-affordance/affordance-context-scope.d.ts +30 -0
  45. package/runtime/core/second-nature/body/tool-affordance/affordance-context-scope.js +92 -0
  46. package/runtime/core/second-nature/body/tool-experience/experience-writer.d.ts +34 -0
  47. package/runtime/core/second-nature/body/tool-experience/experience-writer.js +67 -0
  48. package/runtime/core/second-nature/body/tool-experience/pain-signal-query.d.ts +37 -0
  49. package/runtime/core/second-nature/body/tool-experience/pain-signal-query.js +62 -0
  50. package/runtime/core/second-nature/heartbeat/decision-trace-emitter.d.ts +29 -0
  51. package/runtime/core/second-nature/heartbeat/decision-trace-emitter.js +28 -0
  52. package/runtime/core/second-nature/heartbeat/embodied-context-assembler.d.ts +54 -0
  53. package/runtime/core/second-nature/heartbeat/embodied-context-assembler.js +164 -0
  54. package/runtime/core/second-nature/heartbeat/goal-lifecycle-policy.d.ts +37 -0
  55. package/runtime/core/second-nature/heartbeat/goal-lifecycle-policy.js +61 -0
  56. package/runtime/core/second-nature/heartbeat/idle-curiosity-policy.d.ts +37 -0
  57. package/runtime/core/second-nature/heartbeat/idle-curiosity-policy.js +60 -0
  58. package/runtime/core/second-nature/heartbeat/index.d.ts +4 -0
  59. package/runtime/core/second-nature/heartbeat/index.js +5 -0
  60. package/runtime/core/second-nature/heartbeat/run-heartbeat-cycle-v7.d.ts +63 -0
  61. package/runtime/core/second-nature/heartbeat/run-heartbeat-cycle-v7.js +118 -0
  62. package/runtime/core/second-nature/orchestrator/downstream-intent-orchestrator.d.ts +41 -0
  63. package/runtime/core/second-nature/orchestrator/downstream-intent-orchestrator.js +43 -0
  64. package/runtime/core/second-nature/orchestrator/effect-dispatcher.d.ts +2 -1
  65. package/runtime/core/second-nature/orchestrator/effect-dispatcher.js +2 -0
  66. package/runtime/core/second-nature/orchestrator/hard-guard-evaluator.d.ts +31 -0
  67. package/runtime/core/second-nature/orchestrator/hard-guard-evaluator.js +102 -0
  68. package/runtime/core/second-nature/orchestrator/index.d.ts +5 -0
  69. package/runtime/core/second-nature/orchestrator/index.js +7 -0
  70. package/runtime/core/second-nature/quiet/claim-synthesizer.d.ts +53 -0
  71. package/runtime/core/second-nature/quiet/claim-synthesizer.js +153 -0
  72. package/runtime/core/second-nature/quiet/daily-diary-writer.d.ts +29 -0
  73. package/runtime/core/second-nature/quiet/daily-diary-writer.js +92 -0
  74. package/runtime/core/second-nature/quiet/index.d.ts +5 -0
  75. package/runtime/core/second-nature/quiet/index.js +5 -0
  76. package/runtime/core/second-nature/quiet/run-source-backed-quiet.js +19 -12
  77. package/runtime/core/second-nature/types.d.ts +2 -0
  78. package/runtime/guidance/channel-feedback-ingestion-service.d.ts +88 -0
  79. package/runtime/guidance/channel-feedback-ingestion-service.js +231 -0
  80. package/runtime/guidance/guidance-draft-service.d.ts +60 -0
  81. package/runtime/guidance/guidance-draft-service.js +80 -0
  82. package/runtime/guidance/index.d.ts +3 -0
  83. package/runtime/guidance/index.js +3 -0
  84. package/runtime/guidance/outreach-draft-schema.d.ts +8 -8
  85. package/runtime/guidance/outreach-strategy-selector.d.ts +77 -0
  86. package/runtime/guidance/outreach-strategy-selector.js +211 -0
  87. package/runtime/observability/audit/append-only-audit-store.d.ts +20 -2
  88. package/runtime/observability/audit/append-only-audit-store.js +32 -6
  89. package/runtime/observability/audit/audit-envelope.d.ts +2 -1
  90. package/runtime/observability/audit/audit-envelope.js +8 -7
  91. package/runtime/observability/audit/audit-family-registry.json +66 -0
  92. package/runtime/observability/audit/family-registry.d.ts +43 -0
  93. package/runtime/observability/audit/family-registry.js +70 -0
  94. package/runtime/observability/index.d.ts +6 -1
  95. package/runtime/observability/index.js +6 -1
  96. package/runtime/observability/redaction/policy.d.ts +24 -3
  97. package/runtime/observability/redaction/policy.js +74 -0
  98. package/runtime/observability/services/heartbeat-digest-assembler.d.ts +152 -0
  99. package/runtime/observability/services/heartbeat-digest-assembler.js +248 -0
  100. package/runtime/observability/services/lived-experience-audit.js +6 -6
  101. package/runtime/observability/services/narrative-timeline-query-service.d.ts +136 -0
  102. package/runtime/observability/services/narrative-timeline-query-service.js +169 -0
  103. package/runtime/observability/services/restore-audit-service.d.ts +74 -0
  104. package/runtime/observability/services/restore-audit-service.js +79 -0
  105. package/runtime/observability/services/runtime-secret-anchor-view.d.ts +77 -0
  106. package/runtime/observability/services/runtime-secret-anchor-view.js +168 -0
  107. package/runtime/observability/services/self-health-snapshot.d.ts +92 -0
  108. package/runtime/observability/services/self-health-snapshot.js +251 -0
  109. package/runtime/shared/types/goal.d.ts +62 -0
  110. package/runtime/shared/types/goal.js +20 -0
  111. package/runtime/shared/types/index.d.ts +3 -0
  112. package/runtime/shared/types/index.js +3 -0
  113. package/runtime/shared/types/source-ref.d.ts +14 -0
  114. package/runtime/shared/types/source-ref.js +1 -0
  115. package/runtime/shared/types/v7-entities.d.ts +206 -0
  116. package/runtime/shared/types/v7-entities.js +27 -0
  117. package/runtime/storage/db/index.js +3 -0
  118. package/runtime/storage/db/migration-runner.d.ts +30 -0
  119. package/runtime/storage/db/migration-runner.js +93 -0
  120. package/runtime/storage/db/migrations/index.d.ts +5 -0
  121. package/runtime/storage/db/migrations/index.js +13 -0
  122. package/runtime/storage/db/migrations/v7-001-foundation.d.ts +13 -0
  123. package/runtime/storage/db/migrations/v7-001-foundation.js +144 -0
  124. package/runtime/storage/db/migrations/v7-002-effect-commit-ledger.d.ts +8 -0
  125. package/runtime/storage/db/migrations/v7-002-effect-commit-ledger.js +27 -0
  126. package/runtime/storage/db/migrations/v7-003-circuit-breaker.d.ts +7 -0
  127. package/runtime/storage/db/migrations/v7-003-circuit-breaker.js +26 -0
  128. package/runtime/storage/db/migrations/v7-004-behavior-promotion.d.ts +7 -0
  129. package/runtime/storage/db/migrations/v7-004-behavior-promotion.js +26 -0
  130. package/runtime/storage/db/schema/agent-goal.d.ts +38 -0
  131. package/runtime/storage/db/schema/agent-goal.js +2 -0
  132. package/runtime/storage/db/transaction-utils.d.ts +14 -0
  133. package/runtime/storage/db/transaction-utils.js +29 -0
  134. package/runtime/storage/db/write-queue.d.ts +38 -0
  135. package/runtime/storage/db/write-queue.js +97 -0
  136. package/runtime/storage/quiet/persist-quiet-artifact.js +2 -1
  137. package/runtime/storage/services/credential-vault.js +31 -17
  138. package/runtime/storage/services/diary-dream-store.d.ts +35 -0
  139. package/runtime/storage/services/diary-dream-store.js +165 -0
  140. package/runtime/storage/services/embodied-context-state-port.d.ts +77 -0
  141. package/runtime/storage/services/embodied-context-state-port.js +115 -0
  142. package/runtime/storage/services/goal-lifecycle-store.d.ts +42 -0
  143. package/runtime/storage/services/goal-lifecycle-store.js +181 -0
  144. package/runtime/storage/services/history-digest-store.d.ts +33 -0
  145. package/runtime/storage/services/history-digest-store.js +140 -0
  146. package/runtime/storage/services/identity-profile-store.d.ts +25 -0
  147. package/runtime/storage/services/identity-profile-store.js +81 -0
  148. package/runtime/storage/services/interaction-snapshot-projector.d.ts +15 -0
  149. package/runtime/storage/services/interaction-snapshot-projector.js +35 -0
  150. package/runtime/storage/services/restore-snapshot-store.d.ts +52 -0
  151. package/runtime/storage/services/restore-snapshot-store.js +193 -0
  152. package/runtime/storage/services/runtime-secret-anchor-store.d.ts +26 -0
  153. package/runtime/storage/services/runtime-secret-anchor-store.js +82 -0
  154. package/runtime/storage/services/tool-experience-store.d.ts +25 -0
  155. package/runtime/storage/services/tool-experience-store.js +116 -0
  156. package/runtime/storage/services/write-validation-gate.d.ts +46 -0
  157. package/runtime/storage/services/write-validation-gate.js +200 -0
  158. package/workspace-ops-bridge.js +16 -1
@@ -1,5 +1,5 @@
1
1
  import { z } from "zod";
2
- import { CAPABILITY_INTENTS, CHANNEL_TYPES } from "./contract.js";
2
+ import { CHANNEL_TYPES } from "./contract.js";
3
3
  const sourceRefPolicySchema = z
4
4
  .object({
5
5
  minSourceRefs: z.number().int().min(0).default(1),
@@ -8,9 +8,9 @@ const sourceRefPolicySchema = z
8
8
  .optional();
9
9
  const connectorManifestSchema = z.object({
10
10
  platformId: z.string().min(1),
11
- supportedCapabilities: z.array(z.enum(CAPABILITY_INTENTS)).min(1),
11
+ supportedCapabilities: z.array(z.string().min(1).regex(/^[a-zA-Z0-9_.:-]+$/)).min(1),
12
12
  channelPriority: z.array(z.enum(CHANNEL_TYPES)).min(1),
13
- credentialTypes: z.array(z.string().min(1)).min(1),
13
+ credentialTypes: z.array(z.string().min(1)),
14
14
  degradedChannels: z.array(z.enum(CHANNEL_TYPES)).optional(),
15
15
  sourceRefPolicy: sourceRefPolicySchema,
16
16
  });
@@ -51,8 +51,8 @@ export class CapabilityContractRegistry {
51
51
  if (colonIndex >= 0) {
52
52
  const platformId = intentWithNamespace.slice(0, colonIndex);
53
53
  const intent = intentWithNamespace.slice(colonIndex + 1);
54
- if (!CAPABILITY_INTENTS.includes(intent)) {
55
- throw new Error(`capability_not_recognized:${intent}`);
54
+ if (!intent) {
55
+ throw new Error("capability_not_recognized:");
56
56
  }
57
57
  if (!this.byPlatform.has(platformId)) {
58
58
  throw new Error(`platform_not_found:${platformId}`);
@@ -60,8 +60,8 @@ export class CapabilityContractRegistry {
60
60
  return { platformId, intent, source: "namespace" };
61
61
  }
62
62
  const intent = intentWithNamespace;
63
- if (!CAPABILITY_INTENTS.includes(intent)) {
64
- throw new Error(`capability_not_recognized:${intent}`);
63
+ if (!intent) {
64
+ throw new Error("capability_not_recognized:");
65
65
  }
66
66
  if (explicitPlatformId) {
67
67
  if (!this.byPlatform.has(explicitPlatformId)) {
@@ -72,15 +72,18 @@ export class ConnectorRoutePlanner {
72
72
  if (cooldown.blocked) {
73
73
  throw new ConnectorPolicyError("cooldown_blocked", "platform_or_intent_cooldown_blocked", cooldown.retryAfterMs);
74
74
  }
75
- const credential = await this.statePort.loadCredentialState(request.platformId);
76
- if (credential.status === "missing" || credential.status === "revoked" || credential.status === "failed") {
77
- throw new ConnectorPolicyError("auth_failure", "credential_unavailable_for_route");
78
- }
79
- if (credential.status === "expired") {
80
- throw new ConnectorPolicyError("credential_expired", "credential_expired_for_route");
81
- }
82
75
  const channels = [...manifest.channelPriority];
83
- const byCredential = chooseByCredentialState(channels, credential);
76
+ let byCredential;
77
+ if (manifest.credentialTypes.length > 0) {
78
+ const credential = await this.statePort.loadCredentialState(request.platformId);
79
+ if (credential.status === "missing" || credential.status === "revoked" || credential.status === "failed") {
80
+ throw new ConnectorPolicyError("auth_failure", "credential_unavailable_for_route");
81
+ }
82
+ if (credential.status === "expired") {
83
+ throw new ConnectorPolicyError("credential_expired", "credential_expired_for_route");
84
+ }
85
+ byCredential = chooseByCredentialState(channels, credential);
86
+ }
84
87
  const preferred = choosePreferred(channels, request.preferredChannel);
85
88
  const selected = byCredential ?? preferred ?? chooseHealthy(channels, request.platformId, this.channelHealth);
86
89
  if (!selected) {
@@ -0,0 +1,59 @@
1
+ /**
2
+ * StructuredUnavailableReason Builder — T-CS.C.3
3
+ *
4
+ * Core logic: Every unavailable-connector scenario MUST return a machine-
5
+ * readable reason code. No silent failures allowed.
6
+ *
7
+ * Reason codes:
8
+ * - credentials_missing → credential not found for platform
9
+ * - not_registered → connector manifest not in registry
10
+ * - trust_denied → credential verification failed
11
+ * - circuit_open → circuit breaker is open (cooldown blocked)
12
+ * - platform_error → platform returned 5xx / transport failure
13
+ * - probe_failed → wet probe returned non-2xx
14
+ * - probe_policy_denied → strict idempotencyClass blocked probe (DR-006)
15
+ *
16
+ * Dependencies:
17
+ * - `FailureClass` from `./failure-taxonomy.js`
18
+ *
19
+ * Boundary:
20
+ * - All builder methods are pure; no side effects.
21
+ * - `build()` validates that code and message are present.
22
+ *
23
+ * Test coverage: tests/unit/connectors/structured-unavailable-reason.test.ts
24
+ */
25
+ import type { FailureClass } from "./failure-taxonomy.js";
26
+ export type UnavailableReasonCode = "credentials_missing" | "not_registered" | "trust_denied" | "circuit_open" | "platform_error" | "probe_failed" | "probe_policy_denied";
27
+ export interface StructuredUnavailableReason {
28
+ code: UnavailableReasonCode;
29
+ message: string;
30
+ platformId?: string;
31
+ capabilityId?: string;
32
+ failureClass?: FailureClass;
33
+ retryAfterMs?: number;
34
+ timestamp: string;
35
+ }
36
+ export declare class UnavailableReasonBuilder {
37
+ private code?;
38
+ private message?;
39
+ private platformId?;
40
+ private capabilityId?;
41
+ private failureClass?;
42
+ private retryAfterMs?;
43
+ static for(code: UnavailableReasonCode, message: string): UnavailableReasonBuilder;
44
+ withPlatformId(platformId: string): this;
45
+ withCapabilityId(capabilityId: string): this;
46
+ withFailureClass(failureClass: FailureClass): this;
47
+ withRetryAfterMs(ms: number): this;
48
+ build(): StructuredUnavailableReason;
49
+ }
50
+ /**
51
+ * Convenience factory for common unavailable scenarios.
52
+ */
53
+ export declare function unavailableCredentialsMissing(platformId: string): StructuredUnavailableReason;
54
+ export declare function unavailableNotRegistered(platformId: string): StructuredUnavailableReason;
55
+ export declare function unavailableTrustDenied(platformId: string): StructuredUnavailableReason;
56
+ export declare function unavailableCircuitOpen(platformId: string, retryAfterMs?: number): StructuredUnavailableReason;
57
+ export declare function unavailablePlatformError(platformId: string, failureClass?: FailureClass): StructuredUnavailableReason;
58
+ export declare function unavailableProbeFailed(platformId: string, capabilityId: string): StructuredUnavailableReason;
59
+ export declare function unavailableProbePolicyDenied(platformId: string, capabilityId: string): StructuredUnavailableReason;
@@ -0,0 +1,113 @@
1
+ /**
2
+ * StructuredUnavailableReason Builder — T-CS.C.3
3
+ *
4
+ * Core logic: Every unavailable-connector scenario MUST return a machine-
5
+ * readable reason code. No silent failures allowed.
6
+ *
7
+ * Reason codes:
8
+ * - credentials_missing → credential not found for platform
9
+ * - not_registered → connector manifest not in registry
10
+ * - trust_denied → credential verification failed
11
+ * - circuit_open → circuit breaker is open (cooldown blocked)
12
+ * - platform_error → platform returned 5xx / transport failure
13
+ * - probe_failed → wet probe returned non-2xx
14
+ * - probe_policy_denied → strict idempotencyClass blocked probe (DR-006)
15
+ *
16
+ * Dependencies:
17
+ * - `FailureClass` from `./failure-taxonomy.js`
18
+ *
19
+ * Boundary:
20
+ * - All builder methods are pure; no side effects.
21
+ * - `build()` validates that code and message are present.
22
+ *
23
+ * Test coverage: tests/unit/connectors/structured-unavailable-reason.test.ts
24
+ */
25
+ export class UnavailableReasonBuilder {
26
+ code;
27
+ message;
28
+ platformId;
29
+ capabilityId;
30
+ failureClass;
31
+ retryAfterMs;
32
+ static for(code, message) {
33
+ const b = new UnavailableReasonBuilder();
34
+ b.code = code;
35
+ b.message = message;
36
+ return b;
37
+ }
38
+ withPlatformId(platformId) {
39
+ this.platformId = platformId;
40
+ return this;
41
+ }
42
+ withCapabilityId(capabilityId) {
43
+ this.capabilityId = capabilityId;
44
+ return this;
45
+ }
46
+ withFailureClass(failureClass) {
47
+ this.failureClass = failureClass;
48
+ return this;
49
+ }
50
+ withRetryAfterMs(ms) {
51
+ this.retryAfterMs = ms;
52
+ return this;
53
+ }
54
+ build() {
55
+ if (!this.code) {
56
+ throw new Error("UnavailableReasonBuilder: code is required");
57
+ }
58
+ if (!this.message) {
59
+ throw new Error("UnavailableReasonBuilder: message is required");
60
+ }
61
+ return {
62
+ code: this.code,
63
+ message: this.message,
64
+ platformId: this.platformId,
65
+ capabilityId: this.capabilityId,
66
+ failureClass: this.failureClass,
67
+ retryAfterMs: this.retryAfterMs,
68
+ timestamp: new Date().toISOString(),
69
+ };
70
+ }
71
+ }
72
+ /**
73
+ * Convenience factory for common unavailable scenarios.
74
+ */
75
+ export function unavailableCredentialsMissing(platformId) {
76
+ return UnavailableReasonBuilder.for("credentials_missing", `No active credential found for platform ${platformId}`)
77
+ .withPlatformId(platformId)
78
+ .build();
79
+ }
80
+ export function unavailableNotRegistered(platformId) {
81
+ return UnavailableReasonBuilder.for("not_registered", `Connector manifest not registered for platform ${platformId}`)
82
+ .withPlatformId(platformId)
83
+ .build();
84
+ }
85
+ export function unavailableTrustDenied(platformId) {
86
+ return UnavailableReasonBuilder.for("trust_denied", `Credential verification failed for platform ${platformId}`)
87
+ .withPlatformId(platformId)
88
+ .build();
89
+ }
90
+ export function unavailableCircuitOpen(platformId, retryAfterMs) {
91
+ const b = UnavailableReasonBuilder.for("circuit_open", `Circuit breaker open for platform ${platformId}`).withPlatformId(platformId);
92
+ if (retryAfterMs !== undefined)
93
+ b.withRetryAfterMs(retryAfterMs);
94
+ return b.build();
95
+ }
96
+ export function unavailablePlatformError(platformId, failureClass) {
97
+ const b = UnavailableReasonBuilder.for("platform_error", `Platform error for ${platformId}`).withPlatformId(platformId);
98
+ if (failureClass)
99
+ b.withFailureClass(failureClass);
100
+ return b.build();
101
+ }
102
+ export function unavailableProbeFailed(platformId, capabilityId) {
103
+ return UnavailableReasonBuilder.for("probe_failed", `Wet probe failed for ${platformId}:${capabilityId}`)
104
+ .withPlatformId(platformId)
105
+ .withCapabilityId(capabilityId)
106
+ .build();
107
+ }
108
+ export function unavailableProbePolicyDenied(platformId, capabilityId) {
109
+ return UnavailableReasonBuilder.for("probe_policy_denied", `Probe blocked by policy (strict idempotencyClass) for ${platformId}:${capabilityId}`)
110
+ .withPlatformId(platformId)
111
+ .withCapabilityId(capabilityId)
112
+ .build();
113
+ }
@@ -0,0 +1,40 @@
1
+ /**
2
+ * WetProbeRunner — T-CS.C.2
3
+ *
4
+ * Core logic: Performs real HTTP GET against a connector's safe probe endpoint.
5
+ * Double-verifies `safe_for_probe` before issuing the request:
6
+ * 1. IdempotencyClass check: `strict` side-effects are rejected with
7
+ * `probe_policy_denied` (DR-006).
8
+ * 2. Endpoint validation: only `safeEndpoint` from probeConfig is allowed.
9
+ *
10
+ * Returns a CapabilityProbeResult containing capabilityId, actualStatus,
11
+ * httpStatus, and sampleResponseRef.
12
+ *
13
+ * Dependencies:
14
+ * - `CapabilityContractRegistryV7` from `./manifest-v7.js`
15
+ * - `CapabilityProbeResult` from `../../shared/types/v7-entities.js`
16
+ * - `ProbeActualStatus` from `../../shared/types/v7-entities.js`
17
+ *
18
+ * Boundary:
19
+ * - Never probes endpoints not explicitly marked safe.
20
+ * - Never probes capabilities with idempotencyClass = "strict".
21
+ * - HTTP layer is injectable (`httpGet`) for testability.
22
+ *
23
+ * Test coverage: tests/unit/connectors/wet-probe-runner.test.ts
24
+ */
25
+ import type { CapabilityContractRegistryV7 } from "./manifest-v7.js";
26
+ import type { CapabilityProbeResult } from "../../shared/types/v7-entities.js";
27
+ export type HttpGetFn = (url: string) => Promise<{
28
+ status: number;
29
+ body?: string;
30
+ }>;
31
+ export interface WetProbeResult {
32
+ probeResult: CapabilityProbeResult;
33
+ httpStatus: number;
34
+ }
35
+ export interface WetProbeRunner {
36
+ runWetProbe(platformId: string, capabilityId: string, registry: CapabilityContractRegistryV7, options?: {
37
+ httpGet?: HttpGetFn;
38
+ }): Promise<WetProbeResult>;
39
+ }
40
+ export declare function createWetProbeRunner(): WetProbeRunner;
@@ -0,0 +1,132 @@
1
+ /**
2
+ * WetProbeRunner — T-CS.C.2
3
+ *
4
+ * Core logic: Performs real HTTP GET against a connector's safe probe endpoint.
5
+ * Double-verifies `safe_for_probe` before issuing the request:
6
+ * 1. IdempotencyClass check: `strict` side-effects are rejected with
7
+ * `probe_policy_denied` (DR-006).
8
+ * 2. Endpoint validation: only `safeEndpoint` from probeConfig is allowed.
9
+ *
10
+ * Returns a CapabilityProbeResult containing capabilityId, actualStatus,
11
+ * httpStatus, and sampleResponseRef.
12
+ *
13
+ * Dependencies:
14
+ * - `CapabilityContractRegistryV7` from `./manifest-v7.js`
15
+ * - `CapabilityProbeResult` from `../../shared/types/v7-entities.js`
16
+ * - `ProbeActualStatus` from `../../shared/types/v7-entities.js`
17
+ *
18
+ * Boundary:
19
+ * - Never probes endpoints not explicitly marked safe.
20
+ * - Never probes capabilities with idempotencyClass = "strict".
21
+ * - HTTP layer is injectable (`httpGet`) for testability.
22
+ *
23
+ * Test coverage: tests/unit/connectors/wet-probe-runner.test.ts
24
+ */
25
+ const PROBE_POLICY_DENIED = {
26
+ probeResult: {
27
+ probeResultId: "probe_policy_denied",
28
+ capabilityId: "",
29
+ connectorId: "",
30
+ actualStatus: "unavailable",
31
+ probeConfigRef: "policy:denied",
32
+ createdAt: new Date().toISOString(),
33
+ },
34
+ httpStatus: 0,
35
+ };
36
+ function resolveProbeConfig(registry, platformId, capabilityId) {
37
+ const resolved = registry.resolveCapability(`${platformId}:${capabilityId}`);
38
+ if (!resolved)
39
+ return undefined;
40
+ const probeConfig = resolved.probeConfig;
41
+ if (!probeConfig)
42
+ return undefined;
43
+ return {
44
+ safeEndpoint: probeConfig.safeEndpoint,
45
+ idempotencyClass: probeConfig.idempotencyClass,
46
+ };
47
+ }
48
+ function actualStatusFromHttpStatus(status) {
49
+ if (status >= 200 && status < 300)
50
+ return "available";
51
+ if (status === 429 || status === 503)
52
+ return "degraded";
53
+ return "unavailable";
54
+ }
55
+ export function createWetProbeRunner() {
56
+ const defaultHttpGet = async (url) => {
57
+ const response = await fetch(url, { method: "GET" });
58
+ const body = await response.text().catch(() => undefined);
59
+ return { status: response.status, body };
60
+ };
61
+ return {
62
+ async runWetProbe(platformId, capabilityId, registry, options = {}) {
63
+ const httpGet = options.httpGet ?? defaultHttpGet;
64
+ // 1. Resolve capability and probe config
65
+ const config = resolveProbeConfig(registry, platformId, capabilityId);
66
+ if (!config) {
67
+ return {
68
+ ...PROBE_POLICY_DENIED,
69
+ probeResult: {
70
+ ...PROBE_POLICY_DENIED.probeResult,
71
+ probeResultId: `probe_no_config:${capabilityId}`,
72
+ capabilityId,
73
+ connectorId: platformId,
74
+ actualStatus: "unavailable",
75
+ probeConfigRef: "registry:missing",
76
+ createdAt: new Date().toISOString(),
77
+ },
78
+ httpStatus: 0,
79
+ };
80
+ }
81
+ // 2. DR-006: strict side-effect → probe_policy_denied
82
+ if (config.idempotencyClass === "strict") {
83
+ return {
84
+ ...PROBE_POLICY_DENIED,
85
+ probeResult: {
86
+ ...PROBE_POLICY_DENIED.probeResult,
87
+ probeResultId: `probe_policy_denied:${capabilityId}`,
88
+ capabilityId,
89
+ connectorId: platformId,
90
+ actualStatus: "unavailable",
91
+ probeConfigRef: config.safeEndpoint,
92
+ createdAt: new Date().toISOString(),
93
+ },
94
+ httpStatus: 0,
95
+ };
96
+ }
97
+ // 3. Execute HTTP GET against safe endpoint
98
+ try {
99
+ const response = await httpGet(config.safeEndpoint);
100
+ const actualStatus = actualStatusFromHttpStatus(response.status);
101
+ return {
102
+ probeResult: {
103
+ probeResultId: `probe:${platformId}:${capabilityId}:${Date.now()}`,
104
+ capabilityId,
105
+ connectorId: platformId,
106
+ actualStatus,
107
+ httpStatus: response.status,
108
+ sampleResponseRef: response.body
109
+ ? `probe:body:${Buffer.from(response.body).toString("base64").slice(0, 64)}`
110
+ : undefined,
111
+ probeConfigRef: config.safeEndpoint,
112
+ createdAt: new Date().toISOString(),
113
+ },
114
+ httpStatus: response.status,
115
+ };
116
+ }
117
+ catch {
118
+ return {
119
+ probeResult: {
120
+ probeResultId: `probe_error:${platformId}:${capabilityId}:${Date.now()}`,
121
+ capabilityId,
122
+ connectorId: platformId,
123
+ actualStatus: "unavailable",
124
+ probeConfigRef: config.safeEndpoint,
125
+ createdAt: new Date().toISOString(),
126
+ },
127
+ httpStatus: 0,
128
+ };
129
+ }
130
+ },
131
+ };
132
+ }
@@ -20,6 +20,8 @@ export declare const capabilityDeclarationSchema: z.ZodObject<{
20
20
  id: z.ZodString;
21
21
  channel: z.ZodOptional<z.ZodString>;
22
22
  description: z.ZodOptional<z.ZodString>;
23
+ sourceRefs: z.ZodOptional<z.ZodArray<z.ZodString>>;
24
+ observedCount: z.ZodOptional<z.ZodNumber>;
23
25
  }, z.core.$strip>;
24
26
  export type ConnectorCapabilityDeclaration = z.infer<typeof capabilityDeclarationSchema>;
25
27
  export declare const runnerDeclarationSchema: z.ZodObject<{
@@ -72,6 +74,8 @@ export declare const connectorManifestV6Schema: z.ZodObject<{
72
74
  id: z.ZodString;
73
75
  channel: z.ZodOptional<z.ZodString>;
74
76
  description: z.ZodOptional<z.ZodString>;
77
+ sourceRefs: z.ZodOptional<z.ZodArray<z.ZodString>>;
78
+ observedCount: z.ZodOptional<z.ZodNumber>;
75
79
  }, z.core.$strip>>;
76
80
  runner: z.ZodObject<{
77
81
  kind: z.ZodEnum<{
@@ -18,6 +18,8 @@ export const capabilityDeclarationSchema = z.object({
18
18
  id: z.string().min(1),
19
19
  channel: z.string().optional(),
20
20
  description: z.string().optional(),
21
+ sourceRefs: z.array(z.string().min(1)).optional(),
22
+ observedCount: z.number().int().positive().optional(),
21
23
  });
22
24
  export const runnerDeclarationSchema = z.object({
23
25
  kind: connectorRunnerKindSchema,
@@ -12,5 +12,6 @@ import type { StateDatabase } from "../../storage/db/index.js";
12
12
  export interface ConnectorExecutorAdapterOptions {
13
13
  stateDb: StateDatabase;
14
14
  observabilityDb: ObservabilityDatabase;
15
+ workspaceRoot?: string;
15
16
  }
16
17
  export declare function createConnectorExecutorAdapter(options: ConnectorExecutorAdapterOptions): ConnectorExecutor;
@@ -12,11 +12,114 @@ import { createAgentWorldRunner } from "../agent-network/agent-world/adapter.js"
12
12
  import { ExecutionTelemetry } from "../../observability/services/execution-telemetry.js";
13
13
  import { createCredentialVault } from "../../storage/services/credential-vault.js";
14
14
  import { createCredentialRouteContextPort } from "./credential-route-context.js";
15
+ import { scanConnectorManifests } from "../registry/manifest-scanner.js";
16
+ import { parseConnectorManifestV6 } from "../manifest/manifest-parser.js";
17
+ const DEFAULT_AGENT_WORLD_USERNAME = "nyx_ha";
18
+ const DEFAULT_AGENT_WORLD_PROFILE_PATH_TEMPLATE = "/api/agents/profile/{username}";
19
+ function readString(value) {
20
+ return typeof value === "string" && value.trim().length > 0
21
+ ? value.trim()
22
+ : undefined;
23
+ }
24
+ function channelPriorityForRunner(manifest) {
25
+ const declared = manifest.capabilities
26
+ .map((capability) => capability.channel)
27
+ .filter((channel) => channel === "api_rest" ||
28
+ channel === "api_rpc" ||
29
+ channel === "a2a" ||
30
+ channel === "mcp" ||
31
+ channel === "cli" ||
32
+ channel === "skill" ||
33
+ channel === "browser");
34
+ if (declared.length > 0)
35
+ return [...new Set(declared)];
36
+ if (manifest.runner.kind === "declarative_a2a")
37
+ return ["a2a"];
38
+ if (manifest.runner.kind === "declarative_mcp")
39
+ return ["mcp"];
40
+ if (manifest.runner.kind === "cli_descriptor")
41
+ return ["cli"];
42
+ if (manifest.runner.kind === "skill")
43
+ return ["skill"];
44
+ if (manifest.runner.kind === "browser")
45
+ return ["browser"];
46
+ return ["api_rest"];
47
+ }
48
+ function registerWorkspaceManifests(registry, workspaceRoot) {
49
+ if (!workspaceRoot)
50
+ return;
51
+ for (const file of scanConnectorManifests(workspaceRoot)) {
52
+ const parsed = parseConnectorManifestV6(file.content, file.path);
53
+ if (!parsed.ok)
54
+ continue;
55
+ const manifest = parsed.manifest;
56
+ try {
57
+ registry.register({
58
+ platformId: manifest.platformId,
59
+ supportedCapabilities: manifest.capabilities.map((capability) => capability.id),
60
+ channelPriority: channelPriorityForRunner(manifest),
61
+ credentialTypes: manifest.credentials.map((credential) => credential.type),
62
+ sourceRefPolicy: manifest.sourceRefPolicy,
63
+ });
64
+ }
65
+ catch {
66
+ // Invalid workspace manifests remain visible through connector_status validation.
67
+ // Execution side keeps fail-closed behavior by not registering them here.
68
+ }
69
+ }
70
+ }
71
+ function resolveAgentWorldUsername(payload, purpose) {
72
+ const payloadUsername = (purpose === "discover" ? readString(payload.targetUsername) : undefined) ??
73
+ readString(payload.username) ??
74
+ readString(payload.agentUsername);
75
+ return (payloadUsername ??
76
+ readString(process.env.SECOND_NATURE_AGENT_WORLD_USERNAME) ??
77
+ DEFAULT_AGENT_WORLD_USERNAME);
78
+ }
79
+ function resolveAgentWorldProfilePath(payload, username) {
80
+ const template = readString(payload.profilePathTemplate) ??
81
+ readString(process.env.SECOND_NATURE_AGENT_WORLD_PROFILE_PATH_TEMPLATE) ??
82
+ DEFAULT_AGENT_WORLD_PROFILE_PATH_TEMPLATE;
83
+ return template.replaceAll("{username}", encodeURIComponent(username));
84
+ }
85
+ function joinAgentWorldUrl(baseUrl, path) {
86
+ if (/^https?:\/\//i.test(path))
87
+ return path;
88
+ return `${baseUrl.replace(/\/+$/, "")}/${path.replace(/^\/+/, "")}`;
89
+ }
90
+ async function fetchAgentWorldJson(input) {
91
+ const resp = await fetch(joinAgentWorldUrl(input.baseUrl, input.path), {
92
+ method: input.method ?? "GET",
93
+ headers: {
94
+ "Authorization": `Bearer ${input.apiKey}`,
95
+ "Content-Type": "application/json",
96
+ },
97
+ body: input.body === undefined ? undefined : JSON.stringify(input.body),
98
+ });
99
+ if (!resp.ok) {
100
+ throw { code: "api_error", detail: `agent-world ${input.label}: ${resp.status}` };
101
+ }
102
+ return resp.json();
103
+ }
15
104
  function createAdaptiveExecutionRunner(vault) {
16
105
  return {
17
106
  async run(_plan, request) {
18
107
  const platformId = request.platformId;
19
108
  const started = Date.now();
109
+ if (platformId !== "moltbook" &&
110
+ platformId !== "evomap" &&
111
+ platformId !== "agent-world") {
112
+ return {
113
+ platformId,
114
+ channel: request.preferredChannel ?? "api_rest",
115
+ latencyMs: Date.now() - started,
116
+ success: false,
117
+ error: {
118
+ code: "unknown_platform",
119
+ detail: `no execution runner for ${platformId}`,
120
+ },
121
+ };
122
+ }
20
123
  const credential = await vault.loadCredentialContext(platformId);
21
124
  if (!credential ||
22
125
  credential.status !== "active" ||
@@ -91,47 +194,48 @@ function createAdaptiveExecutionRunner(vault) {
91
194
  };
92
195
  }
93
196
  const runner = createAgentWorldRunner({
197
+ apiKey: credential.encryptedValue,
94
198
  apiClient: {
95
199
  async readFeed(payload, _apiKey) {
96
- const resp = await fetch(`${baseUrl}/api/v1/feed`, {
97
- headers: { "Authorization": `Bearer ${_apiKey}`, "Content-Type": "application/json" },
200
+ const username = resolveAgentWorldUsername(payload, "feed");
201
+ return fetchAgentWorldJson({
202
+ baseUrl,
203
+ path: resolveAgentWorldProfilePath(payload, username),
204
+ apiKey: _apiKey,
205
+ label: "profile feed",
98
206
  });
99
- if (!resp.ok)
100
- throw { code: "api_error", detail: `agent-world feed: ${resp.status}` };
101
- return resp.json();
102
207
  },
103
208
  async discoverWork(payload, _apiKey) {
104
- const resp = await fetch(`${baseUrl}/api/v1/work`, {
105
- headers: { "Authorization": `Bearer ${_apiKey}`, "Content-Type": "application/json" },
209
+ const username = resolveAgentWorldUsername(payload, "discover");
210
+ return fetchAgentWorldJson({
211
+ baseUrl,
212
+ path: resolveAgentWorldProfilePath(payload, username),
213
+ apiKey: _apiKey,
214
+ label: "profile discover",
106
215
  });
107
- if (!resp.ok)
108
- throw { code: "api_error", detail: `agent-world work: ${resp.status}` };
109
- return resp.json();
110
216
  },
111
217
  async claimTask(payload, _apiKey) {
112
- const resp = await fetch(`${baseUrl}/api/v1/tasks/${payload.taskId ?? "unknown"}/claim`, {
218
+ const claimPath = readString(payload.claimEndpointPath);
219
+ if (!claimPath) {
220
+ throw {
221
+ code: "protocol_mismatch",
222
+ detail: "agent_world_task_claim_endpoint_not_configured",
223
+ };
224
+ }
225
+ return fetchAgentWorldJson({
226
+ baseUrl,
227
+ path: claimPath,
228
+ apiKey: _apiKey,
113
229
  method: "POST",
114
- headers: { "Authorization": `Bearer ${_apiKey}`, "Content-Type": "application/json" },
115
- body: JSON.stringify(payload),
230
+ body: payload,
231
+ label: "task claim",
116
232
  });
117
- if (!resp.ok)
118
- throw { code: "api_error", detail: `agent-world claim: ${resp.status}` };
119
- return resp.json();
120
233
  },
121
234
  },
122
235
  });
123
236
  return runner.run(_plan, request);
124
237
  }
125
- return {
126
- platformId,
127
- channel: request.preferredChannel ?? "api_rest",
128
- latencyMs: Date.now() - started,
129
- success: false,
130
- error: {
131
- code: "unknown_platform",
132
- detail: `no execution runner for ${platformId}`,
133
- },
134
- };
238
+ throw new Error(`unreachable_connector_platform:${platformId}`);
135
239
  },
136
240
  };
137
241
  }
@@ -141,6 +245,7 @@ export function createConnectorExecutorAdapter(options) {
141
245
  registry.register({ ...moltbookManifest });
142
246
  registry.register({ ...evomapManifest });
143
247
  registry.register({ ...agentWorldManifest });
248
+ registerWorkspaceManifests(registry, options.workspaceRoot);
144
249
  const routeContextPort = createCredentialRouteContextPort(vault);
145
250
  const routePlanner = new ConnectorRoutePlanner(registry, routeContextPort, new ChannelHealthStore());
146
251
  const telemetry = new ExecutionTelemetry(options.observabilityDb);
@@ -154,6 +259,7 @@ export function createConnectorExecutorAdapter(options) {
154
259
  });
155
260
  return {
156
261
  async executeEffect(input) {
262
+ registerWorkspaceManifests(registry, options.workspaceRoot);
157
263
  return policy.executeWithPolicy(input.intent, {
158
264
  platformId: input.platformId,
159
265
  intent: input.intent,