@purista/harness 1.0.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.
- package/LICENSE +201 -0
- package/README.md +23 -0
- package/dist/agents/index.d.ts +34 -0
- package/dist/agents/index.js +301 -0
- package/dist/errors/catalog.d.ts +185 -0
- package/dist/errors/catalog.js +144 -0
- package/dist/errors/harness-error.d.ts +64 -0
- package/dist/errors/harness-error.js +58 -0
- package/dist/errors/index.d.ts +3 -0
- package/dist/errors/index.js +3 -0
- package/dist/errors/redaction.d.ts +5 -0
- package/dist/errors/redaction.js +64 -0
- package/dist/harness/defineHarness.d.ts +640 -0
- package/dist/harness/defineHarness.js +176 -0
- package/dist/harness/errors.d.ts +62 -0
- package/dist/harness/errors.js +67 -0
- package/dist/harness/types.d.ts +27 -0
- package/dist/harness/types.js +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +12 -0
- package/dist/logger/index.d.ts +2 -0
- package/dist/logger/index.js +2 -0
- package/dist/logger/json-logger.d.ts +31 -0
- package/dist/logger/json-logger.js +65 -0
- package/dist/logger/logger.d.ts +31 -0
- package/dist/logger/logger.js +1 -0
- package/dist/models/json.d.ts +6 -0
- package/dist/models/json.js +1 -0
- package/dist/models/registry.d.ts +112 -0
- package/dist/models/registry.js +286 -0
- package/dist/models/state.d.ts +64 -0
- package/dist/models/state.js +1 -0
- package/dist/ports/base-model-provider.d.ts +56 -0
- package/dist/ports/base-model-provider.js +343 -0
- package/dist/ports/capabilities.d.ts +70 -0
- package/dist/ports/capabilities.js +38 -0
- package/dist/ports/feedback.d.ts +29 -0
- package/dist/ports/feedback.js +1 -0
- package/dist/ports/harness-context.d.ts +20 -0
- package/dist/ports/harness-context.js +1 -0
- package/dist/ports/index.d.ts +6 -0
- package/dist/ports/index.js +6 -0
- package/dist/ports/model-provider.d.ts +280 -0
- package/dist/ports/model-provider.js +1 -0
- package/dist/ports/state.d.ts +72 -0
- package/dist/ports/state.js +24 -0
- package/dist/runtime/durable.d.ts +134 -0
- package/dist/runtime/durable.js +185 -0
- package/dist/runtime/index.d.ts +2 -0
- package/dist/runtime/index.js +2 -0
- package/dist/runtime/steps.d.ts +22 -0
- package/dist/runtime/steps.js +51 -0
- package/dist/sandbox/index.d.ts +111 -0
- package/dist/sandbox/index.js +165 -0
- package/dist/sessions/index.d.ts +23 -0
- package/dist/sessions/index.js +718 -0
- package/dist/skills/index.d.ts +8 -0
- package/dist/skills/index.js +88 -0
- package/dist/state/in-memory.d.ts +35 -0
- package/dist/state/in-memory.js +140 -0
- package/dist/telemetry/index.d.ts +1 -0
- package/dist/telemetry/index.js +1 -0
- package/dist/telemetry/shim.d.ts +26 -0
- package/dist/telemetry/shim.js +120 -0
- package/dist/testing/capabilities.d.ts +11 -0
- package/dist/testing/capabilities.js +20 -0
- package/dist/testing/fakeModelProvider.d.ts +25 -0
- package/dist/testing/fakeModelProvider.js +79 -0
- package/dist/testing/feedback.d.ts +10 -0
- package/dist/testing/feedback.js +24 -0
- package/dist/testing/fixtures/mcp/fake-http-server.d.ts +8 -0
- package/dist/testing/fixtures/mcp/fake-http-server.js +95 -0
- package/dist/testing/index.d.ts +8 -0
- package/dist/testing/index.js +11 -0
- package/dist/testing/sandboxContract.d.ts +4 -0
- package/dist/testing/sandboxContract.js +74 -0
- package/dist/testing/sandboxSnapshot.d.ts +7 -0
- package/dist/testing/sandboxSnapshot.js +201 -0
- package/dist/testing/stateStoreContract.d.ts +2 -0
- package/dist/testing/stateStoreContract.js +109 -0
- package/dist/tools/index.d.ts +9 -0
- package/dist/tools/index.js +123 -0
- package/dist/tools/mcp/http.d.ts +2 -0
- package/dist/tools/mcp/http.js +109 -0
- package/dist/tools/mcp/index.d.ts +2 -0
- package/dist/tools/mcp/index.js +2 -0
- package/dist/tools/mcp/runner.d.ts +74 -0
- package/dist/tools/mcp/runner.js +238 -0
- package/dist/tools/mcp/schema.d.ts +41 -0
- package/dist/tools/mcp/schema.js +251 -0
- package/dist/tools/mcp/stdio.d.ts +2 -0
- package/dist/tools/mcp/stdio.js +122 -0
- package/dist/ulid/index.d.ts +6 -0
- package/dist/ulid/index.js +35 -0
- package/dist/workflows/index.d.ts +8 -0
- package/dist/workflows/index.js +26 -0
- package/package.json +75 -0
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { HarnessError } from './harness-error.js';
|
|
2
|
+
/** Configuration validation and assembly failures. */
|
|
3
|
+
export declare class HarnessConfigError extends HarnessError {
|
|
4
|
+
constructor(message: string, meta: {
|
|
5
|
+
reason: string;
|
|
6
|
+
path?: string;
|
|
7
|
+
id?: string;
|
|
8
|
+
}, cause?: unknown);
|
|
9
|
+
}
|
|
10
|
+
/** Harness validation failures for inputs, outputs, and payload schemas. */
|
|
11
|
+
export declare class ValidationError extends HarnessError {
|
|
12
|
+
constructor(message: string, meta: {
|
|
13
|
+
where:
|
|
14
|
+
/** Agent input schema validation failed. */ 'agent_input'
|
|
15
|
+
/** Agent output schema validation failed. */ | 'agent_output'
|
|
16
|
+
/** Workflow input schema validation failed. */ | 'workflow_input'
|
|
17
|
+
/** Workflow output schema validation failed. */ | 'workflow_output'
|
|
18
|
+
/** Tool input schema validation failed. */ | 'tool_input'
|
|
19
|
+
/** Tool output schema validation failed. */ | 'tool_output'
|
|
20
|
+
/** MCP request schema validation failed. */ | 'mcp_input'
|
|
21
|
+
/** MCP response schema validation failed. */ | 'mcp_output'
|
|
22
|
+
/** Model provider response shape is invalid. */ | 'model_response'
|
|
23
|
+
/** Session memory key is invalid. */ | 'memory_key'
|
|
24
|
+
/** Session memory value is invalid or non-serializable. */ | 'memory_value'
|
|
25
|
+
/** Message envelope validation failed. */ | 'message'
|
|
26
|
+
/** Session history shape validation failed. */ | 'session_history'
|
|
27
|
+
/** Invocation options are invalid. */ | 'invoke_options';
|
|
28
|
+
issues: unknown;
|
|
29
|
+
}, cause?: unknown);
|
|
30
|
+
}
|
|
31
|
+
/** Tool execution denied by policy or approval hook. */
|
|
32
|
+
export declare class PermissionDeniedError extends HarnessError {
|
|
33
|
+
constructor(message: string, meta: {
|
|
34
|
+
tool_name: string;
|
|
35
|
+
agent_id: string;
|
|
36
|
+
reason?: 'mode_deny' | 'hook_deny' | 'hook_failed';
|
|
37
|
+
}, cause?: unknown);
|
|
38
|
+
}
|
|
39
|
+
/** Sandbox filesystem or command execution failed. */
|
|
40
|
+
export declare class SandboxError extends HarnessError {
|
|
41
|
+
constructor(message: string, meta: {
|
|
42
|
+
reason: 'invalid_path' | 'exec_failed' | 'fs_failed' | string;
|
|
43
|
+
stdout?: string;
|
|
44
|
+
stderr?: string;
|
|
45
|
+
}, cause?: unknown);
|
|
46
|
+
}
|
|
47
|
+
/** Sandbox session has no command executor available. */
|
|
48
|
+
export declare class SandboxNoExecutorError extends HarnessError {
|
|
49
|
+
constructor(message: string, meta: {
|
|
50
|
+
session_id: string;
|
|
51
|
+
}, cause?: unknown);
|
|
52
|
+
}
|
|
53
|
+
/** Model/provider call failed or returned unsupported output shape. */
|
|
54
|
+
export declare class ModelError extends HarnessError {
|
|
55
|
+
constructor(message: string, meta: {
|
|
56
|
+
provider: string;
|
|
57
|
+
model: string;
|
|
58
|
+
method: string;
|
|
59
|
+
status?: number;
|
|
60
|
+
reason?: 'http_error' | 'network' | 'unstructured_response' | 'malformed_response' | 'context_length_exceeded';
|
|
61
|
+
providerCode?: string;
|
|
62
|
+
providerType?: string;
|
|
63
|
+
providerParam?: string;
|
|
64
|
+
providerRequestId?: string;
|
|
65
|
+
providerMessage?: string;
|
|
66
|
+
providerBody?: unknown;
|
|
67
|
+
providerHeaders?: Record<string, string>;
|
|
68
|
+
}, cause?: unknown);
|
|
69
|
+
}
|
|
70
|
+
/** Requested model capability is not available for alias/provider method. */
|
|
71
|
+
export declare class ModelCapabilityError extends HarnessError {
|
|
72
|
+
constructor(message: string, meta: {
|
|
73
|
+
alias: string;
|
|
74
|
+
method: string;
|
|
75
|
+
reason: 'missing_capability' | 'method_missing';
|
|
76
|
+
}, cause?: unknown);
|
|
77
|
+
}
|
|
78
|
+
/** Tool execution failed with wrapped/normalized cause information. */
|
|
79
|
+
export declare class ToolError extends HarnessError {
|
|
80
|
+
constructor(message: string, meta: {
|
|
81
|
+
tool_id: string;
|
|
82
|
+
tool_kind: string;
|
|
83
|
+
}, cause?: unknown);
|
|
84
|
+
}
|
|
85
|
+
/** Tool reference was not found in registry, allowlist, or model response mapping. */
|
|
86
|
+
export declare class ToolNotFoundError extends HarnessError {
|
|
87
|
+
constructor(message: string, meta: {
|
|
88
|
+
tool_id: string;
|
|
89
|
+
where: 'registry' | 'agent_allowlist' | 'model_response';
|
|
90
|
+
}, cause?: unknown);
|
|
91
|
+
}
|
|
92
|
+
/** Skill id was not found in configured skill set. */
|
|
93
|
+
export declare class SkillNotFoundError extends HarnessError {
|
|
94
|
+
constructor(message: string, meta: {
|
|
95
|
+
skill_id: string;
|
|
96
|
+
}, cause?: unknown);
|
|
97
|
+
}
|
|
98
|
+
/** Skill manifest/frontmatter/config validation failure. */
|
|
99
|
+
export declare class SkillManifestError extends HarnessError {
|
|
100
|
+
constructor(message: string, meta: {
|
|
101
|
+
directory: string;
|
|
102
|
+
reason: 'missing_skill_md' | 'invalid_frontmatter' | 'name_mismatch' | 'directory_missing' | 'reserved_name';
|
|
103
|
+
skill_id?: string;
|
|
104
|
+
}, cause?: unknown);
|
|
105
|
+
}
|
|
106
|
+
/** Workflow referenced an unknown agent id. */
|
|
107
|
+
export declare class AgentNotFoundError extends HarnessError {
|
|
108
|
+
constructor(message: string, meta: {
|
|
109
|
+
agent_id: string;
|
|
110
|
+
}, cause?: unknown);
|
|
111
|
+
}
|
|
112
|
+
/** Agent exceeded configured loop iteration/step budget. */
|
|
113
|
+
export declare class AgentLoopBudgetError extends HarnessError {
|
|
114
|
+
constructor(message: string, meta: {
|
|
115
|
+
agent_id: string;
|
|
116
|
+
reason: 'iterations_exceeded';
|
|
117
|
+
limit: number;
|
|
118
|
+
}, cause?: unknown);
|
|
119
|
+
}
|
|
120
|
+
/** Session attempted to invoke unknown workflow id. */
|
|
121
|
+
export declare class WorkflowNotFoundError extends HarnessError {
|
|
122
|
+
constructor(message: string, meta: {
|
|
123
|
+
workflow_id: string;
|
|
124
|
+
}, cause?: unknown);
|
|
125
|
+
}
|
|
126
|
+
/** Session id not found in backing store. */
|
|
127
|
+
export declare class SessionNotFoundError extends HarnessError {
|
|
128
|
+
constructor(message: string, meta: {
|
|
129
|
+
session_id: string;
|
|
130
|
+
}, cause?: unknown);
|
|
131
|
+
}
|
|
132
|
+
/** Session is currently busy and cannot accept concurrent mutation/run operations. */
|
|
133
|
+
export declare class SessionBusyError extends HarnessError {
|
|
134
|
+
constructor(message: string, meta: {
|
|
135
|
+
session_id: string;
|
|
136
|
+
reason?: 'concurrent_run' | 'history_clear_during_run' | 'history_replace_during_run';
|
|
137
|
+
}, cause?: unknown);
|
|
138
|
+
}
|
|
139
|
+
/** State backend operation failed. */
|
|
140
|
+
export declare class StateError extends HarnessError {
|
|
141
|
+
constructor(message: string, meta: {
|
|
142
|
+
op: 'getSession' | 'upsertSession' | 'closeSession' | 'appendMessages' | 'listMessages' | 'clearMessages' | 'createRun' | 'finishRun' | 'getRun' | 'listRuns' | 'appendEvents' | 'listEvents';
|
|
143
|
+
reason?: 'duplicate_message_id' | string;
|
|
144
|
+
}, cause?: unknown);
|
|
145
|
+
}
|
|
146
|
+
/** Timed execution budget expired. */
|
|
147
|
+
export declare class OperationTimeoutError extends HarnessError {
|
|
148
|
+
constructor(message: string, meta: {
|
|
149
|
+
scope: 'run' | 'model' | 'tool' | 'sandbox_run';
|
|
150
|
+
timeout_ms: number;
|
|
151
|
+
}, cause?: unknown);
|
|
152
|
+
}
|
|
153
|
+
/** Operation cancelled by abort signal or explicit cancellation path. */
|
|
154
|
+
export declare class OperationCancelledError extends HarnessError {
|
|
155
|
+
constructor(message: string, meta: {
|
|
156
|
+
scope: 'run' | 'workflow' | 'agent' | 'model' | 'tool' | 'sandbox';
|
|
157
|
+
}, cause?: unknown);
|
|
158
|
+
}
|
|
159
|
+
/** MCP transport/protocol failure. */
|
|
160
|
+
export declare class McpProtocolError extends HarnessError {
|
|
161
|
+
constructor(message: string, meta: {
|
|
162
|
+
tool_id: string;
|
|
163
|
+
transport: 'stdio' | 'http';
|
|
164
|
+
phase: 'connect' | 'list' | 'call';
|
|
165
|
+
}, cause?: unknown);
|
|
166
|
+
}
|
|
167
|
+
/** Supported MCP HTTP authentication kinds. */
|
|
168
|
+
export type McpAuthKind =
|
|
169
|
+
/** No authentication. */ 'none'
|
|
170
|
+
/** Bearer token auth. */ | 'bearer'
|
|
171
|
+
/** OAuth2 access token auth. */ | 'oauth2'
|
|
172
|
+
/** API key auth. */ | 'api_key'
|
|
173
|
+
/** Basic auth. */ | 'basic';
|
|
174
|
+
/** MCP authentication/authorization failure. */
|
|
175
|
+
export declare class McpAuthError extends HarnessError {
|
|
176
|
+
constructor(message: string, meta: {
|
|
177
|
+
tool_id: string;
|
|
178
|
+
auth_kind: McpAuthKind;
|
|
179
|
+
status?: number;
|
|
180
|
+
}, cause?: unknown);
|
|
181
|
+
}
|
|
182
|
+
/** Unexpected internal harness invariant failure. */
|
|
183
|
+
export declare class InternalError extends HarnessError {
|
|
184
|
+
constructor(message: string, meta?: Record<string, unknown>, cause?: unknown);
|
|
185
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { HarnessError } from './harness-error.js';
|
|
2
|
+
/** Configuration validation and assembly failures. */
|
|
3
|
+
export class HarnessConfigError extends HarnessError {
|
|
4
|
+
constructor(message, meta, cause) {
|
|
5
|
+
super({ code: 'HARNESS_CONFIG_ERROR', category: 'config', retriable: false, message, meta, cause });
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
/** Harness validation failures for inputs, outputs, and payload schemas. */
|
|
9
|
+
export class ValidationError extends HarnessError {
|
|
10
|
+
constructor(message, meta, cause) {
|
|
11
|
+
super({ code: 'VALIDATION_ERROR', category: 'validation', retriable: false, message, meta, cause });
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
/** Tool execution denied by policy or approval hook. */
|
|
15
|
+
export class PermissionDeniedError extends HarnessError {
|
|
16
|
+
constructor(message, meta, cause) {
|
|
17
|
+
super({ code: 'PERMISSION_DENIED', category: 'permission', retriable: false, message, meta, cause });
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/** Sandbox filesystem or command execution failed. */
|
|
21
|
+
export class SandboxError extends HarnessError {
|
|
22
|
+
constructor(message, meta, cause) {
|
|
23
|
+
super({ code: 'SANDBOX_ERROR', category: 'sandbox', retriable: true, message, meta, cause });
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
/** Sandbox session has no command executor available. */
|
|
27
|
+
export class SandboxNoExecutorError extends HarnessError {
|
|
28
|
+
constructor(message, meta, cause) {
|
|
29
|
+
super({ code: 'SANDBOX_NO_EXECUTOR', category: 'sandbox', retriable: false, message, meta, cause });
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/** Model/provider call failed or returned unsupported output shape. */
|
|
33
|
+
export class ModelError extends HarnessError {
|
|
34
|
+
constructor(message, meta, cause) {
|
|
35
|
+
const retriable = meta.reason === 'network'
|
|
36
|
+
|| meta.status === 429
|
|
37
|
+
|| (typeof meta.status === 'number' && meta.status >= 500);
|
|
38
|
+
super({ code: 'MODEL_ERROR', category: 'model', retriable, message, meta, cause });
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/** Requested model capability is not available for alias/provider method. */
|
|
42
|
+
export class ModelCapabilityError extends HarnessError {
|
|
43
|
+
constructor(message, meta, cause) {
|
|
44
|
+
super({ code: 'MODEL_CAPABILITY_ERROR', category: 'model', retriable: false, message, meta, cause });
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/** Tool execution failed with wrapped/normalized cause information. */
|
|
48
|
+
export class ToolError extends HarnessError {
|
|
49
|
+
constructor(message, meta, cause) {
|
|
50
|
+
super({
|
|
51
|
+
code: 'TOOL_ERROR',
|
|
52
|
+
category: 'tool',
|
|
53
|
+
retriable: cause instanceof HarnessError ? cause.retriable : false,
|
|
54
|
+
message,
|
|
55
|
+
meta,
|
|
56
|
+
cause
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/** Tool reference was not found in registry, allowlist, or model response mapping. */
|
|
61
|
+
export class ToolNotFoundError extends HarnessError {
|
|
62
|
+
constructor(message, meta, cause) {
|
|
63
|
+
super({ code: 'TOOL_NOT_FOUND', category: 'tool', retriable: false, message, meta, cause });
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/** Skill id was not found in configured skill set. */
|
|
67
|
+
export class SkillNotFoundError extends HarnessError {
|
|
68
|
+
constructor(message, meta, cause) {
|
|
69
|
+
super({ code: 'SKILL_NOT_FOUND', category: 'skill', retriable: false, message, meta, cause });
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/** Skill manifest/frontmatter/config validation failure. */
|
|
73
|
+
export class SkillManifestError extends HarnessError {
|
|
74
|
+
constructor(message, meta, cause) {
|
|
75
|
+
super({ code: 'SKILL_MANIFEST_ERROR', category: 'config', retriable: false, message, meta, cause });
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/** Workflow referenced an unknown agent id. */
|
|
79
|
+
export class AgentNotFoundError extends HarnessError {
|
|
80
|
+
constructor(message, meta, cause) {
|
|
81
|
+
super({ code: 'AGENT_NOT_FOUND', category: 'validation', retriable: false, message, meta, cause });
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/** Agent exceeded configured loop iteration/step budget. */
|
|
85
|
+
export class AgentLoopBudgetError extends HarnessError {
|
|
86
|
+
constructor(message, meta, cause) {
|
|
87
|
+
super({ code: 'AGENT_LOOP_BUDGET_EXCEEDED', category: 'validation', retriable: false, message, meta, cause });
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/** Session attempted to invoke unknown workflow id. */
|
|
91
|
+
export class WorkflowNotFoundError extends HarnessError {
|
|
92
|
+
constructor(message, meta, cause) {
|
|
93
|
+
super({ code: 'WORKFLOW_NOT_FOUND', category: 'validation', retriable: false, message, meta, cause });
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
/** Session id not found in backing store. */
|
|
97
|
+
export class SessionNotFoundError extends HarnessError {
|
|
98
|
+
constructor(message, meta, cause) {
|
|
99
|
+
super({ code: 'SESSION_NOT_FOUND', category: 'session', retriable: false, message, meta, cause });
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
/** Session is currently busy and cannot accept concurrent mutation/run operations. */
|
|
103
|
+
export class SessionBusyError extends HarnessError {
|
|
104
|
+
constructor(message, meta, cause) {
|
|
105
|
+
super({ code: 'SESSION_BUSY', category: 'session', retriable: true, message, meta, cause });
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
/** State backend operation failed. */
|
|
109
|
+
export class StateError extends HarnessError {
|
|
110
|
+
constructor(message, meta, cause) {
|
|
111
|
+
super({ code: 'STATE_ERROR', category: 'state', retriable: true, message, meta, cause });
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
/** Timed execution budget expired. */
|
|
115
|
+
export class OperationTimeoutError extends HarnessError {
|
|
116
|
+
constructor(message, meta, cause) {
|
|
117
|
+
super({ code: 'OPERATION_TIMEOUT', category: 'timeout', retriable: true, message, meta, cause });
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
/** Operation cancelled by abort signal or explicit cancellation path. */
|
|
121
|
+
export class OperationCancelledError extends HarnessError {
|
|
122
|
+
constructor(message, meta, cause) {
|
|
123
|
+
super({ code: 'OPERATION_CANCELLED', category: 'cancelled', retriable: false, message, meta, cause });
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
/** MCP transport/protocol failure. */
|
|
127
|
+
export class McpProtocolError extends HarnessError {
|
|
128
|
+
constructor(message, meta, cause) {
|
|
129
|
+
super({ code: 'MCP_PROTOCOL_ERROR', category: 'tool', retriable: true, message, meta, cause });
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
/** MCP authentication/authorization failure. */
|
|
133
|
+
export class McpAuthError extends HarnessError {
|
|
134
|
+
constructor(message, meta, cause) {
|
|
135
|
+
const retriable = typeof meta.status === 'number' ? meta.status >= 500 : false;
|
|
136
|
+
super({ code: 'MCP_AUTH_ERROR', category: 'tool', retriable, message, meta, cause });
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
/** Unexpected internal harness invariant failure. */
|
|
140
|
+
export class InternalError extends HarnessError {
|
|
141
|
+
constructor(message, meta, cause) {
|
|
142
|
+
super({ code: 'INTERNAL_ERROR', category: 'internal', retriable: false, message, ...(meta ? { meta } : {}), cause });
|
|
143
|
+
}
|
|
144
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical harness error categories.
|
|
3
|
+
*
|
|
4
|
+
* Use these values for machine-readable error routing and operational dashboards.
|
|
5
|
+
*/
|
|
6
|
+
export type ErrorCategory =
|
|
7
|
+
/** Invalid or inconsistent harness configuration. */
|
|
8
|
+
'config'
|
|
9
|
+
/** Input/output validation failures. */
|
|
10
|
+
| 'validation'
|
|
11
|
+
/** Permission and approval denials. */
|
|
12
|
+
| 'permission'
|
|
13
|
+
/** Sandbox filesystem or execution failures. */
|
|
14
|
+
| 'sandbox'
|
|
15
|
+
/** Model/provider invocation failures. */
|
|
16
|
+
| 'model'
|
|
17
|
+
/** Tool execution failures. */
|
|
18
|
+
| 'tool'
|
|
19
|
+
/** Skill discovery, loading, or manifest failures. */
|
|
20
|
+
| 'skill'
|
|
21
|
+
/** Session lifecycle failures. */
|
|
22
|
+
| 'session'
|
|
23
|
+
/** State-store persistence failures. */
|
|
24
|
+
| 'state'
|
|
25
|
+
/** Timeout budget failures. */
|
|
26
|
+
| 'timeout'
|
|
27
|
+
/** Cooperative cancellation events. */
|
|
28
|
+
| 'cancelled'
|
|
29
|
+
/** Unclassified internal harness bugs. */
|
|
30
|
+
| 'internal';
|
|
31
|
+
/** Construction options for {@link HarnessError}. */
|
|
32
|
+
export interface HarnessErrorOptions {
|
|
33
|
+
code: string;
|
|
34
|
+
category: ErrorCategory;
|
|
35
|
+
message: string;
|
|
36
|
+
retriable: boolean;
|
|
37
|
+
meta?: Record<string, unknown>;
|
|
38
|
+
cause?: unknown;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Base class for all exported harness errors.
|
|
42
|
+
*
|
|
43
|
+
* Carries stable machine-readable fields (`code`, `category`, `retriable`, `meta`) for
|
|
44
|
+
* API mapping, log processing, and operator automation.
|
|
45
|
+
*/
|
|
46
|
+
export declare class HarnessError extends Error {
|
|
47
|
+
readonly code: string;
|
|
48
|
+
readonly category: ErrorCategory;
|
|
49
|
+
readonly retriable: boolean;
|
|
50
|
+
readonly meta: Record<string, unknown> | undefined;
|
|
51
|
+
readonly cause: unknown;
|
|
52
|
+
constructor(opts: HarnessErrorOptions);
|
|
53
|
+
toJSON(): Record<string, unknown>;
|
|
54
|
+
}
|
|
55
|
+
/** Harness type guard for {@link HarnessError}. */
|
|
56
|
+
export declare function isHarnessError(value: unknown): value is HarnessError;
|
|
57
|
+
/** Converts unknown thrown values into a stable serializable error envelope. */
|
|
58
|
+
export declare function serializeError(error: unknown): {
|
|
59
|
+
code: string;
|
|
60
|
+
category: string;
|
|
61
|
+
retriable: boolean;
|
|
62
|
+
message: string;
|
|
63
|
+
meta?: Record<string, unknown>;
|
|
64
|
+
};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { sanitizeMeta } from './redaction.js';
|
|
2
|
+
/**
|
|
3
|
+
* Base class for all exported harness errors.
|
|
4
|
+
*
|
|
5
|
+
* Carries stable machine-readable fields (`code`, `category`, `retriable`, `meta`) for
|
|
6
|
+
* API mapping, log processing, and operator automation.
|
|
7
|
+
*/
|
|
8
|
+
export class HarnessError extends Error {
|
|
9
|
+
code;
|
|
10
|
+
category;
|
|
11
|
+
retriable;
|
|
12
|
+
meta;
|
|
13
|
+
cause;
|
|
14
|
+
constructor(opts) {
|
|
15
|
+
super(opts.message, { cause: opts.cause });
|
|
16
|
+
this.name = new.target.name;
|
|
17
|
+
this.code = opts.code;
|
|
18
|
+
this.category = opts.category;
|
|
19
|
+
this.retriable = opts.retriable;
|
|
20
|
+
this.meta = opts.meta;
|
|
21
|
+
this.cause = opts.cause;
|
|
22
|
+
}
|
|
23
|
+
toJSON() {
|
|
24
|
+
return {
|
|
25
|
+
name: this.name,
|
|
26
|
+
code: this.code,
|
|
27
|
+
category: this.category,
|
|
28
|
+
retriable: this.retriable,
|
|
29
|
+
message: this.message,
|
|
30
|
+
...(this.meta ? { meta: sanitizeMeta(this.meta) } : {})
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/** Harness type guard for {@link HarnessError}. */
|
|
35
|
+
export function isHarnessError(value) {
|
|
36
|
+
return value instanceof HarnessError;
|
|
37
|
+
}
|
|
38
|
+
/** Converts unknown thrown values into a stable serializable error envelope. */
|
|
39
|
+
export function serializeError(error) {
|
|
40
|
+
if (error instanceof HarnessError) {
|
|
41
|
+
const serialized = {
|
|
42
|
+
code: error.code,
|
|
43
|
+
category: error.category,
|
|
44
|
+
retriable: error.retriable,
|
|
45
|
+
message: error.message
|
|
46
|
+
};
|
|
47
|
+
const meta = sanitizeMeta(error.meta);
|
|
48
|
+
if (meta)
|
|
49
|
+
serialized.meta = meta;
|
|
50
|
+
return serialized;
|
|
51
|
+
}
|
|
52
|
+
return {
|
|
53
|
+
code: 'INTERNAL_ERROR',
|
|
54
|
+
category: 'internal',
|
|
55
|
+
retriable: false,
|
|
56
|
+
message: error instanceof Error ? error.message : String(error)
|
|
57
|
+
};
|
|
58
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export type RedactionMode = 'log' | 'provider_body';
|
|
2
|
+
export declare function sanitizeForLog(value: unknown): unknown;
|
|
3
|
+
export declare function sanitizeProviderBody(value: unknown): unknown;
|
|
4
|
+
export declare function sanitizeMeta(meta: Record<string, unknown> | undefined): Record<string, unknown> | undefined;
|
|
5
|
+
export declare function sanitizeProviderMessage(message: string): string;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
const REDACTED = '[redacted]';
|
|
2
|
+
const TRUNCATED = '[truncated]';
|
|
3
|
+
const SENSITIVE_KEY_PATTERN = /(?:^|[-_.])(authorization|cookie|set-cookie|token|accesstoken|refreshtoken|apikey|api_key|x-api-key|secret|password|credential|privatekey|clientsecret)(?:$|[-_.])/i;
|
|
4
|
+
const CONTENT_KEY_PATTERN = /^(prompt|prompts|messages|input|inputs|output|outputs|content|contents|text|data|body)$/i;
|
|
5
|
+
const SAFE_PROVIDER_ERROR_KEYS = new Set(['code', 'type', 'param', 'message']);
|
|
6
|
+
export function sanitizeForLog(value) {
|
|
7
|
+
return sanitizeValue(value, { mode: 'log', seen: new WeakSet(), depth: 0 });
|
|
8
|
+
}
|
|
9
|
+
export function sanitizeProviderBody(value) {
|
|
10
|
+
return sanitizeValue(value, { mode: 'provider_body', seen: new WeakSet(), depth: 0 });
|
|
11
|
+
}
|
|
12
|
+
export function sanitizeMeta(meta) {
|
|
13
|
+
const sanitized = sanitizeForLog(meta);
|
|
14
|
+
return isRecord(sanitized) ? sanitized : undefined;
|
|
15
|
+
}
|
|
16
|
+
export function sanitizeProviderMessage(message) {
|
|
17
|
+
return redactString(message).slice(0, 500);
|
|
18
|
+
}
|
|
19
|
+
function sanitizeValue(value, opts) {
|
|
20
|
+
if (opts.key && isSensitiveKey(opts.key))
|
|
21
|
+
return REDACTED;
|
|
22
|
+
if (opts.mode === 'provider_body' && opts.key && isContentKey(opts.key) && !SAFE_PROVIDER_ERROR_KEYS.has(opts.key))
|
|
23
|
+
return REDACTED;
|
|
24
|
+
if (value === undefined)
|
|
25
|
+
return undefined;
|
|
26
|
+
if (value === null || typeof value === 'number' || typeof value === 'boolean')
|
|
27
|
+
return value;
|
|
28
|
+
if (typeof value === 'bigint')
|
|
29
|
+
return value.toString();
|
|
30
|
+
if (typeof value === 'string')
|
|
31
|
+
return redactString(value).slice(0, 4000);
|
|
32
|
+
if (typeof value === 'symbol' || typeof value === 'function')
|
|
33
|
+
return String(value).slice(0, 4000);
|
|
34
|
+
if (opts.depth >= 4)
|
|
35
|
+
return TRUNCATED;
|
|
36
|
+
if (Array.isArray(value))
|
|
37
|
+
return value.slice(0, 20).map((item) => sanitizeValue(item, { mode: opts.mode, seen: opts.seen, depth: opts.depth + 1 }));
|
|
38
|
+
if (!isRecord(value))
|
|
39
|
+
return String(value).slice(0, 4000);
|
|
40
|
+
if (opts.seen.has(value))
|
|
41
|
+
return '[circular]';
|
|
42
|
+
opts.seen.add(value);
|
|
43
|
+
const out = {};
|
|
44
|
+
for (const [key, item] of Object.entries(value).slice(0, 40)) {
|
|
45
|
+
out[key] = sanitizeValue(item, { ...opts, depth: opts.depth + 1, key });
|
|
46
|
+
}
|
|
47
|
+
opts.seen.delete(value);
|
|
48
|
+
return out;
|
|
49
|
+
}
|
|
50
|
+
function redactString(value) {
|
|
51
|
+
return value
|
|
52
|
+
.replace(/\bBearer\s+[A-Za-z0-9._~+/=-]+/gi, 'Bearer [redacted]')
|
|
53
|
+
.replace(/\bBasic\s+[A-Za-z0-9+/=-]+/gi, 'Basic [redacted]')
|
|
54
|
+
.replace(/\b(sk|pk|rk|sess|key|token|secret)_[A-Za-z0-9._-]{8,}/gi, '$1_[redacted]');
|
|
55
|
+
}
|
|
56
|
+
function isSensitiveKey(key) {
|
|
57
|
+
return SENSITIVE_KEY_PATTERN.test(key.replace(/[A-Z]/g, (char) => `_${char.toLowerCase()}`));
|
|
58
|
+
}
|
|
59
|
+
function isContentKey(key) {
|
|
60
|
+
return CONTENT_KEY_PATTERN.test(key);
|
|
61
|
+
}
|
|
62
|
+
function isRecord(value) {
|
|
63
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
64
|
+
}
|