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
|
@@ -1,429 +0,0 @@
|
|
|
1
|
-
const DEFAULT_FORCED_STOP_REASONS = ["stop", "aborted", "timeout", "cancelled", "interrupted", "error"];
|
|
2
|
-
const FORCED_STOP_ASSISTANT_METADATA_KEYS = [
|
|
3
|
-
"tool_calls",
|
|
4
|
-
"toolCalls",
|
|
5
|
-
"function_call",
|
|
6
|
-
"functionCall",
|
|
7
|
-
"raw_tool_calls",
|
|
8
|
-
"rawToolCalls",
|
|
9
|
-
];
|
|
10
|
-
function normalizeUserMessageContent(content) {
|
|
11
|
-
if (content == null) {
|
|
12
|
-
return [];
|
|
13
|
-
}
|
|
14
|
-
if (typeof content === "string") {
|
|
15
|
-
return content.length > 0 ? [{ type: "text", text: content }] : [];
|
|
16
|
-
}
|
|
17
|
-
if (Array.isArray(content)) {
|
|
18
|
-
return content;
|
|
19
|
-
}
|
|
20
|
-
return [{ type: "text", text: String(content) }];
|
|
21
|
-
}
|
|
22
|
-
function mergeConsecutiveUserMessages(left, right) {
|
|
23
|
-
return {
|
|
24
|
-
...left,
|
|
25
|
-
content: [
|
|
26
|
-
...normalizeUserMessageContent(left.content),
|
|
27
|
-
...normalizeUserMessageContent(right.content),
|
|
28
|
-
],
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
|
-
function isValidOpenAiChatToolCall(call) {
|
|
32
|
-
if (!call || typeof call !== "object") {
|
|
33
|
-
return false;
|
|
34
|
-
}
|
|
35
|
-
if (call.function && typeof call.function === "object") {
|
|
36
|
-
const functionName = call.function.name;
|
|
37
|
-
if (typeof functionName === "string" && functionName.length > 0) {
|
|
38
|
-
return true;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
return typeof call.name === "string" && call.name.length > 0;
|
|
42
|
-
}
|
|
43
|
-
function normalizeForcedStopReasons(forcedStopReasons) {
|
|
44
|
-
return new Set((forcedStopReasons ?? DEFAULT_FORCED_STOP_REASONS).map((reason) => reason.trim().toLowerCase()));
|
|
45
|
-
}
|
|
46
|
-
function shouldStripForcedStopToolMetadata(stopReason, forcedStopReasons) {
|
|
47
|
-
if (!stopReason) {
|
|
48
|
-
return false;
|
|
49
|
-
}
|
|
50
|
-
return normalizeForcedStopReasons(forcedStopReasons).has(stopReason.trim().toLowerCase());
|
|
51
|
-
}
|
|
52
|
-
function splitOpenAiFunctionCallPairing(id) {
|
|
53
|
-
const separator = id.indexOf("|");
|
|
54
|
-
if (separator <= 0 || separator >= id.length - 1) {
|
|
55
|
-
return { callId: id };
|
|
56
|
-
}
|
|
57
|
-
return {
|
|
58
|
-
callId: id.slice(0, separator),
|
|
59
|
-
itemId: id.slice(separator + 1),
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
|
-
function parseOpenAiReasoningSignature(value) {
|
|
63
|
-
if (!value) {
|
|
64
|
-
return null;
|
|
65
|
-
}
|
|
66
|
-
let candidate = null;
|
|
67
|
-
if (typeof value === "string") {
|
|
68
|
-
const trimmed = value.trim();
|
|
69
|
-
if (!trimmed.startsWith("{") || !trimmed.endsWith("}")) {
|
|
70
|
-
return null;
|
|
71
|
-
}
|
|
72
|
-
try {
|
|
73
|
-
candidate = JSON.parse(trimmed);
|
|
74
|
-
}
|
|
75
|
-
catch {
|
|
76
|
-
return null;
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
else if (typeof value === "object") {
|
|
80
|
-
candidate = value;
|
|
81
|
-
}
|
|
82
|
-
if (!candidate) {
|
|
83
|
-
return null;
|
|
84
|
-
}
|
|
85
|
-
const id = typeof candidate.id === "string" ? candidate.id : "";
|
|
86
|
-
const type = typeof candidate.type === "string" ? candidate.type : "";
|
|
87
|
-
if (!id.startsWith("rs_")) {
|
|
88
|
-
return null;
|
|
89
|
-
}
|
|
90
|
-
if (type === "reasoning" || type.startsWith("reasoning.")) {
|
|
91
|
-
return { id, type };
|
|
92
|
-
}
|
|
93
|
-
return null;
|
|
94
|
-
}
|
|
95
|
-
function hasFollowingNonThinkingBlock(content, index) {
|
|
96
|
-
for (let nextIndex = index + 1; nextIndex < content.length; nextIndex += 1) {
|
|
97
|
-
const block = content[nextIndex];
|
|
98
|
-
if (!block || typeof block !== "object") {
|
|
99
|
-
return true;
|
|
100
|
-
}
|
|
101
|
-
if (block.type !== "thinking") {
|
|
102
|
-
return true;
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
return false;
|
|
106
|
-
}
|
|
107
|
-
export function sanitizeOpenAiChatMessages(messages) {
|
|
108
|
-
if (!Array.isArray(messages) || messages.length === 0) {
|
|
109
|
-
return [...messages];
|
|
110
|
-
}
|
|
111
|
-
const normalizedMessages = messages.map((message) => {
|
|
112
|
-
if (message.role === "assistant" && Array.isArray(message.tool_calls)) {
|
|
113
|
-
const validToolCalls = message.tool_calls.filter((call) => isValidOpenAiChatToolCall(call));
|
|
114
|
-
return {
|
|
115
|
-
...message,
|
|
116
|
-
...(validToolCalls.length > 0 ? { tool_calls: validToolCalls } : { tool_calls: undefined }),
|
|
117
|
-
};
|
|
118
|
-
}
|
|
119
|
-
return { ...message };
|
|
120
|
-
});
|
|
121
|
-
const validToolCallIds = new Set();
|
|
122
|
-
for (const message of normalizedMessages) {
|
|
123
|
-
if (message.role !== "assistant" || !Array.isArray(message.tool_calls)) {
|
|
124
|
-
continue;
|
|
125
|
-
}
|
|
126
|
-
for (const toolCall of message.tool_calls) {
|
|
127
|
-
if (typeof toolCall.id === "string" && toolCall.id) {
|
|
128
|
-
validToolCallIds.add(toolCall.id);
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
const withoutOrphanToolMessages = normalizedMessages.filter((message) => {
|
|
133
|
-
if (message.role !== "tool") {
|
|
134
|
-
return true;
|
|
135
|
-
}
|
|
136
|
-
return Boolean(message.tool_call_id && validToolCallIds.has(message.tool_call_id));
|
|
137
|
-
});
|
|
138
|
-
const survivingToolIds = new Set();
|
|
139
|
-
for (const message of withoutOrphanToolMessages) {
|
|
140
|
-
if (message.role === "tool" && typeof message.tool_call_id === "string" && message.tool_call_id) {
|
|
141
|
-
survivingToolIds.add(message.tool_call_id);
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
const cleanedMessages = [];
|
|
145
|
-
for (const message of withoutOrphanToolMessages) {
|
|
146
|
-
if (message.role === "assistant" && Array.isArray(message.tool_calls) && message.tool_calls.length > 0) {
|
|
147
|
-
const matchedToolCalls = message.tool_calls.filter((toolCall) => typeof toolCall.id === "string" && survivingToolIds.has(toolCall.id));
|
|
148
|
-
if (matchedToolCalls.length === 0) {
|
|
149
|
-
const { tool_calls: _droppedToolCalls, ...rest } = message;
|
|
150
|
-
if (rest.content != null && rest.content !== "") {
|
|
151
|
-
cleanedMessages.push(rest);
|
|
152
|
-
}
|
|
153
|
-
continue;
|
|
154
|
-
}
|
|
155
|
-
if (matchedToolCalls.length < message.tool_calls.length) {
|
|
156
|
-
cleanedMessages.push({ ...message, tool_calls: matchedToolCalls });
|
|
157
|
-
continue;
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
cleanedMessages.push(message);
|
|
161
|
-
}
|
|
162
|
-
const mergedMessages = [];
|
|
163
|
-
for (const message of cleanedMessages) {
|
|
164
|
-
const previous = mergedMessages.length > 0 ? mergedMessages[mergedMessages.length - 1] : undefined;
|
|
165
|
-
if (message.role === "user" && previous?.role === "user") {
|
|
166
|
-
mergedMessages[mergedMessages.length - 1] = mergeConsecutiveUserMessages(previous, message);
|
|
167
|
-
continue;
|
|
168
|
-
}
|
|
169
|
-
mergedMessages.push(message);
|
|
170
|
-
}
|
|
171
|
-
return mergedMessages;
|
|
172
|
-
}
|
|
173
|
-
export function ensureAssistantReasoningContent(messages, defaultValue = "") {
|
|
174
|
-
return messages.map((message) => {
|
|
175
|
-
if (message.role === "assistant" && !Object.hasOwn(message, "reasoning_content")) {
|
|
176
|
-
return { ...message, reasoning_content: defaultValue };
|
|
177
|
-
}
|
|
178
|
-
return { ...message };
|
|
179
|
-
});
|
|
180
|
-
}
|
|
181
|
-
export function stripForcedStopAssistantToolMetadata(messages, options) {
|
|
182
|
-
if (!shouldStripForcedStopToolMetadata(options?.stopReason, options?.forcedStopReasons)) {
|
|
183
|
-
return [...messages];
|
|
184
|
-
}
|
|
185
|
-
return messages.map((message) => {
|
|
186
|
-
if (message.role !== "assistant") {
|
|
187
|
-
return { ...message };
|
|
188
|
-
}
|
|
189
|
-
const nextMessage = { ...message };
|
|
190
|
-
for (const key of FORCED_STOP_ASSISTANT_METADATA_KEYS) {
|
|
191
|
-
delete nextMessage[key];
|
|
192
|
-
}
|
|
193
|
-
return nextMessage;
|
|
194
|
-
});
|
|
195
|
-
}
|
|
196
|
-
export function injectDanglingToolCallPlaceholders(messages, options) {
|
|
197
|
-
const placeholderContent = options?.placeholderToolResult ?? JSON.stringify({ ok: false, error: "Tool loop interrupted before the tool result was replayed." });
|
|
198
|
-
const matchedToolResultIds = new Set();
|
|
199
|
-
for (const message of messages) {
|
|
200
|
-
if (message.role === "tool" && typeof message.tool_call_id === "string" && message.tool_call_id) {
|
|
201
|
-
matchedToolResultIds.add(message.tool_call_id);
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
const repairedMessages = [];
|
|
205
|
-
for (const message of messages) {
|
|
206
|
-
repairedMessages.push({ ...message });
|
|
207
|
-
if (message.role !== "assistant" || !Array.isArray(message.tool_calls) || message.tool_calls.length === 0) {
|
|
208
|
-
continue;
|
|
209
|
-
}
|
|
210
|
-
for (const toolCall of message.tool_calls) {
|
|
211
|
-
if (typeof toolCall.id !== "string" || !toolCall.id || matchedToolResultIds.has(toolCall.id)) {
|
|
212
|
-
continue;
|
|
213
|
-
}
|
|
214
|
-
matchedToolResultIds.add(toolCall.id);
|
|
215
|
-
repairedMessages.push({
|
|
216
|
-
role: "tool",
|
|
217
|
-
tool_call_id: toolCall.id,
|
|
218
|
-
content: placeholderContent,
|
|
219
|
-
});
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
return repairedMessages;
|
|
223
|
-
}
|
|
224
|
-
export function repairOpenAiChatConversation(messages, options) {
|
|
225
|
-
const sanitizedMessages = sanitizeOpenAiChatMessages(messages);
|
|
226
|
-
const strippedMessages = stripForcedStopAssistantToolMetadata(sanitizedMessages, options);
|
|
227
|
-
return injectDanglingToolCallPlaceholders(strippedMessages, options);
|
|
228
|
-
}
|
|
229
|
-
export function downgradeOpenAiFunctionCallReasoningPairs(messages) {
|
|
230
|
-
let changed = false;
|
|
231
|
-
const rewrittenMessages = [];
|
|
232
|
-
let pendingRewrittenIds = null;
|
|
233
|
-
for (const message of messages) {
|
|
234
|
-
if (!message || typeof message !== "object") {
|
|
235
|
-
pendingRewrittenIds = null;
|
|
236
|
-
rewrittenMessages.push(message);
|
|
237
|
-
continue;
|
|
238
|
-
}
|
|
239
|
-
if (message.role === "assistant") {
|
|
240
|
-
if (!Array.isArray(message.content)) {
|
|
241
|
-
pendingRewrittenIds = null;
|
|
242
|
-
rewrittenMessages.push(message);
|
|
243
|
-
continue;
|
|
244
|
-
}
|
|
245
|
-
const localRewrittenIds = new Map();
|
|
246
|
-
let seenReplayableReasoning = false;
|
|
247
|
-
let assistantChanged = false;
|
|
248
|
-
const nextContent = message.content.map((block) => {
|
|
249
|
-
if (!block || typeof block !== "object") {
|
|
250
|
-
return block;
|
|
251
|
-
}
|
|
252
|
-
const maybeThinking = block;
|
|
253
|
-
if (maybeThinking.type === "thinking" && parseOpenAiReasoningSignature(maybeThinking.thinkingSignature)) {
|
|
254
|
-
seenReplayableReasoning = true;
|
|
255
|
-
return block;
|
|
256
|
-
}
|
|
257
|
-
const maybeToolCall = block;
|
|
258
|
-
if (!["toolCall", "toolUse", "functionCall"].includes(String(maybeToolCall.type)) || typeof maybeToolCall.id !== "string") {
|
|
259
|
-
return block;
|
|
260
|
-
}
|
|
261
|
-
const pairing = splitOpenAiFunctionCallPairing(maybeToolCall.id);
|
|
262
|
-
if (seenReplayableReasoning || !pairing.itemId || !pairing.itemId.startsWith("fc_")) {
|
|
263
|
-
return block;
|
|
264
|
-
}
|
|
265
|
-
assistantChanged = true;
|
|
266
|
-
localRewrittenIds.set(maybeToolCall.id, pairing.callId);
|
|
267
|
-
return {
|
|
268
|
-
...block,
|
|
269
|
-
id: pairing.callId,
|
|
270
|
-
};
|
|
271
|
-
});
|
|
272
|
-
pendingRewrittenIds = localRewrittenIds.size > 0 ? localRewrittenIds : null;
|
|
273
|
-
if (!assistantChanged) {
|
|
274
|
-
rewrittenMessages.push(message);
|
|
275
|
-
continue;
|
|
276
|
-
}
|
|
277
|
-
changed = true;
|
|
278
|
-
rewrittenMessages.push({ ...message, content: nextContent });
|
|
279
|
-
continue;
|
|
280
|
-
}
|
|
281
|
-
if (message.role === "toolResult" && pendingRewrittenIds && pendingRewrittenIds.size > 0) {
|
|
282
|
-
const updates = {};
|
|
283
|
-
let toolResultChanged = false;
|
|
284
|
-
if (typeof message.toolCallId === "string") {
|
|
285
|
-
const nextToolCallId = pendingRewrittenIds.get(message.toolCallId);
|
|
286
|
-
if (nextToolCallId && nextToolCallId !== message.toolCallId) {
|
|
287
|
-
updates.toolCallId = nextToolCallId;
|
|
288
|
-
toolResultChanged = true;
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
if (typeof message.toolUseId === "string") {
|
|
292
|
-
const nextToolUseId = pendingRewrittenIds.get(message.toolUseId);
|
|
293
|
-
if (nextToolUseId && nextToolUseId !== message.toolUseId) {
|
|
294
|
-
updates.toolUseId = nextToolUseId;
|
|
295
|
-
toolResultChanged = true;
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
if (!toolResultChanged) {
|
|
299
|
-
rewrittenMessages.push(message);
|
|
300
|
-
continue;
|
|
301
|
-
}
|
|
302
|
-
changed = true;
|
|
303
|
-
rewrittenMessages.push({ ...message, ...updates });
|
|
304
|
-
continue;
|
|
305
|
-
}
|
|
306
|
-
pendingRewrittenIds = null;
|
|
307
|
-
rewrittenMessages.push(message);
|
|
308
|
-
}
|
|
309
|
-
return changed ? rewrittenMessages : [...messages];
|
|
310
|
-
}
|
|
311
|
-
export function downgradeOpenAiReasoningBlocks(messages) {
|
|
312
|
-
const out = [];
|
|
313
|
-
for (const message of messages) {
|
|
314
|
-
if (!message || typeof message !== "object") {
|
|
315
|
-
out.push(message);
|
|
316
|
-
continue;
|
|
317
|
-
}
|
|
318
|
-
if (message.role !== "assistant") {
|
|
319
|
-
out.push(message);
|
|
320
|
-
continue;
|
|
321
|
-
}
|
|
322
|
-
if (!Array.isArray(message.content)) {
|
|
323
|
-
out.push(message);
|
|
324
|
-
continue;
|
|
325
|
-
}
|
|
326
|
-
let changed = false;
|
|
327
|
-
const nextContent = [];
|
|
328
|
-
for (let index = 0; index < message.content.length; index += 1) {
|
|
329
|
-
const block = message.content[index];
|
|
330
|
-
if (!block || typeof block !== "object") {
|
|
331
|
-
nextContent.push(block);
|
|
332
|
-
continue;
|
|
333
|
-
}
|
|
334
|
-
const record = block;
|
|
335
|
-
if (record.type !== "thinking") {
|
|
336
|
-
nextContent.push(block);
|
|
337
|
-
continue;
|
|
338
|
-
}
|
|
339
|
-
const signature = parseOpenAiReasoningSignature(record.thinkingSignature);
|
|
340
|
-
if (!signature) {
|
|
341
|
-
nextContent.push(block);
|
|
342
|
-
continue;
|
|
343
|
-
}
|
|
344
|
-
if (hasFollowingNonThinkingBlock(message.content, index)) {
|
|
345
|
-
nextContent.push(block);
|
|
346
|
-
continue;
|
|
347
|
-
}
|
|
348
|
-
changed = true;
|
|
349
|
-
}
|
|
350
|
-
if (!changed) {
|
|
351
|
-
out.push(message);
|
|
352
|
-
continue;
|
|
353
|
-
}
|
|
354
|
-
if (nextContent.length === 0) {
|
|
355
|
-
continue;
|
|
356
|
-
}
|
|
357
|
-
out.push({ ...message, content: nextContent });
|
|
358
|
-
}
|
|
359
|
-
return out;
|
|
360
|
-
}
|
|
361
|
-
export function repairOpenAiResponsesItems(items, options) {
|
|
362
|
-
const placeholderOutput = options?.placeholderFunctionCallOutput ?? JSON.stringify({ ok: false, error: "Tool loop interrupted before function_call_output was provided." });
|
|
363
|
-
const stripForced = shouldStripForcedStopToolMetadata(options?.stopReason, options?.forcedStopReasons);
|
|
364
|
-
const rewrittenItems = [];
|
|
365
|
-
for (let index = 0; index < items.length; index += 1) {
|
|
366
|
-
const item = items[index];
|
|
367
|
-
if (!item || typeof item !== "object") {
|
|
368
|
-
rewrittenItems.push(item);
|
|
369
|
-
continue;
|
|
370
|
-
}
|
|
371
|
-
if (item.type === "reasoning" && typeof item.id === "string" && item.id.startsWith("rs_")) {
|
|
372
|
-
const hasFollowingNonReasoning = items.slice(index + 1).some((next) => next && typeof next === "object" && next.type !== "reasoning");
|
|
373
|
-
if (!hasFollowingNonReasoning) {
|
|
374
|
-
continue;
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
if (item.type !== "function_call") {
|
|
378
|
-
rewrittenItems.push({ ...item });
|
|
379
|
-
continue;
|
|
380
|
-
}
|
|
381
|
-
const nextItem = { ...item };
|
|
382
|
-
if (typeof nextItem.id === "string") {
|
|
383
|
-
const pairing = splitOpenAiFunctionCallPairing(nextItem.id);
|
|
384
|
-
if (pairing.itemId?.startsWith("fc_")) {
|
|
385
|
-
nextItem.id = pairing.callId;
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
rewrittenItems.push(nextItem);
|
|
389
|
-
}
|
|
390
|
-
const validFunctionCallIds = new Set();
|
|
391
|
-
const functionCallByCallId = new Map();
|
|
392
|
-
for (const item of rewrittenItems) {
|
|
393
|
-
if (item.type !== "function_call") {
|
|
394
|
-
continue;
|
|
395
|
-
}
|
|
396
|
-
const callId = typeof item.call_id === "string" && item.call_id ? item.call_id : typeof item.id === "string" ? item.id : undefined;
|
|
397
|
-
if (!callId) {
|
|
398
|
-
continue;
|
|
399
|
-
}
|
|
400
|
-
validFunctionCallIds.add(callId);
|
|
401
|
-
functionCallByCallId.set(callId, item);
|
|
402
|
-
}
|
|
403
|
-
const pairedOutputs = new Set();
|
|
404
|
-
const repairedItems = [];
|
|
405
|
-
for (const item of rewrittenItems) {
|
|
406
|
-
if (stripForced && item.type === "function_call") {
|
|
407
|
-
continue;
|
|
408
|
-
}
|
|
409
|
-
if (item.type === "function_call_output") {
|
|
410
|
-
const callId = typeof item.call_id === "string" ? item.call_id : "";
|
|
411
|
-
if (!callId || !validFunctionCallIds.has(callId)) {
|
|
412
|
-
continue;
|
|
413
|
-
}
|
|
414
|
-
pairedOutputs.add(callId);
|
|
415
|
-
}
|
|
416
|
-
repairedItems.push(item);
|
|
417
|
-
}
|
|
418
|
-
for (const [callId] of functionCallByCallId) {
|
|
419
|
-
if (pairedOutputs.has(callId) || stripForced) {
|
|
420
|
-
continue;
|
|
421
|
-
}
|
|
422
|
-
repairedItems.push({
|
|
423
|
-
type: "function_call_output",
|
|
424
|
-
call_id: callId,
|
|
425
|
-
output: placeholderOutput,
|
|
426
|
-
});
|
|
427
|
-
}
|
|
428
|
-
return repairedItems;
|
|
429
|
-
}
|
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Curator scheduler — pure strategy for autonomous skill library
|
|
3
|
-
* maintenance. Decides WHEN to run the curator review cycle and
|
|
4
|
-
* computes automatic lifecycle transitions (active → stale → archived).
|
|
5
|
-
*
|
|
6
|
-
* No I/O — all functions are pure; the caller supplies timestamps and
|
|
7
|
-
* skill records, and receives transition instructions back.
|
|
8
|
-
*/
|
|
9
|
-
export const DEFAULT_CURATOR_SCHEDULE_POLICY = {
|
|
10
|
-
minIdleSeconds: 7200,
|
|
11
|
-
intervalSeconds: 604_800,
|
|
12
|
-
staleAfterDays: 30,
|
|
13
|
-
archiveAfterDays: 90,
|
|
14
|
-
maxReviewBatchSize: 50,
|
|
15
|
-
};
|
|
16
|
-
export const INITIAL_CURATOR_STATE = {
|
|
17
|
-
lastRunAt: null,
|
|
18
|
-
lastRunDurationSeconds: 0,
|
|
19
|
-
lastRunSummary: "",
|
|
20
|
-
paused: false,
|
|
21
|
-
runCount: 0,
|
|
22
|
-
};
|
|
23
|
-
/**
|
|
24
|
-
* Determine whether the curator should run now.
|
|
25
|
-
*
|
|
26
|
-
* All gates must pass:
|
|
27
|
-
* 1. Feature enabled
|
|
28
|
-
* 2. Not paused
|
|
29
|
-
* 3. Session idle ≥ minIdleSeconds
|
|
30
|
-
* 4. Time since last run ≥ intervalSeconds
|
|
31
|
-
*/
|
|
32
|
-
export function shouldRunCurator(input) {
|
|
33
|
-
const policy = { ...DEFAULT_CURATOR_SCHEDULE_POLICY, ...input.policy };
|
|
34
|
-
if (!input.enabled) {
|
|
35
|
-
return { shouldRun: false, reason: "curator disabled" };
|
|
36
|
-
}
|
|
37
|
-
if (input.state.paused) {
|
|
38
|
-
return { shouldRun: false, reason: "curator paused by user" };
|
|
39
|
-
}
|
|
40
|
-
if (input.idleSeconds < policy.minIdleSeconds) {
|
|
41
|
-
return {
|
|
42
|
-
shouldRun: false,
|
|
43
|
-
reason: `session not idle long enough (${input.idleSeconds}s < ${policy.minIdleSeconds}s)`,
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
if (input.state.lastRunAt) {
|
|
47
|
-
const elapsed = (new Date(input.now).getTime() - new Date(input.state.lastRunAt).getTime()) / 1000;
|
|
48
|
-
if (elapsed < policy.intervalSeconds) {
|
|
49
|
-
return {
|
|
50
|
-
shouldRun: false,
|
|
51
|
-
reason: `last run too recent (${Math.round(elapsed)}s < ${policy.intervalSeconds}s)`,
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
return { shouldRun: true, reason: "all gates passed" };
|
|
56
|
-
}
|
|
57
|
-
/**
|
|
58
|
-
* Compute automatic lifecycle transitions for agent-created skills.
|
|
59
|
-
*
|
|
60
|
-
* Pure function — no I/O, no mutations. The caller applies the
|
|
61
|
-
* returned transitions to its skill storage.
|
|
62
|
-
*
|
|
63
|
-
* Rules:
|
|
64
|
-
* - Pinned skills are never transitioned.
|
|
65
|
-
* - Non-agent-created skills are skipped.
|
|
66
|
-
* - active → stale: no activity for staleAfterDays.
|
|
67
|
-
* - stale → archived: no activity for archiveAfterDays.
|
|
68
|
-
* - stale/archived → active: if useCount increased (reactivation).
|
|
69
|
-
*/
|
|
70
|
-
export function computeLifecycleTransitions(skills, now, policy) {
|
|
71
|
-
const resolved = { ...DEFAULT_CURATOR_SCHEDULE_POLICY, ...policy };
|
|
72
|
-
const nowMs = new Date(now).getTime();
|
|
73
|
-
const transitions = [];
|
|
74
|
-
let checked = 0;
|
|
75
|
-
for (const skill of skills) {
|
|
76
|
-
if (!skill.agentCreated)
|
|
77
|
-
continue;
|
|
78
|
-
checked++;
|
|
79
|
-
if (skill.pinned)
|
|
80
|
-
continue;
|
|
81
|
-
const lastMs = skill.lastActivityAt
|
|
82
|
-
? new Date(skill.lastActivityAt).getTime()
|
|
83
|
-
: new Date(skill.createdAt).getTime();
|
|
84
|
-
const inactiveDays = (nowMs - lastMs) / (1000 * 60 * 60 * 24);
|
|
85
|
-
if (skill.state === "active" && inactiveDays >= resolved.staleAfterDays) {
|
|
86
|
-
transitions.push({
|
|
87
|
-
skillName: skill.name,
|
|
88
|
-
from: "active",
|
|
89
|
-
to: "stale",
|
|
90
|
-
reason: `inactive for ${Math.floor(inactiveDays)} days (threshold: ${resolved.staleAfterDays})`,
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
|
-
else if (skill.state === "stale" && inactiveDays >= resolved.archiveAfterDays) {
|
|
94
|
-
transitions.push({
|
|
95
|
-
skillName: skill.name,
|
|
96
|
-
from: "stale",
|
|
97
|
-
to: "archived",
|
|
98
|
-
reason: `inactive for ${Math.floor(inactiveDays)} days (threshold: ${resolved.archiveAfterDays})`,
|
|
99
|
-
});
|
|
100
|
-
}
|
|
101
|
-
// Reactivation: stale/archived skill with recent activity
|
|
102
|
-
if ((skill.state === "stale" || skill.state === "archived")
|
|
103
|
-
&& skill.useCount > 0
|
|
104
|
-
&& inactiveDays < resolved.staleAfterDays) {
|
|
105
|
-
transitions.push({
|
|
106
|
-
skillName: skill.name,
|
|
107
|
-
from: skill.state,
|
|
108
|
-
to: "active",
|
|
109
|
-
reason: "recent activity detected — reactivated",
|
|
110
|
-
});
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
return {
|
|
114
|
-
checked,
|
|
115
|
-
transitions,
|
|
116
|
-
markedStale: transitions.filter((t) => t.to === "stale").length,
|
|
117
|
-
archived: transitions.filter((t) => t.to === "archived").length,
|
|
118
|
-
reactivated: transitions.filter((t) => t.to === "active").length,
|
|
119
|
-
};
|
|
120
|
-
}
|
|
121
|
-
// ── Review candidate selection ─────────────────────────────────────
|
|
122
|
-
/**
|
|
123
|
-
* Select skills that should be reviewed by the LLM curator.
|
|
124
|
-
*
|
|
125
|
-
* Only non-pinned, agent-created skills in active or stale state
|
|
126
|
-
* are eligible for review. Returns at most maxReviewBatchSize names.
|
|
127
|
-
*/
|
|
128
|
-
export function selectReviewCandidates(skills, policy) {
|
|
129
|
-
const resolved = { ...DEFAULT_CURATOR_SCHEDULE_POLICY, ...policy };
|
|
130
|
-
return skills
|
|
131
|
-
.filter((s) => s.agentCreated && !s.pinned && (s.state === "active" || s.state === "stale"))
|
|
132
|
-
.sort((a, b) => a.useCount - b.useCount) // least-used first → most likely consolidation candidates
|
|
133
|
-
.slice(0, resolved.maxReviewBatchSize)
|
|
134
|
-
.map((s) => s.name);
|
|
135
|
-
}
|