@inneranimalmedia/agentsam-sdk 2.6.0 → 2.6.1
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/docs/PLATFORM_RUNTIME_EVENTS.md +48 -0
- package/docs/RELEASES.md +7 -7
- package/docs/SOURCE_ARCHITECTURE.md +58 -0
- package/docs/TEST_TIERS.md +26 -0
- package/migrations/runtime/0001_cli_runtime.sql +298 -0
- package/package.json +28 -7
- package/packages/agentsam-repository/README.md +15 -0
- package/packages/agentsam-repository/package.json +25 -0
- package/packages/agentsam-repository/src/contracts.js +113 -0
- package/packages/agentsam-repository/src/index.js +3 -0
- package/{src/lib → packages/agentsam-repository/src}/merkle/cloudflare-persistence.js +14 -24
- package/{src/lib → packages/agentsam-repository/src}/merkle/index.js +1 -0
- package/{src/lib → packages/agentsam-repository/src}/merkle/persistence.js +6 -4
- package/{src/lib → packages/agentsam-repository/src}/merkle/policy.js +1 -0
- package/packages/agentsam-repository/test/contracts.test.mjs +40 -0
- package/packages/agentsam-repository/test/git-context.test.mjs +24 -0
- package/{test/merkle.test.mjs → packages/agentsam-repository/test/merkle-core.test.mjs} +2 -32
- package/{test → packages/agentsam-repository/test}/merkle-persistence.test.mjs +11 -6
- package/packages/identity/package.json +1 -1
- package/protocol/COMPANY_REPOSITORY_GRAPH_V1.md +91 -0
- package/protocol/MERKLE_PERSISTENCE_V1.md +2 -0
- package/protocol/MERKLE_PERSISTENCE_V2.md +40 -0
- package/protocol/repository/repository-contract.schema.json +24 -0
- package/protocol/repository/repository-dependency.schema.json +24 -0
- package/protocol/repository/repository-identity.schema.json +17 -0
- package/protocol/rpc/v1/common.proto +16 -0
- package/protocol/rpc/v1/errors.proto +35 -0
- package/protocol/rpc/v1/knowledge.proto +77 -0
- package/services/knowledge/package-lock.json +333 -0
- package/services/knowledge/package.json +5 -1
- package/src/agent/responses-runner.js +63 -35
- package/src/capabilities/repository-snapshot.js +3 -3
- package/src/cli.js +23 -6
- package/src/commands/context-economics.js +17 -2
- package/src/commands/context.js +1 -1
- package/src/commands/db.js +20 -3
- package/src/commands/env.js +90 -0
- package/src/commands/knowledge.js +12 -4
- package/src/commands/merkle-persist.js +30 -11
- package/src/commands/merkle.js +1 -1
- package/src/commands/models.js +123 -65
- package/src/commands/ollama.js +26 -0
- package/src/commands/preferences.js +53 -26
- package/src/commands/shell.js +236 -48
- package/src/errors/contract.js +236 -0
- package/src/errors/index.js +14 -0
- package/src/index.js +13 -1
- package/src/knowledge/service/auth.js +13 -0
- package/src/knowledge/service/grpc-client.js +115 -0
- package/src/knowledge/service/grpc-codec.js +237 -0
- package/src/knowledge/service/grpc-server.js +83 -0
- package/src/knowledge/service/job-engine.js +248 -0
- package/src/knowledge/service/server.js +87 -135
- package/src/knowledge/source.js +1 -1
- package/src/lib/cli-preferences.js +31 -4
- package/src/lib/deploy-receipt/index.js +2 -2
- package/src/lib/knowledge-docker.js +6 -3
- package/src/lib/local-sessions.js +23 -2
- package/src/lib/local-status.js +1 -1
- package/src/lib/project-config.js +1 -1
- package/src/lib/provider-credentials.js +105 -5
- package/src/lib/slash-commands.js +4 -3
- package/src/local/migrations.js +93 -0
- package/src/local/runtime-store.js +141 -0
- package/src/local/sqlite.js +2 -0
- package/src/local-pty/server.js +113 -51
- package/src/models/discovery.js +292 -0
- package/src/providers/anthropic-messages.js +192 -0
- package/src/providers/cloudflare-chat.js +183 -0
- package/src/providers/factory.js +69 -0
- package/src/providers/gemini-generate-content.js +208 -0
- package/src/providers/index.js +5 -0
- package/src/providers/ollama-chat.js +148 -0
- package/src/providers/openai-responses.js +226 -75
- package/src/repository/index.js +14 -2
- package/src/rpc/generated/common_grpc_pb.js +1 -0
- package/src/rpc/generated/common_pb.js +536 -0
- package/src/rpc/generated/errors_grpc_pb.js +1 -0
- package/src/rpc/generated/errors_pb.js +482 -0
- package/src/rpc/generated/knowledge_grpc_pb.js +135 -0
- package/src/rpc/generated/knowledge_pb.js +2168 -0
- package/src/rpc/generated/package.json +3 -0
- package/src/security/trust-boundary.js +2 -2
- package/src/telemetry/events.js +4 -1
- package/src/ui/cli/activity.js +76 -0
- package/src/ui/cli/compaction.js +15 -0
- package/src/ui/cli/footer.js +39 -0
- package/src/ui/cli/help.js +192 -0
- package/src/ui/cli/plan.js +20 -0
- package/src/ui/cli/runtime-events.js +110 -0
- package/src/ui/cli/waiting.js +16 -0
- package/src/ui/merkle/render.js +1 -1
- package/test/cli/preferences-runtime.test.mjs +11 -0
- package/test/cli/runtime-ui.test.mjs +74 -0
- package/test/error-diagnostics.test.mjs +57 -1
- package/test/fixtures/knowledge-rpc-worker.mjs +16 -0
- package/test/integration/cli-help.test.mjs +37 -0
- package/test/integration/knowledge-rpc.test.mjs +112 -0
- package/test/integration/merkle-cli.test.mjs +61 -0
- package/test/integration/merkle-persistence-identity.test.mjs +48 -0
- package/test/integration/provider-env-cli.test.mjs +49 -0
- package/test/integration/provider-factory.test.mjs +197 -0
- package/test/integration/repository-company-graph.test.mjs +90 -0
- package/test/integration/runtime-migrations.test.mjs +82 -0
- package/test/knowledge-service.test.mjs +5 -0
- package/test/knowledge.test.mjs +16 -0
- package/test/live/terminal-transport.live.test.mjs +24 -0
- package/test/local-sessions.test.mjs +7 -1
- package/test/models.test.mjs +101 -4
- package/test/ollama.test.mjs +21 -0
- package/test/portable-context.test.mjs +1 -1
- package/test/provider-credentials.test.mjs +45 -1
- package/test/release-hygiene.test.mjs +13 -5
- package/test/responses-runner.test.mjs +3 -1
- package/test/shell.test.mjs +50 -8
- package/test/terminal/local-pty.mock.test.mjs +151 -0
- /package/{src/lib → packages/agentsam-repository/src}/git-context.js +0 -0
- /package/{src/lib → packages/agentsam-repository/src}/merkle/diff.js +0 -0
- /package/{src/lib → packages/agentsam-repository/src}/merkle/filemeta.js +0 -0
- /package/{src/lib → packages/agentsam-repository/src}/merkle/git-ignore.js +0 -0
- /package/{src/lib → packages/agentsam-repository/src}/merkle/hash.js +0 -0
- /package/{src/lib → packages/agentsam-repository/src}/merkle/semantic.js +0 -0
- /package/{src/lib → packages/agentsam-repository/src}/merkle/snapshot.js +0 -0
- /package/{src/lib → packages/agentsam-repository/src}/merkle/tree.js +0 -0
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
export const ERROR_SCHEMA_VERSION = 1;
|
|
2
|
+
|
|
3
|
+
// Mirrors google.rpc.Code / gRPC canonical status names without importing a Node gRPC runtime.
|
|
4
|
+
export const ERROR_CODE = Object.freeze({
|
|
5
|
+
OK: 'OK',
|
|
6
|
+
CANCELLED: 'CANCELLED',
|
|
7
|
+
UNKNOWN: 'UNKNOWN',
|
|
8
|
+
INVALID_ARGUMENT: 'INVALID_ARGUMENT',
|
|
9
|
+
DEADLINE_EXCEEDED: 'DEADLINE_EXCEEDED',
|
|
10
|
+
NOT_FOUND: 'NOT_FOUND',
|
|
11
|
+
ALREADY_EXISTS: 'ALREADY_EXISTS',
|
|
12
|
+
PERMISSION_DENIED: 'PERMISSION_DENIED',
|
|
13
|
+
RESOURCE_EXHAUSTED: 'RESOURCE_EXHAUSTED',
|
|
14
|
+
FAILED_PRECONDITION: 'FAILED_PRECONDITION',
|
|
15
|
+
ABORTED: 'ABORTED',
|
|
16
|
+
OUT_OF_RANGE: 'OUT_OF_RANGE',
|
|
17
|
+
UNIMPLEMENTED: 'UNIMPLEMENTED',
|
|
18
|
+
INTERNAL: 'INTERNAL',
|
|
19
|
+
UNAVAILABLE: 'UNAVAILABLE',
|
|
20
|
+
DATA_LOSS: 'DATA_LOSS',
|
|
21
|
+
UNAUTHENTICATED: 'UNAUTHENTICATED',
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
export const GRPC_STATUS = Object.freeze({
|
|
25
|
+
OK: 0,
|
|
26
|
+
CANCELLED: 1,
|
|
27
|
+
UNKNOWN: 2,
|
|
28
|
+
INVALID_ARGUMENT: 3,
|
|
29
|
+
DEADLINE_EXCEEDED: 4,
|
|
30
|
+
NOT_FOUND: 5,
|
|
31
|
+
ALREADY_EXISTS: 6,
|
|
32
|
+
PERMISSION_DENIED: 7,
|
|
33
|
+
RESOURCE_EXHAUSTED: 8,
|
|
34
|
+
FAILED_PRECONDITION: 9,
|
|
35
|
+
ABORTED: 10,
|
|
36
|
+
OUT_OF_RANGE: 11,
|
|
37
|
+
UNIMPLEMENTED: 12,
|
|
38
|
+
INTERNAL: 13,
|
|
39
|
+
UNAVAILABLE: 14,
|
|
40
|
+
DATA_LOSS: 15,
|
|
41
|
+
UNAUTHENTICATED: 16,
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// Stable AgentSam-specific reasons. Add reasons deliberately; callers should not branch on messages.
|
|
45
|
+
export const ERROR_REASON = Object.freeze({
|
|
46
|
+
UNKNOWN: 'unknown',
|
|
47
|
+
|
|
48
|
+
INPUT_INVALID: 'input_invalid',
|
|
49
|
+
INPUT_OUT_OF_RANGE: 'input_out_of_range',
|
|
50
|
+
PRECONDITION_FAILED: 'precondition_failed',
|
|
51
|
+
UNSUPPORTED_OPERATION: 'unsupported_operation',
|
|
52
|
+
|
|
53
|
+
AUTH_MISSING: 'auth_missing',
|
|
54
|
+
AUTH_INVALID: 'auth_invalid',
|
|
55
|
+
IDENTITY_MISSING: 'identity_missing',
|
|
56
|
+
PERMISSION_DENIED: 'permission_denied',
|
|
57
|
+
|
|
58
|
+
TARGET_NOT_FOUND: 'target_not_found',
|
|
59
|
+
ALREADY_EXISTS: 'already_exists',
|
|
60
|
+
CONFLICT: 'conflict',
|
|
61
|
+
STALE_VERSION: 'stale_version',
|
|
62
|
+
|
|
63
|
+
RATE_LIMITED: 'rate_limited',
|
|
64
|
+
QUOTA_EXHAUSTED: 'quota_exhausted',
|
|
65
|
+
CAPACITY_EXHAUSTED: 'capacity_exhausted',
|
|
66
|
+
|
|
67
|
+
TRANSPORT_UNREACHABLE: 'transport_unreachable',
|
|
68
|
+
TRANSPORT_REFUSED: 'transport_refused',
|
|
69
|
+
TRANSPORT_TIMEOUT: 'transport_timeout',
|
|
70
|
+
UPSTREAM_INVALID_RESPONSE: 'upstream_invalid_response',
|
|
71
|
+
DNS_UNRESOLVED: 'dns_unresolved',
|
|
72
|
+
TLS_HANDSHAKE_FAILED: 'tls_handshake_failed',
|
|
73
|
+
TLS_CERTIFICATE_INVALID: 'tls_certificate_invalid',
|
|
74
|
+
TUNNEL_CONNECTOR_UNAVAILABLE: 'tunnel_connector_unavailable',
|
|
75
|
+
|
|
76
|
+
CANCELLED: 'cancelled',
|
|
77
|
+
DEADLINE_EXCEEDED: 'deadline_exceeded',
|
|
78
|
+
EXECUTION_TIMEOUT: 'execution_timeout',
|
|
79
|
+
EXECUTION_FAILED: 'execution_failed',
|
|
80
|
+
|
|
81
|
+
PROVIDER_UNAVAILABLE: 'provider_unavailable',
|
|
82
|
+
MODEL_NOT_FOUND: 'model_not_found',
|
|
83
|
+
POLICY_BLOCKED: 'policy_blocked',
|
|
84
|
+
COPYRIGHT_BLOCKED: 'copyright_blocked',
|
|
85
|
+
|
|
86
|
+
PERSISTENCE_FAILED: 'persistence_failed',
|
|
87
|
+
DATA_CORRUPT: 'data_corrupt',
|
|
88
|
+
DATA_LOSS: 'data_loss',
|
|
89
|
+
|
|
90
|
+
REPOSITORY_SCOPE_VIOLATION: 'repository_scope_violation',
|
|
91
|
+
REPOSITORY_CHANGED: 'repository_changed',
|
|
92
|
+
INDEX_STALE: 'index_stale',
|
|
93
|
+
INDEX_FAILED: 'index_failed',
|
|
94
|
+
EMBEDDING_FAILED: 'embedding_failed',
|
|
95
|
+
VECTOR_STORE_FAILED: 'vector_store_failed',
|
|
96
|
+
|
|
97
|
+
CAD_CONSTRAINT_UNSATISFIED: 'cad_constraint_unsatisfied',
|
|
98
|
+
CAD_GEOMETRY_INVALID: 'cad_geometry_invalid',
|
|
99
|
+
CAD_KERNEL_FAILED: 'cad_kernel_failed',
|
|
100
|
+
CAD_EXPORT_FAILED: 'cad_export_failed',
|
|
101
|
+
CAD_RENDER_FAILED: 'cad_render_failed',
|
|
102
|
+
|
|
103
|
+
INTERNAL: 'internal',
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
const DEFAULT_HTTP_STATUS = Object.freeze({
|
|
107
|
+
[ERROR_CODE.CANCELLED]: 499,
|
|
108
|
+
[ERROR_CODE.UNKNOWN]: 500,
|
|
109
|
+
[ERROR_CODE.INVALID_ARGUMENT]: 400,
|
|
110
|
+
[ERROR_CODE.DEADLINE_EXCEEDED]: 504,
|
|
111
|
+
[ERROR_CODE.NOT_FOUND]: 404,
|
|
112
|
+
[ERROR_CODE.ALREADY_EXISTS]: 409,
|
|
113
|
+
[ERROR_CODE.PERMISSION_DENIED]: 403,
|
|
114
|
+
[ERROR_CODE.RESOURCE_EXHAUSTED]: 429,
|
|
115
|
+
[ERROR_CODE.FAILED_PRECONDITION]: 400,
|
|
116
|
+
[ERROR_CODE.ABORTED]: 409,
|
|
117
|
+
[ERROR_CODE.OUT_OF_RANGE]: 400,
|
|
118
|
+
[ERROR_CODE.UNIMPLEMENTED]: 501,
|
|
119
|
+
[ERROR_CODE.INTERNAL]: 500,
|
|
120
|
+
[ERROR_CODE.UNAVAILABLE]: 503,
|
|
121
|
+
[ERROR_CODE.DATA_LOSS]: 500,
|
|
122
|
+
[ERROR_CODE.UNAUTHENTICATED]: 401,
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
const HTTP_CODE_OVERRIDES = Object.freeze({
|
|
126
|
+
400: ERROR_CODE.INVALID_ARGUMENT,
|
|
127
|
+
401: ERROR_CODE.UNAUTHENTICATED,
|
|
128
|
+
403: ERROR_CODE.PERMISSION_DENIED,
|
|
129
|
+
404: ERROR_CODE.NOT_FOUND,
|
|
130
|
+
408: ERROR_CODE.DEADLINE_EXCEEDED,
|
|
131
|
+
409: ERROR_CODE.ABORTED,
|
|
132
|
+
412: ERROR_CODE.FAILED_PRECONDITION,
|
|
133
|
+
413: ERROR_CODE.RESOURCE_EXHAUSTED,
|
|
134
|
+
415: ERROR_CODE.INVALID_ARGUMENT,
|
|
135
|
+
422: ERROR_CODE.INVALID_ARGUMENT,
|
|
136
|
+
429: ERROR_CODE.RESOURCE_EXHAUSTED,
|
|
137
|
+
499: ERROR_CODE.CANCELLED,
|
|
138
|
+
500: ERROR_CODE.INTERNAL,
|
|
139
|
+
501: ERROR_CODE.UNIMPLEMENTED,
|
|
140
|
+
502: ERROR_CODE.UNAVAILABLE,
|
|
141
|
+
503: ERROR_CODE.UNAVAILABLE,
|
|
142
|
+
504: ERROR_CODE.DEADLINE_EXCEEDED,
|
|
143
|
+
520: ERROR_CODE.UNAVAILABLE,
|
|
144
|
+
521: ERROR_CODE.UNAVAILABLE,
|
|
145
|
+
522: ERROR_CODE.UNAVAILABLE,
|
|
146
|
+
523: ERROR_CODE.UNAVAILABLE,
|
|
147
|
+
524: ERROR_CODE.DEADLINE_EXCEEDED,
|
|
148
|
+
525: ERROR_CODE.UNAVAILABLE,
|
|
149
|
+
526: ERROR_CODE.UNAVAILABLE,
|
|
150
|
+
530: ERROR_CODE.UNAVAILABLE,
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
const GRPC_CODE_BY_NUMBER = new Map(Object.entries(GRPC_STATUS).map(([name, value]) => [value, ERROR_CODE[name]]));
|
|
154
|
+
|
|
155
|
+
export function canonicalCodeFromHttpStatus(status) {
|
|
156
|
+
return HTTP_CODE_OVERRIDES[Number(status)] || (Number(status) >= 500 ? ERROR_CODE.INTERNAL : ERROR_CODE.UNKNOWN);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export function canonicalCodeFromGrpcStatus(status) {
|
|
160
|
+
return GRPC_CODE_BY_NUMBER.get(Number(status)) || ERROR_CODE.UNKNOWN;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export function defaultHttpStatusForCode(code) {
|
|
164
|
+
return DEFAULT_HTTP_STATUS[code] || 500;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export function grpcStatusForCode(code) {
|
|
168
|
+
return GRPC_STATUS[code] ?? GRPC_STATUS.UNKNOWN;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export function classifyCloudflareFailure({ httpStatus, cloudflareCode } = {}) {
|
|
172
|
+
const status = Number(httpStatus || 0) || null;
|
|
173
|
+
const cf = Number(cloudflareCode || 0) || null;
|
|
174
|
+
if (cf === 1033) return { code: ERROR_CODE.UNAVAILABLE, reason: ERROR_REASON.TUNNEL_CONNECTOR_UNAVAILABLE, retryable: true };
|
|
175
|
+
if (status === 521) return { code: ERROR_CODE.UNAVAILABLE, reason: ERROR_REASON.TRANSPORT_REFUSED, retryable: true };
|
|
176
|
+
if (status === 522 || status === 523) return { code: ERROR_CODE.UNAVAILABLE, reason: ERROR_REASON.TRANSPORT_UNREACHABLE, retryable: true };
|
|
177
|
+
if (status === 524) return { code: ERROR_CODE.DEADLINE_EXCEEDED, reason: ERROR_REASON.TRANSPORT_TIMEOUT, retryable: true };
|
|
178
|
+
if (status === 525) return { code: ERROR_CODE.UNAVAILABLE, reason: ERROR_REASON.TLS_HANDSHAKE_FAILED, retryable: false };
|
|
179
|
+
if (status === 526) return { code: ERROR_CODE.UNAVAILABLE, reason: ERROR_REASON.TLS_CERTIFICATE_INVALID, retryable: false };
|
|
180
|
+
if (status === 520 || status === 502) return { code: ERROR_CODE.UNAVAILABLE, reason: ERROR_REASON.UPSTREAM_INVALID_RESPONSE, retryable: true };
|
|
181
|
+
if (status === 530) return { code: ERROR_CODE.UNAVAILABLE, reason: ERROR_REASON.TRANSPORT_UNREACHABLE, retryable: true };
|
|
182
|
+
return { code: canonicalCodeFromHttpStatus(status), reason: ERROR_REASON.UNKNOWN, retryable: status != null && status >= 500 };
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export function classifyOAuthFailure(error) {
|
|
186
|
+
switch (String(error || '').toLowerCase()) {
|
|
187
|
+
case 'invalid_client': return { code: ERROR_CODE.UNAUTHENTICATED, reason: ERROR_REASON.AUTH_INVALID, retryable: false };
|
|
188
|
+
case 'invalid_grant': return { code: ERROR_CODE.UNAUTHENTICATED, reason: ERROR_REASON.AUTH_INVALID, retryable: false };
|
|
189
|
+
case 'unauthorized_client':
|
|
190
|
+
case 'access_denied': return { code: ERROR_CODE.PERMISSION_DENIED, reason: ERROR_REASON.PERMISSION_DENIED, retryable: false };
|
|
191
|
+
case 'invalid_scope':
|
|
192
|
+
case 'invalid_request':
|
|
193
|
+
case 'unsupported_grant_type': return { code: ERROR_CODE.INVALID_ARGUMENT, reason: ERROR_REASON.INPUT_INVALID, retryable: false };
|
|
194
|
+
default: return { code: ERROR_CODE.UNKNOWN, reason: ERROR_REASON.UNKNOWN, retryable: false };
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export function createErrorEnvelope({
|
|
199
|
+
code = ERROR_CODE.UNKNOWN,
|
|
200
|
+
reason = ERROR_REASON.UNKNOWN,
|
|
201
|
+
message = 'Operation failed',
|
|
202
|
+
retryable = false,
|
|
203
|
+
retry_after_ms = null,
|
|
204
|
+
http_status = null,
|
|
205
|
+
grpc_status = null,
|
|
206
|
+
provider = null,
|
|
207
|
+
provider_code = null,
|
|
208
|
+
transport = null,
|
|
209
|
+
request_id = null,
|
|
210
|
+
trace_id = null,
|
|
211
|
+
resource = null,
|
|
212
|
+
details = null,
|
|
213
|
+
} = {}) {
|
|
214
|
+
if (!Object.values(ERROR_CODE).includes(code)) throw new TypeError(`Unknown AgentSam error code: ${code}`);
|
|
215
|
+
if (!Object.values(ERROR_REASON).includes(reason)) throw new TypeError(`Unknown AgentSam error reason: ${reason}`);
|
|
216
|
+
const effectiveHttp = http_status == null ? defaultHttpStatusForCode(code) : Number(http_status);
|
|
217
|
+
const effectiveGrpc = grpc_status == null ? grpcStatusForCode(code) : Number(grpc_status);
|
|
218
|
+
return Object.freeze({
|
|
219
|
+
ok: false,
|
|
220
|
+
schema_version: ERROR_SCHEMA_VERSION,
|
|
221
|
+
code,
|
|
222
|
+
reason,
|
|
223
|
+
message: String(message || 'Operation failed'),
|
|
224
|
+
retryable: Boolean(retryable),
|
|
225
|
+
retry_after_ms: retry_after_ms == null ? null : Math.max(0, Number(retry_after_ms)),
|
|
226
|
+
http_status: Number.isInteger(effectiveHttp) ? effectiveHttp : null,
|
|
227
|
+
grpc_status: Number.isInteger(effectiveGrpc) ? effectiveGrpc : null,
|
|
228
|
+
provider: provider || null,
|
|
229
|
+
provider_code: provider_code || null,
|
|
230
|
+
transport: transport || null,
|
|
231
|
+
request_id: request_id || null,
|
|
232
|
+
trace_id: trace_id || null,
|
|
233
|
+
resource: resource || null,
|
|
234
|
+
details: details || null,
|
|
235
|
+
});
|
|
236
|
+
}
|
package/src/errors/index.js
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
export {
|
|
2
|
+
ERROR_SCHEMA_VERSION,
|
|
3
|
+
ERROR_CODE,
|
|
4
|
+
ERROR_REASON,
|
|
5
|
+
GRPC_STATUS,
|
|
6
|
+
canonicalCodeFromHttpStatus,
|
|
7
|
+
canonicalCodeFromGrpcStatus,
|
|
8
|
+
defaultHttpStatusForCode,
|
|
9
|
+
grpcStatusForCode,
|
|
10
|
+
classifyCloudflareFailure,
|
|
11
|
+
classifyOAuthFailure,
|
|
12
|
+
createErrorEnvelope,
|
|
13
|
+
} from './contract.js';
|
|
14
|
+
|
|
1
15
|
export {
|
|
2
16
|
AgentSamDiagnosticError,
|
|
3
17
|
classifyOpenAIError,
|
package/src/index.js
CHANGED
|
@@ -19,7 +19,7 @@ export {
|
|
|
19
19
|
normalizeGitRemote,
|
|
20
20
|
resolveGitContext,
|
|
21
21
|
tryResolveGitContext,
|
|
22
|
-
} from '
|
|
22
|
+
} from '../packages/agentsam-repository/src/git-context.js';
|
|
23
23
|
export {
|
|
24
24
|
resolveAgentSamBaseUrl,
|
|
25
25
|
resolveBridgeKey,
|
|
@@ -40,6 +40,18 @@ export {
|
|
|
40
40
|
repositorySnapshot,
|
|
41
41
|
} from './capabilities/index.js';
|
|
42
42
|
export { getPreset, listPresets, resolvePreset, getAddon, listAddons } from './presets/index.js';
|
|
43
|
+
export {
|
|
44
|
+
COMPANY_REPOSITORY_GRAPH_SCHEMA_VERSION,
|
|
45
|
+
REPOSITORY_STATUSES,
|
|
46
|
+
REPOSITORY_CONTRACT_TYPES,
|
|
47
|
+
REPOSITORY_CONTRACT_STATUSES,
|
|
48
|
+
REPOSITORY_DEPENDENCY_TYPES,
|
|
49
|
+
REPOSITORY_DEPENDENCY_CRITICALITIES,
|
|
50
|
+
REPOSITORY_FAILURE_POLICIES,
|
|
51
|
+
createRepositoryIdentity,
|
|
52
|
+
createRepositoryContract,
|
|
53
|
+
createRepositoryDependency,
|
|
54
|
+
} from '../packages/agentsam-repository/src/contracts.js';
|
|
43
55
|
export {
|
|
44
56
|
DEFAULT_CONTEXT_RATIOS,
|
|
45
57
|
DEFAULT_RESULT_POLICY,
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { timingSafeEqual } from 'node:crypto';
|
|
2
|
+
|
|
3
|
+
export function createBearerTokenVerifier(token) {
|
|
4
|
+
if (typeof token !== 'string' || token.length < 32 || token.length > 256) {
|
|
5
|
+
throw new Error('Service token must be 32..256 characters.');
|
|
6
|
+
}
|
|
7
|
+
const secret = Buffer.from(token);
|
|
8
|
+
return authorization => {
|
|
9
|
+
const raw = typeof authorization === 'string' ? authorization : '';
|
|
10
|
+
const provided = Buffer.from(raw.replace(/^Bearer /, ''));
|
|
11
|
+
return provided.length === secret.length && timingSafeEqual(provided, secret);
|
|
12
|
+
};
|
|
13
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import {
|
|
2
|
+
grpc,
|
|
3
|
+
KnowledgeServiceClient,
|
|
4
|
+
decodeJob,
|
|
5
|
+
decodeJobEvent,
|
|
6
|
+
decodeRepositoryList,
|
|
7
|
+
encodeGetJobRequest,
|
|
8
|
+
encodeSubmitRequest,
|
|
9
|
+
fromGrpcError,
|
|
10
|
+
knowledgePb,
|
|
11
|
+
metadataForToken,
|
|
12
|
+
} from './grpc-codec.js';
|
|
13
|
+
|
|
14
|
+
function normalizeTimeout(value, fallback) {
|
|
15
|
+
const timeout = value === undefined ? fallback : value;
|
|
16
|
+
if (timeout == null) return null;
|
|
17
|
+
if (!Number.isFinite(timeout) || timeout <= 0) throw new Error('timeoutMs must be a positive number or null.');
|
|
18
|
+
return timeout;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function validateJobId(id) {
|
|
22
|
+
if (typeof id !== 'string' || !/^[a-f0-9-]{36}$/.test(id)) throw new Error('Invalid job id.');
|
|
23
|
+
return id;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Node/native gRPC client. Keep this separate from the fetch-only Worker-safe client. */
|
|
27
|
+
export function createKnowledgeGrpcClient({
|
|
28
|
+
target,
|
|
29
|
+
token,
|
|
30
|
+
timeoutMs = 15000,
|
|
31
|
+
credentials = grpc.credentials.createInsecure(),
|
|
32
|
+
clientOptions,
|
|
33
|
+
} = {}) {
|
|
34
|
+
if (typeof target !== 'string' || !target.trim()) throw new Error('A gRPC target is required.');
|
|
35
|
+
if (!token) throw new Error('A service token is required.');
|
|
36
|
+
const client = new KnowledgeServiceClient(target.trim(), credentials, clientOptions);
|
|
37
|
+
|
|
38
|
+
const unary = (method, request, options = {}) => new Promise((resolve, reject) => {
|
|
39
|
+
const timeout = normalizeTimeout(options.timeoutMs, timeoutMs);
|
|
40
|
+
const callOptions = timeout == null ? {} : { deadline: new Date(Date.now() + timeout) };
|
|
41
|
+
client[method](request, metadataForToken(token), callOptions, (error, response) => {
|
|
42
|
+
if (error) reject(fromGrpcError(error));
|
|
43
|
+
else resolve(response);
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
async function repositories(options = {}) {
|
|
48
|
+
const response = await unary('listRepositories', new knowledgePb.ListRepositoriesRequest(), options);
|
|
49
|
+
return decodeRepositoryList(response);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async function submit(body, idempotencyKey, options = {}) {
|
|
53
|
+
const response = await unary('submitJob', encodeSubmitRequest(body, idempotencyKey), options);
|
|
54
|
+
return decodeJob(response);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async function job(id, options = {}) {
|
|
58
|
+
const response = await unary('getJob', encodeGetJobRequest(validateJobId(id)), options);
|
|
59
|
+
return decodeJob(response);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async function* watch(id, options = {}) {
|
|
63
|
+
validateJobId(id);
|
|
64
|
+
const timeout = normalizeTimeout(options.timeoutMs, null);
|
|
65
|
+
const callOptions = timeout == null ? {} : { deadline: new Date(Date.now() + timeout) };
|
|
66
|
+
const stream = client.watchJob(
|
|
67
|
+
encodeGetJobRequest(id, knowledgePb.WatchJobRequest),
|
|
68
|
+
metadataForToken(token),
|
|
69
|
+
callOptions,
|
|
70
|
+
);
|
|
71
|
+
const queue = [];
|
|
72
|
+
let done = false;
|
|
73
|
+
let failure = null;
|
|
74
|
+
let wake = null;
|
|
75
|
+
const notify = () => { const resolve = wake; wake = null; resolve?.(); };
|
|
76
|
+
const onData = event => { queue.push(decodeJobEvent(event)); notify(); };
|
|
77
|
+
const onEnd = () => { done = true; notify(); };
|
|
78
|
+
const onError = error => {
|
|
79
|
+
if (options.signal?.aborted && error?.code === grpc.status.CANCELLED) {
|
|
80
|
+
done = true;
|
|
81
|
+
} else {
|
|
82
|
+
failure = fromGrpcError(error);
|
|
83
|
+
done = true;
|
|
84
|
+
}
|
|
85
|
+
notify();
|
|
86
|
+
};
|
|
87
|
+
const onAbort = () => stream.cancel();
|
|
88
|
+
stream.on('data', onData);
|
|
89
|
+
stream.once('end', onEnd);
|
|
90
|
+
stream.once('error', onError);
|
|
91
|
+
options.signal?.addEventListener('abort', onAbort, { once: true });
|
|
92
|
+
if (options.signal?.aborted) onAbort();
|
|
93
|
+
|
|
94
|
+
try {
|
|
95
|
+
while (!done || queue.length) {
|
|
96
|
+
if (!queue.length) await new Promise(resolve => { wake = resolve; });
|
|
97
|
+
while (queue.length) yield queue.shift();
|
|
98
|
+
if (failure) throw failure;
|
|
99
|
+
}
|
|
100
|
+
if (failure) throw failure;
|
|
101
|
+
} finally {
|
|
102
|
+
options.signal?.removeEventListener('abort', onAbort);
|
|
103
|
+
stream.off('data', onData);
|
|
104
|
+
if (!done) stream.cancel();
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return Object.freeze({
|
|
109
|
+
repositories,
|
|
110
|
+
submit,
|
|
111
|
+
job,
|
|
112
|
+
watch,
|
|
113
|
+
close() { client.close(); },
|
|
114
|
+
});
|
|
115
|
+
}
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import grpc from '@grpc/grpc-js';
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
import {
|
|
4
|
+
ERROR_CODE,
|
|
5
|
+
ERROR_REASON,
|
|
6
|
+
canonicalCodeFromHttpStatus,
|
|
7
|
+
createErrorEnvelope,
|
|
8
|
+
defaultHttpStatusForCode,
|
|
9
|
+
grpcStatusForCode,
|
|
10
|
+
} from '../../errors/contract.js';
|
|
11
|
+
|
|
12
|
+
const require = createRequire(import.meta.url);
|
|
13
|
+
const knowledgePb = require('../../rpc/generated/knowledge_pb.js');
|
|
14
|
+
const errorsPb = require('../../rpc/generated/errors_pb.js');
|
|
15
|
+
const { KnowledgeServiceService, KnowledgeServiceClient } = require('../../rpc/generated/knowledge_grpc_pb.js');
|
|
16
|
+
const { Timestamp } = require('google-protobuf/google/protobuf/timestamp_pb.js');
|
|
17
|
+
|
|
18
|
+
const operationToProto = {
|
|
19
|
+
index: knowledgePb.JobOperation.JOB_OPERATION_INDEX,
|
|
20
|
+
plan: knowledgePb.JobOperation.JOB_OPERATION_PLAN,
|
|
21
|
+
search: knowledgePb.JobOperation.JOB_OPERATION_SEARCH,
|
|
22
|
+
};
|
|
23
|
+
const operationFromProto = new Map(Object.entries(operationToProto).map(([key, value]) => [value, key]));
|
|
24
|
+
const statusToProto = {
|
|
25
|
+
queued: knowledgePb.JobStatus.JOB_STATUS_QUEUED,
|
|
26
|
+
running: knowledgePb.JobStatus.JOB_STATUS_RUNNING,
|
|
27
|
+
completed: knowledgePb.JobStatus.JOB_STATUS_COMPLETED,
|
|
28
|
+
failed: knowledgePb.JobStatus.JOB_STATUS_FAILED,
|
|
29
|
+
};
|
|
30
|
+
const statusFromProto = new Map(Object.entries(statusToProto).map(([key, value]) => [value, key]));
|
|
31
|
+
const protoErrorCode = code => errorsPb.ErrorCode[`ERROR_CODE_${code}`] ?? errorsPb.ErrorCode.ERROR_CODE_UNKNOWN;
|
|
32
|
+
const errorCodeNames = new Map(Object.entries(errorsPb.ErrorCode).map(([key, value]) => [value, key.replace(/^ERROR_CODE_/, '')]));
|
|
33
|
+
|
|
34
|
+
const grpcToHttp = new Map(Object.entries({
|
|
35
|
+
1: 499, 2: 500, 3: 400, 4: 504, 5: 404, 6: 409, 7: 403, 8: 429,
|
|
36
|
+
9: 400, 10: 409, 11: 400, 12: 501, 13: 500, 14: 503, 15: 500, 16: 401,
|
|
37
|
+
}).map(([key, value]) => [Number(key), value]));
|
|
38
|
+
|
|
39
|
+
function timestamp(value) {
|
|
40
|
+
const message = new Timestamp();
|
|
41
|
+
message.fromDate(new Date(value));
|
|
42
|
+
return message;
|
|
43
|
+
}
|
|
44
|
+
function iso(message) { return message ? message.toDate().toISOString() : null; }
|
|
45
|
+
function jobId(value) {
|
|
46
|
+
const id = new knowledgePb.JobId();
|
|
47
|
+
id.setValue(value || '');
|
|
48
|
+
return id;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export { grpc, knowledgePb, errorsPb, KnowledgeServiceService, KnowledgeServiceClient };
|
|
52
|
+
|
|
53
|
+
export function encodeJob(job) {
|
|
54
|
+
const message = new knowledgePb.Job();
|
|
55
|
+
message.setId(jobId(job.id));
|
|
56
|
+
message.setStatus(statusToProto[job.status] ?? knowledgePb.JobStatus.JOB_STATUS_UNSPECIFIED);
|
|
57
|
+
message.setAttempts(job.attempts || 0);
|
|
58
|
+
message.setCreatedAt(timestamp(job.created_at));
|
|
59
|
+
message.setUpdatedAt(timestamp(job.updated_at));
|
|
60
|
+
if (job.result !== null && job.result !== undefined) message.setResultJson(JSON.stringify(job.result));
|
|
61
|
+
if (job.error) {
|
|
62
|
+
const failure = new errorsPb.ErrorDetail();
|
|
63
|
+
failure.setCode(protoErrorCode(ERROR_CODE.INTERNAL));
|
|
64
|
+
failure.setMessage(job.error);
|
|
65
|
+
failure.setRetryable(false);
|
|
66
|
+
failure.setReason(ERROR_REASON.EXECUTION_FAILED);
|
|
67
|
+
failure.setHttpStatus(500);
|
|
68
|
+
message.setFailure(failure);
|
|
69
|
+
}
|
|
70
|
+
return message;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function decodeJob(message) {
|
|
74
|
+
const resultJson = message.getResultJson();
|
|
75
|
+
const failure = message.hasFailure() ? message.getFailure() : null;
|
|
76
|
+
return {
|
|
77
|
+
id: message.hasId() ? message.getId().getValue() : '',
|
|
78
|
+
status: statusFromProto.get(message.getStatus()) || 'unknown',
|
|
79
|
+
attempts: message.getAttempts(),
|
|
80
|
+
created_at: iso(message.getCreatedAt()),
|
|
81
|
+
updated_at: iso(message.getUpdatedAt()),
|
|
82
|
+
result: resultJson ? JSON.parse(resultJson) : null,
|
|
83
|
+
error: failure ? failure.getMessage() : null,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function encodeRepositoryList(value) {
|
|
88
|
+
const response = new knowledgePb.ListRepositoriesResponse();
|
|
89
|
+
response.setRepositoriesList(value.repositories.map(repo => {
|
|
90
|
+
const ref = new knowledgePb.RepositoryRef();
|
|
91
|
+
ref.setAlias(repo.alias);
|
|
92
|
+
ref.setRepositoryId(repo.repository_id || '');
|
|
93
|
+
return ref;
|
|
94
|
+
}));
|
|
95
|
+
response.setEmbeddingsEnabled(value.embeddings_enabled);
|
|
96
|
+
return response;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function decodeRepositoryList(message) {
|
|
100
|
+
const details = message.getRepositoriesList().map(repo => ({ alias: repo.getAlias(), repository_id: repo.getRepositoryId() }));
|
|
101
|
+
return { repositories: details.map(repo => repo.alias), repository_details: details, embeddings_enabled: message.getEmbeddingsEnabled() };
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function decodeSubmitRequest(message) {
|
|
105
|
+
const body = { repository: message.getRepository(), operation: operationFromProto.get(message.getOperation()) };
|
|
106
|
+
if (message.hasScope()) body.scope = message.getScope();
|
|
107
|
+
if (message.hasInclude()) body.include = message.getInclude().getValuesList();
|
|
108
|
+
if (message.hasExclude()) body.exclude = message.getExclude().getValuesList();
|
|
109
|
+
if (message.hasEmbed()) body.embed = message.getEmbed();
|
|
110
|
+
if (message.hasSemantic()) body.semantic = message.getSemantic();
|
|
111
|
+
if (message.hasQuery()) body.query = message.getQuery();
|
|
112
|
+
if (message.hasTopK()) body.top_k = message.getTopK();
|
|
113
|
+
if (message.hasTokenBudget()) body.token_budget = message.getTokenBudget();
|
|
114
|
+
if (message.hasGenerationId()) body.generation_id = message.getGenerationId();
|
|
115
|
+
if (message.hasMaxInputs()) body.max_inputs = message.getMaxInputs();
|
|
116
|
+
if (message.hasMaxCharacters()) body.max_characters = message.getMaxCharacters();
|
|
117
|
+
return { body, idempotencyKey: message.hasIdempotencyKey() ? message.getIdempotencyKey() : undefined };
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export function encodeSubmitRequest(body, idempotencyKey) {
|
|
121
|
+
const message = new knowledgePb.SubmitJobRequest();
|
|
122
|
+
if (body?.repository !== undefined) message.setRepository(body.repository);
|
|
123
|
+
if (body?.operation !== undefined) message.setOperation(operationToProto[body.operation] ?? knowledgePb.JobOperation.JOB_OPERATION_UNSPECIFIED);
|
|
124
|
+
if (body?.scope !== undefined) message.setScope(body.scope);
|
|
125
|
+
if (body?.include !== undefined) {
|
|
126
|
+
const list = new knowledgePb.StringList();
|
|
127
|
+
list.setValuesList(body.include);
|
|
128
|
+
message.setInclude(list);
|
|
129
|
+
}
|
|
130
|
+
if (body?.exclude !== undefined) {
|
|
131
|
+
const list = new knowledgePb.StringList();
|
|
132
|
+
list.setValuesList(body.exclude);
|
|
133
|
+
message.setExclude(list);
|
|
134
|
+
}
|
|
135
|
+
if (body?.embed !== undefined) message.setEmbed(body.embed);
|
|
136
|
+
if (body?.semantic !== undefined) message.setSemantic(body.semantic);
|
|
137
|
+
if (body?.query !== undefined) message.setQuery(body.query);
|
|
138
|
+
if (body?.top_k !== undefined) message.setTopK(body.top_k);
|
|
139
|
+
if (body?.token_budget !== undefined) message.setTokenBudget(body.token_budget);
|
|
140
|
+
if (body?.generation_id !== undefined) message.setGenerationId(body.generation_id);
|
|
141
|
+
if (body?.max_inputs !== undefined) message.setMaxInputs(body.max_inputs);
|
|
142
|
+
if (body?.max_characters !== undefined) message.setMaxCharacters(body.max_characters);
|
|
143
|
+
if (idempotencyKey !== undefined) message.setIdempotencyKey(idempotencyKey);
|
|
144
|
+
return message;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export function encodeGetJobRequest(id, RequestType = knowledgePb.GetJobRequest) {
|
|
148
|
+
const request = new RequestType();
|
|
149
|
+
request.setJobId(jobId(id));
|
|
150
|
+
return request;
|
|
151
|
+
}
|
|
152
|
+
export function decodeJobId(request) { return request.hasJobId() ? request.getJobId().getValue() : ''; }
|
|
153
|
+
|
|
154
|
+
export function encodeJobEvent(job, sequence) {
|
|
155
|
+
const event = new knowledgePb.JobEvent();
|
|
156
|
+
event.setJob(encodeJob(job));
|
|
157
|
+
event.setObservedAt(timestamp(new Date().toISOString()));
|
|
158
|
+
event.setSequence(sequence);
|
|
159
|
+
return event;
|
|
160
|
+
}
|
|
161
|
+
export function decodeJobEvent(event) {
|
|
162
|
+
return { job: decodeJob(event.getJob()), observed_at: iso(event.getObservedAt()), sequence: event.getSequence() };
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export function metadataForToken(token) {
|
|
166
|
+
if (!token) throw new Error('A service token is required.');
|
|
167
|
+
const metadata = new grpc.Metadata();
|
|
168
|
+
metadata.set('authorization', `Bearer ${token}`);
|
|
169
|
+
return metadata;
|
|
170
|
+
}
|
|
171
|
+
export function authorizationFromMetadata(metadata) {
|
|
172
|
+
const values = metadata?.get('authorization') || [];
|
|
173
|
+
return values.find(entry => typeof entry === 'string') || '';
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export function toGrpcError(error) {
|
|
177
|
+
const safeMessage = error?.status ? error.message : 'Service request failed.';
|
|
178
|
+
const legacyCode = error?.code === 'CONFLICT' ? ERROR_CODE.ABORTED : error?.code;
|
|
179
|
+
const code = Object.values(ERROR_CODE).includes(legacyCode) ? legacyCode : canonicalCodeFromHttpStatus(error?.status);
|
|
180
|
+
const reason = error?.reason || (code === ERROR_CODE.UNAUTHENTICATED ? ERROR_REASON.AUTH_INVALID
|
|
181
|
+
: code === ERROR_CODE.PERMISSION_DENIED ? ERROR_REASON.PERMISSION_DENIED
|
|
182
|
+
: code === ERROR_CODE.NOT_FOUND ? ERROR_REASON.TARGET_NOT_FOUND
|
|
183
|
+
: code === ERROR_CODE.ABORTED ? ERROR_REASON.CONFLICT
|
|
184
|
+
: code === ERROR_CODE.RESOURCE_EXHAUSTED ? ERROR_REASON.CAPACITY_EXHAUSTED
|
|
185
|
+
: code === ERROR_CODE.UNAVAILABLE ? ERROR_REASON.PROVIDER_UNAVAILABLE
|
|
186
|
+
: code === ERROR_CODE.INVALID_ARGUMENT ? ERROR_REASON.INPUT_INVALID
|
|
187
|
+
: ERROR_REASON.INTERNAL);
|
|
188
|
+
const envelope = createErrorEnvelope({
|
|
189
|
+
code,
|
|
190
|
+
reason,
|
|
191
|
+
message: safeMessage,
|
|
192
|
+
retryable: error?.retryable ?? (error?.status === 429 || error?.status === 503),
|
|
193
|
+
retry_after_ms: error?.retry_after_ms ?? null,
|
|
194
|
+
http_status: error?.status ?? defaultHttpStatusForCode(code),
|
|
195
|
+
provider: error?.provider ?? null,
|
|
196
|
+
provider_code: error?.provider_code ?? null,
|
|
197
|
+
});
|
|
198
|
+
const detail = new errorsPb.ErrorDetail();
|
|
199
|
+
detail.setCode(protoErrorCode(envelope.code));
|
|
200
|
+
detail.setMessage(envelope.message);
|
|
201
|
+
detail.setRetryable(envelope.retryable);
|
|
202
|
+
detail.setReason(envelope.reason);
|
|
203
|
+
if (envelope.http_status != null) detail.setHttpStatus(envelope.http_status);
|
|
204
|
+
if (envelope.retry_after_ms != null) detail.setRetryAfterMs(envelope.retry_after_ms);
|
|
205
|
+
if (envelope.provider) detail.setProvider(envelope.provider);
|
|
206
|
+
if (envelope.provider_code) detail.setProviderCode(envelope.provider_code);
|
|
207
|
+
const metadata = new grpc.Metadata();
|
|
208
|
+
metadata.set('agentsam-error-bin', Buffer.from(detail.serializeBinary()));
|
|
209
|
+
return Object.assign(new Error(safeMessage), {
|
|
210
|
+
code: grpcStatusForCode(envelope.code),
|
|
211
|
+
details: safeMessage,
|
|
212
|
+
metadata,
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
export function fromGrpcError(error) {
|
|
217
|
+
let detail = null;
|
|
218
|
+
try {
|
|
219
|
+
const values = error?.metadata?.get('agentsam-error-bin') || [];
|
|
220
|
+
const binary = values.find(value => Buffer.isBuffer(value));
|
|
221
|
+
if (binary) detail = errorsPb.ErrorDetail.deserializeBinary(new Uint8Array(binary));
|
|
222
|
+
} catch { /* fall back to gRPC status */ }
|
|
223
|
+
const message = detail?.getMessage() || error?.details || error?.message || 'Knowledge RPC failed.';
|
|
224
|
+
const rpcCode = detail ? errorCodeNames.get(detail.getCode()) || 'UNKNOWN' : undefined;
|
|
225
|
+
return Object.assign(new Error(message), {
|
|
226
|
+
status: detail?.hasHttpStatus?.() ? detail.getHttpStatus() : grpcToHttp.get(error?.code) ?? 500,
|
|
227
|
+
grpcCode: error?.code,
|
|
228
|
+
rpcCode,
|
|
229
|
+
reason: detail?.getReason?.() || ERROR_REASON.UNKNOWN,
|
|
230
|
+
retryable: detail?.getRetryable() || false,
|
|
231
|
+
retry_after_ms: detail?.hasRetryAfterMs?.() ? detail.getRetryAfterMs() : null,
|
|
232
|
+
provider: detail?.hasProvider?.() ? detail.getProvider() : null,
|
|
233
|
+
provider_code: detail?.hasProviderCode?.() ? detail.getProviderCode() : null,
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
export function isTerminalJob(job) { return job.status === 'completed' || job.status === 'failed'; }
|