@librechat/agents 4.0.1 → 4.0.2

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 (138) hide show
  1. package/dist/cjs/decisions/deadline.cjs +52 -0
  2. package/dist/cjs/decisions/deadline.cjs.map +1 -0
  3. package/dist/cjs/decisions/dialect.cjs +88 -0
  4. package/dist/cjs/decisions/dialect.cjs.map +1 -0
  5. package/dist/cjs/decisions/http.cjs +77 -0
  6. package/dist/cjs/decisions/http.cjs.map +1 -0
  7. package/dist/cjs/decisions/index.cjs +7 -0
  8. package/dist/cjs/decisions/presets.cjs +82 -0
  9. package/dist/cjs/decisions/presets.cjs.map +1 -0
  10. package/dist/cjs/decisions/questions.cjs +70 -0
  11. package/dist/cjs/decisions/questions.cjs.map +1 -0
  12. package/dist/cjs/decisions/structuredChat.cjs +178 -0
  13. package/dist/cjs/decisions/structuredChat.cjs.map +1 -0
  14. package/dist/cjs/decisions/traceMarker.cjs +6 -0
  15. package/dist/cjs/decisions/traceMarker.cjs.map +1 -0
  16. package/dist/cjs/decisions/transport.cjs +166 -0
  17. package/dist/cjs/decisions/transport.cjs.map +1 -0
  18. package/dist/cjs/decisions/types.cjs +44 -0
  19. package/dist/cjs/decisions/types.cjs.map +1 -0
  20. package/dist/cjs/graphs/Graph.cjs +4 -1
  21. package/dist/cjs/graphs/Graph.cjs.map +1 -1
  22. package/dist/cjs/langfuseToolOutputTracing.cjs +6 -1
  23. package/dist/cjs/langfuseToolOutputTracing.cjs.map +1 -1
  24. package/dist/cjs/main.cjs +39 -0
  25. package/dist/cjs/rerank/index.cjs +2 -0
  26. package/dist/cjs/rerank/search.cjs +45 -0
  27. package/dist/cjs/rerank/search.cjs.map +1 -0
  28. package/dist/cjs/rerank/systemone.cjs +70 -0
  29. package/dist/cjs/rerank/systemone.cjs.map +1 -0
  30. package/dist/cjs/run.cjs +4 -0
  31. package/dist/cjs/run.cjs.map +1 -1
  32. package/dist/cjs/tools/search/rerankers.cjs +1 -0
  33. package/dist/cjs/tools/search/search.cjs +3 -3
  34. package/dist/cjs/tools/search/search.cjs.map +1 -1
  35. package/dist/cjs/tools/search/tool.cjs +4 -3
  36. package/dist/cjs/tools/search/tool.cjs.map +1 -1
  37. package/dist/cjs/tools/subagent/SubagentExecutionRegistry.cjs +11 -0
  38. package/dist/cjs/tools/subagent/SubagentExecutionRegistry.cjs.map +1 -1
  39. package/dist/cjs/tools/subagent/SubagentExecutor.cjs +87 -14
  40. package/dist/cjs/tools/subagent/SubagentExecutor.cjs.map +1 -1
  41. package/dist/cjs/tools/subagent/diagnostics.cjs +45 -2
  42. package/dist/cjs/tools/subagent/diagnostics.cjs.map +1 -1
  43. package/dist/cjs/tools/subagent/index.cjs +1 -0
  44. package/dist/esm/decisions/deadline.mjs +52 -0
  45. package/dist/esm/decisions/deadline.mjs.map +1 -0
  46. package/dist/esm/decisions/dialect.mjs +87 -0
  47. package/dist/esm/decisions/dialect.mjs.map +1 -0
  48. package/dist/esm/decisions/http.mjs +75 -0
  49. package/dist/esm/decisions/http.mjs.map +1 -0
  50. package/dist/esm/decisions/index.mjs +8 -0
  51. package/dist/esm/decisions/presets.mjs +77 -0
  52. package/dist/esm/decisions/presets.mjs.map +1 -0
  53. package/dist/esm/decisions/questions.mjs +67 -0
  54. package/dist/esm/decisions/questions.mjs.map +1 -0
  55. package/dist/esm/decisions/structuredChat.mjs +178 -0
  56. package/dist/esm/decisions/structuredChat.mjs.map +1 -0
  57. package/dist/esm/decisions/traceMarker.mjs +6 -0
  58. package/dist/esm/decisions/traceMarker.mjs.map +1 -0
  59. package/dist/esm/decisions/transport.mjs +164 -0
  60. package/dist/esm/decisions/transport.mjs.map +1 -0
  61. package/dist/esm/decisions/types.mjs +39 -0
  62. package/dist/esm/decisions/types.mjs.map +1 -0
  63. package/dist/esm/graphs/Graph.mjs +4 -1
  64. package/dist/esm/graphs/Graph.mjs.map +1 -1
  65. package/dist/esm/langfuseToolOutputTracing.mjs +6 -1
  66. package/dist/esm/langfuseToolOutputTracing.mjs.map +1 -1
  67. package/dist/esm/main.mjs +13 -1
  68. package/dist/esm/rerank/index.mjs +3 -0
  69. package/dist/esm/rerank/search.mjs +45 -0
  70. package/dist/esm/rerank/search.mjs.map +1 -0
  71. package/dist/esm/rerank/systemone.mjs +70 -0
  72. package/dist/esm/rerank/systemone.mjs.map +1 -0
  73. package/dist/esm/run.mjs +4 -0
  74. package/dist/esm/run.mjs.map +1 -1
  75. package/dist/esm/tools/search/rerankers.mjs +1 -1
  76. package/dist/esm/tools/search/search.mjs +3 -3
  77. package/dist/esm/tools/search/search.mjs.map +1 -1
  78. package/dist/esm/tools/search/tool.mjs +4 -3
  79. package/dist/esm/tools/search/tool.mjs.map +1 -1
  80. package/dist/esm/tools/subagent/SubagentExecutionRegistry.mjs +11 -0
  81. package/dist/esm/tools/subagent/SubagentExecutionRegistry.mjs.map +1 -1
  82. package/dist/esm/tools/subagent/SubagentExecutor.mjs +88 -15
  83. package/dist/esm/tools/subagent/SubagentExecutor.mjs.map +1 -1
  84. package/dist/esm/tools/subagent/diagnostics.mjs +44 -3
  85. package/dist/esm/tools/subagent/diagnostics.mjs.map +1 -1
  86. package/dist/esm/tools/subagent/index.mjs +1 -0
  87. package/dist/types/decisions/deadline.d.ts +3 -0
  88. package/dist/types/decisions/dialect.d.ts +11 -0
  89. package/dist/types/decisions/http.d.ts +22 -0
  90. package/dist/types/decisions/index.d.ts +10 -0
  91. package/dist/types/decisions/presets.d.ts +17 -0
  92. package/dist/types/decisions/questions.d.ts +8 -0
  93. package/dist/types/decisions/structuredChat.d.ts +16 -0
  94. package/dist/types/decisions/traceMarker.d.ts +2 -0
  95. package/dist/types/decisions/transport.d.ts +29 -0
  96. package/dist/types/decisions/types.d.ts +119 -0
  97. package/dist/types/graphs/Graph.d.ts +2 -1
  98. package/dist/types/index.d.ts +2 -0
  99. package/dist/types/rerank/index.d.ts +4 -0
  100. package/dist/types/rerank/search.d.ts +7 -0
  101. package/dist/types/rerank/systemone.d.ts +13 -0
  102. package/dist/types/rerank/types.d.ts +24 -0
  103. package/dist/types/run.d.ts +1 -0
  104. package/dist/types/tools/search/types.d.ts +3 -1
  105. package/dist/types/tools/subagent/SubagentExecutionRegistry.d.ts +4 -0
  106. package/dist/types/tools/subagent/SubagentExecutor.d.ts +13 -0
  107. package/dist/types/tools/subagent/diagnostics.d.ts +32 -8
  108. package/dist/types/tools/subagent/index.d.ts +2 -0
  109. package/dist/types/types/graph.d.ts +3 -0
  110. package/dist/types/types/run.d.ts +2 -0
  111. package/package.json +1 -1
  112. package/src/decisions/deadline.ts +92 -0
  113. package/src/decisions/dialect.ts +200 -0
  114. package/src/decisions/http.ts +175 -0
  115. package/src/decisions/index.ts +14 -0
  116. package/src/decisions/presets.ts +111 -0
  117. package/src/decisions/questions.ts +128 -0
  118. package/src/decisions/structuredChat.ts +364 -0
  119. package/src/decisions/traceMarker.ts +2 -0
  120. package/src/decisions/transport.ts +335 -0
  121. package/src/decisions/types.ts +193 -0
  122. package/src/graphs/Graph.ts +4 -0
  123. package/src/index.ts +2 -0
  124. package/src/langfuseToolOutputTracing.ts +8 -1
  125. package/src/rerank/index.ts +4 -0
  126. package/src/rerank/search.ts +85 -0
  127. package/src/rerank/systemone.ts +140 -0
  128. package/src/rerank/types.ts +28 -0
  129. package/src/run.ts +4 -0
  130. package/src/tools/search/search.ts +4 -4
  131. package/src/tools/search/tool.ts +27 -25
  132. package/src/tools/search/types.ts +4 -1
  133. package/src/tools/subagent/SubagentExecutionRegistry.ts +21 -0
  134. package/src/tools/subagent/SubagentExecutor.ts +190 -22
  135. package/src/tools/subagent/diagnostics.ts +88 -9
  136. package/src/tools/subagent/index.ts +11 -0
  137. package/src/types/graph.ts +3 -0
  138. package/src/types/run.ts +2 -0
