camelagi 0.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/LICENSE +21 -0
- package/README.md +224 -0
- package/camelagi.mjs +2 -0
- package/config.example.yaml +107 -0
- package/dist/agent/agent-openai.js +206 -0
- package/dist/agent/agent-openai.js.map +1 -0
- package/dist/agent/agent-sdk.js +209 -0
- package/dist/agent/agent-sdk.js.map +1 -0
- package/dist/agent/tool-adapter.js +31 -0
- package/dist/agent/tool-adapter.js.map +1 -0
- package/dist/agent/types.js +3 -0
- package/dist/agent/types.js.map +1 -0
- package/dist/agent.js +17 -0
- package/dist/agent.js.map +1 -0
- package/dist/approval-forward.js +42 -0
- package/dist/approval-forward.js.map +1 -0
- package/dist/approvals.js +151 -0
- package/dist/approvals.js.map +1 -0
- package/dist/boot.js +34 -0
- package/dist/boot.js.map +1 -0
- package/dist/bootstrap.js +451 -0
- package/dist/bootstrap.js.map +1 -0
- package/dist/camelagi-gateway.mjs +93611 -0
- package/dist/camelagi-gateway.mjs.map +7 -0
- package/dist/channels/adapter.js +10 -0
- package/dist/channels/adapter.js.map +1 -0
- package/dist/channels/discord.js +232 -0
- package/dist/channels/discord.js.map +1 -0
- package/dist/channels/handler.js +349 -0
- package/dist/channels/handler.js.map +1 -0
- package/dist/channels/index.js +19 -0
- package/dist/channels/index.js.map +1 -0
- package/dist/channels/registry.js +71 -0
- package/dist/channels/registry.js.map +1 -0
- package/dist/channels/telegram.js +83 -0
- package/dist/channels/telegram.js.map +1 -0
- package/dist/channels/types.js +3 -0
- package/dist/channels/types.js.map +1 -0
- package/dist/chunker.js +102 -0
- package/dist/chunker.js.map +1 -0
- package/dist/cli/cmd-agents.js +65 -0
- package/dist/cli/cmd-agents.js.map +1 -0
- package/dist/cli/cmd-bootstrap.js +10 -0
- package/dist/cli/cmd-bootstrap.js.map +1 -0
- package/dist/cli/cmd-chat.js +32 -0
- package/dist/cli/cmd-chat.js.map +1 -0
- package/dist/cli/cmd-config.js +88 -0
- package/dist/cli/cmd-config.js.map +1 -0
- package/dist/cli/cmd-cron.js +120 -0
- package/dist/cli/cmd-cron.js.map +1 -0
- package/dist/cli/cmd-daemon.js +37 -0
- package/dist/cli/cmd-daemon.js.map +1 -0
- package/dist/cli/cmd-doctor.js +18 -0
- package/dist/cli/cmd-doctor.js.map +1 -0
- package/dist/cli/cmd-logs.js +30 -0
- package/dist/cli/cmd-logs.js.map +1 -0
- package/dist/cli/cmd-pairing.js +41 -0
- package/dist/cli/cmd-pairing.js.map +1 -0
- package/dist/cli/cmd-reset.js +39 -0
- package/dist/cli/cmd-reset.js.map +1 -0
- package/dist/cli/cmd-serve.js +30 -0
- package/dist/cli/cmd-serve.js.map +1 -0
- package/dist/cli/cmd-sessions.js +56 -0
- package/dist/cli/cmd-sessions.js.map +1 -0
- package/dist/cli/cmd-setup.js +11 -0
- package/dist/cli/cmd-setup.js.map +1 -0
- package/dist/cli/cmd-soul.js +43 -0
- package/dist/cli/cmd-soul.js.map +1 -0
- package/dist/cli/parse.js +50 -0
- package/dist/cli/parse.js.map +1 -0
- package/dist/cli/registry.js +15 -0
- package/dist/cli/registry.js.map +1 -0
- package/dist/cli.js +103 -0
- package/dist/cli.js.map +1 -0
- package/dist/compact.js +92 -0
- package/dist/compact.js.map +1 -0
- package/dist/config.js +153 -0
- package/dist/config.js.map +1 -0
- package/dist/constants.js +21 -0
- package/dist/constants.js.map +1 -0
- package/dist/core/config.js +212 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/constants.js +21 -0
- package/dist/core/constants.js.map +1 -0
- package/dist/core/errors.js +5 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/log.js +41 -0
- package/dist/core/log.js.map +1 -0
- package/dist/core/models.js +123 -0
- package/dist/core/models.js.map +1 -0
- package/dist/core/types.js +3 -0
- package/dist/core/types.js.map +1 -0
- package/dist/core/update-check.js +51 -0
- package/dist/core/update-check.js.map +1 -0
- package/dist/cron.js +81 -0
- package/dist/cron.js.map +1 -0
- package/dist/daemon.js +109 -0
- package/dist/daemon.js.map +1 -0
- package/dist/doctor.js +194 -0
- package/dist/doctor.js.map +1 -0
- package/dist/errors.js +5 -0
- package/dist/errors.js.map +1 -0
- package/dist/extensions/approval-forward.js +42 -0
- package/dist/extensions/approval-forward.js.map +1 -0
- package/dist/extensions/approvals.js +144 -0
- package/dist/extensions/approvals.js.map +1 -0
- package/dist/extensions/cron.js +306 -0
- package/dist/extensions/cron.js.map +1 -0
- package/dist/extensions/hooks.js +72 -0
- package/dist/extensions/hooks.js.map +1 -0
- package/dist/extensions/skills.js +97 -0
- package/dist/extensions/skills.js.map +1 -0
- package/dist/gateway/csrf.js +44 -0
- package/dist/gateway/csrf.js.map +1 -0
- package/dist/gateway/logger.js +81 -0
- package/dist/gateway/logger.js.map +1 -0
- package/dist/gateway/rate-limit.js +33 -0
- package/dist/gateway/rate-limit.js.map +1 -0
- package/dist/gateway/routes.js +315 -0
- package/dist/gateway/routes.js.map +1 -0
- package/dist/gateway/state.js +54 -0
- package/dist/gateway/state.js.map +1 -0
- package/dist/gateway/ws-handler.js +200 -0
- package/dist/gateway/ws-handler.js.map +1 -0
- package/dist/gateway-entry.js +16 -0
- package/dist/gateway-entry.js.map +1 -0
- package/dist/hooks.js +72 -0
- package/dist/hooks.js.map +1 -0
- package/dist/lanes.js +62 -0
- package/dist/lanes.js.map +1 -0
- package/dist/model.js +30 -0
- package/dist/model.js.map +1 -0
- package/dist/policy.js +22 -0
- package/dist/policy.js.map +1 -0
- package/dist/queue.js +45 -0
- package/dist/queue.js.map +1 -0
- package/dist/retry.js +96 -0
- package/dist/retry.js.map +1 -0
- package/dist/runs.js +83 -0
- package/dist/runs.js.map +1 -0
- package/dist/runtime/compact.js +99 -0
- package/dist/runtime/compact.js.map +1 -0
- package/dist/runtime/lanes.js +66 -0
- package/dist/runtime/lanes.js.map +1 -0
- package/dist/runtime/orchestrate.js +121 -0
- package/dist/runtime/orchestrate.js.map +1 -0
- package/dist/runtime/queue.js +50 -0
- package/dist/runtime/queue.js.map +1 -0
- package/dist/runtime/retry.js +127 -0
- package/dist/runtime/retry.js.map +1 -0
- package/dist/runtime/runs.js +105 -0
- package/dist/runtime/runs.js.map +1 -0
- package/dist/serve.js +209 -0
- package/dist/serve.js.map +1 -0
- package/dist/session.js +75 -0
- package/dist/session.js.map +1 -0
- package/dist/setup.js +254 -0
- package/dist/setup.js.map +1 -0
- package/dist/skills.js +89 -0
- package/dist/skills.js.map +1 -0
- package/dist/subagent.js +71 -0
- package/dist/subagent.js.map +1 -0
- package/dist/system-prompt.js +157 -0
- package/dist/system-prompt.js.map +1 -0
- package/dist/telegram/admin-bot.js +705 -0
- package/dist/telegram/admin-bot.js.map +1 -0
- package/dist/telegram/agent-bot.js +551 -0
- package/dist/telegram/agent-bot.js.map +1 -0
- package/dist/telegram/bot-approval.js +63 -0
- package/dist/telegram/bot-approval.js.map +1 -0
- package/dist/telegram/draft-stream.js +86 -0
- package/dist/telegram/draft-stream.js.map +1 -0
- package/dist/telegram/format.js +106 -0
- package/dist/telegram/format.js.map +1 -0
- package/dist/telegram/helpers.js +87 -0
- package/dist/telegram/helpers.js.map +1 -0
- package/dist/telegram/pairing-notify.js +52 -0
- package/dist/telegram/pairing-notify.js.map +1 -0
- package/dist/telegram/pairing.js +138 -0
- package/dist/telegram/pairing.js.map +1 -0
- package/dist/telegram/resolve.js +33 -0
- package/dist/telegram/resolve.js.map +1 -0
- package/dist/telegram/transcribe.js +77 -0
- package/dist/telegram/transcribe.js.map +1 -0
- package/dist/telegram/types.js +3 -0
- package/dist/telegram/types.js.map +1 -0
- package/dist/telegram/voice-wizard.js +84 -0
- package/dist/telegram/voice-wizard.js.map +1 -0
- package/dist/telegram/wizard.js +89 -0
- package/dist/telegram/wizard.js.map +1 -0
- package/dist/telegram/wizards.js +297 -0
- package/dist/telegram/wizards.js.map +1 -0
- package/dist/telegram-admin.js +800 -0
- package/dist/telegram-admin.js.map +1 -0
- package/dist/telegram.js +118 -0
- package/dist/telegram.js.map +1 -0
- package/dist/tools/cron.js +94 -0
- package/dist/tools/cron.js.map +1 -0
- package/dist/tools/edit.js +29 -0
- package/dist/tools/edit.js.map +1 -0
- package/dist/tools/exec.js +38 -0
- package/dist/tools/exec.js.map +1 -0
- package/dist/tools/fetch.js +28 -0
- package/dist/tools/fetch.js.map +1 -0
- package/dist/tools/index.js +16 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/memory.js +164 -0
- package/dist/tools/memory.js.map +1 -0
- package/dist/tools/patch.js +284 -0
- package/dist/tools/patch.js.map +1 -0
- package/dist/tools/read.js +26 -0
- package/dist/tools/read.js.map +1 -0
- package/dist/tools/search.js +62 -0
- package/dist/tools/search.js.map +1 -0
- package/dist/tools/subagent.js +48 -0
- package/dist/tools/subagent.js.map +1 -0
- package/dist/tools/write.js +22 -0
- package/dist/tools/write.js.map +1 -0
- package/dist/tui/commands.js +450 -0
- package/dist/tui/commands.js.map +1 -0
- package/dist/tui/components/assistant-message.js +26 -0
- package/dist/tui/components/assistant-message.js.map +1 -0
- package/dist/tui/components/chat-log.js +94 -0
- package/dist/tui/components/chat-log.js.map +1 -0
- package/dist/tui/components/custom-editor.js +40 -0
- package/dist/tui/components/custom-editor.js.map +1 -0
- package/dist/tui/components/hint-bar.js +13 -0
- package/dist/tui/components/hint-bar.js.map +1 -0
- package/dist/tui/components/tool-execution.js +73 -0
- package/dist/tui/components/tool-execution.js.map +1 -0
- package/dist/tui/components/user-message.js +19 -0
- package/dist/tui/components/user-message.js.map +1 -0
- package/dist/tui/components/welcome.js +147 -0
- package/dist/tui/components/welcome.js.map +1 -0
- package/dist/tui/context.js +3 -0
- package/dist/tui/context.js.map +1 -0
- package/dist/tui/theme.js +91 -0
- package/dist/tui/theme.js.map +1 -0
- package/dist/tui/tui.js +389 -0
- package/dist/tui/tui.js.map +1 -0
- package/dist/tui/ws-handler.js +154 -0
- package/dist/tui/ws-handler.js.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/dist/usage.js +88 -0
- package/dist/usage.js.map +1 -0
- package/dist/workspace.js +245 -0
- package/dist/workspace.js.map +1 -0
- package/package.json +74 -0
package/dist/runs.js
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
// Run tracking: prevents concurrent runs on the same session
|
|
2
|
+
import { QUEUE_WAIT_TIMEOUT_MS } from "./core/constants.js";
|
|
3
|
+
// Primary index: runId -> handle
|
|
4
|
+
const runsByRunId = new Map();
|
|
5
|
+
// Secondary index: sessionId -> runId (latest)
|
|
6
|
+
const sessionToRunId = new Map();
|
|
7
|
+
const waiters = new Map();
|
|
8
|
+
let runCounter = 0;
|
|
9
|
+
export function generateRunId() {
|
|
10
|
+
return `run-${Date.now()}-${++runCounter}`;
|
|
11
|
+
}
|
|
12
|
+
export function setActiveRun(sessionId, handle) {
|
|
13
|
+
// Abort any existing run for this session
|
|
14
|
+
const existingRunId = sessionToRunId.get(sessionId);
|
|
15
|
+
if (existingRunId) {
|
|
16
|
+
const existing = runsByRunId.get(existingRunId);
|
|
17
|
+
if (existing)
|
|
18
|
+
existing.abort();
|
|
19
|
+
runsByRunId.delete(existingRunId);
|
|
20
|
+
}
|
|
21
|
+
runsByRunId.set(handle.runId, handle);
|
|
22
|
+
sessionToRunId.set(sessionId, handle.runId);
|
|
23
|
+
}
|
|
24
|
+
export function clearActiveRun(runId) {
|
|
25
|
+
const handle = runsByRunId.get(runId);
|
|
26
|
+
if (!handle)
|
|
27
|
+
return;
|
|
28
|
+
runsByRunId.delete(runId);
|
|
29
|
+
// Only clear session mapping if this is still the latest run
|
|
30
|
+
if (sessionToRunId.get(handle.sessionId) === runId) {
|
|
31
|
+
sessionToRunId.delete(handle.sessionId);
|
|
32
|
+
}
|
|
33
|
+
// Notify waiters for this session
|
|
34
|
+
const sessionWaiters = waiters.get(handle.sessionId);
|
|
35
|
+
if (sessionWaiters) {
|
|
36
|
+
for (const resolve of sessionWaiters)
|
|
37
|
+
resolve(true);
|
|
38
|
+
waiters.delete(handle.sessionId);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
export function isRunActive(sessionId) {
|
|
42
|
+
const runId = sessionToRunId.get(sessionId);
|
|
43
|
+
return runId !== undefined && runsByRunId.has(runId);
|
|
44
|
+
}
|
|
45
|
+
export function getActiveRun(sessionId) {
|
|
46
|
+
const runId = sessionToRunId.get(sessionId);
|
|
47
|
+
return runId ? runsByRunId.get(runId) : undefined;
|
|
48
|
+
}
|
|
49
|
+
export function abortRun(sessionId) {
|
|
50
|
+
const runId = sessionToRunId.get(sessionId);
|
|
51
|
+
if (!runId)
|
|
52
|
+
return false;
|
|
53
|
+
const handle = runsByRunId.get(runId);
|
|
54
|
+
if (handle) {
|
|
55
|
+
handle.abort();
|
|
56
|
+
clearActiveRun(runId);
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
export function waitForRunEnd(sessionId, timeoutMs = QUEUE_WAIT_TIMEOUT_MS) {
|
|
62
|
+
if (!isRunActive(sessionId))
|
|
63
|
+
return Promise.resolve(true);
|
|
64
|
+
return new Promise((resolve) => {
|
|
65
|
+
const timer = setTimeout(() => {
|
|
66
|
+
const set = waiters.get(sessionId);
|
|
67
|
+
if (set)
|
|
68
|
+
set.delete(wrappedResolve);
|
|
69
|
+
resolve(false);
|
|
70
|
+
}, timeoutMs);
|
|
71
|
+
const wrappedResolve = (ended) => {
|
|
72
|
+
clearTimeout(timer);
|
|
73
|
+
resolve(ended);
|
|
74
|
+
};
|
|
75
|
+
if (!waiters.has(sessionId))
|
|
76
|
+
waiters.set(sessionId, new Set());
|
|
77
|
+
waiters.get(sessionId).add(wrappedResolve);
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
export function getActiveRunCount() {
|
|
81
|
+
return runsByRunId.size;
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=runs.js.map
|
package/dist/runs.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runs.js","sourceRoot":"","sources":["../src/runs.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
// Context compaction: summarize old messages when context gets too large
|
|
2
|
+
import { chatDirect } from "../model.js";
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { agentMemoryDir } from "../workspace.js";
|
|
6
|
+
import { CHARS_PER_TOKEN, COMPACTION_TRIGGER_RATIO, MEMORY_FLUSH_MAX_CHARS } from "../core/constants.js";
|
|
7
|
+
function estimateTokens(messages) {
|
|
8
|
+
let chars = 0;
|
|
9
|
+
for (const msg of messages) {
|
|
10
|
+
chars += msg.content.length;
|
|
11
|
+
}
|
|
12
|
+
return Math.ceil(chars / CHARS_PER_TOKEN);
|
|
13
|
+
}
|
|
14
|
+
function splitHistory(messages, keepTurns) {
|
|
15
|
+
if (keepTurns <= 0)
|
|
16
|
+
return { old: messages, recent: [] };
|
|
17
|
+
const turnStarts = [];
|
|
18
|
+
for (let i = 0; i < messages.length; i++) {
|
|
19
|
+
if (messages[i].role === "user") {
|
|
20
|
+
turnStarts.push(i);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
if (turnStarts.length <= keepTurns) {
|
|
24
|
+
return { old: [], recent: messages };
|
|
25
|
+
}
|
|
26
|
+
const cutoff = turnStarts[turnStarts.length - keepTurns];
|
|
27
|
+
return {
|
|
28
|
+
old: messages.slice(0, cutoff),
|
|
29
|
+
recent: messages.slice(cutoff),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
const COMPACT_PROMPT = `Summarize the following conversation history concisely. Preserve:
|
|
33
|
+
- Key facts, decisions, and context established
|
|
34
|
+
- Important file paths, names, and technical details mentioned
|
|
35
|
+
- Current state of any ongoing tasks
|
|
36
|
+
- User preferences or instructions given
|
|
37
|
+
|
|
38
|
+
Be concise but don't lose critical context. Output only the summary, no preamble.`;
|
|
39
|
+
export async function compactHistory(client, model, history, opts) {
|
|
40
|
+
if (!opts.enabled)
|
|
41
|
+
return null;
|
|
42
|
+
const tokens = estimateTokens(history);
|
|
43
|
+
if (tokens < opts.maxTokens * COMPACTION_TRIGGER_RATIO)
|
|
44
|
+
return null;
|
|
45
|
+
const { old, recent } = splitHistory(history, opts.keepTurns);
|
|
46
|
+
if (old.length === 0)
|
|
47
|
+
return null;
|
|
48
|
+
await memoryFlush(client, model, old, opts.agentId);
|
|
49
|
+
const oldText = old.map((m) => {
|
|
50
|
+
return `[${m.role}]: ${m.content}`;
|
|
51
|
+
}).join("\n\n");
|
|
52
|
+
const summaryResult = await chatDirect(client, model, COMPACT_PROMPT, oldText);
|
|
53
|
+
const summaryMessage = {
|
|
54
|
+
role: "user",
|
|
55
|
+
content: `[Previous conversation summary]\n${summaryResult.content}\n[End of summary — conversation continues below]`,
|
|
56
|
+
};
|
|
57
|
+
const compacted = [summaryMessage, ...recent];
|
|
58
|
+
// Validate: compaction must actually reduce size
|
|
59
|
+
const compactedTokens = estimateTokens(compacted);
|
|
60
|
+
if (compactedTokens >= tokens) {
|
|
61
|
+
process.stderr.write(`\x1b[33m⚠ Compaction skipped: result (${compactedTokens} tokens) >= original (${tokens} tokens)\x1b[0m\n`);
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
return compacted;
|
|
65
|
+
}
|
|
66
|
+
// --- Memory flush ---
|
|
67
|
+
const FLUSH_PROMPT = `You are about to lose the following conversation history due to context compaction.
|
|
68
|
+
Extract any durable facts worth remembering: decisions made, user preferences discovered,
|
|
69
|
+
project details, file paths, names, dates, or anything the user would expect you to know later.
|
|
70
|
+
|
|
71
|
+
Format as concise bullet points. If nothing is worth saving, reply with "NOTHING".`;
|
|
72
|
+
async function memoryFlush(client, model, oldMessages, agentId) {
|
|
73
|
+
if (oldMessages.length === 0)
|
|
74
|
+
return;
|
|
75
|
+
try {
|
|
76
|
+
const oldText = oldMessages.map((m) => {
|
|
77
|
+
return `[${m.role}]: ${m.content}`;
|
|
78
|
+
}).join("\n\n");
|
|
79
|
+
if (oldText.length < 200)
|
|
80
|
+
return;
|
|
81
|
+
const response = await chatDirect(client, model, FLUSH_PROMPT, oldText.slice(0, MEMORY_FLUSH_MAX_CHARS));
|
|
82
|
+
const extracted = response.content;
|
|
83
|
+
if (extracted.trim() === "NOTHING" || extracted.trim().length < 10)
|
|
84
|
+
return;
|
|
85
|
+
const rootDir = agentMemoryDir(agentId);
|
|
86
|
+
const memoryDir = path.join(rootDir, "memory");
|
|
87
|
+
fs.mkdirSync(memoryDir, { recursive: true });
|
|
88
|
+
const today = new Date().toISOString().split("T")[0];
|
|
89
|
+
const dailyFile = path.join(memoryDir, `${today}.md`);
|
|
90
|
+
const header = fs.existsSync(dailyFile) ? "" : `# ${today}\n\n`;
|
|
91
|
+
const timestamp = new Date().toTimeString().split(" ")[0];
|
|
92
|
+
const entry = `${header}## ${timestamp} (auto-flush)\n\n${extracted}\n\n`;
|
|
93
|
+
fs.appendFileSync(dailyFile, entry);
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
// Memory flush is best-effort
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=compact.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compact.js","sourceRoot":"","sources":["../../src/runtime/compact.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// Concurrency lanes: limit parallel agent runs by type
|
|
2
|
+
export var Lane;
|
|
3
|
+
(function (Lane) {
|
|
4
|
+
Lane["Main"] = "main";
|
|
5
|
+
Lane["Cron"] = "cron";
|
|
6
|
+
Lane["Subagent"] = "subagent";
|
|
7
|
+
})(Lane || (Lane = {}));
|
|
8
|
+
export function createLaneManager() {
|
|
9
|
+
const lanes = new Map();
|
|
10
|
+
function configureLane(lane, limit) {
|
|
11
|
+
const existing = lanes.get(lane);
|
|
12
|
+
if (existing) {
|
|
13
|
+
existing.limit = limit;
|
|
14
|
+
}
|
|
15
|
+
else {
|
|
16
|
+
lanes.set(lane, { limit, active: 0, queue: [] });
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function releaseLane(lane) {
|
|
20
|
+
const config = lanes.get(lane);
|
|
21
|
+
if (!config)
|
|
22
|
+
return;
|
|
23
|
+
config.active--;
|
|
24
|
+
// Wake up next waiter
|
|
25
|
+
const next = config.queue.shift();
|
|
26
|
+
if (next)
|
|
27
|
+
next();
|
|
28
|
+
}
|
|
29
|
+
async function acquireLane(lane) {
|
|
30
|
+
let config = lanes.get(lane);
|
|
31
|
+
if (!config) {
|
|
32
|
+
// Default: unlimited
|
|
33
|
+
config = { limit: Infinity, active: 0, queue: [] };
|
|
34
|
+
lanes.set(lane, config);
|
|
35
|
+
}
|
|
36
|
+
if (config.active < config.limit) {
|
|
37
|
+
config.active++;
|
|
38
|
+
return () => releaseLane(lane);
|
|
39
|
+
}
|
|
40
|
+
// Wait for a slot
|
|
41
|
+
await new Promise((resolve) => {
|
|
42
|
+
config.queue.push(resolve);
|
|
43
|
+
});
|
|
44
|
+
config.active++;
|
|
45
|
+
return () => releaseLane(lane);
|
|
46
|
+
}
|
|
47
|
+
function getLaneStats() {
|
|
48
|
+
const stats = {};
|
|
49
|
+
for (const [lane, config] of lanes) {
|
|
50
|
+
stats[lane] = {
|
|
51
|
+
active: config.active,
|
|
52
|
+
limit: config.limit === Infinity ? -1 : config.limit,
|
|
53
|
+
queued: config.queue.length,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
return stats;
|
|
57
|
+
}
|
|
58
|
+
function reset() {
|
|
59
|
+
lanes.clear();
|
|
60
|
+
}
|
|
61
|
+
return { configureLane, acquireLane, getLaneStats, reset };
|
|
62
|
+
}
|
|
63
|
+
// Backward-compat singleton
|
|
64
|
+
const defaultManager = createLaneManager();
|
|
65
|
+
export const { configureLane, acquireLane, getLaneStats } = defaultManager;
|
|
66
|
+
//# sourceMappingURL=lanes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lanes.js","sourceRoot":"","sources":["../../src/runtime/lanes.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
// Chat orchestrator: single source of truth for the chat flow
|
|
2
|
+
//
|
|
3
|
+
// Deduplicates the check-active → queue → acquire-lane → prepare-history →
|
|
4
|
+
// run-agent-with-retry → save-messages → release sequence that was previously
|
|
5
|
+
// duplicated in routes.ts, ws-handler.ts, and agent-bot.ts.
|
|
6
|
+
import { runAgent } from "../agent.js";
|
|
7
|
+
import { loadMessages, saveMessage } from "../session.js";
|
|
8
|
+
import { setActiveRun, clearActiveRun, isRunActive, generateRunId } from "./runs.js";
|
|
9
|
+
import { queueOrProcess, drainQueue } from "./queue.js";
|
|
10
|
+
import { compactHistory } from "./compact.js";
|
|
11
|
+
import { withRetry } from "./retry.js";
|
|
12
|
+
import { acquireLane, Lane } from "./lanes.js";
|
|
13
|
+
/**
|
|
14
|
+
* Orchestrate a single chat turn. Handles:
|
|
15
|
+
* - Queue check (if a run is already active on this session)
|
|
16
|
+
* - Lane acquisition
|
|
17
|
+
* - History loading + compaction
|
|
18
|
+
* - Agent execution with retry
|
|
19
|
+
* - Message persistence
|
|
20
|
+
* - Cleanup (clear run, release lane)
|
|
21
|
+
*/
|
|
22
|
+
export async function orchestrate(opts) {
|
|
23
|
+
const { sessionId, message, config, systemPrompt, client, signal, onEvent, onRetry, onCompact, agentId, resumeSessionId, label, } = opts;
|
|
24
|
+
const model = opts.model ?? config.model;
|
|
25
|
+
const agentSystemPrompt = opts.agentSystemPrompt ?? systemPrompt;
|
|
26
|
+
const thinking = opts.thinking ?? config.thinking;
|
|
27
|
+
const effort = opts.effort ?? config.effort;
|
|
28
|
+
const maxTurns = opts.maxTurns ?? config.maxTurns;
|
|
29
|
+
// If a run is already active, queue this message
|
|
30
|
+
if (isRunActive(sessionId)) {
|
|
31
|
+
const queueResult = await queueOrProcess(sessionId, message);
|
|
32
|
+
if (queueResult.queued) {
|
|
33
|
+
const response = await queueResult.promise;
|
|
34
|
+
return { response, runId: "", sessionId, usage: null, queued: true };
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
const release = await acquireLane(Lane.Main);
|
|
38
|
+
const runId = generateRunId();
|
|
39
|
+
const abortController = signal ? new AbortController() : undefined;
|
|
40
|
+
if (signal && abortController) {
|
|
41
|
+
if (signal.aborted)
|
|
42
|
+
abortController.abort();
|
|
43
|
+
else
|
|
44
|
+
signal.addEventListener("abort", () => abortController.abort(), { once: true });
|
|
45
|
+
}
|
|
46
|
+
let streaming = false;
|
|
47
|
+
setActiveRun(sessionId, {
|
|
48
|
+
sessionId,
|
|
49
|
+
runId,
|
|
50
|
+
startedAt: Date.now(),
|
|
51
|
+
abort: () => abortController?.abort(),
|
|
52
|
+
isStreaming: () => streaming,
|
|
53
|
+
});
|
|
54
|
+
try {
|
|
55
|
+
// Load + compact history
|
|
56
|
+
let history = loadMessages(sessionId);
|
|
57
|
+
const compacted = await compactHistory(client, model, history, { ...config.compaction, agentId });
|
|
58
|
+
if (compacted) {
|
|
59
|
+
onCompact?.(history.length, compacted.length);
|
|
60
|
+
history = compacted;
|
|
61
|
+
}
|
|
62
|
+
streaming = true;
|
|
63
|
+
const result = await withRetry(() => runAgent(config.apiKey, model, agentSystemPrompt, history, message, {
|
|
64
|
+
maxTurns,
|
|
65
|
+
timeoutMs: config.timeoutSeconds * 1000,
|
|
66
|
+
signal: abortController?.signal,
|
|
67
|
+
onEvent,
|
|
68
|
+
toolPolicy: config.tools,
|
|
69
|
+
hooksEnabled: config.hooks.enabled,
|
|
70
|
+
sessionId,
|
|
71
|
+
thinking,
|
|
72
|
+
effort,
|
|
73
|
+
provider: config.provider,
|
|
74
|
+
baseUrl: config.baseUrl,
|
|
75
|
+
approvals: config.approvals,
|
|
76
|
+
...(Object.keys(config.mcp.servers).length > 0 && { mcpServers: config.mcp.servers }),
|
|
77
|
+
...(config.maxBudgetUsd && { maxBudgetUsd: config.maxBudgetUsd }),
|
|
78
|
+
...(resumeSessionId && { resumeSessionId }),
|
|
79
|
+
...(agentId && { agentId }),
|
|
80
|
+
}), {
|
|
81
|
+
maxRetries: config.retry.maxRetries,
|
|
82
|
+
backoffMs: config.retry.backoffMs,
|
|
83
|
+
onRetry: onRetry
|
|
84
|
+
? (attempt, kind) => onRetry(attempt, kind)
|
|
85
|
+
: undefined,
|
|
86
|
+
onCompact: async () => {
|
|
87
|
+
const h = loadMessages(sessionId);
|
|
88
|
+
await compactHistory(client, model, h, { ...config.compaction, enabled: true, agentId });
|
|
89
|
+
},
|
|
90
|
+
});
|
|
91
|
+
streaming = false;
|
|
92
|
+
// Persist messages
|
|
93
|
+
saveMessage(sessionId, { role: "user", content: message }, model, label);
|
|
94
|
+
if (result.response) {
|
|
95
|
+
saveMessage(sessionId, { role: "assistant", content: result.response }, model, label);
|
|
96
|
+
}
|
|
97
|
+
return {
|
|
98
|
+
response: result.response,
|
|
99
|
+
runId,
|
|
100
|
+
sessionId,
|
|
101
|
+
usage: result.usage,
|
|
102
|
+
sdkSessionId: result.sessionId,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
finally {
|
|
106
|
+
clearActiveRun(runId);
|
|
107
|
+
release();
|
|
108
|
+
// Drain queued messages: process the next one, reject the rest
|
|
109
|
+
const queued = drainQueue(sessionId);
|
|
110
|
+
if (queued.length > 0) {
|
|
111
|
+
const next = queued[0];
|
|
112
|
+
orchestrate({ ...opts, message: next.text })
|
|
113
|
+
.then((r) => next.resolve(r.response))
|
|
114
|
+
.catch((err) => next.reject(err instanceof Error ? err : new Error(String(err))));
|
|
115
|
+
for (const q of queued.slice(1)) {
|
|
116
|
+
q.reject(new Error("Superseded by newer message"));
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=orchestrate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"orchestrate.js","sourceRoot":"","sources":["../../src/runtime/orchestrate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// Message queue: per-session queue for inbound messages while agent is running
|
|
2
|
+
import { isRunActive, waitForRunEnd } from "./runs.js";
|
|
3
|
+
export function createMessageQueue() {
|
|
4
|
+
const queues = new Map();
|
|
5
|
+
function enqueueMessage(sessionId, text) {
|
|
6
|
+
return new Promise((resolve, reject) => {
|
|
7
|
+
if (!queues.has(sessionId))
|
|
8
|
+
queues.set(sessionId, []);
|
|
9
|
+
queues.get(sessionId).push({ text, resolve, reject, enqueuedAt: Date.now() });
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
function getQueueLength(sessionId) {
|
|
13
|
+
return queues.get(sessionId)?.length ?? 0;
|
|
14
|
+
}
|
|
15
|
+
function drainQueue(sessionId) {
|
|
16
|
+
const queue = queues.get(sessionId);
|
|
17
|
+
if (!queue || queue.length === 0)
|
|
18
|
+
return [];
|
|
19
|
+
queues.delete(sessionId);
|
|
20
|
+
return queue;
|
|
21
|
+
}
|
|
22
|
+
function clearQueue(sessionId) {
|
|
23
|
+
const queue = queues.get(sessionId);
|
|
24
|
+
if (queue) {
|
|
25
|
+
for (const msg of queue) {
|
|
26
|
+
msg.reject(new Error("Queue cleared"));
|
|
27
|
+
}
|
|
28
|
+
queues.delete(sessionId);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
async function queueOrProcess(sessionId, text) {
|
|
32
|
+
if (!isRunActive(sessionId)) {
|
|
33
|
+
return { queued: false };
|
|
34
|
+
}
|
|
35
|
+
// Run is active — enqueue and wait for run to end
|
|
36
|
+
const promise = enqueueMessage(sessionId, text);
|
|
37
|
+
await waitForRunEnd(sessionId);
|
|
38
|
+
return { queued: true, promise };
|
|
39
|
+
}
|
|
40
|
+
function reset() {
|
|
41
|
+
for (const [sid] of queues) {
|
|
42
|
+
clearQueue(sid);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return { enqueueMessage, getQueueLength, drainQueue, clearQueue, queueOrProcess, reset };
|
|
46
|
+
}
|
|
47
|
+
// Backward-compat singleton
|
|
48
|
+
const defaultQueue = createMessageQueue();
|
|
49
|
+
export const { enqueueMessage, getQueueLength, drainQueue, clearQueue, queueOrProcess } = defaultQueue;
|
|
50
|
+
//# sourceMappingURL=queue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queue.js","sourceRoot":"","sources":["../../src/runtime/queue.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAE/E,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAkBvD,MAAM,UAAU,kBAAkB;IAChC,MAAM,MAAM,GAAG,IAAI,GAAG,EAA2B,CAAC;IAElD,SAAS,cAAc,CAAC,SAAiB,EAAE,IAAY;QACrD,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC;gBAAE,MAAM,CAAC,GAAG,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;YACtD,MAAM,CAAC,GAAG,CAAC,SAAS,CAAE,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;QACjF,CAAC,CAAC,CAAC;IACL,CAAC;IAED,SAAS,cAAc,CAAC,SAAiB;QACvC,OAAO,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC,CAAC;IAC5C,CAAC;IAED,SAAS,UAAU,CAAC,SAAiB;QACnC,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QACpC,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,EAAE,CAAC;QAC5C,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QACzB,OAAO,KAAK,CAAC;IACf,CAAC;IAED,SAAS,UAAU,CAAC,SAAiB;QACnC,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QACpC,IAAI,KAAK,EAAE,CAAC;YACV,KAAK,MAAM,GAAG,IAAI,KAAK,EAAE,CAAC;gBACxB,GAAG,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,eAAe,CAAC,CAAC,CAAC;YACzC,CAAC;YACD,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QAC3B,CAAC;IACH,CAAC;IAED,KAAK,UAAU,cAAc,CAC3B,SAAiB,EACjB,IAAY;QAEZ,IAAI,CAAC,WAAW,CAAC,SAAS,CAAC,EAAE,CAAC;YAC5B,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;QAC3B,CAAC;QAED,kDAAkD;QAClD,MAAM,OAAO,GAAG,cAAc,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;QAChD,MAAM,aAAa,CAAC,SAAS,CAAC,CAAC;QAC/B,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;IACnC,CAAC;IAED,SAAS,KAAK;QACZ,KAAK,MAAM,CAAC,GAAG,CAAC,IAAI,MAAM,EAAE,CAAC;YAC3B,UAAU,CAAC,GAAG,CAAC,CAAC;QAClB,CAAC;IACH,CAAC;IAED,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,UAAU,EAAE,UAAU,EAAE,cAAc,EAAE,KAAK,EAAE,CAAC;AAC3F,CAAC;AAED,4BAA4B;AAC5B,MAAM,YAAY,GAAG,kBAAkB,EAAE,CAAC;AAC1C,MAAM,CAAC,MAAM,EAAE,cAAc,EAAE,cAAc,EAAE,UAAU,EAAE,UAAU,EAAE,cAAc,EAAE,GAAG,YAAY,CAAC"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
// Error classification + retry logic for agent runs
|
|
2
|
+
/** Extract HTTP status code from SDK error objects or message */
|
|
3
|
+
function extractStatusCode(err) {
|
|
4
|
+
// OpenAI SDK and Anthropic SDK both set .status on errors
|
|
5
|
+
const status = err.status ?? err.statusCode;
|
|
6
|
+
if (typeof status === "number")
|
|
7
|
+
return status;
|
|
8
|
+
// Fallback: extract 3-digit HTTP codes from message — but only when
|
|
9
|
+
// they appear as standalone tokens (e.g. "Error 429" not "model-429b")
|
|
10
|
+
const match = err.message.match(/\b([4-5]\d{2})\b/);
|
|
11
|
+
return match ? Number(match[1]) : undefined;
|
|
12
|
+
}
|
|
13
|
+
export function classifyError(err) {
|
|
14
|
+
const msg = err.message.toLowerCase();
|
|
15
|
+
const status = extractStatusCode(err);
|
|
16
|
+
// 1. User abort — exact match only (not substring) to avoid collision with timeout
|
|
17
|
+
if (msg === "aborted" || msg === "the operation was aborted" || msg === "this operation was aborted") {
|
|
18
|
+
return "abort";
|
|
19
|
+
}
|
|
20
|
+
// Also check for AbortError name (DOMException style)
|
|
21
|
+
if (err.name === "AbortError") {
|
|
22
|
+
return "abort";
|
|
23
|
+
}
|
|
24
|
+
// 2. Status-code-first classification (more reliable than string matching)
|
|
25
|
+
if (status) {
|
|
26
|
+
if (status === 401 || status === 403)
|
|
27
|
+
return "auth";
|
|
28
|
+
if (status === 402)
|
|
29
|
+
return "billing";
|
|
30
|
+
if (status === 429)
|
|
31
|
+
return "rate_limit";
|
|
32
|
+
if (status === 400 || status === 422)
|
|
33
|
+
return "format";
|
|
34
|
+
if (status === 408)
|
|
35
|
+
return "timeout";
|
|
36
|
+
if (status >= 500 && status < 600)
|
|
37
|
+
return "server_error";
|
|
38
|
+
}
|
|
39
|
+
// 3. String-based fallbacks for errors without status codes
|
|
40
|
+
// Auth
|
|
41
|
+
if (msg.includes("unauthorized") || msg.includes("invalid api key") || msg.includes("token expired")) {
|
|
42
|
+
return "auth";
|
|
43
|
+
}
|
|
44
|
+
// Billing
|
|
45
|
+
if (msg.includes("insufficient") || msg.includes("payment required") || msg.includes("billing")) {
|
|
46
|
+
return "billing";
|
|
47
|
+
}
|
|
48
|
+
// Rate limit
|
|
49
|
+
if (msg.includes("rate limit") || msg.includes("too many requests")
|
|
50
|
+
|| msg.includes("quota") || msg.includes("resource exhausted")) {
|
|
51
|
+
return "rate_limit";
|
|
52
|
+
}
|
|
53
|
+
// Context overflow
|
|
54
|
+
if (msg.includes("context") && (msg.includes("exceeded") || msg.includes("too large"))
|
|
55
|
+
|| msg.includes("prompt is too long") || msg.includes("request too large")
|
|
56
|
+
|| msg.includes("maximum context length")) {
|
|
57
|
+
return "overflow";
|
|
58
|
+
}
|
|
59
|
+
// Timeout (check after abort to avoid misclassification)
|
|
60
|
+
if (msg.includes("timeout") || msg.includes("deadline exceeded") || msg.includes("etimedout")) {
|
|
61
|
+
return "timeout";
|
|
62
|
+
}
|
|
63
|
+
// Server errors
|
|
64
|
+
if (msg.includes("service unavailable") || msg.includes("internal server error")
|
|
65
|
+
|| msg.includes("bad gateway")) {
|
|
66
|
+
return "server_error";
|
|
67
|
+
}
|
|
68
|
+
// Format errors
|
|
69
|
+
if (msg.includes("invalid request") || msg.includes("validation")) {
|
|
70
|
+
return "format";
|
|
71
|
+
}
|
|
72
|
+
return "unknown";
|
|
73
|
+
}
|
|
74
|
+
export function isRetryable(kind) {
|
|
75
|
+
return kind === "rate_limit" || kind === "timeout" || kind === "server_error";
|
|
76
|
+
}
|
|
77
|
+
const DEFAULT_MAX_BACKOFF_MS = 30_000;
|
|
78
|
+
/**
|
|
79
|
+
* Wrap an async function with retry logic.
|
|
80
|
+
* - rate_limit/timeout/server_error: retry with capped exponential backoff
|
|
81
|
+
* - overflow: call onCompact() then retry once
|
|
82
|
+
* - auth/billing/format/abort: fail immediately
|
|
83
|
+
*/
|
|
84
|
+
export async function withRetry(fn, opts) {
|
|
85
|
+
let lastError;
|
|
86
|
+
let overflowRetried = false;
|
|
87
|
+
const maxBackoff = opts.maxBackoffMs ?? DEFAULT_MAX_BACKOFF_MS;
|
|
88
|
+
for (let attempt = 0; attempt <= opts.maxRetries; attempt++) {
|
|
89
|
+
try {
|
|
90
|
+
return await fn();
|
|
91
|
+
}
|
|
92
|
+
catch (err) {
|
|
93
|
+
lastError = err instanceof Error ? err : new Error(String(err));
|
|
94
|
+
const kind = classifyError(lastError);
|
|
95
|
+
// Non-retryable errors
|
|
96
|
+
if (kind === "auth" || kind === "billing" || kind === "format" || kind === "abort") {
|
|
97
|
+
throw lastError;
|
|
98
|
+
}
|
|
99
|
+
// Overflow: compact and retry once
|
|
100
|
+
if (kind === "overflow" && !overflowRetried && opts.onCompact) {
|
|
101
|
+
overflowRetried = true;
|
|
102
|
+
opts.onRetry?.(attempt, kind, lastError);
|
|
103
|
+
await opts.onCompact();
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
// Last attempt — don't retry
|
|
107
|
+
if (attempt === opts.maxRetries)
|
|
108
|
+
break;
|
|
109
|
+
// Retryable: capped exponential backoff
|
|
110
|
+
if (isRetryable(kind)) {
|
|
111
|
+
opts.onRetry?.(attempt, kind, lastError);
|
|
112
|
+
const delay = Math.min(opts.backoffMs * Math.pow(2, attempt), maxBackoff);
|
|
113
|
+
await new Promise((r) => setTimeout(r, delay));
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
// Unknown errors: retry once then fail
|
|
117
|
+
if (kind === "unknown" && attempt === 0) {
|
|
118
|
+
opts.onRetry?.(attempt, kind, lastError);
|
|
119
|
+
await new Promise((r) => setTimeout(r, opts.backoffMs));
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
break;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
throw lastError;
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=retry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retry.js","sourceRoot":"","sources":["../../src/runtime/retry.ts"],"names":[],"mappings":"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"}
|