qlogicagent 0.2.1 → 0.3.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/dist/agent.js +1 -0
- package/dist/cli.js +9 -0
- package/dist/contracts.js +1 -0
- package/dist/index.js +5 -15
- package/dist/orchestration.js +118 -0
- package/package.json +56 -42
- package/dist/agent/agent.js +0 -113
- package/dist/agent/tool-loop.js +0 -575
- package/dist/agent/types.js +0 -14
- package/dist/cli/main.js +0 -23
- package/dist/cli/stdio-server.js +0 -463
- package/dist/config/config.js +0 -21
- package/dist/contracts/hooks.js +0 -7
- package/dist/contracts/index.js +0 -10
- package/dist/contracts/planner.js +0 -2
- package/dist/contracts/skill-candidate.js +0 -195
- package/dist/contracts/todo.js +0 -9
- package/dist/llm/builtin-providers.js +0 -531
- package/dist/llm/index.js +0 -14
- package/dist/llm/llm-client.js +0 -67
- package/dist/llm/model-catalog.js +0 -191
- package/dist/llm/provider-def.js +0 -12
- package/dist/llm/provider-registry.js +0 -147
- package/dist/llm/transport.js +0 -27
- package/dist/llm/transports/anthropic-messages.js +0 -293
- package/dist/llm/transports/openai-chat.js +0 -165
- package/dist/orchestration/agent-registry.js +0 -116
- package/dist/orchestration/approval-aware-tool-plan.js +0 -87
- package/dist/orchestration/context-compression.js +0 -583
- package/dist/orchestration/conversation-repair.js +0 -429
- package/dist/orchestration/curator-scheduler.js +0 -135
- package/dist/orchestration/embedded-failover-policy.js +0 -168
- package/dist/orchestration/error-classification.js +0 -77
- package/dist/orchestration/failover-classification.js +0 -381
- package/dist/orchestration/failover-error.js +0 -198
- package/dist/orchestration/fork-subagent.js +0 -98
- package/dist/orchestration/index.js +0 -267
- package/dist/orchestration/memory-flush-policy.js +0 -85
- package/dist/orchestration/memory-provider.js +0 -2
- package/dist/orchestration/parallel-tool-calls.js +0 -59
- package/dist/orchestration/prompt-cache-strategy.js +0 -228
- package/dist/orchestration/reactive-compact.js +0 -78
- package/dist/orchestration/retry-loop.js +0 -24
- package/dist/orchestration/skill-candidate.js +0 -141
- package/dist/orchestration/skill-consolidation.js +0 -220
- package/dist/orchestration/skill-improvement.js +0 -66
- package/dist/orchestration/skill-similarity.js +0 -131
- package/dist/orchestration/streaming-tool-executor.js +0 -96
- package/dist/orchestration/team-orchestration.js +0 -369
- package/dist/orchestration/team-tool-loop-wiring.js +0 -147
- package/dist/orchestration/tool-choice-policy.js +0 -164
- package/dist/orchestration/tool-loop-state.js +0 -133
- package/dist/orchestration/tool-schema.js +0 -297
- package/dist/orchestration/transcript-repair.js +0 -426
- package/dist/orchestration/turn-loop-guard.js +0 -92
- package/dist/orchestration/web-browser-policy.js +0 -39
- package/dist/runtime/context-compression.js +0 -274
- package/dist/runtime/hook-registry.js +0 -53
- package/dist/runtime/memory-hooks.js +0 -65
- package/dist/runtime/tool-eligibility.js +0 -111
- package/dist/skills/index.js +0 -82
- package/dist/skills/memory-extractor.js +0 -173
- package/dist/skills/memory-query-tool.js +0 -127
- package/dist/skills/memory-store.js +0 -228
- package/dist/skills/memory-tool.js +0 -192
- package/dist/skills/portable-tool.js +0 -14
- package/dist/skills/qmemory-adapter.js +0 -165
- package/dist/skills/skill-frontmatter.js +0 -344
- package/dist/skills/skill-guard.js +0 -229
- package/dist/skills/skill-loader.js +0 -303
- package/dist/skills/skill-source.js +0 -126
- package/dist/skills/skill-types.js +0 -6
- package/dist/skills/think-tool.js +0 -59
- package/dist/skills/todo-tool.js +0 -114
- package/dist/skills/tools/agent-tool.js +0 -142
- package/dist/skills/tools/apply-patch-tool.js +0 -184
- package/dist/skills/tools/ask-user-tool.js +0 -121
- package/dist/skills/tools/brief-tool.js +0 -95
- package/dist/skills/tools/browser-tool.js +0 -155
- package/dist/skills/tools/checkpoint-tool.js +0 -102
- package/dist/skills/tools/config-tool.js +0 -143
- package/dist/skills/tools/cron-tool.js +0 -175
- package/dist/skills/tools/edit-tool.js +0 -70
- package/dist/skills/tools/exec-tool.js +0 -133
- package/dist/skills/tools/image-generate-tool.js +0 -67
- package/dist/skills/tools/instructions-tool.js +0 -187
- package/dist/skills/tools/lsp-tool.js +0 -227
- package/dist/skills/tools/mcp-client-types.js +0 -53
- package/dist/skills/tools/mcp-tool.js +0 -503
- package/dist/skills/tools/memory-tool.js +0 -88
- package/dist/skills/tools/monitor-tool.js +0 -131
- package/dist/skills/tools/music-generate-tool.js +0 -62
- package/dist/skills/tools/notify-tool.js +0 -62
- package/dist/skills/tools/patch-tool.js +0 -505
- package/dist/skills/tools/pdf-tool.js +0 -88
- package/dist/skills/tools/plan-mode-tool.js +0 -122
- package/dist/skills/tools/read-tool.js +0 -84
- package/dist/skills/tools/repl-tool.js +0 -69
- package/dist/skills/tools/search-tool.js +0 -225
- package/dist/skills/tools/send-message-tool.js +0 -76
- package/dist/skills/tools/skill-list-tool.js +0 -54
- package/dist/skills/tools/skill-manage-tool.js +0 -153
- package/dist/skills/tools/skill-view-tool.js +0 -72
- package/dist/skills/tools/sleep-tool.js +0 -81
- package/dist/skills/tools/structured-output-tool.js +0 -176
- package/dist/skills/tools/task-tool.js +0 -161
- package/dist/skills/tools/team-tool.js +0 -105
- package/dist/skills/tools/tool-search-tool.js +0 -110
- package/dist/skills/tools/tts-tool.js +0 -45
- package/dist/skills/tools/video-edit-tool.js +0 -74
- package/dist/skills/tools/video-generate-tool.js +0 -66
- package/dist/skills/tools/video-merge-tool.js +0 -92
- package/dist/skills/tools/video-upscale-tool.js +0 -52
- package/dist/skills/tools/web-fetch-tool.js +0 -92
- package/dist/skills/tools/web-search-tool.js +0 -86
- package/dist/skills/tools/worktree-tool.js +0 -147
- package/dist/skills/tools/write-tool.js +0 -81
- /package/dist/{agent → types/agent}/agent.d.ts +0 -0
- /package/dist/{agent → types/agent}/tool-loop.d.ts +0 -0
- /package/dist/{agent → types/agent}/types.d.ts +0 -0
- /package/dist/{cli → types/cli}/main.d.ts +0 -0
- /package/dist/{cli → types/cli}/stdio-server.d.ts +0 -0
- /package/dist/{config → types/config}/config.d.ts +0 -0
- /package/dist/{contracts → types/contracts}/hooks.d.ts +0 -0
- /package/dist/{contracts → types/contracts}/index.d.ts +0 -0
- /package/dist/{contracts → types/contracts}/planner.d.ts +0 -0
- /package/dist/{contracts → types/contracts}/skill-candidate.d.ts +0 -0
- /package/dist/{contracts → types/contracts}/todo.d.ts +0 -0
- /package/dist/{index.d.ts → types/index.d.ts} +0 -0
- /package/dist/{llm → types/llm}/builtin-providers.d.ts +0 -0
- /package/dist/{llm → types/llm}/index.d.ts +0 -0
- /package/dist/{llm → types/llm}/llm-client.d.ts +0 -0
- /package/dist/{llm → types/llm}/model-catalog.d.ts +0 -0
- /package/dist/{llm → types/llm}/provider-def.d.ts +0 -0
- /package/dist/{llm → types/llm}/provider-registry.d.ts +0 -0
- /package/dist/{llm → types/llm}/transport.d.ts +0 -0
- /package/dist/{llm → types/llm}/transports/anthropic-messages.d.ts +0 -0
- /package/dist/{llm → types/llm}/transports/openai-chat.d.ts +0 -0
- /package/dist/{orchestration → types/orchestration}/agent-registry.d.ts +0 -0
- /package/dist/{orchestration → types/orchestration}/approval-aware-tool-plan.d.ts +0 -0
- /package/dist/{orchestration → types/orchestration}/context-compression.d.ts +0 -0
- /package/dist/{orchestration → types/orchestration}/conversation-repair.d.ts +0 -0
- /package/dist/{orchestration → types/orchestration}/curator-scheduler.d.ts +0 -0
- /package/dist/{orchestration → types/orchestration}/embedded-failover-policy.d.ts +0 -0
- /package/dist/{orchestration → types/orchestration}/error-classification.d.ts +0 -0
- /package/dist/{orchestration → types/orchestration}/failover-classification.d.ts +0 -0
- /package/dist/{orchestration → types/orchestration}/failover-error.d.ts +0 -0
- /package/dist/{orchestration → types/orchestration}/fork-subagent.d.ts +0 -0
- /package/dist/{orchestration → types/orchestration}/index.d.ts +0 -0
- /package/dist/{orchestration → types/orchestration}/memory-flush-policy.d.ts +0 -0
- /package/dist/{orchestration → types/orchestration}/memory-provider.d.ts +0 -0
- /package/dist/{orchestration → types/orchestration}/parallel-tool-calls.d.ts +0 -0
- /package/dist/{orchestration → types/orchestration}/prompt-cache-strategy.d.ts +0 -0
- /package/dist/{orchestration → types/orchestration}/reactive-compact.d.ts +0 -0
- /package/dist/{orchestration → types/orchestration}/retry-loop.d.ts +0 -0
- /package/dist/{orchestration → types/orchestration}/skill-candidate.d.ts +0 -0
- /package/dist/{orchestration → types/orchestration}/skill-consolidation.d.ts +0 -0
- /package/dist/{orchestration → types/orchestration}/skill-improvement.d.ts +0 -0
- /package/dist/{orchestration → types/orchestration}/skill-similarity.d.ts +0 -0
- /package/dist/{orchestration → types/orchestration}/streaming-tool-executor.d.ts +0 -0
- /package/dist/{orchestration → types/orchestration}/team-orchestration.d.ts +0 -0
- /package/dist/{orchestration → types/orchestration}/team-tool-loop-wiring.d.ts +0 -0
- /package/dist/{orchestration → types/orchestration}/tool-choice-policy.d.ts +0 -0
- /package/dist/{orchestration → types/orchestration}/tool-loop-state.d.ts +0 -0
- /package/dist/{orchestration → types/orchestration}/tool-schema.d.ts +0 -0
- /package/dist/{orchestration → types/orchestration}/transcript-repair.d.ts +0 -0
- /package/dist/{orchestration → types/orchestration}/turn-loop-guard.d.ts +0 -0
- /package/dist/{orchestration → types/orchestration}/web-browser-policy.d.ts +0 -0
- /package/dist/{runtime → types/runtime}/context-compression.d.ts +0 -0
- /package/dist/{runtime → types/runtime}/hook-registry.d.ts +0 -0
- /package/dist/{runtime → types/runtime}/memory-hooks.d.ts +0 -0
- /package/dist/{runtime → types/runtime}/tool-eligibility.d.ts +0 -0
- /package/dist/{skills → types/skills}/index.d.ts +0 -0
- /package/dist/{skills → types/skills}/memory-extractor.d.ts +0 -0
- /package/dist/{skills → types/skills}/memory-query-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/memory-store.d.ts +0 -0
- /package/dist/{skills → types/skills}/memory-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/portable-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/qmemory-adapter.d.ts +0 -0
- /package/dist/{skills → types/skills}/skill-frontmatter.d.ts +0 -0
- /package/dist/{skills → types/skills}/skill-guard.d.ts +0 -0
- /package/dist/{skills → types/skills}/skill-loader.d.ts +0 -0
- /package/dist/{skills → types/skills}/skill-source.d.ts +0 -0
- /package/dist/{skills → types/skills}/skill-types.d.ts +0 -0
- /package/dist/{skills → types/skills}/think-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/todo-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/agent-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/apply-patch-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/ask-user-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/brief-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/browser-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/checkpoint-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/config-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/cron-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/edit-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/exec-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/image-generate-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/instructions-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/lsp-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/mcp-client-types.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/mcp-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/memory-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/monitor-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/music-generate-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/notify-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/patch-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/pdf-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/plan-mode-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/read-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/repl-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/search-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/send-message-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/skill-list-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/skill-manage-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/skill-view-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/sleep-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/structured-output-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/task-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/team-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/tool-search-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/tts-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/video-edit-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/video-generate-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/video-merge-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/video-upscale-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/web-fetch-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/web-search-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/worktree-tool.d.ts +0 -0
- /package/dist/{skills → types/skills}/tools/write-tool.d.ts +0 -0
package/dist/agent/tool-loop.js
DELETED
|
@@ -1,575 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tool loop state machine — faithful port of Hub's semantic-turn-tools.ts.
|
|
3
|
-
*
|
|
4
|
-
* Replaces Hub infrastructure with DI abstractions:
|
|
5
|
-
* - invokeGatewayTool() (WS) → ToolInvoker.invoke()
|
|
6
|
-
* - createAdminInferProxyClient().stream() → LLMTransport.stream()
|
|
7
|
-
* - SemanticTurnDeps (PG/Redis) → in-memory state
|
|
8
|
-
* - sidechain DB depth → in-memory depth counter
|
|
9
|
-
* - Hub approval flow → skipped (CLI is local, all tools pre-authorized)
|
|
10
|
-
*
|
|
11
|
-
* Keeps ALL orchestration strategy calls from Hub:
|
|
12
|
-
* - turn-loop-guard (abort, token budget, output escalation)
|
|
13
|
-
* - tool-loop-state (advance/settle/recover)
|
|
14
|
-
* - parallel/serial tool call scheduling
|
|
15
|
-
* - sidechain tool access policy filtering
|
|
16
|
-
* - consecutive failure early exit
|
|
17
|
-
* - API error recovery
|
|
18
|
-
* - message repair
|
|
19
|
-
* - skill learning evaluation
|
|
20
|
-
*
|
|
21
|
-
* Zero imports from express/pg/ioredis/ws.
|
|
22
|
-
*/
|
|
23
|
-
import { accumulateToolCalls } from "../llm/transport.js";
|
|
24
|
-
import { advanceToolLoopState, applyToolChoicePolicy, buildAssistantToolCallMessage, buildToolResultMessage, filterToolCallsByAccessPolicy, recoverToolLoopStateFromChatConversation, resolveParallelToolCallScheduling, resolveSidechainToolAccessByType, settleToolLoopState, calculateTokenWarningState, resolveApiErrorRecovery, resolveOutputTokenEscalation, shouldAbortTurn, createTurnLoopGuardState, shouldAttemptReactiveCompact, createReactiveCompactState, classifyError, buildSkillInstruction, } from "../orchestration/index.js";
|
|
25
|
-
// ── Budget constants (same as Hub) ───────────────────────────────────────────
|
|
26
|
-
const MAX_TOOL_BUDGET_CAP = 100;
|
|
27
|
-
const DEFAULT_TOOL_BUDGET = Math.min(Math.max(1, Number(process.env.TOOL_LOOP_DEFAULT_BUDGET) || 25), MAX_TOOL_BUDGET_CAP);
|
|
28
|
-
const MAX_CONSECUTIVE_FAILURES = 3;
|
|
29
|
-
const MAX_SIDECHAIN_DEPTH = 2;
|
|
30
|
-
// ── Helper: resolve budget ───────────────────────────────────────────────────
|
|
31
|
-
function resolveToolLoopBudget(requested) {
|
|
32
|
-
if (typeof requested === "number" && Number.isFinite(requested) && requested >= 1) {
|
|
33
|
-
return Math.min(Math.round(requested), MAX_TOOL_BUDGET_CAP);
|
|
34
|
-
}
|
|
35
|
-
return DEFAULT_TOOL_BUDGET;
|
|
36
|
-
}
|
|
37
|
-
// ── Main tool loop ───────────────────────────────────────────────────────────
|
|
38
|
-
/**
|
|
39
|
-
* Execute the tool loop — faithful port of Hub's orchestrateAgentTurnWithGatewayTools().
|
|
40
|
-
*
|
|
41
|
-
* Yields TurnEvent stream. The caller (Agent class) wraps this with
|
|
42
|
-
* start/end lifecycle and error handling.
|
|
43
|
-
*
|
|
44
|
-
* Events yielded:
|
|
45
|
-
* - delta: streaming text from LLM
|
|
46
|
-
* - tool_call: LLM requested a tool call
|
|
47
|
-
* - tool_result: tool execution completed
|
|
48
|
-
* - skill_instruction: skill learning evaluation result
|
|
49
|
-
* - end: turn completed successfully
|
|
50
|
-
* - error: unrecoverable error
|
|
51
|
-
*/
|
|
52
|
-
export async function* executeToolLoop(params, transport, toolInvoker, log) {
|
|
53
|
-
const { turnId, sessionId, messages: inputMessages, tools, model, apiKey, temperature, hooks, signal, } = params;
|
|
54
|
-
const hookCtx = { sessionId, turnId };
|
|
55
|
-
// ── Tool eligibility filtering (CC permission-level parity) ────
|
|
56
|
-
const { resolveToolEligibility } = await import("../runtime/tool-eligibility.js");
|
|
57
|
-
const eligibility = resolveToolEligibility(tools, params.toolEligibilityContext);
|
|
58
|
-
const effectiveTools = eligibility.eligibleTools;
|
|
59
|
-
// Emit blocked tool events
|
|
60
|
-
for (const blocked of eligibility.blockedTools) {
|
|
61
|
-
yield { type: "tool_blocked", turnId, callId: "", name: blocked.toolName, reason: "blocked-by-policy" };
|
|
62
|
-
}
|
|
63
|
-
// ── No tools: single LLM call, no loop ─────────────────────────
|
|
64
|
-
if (!effectiveTools.length) {
|
|
65
|
-
yield* executeSingleLLMRound(turnId, model, inputMessages, apiKey, temperature, signal, transport, log);
|
|
66
|
-
return;
|
|
67
|
-
}
|
|
68
|
-
// ── Tool loop setup ────────────────────────────────────────────
|
|
69
|
-
const toolBudget = resolveToolLoopBudget(params.maxRounds);
|
|
70
|
-
const messages = [...inputMessages];
|
|
71
|
-
// Recover tool loop state from conversation history
|
|
72
|
-
let toolLoopState = recoverToolLoopStateFromChatConversation({
|
|
73
|
-
maxRounds: toolBudget,
|
|
74
|
-
replayMessages: messages,
|
|
75
|
-
}).state;
|
|
76
|
-
// Turn-loop-guard config (CC-aligned)
|
|
77
|
-
const guardConfig = {
|
|
78
|
-
contextWindowTokens: params.contextWindowTokens ?? 128_000,
|
|
79
|
-
responseBufferTokens: 13_000,
|
|
80
|
-
maxOutputTokens: params.maxOutputTokens ?? 16_384,
|
|
81
|
-
abortSignal: signal,
|
|
82
|
-
reactiveCompactEnabled: true,
|
|
83
|
-
outputEscalationEnabled: true,
|
|
84
|
-
};
|
|
85
|
-
let guardState = createTurnLoopGuardState(guardConfig);
|
|
86
|
-
let reactiveCompactState = createReactiveCompactState();
|
|
87
|
-
// In-memory sidechain depth (replaces Hub's PG recursive CTE)
|
|
88
|
-
let sidechainStarted = false;
|
|
89
|
-
let activeSidechainToolPolicy = null;
|
|
90
|
-
const sidechainDepth = (params.parentDepth ?? 0) + 1;
|
|
91
|
-
let lastUsage;
|
|
92
|
-
let finalText = "";
|
|
93
|
-
let consecutiveFailedRounds = 0;
|
|
94
|
-
let totalUsage = { prompt: 0, completion: 0 };
|
|
95
|
-
// Skill learning metrics
|
|
96
|
-
const distinctToolNames = new Set();
|
|
97
|
-
let totalToolCallCount = 0;
|
|
98
|
-
// ── Main loop ──────────────────────────────────────────────────
|
|
99
|
-
for (let round = 0; round < toolBudget; round++) {
|
|
100
|
-
// Turn-loop-guard: pre-round abort check (CC-aligned)
|
|
101
|
-
if (shouldAbortTurn(guardState, guardConfig)) {
|
|
102
|
-
log.info(`turn aborted by guard at round ${round}`);
|
|
103
|
-
break;
|
|
104
|
-
}
|
|
105
|
-
// Turn-loop-guard: token budget pre-check
|
|
106
|
-
const tokenWarning = calculateTokenWarningState(guardState, guardConfig);
|
|
107
|
-
if (tokenWarning.level === "blocking") {
|
|
108
|
-
if (tokenWarning.reason === "prompt_too_long" &&
|
|
109
|
-
shouldAttemptReactiveCompact(reactiveCompactState)) {
|
|
110
|
-
reactiveCompactState.attemptedThisTurn = true;
|
|
111
|
-
guardState.hasAttemptedReactiveCompact = true;
|
|
112
|
-
log.info(`token budget blocking (${tokenWarning.reason}), reactive compact needed`);
|
|
113
|
-
}
|
|
114
|
-
log.info(`token budget blocking (${tokenWarning.reason}), breaking tool loop`);
|
|
115
|
-
break;
|
|
116
|
-
}
|
|
117
|
-
if (tokenWarning.level === "warning") {
|
|
118
|
-
log.info(`token budget warning: ${tokenWarning.usagePercent}% used, ${tokenWarning.remainingTokens} remaining`);
|
|
119
|
-
}
|
|
120
|
-
// Build tool loop request: tool choice policy + state recovery
|
|
121
|
-
const toolChoiceResult = applyToolChoicePolicy({
|
|
122
|
-
tools: effectiveTools,
|
|
123
|
-
toolChoice: params.toolChoice ?? "auto",
|
|
124
|
-
});
|
|
125
|
-
const repaired = recoverToolLoopStateFromChatConversation({
|
|
126
|
-
maxRounds: toolBudget,
|
|
127
|
-
replayMessages: messages,
|
|
128
|
-
lastStopReason: toolLoopState.lastStopReason,
|
|
129
|
-
options: { stopReason: toolLoopState.lastStopReason },
|
|
130
|
-
});
|
|
131
|
-
const requestMessages = toolChoiceResult.extraSystemPrompt
|
|
132
|
-
? [
|
|
133
|
-
{ role: "system", content: toolChoiceResult.extraSystemPrompt },
|
|
134
|
-
...repaired.state.replayMessages,
|
|
135
|
-
]
|
|
136
|
-
: repaired.state.replayMessages;
|
|
137
|
-
if (repaired.recoveryActions.length > 0) {
|
|
138
|
-
log.debug(`tool loop recovery: ${repaired.recoveryActions.map((a) => a.detail ?? a.kind).join("; ")}`);
|
|
139
|
-
}
|
|
140
|
-
log.debug(`round ${round + 1}/${toolBudget}, messages: ${requestMessages.length}`);
|
|
141
|
-
// Hook: turn.before_inference
|
|
142
|
-
hooks?.invoke("turn.before_inference", { ...hookCtx, model }).catch(() => { });
|
|
143
|
-
// ── Streaming LLM round with Hermes delta suppression ───────
|
|
144
|
-
let hasToolCalls = false;
|
|
145
|
-
const textChunks = [];
|
|
146
|
-
const toolCallAccumulator = new Map();
|
|
147
|
-
let finishReason = "stop";
|
|
148
|
-
let roundUsage;
|
|
149
|
-
let streamError = null;
|
|
150
|
-
try {
|
|
151
|
-
for await (const chunk of transport.stream({
|
|
152
|
-
model,
|
|
153
|
-
messages: requestMessages,
|
|
154
|
-
tools: toolChoiceResult.tools,
|
|
155
|
-
toolChoice: toolChoiceResult.normalizedToolChoice ??
|
|
156
|
-
"auto",
|
|
157
|
-
temperature,
|
|
158
|
-
maxTokens: guardState.currentMaxOutputTokens || undefined,
|
|
159
|
-
}, apiKey, signal)) {
|
|
160
|
-
switch (chunk.type) {
|
|
161
|
-
case "delta":
|
|
162
|
-
textChunks.push(chunk.text);
|
|
163
|
-
// Hermes delta suppression: stop text forwarding once tool_calls detected
|
|
164
|
-
if (!hasToolCalls) {
|
|
165
|
-
yield { type: "delta", turnId, text: chunk.text };
|
|
166
|
-
}
|
|
167
|
-
break;
|
|
168
|
-
case "tool_call_delta":
|
|
169
|
-
hasToolCalls = true;
|
|
170
|
-
accumulateToolCalls(toolCallAccumulator, chunk);
|
|
171
|
-
break;
|
|
172
|
-
case "reasoning_delta":
|
|
173
|
-
break;
|
|
174
|
-
case "usage":
|
|
175
|
-
roundUsage = {
|
|
176
|
-
prompt: chunk.promptTokens,
|
|
177
|
-
completion: chunk.completionTokens,
|
|
178
|
-
reasoning: chunk.reasoningTokens,
|
|
179
|
-
};
|
|
180
|
-
break;
|
|
181
|
-
case "done":
|
|
182
|
-
finishReason = chunk.finishReason;
|
|
183
|
-
break;
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
// Hook: turn.after_inference (success path)
|
|
187
|
-
if (!hasToolCalls) {
|
|
188
|
-
hooks?.invoke("turn.after_inference", { ...hookCtx, model }).catch(() => { });
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
catch (err) {
|
|
192
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
193
|
-
const status = typeof err?.status === "number"
|
|
194
|
-
? err.status
|
|
195
|
-
: undefined;
|
|
196
|
-
streamError = { status, message };
|
|
197
|
-
}
|
|
198
|
-
// Hook: turn.after_inference (error path)
|
|
199
|
-
if (streamError) {
|
|
200
|
-
hooks?.invoke("turn.after_inference", { ...hookCtx, model, response: { error: streamError.message } }).catch(() => { });
|
|
201
|
-
}
|
|
202
|
-
// ── Handle LLM errors with turn-loop-guard recovery ────────
|
|
203
|
-
if (streamError) {
|
|
204
|
-
const recovery = resolveApiErrorRecovery({ status: streamError.status ?? 500, message: streamError.message }, guardState, guardConfig);
|
|
205
|
-
if (recovery.action === "reactive_compact" &&
|
|
206
|
-
shouldAttemptReactiveCompact(reactiveCompactState)) {
|
|
207
|
-
reactiveCompactState.attemptedThisTurn = true;
|
|
208
|
-
guardState.hasAttemptedReactiveCompact = true;
|
|
209
|
-
yield { type: "recovery", turnId, action: "reactive_compact", detail: `API ${streamError.status ?? 500}: ${streamError.message}` };
|
|
210
|
-
}
|
|
211
|
-
if (recovery.action === "retry") {
|
|
212
|
-
yield { type: "recovery", turnId, action: "retry", detail: recovery.reason };
|
|
213
|
-
continue;
|
|
214
|
-
}
|
|
215
|
-
const category = classifyError(streamError.status, streamError.message);
|
|
216
|
-
yield { type: "error", turnId, error: streamError.message, code: category };
|
|
217
|
-
return;
|
|
218
|
-
}
|
|
219
|
-
// Accumulate usage
|
|
220
|
-
if (roundUsage) {
|
|
221
|
-
totalUsage.prompt += roundUsage.prompt;
|
|
222
|
-
totalUsage.completion += roundUsage.completion;
|
|
223
|
-
if (roundUsage.reasoning) {
|
|
224
|
-
totalUsage.reasoning = (totalUsage.reasoning ?? 0) + roundUsage.reasoning;
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
// Turn-loop-guard: update token state from usage feedback
|
|
228
|
-
if (roundUsage?.prompt) {
|
|
229
|
-
guardState.promptTokens = roundUsage.prompt;
|
|
230
|
-
}
|
|
231
|
-
// Turn-loop-guard: handle max_tokens truncation → escalate output budget (CC-aligned)
|
|
232
|
-
if (finishReason === "length" || finishReason === "max_tokens") {
|
|
233
|
-
guardState.consecutiveTruncations += 1;
|
|
234
|
-
const modelMaxOutput = params.modelMaxOutputTokens ?? 65_536;
|
|
235
|
-
const escalation = resolveOutputTokenEscalation(guardState, guardConfig, modelMaxOutput);
|
|
236
|
-
if (escalation.shouldEscalate) {
|
|
237
|
-
guardState.currentMaxOutputTokens = escalation.newMax;
|
|
238
|
-
yield { type: "recovery", turnId, action: "output_escalation", detail: `${escalation.newMax} tokens` };
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
else {
|
|
242
|
-
guardState.consecutiveTruncations = 0;
|
|
243
|
-
}
|
|
244
|
-
lastUsage = roundUsage;
|
|
245
|
-
finalText = textChunks.join("");
|
|
246
|
-
// Convert accumulated tool call deltas to OpenAiToolCall[]
|
|
247
|
-
let toolCalls = [...toolCallAccumulator.values()].map((tc) => ({
|
|
248
|
-
id: tc.id || `tc_${turnId}_${round}_${Math.random().toString(36).slice(2, 8)}`,
|
|
249
|
-
type: "function",
|
|
250
|
-
function: { name: tc.name, arguments: tc.arguments },
|
|
251
|
-
}));
|
|
252
|
-
// Filter tool_calls against active sidechain policy
|
|
253
|
-
if (activeSidechainToolPolicy && toolCalls.length > 0) {
|
|
254
|
-
const filterResult = filterToolCallsByAccessPolicy(toolCalls, activeSidechainToolPolicy);
|
|
255
|
-
if (filterResult.denied.length > 0) {
|
|
256
|
-
log.info(`sidechain policy denied: ${filterResult.denied.map((d) => `${d.toolCall.function.name} (${d.reason})`).join(", ")}`);
|
|
257
|
-
}
|
|
258
|
-
toolCalls = filterResult.allowed;
|
|
259
|
-
}
|
|
260
|
-
// ── No tool calls → loop complete ──────────────────────────
|
|
261
|
-
if (toolCalls.length === 0) {
|
|
262
|
-
toolLoopState = settleToolLoopState(toolLoopState, {
|
|
263
|
-
replayMessages: messages,
|
|
264
|
-
lastStopReason: "completed",
|
|
265
|
-
});
|
|
266
|
-
// Sidechain completed event
|
|
267
|
-
if (sidechainStarted) {
|
|
268
|
-
yield { type: "sidechain_completed", turnId, depth: sidechainDepth, toolCallCount: totalToolCallCount };
|
|
269
|
-
}
|
|
270
|
-
// Skill learning: evaluate turn for skill instruction
|
|
271
|
-
if (totalToolCallCount > 0) {
|
|
272
|
-
const skillResult = {
|
|
273
|
-
ok: true,
|
|
274
|
-
toolCallCount: totalToolCallCount,
|
|
275
|
-
distinctToolCount: distinctToolNames.size,
|
|
276
|
-
multiStep: totalToolCallCount >= 2,
|
|
277
|
-
hasSidechain: sidechainStarted,
|
|
278
|
-
feedback: null,
|
|
279
|
-
existingSkillName: null,
|
|
280
|
-
};
|
|
281
|
-
const skillInstruction = buildSkillInstruction(skillResult, {
|
|
282
|
-
tools: [...distinctToolNames],
|
|
283
|
-
});
|
|
284
|
-
if (skillInstruction) {
|
|
285
|
-
yield { type: "skill_instruction", turnId, instruction: skillInstruction };
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
yield { type: "end", turnId, content: finalText, usage: totalUsage, model };
|
|
289
|
-
return;
|
|
290
|
-
}
|
|
291
|
-
// ── Has tool calls → execute them ──────────────────────────
|
|
292
|
-
// Emit tool_call events for the host
|
|
293
|
-
for (const tc of toolCalls) {
|
|
294
|
-
yield {
|
|
295
|
-
type: "tool_call",
|
|
296
|
-
turnId,
|
|
297
|
-
callId: tc.id,
|
|
298
|
-
name: tc.function.name,
|
|
299
|
-
arguments: tc.function.arguments,
|
|
300
|
-
};
|
|
301
|
-
}
|
|
302
|
-
// Build assistant message with tool calls
|
|
303
|
-
messages.push(buildAssistantToolCallMessage(toolCalls));
|
|
304
|
-
// Setup sidechain on first tool calls (in-memory depth tracking)
|
|
305
|
-
if (!sidechainStarted) {
|
|
306
|
-
if (sidechainDepth > MAX_SIDECHAIN_DEPTH) {
|
|
307
|
-
yield {
|
|
308
|
-
type: "error",
|
|
309
|
-
turnId,
|
|
310
|
-
error: `sidechain depth ${sidechainDepth} exceeds max ${MAX_SIDECHAIN_DEPTH}`,
|
|
311
|
-
code: "SIDECHAIN_DEPTH_LIMIT",
|
|
312
|
-
};
|
|
313
|
-
return;
|
|
314
|
-
}
|
|
315
|
-
sidechainStarted = true;
|
|
316
|
-
const sidechainRole = sidechainDepth >= MAX_SIDECHAIN_DEPTH ? "leaf" : "orchestrator";
|
|
317
|
-
yield { type: "sidechain_started", turnId, depth: sidechainDepth, role: sidechainRole };
|
|
318
|
-
activeSidechainToolPolicy = resolveSidechainToolAccessByType({
|
|
319
|
-
type: "planner",
|
|
320
|
-
depth: sidechainDepth,
|
|
321
|
-
maxDepth: MAX_SIDECHAIN_DEPTH,
|
|
322
|
-
role: sidechainDepth >= MAX_SIDECHAIN_DEPTH ? "leaf" : "orchestrator",
|
|
323
|
-
toolNames: tools.map((t) => t.function.name),
|
|
324
|
-
});
|
|
325
|
-
}
|
|
326
|
-
// Advance tool loop state
|
|
327
|
-
toolLoopState = advanceToolLoopState(toolLoopState, {
|
|
328
|
-
replayMessages: messages,
|
|
329
|
-
pendingToolCallIds: toolCalls.map((tc) => tc.id),
|
|
330
|
-
completedToolCallIds: toolLoopState.completedToolCallIds,
|
|
331
|
-
lastStopReason: "tool_calls",
|
|
332
|
-
});
|
|
333
|
-
// Resolve parallel/serial scheduling
|
|
334
|
-
const scheduling = resolveParallelToolCallScheduling({
|
|
335
|
-
requestedPreference: undefined,
|
|
336
|
-
providerSupportsParallel: true,
|
|
337
|
-
toolCalls,
|
|
338
|
-
toolCapabilities: effectiveTools.map((t) => ({
|
|
339
|
-
name: t.function.name,
|
|
340
|
-
requiresApproval: t.meta?.requiresApproval ?? false,
|
|
341
|
-
approvalMode: (t.meta?.requiresApproval ? "user-confirm" : "pre-authorized"),
|
|
342
|
-
parallelSafe: t.meta?.parallelSafe ?? true,
|
|
343
|
-
serialOnly: t.meta?.serialOnly ?? false,
|
|
344
|
-
})),
|
|
345
|
-
});
|
|
346
|
-
// ── Execute tool calls in batches ──────────────────────────
|
|
347
|
-
const executedToolCallIds = [];
|
|
348
|
-
try {
|
|
349
|
-
for (const batch of scheduling.batches) {
|
|
350
|
-
if (batch.mode === "parallel") {
|
|
351
|
-
const toolCallOpts = { hooks, sessionId };
|
|
352
|
-
const batchResults = await Promise.all(batch.calls.map((tc) => executeToolCall(tc, turnId, toolInvoker, signal, log, toolCallOpts)));
|
|
353
|
-
for (const r of batchResults) {
|
|
354
|
-
if (r.blocked) {
|
|
355
|
-
yield { type: "tool_blocked", turnId, callId: r.callId, name: r.toolName, reason: r.blockReason ?? "blocked" };
|
|
356
|
-
}
|
|
357
|
-
messages.push(r.message);
|
|
358
|
-
executedToolCallIds.push(r.callId);
|
|
359
|
-
distinctToolNames.add(r.toolName);
|
|
360
|
-
totalToolCallCount++;
|
|
361
|
-
yield {
|
|
362
|
-
type: "tool_result",
|
|
363
|
-
turnId,
|
|
364
|
-
callId: r.callId,
|
|
365
|
-
name: r.toolName,
|
|
366
|
-
ok: r.ok,
|
|
367
|
-
error: r.error,
|
|
368
|
-
};
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
else {
|
|
372
|
-
for (const tc of batch.calls) {
|
|
373
|
-
if (signal?.aborted) {
|
|
374
|
-
yield { type: "error", turnId, error: "Turn aborted", code: "ABORTED" };
|
|
375
|
-
return;
|
|
376
|
-
}
|
|
377
|
-
const r = await executeToolCall(tc, turnId, toolInvoker, signal, log, { hooks, sessionId });
|
|
378
|
-
if (r.blocked) {
|
|
379
|
-
yield { type: "tool_blocked", turnId, callId: r.callId, name: r.toolName, reason: r.blockReason ?? "blocked" };
|
|
380
|
-
}
|
|
381
|
-
messages.push(r.message);
|
|
382
|
-
executedToolCallIds.push(r.callId);
|
|
383
|
-
distinctToolNames.add(r.toolName);
|
|
384
|
-
totalToolCallCount++;
|
|
385
|
-
yield {
|
|
386
|
-
type: "tool_result",
|
|
387
|
-
turnId,
|
|
388
|
-
callId: r.callId,
|
|
389
|
-
name: r.toolName,
|
|
390
|
-
ok: r.ok,
|
|
391
|
-
error: r.error,
|
|
392
|
-
};
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
catch (err) {
|
|
398
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
399
|
-
yield { type: "error", turnId, error: message, code: "TOOL_EXECUTION_ERROR" };
|
|
400
|
-
return;
|
|
401
|
-
}
|
|
402
|
-
// Settle tool loop state
|
|
403
|
-
toolLoopState = settleToolLoopState(toolLoopState, {
|
|
404
|
-
replayMessages: messages,
|
|
405
|
-
completedToolCallIds: [
|
|
406
|
-
...toolLoopState.completedToolCallIds,
|
|
407
|
-
...executedToolCallIds,
|
|
408
|
-
],
|
|
409
|
-
lastStopReason: scheduling.mode,
|
|
410
|
-
});
|
|
411
|
-
// ── Consecutive failure tracking ───────────────────────────
|
|
412
|
-
const roundToolResults = messages.slice(-toolCalls.length);
|
|
413
|
-
const allFailed = roundToolResults.length > 0 &&
|
|
414
|
-
roundToolResults.every((msg) => {
|
|
415
|
-
const content = msg?.content;
|
|
416
|
-
if (typeof content !== "string")
|
|
417
|
-
return false;
|
|
418
|
-
try {
|
|
419
|
-
return JSON.parse(content)?.ok === false;
|
|
420
|
-
}
|
|
421
|
-
catch {
|
|
422
|
-
return false;
|
|
423
|
-
}
|
|
424
|
-
});
|
|
425
|
-
if (allFailed) {
|
|
426
|
-
consecutiveFailedRounds += 1;
|
|
427
|
-
if (consecutiveFailedRounds >= MAX_CONSECUTIVE_FAILURES && finalText) {
|
|
428
|
-
log.info(`early exit: ${consecutiveFailedRounds} consecutive failed rounds, returning partial response`);
|
|
429
|
-
yield { type: "end", turnId, content: finalText, usage: totalUsage };
|
|
430
|
-
return;
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
else {
|
|
434
|
-
consecutiveFailedRounds = 0;
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
// ── Budget exhaustion ──────────────────────────────────────────
|
|
438
|
-
if (finalText) {
|
|
439
|
-
log.info(`tool loop budget exhausted (${toolBudget} rounds), returning partial response`);
|
|
440
|
-
// Sidechain completed event (budget exhaustion)
|
|
441
|
-
if (sidechainStarted) {
|
|
442
|
-
yield { type: "sidechain_completed", turnId, depth: sidechainDepth, toolCallCount: totalToolCallCount };
|
|
443
|
-
}
|
|
444
|
-
// Skill learning on partial completion
|
|
445
|
-
if (totalToolCallCount > 0) {
|
|
446
|
-
const skillResult = {
|
|
447
|
-
ok: true,
|
|
448
|
-
toolCallCount: totalToolCallCount,
|
|
449
|
-
distinctToolCount: distinctToolNames.size,
|
|
450
|
-
multiStep: totalToolCallCount >= 2,
|
|
451
|
-
hasSidechain: sidechainStarted,
|
|
452
|
-
feedback: null,
|
|
453
|
-
existingSkillName: null,
|
|
454
|
-
};
|
|
455
|
-
const skillInstruction = buildSkillInstruction(skillResult, {
|
|
456
|
-
tools: [...distinctToolNames],
|
|
457
|
-
});
|
|
458
|
-
if (skillInstruction) {
|
|
459
|
-
yield { type: "skill_instruction", turnId, instruction: skillInstruction };
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
|
-
yield { type: "end", turnId, content: finalText, usage: totalUsage, model };
|
|
463
|
-
return;
|
|
464
|
-
}
|
|
465
|
-
yield {
|
|
466
|
-
type: "error",
|
|
467
|
-
turnId,
|
|
468
|
-
error: `Tool loop exceeded budget (${toolBudget} rounds)`,
|
|
469
|
-
code: "TOOL_LOOP_LIMIT",
|
|
470
|
-
};
|
|
471
|
-
}
|
|
472
|
-
async function executeToolCall(toolCall, turnId, toolInvoker, signal, log, options) {
|
|
473
|
-
const toolName = toolCall.function.name;
|
|
474
|
-
log.debug(`tool_call: ${toolName}`);
|
|
475
|
-
const hooks = options?.hooks;
|
|
476
|
-
// Hook: tool.before_invoke — can block or mutate input (CC PreToolUse parity)
|
|
477
|
-
let effectiveArguments = toolCall.function.arguments;
|
|
478
|
-
if (hooks) {
|
|
479
|
-
try {
|
|
480
|
-
const hookResult = await hooks.invoke("tool.before_invoke", {
|
|
481
|
-
sessionId: options?.sessionId ?? "",
|
|
482
|
-
turnId,
|
|
483
|
-
callId: toolCall.id,
|
|
484
|
-
toolName,
|
|
485
|
-
arguments: safeParseJsonArgs(toolCall.function.arguments),
|
|
486
|
-
});
|
|
487
|
-
if (hookResult.action === "abort") {
|
|
488
|
-
const reason = hookResult.reason ?? "blocked by policy";
|
|
489
|
-
log.info(`tool ${toolName} blocked: ${reason}`);
|
|
490
|
-
// Emit approval.requested for blocked tools requiring approval
|
|
491
|
-
hooks.invoke("approval.requested", {
|
|
492
|
-
sessionId: options?.sessionId ?? "",
|
|
493
|
-
turnId,
|
|
494
|
-
approvalId: toolCall.id,
|
|
495
|
-
callId: toolCall.id,
|
|
496
|
-
toolName,
|
|
497
|
-
}).catch(() => { });
|
|
498
|
-
const message = buildToolResultMessage(toolCall.id, {
|
|
499
|
-
ok: false,
|
|
500
|
-
error: reason,
|
|
501
|
-
});
|
|
502
|
-
return { callId: toolCall.id, toolName, ok: false, error: reason, blocked: true, blockReason: reason, message };
|
|
503
|
-
}
|
|
504
|
-
// CC PreToolUse input mutation: hook can return modified arguments via context
|
|
505
|
-
if (hookResult.action === "continue" && hookResult.context?.arguments) {
|
|
506
|
-
effectiveArguments = JSON.stringify(hookResult.context.arguments);
|
|
507
|
-
log.debug(`tool ${toolName}: input mutated by hook`);
|
|
508
|
-
}
|
|
509
|
-
}
|
|
510
|
-
catch {
|
|
511
|
-
// Hook failures are non-blocking — proceed with tool execution
|
|
512
|
-
}
|
|
513
|
-
}
|
|
514
|
-
const invokeResult = await toolInvoker.invoke(turnId, toolName, effectiveArguments, signal);
|
|
515
|
-
const ok = !invokeResult.error;
|
|
516
|
-
const message = buildToolResultMessage(toolCall.id, {
|
|
517
|
-
ok,
|
|
518
|
-
payload: invokeResult.result,
|
|
519
|
-
error: invokeResult.error,
|
|
520
|
-
});
|
|
521
|
-
// Hook: tool.after_invoke / tool.invoke_failed (fire-and-forget observability)
|
|
522
|
-
hooks?.invoke(ok ? "tool.after_invoke" : "tool.invoke_failed", {
|
|
523
|
-
sessionId: options?.sessionId ?? "",
|
|
524
|
-
turnId,
|
|
525
|
-
callId: toolCall.id,
|
|
526
|
-
toolName,
|
|
527
|
-
ok,
|
|
528
|
-
...(invokeResult.error ? { error: invokeResult.error } : {}),
|
|
529
|
-
}).catch(() => { });
|
|
530
|
-
return {
|
|
531
|
-
callId: toolCall.id,
|
|
532
|
-
toolName,
|
|
533
|
-
ok,
|
|
534
|
-
error: invokeResult.error,
|
|
535
|
-
message,
|
|
536
|
-
};
|
|
537
|
-
}
|
|
538
|
-
function safeParseJsonArgs(raw) {
|
|
539
|
-
try {
|
|
540
|
-
return JSON.parse(raw);
|
|
541
|
-
}
|
|
542
|
-
catch {
|
|
543
|
-
return undefined;
|
|
544
|
-
}
|
|
545
|
-
}
|
|
546
|
-
// ── Internal: single LLM round (no tools) ───────────────────────────────────
|
|
547
|
-
async function* executeSingleLLMRound(turnId, model, messages, apiKey, temperature, signal, transport, log) {
|
|
548
|
-
const textChunks = [];
|
|
549
|
-
let usage;
|
|
550
|
-
log.debug(`single LLM round, messages: ${messages.length}`);
|
|
551
|
-
for await (const chunk of transport.stream({ model, messages, temperature }, apiKey, signal)) {
|
|
552
|
-
switch (chunk.type) {
|
|
553
|
-
case "delta":
|
|
554
|
-
textChunks.push(chunk.text);
|
|
555
|
-
yield { type: "delta", turnId, text: chunk.text };
|
|
556
|
-
break;
|
|
557
|
-
case "usage":
|
|
558
|
-
usage = {
|
|
559
|
-
prompt: chunk.promptTokens,
|
|
560
|
-
completion: chunk.completionTokens,
|
|
561
|
-
reasoning: chunk.reasoningTokens,
|
|
562
|
-
};
|
|
563
|
-
break;
|
|
564
|
-
case "done":
|
|
565
|
-
break;
|
|
566
|
-
}
|
|
567
|
-
}
|
|
568
|
-
yield {
|
|
569
|
-
type: "end",
|
|
570
|
-
turnId,
|
|
571
|
-
content: textChunks.join(""),
|
|
572
|
-
usage: usage ?? { prompt: 0, completion: 0 },
|
|
573
|
-
model,
|
|
574
|
-
};
|
|
575
|
-
}
|
package/dist/agent/types.js
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Pure agent types — zero I/O framework dependencies.
|
|
3
|
-
*
|
|
4
|
-
* These types define the contract between:
|
|
5
|
-
* - CLI stdio layer ↔ Agent class
|
|
6
|
-
* - Agent class ↔ LLM Provider
|
|
7
|
-
* - Agent class ↔ Tool loop
|
|
8
|
-
*
|
|
9
|
-
* Ported from Hub semantic-turn-shared.ts / semantic-turn-tools.ts,
|
|
10
|
-
* with Hub infrastructure (WS/PG/Redis) replaced by DI interfaces.
|
|
11
|
-
*
|
|
12
|
-
* No imports from express/pg/ioredis/ws.
|
|
13
|
-
*/
|
|
14
|
-
export {};
|
package/dist/cli/main.js
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* qlogicagent CLI — Agent subprocess entry point.
|
|
4
|
-
*
|
|
5
|
-
* Communicates with openclaw Gateway via JSON-RPC over stdio.
|
|
6
|
-
* All log/debug output goes to stderr (never pollutes the stdio protocol).
|
|
7
|
-
*
|
|
8
|
-
* Usage:
|
|
9
|
-
* qlogicagent [--verbose]
|
|
10
|
-
*/
|
|
11
|
-
import { StdioServer } from "./stdio-server.js";
|
|
12
|
-
import { parseCliArgs } from "../config/config.js";
|
|
13
|
-
const config = parseCliArgs(process.argv);
|
|
14
|
-
const server = new StdioServer({ verbose: config.verbose });
|
|
15
|
-
// Graceful shutdown
|
|
16
|
-
const shutdown = () => {
|
|
17
|
-
server.stop();
|
|
18
|
-
process.exit(0);
|
|
19
|
-
};
|
|
20
|
-
process.on("SIGTERM", shutdown);
|
|
21
|
-
process.on("SIGINT", shutdown);
|
|
22
|
-
// Start the JSON-RPC stdio server
|
|
23
|
-
server.start();
|