@@ -0,0 +1,29 @@
1
+ import type { DecisionCredential } from './types';
2
+ export type DecisionFetch = (input: string, init: {
3
+ method: string;
4
+ headers: Record<string, string>;
5
+ body: string;
6
+ signal: AbortSignal;
7
+ redirect: 'error';
8
+ }) => Promise<Pick<Response, 'ok' | 'status' | 'headers' | 'body'>>;
9
+ export interface TransportOptions {
10
+ providerId: string;
11
+ apiKey?: DecisionCredential;
12
+ requiresAuth?: boolean;
13
+ /** Full URL, not a base path. */
14
+ endpoint: string;
15
+ timeoutMs?: number;
16
+ maxRetries?: number;
17
+ fetch?: DecisionFetch;
18
+ sleep?: (ms: number, signal?: AbortSignal) => Promise<void>;
19
+ /** Called only after the response parses, with no state, answers, or credentials. */
20
+ onAnswered?: (label: string, ms: number) => void;
21
+ }
22
+ export type Transport = <T>(prepare: () => {
23
+ payload: string;
24
+ parse: (body: string) => T;
25
+ }, signal: AbortSignal | undefined, label: string, timeoutOverrideMs?: number) => Promise<T>;
26
+ export declare function failureForStatus(status: number): 'unauthorized' | 'rate_limited' | 'server_error' | 'bad_request';
27
+ export declare function retryAfterMs(header: string | null): number | undefined;
28
+ /** One immutable transport; every call owns its signal, deadline, and credentials. */
29
+ export declare function createTransport(options: TransportOptions): Transport;
@@ -0,0 +1,119 @@
1
+ /** Typed decisions over content, independent of the model that makes them. */
2
+ export type DecisionJson = string | number | boolean | null | DecisionJson[] | {
3
+ [key: string]: DecisionJson;
4
+ };
5
+ export type DecisionText = string | DecisionJson[] | {
6
+ [key: string]: DecisionJson;
7
+ };
8
+ export type DecisionState = DecisionText;
9
+ export interface BooleanCriteria {
10
+ true?: DecisionText;
11
+ false?: DecisionText;
12
+ }
13
+ /** A string criterion describes the yes side of a yes/no question. */
14
+ export interface BooleanQuestion {
15
+ type: 'boolean';
16
+ instructions: DecisionText;
17
+ criteria?: BooleanCriteria | string;
18
+ }
19
+ export interface ChoiceQuestion {
20
+ type: 'choice';
21
+ instructions: DecisionText;
22
+ criteria: Record<string, DecisionText | null>;
23
+ }
24
+ /** The measured answer is an expected level, which may fall between two rubric levels. */
25
+ export interface ScoreQuestion {
26
+ type: 'score';
27
+ instructions: DecisionText;
28
+ criteria: DecisionText[];
29
+ }
30
+ export type DecisionQuestion = BooleanQuestion | ChoiceQuestion | ScoreQuestion;
31
+ /** `null` is an unmeasured decision, not a probability of zero. */
32
+ export type BooleanAnswer = {
33
+ type: 'boolean';
34
+ probability: number;
35
+ decision?: never;
36
+ } | {
37
+ type: 'boolean';
38
+ probability: null;
39
+ decision: boolean;
40
+ };
41
+ export interface ChoiceAnswer {
42
+ type: 'choice';
43
+ choice: string;
44
+ /** Backend-specific concentration measure; Jev and Laya use different definitions. */
45
+ confidence: number | null;
46
+ /** `null` when no distribution was measured; never substitute an empty distribution. */
47
+ probabilities: Record<string, number> | null;
48
+ }
49
+ export interface ScoreAnswer {
50
+ type: 'score';
51
+ /** Expected value over rubric levels, not an LLM's selection of one level. */
52
+ score: number;
53
+ /** Backend-specific concentration measure; do not transfer thresholds between checkpoints. */
54
+ confidence: number | null;
55
+ probabilities: Record<string, number> | null;
56
+ }
57
+ export type DecisionAnswer = BooleanAnswer | ChoiceAnswer | ScoreAnswer;
58
+ /** Unknown counts are omitted, never inferred from an answer or replaced by zero. */
59
+ export interface DecisionUsage {
60
+ inputTokens?: number;
61
+ outputTokens?: number;
62
+ }
63
+ export interface DecisionRequest {
64
+ state: DecisionState;
65
+ questions: Record<string, DecisionQuestion>;
66
+ signal?: AbortSignal;
67
+ label?: string;
68
+ /** Overrides the provider's timeout for this request alone. */
69
+ timeoutMs?: number;
70
+ }
71
+ export interface DecisionResult {
72
+ model: string;
73
+ /** A requested answer can be omitted; callers must handle `undefined` explicitly. */
74
+ answers: Record<string, DecisionAnswer | undefined>;
75
+ usage: DecisionUsage | null;
76
+ }
77
+ export interface DecisionModel {
78
+ readonly id: string;
79
+ readonly model: string;
80
+ decide(request: DecisionRequest): Promise<DecisionResult>;
81
+ }
82
+ export type DecisionDialect = 'port' | 'systemone';
83
+ /** A bearer key, or a per-request token minter; a 401 requests one fresh token for that call. */
84
+ export type DecisionCredential = string | ((options: {
85
+ refresh: boolean;
86
+ signal?: AbortSignal;
87
+ }) => Promise<string>);
88
+ export interface DecisionProviderSettings {
89
+ /** Full URL of the decision endpoint, not a base path. */
90
+ baseURL?: string;
91
+ model?: string;
92
+ dialect?: DecisionDialect;
93
+ requestKey?: string;
94
+ responseKey?: string;
95
+ timeoutMs?: number;
96
+ maxRetries?: number;
97
+ /** Auth is required by default; self-hosted Laya can be unauthenticated. */
98
+ requiresAuth?: boolean;
99
+ apiKeyEnv?: string;
100
+ }
101
+ export type DecisionFailure = 'timeout' | 'aborted' | 'rate_limited' | 'unauthorized' | 'bad_request' | 'server_error' | 'network' | 'unsupported_question' | 'unsupported_mode' | 'malformed_response';
102
+ export declare class DecisionError extends Error {
103
+ readonly failure: DecisionFailure;
104
+ readonly provider: string;
105
+ readonly status?: number;
106
+ retryAfterMs?: number;
107
+ constructor(failure: DecisionFailure, message: string, options?: {
108
+ provider?: string;
109
+ status?: number;
110
+ });
111
+ }
112
+ /** A JSON boundary guard; never trust a parsed response's declared TypeScript type. */
113
+ export declare function isDecisionObject(value: unknown): value is {
114
+ [key: string]: unknown;
115
+ };
116
+ export declare function readDecisionUsage(raw: unknown): DecisionUsage | null;
117
+ export declare function isBooleanAnswer(answer: DecisionAnswer | undefined): answer is BooleanAnswer;
118
+ export declare function isChoiceAnswer(answer: DecisionAnswer | undefined): answer is ChoiceAnswer;
119
+ export declare function isScoreAnswer(answer: DecisionAnswer | undefined): answer is ScoreAnswer;
@@ -305,6 +305,7 @@ export declare class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode>
305
305
  /** See {@link t.StandardGraphInput.subagentExecutionContext}. */
