@mininglamp-oss/octo-runtime-client 0.0.0-stage → 1.1.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/LICENSE +190 -0
  2. package/README.md +38 -2
  3. package/build-info.json +5 -0
  4. package/dist/async-question-reply.d.ts +30 -0
  5. package/dist/async-question-reply.js +199 -0
  6. package/dist/browser.d.ts +1 -0
  7. package/dist/browser.js +1 -0
  8. package/dist/client.d.ts +10 -0
  9. package/dist/client.js +465 -0
  10. package/dist/index.d.ts +6 -0
  11. package/dist/index.js +6 -0
  12. package/dist/internal.d.ts +4 -0
  13. package/dist/internal.js +5 -0
  14. package/dist/invocation-lifecycle.d.ts +59 -0
  15. package/dist/invocation-lifecycle.js +287 -0
  16. package/dist/notification.d.ts +1 -0
  17. package/dist/notification.js +1 -0
  18. package/dist/protocol.d.ts +1 -0
  19. package/dist/protocol.js +1 -0
  20. package/dist/runtime-path.d.ts +21 -0
  21. package/dist/runtime-path.js +138 -0
  22. package/dist/startup-catalog.d.ts +8 -0
  23. package/dist/startup-catalog.js +65 -0
  24. package/dist/startup-diagnostics.d.ts +82 -0
  25. package/dist/startup-diagnostics.js +371 -0
  26. package/dist/submission-error.d.ts +10 -0
  27. package/dist/submission-error.js +23 -0
  28. package/dist/types.d.ts +72 -0
  29. package/dist/types.js +10 -0
  30. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/actmode.d.ts +4 -0
  31. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/actmode.js +36 -0
  32. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/adapter-metadata.d.ts +7 -0
  33. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/adapter-metadata.js +1 -0
  34. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/browser.d.ts +7 -0
  35. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/browser.js +6 -0
  36. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/context-catalog.d.ts +11 -0
  37. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/context-catalog.js +33 -0
  38. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/context-configuration.d.ts +5 -0
  39. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/context-configuration.js +58 -0
  40. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/context-hash.d.ts +2 -0
  41. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/context-hash.js +5 -0
  42. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/context.d.ts +29 -0
  43. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/context.js +566 -0
  44. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/execution-plan.d.ts +3 -0
  45. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/execution-plan.js +80 -0
  46. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/experience.d.ts +4 -0
  47. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/experience.js +166 -0
  48. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/failure.d.ts +82 -0
  49. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/failure.js +562 -0
  50. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/generated/protocol-types.d.ts +3022 -0
  51. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/generated/protocol-types.js +6 -0
  52. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/index.d.ts +124 -0
  53. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/index.js +709 -0
  54. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/invoke-input.d.ts +129 -0
  55. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/invoke-input.js +177 -0
  56. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/invoke-size.d.ts +3 -0
  57. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/invoke-size.js +11 -0
  58. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/json-size.d.ts +2 -0
  59. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/json-size.js +91 -0
  60. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/model-context.d.ts +3 -0
  61. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/model-context.js +9 -0
  62. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/multimodal.d.ts +15 -0
  63. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/multimodal.js +166 -0
  64. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/notification.d.ts +2 -0
  65. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/notification.js +137 -0
  66. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/questions.d.ts +9 -0
  67. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/questions.js +84 -0
  68. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/run-protocol.d.ts +20 -0
  69. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/run-protocol.js +294 -0
  70. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/runtime-selection.d.ts +3 -0
  71. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/runtime-selection.js +68 -0
  72. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/service-processes.d.ts +5 -0
  73. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/service-processes.js +65 -0
  74. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/stream-limits.d.ts +27 -0
  75. package/node_modules/@mininglamp-oss/octo-runtime-protocol/dist/stream-limits.js +56 -0
  76. package/node_modules/@mininglamp-oss/octo-runtime-protocol/package.json +15 -0
  77. package/package.json +55 -4
  78. package/schema/context-configuration-fixtures.json +1219 -0
  79. package/schema/context-fixtures.json +5988 -0
  80. package/schema/kernel-client.behavior.json +56 -0
  81. package/schema/kernel-ipc-fixtures.json +53 -0
  82. package/schema/kernel-ipc.schema.json +538 -0
  83. package/schema/native-invoke-fixtures.json +116 -0
  84. package/schema/protocol.schema.json +4525 -0
  85. package/schema/run-protocol-fixtures.json +761 -0
  86. package/schema/runtime-selection-fixtures.json +663 -0
  87. package/schema/session-name-fixtures.json +191 -0
