@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.
- package/SKILL.md +35 -0
- package/agent-inner-guide.md +144 -0
- package/index.js +280 -2
- package/openclaw.plugin.json +2 -2
- package/package.json +4 -1
- package/runtime/cli/commands/connector-behavior.d.ts +20 -0
- package/runtime/cli/commands/connector-behavior.js +160 -0
- package/runtime/cli/commands/index.js +8 -0
- package/runtime/cli/index.js +9 -2
- package/runtime/cli/ops/manual-run-dispatcher.d.ts +79 -0
- package/runtime/cli/ops/manual-run-dispatcher.js +110 -0
- package/runtime/cli/ops/ops-router.d.ts +45 -4
- package/runtime/cli/ops/ops-router.js +543 -2
- package/runtime/cli/read-models/index.js +35 -18
- package/runtime/cli/read-models/types.d.ts +1 -0
- package/runtime/connectors/agent-network/agent-world/adapter.d.ts +1 -0
- package/runtime/connectors/agent-network/agent-world/adapter.js +2 -2
- package/runtime/connectors/base/contract.d.ts +4 -1
- package/runtime/connectors/base/contract.js +5 -1
- package/runtime/connectors/base/effect-commit-ledger-sqlite.d.ts +31 -0
- package/runtime/connectors/base/effect-commit-ledger-sqlite.js +86 -0
- package/runtime/connectors/base/failure-taxonomy.js +5 -0
- package/runtime/connectors/base/manifest-v7.d.ts +151 -0
- package/runtime/connectors/base/manifest-v7.js +170 -0
- package/runtime/connectors/base/manifest.d.ts +3 -13
- package/runtime/connectors/base/manifest.js +7 -7
- package/runtime/connectors/base/route-planner.js +11 -8
- package/runtime/connectors/base/structured-unavailable-reason.d.ts +59 -0
- package/runtime/connectors/base/structured-unavailable-reason.js +113 -0
- package/runtime/connectors/base/wet-probe-runner.d.ts +40 -0
- package/runtime/connectors/base/wet-probe-runner.js +132 -0
- package/runtime/connectors/manifest/manifest-schema.d.ts +4 -0
- package/runtime/connectors/manifest/manifest-schema.js +2 -0
- package/runtime/connectors/services/connector-executor-adapter.d.ts +1 -0
- package/runtime/connectors/services/connector-executor-adapter.js +132 -26
- package/runtime/core/second-nature/body/behavior-promotion/behavior-promotion-loop.d.ts +45 -0
- package/runtime/core/second-nature/body/behavior-promotion/behavior-promotion-loop.js +132 -0
- package/runtime/core/second-nature/body/circuit-breaker/circuit-breaker-manager.d.ts +60 -0
- package/runtime/core/second-nature/body/circuit-breaker/circuit-breaker-manager.js +174 -0
- package/runtime/core/second-nature/body/probe-signal-adapter.d.ts +38 -0
- package/runtime/core/second-nature/body/probe-signal-adapter.js +60 -0
- package/runtime/core/second-nature/body/tool-affordance/affordance-assembler.d.ts +51 -0
- package/runtime/core/second-nature/body/tool-affordance/affordance-assembler.js +129 -0
- package/runtime/core/second-nature/body/tool-affordance/affordance-context-scope.d.ts +30 -0
- package/runtime/core/second-nature/body/tool-affordance/affordance-context-scope.js +92 -0
- package/runtime/core/second-nature/body/tool-experience/experience-writer.d.ts +34 -0
- package/runtime/core/second-nature/body/tool-experience/experience-writer.js +67 -0
- package/runtime/core/second-nature/body/tool-experience/pain-signal-query.d.ts +37 -0
- package/runtime/core/second-nature/body/tool-experience/pain-signal-query.js +62 -0
- package/runtime/core/second-nature/heartbeat/decision-trace-emitter.d.ts +29 -0
- package/runtime/core/second-nature/heartbeat/decision-trace-emitter.js +28 -0
- package/runtime/core/second-nature/heartbeat/embodied-context-assembler.d.ts +54 -0
- package/runtime/core/second-nature/heartbeat/embodied-context-assembler.js +164 -0
- package/runtime/core/second-nature/heartbeat/goal-lifecycle-policy.d.ts +37 -0
- package/runtime/core/second-nature/heartbeat/goal-lifecycle-policy.js +61 -0
- package/runtime/core/second-nature/heartbeat/idle-curiosity-policy.d.ts +37 -0
- package/runtime/core/second-nature/heartbeat/idle-curiosity-policy.js +60 -0
- package/runtime/core/second-nature/heartbeat/index.d.ts +4 -0
- package/runtime/core/second-nature/heartbeat/index.js +5 -0
- package/runtime/core/second-nature/heartbeat/run-heartbeat-cycle-v7.d.ts +63 -0
- package/runtime/core/second-nature/heartbeat/run-heartbeat-cycle-v7.js +118 -0
- package/runtime/core/second-nature/orchestrator/downstream-intent-orchestrator.d.ts +41 -0
- package/runtime/core/second-nature/orchestrator/downstream-intent-orchestrator.js +43 -0
- package/runtime/core/second-nature/orchestrator/effect-dispatcher.d.ts +2 -1
- package/runtime/core/second-nature/orchestrator/effect-dispatcher.js +2 -0
- package/runtime/core/second-nature/orchestrator/hard-guard-evaluator.d.ts +31 -0
- package/runtime/core/second-nature/orchestrator/hard-guard-evaluator.js +102 -0
- package/runtime/core/second-nature/orchestrator/index.d.ts +5 -0
- package/runtime/core/second-nature/orchestrator/index.js +7 -0
- package/runtime/core/second-nature/quiet/claim-synthesizer.d.ts +53 -0
- package/runtime/core/second-nature/quiet/claim-synthesizer.js +153 -0
- package/runtime/core/second-nature/quiet/daily-diary-writer.d.ts +29 -0
- package/runtime/core/second-nature/quiet/daily-diary-writer.js +92 -0
- package/runtime/core/second-nature/quiet/index.d.ts +5 -0
- package/runtime/core/second-nature/quiet/index.js +5 -0
- package/runtime/core/second-nature/quiet/run-source-backed-quiet.js +19 -12
- package/runtime/core/second-nature/types.d.ts +2 -0
- package/runtime/guidance/channel-feedback-ingestion-service.d.ts +88 -0
- package/runtime/guidance/channel-feedback-ingestion-service.js +231 -0
- package/runtime/guidance/guidance-draft-service.d.ts +60 -0
- package/runtime/guidance/guidance-draft-service.js +80 -0
- package/runtime/guidance/index.d.ts +3 -0
- package/runtime/guidance/index.js +3 -0
- package/runtime/guidance/outreach-draft-schema.d.ts +8 -8
- package/runtime/guidance/outreach-strategy-selector.d.ts +77 -0
- package/runtime/guidance/outreach-strategy-selector.js +211 -0
- package/runtime/observability/audit/append-only-audit-store.d.ts +20 -2
- package/runtime/observability/audit/append-only-audit-store.js +32 -6
- package/runtime/observability/audit/audit-envelope.d.ts +2 -1
- package/runtime/observability/audit/audit-envelope.js +8 -7
- package/runtime/observability/audit/audit-family-registry.json +66 -0
- package/runtime/observability/audit/family-registry.d.ts +43 -0
- package/runtime/observability/audit/family-registry.js +70 -0
- package/runtime/observability/index.d.ts +6 -1
- package/runtime/observability/index.js +6 -1
- package/runtime/observability/redaction/policy.d.ts +24 -3
- package/runtime/observability/redaction/policy.js +74 -0
- package/runtime/observability/services/heartbeat-digest-assembler.d.ts +152 -0
- package/runtime/observability/services/heartbeat-digest-assembler.js +248 -0
- package/runtime/observability/services/lived-experience-audit.js +6 -6
- package/runtime/observability/services/narrative-timeline-query-service.d.ts +136 -0
- package/runtime/observability/services/narrative-timeline-query-service.js +169 -0
- package/runtime/observability/services/restore-audit-service.d.ts +74 -0
- package/runtime/observability/services/restore-audit-service.js +79 -0
- package/runtime/observability/services/runtime-secret-anchor-view.d.ts +77 -0
- package/runtime/observability/services/runtime-secret-anchor-view.js +168 -0
- package/runtime/observability/services/self-health-snapshot.d.ts +92 -0
- package/runtime/observability/services/self-health-snapshot.js +251 -0
- package/runtime/shared/types/goal.d.ts +62 -0
- package/runtime/shared/types/goal.js +20 -0
- package/runtime/shared/types/index.d.ts +3 -0
- package/runtime/shared/types/index.js +3 -0
- package/runtime/shared/types/source-ref.d.ts +14 -0
- package/runtime/shared/types/source-ref.js +1 -0
- package/runtime/shared/types/v7-entities.d.ts +206 -0
- package/runtime/shared/types/v7-entities.js +27 -0
- package/runtime/storage/db/index.js +3 -0
- package/runtime/storage/db/migration-runner.d.ts +30 -0
- package/runtime/storage/db/migration-runner.js +93 -0
- package/runtime/storage/db/migrations/index.d.ts +5 -0
- package/runtime/storage/db/migrations/index.js +13 -0
- package/runtime/storage/db/migrations/v7-001-foundation.d.ts +13 -0
- package/runtime/storage/db/migrations/v7-001-foundation.js +144 -0
- package/runtime/storage/db/migrations/v7-002-effect-commit-ledger.d.ts +8 -0
- package/runtime/storage/db/migrations/v7-002-effect-commit-ledger.js +27 -0
- package/runtime/storage/db/migrations/v7-003-circuit-breaker.d.ts +7 -0
- package/runtime/storage/db/migrations/v7-003-circuit-breaker.js +26 -0
- package/runtime/storage/db/migrations/v7-004-behavior-promotion.d.ts +7 -0
- package/runtime/storage/db/migrations/v7-004-behavior-promotion.js +26 -0
- package/runtime/storage/db/schema/agent-goal.d.ts +38 -0
- package/runtime/storage/db/schema/agent-goal.js +2 -0
- package/runtime/storage/db/transaction-utils.d.ts +14 -0
- package/runtime/storage/db/transaction-utils.js +29 -0
- package/runtime/storage/db/write-queue.d.ts +38 -0
- package/runtime/storage/db/write-queue.js +97 -0
- package/runtime/storage/quiet/persist-quiet-artifact.js +2 -1
- package/runtime/storage/services/credential-vault.js +31 -17
- package/runtime/storage/services/diary-dream-store.d.ts +35 -0
- package/runtime/storage/services/diary-dream-store.js +165 -0
- package/runtime/storage/services/embodied-context-state-port.d.ts +77 -0
- package/runtime/storage/services/embodied-context-state-port.js +115 -0
- package/runtime/storage/services/goal-lifecycle-store.d.ts +42 -0
- package/runtime/storage/services/goal-lifecycle-store.js +181 -0
- package/runtime/storage/services/history-digest-store.d.ts +33 -0
- package/runtime/storage/services/history-digest-store.js +140 -0
- package/runtime/storage/services/identity-profile-store.d.ts +25 -0
- package/runtime/storage/services/identity-profile-store.js +81 -0
- package/runtime/storage/services/interaction-snapshot-projector.d.ts +15 -0
- package/runtime/storage/services/interaction-snapshot-projector.js +35 -0
- package/runtime/storage/services/restore-snapshot-store.d.ts +52 -0
- package/runtime/storage/services/restore-snapshot-store.js +193 -0
- package/runtime/storage/services/runtime-secret-anchor-store.d.ts +26 -0
- package/runtime/storage/services/runtime-secret-anchor-store.js +82 -0
- package/runtime/storage/services/tool-experience-store.d.ts +25 -0
- package/runtime/storage/services/tool-experience-store.js +116 -0
- package/runtime/storage/services/write-validation-gate.d.ts +46 -0
- package/runtime/storage/services/write-validation-gate.js +200 -0
- package/workspace-ops-bridge.js +16 -1
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RuntimeSecretAnchorView — T-OBS.C.7
|
|
3
|
+
*
|
|
4
|
+
* Core logic:
|
|
5
|
+
* viewSecretAnchor() probes the encryption key anchor and returns a
|
|
6
|
+
* RuntimeSecretAnchorView that describes the current health status.
|
|
7
|
+
*
|
|
8
|
+
* Three detection scenarios (DR-034):
|
|
9
|
+
* 1. Key path missing / env var not set → status "missing"
|
|
10
|
+
* reasonCode: "runtime_secret_anchor_missing"
|
|
11
|
+
* 2. Key path present but sample decrypt fails with wrong-key signal
|
|
12
|
+
* reasonCode: "credential_recovery_required"
|
|
13
|
+
* 3. Key path present but decrypt call throws / unrecoverable error
|
|
14
|
+
* reasonCode: "runtime_secret_unavailable"
|
|
15
|
+
*
|
|
16
|
+
* RecoveryStep[] is always inline in the view (DR-034).
|
|
17
|
+
* Key plaintext is NEVER stored or returned (ADR-007).
|
|
18
|
+
* Only the key path (env var name / file path) is included.
|
|
19
|
+
*
|
|
20
|
+
* Test coverage: tests/unit/observability/runtime-secret-anchor-view.test.ts
|
|
21
|
+
*/
|
|
22
|
+
// ─── Recovery step templates ─────────────────────────────────────────────────
|
|
23
|
+
const RECOVERY_STEPS_MISSING = [
|
|
24
|
+
{
|
|
25
|
+
step: 1,
|
|
26
|
+
action: "Locate your encryption key from a secure vault or backup (see AGENTS.md §Bootstrap Recovery).",
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
step: 2,
|
|
30
|
+
action: "Set the environment variable specified in keyPath to the correct key value.",
|
|
31
|
+
command: "export SECOND_NATURE_ENCRYPTION_KEY=<your-key>",
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
step: 3,
|
|
35
|
+
action: "Restart the agent process or reload the workspace to pick up the new environment variable.",
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
step: 4,
|
|
39
|
+
action: "Run `self_health` to confirm the anchor status changes to verified.",
|
|
40
|
+
},
|
|
41
|
+
];
|
|
42
|
+
const RECOVERY_STEPS_WRONG_KEY = [
|
|
43
|
+
{
|
|
44
|
+
step: 1,
|
|
45
|
+
action: "The environment variable is set but the key does not match stored credentials. Retrieve the correct key from your vault.",
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
step: 2,
|
|
49
|
+
action: "Replace the current environment variable value with the correct key.",
|
|
50
|
+
command: "export SECOND_NATURE_ENCRYPTION_KEY=<correct-key>",
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
step: 3,
|
|
54
|
+
action: "If the correct key is unavailable, initiate credential re-encryption with the new key (see AGENTS.md §Credential Rotation).",
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
step: 4,
|
|
58
|
+
action: "Run `self_health` to verify the anchor resolves to verified.",
|
|
59
|
+
},
|
|
60
|
+
];
|
|
61
|
+
const RECOVERY_STEPS_UNAVAILABLE = [
|
|
62
|
+
{
|
|
63
|
+
step: 1,
|
|
64
|
+
action: "The key path exists but the encryption subsystem encountered an unexpected error. Check agent logs for details.",
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
step: 2,
|
|
68
|
+
action: "Ensure no other process is locking the key file or environment context.",
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
step: 3,
|
|
72
|
+
action: "If the error persists, rotate the key following the procedure in AGENTS.md §Credential Rotation.",
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
step: 4,
|
|
76
|
+
action: "Run `self_health` after remediation to confirm resolution.",
|
|
77
|
+
},
|
|
78
|
+
];
|
|
79
|
+
const RECOVERY_STEPS_VERIFIED = [];
|
|
80
|
+
// ─── Public API ──────────────────────────────────────────────────────────────
|
|
81
|
+
/**
|
|
82
|
+
* Probe the encryption key anchor and return a safe view.
|
|
83
|
+
*
|
|
84
|
+
* Guarantees:
|
|
85
|
+
* - keyPath is a path string (env var name or file path), never a value.
|
|
86
|
+
* - No field in the returned object contains the key plaintext.
|
|
87
|
+
* - recoverySteps is always populated when status ≠ "verified".
|
|
88
|
+
*/
|
|
89
|
+
export async function viewSecretAnchor(deps) {
|
|
90
|
+
const now = (deps.now ?? (() => new Date().toISOString()))();
|
|
91
|
+
const keyPath = deps.runtimeOpsPort.getEncryptionKeyPath();
|
|
92
|
+
// Step 1: check whether the key path exists
|
|
93
|
+
let keyExists;
|
|
94
|
+
try {
|
|
95
|
+
keyExists = await deps.runtimeOpsPort.checkKeyPathExists(keyPath);
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
// checkKeyPathExists itself threw — treat as missing
|
|
99
|
+
keyExists = false;
|
|
100
|
+
}
|
|
101
|
+
if (!keyExists) {
|
|
102
|
+
return {
|
|
103
|
+
anchorId: "primary",
|
|
104
|
+
keyPath,
|
|
105
|
+
status: "missing",
|
|
106
|
+
lastCheckedAt: now,
|
|
107
|
+
recoveryDocRef: "AGENTS.md#bootstrap-recovery",
|
|
108
|
+
recoverySteps: RECOVERY_STEPS_MISSING,
|
|
109
|
+
reasonCode: "runtime_secret_anchor_missing",
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
// Step 2: try a sample decrypt to validate the key is correct
|
|
113
|
+
let sampleResult;
|
|
114
|
+
try {
|
|
115
|
+
sampleResult = await deps.credentialPort.verifySampleDecrypt();
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
// verifySampleDecrypt threw — key exists but subsystem is broken
|
|
119
|
+
return {
|
|
120
|
+
anchorId: "primary",
|
|
121
|
+
keyPath,
|
|
122
|
+
status: "decryption_failed",
|
|
123
|
+
lastCheckedAt: now,
|
|
124
|
+
recoveryDocRef: "AGENTS.md#bootstrap-recovery",
|
|
125
|
+
rotationSchedule: "on workspace migration or manual rotation request",
|
|
126
|
+
checkedCredentialIds: [],
|
|
127
|
+
recoverySteps: RECOVERY_STEPS_UNAVAILABLE,
|
|
128
|
+
reasonCode: "runtime_secret_unavailable",
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
if (sampleResult.status === "ok") {
|
|
132
|
+
return {
|
|
133
|
+
anchorId: "primary",
|
|
134
|
+
keyPath,
|
|
135
|
+
status: "verified",
|
|
136
|
+
lastCheckedAt: now,
|
|
137
|
+
recoveryDocRef: "AGENTS.md#bootstrap-recovery",
|
|
138
|
+
rotationSchedule: "on workspace migration or manual rotation request",
|
|
139
|
+
checkedCredentialIds: sampleResult.checkedIds,
|
|
140
|
+
recoverySteps: RECOVERY_STEPS_VERIFIED,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
if (sampleResult.status === "wrong_key") {
|
|
144
|
+
return {
|
|
145
|
+
anchorId: "primary",
|
|
146
|
+
keyPath,
|
|
147
|
+
status: "wrong_key",
|
|
148
|
+
lastCheckedAt: now,
|
|
149
|
+
recoveryDocRef: "AGENTS.md#bootstrap-recovery",
|
|
150
|
+
rotationSchedule: "on workspace migration or manual rotation request",
|
|
151
|
+
checkedCredentialIds: sampleResult.checkedIds,
|
|
152
|
+
recoverySteps: RECOVERY_STEPS_WRONG_KEY,
|
|
153
|
+
reasonCode: "credential_recovery_required",
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
// sampleResult.status === "error"
|
|
157
|
+
return {
|
|
158
|
+
anchorId: "primary",
|
|
159
|
+
keyPath,
|
|
160
|
+
status: "decryption_failed",
|
|
161
|
+
lastCheckedAt: now,
|
|
162
|
+
recoveryDocRef: "AGENTS.md#bootstrap-recovery",
|
|
163
|
+
rotationSchedule: "on workspace migration or manual rotation request",
|
|
164
|
+
checkedCredentialIds: sampleResult.checkedIds,
|
|
165
|
+
recoverySteps: RECOVERY_STEPS_UNAVAILABLE,
|
|
166
|
+
reasonCode: "runtime_secret_unavailable",
|
|
167
|
+
};
|
|
168
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SelfHealthSnapshot — T-OBS.C.2
|
|
3
|
+
*
|
|
4
|
+
* Core logic: Run independent health probes for each registered dimension,
|
|
5
|
+
* each with per-probe timeout (DR-036). Total cap: 3000ms via Promise.allSettled.
|
|
6
|
+
* Returns SelfHealthSnapshot with overall status and per-dimension results.
|
|
7
|
+
*
|
|
8
|
+
* DR-036 timeouts:
|
|
9
|
+
* env / storage 200ms → unknown + probe_timeout:env
|
|
10
|
+
* cron / bridge 500ms → unknown + probe_timeout:cron
|
|
11
|
+
* secret / credential 1000ms → unknown + probe_timeout:secret
|
|
12
|
+
* delivery / circuit 800ms → unknown + probe_timeout:delivery
|
|
13
|
+
* state_memory 500ms → degraded + state_memory_unavailable
|
|
14
|
+
*
|
|
15
|
+
* DR-032 circular dependency degradation:
|
|
16
|
+
* If state-memory is unavailable, narrative_timeline and digest probes
|
|
17
|
+
* return degraded — other probes are unaffected.
|
|
18
|
+
*
|
|
19
|
+
* Total timeout:
|
|
20
|
+
* All probes run concurrently via Promise.allSettled, capped at 3000ms.
|
|
21
|
+
* If ALL probes time out: overall = "unknown", reason = "all_probes_timed_out".
|
|
22
|
+
*
|
|
23
|
+
* Dynamic dimension registration:
|
|
24
|
+
* Callers can register additional probe functions via registerHealthProbe().
|
|
25
|
+
* Minimum required dimensions are always included.
|
|
26
|
+
*
|
|
27
|
+
* Test coverage: tests/unit/observability/self-health-snapshot.test.ts
|
|
28
|
+
*/
|
|
29
|
+
export type HealthStatus = "healthy" | "degraded" | "unknown";
|
|
30
|
+
export interface DimensionHealth {
|
|
31
|
+
status: HealthStatus;
|
|
32
|
+
reason?: string;
|
|
33
|
+
checkedAt: string;
|
|
34
|
+
lastKnownAt?: string;
|
|
35
|
+
}
|
|
36
|
+
export interface SelfHealthSnapshot {
|
|
37
|
+
generatedAt: string;
|
|
38
|
+
overall: HealthStatus;
|
|
39
|
+
/** Populated if overall = "unknown" due to all probes timing out */
|
|
40
|
+
reason?: string;
|
|
41
|
+
/** Timestamp of last successful snapshot (for all-timeout fallback) */
|
|
42
|
+
lastKnownAt?: string;
|
|
43
|
+
dimensions: Record<string, DimensionHealth>;
|
|
44
|
+
diagnosticReasonCodes: string[];
|
|
45
|
+
/** Dimension IDs that are degraded or unknown */
|
|
46
|
+
degradedDimensions: string[];
|
|
47
|
+
}
|
|
48
|
+
/** A probe function: resolves with DimensionHealth or throws on error */
|
|
49
|
+
export type HealthProbeFunction = () => Promise<DimensionHealth>;
|
|
50
|
+
export interface RegisteredProbe {
|
|
51
|
+
dimensionId: string;
|
|
52
|
+
probe: HealthProbeFunction;
|
|
53
|
+
timeoutMs: number;
|
|
54
|
+
/** If true, timeout returns "degraded" instead of "unknown" (DR-032 state-memory path) */
|
|
55
|
+
timeoutAsDegraded?: boolean;
|
|
56
|
+
}
|
|
57
|
+
export declare const MINIMUM_REQUIRED_DIMENSIONS: string[];
|
|
58
|
+
/**
|
|
59
|
+
* Register a health probe for a named dimension.
|
|
60
|
+
* Built-in minimum dimensions are registered by default with no-op probes
|
|
61
|
+
* that return healthy. Callers override with real probe functions.
|
|
62
|
+
*/
|
|
63
|
+
export declare function registerHealthProbe(probe: RegisteredProbe): void;
|
|
64
|
+
/**
|
|
65
|
+
* Remove a probe from the registry (useful for testing).
|
|
66
|
+
*/
|
|
67
|
+
export declare function unregisterHealthProbe(dimensionId: string): void;
|
|
68
|
+
/**
|
|
69
|
+
* Clear all registered probes (useful for testing).
|
|
70
|
+
*/
|
|
71
|
+
export declare function clearHealthProbeRegistry(): void;
|
|
72
|
+
/** Get all currently registered probes. */
|
|
73
|
+
export declare function getRegisteredProbes(): RegisteredProbe[];
|
|
74
|
+
/**
|
|
75
|
+
* Ensure minimum required dimensions are represented in the registry.
|
|
76
|
+
* Existing registrations (real probes) are not overwritten.
|
|
77
|
+
*/
|
|
78
|
+
export declare function ensureMinimumProbes(): void;
|
|
79
|
+
export interface HealthProbeScope {
|
|
80
|
+
/** If specified, only run probes for these dimension IDs */
|
|
81
|
+
dimensions?: string[];
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Run all registered health probes and assemble SelfHealthSnapshot.
|
|
85
|
+
*
|
|
86
|
+
* Probes run concurrently (Promise.allSettled) with per-probe timeouts (DR-036).
|
|
87
|
+
* Total cap: 3000ms.
|
|
88
|
+
* All-timeout fallback: overall = "unknown", reason = "all_probes_timed_out".
|
|
89
|
+
*
|
|
90
|
+
* DR-032: state_memory probe timeout marks narrative_timeline + digest as degraded too.
|
|
91
|
+
*/
|
|
92
|
+
export declare function getSelfHealthSnapshot(scope?: HealthProbeScope, lastKnownAt?: string): Promise<SelfHealthSnapshot>;
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SelfHealthSnapshot — T-OBS.C.2
|
|
3
|
+
*
|
|
4
|
+
* Core logic: Run independent health probes for each registered dimension,
|
|
5
|
+
* each with per-probe timeout (DR-036). Total cap: 3000ms via Promise.allSettled.
|
|
6
|
+
* Returns SelfHealthSnapshot with overall status and per-dimension results.
|
|
7
|
+
*
|
|
8
|
+
* DR-036 timeouts:
|
|
9
|
+
* env / storage 200ms → unknown + probe_timeout:env
|
|
10
|
+
* cron / bridge 500ms → unknown + probe_timeout:cron
|
|
11
|
+
* secret / credential 1000ms → unknown + probe_timeout:secret
|
|
12
|
+
* delivery / circuit 800ms → unknown + probe_timeout:delivery
|
|
13
|
+
* state_memory 500ms → degraded + state_memory_unavailable
|
|
14
|
+
*
|
|
15
|
+
* DR-032 circular dependency degradation:
|
|
16
|
+
* If state-memory is unavailable, narrative_timeline and digest probes
|
|
17
|
+
* return degraded — other probes are unaffected.
|
|
18
|
+
*
|
|
19
|
+
* Total timeout:
|
|
20
|
+
* All probes run concurrently via Promise.allSettled, capped at 3000ms.
|
|
21
|
+
* If ALL probes time out: overall = "unknown", reason = "all_probes_timed_out".
|
|
22
|
+
*
|
|
23
|
+
* Dynamic dimension registration:
|
|
24
|
+
* Callers can register additional probe functions via registerHealthProbe().
|
|
25
|
+
* Minimum required dimensions are always included.
|
|
26
|
+
*
|
|
27
|
+
* Test coverage: tests/unit/observability/self-health-snapshot.test.ts
|
|
28
|
+
*/
|
|
29
|
+
// ─── DR-036 Timeout Constants ─────────────────────────────────────────────────
|
|
30
|
+
const PROBE_TIMEOUTS = {
|
|
31
|
+
env: 200,
|
|
32
|
+
storage: 200,
|
|
33
|
+
cron: 500,
|
|
34
|
+
bridge: 500,
|
|
35
|
+
secret: 1000,
|
|
36
|
+
credential: 1000,
|
|
37
|
+
delivery: 800,
|
|
38
|
+
circuit_breaker: 800,
|
|
39
|
+
dream: 800,
|
|
40
|
+
state_memory: 500,
|
|
41
|
+
narrative_timeline: 500,
|
|
42
|
+
digest: 500,
|
|
43
|
+
};
|
|
44
|
+
const STATE_MEMORY_DEGRADED_DIMENSIONS = new Set([
|
|
45
|
+
"state_memory",
|
|
46
|
+
"narrative_timeline",
|
|
47
|
+
"digest",
|
|
48
|
+
]);
|
|
49
|
+
const TOTAL_TIMEOUT_MS = 3000;
|
|
50
|
+
// ─── Minimum required dimensions ─────────────────────────────────────────────
|
|
51
|
+
export const MINIMUM_REQUIRED_DIMENSIONS = [
|
|
52
|
+
"env",
|
|
53
|
+
"cron",
|
|
54
|
+
"secret",
|
|
55
|
+
"credential",
|
|
56
|
+
"storage",
|
|
57
|
+
"delivery",
|
|
58
|
+
"dream",
|
|
59
|
+
"bridge",
|
|
60
|
+
"circuit_breaker",
|
|
61
|
+
"state_memory",
|
|
62
|
+
];
|
|
63
|
+
// ─── Registry ─────────────────────────────────────────────────────────────────
|
|
64
|
+
const _probeRegistry = new Map();
|
|
65
|
+
/**
|
|
66
|
+
* Register a health probe for a named dimension.
|
|
67
|
+
* Built-in minimum dimensions are registered by default with no-op probes
|
|
68
|
+
* that return healthy. Callers override with real probe functions.
|
|
69
|
+
*/
|
|
70
|
+
export function registerHealthProbe(probe) {
|
|
71
|
+
_probeRegistry.set(probe.dimensionId, probe);
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Remove a probe from the registry (useful for testing).
|
|
75
|
+
*/
|
|
76
|
+
export function unregisterHealthProbe(dimensionId) {
|
|
77
|
+
_probeRegistry.delete(dimensionId);
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Clear all registered probes (useful for testing).
|
|
81
|
+
*/
|
|
82
|
+
export function clearHealthProbeRegistry() {
|
|
83
|
+
_probeRegistry.clear();
|
|
84
|
+
}
|
|
85
|
+
/** Get all currently registered probes. */
|
|
86
|
+
export function getRegisteredProbes() {
|
|
87
|
+
return Array.from(_probeRegistry.values());
|
|
88
|
+
}
|
|
89
|
+
// ─── Default probes for minimum dimensions ───────────────────────────────────
|
|
90
|
+
function makeDefaultProbe(dimensionId) {
|
|
91
|
+
const timeoutMs = PROBE_TIMEOUTS[dimensionId] ?? 500;
|
|
92
|
+
const timeoutAsDegraded = STATE_MEMORY_DEGRADED_DIMENSIONS.has(dimensionId);
|
|
93
|
+
return {
|
|
94
|
+
dimensionId,
|
|
95
|
+
timeoutMs,
|
|
96
|
+
timeoutAsDegraded,
|
|
97
|
+
probe: async () => ({
|
|
98
|
+
status: "healthy",
|
|
99
|
+
checkedAt: new Date().toISOString(),
|
|
100
|
+
}),
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Ensure minimum required dimensions are represented in the registry.
|
|
105
|
+
* Existing registrations (real probes) are not overwritten.
|
|
106
|
+
*/
|
|
107
|
+
export function ensureMinimumProbes() {
|
|
108
|
+
for (const dim of MINIMUM_REQUIRED_DIMENSIONS) {
|
|
109
|
+
if (!_probeRegistry.has(dim)) {
|
|
110
|
+
_probeRegistry.set(dim, makeDefaultProbe(dim));
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
// ─── Probe runner ─────────────────────────────────────────────────────────────
|
|
115
|
+
/**
|
|
116
|
+
* Run a single probe with per-probe timeout.
|
|
117
|
+
* On timeout: returns unknown (or degraded for state_memory path, DR-032).
|
|
118
|
+
* On error: returns unknown + error message.
|
|
119
|
+
*/
|
|
120
|
+
async function runProbeWithTimeout(rp, now) {
|
|
121
|
+
const timeoutStatus = rp.timeoutAsDegraded ? "degraded" : "unknown";
|
|
122
|
+
const timeoutReason = STATE_MEMORY_DEGRADED_DIMENSIONS.has(rp.dimensionId)
|
|
123
|
+
? "state_memory_unavailable"
|
|
124
|
+
: `probe_timeout:${rp.dimensionId}`;
|
|
125
|
+
const timeoutPromise = new Promise((resolve) => {
|
|
126
|
+
setTimeout(() => {
|
|
127
|
+
resolve({
|
|
128
|
+
status: timeoutStatus,
|
|
129
|
+
reason: timeoutReason,
|
|
130
|
+
checkedAt: now,
|
|
131
|
+
});
|
|
132
|
+
}, rp.timeoutMs);
|
|
133
|
+
});
|
|
134
|
+
const probePromise = rp.probe().catch((err) => ({
|
|
135
|
+
status: "unknown",
|
|
136
|
+
reason: `probe_error:${rp.dimensionId}:${err instanceof Error ? err.message : String(err)}`,
|
|
137
|
+
checkedAt: now,
|
|
138
|
+
}));
|
|
139
|
+
return [rp.dimensionId, await Promise.race([probePromise, timeoutPromise])];
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Run all registered health probes and assemble SelfHealthSnapshot.
|
|
143
|
+
*
|
|
144
|
+
* Probes run concurrently (Promise.allSettled) with per-probe timeouts (DR-036).
|
|
145
|
+
* Total cap: 3000ms.
|
|
146
|
+
* All-timeout fallback: overall = "unknown", reason = "all_probes_timed_out".
|
|
147
|
+
*
|
|
148
|
+
* DR-032: state_memory probe timeout marks narrative_timeline + digest as degraded too.
|
|
149
|
+
*/
|
|
150
|
+
export async function getSelfHealthSnapshot(scope, lastKnownAt) {
|
|
151
|
+
// Ensure minimum probes exist
|
|
152
|
+
ensureMinimumProbes();
|
|
153
|
+
const now = new Date().toISOString();
|
|
154
|
+
// Determine which probes to run
|
|
155
|
+
const allProbes = Array.from(_probeRegistry.values());
|
|
156
|
+
const probes = scope?.dimensions
|
|
157
|
+
? allProbes.filter((p) => scope.dimensions.includes(p.dimensionId))
|
|
158
|
+
: allProbes;
|
|
159
|
+
if (probes.length === 0) {
|
|
160
|
+
return {
|
|
161
|
+
generatedAt: now,
|
|
162
|
+
overall: "unknown",
|
|
163
|
+
reason: "no_probes_registered",
|
|
164
|
+
lastKnownAt,
|
|
165
|
+
dimensions: {},
|
|
166
|
+
diagnosticReasonCodes: ["no_probes_registered"],
|
|
167
|
+
degradedDimensions: [],
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
// Run all probes with individual timeouts, wrapped in a 3s global cap
|
|
171
|
+
const globalTimeoutPromise = new Promise((resolve) => {
|
|
172
|
+
setTimeout(() => {
|
|
173
|
+
// Return all probes as unknown on global timeout
|
|
174
|
+
const fallback = probes.map((p) => [
|
|
175
|
+
p.dimensionId,
|
|
176
|
+
{
|
|
177
|
+
status: "unknown",
|
|
178
|
+
reason: `probe_timeout:${p.dimensionId}`,
|
|
179
|
+
checkedAt: now,
|
|
180
|
+
},
|
|
181
|
+
]);
|
|
182
|
+
resolve(fallback);
|
|
183
|
+
}, TOTAL_TIMEOUT_MS);
|
|
184
|
+
});
|
|
185
|
+
const probeRunnerPromise = Promise.allSettled(probes.map((p) => runProbeWithTimeout(p, now))).then((results) => results
|
|
186
|
+
.filter((r) => r.status === "fulfilled")
|
|
187
|
+
.map((r) => r.value));
|
|
188
|
+
const results = await Promise.race([probeRunnerPromise, globalTimeoutPromise]);
|
|
189
|
+
// Assemble dimensions map
|
|
190
|
+
const dimensions = {};
|
|
191
|
+
for (const [dimId, health] of results) {
|
|
192
|
+
dimensions[dimId] = health;
|
|
193
|
+
}
|
|
194
|
+
// DR-032: if state_memory is degraded/unknown, propagate to narrative_timeline + digest
|
|
195
|
+
const stateMemoryHealth = dimensions["state_memory"];
|
|
196
|
+
if (stateMemoryHealth &&
|
|
197
|
+
(stateMemoryHealth.status === "degraded" || stateMemoryHealth.status === "unknown")) {
|
|
198
|
+
const smReason = "state_memory_unavailable";
|
|
199
|
+
for (const dim of ["narrative_timeline", "digest"]) {
|
|
200
|
+
if (dimensions[dim] && dimensions[dim].status === "healthy") {
|
|
201
|
+
dimensions[dim] = {
|
|
202
|
+
status: "degraded",
|
|
203
|
+
reason: smReason,
|
|
204
|
+
checkedAt: now,
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
else if (!dimensions[dim] && _probeRegistry.has(dim)) {
|
|
208
|
+
dimensions[dim] = {
|
|
209
|
+
status: "degraded",
|
|
210
|
+
reason: smReason,
|
|
211
|
+
checkedAt: now,
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
// Collect diagnostic reason codes and degraded dimensions
|
|
217
|
+
const diagnosticReasonCodes = [];
|
|
218
|
+
const degradedDimensions = [];
|
|
219
|
+
for (const [dimId, health] of Object.entries(dimensions)) {
|
|
220
|
+
if (health.status !== "healthy") {
|
|
221
|
+
degradedDimensions.push(dimId);
|
|
222
|
+
if (health.reason)
|
|
223
|
+
diagnosticReasonCodes.push(health.reason);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
// Compute overall status
|
|
227
|
+
const allUnknown = Object.values(dimensions).every((d) => d.status === "unknown");
|
|
228
|
+
const anyDegraded = Object.values(dimensions).some((d) => d.status === "degraded");
|
|
229
|
+
const anyUnknown = Object.values(dimensions).some((d) => d.status === "unknown");
|
|
230
|
+
let overall;
|
|
231
|
+
let reason;
|
|
232
|
+
if (allUnknown) {
|
|
233
|
+
overall = "unknown";
|
|
234
|
+
reason = "all_probes_timed_out";
|
|
235
|
+
}
|
|
236
|
+
else if (anyDegraded || anyUnknown) {
|
|
237
|
+
overall = "degraded";
|
|
238
|
+
}
|
|
239
|
+
else {
|
|
240
|
+
overall = "healthy";
|
|
241
|
+
}
|
|
242
|
+
return {
|
|
243
|
+
generatedAt: now,
|
|
244
|
+
overall,
|
|
245
|
+
reason,
|
|
246
|
+
lastKnownAt: allUnknown ? lastKnownAt : undefined,
|
|
247
|
+
dimensions,
|
|
248
|
+
diagnosticReasonCodes,
|
|
249
|
+
degradedDimensions,
|
|
250
|
+
};
|
|
251
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AgentGoal v7 shared types — DR-014 snake_case kind / scope enforcement.
|
|
3
|
+
*
|
|
4
|
+
* Core logic:
|
|
5
|
+
* - `kind` is a closed union of snake_case lowercase strings.
|
|
6
|
+
* - `scope` controls visibility (global vs platform-specific vs session-bound).
|
|
7
|
+
* - `status` supports full v7 lifecycle including paused → expired/replaced.
|
|
8
|
+
*
|
|
9
|
+
* Dependencies:
|
|
10
|
+
* - `SourceRef` from `./source-ref.js` for grounding.
|
|
11
|
+
*
|
|
12
|
+
* Boundary:
|
|
13
|
+
* - Used by state-memory (GoalLifecycleStore), control-plane
|
|
14
|
+
* (GoalLifecyclePolicy), and runtime-ops (goal command surface).
|
|
15
|
+
* - Non-enum values trigger compile errors via exhaustive union checks.
|
|
16
|
+
*
|
|
17
|
+
* Test coverage: tests/unit/shared/v7-entities.test.ts (invalid kind
|
|
18
|
+
* `@ts-expect-error` compile guard).
|
|
19
|
+
*/
|
|
20
|
+
import type { SourceRef } from "./source-ref.js";
|
|
21
|
+
export type AgentGoalKind = "short_term" | "long_term" | "habit" | "maintenance" | "passive_sensing" | "outreach" | "exploration";
|
|
22
|
+
export type AgentGoalStatus = "proposal" | "accepted" | "rejected" | "completed" | "paused" | "expired" | "replaced";
|
|
23
|
+
export type AgentGoalOrigin = "owner_set" | "agent_proposed" | "policy_seeded";
|
|
24
|
+
export type AgentGoalScope = "global" | "platform_specific" | "session_bound";
|
|
25
|
+
export interface AgentGoal {
|
|
26
|
+
goalId: string;
|
|
27
|
+
kind: AgentGoalKind;
|
|
28
|
+
scope: AgentGoalScope;
|
|
29
|
+
status: AgentGoalStatus;
|
|
30
|
+
origin: AgentGoalOrigin;
|
|
31
|
+
description: string;
|
|
32
|
+
completionCriteria: string;
|
|
33
|
+
risk: "low" | "medium" | "high";
|
|
34
|
+
priorityHint: number;
|
|
35
|
+
sourceRefs: SourceRef;
|
|
36
|
+
acceptedBy?: "owner" | "policy_allowlist";
|
|
37
|
+
expiresAt?: string;
|
|
38
|
+
createdAt: string;
|
|
39
|
+
updatedAt: string;
|
|
40
|
+
}
|
|
41
|
+
export interface AgentGoalWrite {
|
|
42
|
+
goalId: string;
|
|
43
|
+
kind: AgentGoalKind;
|
|
44
|
+
scope: AgentGoalScope;
|
|
45
|
+
status: AgentGoalStatus;
|
|
46
|
+
origin: AgentGoalOrigin;
|
|
47
|
+
description: string;
|
|
48
|
+
completionCriteria: string;
|
|
49
|
+
risk: "low" | "medium" | "high";
|
|
50
|
+
priorityHint: number;
|
|
51
|
+
sourceRefs: SourceRef;
|
|
52
|
+
acceptedBy?: "owner" | "policy_allowlist";
|
|
53
|
+
expiresAt?: string;
|
|
54
|
+
createdAt: string;
|
|
55
|
+
updatedAt: string;
|
|
56
|
+
}
|
|
57
|
+
export interface AgentGoalStatusTransition {
|
|
58
|
+
goalId: string;
|
|
59
|
+
newStatus: AgentGoalStatus;
|
|
60
|
+
acceptedBy?: "owner" | "policy_allowlist";
|
|
61
|
+
updatedAt: string;
|
|
62
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AgentGoal v7 shared types — DR-014 snake_case kind / scope enforcement.
|
|
3
|
+
*
|
|
4
|
+
* Core logic:
|
|
5
|
+
* - `kind` is a closed union of snake_case lowercase strings.
|
|
6
|
+
* - `scope` controls visibility (global vs platform-specific vs session-bound).
|
|
7
|
+
* - `status` supports full v7 lifecycle including paused → expired/replaced.
|
|
8
|
+
*
|
|
9
|
+
* Dependencies:
|
|
10
|
+
* - `SourceRef` from `./source-ref.js` for grounding.
|
|
11
|
+
*
|
|
12
|
+
* Boundary:
|
|
13
|
+
* - Used by state-memory (GoalLifecycleStore), control-plane
|
|
14
|
+
* (GoalLifecyclePolicy), and runtime-ops (goal command surface).
|
|
15
|
+
* - Non-enum values trigger compile errors via exhaustive union checks.
|
|
16
|
+
*
|
|
17
|
+
* Test coverage: tests/unit/shared/v7-entities.test.ts (invalid kind
|
|
18
|
+
* `@ts-expect-error` compile guard).
|
|
19
|
+
*/
|
|
20
|
+
export {};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SourceRef — v7 non-empty tuple for source grounding.
|
|
3
|
+
*
|
|
4
|
+
* Core logic: Every fact claim must carry at least one source reference.
|
|
5
|
+
* DR-025 enforces non-empty at compile time. Empty array assignments are
|
|
6
|
+
* rejected by the TypeScript compiler (`strict: true`).
|
|
7
|
+
*
|
|
8
|
+
* Dependencies: none (primitive shared type).
|
|
9
|
+
* Boundary: Used across state-memory, dream-quiet, guidance-voice, and
|
|
10
|
+
* observability systems for source-backed assertions.
|
|
11
|
+
* Test coverage: tests/unit/shared/v7-entities.test.ts (compile-time
|
|
12
|
+
* `@ts-expect-error` guard for empty tuple).
|
|
13
|
+
*/
|
|
14
|
+
export type SourceRef = readonly [string, ...string[]];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|