@haaaiawd/second-nature 0.1.43 → 0.1.51

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.
@@ -1,202 +1,202 @@
1
- import { classifyFailure } from "./failure-taxonomy.js";
2
- import { enforceExecutionPolicy } from "./execution-policy.js";
3
- const DEFAULT_RETRY_MAX = 3;
4
- const DEFAULT_BASE_DELAY_MS = 1000;
5
- const DEFAULT_MAX_DELAY_MS = 30000;
6
- function resolveRetryPolicy(input) {
7
- return {
8
- maxRetries: input?.maxRetries ?? DEFAULT_RETRY_MAX,
9
- baseDelayMs: input?.baseDelayMs ?? DEFAULT_BASE_DELAY_MS,
10
- maxDelayMs: input?.maxDelayMs ?? DEFAULT_MAX_DELAY_MS,
11
- jitter: input?.jitter ?? true,
12
- };
13
- }
14
- function computeRetryDelayMs(attempt, policy, retryAfterMs) {
15
- if (typeof retryAfterMs === "number" && retryAfterMs > 0) {
16
- return retryAfterMs;
17
- }
18
- const base = Math.min(policy.baseDelayMs * 2 ** Math.max(0, attempt - 1), policy.maxDelayMs);
19
- if (!policy.jitter)
20
- return base;
21
- return Math.floor(base * 0.8 + Math.random() * base * 0.4);
22
- }
23
- async function sleep(ms) {
24
- if (ms <= 0)
25
- return;
26
- await new Promise((resolve) => setTimeout(resolve, ms));
27
- }
28
- function makeTraceId(request, plan) {
29
- return `${request.platformId}:${request.intent}:${plan.channel}:${Date.now()}`;
30
- }
31
- function resolveIdentity(request) {
32
- if (!request.decisionId || !request.intentId) {
33
- throw new Error("connector_policy_missing_decision_or_intent_identity");
34
- }
35
- return {
36
- decisionId: request.decisionId,
37
- intentId: request.intentId,
38
- };
39
- }
40
- function isDegradedChannel(channel) {
41
- return channel === "cli" || channel === "skill" || channel === "browser";
42
- }
43
- function adaptProtocolErrors(error) {
44
- if (!error || typeof error !== "object") {
45
- return error;
46
- }
47
- const record = error;
48
- const detail = typeof record.detail === "string" ? record.detail : "";
49
- if (detail === "node_secret_required") {
50
- return { code: "verification_required", detail };
51
- }
52
- if (detail === "bundle_required") {
53
- return { code: "protocol_mismatch", detail };
54
- }
55
- if (detail === "asset_id mismatch") {
56
- return { code: "protocol_mismatch", detail };
57
- }
58
- return error;
59
- }
60
- export function createConnectorPolicyLayer(ctx) {
61
- const retryPolicy = resolveRetryPolicy(ctx.retryPolicy);
62
- const allowDegradedFallback = ctx.allowDegradedFallback ?? (() => true);
63
- return {
64
- async executeWithPolicy(intent, request) {
65
- if (ctx.cooldownPort) {
66
- const cooldown = await ctx.cooldownPort.isBlocked(request.platformId, intent);
67
- if (cooldown.blocked) {
68
- return {
69
- status: "terminal_failure",
70
- failureClass: "cooldown_blocked",
71
- retryAfterMs: cooldown.retryAfterMs,
72
- metadata: {
73
- platformId: request.platformId,
74
- channel: request.preferredChannel ?? "api_rest",
75
- latencyMs: 0,
76
- },
77
- };
78
- }
79
- }
80
- const identity = resolveIdentity(request);
81
- let plan;
82
- try {
83
- plan = await ctx.routePlanner.planRoute(intent, request);
84
- }
85
- catch (error) {
86
- const failure = classifyFailure(error);
87
- return {
88
- status: "terminal_failure",
89
- failureClass: failure.class,
90
- retryAfterMs: failure.retryAfterMs,
91
- metadata: {
92
- platformId: request.platformId,
93
- channel: request.preferredChannel ?? "api_rest",
94
- latencyMs: 0,
95
- },
96
- };
97
- }
98
- if (isDegradedChannel(plan.channel) && !allowDegradedFallback(plan, request)) {
99
- return {
100
- status: "terminal_failure",
101
- failureClass: "protocol_mismatch",
102
- metadata: {
103
- platformId: request.platformId,
104
- channel: plan.channel,
105
- latencyMs: 0,
106
- degraded: true,
107
- },
108
- };
109
- }
110
- const policyGate = await enforceExecutionPolicy(plan, intent, request, {
111
- effectCommitLedger: ctx.effectCommitLedger,
112
- });
113
- if (policyGate.skipAdapter && policyGate.existingOutcomeRef) {
114
- return {
115
- status: "success",
116
- data: { replayedCommit: true, outcomeRef: policyGate.existingOutcomeRef },
117
- metadata: {
118
- platformId: request.platformId,
119
- channel: plan.channel,
120
- latencyMs: 0,
121
- degraded: plan.degraded,
122
- },
123
- };
124
- }
125
- let lastFailure;
126
- for (let attempt = 1; attempt <= retryPolicy.maxRetries; attempt += 1) {
127
- const traceId = `${makeTraceId(request, plan)}:${attempt}`;
128
- if (ctx.telemetry) {
129
- await ctx.telemetry.startAttempt({
130
- traceId,
131
- decisionId: identity.decisionId,
132
- intentId: identity.intentId,
133
- platformId: request.platformId,
134
- capability: request.intent,
135
- channel: plan.channel,
136
- retryPolicy: JSON.stringify(retryPolicy),
137
- idempotencyKey: request.idempotencyKey,
138
- });
139
- }
140
- const raw = await ctx.executionRunner.run(plan, request);
141
- if (raw.success) {
142
- if (ctx.telemetry) {
143
- await ctx.telemetry.completeAttempt(traceId, "succeeded");
144
- }
145
- return {
146
- status: "success",
147
- data: raw.payload,
148
- metadata: {
149
- platformId: raw.platformId,
150
- channel: raw.channel,
151
- latencyMs: raw.latencyMs,
152
- degraded: raw.degraded,
153
- },
154
- };
155
- }
156
- const classified = classifyFailure(adaptProtocolErrors(raw.error));
157
- lastFailure = {
158
- failureClass: classified.class,
159
- retryAfterMs: classified.retryAfterMs,
160
- channel: raw.channel,
161
- };
162
- if (ctx.telemetry) {
163
- await ctx.telemetry.completeAttempt(traceId, "failed", undefined, classified.class);
164
- }
165
- if (ctx.cooldownPort) {
166
- await ctx.cooldownPort.markFailure(request.platformId, intent, classified.class, classified.retryAfterMs);
167
- }
168
- const isRetryable = classified.retryable;
169
- const errorDetail = raw.error && typeof raw.error === "object" && "detail" in raw.error
170
- ? String(raw.error.detail)
171
- : undefined;
172
- if (!isRetryable || attempt >= retryPolicy.maxRetries) {
173
- return {
174
- status: "terminal_failure",
175
- failureClass: classified.class,
176
- retryAfterMs: classified.retryAfterMs,
177
- metadata: {
178
- platformId: raw.platformId,
179
- channel: raw.channel,
180
- latencyMs: raw.latencyMs,
181
- degraded: raw.degraded,
182
- detail: errorDetail,
183
- },
184
- };
185
- }
186
- const delay = computeRetryDelayMs(attempt, retryPolicy, classified.retryAfterMs);
187
- await sleep(delay);
188
- }
189
- return {
190
- status: "terminal_failure",
191
- failureClass: lastFailure?.failureClass ?? "unknown_platform_change",
192
- retryAfterMs: lastFailure?.retryAfterMs,
193
- metadata: {
194
- platformId: request.platformId,
195
- channel: lastFailure?.channel ?? plan.channel,
196
- latencyMs: 0,
197
- degraded: isDegradedChannel(lastFailure?.channel ?? plan.channel),
198
- },
199
- };
200
- },
201
- };
202
- }
1
+ import { classifyFailure } from "./failure-taxonomy.js";
2
+ import { enforceExecutionPolicy } from "./execution-policy.js";
3
+ const DEFAULT_RETRY_MAX = 3;
4
+ const DEFAULT_BASE_DELAY_MS = 1000;
5
+ const DEFAULT_MAX_DELAY_MS = 30000;
6
+ function resolveRetryPolicy(input) {
7
+ return {
8
+ maxRetries: input?.maxRetries ?? DEFAULT_RETRY_MAX,
9
+ baseDelayMs: input?.baseDelayMs ?? DEFAULT_BASE_DELAY_MS,
10
+ maxDelayMs: input?.maxDelayMs ?? DEFAULT_MAX_DELAY_MS,
11
+ jitter: input?.jitter ?? true,
12
+ };
13
+ }
14
+ function computeRetryDelayMs(attempt, policy, retryAfterMs) {
15
+ if (typeof retryAfterMs === "number" && retryAfterMs > 0) {
16
+ return retryAfterMs;
17
+ }
18
+ const base = Math.min(policy.baseDelayMs * 2 ** Math.max(0, attempt - 1), policy.maxDelayMs);
19
+ if (!policy.jitter)
20
+ return base;
21
+ return Math.floor(base * 0.8 + Math.random() * base * 0.4);
22
+ }
23
+ async function sleep(ms) {
24
+ if (ms <= 0)
25
+ return;
26
+ await new Promise((resolve) => setTimeout(resolve, ms));
27
+ }
28
+ function makeTraceId(request, plan) {
29
+ return `${request.platformId}:${request.intent}:${plan.channel}:${Date.now()}`;
30
+ }
31
+ function resolveIdentity(request) {
32
+ if (!request.decisionId || !request.intentId) {
33
+ throw new Error("connector_policy_missing_decision_or_intent_identity");
34
+ }
35
+ return {
36
+ decisionId: request.decisionId,
37
+ intentId: request.intentId,
38
+ };
39
+ }
40
+ function isDegradedChannel(channel) {
41
+ return channel === "cli" || channel === "skill" || channel === "browser";
42
+ }
43
+ function adaptProtocolErrors(error) {
44
+ if (!error || typeof error !== "object") {
45
+ return error;
46
+ }
47
+ const record = error;
48
+ const detail = typeof record.detail === "string" ? record.detail : "";
49
+ if (detail === "node_secret_required") {
50
+ return { code: "verification_required", detail };
51
+ }
52
+ if (detail === "bundle_required") {
53
+ return { code: "protocol_mismatch", detail };
54
+ }
55
+ if (detail === "asset_id mismatch") {
56
+ return { code: "protocol_mismatch", detail };
57
+ }
58
+ return error;
59
+ }
60
+ export function createConnectorPolicyLayer(ctx) {
61
+ const retryPolicy = resolveRetryPolicy(ctx.retryPolicy);
62
+ const allowDegradedFallback = ctx.allowDegradedFallback ?? (() => true);
63
+ return {
64
+ async executeWithPolicy(intent, request) {
65
+ if (ctx.cooldownPort) {
66
+ const cooldown = await ctx.cooldownPort.isBlocked(request.platformId, intent);
67
+ if (cooldown.blocked) {
68
+ return {
69
+ status: "terminal_failure",
70
+ failureClass: "cooldown_blocked",
71
+ retryAfterMs: cooldown.retryAfterMs,
72
+ metadata: {
73
+ platformId: request.platformId,
74
+ channel: request.preferredChannel ?? "api_rest",
75
+ latencyMs: 0,
76
+ },
77
+ };
78
+ }
79
+ }
80
+ const identity = resolveIdentity(request);
81
+ let plan;
82
+ try {
83
+ plan = await ctx.routePlanner.planRoute(intent, request);
84
+ }
85
+ catch (error) {
86
+ const failure = classifyFailure(error);
87
+ return {
88
+ status: "terminal_failure",
89
+ failureClass: failure.class,
90
+ retryAfterMs: failure.retryAfterMs,
91
+ metadata: {
92
+ platformId: request.platformId,
93
+ channel: request.preferredChannel ?? "api_rest",
94
+ latencyMs: 0,
95
+ },
96
+ };
97
+ }
98
+ if (isDegradedChannel(plan.channel) && !allowDegradedFallback(plan, request)) {
99
+ return {
100
+ status: "terminal_failure",
101
+ failureClass: "protocol_mismatch",
102
+ metadata: {
103
+ platformId: request.platformId,
104
+ channel: plan.channel,
105
+ latencyMs: 0,
106
+ degraded: true,
107
+ },
108
+ };
109
+ }
110
+ const policyGate = await enforceExecutionPolicy(plan, intent, request, {
111
+ effectCommitLedger: ctx.effectCommitLedger,
112
+ });
113
+ if (policyGate.skipAdapter && policyGate.existingOutcomeRef) {
114
+ return {
115
+ status: "success",
116
+ data: { replayedCommit: true, outcomeRef: policyGate.existingOutcomeRef },
117
+ metadata: {
118
+ platformId: request.platformId,
119
+ channel: plan.channel,
120
+ latencyMs: 0,
121
+ degraded: plan.degraded,
122
+ },
123
+ };
124
+ }
125
+ let lastFailure;
126
+ for (let attempt = 1; attempt <= retryPolicy.maxRetries; attempt += 1) {
127
+ const traceId = `${makeTraceId(request, plan)}:${attempt}`;
128
+ if (ctx.telemetry) {
129
+ await ctx.telemetry.startAttempt({
130
+ traceId,
131
+ decisionId: identity.decisionId,
132
+ intentId: identity.intentId,
133
+ platformId: request.platformId,
134
+ capability: request.intent,
135
+ channel: plan.channel,
136
+ retryPolicy: JSON.stringify(retryPolicy),
137
+ idempotencyKey: request.idempotencyKey,
138
+ });
139
+ }
140
+ const raw = await ctx.executionRunner.run(plan, request);
141
+ if (raw.success) {
142
+ if (ctx.telemetry) {
143
+ await ctx.telemetry.completeAttempt(traceId, "succeeded");
144
+ }
145
+ return {
146
+ status: "success",
147
+ data: raw.payload,
148
+ metadata: {
149
+ platformId: raw.platformId,
150
+ channel: raw.channel,
151
+ latencyMs: raw.latencyMs,
152
+ degraded: raw.degraded,
153
+ },
154
+ };
155
+ }
156
+ const classified = classifyFailure(adaptProtocolErrors(raw.error));
157
+ lastFailure = {
158
+ failureClass: classified.class,
159
+ retryAfterMs: classified.retryAfterMs,
160
+ channel: raw.channel,
161
+ };
162
+ if (ctx.telemetry) {
163
+ await ctx.telemetry.completeAttempt(traceId, "failed", undefined, classified.class);
164
+ }
165
+ if (ctx.cooldownPort) {
166
+ await ctx.cooldownPort.markFailure(request.platformId, intent, classified.class, classified.retryAfterMs);
167
+ }
168
+ const isRetryable = classified.retryable;
169
+ const errorDetail = raw.error && typeof raw.error === "object" && "detail" in raw.error
170
+ ? String(raw.error.detail)
171
+ : undefined;
172
+ if (!isRetryable || attempt >= retryPolicy.maxRetries) {
173
+ return {
174
+ status: "terminal_failure",
175
+ failureClass: classified.class,
176
+ retryAfterMs: classified.retryAfterMs,
177
+ metadata: {
178
+ platformId: raw.platformId,
179
+ channel: raw.channel,
180
+ latencyMs: raw.latencyMs,
181
+ degraded: raw.degraded,
182
+ detail: errorDetail,
183
+ },
184
+ };
185
+ }
186
+ const delay = computeRetryDelayMs(attempt, retryPolicy, classified.retryAfterMs);
187
+ await sleep(delay);
188
+ }
189
+ return {
190
+ status: "terminal_failure",
191
+ failureClass: lastFailure?.failureClass ?? "unknown_platform_change",
192
+ retryAfterMs: lastFailure?.retryAfterMs,
193
+ metadata: {
194
+ platformId: request.platformId,
195
+ channel: lastFailure?.channel ?? plan.channel,
196
+ latencyMs: 0,
197
+ degraded: isDegradedChannel(lastFailure?.channel ?? plan.channel),
198
+ },
199
+ };
200
+ },
201
+ };
202
+ }