@@ -0,0 +1,562 @@
1
+ import { randomUUID } from "node:crypto";
2
+ /** Runtime-owned subset of the global catalog in improvement 70-01. */
3
+ export const FAILURE_CATALOG = Object.freeze({
4
+ "OH-INP-5001": entry("input_modality", "所选模型或运行方式不支持图片输入,请更换模型或移除图片。", "error.input.unsupported_modality", false, "change_model"),
5
+ "OH-INP-5002": entry("input_limit", "图片数量或大小超过本次输入限制。", "error.input.image_limit_exceeded", false, "retry"),
6
+ "OH-INP-5003": entry("input_resource", "本次输入的图片暂时无法读取。", "error.input.artifact_unavailable", true, "retry"),
7
+ "OH-INP-5004": entry("input_integrity", "图片内容与提交的资源记录不一致,请重新添加图片。", "error.input.artifact_integrity_mismatch", false, "retry"),
8
+ "OH-INP-5005": entry("input_permission", "当前执行没有读取图片的权限。", "error.input.artifact_access_denied", false, "fix_permissions"),
9
+ "OH-RUN-2002": entry("runtime_admission", "运行环境正在维护,本轮任务未启动,请稍后重试。", "error.runtime.draining", true, "retry_later", 30_000),
10
+ "OH-RUN-2003": entry("runtime_capacity", "运行环境当前繁忙,暂时无法接收新任务。", "error.runtime.capacity", true, "retry_later", 30_000),
11
+ "OH-RUN-2004": entry("session_queue_capacity", "当前会话的排队任务已满,请稍后重试。", "error.runtime.session_queue_capacity", true, "retry_later", 30_000),
12
+ "OH-RUN-2005": entry("channel_delivery_unknown", "回复已尝试发送,但未收到消息服务的确认,可能已送达。请先检查聊天记录,避免重复提交任务。", "error.channel.delivery_unknown", false, "contact_admin"),
13
+ "OH-RUN-2008": entry("runtime_local_store", "运行设备存储异常,本轮任务已安全结束。", "error.runtime.local_store", true, "check_runtime"),
14
+ "OH-RUN-2009": entry("runtime_stream_capacity", "运行输出队列超过容量限制,本轮任务已停止。请检查运行设备的输出容量配置。", "error.runtime.stream_queue_capacity", false, "check_runtime"),
15
+ "OH-RUN-2010": entry("channel_session_store", "会话读取失败,尚未发起执行,请检查运行设备或稍后重试。", "error.channel.session_store", true, "check_runtime"),
16
+ "OH-RUN-2011": entry("channel_presentation_initialization", "状态展示初始化失败,尚未发起执行,请检查运行设备或稍后重试。", "error.channel.presentation_initialization", true, "check_runtime"),
17
+ "OH-RUN-2012": entry("channel_invocation_admission", "执行服务当前不接收新任务,本次尝试尚未提交,请稍后重试。", "error.channel.invocation_admission", true, "retry_later"),
18
+ "OH-ADP-3002": entry("adapter_dependency", "未找到 Agent 命令或必要程序。", "error.adapter.command_not_found", false, "install_dependency"),
19
+ "OH-ADP-3003": entry("adapter_permission", "Agent 进程没有访问所需资源的权限。", "error.adapter.permission_denied", false, "fix_permissions"),
20
+ "OH-ADP-3004": entry("adapter_invocation", "Agent 适配器启动或执行失败。", "error.adapter.invocation_failed", false, "contact_admin"),
21
+ "OH-ADP-3005": entry("adapter_protocol", "Agent 返回了无法识别的协议数据。", "error.adapter.protocol_invalid", false, "contact_admin"),
22
+ "OH-ADP-3006": entry("adapter_timeout", "Agent 响应超时。", "error.adapter.timeout", true, "retry"),
23
+ "OH-ADP-3007": entry("adapter_start_timeout", "Agent 启动等待超时。", "error.adapter.first_frame_timeout", true, "retry"),
24
+ "OH-ADP-3008": entry("adapter_model_idle", "Agent 长时间没有新的执行进展,任务已停止。", "error.adapter.model_idle_timeout", true, "retry"),
25
+ "OH-ADP-3009": entry("adapter_tool_idle", "工具长时间没有新的执行进展,任务已停止。", "error.adapter.tool_idle_timeout", true, "retry"),
26
+ "OH-ADP-3010": entry("adapter_tool_deadline", "工具执行已达到单次运行时限。", "error.adapter.tool_deadline", true, "retry"),
27
+ "OH-ADP-3011": entry("adapter_run_deadline", "任务已达到总运行时限。", "error.adapter.run_deadline", true, "retry"),
28
+ "OH-ADP-3012": entry("adapter_cleanup_timeout", "Agent 结束清理超时,请检查运行环境。", "error.adapter.cleanup_timeout", false, "check_runtime"),
29
+ "OH-ADP-3013": entry("adapter_rpc_timeout", "Agent 协议请求响应超时。", "error.adapter.rpc_timeout", true, "check_runtime"),
30
+ "OH-ADP-3014": entry("adapter_frame_capacity", "Agent 输出帧超过运行时容量限制,本轮任务已停止。", "error.adapter.host_frame_too_large", false, "check_runtime"),
31
+ "OH-PRV-4001": entry("provider_authentication", "模型服务凭据无效或已失效。", "error.provider.credentials_invalid", false, "update_credentials"),
32
+ "OH-PRV-4002": entry("provider_authorization", "当前账号无权使用所选模型或组织。", "error.provider.permission_denied", false, "contact_admin"),
33
+ "OH-PRV-4003": entry("provider_model", "所选模型不存在或当前不可用。", "error.provider.model_not_found", false, "change_model"),
34
+ "OH-PRV-4004": entry("provider_quota", "模型调用额度不足。", "error.provider.quota_exhausted", false, "contact_admin"),
35
+ "OH-PRV-4005": entry("provider_rate_limit", "模型服务请求过于频繁,请稍后重试。", "error.provider.rate_limited", true, "retry_later", 30_000),
36
+ "OH-PRV-4006": entry("provider_availability", "模型服务暂时不可用。", "error.provider.unavailable", true, "retry_later", 30_000),
37
+ "OH-PRV-4007": entry("provider_client_policy", "当前授权方式不允许此客户端调用。", "error.provider.client_not_allowed", false, "update_credentials"),
38
+ "OH-PRV-4008": entry("provider_configuration", "此 Agent 绑定的 Provider 配置已被删除或不可用。", "error.provider.configuration_missing", false, "change_model"),
39
+ "OH-PRV-4009": entry("provider_message_capacity", "模型服务返回的单条消息超过运行时容量限制,本轮任务已停止。", "error.provider.message_too_large", false, "check_runtime"),
40
+ "OH-PRV-4010": entry("provider_acp", "ACP 模型请求失败。", "error.provider.acp_request_failed", false, "contact_admin"),
41
+ "OH-PRV-4099": entry("provider_unknown", "模型服务调用失败,暂时无法识别具体原因。", "error.provider.unknown", true, "retry"),
42
+ "OH-SYS-9001": entry("system_unknown", "系统发生未知错误。", "error.system.unknown", true, "retry")
43
+ });
44
+ /** Codex 本地审批/发送失败的封闭原因表;与 Provider 原生 RPC 错误分别处理。 */
45
+ const CODEX_LOCAL_FAILURES = Object.freeze({
46
+ APPROVAL_PENDING_LIMIT_EXCEEDED: "OH-ADP-3004",
47
+ CODEX_INTERACTION_PENDING_LIMIT_EXCEEDED: "OH-ADP-3004",
48
+ INTERACTION_PENDING_LIMIT_EXCEEDED: "OH-ADP-3004",
49
+ CODEX_INTERACTION_BYTES_EXCEEDED: "OH-ADP-3004",
50
+ CODEX_STDIN_QUEUE_ITEMS_EXCEEDED: "OH-ADP-3004",
51
+ CODEX_STDIN_QUEUE_BYTES_EXCEEDED: "OH-ADP-3004",
52
+ CODEX_STDIN_WRITE_FAILED: "OH-ADP-3004",
53
+ CODEX_TRANSPORT_FAILED: "OH-ADP-3004",
54
+ CODEX_STDIN_STALLED: "OH-ADP-3013",
55
+ CODEX_STDIN_DELIVERY_TIMEOUT: "OH-ADP-3013",
56
+ INTERACTION_EXPIRED: "OH-ADP-3004",
57
+ APPROVAL_GRANT_SCOPE_UNSUPPORTED: "OH-ADP-3004",
58
+ APPROVAL_OPERATION_UNSUPPORTED: "OH-ADP-3004"
59
+ });
60
+ export function isCodexLocalFailureReason(value) {
61
+ return typeof value === "string" && Object.hasOwn(CODEX_LOCAL_FAILURES, value);
62
+ }
63
+ /** native 仅允许中央目录的本地失败;不开放任意 params 或 Provider 自报错误提升。 */
64
+ export function isCodexLocalFailure(failure) {
65
+ const reason = failure.reasonCode;
66
+ if (!isCodexLocalFailureReason(reason))
67
+ return false;
68
+ const catalog = FAILURE_CATALOG[CODEX_LOCAL_FAILURES[reason]];
69
+ return (failure.source.component === "octo-runtime" &&
70
+ failure.source.adapter === "codex" &&
71
+ failure.code === CODEX_LOCAL_FAILURES[reason] &&
72
+ failure.category === catalog.category &&
73
+ failure.messageKey === catalog.messageKey &&
74
+ failure.retryable === false &&
75
+ failure.action.type === "contact_admin" &&
76
+ failure.action.labelKey === "action.contact_admin" &&
77
+ failure.retryAfterMs === undefined &&
78
+ Object.keys(failure.params).length === 0 &&
79
+ (failure.cause === undefined ||
80
+ (failure.cause.reasonCode === reason && failure.cause.httpStatus === undefined)));
81
+ }
82
+ /** Classify untrusted adapter/provider error data without copying raw messages or secrets into the result. */
83
+ export function classifyFailure(input, context) {
84
+ const evidence = failureEvidence(input);
85
+ // 执行前分类同时依赖可信调用阶段;不能由 Provider 的同名原因推断执行从未提交。
86
+ const channelCode = context.stage === "channel_session_read" && evidence.explicitCode === "IM_SESSION_STORE_FAILED"
87
+ ? "OH-RUN-2010"
88
+ : context.stage === "channel_presentation_initialize" &&
89
+ evidence.explicitCode === "IM_PRESENTATION_INITIALIZATION_FAILED"
90
+ ? "OH-RUN-2011"
91
+ : context.stage === "channel_admission" &&
92
+ context.invocationNotSubmitted === true &&
93
+ [
94
+ "CLIENT_INVOCATION_ADMISSION_CLOSED",
95
+ "KERNEL_UNAVAILABLE",
96
+ "WORKER_RESTART_EXHAUSTED"
97
+ ].includes(evidence.explicitCode ?? "")
98
+ ? "OH-RUN-2012"
99
+ : undefined;
100
+ const classification = channelCode
101
+ ? result(channelCode, evidence.explicitCode)
102
+ : classify(evidence, context.defaultDomain, context.source?.adapter);
103
+ const catalog = FAILURE_CATALOG[classification.code];
104
+ const codexLocal = context.source?.adapter === "codex" && isCodexLocalFailureReason(classification.reasonCode);
105
+ return {
106
+ schemaVersion: 1,
107
+ code: classification.code,
108
+ reasonCode: classification.reasonCode,
109
+ category: catalog.category,
110
+ stage: context.stage,
111
+ messageKey: catalog.messageKey,
112
+ params: codexLocal
113
+ ? {}
114
+ : classification.reasonCode === "CODEX_FINALIZATION_TIMEOUT"
115
+ ? {
116
+ configParameter: "execution_profile.agent_config.providerOptions.finalizationTimeoutMs"
117
+ }
118
+ : timeoutConfiguration(classification.code, context.source?.adapter),
119
+ retryable: codexLocal || classification.reasonCode === "CODEX_FINALIZATION_TIMEOUT"
120
+ ? false
121
+ : catalog.retryable,
122
+ ...(catalog.retryAfterMs === undefined ? {} : { retryAfterMs: catalog.retryAfterMs }),
123
+ action: codexLocal
124
+ ? { type: "contact_admin", labelKey: "action.contact_admin" }
125
+ : catalog.action,
126
+ diagnosticId: diagnosticIdForContext(context),
127
+ source: context.source ?? { component: "octo-runtime" },
128
+ cause: {
129
+ reasonCode: classification.reasonCode,
130
+ ...(codexLocal || evidence.httpStatus === undefined
131
+ ? {}
132
+ : { httpStatus: evidence.httpStatus })
133
+ },
134
+ occurredAt: context.occurredAt ?? new Date().toISOString()
135
+ };
136
+ }
137
+ /**
138
+ * Produce the backwards-compatible terminal payload (`code`/`message`) plus
139
+ * the structured envelope. Only allowlisted metadata survives this boundary.
140
+ */
141
+ export function failurePayload(input, context) {
142
+ const record = asRecord(input) ?? {};
143
+ const existing = context.preserveExisting && isFailureEnvelope(record.failure) ? record.failure : undefined;
144
+ const runDiagnosticId = diagnosticIdFromRunId(context.runId);
145
+ const classified = existing
146
+ ? runDiagnosticId === undefined
147
+ ? existing
148
+ : { ...existing, diagnosticId: runDiagnosticId }
149
+ : classifyFailure(input, context);
150
+ // Adapters may already have normalized a failure; revalidate only the safe
151
+ // timeout fields so Host normalization does not erase their evidence.
152
+ const upstreamParams = asRecord(asRecord(record.failure)?.params) ?? {};
153
+ const timeoutStage = safeTimeoutStage(record.timeoutStage ?? upstreamParams.timeoutStage);
154
+ const timeoutMs = safeTimeoutMs(record.timeoutMs ?? upstreamParams.timeoutMs);
155
+ const reason = record.timeoutReason ?? upstreamParams.timeoutReason;
156
+ const timeoutReason = typeof reason === "string" &&
157
+ [
158
+ "first_frame",
159
+ "model_silence",
160
+ "tool_idle",
161
+ "tool_deadline",
162
+ "run_deadline",
163
+ "cleanup_timeout",
164
+ "rpc_timeout"
165
+ ].includes(reason)
166
+ ? reason
167
+ : undefined;
168
+ const baseFailure = timeoutStage || timeoutMs !== undefined
169
+ ? {
170
+ ...classified,
171
+ params: {
172
+ ...classified.params,
173
+ ...(timeoutStage ? { timeoutStage } : {}),
174
+ ...(timeoutMs === undefined ? {} : { timeoutMs }),
175
+ ...(timeoutReason ? { timeoutReason } : {}),
176
+ ...safeWatchdogEvidence(record.watchdog ?? upstreamParams)
177
+ }
178
+ }
179
+ : classified;
180
+ const capacity = safeCapacityEvidence(record);
181
+ const failure = Object.keys(capacity).length > 0
182
+ ? { ...baseFailure, params: { ...baseFailure.params, ...capacity } }
183
+ : baseFailure;
184
+ const adapterType = safeAdapterType(record.adapterType) ?? failure.source.adapter;
185
+ const legacyCode = safeReasonCode(record.code) ?? failure.reasonCode;
186
+ return {
187
+ code: legacyCode,
188
+ message: failureMessage(failure.code),
189
+ ...(adapterType ? { adapterType } : {}),
190
+ failure
191
+ };
192
+ }
193
+ function diagnosticIdForContext(context) {
194
+ return (diagnosticIdFromRunId(context.runId) ??
195
+ context.diagnosticId ??
196
+ `diag_${randomUUID().replaceAll("-", "")}`);
197
+ }
198
+ function diagnosticIdFromRunId(runId) {
199
+ const match = /^run_([0-9a-f]{32})$/.exec(runId ?? "");
200
+ return match ? `diag_${match[1]}` : undefined;
201
+ }
202
+ function safeTimeoutStage(value) {
203
+ return typeof value === "string" &&
204
+ ["starting", "model_active", "tool_active", "run_deadline", "cleanup", "rpc"].includes(value)
205
+ ? value
206
+ : undefined;
207
+ }
208
+ function timeoutConfiguration(code, adapter) {
209
+ const fields = {
210
+ "OH-ADP-3007": "first_frame_timeout_ms",
211
+ "OH-ADP-3008": "model_idle_timeout_ms",
212
+ "OH-ADP-3009": "tool_timeout_ms",
213
+ "OH-ADP-3010": "tool_wall_timeout_ms",
214
+ "OH-ADP-3011": "run_timeout_ms",
215
+ "OH-ADP-3012": "cancel_grace_ms"
216
+ };
217
+ if (fields[code])
218
+ return { configParameter: `execution_profile.runtime_limits.${fields[code]}` };
219
+ if (code === "OH-ADP-3013" && adapter === "codex")
220
+ return {
221
+ configParameter: "execution_profile.agent_config.providerOptions.appServerRequestTimeoutMs"
222
+ };
223
+ return {};
224
+ }
225
+ function safeWatchdogEvidence(value) {
226
+ const input = asRecord(value) ?? {};
227
+ const result = {};
228
+ for (const key of [
229
+ "elapsedMs",
230
+ "phaseAgeMs",
231
+ "lastCanonicalAgeMs",
232
+ "lastProgressAgeMs",
233
+ "lastTransportAgeMs",
234
+ "openToolCount",
235
+ "retryCount"
236
+ ]) {
237
+ const v = input[key];
238
+ // 时间证据可能超过预算(事件循环延迟);计数仍沿用既有过滤上限。
239
+ const maximum = key.endsWith("Ms") ? Number.MAX_SAFE_INTEGER : 86_400_000;
240
+ if (typeof v === "number" && Number.isSafeInteger(v) && v >= 0 && v <= maximum)
241
+ result[key] = v;
242
+ }
243
+ if (input.activitySupport === "native_progress" || input.activitySupport === "canonical_only")
244
+ result.activitySupport = input.activitySupport;
245
+ return result;
246
+ }
247
+ export function safeCapacityEvidence(value) {
248
+ const input = asRecord(value) ?? {};
249
+ const result = {};
250
+ if (typeof input.boundary === "string" && /^[a-z0-9][a-z0-9._-]{0,127}$/i.test(input.boundary))
251
+ result.boundary = input.boundary;
252
+ for (const key of ["measuredBytes", "limitBytes", "itemCount", "limitItems"]) {
253
+ const entry = input[key];
254
+ if (typeof entry === "number" && Number.isSafeInteger(entry) && entry >= 0)
255
+ result[key] = entry;
256
+ }
257
+ return result;
258
+ }
259
+ function safeTimeoutMs(value) {
260
+ return typeof value === "number" &&
261
+ Number.isSafeInteger(value) &&
262
+ value >= 1 &&
263
+ value <= Number.MAX_SAFE_INTEGER
264
+ ? value
265
+ : undefined;
266
+ }
267
+ export function failureMessage(code) {
268
+ return isPublicFailureCode(code)
269
+ ? FAILURE_CATALOG[code].message
270
+ : FAILURE_CATALOG["OH-SYS-9001"].message;
271
+ }
272
+ export function isFailureEnvelope(value) {
273
+ const record = asRecord(value);
274
+ if (!record)
275
+ return false;
276
+ const source = asRecord(record.source);
277
+ const action = asRecord(record.action);
278
+ if (!source || !action)
279
+ return false;
280
+ return (record.schemaVersion === 1 &&
281
+ typeof record.code === "string" &&
282
+ /^OH-[A-Z]{3}-\d{4}$/.test(record.code) &&
283
+ typeof record.reasonCode === "string" &&
284
+ record.reasonCode.length > 0 &&
285
+ typeof record.category === "string" &&
286
+ record.category.length > 0 &&
287
+ typeof record.stage === "string" &&
288
+ record.stage.length > 0 &&
289
+ typeof record.messageKey === "string" &&
290
+ record.messageKey.length > 0 &&
291
+ asRecord(record.params) !== undefined &&
292
+ typeof record.retryable === "boolean" &&
293
+ typeof record.diagnosticId === "string" &&
294
+ /^diag_[0-9a-f]{32}$/.test(record.diagnosticId) &&
295
+ (source.component === "octo-runtime" || source.component === "octo-agent-worker") &&
296
+ typeof action.type === "string" &&
297
+ typeof action.labelKey === "string" &&
298
+ typeof record.occurredAt === "string" &&
299
+ !Number.isNaN(Date.parse(record.occurredAt)));
300
+ }
301
+ function entry(category, message, messageKey, retryable, actionType, retryAfterMs) {
302
+ return {
303
+ category,
304
+ message,
305
+ messageKey,
306
+ retryable,
307
+ ...(retryAfterMs === undefined ? {} : { retryAfterMs }),
308
+ action: { type: actionType, labelKey: `action.${actionType}` }
309
+ };
310
+ }
311
+ function failureEvidence(input) {
312
+ const values = [];
313
+ let explicitCode;
314
+ let httpStatus;
315
+ const visit = (value, depth) => {
316
+ if (depth > 4 || value === null || value === undefined)
317
+ return;
318
+ if (value instanceof Error) {
319
+ const code = safeReasonCode(value.code);
320
+ if (code)
321
+ explicitCode ??= code;
322
+ values.push(value.name, value.message, code ?? "");
323
+ visit(value.cause, depth + 1);
324
+ return;
325
+ }
326
+ if (typeof value === "string" || typeof value === "number") {
327
+ values.push(String(value));
328
+ return;
329
+ }
330
+ const record = asRecord(value);
331
+ if (!record)
332
+ return;
333
+ for (const key of ["code", "reasonCode", "reason_code"]) {
334
+ const code = safeReasonCode(record[key]);
335
+ if (code) {
336
+ explicitCode ??= code;
337
+ values.push(code);
338
+ }
339
+ }
340
+ for (const key of ["status", "statusCode", "httpStatus"]) {
341
+ const status = numericHttpStatus(record[key]);
342
+ if (status)
343
+ httpStatus ??= status;
344
+ }
345
+ for (const key of [
346
+ "message",
347
+ "reason",
348
+ "type",
349
+ "name",
350
+ "error",
351
+ "providerError",
352
+ "data",
353
+ "cause"
354
+ ]) {
355
+ visit(record[key], depth + 1);
356
+ }
357
+ };
358
+ visit(input, 0);
359
+ return { fingerprint: values.join(" ").toLowerCase().slice(0, 12_000), explicitCode, httpStatus };
360
+ }
361
+ function classify(evidence, defaultDomain, adapter) {
362
+ const text = evidence.fingerprint;
363
+ const explicit = evidence.explicitCode?.toUpperCase();
364
+ if (adapter === "codex" && isCodexLocalFailureReason(explicit))
365
+ return result(CODEX_LOCAL_FAILURES[explicit], explicit);
366
+ // ACP 已明确声明失败;meta 仅作诊断,不从其中的状态码或文字猜测其他类别。
367
+ if (explicit === "ACP_MODEL_REQUEST_FAILED")
368
+ return result("OH-PRV-4010", explicit);
369
+ for (const [reason, code] of Object.entries({
370
+ UNSUPPORTED_INPUT_MODALITY: "OH-INP-5001",
371
+ IMAGE_RECOVERY_INPUT_REQUIRED: "OH-INP-5001",
372
+ IMAGE_LIMIT_EXCEEDED: "OH-INP-5002",
373
+ ARTIFACT_UNAVAILABLE: "OH-INP-5003",
374
+ ARTIFACT_INTEGRITY_MISMATCH: "OH-INP-5004",
375
+ ARTIFACT_ACCESS_DENIED: "OH-INP-5005"
376
+ })) {
377
+ if (explicit === reason || text.includes(reason.toLowerCase()))
378
+ return result(code, reason);
379
+ }
380
+ const timeouts = {
381
+ ADAPTER_FIRST_FRAME_TIMEOUT: "OH-ADP-3007",
382
+ ADAPTER_MODEL_IDLE_TIMEOUT: "OH-ADP-3008",
383
+ ADAPTER_TOOL_IDLE_TIMEOUT: "OH-ADP-3009",
384
+ ADAPTER_TOOL_DEADLINE: "OH-ADP-3010",
385
+ ADAPTER_RUN_DEADLINE: "OH-ADP-3011",
386
+ ADAPTER_CLEANUP_TIMEOUT: "OH-ADP-3012",
387
+ RPC_TIMEOUT: "OH-ADP-3013",
388
+ CODEX_FINALIZATION_TIMEOUT: "OH-ADP-3013"
389
+ };
390
+ if (explicit && timeouts[explicit])
391
+ return result(timeouts[explicit], explicit);
392
+ // DSH 的公开分类复用共同目录,原生阶段仍由稳定 reasonCode 保留。
393
+ const dshCodes = {
394
+ DSH_CONFIG_INVALID: "OH-ADP-3004",
395
+ DSH_ACTMODE_UNSUPPORTED: "OH-ADP-3004",
396
+ DSH_EXECUTABLE_NOT_FOUND: "OH-ADP-3002",
397
+ DSH_VERSION_UNSUPPORTED: "OH-ADP-3005",
398
+ DSH_START_FAILED: "OH-ADP-3004",
399
+ DSH_INITIALIZE_TIMEOUT: "OH-ADP-3007",
400
+ DSH_RPC_ERROR: "OH-ADP-3004",
401
+ DSH_REQUEST_TIMEOUT: "OH-ADP-3013",
402
+ DSH_TRANSPORT_CLOSED: "OH-ADP-3005",
403
+ DSH_PROTOCOL_ERROR: "OH-ADP-3005",
404
+ DSH_TRAJECTORY_INVALID: "OH-ADP-3005",
405
+ DSH_SESSION_RESUME_FAILED: "OH-ADP-3004",
406
+ DSH_SESSION_BUSY: "OH-ADP-3004",
407
+ DSH_EXECUTION_FAILED: "OH-PRV-4099",
408
+ DSH_NO_FINAL_OUTPUT: "OH-ADP-3005",
409
+ DSH_CANCEL_FAILED: "OH-ADP-3012",
410
+ DSH_CLEANUP_FAILED: "OH-ADP-3012"
411
+ };
412
+ if (explicit && dshCodes[explicit])
413
+ return result(dshCodes[explicit], explicit);
414
+ if (explicit === "PROVIDER_MESSAGE_TOO_LARGE")
415
+ return result("OH-PRV-4009", "PROVIDER_MESSAGE_TOO_LARGE");
416
+ if (explicit === "HOST_FRAME_TOO_LARGE")
417
+ return result("OH-ADP-3014", "HOST_FRAME_TOO_LARGE");
418
+ if ([
419
+ "STREAM_QUEUE_ITEM_TOO_LARGE",
420
+ "STREAM_QUEUE_BYTES_EXCEEDED",
421
+ "STREAM_QUEUE_ITEMS_EXCEEDED"
422
+ ].includes(explicit ?? ""))
423
+ return result("OH-RUN-2009", explicit);
424
+ if (explicit === "RUNTIME_DRAINING")
425
+ return result("OH-RUN-2002", "RUNTIME_DRAINING");
426
+ if (explicit === "IM_SESSION_QUEUE_FULL")
427
+ return result("OH-RUN-2004", explicit);
428
+ if (explicit === "IM_DELIVERY_UNKNOWN")
429
+ return result("OH-RUN-2005", explicit);
430
+ if ([
431
+ "OUTBOX_HARD_LIMIT",
432
+ "OUTBOX_BACKPRESSURE",
433
+ "RUNTIME_CAPACITY_EXHAUSTED",
434
+ "IM_BOT_WORKING_FULL"
435
+ ].includes(explicit ?? "")) {
436
+ return result("OH-RUN-2003", explicit ?? "RUNTIME_CAPACITY_EXHAUSTED");
437
+ }
438
+ if (["LOCAL_STORE_FULL", "LOCAL_STORE_UNAVAILABLE"].includes(explicit ?? "")) {
439
+ return result("OH-RUN-2008", explicit ?? "LOCAL_STORE_UNAVAILABLE");
440
+ }
441
+ if (explicit === "ENOENT" ||
442
+ text.includes("command not found") ||
443
+ text.includes("executable not found")) {
444
+ return result("OH-ADP-3002", explicit === "ENOENT" ? "ENOENT" : "COMMAND_NOT_FOUND");
445
+ }
446
+ if (explicit === "EPERM" || explicit === "EACCES" || text.includes("permission denied")) {
447
+ return result("OH-ADP-3003", explicit === "EPERM" || explicit === "EACCES" ? explicit : "EACCES");
448
+ }
449
+ if (isProtocolFailure(explicit, text))
450
+ return result("OH-ADP-3005", "ADAPTER_PROTOCOL_ERROR");
451
+ if (isAdapterTimeout(explicit, text, defaultDomain))
452
+ return result("OH-ADP-3006", "ADAPTER_TIMEOUT");
453
+ if (explicit === "PROVIDER_CONFIG_MISSING" ||
454
+ containsAny(text, ["provider_config_missing", "provider configuration missing"])) {
455
+ return result("OH-PRV-4008", "PROVIDER_CONFIG_MISSING");
456
+ }
457
+ const status = evidence.httpStatus;
458
+ if (status === 401 ||
459
+ containsAny(text, ["invalid_api_key", "invalid api key", "token_expired", "unauthorized"])) {
460
+ return result("OH-PRV-4001", status === 401 ? "PROVIDER_HTTP_401" : "PROVIDER_CREDENTIAL_INVALID");
461
+ }
462
+ if (containsAny(text, ["official_clients_only", "official clients only", "client_not_allowed"])) {
463
+ return result("OH-PRV-4007", "PROVIDER_CLIENT_NOT_ALLOWED");
464
+ }
465
+ if (status === 403 || containsAny(text, ["permission_denied", "forbidden"])) {
466
+ return result("OH-PRV-4002", status === 403 ? "PROVIDER_HTTP_403" : "PROVIDER_PERMISSION_DENIED");
467
+ }
468
+ if (containsAny(text, ["model_not_found", "model not found", "unknown model"]) ||
469
+ status === 404) {
470
+ return result("OH-PRV-4003", "PROVIDER_MODEL_NOT_FOUND");
471
+ }
472
+ if (containsAny(text, [
473
+ "insufficient_quota",
474
+ "quota exceeded",
475
+ "quota_exhausted",
476
+ "balance_exhausted",
477
+ "billing limit"
478
+ ])) {
479
+ return result("OH-PRV-4004", "PROVIDER_QUOTA_EXHAUSTED");
480
+ }
481
+ if (status === 429 || containsAny(text, ["rate_limit", "rate limit", "too many requests"])) {
482
+ return result("OH-PRV-4005", "PROVIDER_RATE_LIMITED");
483
+ }
484
+ if ((status !== undefined && status >= 500) ||
485
+ containsAny(text, [
486
+ "provider_unavailable",
487
+ "service unavailable",
488
+ "bad gateway",
489
+ "gateway timeout"
490
+ ])) {
491
+ return result("OH-PRV-4006", "PROVIDER_UNAVAILABLE");
492
+ }
493
+ if (defaultDomain === "provider" &&
494
+ (hasTimeoutEvidence(explicit, text) || explicit === "ECONNRESET" || /\beconnreset\b/.test(text))) {
495
+ return result("OH-PRV-4006", "PROVIDER_TIMEOUT");
496
+ }
497
+ if (defaultDomain === "adapter") {
498
+ return result("OH-ADP-3004", explicit === "ADAPTER_INVOCATION_FAILED" ? explicit : "ADAPTER_INVOCATION_FAILED");
499
+ }
500
+ if (defaultDomain === "runtime")
501
+ return result("OH-SYS-9001", "RUNTIME_ERROR_UNMAPPED");
502
+ if (defaultDomain === "provider")
503
+ return result("OH-PRV-4099", "PROVIDER_ERROR");
504
+ return result("OH-SYS-9001", "INTERNAL_ERROR");
505
+ }
506
+ function isProtocolFailure(code, text) {
507
+ return (code === "INVALID_JSON" ||
508
+ code?.endsWith("_RPC_ERROR") === true ||
509
+ containsAny(text, [
510
+ "invalid json",
511
+ "protocol error",
512
+ "rpc error",
513
+ "unsupported canonical event"
514
+ ]));
515
+ }
516
+ function isAdapterTimeout(code, text, domain) {
517
+ if (code === "ADAPTER_TIMEOUT" || code === "RPC_TIMEOUT")
518
+ return true;
519
+ return domain === "adapter" && hasTimeoutEvidence(code, text);
520
+ }
521
+ function hasTimeoutEvidence(code, text) {
522
+ if (code === "ETIMEDOUT" || code === "TIMEOUT" || code?.endsWith("_TIMEOUT"))
523
+ return true;
524
+ // Keep timeout words and error identifiers, but exclude fields such as tool_timeout_sec.
525
+ return /\b(?:[a-z0-9]+_)*timeout(?:error)?\b|\btimed\s+out\b|\betimedout\b/.test(text);
526
+ }
527
+ function result(code, reasonCode) {
528
+ return { code, reasonCode };
529
+ }
530
+ function isPublicFailureCode(value) {
531
+ return Object.hasOwn(FAILURE_CATALOG, value);
532
+ }
533
+ function safeReasonCode(value) {
534
+ if (typeof value !== "string")
535
+ return undefined;
536
+ const normalized = value
537
+ .trim()
538
+ .replace(/[^A-Za-z0-9_.-]+/g, "_")
539
+ .slice(0, 128);
540
+ return normalized.length > 0 ? normalized : undefined;
541
+ }
542
+ function safeAdapterType(value) {
543
+ if (typeof value !== "string" || !/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(value))
544
+ return undefined;
545
+ return value;
546
+ }
547
+ function numericHttpStatus(value) {
548
+ const numeric = typeof value === "number"
549
+ ? value
550
+ : typeof value === "string" && /^\d{3}$/.test(value)
551
+ ? Number(value)
552
+ : NaN;
553
+ return Number.isInteger(numeric) && numeric >= 100 && numeric <= 599 ? numeric : undefined;
554
+ }
555
+ function containsAny(value, candidates) {
556
+ return candidates.some((candidate) => value.includes(candidate));
557
+ }
558
+ function asRecord(value) {
559
+ return value !== null && typeof value === "object" && !Array.isArray(value)
560
+ ? value
561
+ : undefined;
562
+ }