306
306
  readonly subagentExecutionContext?: t.SubagentExecutionContext;
307
307
  private readonly subagentContext?;
308
+ private readonly onSubagentResolutionFailure?;
308
309
  /** See {@link t.StandardGraphInput.preemption}. */
309
310
  preemption?: t.StreamPreemption;
310
311
  /**
@@ -441,7 +442,7 @@ export declare class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode>
441
442
  * from a discard.
442
443
  */
443
444
  private preemptRestartPending;
444
- constructor({ runId, signal, agents, langfuse, tokenCounter, indexTokenCountMap, calibrationRatio, fadingTier, fadingTiers, subagentUsageSink, subagentTasks, subagentScope, subagentExecutionContext, subagentContext, preemption, streamLimits, toolExecution, clientDelegatedToolNames, }: t.StandardGraphInput, dependencies?: GraphFactoryDependencies);
445
+ constructor({ runId, signal, agents, langfuse, tokenCounter, indexTokenCountMap, calibrationRatio, fadingTier, fadingTiers, subagentUsageSink, subagentTasks, subagentScope, subagentExecutionContext, subagentContext, onSubagentResolutionFailure, preemption, streamLimits, toolExecution, clientDelegatedToolNames, }: t.StandardGraphInput, dependencies?: GraphFactoryDependencies);
445
446
  /**
446
447
  * A compaction applies its remove-all inside the agent subgraph, so the
447
448
  * subgraph's result carries only the retained tail, and an outer reducer
@@ -59,3 +59,5 @@ export { canSealPreempt } from './llm/preempt';
59
59
  export { isThinkingEnabled, getMaxOutputTokensKey } from './llm/request';
60
60
  export { DEFAULT_MAX_TOOL_CALL_ARG_BYTES, StreamLimitExceededError, resolveStreamLimits, } from './llm/streamLimits';
61
61
  export type { StreamedToolCallArgTally, ResolvedStreamLimits, StreamLimitState, StreamLimitKind, } from './llm/streamLimits';
62
+ export * from './decisions';
63
+ export * from './rerank';
@@ -0,0 +1,4 @@
1
+ export type * from './types';
2
+ export { createSystemOneReranker } from './systemone';
3
+ export type { SystemOneRerankerOptions } from './systemone';
4
+ export { createWebSearchReranker } from './search';
@@ -0,0 +1,7 @@
1
+ import type { Logger, SearchReranker } from '@/tools/search/types';
2
+ import type { Reranker } from './types';
3
+ /** Adapts index-based rankings to web-search highlights and its existing metrics/fallback policy. */
4
+ export declare function createWebSearchReranker(reranker: Reranker, options?: {
5
+ timeoutMs?: number;
6
+ logger?: Logger;
7
+ }): SearchReranker;
@@ -0,0 +1,13 @@
1
+ import type { Reranker } from './types';
2
+ import type { DecisionModel } from '@/decisions';
3
+ export interface SystemOneRerankerOptions {
4
+ /** Inject a configured Jev/Laya HTTP decision model; chat cannot produce expected scores. */
5
+ model: DecisionModel;
6
+ /** Relevance levels from least to most relevant; evaluate this rubric for the checkpoint. */
7
+ rubric: readonly string[];
8
+ /** Reject oversized batches instead of silently dropping candidates. Defaults to 128. */
9
+ maxDocuments?: number;
10
+ timeoutMs?: number;
11
+ }
12
+ /** One shared query/candidate state and one score question per candidate, in one request. */
13
+ export declare function createSystemOneReranker(options: SystemOneRerankerOptions): Reranker;
@@ -0,0 +1,24 @@
1
+ import type { DecisionUsage } from '@/decisions';
2
+ export interface RerankRequest {
3
+ query: string;
4
+ documents: readonly string[];
5
+ topK?: number;
6
+ signal?: AbortSignal;
7
+ timeoutMs?: number;
8
+ }
9
+ export interface RankedDocument {
10
+ /** Index into the submitted documents, not an identity derived from text. */
11
+ index: number;
12
+ /** Provider-specific relevance score, ordered descending; not a portable threshold. */
13
+ score: number;
14
+ }
15
+ export interface RerankResult {
16
+ results: RankedDocument[];
17
+ model: string;
18
+ usage: DecisionUsage | null;
19
+ }
20
+ /** Rankings retain candidate identity; failures reject rather than fabricate scores. */
21
+ export interface Reranker {
22
+ readonly id: string;
23
+ rerank(request: RerankRequest): Promise<RerankResult>;
24
+ }
@@ -24,6 +24,7 @@ export declare class Run<_T extends t.BaseGraphState> {
24
24
  private streamLimits?;
25
25
  private subagentTasks?;
26
26
  private subagentContext?;
27
+ private onSubagentResolutionFailure?;
27
28
  private indexTokenCountMap?;
28
29
  calibrationRatio: number;
29
30
  fadingTier?: t.FadingTier;
@@ -239,6 +239,8 @@ export interface ScrapeResult {
239
239
  references?: References;
240
240
  highlights?: Highlight[];
241
241
  }
242
+ /** Host implementations need only the public ranking seam, not BaseReranker's private state. */
243
+ export type SearchReranker = Pick<BaseReranker, 'provider' | 'rerank'>;
242
244
  export interface ProcessSourcesConfig {
243
245
  topResults?: number;
244
246
  /** Max chars of scraped content stored per source and passed to the
@@ -258,7 +260,7 @@ export interface ProcessSourcesConfig {
258
260
  separatorExpandBy?: number;
259
261
  strategies?: string[];
260
262
  filterContent?: boolean;
261
- reranker?: BaseReranker;
263
+ reranker?: SearchReranker;
262
264
  logger?: Logger;
263
265
  }
264
266
  export interface FirecrawlConfig {
@@ -98,6 +98,7 @@ export declare class SubagentSettlementBindingError extends Error {
98
98
  export declare class SubagentExecutionRecord<TResult, TResolvedConfig, TActiveRun, TSettledOutput = never> {
99
99
  readonly address: SubagentExecutionAddress;
100
100
  private identityValue?;
101
+ private attemptedIdentityValue?;
101
102
  private bindingValue?;
102
103
  private bindingAuthority?;
103
104
  private invocationValue?;
@@ -118,6 +119,9 @@ export declare class SubagentExecutionRecord<TResult, TResolvedConfig, TActiveRu
118
119
  constructor(address: SubagentExecutionAddress, resumeExecution?: SubagentResumeExecution);
119
120
  get snapshot(): ExecutionSnapshot<TResult, TResolvedConfig, TActiveRun>;
120
121
  get identity(): SubagentExecutionIdentity | undefined;
122
+ /** Correlation only; never committed identity or resume authority. */
123
+ get attemptedIdentity(): SubagentExecutionIdentity | undefined;
124
+ recordIdentityAttempt(identity: SubagentExecutionIdentity): void;
121
125
  get binding(): SubagentDefinitionBinding | undefined;
122
126
  get resumeExecution(): SubagentResumeExecution | undefined;
123
127
  get invocation(): SubagentInvocationBinding | undefined;
@@ -3,6 +3,7 @@ import type { RunnableConfig } from '@langchain/core/runnables';
3
3
  import type { ToolCall } from '@langchain/core/messages/tool';
4
4
  import type { MultiAgentGraphState, HumanInTheLoopConfig, StandardGraphInput, ExecutableSubagentConfigEntry, SubagentExecutionContext, SubagentContextAdapter, SubagentContextResult, SubagentTaskConfig, SubagentTaskRuntime, SubagentUsageSink, TokenCounter } from '@/types';
5
5
  import type { SubagentResumeManifest, SettledSubagentToolOutput } from './SubagentReplay';
6
+ import type { SubagentResolutionPhase, SubagentResolutionCause, SubagentResolutionFailureHandler } from './diagnostics';
6
7
  import type { GraphFactory } from '@/graphs/graphFactory';
7
8
  import type { StandardGraph } from '@/graphs/Graph';
8
9
  import { HookRegistry } from '@/hooks';
@@ -74,6 +75,11 @@ export type SubagentExecuteParams = {
74
75
  export type SubagentExecuteResult = SubagentContextResult & {
75
76
  /** Tagged internal failure; foreground callers retain the legacy content. */
76
77
  error?: string;
78
+ /** Safe startup classification retained for detached host delivery. */
79
+ resolutionFailure?: {
80
+ phase: SubagentResolutionPhase;
81
+ cause: SubagentResolutionCause;
82
+ };
77
83
  /** Completed child work whose host projection must be retried without re-execution. */
78
84
  retryableDelivery?: true;
79
85
  };
@@ -161,6 +167,8 @@ export type SubagentExecutorOptions = {
161
167
  /** Host-owned process-local task namespace for detached execution. */
162
168
  taskConfig?: SubagentTaskConfig;
163
169
  subagentContext?: SubagentContextAdapter;
170
+ /** Host diagnostic sink and safe cause classifier for startup failures. */
171
+ onResolutionFailure?: SubagentResolutionFailureHandler;
164
172
  };
165
173
  export declare class SubagentExecutor {
166
174
  private readonly configs;
@@ -189,6 +197,7 @@ export declare class SubagentExecutor {
189
197
  private readonly usageSink?;
190
198
  private readonly taskConfig?;
191
199
  private readonly subagentContext?;
200
+ private readonly onResolutionFailure?;
192
201
  private readonly executions;
193
202
  private replayCheckpointWorkflow?;
194
203
  private readonly resolveParentHandlerRegistry?;
@@ -264,9 +273,13 @@ export declare class SubagentExecutor {
264
273
  clearHeavyState(): void;
265
274
  private getResumeCheckpointMarker;
266
275
  getSettledToolOutput(call: ToolCall, config: RunnableConfig): Promise<SettledSubagentToolOutput | undefined>;
276
+ private restoreSettledToolOutput;
267
277
  persistSettledToolOutput(call: ToolCall, config: RunnableConfig, settled: SettledSubagentToolOutput): Promise<void>;
268
278
  private persistChildCheckpointMarker;
269
279
  execute(params: SubagentExecuteParams): Promise<SubagentExecuteResult>;
280
+ private getReplaySignal;
281
+ private createReplayResolutionError;
282
+ private createResolutionFailure;
270
283
  private executeOnce;
271
284
  private completeHostContext;
272
285
  /**
@@ -4,17 +4,41 @@ import { describeCodeApiError } from '@/tools/diagnostics';
4
4
  export type SubagentResolutionPhase = 'identity' | 'config';
5
5
  declare const SUBAGENT_ERROR_TYPES: readonly [readonly ["SubagentExecutionInvalidatedError", typeof SubagentExecutionInvalidatedError], readonly ["SubagentDefinitionBindingError", typeof SubagentDefinitionBindingError], readonly ["SubagentInvocationBindingError", typeof SubagentInvocationBindingError], readonly ["SubagentSettlementBindingError", typeof SubagentSettlementBindingError]];
6
6
  type SubagentErrorLabel = (typeof SUBAGENT_ERROR_TYPES)[number][0] | ReturnType<typeof describeCodeApiError>['type'];
7
- /**
8
- * Follows the rule in `@/tools/diagnostics`: every field is a value this
9
- * module owns or a host-registered subagent type, never text from the error.
10
- * The failure the model sees is a fixed sentence, so this line is the only
11
- * operator account of it; the host logs its own error text where it throws.
12
- */
7
+ declare const SUBAGENT_RESOLUTION_MESSAGES: {
8
+ readonly workspace_unavailable: "Subagent error: Workspace unavailable. Retry later or choose another workspace.";
9
+ readonly agent_unavailable: "Subagent error: Agent not found or not accessible.";
10
+ readonly model_unavailable: "Subagent error: Model or provider unavailable.";
11
+ readonly configuration_changed: "Subagent error: Subagent configuration changed. Start a new execution.";
12
+ readonly unknown: "Subagent error: Unable to initialize the selected subagent.";
13
+ };
14
+ /** Host classifications accepted at the public failure boundary. */
15
+ export type SubagentResolutionCause = keyof typeof SUBAGENT_RESOLUTION_MESSAGES;
16
+ export type SubagentResolutionContext = {
17
+ parentRunId: string;
18
+ parentAgentId?: string;
19
+ parentToolCallId?: string;
20
+ threadId?: string;
21
+ childRunId: string;
22
+ childThreadId: string;
23
+ taskId?: string;
24
+ };
25
+ /** No received error text, writable error names, or stacks enter this payload. */
13
26
  export type SubagentResolutionDiagnostic = {
14
27
  phase: SubagentResolutionPhase;
15
28
  subagentType: string;
16
29
  aborted: boolean;
17
30
  type: SubagentErrorLabel;
18
- };
19
- export declare function logSubagentResolutionFailure(phase: SubagentResolutionPhase, subagentType: string, signal: AbortSignal, error: unknown): void;
31
+ cause: SubagentResolutionCause;
32
+ message: (typeof SUBAGENT_RESOLUTION_MESSAGES)[SubagentResolutionCause];
33
+ } & Partial<SubagentResolutionContext>;
34
+ /** The original error is private. Return a classification, never error text. */
35
+ export type SubagentResolutionFailureHandler = (detail: Readonly<SubagentResolutionDiagnostic>, error: unknown) => SubagentResolutionCause | void;
36
+ export declare function getSubagentResolutionFailureMessage(cause: SubagentResolutionCause): SubagentResolutionDiagnostic['message'];
37
+ /** Safe typed failure retained when detached execution crosses the host boundary. */
38
+ export declare class SubagentResolutionError extends Error {
39
+ readonly phase: SubagentResolutionPhase;
40
+ readonly resolutionCause: SubagentResolutionCause;
41
+ constructor(phase: SubagentResolutionPhase, cause: SubagentResolutionCause);
42
+ }
43
+ export declare function logSubagentResolutionFailure(phase: SubagentResolutionPhase, subagentType: string, signal: AbortSignal, error: unknown, context?: SubagentResolutionContext, onResolutionFailure?: SubagentResolutionFailureHandler): SubagentResolutionDiagnostic;
20
44
  export {};
@@ -2,3 +2,5 @@ export { DEFAULT_SUBAGENT_DESCRIPTION, SubagentExecutor, filterSubagentResult, f
2
2
  export type { SubagentExecuteParams, SubagentExecuteResult, SubagentExecutorOptions, ChildGraphFactory, } from './SubagentExecutor';
3
3
  export { InMemorySubagentTaskStore } from './InMemorySubagentTaskStore';
4
4
  export type { InMemorySubagentTaskStoreOptions } from './InMemorySubagentTaskStore';
5
+ export { SubagentResolutionError, getSubagentResolutionFailureMessage, } from './diagnostics';
6
+ export type { SubagentResolutionPhase, SubagentResolutionCause, SubagentResolutionContext, SubagentResolutionDiagnostic, SubagentResolutionFailureHandler, } from './diagnostics';
@@ -8,6 +8,7 @@ import type { RunStep, ModelResponseEvent, ModelToolsClaimedEvent, RunStepDeltaE
8
8
  import type { SummarizationNodeInput, SummarizeCompleteEvent, CompactionSemanticIndex, SummarizationConfig, SummarizeStartEvent, SummarizeDeltaEvent } from '@/types/summarize';
9
9
  import type { ToolMap, ToolSessionMap, ToolEndEvent, GenericTool, LCTool, ToolExecutionConfig, ToolExecuteBatchRequest } from '@/types/tools';
10
10
  import type { TokenCounter, StreamLimits, StreamPreemption, TokenBudgetBreakdown } from '@/types/run';
11
+ import type { SubagentResolutionFailureHandler } from '@/tools/subagent/diagnostics';
11
12
  import type { SubagentTaskConfig } from '@/types/subagentTasks';
12
13
  import type { StandardGraph, MultiAgentGraph } from '@/graphs';
13
14
  import type { ProviderClientOptionsConfig } from '@/types/llm';
@@ -320,6 +321,8 @@ export type StandardGraphInput = {
320
321
  subagentTasks?: SubagentTaskConfig;
321
322
  /** Host-owned context and result projection for each isolated child execution. */
322
323
  subagentContext?: SubagentContextAdapter;
324
+ /** Receives private startup errors; returns only an SDK-owned public cause. */
325
+ onSubagentResolutionFailure?: SubagentResolutionFailureHandler;
323
326
  /**
324
327
  * True when this graph IS a subagent child run (set by `SubagentExecutor`
325
328
  * when it constructs the child graph). Drives the hook-input `agentId`
@@ -281,6 +281,8 @@ export type RunConfig = {
281
281
  subagentTasks?: SubagentTaskConfig;
282
282
  /** Authorizes and supplies host-owned context to isolated child runs. */
283
283
  subagentContext?: g.SubagentContextAdapter;
284
+ /** Host diagnostic sink for foreground, detached, and nested startup failures. */
285
+ onSubagentResolutionFailure?: g.StandardGraphInput['onSubagentResolutionFailure'];
284
286
  /**
285
287
  * Pre-constructed hook registry for this run. Hooks fire at lifecycle
286
288
  * points in `processStream` (RunStart, UserPromptSubmit, Stop,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@librechat/agents",
3
- "version": "4.0.1",
3
+ "version": "4.0.2",
4
4
  "reova": {
5
5
  "enabled": true,
6
6
  "endpoint": "https://telemetry.reo.dev/data"
@@ -0,0 +1,92 @@
1
+ import { DecisionError } from './types';
2
+
3
+ export type AwaitWithinDeadline = <T>(task: Promise<T>) => Promise<T>;
4
+
5
+ /** Bounds even non-cooperative credential minters, fetches, body reads, and model calls. */
6
+ export async function withDecisionDeadline<T>(
7
+ provider: string,
8
+ timeoutMs: number,
9
+ callerSignal: AbortSignal | undefined,
10
+ operation: (signal: AbortSignal, waitFor: AwaitWithinDeadline) => Promise<T>
11
+ ): Promise<T> {
12
+ if (
13
+ !Number.isSafeInteger(timeoutMs) ||
14
+ timeoutMs <= 0 ||
15
+ timeoutMs > 3_600_000
16
+ ) {
17
+ throw new DecisionError('bad_request', 'invalid decision model timeout', {
18
+ provider,
19
+ });
20
+ }
21
+ if (callerSignal?.aborted === true) {
22
+ throw new DecisionError('aborted', 'caller aborted the request', {
23
+ provider,
24
+ });
25
+ }
26
+
27
+ const expiresAt = performance.now() + timeoutMs;
28
+ const deadline = new AbortController();
29
+ const signal = callerSignal
30
+ ? AbortSignal.any([callerSignal, deadline.signal])
31
+ : deadline.signal;
32
+ const timer = setTimeout(() => deadline.abort(), timeoutMs);
33
+ const expired = (): boolean => {
34
+ if (performance.now() >= expiresAt) {
35
+ deadline.abort();
36
+ }
37
+ return signal.aborted;
38
+ };
39
+ const abortError = (): DecisionError => {
40
+ if (callerSignal?.aborted === true) {
41
+ return new DecisionError('aborted', 'caller aborted the request', {
42
+ provider,
43
+ });
44
+ }
45
+ return new DecisionError('timeout', 'decision model deadline exceeded', {
46
+ provider,
47
+ });
48
+ };
49
+
50
+ const waitFor: AwaitWithinDeadline = <U>(task: Promise<U>): Promise<U> =>
51
+ new Promise<U>((resolve, reject) => {
52
+ if (expired()) {
53
+ void task.catch(() => {});
54
+ reject(abortError());
55
+ return;
56
+ }
57
+ const onAbort = (): void => {
58
+ signal.removeEventListener('abort', onAbort);
59
+ reject(abortError());
60
+ };
61
+ signal.addEventListener('abort', onAbort, { once: true });
62
+ if (expired()) {
63
+ onAbort();
64
+ }
65
+ task.then(
66
+ (value) => {
67
+ signal.removeEventListener('abort', onAbort);
68
+ if (expired()) {
69
+ reject(abortError());
70
+ } else {
71
+ resolve(value);
72
+ }
73
+ },
74
+ (error: unknown) => {
75
+ signal.removeEventListener('abort', onAbort);
76
+ reject(expired() ? abortError() : error);
77
+ }
78
+ );
79
+ });
80
+
81
+ try {
82
+ return await waitFor(operation(signal, waitFor));
83
+ } catch (error) {
84
+ if (expired()) {
85
+ throw abortError();
86
+ }
87
+ throw error;
88
+ } finally {
89
+ expired();
90
+ clearTimeout(timer);
91
+ }
92
+ }