@open-multi-agent/core 1.4.1 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +103 -51
- package/dist/agent/agent.d.ts.map +1 -1
- package/dist/agent/agent.js +5 -0
- package/dist/agent/agent.js.map +1 -1
- package/dist/agent/runner.d.ts +12 -0
- package/dist/agent/runner.d.ts.map +1 -1
- package/dist/agent/runner.js +48 -12
- package/dist/agent/runner.js.map +1 -1
- package/dist/cli/oma.d.ts +10 -2
- package/dist/cli/oma.d.ts.map +1 -1
- package/dist/cli/oma.js +10 -5
- package/dist/cli/oma.js.map +1 -1
- package/dist/dashboard/render-team-run-dashboard.d.ts.map +1 -1
- package/dist/dashboard/render-team-run-dashboard.js +177 -84
- package/dist/dashboard/render-team-run-dashboard.js.map +1 -1
- package/dist/llm/adapter.d.ts +3 -1
- package/dist/llm/adapter.d.ts.map +1 -1
- package/dist/llm/adapter.js +10 -0
- package/dist/llm/adapter.js.map +1 -1
- package/dist/llm/ai-sdk.d.ts +5 -1
- package/dist/llm/ai-sdk.d.ts.map +1 -1
- package/dist/llm/ai-sdk.js +50 -9
- package/dist/llm/ai-sdk.js.map +1 -1
- package/dist/llm/anthropic.d.ts +3 -0
- package/dist/llm/anthropic.d.ts.map +1 -1
- package/dist/llm/anthropic.js +29 -13
- package/dist/llm/anthropic.js.map +1 -1
- package/dist/llm/azure-openai.d.ts +3 -0
- package/dist/llm/azure-openai.d.ts.map +1 -1
- package/dist/llm/azure-openai.js +8 -3
- package/dist/llm/azure-openai.js.map +1 -1
- package/dist/llm/bedrock.d.ts +3 -0
- package/dist/llm/bedrock.d.ts.map +1 -1
- package/dist/llm/bedrock.js +49 -21
- package/dist/llm/bedrock.js.map +1 -1
- package/dist/llm/copilot.d.ts +3 -0
- package/dist/llm/copilot.d.ts.map +1 -1
- package/dist/llm/copilot.js +8 -3
- package/dist/llm/copilot.js.map +1 -1
- package/dist/llm/deepseek.d.ts +9 -2
- package/dist/llm/deepseek.d.ts.map +1 -1
- package/dist/llm/deepseek.js +21 -2
- package/dist/llm/deepseek.js.map +1 -1
- package/dist/llm/doubao.d.ts +21 -0
- package/dist/llm/doubao.d.ts.map +1 -0
- package/dist/llm/doubao.js +24 -0
- package/dist/llm/doubao.js.map +1 -0
- package/dist/llm/gemini.d.ts +3 -0
- package/dist/llm/gemini.d.ts.map +1 -1
- package/dist/llm/gemini.js +40 -19
- package/dist/llm/gemini.js.map +1 -1
- package/dist/llm/mimo.d.ts +24 -0
- package/dist/llm/mimo.d.ts.map +1 -0
- package/dist/llm/mimo.js +30 -0
- package/dist/llm/mimo.js.map +1 -0
- package/dist/llm/openai-common.d.ts +10 -3
- package/dist/llm/openai-common.d.ts.map +1 -1
- package/dist/llm/openai-common.js +95 -8
- package/dist/llm/openai-common.js.map +1 -1
- package/dist/llm/openai.d.ts +24 -0
- package/dist/llm/openai.d.ts.map +1 -1
- package/dist/llm/openai.js +49 -4
- package/dist/llm/openai.js.map +1 -1
- package/dist/llm/reasoning-fallback.d.ts +132 -0
- package/dist/llm/reasoning-fallback.d.ts.map +1 -0
- package/dist/llm/reasoning-fallback.js +128 -0
- package/dist/llm/reasoning-fallback.js.map +1 -0
- package/dist/orchestrator/orchestrator.d.ts.map +1 -1
- package/dist/orchestrator/orchestrator.js +72 -10
- package/dist/orchestrator/orchestrator.js.map +1 -1
- package/dist/tool/built-in/bash.d.ts +1 -1
- package/dist/tool/built-in/bash.d.ts.map +1 -1
- package/dist/tool/built-in/bash.js +60 -7
- package/dist/tool/built-in/bash.js.map +1 -1
- package/dist/tool/built-in/file-edit.d.ts.map +1 -1
- package/dist/tool/built-in/file-edit.js +13 -8
- package/dist/tool/built-in/file-edit.js.map +1 -1
- package/dist/tool/built-in/file-read.d.ts.map +1 -1
- package/dist/tool/built-in/file-read.js +9 -4
- package/dist/tool/built-in/file-read.js.map +1 -1
- package/dist/tool/built-in/file-write.d.ts.map +1 -1
- package/dist/tool/built-in/file-write.js +11 -6
- package/dist/tool/built-in/file-write.js.map +1 -1
- package/dist/tool/built-in/fs-walk.d.ts.map +1 -1
- package/dist/tool/built-in/fs-walk.js +6 -3
- package/dist/tool/built-in/fs-walk.js.map +1 -1
- package/dist/tool/built-in/glob.d.ts.map +1 -1
- package/dist/tool/built-in/glob.js +10 -4
- package/dist/tool/built-in/glob.js.map +1 -1
- package/dist/tool/built-in/grep.d.ts.map +1 -1
- package/dist/tool/built-in/grep.js +15 -6
- package/dist/tool/built-in/grep.js.map +1 -1
- package/dist/tool/built-in/path-safety.d.ts +30 -0
- package/dist/tool/built-in/path-safety.d.ts.map +1 -0
- package/dist/tool/built-in/path-safety.js +106 -0
- package/dist/tool/built-in/path-safety.js.map +1 -0
- package/dist/tool/mcp.d.ts.map +1 -1
- package/dist/tool/mcp.js +58 -33
- package/dist/tool/mcp.js.map +1 -1
- package/dist/types.d.ts +159 -4
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/redaction.d.ts +4 -0
- package/dist/utils/redaction.d.ts.map +1 -0
- package/dist/utils/redaction.js +78 -0
- package/dist/utils/redaction.js.map +1 -0
- package/package.json +1 -2
- package/docs/DECISIONS.md +0 -49
- package/docs/cli.md +0 -265
- package/docs/context-management.md +0 -24
- package/docs/featured-partner.md +0 -28
- package/docs/observability.md +0 -56
- package/docs/providers/minimax.md +0 -75
- package/docs/providers.md +0 -78
- package/docs/shared-memory.md +0 -27
- package/docs/tool-configuration.md +0 -152
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Shared `<thinking>` text fallback for {@link ReasoningBlock}
|
|
3
|
+
* round-tripping across adapter boundaries.
|
|
4
|
+
*
|
|
5
|
+
* When an outbound IR-to-native conversion encounters a {@link ReasoningBlock}
|
|
6
|
+
* that the target adapter cannot natively echo — either because the wire
|
|
7
|
+
* protocol does not accept reasoning input at all
|
|
8
|
+
* ({@link LLMAdapter.capabilities.echoesReasoning} `=== 'never'`) or because
|
|
9
|
+
* the block's {@link ReasoningBlock.provenance} does not match the target
|
|
10
|
+
* adapter for an `'own-issued'` adapter — this helper converts the block to
|
|
11
|
+
* an inline `<thinking>...</thinking>` text snippet that callers prepend to
|
|
12
|
+
* the next outgoing text part.
|
|
13
|
+
*
|
|
14
|
+
* SAFETY CONTRACT (one-way invariant):
|
|
15
|
+
* The reverse direction — parsing `<thinking>` text back into a
|
|
16
|
+
* {@link ReasoningBlock} — must never happen. A reconstructed block would
|
|
17
|
+
* carry no verifiable signature and would be rejected if re-sent to
|
|
18
|
+
* Anthropic / Bedrock / Gemini 3. `ReasoningBlock` instances are only ever
|
|
19
|
+
* produced from native API extraction (and always stamped with
|
|
20
|
+
* `provenance`), never from text parsing.
|
|
21
|
+
*
|
|
22
|
+
* Wiring (post #223 Phase 2): every adapter outbound path consults
|
|
23
|
+
* {@link ReasoningOutboundOptions} resolved from
|
|
24
|
+
* {@link AgentConfig.preserveReasoningAsText} +
|
|
25
|
+
* {@link AgentConfig.compressReasoningText}. When opt-in is off, reasoning
|
|
26
|
+
* blocks that can't be native-echoed are dropped silently (preserving
|
|
27
|
+
* pre-Phase-2 behaviour). When opt-in is on, they pass through
|
|
28
|
+
* {@link reasoningBlockToInlineText} and become inline `<thinking>` text.
|
|
29
|
+
*/
|
|
30
|
+
import type { ReasoningBlock } from '../types.js';
|
|
31
|
+
/**
|
|
32
|
+
* Default maximum character budget per `<thinking>` block after truncation.
|
|
33
|
+
* Aligned with the value used by the OpenAI-family private replay helper
|
|
34
|
+
* shipped in #234 so the Phase 2 consolidation is behaviour-preserving.
|
|
35
|
+
*/
|
|
36
|
+
export declare const DEFAULT_REASONING_FALLBACK_MAX_CHARS = 1200;
|
|
37
|
+
/**
|
|
38
|
+
* Sentinel value the resolver returns when the caller explicitly disables
|
|
39
|
+
* truncation (`compressReasoningText: false`). Exported so the special-case
|
|
40
|
+
* branch in {@link resolveMaxChars} stays searchable — removing the branch
|
|
41
|
+
* would silently re-introduce truncation for users who opted out.
|
|
42
|
+
*
|
|
43
|
+
* Equal to `Number.POSITIVE_INFINITY`; the resolver maps it to
|
|
44
|
+
* `Number.MAX_SAFE_INTEGER` so `text.length <= maxChars` is always true.
|
|
45
|
+
*/
|
|
46
|
+
export declare const NO_TRUNCATION: number;
|
|
47
|
+
export interface ReasoningFallbackOptions {
|
|
48
|
+
/**
|
|
49
|
+
* Hard upper bound on the inner text length (the `<thinking>` wrapper
|
|
50
|
+
* itself is not counted). Special values:
|
|
51
|
+
* - `undefined` → defaults to {@link DEFAULT_REASONING_FALLBACK_MAX_CHARS}.
|
|
52
|
+
* - {@link NO_TRUNCATION} (`Number.POSITIVE_INFINITY`) → no truncation;
|
|
53
|
+
* the entire reasoning text passes through unchanged. This is the
|
|
54
|
+
* sentinel returned by {@link resolveReasoningOutboundMaxChars} when
|
|
55
|
+
* the caller sets `compressReasoningText: false`.
|
|
56
|
+
* - Any other non-finite value (`NaN`, `-Infinity`) → clamped to 1
|
|
57
|
+
* (defensive — these values are invalid input).
|
|
58
|
+
* - Finite values below 1 → clamped to 1.
|
|
59
|
+
* - Finite values ≥ 1 → used as-is (floored).
|
|
60
|
+
*/
|
|
61
|
+
readonly maxChars?: number;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Outbound-conversion options derived from {@link AgentConfig}'s reasoning
|
|
65
|
+
* fields. Threaded through every adapter's outbound IR-to-native conversion
|
|
66
|
+
* so each can opt the user into the cross-provider `<thinking>` text
|
|
67
|
+
* fallback (see #223). Internal to the LLM layer — adapters pick the values
|
|
68
|
+
* out of {@link LLMChatOptions} themselves.
|
|
69
|
+
*/
|
|
70
|
+
export interface ReasoningOutboundOptions {
|
|
71
|
+
/** Mirrors {@link LLMChatOptions.preserveReasoningAsText}. */
|
|
72
|
+
readonly preserveReasoningAsText?: boolean;
|
|
73
|
+
/** Mirrors {@link LLMChatOptions.compressReasoningText}. */
|
|
74
|
+
readonly compressReasoningText?: boolean | {
|
|
75
|
+
readonly minChars?: number;
|
|
76
|
+
};
|
|
77
|
+
/**
|
|
78
|
+
* Adapter name to use for native `reasoning_content` echo on outbound
|
|
79
|
+
* assistant messages inside a tool-calling conversation. Wired by adapters
|
|
80
|
+
* whose {@link LLMAdapter.capabilities.echoesReasoning} is `'tool-use-only'`
|
|
81
|
+
* (currently DeepSeek V4 thinking-mode — see PR #251 / DeepSeek API spec).
|
|
82
|
+
*
|
|
83
|
+
* When set, each assistant message is scanned for {@link ReasoningBlock}s
|
|
84
|
+
* whose {@link ReasoningBlock.provenance} matches this value. The collected
|
|
85
|
+
* reasoning is attached as a `reasoning_content` field on the outbound
|
|
86
|
+
* payload (a non-standard OpenAI-compat field) IF AND ONLY IF the overall
|
|
87
|
+
* conversation contains at least one `tool_use` block somewhere in its
|
|
88
|
+
* history. Non-tool conversations skip the attachment entirely.
|
|
89
|
+
*
|
|
90
|
+
* Foreign-provenance blocks fall through to the {@link preserveReasoningAsText}
|
|
91
|
+
* `<thinking>` text path when that flag is on, so the two mechanisms
|
|
92
|
+
* compose: native echo for own-provenance + tool-use; text fallback for
|
|
93
|
+
* everything else.
|
|
94
|
+
*
|
|
95
|
+
* Adapters that don't need this leave it unset (default `'never'` and
|
|
96
|
+
* `'own-issued'` capability paths).
|
|
97
|
+
*/
|
|
98
|
+
readonly nativeReasoningEchoProvider?: string;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Resolve {@link ReasoningOutboundOptions} to the `maxChars` value accepted
|
|
102
|
+
* by {@link reasoningBlockToInlineText}. Encodes the
|
|
103
|
+
* default-on-when-preserve-on semantics documented in #223:
|
|
104
|
+
*
|
|
105
|
+
* - `preserve=false` → returns `undefined` (fallback never runs;
|
|
106
|
+
* callers must check `preserve` themselves)
|
|
107
|
+
* - `compress=undefined`/`true`→ returns `undefined` so the helper applies
|
|
108
|
+
* its own default head+tail budget
|
|
109
|
+
* - `compress=false` → returns {@link NO_TRUNCATION}; the helper
|
|
110
|
+
* treats this as a no-op cap (full text)
|
|
111
|
+
* - `compress={minChars: N}` → `N` (the threshold value also serves as
|
|
112
|
+
* the truncation cap)
|
|
113
|
+
*/
|
|
114
|
+
export declare function resolveReasoningOutboundMaxChars(options: ReasoningOutboundOptions | undefined): number | undefined;
|
|
115
|
+
/**
|
|
116
|
+
* Convert a {@link ReasoningBlock} into its `<thinking>...</thinking>` text
|
|
117
|
+
* representation for outbound replay through adapters that cannot natively
|
|
118
|
+
* echo reasoning.
|
|
119
|
+
*
|
|
120
|
+
* Behaviour:
|
|
121
|
+
* - `redactedData` non-empty → returns {@link REDACTED_PLACEHOLDER} exactly.
|
|
122
|
+
* Plaintext is unavailable so emitting the original `text` (which is
|
|
123
|
+
* conventionally empty for redacted blocks) would yield an empty
|
|
124
|
+
* `<thinking></thinking>` and confuse the next model; the placeholder
|
|
125
|
+
* signals that reasoning occurred without leaking any content.
|
|
126
|
+
* - Empty non-redacted text → returns the empty string. Callers should
|
|
127
|
+
* skip emitting an assistant-message slot rather than pushing an empty
|
|
128
|
+
* payload.
|
|
129
|
+
* - Otherwise → returns `<thinking>${truncate(text)}</thinking>`.
|
|
130
|
+
*/
|
|
131
|
+
export declare function reasoningBlockToInlineText(block: ReasoningBlock, options?: ReasoningFallbackOptions): string;
|
|
132
|
+
//# sourceMappingURL=reasoning-fallback.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reasoning-fallback.d.ts","sourceRoot":"","sources":["../../src/llm/reasoning-fallback.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAEjD;;;;GAIG;AACH,eAAO,MAAM,oCAAoC,OAAO,CAAA;AAExD;;;;;;;;GAQG;AACH,eAAO,MAAM,aAAa,QAA2B,CAAA;AAQrD,MAAM,WAAW,wBAAwB;IACvC;;;;;;;;;;;;OAYG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAC3B;AAED;;;;;;GAMG;AACH,MAAM,WAAW,wBAAwB;IACvC,8DAA8D;IAC9D,QAAQ,CAAC,uBAAuB,CAAC,EAAE,OAAO,CAAA;IAC1C,4DAA4D;IAC5D,QAAQ,CAAC,qBAAqB,CAAC,EAAE,OAAO,GAAG;QAAE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IACzE;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,QAAQ,CAAC,2BAA2B,CAAC,EAAE,MAAM,CAAA;CAC9C;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,gCAAgC,CAC9C,OAAO,EAAE,wBAAwB,GAAG,SAAS,GAC5C,MAAM,GAAG,SAAS,CAQpB;AA0BD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,cAAc,EACrB,OAAO,CAAC,EAAE,wBAAwB,GACjC,MAAM,CAOR"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Shared `<thinking>` text fallback for {@link ReasoningBlock}
|
|
3
|
+
* round-tripping across adapter boundaries.
|
|
4
|
+
*
|
|
5
|
+
* When an outbound IR-to-native conversion encounters a {@link ReasoningBlock}
|
|
6
|
+
* that the target adapter cannot natively echo — either because the wire
|
|
7
|
+
* protocol does not accept reasoning input at all
|
|
8
|
+
* ({@link LLMAdapter.capabilities.echoesReasoning} `=== 'never'`) or because
|
|
9
|
+
* the block's {@link ReasoningBlock.provenance} does not match the target
|
|
10
|
+
* adapter for an `'own-issued'` adapter — this helper converts the block to
|
|
11
|
+
* an inline `<thinking>...</thinking>` text snippet that callers prepend to
|
|
12
|
+
* the next outgoing text part.
|
|
13
|
+
*
|
|
14
|
+
* SAFETY CONTRACT (one-way invariant):
|
|
15
|
+
* The reverse direction — parsing `<thinking>` text back into a
|
|
16
|
+
* {@link ReasoningBlock} — must never happen. A reconstructed block would
|
|
17
|
+
* carry no verifiable signature and would be rejected if re-sent to
|
|
18
|
+
* Anthropic / Bedrock / Gemini 3. `ReasoningBlock` instances are only ever
|
|
19
|
+
* produced from native API extraction (and always stamped with
|
|
20
|
+
* `provenance`), never from text parsing.
|
|
21
|
+
*
|
|
22
|
+
* Wiring (post #223 Phase 2): every adapter outbound path consults
|
|
23
|
+
* {@link ReasoningOutboundOptions} resolved from
|
|
24
|
+
* {@link AgentConfig.preserveReasoningAsText} +
|
|
25
|
+
* {@link AgentConfig.compressReasoningText}. When opt-in is off, reasoning
|
|
26
|
+
* blocks that can't be native-echoed are dropped silently (preserving
|
|
27
|
+
* pre-Phase-2 behaviour). When opt-in is on, they pass through
|
|
28
|
+
* {@link reasoningBlockToInlineText} and become inline `<thinking>` text.
|
|
29
|
+
*/
|
|
30
|
+
/**
|
|
31
|
+
* Default maximum character budget per `<thinking>` block after truncation.
|
|
32
|
+
* Aligned with the value used by the OpenAI-family private replay helper
|
|
33
|
+
* shipped in #234 so the Phase 2 consolidation is behaviour-preserving.
|
|
34
|
+
*/
|
|
35
|
+
export const DEFAULT_REASONING_FALLBACK_MAX_CHARS = 1200;
|
|
36
|
+
/**
|
|
37
|
+
* Sentinel value the resolver returns when the caller explicitly disables
|
|
38
|
+
* truncation (`compressReasoningText: false`). Exported so the special-case
|
|
39
|
+
* branch in {@link resolveMaxChars} stays searchable — removing the branch
|
|
40
|
+
* would silently re-introduce truncation for users who opted out.
|
|
41
|
+
*
|
|
42
|
+
* Equal to `Number.POSITIVE_INFINITY`; the resolver maps it to
|
|
43
|
+
* `Number.MAX_SAFE_INTEGER` so `text.length <= maxChars` is always true.
|
|
44
|
+
*/
|
|
45
|
+
export const NO_TRUNCATION = Number.POSITIVE_INFINITY;
|
|
46
|
+
/** Marker inserted between head and tail when reasoning text is truncated. */
|
|
47
|
+
const TRUNCATION_MARKER = '...[truncated]...';
|
|
48
|
+
/** Placeholder emitted in place of opaque encrypted (redacted) reasoning. */
|
|
49
|
+
const REDACTED_PLACEHOLDER = '<thinking>[redacted]</thinking>';
|
|
50
|
+
/**
|
|
51
|
+
* Resolve {@link ReasoningOutboundOptions} to the `maxChars` value accepted
|
|
52
|
+
* by {@link reasoningBlockToInlineText}. Encodes the
|
|
53
|
+
* default-on-when-preserve-on semantics documented in #223:
|
|
54
|
+
*
|
|
55
|
+
* - `preserve=false` → returns `undefined` (fallback never runs;
|
|
56
|
+
* callers must check `preserve` themselves)
|
|
57
|
+
* - `compress=undefined`/`true`→ returns `undefined` so the helper applies
|
|
58
|
+
* its own default head+tail budget
|
|
59
|
+
* - `compress=false` → returns {@link NO_TRUNCATION}; the helper
|
|
60
|
+
* treats this as a no-op cap (full text)
|
|
61
|
+
* - `compress={minChars: N}` → `N` (the threshold value also serves as
|
|
62
|
+
* the truncation cap)
|
|
63
|
+
*/
|
|
64
|
+
export function resolveReasoningOutboundMaxChars(options) {
|
|
65
|
+
if (options?.preserveReasoningAsText !== true)
|
|
66
|
+
return undefined;
|
|
67
|
+
const compress = options.compressReasoningText;
|
|
68
|
+
if (compress === false)
|
|
69
|
+
return NO_TRUNCATION;
|
|
70
|
+
if (compress === undefined || compress === true) {
|
|
71
|
+
return undefined; // helper applies its own default when maxChars omitted
|
|
72
|
+
}
|
|
73
|
+
return compress.minChars; // undefined → helper default, number → that cap
|
|
74
|
+
}
|
|
75
|
+
function resolveMaxChars(value) {
|
|
76
|
+
if (value === undefined)
|
|
77
|
+
return DEFAULT_REASONING_FALLBACK_MAX_CHARS;
|
|
78
|
+
if (value === NO_TRUNCATION)
|
|
79
|
+
return Number.MAX_SAFE_INTEGER;
|
|
80
|
+
if (!Number.isFinite(value))
|
|
81
|
+
return 1;
|
|
82
|
+
const floored = Math.floor(value);
|
|
83
|
+
if (floored < 1)
|
|
84
|
+
return 1;
|
|
85
|
+
return floored;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Truncate `text` to at most `maxChars` characters via a head+tail excerpt
|
|
89
|
+
* with a `...[truncated]...` marker. The head receives ~70% of the budget
|
|
90
|
+
* so the model sees more of the leading reasoning steps. When `maxChars` is
|
|
91
|
+
* smaller than the marker itself, falls back to a simple head slice.
|
|
92
|
+
*/
|
|
93
|
+
function truncate(text, maxChars) {
|
|
94
|
+
if (text.length <= maxChars)
|
|
95
|
+
return text;
|
|
96
|
+
if (TRUNCATION_MARKER.length >= maxChars)
|
|
97
|
+
return text.slice(0, maxChars);
|
|
98
|
+
const budget = maxChars - TRUNCATION_MARKER.length;
|
|
99
|
+
const head = Math.ceil(budget * 0.7);
|
|
100
|
+
const tail = budget - head;
|
|
101
|
+
return `${text.slice(0, head)}${TRUNCATION_MARKER}${text.slice(text.length - tail)}`;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Convert a {@link ReasoningBlock} into its `<thinking>...</thinking>` text
|
|
105
|
+
* representation for outbound replay through adapters that cannot natively
|
|
106
|
+
* echo reasoning.
|
|
107
|
+
*
|
|
108
|
+
* Behaviour:
|
|
109
|
+
* - `redactedData` non-empty → returns {@link REDACTED_PLACEHOLDER} exactly.
|
|
110
|
+
* Plaintext is unavailable so emitting the original `text` (which is
|
|
111
|
+
* conventionally empty for redacted blocks) would yield an empty
|
|
112
|
+
* `<thinking></thinking>` and confuse the next model; the placeholder
|
|
113
|
+
* signals that reasoning occurred without leaking any content.
|
|
114
|
+
* - Empty non-redacted text → returns the empty string. Callers should
|
|
115
|
+
* skip emitting an assistant-message slot rather than pushing an empty
|
|
116
|
+
* payload.
|
|
117
|
+
* - Otherwise → returns `<thinking>${truncate(text)}</thinking>`.
|
|
118
|
+
*/
|
|
119
|
+
export function reasoningBlockToInlineText(block, options) {
|
|
120
|
+
if (typeof block.redactedData === 'string' && block.redactedData.length > 0) {
|
|
121
|
+
return REDACTED_PLACEHOLDER;
|
|
122
|
+
}
|
|
123
|
+
if (block.text.length === 0)
|
|
124
|
+
return '';
|
|
125
|
+
const max = resolveMaxChars(options?.maxChars);
|
|
126
|
+
return `<thinking>${truncate(block.text, max)}</thinking>`;
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=reasoning-fallback.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reasoning-fallback.js","sourceRoot":"","sources":["../../src/llm/reasoning-fallback.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAIH;;;;GAIG;AACH,MAAM,CAAC,MAAM,oCAAoC,GAAG,IAAI,CAAA;AAExD;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,MAAM,CAAC,iBAAiB,CAAA;AAErD,8EAA8E;AAC9E,MAAM,iBAAiB,GAAG,mBAAmB,CAAA;AAE7C,6EAA6E;AAC7E,MAAM,oBAAoB,GAAG,iCAAiC,CAAA;AAuD9D;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,gCAAgC,CAC9C,OAA6C;IAE7C,IAAI,OAAO,EAAE,uBAAuB,KAAK,IAAI;QAAE,OAAO,SAAS,CAAA;IAC/D,MAAM,QAAQ,GAAG,OAAO,CAAC,qBAAqB,CAAA;IAC9C,IAAI,QAAQ,KAAK,KAAK;QAAE,OAAO,aAAa,CAAA;IAC5C,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;QAChD,OAAO,SAAS,CAAA,CAAE,uDAAuD;IAC3E,CAAC;IACD,OAAO,QAAQ,CAAC,QAAQ,CAAA,CAAE,gDAAgD;AAC5E,CAAC;AAED,SAAS,eAAe,CAAC,KAAyB;IAChD,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,oCAAoC,CAAA;IACpE,IAAI,KAAK,KAAK,aAAa;QAAE,OAAO,MAAM,CAAC,gBAAgB,CAAA;IAC3D,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,CAAC,CAAA;IACrC,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;IACjC,IAAI,OAAO,GAAG,CAAC;QAAE,OAAO,CAAC,CAAA;IACzB,OAAO,OAAO,CAAA;AAChB,CAAC;AAED;;;;;GAKG;AACH,SAAS,QAAQ,CAAC,IAAY,EAAE,QAAgB;IAC9C,IAAI,IAAI,CAAC,MAAM,IAAI,QAAQ;QAAE,OAAO,IAAI,CAAA;IACxC,IAAI,iBAAiB,CAAC,MAAM,IAAI,QAAQ;QAAE,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAA;IACxE,MAAM,MAAM,GAAG,QAAQ,GAAG,iBAAiB,CAAC,MAAM,CAAA;IAClD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,GAAG,CAAC,CAAA;IACpC,MAAM,IAAI,GAAG,MAAM,GAAG,IAAI,CAAA;IAC1B,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,iBAAiB,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,CAAA;AACtF,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,0BAA0B,CACxC,KAAqB,EACrB,OAAkC;IAElC,IAAI,OAAO,KAAK,CAAC,YAAY,KAAK,QAAQ,IAAI,KAAK,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC5E,OAAO,oBAAoB,CAAA;IAC7B,CAAC;IACD,IAAI,KAAK,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAA;IACtC,MAAM,GAAG,GAAG,eAAe,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAA;IAC9C,OAAO,aAAa,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,GAAG,CAAC,aAAa,CAAA;AAC5D,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"orchestrator.d.ts","sourceRoot":"","sources":["../../src/orchestrator/orchestrator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AAEH,OAAO,KAAK,EACV,WAAW,EACX,cAAc,EAEd,cAAc,EACd,kBAAkB,EAElB,IAAI,EAIJ,UAAU,EAEV,aAAa,EAEd,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"orchestrator.d.ts","sourceRoot":"","sources":["../../src/orchestrator/orchestrator.ts"],"names":[],"mappings":"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"}
|
|
@@ -46,6 +46,7 @@ import { emitTrace, generateRunId } from '../utils/trace.js';
|
|
|
46
46
|
import { ToolRegistry } from '../tool/framework.js';
|
|
47
47
|
import { ToolExecutor } from '../tool/executor.js';
|
|
48
48
|
import { registerBuiltInTools } from '../tool/built-in/index.js';
|
|
49
|
+
import { defaultWorkspaceDir } from '../tool/built-in/path-safety.js';
|
|
49
50
|
import { Team } from '../team/team.js';
|
|
50
51
|
import { TaskQueue } from '../task/queue.js';
|
|
51
52
|
import { createTask } from '../task/task.js';
|
|
@@ -321,6 +322,21 @@ function parseTaskSpecs(raw) {
|
|
|
321
322
|
return null;
|
|
322
323
|
}
|
|
323
324
|
}
|
|
325
|
+
function buildRevealCoordinatorLines(revealContext, assignee) {
|
|
326
|
+
return [
|
|
327
|
+
'## Team context',
|
|
328
|
+
`Goal: ${revealContext.goal}`,
|
|
329
|
+
`Team: ${revealContext.rosterNames.join(', ')}`,
|
|
330
|
+
`Your role in this team: ${assignee}`,
|
|
331
|
+
'Assignment: You are responsible for the prompt below in this team run.',
|
|
332
|
+
'',
|
|
333
|
+
];
|
|
334
|
+
}
|
|
335
|
+
function prependRevealCoordinatorContext(prompt, revealContext, assignee) {
|
|
336
|
+
return revealContext
|
|
337
|
+
? [...buildRevealCoordinatorLines(revealContext, assignee), prompt].join('\n')
|
|
338
|
+
: prompt;
|
|
339
|
+
}
|
|
324
340
|
/**
|
|
325
341
|
* Build {@link TeamInfo} for tool context, including nested `runDelegatedAgent`
|
|
326
342
|
* that respects pool capacity to avoid semaphore deadlocks.
|
|
@@ -365,6 +381,7 @@ function buildTaskAgentTeamInfo(ctx, taskId, traceBase, delegationDepth, delegat
|
|
|
365
381
|
provider: targetConfig.provider ?? ctx.config.defaultProvider,
|
|
366
382
|
baseURL: targetConfig.baseURL ?? ctx.config.defaultBaseURL,
|
|
367
383
|
apiKey: targetConfig.apiKey ?? ctx.config.defaultApiKey,
|
|
384
|
+
cwd: targetConfig.cwd === undefined ? ctx.config.defaultCwd : targetConfig.cwd,
|
|
368
385
|
};
|
|
369
386
|
const tempAgent = buildAgent(effective, { includeDelegateTool: true });
|
|
370
387
|
const nestedTeam = buildTaskAgentTeamInfo(ctx, taskId, traceBase, delegationDepth + 1, [...delegationChain, targetAgent]);
|
|
@@ -374,7 +391,7 @@ function buildTaskAgentTeamInfo(ctx, taskId, traceBase, delegationDepth, delegat
|
|
|
374
391
|
taskId,
|
|
375
392
|
team: nestedTeam,
|
|
376
393
|
};
|
|
377
|
-
return pool.runEphemeral(tempAgent, prompt, childOpts);
|
|
394
|
+
return pool.runEphemeral(tempAgent, prependRevealCoordinatorContext(prompt, ctx.revealCoordinatorContext, targetAgent), childOpts);
|
|
378
395
|
};
|
|
379
396
|
return {
|
|
380
397
|
name: ctx.team.name,
|
|
@@ -469,7 +486,7 @@ async function executeQueue(queue, ctx) {
|
|
|
469
486
|
data: task,
|
|
470
487
|
});
|
|
471
488
|
// Build the prompt: task description + dependency-only context by default.
|
|
472
|
-
const prompt = await buildTaskPrompt(task, team, queue);
|
|
489
|
+
const prompt = await buildTaskPrompt(task, team, queue, ctx.revealCoordinatorContext);
|
|
473
490
|
// Trace + abort + team tool context (delegate_to_agent)
|
|
474
491
|
const traceBase = {
|
|
475
492
|
...(config.onTrace
|
|
@@ -622,17 +639,23 @@ async function executeQueue(queue, ctx) {
|
|
|
622
639
|
* Build the agent prompt for a specific task.
|
|
623
640
|
*
|
|
624
641
|
* Injects:
|
|
642
|
+
* - Optional team-context block at the top when `revealContext` is provided
|
|
643
|
+
* (set via `RunTeamOptions.revealCoordinator`)
|
|
625
644
|
* - Task title and description
|
|
626
645
|
* - Direct dependency task results by default (clean slate when none)
|
|
627
646
|
* - Optional full shared-memory context when `task.memoryScope === 'all'`
|
|
628
647
|
* - Any messages addressed to this agent from the team bus
|
|
629
648
|
*/
|
|
630
|
-
async function buildTaskPrompt(task, team, queue) {
|
|
631
|
-
const lines = [
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
649
|
+
async function buildTaskPrompt(task, team, queue, revealContext) {
|
|
650
|
+
const lines = [];
|
|
651
|
+
// `task.assignee` is belt-and-suspenders: `executeQueue` already fails any
|
|
652
|
+
// task without an assignee before reaching this function (see the assignee
|
|
653
|
+
// check in the dispatch loop). The guard here documents the precondition and
|
|
654
|
+
// protects against future refactors that move the call site.
|
|
655
|
+
if (revealContext && task.assignee) {
|
|
656
|
+
lines.push(...buildRevealCoordinatorLines(revealContext, task.assignee));
|
|
657
|
+
}
|
|
658
|
+
lines.push(`# Task: ${task.title}`, '', task.description);
|
|
636
659
|
if (task.memoryScope === 'all') {
|
|
637
660
|
// Explicit opt-in for full visibility (legacy/shared-memory behavior).
|
|
638
661
|
const sharedMem = team.getSharedMemoryInstance();
|
|
@@ -698,6 +721,10 @@ export class OpenMultiAgent {
|
|
|
698
721
|
defaultProvider: config.defaultProvider ?? 'anthropic',
|
|
699
722
|
defaultBaseURL: config.defaultBaseURL,
|
|
700
723
|
defaultApiKey: config.defaultApiKey,
|
|
724
|
+
// `defaultCwd === undefined` means "use the default sandbox rooted at
|
|
725
|
+
// <cwd>/.agent-workspace". An explicit `null` propagates through to
|
|
726
|
+
// disable the filesystem sandbox; a string sets a custom sandbox root.
|
|
727
|
+
defaultCwd: config.defaultCwd === undefined ? defaultWorkspaceDir() : config.defaultCwd,
|
|
701
728
|
maxTokenBudget: config.maxTokenBudget,
|
|
702
729
|
onApproval: config.onApproval,
|
|
703
730
|
onPlanReady: config.onPlanReady,
|
|
@@ -748,6 +775,7 @@ export class OpenMultiAgent {
|
|
|
748
775
|
provider: config.provider ?? this.config.defaultProvider,
|
|
749
776
|
baseURL: config.baseURL ?? this.config.defaultBaseURL,
|
|
750
777
|
apiKey: config.apiKey ?? this.config.defaultApiKey,
|
|
778
|
+
cwd: config.cwd === undefined ? this.config.defaultCwd : config.cwd,
|
|
751
779
|
maxTokenBudget: effectiveBudget,
|
|
752
780
|
};
|
|
753
781
|
const agent = buildAgent(effective);
|
|
@@ -835,6 +863,7 @@ export class OpenMultiAgent {
|
|
|
835
863
|
provider: bestAgent.provider ?? this.config.defaultProvider,
|
|
836
864
|
baseURL: bestAgent.baseURL ?? this.config.defaultBaseURL,
|
|
837
865
|
apiKey: bestAgent.apiKey ?? this.config.defaultApiKey,
|
|
866
|
+
cwd: bestAgent.cwd === undefined ? this.config.defaultCwd : bestAgent.cwd,
|
|
838
867
|
maxTokenBudget: effectiveBudget,
|
|
839
868
|
};
|
|
840
869
|
const agent = buildAgent(effective);
|
|
@@ -907,6 +936,9 @@ export class OpenMultiAgent {
|
|
|
907
936
|
toolPreset: coordinatorOverrides?.toolPreset,
|
|
908
937
|
tools: coordinatorOverrides?.tools,
|
|
909
938
|
disallowedTools: coordinatorOverrides?.disallowedTools,
|
|
939
|
+
cwd: coordinatorOverrides?.cwd === undefined
|
|
940
|
+
? this.config.defaultCwd
|
|
941
|
+
: coordinatorOverrides.cwd,
|
|
910
942
|
loopDetection: coordinatorOverrides?.loopDetection,
|
|
911
943
|
timeoutMs: coordinatorOverrides?.timeoutMs,
|
|
912
944
|
};
|
|
@@ -980,6 +1012,14 @@ export class OpenMultiAgent {
|
|
|
980
1012
|
budgetExceededTriggered: false,
|
|
981
1013
|
budgetExceededReason: undefined,
|
|
982
1014
|
taskMetrics,
|
|
1015
|
+
...(options?.revealCoordinator
|
|
1016
|
+
? {
|
|
1017
|
+
revealCoordinatorContext: {
|
|
1018
|
+
goal,
|
|
1019
|
+
rosterNames: agentConfigs.map((a) => a.name),
|
|
1020
|
+
},
|
|
1021
|
+
}
|
|
1022
|
+
: {}),
|
|
983
1023
|
};
|
|
984
1024
|
const planTasks = queue.list();
|
|
985
1025
|
const planReadyStartMs = Date.now();
|
|
@@ -1040,6 +1080,9 @@ export class OpenMultiAgent {
|
|
|
1040
1080
|
// ------------------------------------------------------------------
|
|
1041
1081
|
// Step 5: Coordinator synthesises final result
|
|
1042
1082
|
// ------------------------------------------------------------------
|
|
1083
|
+
if (options?.abortSignal?.aborted) {
|
|
1084
|
+
return this.buildTeamRunResult(agentResults, goal, taskRecords);
|
|
1085
|
+
}
|
|
1043
1086
|
if (maxTokenBudget !== undefined
|
|
1044
1087
|
&& cumulativeUsage.input_tokens + cumulativeUsage.output_tokens > maxTokenBudget) {
|
|
1045
1088
|
return this.buildTeamRunResult(agentResults, goal, taskRecords);
|
|
@@ -1290,6 +1333,12 @@ export class OpenMultiAgent {
|
|
|
1290
1333
|
*/
|
|
1291
1334
|
loadSpecsIntoQueue(specs, agentConfigs, queue) {
|
|
1292
1335
|
const agentNames = new Set(agentConfigs.map((a) => a.name));
|
|
1336
|
+
const normalizeTitle = (title) => title.toLowerCase().trim();
|
|
1337
|
+
const titleCounts = new Map();
|
|
1338
|
+
for (const spec of specs) {
|
|
1339
|
+
const key = normalizeTitle(spec.title);
|
|
1340
|
+
titleCounts.set(key, (titleCounts.get(key) ?? 0) + 1);
|
|
1341
|
+
}
|
|
1293
1342
|
// First pass: create tasks (without dependencies) to get stable IDs.
|
|
1294
1343
|
const titleToId = new Map();
|
|
1295
1344
|
const createdTasks = [];
|
|
@@ -1305,7 +1354,10 @@ export class OpenMultiAgent {
|
|
|
1305
1354
|
retryDelayMs: spec.retryDelayMs,
|
|
1306
1355
|
retryBackoff: spec.retryBackoff,
|
|
1307
1356
|
});
|
|
1308
|
-
|
|
1357
|
+
const titleKey = normalizeTitle(spec.title);
|
|
1358
|
+
if ((titleCounts.get(titleKey) ?? 0) === 1) {
|
|
1359
|
+
titleToId.set(titleKey, task.id);
|
|
1360
|
+
}
|
|
1309
1361
|
createdTasks.push(task);
|
|
1310
1362
|
}
|
|
1311
1363
|
// Second pass: resolve title-based dependsOn to IDs.
|
|
@@ -1317,20 +1369,29 @@ export class OpenMultiAgent {
|
|
|
1317
1369
|
continue;
|
|
1318
1370
|
}
|
|
1319
1371
|
const resolvedDeps = [];
|
|
1372
|
+
const unresolvedDeps = [];
|
|
1320
1373
|
for (const depRef of spec.dependsOn) {
|
|
1321
1374
|
// Accept both raw IDs and title strings
|
|
1322
1375
|
const byId = createdTasks.find((t) => t.id === depRef);
|
|
1323
|
-
const
|
|
1376
|
+
const depTitleKey = normalizeTitle(depRef);
|
|
1377
|
+
const byTitle = titleToId.get(depTitleKey);
|
|
1324
1378
|
const resolvedId = byId?.id ?? byTitle;
|
|
1325
1379
|
if (resolvedId) {
|
|
1326
1380
|
resolvedDeps.push(resolvedId);
|
|
1327
1381
|
}
|
|
1382
|
+
else {
|
|
1383
|
+
const count = titleCounts.get(depTitleKey) ?? 0;
|
|
1384
|
+
unresolvedDeps.push(count > 1 ? `${depRef} (ambiguous duplicate title)` : depRef);
|
|
1385
|
+
}
|
|
1328
1386
|
}
|
|
1329
1387
|
const taskWithDeps = {
|
|
1330
1388
|
...task,
|
|
1331
1389
|
dependsOn: resolvedDeps.length > 0 ? resolvedDeps : undefined,
|
|
1332
1390
|
};
|
|
1333
1391
|
queue.add(taskWithDeps);
|
|
1392
|
+
if (unresolvedDeps.length > 0) {
|
|
1393
|
+
queue.fail(task.id, `Unresolved dependency reference(s): ${unresolvedDeps.join(', ')}`);
|
|
1394
|
+
}
|
|
1334
1395
|
}
|
|
1335
1396
|
}
|
|
1336
1397
|
/** Build an {@link AgentPool} from a list of agent configurations. */
|
|
@@ -1343,6 +1404,7 @@ export class OpenMultiAgent {
|
|
|
1343
1404
|
provider: config.provider ?? this.config.defaultProvider,
|
|
1344
1405
|
baseURL: config.baseURL ?? this.config.defaultBaseURL,
|
|
1345
1406
|
apiKey: config.apiKey ?? this.config.defaultApiKey,
|
|
1407
|
+
cwd: config.cwd === undefined ? this.config.defaultCwd : config.cwd,
|
|
1346
1408
|
};
|
|
1347
1409
|
pool.add(buildAgent(effective, { includeDelegateTool: true }));
|
|
1348
1410
|
}
|